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