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 [] (uv_work_t* work, int status) {
61 if (work == nullptr || work->data == nullptr) {
62 DeleteWork(work);
63 return;
64 }
65 CallbackContext* context = reinterpret_cast<CallbackContext*>(work->data);
66 if (context == nullptr) {
67 DeleteWork(work);
68 return;
69 }
70 napi_handle_scope scope = nullptr;
71 if (context->env == nullptr) {
72 DeleteWork(work);
73 return;
74 }
75 napi_open_handle_scope(context->env, &scope);
76 if (scope == nullptr) {
77 HILOG_DEBUG(LOG_CORE, "napi scope is null.");
78 DeleteWork(work);
79 return;
80 }
81 if (context->callback != nullptr) {
82 context->callback(context->env, context->ref, context->threadId);
83 }
84 napi_close_handle_scope(context->env, scope);
85 DeleteWork(work);
86 if (context->release != nullptr) {
87 context->release(context->threadId);
88 }
89 }, uv_qos_default);
90 }
91 }
92
Add(CallbackContext * context,CALLBACK_FUNC callback,RELEASE_FUNC release)93 void JsCallbackManager::Add(CallbackContext* context, CALLBACK_FUNC callback,
94 RELEASE_FUNC release)
95 {
96 {
97 if (IsReleased.load(std::memory_order_acquire)) {
98 return;
99 }
100 std::lock_guard<std::mutex> lock(managerMutex);
101 jsCallbacks.emplace(std::make_tuple(context, callback, [this, release] (pid_t threadId) {
102 if (release == nullptr) {
103 this->ImmediateRun(true);
104 } else {
105 // Destructor of JsCallbackManager will be called in release callback,
106 // so no need to call next callback in queue.
107 release(threadId);
108 }
109 }));
110 if (inCalling.load(std::memory_order_acquire)) {
111 return;
112 }
113 }
114 ImmediateRun();
115 }
116
Release()117 void JsCallbackManager::Release()
118 {
119 IsReleased = true;
120 Clear(jsCallbacks);
121 }
122
ImmediateRun(bool needPop)123 void JsCallbackManager::ImmediateRun(bool needPop)
124 {
125 inCalling = true;
126 std::tuple<CallbackContext*, CALLBACK_FUNC, RELEASE_FUNC> current;
127 CallbackContext* context;
128 {
129 if (IsReleased.load(std::memory_order_acquire)) {
130 return;
131 }
132 std::lock_guard<std::mutex> lock(managerMutex);
133 if (needPop && !jsCallbacks.empty()) {
134 jsCallbacks.pop();
135 }
136 if (jsCallbacks.empty() && !IsReleased.load(std::memory_order_acquire)) {
137 inCalling = false;
138 return;
139 }
140 current = jsCallbacks.front();
141 context = std::get<CONTEXT_INDEX>(current);
142 if (context == nullptr || IsReleased.load(std::memory_order_acquire)) {
143 inCalling = false;
144 return;
145 }
146 }
147 if (IsReleased.load(std::memory_order_acquire)) {
148 return;
149 }
150 RunCallback(context, current);
151 }
152
Clear(TaskQueue & tasks)153 void JsCallbackManager::Clear(TaskQueue& tasks)
154 {
155 TaskQueue empty;
156 swap(empty, tasks);
157 }
158 } // HiviewDFX
159 } // OHOS
160