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