1 /*
2 * Copyright (C) 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 #ifndef LOG_TAG
16 #define LOG_TAG "bt_napi_async_work"
17 #endif
18
19 #include "napi_async_work.h"
20 #include "bluetooth_errorcode.h"
21 #include "bluetooth_log.h"
22 #include "napi_async_callback.h"
23 #include "napi_bluetooth_utils.h"
24 #include "napi_timer.h"
25 #include "parser/napi_parser_utils.h"
26
27 namespace OHOS {
28 namespace Bluetooth {
CreateAsyncWork(napi_env env,napi_callback_info info,std::function<NapiAsyncWorkRet (void)> asyncWork,bool needCallback,std::shared_ptr<NapiHaEventUtils> haUtils)29 std::shared_ptr<NapiAsyncWork> NapiAsyncWorkFactory::CreateAsyncWork(napi_env env, napi_callback_info info,
30 std::function<NapiAsyncWorkRet(void)> asyncWork, bool needCallback, std::shared_ptr<NapiHaEventUtils> haUtils)
31 {
32 auto asyncCallback = NapiParseAsyncCallback(env, info);
33 if (!asyncCallback) {
34 HILOGE("asyncCallback is nullptr!");
35 return nullptr;
36 }
37 // add custom deleter for destructing in JS thread.
38 std::shared_ptr<NapiAsyncWork> napiAsyncWork(
39 new NapiAsyncWork(env, asyncWork, asyncCallback, needCallback, haUtils),
40 [env](NapiAsyncWork *ptr) {
41 DoInJsMainThread(env, [ptr]() {
42 if (ptr) {
43 delete ptr;
44 }
45 });
46 });
47 return napiAsyncWork;
48 }
49
Execute(void)50 void NapiAsyncWork::Info::Execute(void)
51 {
52 if (napiAsyncWork == nullptr) {
53 HILOGE("napiAsyncWork is nullptr");
54 errCode = BT_ERR_INTERNAL_ERROR;
55 object = nullptr;
56 return;
57 }
58 auto ret = napiAsyncWork->func_();
59 errCode = ret.errCode;
60 object = ret.object;
61 }
62
Complete(void)63 void NapiAsyncWork::Info::Complete(void)
64 {
65 HILOGD("needCallback: %{public}d, errCode: %{public}d", needCallback, errCode);
66 if (napiAsyncWork == nullptr) {
67 HILOGE("napiAsyncWork is nullptr");
68 return;
69 }
70
71 // need wait callback
72 if (needCallback && (errCode == BT_NO_ERROR)) {
73 if (napiAsyncWork->triggered_) {
74 HILOGE("NapiAsyncWork is triggered, Callback is earlier than Complete in thread scheduling");
75 return;
76 }
77 // start timer to avoid the callback is lost.
78 std::weak_ptr<NapiAsyncWork> asyncWorkWptr = napiAsyncWork;
79 auto func = [asyncWorkWptr]() {
80 auto asyncWorkSptr = asyncWorkWptr.lock();
81 if (asyncWorkSptr == nullptr) {
82 HILOGE("asyncWorkSptr is nullptr");
83 return;
84 }
85 asyncWorkSptr->TimeoutCallback();
86 };
87 NapiTimer::GetInstance()->Register(func, napiAsyncWork->timerId_);
88 return;
89 }
90
91 if (object == nullptr) {
92 HILOGD("napi native object is nullptr");
93 object = std::make_shared<NapiNativeEmpty>();
94 }
95
96 if (napiAsyncWork->napiAsyncCallback_) {
97 napiAsyncWork->triggered_ = true;
98 auto haUtils = napiAsyncWork->GetHaUtilsPtr();
99 if (haUtils) {
100 haUtils->WriteErrCode(errCode);
101 }
102 napiAsyncWork->napiAsyncCallback_->CallFunction(errCode, object);
103 }
104 }
105
Run(void)106 void NapiAsyncWork::Run(void)
107 {
108 napi_value resource = nullptr;
109 napi_create_string_utf8(env_, "napiAsyncWork", NAPI_AUTO_LENGTH, &resource);
110
111 NapiAsyncWork::Info *info = new NapiAsyncWork::Info();
112 info->needCallback = needCallback_.load();
113 info->napiAsyncWork = shared_from_this();
114
115 napi_status status = napi_create_async_work(env_, nullptr, resource,
116 [](napi_env env, void* data) {
117 NapiAsyncWork::Info *info = static_cast<NapiAsyncWork::Info *>(data);
118 if (info == nullptr) {
119 HILOGE("Async work info is nullptr");
120 return;
121 }
122 info->Execute();
123 },
124 [](napi_env env, napi_status status, void* data) {
125 NapiAsyncWork::Info *info = static_cast<NapiAsyncWork::Info *>(data);
126 if (info == nullptr) {
127 HILOGE("info is nullptr");
128 return;
129 }
130 info->Complete();
131 napi_delete_async_work(env, info->asyncWork);
132 delete info;
133 },
134 static_cast<void *>(info), &info->asyncWork);
135 if (status != napi_ok) {
136 HILOGE("napi_create_async_work failed, status(%{public}d)", status);
137 delete info;
138 return;
139 }
140
141 status = napi_queue_async_work(env_, info->asyncWork);
142 if (status != napi_ok) {
143 HILOGE("napi_queue_async_work failed, status(%{public}d)", status);
144 napi_delete_async_work(env_, info->asyncWork);
145 delete info;
146 return;
147 }
148 }
149
GetHaUtilsPtr(void) const150 std::shared_ptr<NapiHaEventUtils> NapiAsyncWork::GetHaUtilsPtr(void) const
151 {
152 return haUtils_;
153 }
154
TimeoutCallback(void)155 void NapiAsyncWork::TimeoutCallback(void)
156 {
157 HILOGI("enter");
158 CallFunction(BT_ERR_TIMEOUT, nullptr);
159 }
160
CallFunction(int errCode,std::shared_ptr<NapiNativeObject> object)161 void NapiAsyncWork::CallFunction(int errCode, std::shared_ptr<NapiNativeObject> object)
162 {
163 if (!needCallback_.load()) {
164 HILOGE("Unsupported in no needCallback mode");
165 if (haUtils_) {
166 haUtils_->WriteErrCode(BT_ERR_INTERNAL_ERROR);
167 }
168 return;
169 }
170
171 HILOGI("enter");
172 auto nativeObj = object;
173 if (nativeObj == nullptr) {
174 HILOGD("napi native object is nullptr");
175 nativeObj = std::make_shared<NapiNativeEmpty>();
176 }
177 // Check timer triggered & remove timer if supported
178 NapiTimer::GetInstance()->Unregister(timerId_);
179
180 triggered_ = true;
181 auto func = [errCode, nativeObj, asyncWorkPtr = shared_from_this()]() {
182 if (asyncWorkPtr && asyncWorkPtr->napiAsyncCallback_) {
183 auto haUtils = asyncWorkPtr->GetHaUtilsPtr();
184 if (haUtils) {
185 haUtils->WriteErrCode(errCode);
186 }
187 asyncWorkPtr->napiAsyncCallback_->CallFunction(errCode, nativeObj);
188 }
189 };
190 DoInJsMainThread(env_, std::move(func));
191 }
192
GetRet(void)193 napi_value NapiAsyncWork::GetRet(void)
194 {
195 if (!napiAsyncCallback_) {
196 HILOGI("napiAsyncCallback_ is nullptr");
197 return NapiGetUndefinedRet(env_);
198 }
199 return napiAsyncCallback_->GetRet();
200 }
201
AsyncWorkCallFunction(NapiAsyncWorkMap & map,NapiAsyncType type,std::shared_ptr<NapiNativeObject> nativeObject,int status)202 void AsyncWorkCallFunction(NapiAsyncWorkMap &map, NapiAsyncType type, std::shared_ptr<NapiNativeObject> nativeObject,
203 int status)
204 {
205 HILOGD("type: %{public}d", type);
206 auto asyncWork = map.Get(type);
207 if (!asyncWork) {
208 HILOGD("async work(%{public}d) is nullptr", type);
209 return;
210 }
211 map.Erase(type);
212
213 asyncWork->CallFunction(status, nativeObject);
214 }
215
TryPush(NapiAsyncType type,std::shared_ptr<NapiAsyncWork> asyncWork)216 bool NapiAsyncWorkMap::TryPush(NapiAsyncType type, std::shared_ptr<NapiAsyncWork> asyncWork)
217 {
218 if (!asyncWork) {
219 HILOGE("asyncWork is nullptr");
220 return false;
221 }
222
223 std::lock_guard<std::mutex> lock(mutex_);
224 auto it = map_.find(type);
225 if (it != map_.end()) {
226 auto const &storedAsyncWork = it->second;
227 if (storedAsyncWork != nullptr && !storedAsyncWork->triggered_) {
228 HILOGE("Async work(%{public}d) hasn't been triggered", type);
229 return false;
230 }
231 HILOGI("Async work(%{public}d) hasn't been removed, but triggered, remove it", type);
232 map_.erase(it);
233 }
234
235 map_[type] = std::move(asyncWork);
236 return true;
237 }
238
Erase(NapiAsyncType type)239 void NapiAsyncWorkMap::Erase(NapiAsyncType type)
240 {
241 std::lock_guard<std::mutex> lock(mutex_);
242 map_.erase(type);
243 }
244
Get(NapiAsyncType type)245 std::shared_ptr<NapiAsyncWork> NapiAsyncWorkMap::Get(NapiAsyncType type)
246 {
247 std::lock_guard<std::mutex> lock(mutex_);
248 auto it = map_.find(type);
249 return it != map_.end() ? it->second : nullptr;
250 }
251
252 } // namespace Bluetooth
253 } // namespace OHOS