/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "js_callback_manager.h" #include "hilog/log.h" #include "uv.h" #undef LOG_DOMAIN #define LOG_DOMAIN 0xD002D08 #undef LOG_TAG #define LOG_TAG "JS_CALLBACK_MANAGER" namespace OHOS { namespace HiviewDFX { namespace { constexpr int CONTEXT_INDEX = 0; constexpr int CALLBACK_FUNC_INDEX = 1; constexpr int RELEASE_FUNC_INDEX = 2; void DeleteWork(uv_work_t* work) { if (work != nullptr) { delete work; } } void RunCallback(CallbackContext* context, std::tuple<CallbackContext*, CALLBACK_FUNC, RELEASE_FUNC>& current) { uv_loop_t* loop = nullptr; napi_get_uv_event_loop(context->env, &loop); if (loop == nullptr) { HILOG_DEBUG(LOG_CORE, "failed to get uv_loop."); return; } context->callback = std::get<CALLBACK_FUNC_INDEX>(current); context->release = std::get<RELEASE_FUNC_INDEX>(current); uv_work_t* work = new(std::nothrow) uv_work_t(); if (work == nullptr) { HILOG_DEBUG(LOG_CORE, "uv_work new failed, no memory left."); return; } work->data = reinterpret_cast<void*>(context); uv_queue_work_with_qos( loop, work, [] (uv_work_t* work) { HILOG_DEBUG(LOG_CORE, "enter uv work callback."); }, [] (uv_work_t* work, int status) { if (work == nullptr || work->data == nullptr) { DeleteWork(work); return; } CallbackContext* context = reinterpret_cast<CallbackContext*>(work->data); if (context == nullptr || context->env == nullptr) { DeleteWork(work); return; } napi_handle_scope scope = nullptr; napi_open_handle_scope(context->env, &scope); if (scope == nullptr) { HILOG_DEBUG(LOG_CORE, "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); } }, 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<std::mutex> lock(managerMutex); jsCallbacks.emplace(std::make_tuple(context, callback, [this, release] (pid_t threadId) { if (release == nullptr) { this->ImmediateRun(true); } else { // Destructor of JsCallbackManager will be called in release callback, // so no need to call next callback in queue. release(threadId); } })); if (inCalling.load(std::memory_order_acquire)) { return; } } ImmediateRun(); } void JsCallbackManager::Release() { IsReleased = true; Clear(jsCallbacks); } void JsCallbackManager::ImmediateRun(bool needPop) { inCalling = true; std::tuple<CallbackContext*, CALLBACK_FUNC, RELEASE_FUNC> current; CallbackContext* context; { if (IsReleased.load(std::memory_order_acquire)) { return; } std::lock_guard<std::mutex> lock(managerMutex); if (needPop && !jsCallbacks.empty()) { jsCallbacks.pop(); } if (jsCallbacks.empty() && !IsReleased.load(std::memory_order_acquire)) { inCalling = false; return; } current = jsCallbacks.front(); context = std::get<CONTEXT_INDEX>(current); if (context == nullptr || IsReleased.load(std::memory_order_acquire)) { inCalling = false; return; } } if (IsReleased.load(std::memory_order_acquire)) { return; } RunCallback(context, current); } void JsCallbackManager::Clear(TaskQueue& tasks) { TaskQueue empty; swap(empty, tasks); } } // HiviewDFX } // OHOS