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