• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_is_sendable(env, self, &sendable_);
35     napi_valuetype valueType = napi_undefined;
36     if (status == napi_ok && argc > 0) {
37         napi_typeof(env, argv[argc - 1], &valueType);
38         if (!sendable_ && valueType == napi_function) {
39             status = napi_create_reference(env, argv[argc - 1], 1, &callback_);
40             argc = argc - 1;
41         }
42     }
43     if (data) {
44         isAsync_ = *reinterpret_cast<bool *>(data);
45     }
46 
47     // int -->input_(env, argc, argv, self)
48     if (status == napi_ok) {
49         input(env, argc, argv, self);
50     } else {
51         error = std::make_shared<InnerError>("Failed to set action.");
52     }
53 
54     // if input return is not ok, then napi_throw_error context error
55     PRE_NAPI_ASSERT_RETURN_VOID(env, error == nullptr, error);
56 
57     output_ = std::move(output);
58     exec_ = std::move(exec);
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     // if error is not inner error
87     if (error != nullptr && error->GetCode() != E_INVALID_PARAM) {
88         napi_create_int32(env, error->GetCode(), &code);
89         napi_create_string_utf8(env, error->GetMsg().c_str(), NAPI_AUTO_LENGTH, &msg);
90         napi_property_descriptor descriptors[] = {
91             DECLARE_NAPI_DEFAULT_PROPERTY("code", code),
92             DECLARE_NAPI_DEFAULT_PROPERTY("message", msg),
93         };
94         // 2 represents the current number of incorrect object attributes
95         napi_create_object_with_properties(env, businessError, 2, descriptors);
96     }
97 }
98 
Call(napi_env env,std::shared_ptr<BaseContext> context,const std::string & name)99 napi_value AsyncCall::Call(napi_env env, std::shared_ptr<BaseContext> context, const std::string &name)
100 {
101     return context->isAsync_ ? Async(env, context, name) : Sync(env, context);
102 }
103 
Async(napi_env env,std::shared_ptr<BaseContext> context,const std::string & name)104 napi_value AsyncCall::Async(napi_env env, std::shared_ptr<BaseContext> context, const std::string &name)
105 {
106     napi_value promise = nullptr;
107 
108     auto start_time = std::chrono::steady_clock::now();
109     if (context->callback_ == nullptr) {
110         napi_status status = napi_create_promise(env, &context->defer_, &promise);
111         PRE_NAPI_ASSERT(env, status == napi_ok, std::make_shared<InnerError>("Failed to create promise."));
112     } else {
113         napi_get_undefined(env, &promise);
114     }
115     context->keep_ = context;
116     napi_value resource = nullptr;
117     napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &resource);
118     // create async work, execute function is OnExecute, complete function is OnComplete
119     napi_create_async_work(env, nullptr, resource, AsyncCall::OnExecute, AsyncCall::OnComplete,
120                            reinterpret_cast<void *>(context.get()), &context->work_);
121     // add async work to execute queue
122     napi_queue_async_work_with_qos(env, context->work_, napi_qos_user_initiated);
123     auto end_time = std::chrono::steady_clock::now();
124     auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
125     if (duration.count() > ASYNC_PROCESS_WARING_TIME) {
126         LOG_ERROR("The execution time of %{public}s is %{public}lld.", name.c_str(), duration.count());
127     }
128     return promise;
129 }
130 
Sync(napi_env env,std::shared_ptr<BaseContext> context)131 napi_value AsyncCall::Sync(napi_env env, std::shared_ptr<BaseContext> context)
132 {
133     OnExecute(env, reinterpret_cast<void *>(context.get()));
134     OnComplete(env, reinterpret_cast<void *>(context.get()));
135     return context->result_;
136 }
137 
OnExecute(napi_env env,void * data)138 void AsyncCall::OnExecute(napi_env env, void *data)
139 {
140     BaseContext *context = reinterpret_cast<BaseContext *>(data);
141     if (context->exec_) {
142         context->execCode_ = context->exec_();
143     }
144     context->exec_ = nullptr;
145 }
146 
OnComplete(napi_env env,void * data)147 void AsyncCall::OnComplete(napi_env env, void *data)
148 {
149     BaseContext *context = reinterpret_cast<BaseContext *>(data);
150     if (context->execCode_ != NativePreferences::E_OK) {
151         context->SetError(std::make_shared<InnerError>(context->execCode_));
152         LOG_ERROR("The execute status is %{public}d", context->execCode_);
153     }
154     // if async execute status is not napi_ok then un-execute out function
155     if ((context->error == nullptr) && context->output_) {
156         context->output_(env, context->result_);
157     }
158     context->output_ = nullptr;
159 }
160 
OnComplete(napi_env env,napi_status status,void * data)161 void AsyncCall::OnComplete(napi_env env, napi_status status, void *data)
162 {
163     OnComplete(env, data);
164     OnReturn(env, status, data);
165 }
166 
OnReturn(napi_env env,napi_status status,void * data)167 void AsyncCall::OnReturn(napi_env env, napi_status status, void *data)
168 {
169     BaseContext *context = reinterpret_cast<BaseContext *>(data);
170     napi_value result[ARG_BUTT] = { 0 };
171     // if out function status is ok then async renturn output data, else return error.
172     if (context->error == nullptr) {
173         napi_get_undefined(env, &result[ARG_ERROR]);
174         if (context->result_ != nullptr) {
175             result[ARG_DATA] = context->result_;
176         } else {
177             napi_get_undefined(env, &result[ARG_DATA]);
178         }
179     } else {
180         SetBusinessError(env, &result[ARG_ERROR], context->error);
181         napi_get_undefined(env, &result[ARG_DATA]);
182     }
183     if (context->defer_ != nullptr) {
184         // promise
185         if (status == napi_ok && (context->error == nullptr)) {
186             napi_resolve_deferred(env, context->defer_, result[ARG_DATA]);
187         } else {
188             napi_reject_deferred(env, context->defer_, result[ARG_ERROR]);
189         }
190     } else if (!context->sendable_) {
191         // callback
192         napi_value callback = nullptr;
193         napi_get_reference_value(env, context->callback_, &callback);
194         napi_value returnValue;
195         napi_call_function(env, nullptr, callback, ARG_BUTT, result, &returnValue);
196     }
197     context->keep_.reset();
198 }
199 } // namespace PreferencesJsKit
200 } // namespace OHOS