• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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