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