1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/mem/partial_gc.h"
17
18#include "ecmascript/mem/concurrent_marker.h"
19#include "ecmascript/mem/incremental_marker.h"
20#include "ecmascript/mem/parallel_evacuator.h"
21#include "ecmascript/mem/parallel_marker-inl.h"
22#include "ecmascript/runtime_call_id.h"
23#include "ecmascript/mem/verification.h"
24
25namespace panda::ecmascript {
26PartialGC::PartialGC(Heap *heap) : heap_(heap), workManager_(heap->GetWorkManager()) {}
27
28void PartialGC::RunPhases()
29{
30    GCStats *gcStats = heap_->GetEcmaVM()->GetEcmaGCStats();
31    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PartialGC::RunPhases" + std::to_string(heap_->IsConcurrentFullMark())
32        + ";Reason" + std::to_string(static_cast<int>(gcStats->GetGCReason()))
33        + ";Sensitive" + std::to_string(static_cast<int>(heap_->GetSensitiveStatus()))
34        + ";IsInBackground" + std::to_string(heap_->IsInBackground())
35        + ";Startup" + std::to_string(heap_->OnStartupEvent())
36        + ";ConMark" + std::to_string(static_cast<int>(heap_->GetJSThread()->GetMarkStatus()))
37        + ";Young" + std::to_string(heap_->GetNewSpace()->GetCommittedSize())
38        + ";Old" + std::to_string(heap_->GetOldSpace()->GetCommittedSize())
39        + ";TotalCommit" + std::to_string(heap_->GetCommittedSize()));
40    TRACE_GC(GCStats::Scope::ScopeId::TotalGC, gcStats);
41    MEM_ALLOCATE_AND_GC_TRACE(heap_->GetEcmaVM(), PartialGC_RunPhases);
42    bool mainThreadInForeground = heap_->GetJSThread()->IsMainThreadFast() && !heap_->IsInBackground();
43    bool needAjustGCThreadPrio = heap_->GetGCType() == TriggerGCType::OLD_GC ||
44        heap_->GetNewSpace()->GetCommittedSize() >= heap_->GetNewSpace()->GetMaximumCapacity();
45    if (mainThreadInForeground && needAjustGCThreadPrio) {
46        Taskpool::GetCurrentTaskpool()->SetThreadPriority(PriorityMode::STW);
47    }
48    markingInProgress_ = heap_->CheckOngoingConcurrentMarking();
49    LOG_GC(DEBUG) << "markingInProgress_" << markingInProgress_;
50    Initialize();
51    Mark();
52    if (UNLIKELY(heap_->ShouldVerifyHeap())) {
53        Verification::VerifyMark(heap_);
54    }
55    ProcessSharedGCRSetWorkList();
56    Sweep();
57    Evacuate();
58    if (heap_->IsConcurrentFullMark()) {
59        heap_->GetSweeper()->PostTask();
60    }
61    if (UNLIKELY(heap_->ShouldVerifyHeap())) {
62        Verification::VerifyEvacuate(heap_);
63    }
64    Finish();
65    if (mainThreadInForeground && needAjustGCThreadPrio) {
66        Taskpool::GetCurrentTaskpool()->SetThreadPriority(PriorityMode::FOREGROUND);
67    }
68}
69
70void PartialGC::Initialize()
71{
72    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PartialGC::Initialize");
73    TRACE_GC(GCStats::Scope::ScopeId::Initialize, heap_->GetEcmaVM()->GetEcmaGCStats());
74    if (!markingInProgress_ && !heap_->GetIncrementalMarker()->IsTriggeredIncrementalMark()) {
75        LOG_GC(DEBUG) << "No ongoing Concurrent marking. Initializing...";
76        heap_->Prepare();
77        if (heap_->IsConcurrentFullMark()) {
78            heap_->GetOldSpace()->SelectCSet();
79            heap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
80                current->ClearMarkGCBitset();
81                current->ClearCrossRegionRSet();
82            });
83            heap_->EnumerateNonNewSpaceRegions([](Region *current) {
84                current->ResetAliveObject();
85            });
86        }
87        workManager_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::OLD_HANDLE_GLOBAL_POOL_TASK);
88
89        freeSize_ = 0;
90        hugeSpaceFreeSize_ = 0;
91        oldSpaceCommitSize_ = heap_->GetOldSpace()->GetCommittedSize();
92        nonMoveSpaceCommitSize_ = heap_->GetNonMovableSpace()->GetCommittedSize();
93    }
94}
95
96void PartialGC::Finish()
97{
98    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PartialGC::Finish");
99    TRACE_GC(GCStats::Scope::ScopeId::Finish, heap_->GetEcmaVM()->GetEcmaGCStats());
100    heap_->Resume(OLD_GC);
101    if (heap_->GetIncrementalMarker()->IsTriggeredIncrementalMark()) {
102        heap_->GetIncrementalMarker()->Reset();
103    } else if (markingInProgress_) {
104        auto marker = heap_->GetConcurrentMarker();
105        marker->Reset(false);
106    } else {
107        workManager_->Finish();
108    }
109    if (heap_->IsConcurrentFullMark()) {
110        heap_->GetSweeper()->TryFillSweptRegion();
111        heap_->SetFullMarkRequestedState(false);
112    }
113}
114
115void PartialGC::Mark()
116{
117    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PartialGC::Mark");
118    TRACE_GC(GCStats::Scope::ScopeId::Mark, heap_->GetEcmaVM()->GetEcmaGCStats());
119    if (markingInProgress_) {
120        heap_->GetConcurrentMarker()->ReMark();
121        return;
122    }
123    heap_->GetNonMovableMarker()->MarkRoots(MAIN_THREAD_INDEX);
124    if (heap_->IsConcurrentFullMark()) {
125        heap_->GetNonMovableMarker()->ProcessMarkStack(MAIN_THREAD_INDEX);
126    } else if (heap_->IsEdenMark()) {
127        {
128            ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::ProcessOldToNew");
129            heap_->GetNonMovableMarker()->ProcessOldToNew(MAIN_THREAD_INDEX);
130        }
131        {
132            ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::ProcessNewToEden");
133            heap_->GetNonMovableMarker()->ProcessNewToEden(MAIN_THREAD_INDEX);
134        }
135        heap_->GetNonMovableMarker()->ProcessSnapshotRSet(MAIN_THREAD_INDEX);
136    } else if (heap_->IsYoungMark()) {
137        {
138            ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::ProcessOldToNew");
139            heap_->GetNonMovableMarker()->ProcessOldToNew(MAIN_THREAD_INDEX);
140        }
141        heap_->GetNonMovableMarker()->ProcessSnapshotRSet(MAIN_THREAD_INDEX);
142    }
143    heap_->WaitRunningTaskFinished();
144    // MarkJitCodeMap must be call after other mark work finish to make sure which jserror object js alive.
145    heap_->GetNonMovableMarker()->MarkJitCodeMap(MAIN_THREAD_INDEX);
146}
147
148void PartialGC::Sweep()
149{
150    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PartialGC::Sweep");
151    ProcessNativeDelete();
152    if (heap_->IsConcurrentFullMark()) {
153        heap_->GetOldSpace()->EnumerateRegions([](Region *current) {
154            current->SetRegionAliveSize();
155        });
156        TRACE_GC(GCStats::Scope::ScopeId::Sweep, heap_->GetEcmaVM()->GetEcmaGCStats());
157        heap_->GetSweeper()->Sweep();
158    }
159}
160
161void PartialGC::ProcessNativeDelete()
162{
163    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::ProcessNativeDelete");
164    TRACE_GC(GCStats::Scope::ScopeId::ClearNativeObject, heap_->GetEcmaVM()->GetEcmaGCStats());
165    WeakRootVisitor gcUpdateWeak = [this](TaggedObject *header) -> TaggedObject* {
166        Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(header));
167        ASSERT(!objectRegion->InSharedHeap());
168        if (heap_->IsEdenMark() && !objectRegion->InEdenSpace()) {
169            return header;
170        }
171        if (!objectRegion->InGeneralNewSpaceOrCSet() && heap_->IsYoungMark()) {
172            return header;
173        }
174        if (!objectRegion->Test(header)) {
175            return nullptr;
176        }
177        return header;
178    };
179    heap_->GetEcmaVM()->ProcessNativeDelete(gcUpdateWeak);
180}
181
182void PartialGC::Evacuate()
183{
184    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PartialGC::Evacuate");
185    TRACE_GC(GCStats::Scope::ScopeId::Evacuate, heap_->GetEcmaVM()->GetEcmaGCStats());
186    heap_->GetEvacuator()->Evacuate();
187}
188
189ARK_INLINE void PartialGC::ProcessSharedGCRSetWorkList()
190{
191    TRACE_GC(GCStats::Scope::ScopeId::ProcessSharedGCRSetWorkList, heap_->GetEcmaVM()->GetEcmaGCStats());
192    heap_->ProcessSharedGCRSetWorkList();
193}
194}  // namespace panda::ecmascript
195