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