1 /**
2 * Copyright (c) 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 <unistd.h>
17 #include "libpandabase/utils/logger.h"
18 #include "plugins/ets/runtime/interop_js/logger.h"
19 #include "plugins/ets/runtime/interop_js/timer_module.h"
20 #include "plugins/ets/runtime/interop_js/code_scopes.h"
21 #include "plugins/ets/runtime/interop_js/interop_context.h"
22
23 pid_t TimerModule::mainTid_ = 0;
24 EtsEnv *TimerModule::mainEtsEnv_ = nullptr;
25 napi_env TimerModule::jsEnv_ = nullptr;
26 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
27 std::unordered_map<uint32_t, uv_timer_t *> TimerModule::timers_;
28 uint32_t TimerModule::nextTimerId_ = 0;
29
TimerInfo(uint32_t id,ets_object funcObject,bool oneShotTimer)30 TimerModule::TimerInfo::TimerInfo(uint32_t id, ets_object funcObject, bool oneShotTimer)
31 : id_(id), funcObject_(funcObject), oneShotTimer_(oneShotTimer)
32 {
33 }
34
Init(EtsEnv * env,napi_env jsEnv)35 bool TimerModule::Init(EtsEnv *env, napi_env jsEnv)
36 {
37 mainTid_ = gettid();
38 mainEtsEnv_ = env;
39 jsEnv_ = jsEnv;
40
41 ets_class globalClass = env->FindClass("escompat/ETSGLOBAL");
42 if (globalClass == nullptr) {
43 INTEROP_LOG(ERROR) << "Cannot find GLOBAL class";
44 return false;
45 }
46
47 const std::array<EtsNativeMethod, 2> impls = {
48 {{"startTimer", nullptr, reinterpret_cast<void *>(TimerModule::StartTimer)},
49 {"stopTimer", nullptr, reinterpret_cast<void *>(TimerModule::StopTimer)}}};
50 return env->RegisterNatives(globalClass, impls.data(), impls.size()) == ETS_OK;
51 }
52
StartTimer(EtsEnv * env,ets_class klass,ets_object funcObject,ets_int delayMs,ets_boolean oneShotTimer)53 ets_int TimerModule::StartTimer(EtsEnv *env, [[maybe_unused]] ets_class klass, ets_object funcObject, ets_int delayMs,
54 ets_boolean oneShotTimer)
55 {
56 if (!CheckMainThread(env)) {
57 return 0;
58 }
59 uv_loop_t *loop = GetMainEventLoop();
60 auto *timer = new uv_timer_t();
61 uv_timer_init(loop, timer);
62
63 uint32_t timerId = GetTimerId();
64 timer->data = new TimerInfo(timerId, env->NewGlobalRef(funcObject), static_cast<bool>(oneShotTimer));
65 timers_[timerId] = timer;
66
67 uv_update_time(loop);
68 uv_timer_start(timer, TimerCallback, delayMs, !static_cast<bool>(oneShotTimer) ? delayMs : 0);
69 uv_async_send(&loop->wq_async);
70 return timerId;
71 }
72
StopTimer(EtsEnv * env,ets_class klass,ets_int timerId)73 void TimerModule::StopTimer(EtsEnv *env, [[maybe_unused]] ets_class klass, ets_int timerId)
74 {
75 if (!CheckMainThread(env)) {
76 return;
77 }
78 auto it = timers_.find(timerId);
79 if (it == timers_.end()) {
80 return;
81 }
82 DisarmTimer(it->second);
83 }
84
TimerCallback(uv_timer_t * timer)85 void TimerModule::TimerCallback(uv_timer_t *timer)
86 {
87 auto *info = reinterpret_cast<TimerInfo *>(timer->data);
88 bool oneShotTimer = info->IsOneShotTimer();
89 if (oneShotTimer) {
90 DisarmTimer(timer);
91 }
92 ets_class cls = mainEtsEnv_->GetObjectClass(info->GetFuncObject());
93 ets_method invokeMethod = mainEtsEnv_->Getp_method(cls, "invoke", nullptr);
94 ark::ets::interop::js::JSNapiEnvScope scope(ark::ets::interop::js::InteropCtx::Current(), jsEnv_);
95 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
96 mainEtsEnv_->CallVoidMethod(info->GetFuncObject(), invokeMethod);
97 if (!oneShotTimer) {
98 uv_timer_again(timer);
99 }
100 }
101
DisarmTimer(uv_timer_t * timer)102 void TimerModule::DisarmTimer(uv_timer_t *timer)
103 {
104 auto *info = reinterpret_cast<TimerInfo *>(timer->data);
105 timers_.erase(info->GetId());
106 uv_timer_stop(timer);
107 uv_close(reinterpret_cast<uv_handle_t *>(timer), FreeTimer);
108 }
109
FreeTimer(uv_handle_t * timer)110 void TimerModule::FreeTimer(uv_handle_t *timer)
111 {
112 auto *info = reinterpret_cast<TimerInfo *>(timer->data);
113 mainEtsEnv_->DeleteGlobalRef(info->GetFuncObject());
114 delete info;
115 delete timer;
116 }
117
GetMainEventLoop()118 uv_loop_t *TimerModule::GetMainEventLoop()
119 {
120 #if defined(PANDA_TARGET_OHOS)
121 uv_loop_t *loop;
122 napi_get_uv_event_loop(ark::ets::interop::js::InteropCtx::Current()->GetJSEnv(), &loop);
123 return loop;
124 #else
125 return uv_default_loop();
126 #endif
127 }
128
GetTimerId()129 uint32_t TimerModule::GetTimerId()
130 {
131 return nextTimerId_++;
132 }
133
CheckMainThread(EtsEnv * env)134 bool TimerModule::CheckMainThread(EtsEnv *env)
135 {
136 if (gettid() == mainTid_) {
137 return true;
138 }
139 ets_class errorClass = env->FindClass("std/core/InternalError");
140 ASSERT(errorClass != nullptr);
141 env->ThrowErrorNew(errorClass, "The function must be called from the main coroutine");
142 return false;
143 }
144