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