• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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