• 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 namespace OHOS {
22 namespace HiviewDFX {
23 namespace {
24 constexpr HiLogLabel LABEL = { LOG_CORE, 0xD002D08, "JS_CALLBACK_MANAGER" };
25 constexpr int CONTEXT_INDEX = 0;
26 constexpr int CALLBACK_FUNC_INDEX = 1;
27 constexpr int RELEASE_FUNC_INDEX = 2;
DeleteWork(uv_work_t * work)28 void DeleteWork(uv_work_t* work)
29 {
30     if (work != nullptr) {
31         delete work;
32     }
33 }
34 
RunCallback(CallbackContext * context,std::tuple<CallbackContext *,CALLBACK_FUNC,RELEASE_FUNC> & current)35 void RunCallback(CallbackContext* context, std::tuple<CallbackContext*, CALLBACK_FUNC, RELEASE_FUNC>& current)
36 {
37     uv_loop_t* loop = nullptr;
38     napi_get_uv_event_loop(context->env, &loop);
39     if (loop == nullptr) {
40         HiLog::Debug(LABEL, "failed to get uv_loop.");
41         return;
42     }
43     context->callback = std::get<CALLBACK_FUNC_INDEX>(current);
44     context->release = std::get<RELEASE_FUNC_INDEX>(current);
45     uv_work_t* work = new(std::nothrow) uv_work_t();
46     if (work == nullptr) {
47         HiLog::Debug(LABEL, "uv_work new failed, no memory left.");
48         return;
49     }
50     work->data = reinterpret_cast<void*>(context);
51     uv_queue_work(
52         loop,
53         work,
54         [] (uv_work_t* work) {},
55         [] (uv_work_t* work, int status) {
56             if (work == nullptr || work->data == nullptr) {
57                 DeleteWork(work);
58                 return;
59             }
60             CallbackContext* context = reinterpret_cast<CallbackContext*>(work->data);
61             if (context == nullptr) {
62                 DeleteWork(work);
63                 return;
64             }
65             napi_handle_scope scope = nullptr;
66             if (context->env == nullptr) {
67                 DeleteWork(work);
68                 return;
69             }
70             napi_open_handle_scope(context->env, &scope);
71             if (scope == nullptr) {
72                 HiLog::Debug(LABEL, "napi scope is null.");
73                 DeleteWork(work);
74                 return;
75             }
76             if (context->callback != nullptr) {
77                 context->callback(context->env, context->ref, context->threadId);
78             }
79             napi_close_handle_scope(context->env, scope);
80             DeleteWork(work);
81             if (context->release != nullptr) {
82                 context->release(context->threadId);
83             }
84         });
85 }
86 }
87 
Add(CallbackContext * context,CALLBACK_FUNC callback,RELEASE_FUNC release)88 void JsCallbackManager::Add(CallbackContext* context, CALLBACK_FUNC callback,
89     RELEASE_FUNC release)
90 {
91     {
92         if (IsReleased.load(std::memory_order_acquire)) {
93             return;
94         }
95         std::lock_guard<std::mutex> lock(managerMutex);
96         jsCallbacks.emplace(std::make_tuple(context, callback, [this, release] (pid_t threadId) {
97             if (release == nullptr) {
98                 this->ImmediateRun(true);
99             } else {
100                 // Destructor of JsCallbackManager will be called in release callback,
101                 // so no need to call next callback in queue.
102                 release(threadId);
103             }
104         }));
105         if (inCalling.load(std::memory_order_acquire)) {
106             return;
107         }
108     }
109     ImmediateRun();
110 }
111 
Release()112 void JsCallbackManager::Release()
113 {
114     IsReleased = true;
115     Clear(jsCallbacks);
116 }
117 
ImmediateRun(bool needPop)118 void JsCallbackManager::ImmediateRun(bool needPop)
119 {
120     inCalling = true;
121     std::tuple<CallbackContext*, CALLBACK_FUNC, RELEASE_FUNC> current;
122     CallbackContext* context;
123     {
124         if (IsReleased.load(std::memory_order_acquire)) {
125             return;
126         }
127         std::lock_guard<std::mutex> lock(managerMutex);
128         if (needPop && !jsCallbacks.empty()) {
129             jsCallbacks.pop();
130         }
131         if (jsCallbacks.empty() && !IsReleased.load(std::memory_order_acquire)) {
132             inCalling = false;
133             return;
134         }
135         current = jsCallbacks.front();
136         context = std::get<CONTEXT_INDEX>(current);
137         if (context == nullptr || IsReleased.load(std::memory_order_acquire)) {
138             inCalling = false;
139             return;
140         }
141     }
142     if (IsReleased.load(std::memory_order_acquire)) {
143         return;
144     }
145     RunCallback(context, current);
146 }
147 
Clear(TaskQueue & tasks)148 void JsCallbackManager::Clear(TaskQueue& tasks)
149 {
150     TaskQueue empty;
151     swap(empty, tasks);
152 }
153 } // HiviewDFX
154 } // OHOS