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