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