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