• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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