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