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 "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_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(NativeEngine & nativeEngine,const std::shared_ptr<NativeReference> & jsFunction,uint32_t id)40 JsTimer(NativeEngine &nativeEngine, const std::shared_ptr<NativeReference> &jsFunction, uint32_t id)
41 : nativeEngine_(nativeEngine), jsFunction_(jsFunction), id_(id)
42 {
43 uv_timer_init(nativeEngine.GetUVLoop(), &timerReq_);
44 timerReq_.data = this;
45 }
46
~JsTimer()47 ~JsTimer()
48 {
49 uv_timer_stop(&timerReq_);
50 }
51
Start(int64_t timeout,int64_t repeat)52 void Start(int64_t timeout, int64_t repeat)
53 {
54 uv_timer_start(&timerReq_, [](uv_timer_t *timerReq) {
55 auto me = static_cast<JsTimer*>(timerReq->data);
56 me->OnTimeout();
57 }, timeout, repeat);
58 }
59
OnTimeout()60 void OnTimeout()
61 {
62 std::vector<NativeValue*> args;
63 args.reserve(jsArgs_.size());
64 for (auto arg : jsArgs_) {
65 args.emplace_back(arg->Get());
66 }
67 nativeEngine_.CallFunction(nativeEngine_.CreateUndefined(), jsFunction_->Get(), args.data(), args.size());
68
69 if (uv_timer_get_repeat(&timerReq_) == 0) {
70 std::lock_guard<std::mutex> lock(g_mutex);
71 g_timerTable.erase(id_);
72 }
73 }
74
PushArgs(const std::shared_ptr<NativeReference> & ref)75 void PushArgs(const std::shared_ptr<NativeReference> &ref)
76 {
77 jsArgs_.emplace_back(ref);
78 }
79
80 private:
81 NativeEngine& nativeEngine_;
82 std::shared_ptr<NativeReference> jsFunction_;
83 std::vector<std::shared_ptr<NativeReference>> jsArgs_;
84 uv_timer_t timerReq_;
85 uint32_t id_ = 0;
86 };
87
StartTimeoutOrInterval(NativeEngine * engine,NativeCallbackInfo * info,bool isInterval)88 NativeValue *StartTimeoutOrInterval(NativeEngine *engine, NativeCallbackInfo *info, bool isInterval)
89 {
90 if (engine == nullptr || info == nullptr) {
91 HILOG_ERROR("Start timeout or interval failed with engine or callback info is nullptr.");
92 return nullptr;
93 }
94
95 // parameter check, must have at least 2 params
96 if (info->argc < 2 || info->argv[0]->TypeOf() != NATIVE_FUNCTION || info->argv[1]->TypeOf() != NATIVE_NUMBER) {
97 HILOG_ERROR("Set callback timer failed with invalid parameter.");
98 return engine->CreateUndefined();
99 }
100
101 // parse parameter
102 std::shared_ptr<NativeReference> jsFunction(engine->CreateReference(info->argv[0], 1));
103 int64_t delayTime = *ConvertNativeValueTo<NativeNumber>(info->argv[1]);
104 uint32_t callbackId = g_callbackId.fetch_add(1, std::memory_order_relaxed);
105
106 auto task = std::make_shared<JsTimer>(*engine, jsFunction, callbackId);
107 for (size_t index = 2; index < info->argc; ++index) {
108 task->PushArgs(std::shared_ptr<NativeReference>(engine->CreateReference(info->argv[index], 1)));
109 }
110
111 // if setInterval is called, interval must not be zero for repeat, so set to 1ms
112 int64_t interval = 0;
113 if (isInterval) {
114 interval = delayTime > 0 ? delayTime : 1;
115 }
116 task->Start(delayTime, interval);
117
118 {
119 std::lock_guard<std::mutex> lock(g_mutex);
120 g_timerTable.emplace(callbackId, task);
121 }
122
123 return engine->CreateNumber(callbackId);
124 }
125
StartTimeout(NativeEngine * engine,NativeCallbackInfo * info)126 NativeValue *StartTimeout(NativeEngine *engine, NativeCallbackInfo *info)
127 {
128 return StartTimeoutOrInterval(engine, info, false);
129 }
130
StartInterval(NativeEngine * engine,NativeCallbackInfo * info)131 NativeValue *StartInterval(NativeEngine *engine, NativeCallbackInfo *info)
132 {
133 return StartTimeoutOrInterval(engine, info, true);
134 }
135
StopTimeoutOrInterval(NativeEngine * engine,NativeCallbackInfo * info)136 NativeValue *StopTimeoutOrInterval(NativeEngine *engine, NativeCallbackInfo *info)
137 {
138 if (engine == nullptr || info == nullptr) {
139 HILOG_ERROR("Stop timeout or interval failed with engine or callback info is nullptr.");
140 return nullptr;
141 }
142
143 // parameter check, must have at least 1 param
144 if (info->argc < 1 || info->argv[0]->TypeOf() != NATIVE_NUMBER) {
145 HILOG_ERROR("Clear callback timer failed with invalid parameter.");
146 return engine->CreateUndefined();
147 }
148
149 uint32_t callbackId = *ConvertNativeValueTo<NativeNumber>(info->argv[0]);
150 {
151 std::lock_guard<std::mutex> lock(g_mutex);
152 g_timerTable.erase(callbackId);
153 }
154 return engine->CreateUndefined();
155 }
156 }
157
InitTimer(NativeEngine & engine,NativeObject & globalObject)158 void InitTimer(NativeEngine &engine, NativeObject &globalObject)
159 {
160 const char *moduleName = "AsJsTimer";
161 BindNativeFunction(engine, globalObject, "setTimeout", moduleName, StartTimeout);
162 BindNativeFunction(engine, globalObject, "setInterval", moduleName, StartInterval);
163 BindNativeFunction(engine, globalObject, "clearTimeout", moduleName, StopTimeoutOrInterval);
164 BindNativeFunction(engine, globalObject, "clearInterval", moduleName, StopTimeoutOrInterval);
165 }
166 } // namespace AbilityRuntime
167 } // namespace OHOS