From f5c4afc8d2373668bf2992e183e3ae9ce4c65608 Mon Sep 17 00:00:00 2001 From: xuyong Date: Fri, 21 Jul 2023 14:53:12 +0800 Subject: [PATCH] Optimize the logic of handling sysevent js callbacks Signed-off-by: xuyong --- .../kits/napi/include/js_callback_manager.h | 24 +++- .../kits/napi/include/napi_callback_context.h | 3 + .../js/kits/napi/src/js_callback_manager.cpp | 123 ++++++++++-------- .../common/napi/napi_hisysevent.test.js | 8 +- 4 files changed, 96 insertions(+), 62 deletions(-) diff --git a/interfaces/js/kits/napi/include/js_callback_manager.h b/interfaces/js/kits/napi/include/js_callback_manager.h index e40bc10..0131949 100644 --- a/interfaces/js/kits/napi/include/js_callback_manager.h +++ b/interfaces/js/kits/napi/include/js_callback_manager.h @@ -17,6 +17,8 @@ #define JS_CALLBACK_MANAGER_H #include +#include +#include #include #include #include @@ -27,10 +29,22 @@ namespace OHOS { namespace HiviewDFX { -using TaskQueue = std::queue>; +using Task = std::tuple; +using TaskQueue = std::queue; +struct AsyncInfo { + bool inCalling; + bool isReleased; + std::mutex mtx; + std::condition_variable cv; +}; + class JsCallbackManager final { public: - explicit JsCallbackManager() {} + explicit JsCallbackManager() + { + asyncInfo = std::make_shared(); + } + public: void Add(CallbackContext* context, CALLBACK_FUNC callback, RELEASE_FUNC release = nullptr); void Release(); @@ -38,12 +52,12 @@ public: private: void ImmediateRun(bool needPop = false); void Clear(TaskQueue& tasks); + void RunCallback(CallbackContext* context, Task& current); private: - std::atomic inCalling = false; - std::atomic IsReleased = false; - std::mutex managerMutex; + std::shared_ptr asyncInfo = nullptr; TaskQueue jsCallbacks; + std::mutex taskQueueOptMtx; }; } // namespace HiviewDFX } // namespace OHOS diff --git a/interfaces/js/kits/napi/include/napi_callback_context.h b/interfaces/js/kits/napi/include/napi_callback_context.h index 6920f7a..924642b 100644 --- a/interfaces/js/kits/napi/include/napi_callback_context.h +++ b/interfaces/js/kits/napi/include/napi_callback_context.h @@ -17,6 +17,7 @@ #define NAPI_CALLBACK_CONTEXT_H #include +#include #include #include "napi/native_api.h" @@ -26,12 +27,14 @@ namespace OHOS { namespace HiviewDFX { using CALLBACK_FUNC = std::function; using RELEASE_FUNC = std::function; +struct AsyncInfo; using CallbackContext = struct CallbackContext { napi_env env = nullptr; napi_ref ref = nullptr; CALLBACK_FUNC callback; RELEASE_FUNC release; pid_t threadId; + std::shared_ptr asyncInfo; }; } // namespace HiviewDFX } // namespace OHOS diff --git a/interfaces/js/kits/napi/src/js_callback_manager.cpp b/interfaces/js/kits/napi/src/js_callback_manager.cpp index 433ff8f..f72b255 100644 --- a/interfaces/js/kits/napi/src/js_callback_manager.cpp +++ b/interfaces/js/kits/napi/src/js_callback_manager.cpp @@ -32,19 +32,63 @@ void DeleteWork(uv_work_t* work) } } -void RunCallback(CallbackContext* context, std::tuple& current) +void HandleJsCallback(uv_work_t* work, int status) +{ + if (work == nullptr || work->data == nullptr) { + DeleteWork(work); + return; + } + CallbackContext* context = reinterpret_cast(work->data); + if (context == nullptr || (context->asyncInfo == nullptr) || (context->env == nullptr)) { + DeleteWork(work); + return; + } + { + auto asyncInfo = context->asyncInfo; + std::unique_lock lock(asyncInfo->mtx); + asyncInfo->cv.wait(lock, [&asyncInfo] () { + return !asyncInfo->isReleased && asyncInfo->inCalling; + }); + napi_handle_scope scope = nullptr; + napi_open_handle_scope(context->env, &scope); + if (scope == nullptr) { + HiLog::Debug(LABEL, "napi scope is null."); + DeleteWork(work); + asyncInfo->inCalling = false; + asyncInfo->cv.notify_one(); + return; + } + if (context->callback != nullptr) { + context->callback(context->env, context->ref, context->threadId); + } + napi_close_handle_scope(context->env, scope); + DeleteWork(work); + asyncInfo->inCalling = false; + asyncInfo->cv.notify_one(); + } + if (context->release != nullptr) { + context->release(context->threadId); + } +} +} + +void JsCallbackManager::RunCallback(CallbackContext* context, std::tuple& current) { uv_loop_t* loop = nullptr; napi_get_uv_event_loop(context->env, &loop); if (loop == nullptr) { HiLog::Debug(LABEL, "failed to get uv_loop."); + asyncInfo->inCalling = false; return; } context->callback = std::get(current); context->release = std::get(current); + context->asyncInfo = asyncInfo; uv_work_t* work = new(std::nothrow) uv_work_t(); if (work == nullptr) { HiLog::Debug(LABEL, "uv_work new failed, no memory left."); + asyncInfo->inCalling = false; return; } work->data = reinterpret_cast(context); @@ -53,46 +97,15 @@ void RunCallback(CallbackContext* context, std::tupledata == nullptr) { - DeleteWork(work); - return; - } - CallbackContext* context = reinterpret_cast(work->data); - if (context == nullptr) { - DeleteWork(work); - return; - } - napi_handle_scope scope = nullptr; - if (context->env == nullptr) { - DeleteWork(work); - return; - } - napi_open_handle_scope(context->env, &scope); - if (scope == nullptr) { - HiLog::Debug(LABEL, "napi scope is null."); - DeleteWork(work); - return; - } - if (context->callback != nullptr) { - context->callback(context->env, context->ref, context->threadId); - } - napi_close_handle_scope(context->env, scope); - DeleteWork(work); - if (context->release != nullptr) { - context->release(context->threadId); - } + HandleJsCallback(work, status); }, uv_qos_default); } -} void JsCallbackManager::Add(CallbackContext* context, CALLBACK_FUNC callback, RELEASE_FUNC release) { { - if (IsReleased.load(std::memory_order_acquire)) { - return; - } - std::lock_guard lock(managerMutex); + std::lock_guard lock(taskQueueOptMtx); jsCallbacks.emplace(std::make_tuple(context, callback, [this, release] (pid_t threadId) { if (release == nullptr) { this->ImmediateRun(true); @@ -102,52 +115,56 @@ void JsCallbackManager::Add(CallbackContext* context, CALLBACK_FUNC callback, release(threadId); } })); - if (inCalling.load(std::memory_order_acquire)) { - return; - } } ImmediateRun(); } void JsCallbackManager::Release() { - IsReleased = true; + std::unique_lock lock(asyncInfo->mtx); + asyncInfo->cv.wait(lock, [this] () { + return !this->asyncInfo->isReleased && !this->asyncInfo->inCalling; + }); + asyncInfo->isReleased = true; Clear(jsCallbacks); + asyncInfo->cv.notify_one(); } void JsCallbackManager::ImmediateRun(bool needPop) { - inCalling = true; - std::tuple current; - CallbackContext* context; + Task current; { - if (IsReleased.load(std::memory_order_acquire)) { + std::lock_guard lock(taskQueueOptMtx); + if (jsCallbacks.empty()) { + asyncInfo->inCalling = false; return; } - std::lock_guard lock(managerMutex); - if (needPop && !jsCallbacks.empty()) { + if (needPop) { jsCallbacks.pop(); } - if (jsCallbacks.empty() && !IsReleased.load(std::memory_order_acquire)) { - inCalling = false; + if (!jsCallbacks.empty()) { + current = jsCallbacks.front(); + } + } + CallbackContext* context = std::get(current); + { + std::lock_guard lock(asyncInfo->mtx); + if (this->asyncInfo->isReleased || this->asyncInfo->inCalling) { return; } - current = jsCallbacks.front(); - context = std::get(current); - if (context == nullptr || IsReleased.load(std::memory_order_acquire)) { - inCalling = false; + asyncInfo->inCalling = true; + if (context == nullptr) { + asyncInfo->inCalling = false; return; } } - if (IsReleased.load(std::memory_order_acquire)) { - return; - } RunCallback(context, current); } void JsCallbackManager::Clear(TaskQueue& tasks) { TaskQueue empty; + std::lock_guard lock(taskQueueOptMtx); swap(empty, tasks); } } // HiviewDFX diff --git a/test/unittest/common/napi/napi_hisysevent.test.js b/test/unittest/common/napi/napi_hisysevent.test.js index 4b5364d..c496fec 100644 --- a/test/unittest/common/napi/napi_hisysevent.test.js +++ b/test/unittest/common/napi/napi_hisysevent.test.js @@ -349,10 +349,10 @@ describe('hiSysEventJsUnitTest', function () { if (infos instanceof Array) { for (let i = 0; i < infos.length; i++) { let item = infos[i]; - console.info(`hiSysEventJsUnitTest005: domain is ${item.domain}, name is ${item.name}, eventType is ${item.eventType}`) + console.info(`hiSysEventJsUnitTest006: domain is ${item.domain}, name is ${item.name}, eventType is ${item.eventType}`) if (item.params instanceof Object) { for (const key in item.params) { - console.info(`hiSysEventJsUnitTest005: ${key}: ${item.params[key]}`) + console.info(`hiSysEventJsUnitTest006: ${key}: ${item.params[key]}`) } } } @@ -424,10 +424,10 @@ describe('hiSysEventJsUnitTest', function () { if (infos instanceof Array) { for (let i = 0; i < infos.length; i++) { let item = infos[i]; - console.info(`hiSysEventJsUnitTest005: domain is ${item.domain}, name is ${item.name}, eventType is ${item.eventType}`) + console.info(`hiSysEventJsUnitTest007: domain is ${item.domain}, name is ${item.name}, eventType is ${item.eventType}`) if (item.params instanceof Object) { for (const key in item.params) { - console.info(`hiSysEventJsUnitTest005: ${key}: ${item.params[key]}`) + console.info(`hiSysEventJsUnitTest007: ${key}: ${item.params[key]}`) } } } -- Gitee