* Copyright (c) 2026 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 "ecmascript/mem/shared_heap/global_parallel_cleaner.h"
#include "common_components/taskpool/taskpool.h"
#include "ecmascript/free_object.h"
#include "ecmascript/mem/heap.h"
#include "ecmascript/mem/heap-inl.h"
#include "ecmascript/mem/region-inl.h"
#include "ecmascript/runtime.h"
namespace panda::ecmascript {
void GlobalParallelCleaner::Clean()
{
CollectRegions();
if (oldRegions_.empty() && youngRegions_.empty()) {
return;
}
if (sHeap_->IsParallelGCEnabled()) {
ParallelCleanRegions();
} else {
SequentialCleanRegions();
}
}
void GlobalParallelCleaner::CollectRegions()
{
oldRegions_.clear();
youngRegions_.clear();
regionIdx_ = 0;
Runtime::GetInstance()->GCIterateThreadList([&](JSThread *iteratedThread) {
Heap *heap = iteratedThread->GetEcmaVM()->GetHeap();
heap->EnumerateNonNewSpaceRegions([&](Region *region) {
oldRegions_.push_back(region);
});
SemiSpace *newSpace = heap->GetNewSpace();
Region *current = newSpace->GetCurrentRegion();
uintptr_t top = newSpace->GetTop();
newSpace->EnumerateRegions([&](Region *region) {
uintptr_t endAddr = (region == current)
? region->GetBegin() + region->GetAllocatedBytes(top)
: region->GetBegin() + region->GetAllocatedBytes();
youngRegions_.push_back({region, endAddr});
});
});
}
void GlobalParallelCleaner::ParallelCleanRegions()
{
uint32_t taskCount = std::min(static_cast<uint32_t>(oldRegions_.size() + youngRegions_.size()),
common::Taskpool::GetCurrentTaskpool()->GetTotalThreadNum());
common::TaskPackMonitor monitor(taskCount, taskCount);
for (uint32_t i = 0; i < taskCount; i++) {
common::Taskpool::GetCurrentTaskpool()->PostTask(
std::make_unique<CleanerTask>(common::GLOBAL_TASK_ID, this, &monitor));
}
monitor.WaitAllFinished();
}
void GlobalParallelCleaner::ProcessRegions(common::TaskPackMonitor *monitor)
{
size_t oldSize = oldRegions_.size();
size_t youngSize = youngRegions_.size();
size_t idx = regionIdx_.fetch_add(1, std::memory_order_relaxed);
while (idx < oldSize) {
ClearDeadRSetForRegion(oldRegions_[idx], oldRegions_[idx]->GetEnd());
idx = regionIdx_.fetch_add(1, std::memory_order_relaxed);
}
while (idx < oldSize + youngSize) {
auto [region, endAddr] = youngRegions_[idx - oldSize];
ClearDeadRSetForRegion(region, endAddr);
idx = regionIdx_.fetch_add(1, std::memory_order_relaxed);
}
monitor->NotifyFinishOne();
}
void GlobalParallelCleaner::SequentialCleanRegions()
{
for (auto *region : oldRegions_) {
ClearDeadRSetForRegion(region, region->GetEnd());
}
for (auto [region, endAddr] : youngRegions_) {
ClearDeadRSetForRegion(region, endAddr);
}
}
void GlobalParallelCleaner::ClearDeadRSetForRegion(Region *region, uintptr_t endAddr)
{
uintptr_t freeStart = region->GetBegin();
region->IterateAllMarkedBits([&freeStart, region, this](void *mem) {
auto header = reinterpret_cast<TaggedObject *>(mem);
size_t size = header->GetSize();
uintptr_t freeEnd = ToUintPtr(mem);
if (freeStart != freeEnd) {
region->ClearLocalToShareRSetInRange(freeStart, freeEnd);
region->ClearOldToNewRSetInRange(freeStart, freeEnd);
FreeObject::FillFreeObject(sHeap_, freeStart, freeEnd - freeStart);
}
freeStart = freeEnd + size;
});
if (freeStart < endAddr) {
region->ClearLocalToShareRSetInRange(freeStart, endAddr);
region->ClearOldToNewRSetInRange(freeStart, endAddr);
FreeObject::FillFreeObject(sHeap_, freeStart, endAddr - freeStart);
}
}
bool GlobalParallelCleaner::CleanerTask::Run(uint32_t threadIndex)
{
cleaner_->ProcessRegions(monitor_);
return true;
}
}