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 #include "native_event.h"
16 #include <string>
17
18 #ifdef ENABLE_CONTAINER_SCOPE_SEND_EVENT
19 #include "core/common/container_scope.h"
20 #endif
21 #include "native_api_internal.h"
22 #if defined(ENABLE_EVENT_HANDLER)
23 #include "event_handler.h"
24 #endif
25 #ifdef ENABLE_HITRACE
26 #include "hitrace_meter.h"
27 #endif
28 #include "native_engine.h"
29 #include "utils/log.h"
30 #if defined(ENABLE_EVENT_HANDLER)
31 using namespace OHOS::AppExecFwk;
32 #endif
33 #ifdef ENABLE_CONTAINER_SCOPE_SEND_EVENT
34 using OHOS::Ace::ContainerScope;
35 #endif
36
37 static constexpr uint64_t INVALID_EVENT_ID = std::numeric_limits<uint64_t>::max();
38 static constexpr const char DEFAULT_NAME[] = "defaultName";
39
40 // trace and log
41 class TraceLogClass {
42 #ifdef ENABLE_HITRACE
43 uint64_t traceLable = HITRACE_TAG_ACE;
44 #endif
45 std::string value;
46 public:
TraceLogClass(std::string value)47 explicit TraceLogClass(std::string value) : value(value)
48 {
49 #ifdef ENABLE_HITRACE
50 StartTrace(traceLable, value);
51 #endif
52 HILOG_DEBUG("log start{%{public}s}", value.c_str());
53 }
~TraceLogClass()54 ~TraceLogClass()
55 {
56 #ifdef ENABLE_HITRACE
57 FinishTrace(traceLable);
58 #endif
59 HILOG_DEBUG("log end{%{public}s}", value.c_str());
60 }
61 };
62
63 // API
napi_send_event(napi_env env,const std::function<void ()> & cb,napi_event_priority priority)64 NAPI_EXTERN napi_status napi_send_event(napi_env env, const std::function<void()>& cb, napi_event_priority priority)
65 {
66 CHECK_ENV(env);
67 if (cb == nullptr) {
68 HILOG_ERROR("invalid function is nullptr");
69 return napi_status::napi_invalid_arg;
70 }
71 if (priority < napi_eprio_vip || priority > napi_eprio_idle) {
72 HILOG_ERROR("invalid priority %{public}d", static_cast<int32_t>(priority));
73 return napi_status::napi_invalid_arg;
74 }
75 NativeEngine *eng = reinterpret_cast<NativeEngine *>(env);
76 NativeEngine::GetAliveEngineMutex().lock();
77 if (!NativeEngine::IsAliveLocked(eng)) {
78 NativeEngine::GetAliveEngineMutex().unlock();
79 HILOG_ERROR("call NativeEngine not alive");
80 return napi_status::napi_closing;
81 }
82 std::shared_lock<std::shared_mutex> readLock(eng->GetEventMutex());
83 NativeEngine::GetAliveEngineMutex().unlock();
84
85 // Prevent cb from becoming a floating pointer
86 auto realCb = [cbIn = cb](void *data) {
87 if (cbIn != nullptr) {
88 HILOG_DEBUG("napi_send_event callBack called");
89 cbIn();
90 } else {
91 HILOG_ERROR("napi_send_event callBack is null");
92 }
93 };
94 uint64_t handleId = 0;
95
96 if (!eng->GetDefaultFunc()) {
97 HILOG_ERROR("default function is nullptr!");
98 return napi_status::napi_generic_failure;
99 }
100 auto safeAsyncWork = reinterpret_cast<NativeEvent*>(eng->GetDefaultFunc());
101 return safeAsyncWork->SendCancelableEvent(realCb, nullptr, priority, DEFAULT_NAME, &handleId);
102 }
103
napi_send_cancelable_event(napi_env env,const std::function<void (void *)> & cb,void * data,napi_event_priority priority,uint64_t * handleId,const char * name)104 NAPI_EXTERN napi_status napi_send_cancelable_event(napi_env env,
105 const std::function<void(void*)>& cb,
106 void* data,
107 napi_event_priority priority,
108 uint64_t* handleId,
109 const char* name)
110 {
111 CHECK_ENV(env);
112 if (cb == nullptr) {
113 HILOG_ERROR("invalid function is nullptr");
114 return napi_status::napi_invalid_arg;
115 }
116 if (priority < napi_eprio_vip || priority > napi_eprio_idle) {
117 HILOG_ERROR("invalid priority %{public}d", static_cast<int32_t>(priority));
118 return napi_status::napi_invalid_arg;
119 }
120 if (handleId == nullptr) {
121 HILOG_ERROR("invalid handleId");
122 return napi_status::napi_invalid_arg;
123 }
124 NativeEngine *eng = reinterpret_cast<NativeEngine *>(env);
125
126 NativeEngine::GetAliveEngineMutex().lock();
127 if (!NativeEngine::IsAliveLocked(eng)) {
128 NativeEngine::GetAliveEngineMutex().unlock();
129 HILOG_ERROR("call NativeEngine not alive");
130 return napi_status::napi_closing;
131 }
132 std::shared_lock<std::shared_mutex> readLock(eng->GetEventMutex());
133 NativeEngine::GetAliveEngineMutex().unlock();
134
135 if (!eng->GetDefaultFunc()) {
136 HILOG_ERROR("default function is nullptr!");
137 return napi_status::napi_generic_failure;
138 }
139 auto safeAsyncWork = reinterpret_cast<NativeEvent*>(eng->GetDefaultFunc());
140 return safeAsyncWork->SendCancelableEvent(cb, data, priority,
141 ((name == nullptr) ? DEFAULT_NAME: name), handleId);
142 }
143
napi_cancel_event(napi_env env,uint64_t handleId,const char * name)144 NAPI_EXTERN napi_status napi_cancel_event(napi_env env, uint64_t handleId, const char* name)
145 {
146 CHECK_ENV(env);
147 if (handleId == 0) {
148 HILOG_ERROR("invalid handleId 0");
149 return napi_status::napi_invalid_arg;
150 }
151 NativeEngine *eng = reinterpret_cast<NativeEngine *>(env);
152
153 NativeEngine::GetAliveEngineMutex().lock();
154 if (!NativeEngine::IsAliveLocked(eng)) {
155 NativeEngine::GetAliveEngineMutex().unlock();
156 HILOG_ERROR("call NativeEngine not alive");
157 return napi_status::napi_closing;
158 }
159 std::shared_lock<std::shared_mutex> readLock(eng->GetEventMutex());
160 NativeEngine::GetAliveEngineMutex().unlock();
161
162 if (!eng->GetDefaultFunc()) {
163 HILOG_ERROR("default function is nullptr!");
164 return napi_status::napi_generic_failure;
165 }
166 auto safeAsyncWork = reinterpret_cast<NativeEvent*>(eng->GetDefaultFunc());
167 return safeAsyncWork->CancelEvent(((name == nullptr) ? DEFAULT_NAME: name), handleId);
168 }
169
170 // static method
ThreadSafeCallback(napi_env env,napi_value jsCallback,void * context,void * data)171 static void ThreadSafeCallback(napi_env env, napi_value jsCallback, void* context, void* data)
172 {
173 if (data != nullptr) {
174 CallbackWrapper *cbw = static_cast<CallbackWrapper *>(data);
175 uint64_t expected = cbw->handleId.load(std::memory_order_acquire);
176 if (expected != INVALID_EVENT_ID &&
177 cbw->handleId.compare_exchange_strong(expected, INVALID_EVENT_ID,
178 std::memory_order_acq_rel, std::memory_order_relaxed)) {
179 #ifdef ENABLE_HITRACE
180 StartTrace(HITRACE_TAG_ACE, "ThreadSafeCallback excute");
181 #endif
182 cbw->cb();
183 #ifdef ENABLE_HITRACE
184 FinishTrace(HITRACE_TAG_ACE);
185 #endif
186 }
187 delete cbw;
188 }
189 }
190
CreateDefaultFunction(NativeEngine * eng,napi_threadsafe_function & defaultFunc,std::shared_mutex & eventMutex)191 void NativeEvent::CreateDefaultFunction(NativeEngine* eng, napi_threadsafe_function &defaultFunc,
192 std::shared_mutex &eventMutex)
193 {
194 std::unique_lock<std::shared_mutex> writeLock(eventMutex);
195 if (defaultFunc) {
196 return;
197 }
198 napi_env env = reinterpret_cast<napi_env>(eng);
199 napi_value resourceName = nullptr;
200 napi_create_string_utf8(env, "call_default_threadsafe_function", NAPI_AUTO_LENGTH, &resourceName);
201
202 auto callJsCallback = reinterpret_cast<NativeThreadSafeFunctionCallJs>(ThreadSafeCallback);
203 auto safeAsyncWork = new NativeEvent(eng, nullptr, nullptr, resourceName, 0, 1,
204 nullptr, nullptr, nullptr, callJsCallback);
205 auto ret = safeAsyncWork->Init();
206 if (ret) {
207 defaultFunc = reinterpret_cast<napi_threadsafe_function>(safeAsyncWork);
208 } else {
209 delete safeAsyncWork;
210 }
211 }
212
DestoryDefaultFunction(bool release,napi_threadsafe_function & defaultFunc,std::shared_mutex & eventMutex)213 void NativeEvent::DestoryDefaultFunction(bool release, napi_threadsafe_function &defaultFunc,
214 std::shared_mutex &eventMutex)
215 {
216 napi_threadsafe_function toReleaseFunc = nullptr;
217 {
218 std::unique_lock<std::shared_mutex> writeLock(eventMutex);
219 if (!defaultFunc) {
220 return;
221 }
222 toReleaseFunc = defaultFunc;
223 defaultFunc = nullptr;
224 }
225
226 if (release) {
227 napi_release_threadsafe_function(toReleaseFunc, napi_tsfn_abort);
228 } else {
229 NativeEvent* work = reinterpret_cast<NativeEvent*>(toReleaseFunc);
230 delete work; // only free mem due to uv_loop is invalid
231 }
232 }
233
234 //NativeEvent method
NativeEvent(NativeEngine * engine,napi_value func,napi_value asyncResource,napi_value asyncResourceName,size_t maxQueueSize,size_t threadCount,void * finalizeData,NativeFinalize finalizeCallback,void * context,NativeThreadSafeFunctionCallJs callJsCallback)235 NativeEvent::NativeEvent(NativeEngine* engine,
236 napi_value func,
237 napi_value asyncResource,
238 napi_value asyncResourceName,
239 size_t maxQueueSize,
240 size_t threadCount,
241 void* finalizeData,
242 NativeFinalize finalizeCallback,
243 void* context,
244 NativeThreadSafeFunctionCallJs callJsCallback) : NativeSafeAsyncWork(engine,
245 func, asyncResource, asyncResourceName, maxQueueSize, threadCount,
246 finalizeData, finalizeCallback, context, callJsCallback)
247 {
248 #if defined(ENABLE_EVENT_HANDLER)
249 if (runner_ != nullptr) {
250 eventHandler_ = std::make_shared<EventHandler>(runner_);
251 }
252 #endif
253 }
254
Init()255 bool NativeEvent::Init()
256 {
257 sequence_.store(1, std::memory_order_relaxed);
258 return NativeSafeAsyncWork::Init();
259 }
260
SendCancelableEvent(const std::function<void (void *)> & callback,void * data,int32_t priority,const char * name,uint64_t * handleId)261 napi_status NativeEvent::SendCancelableEvent(const std::function<void(void*)> &callback,
262 void* data,
263 int32_t priority,
264 const char* name,
265 uint64_t* handleId)
266 {
267 uint64_t eventId = GenerateUniqueID();
268 #ifdef ENABLE_CONTAINER_SCOPE_SEND_EVENT
269 int32_t containerScopeId = ContainerScope::CurrentId();
270 std::function<void()> task = [eng = engine_, callback, data, eventId, containerScopeId]() {
271 ContainerScope containerScope(containerScopeId);
272 #else
273 std::function<void()> task = [eng = engine_, callback, data, eventId]() {
274 #endif
275 auto tcin = TraceLogClass("Cancelable Event callback:handleId:" + std::to_string(eventId));
276 auto vm = eng->GetEcmaVm();
277 panda::LocalScope scope(vm);
278 callback(data);
279 };
280
281 napi_status sentEventRes = SendEventByEventHandler(task, eventId, priority, name, handleId);
282 if (sentEventRes != napi_status::napi_invalid_arg) {
283 return sentEventRes;
284 }
285
286 return SendEventByUv(task, eventId, name, handleId);
287 }
288
289 napi_status NativeEvent::SendEventByEventHandler(const std::function<void()> &task, uint64_t eventId,
290 int32_t priority, const char* name, uint64_t* handleId)
291 {
292 #ifdef ENABLE_EVENT_HANDLER
293 if (!eventHandler_) {
294 // Internal temporary code
295 return napi_status::napi_invalid_arg;
296 }
297 bool postRes = eventHandler_->PostTask(task,
298 std::string(name) + std::to_string(eventId),
299 0,
300 static_cast<EventQueue::Priority>(priority),
301 {});
302 std::string res = (postRes ? "ok" : "fail");
303 auto evt = TraceLogClass(
304 "eventHandler Send task: " + std::string(name) +
305 " | handleId: " + std::to_string(eventId) +
306 " | postRes: " + res
307 );
308 if (postRes) {
309 *handleId = eventId;
310 return napi_status::napi_ok;
311 }
312 *handleId = 0;
313 HILOG_ERROR("PostTask fail %{public}s", std::to_string(eventId).c_str());
314 return napi_status::napi_generic_failure;
315 #endif
316 // Internal temporary code
317 return napi_status::napi_invalid_arg;
318 }
319
320 napi_status NativeEvent::SendEventByUv(const std::function<void()> &task, uint64_t eventId,
321 const char* name, uint64_t* handleId)
322 {
323 CallbackWrapper* cbw = new (std::nothrow) CallbackWrapper();
324 if (!cbw) {
325 HILOG_ERROR("malloc failed!");
326 return napi_status::napi_generic_failure;
327 }
328 engine_->IncreaseWaitingRequestCounter();
329 auto incCountTask = [eng = engine_, task]() {
330 eng->DecreaseWaitingRequestCounter();
331 task();
332 };
333 cbw->cb = incCountTask;
334 cbw->handleId.store(eventId, std::memory_order_release);
335 napi_status status = SendConvertStatus2NapiStatus(reinterpret_cast<void *>(cbw), NATIVE_TSFUNC_NONBLOCKING);
336 *handleId = eventId;
337 std::string res = (status == napi_status::napi_ok? "ok": "fail");
338 auto uvt = TraceLogClass(
339 "uv Send task:" + std::string(name) + " | handleId:" + std::to_string(eventId) + " | postRes: " + res);
340 if (status != napi_status::napi_ok) {
341 HILOG_ERROR("send event failed(%{public}d)", status);
342 delete cbw;
343 *handleId = 0;
344 engine_->DecreaseWaitingRequestCounter();
345 }
346 return status;
347 }
348
349 napi_status NativeEvent::CancelEvent(const char* name, uint64_t handleId)
350 {
351 auto tc = TraceLogClass("Cancel Event:" + std::string(name) + "|handleId:" + std::to_string(handleId));
352 if (handleId == 0) {
353 HILOG_ERROR("handleId = 0");
354 return napi_status::napi_invalid_arg;
355 }
356 #ifdef ENABLE_EVENT_HANDLER
357 if (eventHandler_) {
358 auto evt = TraceLogClass("eventHandler remove task");
359 eventHandler_->RemoveTask(std::string(name) + std::to_string(handleId));
360 return napi_status::napi_ok;
361 }
362 #endif
363 auto code = UvCancelEvent(handleId);
364 std::string res = (code == SafeAsyncCode::SAFE_ASYNC_OK ? "ok" : "fail");
365 auto uvt = TraceLogClass("uv remove task res:" + res);
366 napi_status status = napi_status::napi_ok;
367 if (code == SafeAsyncCode::SAFE_ASYNC_FAILED || code == SafeAsyncCode::SAFE_ASYNC_CLOSED) {
368 status = napi_status::napi_generic_failure;
369 }
370 return status;
371 }
372
373 SafeAsyncCode NativeEvent::UvCancelEvent(uint64_t handleId)
374 {
375 std::unique_lock<std::mutex> lock(mutex_);
376 if (status_ == SafeAsyncStatus::SAFE_ASYNC_STATUS_CLOSED ||
377 status_ == SafeAsyncStatus::SAFE_ASYNC_STATUS_CLOSING) {
378 HILOG_WARN("Do not cancel, thread is closed!");
379 return SafeAsyncCode::SAFE_ASYNC_CLOSED;
380 }
381 if (callJsCallback_ == nullptr) {
382 HILOG_ERROR("callJsCallback is nullptr");
383 return SafeAsyncCode::SAFE_ASYNC_FAILED;
384 }
385
386 for (const auto ¤t : queue_) {
387 if (current == nullptr) {
388 continue;
389 }
390 CallbackWrapper* cbw = reinterpret_cast<CallbackWrapper *>(current);
391 if (cbw->handleId.load(std::memory_order_relaxed) != handleId) {
392 continue;
393 }
394 if (cbw->handleId.compare_exchange_strong(handleId, INVALID_EVENT_ID,
395 std::memory_order_acq_rel, std::memory_order_relaxed)) {
396 engine_->DecreaseWaitingRequestCounter();
397 return SafeAsyncCode::SAFE_ASYNC_OK;
398 }
399 HILOG_WARN("UvCancelEvent false %{public}s", std::to_string(handleId).c_str());
400 return SafeAsyncCode::SAFE_ASYNC_FAILED;
401 }
402 return SafeAsyncCode::SAFE_ASYNC_FAILED;
403 }
404
405 napi_status NativeEvent::SendConvertStatus2NapiStatus(void* data, NativeThreadSafeFunctionCallMode mode)
406 {
407 auto code = Send(data, mode);
408 napi_status status = napi_status::napi_ok;
409 switch (code) {
410 case SafeAsyncCode::SAFE_ASYNC_OK:
411 status = napi_status::napi_ok;
412 break;
413 case SafeAsyncCode::SAFE_ASYNC_QUEUE_FULL:
414 status = napi_status::napi_queue_full;
415 break;
416 case SafeAsyncCode::SAFE_ASYNC_INVALID_ARGS:
417 status = napi_status::napi_invalid_arg;
418 break;
419 case SafeAsyncCode::SAFE_ASYNC_CLOSED:
420 status = napi_status::napi_closing;
421 break;
422 case SafeAsyncCode::SAFE_ASYNC_FAILED:
423 status = napi_status::napi_generic_failure;
424 break;
425 default:
426 HILOG_ERROR("this branch is unreachable, code is %{public}d", code);
427 status = napi_status::napi_generic_failure;
428 break;
429 }
430 return status;
431 }
432
433 uint64_t NativeEvent::GenerateUniqueID()
434 {
435 uint64_t currentSequence = sequence_.fetch_add(1, std::memory_order_relaxed);
436 // if sequence_ max, store 1
437 if (currentSequence == INVALID_EVENT_ID - 1) {
438 sequence_.store(1, std::memory_order_relaxed);
439 return 1;
440 } else {
441 return currentSequence;
442 }
443 }