14d6c458bSopenharmony_ci/*
24d6c458bSopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd.
34d6c458bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44d6c458bSopenharmony_ci * you may not use this file except in compliance with the License.
54d6c458bSopenharmony_ci * You may obtain a copy of the License at
64d6c458bSopenharmony_ci *
74d6c458bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84d6c458bSopenharmony_ci *
94d6c458bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104d6c458bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114d6c458bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124d6c458bSopenharmony_ci * See the License for the specific language governing permissions and
134d6c458bSopenharmony_ci * limitations under the License.
144d6c458bSopenharmony_ci */
154d6c458bSopenharmony_ci
164d6c458bSopenharmony_ci#include "timer.h"
174d6c458bSopenharmony_ci
184d6c458bSopenharmony_ci#include "native_engine/native_engine.h"
194d6c458bSopenharmony_ci#include "tools/log.h"
204d6c458bSopenharmony_ci
214d6c458bSopenharmony_ci#ifdef ENABLE_CONTAINER_SCOPE
224d6c458bSopenharmony_ciusing OHOS::Ace::ContainerScope;
234d6c458bSopenharmony_ci#endif
244d6c458bSopenharmony_ci
254d6c458bSopenharmony_cinamespace OHOS::JsSysModule {
264d6c458bSopenharmony_ciusing namespace Commonlibrary::Concurrent::Common;
274d6c458bSopenharmony_ci
284d6c458bSopenharmony_ciuint32_t Timer::timeCallbackId = 0;
294d6c458bSopenharmony_ciuint32_t deleteTimerCount = 0;
304d6c458bSopenharmony_cistd::map<uint32_t, TimerCallbackInfo*> Timer::timerTable;
314d6c458bSopenharmony_cistd::mutex Timer::timeLock;
324d6c458bSopenharmony_ci
334d6c458bSopenharmony_ciTimerCallbackInfo::~TimerCallbackInfo()
344d6c458bSopenharmony_ci{
354d6c458bSopenharmony_ci    Helper::NapiHelper::DeleteReference(env_, callback_);
364d6c458bSopenharmony_ci    for (size_t idx = 0; idx < argc_; idx++) {
374d6c458bSopenharmony_ci        Helper::NapiHelper::DeleteReference(env_, argv_[idx]);
384d6c458bSopenharmony_ci    }
394d6c458bSopenharmony_ci    Helper::CloseHelp::DeletePointer(argv_, true);
404d6c458bSopenharmony_ci
414d6c458bSopenharmony_ci    uv_timer_stop(timeReq_);
424d6c458bSopenharmony_ci    uv_close(reinterpret_cast<uv_handle_t*>(timeReq_), [](uv_handle_t* handle) {
434d6c458bSopenharmony_ci        if (handle != nullptr) {
444d6c458bSopenharmony_ci            delete (uv_timer_t*)handle;
454d6c458bSopenharmony_ci            handle = nullptr;
464d6c458bSopenharmony_ci        }
474d6c458bSopenharmony_ci    });
484d6c458bSopenharmony_ci}
494d6c458bSopenharmony_ci
504d6c458bSopenharmony_cibool Timer::RegisterTime(napi_env env)
514d6c458bSopenharmony_ci{
524d6c458bSopenharmony_ci    if (env == nullptr) {
534d6c458bSopenharmony_ci        return false;
544d6c458bSopenharmony_ci    }
554d6c458bSopenharmony_ci    napi_property_descriptor properties[] = {
564d6c458bSopenharmony_ci        DECLARE_NAPI_DEFAULT_PROPERTY_FUNCTION("setTimeout", SetTimeout),
574d6c458bSopenharmony_ci        DECLARE_NAPI_DEFAULT_PROPERTY_FUNCTION("setInterval", SetInterval),
584d6c458bSopenharmony_ci        DECLARE_NAPI_DEFAULT_PROPERTY_FUNCTION("clearTimeout", ClearTimer),
594d6c458bSopenharmony_ci        DECLARE_NAPI_DEFAULT_PROPERTY_FUNCTION("clearInterval", ClearTimer)
604d6c458bSopenharmony_ci    };
614d6c458bSopenharmony_ci    napi_value globalObj = Helper::NapiHelper::GetGlobalObject(env);
624d6c458bSopenharmony_ci    napi_status status = napi_define_properties(env, globalObj, sizeof(properties) / sizeof(properties[0]), properties);
634d6c458bSopenharmony_ci    return status == napi_ok;
644d6c458bSopenharmony_ci}
654d6c458bSopenharmony_ci
664d6c458bSopenharmony_cinapi_value Timer::SetTimeout(napi_env env, napi_callback_info cbinfo)
674d6c458bSopenharmony_ci{
684d6c458bSopenharmony_ci    return Timer::SetTimeoutInner(env, cbinfo, false);
694d6c458bSopenharmony_ci}
704d6c458bSopenharmony_ci
714d6c458bSopenharmony_cinapi_value Timer::SetInterval(napi_env env, napi_callback_info cbinfo)
724d6c458bSopenharmony_ci{
734d6c458bSopenharmony_ci    return Timer::SetTimeoutInner(env, cbinfo, true);
744d6c458bSopenharmony_ci}
754d6c458bSopenharmony_ci
764d6c458bSopenharmony_cinapi_value Timer::ClearTimer(napi_env env, napi_callback_info cbinfo)
774d6c458bSopenharmony_ci{
784d6c458bSopenharmony_ci    // 1. check args
794d6c458bSopenharmony_ci    size_t argc = 1;
804d6c458bSopenharmony_ci    napi_value argv[1];
814d6c458bSopenharmony_ci    napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, nullptr);
824d6c458bSopenharmony_ci    if (argc < 1) {
834d6c458bSopenharmony_ci        HILOG_ERROR("the number of params must be one");
844d6c458bSopenharmony_ci        return nullptr;
854d6c458bSopenharmony_ci    }
864d6c458bSopenharmony_ci
874d6c458bSopenharmony_ci    uint32_t tId;
884d6c458bSopenharmony_ci    napi_status status = napi_get_value_uint32(env, argv[0], &tId);
894d6c458bSopenharmony_ci    if (status != napi_ok) {
904d6c458bSopenharmony_ci        HILOG_DEBUG("first param should be number");
914d6c458bSopenharmony_ci        return nullptr;
924d6c458bSopenharmony_ci    }
934d6c458bSopenharmony_ci
944d6c458bSopenharmony_ci    {
954d6c458bSopenharmony_ci        std::lock_guard<std::mutex> lock(timeLock);
964d6c458bSopenharmony_ci        auto iter = timerTable.find(tId);
974d6c458bSopenharmony_ci        if (iter == timerTable.end()) {
984d6c458bSopenharmony_ci            // timer already cleared
994d6c458bSopenharmony_ci            return nullptr;
1004d6c458bSopenharmony_ci        }
1014d6c458bSopenharmony_ci        TimerCallbackInfo* callbackInfo = iter->second;
1024d6c458bSopenharmony_ci        if (callbackInfo->env_ != env) {
1034d6c458bSopenharmony_ci            HILOG_ERROR("Timer is deleting by another thread, please check js code. TimerID:%{public}u", tId);
1044d6c458bSopenharmony_ci        } else {
1054d6c458bSopenharmony_ci            timerTable.erase(tId);
1064d6c458bSopenharmony_ci            Helper::CloseHelp::DeletePointer(callbackInfo, false);
1074d6c458bSopenharmony_ci            HILOG_INFO("DeleteTimer ID: %{public}u, count: %{public}u", tId, ++deleteTimerCount);
1084d6c458bSopenharmony_ci        }
1094d6c458bSopenharmony_ci    }
1104d6c458bSopenharmony_ci    return Helper::NapiHelper::GetUndefinedValue(env);
1114d6c458bSopenharmony_ci}
1124d6c458bSopenharmony_ci
1134d6c458bSopenharmony_civoid Timer::TimerCallback(uv_timer_t* handle)
1144d6c458bSopenharmony_ci{
1154d6c458bSopenharmony_ci    TimerCallbackInfo* callbackInfo = static_cast<TimerCallbackInfo*>(handle->data);
1164d6c458bSopenharmony_ci    if (callbackInfo == nullptr) {
1174d6c458bSopenharmony_ci        return;
1184d6c458bSopenharmony_ci    }
1194d6c458bSopenharmony_ci    // Save the following parameters to ensure that they can still obtained if callback clears the callbackInfo.
1204d6c458bSopenharmony_ci    bool repeat = callbackInfo->repeat_;
1214d6c458bSopenharmony_ci    uint32_t tId = callbackInfo->tId_;
1224d6c458bSopenharmony_ci    napi_env env = callbackInfo->env_;
1234d6c458bSopenharmony_ci#ifdef ENABLE_CONTAINER_SCOPE
1244d6c458bSopenharmony_ci    ContainerScope containerScope(callbackInfo->containerScopeId_);
1254d6c458bSopenharmony_ci#endif
1264d6c458bSopenharmony_ci
1274d6c458bSopenharmony_ci    napi_handle_scope scope = nullptr;
1284d6c458bSopenharmony_ci    napi_open_handle_scope(env, &scope);
1294d6c458bSopenharmony_ci    if (scope == nullptr) {
1304d6c458bSopenharmony_ci        return;
1314d6c458bSopenharmony_ci    }
1324d6c458bSopenharmony_ci    napi_value callback = Helper::NapiHelper::GetReferenceValue(env, callbackInfo->callback_);
1334d6c458bSopenharmony_ci    napi_value undefinedValue = Helper::NapiHelper::GetUndefinedValue(env);
1344d6c458bSopenharmony_ci    napi_value callbackResult = nullptr;
1354d6c458bSopenharmony_ci    napi_value* callbackArgv = new napi_value[callbackInfo->argc_];
1364d6c458bSopenharmony_ci    for (size_t idx = 0; idx < callbackInfo->argc_; idx++) {
1374d6c458bSopenharmony_ci        callbackArgv[idx] = Helper::NapiHelper::GetReferenceValue(env, callbackInfo->argv_[idx]);
1384d6c458bSopenharmony_ci    }
1394d6c458bSopenharmony_ci
1404d6c458bSopenharmony_ci    napi_call_function(env, undefinedValue, callback,
1414d6c458bSopenharmony_ci                       callbackInfo->argc_, callbackArgv, &callbackResult);
1424d6c458bSopenharmony_ci    Helper::CloseHelp::DeletePointer(callbackArgv, true);
1434d6c458bSopenharmony_ci    bool isExceptionPending = false;
1444d6c458bSopenharmony_ci    napi_is_exception_pending(env, &isExceptionPending);
1454d6c458bSopenharmony_ci    NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
1464d6c458bSopenharmony_ci    if (isExceptionPending) {
1474d6c458bSopenharmony_ci        HILOG_ERROR("Pending exception in TimerCallback. Triggering HandleUncaughtException");
1484d6c458bSopenharmony_ci        // worker will handle exception itself
1494d6c458bSopenharmony_ci        if (engine->IsMainThread()) {
1504d6c458bSopenharmony_ci            engine->HandleUncaughtException();
1514d6c458bSopenharmony_ci        }
1524d6c458bSopenharmony_ci        napi_close_handle_scope(env, scope);
1534d6c458bSopenharmony_ci        DeleteTimer(tId, callbackInfo);
1544d6c458bSopenharmony_ci        return;
1554d6c458bSopenharmony_ci    }
1564d6c458bSopenharmony_ci
1574d6c458bSopenharmony_ci    // callback maybe contain ClearTimer, so we need check to avoid use-after-free and double-free of callbackInfo
1584d6c458bSopenharmony_ci    std::lock_guard<std::mutex> lock(timeLock);
1594d6c458bSopenharmony_ci    if (timerTable.find(tId) == timerTable.end()) {
1604d6c458bSopenharmony_ci        napi_close_handle_scope(env, scope);
1614d6c458bSopenharmony_ci        return;
1624d6c458bSopenharmony_ci    }
1634d6c458bSopenharmony_ci    if (!repeat) {
1644d6c458bSopenharmony_ci        timerTable.erase(tId);
1654d6c458bSopenharmony_ci        napi_close_handle_scope(env, scope);
1664d6c458bSopenharmony_ci        Helper::CloseHelp::DeletePointer(callbackInfo, false);
1674d6c458bSopenharmony_ci    } else {
1684d6c458bSopenharmony_ci        napi_close_handle_scope(env, scope);
1694d6c458bSopenharmony_ci        uv_timer_again(handle);
1704d6c458bSopenharmony_ci    }
1714d6c458bSopenharmony_ci}
1724d6c458bSopenharmony_ci
1734d6c458bSopenharmony_cinapi_value Timer::SetTimeoutInnerCore(napi_env env, napi_value* argv, size_t argc, bool repeat)
1744d6c458bSopenharmony_ci{
1754d6c458bSopenharmony_ci    int32_t timeout = 0;
1764d6c458bSopenharmony_ci    if (argc > 1) {
1774d6c458bSopenharmony_ci        napi_status status = napi_get_value_int32(env, argv[1], &timeout);
1784d6c458bSopenharmony_ci        if (status != napi_ok) {
1794d6c458bSopenharmony_ci            HILOG_WARN("timeout should be number");
1804d6c458bSopenharmony_ci            timeout = 0;
1814d6c458bSopenharmony_ci        }
1824d6c458bSopenharmony_ci    }
1834d6c458bSopenharmony_ci    if (timeout < 0) {
1844d6c458bSopenharmony_ci        HILOG_DEBUG("timeout < 0 is unreasonable");
1854d6c458bSopenharmony_ci    }
1864d6c458bSopenharmony_ci    // 2. get callback args
1874d6c458bSopenharmony_ci    size_t callbackArgc = argc >= 2 ? argc - 2 : 0; // 2 include callback and timeout
1884d6c458bSopenharmony_ci    napi_ref* callbackArgv = nullptr;
1894d6c458bSopenharmony_ci    if (callbackArgc > 0) {
1904d6c458bSopenharmony_ci        callbackArgv = new napi_ref[callbackArgc];
1914d6c458bSopenharmony_ci        for (size_t idx = 0; idx < callbackArgc; idx++) {
1924d6c458bSopenharmony_ci            callbackArgv[idx] =
1934d6c458bSopenharmony_ci                Helper::NapiHelper::CreateReference(env, argv[idx + 2], 1); // 2 include callback and timeout
1944d6c458bSopenharmony_ci        }
1954d6c458bSopenharmony_ci    }
1964d6c458bSopenharmony_ci    // 3. generate time callback id
1974d6c458bSopenharmony_ci    // 4. generate time callback info
1984d6c458bSopenharmony_ci    // 5. push callback info into timerTable
1994d6c458bSopenharmony_ci    uint32_t tId = 0;
2004d6c458bSopenharmony_ci    TimerCallbackInfo* callbackInfo = nullptr;
2014d6c458bSopenharmony_ci    {
2024d6c458bSopenharmony_ci        std::lock_guard<std::mutex> lock(timeLock);
2034d6c458bSopenharmony_ci        tId = timeCallbackId++;
2044d6c458bSopenharmony_ci        napi_ref callbackRef = Helper::NapiHelper::CreateReference(env, argv[0], 1);
2054d6c458bSopenharmony_ci        callbackInfo = new TimerCallbackInfo(env, tId, timeout, callbackRef, repeat, callbackArgc, callbackArgv);
2064d6c458bSopenharmony_ci#ifdef ENABLE_CONTAINER_SCOPE
2074d6c458bSopenharmony_ci        callbackInfo->containerScopeId_ = ContainerScope::CurrentId();
2084d6c458bSopenharmony_ci#endif
2094d6c458bSopenharmony_ci        if (timerTable.find(tId) != timerTable.end()) {
2104d6c458bSopenharmony_ci            HILOG_ERROR("timerTable occurs error");
2114d6c458bSopenharmony_ci        } else {
2124d6c458bSopenharmony_ci            timerTable[tId] = callbackInfo;
2134d6c458bSopenharmony_ci        }
2144d6c458bSopenharmony_ci    }
2154d6c458bSopenharmony_ci
2164d6c458bSopenharmony_ci    HILOG_DEBUG("SetTimeoutInnerCore function call before libuv! tId = %{public}u,timeout = %{public}u", tId, timeout);
2174d6c458bSopenharmony_ci    // 6. start timer
2184d6c458bSopenharmony_ci    uv_loop_t* loop = Helper::NapiHelper::GetLibUV(env);
2194d6c458bSopenharmony_ci    NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
2204d6c458bSopenharmony_ci    uv_update_time(loop);
2214d6c458bSopenharmony_ci    uv_timer_start(callbackInfo->timeReq_, TimerCallback, timeout >= 0 ? timeout : 1, timeout > 0 ? timeout : 1);
2224d6c458bSopenharmony_ci    if (engine->IsMainThread()) {
2234d6c458bSopenharmony_ci        uv_async_send(&loop->wq_async);
2244d6c458bSopenharmony_ci    } else {
2254d6c458bSopenharmony_ci        uv_work_t *work = new uv_work_t;
2264d6c458bSopenharmony_ci        uv_queue_work_with_qos(loop, work, [](uv_work_t *) {},
2274d6c458bSopenharmony_ci                               [](uv_work_t *work, int32_t) {delete work; }, uv_qos_user_initiated);
2284d6c458bSopenharmony_ci    }
2294d6c458bSopenharmony_ci    return Helper::NapiHelper::CreateUint32(env, tId);
2304d6c458bSopenharmony_ci}
2314d6c458bSopenharmony_ci
2324d6c458bSopenharmony_cinapi_value Timer::SetTimeoutInner(napi_env env, napi_callback_info cbinfo, bool repeat)
2334d6c458bSopenharmony_ci{
2344d6c458bSopenharmony_ci    size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
2354d6c458bSopenharmony_ci    if (argc < 1) {
2364d6c458bSopenharmony_ci        napi_throw_error(env, nullptr, "StartTimeoutOrInterval, callback info is nullptr.");
2374d6c458bSopenharmony_ci        return nullptr;
2384d6c458bSopenharmony_ci    }
2394d6c458bSopenharmony_ci    napi_value* argv = new napi_value[argc];
2404d6c458bSopenharmony_ci    Helper::ObjectScope<napi_value> scope(argv, true);
2414d6c458bSopenharmony_ci    napi_value thisVar = nullptr;
2424d6c458bSopenharmony_ci    napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
2434d6c458bSopenharmony_ci    if (!Helper::NapiHelper::IsCallable(env, argv[0])) {
2444d6c458bSopenharmony_ci        HILOG_ERROR("Set callback timer failed with invalid parameter.");
2454d6c458bSopenharmony_ci        return Helper::NapiHelper::GetUndefinedValue(env);
2464d6c458bSopenharmony_ci    }
2474d6c458bSopenharmony_ci
2484d6c458bSopenharmony_ci    return SetTimeoutInnerCore(env, argv, argc, repeat);
2494d6c458bSopenharmony_ci}
2504d6c458bSopenharmony_ci
2514d6c458bSopenharmony_civoid Timer::ClearEnvironmentTimer(napi_env env)
2524d6c458bSopenharmony_ci{
2534d6c458bSopenharmony_ci    std::lock_guard<std::mutex> lock(timeLock);
2544d6c458bSopenharmony_ci    auto iter = timerTable.begin();
2554d6c458bSopenharmony_ci    while (iter != timerTable.end()) {
2564d6c458bSopenharmony_ci        TimerCallbackInfo* callbackInfo = iter->second;
2574d6c458bSopenharmony_ci        if (callbackInfo->env_ == env) {
2584d6c458bSopenharmony_ci            iter = timerTable.erase(iter);
2594d6c458bSopenharmony_ci            Helper::CloseHelp::DeletePointer(callbackInfo, false);
2604d6c458bSopenharmony_ci        } else {
2614d6c458bSopenharmony_ci            iter++;
2624d6c458bSopenharmony_ci        }
2634d6c458bSopenharmony_ci    }
2644d6c458bSopenharmony_ci}
2654d6c458bSopenharmony_ci
2664d6c458bSopenharmony_cibool Timer::HasTimer(napi_env env)
2674d6c458bSopenharmony_ci{
2684d6c458bSopenharmony_ci    std::lock_guard<std::mutex> lock(timeLock);
2694d6c458bSopenharmony_ci    auto iter = std::find_if(timerTable.begin(), timerTable.end(), [env](const auto &item) {
2704d6c458bSopenharmony_ci        return item.second->env_ == env;
2714d6c458bSopenharmony_ci    });
2724d6c458bSopenharmony_ci    return iter != timerTable.end();
2734d6c458bSopenharmony_ci}
2744d6c458bSopenharmony_ci
2754d6c458bSopenharmony_civoid Timer::DeleteTimer(uint32_t tId, TimerCallbackInfo* callbackInfo)
2764d6c458bSopenharmony_ci{
2774d6c458bSopenharmony_ci    std::lock_guard<std::mutex> lock(timeLock);
2784d6c458bSopenharmony_ci    // callback maybe contain ClearTimer, so we need check to avoid use-after-free and double-free of callbackInfo
2794d6c458bSopenharmony_ci    if (timerTable.find(tId) != timerTable.end()) {
2804d6c458bSopenharmony_ci        timerTable.erase(tId);
2814d6c458bSopenharmony_ci        Helper::CloseHelp::DeletePointer(callbackInfo, false);
2824d6c458bSopenharmony_ci    }
2834d6c458bSopenharmony_ci}
2844d6c458bSopenharmony_ci} // namespace Commonlibrary::JsSysModule
285