1 /*
2 * Copyright (c) 2023 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 #define LOG_TAG "UvQueue"
16 #include "js_uv_queue.h"
17
18 #include <memory>
19
20 #include "js_scope.h"
21 #include "logger.h"
22 #include "napi/native_node_api.h"
23 namespace OHOS::AppDataMgrJsKit {
24 using namespace OHOS::Rdb;
25 constexpr size_t ARGC_MAX = 6;
UvQueue(napi_env env)26 UvQueue::UvQueue(napi_env env) : env_(env)
27 {
28 if (env != nullptr) {
29 napi_get_uv_event_loop(env, &loop_);
30 }
31 handler_ = AppExecFwk::EventHandler::Current();
32 }
33
~UvQueue()34 UvQueue::~UvQueue()
35 {
36 LOG_DEBUG("No memory leak for queue-callback.");
37 env_ = nullptr;
38 handler_ = nullptr;
39 }
40
AsyncCall(UvCallback callback,Args args,Result result)41 void UvQueue::AsyncCall(UvCallback callback, Args args, Result result)
42 {
43 if (callback.IsNull()) {
44 LOG_ERROR("callback is nullptr.");
45 return;
46 }
47 auto entry = std::make_shared<UvEntry>();
48 entry->env_ = env_;
49 entry->object_ = callback.object_;
50 entry->callback_ = callback.callback_;
51 entry->repeat_ = callback.repeat_;
52 entry->getter_ = std::move(callback.getter_);
53 entry->args_ = std::move(args);
54 entry->result_ = std::move(result);
55 auto status = napi_send_event(env_, GenCallbackTask(entry), napi_eprio_immediate);
56 if (status != napi_ok) {
57 LOG_ERROR("Failed to SendEvent, status:%{public}d", status);
58 }
59 }
60
AsyncCallInOrder(UvCallback callback,Args args,Result result)61 void UvQueue::AsyncCallInOrder(UvCallback callback, Args args, Result result)
62 {
63 if (handler_ == nullptr) {
64 AsyncCall(std::move(callback), std::move(args), std::move(result));
65 }
66 if (callback.IsNull()) {
67 LOG_ERROR("handler_ or callback is nullptr.");
68 return;
69 }
70 auto entry = std::make_shared<UvEntry>();
71 if (entry == nullptr) {
72 LOG_ERROR("No memory for UvEntry.");
73 return;
74 }
75 entry->env_ = env_;
76 entry->callback_ = callback.callback_;
77 entry->repeat_ = callback.repeat_;
78 entry->args_ = std::move(args);
79 if (handler_ != nullptr) {
80 handler_->PostTask(GenCallbackTask(entry));
81 }
82 }
83
AsyncPromise(UvPromise promise,UvQueue::Args args)84 void UvQueue::AsyncPromise(UvPromise promise, UvQueue::Args args)
85 {
86 if (promise.IsNull()) {
87 LOG_ERROR("promise is nullptr.");
88 return;
89 }
90 auto entry = std::make_shared<UvEntry>();
91 entry->env_ = env_;
92 entry->defer_ = promise.defer_;
93 entry->args_ = std::move(args);
94 auto status = napi_send_event(env_, GenPromiseTask(entry), napi_eprio_immediate);
95 if (status != napi_ok) {
96 LOG_ERROR("Failed to SendEvent, status:%{public}d", status);
97 }
98 }
99
Execute(UvQueue::Task task)100 void UvQueue::Execute(UvQueue::Task task)
101 {
102 if (loop_ == nullptr || !task) {
103 LOG_ERROR("loop_ or task is nullptr.");
104 return;
105 }
106 uv_work_t *work = new (std::nothrow) uv_work_t;
107 if (work == nullptr) {
108 LOG_ERROR("No memory for uv_work_t.");
109 return;
110 }
111 auto entry = new (std::nothrow) Task();
112 if (entry == nullptr) {
113 delete work;
114 LOG_ERROR("No memory for Task.");
115 return;
116 }
117 *entry = task;
118 work->data = entry;
119 int ret = uv_queue_work(loop_, work, DoExecute, [](uv_work_t *work, int status) { delete work; });
120 if (ret < 0) {
121 LOG_ERROR("uv_queue_work failed, errCode:%{public}d", ret);
122 delete entry;
123 delete work;
124 }
125 }
126
GetEnv()127 napi_env UvQueue::GetEnv()
128 {
129 return env_;
130 }
131
Resolved(napi_env env,napi_callback_info info)132 napi_value UvQueue::Resolved(napi_env env, napi_callback_info info)
133 {
134 return Future(env, info, false);
135 }
136
Rejected(napi_env env,napi_callback_info info)137 napi_value UvQueue::Rejected(napi_env env, napi_callback_info info)
138 {
139 return Future(env, info, true);
140 }
141
Future(napi_env env,napi_callback_info info,bool exception)142 napi_value UvQueue::Future(napi_env env, napi_callback_info info, bool exception)
143 {
144 size_t argc = ARGC_MAX;
145 napi_value argv[ARGC_MAX] = { nullptr };
146 void *data = nullptr;
147 auto status = napi_get_cb_info(env, info, &argc, argv, nullptr, &data);
148 if (status != napi_ok || data == nullptr) {
149 return nullptr;
150 }
151 auto *entry = static_cast<Result *>(data);
152 if (entry) {
153 (*entry)(env, argc, argv, exception);
154 delete entry;
155 }
156 return nullptr;
157 }
158
DoExecute(uv_work_t * work)159 void UvQueue::DoExecute(uv_work_t *work)
160 {
161 if (work == nullptr) {
162 return;
163 }
164 Task *task = static_cast<Task *>(work->data);
165 work->data = nullptr;
166 (*task)();
167 delete task;
168 }
169
GenCallbackTask(std::shared_ptr<UvEntry> entry)170 UvQueue::Task UvQueue::GenCallbackTask(std::shared_ptr<UvEntry> entry)
171 {
172 return [entry]() {
173 if (entry == nullptr) {
174 return;
175 }
176 Scope scope(entry->env_);
177 napi_value method = entry->GetCallback();
178 if (method == nullptr) {
179 entry->DelReference();
180 LOG_ERROR("The callback is invalid, maybe is cleared!");
181 return;
182 }
183 napi_value argv[ARGC_MAX] = { nullptr };
184 auto argc = entry->GetArgv(argv, ARGC_MAX);
185 auto object = entry->GetObject();
186 napi_value promise = nullptr;
187 auto status = napi_call_function(entry->env_, object, method, argc, argv, &promise);
188 entry->DelReference();
189 if (status != napi_ok) {
190 LOG_ERROR("Notify data change failed status:%{public}d.", status);
191 return;
192 }
193 entry->BindPromise(promise);
194 };
195 }
196
GenPromiseTask(std::shared_ptr<UvEntry> entry)197 UvQueue::Task UvQueue::GenPromiseTask(std::shared_ptr<UvEntry> entry)
198 {
199 return [entry]() {
200 if (entry == nullptr) {
201 return;
202 }
203 Scope scope(entry->env_);
204 napi_value argv[ARG_BUTT] = { nullptr };
205 auto argc = entry->GetArgv(argv, ARG_BUTT);
206 if (argv[ARG_ERROR] != nullptr || argc != ARG_BUTT) {
207 napi_reject_deferred(entry->env_, entry->defer_, argv[ARG_ERROR]);
208 } else {
209 napi_resolve_deferred(entry->env_, entry->defer_, argv[ARG_DATA]);
210 }
211 };
212 }
213
~UvEntry()214 UvQueue::UvEntry::~UvEntry()
215 {
216 }
217
DelReference()218 void UvQueue::UvEntry::DelReference()
219 {
220 if (callback_ != nullptr && !repeat_) {
221 napi_delete_reference(env_, callback_);
222 callback_ = nullptr;
223 }
224 if (object_ != nullptr) {
225 napi_delete_reference(env_, object_);
226 object_ = nullptr;
227 }
228 }
229
GetCallback()230 napi_value UvQueue::UvEntry::GetCallback()
231 {
232 napi_value method = nullptr;
233 if (callback_ != nullptr) {
234 napi_get_reference_value(env_, callback_, &method);
235 } else if (getter_) {
236 method = getter_(env_);
237 }
238 return method;
239 }
240
GetArgv(napi_value * argv,int32_t max)241 int32_t UvQueue::UvEntry::GetArgv(napi_value *argv, int32_t max)
242 {
243 int32_t argc = 0;
244 if (args_) {
245 argc = max;
246 args_(env_, argc, argv);
247 }
248 return argc;
249 }
250
GetObject()251 napi_value UvQueue::UvEntry::GetObject()
252 {
253 napi_value object = nullptr;
254 if (object_ == nullptr) {
255 napi_get_global(env_, &object);
256 } else {
257 napi_get_reference_value(env_, object_, &object);
258 }
259 return object;
260 }
261
BindPromise(napi_value promise)262 void UvQueue::UvEntry::BindPromise(napi_value promise)
263 {
264 if (promise == nullptr || !result_) {
265 return;
266 }
267
268 bool isPromise = false;
269 auto status = napi_is_promise(env_, promise, &isPromise);
270 if (status != napi_ok || !isPromise) {
271 result_(env_, 1, &promise, false);
272 return;
273 }
274
275 napi_value then = nullptr;
276 if (napi_get_named_property(env_, promise, "then", &then) != napi_ok || then == nullptr) {
277 return;
278 }
279
280 auto object = StealResult();
281 napi_value argv[ARGC_MAX] = { nullptr };
282 auto resolvedStatus = napi_create_function(env_, RESOLVED, RESOLVED_SIZE, Resolved, object, &argv[0]);
283 auto rejectedStatus = napi_create_function(env_, REJECTED, REJECTED_SIZE, Rejected, object, &argv[1]);
284 if (resolvedStatus != napi_ok || rejectedStatus != napi_ok) {
285 if (object) {
286 delete object;
287 }
288 LOG_ERROR("napi_create_function failed. resolvedStat:%{public}d, rejectedStat:%{public}d", resolvedStatus,
289 rejectedStatus);
290 return;
291 }
292 napi_value result = nullptr;
293 // Enter 2 parameters argv[0] and argv[1]
294 status = napi_call_function(env_, promise, then, 2, argv, &result);
295 if (status != napi_ok && object != nullptr) {
296 delete object;
297 }
298 }
299
StealResult()300 UvQueue::Result *UvQueue::UvEntry::StealResult()
301 {
302 if (!result_) {
303 return nullptr;
304 }
305 auto *result = new (std::nothrow) Result();
306 if (result == nullptr) {
307 LOG_ERROR("No memory for Result.");
308 return nullptr;
309 }
310 *result = std::move(result_);
311 return result;
312 }
313 } // namespace OHOS::AppDataMgrJsKit
314