1 /*
2 * Copyright (c) 2022-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
16 #include "n_async_work_callback.h"
17
18 #include <memory>
19 #include <new>
20
21 #include "file_utils.h"
22 #include "filemgmt_libhilog.h"
23 #include "uv.h"
24
25 namespace OHOS {
26 namespace FileManagement {
27 namespace LibN {
28 using namespace std;
29
NAsyncWorkCallback(napi_env env,NVal thisPtr,NVal cb)30 NAsyncWorkCallback::NAsyncWorkCallback(napi_env env, NVal thisPtr, NVal cb) : NAsyncWork(env)
31 {
32 ctx_ = new(std::nothrow) NAsyncContextCallback(thisPtr, cb);
33 }
34
~NAsyncWorkCallback()35 NAsyncWorkCallback::~NAsyncWorkCallback()
36 {
37 if (!ctx_) {
38 return;
39 }
40
41 unique_ptr<NAsyncContextCallback> ptr(ctx_);
42 uv_loop_s *loop = nullptr;
43 napi_status status = napi_get_uv_event_loop(env_, &loop);
44 if (status != napi_ok) {
45 ptr->cb_.CleanJsEnv();
46 HILOGE("Failed to get uv event loop");
47 return;
48 }
49
50 auto work = CreateUniquePtr<uv_work_t>();
51 if (work == nullptr) {
52 HILOGE("Failed to new uv_work_t");
53 return;
54 }
55 work->data = static_cast<void *>(ctx_);
56
57 int ret = uv_queue_work(
58 loop, work.get(), [](uv_work_t *work) {},
59 [](uv_work_t *work, int status) {
60 NAsyncContextCallback *ctx = static_cast<NAsyncContextCallback *>(work->data);
61 delete ctx;
62 delete work;
63 });
64 if (ret) {
65 HILOGE("Failed to call uv_queue_work %{public}d", status);
66 return;
67 }
68 ptr.release();
69 work.release();
70 ctx_ = nullptr;
71 }
72
operator bool() const73 NAsyncWorkCallback::operator bool() const
74 {
75 return bool(ctx_->cb_);
76 }
77
CallbackExecute(napi_env env,void * data)78 static void CallbackExecute(napi_env env, void *data)
79 {
80 auto ctx = static_cast<NAsyncContextCallback *>(data);
81 if (ctx != nullptr && ctx->cbExec_ != nullptr) {
82 ctx->err_ = ctx->cbExec_();
83 } else {
84 HILOGE("Callback execute function, This pointer or function address is empty");
85 }
86 }
87
CallbackComplete(napi_env env,napi_status status,void * data)88 static void CallbackComplete(napi_env env, napi_status status, void *data)
89 {
90 napi_handle_scope scope = nullptr;
91 napi_open_handle_scope(env, &scope);
92 auto ctx = static_cast<NAsyncContextCallback *>(data);
93 if (ctx == nullptr) {
94 HILOGE("This pointer address is empty");
95 napi_close_handle_scope(env, scope);
96 return;
97 }
98 if (ctx->cbComplete_ != nullptr) {
99 ctx->res_ = ctx->cbComplete_(env, ctx->err_);
100 ctx->cbComplete_ = nullptr;
101 }
102
103 vector<napi_value> argv;
104 if (!ctx->res_.TypeIsError(true)) {
105 argv = {NError(ERRNO_NOERR).GetNapiErr(env), ctx->res_.val_};
106 } else {
107 argv = {ctx->res_.val_};
108 }
109
110 napi_value global = nullptr;
111 napi_value callback = ctx->cb_.Deref(env).val_;
112 napi_value tmp = nullptr;
113 napi_get_global(env, &global);
114 napi_status stat = napi_call_function(env, global, callback, argv.size(), argv.data(), &tmp);
115 if (stat != napi_ok) {
116 HILOGE("Failed to call function for %{public}d", stat);
117 }
118 napi_close_handle_scope(env, scope);
119 napi_delete_async_work(env, ctx->awork_);
120 delete ctx;
121 }
122
Schedule(string procedureName,NContextCBExec cbExec,NContextCBComplete cbComplete)123 NVal NAsyncWorkCallback::Schedule(string procedureName, NContextCBExec cbExec, NContextCBComplete cbComplete)
124 {
125 if (!ctx_ || !ctx_->cb_ || !ctx_->cb_.Deref(env_).TypeIs(napi_function)) {
126 HILOGE("The callback should be a function");
127 NError(EINVAL).ThrowErr(env_);
128 return NVal();
129 }
130
131 ctx_->cbExec_ = move(cbExec);
132 ctx_->cbComplete_ = move(cbComplete);
133
134 napi_value resource = NVal::CreateUTF8String(env_, procedureName).val_;
135
136 napi_status status =
137 napi_create_async_work(env_, nullptr, resource, CallbackExecute, CallbackComplete, ctx_, &ctx_->awork_);
138 if (status != napi_ok) {
139 HILOGE("INNER BUG. Failed to create async work for %{public}d", status);
140 return NVal();
141 }
142
143 status = napi_queue_async_work(env_, ctx_->awork_);
144 if (status != napi_ok) {
145 HILOGE("INNER BUG. Failed to queue async work for %{public}d", status);
146 return NVal();
147 }
148
149 ctx_ = nullptr; // The ownership of ctx_ has been transferred
150 return NVal::CreateUndefined(env_);
151 }
152
AfterWorkCallback(napi_env env,napi_status status,void * data,NContextCBComplete cbComplete)153 static void AfterWorkCallback(napi_env env, napi_status status, void *data, NContextCBComplete cbComplete)
154 {
155 napi_handle_scope scope = nullptr;
156 napi_open_handle_scope(env, &scope);
157 auto ctx = static_cast<NAsyncContextCallback *>(data);
158 if (ctx == nullptr) {
159 HILOGE("This pointer address is empty");
160 napi_close_handle_scope(env, scope);
161 return;
162 }
163 if (cbComplete) {
164 ctx->res_ = cbComplete(env, ctx->err_);
165 ctx->cbComplete_ = nullptr;
166 }
167
168 vector<napi_value> argv;
169 if (!ctx->res_.TypeIsError(true)) {
170 argv = {NError(ERRNO_NOERR).GetNapiErr(env), ctx->res_.val_};
171 } else {
172 argv = {ctx->res_.val_};
173 }
174
175 napi_value global = nullptr;
176 napi_value callback = ctx->cb_.Deref(env).val_;
177 napi_value tmp = nullptr;
178 napi_get_global(env, &global);
179 napi_status stat = napi_call_function(env, global, callback, argv.size(), argv.data(), &tmp);
180 if (stat != napi_ok) {
181 HILOGE("Failed to call function for %{public}d", stat);
182 }
183
184 napi_close_handle_scope(env, scope);
185 napi_delete_async_work(env, ctx->awork_);
186 }
187
ThreadSafeSchedule(NContextCBComplete cbComplete)188 void NAsyncWorkCallback::ThreadSafeSchedule(NContextCBComplete cbComplete)
189 {
190 ctx_->cbExec_ = nullptr;
191 ctx_->cbComplete_ = nullptr;
192
193 uv_loop_s *loop = nullptr;
194 napi_status status = napi_get_uv_event_loop(env_, &loop);
195 if (status != napi_ok) {
196 HILOGE("Failed to get uv event loop");
197 return;
198 }
199
200 auto work = CreateUniquePtr<uv_work_t>();
201 if (!work) {
202 HILOGE("Failed to new uv_work_t");
203 return;
204 }
205
206 struct WorkArgs {
207 NAsyncWorkCallback *ptr = nullptr;
208 NContextCBComplete cb;
209 };
210 auto workArgs = make_unique<WorkArgs>();
211 workArgs->ptr = this;
212 workArgs->cb = cbComplete;
213
214 work->data = static_cast<void *>(workArgs.get());
215
216 int ret = uv_queue_work(
217 loop, work.get(), [](uv_work_t *work) {},
218 [](uv_work_t *work, int status) {
219 auto workArgs = static_cast<WorkArgs *>(work->data);
220 AfterWorkCallback(workArgs->ptr->env_, napi_ok, workArgs->ptr->ctx_, workArgs->cb);
221 delete workArgs;
222 delete work;
223 });
224 if (ret) {
225 HILOGE("Failed to call uv_queue_work %{public}d", status);
226 workArgs.reset();
227 work.reset();
228 return;
229 }
230 workArgs.release();
231 work.release();
232 }
233 } // namespace LibN
234 } // namespace FileManagement
235 } // namespace OHOS
236