• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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