1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "js_callback_manager.h"
17
18 #include "hilog/log.h"
19 #include "uv.h"
20
21 #undef LOG_DOMAIN
22 #define LOG_DOMAIN 0xD002D08
23
24 #undef LOG_TAG
25 #define LOG_TAG "JS_CALLBACK_MANAGER"
26
27 namespace OHOS {
28 namespace HiviewDFX {
29 namespace {
30 constexpr int CONTEXT_INDEX = 0;
31 constexpr int CALLBACK_FUNC_INDEX = 1;
32 constexpr int RELEASE_FUNC_INDEX = 2;
DeleteWork(uv_work_t * work)33 void DeleteWork(uv_work_t* work)
34 {
35 if (work != nullptr) {
36 delete work;
37 }
38 }
39
RunCallback(CallbackContext * context,std::tuple<CallbackContext *,CALLBACK_FUNC,RELEASE_FUNC> & current)40 void RunCallback(CallbackContext* context, std::tuple<CallbackContext*, CALLBACK_FUNC, RELEASE_FUNC>& current)
41 {
42 uv_loop_t* loop = nullptr;
43 napi_get_uv_event_loop(context->env, &loop);
44 if (loop == nullptr) {
45 HILOG_DEBUG(LOG_CORE, "failed to get uv_loop.");
46 return;
47 }
48 context->callback = std::get<CALLBACK_FUNC_INDEX>(current);
49 context->release = std::get<RELEASE_FUNC_INDEX>(current);
50 uv_work_t* work = new(std::nothrow) uv_work_t();
51 if (work == nullptr) {
52 HILOG_DEBUG(LOG_CORE, "uv_work new failed, no memory left.");
53 return;
54 }
55 work->data = reinterpret_cast<void*>(context);
56 uv_queue_work_with_qos(
57 loop,
58 work,
59 [] (uv_work_t* work) {
60 HILOG_DEBUG(LOG_CORE, "enter uv work callback.");
61 },
62 [] (uv_work_t* work, int status) {
63 if (work == nullptr || work->data == nullptr) {
64 DeleteWork(work);
65 return;
66 }
67 CallbackContext* context = reinterpret_cast<CallbackContext*>(work->data);
68 if (context == nullptr || context->env == nullptr) {
69 DeleteWork(work);
70 return;
71 }
72 napi_handle_scope scope = nullptr;
73 napi_open_handle_scope(context->env, &scope);
74 if (scope == nullptr) {
75 HILOG_DEBUG(LOG_CORE, "napi scope is null.");
76 DeleteWork(work);
77 return;
78 }
79 if (context->callback != nullptr) {
80 context->callback(context->env, context->ref, context->threadId);
81 }
82 napi_close_handle_scope(context->env, scope);
83 DeleteWork(work);
84 if (context->release != nullptr) {
85 context->release(context->threadId);
86 }
87 }, uv_qos_default);
88 }
89 }
90
Add(CallbackContext * context,CALLBACK_FUNC callback,RELEASE_FUNC release)91 void JsCallbackManager::Add(CallbackContext* context, CALLBACK_FUNC callback,
92 RELEASE_FUNC release)
93 {
94 {
95 if (IsReleased.load(std::memory_order_acquire)) {
96 return;
97 }
98 std::lock_guard<std::mutex> lock(managerMutex);
99 jsCallbacks.emplace(std::make_tuple(context, callback, [this, release] (pid_t threadId) {
100 if (release == nullptr) {
101 this->ImmediateRun(true);
102 } else {
103 // Destructor of JsCallbackManager will be called in release callback,
104 // so no need to call next callback in queue.
105 release(threadId);
106 }
107 }));
108 if (inCalling.load(std::memory_order_acquire)) {
109 return;
110 }
111 }
112 ImmediateRun();
113 }
114
Release()115 void JsCallbackManager::Release()
116 {
117 IsReleased = true;
118 Clear(jsCallbacks);
119 }
120
ImmediateRun(bool needPop)121 void JsCallbackManager::ImmediateRun(bool needPop)
122 {
123 inCalling = true;
124 std::tuple<CallbackContext*, CALLBACK_FUNC, RELEASE_FUNC> current;
125 CallbackContext* context;
126 {
127 if (IsReleased.load(std::memory_order_acquire)) {
128 return;
129 }
130 std::lock_guard<std::mutex> lock(managerMutex);
131 if (needPop && !jsCallbacks.empty()) {
132 jsCallbacks.pop();
133 }
134 if (jsCallbacks.empty() && !IsReleased.load(std::memory_order_acquire)) {
135 inCalling = false;
136 return;
137 }
138 current = jsCallbacks.front();
139 context = std::get<CONTEXT_INDEX>(current);
140 if (context == nullptr || IsReleased.load(std::memory_order_acquire)) {
141 inCalling = false;
142 return;
143 }
144 }
145 if (IsReleased.load(std::memory_order_acquire)) {
146 return;
147 }
148 RunCallback(context, current);
149 }
150
Clear(TaskQueue & tasks)151 void JsCallbackManager::Clear(TaskQueue& tasks)
152 {
153 TaskQueue empty;
154 swap(empty, tasks);
155 }
156 } // HiviewDFX
157 } // OHOS
158