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/concurrent_marker.h"
17
18#include "ecmascript/mem/parallel_marker-inl.h"
19#include "ecmascript/runtime_call_id.h"
20
21namespace panda::ecmascript {
22size_t ConcurrentMarker::taskCounts_ = 0;
23Mutex ConcurrentMarker::taskCountMutex_;
24
25ConcurrentMarker::ConcurrentMarker(Heap *heap, EnableConcurrentMarkType type)
26    : heap_(heap),
27      vm_(heap->GetEcmaVM()),
28      thread_(vm_->GetJSThread()),
29      workManager_(heap->GetWorkManager()),
30      enableMarkType_(type)
31{
32    thread_->SetMarkStatus(MarkStatus::READY_TO_MARK);
33}
34
35void ConcurrentMarker::EnableConcurrentMarking(EnableConcurrentMarkType type)
36{
37    if (IsConfigDisabled()) {
38        return;
39    }
40    if (IsEnabled() && !thread_->IsReadyToConcurrentMark() && type == EnableConcurrentMarkType::DISABLE) {
41        enableMarkType_ = EnableConcurrentMarkType::REQUEST_DISABLE;
42    } else {
43        enableMarkType_ = type;
44    }
45}
46
47void ConcurrentMarker::Mark()
48{
49    RecursionScope recurScope(this);
50    TRACE_GC(GCStats::Scope::ScopeId::ConcurrentMark, heap_->GetEcmaVM()->GetEcmaGCStats());
51    LOG_GC(DEBUG) << "ConcurrentMarker: Concurrent Marking Begin";
52    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "ConcurrentMarker::Mark");
53    MEM_ALLOCATE_AND_GC_TRACE(vm_, ConcurrentMarking);
54    InitializeMarking();
55    clockScope_.Reset();
56    heap_->PostParallelGCTask(ParallelGCTaskPhase::CONCURRENT_HANDLE_GLOBAL_POOL_TASK);
57}
58
59void ConcurrentMarker::Finish()
60{
61    workManager_->Finish();
62}
63
64void ConcurrentMarker::ReMark()
65{
66    TRACE_GC(GCStats::Scope::ScopeId::ReMark, heap_->GetEcmaVM()->GetEcmaGCStats());
67    LOG_GC(DEBUG) << "ConcurrentMarker: Remarking Begin";
68    MEM_ALLOCATE_AND_GC_TRACE(vm_, ReMarking);
69    Marker *nonMovableMarker = heap_->GetNonMovableMarker();
70    nonMovableMarker->MarkRoots(MAIN_THREAD_INDEX);
71    nonMovableMarker->ProcessMarkStack(MAIN_THREAD_INDEX);
72    heap_->WaitRunningTaskFinished();
73    // MarkJitCodeMap must be call after other mark work finish to make sure which jserror object js alive.
74    nonMovableMarker->MarkJitCodeMap(MAIN_THREAD_INDEX);
75}
76
77void ConcurrentMarker::HandleMarkingFinished(GCReason gcReason)  // js-thread wait for sweep
78{
79    LockHolder lock(waitMarkingFinishedMutex_);
80    ASSERT(markingFinished_);
81    TriggerGCType gcType;
82    if (heap_->IsConcurrentFullMark()) {
83        gcType = TriggerGCType::OLD_GC;
84    } else if (heap_->IsEdenMark()) {
85        gcType = TriggerGCType::EDEN_GC;
86    } else {
87        gcType = TriggerGCType::YOUNG_GC;
88    }
89    heap_->CollectGarbage(gcType, gcReason);
90}
91
92void ConcurrentMarker::WaitMarkingFinished()  // call in EcmaVm thread, wait for mark finished
93{
94    LockHolder lock(waitMarkingFinishedMutex_);
95    while (!markingFinished_) {
96        waitMarkingFinishedCV_.Wait(&waitMarkingFinishedMutex_);
97    }
98}
99
100void ConcurrentMarker::Reset(bool revertCSet)
101{
102    ASSERT(runningTaskCount_ == 0);
103    Finish();
104    thread_->SetMarkStatus(MarkStatus::READY_TO_MARK);
105    isConcurrentMarking_ = false;
106    markingFinished_ = false;
107    notifyMarkingFinished_ = false;
108    if (revertCSet) {
109        // Partial gc clear cset when evacuation allocator finalize
110        heap_->GetOldSpace()->RevertCSet();
111        auto callback = [](Region *region) {
112            region->ResetRegionTypeFlag();
113            region->ClearMarkGCBitset();
114            region->ClearCrossRegionRSet();
115            region->ResetAliveObject();
116        };
117        if (heap_->IsConcurrentFullMark()) {
118            heap_->EnumerateRegions(callback);
119        } else {
120            heap_->EnumerateNewSpaceRegions(callback);
121        }
122    }
123}
124
125void ConcurrentMarker::InitializeMarking()
126{
127    ASSERT(runningTaskCount_ == 0);
128    MEM_ALLOCATE_AND_GC_TRACE(vm_, ConcurrentMarkingInitialize);
129    heap_->Prepare();
130    ASSERT(VerifyAllRegionsNonFresh());
131    heap_->GetNewSpace()->RecordCurrentRegionAsHalfFresh();
132    isConcurrentMarking_ = true;
133    thread_->SetMarkStatus(MarkStatus::MARKING);
134
135    if (heap_->IsConcurrentFullMark()) {
136        heapObjectSize_ = heap_->GetHeapObjectSize();
137        heap_->GetOldSpace()->SelectCSet();
138        heap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
139            current->ClearMarkGCBitset();
140            current->ClearCrossRegionRSet();
141        });
142        // The alive object size of Region in OldSpace will be recalculated.
143        heap_->EnumerateNonNewSpaceRegions([](Region *current) {
144            current->ResetAliveObject();
145        });
146    } else if (heap_->IsEdenMark()) {
147        heapObjectSize_ = heap_->GetEdenSpace()->GetHeapObjectSize();
148    } else {
149        heapObjectSize_ = heap_->GetNewSpace()->GetHeapObjectSize();
150    }
151    workManager_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::CONCURRENT_HANDLE_GLOBAL_POOL_TASK);
152    if (heap_->IsYoungMark()) {
153        {
154            ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::MarkOldToNew");
155            heap_->GetNonMovableMarker()->ProcessOldToNewNoMarkStack(MAIN_THREAD_INDEX);
156        }
157        heap_->GetNonMovableMarker()->ProcessSnapshotRSetNoMarkStack(MAIN_THREAD_INDEX);
158    } else if (heap_->IsEdenMark()) {
159        {
160            ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::MarkNewToEden");
161            heap_->GetNonMovableMarker()->ProcessOldToNewNoMarkStack(MAIN_THREAD_INDEX);
162            heap_->GetNonMovableMarker()->ProcessNewToEdenNoMarkStack(MAIN_THREAD_INDEX);
163        }
164        heap_->GetNonMovableMarker()->ProcessSnapshotRSetNoMarkStack(MAIN_THREAD_INDEX);
165    }
166    heap_->GetNonMovableMarker()->MarkRoots(MAIN_THREAD_INDEX);
167}
168
169bool ConcurrentMarker::ShouldNotifyMarkingFinished()
170{
171    if (runningTaskCount_.fetch_sub(1, std::memory_order_relaxed) != 1) {
172        return false;
173    }
174    return reinterpret_cast<std::atomic<bool>*>(&notifyMarkingFinished_)
175        ->exchange(true, std::memory_order_relaxed) == false;
176}
177
178void ConcurrentMarker::FinishMarking()
179{
180    LockHolder lock(waitMarkingFinishedMutex_);
181    ASSERT(!markingFinished_);
182    ASSERT(notifyMarkingFinished_);
183    float spendTime = clockScope_.TotalSpentTime();
184    if (heap_->IsYoungMark()) {
185        heapObjectSize_ = heap_->GetNewSpace()->GetHeapObjectSize();
186    } else if (heap_->IsConcurrentFullMark()) {
187        heapObjectSize_ = heap_->GetHeapObjectSize();
188    } else if (heap_->IsEdenMark()) {
189        heapObjectSize_ = heap_->GetEdenSpace()->GetHeapObjectSize();
190    }
191    SetDuration(spendTime);
192    if (heap_->IsFullMarkRequested()) {
193        heap_->SetFullMarkRequestedState(false);
194    }
195    thread_->SetMarkStatus(MarkStatus::MARK_FINISHED);
196    thread_->SetCheckSafePointStatus();
197    markingFinished_ = true;
198    waitMarkingFinishedCV_.Signal();
199    DecreaseTaskCounts();
200}
201
202void ConcurrentMarker::ProcessConcurrentMarkTask(uint32_t threadId)
203{
204    runningTaskCount_.fetch_add(1, std::memory_order_relaxed);
205    heap_->GetNonMovableMarker()->ProcessMarkStack(threadId);
206    if (ShouldNotifyMarkingFinished()) {
207        FinishMarking();
208        heap_->GetIdleGCTrigger()->TryPostHandleMarkFinished();
209    }
210}
211
212bool ConcurrentMarker::VerifyAllRegionsNonFresh()
213{
214    bool ok = true;
215    heap_->EnumerateRegions([&ok](Region *region) {
216        ok &= !region->IsFreshRegion() && !region->IsHalfFreshRegion();
217    });
218    return ok;
219}
220}  // namespace panda::ecmascript
221