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