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