* Copyright (c) 2021 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/concurrent_marker.h"
#include "common_components/taskpool/taskpool.h"
#include "ecmascript/mem/cms_mem/sweep_gc_visitor-inl.h"
#include "ecmascript/mem/idle_gc_trigger.h"
#include "ecmascript/mem/local_cmc/cc_gc_visitor-inl.h"
#include "ecmascript/mem/old_gc_visitor-inl.h"
#include "ecmascript/mem/parallel_marker.h"
#include "ecmascript/mem/young_gc_visitor-inl.h"
#include "ecmascript/runtime_call_id.h"
namespace panda::ecmascript {
size_t ConcurrentMarker::taskCounts_ = 0;
Mutex ConcurrentMarker::taskCountMutex_;
ConcurrentMarker::ConcurrentMarker(Heap *heap, EnableConcurrentMarkType type)
: heap_(heap),
vm_(heap->GetEcmaVM()),
thread_(vm_->GetJSThread()),
workManager_(heap->GetWorkManager()),
enableMarkType_(type)
{
thread_->SetMarkStatus(MarkStatus::READY_TO_MARK);
}
bool ConcurrentMarker::TryIncreaseTaskCounts()
{
size_t taskPoolSize = common::Taskpool::GetCurrentTaskpool()->GetTotalThreadNum();
{
LockHolder holder(taskCountMutex_);
if (taskCounts_ + 1 < taskPoolSize) {
taskCounts_++;
return true;
}
}
LOG_FULL(INFO) << "Concurrent mark tasks in taskPool are full";
return false;
}
void ConcurrentMarker::EnableConcurrentMarking(EnableConcurrentMarkType type)
{
if (IsConfigDisabled()) {
return;
}
if (IsEnabled() && !thread_->IsReadyToConcurrentMark() && type == EnableConcurrentMarkType::DISABLE) {
enableMarkType_ = EnableConcurrentMarkType::REQUEST_DISABLE;
} else {
enableMarkType_ = type;
}
}
void ConcurrentMarker::MarkRoots()
{
if constexpr (G_USE_CMS_GC) {
SweepGCMarkRootVisitor sweepGCMarkRootVisitor(workManager_->GetWorkNodeHolder(MAIN_THREAD_INDEX));
heap_->GetNonMovableMarker()->MarkRoots(sweepGCMarkRootVisitor);
return;
}
if (heap_->IsYoungMark()) {
YoungGCMarkRootVisitor youngGCMarkRootVisitor(workManager_->GetWorkNodeHolder(MAIN_THREAD_INDEX));
heap_->GetNonMovableMarker()->MarkRoots(youngGCMarkRootVisitor);
} else if (heap_->IsConcurrentFullMark()) {
OldGCMarkRootVisitor oldGCMarkRootVisitor(workManager_->GetWorkNodeHolder(MAIN_THREAD_INDEX));
heap_->GetNonMovableMarker()->MarkRoots(oldGCMarkRootVisitor);
} else {
ASSERT(heap_->IsCCMark());
CCMarkRootVisitor ccMarkRootVisitor(workManager_->GetWorkNodeHolder(MAIN_THREAD_INDEX));
heap_->GetNonMovableMarker()->MarkRoots(ccMarkRootVisitor);
}
}
void ConcurrentMarker::Mark()
{
GCStats *gcStats = heap_->GetEcmaVM()->GetEcmaGCStats();
RecursionScope recurScope(this);
TRACE_GC(GCStats::Scope::ScopeId::ConcurrentMark, gcStats);
LOG_GC(DEBUG) << "ConcurrentMarker: Concurrent Marking Begin";
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK,
("ConcurrentMarker::Mark" + std::to_string(heap_->IsFullMarkRequested())
+ ";MarkReason" + std::to_string(static_cast<int>(gcStats->GetMarkReason()))
+ ";Sensitive" + std::to_string(static_cast<int>(heap_->GetSensitiveStatus()))
+ ";Cms" + std::to_string(static_cast<int>(heap_->GetCmsGC()))
+ ";IsInBackground" + std::to_string(Runtime::GetInstance()->IsInBackground())
+ ";Startup" + std::to_string(static_cast<int>(heap_->GetStartupStatus()))
+ ";ConMark" + std::to_string(static_cast<int>(heap_->GetJSThread()->GetMarkStatus()))
#if USE_CMS_GC
+ ";Slot" + std::to_string(static_cast<int>(heap_->GetSlotSpace()->GetCommittedSize()))
#else
+ ";Young" + std::to_string(heap_->GetNewSpace()->GetCommittedSize())
+ ";Old" + std::to_string(heap_->GetOldSpace()->GetCommittedSize())
#endif
+ ";TotalCommit" + std::to_string(heap_->GetCommittedSize())
+ ";NativeBindingSize" + std::to_string(heap_->GetNativeBindingSize())
+ ";NativeLimitSize" + std::to_string(heap_->GetGlobalSpaceNativeLimit())).c_str(), "");
MEM_ALLOCATE_AND_GC_TRACE(vm_, ConcurrentMarking);
ASSERT(runningTaskCount_ == 0);
runningTaskCount_.fetch_add(1, std::memory_order_relaxed);
InitializeMarking();
clockScope_.Reset();
runningTaskCount_.fetch_sub(1, std::memory_order_relaxed);
heap_->PostParallelGCTask(ParallelGCTaskPhase::CONCURRENT_HANDLE_GLOBAL_POOL_TASK);
}
void ConcurrentMarker::Finish()
{
workManager_->Finish();
heap_->SetCmsGC(false);
heap_->SetDisableCmsGC(false);
}
void ConcurrentMarker::ReMark()
{
TRACE_GC(GCStats::Scope::ScopeId::ReMark, heap_->GetEcmaVM()->GetEcmaGCStats());
LOG_GC(DEBUG) << "ConcurrentMarker: Remarking Begin";
MEM_ALLOCATE_AND_GC_TRACE(vm_, ReMarking);
Marker *marker = heap_->GetNonMovableMarker();
MarkRoots();
marker->ProcessMarkStack(MAIN_THREAD_INDEX);
heap_->WaitRunningMarkTaskFinished();
marker->MarkJitCodeMap(MAIN_THREAD_INDEX);
}
void ConcurrentMarker::HandleMarkingFinished(GCReason gcReason)
{
if (heap_->IsCCMark()) {
heap_->CollectGarbageFromCCMark(gcReason);
} else {
TriggerGCType gcType = heap_->IsConcurrentFullMark() ? TriggerGCType::OLD_GC : TriggerGCType::YOUNG_GC;
heap_->CollectGarbage(gcType, gcReason);
}
}
void ConcurrentMarker::WaitMarkingFinished()
{
LockHolder lock(waitMarkingFinishedMutex_);
while (!markingFinished_) {
waitMarkingFinishedCV_.Wait(&waitMarkingFinishedMutex_);
}
}
void ConcurrentMarker::Reset(bool revertCSet)
{
ASSERT(runningTaskCount_ == 0);
Finish();
thread_->SetMarkStatus(MarkStatus::READY_TO_MARK);
isConcurrentMarking_ = false;
markingFinished_ = false;
notifyMarkingFinished_ = false;
if (revertCSet) {
if constexpr (!G_USE_CMS_GC) {
heap_->GetOldSpace()->RevertCSet();
}
auto callback = [](Region *region) {
region->ResetRegionTypeFlag();
region->ClearMarkGCBitset();
region->ClearCrossRegionRSet();
region->ResetAliveObject();
};
if constexpr (G_USE_CMS_GC) {
heap_->EnumerateRegions(callback);
} else {
if (heap_->IsYoungMark()) {
heap_->GetNewSpace()->EnumerateRegions(callback);
} else {
heap_->EnumerateRegions(callback);
}
}
}
}
void ConcurrentMarker::InitializeMarking()
{
MEM_ALLOCATE_AND_GC_TRACE(vm_, ConcurrentMarkingInitialize);
heap_->Prepare();
#if ENABLE_MEMORY_OPTIMIZATION
if (!heap_->IsCCMark()) {
bool useCms = heap_->GetEcmaParamConfiguration().GetMaxHeapSize() >= DEFAULT_HEAP_SIZE
&& heap_->InSensitiveStatus()
&& !heap_->GetDisableCmsGC();
heap_->SetCmsGC(useCms);
}
#endif
ASSERT(VerifyAllRegionsNonFresh());
if constexpr (!G_USE_CMS_GC) {
heap_->GetNewSpace()->RecordCurrentRegionAsHalfFresh();
}
isConcurrentMarking_ = true;
thread_->SetMarkStatus(MarkStatus::MARKING);
workManager_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::CONCURRENT_HANDLE_GLOBAL_POOL_TASK);
if (heap_->IsYoungMark()) {
if constexpr (G_USE_CMS_GC) {
heapObjectSize_ = heap_->GetHeapObjectSize();
if constexpr (G_USE_STICKY_CMS_GC) {
NonMovableMarker *marker = static_cast<NonMovableMarker*>(heap_->GetNonMovableMarker());
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GC::MarkOldToNew", "");
marker->ProcessOldToNewNoMarkStackForSticky(MAIN_THREAD_INDEX);
}
marker->ProcessSnapshotRSetNoMarkStackForSticky(MAIN_THREAD_INDEX);
}
} else {
heapObjectSize_ = heap_->GetNewSpace()->GetHeapObjectSize();
NonMovableMarker *marker = static_cast<NonMovableMarker*>(heap_->GetNonMovableMarker());
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GC::MarkOldToNew", "");
marker->ProcessOldToNewNoMarkStack(MAIN_THREAD_INDEX);
}
marker->ProcessSnapshotRSetNoMarkStack(MAIN_THREAD_INDEX);
}
} else {
heapObjectSize_ = heap_->GetHeapObjectSize();
heap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
current->ClearMarkGCBitset();
current->ClearCrossRegionRSet();
});
if (heap_->IsConcurrentFullMark() && !G_USE_CMS_GC) {
heap_->GetOldSpace()->SelectCSet();
} else if constexpr (G_USE_STICKY_CMS_GC) {
heap_->ClearGCBitSetForCMS();
}
heap_->EnumerateNonNewSpaceRegions([](Region *current) {
if constexpr (G_USE_CMS_GC) {
ASSERT(current->AliveObject() == 0);
} else {
current->ResetAliveObject();
}
});
}
MarkRoots();
workManager_->GetWorkNodeHolder(MAIN_THREAD_INDEX)->FlushAll();
}
bool ConcurrentMarker::ShouldNotifyMarkingFinished()
{
if (runningTaskCount_.fetch_sub(1, std::memory_order_relaxed) != 1) {
return false;
}
return reinterpret_cast<std::atomic<bool>*>(¬ifyMarkingFinished_)
->exchange(true, std::memory_order_relaxed) == false;
}
void ConcurrentMarker::FinishMarking()
{
LockHolder lock(waitMarkingFinishedMutex_);
ASSERT(!markingFinished_);
ASSERT(notifyMarkingFinished_);
float spendTime = clockScope_.TotalSpentTime();
if (heap_->IsYoungMark()) {
if constexpr (G_USE_CMS_GC) {
heapObjectSize_ = heap_->GetHeapObjectSize();
} else {
heapObjectSize_ = heap_->GetNewSpace()->GetHeapObjectSize();
}
} else {
heapObjectSize_ = heap_->GetHeapObjectSize();
}
SetDuration(spendTime);
if (heap_->IsFullMarkRequested()) {
heap_->SetFullMarkRequestedState(false);
}
thread_->SetMarkStatus(MarkStatus::MARK_FINISHED);
thread_->SetCheckSafePointStatus();
markingFinished_ = true;
waitMarkingFinishedCV_.Signal();
DecreaseTaskCounts();
}
void ConcurrentMarker::ProcessConcurrentMarkTask(uint32_t threadId)
{
runningTaskCount_.fetch_add(1, std::memory_order_relaxed);
Marker *marker = heap_->GetNonMovableMarker();
marker->ProcessMarkStack(threadId);
if (ShouldNotifyMarkingFinished()) {
FinishMarking();
heap_->GetIdleGCTrigger()->TryPostHandleMarkFinished();
}
}
bool ConcurrentMarker::VerifyAllRegionsNonFresh()
{
bool ok = true;
heap_->EnumerateRegions([&ok](Region *region) {
ok &= !region->IsFreshRegion() && !region->IsHalfFreshRegion();
});
return ok;
}
}