• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2025 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 <array>
19 #include <unordered_map>
20 #include <iostream>
21 
22 #if defined(__clang__)
23 #pragma clang diagnostic push
24 #pragma clang diagnostic ignored "-Wshadow"
25 #elif defined(__GNUC__)
26 #pragma GCC diagnostic push
27 #pragma GCC diagnostic ignored "-Wshadow"
28 #endif
29 
30 // NOLINTBEGIN(readability-identifier-naming, readability-redundant-declaration)
31 // CC-OFFNXT(G.FMT.10-CPP) project code style
32 napi_status __attribute__((weak)) napi_is_callable(napi_env env, napi_value value, bool *result);
33 // CC-OFFNXT(G.FMT.10-CPP) project code style
34 napi_status __attribute__((weak)) napi_get_uv_event_loop(napi_env env, struct uv_loop_s **loop);
35 // NOLINTEND(readability-identifier-naming, readability-redundant-declaration)
36 
37 namespace ark::ets::interop::js::helper {
38 
39 // NOLINTBEGIN(fuchsia-statically-constructed-objects)
40 // CC-OFFNXT(G.NAM.03-CPP) project code style
41 static uint32_t g_nextTimerId = 0;
42 static std::unordered_map<uint32_t, TimerInfo *> g_timers;
43 // NOLINTEND(fuchsia-statically-constructed-objects)
44 
TimerInfo(napi_env env,napi_ref cb,std::vector<napi_ref> cbArgs,bool repeat)45 TimerInfo::TimerInfo(napi_env env, napi_ref cb, std::vector<napi_ref> cbArgs, bool repeat)
46     : env(env), cb(cb), cbArgs(std::move(cbArgs)), repeat(repeat), timer(new uv_timer_t())
47 {
48     timerId = g_nextTimerId++;
49     g_timers[timerId] = this;
50     timer->data = this;
51     uv_loop_t *loop = nullptr;
52     napi_get_uv_event_loop(env, &loop);
53     uv_timer_init(loop, timer);
54 }
55 
~TimerInfo()56 TimerInfo::~TimerInfo()
57 {
58     napi_delete_reference(env, cb);
59     for (auto &ref : cbArgs) {
60         napi_delete_reference(env, ref);
61     }
62     g_timers.erase(timerId);
63     uv_timer_stop(timer);
64     uv_close(reinterpret_cast<uv_handle_t *>(timer), TIMER_CLOSE_CALLBACK);
65 }
66 
RegisterTimer(napi_env env,napi_ref cb,std::vector<napi_ref> cbArgs,int32_t timeout,bool repeat)67 static napi_value RegisterTimer(napi_env env, napi_ref cb, std::vector<napi_ref> cbArgs, int32_t timeout, bool repeat)
68 {
69     auto timerCallback = [](uv_timer_t *timer) {
70         auto *timerInfo = reinterpret_cast<TimerInfo *>(timer->data);
71         napi_env env = timerInfo->env;
72         napi_value callback = nullptr;
73         napi_get_reference_value(env, timerInfo->cb, &callback);
74         std::vector<napi_value> callbackArgs(timerInfo->cbArgs.size());
75         for (auto argIdx = 0U; argIdx < callbackArgs.size(); ++argIdx) {
76             napi_get_reference_value(env, timerInfo->cbArgs[argIdx], &callbackArgs[argIdx]);
77         }
78 
79         auto timerId = timerInfo->timerId;
80         napi_value undefined = nullptr;
81         napi_get_undefined(env, &undefined);
82 
83         napi_value result = nullptr;
84         napi_call_function(env, undefined, callback, callbackArgs.size(), callbackArgs.data(), &result);
85 
86         bool isExceptionPending = false;
87         napi_is_exception_pending(env, &isExceptionPending);
88         if (isExceptionPending || (result == nullptr)) {
89             std::cerr << "Error occured during timer callback";
90             std::abort();  // CC-OFF(G.STD.16-CPP) fatal error
91         }
92 
93         if (g_timers.find(timerId) == g_timers.end()) {
94             // timer was cleared during callback
95             return;
96         }
97 
98         if (timerInfo->repeat) {
99             uv_timer_again(timer);
100         } else {
101             delete timerInfo;
102         }
103     };
104 
105     uv_loop_t *loop = nullptr;
106     napi_get_uv_event_loop(env, &loop);
107 
108     auto *timerInfo = new TimerInfo(env, cb, std::move(cbArgs), repeat);
109 
110     uv_update_time(loop);
111     uv_timer_start(timerInfo->timer, timerCallback, timeout, timeout > 0 ? timeout : 1);
112     uv_async_send(&loop->wq_async);
113 
114     napi_value timerId = nullptr;
115     napi_create_uint32(env, timerInfo->timerId, &timerId);
116     return timerId;
117 }
118 
SetTimeoutImpl(napi_env env,napi_callback_info info,bool repeat)119 napi_value SetTimeoutImpl(napi_env env, napi_callback_info info, bool repeat)
120 {
121     size_t argc = 0;
122     napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
123     if (argc < 1) {
124         napi_throw_error(env, nullptr, "setTimeout/setInterval: callback info is nullptr");
125         return nullptr;
126     }
127 
128     std::vector<napi_value> argv(argc);
129     napi_value thisVar = nullptr;
130     napi_get_cb_info(env, info, &argc, argv.data(), &thisVar, nullptr);
131 
132     napi_value undefined;
133     napi_get_undefined(env, &undefined);
134 
135     bool isCallable = false;
136     napi_is_callable(env, argv[0], &isCallable);
137     if (!isCallable) {
138         return undefined;
139     }
140 
141     int32_t timeout = 0;
142     if (argc > 1) {
143         napi_status status = napi_get_value_int32(env, argv[1], &timeout);
144         if (status != napi_ok) {
145             napi_throw_error(env, nullptr, "timeout should be number");
146             return nullptr;
147         }
148     }
149     if (timeout < 0) {
150         napi_throw_error(env, nullptr, "timeout < 0 is unreasonable");
151         return nullptr;
152     }
153 
154     napi_ref callbackRef = nullptr;
155     napi_create_reference(env, argv[0], 1, &callbackRef);
156 
157     std::vector<napi_ref> callbackArgv;
158     for (size_t argsIdx = 2U; argsIdx < argc; argsIdx++) {
159         napi_ref callbackArg = nullptr;
160         napi_create_reference(env, argv[argsIdx], 1, &callbackArg);
161         callbackArgv.push_back(callbackArg);
162     }
163 
164     return RegisterTimer(env, callbackRef, std::move(callbackArgv), timeout, repeat);
165 }
166 
ClearTimerImpl(napi_env env,napi_callback_info info)167 napi_value ClearTimerImpl(napi_env env, napi_callback_info info)
168 {
169     size_t argc = 1;
170     std::array<napi_value, 1> argv {};
171     napi_get_cb_info(env, info, &argc, argv.data(), nullptr, nullptr);
172     if (argc < 1) {
173         napi_throw_error(env, nullptr, "The number of params must be one");
174         return nullptr;
175     }
176 
177     uint32_t tId;
178     napi_status status = napi_get_value_uint32(env, argv[0], &tId);
179     if (status != napi_ok) {
180         napi_throw_error(env, nullptr, "Function param must be number");
181         return nullptr;
182     }
183 
184     auto timerIt = g_timers.find(tId);
185     if (timerIt == g_timers.end()) {
186         // timer already cleared
187         return nullptr;
188     }
189     auto *timerInfo = timerIt->second;
190     g_timers.erase(timerIt);
191     delete timerInfo;
192 
193     napi_value undefined;
194     napi_get_undefined(env, &undefined);
195     return undefined;
196 }
197 
198 }  // namespace ark::ets::interop::js::helper
199