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 "utils/log.h"
19
20 namespace Commonlibrary::Concurrent::Common::Plugin {
21 uint32_t Timer::timeCallbackId = 0;
22 std::map<uint32_t, TimerCallbackInfo*> Timer::timerTable;
23 std::mutex Timer::timeLock;
24
~TimerCallbackInfo()25 TimerCallbackInfo::~TimerCallbackInfo()
26 {
27 Helper::NapiHelper::DeleteReference(env_, callback_);
28 for (size_t idx = 0; idx < argc_; idx++) {
29 Helper::NapiHelper::DeleteReference(env_, argv_[idx]);
30 }
31 Helper::CloseHelp::DeletePointer(argv_, true);
32
33 uv_timer_stop(timeReq_);
34 uv_close(reinterpret_cast<uv_handle_t*>(timeReq_), [](uv_handle_t* handle) {
35 if (handle != nullptr) {
36 delete (uv_timer_t*)handle;
37 handle = nullptr;
38 }
39 });
40 }
41
RegisterTime(napi_env env)42 bool Timer::RegisterTime(napi_env env)
43 {
44 if (env == nullptr) {
45 return false;
46 }
47 napi_property_descriptor properties[] = {
48 DECLARE_NAPI_FUNCTION("setTimeout", SetTimeout),
49 DECLARE_NAPI_FUNCTION("setInterval", SetInterval),
50 DECLARE_NAPI_FUNCTION("clearTimeout", ClearTimer),
51 DECLARE_NAPI_FUNCTION("clearInterval", ClearTimer)
52 };
53 napi_value globalObj = Helper::NapiHelper::GetGlobalObject(env);
54 napi_status status = napi_define_properties(env, globalObj, sizeof(properties) / sizeof(properties[0]), properties);
55 return status == napi_ok;
56 }
57
SetTimeout(napi_env env,napi_callback_info cbinfo)58 napi_value Timer::SetTimeout(napi_env env, napi_callback_info cbinfo)
59 {
60 return Timer::SetTimeoutInner(env, cbinfo, false);
61 }
62
SetInterval(napi_env env,napi_callback_info cbinfo)63 napi_value Timer::SetInterval(napi_env env, napi_callback_info cbinfo)
64 {
65 return Timer::SetTimeoutInner(env, cbinfo, true);
66 }
67
ClearTimer(napi_env env,napi_callback_info cbinfo)68 napi_value Timer::ClearTimer(napi_env env, napi_callback_info cbinfo)
69 {
70 // 1. check args
71 size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
72 if (argc < 1) {
73 HILOG_WARN("first arg should be number");
74 return nullptr;
75 }
76 napi_value* argv = new napi_value[argc];
77 Helper::ObjectScope<napi_value> scope(argv, true);
78 napi_value thisVar = nullptr;
79 napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
80
81 uint32_t tId;
82 napi_status status = napi_get_value_uint32(env, argv[0], &tId);
83 if (status != napi_ok) {
84 HILOG_WARN("handler should be number");
85 return nullptr;
86 }
87 TimerCallbackInfo* callbackInfo = nullptr;
88 {
89 std::lock_guard<std::mutex> lock(timeLock);
90 auto iter = timerTable.find(tId);
91 if (iter == timerTable.end()) {
92 HILOG_INFO("handler not in table");
93 return nullptr;
94 }
95 callbackInfo = iter->second;
96 timerTable.erase(tId);
97 }
98 Helper::CloseHelp::DeletePointer(callbackInfo, false);
99 return Helper::NapiHelper::GetUndefinedValue(env);
100 }
101
TimerCallback(uv_timer_t * handle)102 void Timer::TimerCallback(uv_timer_t* handle)
103 {
104 TimerCallbackInfo* callbackInfo = static_cast<TimerCallbackInfo*>(handle->data);
105 if (callbackInfo == nullptr) {
106 return;
107 }
108 napi_value callback = Helper::NapiHelper::GetReferenceValue(callbackInfo->env_, callbackInfo->callback_);
109 napi_value undefinedValue = Helper::NapiHelper::GetUndefinedValue(callbackInfo->env_);
110 napi_value callbackResult = nullptr;
111 napi_value* callbackArgv = new napi_value[callbackInfo->argc_];
112 Helper::ObjectScope<napi_value> scope(callbackArgv, true);
113 for (size_t idx = 0; idx < callbackInfo->argc_; idx++) {
114 callbackArgv[idx] = Helper::NapiHelper::GetReferenceValue(callbackInfo->env_, callbackInfo->argv_[idx]);
115 }
116 napi_call_function(callbackInfo->env_, undefinedValue, callback,
117 callbackInfo->argc_, callbackArgv, &callbackResult);
118 if (callbackResult == nullptr) {
119 HILOG_WARN("call callback error");
120 return;
121 }
122 if (!callbackInfo->repeat_) {
123 {
124 std::lock_guard<std::mutex> lock(timeLock);
125 if (timerTable.find(callbackInfo->tId_) == timerTable.end()) {
126 HILOG_ERROR("erase inexistent timerCallbackInfo");
127 } else {
128 timerTable.erase(callbackInfo->tId_);
129 }
130 }
131 Helper::CloseHelp::DeletePointer(callbackInfo, false);
132 } else {
133 uv_timer_again(handle);
134 }
135 }
136
SetTimeoutInner(napi_env env,napi_callback_info cbinfo,bool repeat)137 napi_value Timer::SetTimeoutInner(napi_env env, napi_callback_info cbinfo, bool repeat)
138 {
139 // 1. check args
140 size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
141 if (argc < 1) {
142 napi_throw_error(env, nullptr, "callback must be a function. received undefined");
143 return nullptr;
144 }
145 napi_value* argv = new napi_value[argc];
146 Helper::ObjectScope<napi_value> scope(argv, true);
147 napi_value thisVar = nullptr;
148 napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
149 if (!Helper::NapiHelper::IsCallable(env, argv[0])) {
150 napi_throw_error(env, nullptr, "callback must be a function.");
151 return nullptr;
152 }
153 int32_t timeout = 0;
154 if (argc > 1) {
155 napi_status status = napi_get_value_int32(env, argv[1], &timeout);
156 if (status != napi_ok) {
157 HILOG_WARN("timeout should be number");
158 timeout = 0;
159 }
160 }
161 if (timeout < 0) {
162 HILOG_WARN("worker:: timeout < 0 is unreasonable");
163 }
164 // 2. get callback args
165 size_t callbackArgc = argc >= 2 ? argc - 2 : 0; // 2 include callback and timeout
166 napi_ref* callbackArgv = nullptr;
167 if (callbackArgc > 0) {
168 callbackArgv = new napi_ref[callbackArgc];
169 for (size_t idx = 0; idx < callbackArgc; idx++) {
170 callbackArgv[idx] =
171 Helper::NapiHelper::CreateReference(env, argv[idx + 2], 1); // 2 include callback and timeout
172 }
173 }
174
175 // 3. generate time callback id
176 // 4. generate time callback info
177 // 5. push callback info into timerTable
178 uint32_t tId = 0;
179 TimerCallbackInfo* callbackInfo = nullptr;
180 {
181 std::lock_guard<std::mutex> lock(timeLock);
182 tId = timeCallbackId++;
183 napi_ref callbackRef = Helper::NapiHelper::CreateReference(env, argv[0], 1);
184 callbackInfo = new TimerCallbackInfo(env, tId, timeout, callbackRef, repeat, callbackArgc, callbackArgv);
185 if (timerTable.find(tId) != timerTable.end()) {
186 HILOG_ERROR("timerTable occurs error");
187 } else {
188 timerTable[tId] = callbackInfo;
189 }
190 }
191
192 // 6. start timer
193 uv_timer_start(callbackInfo->timeReq_, TimerCallback, timeout >= 0 ? timeout : 1, timeout > 0 ? timeout : 1);
194 return Helper::NapiHelper::CreateUint32(env, tId);
195 }
196
ClearEnvironmentTimer(napi_env env)197 void Timer::ClearEnvironmentTimer(napi_env env)
198 {
199 std::lock_guard<std::mutex> lock(timeLock);
200 auto iter = timerTable.begin();
201 while (iter != timerTable.end()) {
202 TimerCallbackInfo* callbackInfo = iter->second;
203 if (callbackInfo->env_ == env) {
204 iter = timerTable.erase(iter);
205 Helper::CloseHelp::DeletePointer(callbackInfo, false);
206 } else {
207 iter++;
208 }
209 }
210 }
211 } // namespace Commonlibrary::Concurrent::Common::Plugin
212