14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#include "ecmascript/mem/idle_gc_trigger.h"
174514f5e3Sopenharmony_ci
184514f5e3Sopenharmony_ci#include <algorithm>
194514f5e3Sopenharmony_ci
204514f5e3Sopenharmony_ci#include "ecmascript/mem/mem_controller.h"
214514f5e3Sopenharmony_ci#include "ecmascript/mem/shared_mem_controller.h"
224514f5e3Sopenharmony_ci#include "ecmascript/mem/concurrent_marker.h"
234514f5e3Sopenharmony_ci#include "ecmascript/mem/heap-inl.h"
244514f5e3Sopenharmony_ci
254514f5e3Sopenharmony_cinamespace panda::ecmascript {
264514f5e3Sopenharmony_civoid IdleGCTrigger::NotifyVsyncIdleStart()
274514f5e3Sopenharmony_ci{
284514f5e3Sopenharmony_ci    TryTriggerIdleLocalOldGC();
294514f5e3Sopenharmony_ci}
304514f5e3Sopenharmony_ci
314514f5e3Sopenharmony_civoid IdleGCTrigger::NotifyLooperIdleStart([[maybe_unused]] int64_t timestamp, [[maybe_unused]] int idleTime)
324514f5e3Sopenharmony_ci{
334514f5e3Sopenharmony_ci    LOG_ECMA_IF(optionalLogEnabled_, DEBUG) << "IdleGCTrigger: recv once looper idle time";
344514f5e3Sopenharmony_ci    idleState_.store(true);
354514f5e3Sopenharmony_ci    if (!TryTriggerIdleLocalOldGC()) {
364514f5e3Sopenharmony_ci        TryTriggerIdleSharedOldGC();
374514f5e3Sopenharmony_ci    }
384514f5e3Sopenharmony_ci}
394514f5e3Sopenharmony_ci
404514f5e3Sopenharmony_civoid IdleGCTrigger::NotifyLooperIdleEnd([[maybe_unused]] int64_t timestamp)
414514f5e3Sopenharmony_ci{
424514f5e3Sopenharmony_ci    idleState_.store(false);
434514f5e3Sopenharmony_ci}
444514f5e3Sopenharmony_ci
454514f5e3Sopenharmony_civoid IdleGCTrigger::TryTriggerHandleMarkFinished()
464514f5e3Sopenharmony_ci{
474514f5e3Sopenharmony_ci    // wait sharedGC finish
484514f5e3Sopenharmony_ci    if (!thread_->IsReadyToSharedConcurrentMark()) {
494514f5e3Sopenharmony_ci        return ;
504514f5e3Sopenharmony_ci    }
514514f5e3Sopenharmony_ci    if (heap_->GetJSThread()->IsMarkFinished() && heap_->GetConcurrentMarker()->IsTriggeredConcurrentMark()
524514f5e3Sopenharmony_ci        && !heap_->GetOnSerializeEvent() && !heap_->InSensitiveStatus()) {
534514f5e3Sopenharmony_ci        heap_->SetCanThrowOOMError(false);
544514f5e3Sopenharmony_ci        heap_->GetConcurrentMarker()->HandleMarkingFinished();
554514f5e3Sopenharmony_ci        heap_->SetCanThrowOOMError(true);
564514f5e3Sopenharmony_ci    }
574514f5e3Sopenharmony_ci}
584514f5e3Sopenharmony_ci
594514f5e3Sopenharmony_civoid IdleGCTrigger::TryTriggerLocalConcurrentMark()
604514f5e3Sopenharmony_ci{
614514f5e3Sopenharmony_ci    if (heap_->GetConcurrentMarker()->IsEnabled() && heap_->CheckCanTriggerConcurrentMarking()) {
624514f5e3Sopenharmony_ci        heap_->SetFullMarkRequestedState(true);
634514f5e3Sopenharmony_ci        heap_->SetMarkType(MarkType::MARK_FULL);
644514f5e3Sopenharmony_ci        heap_->TriggerConcurrentMarking();
654514f5e3Sopenharmony_ci    }
664514f5e3Sopenharmony_ci}
674514f5e3Sopenharmony_ci
684514f5e3Sopenharmony_cibool IdleGCTrigger::TryTriggerIdleLocalOldGC()
694514f5e3Sopenharmony_ci{
704514f5e3Sopenharmony_ci    if (heap_->GetJSThread()->IsMarkFinished() &&
714514f5e3Sopenharmony_ci        heap_->GetConcurrentMarker()->IsTriggeredConcurrentMark()) {
724514f5e3Sopenharmony_ci        PostIdleGCTask(TRIGGER_IDLE_GC_TYPE::LOCAL_REMARK);
734514f5e3Sopenharmony_ci        return true;
744514f5e3Sopenharmony_ci    }
754514f5e3Sopenharmony_ci    if (!IsPossiblePostGCTask(TRIGGER_IDLE_GC_TYPE::LOCAL_CONCURRENT_MARK) ||
764514f5e3Sopenharmony_ci        !heap_->CheckCanTriggerConcurrentMarking()) {
774514f5e3Sopenharmony_ci        return true;
784514f5e3Sopenharmony_ci    }
794514f5e3Sopenharmony_ci    if (CheckIdleOrHintOldGC<Heap>(heap_) && ReachIdleLocalOldGCThresholds() && !heap_->NeedStopCollection()) {
804514f5e3Sopenharmony_ci        PostIdleGCTask(TRIGGER_IDLE_GC_TYPE::LOCAL_CONCURRENT_MARK);
814514f5e3Sopenharmony_ci        return true;
824514f5e3Sopenharmony_ci    }
834514f5e3Sopenharmony_ci    return false;
844514f5e3Sopenharmony_ci}
854514f5e3Sopenharmony_ci
864514f5e3Sopenharmony_cibool IdleGCTrigger::TryTriggerIdleSharedOldGC()
874514f5e3Sopenharmony_ci{
884514f5e3Sopenharmony_ci    if (CheckIdleOrHintOldGC<SharedHeap>(sHeap_) &&
894514f5e3Sopenharmony_ci        ReachIdleSharedGCThresholds() && !sHeap_->NeedStopCollection() &&
904514f5e3Sopenharmony_ci        sHeap_->CheckCanTriggerConcurrentMarking(thread_)) {
914514f5e3Sopenharmony_ci        PostIdleGCTask(TRIGGER_IDLE_GC_TYPE::SHARED_CONCURRENT_MARK);
924514f5e3Sopenharmony_ci        return true;
934514f5e3Sopenharmony_ci    }
944514f5e3Sopenharmony_ci    return false;
954514f5e3Sopenharmony_ci}
964514f5e3Sopenharmony_ci
974514f5e3Sopenharmony_cibool IdleGCTrigger::ReachIdleLocalOldGCThresholds()
984514f5e3Sopenharmony_ci{
994514f5e3Sopenharmony_ci    bool isFullMarking = heap_->IsConcurrentFullMark() && heap_->GetJSThread()->IsMarking();
1004514f5e3Sopenharmony_ci    bool isNativeSizeLargeTrigger = isFullMarking ? false : heap_->GlobalNativeSizeLargerThanLimitForIdle();
1014514f5e3Sopenharmony_ci    if (isNativeSizeLargeTrigger) {
1024514f5e3Sopenharmony_ci        return true;
1034514f5e3Sopenharmony_ci    }
1044514f5e3Sopenharmony_ci
1054514f5e3Sopenharmony_ci    OldSpace *oldSpace = heap_->GetOldSpace();
1064514f5e3Sopenharmony_ci    HugeObjectSpace *hugeObjectSpace = heap_->GetHugeObjectSpace();
1074514f5e3Sopenharmony_ci    size_t idleSizeLimit = static_cast<size_t>(oldSpace->GetInitialCapacity() *
1084514f5e3Sopenharmony_ci                                                IDLE_SPACE_SIZE_LIMIT_RATE);
1094514f5e3Sopenharmony_ci    size_t currentSize = oldSpace->GetHeapObjectSize() + hugeObjectSpace->GetHeapObjectSize();
1104514f5e3Sopenharmony_ci    if (currentSize >= idleSizeLimit) {
1114514f5e3Sopenharmony_ci        return true;
1124514f5e3Sopenharmony_ci    }
1134514f5e3Sopenharmony_ci
1144514f5e3Sopenharmony_ci    size_t maxCapacity = oldSpace->GetMaximumCapacity() + oldSpace->GetOvershootSize() +
1154514f5e3Sopenharmony_ci                        oldSpace->GetOutOfMemoryOvershootSize();
1164514f5e3Sopenharmony_ci    size_t currentCapacity = oldSpace->GetCommittedSize() + hugeObjectSpace->GetCommittedSize();
1174514f5e3Sopenharmony_ci    size_t idleCapacityLimit = static_cast<size_t>(maxCapacity * IDLE_SPACE_SIZE_LIMIT_RATE);
1184514f5e3Sopenharmony_ci    if (currentCapacity >= idleCapacityLimit) {
1194514f5e3Sopenharmony_ci        return true;
1204514f5e3Sopenharmony_ci    }
1214514f5e3Sopenharmony_ci
1224514f5e3Sopenharmony_ci    size_t oldSpaceAllocLimit = heap_->GetGlobalSpaceAllocLimit() + oldSpace->GetOvershootSize();
1234514f5e3Sopenharmony_ci    size_t idleOldSpaceAllocLimit = static_cast<size_t>(oldSpaceAllocLimit * IDLE_SPACE_SIZE_LIMIT_RATE);
1244514f5e3Sopenharmony_ci    if (heap_->GetHeapObjectSize() > idleOldSpaceAllocLimit) {
1254514f5e3Sopenharmony_ci        return true;
1264514f5e3Sopenharmony_ci    }
1274514f5e3Sopenharmony_ci    return false;
1284514f5e3Sopenharmony_ci}
1294514f5e3Sopenharmony_ci
1304514f5e3Sopenharmony_cibool IdleGCTrigger::ReachIdleSharedGCThresholds()
1314514f5e3Sopenharmony_ci{
1324514f5e3Sopenharmony_ci    size_t expectSizeLimit = sHeap_->GetOldSpace()->GetInitialCapacity() * IDLE_SPACE_SIZE_LIMIT_RATE;
1334514f5e3Sopenharmony_ci    size_t currentOldSize = sHeap_->GetOldSpace()->GetHeapObjectSize();
1344514f5e3Sopenharmony_ci    size_t expectGlobalSizeLimit = sHeap_->GetGlobalSpaceAllocLimit() * IDLE_SPACE_SIZE_LIMIT_RATE;
1354514f5e3Sopenharmony_ci    return currentOldSize > expectSizeLimit || sHeap_->GetHeapObjectSize() > expectGlobalSizeLimit;
1364514f5e3Sopenharmony_ci}
1374514f5e3Sopenharmony_ci
1384514f5e3Sopenharmony_civoid IdleGCTrigger::TryPostHandleMarkFinished()
1394514f5e3Sopenharmony_ci{
1404514f5e3Sopenharmony_ci    if (IsIdleState()) {
1414514f5e3Sopenharmony_ci        PostIdleGCTask(TRIGGER_IDLE_GC_TYPE::LOCAL_REMARK);
1424514f5e3Sopenharmony_ci    }
1434514f5e3Sopenharmony_ci}
1444514f5e3Sopenharmony_ci
1454514f5e3Sopenharmony_civoid IdleGCTrigger::PostIdleGCTask(TRIGGER_IDLE_GC_TYPE gcType)
1464514f5e3Sopenharmony_ci{
1474514f5e3Sopenharmony_ci    if (triggerGCTaskCallback_ != nullptr && IsPossiblePostGCTask(gcType) && !heap_->NeedStopCollection()) {
1484514f5e3Sopenharmony_ci        std::pair<void*, uint8_t> data(heap_->GetEcmaVM(), static_cast<uint8_t>(gcType));
1494514f5e3Sopenharmony_ci        triggerGCTaskCallback_(data);
1504514f5e3Sopenharmony_ci        SetPostGCTask(gcType);
1514514f5e3Sopenharmony_ci        LOG_GC(INFO) << "IdleGCTrigger: post once " << GetGCTypeName(gcType) << " on idleTime";
1524514f5e3Sopenharmony_ci    }
1534514f5e3Sopenharmony_ci    LOG_GC(DEBUG) << "IdleGCTrigger: failed to post once " << GetGCTypeName(gcType);
1544514f5e3Sopenharmony_ci}
1554514f5e3Sopenharmony_ci
1564514f5e3Sopenharmony_cibool IdleGCTrigger::CheckIdleYoungGC() const
1574514f5e3Sopenharmony_ci{
1584514f5e3Sopenharmony_ci    auto newSpace = heap_->GetNewSpace();
1594514f5e3Sopenharmony_ci    LOG_GC(DEBUG) << "IdleGCTrigger: check young GC semi Space size since gc:"
1604514f5e3Sopenharmony_ci        << newSpace->GetAllocatedSizeSinceGC(newSpace->GetTop());
1614514f5e3Sopenharmony_ci    return newSpace->GetAllocatedSizeSinceGC(newSpace->GetTop()) > IDLE_MIN_EXPECT_RECLAIM_SIZE;
1624514f5e3Sopenharmony_ci}
1634514f5e3Sopenharmony_ci
1644514f5e3Sopenharmony_civoid IdleGCTrigger::TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE gcType)
1654514f5e3Sopenharmony_ci{
1664514f5e3Sopenharmony_ci    LOG_GC(DEBUG) << "IdleGCTrigger: recv once notify of " << GetGCTypeName(gcType);
1674514f5e3Sopenharmony_ci    switch (gcType) {
1684514f5e3Sopenharmony_ci        case TRIGGER_IDLE_GC_TYPE::FULL_GC:
1694514f5e3Sopenharmony_ci            if (CheckIdleOrHintFullGC<Heap>(heap_) && !heap_->NeedStopCollection()) {
1704514f5e3Sopenharmony_ci                LOG_GC(INFO) << "IdleGCTrigger: trigger " << GetGCTypeName(gcType);
1714514f5e3Sopenharmony_ci                heap_->CollectGarbage(TriggerGCType::FULL_GC, GCReason::IDLE);
1724514f5e3Sopenharmony_ci            } else if (CheckIdleYoungGC() && !heap_->NeedStopCollection()) {
1734514f5e3Sopenharmony_ci                LOG_GC(INFO) << "IdleGCTrigger: trigger young gc";
1744514f5e3Sopenharmony_ci                heap_->CollectGarbage(TriggerGCType::YOUNG_GC, GCReason::IDLE);
1754514f5e3Sopenharmony_ci            }
1764514f5e3Sopenharmony_ci            break;
1774514f5e3Sopenharmony_ci        case TRIGGER_IDLE_GC_TYPE::SHARED_CONCURRENT_MARK:
1784514f5e3Sopenharmony_ci            if (CheckIdleOrHintOldGC<SharedHeap>(sHeap_) && sHeap_->CheckCanTriggerConcurrentMarking(thread_)
1794514f5e3Sopenharmony_ci                && !sHeap_->NeedStopCollection()) {
1804514f5e3Sopenharmony_ci                LOG_GC(INFO) << "IdleGCTrigger: trigger " << GetGCTypeName(gcType);
1814514f5e3Sopenharmony_ci                sHeap_->TriggerConcurrentMarking<TriggerGCType::SHARED_GC, GCReason::IDLE>(thread_);
1824514f5e3Sopenharmony_ci            }
1834514f5e3Sopenharmony_ci            break;
1844514f5e3Sopenharmony_ci        case TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC:
1854514f5e3Sopenharmony_ci            if (CheckIdleOrHintFullGC<SharedHeap>(sHeap_) && !sHeap_->NeedStopCollection()) {
1864514f5e3Sopenharmony_ci                LOG_GC(INFO) << "IdleGCTrigger: trigger " << GetGCTypeName(gcType);
1874514f5e3Sopenharmony_ci                sHeap_->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::IDLE>(thread_);
1884514f5e3Sopenharmony_ci            }
1894514f5e3Sopenharmony_ci            break;
1904514f5e3Sopenharmony_ci        case TRIGGER_IDLE_GC_TYPE::LOCAL_CONCURRENT_MARK:
1914514f5e3Sopenharmony_ci            if (CheckIdleOrHintOldGC<Heap>(heap_) && !heap_->NeedStopCollection()) {
1924514f5e3Sopenharmony_ci                LOG_GC(INFO) << "IdleGCTrigger: trigger " << GetGCTypeName(gcType);
1934514f5e3Sopenharmony_ci                TryTriggerLocalConcurrentMark();
1944514f5e3Sopenharmony_ci            }
1954514f5e3Sopenharmony_ci            break;
1964514f5e3Sopenharmony_ci        case TRIGGER_IDLE_GC_TYPE::LOCAL_REMARK:
1974514f5e3Sopenharmony_ci            if (!heap_->NeedStopCollection()) {
1984514f5e3Sopenharmony_ci                LOG_GC(INFO) << "IdleGCTrigger: trigger " << GetGCTypeName(gcType);
1994514f5e3Sopenharmony_ci                TryTriggerHandleMarkFinished();
2004514f5e3Sopenharmony_ci            }
2014514f5e3Sopenharmony_ci            break;
2024514f5e3Sopenharmony_ci        default: // LCOV_EXCL_BR_LINE
2034514f5e3Sopenharmony_ci            LOG_GC(ERROR) << "IdleGCTrigger: this branch is unreachable";
2044514f5e3Sopenharmony_ci            return;
2054514f5e3Sopenharmony_ci    }
2064514f5e3Sopenharmony_ci    ClearPostGCTask(gcType);
2074514f5e3Sopenharmony_ci}
2084514f5e3Sopenharmony_ci}