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