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 <memory>
17
18 #include "js_scope.h"
19 #include "js_uv_queue.h"
20 #include "logger.h"
21 namespace OHOS::AppDataMgrJsKit {
22 using namespace OHOS::Rdb;
23 constexpr size_t ARGC_MAX = 6;
UvQueue(napi_env env)24 UvQueue::UvQueue(napi_env env) : env_(env)
25 {
26 if (env != nullptr) {
27 napi_get_uv_event_loop(env, &loop_);
28 }
29 handler_ = AppExecFwk::EventHandler::Current();
30 }
31
~UvQueue()32 UvQueue::~UvQueue()
33 {
34 LOG_DEBUG("no memory leak for queue-callback.");
35 env_ = nullptr;
36 handler_ = nullptr;
37 }
38
AsyncCall(UvCallback callback,Args args,Result result)39 void UvQueue::AsyncCall(UvCallback callback, Args args, Result result)
40 {
41 if (loop_ == nullptr || callback.IsNull()) {
42 LOG_ERROR("loop_ or callback is nullptr.");
43 return;
44 }
45 uv_work_t *work = new (std::nothrow) uv_work_t;
46 if (work == nullptr) {
47 LOG_ERROR("no memory for uv_work_t.");
48 return;
49 }
50 auto entry = new (std::nothrow) UvEntry();
51 if (entry == nullptr) {
52 delete work;
53 LOG_ERROR("no memory for UvEntry.");
54 return;
55 }
56 entry->env_ = env_;
57 entry->object_ = callback.object_;
58 entry->callback_ = callback.callback_;
59 entry->repeat_ = callback.repeat_;
60 entry->getter_ = std::move(callback.getter_);
61 entry->args_ = std::move(args);
62 entry->result_ = std::move(result);
63 work->data = entry;
64 int ret = uv_queue_work(loop_, work, DoWork, DoUvCallback);
65 if (ret < 0) {
66 LOG_ERROR("uv_queue_work failed, errCode:%{public}d", ret);
67 delete entry;
68 delete work;
69 }
70 }
71
AsyncCallInOrder(UvCallback callback,Args args,Result result)72 void UvQueue::AsyncCallInOrder(UvCallback callback, Args args, Result result)
73 {
74 if (handler_ == nullptr) {
75 AsyncCall(std::move(callback), std::move(args), std::move(result));
76 }
77 if (callback.IsNull()) {
78 LOG_ERROR("handler_ or callback is nullptr.");
79 return;
80 }
81 auto entry = std::make_shared<UvEntry>();
82 if (entry == nullptr) {
83 LOG_ERROR("no memory for UvEntry.");
84 return;
85 }
86 entry->env_ = env_;
87 entry->callback_ = callback.callback_;
88 entry->repeat_ = callback.repeat_;
89 entry->args_ = std::move(args);
90 if (handler_ != nullptr) {
91 handler_->PostTask(GenCallbackTask(entry));
92 }
93 }
94
AsyncPromise(UvPromise promise,UvQueue::Args args)95 void UvQueue::AsyncPromise(UvPromise promise, UvQueue::Args args)
96 {
97 if (loop_ == nullptr || promise.IsNull()) {
98 LOG_ERROR("loop_ or promise is nullptr.");
99 return;
100 }
101 uv_work_t *work = new (std::nothrow) uv_work_t;
102 if (work == nullptr) {
103 LOG_ERROR("no memory for uv_work_t.");
104 return;
105 }
106 auto entry = new (std::nothrow) UvEntry();
107 if (entry == nullptr) {
108 delete work;
109 LOG_ERROR("no memory for UvEntry.");
110 return;
111 }
112 entry->env_ = env_;
113 entry->defer_ = promise.defer_;
114 entry->args_ = std::move(args);
115 work->data = entry;
116 int ret = uv_queue_work(loop_, work, DoWork, DoUvPromise);
117 if (ret < 0) {
118 LOG_ERROR("uv_queue_work failed, errCode:%{public}d", ret);
119 delete entry;
120 delete work;
121 }
122 }
123
Execute(UvQueue::Task task)124 void UvQueue::Execute(UvQueue::Task task)
125 {
126 if (loop_ == nullptr || !task) {
127 LOG_ERROR("loop_ or task is nullptr.");
128 return;
129 }
130 uv_work_t *work = new (std::nothrow) uv_work_t;
131 if (work == nullptr) {
132 LOG_ERROR("no memory for uv_work_t.");
133 return;
134 }
135 auto entry = new (std::nothrow) Task();
136 if (entry == nullptr) {
137 delete work;
138 LOG_ERROR("no memory for Task.");
139 return;
140 }
141 *entry = task;
142 work->data = entry;
143 int ret = uv_queue_work(loop_, work, DoExecute, [](uv_work_t *work, int status) { delete work; });
144 if (ret < 0) {
145 LOG_ERROR("uv_queue_work failed, errCode:%{public}d", ret);
146 delete entry;
147 delete work;
148 }
149 }
150
GetEnv()151 napi_env UvQueue::GetEnv()
152 {
153 return env_;
154 }
155
Resolved(napi_env env,napi_callback_info info)156 napi_value UvQueue::Resolved(napi_env env, napi_callback_info info)
157 {
158 return Future(env, info, false);
159 }
160
Rejected(napi_env env,napi_callback_info info)161 napi_value UvQueue::Rejected(napi_env env, napi_callback_info info)
162 {
163 return Future(env, info, true);
164 }
165
Future(napi_env env,napi_callback_info info,bool exception)166 napi_value UvQueue::Future(napi_env env, napi_callback_info info, bool exception)
167 {
168 size_t argc = ARGC_MAX;
169 napi_value argv[ARGC_MAX] = { nullptr };
170 void *data = nullptr;
171 auto status = napi_get_cb_info(env, info, &argc, argv, nullptr, &data);
172 if (status != napi_ok) {
173 return nullptr;
174 }
175 auto *entry = static_cast<Result *>(data);
176 if (entry) {
177 (*entry)(env, argc, argv, exception);
178 delete entry;
179 }
180 return nullptr;
181 }
182
DoWork(uv_work_t * work)183 void UvQueue::DoWork(uv_work_t *work)
184 {
185 }
186
DoExecute(uv_work_t * work)187 void UvQueue::DoExecute(uv_work_t *work)
188 {
189 Task *task = static_cast<Task *>(work->data);
190 work->data = nullptr;
191 (*task)();
192 delete task;
193 }
194
DoUvCallback(uv_work_t * work,int status)195 void UvQueue::DoUvCallback(uv_work_t *work, int status)
196 {
197 std::shared_ptr<UvEntry> entry(static_cast<UvEntry *>(work->data), [work](UvEntry *data) {
198 delete data;
199 delete work;
200 });
201
202 GenCallbackTask(entry)();
203 }
204
GenCallbackTask(std::shared_ptr<UvEntry> entry)205 UvQueue::Task UvQueue::GenCallbackTask(std::shared_ptr<UvEntry> entry)
206 {
207 return [entry]() {
208 if (entry == nullptr) {
209 return;
210 }
211 Scope scope(entry->env_);
212 napi_value method = entry->GetCallback();
213 if (method == nullptr) {
214 entry->DelReference();
215 LOG_ERROR("the callback is invalid, maybe is cleared!");
216 return;
217 }
218 napi_value argv[ARGC_MAX] = { nullptr };
219 auto argc = entry->GetArgv(argv, ARGC_MAX);
220 auto object = entry->GetObject();
221 napi_value promise = nullptr;
222 auto status = napi_call_function(entry->env_, object, method, argc, argv, &promise);
223 entry->DelReference();
224 if (status != napi_ok) {
225 LOG_ERROR("notify data change failed status:%{public}d.", status);
226 return;
227 }
228 entry->BindPromise(promise);
229 };
230 }
231
DoUvPromise(uv_work_t * work,int status)232 void UvQueue::DoUvPromise(uv_work_t *work, int status)
233 {
234 std::shared_ptr<UvEntry> entry(static_cast<UvEntry *>(work->data), [work](UvEntry *data) {
235 delete data;
236 delete work;
237 });
238
239 Scope scope(entry->env_);
240
241 napi_value argv[ARG_BUTT] = { nullptr };
242 auto argc = entry->GetArgv(argv, ARG_BUTT);
243 if (argv[ARG_ERROR] != nullptr || argc != ARG_BUTT) {
244 napi_reject_deferred(entry->env_, entry->defer_, argv[ARG_ERROR]);
245 } else {
246 napi_resolve_deferred(entry->env_, entry->defer_, argv[ARG_DATA]);
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 napi_call_function(env_, promise, then, 2, argv, &result);
323 }
324
StealResult()325 UvQueue::Result *UvQueue::UvEntry::StealResult()
326 {
327 if (!result_) {
328 return nullptr;
329 }
330 auto *result = new Result();
331 *result = std::move(result_);
332 return result;
333 }
334 } // namespace OHOS::AppDataMgrJsKit
335