• 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 #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 &current : 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 }