1 /*
2 * Copyright (c) 2022 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 "napi_async_call.h"
17 #include <chrono>
18
19 namespace OHOS {
20 namespace PreferencesJsKit {
21 bool g_async = true; // do not reset the value, used in DECLARE_NAPI_FUNCTION_WITH_DATA only
22 bool g_sync = !g_async; // do not reset the value, used in DECLARE_NAPI_FUNCTION_WITH_DATA only
23 static constexpr int64_t ASYNC_PROCESS_WARING_TIME = 500;
24
SetAction(napi_env env,napi_callback_info info,InputAction input,ExecuteAction exec,OutputAction output)25 void BaseContext::SetAction(
26 napi_env env, napi_callback_info info, InputAction input, ExecuteAction exec, OutputAction output)
27 {
28 env_ = env;
29 size_t argc = MAX_INPUT_COUNT;
30 napi_value self = nullptr;
31 napi_value argv[MAX_INPUT_COUNT] = { nullptr };
32 void *data = nullptr;
33 napi_status status = napi_get_cb_info(env, info, &argc, argv, &self, &data);
34 napi_valuetype valueType = napi_undefined;
35 if (status == napi_ok && argc > 0) {
36 napi_typeof(env, argv[argc - 1], &valueType);
37 if (valueType == napi_function) {
38 status = napi_create_reference(env, argv[argc - 1], 1, &callback_);
39 argc = argc - 1;
40 }
41 }
42 if (data) {
43 isAsync_ = *reinterpret_cast<bool *>(data);
44 }
45
46 // int -->input_(env, argc, argv, self)
47 if (status == napi_ok) {
48 input(env, argc, argv, self);
49 } else {
50 error = std::make_shared<InnerError>("Failed to set action.");
51 }
52
53 // if input return is not ok, then napi_throw_error context error
54 PRE_NAPI_ASSERT_RETURN_VOID(env, error == nullptr, error);
55
56 output_ = std::move(output);
57 exec_ = std::move(exec);
58
59 napi_create_reference(env, self, 1, &self_);
60 }
61
SetError(std::shared_ptr<JSError> err)62 void BaseContext::SetError(std::shared_ptr<JSError> err)
63 {
64 error = err;
65 }
66
~BaseContext()67 BaseContext::~BaseContext()
68 {
69 if (env_ == nullptr) {
70 return;
71 }
72 if (work_ != nullptr) {
73 napi_delete_async_work(env_, work_);
74 }
75 if (callback_ != nullptr) {
76 napi_delete_reference(env_, callback_);
77 }
78 napi_delete_reference(env_, self_);
79 env_ = nullptr;
80 }
81
SetBusinessError(napi_env env,napi_value * businessError,std::shared_ptr<JSError> error)82 void AsyncCall::SetBusinessError(napi_env env, napi_value *businessError, std::shared_ptr<JSError> error)
83 {
84 napi_value code = nullptr;
85 napi_value msg = nullptr;
86 napi_create_object(env, businessError);
87 // if error is not inner error
88 if (error != nullptr && error->GetCode() != E_INVALID_PARAM) {
89 napi_create_int32(env, error->GetCode(), &code);
90 napi_create_string_utf8(env, error->GetMsg().c_str(), NAPI_AUTO_LENGTH, &msg);
91 napi_set_named_property(env, *businessError, "code", code);
92 napi_set_named_property(env, *businessError, "message", msg);
93 }
94 }
95
Call(napi_env env,std::shared_ptr<BaseContext> context,const std::string & name)96 napi_value AsyncCall::Call(napi_env env, std::shared_ptr<BaseContext> context, const std::string &name)
97 {
98 return context->isAsync_ ? Async(env, context, name) : Sync(env, context);
99 }
100
Async(napi_env env,std::shared_ptr<BaseContext> context,const std::string & name)101 napi_value AsyncCall::Async(napi_env env, std::shared_ptr<BaseContext> context, const std::string &name)
102 {
103 napi_value promise = nullptr;
104
105 auto start_time = std::chrono::steady_clock::now();
106 if (context->callback_ == nullptr) {
107 napi_create_promise(env, &context->defer_, &promise);
108 } else {
109 napi_get_undefined(env, &promise);
110 }
111 context->keep_ = context;
112 napi_value resource = nullptr;
113 napi_create_string_utf8(env, "PreferencesAsyncCall", NAPI_AUTO_LENGTH, &resource);
114 // create async work, execute function is OnExecute, complete function is OnComplete
115 napi_create_async_work(env, nullptr, resource, AsyncCall::OnExecute, AsyncCall::OnComplete,
116 reinterpret_cast<void *>(context.get()), &context->work_);
117 // add async work to execute queue
118 napi_queue_async_work_with_qos(env, context->work_, napi_qos_user_initiated);
119 auto end_time = std::chrono::steady_clock::now();
120 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
121 if (duration.count() > ASYNC_PROCESS_WARING_TIME) {
122 LOG_ERROR("The execution time of %{public}s is %{public}lld.", name.c_str(), duration.count());
123 }
124 return promise;
125 }
126
Sync(napi_env env,std::shared_ptr<BaseContext> context)127 napi_value AsyncCall::Sync(napi_env env, std::shared_ptr<BaseContext> context)
128 {
129 OnExecute(env, reinterpret_cast<void *>(context.get()));
130 OnComplete(env, reinterpret_cast<void *>(context.get()));
131 return context->result_;
132 }
133
OnExecute(napi_env env,void * data)134 void AsyncCall::OnExecute(napi_env env, void *data)
135 {
136 BaseContext *context = reinterpret_cast<BaseContext *>(data);
137 if (context->exec_) {
138 context->execCode_ = context->exec_();
139 }
140 context->exec_ = nullptr;
141 }
142
OnComplete(napi_env env,void * data)143 void AsyncCall::OnComplete(napi_env env, void *data)
144 {
145 BaseContext *context = reinterpret_cast<BaseContext *>(data);
146 if (context->execCode_ != NativePreferences::E_OK) {
147 context->SetError(std::make_shared<InnerError>(context->execCode_));
148 }
149 // if async execute status is not napi_ok then un-execute out function
150 if ((context->error == nullptr) && context->output_) {
151 context->output_(env, context->result_);
152 }
153 context->output_ = nullptr;
154 }
155
OnComplete(napi_env env,napi_status status,void * data)156 void AsyncCall::OnComplete(napi_env env, napi_status status, void *data)
157 {
158 OnComplete(env, data);
159 OnReturn(env, status, data);
160 }
161
OnReturn(napi_env env,napi_status status,void * data)162 void AsyncCall::OnReturn(napi_env env, napi_status status, void *data)
163 {
164 BaseContext *context = reinterpret_cast<BaseContext *>(data);
165 napi_value result[ARG_BUTT] = { 0 };
166 // if out function status is ok then async renturn output data, else return error.
167 if (context->error == nullptr) {
168 napi_get_undefined(env, &result[ARG_ERROR]);
169 if (context->result_ != nullptr) {
170 result[ARG_DATA] = context->result_;
171 } else {
172 napi_get_undefined(env, &result[ARG_DATA]);
173 }
174 } else {
175 SetBusinessError(env, &result[ARG_ERROR], context->error);
176 napi_get_undefined(env, &result[ARG_DATA]);
177 }
178 if (context->defer_ != nullptr) {
179 // promise
180 if (status == napi_ok && (context->error == nullptr)) {
181 napi_resolve_deferred(env, context->defer_, result[ARG_DATA]);
182 } else {
183 napi_reject_deferred(env, context->defer_, result[ARG_ERROR]);
184 }
185 } else {
186 // callback
187 napi_value callback = nullptr;
188 napi_get_reference_value(env, context->callback_, &callback);
189 napi_value returnValue;
190 napi_call_function(env, nullptr, callback, ARG_BUTT, result, &returnValue);
191 }
192 context->keep_.reset();
193 }
194 } // namespace PreferencesJsKit
195 } // namespace OHOS