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