/*
 * Copyright (c) 2024 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 "ark_idle_monitor.h"

#if defined(LINUX_PLATFORM) || defined(OHOS_PLATFORM)
#include <dlfcn.h>
#include <signal.h>
#endif
#include "utils/log.h"
#include <cinttypes>
#if defined(ENABLE_FFRT)
#include "ffrt.h"
#include "c/executor_task.h"
#endif
#ifdef ENABLE_UCOLLECTION
#include "cpu_collector_client.h"
#endif
#if defined(ENABLE_HITRACE) || defined(ENABLE_EVENT_HANDLER)
#include "hitrace/trace.h"
#include "hitrace_meter.h"
#include "parameter.h"
#include "parameters.h"
#include "musl_preinit_common.h"
#include "memory_trace.h"
#endif
#include "ark_native_engine.h"

namespace panda::ecmascript {
const std::string RES_SCHED_CLIENT_SO = "libressched_client.z.so";
using ReportDataFunc = void (*)(uint32_t resType, int64_t value,
    const std::unordered_map<std::string, std::string>& payload);

class CallbackTimerScope {
public:
    static constexpr int64_t CALLBACK_TIMEOUT_THRESHOLD_MS = 100;
    explicit CallbackTimerScope(const char* name, int64_t thresholdMs = CALLBACK_TIMEOUT_THRESHOLD_MS)
        : name_(name), thresholdMs_(thresholdMs), startTime_(std::chrono::steady_clock::now()) {}
    ~CallbackTimerScope()
    {
        auto endTime = std::chrono::steady_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime_).count();
        if (duration > thresholdMs_) {
            HILOG_WARN("ArkIdleMonitor: %{public}s timeout", name_);
        }
    }
private:
    const char* name_;
    int64_t thresholdMs_;
    std::chrono::steady_clock::time_point startTime_;
};

#if defined(ENABLE_EVENT_HANDLER)
static constexpr uint64_t IDLE_GC_TIME_MIN = 1000;
static constexpr uint64_t IDLE_GC_TIME_MAX = 10000;
uint64_t ArkIdleMonitor::gIdleMonitoringInterval = ArkIdleMonitor::GetIdleMonitoringInterval();
bool ArkIdleMonitor::gEnableIdleGC =
    OHOS::system::GetBoolParameter("persist.ark.enableidlegc", true);
bool ArkIdleMonitor::gEnableDeferFreeze =
    OHOS::system::GetBoolParameter("persist.ark.deferfreeze", true);
#else
uint64_t ArkIdleMonitor::gIdleMonitoringInterval = 1000; // ms
#endif
// gDelayOverTime Detect whether there is any process freezing during the delay process of the delay task
uint64_t ArkIdleMonitor::gDelayOverTime = gIdleMonitoringInterval + 100; // ms


class TraceScope {
public:
    explicit TraceScope(std::string context) : context_(context)
    {
#ifdef ENABLE_HITRACE
        StartTrace(HITRACE_TAG_ACE, context_);
#endif
    };

    ~TraceScope()
    {
#ifdef ENABLE_HITRACE
        FinishTrace(HITRACE_TAG_ACE);
#endif
    }

private:
    std::string context_;
};

void ArkIdleMonitor::NotifyLooperIdleStart(int64_t timestamp, [[maybe_unused]] int idleTime)
{
    SetIdleState(true);
    AddIdleNotifyCount();
    recordedRunningNotifyInterval_.Push(timestamp - idleEndTimestamp_);
#ifndef DISABLE_SHORT_IDLE_CHECK
    CheckShortIdleTask(timestamp, idleTime);
#endif
    SetNotifyTimestamp(timestamp);
}

void ArkIdleMonitor::CheckShortIdleTask(int64_t timestamp, int idleTime)
{
#if defined(ENABLE_FFRT)
    while (handlerWaitToStopCount_ > 0) {
        if (timerHandlerQueue_.size() <= 0) {
            handlerWaitToStopCount_ = 0;
            break;
        }
        int handler = timerHandlerQueue_.front();
        timerHandlerQueue_.pop();
        int ret = ffrt_timer_stop(ffrt_qos_user_initiated, handler);
        if (ret != 0) {
            HILOG_ERROR("ArkIdleMonitor: ffrt_timer_stop error handler: timerHandler='%{public}d', ret='%{public}d'",
                handler, ret);
        }
        handlerWaitToStopCount_--;
    }
#endif
    if (triggeredGC_ && mainVM_ != nullptr) {
#ifdef ENABLE_HITRACE
        StartTrace(HITRACE_TAG_ACE, "NotifyLooperIdleStart::TriggeredGC");
#endif
        triggeredGC_ = JSNApi::NotifyLooperIdleStart(mainVM_, timestamp, idleTime);
#ifdef ENABLE_HITRACE
        FinishTrace(HITRACE_TAG_ACE);
#endif
         return;
     }
    if (ShouldTryTriggerGC(timestamp - GetNotifyTimestamp()) &&
        idleTime > MIN_TRIGGER_GC_IDLE_INTERVAL &&
        needCheckIntervalIdle_) {
        PostIdleCheckTask();
    }
    if (!needCheckIntervalIdle_) {
        needCheckIntervalIdle_ = true;
    }
}

bool ArkIdleMonitor::ShouldTryTriggerGC(int64_t interval)
{
    if (interval < MIN_TRIGGER_GC_IDLE_INTERVAL ||
        recordedIdleNotifyInterval_.Count() != IDLE_CHECK_INTERVAL_LENGTH) {
        return false;
    }
    int64_t sumIdleInterval = recordedIdleNotifyInterval_.Sum([](int64_t a, int64_t b) {return a + b;}, 0);
    int64_t averageIdleInterval = sumIdleInterval / recordedIdleNotifyInterval_.Count();
    int64_t sumRunningInterval = recordedRunningNotifyInterval_.Sum([](int64_t a, int64_t b) {return a + b;}, 0);
    int64_t averageRunningInterval = sumRunningInterval / recordedRunningNotifyInterval_.Count();
    if (averageIdleInterval > MIN_TRIGGER_GC_IDLE_INTERVAL &&
            averageRunningInterval <= MAX_TRIGGER_GC_RUNNING_INTERVAL) {
        return true;
    }
    return false;
}

void ArkIdleMonitor::NotifyLooperIdleEnd(int64_t timestamp)
{
    idleEndTimestamp_ = timestamp;
    SetIdleState(false);
    int64_t duration = timestamp - GetNotifyTimestamp();
    recordedIdleNotifyInterval_.Push(duration);
    AddIdleDuration(duration);
    if (mainVM_ != nullptr) {
        JSNApi::NotifyLooperIdleEnd(mainVM_, timestamp);
    }
}

bool ArkIdleMonitor::CheckLowNotifyState() const
{
    uint32_t checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
    HILOG_DEBUG("ArkIdleMonitor: low Notify checkCounts '%{public}d', result '%{public}d' ",
        checkCounts, static_cast<int>(numberOfLowIdleNotifyCycles_));
    return numberOfLowIdleNotifyCycles_ >= checkCounts;
}

bool ArkIdleMonitor::CheckLowRunningDurationState() const
{
    uint32_t checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
    HILOG_DEBUG("ArkIdleMonitor: low Duration checkCounts '%{public}d', result '%{public}d' ",
        checkCounts, static_cast<int>(numberOfHighIdleTimeRatio_));
    return numberOfHighIdleTimeRatio_ >= checkCounts;
}

void ArkIdleMonitor::IntervalMonitor()
{
    if (!timerMutex_.try_lock()) {
        HILOG_INFO("ArkIdleMonitor: IntervalMonitor stop by timerMutex_");
        return;
    }
    auto nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now()).time_since_epoch().count();
    int64_t idleDuration = GetTotalIdleDuration() - lastTotalIdleDuration_;
    if (IsIdleState()) {
        idleDuration += (nowTimestamp - GetNotifyTimestamp());
    }
    lastTotalIdleDuration_ = GetTotalIdleDuration();
    if (GetIdleNotifyCount() <= LOW_IDLE_NOTIFY_THRESHOLD) {
        numberOfLowIdleNotifyCycles_++;
    } else {
        numberOfLowIdleNotifyCycles_ = 0U;
    }
    ResetIdleNotifyCount();
    int64_t recordTotalDuration = nowTimestamp - startRecordTimestamp_;
    if (recordTotalDuration <= 0) {
        numberOfHighIdleTimeRatio_ = 0U;
        HILOG_ERROR("ArkIdleMonitor: recordTotalDuration <= 0");
    } else {
        double idleTimeRatio = static_cast<double>(idleDuration) / recordTotalDuration;
        HILOG_DEBUG("ArkIdleMonitor: idleTimeRatio '%{public}.2f'", idleTimeRatio);
        idleTimeRatio >= IDLE_RATIO ? numberOfHighIdleTimeRatio_++ : (numberOfHighIdleTimeRatio_ = 0U);
    }
    startRecordTimestamp_ = nowTimestamp;
    CheckWorkerEnvQueue();
    uint32_t checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
    int64_t intervalDuration = nowTimestamp - intervalTimestamp_;
    if (numberOfLowIdleNotifyCycles_ >= checkCounts &&
            numberOfHighIdleTimeRatio_ >= checkCounts &&
            intervalDuration < static_cast<int64_t>(gDelayOverTime)) {
        triggerTaskStartTimestamp_ = nowTimestamp;
        if (IsInBackground()) {
            // In the background application state, idle garbage collection should be triggered exclusively for
            // applications that remain resident in memory, thereby preventing non-resident applications
            // from being inadvertently suspended during garbage collection.
            if (enableIdleProcessGCBackground_) {
                TryTriggerCompressGCOfProcess();
            } else {
                HILOG_INFO("ArkIdleMonitor: not enable background gc.");
            }
        } else {
            NotifyMainThreadTryCompressGC();
        }
        PostMonitorTask(SLEEP_MONITORING_INTERVAL);
        ClearIdleStats();
    } else {
        PostMonitorTask(gIdleMonitoringInterval);
    }
    intervalTimestamp_ = nowTimestamp;
    timerMutex_.unlock();
}

void ArkIdleMonitor::PostMonitorTask(uint64_t delayMs)
{
#if defined(ENABLE_FFRT)
    auto task = [](void* idleMonitorPtr) {
        if (idleMonitorPtr != nullptr) {
            ArkIdleMonitor* arkIdleMonitor = reinterpret_cast<ArkIdleMonitor *>(idleMonitorPtr);
            arkIdleMonitor->IntervalMonitor();
        }
    };
    if (waitForStopTimerHandler_ != -1) {
        int ret = ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
        if (ret != 0) {
            HILOG_ERROR("ArkIdleMonitor: ffrt_timer_stop error handler: timerHandler='%{public}d', ret='%{public}d'",
                waitForStopTimerHandler_, ret);
        }
    }
    waitForStopTimerHandler_ = currentTimerHandler_;
    currentTimerHandler_ = ffrt_timer_start(ffrt_qos_user_initiated, delayMs, this, task, false);
#endif
}

void ArkIdleMonitor::ClearIdleStats()
{
    ResetIdleNotifyCount();
    numberOfLowIdleNotifyCycles_ = 0U;
    numberOfHighIdleTimeRatio_ = 0U;
    std::lock_guard<std::mutex> lock(envVectorMutex_);
    for (auto it : workerEnvVector_) {
        reinterpret_cast<ArkNativeEngine*>(it)->GetWorkerThreadState()->ResetCheckCount();
    }
}

void ArkIdleMonitor::NotifyMainThreadTryCompressGC()
{
#if defined(ENABLE_EVENT_HANDLER)
    double cpuUsage = GetCpuUsage();
    if (cpuUsage >= IDLE_CPU_USAGE) {
        HILOG_INFO("ArkIdleMonitor: Sending a quiet notification is canceled due to high CPU usage: %{public}.2f",
            cpuUsage);
        return;
    }
    if (mainVM_ == nullptr) {
        return;
    }
    auto task = [this]() {
        JSNApi::TriggerIdleGC(mainVM_, TRIGGER_IDLE_GC_TYPE::FULL_GC);
        if (CheckWorkerEnvQueueAllInIdle(IDLE_WORKER_TRIGGER_COUNT)) {
            JSNApi::TriggerIdleGC(mainVM_, TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC);
        }
    };
    mainThreadHandler_->PostTask(task, "ARKTS_IDLE_COMPRESS", 0, OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
#endif
}

void ArkIdleMonitor::SetStartTimerCallback()
{
    JSNApi::SetStartIdleMonitorCallback([this]() {
        // prevents duplicate invok to avoid deadlocks
        if (!started_) {
            this->IntervalMonitor();
            started_ = true;
        }
    });
}

void ArkIdleMonitor::NotifyNeedFreeze(bool needFreeze)
{
#if defined(ENABLE_EVENT_HANDLER)
    if (!gEnableDeferFreeze || !IsSwitchToBackgroundTask()) {
        return;
    }
    if (!needFreeze) {
        if (!deferfreeze_.load(std::memory_order_relaxed)) {
            deferfreeze_.store(true, std::memory_order_relaxed);
            ReportDataToRSS(false);
        } else {
            // The delay in freezing has been notified
            HILOG_DEBUG("ArkIdleMonitor: NotifyFreeze Abandoned message");
        }
    } else {
        if (deferfreeze_.load(std::memory_order_relaxed)) {
            deferfreeze_.store(false, std::memory_order_relaxed);
            SetSwitchToBackgroundTask(false);
            if (externalClearCallback_) {
                TraceScope trace("ArkIdleMonitor:ExternalClearCallback");
                HILOG_DEBUG("ArkIdleMonitor: External Clear Callback.");
                CallbackTimerScope timeScope("External Clear Callback");
                externalClearCallback_();
            }
            ReportDataToRSS(true);
        } else {
            // It is not in a delayed freeze state, so there is no need to notify the freeze
            HILOG_DEBUG("ArkIdleMonitor: NotifyFreeze Abandoned message");
        }
    }
#endif
}

void ArkIdleMonitor::PostLooperTriggerIdleGCTask()
{
#if defined(ENABLE_EVENT_HANDLER)
    std::shared_ptr<OHOS::AppExecFwk::EventRunner> mainThreadRunner =
        OHOS::AppExecFwk::EventRunner::GetMainEventRunner();
    if (mainThreadRunner.get() == nullptr) {
        HILOG_FATAL("ArkNativeEngine:: the mainEventRunner is nullptr");
        return;
    }
    std::weak_ptr<ArkIdleMonitor> weakArkIdleMonitor = ArkIdleMonitor::GetInstance();
    auto callback = [weakArkIdleMonitor](OHOS::AppExecFwk::EventRunnerStage stage,
        const OHOS::AppExecFwk::StageInfo* info) -> int {
        auto arkIdleMonitor = weakArkIdleMonitor.lock();
        if (nullptr == arkIdleMonitor) {
            HILOG_ERROR("ArkIdleMonitor has been destructed.");
            return 0;
        }
        switch (stage) {
            case OHOS::AppExecFwk::EventRunnerStage::STAGE_BEFORE_WAITING:
                arkIdleMonitor->NotifyLooperIdleStart(info->timestamp, info->sleepTime);
                break;
            case OHOS::AppExecFwk::EventRunnerStage::STAGE_AFTER_WAITING:
                arkIdleMonitor->NotifyLooperIdleEnd(info->timestamp);
                break;
            default:
                HILOG_ERROR("this branch is unreachable");
        }
        return 0;
    };
    uint32_t stage = (static_cast<uint32_t>(OHOS::AppExecFwk::EventRunnerStage::STAGE_BEFORE_WAITING) |
        static_cast<uint32_t>(OHOS::AppExecFwk::EventRunnerStage::STAGE_AFTER_WAITING));
    mainThreadRunner->GetEventQueue()->AddObserver(OHOS::AppExecFwk::Observer::ARKTS_GC, stage, callback);
#endif
}

void ArkIdleMonitor::EnableIdleGC(NativeEngine *engine)
{
#if defined(ENABLE_EVENT_HANDLER)
    auto vm = const_cast<EcmaVM *>(engine->GetEcmaVm());
    if (gEnableIdleGC && JSNApi::IsJSMainThreadOfEcmaVM(vm)) {
        SetMainThreadEcmaVM(vm);
        JSNApi::SetTriggerGCTaskCallback(vm, [engine](TriggerGCData& data) {
            engine->PostTriggerGCTask(data, nullptr);
        });
        SetStartTimerCallback();
        PostLooperTriggerIdleGCTask();
        JSNApi::SetNotifyDeferFreezeCallback([this](bool isNeedFreeze) {
            NotifyNeedFreeze(isNeedFreeze);
        });
    } else {
        RegisterWorkerEnv(reinterpret_cast<napi_env>(engine));
    }
    if (mainThreadHandler_ == nullptr) {
        mainThreadHandler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(
            OHOS::AppExecFwk::EventRunner::GetMainEventRunner());
    };
#elif defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
    auto vm = const_cast<EcmaVM *>(engine->GetEcmaVm());
    if (JSNApi::IsJSMainThreadOfEcmaVM(vm)) {
        SetMainThreadEcmaVM(vm);
        mainLoop_ = engine->GetUVLoop();
    } else {
        RegisterWorkerEnv(reinterpret_cast<napi_env>(engine));
    }
#endif
}

void ArkIdleMonitor::UnregisterEnv(NativeEngine *engine)
{
#if defined(ENABLE_EVENT_HANDLER)
    if (!gEnableIdleGC || !JSNApi::IsJSMainThreadOfEcmaVM(engine->GetEcmaVm())) {
        UnregisterWorkerEnv(reinterpret_cast<napi_env>(engine));
    }
#elif defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
    if (!JSNApi::IsJSMainThreadOfEcmaVM(engine->GetEcmaVm())) {
        UnregisterWorkerEnv(reinterpret_cast<napi_env>(engine));
    }
#endif
}

uint64_t ArkIdleMonitor::GetIdleMonitoringInterval()
{
#if defined(ENABLE_EVENT_HANDLER)
    uint64_t value =
        static_cast<uint64_t>(OHOS::system::GetIntParameter("const.arkui.idle_monitoring_interval", 1000)); // ms
    if (value < IDLE_GC_TIME_MIN) {
        value = IDLE_GC_TIME_MIN;
    }
    if (value > IDLE_GC_TIME_MAX) {
        value = IDLE_GC_TIME_MAX;
    }
    return value;
#else
    return gIdleMonitoringInterval;
#endif
}

void ArkIdleMonitor::NotifyChangeBackgroundState(bool inBackground)
{
    if (enableIdleProcessGCBackground_) {
        enableIdleProcessGCBackground_ = false;
    }
    ClearIdleStats();
#if defined(ENABLE_FFRT)
    if (started_ && inBackground) {
        HILOG_DEBUG("ArkIdleMonitor post check switch background gc task");
        StopIdleMonitorTimerTask();
        CheckWorkerEnvQueue();
        PostSwitchBackgroundGCTask();
    }
#elif defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
    inBackground_.store(inBackground, std::memory_order_release);
    if (inBackground) {
        HILOG_DEBUG("ArkIdleMonitor post check switch background gc task");
        PostCrossPlatformSwitchBackgroundGCTask();
    }
    return;
#endif
    inBackground_.store(inBackground, std::memory_order_release);
    if (!started_ && inBackground) {
        HILOG_DEBUG("ArkIdleMonitor change to background but not started idle check");
    }
}

double ArkIdleMonitor::GetCpuUsage() const
{
#ifdef ENABLE_UCOLLECTION
    auto collector = OHOS::HiviewDFX::UCollectClient::CpuCollector::Create();
    auto collectResult = collector->GetSysCpuUsage();
    if (collectResult.retCode == OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
        HILOG_DEBUG("ArkIdleMonitor cpu usage: %{public}.2f", collectResult.data);
        return collectResult.data;
    }
    HILOG_ERROR("ArkIdleMonitor get cpu usage failed, error code:%{public}d", collectResult.retCode);
#endif
    return 0.0f;
}

bool ArkIdleMonitor::CheckIntervalIdle(int64_t timestamp, int64_t idleDuration)
{
    if (!IsIdleState()) {
        return false;
    }
    int64_t nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now()).time_since_epoch().count();
    int64_t sumDuration = nowTimestamp - timestamp;
    int64_t sumIdleDuration = (GetTotalIdleDuration() - idleDuration) + (nowTimestamp - GetNotifyTimestamp());
    [[maybe_unused]] double idlePercentage = static_cast<double>(sumIdleDuration) / static_cast<double>(sumDuration);
#ifdef ENABLE_HITRACE
    StartTrace(HITRACE_TAG_ACE, "CheckIntervalIdle::sumDuration:" + std::to_string(sumDuration)
        + "sumIdleDuration:" + std::to_string(sumIdleDuration)
        + "idlePercentage" + std::to_string(idlePercentage));
#endif
#if defined(ENABLE_EVENT_HANDLER)
    if (idlePercentage > SHORT_IDLE_RATIO && mainVM_!= nullptr) {
        auto task = [this]() {
            triggeredGC_ = JSNApi::NotifyLooperIdleStart(mainVM_, 0, 0);
            if (!triggeredGC_ && !IsInBackground()) {
                TryTriggerWorkerLocalGC();
            }
            needCheckIntervalIdle_ = false;
            handlerWaitToStopCount_++;
            // If GC is triggered, reset the statistics to avoid triggering monitoring tasks continuously.
            if (!triggeredGC_) {
                recordedIdleNotifyInterval_.Reset();
            }
        };
        mainThreadHandler_->PostTask(task, "ARKTS_IDLE_NOTIFY", 0, OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
    }
#endif
#ifdef ENABLE_HITRACE
    FinishTrace(HITRACE_TAG_ACE);
#endif
    return true;
}

void ArkIdleMonitor::PostIdleCheckTask()
{
#ifdef ENABLE_HITRACE
        StartTrace(HITRACE_TAG_ACE, "NotifyLooperIdleStart::PostIdleCheckTask");
#endif
#if defined(ENABLE_FFRT)
    auto nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now()).time_since_epoch().count();
    std::tuple<ArkIdleMonitor*, int64_t, int64_t> myTuple = std::make_tuple(this, nowTimestamp, GetTotalIdleDuration());
    std::tuple<ArkIdleMonitor*, int64_t, int64_t> *data = new std::tuple<ArkIdleMonitor*, int64_t, int64_t>(myTuple);
    auto task = [](void* data) {
        std::tuple<ArkIdleMonitor*, int64_t, int64_t>* tuple =
            reinterpret_cast<std::tuple<ArkIdleMonitor*, int64_t, int64_t>*>(data);
        if (tuple == nullptr || std::get<0>(*tuple) == nullptr) {
            return;
        }
        std::get<0>(*tuple)->CheckIntervalIdle(std::get<1>(*tuple), std::get<2>(*tuple));
        delete tuple;
    };
    int timerHandler = ffrt_timer_start(ffrt_qos_user_initiated, SHORT_IDLE_DELAY_INTERVAL,
        reinterpret_cast<void*>(data), task, false);
    timerHandlerQueue_.push(timerHandler);
#endif
#ifdef ENABLE_HITRACE
    FinishTrace(HITRACE_TAG_ACE);
#endif
}

bool ArkIdleMonitor::SwitchBackgroundCheckGCTask(int64_t timestamp, int64_t idleDuration)
{
    int64_t nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now()).time_since_epoch().count();
    int64_t sumDuration = nowTimestamp - timestamp;
    int64_t sumIdleDuration = (GetTotalIdleDuration() - idleDuration) + (nowTimestamp - GetNotifyTimestamp());
    double idlePercentage = static_cast<double>(sumIdleDuration) / static_cast<double>(sumDuration);
    double cpuUsage = GetCpuUsage();
    if (idlePercentage > BACKGROUND_IDLE_RATIO && sumDuration < static_cast<int64_t>(BACKGROUND_GC_DELAY_OVER_TIME)) {
        triggerTaskStartTimestamp_ = nowTimestamp;
        CheckWorkerEnvQueue();
        SetSwitchToBackgroundTask(true);
        TryTriggerCompressGCOfProcess();
        return true;
    } else {
        HILOG_INFO("ArkIdleMonitor skip BGGCTask, idlePer:%{public}.2f;cpuUsage:%{public}.2f"
                   ";sumDuration:%{public}" PRId64,
                   idlePercentage, cpuUsage, sumDuration);
        return false;
    }
}

void ArkIdleMonitor::PostSwitchBackgroundGCTask()
{
#if defined(ENABLE_FFRT)
    if (IsDuringBackgroundTask()) {
        return;
    }
    if (switchBackgroundTimerHandler_ != -1) {
        ffrt_timer_stop(ffrt_qos_user_initiated, switchBackgroundTimerHandler_);
    }
    auto nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now()).time_since_epoch().count();
    std::tuple<ArkIdleMonitor*, int64_t, int64_t> myTuple = std::make_tuple(this, nowTimestamp, GetTotalIdleDuration());
    auto* taskData = new std::tuple<ArkIdleMonitor*, int64_t, int64_t>(myTuple);
    auto task = [](void* data) {
        auto* tuple = reinterpret_cast<std::tuple<ArkIdleMonitor*, int64_t, int64_t>*>(data);
        if (tuple == nullptr || std::get<0>(*tuple) == nullptr) {
            delete tuple;
            return;
        }
        auto* monitor = std::get<0>(*tuple);
        for (uint32_t retry = 0; retry < BACKGROUND_GC_CHECK_COUNT; ++retry) {
            if (!monitor->IsInBackground()) {
                HILOG_INFO("ArkIdleMonitor exit BGGCTask loop, app switched to foreground");
                break;
            }
            bool triggered = monitor->SwitchBackgroundCheckGCTask(std::get<1>(*tuple), std::get<2>(*tuple));
            if (triggered) {
                break;
            }
            HILOG_INFO("ArkIdleMonitor retry BGGCTask, count:%{public}u", retry + 1);
            if (retry < BACKGROUND_GC_CHECK_COUNT - 1) {
                std::get<1>(*tuple) = std::chrono::time_point_cast<std::chrono::milliseconds>(
                    std::chrono::high_resolution_clock::now()).time_since_epoch().count();
                std::get<2>(*tuple) = monitor->GetTotalIdleDuration();
                ffrt_usleep(BACKGROUND_GC_CHECK_INTERVAL * 1000); // ms → µs
            }
        }
        JSNApi::ExecuteTaskpoolShrinkCallback(monitor->inBackground_);
        monitor->StopIdleMonitorTimerTaskAndPostSleepTask();
        monitor->ClearIdleStats();
        monitor->SetDuringBackgroundTask(false);
        delete tuple;
    };
    switchBackgroundTimerHandler_ = ffrt_timer_start(ffrt_qos_user_initiated, BACKGROUND_GC_CHECK_INTERVAL,
                                                     reinterpret_cast<void*>(taskData), task, false);
    SetDuringBackgroundTask(true);
#endif
}

void ArkIdleMonitor::CheckWorkerEnvQueue()
{
    std::lock_guard<std::mutex> lock(envVectorMutex_);
    for (auto it = workerEnvVector_.begin(); it != workerEnvVector_.end();) {
        auto arkNativeEngine = reinterpret_cast<ArkNativeEngine*>(*it);
        arkNativeEngine->GetWorkerThreadState()->CheckIdleState();
        HILOG_DEBUG("ArkIdleMonitor::CheckWorkerEnvQueue,tid=%{public}d, workerCount=%{public}d",
            arkNativeEngine->GetSysTid(), arkNativeEngine->GetWorkerThreadState()->GetCheckCount());
        ++it;
    }
}

bool ArkIdleMonitor::CheckWorkerEnvQueueAllInIdle(uint32_t idleCount)
{
    std::lock_guard<std::mutex> lock(envVectorMutex_);
    for (auto it = workerEnvVector_.begin(); it != workerEnvVector_.end();) {
        auto arkNativeEngine = reinterpret_cast<ArkNativeEngine*>(*it);
        if (arkNativeEngine->GetWorkerThreadState()->CheckIdleState() < idleCount) {
            return false;
        }
        ++it;
    }
    return true;
}

void ArkIdleMonitor::StopIdleMonitorTimerTask()
{
#if defined(ENABLE_FFRT)
    std::lock_guard<std::mutex> lock(timerMutex_);
    HILOG_INFO("Get timerMutex_");
    if (currentTimerHandler_ != -1) {
        ffrt_timer_stop(ffrt_qos_user_initiated, currentTimerHandler_);
        currentTimerHandler_ = -1;
    }
    if (waitForStopTimerHandler_ != -1) {
        ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
        waitForStopTimerHandler_ = -1;
    }
#endif
}

void ArkIdleMonitor::StopIdleMonitorTimerTaskAndPostSleepTask()
{
#if defined(ENABLE_FFRT)
    std::lock_guard<std::mutex> lock(timerMutex_);
    if (currentTimerHandler_ != -1) {
        ffrt_timer_stop(ffrt_qos_user_initiated, currentTimerHandler_);
        currentTimerHandler_ = -1;
    }
    if (waitForStopTimerHandler_ != -1) {
        ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
        waitForStopTimerHandler_ = -1;
    }
    PostMonitorTask(SLEEP_MONITORING_INTERVAL);
#endif
}

void ArkIdleMonitor::ReportDataToRSS(bool needFreeze)
{
#if defined(LINUX_PLATFORM) || defined(OHOS_PLATFORM)
    auto pid = getpid();
    auto bundleName = JSNApi::GetBundleName(mainVM_);
    std::unordered_map<std::string, std::string> eventParams {
        { "pid", std::to_string(pid) },
        { "bundleName", bundleName }
    };
    if (!reportDataFunc_) {
        reportDataFunc_ = LoadReportDataFunc();
    }
    if (reportDataFunc_ != nullptr) {
        reportDataFunc_(RES_TYPE_GC_EVENT, needFreeze, eventParams);
        HILOG_INFO("ArkIdleMonitor: ReportDataToRSS %{public}d, pid: %{public}d, bundleName: %{public}s",
            needFreeze, pid, bundleName.c_str());
    } else {
        HILOG_WARN("ArkIdleMonitor: ReportDataToRSS func is nullptr.");
    }
#else
    HILOG_WARN("ArkIdleMonitor: Only linux supports LoadReportDataFunc");
#endif
}

ReportDataFunc ArkIdleMonitor::LoadReportDataFunc()
{
#if defined(LINUX_PLATFORM) || defined(OHOS_PLATFORM)
    if (dynamicLoadHandle_ != nullptr) {
        dlclose(dynamicLoadHandle_);
    }
    dynamicLoadHandle_ = dlopen(RES_SCHED_CLIENT_SO.c_str(), RTLD_NOW);
    auto func = reinterpret_cast<ReportDataFunc>(dlsym(dynamicLoadHandle_, "ReportData"));
    if (func == nullptr) {
        dlclose(dynamicLoadHandle_);
        HILOG_ERROR("ArkIdleMonitor: LoadReportDataFunc failed!");
        return nullptr;
    }
    HILOG_INFO("ArkIdleMonitor: LoadReportDataFunc success.");
    return func;
#else
    HILOG_DEBUG("ArkIdleMonitor: Only linux supports LoadReportDataFunc");
    return nullptr;
#endif
}

ArkIdleMonitor::~ArkIdleMonitor()
{
#if defined(LINUX_PLATFORM) || defined(OHOS_PLATFORM)
    if (dynamicLoadHandle_ != nullptr) {
        dlclose(dynamicLoadHandle_);
        dynamicLoadHandle_ = nullptr;
    }
#endif
#if defined(ENABLE_FFRT)
    StopIdleMonitorTimerTask();
    while (timerHandlerQueue_.size() > 0) {
        ffrt_timer_stop(ffrt_qos_user_initiated, timerHandlerQueue_.front());
        timerHandlerQueue_.pop();
    }
#endif
#if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
    if (CrossPlatformBgGCThreadStarted_) {
        uv_thread_join(&CrossPlatformBgGCThread_);
        CrossPlatformBgGCThreadStarted_ = false;
    }
#endif
}

std::shared_ptr<ArkIdleMonitor> ArkIdleMonitor::GetInstance()
{
    static std::shared_ptr<ArkIdleMonitor> instance = std::make_shared<ArkIdleMonitor>();
    return instance;
}

bool ArkIdleMonitor::CheckIfInBackgroundInCompressGC()
{
    if (!IsInBackground() && IsSwitchToBackgroundTask()) {
        deferfreeze_.store(false, std::memory_order_relaxed);
        SetSwitchToBackgroundTask(false);
        return false;
    }
    return true;
}

ArkIdleMonitor::WorkerGCResult ArkIdleMonitor::EvaluateWorkerGC(napi_env workerEnv, bool isForeground)
{
    auto arkNativeEngine = reinterpret_cast<ArkNativeEngine*>(workerEnv);
#if (defined(LINUX_PLATFORM) || defined(OHOS_PLATFORM))
    if (kill(arkNativeEngine->GetSysTid(), 0) != 0) {
        HILOG_ERROR("ArkIdleMonitor: tid: %{public}u is dead!", arkNativeEngine->GetSysTid());
        return WorkerGCResult::ERASED;
    }
#endif
    if (IsSentTask(workerEnv)) {
        HILOG_DEBUG("ArkIdleMonitor: tid: %{public}u is not respond!", arkNativeEngine->GetSysTid());
        return WorkerGCResult::SKIPPED;
    }
    uint32_t checkCount = arkNativeEngine->GetWorkerThreadState()->GetCheckCount();
    if (checkCount == 0) {
        HILOG_DEBUG("ArkIdleMonitor: tid: %{public}u NOT idle", arkNativeEngine->GetSysTid());
        return WorkerGCResult::NOT_IDLE;
    }
    if (isForeground && checkCount % IDLE_WORKER_GC_CHECK_COUNT != 0) {
        HILOG_DEBUG("ArkIdleMonitor: tid: %{public}u checkCount:%{public}u skip",
                    arkNativeEngine->GetSysTid(), checkCount);
        return WorkerGCResult::SKIPPED;
    }
    if (arkNativeEngine->IsNativeThread()) {
        HILOG_DEBUG("ArkIdleMonitor: tid: %{public}u is native thread.", arkNativeEngine->GetSysTid());
        return WorkerGCResult::SKIPPED;
    }
    size_t expectedSize = JSNApi::GetEcmaVMExpectedMemoryReclamationSize(arkNativeEngine->GetEcmaVm());
    size_t minReclaimSize = isForeground ? IDLE_MIN_EXPECT_RECLAIM_SIZE_FOREGROUND : IDLE_MIN_EXPECT_RECLAIM_SIZE;
    if (expectedSize <= minReclaimSize) {
        HILOG_DEBUG("ArkIdleMonitor: tid: %{public}u expectedSize:%{public}zu too small",
                    arkNativeEngine->GetSysTid(), expectedSize);
        return WorkerGCResult::SKIPPED;
    }
    return WorkerGCResult::READY;
}

void ArkIdleMonitor::PostWorkerFullGC(napi_env workerEnv, bool notifyFinished)
{
    auto arkNativeEngine = reinterpret_cast<ArkNativeEngine*>(workerEnv);
    std::pair<void*, uint16_t> data(reinterpret_cast<void*>(const_cast<EcmaVM*>(arkNativeEngine->GetEcmaVm())),
                                   static_cast<uint16_t>(TRIGGER_IDLE_GC_TYPE::FULL_GC));
    auto callbackTask = [this, workerEnv, notifyFinished]() {
        HILOG_DEBUG("ArkIdleMonitor: try trigger thread full gc end");
        UnRegisterSentTaskWorkerEnv(workerEnv);
        if (notifyFinished) {
            GCTaskFinishedCallback();
        }
    };
    HILOG_DEBUG("ArkIdleMonitor: try trigger thread full gc start,tid: %{public}u", arkNativeEngine->GetSysTid());
    RegisterSentTaskWorkerEnv(workerEnv);
    arkNativeEngine->PostTriggerGCTask(data, callbackTask);
}

void ArkIdleMonitor::TryTriggerWorkerLocalGC()
{
    HILOG_DEBUG("ArkIdleMonitor: try trigger worker local gc");
    int64_t nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now()).time_since_epoch().count();
    if (nowTimestamp - lastWorkerLocalGCTimestamp_ < IDLE_WORKER_GC_COOLDOWN_MS) {
        HILOG_DEBUG("ArkIdleMonitor: worker local gc in cooldown, last triggered %{public}s ms ago",
                    std::to_string(nowTimestamp - lastWorkerLocalGCTimestamp_).c_str());
        return;
    }
    size_t triggeredCount = 0;
    size_t skippedCount = 0;
    std::lock_guard<std::mutex> vectorLock(envVectorMutex_);
    for (auto it = workerEnvVector_.begin(); it != workerEnvVector_.end();) {
        WorkerGCResult result = EvaluateWorkerGC(*it, true /* isForeground */);
        if (result == WorkerGCResult::READY) {
            PostWorkerFullGC(*it, false /* notifyFinished */);
            triggeredCount++;
        } else {
            skippedCount++;
        }
        if (result == WorkerGCResult::ERASED) {
            it = workerEnvVector_.erase(it);
        } else {
            ++it;
        }
    }
    if (triggeredCount > 0) {
        lastWorkerLocalGCTimestamp_ = nowTimestamp;
        HILOG_INFO("ArkIdleMonitor: worker local gc triggered:%{public}zu, skipped:%{public}zu", triggeredCount,
                   skippedCount);
    } else {
        HILOG_DEBUG("ArkIdleMonitor: worker local gc skipped all:%{public}zu", skippedCount);
    }
}

void ArkIdleMonitor::TryTriggerCompressGCOfProcess()
{
    NotifyNeedFreeze(false);
#if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
    TryTriggerCompressGCOfProcessCrossPlatformGC(TRIGGER_IDLE_GC_TYPE::FULL_GC);
#elif defined(ENABLE_EVENT_HANDLER)
    // trigger main thread local gc.
    auto mainThreadLocalTask = [this]() {
        HILOG_DEBUG("ArkIdleMonitor: try trigger local full gc start");
        std::unique_lock<std::mutex> lock(waitGCFinishjedMutex_);
        JSNApi::TriggerIdleGC(mainVM_, TRIGGER_IDLE_GC_TYPE::FULL_GC);
        HILOG_DEBUG("ArkIdleMonitor: try trigger local full gc end");
        gcFinishCV_.notify_one();
    };

    {
        TraceScope trace("TriggerMainThreadLocalGC");
        std::unique_lock<std::mutex> lock(waitGCFinishjedMutex_);
        mainThreadHandler_->PostTask(mainThreadLocalTask, "ARKTS_IDLE_COMPRESS", 0,
            OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
        gcFinishCV_.wait(lock);
    }
#else
    HILOG_WARN("ArkIdleMonitor: not enable ENABLE_EVENT_HANDLER");
    return;
#endif

    bool isAllInIdle = true;
    size_t index = 0;
    // Traverse all threads to attempt to trigger the compression GC
    while (true) {
#if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
        if (!gEnableDeferFreeze) {
            isAllInIdle = CheckWorkerEnvQueueAllInIdle(IDLE_WORKER_TRIGGER_COUNT);
            break;
        }
#endif
        {
            if (!CheckIfInBackgroundInCompressGC()) {
                return;
            }
            std::unique_lock<std::mutex> vectorLock(envVectorMutex_);
            if (index >= workerEnvVector_.size()) {
                break;
            }
            napi_env it = workerEnvVector_.at(index);
            WorkerGCResult result = EvaluateWorkerGC(it, false /* isForeground */);
            if (result == WorkerGCResult::ERASED) {
                workerEnvVector_.erase(workerEnvVector_.begin() + index);
                continue;
            }
            if (result == WorkerGCResult::NOT_IDLE) {
                isAllInIdle = false;
            }
            if (result != WorkerGCResult::READY) {
                index++;
                continue;
            }
            TraceScope trace("TriggerWorkerThreadLocalGC");
            std::unique_lock<std::mutex> lock(waitGCFinishjedMutex_);
            PostWorkerFullGC(it, true /* notifyFinished */);
            vectorLock.unlock();
            gcFinishCV_.wait_for(lock, std::chrono::milliseconds(WAIT_GC_FINISH_INTERVAL));
            auto nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
                std::chrono::high_resolution_clock::now()).time_since_epoch().count();
            int64_t totalSpentTime = nowTimestamp - triggerTaskStartTimestamp_;
            if (totalSpentTime > WAIT_LOCAL_GC_LOWER_INTERVAL && totalSpentTime <= WAIT_LOCAL_GC_UPPER_INTERVAL) {
                HILOG_INFO("ArkIdleMonitor: local gc overtime, try to trigger shared gc directly.");
                break;
            } else if (totalSpentTime > WAIT_LOCAL_GC_UPPER_INTERVAL) {
                NotifyNeedFreeze(true);
                HILOG_INFO("ArkIdleMonitor: cancel shared gc because over time.");
                return;
            }
            index++;
        }
    }

    // cancel gc if in high cpu usage or not all in idle.
    if (!isAllInIdle) {
        NotifyNeedFreeze(true);
        HILOG_INFO("ArkIdleMonitor: cancel shared gc because not all in idle");
        return;
    }
#if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
    double cpuUsage = GetCpuUsage();
    if (cpuUsage > IDLE_BACKGROUND_CPU_USAGE) {
        NotifyNeedFreeze(true);
        SetSwitchToBackgroundTask(false);
        HILOG_INFO("ArkIdleMonitor: cancel process gc because high cpu usage:%{public}.2f", cpuUsage);
        return;
    }
#endif

#if defined(ENABLE_EVENT_HANDLER)
    // try trigger shared gc
    auto mainThreadSharedTask = [this]() {
        HILOG_DEBUG("ArkIdleMonitor: try trigger shared cc");
        std::unique_lock<std::mutex> lock(waitGCFinishjedMutex_);
        JSNApi::TriggerIdleGC(mainVM_, TRIGGER_IDLE_GC_TYPE::SHARED_CC);
        gcFinishCV_.notify_one();
    };
    if (CheckIfInBackgroundInCompressGC()) {
        TraceScope trace("TriggerMainThreadSharedGC");
        std::unique_lock<std::mutex> lock(waitGCFinishjedMutex_);
        mainThreadHandler_->PostTask(mainThreadSharedTask, "ARKTS_IDLE_SHARED_COMPRESS", 0,
            OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
    } else {
        NotifyNeedFreeze(true);
        HILOG_WARN("ArkIdleMonitor: app is not in idle or in background.");
    }
#elif defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
    TryTriggerCompressGCOfProcessCrossPlatformGC(TRIGGER_IDLE_GC_TYPE::SHARED_CC);
#endif
}

#if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
void ArkIdleMonitor::PostCrossPlatformSwitchBackgroundGCTask()
{
    auto monitorPtr = ArkIdleMonitor::GetInstance();
    ArkIdleMonitor* monitor = monitorPtr.get();
    if (monitor == nullptr) {
        HILOG_ERROR("ArkIdleMonitor: PostCrossPlatformSwitchBackgroundGCTask called but monitor is null");
        return;
    }
    HILOG_DEBUG("ArkIdleMonitor: background GC triggered, inBackground=%{public}d, during=%{public}d",
        static_cast<int>(monitor->IsInBackground()),
        static_cast<int>(monitor->IsDuringBackgroundTask()));
    if (!monitor->IsInBackground() || monitor->IsDuringBackgroundTask()) {
        return;
    }
    monitor->SetDuringBackgroundTask(true);
    if (monitor->CrossPlatformBgGCThreadStarted_) {
        uv_thread_join(&monitor->CrossPlatformBgGCThread_);
        monitor->CrossPlatformBgGCThreadStarted_ = false;
    }

    if (uv_thread_create(&monitor->CrossPlatformBgGCThread_, ArkIdleMonitor::OnCrossPlatformBackgroundGCThreadStart,
        reinterpret_cast<void*>(monitor)) != 0) {
        HILOG_ERROR("ArkIdleMonitor: failed to create cross-platform background GC thread");
        monitor->SetDuringBackgroundTask(false);
        return;
    }
    monitor->CrossPlatformBgGCThreadStarted_ = true;
}

void ArkIdleMonitor::OnCrossPlatformBackgroundGCThreadStart(void* arg)
{
    auto* monitor = reinterpret_cast<ArkIdleMonitor*>(arg);
    if (monitor == nullptr) {
        HILOG_ERROR("ArkIdleMonitor: OnCrossPlatformBackgroundGCThreadStart called with null monitor");
        return;
    }
    HILOG_DEBUG("ArkIdleMonitor: cross-platform background GC thread started");
    int64_t nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now()).time_since_epoch().count();
    monitor->triggerTaskStartTimestamp_ = nowTimestamp;
    monitor->CheckWorkerEnvQueue();
    monitor->SetSwitchToBackgroundTask(true);
    monitor->TryTriggerCompressGCOfProcess();
    monitor->SetDuringBackgroundTask(false);
    HILOG_DEBUG("ArkIdleMonitor: cross-platform background GC thread finished");
}

struct MainThreadGCData {
    ArkIdleMonitor* monitor;
    std::mutex mtx;
    std::condition_variable cv;
    bool finished {false};
    panda::JSNApi::TRIGGER_IDLE_GC_TYPE type;
};

void ArkIdleMonitor::OnMainThreadGCTask(uv_async_t* handle)
{
    if (handle == nullptr) {
        HILOG_ERROR("ArkIdleMonitor: OnMainThreadGCTask called with null handle");
        return;
    }
    MainThreadGCData* data = reinterpret_cast<MainThreadGCData*>(handle->data);
    if (data == nullptr) {
        HILOG_ERROR("ArkIdleMonitor: OnMainThreadGCTask called with null data");
        return;
    }
    EcmaVM* vm = nullptr;
    if (data->monitor != nullptr) {
        vm = data->monitor->GetMainThreadEcmaVM();
    } else {
        HILOG_ERROR("ArkIdleMonitor: OnMainThreadGCTask monitor is null");
    }
    if (vm != nullptr) {
        JSNApi::TriggerIdleGC(vm, data->type);
    } else {
        HILOG_ERROR("ArkIdleMonitor: OnMainThreadGCTask skipped TriggerIdleGC because vm is null");
    }
    {
        std::lock_guard<std::mutex> lock(data->mtx);
        data->finished = true;
        data->cv.notify_one();
    }
    HILOG_DEBUG("ArkIdleMonitor: OnMainThreadGCTask completed and notified");
    // close and free in loop thread
    uv_close(reinterpret_cast<uv_handle_t*>(handle), [](uv_handle_t* h) {
        delete reinterpret_cast<uv_async_t*>(h);
    });
}

void ArkIdleMonitor::TryTriggerCompressGCOfProcessCrossPlatformGC(TRIGGER_IDLE_GC_TYPE type)
{
    if (mainLoop_ == nullptr) {
        HILOG_ERROR("ArkIdleMonitor: skip GC, no mainLoop");
        return;
    }

    if (!CheckIfInBackgroundInCompressGC()) {
        HILOG_WARN("ArkIdleMonitor: skip GC, not in background");
        return;
    }

    MainThreadGCData* data = new MainThreadGCData();
    data->monitor = this;
    data->type = type;

    uv_async_t* async = new uv_async_t();
    async->data = data;
    if (uv_async_init(mainLoop_, async, ArkIdleMonitor::OnMainThreadGCTask) != 0) {
        HILOG_ERROR("ArkIdleMonitor: uv_async_init failed");
        delete data;
        delete async;
        return;
    }
    const char* gcTypeStr = (type == TRIGGER_IDLE_GC_TYPE::FULL_GC) ? "local full gc" : "shared full gc";
    HILOG_DEBUG("ArkIdleMonitor: try trigger %s start", gcTypeStr);
    uv_async_send(async);

    {
        std::unique_lock<std::mutex> lock(data->mtx);
        data->cv.wait(lock, [data]() { return data->finished; });
        HILOG_DEBUG("ArkIdleMonitor: try trigger %s end", gcTypeStr);
    }
    delete data;
}
#endif
} // namespace panda::ecmascript