1 /*
2 * Copyright (c) 2022 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 "request_suspend_delay.h"
17
18 #include <uv.h>
19
20 #include "singleton.h"
21
22 #include "background_task_manager.h"
23 #include "transient_task_log.h"
24
25 namespace OHOS {
26 namespace BackgroundTaskMgr {
27 std::map<int32_t, std::shared_ptr<ExpiredCallback>> callbackInstances_;
28 std::mutex callbackLock_;
29 static const uint32_t REQUEST_SUSPEND_DELAY_PARAMS = 2;
30
31 struct CallbackReceiveDataWorker {
32 napi_env env = nullptr;
33 napi_ref ref = nullptr;
34 std::shared_ptr<ExpiredCallback> callback = nullptr;
35 };
36
CallbackInstance()37 CallbackInstance::CallbackInstance() {}
38
~CallbackInstance()39 CallbackInstance::~CallbackInstance()
40 {
41 if (expiredCallbackInfo_.ref == nullptr) {
42 return;
43 }
44 DeleteNapiRef();
45 }
46
UvQueueWorkDeleteRef(uv_work_t * work,int32_t status)47 void UvQueueWorkDeleteRef(uv_work_t *work, int32_t status)
48 {
49 if (work == nullptr) {
50 return;
51 }
52 CallbackReceiveDataWorker *dataWorkerData = static_cast<CallbackReceiveDataWorker *>(work->data);
53 if (dataWorkerData == nullptr) {
54 delete work;
55 work = nullptr;
56 return;
57 }
58 napi_delete_reference(dataWorkerData->env, dataWorkerData->ref);
59 delete dataWorkerData;
60 dataWorkerData = nullptr;
61 delete work;
62 }
63
DeleteNapiRef()64 void CallbackInstance::DeleteNapiRef()
65 {
66 uv_loop_s *loop = nullptr;
67 napi_get_uv_event_loop(expiredCallbackInfo_.env, &loop);
68 if (loop == nullptr) {
69 return;
70 }
71 CallbackReceiveDataWorker *dataWorker = new (std::nothrow) CallbackReceiveDataWorker();
72 if (dataWorker == nullptr) {
73 return;
74 }
75
76 dataWorker->env = expiredCallbackInfo_.env;
77 dataWorker->ref = expiredCallbackInfo_.ref;
78 uv_work_t *work = new (std::nothrow) uv_work_t;
79 if (work == nullptr) {
80 BGTASK_LOGE("DeleteNapiRef new work failed");
81 delete dataWorker;
82 dataWorker = nullptr;
83 return;
84 }
85 work->data = static_cast<void *>(dataWorker);
86
87 int32_t ret = uv_queue_work(loop, work, [](uv_work_t *work) {}, UvQueueWorkDeleteRef);
88 if (ret != 0) {
89 delete dataWorker;
90 dataWorker = nullptr;
91 delete work;
92 work = nullptr;
93 }
94 }
95
UvQueueWorkOnExpired(uv_work_t * work,int32_t status)96 void UvQueueWorkOnExpired(uv_work_t *work, int32_t status)
97 {
98 BGTASK_LOGD("OnExpired uv_work_t start");
99
100 if (work == nullptr) {
101 BGTASK_LOGE("UvQueueWorkOnExpired work is null");
102 return;
103 }
104
105 CallbackReceiveDataWorker *dataWorkerData = static_cast<CallbackReceiveDataWorker *>(work->data);
106 if (dataWorkerData == nullptr) {
107 BGTASK_LOGE("UvQueueWorkOnExpired dataWorkerData is null");
108 delete work;
109 work = nullptr;
110 return;
111 }
112
113 Common::SetCallback(dataWorkerData->env, dataWorkerData->ref, Common::NapiGetNull(dataWorkerData->env));
114
115 std::lock_guard<std::mutex> lock(callbackLock_);
116 auto findCallback = std::find_if(callbackInstances_.begin(), callbackInstances_.end(),
117 [&](const auto& callbackInstance) { return callbackInstance.second == dataWorkerData->callback; }
118 );
119 if (findCallback != callbackInstances_.end()) {
120 callbackInstances_.erase(findCallback);
121 }
122
123 delete dataWorkerData;
124 dataWorkerData = nullptr;
125 delete work;
126 }
127
OnExpired()128 void CallbackInstance::OnExpired()
129 {
130 std::lock_guard<std::mutex> lock(callbackLock_);
131 auto findCallback = std::find_if(callbackInstances_.begin(), callbackInstances_.end(),
132 [&](const auto& callbackInstance) { return callbackInstance.second.get() == this; }
133 );
134 if (findCallback == callbackInstances_.end()) {
135 BGTASK_LOGI("expired callback is not found");
136 return;
137 }
138
139 if (expiredCallbackInfo_.ref == nullptr) {
140 BGTASK_LOGE("expired callback unset");
141 callbackInstances_.erase(findCallback);
142 return;
143 }
144
145 uv_loop_s *loop = nullptr;
146 napi_get_uv_event_loop(expiredCallbackInfo_.env, &loop);
147 if (loop == nullptr) {
148 BGTASK_LOGE("loop instance is nullptr");
149 callbackInstances_.erase(findCallback);
150 return;
151 }
152
153 CallbackReceiveDataWorker *dataWorker = new (std::nothrow) CallbackReceiveDataWorker();
154 if (dataWorker == nullptr) {
155 BGTASK_LOGE("new dataWorker failed");
156 callbackInstances_.erase(findCallback);
157 return;
158 }
159
160 dataWorker->env = expiredCallbackInfo_.env;
161 dataWorker->ref = expiredCallbackInfo_.ref;
162 dataWorker->callback = shared_from_this();
163
164 uv_work_t *work = new (std::nothrow) uv_work_t;
165 if (work == nullptr) {
166 BGTASK_LOGW("OnExpired new work failed");
167 delete dataWorker;
168 dataWorker = nullptr;
169 callbackInstances_.erase(findCallback);
170 return;
171 }
172
173 work->data = static_cast<void *>(dataWorker);
174
175 int32_t ret = uv_queue_work(loop, work, [](uv_work_t *work) {}, UvQueueWorkOnExpired);
176 if (ret != 0) {
177 delete dataWorker;
178 dataWorker = nullptr;
179 delete work;
180 work = nullptr;
181 callbackInstances_.erase(findCallback);
182 }
183 }
184
SetCallbackInfo(const napi_env & env,const napi_ref & ref)185 void CallbackInstance::SetCallbackInfo(const napi_env &env, const napi_ref &ref)
186 {
187 expiredCallbackInfo_.env = env;
188 expiredCallbackInfo_.ref = ref;
189 }
190
GetExpiredCallback(const napi_env & env,const napi_value & value,std::shared_ptr<CallbackInstance> & callback)191 napi_value GetExpiredCallback(
192 const napi_env &env, const napi_value &value, std::shared_ptr<CallbackInstance> &callback)
193 {
194 napi_ref result = nullptr;
195 callback = std::make_shared<CallbackInstance>();
196 callback->Init();
197
198 napi_create_reference(env, value, 1, &result);
199 callback->SetCallbackInfo(env, result);
200
201 return Common::NapiGetNull(env);
202 }
203
ParseParameters(const napi_env & env,const napi_callback_info & info,std::u16string & reason,std::shared_ptr<CallbackInstance> & callback,bool isThrow)204 napi_value ParseParameters(const napi_env &env, const napi_callback_info &info,
205 std::u16string &reason, std::shared_ptr<CallbackInstance> &callback, bool isThrow)
206 {
207 size_t argc = REQUEST_SUSPEND_DELAY_PARAMS;
208 napi_value argv[REQUEST_SUSPEND_DELAY_PARAMS] = {nullptr};
209 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
210 if (argc != REQUEST_SUSPEND_DELAY_PARAMS) {
211 Common::HandleParamErr(env, ERR_PARAM_NUMBER_ERR, isThrow);
212 return nullptr;
213 }
214
215 // argv[0] : reason
216 if (Common::GetU16StringValue(env, argv[0], reason) == nullptr) {
217 BGTASK_LOGE("ParseParameters failed, reason is nullptr.");
218 Common::HandleParamErr(env, ERR_REASON_NULL_OR_TYPE_ERR, isThrow);
219 return nullptr;
220 }
221
222 // arg[1] : callback
223 napi_valuetype valuetype = napi_undefined;
224 NAPI_CALL(env, napi_typeof(env, argv[1], &valuetype));
225 if (valuetype != napi_function) {
226 Common::HandleParamErr(env, ERR_CALLBACK_NULL_OR_TYPE_ERR, isThrow);
227 return nullptr;
228 }
229
230 if (GetExpiredCallback(env, argv[1], callback) == nullptr) {
231 BGTASK_LOGE("ExpiredCallback parse failed");
232 Common::HandleParamErr(env, ERR_CALLBACK_NULL_OR_TYPE_ERR, isThrow);
233 return nullptr;
234 }
235 return Common::NapiGetNull(env);
236 }
237
RequestSuspendDelay(napi_env env,napi_callback_info info,bool isThrow)238 napi_value RequestSuspendDelay(napi_env env, napi_callback_info info, bool isThrow)
239 {
240 std::shared_ptr<CallbackInstance> callback = nullptr;
241 std::u16string reason;
242 if (ParseParameters(env, info, reason, callback, isThrow) == nullptr) {
243 return Common::NapiGetNull(env);
244 }
245
246 std::shared_ptr<DelaySuspendInfo> delaySuspendInfo {nullptr};
247 ErrCode errCode = DelayedSingleton<BackgroundTaskManager>::GetInstance()->
248 RequestSuspendDelay(reason, *callback, delaySuspendInfo);
249 Common::HandleErrCode(env, errCode, isThrow);
250 if (!delaySuspendInfo) {
251 return Common::NapiGetNull(env);
252 }
253 {
254 std::lock_guard<std::mutex> lock(callbackLock_);
255 callbackInstances_[delaySuspendInfo->GetRequestId()] = callback;
256 }
257
258 napi_value result = nullptr;
259 napi_create_object(env, &result);
260 if (!Common::SetDelaySuspendInfo(env, delaySuspendInfo, result)) {
261 BGTASK_LOGW("Set DelaySuspendInfo object failed");
262 }
263 return result;
264 }
265
RequestSuspendDelay(napi_env env,napi_callback_info info)266 napi_value RequestSuspendDelay(napi_env env, napi_callback_info info)
267 {
268 return RequestSuspendDelay(env, info, false);
269 }
270
RequestSuspendDelayThrow(napi_env env,napi_callback_info info)271 napi_value RequestSuspendDelayThrow(napi_env env, napi_callback_info info)
272 {
273 return RequestSuspendDelay(env, info, true);
274 }
275 } // namespace BackgroundTaskMgr
276 } // namespace OHOS