• 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 "timer.h"
17 
18 #include "native_engine/native_engine.h"
19 #include "utils/log.h"
20 
21 namespace OHOS::JsSysModule {
22 using namespace Commonlibrary::Concurrent::Common;
23 
24 uint32_t Timer::timeCallbackId = 0;
25 std::map<uint32_t, TimerCallbackInfo*> Timer::timerTable;
26 std::mutex Timer::timeLock;
27 
~TimerCallbackInfo()28 TimerCallbackInfo::~TimerCallbackInfo()
29 {
30     Helper::NapiHelper::DeleteReference(env_, callback_);
31     for (size_t idx = 0; idx < argc_; idx++) {
32         Helper::NapiHelper::DeleteReference(env_, argv_[idx]);
33     }
34     Helper::CloseHelp::DeletePointer(argv_, true);
35 
36     uv_timer_stop(timeReq_);
37     uv_close(reinterpret_cast<uv_handle_t*>(timeReq_), [](uv_handle_t* handle) {
38         if (handle != nullptr) {
39             delete (uv_timer_t*)handle;
40             handle = nullptr;
41         }
42     });
43 }
44 
RegisterTime(napi_env env)45 bool Timer::RegisterTime(napi_env env)
46 {
47     if (env == nullptr) {
48         return false;
49     }
50     napi_property_descriptor properties[] = {
51         DECLARE_NAPI_DEFAULT_PROPERTY_FUNCTION("setTimeout", SetTimeout),
52         DECLARE_NAPI_DEFAULT_PROPERTY_FUNCTION("setInterval", SetInterval),
53         DECLARE_NAPI_DEFAULT_PROPERTY_FUNCTION("clearTimeout", ClearTimer),
54         DECLARE_NAPI_DEFAULT_PROPERTY_FUNCTION("clearInterval", ClearTimer)
55     };
56     napi_value globalObj = Helper::NapiHelper::GetGlobalObject(env);
57     napi_status status = napi_define_properties(env, globalObj, sizeof(properties) / sizeof(properties[0]), properties);
58     return status == napi_ok;
59 }
60 
SetTimeout(napi_env env,napi_callback_info cbinfo)61 napi_value Timer::SetTimeout(napi_env env, napi_callback_info cbinfo)
62 {
63     return Timer::SetTimeoutInner(env, cbinfo, false);
64 }
65 
SetInterval(napi_env env,napi_callback_info cbinfo)66 napi_value Timer::SetInterval(napi_env env, napi_callback_info cbinfo)
67 {
68     return Timer::SetTimeoutInner(env, cbinfo, true);
69 }
70 
ClearTimer(napi_env env,napi_callback_info cbinfo)71 napi_value Timer::ClearTimer(napi_env env, napi_callback_info cbinfo)
72 {
73     // 1. check args
74     size_t argc = 1;
75     napi_value argv[1];
76     napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, nullptr);
77     if (argc < 1) {
78         HILOG_ERROR("the number of params must be one");
79         return nullptr;
80     }
81 
82     uint32_t tId;
83     napi_status status = napi_get_value_uint32(env, argv[0], &tId);
84     if (status != napi_ok) {
85         HILOG_ERROR("first param should be number");
86         return nullptr;
87     }
88 
89     {
90         std::lock_guard<std::mutex> lock(timeLock);
91         auto iter = timerTable.find(tId);
92         if (iter == timerTable.end()) {
93             HILOG_ERROR("timerId is inexistent");
94             return nullptr;
95         }
96         TimerCallbackInfo* callbackInfo = iter->second;
97         timerTable.erase(tId);
98         Helper::CloseHelp::DeletePointer(callbackInfo, false);
99     }
100     return Helper::NapiHelper::GetUndefinedValue(env);
101 }
102 
TimerCallback(uv_timer_t * handle)103 void Timer::TimerCallback(uv_timer_t* handle)
104 {
105     TimerCallbackInfo* callbackInfo = static_cast<TimerCallbackInfo*>(handle->data);
106     if (callbackInfo == nullptr) {
107         return;
108     }
109     // Save the following parameters to ensure that they can still obtained if callback clears the callbackInfo.
110     bool repeat = callbackInfo->repeat_;
111     uint32_t tId = callbackInfo->tId_;
112     napi_env env = callbackInfo->env_;
113 
114     napi_handle_scope scope = nullptr;
115     napi_open_handle_scope(env, &scope);
116     if (scope == nullptr) {
117         return;
118     }
119     napi_value callback = Helper::NapiHelper::GetReferenceValue(env, callbackInfo->callback_);
120     napi_value undefinedValue = Helper::NapiHelper::GetUndefinedValue(env);
121     napi_value callbackResult = nullptr;
122     napi_value* callbackArgv = new napi_value[callbackInfo->argc_];
123     for (size_t idx = 0; idx < callbackInfo->argc_; idx++) {
124         callbackArgv[idx] = Helper::NapiHelper::GetReferenceValue(env, callbackInfo->argv_[idx]);
125     }
126 
127     napi_call_function(env, undefinedValue, callback,
128                        callbackInfo->argc_, callbackArgv, &callbackResult);
129     Helper::CloseHelp::DeletePointer(callbackArgv, true);
130     bool isPendingException = false;
131     napi_is_exception_pending(env, &isPendingException);
132     NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
133     if (isPendingException && engine->IsMainThread()) {
134         HILOG_ERROR("Pending exception in TimerCallback. Triggering HandleUncaughtException");
135         engine->HandleUncaughtException();
136         napi_close_handle_scope(env, scope);
137         return;
138     }
139     if (callbackResult == nullptr) {
140         HILOG_ERROR("call timerCallback error");
141         napi_close_handle_scope(env, scope);
142         Helper::CloseHelp::DeletePointer(callbackInfo, false);
143         return;
144     }
145 
146     // callback maybe contain ClearTimer, so we need check to avoid use-after-free and double-free of callbackInfo
147     std::lock_guard<std::mutex> lock(timeLock);
148     if (timerTable.find(tId) == timerTable.end()) {
149         HILOG_WARN("not found timerCallbackInfo");
150         napi_close_handle_scope(env, scope);
151         return;
152     }
153     if (!repeat) {
154         timerTable.erase(tId);
155         napi_close_handle_scope(env, scope);
156         Helper::CloseHelp::DeletePointer(callbackInfo, false);
157     } else {
158         napi_close_handle_scope(env, scope);
159         uv_timer_again(handle);
160     }
161 }
162 
SetTimeoutInner(napi_env env,napi_callback_info cbinfo,bool repeat)163 napi_value Timer::SetTimeoutInner(napi_env env, napi_callback_info cbinfo, bool repeat)
164 {
165     // 1. check args
166     size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
167     if (argc < 1) {
168         napi_throw_error(env, nullptr, "StartTimeoutOrInterval, callback info is nullptr.");
169         return nullptr;
170     }
171     napi_value* argv = new napi_value[argc];
172     Helper::ObjectScope<napi_value> scope(argv, true);
173     napi_value thisVar = nullptr;
174     napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
175     if (!Helper::NapiHelper::IsCallable(env, argv[0])) {
176         HILOG_ERROR("Set callback timer failed with invalid parameter.");
177         return Helper::NapiHelper::GetUndefinedValue(env);
178     }
179     int32_t timeout = 0;
180     if (argc > 1) {
181         napi_status status = napi_get_value_int32(env, argv[1], &timeout);
182         if (status != napi_ok) {
183             HILOG_WARN("timeout should be number");
184             timeout = 0;
185         }
186     }
187     if (timeout < 0) {
188         HILOG_WARN("timeout < 0 is unreasonable");
189     }
190     // 2. get callback args
191     size_t callbackArgc = argc >= 2 ? argc - 2 : 0; // 2 include callback and timeout
192     napi_ref* callbackArgv = nullptr;
193     if (callbackArgc > 0) {
194         callbackArgv = new napi_ref[callbackArgc];
195         for (size_t idx = 0; idx < callbackArgc; idx++) {
196             callbackArgv[idx] =
197                 Helper::NapiHelper::CreateReference(env, argv[idx + 2], 1); // 2 include callback and timeout
198         }
199     }
200 
201     // 3. generate time callback id
202     // 4. generate time callback info
203     // 5. push callback info into timerTable
204     uint32_t tId = 0;
205     TimerCallbackInfo* callbackInfo = nullptr;
206     {
207         std::lock_guard<std::mutex> lock(timeLock);
208         tId = timeCallbackId++;
209         napi_ref callbackRef = Helper::NapiHelper::CreateReference(env, argv[0], 1);
210         callbackInfo = new TimerCallbackInfo(env, tId, timeout, callbackRef, repeat, callbackArgc, callbackArgv);
211         if (timerTable.find(tId) != timerTable.end()) {
212             HILOG_ERROR("timerTable occurs error");
213         } else {
214             timerTable[tId] = callbackInfo;
215         }
216     }
217 
218     // 6. start timer
219     uv_loop_t* loop = Helper::NapiHelper::GetLibUV(env);
220     NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
221     if (engine->IsMainThread()) {
222         uv_update_time(loop);
223     }
224     uv_timer_start(callbackInfo->timeReq_, TimerCallback, timeout >= 0 ? timeout : 1, timeout > 0 ? timeout : 1);
225     if (engine->IsMainThread()) {
226         uv_work_t *work = new uv_work_t;
227         uv_queue_work(loop, work, [](uv_work_t *){ }, [](uv_work_t *work, int32_t) {delete work; });
228     }
229     return Helper::NapiHelper::CreateUint32(env, tId);
230 }
231 
ClearEnvironmentTimer(napi_env env)232 void Timer::ClearEnvironmentTimer(napi_env env)
233 {
234     std::lock_guard<std::mutex> lock(timeLock);
235     auto iter = timerTable.begin();
236     while (iter != timerTable.end()) {
237         TimerCallbackInfo* callbackInfo = iter->second;
238         if (callbackInfo->env_ == env) {
239             iter = timerTable.erase(iter);
240             Helper::CloseHelp::DeletePointer(callbackInfo, false);
241         } else {
242             iter++;
243         }
244     }
245 }
246 
HasTimer(napi_env env)247 bool Timer::HasTimer(napi_env env)
248 {
249     std::lock_guard<std::mutex> lock(timeLock);
250     auto iter = std::find_if(timerTable.begin(), timerTable.end(), [env](const auto &item) {
251         return item.second->env_ == env;
252     });
253     return iter != timerTable.end();
254 }
255 } // namespace Commonlibrary::JsSysModule
256