1 /*
2 * Copyright (c) 2022-2024 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 "js_timer.h"
17
18 #include <atomic>
19 #include <memory>
20 #include <mutex>
21 #include <string>
22 #include <vector>
23 #include <unordered_map>
24
25 #include "hilog_tag_wrapper.h"
26 #include "js_runtime.h"
27 #include "js_runtime_utils.h"
28
29 namespace OHOS {
30 namespace AbilityRuntime {
31 namespace {
32 class JsTimer;
33
34 std::atomic<uint32_t> g_callbackId(1);
35 std::mutex g_mutex;
36 std::unordered_map<uint32_t, std::shared_ptr<JsTimer>> g_timerTable;
37
38 class JsTimer final {
39 public:
JsTimer(napi_env env,const std::shared_ptr<NativeReference> & jsFunction,uint32_t id)40 JsTimer(napi_env env, const std::shared_ptr<NativeReference> &jsFunction, uint32_t id)
41 : env_(env), jsFunction_(jsFunction), id_(id)
42 {
43 uv_loop_s* loop = nullptr;
44 napi_get_uv_event_loop(env_, &loop);
45 if (loop == nullptr) {
46 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null loop");
47 return;
48 }
49 uv_timer_init(loop, &timerReq_);
50 timerReq_.data = this;
51 }
52
~JsTimer()53 ~JsTimer()
54 {
55 uv_timer_stop(&timerReq_);
56 }
57
Start(int64_t timeout,int64_t repeat)58 void Start(int64_t timeout, int64_t repeat)
59 {
60 uv_timer_start(&timerReq_, [](uv_timer_t *timerReq) {
61 auto me = static_cast<JsTimer*>(timerReq->data);
62 me->OnTimeout();
63 }, timeout, repeat);
64 }
65
OnTimeout()66 void OnTimeout()
67 {
68 std::vector<napi_value> args;
69 args.reserve(jsArgs_.size());
70 for (auto arg : jsArgs_) {
71 args.emplace_back(arg->GetNapiValue());
72 }
73 napi_value res = nullptr;
74 napi_call_function(env_, CreateJsUndefined(env_),
75 jsFunction_->GetNapiValue(), args.size(), args.data(), &res);
76
77 if (uv_timer_get_repeat(&timerReq_) == 0) {
78 std::lock_guard<std::mutex> lock(g_mutex);
79 g_timerTable.erase(id_);
80 }
81 }
82
PushArgs(const std::shared_ptr<NativeReference> & ref)83 void PushArgs(const std::shared_ptr<NativeReference> &ref)
84 {
85 jsArgs_.emplace_back(ref);
86 }
87
88 private:
89 napi_env env_;
90 std::shared_ptr<NativeReference> jsFunction_;
91 std::vector<std::shared_ptr<NativeReference>> jsArgs_;
92 uv_timer_t timerReq_;
93 uint32_t id_ = 0;
94 };
95
StartTimeoutOrInterval(napi_env env,napi_callback_info info,bool isInterval)96 napi_value StartTimeoutOrInterval(napi_env env, napi_callback_info info, bool isInterval)
97 {
98 if (env == nullptr || info == nullptr) {
99 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null env/callback info");
100 return nullptr;
101 }
102 size_t argc = ARGC_MAX_COUNT;
103 napi_value argv[ARGC_MAX_COUNT] = {nullptr};
104 napi_value thisVar = nullptr;
105 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
106
107 // parameter check, must have at least 2 params
108 if (argc < 2 ||!CheckTypeForNapiValue(env, argv[0], napi_function)
109 || !CheckTypeForNapiValue(env, argv[1], napi_number)) {
110 TAG_LOGE(AAFwkTag::ABILITY_SIM, "invalid parameter");
111 return CreateJsUndefined(env);
112 }
113
114 // parse parameter
115 napi_ref ref = nullptr;
116 napi_create_reference(env, argv[0], 1, &ref);
117 std::shared_ptr<NativeReference> jsFunction(reinterpret_cast<NativeReference*>(ref));
118 int64_t delayTime = 0;
119 napi_get_value_int64(env, argv[1], &delayTime);
120 uint32_t callbackId = g_callbackId.fetch_add(1, std::memory_order_relaxed);
121
122 auto task = std::make_shared<JsTimer>(env, jsFunction, callbackId);
123 for (size_t index = 2; index < argc; ++index) {
124 napi_ref taskRef = nullptr;
125 napi_create_reference(env, argv[index], 1, &taskRef);
126 task->PushArgs(std::shared_ptr<NativeReference>(reinterpret_cast<NativeReference*>(taskRef)));
127 }
128
129 // if setInterval is called, interval must not be zero for repeat, so set to 1ms
130 int64_t interval = 0;
131 if (isInterval) {
132 interval = delayTime > 0 ? delayTime : 1;
133 }
134 task->Start(delayTime, interval);
135
136 {
137 std::lock_guard<std::mutex> lock(g_mutex);
138 g_timerTable.emplace(callbackId, task);
139 }
140
141 return CreateJsValue(env, callbackId);
142 }
143
StartTimeout(napi_env env,napi_callback_info info)144 napi_value StartTimeout(napi_env env, napi_callback_info info)
145 {
146 return StartTimeoutOrInterval(env, info, false);
147 }
148
StartInterval(napi_env env,napi_callback_info info)149 napi_value StartInterval(napi_env env, napi_callback_info info)
150 {
151 return StartTimeoutOrInterval(env, info, true);
152 }
153
StopTimeoutOrInterval(napi_env env,napi_callback_info info)154 napi_value StopTimeoutOrInterval(napi_env env, napi_callback_info info)
155 {
156 if (env == nullptr || info == nullptr) {
157 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null env/callback info");
158 return nullptr;
159 }
160 size_t argc = ARGC_MAX_COUNT;
161 napi_value argv[ARGC_MAX_COUNT] = {nullptr};
162 napi_value thisVar = nullptr;
163 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
164
165 // parameter check, must have at least 1 param
166 if (argc < 1 || !CheckTypeForNapiValue(env, argv[0], napi_number)) {
167 TAG_LOGE(AAFwkTag::ABILITY_SIM, "invalid parameter");
168 return CreateJsUndefined(env);
169 }
170 uint32_t callbackId = 0;
171 napi_get_value_uint32(env, argv[0], &callbackId);
172 {
173 std::lock_guard<std::mutex> lock(g_mutex);
174 g_timerTable.erase(callbackId);
175 }
176 return CreateJsUndefined(env);
177 }
178 }
179
InitTimer(napi_env env,napi_value globalObject)180 void InitTimer(napi_env env, napi_value globalObject)
181 {
182 const char *moduleName = "AsJsTimer";
183 BindNativeFunction(env, globalObject, "setTimeout", moduleName, StartTimeout);
184 BindNativeFunction(env, globalObject, "setInterval", moduleName, StartInterval);
185 BindNativeFunction(env, globalObject, "clearTimeout", moduleName, StopTimeoutOrInterval);
186 BindNativeFunction(env, globalObject, "clearInterval", moduleName, StopTimeoutOrInterval);
187 }
188 } // namespace AbilityRuntime
189 } // namespace OHOS