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