• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024-2025 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 "plugins/ets/runtime/interop_js/event_loop_module.h"
17 #include "runtime/coroutines/stackful_coroutine.h"
18 #include "plugins/ets/runtime/interop_js/interop_context.h"
19 #include "plugins/ets/runtime/interop_js/intrinsics_api_impl.h"
20 
21 // NOTE(konstanting, #23205): A workaround for the hybrid cmake build. Will be removed soon
22 // using a separate .cpp file with weak symbols.
23 #if defined(PANDA_JS_ETS_HYBRID_MODE_NEED_WEAK_SYMBOLS)
24 extern "C" napi_status __attribute__((weak))  // CC-OFF(G.FMT.10) project code style
napi_get_uv_event_loop(napi_env env,struct uv_loop_s ** loop)25 napi_get_uv_event_loop([[maybe_unused]] napi_env env, [[maybe_unused]] struct uv_loop_s **loop)
26 {
27     // NOTE: Empty stub. Needed only for the corner case with verifier/aot in the hybrid cmake build
28     INTEROP_LOG(ERROR) << "napi_add_env_cleanup_hook is implemented in OHOS since 4.1.0, please update" << std::endl;
29     return napi_ok;
30 }
31 #endif /* PANDA_JS_ETS_HYBRID_MODE_NEED_WEAK_SYMBOLS */
32 
33 namespace ark::ets::interop::js {
34 
35 /*static*/
GetEventLoop()36 uv_loop_t *EventLoop::GetEventLoop()
37 {
38     uv_loop_t *loop = nullptr;
39 #if defined(PANDA_TARGET_OHOS) || defined(PANDA_JS_ETS_HYBRID_MODE)
40     [[maybe_unused]] auto nstatus = napi_get_uv_event_loop(InteropCtx::Current()->GetJSEnv(), &loop);
41     ASSERT(nstatus == napi_ok);
42 #else
43     loop = uv_default_loop();
44 #endif
45     return loop;
46 }
47 
RunEventLoop(EventLoopRunMode mode)48 void EventLoop::RunEventLoop(EventLoopRunMode mode)
49 {
50     ark::ets::interop::js::InteropCtx::Current(EtsCoroutine::GetCurrent())->UpdateInteropStackInfoIfNeeded();
51     auto *loop = GetEventLoop();
52     switch (mode) {
53         case EventLoopRunMode::RUN_DEFAULT:
54             uv_run(loop, UV_RUN_DEFAULT);
55             break;
56         case EventLoopRunMode::RUN_ONCE:
57             uv_run(loop, UV_RUN_ONCE);
58             break;
59         case EventLoopRunMode::RUN_NOWAIT:
60             uv_run(loop, UV_RUN_NOWAIT);
61             break;
62         default:
63             UNREACHABLE();
64     };
65 }
66 
WalkEventLoop(WalkEventLoopCallback & callback,void * args)67 void EventLoop::WalkEventLoop(WalkEventLoopCallback &callback, void *args)
68 {
69     auto *loop = GetEventLoop();
70 
71     struct CallbackStruct {
72         std::function<void(void *, void *)> *callback;
73         void *args;
74     };
75 
76     CallbackStruct parsedArgs {&callback, args};
77 
78     auto uvCalback = []([[maybe_unused]] uv_handle_t *handle, void *rawArgs) {
79         auto callbackStruct = reinterpret_cast<CallbackStruct *>(rawArgs);
80         (*(callbackStruct->callback))(reinterpret_cast<void *>(handle), callbackStruct->args);
81     };
82     uv_walk(loop, uvCalback, &parsedArgs);
83 }
84 
EventLoopCallbackPoster()85 EventLoopCallbackPoster::EventLoopCallbackPoster()
86 {
87     auto loop = EventLoop::GetEventLoop();
88     // These resources will be deleted in the event loop callback after Runtime destruction,
89     // so we need to use a standard allocator
90     async_ = new uv_async_t();
91     callbackQueue_ = new ThreadSafeCallbackQueue();
92     [[maybe_unused]] auto uvstatus = uv_async_init(loop, async_, AsyncEventToExecuteCallbacks);
93     ASSERT(uvstatus == 0);
94     async_->data = callbackQueue_;
95 }
96 
~EventLoopCallbackPoster()97 EventLoopCallbackPoster::~EventLoopCallbackPoster()
98 {
99     ASSERT(async_ != nullptr);
100     auto destroyCb = [async = this->async_]() {
101         auto deleter = [](uv_handle_t *handle) {
102             auto *poster = reinterpret_cast<ThreadSafeCallbackQueue *>(handle->data);
103             delete poster;
104             delete handle;
105         };
106         uv_close(reinterpret_cast<uv_handle_t *>(async), deleter);
107     };
108     if (NeedDestroyInPlace()) {
109         destroyCb();
110         return;
111     }
112     PostToEventLoop(std::move(destroyCb));
113 }
114 
PostImpl(WrappedCallback && callback)115 void EventLoopCallbackPoster::PostImpl(WrappedCallback &&callback)
116 {
117     ASSERT(callback != nullptr);
118     PostToEventLoop(std::move(callback));
119 }
120 
PostToEventLoop(WrappedCallback && callback)121 void EventLoopCallbackPoster::PostToEventLoop(WrappedCallback &&callback)
122 {
123     ASSERT(async_ != nullptr);
124     callbackQueue_->PushCallback(std::move(callback), async_);
125 }
126 
127 /*static*/
AsyncEventToExecuteCallbacks(uv_async_t * async)128 void EventLoopCallbackPoster::AsyncEventToExecuteCallbacks(uv_async_t *async)
129 {
130     auto *callbackQueue = reinterpret_cast<ThreadSafeCallbackQueue *>(async->data);
131     ASSERT(callbackQueue != nullptr);
132     auto *coro = EtsCoroutine::GetCurrent();
133     InteropCodeScope<false> codeScope(coro, __PRETTY_FUNCTION__);
134     callbackQueue->ExecuteAllCallbacks();
135 }
136 
PushCallback(WrappedCallback && callback,uv_async_t * async)137 void EventLoopCallbackPoster::ThreadSafeCallbackQueue::PushCallback(WrappedCallback &&callback, uv_async_t *async)
138 {
139     {
140         os::memory::LockHolder lh(lock_);
141         callbackQueue_.push(std::move(callback));
142     }
143     ASSERT(async != nullptr);
144     [[maybe_unused]] auto uvstatus = uv_async_send(async);
145     ASSERT(uvstatus == 0);
146 }
147 
ExecuteAllCallbacks()148 void EventLoopCallbackPoster::ThreadSafeCallbackQueue::ExecuteAllCallbacks()
149 {
150     while (!IsEmpty()) {
151         auto localQueue = CallbackQueue {};
152         {
153             os::memory::LockHolder lh(lock_);
154             std::swap(localQueue, callbackQueue_);
155         }
156         while (!localQueue.empty()) {
157             auto callback = std::move(localQueue.front());
158             callback();
159             localQueue.pop();
160         }
161     }
162 }
163 
IsEmpty()164 bool EventLoopCallbackPoster::ThreadSafeCallbackQueue::IsEmpty()
165 {
166     os::memory::LockHolder lh(lock_);
167     return callbackQueue_.empty();
168 }
169 
CreatePoster()170 PandaUniquePtr<CallbackPoster> EventLoopCallbackPosterFactoryImpl::CreatePoster()
171 {
172     auto *coro = Coroutine::GetCurrent();
173     ASSERT(coro != nullptr);
174     [[maybe_unused]] auto *w = coro->GetContext<StackfulCoroutineContext>()->GetWorker();
175     ASSERT(w->IsMainWorker() || w->InExclusiveMode());
176     auto poster = MakePandaUnique<EventLoopCallbackPoster>();
177     ASSERT(poster != nullptr);
178     return poster;
179 }
180 
181 }  // namespace ark::ets::interop::js
182