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