• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 "GdbAsyncCall"
16 #include "napi_async_call.h"
17 
18 #include "db_trace.h"
19 #include "js_native_api_types.h"
20 #include "logger.h"
21 
22 namespace OHOS::GraphStoreJsKit {
23 bool g_async = true; // do not reset the value, used in DECLARE_NAPI_FUNCTION_WITH_DATA only
24 bool g_sync = false; // do not reset the value, used in DECLARE_NAPI_FUNCTION_WITH_DATA only
25 thread_local AsyncCall::Record AsyncCall::record_;
SetAction(napi_env env,napi_callback_info info,InputAction input,ExecuteAction exec,OutputAction output)26 void ContextBase::SetAction(
27     napi_env env, napi_callback_info info, InputAction input, ExecuteAction exec, OutputAction output)
28 {
29     env_ = env;
30     size_t argc = MAX_INPUT_COUNT;
31     napi_value self = nullptr;
32     napi_value argv[MAX_INPUT_COUNT] = { nullptr };
33     void *data = nullptr;
34     napi_status status = napi_get_cb_info(env, info, &argc, argv, &self, &data);
35     if (status == napi_ok && argc > 0) {
36         napi_valuetype valueType = napi_undefined;
37         status = napi_typeof(env, argv[argc - 1], &valueType);
38         if (status == napi_ok && valueType == napi_function) {
39             LOG_DEBUG("asyncCall set callback");
40             if (argc == 1) {
41                 error = std::make_shared<ParamNumError>("1");
42             } else {
43                 status = napi_create_reference(env, argv[argc - 1], 1, &callback_);
44                 argc = argc - 1;
45             }
46         }
47     }
48     if (data) {
49         isAsync_ = *reinterpret_cast<bool *>(data);
50     }
51 
52     // int -->input_(env, argc, argv, self)
53     if (status == napi_ok) {
54         input(env, argc, argv, self);
55     } else {
56         error = std::make_shared<InnerError>("Failed to set action.");
57     }
58 
59     // if input return is not ok, then napi_throw_error context error
60     GDB_NAPI_ASSERT_BASE(env, error == nullptr, error, NAPI_RETVAL_NOTHING);
61     output_ = std::move(output);
62     exec_ = std::move(exec);
63     napi_create_reference(env, self, 1, &self_);
64 }
65 
SetAll(napi_env env,napi_callback_info info,InputAction input,ExecuteAction exec,OutputAction output)66 void ContextBase::SetAll(
67     napi_env env, napi_callback_info info, InputAction input, ExecuteAction exec, OutputAction output)
68 {
69     env_ = env;
70     size_t argc = MAX_INPUT_COUNT;
71     napi_value self = nullptr;
72     napi_value argv[MAX_INPUT_COUNT] = { nullptr };
73     NAPI_CALL_RETURN_VOID(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
74 
75     // int -->input_(env, argc, argv, self)
76     input(env, argc, argv, self);
77 
78     // if input return is not ok, then napi_throw_error context error
79     GDB_NAPI_ASSERT_BASE(env, error == nullptr, error, NAPI_RETVAL_NOTHING);
80     output_ = std::move(output);
81     exec_ = std::move(exec);
82     napi_create_reference(env, self, 1, &self_);
83 }
84 
SetError(std::shared_ptr<Error> err)85 void ContextBase::SetError(std::shared_ptr<Error> err)
86 {
87     error = err;
88 }
89 
~ContextBase()90 ContextBase::~ContextBase()
91 {
92     if (env_ == nullptr) {
93         return;
94     }
95     if (work_ != nullptr) {
96         napi_delete_async_work(env_, work_);
97         work_ = nullptr;
98     }
99     if (callback_ != nullptr) {
100         napi_delete_reference(env_, callback_);
101         callback_ = nullptr;
102     }
103     napi_delete_reference(env_, self_);
104     env_ = nullptr;
105 }
106 
SetBusinessError(napi_env env,std::shared_ptr<Error> error,napi_value * businessError)107 void AsyncCall::SetBusinessError(napi_env env, std::shared_ptr<Error> error, napi_value *businessError)
108 {
109     LOG_DEBUG("SetBusinessError enter");
110     // if error is not inner error
111     if (error != nullptr) {
112         napi_value code = nullptr;
113         napi_value msg = nullptr;
114         napi_create_int32(env, error->GetCode(), &code);
115         napi_create_string_utf8(env, error->GetMessage().c_str(), NAPI_AUTO_LENGTH, &msg);
116         napi_create_error(env, code, msg, businessError);
117     }
118 }
119 
Call(napi_env env,std::shared_ptr<ContextBase> context,const char * fun)120 napi_value AsyncCall::Call(napi_env env, std::shared_ptr<ContextBase> context, const char *fun)
121 {
122     record_.total_.times_++;
123     record_.total_.lastTime_ =
124         std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
125             .count();
126     context->executed_ = record_.executed_;
127     context->fun = (fun == nullptr || fun[0] == '\0') ? "RelationalStoreAsyncCall" : fun;
128     return context->isAsync_ ? Async(env, context) : Sync(env, context);
129 }
130 
Async(napi_env env,std::shared_ptr<ContextBase> context)131 napi_value AsyncCall::Async(napi_env env, std::shared_ptr<ContextBase> context)
132 {
133     napi_value promise = nullptr;
134     if (context->callback_ == nullptr) {
135         napi_status status = napi_create_promise(env, &context->defer_, &promise);
136         GDB_NAPI_ASSERT_BASE(env, status == napi_ok,
137             std::make_shared<InnerError>("failed(" + std::to_string(status) + ") to create promise"), nullptr);
138     } else {
139         napi_get_undefined(env, &promise);
140     }
141     context->keep_ = context;
142     napi_value resource = nullptr;
143     napi_create_string_utf8(env, context->fun, NAPI_AUTO_LENGTH, &resource);
144     // create async work, execute function is OnExecute, complete function is OnComplete
145     napi_create_async_work(env, nullptr, resource, AsyncCall::OnExecute, AsyncCall::OnComplete,
146         reinterpret_cast<void *>(context.get()), &context->work_);
147     // add async work to execute queue
148     auto status = napi_queue_async_work_with_qos(env, context->work_, napi_qos_user_initiated);
149     if (status != napi_ok) {
150         napi_get_undefined(env, &promise);
151     }
152     auto report = (record_.total_.times_.load() - record_.completed_.times_.load()) / EXCEPT_DELTA;
153     if (report > record_.reportTimes_ && record_.executed_ != nullptr) {
154         LOG_WARN("Warning:Times:(C:%{public}" PRId64 ", E:%{public}" PRId64 ", F:%{public}" PRId64 ") Last time("
155                  "C:%{public}" PRId64 ", E:%{public}" PRId64 ", F:%{public}" PRId64 ")",
156             record_.total_.times_.load(), record_.executed_->times_.load(), record_.completed_.times_.load(),
157             record_.total_.lastTime_, record_.executed_->lastTime_, record_.completed_.lastTime_);
158     }
159     record_.reportTimes_ = report;
160     return promise;
161 }
162 
Sync(napi_env env,std::shared_ptr<ContextBase> context)163 napi_value AsyncCall::Sync(napi_env env, std::shared_ptr<ContextBase> context)
164 {
165     OnExecute(env, reinterpret_cast<void *>(context.get()));
166     OnComplete(env, reinterpret_cast<void *>(context.get()));
167     if (context->execCode_ != DistributedDataAip::E_OK) {
168         napi_value error;
169         SetBusinessError(env, context->error, &error);
170         napi_throw(env, error);
171     }
172     return context->result_;
173 }
174 
OnExecute(napi_env env,void * data)175 void AsyncCall::OnExecute(napi_env env, void *data)
176 {
177     DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
178     ContextBase *context = reinterpret_cast<ContextBase *>(data);
179     if (context->executed_ != nullptr) {
180         context->executed_->times_++;
181         context->executed_->lastTime_ =
182             std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
183                 .count();
184     }
185     if (context->error == nullptr && context->exec_) {
186         context->execCode_ = context->exec_();
187     }
188     context->exec_ = nullptr;
189 }
190 
OnComplete(napi_env env,void * data)191 void AsyncCall::OnComplete(napi_env env, void *data)
192 {
193     ContextBase *context = reinterpret_cast<ContextBase *>(data);
194     if (context->execCode_ != DistributedDataAip::E_OK) {
195         context->SetError(std::make_shared<InnerError>(context->execCode_));
196     }
197     // if async execute status is not napi_ok then un-execute out function
198     if ((context->error == nullptr) && context->output_) {
199         context->output_(env, context->result_);
200     }
201     context->output_ = nullptr;
202     record_.completed_.times_++;
203     record_.completed_.lastTime_ =
204         std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
205             .count();
206 }
207 
OnComplete(napi_env env,napi_status status,void * data)208 void AsyncCall::OnComplete(napi_env env, napi_status status, void *data)
209 {
210     DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
211     OnComplete(env, data);
212     OnReturn(env, status, data);
213 }
214 
OnReturn(napi_env env,napi_status status,void * data)215 void AsyncCall::OnReturn(napi_env env, napi_status status, void *data)
216 {
217     ContextBase *context = reinterpret_cast<ContextBase *>(data);
218     napi_value result[ARG_BUTT] = { 0 };
219     // if out function status is ok then async renturn output data, else return error.
220     if (context->error == nullptr) {
221         napi_get_undefined(env, &result[ARG_ERROR]);
222         if (context->result_ != nullptr) {
223             result[ARG_DATA] = context->result_;
224         } else {
225             napi_get_undefined(env, &result[ARG_DATA]);
226         }
227     } else {
228         SetBusinessError(env, context->error, &result[ARG_ERROR]);
229         napi_get_undefined(env, &result[ARG_DATA]);
230     }
231     if (context->defer_ != nullptr) {
232         // promise
233         if (status == napi_ok && (context->error == nullptr)) {
234             napi_resolve_deferred(env, context->defer_, result[ARG_DATA]);
235         } else {
236             napi_reject_deferred(env, context->defer_, result[ARG_ERROR]);
237         }
238     } else {
239         // callback
240         napi_value callback = nullptr;
241         napi_get_reference_value(env, context->callback_, &callback);
242         napi_value returnValue = nullptr;
243         napi_call_function(env, nullptr, callback, ARG_BUTT, result, &returnValue);
244     }
245     context->keep_.reset();
246 }
247 } // namespace OHOS::GraphStoreJsKit