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