Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(595)

Side by Side Diff: runtime/vm/gc_marker.cc

Issue 1351453008: Parallel marking. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Add TODO to remove busy wait Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « runtime/vm/gc_marker.h ('k') | runtime/vm/heap_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/gc_marker.h" 5 #include "vm/gc_marker.h"
6 6
7 #include <map> 7 #include <map>
8 #include <utility> 8 #include <utility>
9 #include <vector> 9 #include <vector>
10 10
11 #include "vm/allocation.h" 11 #include "vm/allocation.h"
12 #include "vm/dart_api_state.h" 12 #include "vm/dart_api_state.h"
13 #include "vm/isolate.h" 13 #include "vm/isolate.h"
14 #include "vm/log.h" 14 #include "vm/log.h"
15 #include "vm/pages.h" 15 #include "vm/pages.h"
16 #include "vm/raw_object.h" 16 #include "vm/raw_object.h"
17 #include "vm/stack_frame.h" 17 #include "vm/stack_frame.h"
18 #include "vm/store_buffer.h" 18 #include "vm/store_buffer.h"
19 #include "vm/thread_barrier.h" 19 #include "vm/thread_barrier.h"
20 #include "vm/thread_pool.h" 20 #include "vm/thread_pool.h"
21 #include "vm/visitor.h" 21 #include "vm/visitor.h"
22 #include "vm/object_id_ring.h" 22 #include "vm/object_id_ring.h"
23 23
24 namespace dart { 24 namespace dart {
25 25
26 DEFINE_FLAG(int, marker_tasks, 1, 26 DEFINE_FLAG(int, marker_tasks, 2,
27 "The number of tasks to spawn during old gen GC marking (0 means " 27 "The number of tasks to spawn during old gen GC marking (0 means "
28 "perform all marking on main thread)."); 28 "perform all marking on main thread).");
29 DEFINE_FLAG(bool, log_marker_tasks, false,
30 "Log debugging information for old gen GC marking tasks.");
29 31
30 class DelaySet { 32 class DelaySet {
31 private: 33 private:
32 typedef std::multimap<RawObject*, RawWeakProperty*> Map; 34 typedef std::multimap<RawObject*, RawWeakProperty*> Map;
33 typedef std::pair<RawObject*, RawWeakProperty*> MapEntry; 35 typedef std::pair<RawObject*, RawWeakProperty*> MapEntry;
34 36
35 public: 37 public:
36 DelaySet() : mutex_(new Mutex()) {} 38 DelaySet() : mutex_(new Mutex()) {}
37 ~DelaySet() { delete mutex_; } 39 ~DelaySet() { delete mutex_; }
38 40
39 // Returns 'true' if this inserted a new key (not just added a value). 41 // Atomically inserts raw_weak if its key is white, so that any future call to
40 bool Insert(RawWeakProperty* raw_weak) { 42 // VisitValuesForKey is guaranteed to include its value. Returns true on
43 // success, and false if the key is not white.
Ivan Posva 2015/10/08 19:56:12 Please also note the precondition that this thread
koda 2015/10/08 21:32:08 Done.
44 bool InsertIfWhite(RawWeakProperty* raw_weak) {
41 MutexLocker ml(mutex_); 45 MutexLocker ml(mutex_);
42 RawObject* raw_key = raw_weak->ptr()->key_; 46 RawObject* raw_key = raw_weak->ptr()->key_;
43 bool new_key = (delay_set_.find(raw_key) == delay_set_.end()); 47 if (raw_key->IsMarked()) return false;
48 // The key was white *after* acquiring the lock. Thus any future call to
49 // VisitValuesForKey is guaranteed to include the entry inserted below.
44 delay_set_.insert(std::make_pair(raw_key, raw_weak)); 50 delay_set_.insert(std::make_pair(raw_key, raw_weak));
45 return new_key; 51 return true;
46 } 52 }
47 53
48 void ClearReferences() { 54 void ClearReferences() {
49 MutexLocker ml(mutex_); 55 MutexLocker ml(mutex_);
50 for (Map::iterator it = delay_set_.begin(); it != delay_set_.end(); ++it) { 56 for (Map::iterator it = delay_set_.begin(); it != delay_set_.end(); ++it) {
57 ASSERT(!it->first->IsMarked());
51 WeakProperty::Clear(it->second); 58 WeakProperty::Clear(it->second);
52 } 59 }
53 } 60 }
54 61
55 // Visit all values with a key equal to raw_obj. 62 // Visit all values with a key equal to raw_obj, which must already be marked.
56 void VisitValuesForKey(RawObject* raw_obj, ObjectPointerVisitor* visitor) { 63 void VisitValuesForKey(RawObject* raw_obj, ObjectPointerVisitor* visitor) {
64 ASSERT(raw_obj->IsMarked());
57 // Extract the range into a temporary vector to iterate over it 65 // Extract the range into a temporary vector to iterate over it
58 // while delay_set_ may be modified. 66 // while delay_set_ may be modified.
59 std::vector<MapEntry> temp_copy; 67 std::vector<MapEntry> temp_copy;
60 { 68 {
61 MutexLocker ml(mutex_); 69 MutexLocker ml(mutex_);
62 std::pair<Map::iterator, Map::iterator> ret = 70 std::pair<Map::iterator, Map::iterator> ret =
63 delay_set_.equal_range(raw_obj); 71 delay_set_.equal_range(raw_obj);
64 temp_copy.insert(temp_copy.end(), ret.first, ret.second); 72 temp_copy.insert(temp_copy.end(), ret.first, ret.second);
65 delay_set_.erase(ret.first, ret.second); 73 delay_set_.erase(ret.first, ret.second);
66 } 74 }
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
133 skipped_code_functions_.Clear(); 141 skipped_code_functions_.Clear();
134 } 142 }
135 143
136 private: 144 private:
137 GrowableArray<RawFunction*> skipped_code_functions_; 145 GrowableArray<RawFunction*> skipped_code_functions_;
138 146
139 DISALLOW_COPY_AND_ASSIGN(SkippedCodeFunctions); 147 DISALLOW_COPY_AND_ASSIGN(SkippedCodeFunctions);
140 }; 148 };
141 149
142 150
143 class MarkingVisitor : public ObjectPointerVisitor { 151 class WorkList : public ValueObject {
Ivan Posva 2015/10/08 19:56:12 Thanks! Maybe rename to MarkerWorkList?
koda 2015/10/08 21:32:07 Done.
144 public: 152 public:
145 MarkingVisitor(Isolate* isolate, 153 explicit WorkList(MarkingStack* marking_stack)
154 : marking_stack_(marking_stack) {
155 work_ = marking_stack_->PopEmptyBlock();
156 }
157
158 ~WorkList() {
159 ASSERT(work_ == NULL);
160 ASSERT(marking_stack_ == NULL);
161 }
162
163 // Returns NULL if no more work was found.
164 RawObject* Pop() {
165 ASSERT(work_ != NULL);
166 if (work_->IsEmpty()) {
167 // TODO(koda): Track over/underflow events and use in heuristics to
168 // distribute work and prevent degenerate flip-flopping.
169 MarkingStack::Block* new_work = marking_stack_->PopNonEmptyBlock();
170 if (new_work == NULL) {
171 return NULL;
172 }
173 marking_stack_->PushBlock(work_);
174 work_ = new_work;
175 }
176 return work_->Pop();
177 }
178
179 void Push(RawObject* raw_obj) {
180 if (work_->IsFull()) {
181 // TODO(koda): Track over/underflow events and use in heuristics to
182 // distribute work and prevent degenerate flip-flopping.
183 marking_stack_->PushBlock(work_);
184 work_ = marking_stack_->PopEmptyBlock();
185 }
186 work_->Push(raw_obj);
187 }
188
189 void AbandonWork() {
190 marking_stack_->PushBlock(work_);
191 work_ = marking_stack_->PopEmptyBlock();
192 }
193
194 void Finalize() {
195 ASSERT(work_->IsEmpty());
196 marking_stack_->PushBlock(work_);
197 work_ = NULL;
198 // Fail fast on attempts to mark after finalizing.
199 marking_stack_ = NULL;
200 }
201
202 private:
203 MarkingStack::Block* work_;
204 MarkingStack* marking_stack_;
205 };
206
207
208 template<bool sync>
209 class MarkingVisitorBase : public ObjectPointerVisitor {
210 public:
211 MarkingVisitorBase(Isolate* isolate,
146 Heap* heap, 212 Heap* heap,
147 PageSpace* page_space, 213 PageSpace* page_space,
148 MarkingStack* marking_stack, 214 MarkingStack* marking_stack,
149 DelaySet* delay_set, 215 DelaySet* delay_set,
150 SkippedCodeFunctions* skipped_code_functions) 216 SkippedCodeFunctions* skipped_code_functions)
151 : ObjectPointerVisitor(isolate), 217 : ObjectPointerVisitor(isolate),
152 thread_(Thread::Current()), 218 thread_(Thread::Current()),
153 heap_(heap), 219 heap_(heap),
154 vm_heap_(Dart::vm_isolate()->heap()), 220 vm_heap_(Dart::vm_isolate()->heap()),
155 class_stats_count_(isolate->class_table()->NumCids()), 221 class_stats_count_(isolate->class_table()->NumCids()),
(...skipping 27 matching lines...) Expand all
183 // Returns true if some non-zero amount of work was performed. 249 // Returns true if some non-zero amount of work was performed.
184 bool DrainMarkingStack() { 250 bool DrainMarkingStack() {
185 RawObject* raw_obj = work_list_.Pop(); 251 RawObject* raw_obj = work_list_.Pop();
186 if (raw_obj == NULL) { 252 if (raw_obj == NULL) {
187 ASSERT(visiting_old_object_ == NULL); 253 ASSERT(visiting_old_object_ == NULL);
188 return false; 254 return false;
189 } 255 }
190 do { 256 do {
191 VisitingOldObject(raw_obj); 257 VisitingOldObject(raw_obj);
192 const intptr_t class_id = raw_obj->GetClassId(); 258 const intptr_t class_id = raw_obj->GetClassId();
193 // Currently, classes are considered roots (see issue 18284), so at this
194 // point, they should all be marked.
195 ASSERT(isolate()->class_table()->At(class_id)->IsMarked());
196 if (class_id != kWeakPropertyCid) { 259 if (class_id != kWeakPropertyCid) {
197 marked_bytes_ += raw_obj->VisitPointers(this); 260 marked_bytes_ += raw_obj->VisitPointers(this);
198 } else { 261 } else {
199 RawWeakProperty* raw_weak = reinterpret_cast<RawWeakProperty*>(raw_obj); 262 RawWeakProperty* raw_weak = reinterpret_cast<RawWeakProperty*>(raw_obj);
200 marked_bytes_ += raw_weak->Size(); 263 marked_bytes_ += raw_weak->Size();
201 ProcessWeakProperty(raw_weak); 264 ProcessWeakProperty(raw_weak);
202 } 265 }
203 raw_obj = work_list_.Pop(); 266 raw_obj = work_list_.Pop();
204 } while (raw_obj != NULL); 267 } while (raw_obj != NULL);
205 VisitingOldObject(NULL); 268 VisitingOldObject(NULL);
206 return true; 269 return true;
207 } 270 }
208 271
209 void VisitPointers(RawObject** first, RawObject** last) { 272 void VisitPointers(RawObject** first, RawObject** last) {
210 for (RawObject** current = first; current <= last; current++) { 273 for (RawObject** current = first; current <= last; current++) {
211 MarkObject(*current, current); 274 MarkObject(*current, current);
212 } 275 }
213 } 276 }
214 277
215 bool visit_function_code() const { 278 bool visit_function_code() const {
216 return skipped_code_functions_ == NULL; 279 return skipped_code_functions_ == NULL;
217 } 280 }
218 281
219 virtual void add_skipped_code_function(RawFunction* func) { 282 virtual void add_skipped_code_function(RawFunction* func) {
220 ASSERT(!visit_function_code()); 283 ASSERT(!visit_function_code());
221 skipped_code_functions_->Add(func); 284 skipped_code_functions_->Add(func);
222 } 285 }
223 286
224 // Returns the mark bit. Sets the watch bit if unmarked. (The prior value of 287 // If unmarked, sets the watch bit and returns true.
225 // the watched bit is returned in 'watched_before' for validation purposes.) 288 // If marked, does nothing and returns false.
226 // TODO(koda): When synchronizing header bits, this goes in a single CAS loop. 289 static bool EnsureWatchedIfWhite(RawObject* obj) {
227 static bool EnsureWatchedIfWhite(RawObject* obj, bool* watched_before) { 290 if (!sync) {
228 if (obj->IsMarked()) { 291 if (obj->IsMarked()) return false;
229 return false; 292 if (!obj->IsWatched()) obj->SetWatchedBitUnsynchronized();
293 return true;
230 } 294 }
231 if (!obj->IsWatched()) { 295 uword tags = obj->ptr()->tags_;
232 *watched_before = false; 296 uword old_tags;
233 obj->SetWatchedBitUnsynchronized(); 297 do {
234 } else { 298 old_tags = tags;
235 *watched_before = true; 299 if (RawObject::MarkBit::decode(tags)) return false;
236 } 300 if (RawObject::WatchedBit::decode(tags)) return true;
301 uword new_tags = RawObject::WatchedBit::update(true, old_tags);
302 tags = AtomicOperations::CompareAndSwapWord(
303 &obj->ptr()->tags_, old_tags, new_tags);
304 } while (tags != old_tags);
237 return true; 305 return true;
238 } 306 }
239 307
240 void ProcessWeakProperty(RawWeakProperty* raw_weak) { 308 void ProcessWeakProperty(RawWeakProperty* raw_weak) {
241 // The fate of the weak property is determined by its key. 309 // The fate of the weak property is determined by its key.
242 RawObject* raw_key = raw_weak->ptr()->key_; 310 RawObject* raw_key = raw_weak->ptr()->key_;
243 bool watched_before = false;
244 if (raw_key->IsHeapObject() && 311 if (raw_key->IsHeapObject() &&
245 raw_key->IsOldObject() && 312 raw_key->IsOldObject() &&
246 EnsureWatchedIfWhite(raw_key, &watched_before)) { 313 EnsureWatchedIfWhite(raw_key) &&
247 // Key is white. Delay the weak property. 314 delay_set_->InsertIfWhite(raw_weak)) {
248 bool new_key = delay_set_->Insert(raw_weak); 315 // Key was white. Delayed the weak property.
249 ASSERT(new_key == !watched_before);
250 } else { 316 } else {
251 // Key is gray or black. Make the weak property black. 317 // Key is gray or black. Make the weak property black.
252 raw_weak->VisitPointers(this); 318 raw_weak->VisitPointers(this);
253 } 319 }
254 } 320 }
255 321
256 // Called when all marking is complete. 322 // Called when all marking is complete.
257 void Finalize() { 323 void Finalize() {
258 work_list_.Finalize(); 324 work_list_.Finalize();
259 if (skipped_code_functions_ != NULL) { 325 if (skipped_code_functions_ != NULL) {
260 skipped_code_functions_->DetachCode(); 326 skipped_code_functions_->DetachCode();
261 } 327 }
262 } 328 }
263 329
264 void VisitingOldObject(RawObject* obj) { 330 void VisitingOldObject(RawObject* obj) {
265 ASSERT((obj == NULL) || obj->IsOldObject()); 331 ASSERT((obj == NULL) || obj->IsOldObject());
266 visiting_old_object_ = obj; 332 visiting_old_object_ = obj;
267 } 333 }
268 334
335 void AbandonWork() {
Ivan Posva 2015/10/08 19:56:12 Please document where this is used.
koda 2015/10/08 21:32:07 Removed.
336 work_list_.AbandonWork();
337 }
338
269 private: 339 private:
270 class WorkList : public ValueObject { 340 void PushMarked(RawObject* raw_obj) {
271 public:
272 explicit WorkList(MarkingStack* marking_stack)
273 : marking_stack_(marking_stack) {
274 work_ = marking_stack_->PopEmptyBlock();
275 }
276
277 ~WorkList() {
278 ASSERT(work_ == NULL);
279 ASSERT(marking_stack_ == NULL);
280 }
281
282 // Returns NULL if no more work was found.
283 RawObject* Pop() {
284 ASSERT(work_ != NULL);
285 if (work_->IsEmpty()) {
286 // TODO(koda): Track over/underflow events and use in heuristics to
287 // distribute work and prevent degenerate flip-flopping.
288 MarkingStack::Block* new_work = marking_stack_->PopNonEmptyBlock();
289 if (new_work == NULL) {
290 return NULL;
291 }
292 marking_stack_->PushBlock(work_);
293 work_ = new_work;
294 }
295 return work_->Pop();
296 }
297
298 void Push(RawObject* raw_obj) {
299 if (work_->IsFull()) {
300 // TODO(koda): Track over/underflow events and use in heuristics to
301 // distribute work and prevent degenerate flip-flopping.
302 marking_stack_->PushBlock(work_);
303 work_ = marking_stack_->PopEmptyBlock();
304 }
305 work_->Push(raw_obj);
306 }
307
308 void Finalize() {
309 ASSERT(work_->IsEmpty());
310 marking_stack_->PushBlock(work_);
311 work_ = NULL;
312 // Fail fast on attempts to mark after finalizing.
313 marking_stack_ = NULL;
314 }
315
316 private:
317 MarkingStack::Block* work_;
318 MarkingStack* marking_stack_;
319 };
320
321 void MarkAndPush(RawObject* raw_obj) {
322 ASSERT(raw_obj->IsHeapObject()); 341 ASSERT(raw_obj->IsHeapObject());
323 ASSERT((FLAG_verify_before_gc || FLAG_verify_before_gc) ? 342 ASSERT((FLAG_verify_before_gc || FLAG_verify_before_gc) ?
324 page_space_->Contains(RawObject::ToAddr(raw_obj)) : 343 page_space_->Contains(RawObject::ToAddr(raw_obj)) :
325 true); 344 true);
326 345
327 // Mark the object and push it on the marking stack. 346 // Push the marked object on the marking stack.
328 ASSERT(!raw_obj->IsMarked()); 347 ASSERT(raw_obj->IsMarked());
329 const bool is_watched = raw_obj->IsWatched(); 348 const bool is_watched = raw_obj->IsWatched();
330 raw_obj->SetMarkBitUnsynchronized(); 349 // We acquired the mark bit => no other task is modifying the header.
350 // TODO(koda): For concurrent mutator, this needs synchronization. Consider
351 // clearing these bits already in the CAS for the mark bit.
331 raw_obj->ClearRememberedBitUnsynchronized(); 352 raw_obj->ClearRememberedBitUnsynchronized();
332 raw_obj->ClearWatchedBitUnsynchronized(); 353 raw_obj->ClearWatchedBitUnsynchronized();
333 if (is_watched) { 354 if (is_watched) {
334 delay_set_->VisitValuesForKey(raw_obj, this); 355 delay_set_->VisitValuesForKey(raw_obj, this);
335 } 356 }
336 work_list_.Push(raw_obj); 357 work_list_.Push(raw_obj);
337 } 358 }
338 359
360 static bool TryAcquireMarkBit(RawObject* raw_obj) {
361 if (!sync) {
362 if (raw_obj->IsMarked()) return false;
363 raw_obj->SetMarkBitUnsynchronized();
364 return true;
365 }
366 return raw_obj->TryAcquireMarkBit();
367 }
368
339 void MarkObject(RawObject* raw_obj, RawObject** p) { 369 void MarkObject(RawObject* raw_obj, RawObject** p) {
340 // Fast exit if the raw object is a Smi. 370 // Fast exit if the raw object is a Smi.
341 if (!raw_obj->IsHeapObject()) { 371 if (!raw_obj->IsHeapObject()) {
342 return; 372 return;
343 } 373 }
344 374
345 // Fast exit if the raw object is marked. 375 // Fast exit if the raw object is marked.
346 if (raw_obj->IsMarked()) { 376 if (raw_obj->IsMarked()) {
347 return; 377 return;
348 } 378 }
349 379
350 // Skip over new objects, but verify consistency of heap while at it. 380 // TODO(koda): Investigate performance impact of alternative branching:
381 // if (smi or new) <-- can be done as single compare + conditional jump
382 // if (smi) return;
383 // else ...
384 // if (marked) return;
385 // ...
351 if (raw_obj->IsNewObject()) { 386 if (raw_obj->IsNewObject()) {
352 // TODO(iposva): Add consistency check. 387 ProcessNewSpaceObject(raw_obj, p);
353 if ((visiting_old_object_ != NULL) && 388 return;
354 !visiting_old_object_->IsRemembered()) { 389 }
355 ASSERT(p != NULL); 390
356 visiting_old_object_->SetRememberedBitUnsynchronized(); 391 if (!TryAcquireMarkBit(raw_obj)) {
357 thread_->StoreBufferAddObjectGC(visiting_old_object_); 392 // Already marked.
358 }
359 return; 393 return;
360 } 394 }
361 if (RawObject::IsVariableSizeClassId(raw_obj->GetClassId())) { 395 if (RawObject::IsVariableSizeClassId(raw_obj->GetClassId())) {
362 UpdateLiveOld(raw_obj->GetClassId(), raw_obj->Size()); 396 UpdateLiveOld(raw_obj->GetClassId(), raw_obj->Size());
363 } else { 397 } else {
364 UpdateLiveOld(raw_obj->GetClassId(), 0); 398 UpdateLiveOld(raw_obj->GetClassId(), 0);
365 } 399 }
366 400
367 MarkAndPush(raw_obj); 401 PushMarked(raw_obj);
402 }
403
404 static bool TryAcquireRememberedBit(RawObject* raw_obj) {
405 if (!sync) {
406 if (raw_obj->IsRemembered()) return false;
407 raw_obj->SetRememberedBitUnsynchronized();
408 return true;
409 }
410 return raw_obj->TryAcquireRememberedBit();
411 }
412
413 void ProcessNewSpaceObject(RawObject* raw_obj, RawObject** p) {
414 // TODO(iposva): Add consistency check.
415 if ((visiting_old_object_ != NULL) &&
416 TryAcquireRememberedBit(visiting_old_object_)) {
417 // NOTE: We pass in the pointer to the address we are visiting
418 // allows us to get a distance from the object start. At some
419 // point we might want to store exact addresses in store buffers
420 // for locations far enough from the header, so that we do not
421 // need to walk big objects only to find the single new
422 // reference in the last word during scavenge. This doesn't seem
423 // to be a problem though currently.
424 ASSERT(p != NULL);
425 thread_->StoreBufferAddObjectGC(visiting_old_object_);
426 }
368 } 427 }
369 428
370 void UpdateLiveOld(intptr_t class_id, intptr_t size) { 429 void UpdateLiveOld(intptr_t class_id, intptr_t size) {
371 // TODO(koda): Support growing the array once mutator runs concurrently. 430 // TODO(koda): Support growing the array once mutator runs concurrently.
372 ASSERT(class_id < class_stats_count_.length()); 431 ASSERT(class_id < class_stats_count_.length());
373 class_stats_count_[class_id] += 1; 432 class_stats_count_[class_id] += 1;
374 class_stats_size_[class_id] += size; 433 class_stats_size_[class_id] += size;
375 } 434 }
376 435
377 Thread* thread_; 436 Thread* thread_;
378 Heap* heap_; 437 Heap* heap_;
379 Heap* vm_heap_; 438 Heap* vm_heap_;
380 GrowableArray<intptr_t> class_stats_count_; 439 GrowableArray<intptr_t> class_stats_count_;
381 GrowableArray<intptr_t> class_stats_size_; 440 GrowableArray<intptr_t> class_stats_size_;
382 PageSpace* page_space_; 441 PageSpace* page_space_;
383 WorkList work_list_; 442 WorkList work_list_;
384 DelaySet* delay_set_; 443 DelaySet* delay_set_;
385 RawObject* visiting_old_object_; 444 RawObject* visiting_old_object_;
386 SkippedCodeFunctions* skipped_code_functions_; 445 SkippedCodeFunctions* skipped_code_functions_;
387 uintptr_t marked_bytes_; 446 uintptr_t marked_bytes_;
388 447
389 DISALLOW_IMPLICIT_CONSTRUCTORS(MarkingVisitor); 448 DISALLOW_IMPLICIT_CONSTRUCTORS(MarkingVisitorBase);
390 }; 449 };
391 450
392 451
452 typedef MarkingVisitorBase<false> UnsyncMarkingVisitor;
453 typedef MarkingVisitorBase<true> SyncMarkingVisitor;
454
455
393 static bool IsUnreachable(const RawObject* raw_obj) { 456 static bool IsUnreachable(const RawObject* raw_obj) {
394 if (!raw_obj->IsHeapObject()) { 457 if (!raw_obj->IsHeapObject()) {
395 return false; 458 return false;
396 } 459 }
397 if (raw_obj == Object::null()) { 460 if (raw_obj == Object::null()) {
398 return true; 461 return true;
399 } 462 }
400 if (!raw_obj->IsOldObject()) { 463 if (!raw_obj->IsOldObject()) {
401 return false; 464 return false;
402 } 465 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
435 498
436 void GCMarker::Epilogue(Isolate* isolate, bool invoke_api_callbacks) { 499 void GCMarker::Epilogue(Isolate* isolate, bool invoke_api_callbacks) {
437 if (invoke_api_callbacks && (isolate->gc_epilogue_callback() != NULL)) { 500 if (invoke_api_callbacks && (isolate->gc_epilogue_callback() != NULL)) {
438 (isolate->gc_epilogue_callback())(); 501 (isolate->gc_epilogue_callback())();
439 } 502 }
440 } 503 }
441 504
442 505
443 void GCMarker::IterateRoots(Isolate* isolate, 506 void GCMarker::IterateRoots(Isolate* isolate,
444 ObjectPointerVisitor* visitor, 507 ObjectPointerVisitor* visitor,
445 bool visit_prologue_weak_persistent_handles) { 508 bool visit_prologue_weak_persistent_handles,
446 isolate->VisitObjectPointers(visitor, 509 intptr_t slice_index, intptr_t num_slices) {
447 visit_prologue_weak_persistent_handles, 510 ASSERT(0 <= slice_index && slice_index < num_slices);
448 StackFrameIterator::kDontValidateFrames); 511 if (slice_index == 0 || num_slices <= 1) {
Ivan Posva 2015/10/08 19:56:12 ()
koda 2015/10/08 21:32:07 Done x2.
449 heap_->new_space()->VisitObjectPointers(visitor); 512 isolate->VisitObjectPointers(visitor,
513 visit_prologue_weak_persistent_handles,
514 StackFrameIterator::kDontValidateFrames);
515 }
516 if (slice_index == 1 || num_slices <= 1) {
517 heap_->new_space()->VisitObjectPointers(visitor);
518 }
519
520 // For now, we just distinguish two parts of the root set, so any remaining
521 // slices are empty.
450 } 522 }
451 523
452 524
453 void GCMarker::IterateWeakRoots(Isolate* isolate, 525 void GCMarker::IterateWeakRoots(Isolate* isolate,
454 HandleVisitor* visitor, 526 HandleVisitor* visitor,
455 bool visit_prologue_weak_persistent_handles) { 527 bool visit_prologue_weak_persistent_handles) {
456 ApiState* state = isolate->api_state(); 528 ApiState* state = isolate->api_state();
457 ASSERT(state != NULL); 529 ASSERT(state != NULL);
458 isolate->VisitWeakPersistentHandles(visitor, 530 isolate->VisitWeakPersistentHandles(visitor,
459 visit_prologue_weak_persistent_handles); 531 visit_prologue_weak_persistent_handles);
460 } 532 }
461 533
462 534
535 template<class MarkingVisitorType>
463 void GCMarker::IterateWeakReferences(Isolate* isolate, 536 void GCMarker::IterateWeakReferences(Isolate* isolate,
464 MarkingVisitor* visitor) { 537 MarkingVisitorType* visitor) {
465 ApiState* state = isolate->api_state(); 538 ApiState* state = isolate->api_state();
466 ASSERT(state != NULL); 539 ASSERT(state != NULL);
467 while (true) { 540 while (true) {
468 WeakReferenceSet* queue = state->delayed_weak_reference_sets(); 541 WeakReferenceSet* queue = state->delayed_weak_reference_sets();
469 if (queue == NULL) { 542 if (queue == NULL) {
470 // The delay queue is empty therefore no clean-up is required. 543 // The delay queue is empty therefore no clean-up is required.
471 return; 544 return;
472 } 545 }
473 state->set_delayed_weak_reference_sets(NULL); 546 state->set_delayed_weak_reference_sets(NULL);
474 while (queue != NULL) { 547 while (queue != NULL) {
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
569 class MarkTask : public ThreadPool::Task { 642 class MarkTask : public ThreadPool::Task {
570 public: 643 public:
571 MarkTask(GCMarker* marker, 644 MarkTask(GCMarker* marker,
572 Isolate* isolate, 645 Isolate* isolate,
573 Heap* heap, 646 Heap* heap,
574 PageSpace* page_space, 647 PageSpace* page_space,
575 MarkingStack* marking_stack, 648 MarkingStack* marking_stack,
576 DelaySet* delay_set, 649 DelaySet* delay_set,
577 ThreadBarrier* barrier, 650 ThreadBarrier* barrier,
578 bool collect_code, 651 bool collect_code,
579 bool visit_prologue_weak_persistent_handles) 652 bool visit_prologue_weak_persistent_handles,
653 intptr_t task_index,
654 intptr_t num_tasks,
655 uintptr_t* num_busy)
580 : marker_(marker), 656 : marker_(marker),
581 isolate_(isolate), 657 isolate_(isolate),
582 heap_(heap), 658 heap_(heap),
583 page_space_(page_space), 659 page_space_(page_space),
584 marking_stack_(marking_stack), 660 marking_stack_(marking_stack),
585 delay_set_(delay_set), 661 delay_set_(delay_set),
586 barrier_(barrier), 662 barrier_(barrier),
587 collect_code_(collect_code), 663 collect_code_(collect_code),
588 visit_prologue_weak_persistent_handles_( 664 visit_prologue_weak_persistent_handles_(
589 visit_prologue_weak_persistent_handles) { 665 visit_prologue_weak_persistent_handles),
666 task_index_(task_index),
667 num_tasks_(num_tasks),
668 num_busy_(num_busy) {
590 } 669 }
591 670
592 virtual void Run() { 671 virtual void Run() {
593 Thread::EnterIsolateAsHelper(isolate_, true); 672 Thread::EnterIsolateAsHelper(isolate_, true);
594 { 673 {
595 StackZone stack_zone(Thread::Current()); 674 StackZone stack_zone(Thread::Current());
596 Zone* zone = stack_zone.GetZone(); 675 Zone* zone = stack_zone.GetZone();
597 SkippedCodeFunctions* skipped_code_functions = 676 SkippedCodeFunctions* skipped_code_functions =
598 collect_code_ ? new(zone) SkippedCodeFunctions() : NULL; 677 collect_code_ ? new(zone) SkippedCodeFunctions() : NULL;
599 MarkingVisitor visitor(isolate_, heap_, page_space_, marking_stack_, 678 SyncMarkingVisitor visitor(isolate_, heap_, page_space_, marking_stack_,
600 delay_set_, skipped_code_functions); 679 delay_set_, skipped_code_functions);
601 // Phase 1: Populate and drain marking stack in task. 680 // Phase 1: Iterate over roots and drain marking stack in tasks.
602 // TODO(koda): Split root iteration work among multiple tasks.
603 marker_->IterateRoots(isolate_, &visitor, 681 marker_->IterateRoots(isolate_, &visitor,
604 visit_prologue_weak_persistent_handles_); 682 visit_prologue_weak_persistent_handles_,
605 visitor.DrainMarkingStack(); 683 task_index_, num_tasks_);
684 do {
685 visitor.DrainMarkingStack();
686
687 // I can't find more work right now. If no other task is busy,
688 // then there will never be more work (NB: 1 is *before* decrement).
689 if (AtomicOperations::FetchAndDecrement(num_busy_) == 1) break;
690
691 // Wait for some work to appear.
692 // TODO(iposva): Replace busy-waiting with a solution using Monitor,
693 // and redraw the boundaries between stack/visitor/task as needed.
694 while (marking_stack_->IsEmpty() &&
695 AtomicOperations::LoadRelaxed(num_busy_) > 0) {
696 }
697
698 // If no tasks are busy, there will never be more work.
699 if (AtomicOperations::LoadRelaxed(num_busy_) == 0) break;
700
701 // I saw some work; get busy and compete for it.
702 AtomicOperations::FetchAndIncrement(num_busy_);
703 } while (true);
704 ASSERT(AtomicOperations::LoadRelaxed(num_busy_) == 0);
606 barrier_->Sync(); 705 barrier_->Sync();
706
607 // Phase 2: Weak processing and follow-up marking on main thread. 707 // Phase 2: Weak processing and follow-up marking on main thread.
608 barrier_->Sync(); 708 barrier_->Sync();
709
609 // Phase 3: Finalize results from all markers (detach code, etc.). 710 // Phase 3: Finalize results from all markers (detach code, etc.).
711 if (FLAG_log_marker_tasks) {
712 THR_Print("Task %" Pd " marked %" Pd " bytes.\n",
713 task_index_, visitor.marked_bytes());
714 }
610 marker_->FinalizeResultsFrom(&visitor); 715 marker_->FinalizeResultsFrom(&visitor);
611 } 716 }
612 Thread::ExitIsolateAsHelper(true); 717 Thread::ExitIsolateAsHelper(true);
718
613 // This task is done. Notify the original thread. 719 // This task is done. Notify the original thread.
614 barrier_->Exit(); 720 barrier_->Exit();
615 } 721 }
616 722
617 private: 723 private:
618 GCMarker* marker_; 724 GCMarker* marker_;
619 Isolate* isolate_; 725 Isolate* isolate_;
620 Heap* heap_; 726 Heap* heap_;
621 PageSpace* page_space_; 727 PageSpace* page_space_;
622 MarkingStack* marking_stack_; 728 MarkingStack* marking_stack_;
623 DelaySet* delay_set_; 729 DelaySet* delay_set_;
624 ThreadBarrier* barrier_; 730 ThreadBarrier* barrier_;
625 bool collect_code_; 731 bool collect_code_;
626 bool visit_prologue_weak_persistent_handles_; 732 bool visit_prologue_weak_persistent_handles_;
733 const intptr_t task_index_;
734 const intptr_t num_tasks_;
735 uintptr_t* num_busy_;
627 736
628 DISALLOW_COPY_AND_ASSIGN(MarkTask); 737 DISALLOW_COPY_AND_ASSIGN(MarkTask);
629 }; 738 };
630 739
631 740
632 void GCMarker::FinalizeResultsFrom(MarkingVisitor* visitor) { 741 template<class MarkingVisitorType>
742 void GCMarker::FinalizeResultsFrom(MarkingVisitorType* visitor) {
633 { 743 {
634 MutexLocker ml(&stats_mutex_); 744 MutexLocker ml(&stats_mutex_);
635 marked_bytes_ += visitor->marked_bytes(); 745 marked_bytes_ += visitor->marked_bytes();
636 // Class heap stats are not themselves thread-safe yet, so we update the 746 // Class heap stats are not themselves thread-safe yet, so we update the
637 // stats while holding stats_mutex_. 747 // stats while holding stats_mutex_.
638 ClassTable* table = heap_->isolate()->class_table(); 748 ClassTable* table = heap_->isolate()->class_table();
639 for (intptr_t i = 0; i < table->NumCids(); ++i) { 749 for (intptr_t i = 0; i < table->NumCids(); ++i) {
640 const intptr_t count = visitor->live_count(i); 750 const intptr_t count = visitor->live_count(i);
641 if (count > 0) { 751 if (count > 0) {
642 const intptr_t size = visitor->live_size(i); 752 const intptr_t size = visitor->live_size(i);
(...skipping 17 matching lines...) Expand all
660 Zone* zone = stack_zone.GetZone(); 770 Zone* zone = stack_zone.GetZone();
661 MarkingStack marking_stack; 771 MarkingStack marking_stack;
662 DelaySet delay_set; 772 DelaySet delay_set;
663 const bool visit_prologue_weak_persistent_handles = !invoke_api_callbacks; 773 const bool visit_prologue_weak_persistent_handles = !invoke_api_callbacks;
664 marked_bytes_ = 0; 774 marked_bytes_ = 0;
665 const int num_tasks = FLAG_marker_tasks; 775 const int num_tasks = FLAG_marker_tasks;
666 if (num_tasks == 0) { 776 if (num_tasks == 0) {
667 // Mark everything on main thread. 777 // Mark everything on main thread.
668 SkippedCodeFunctions* skipped_code_functions = 778 SkippedCodeFunctions* skipped_code_functions =
669 collect_code ? new(zone) SkippedCodeFunctions() : NULL; 779 collect_code ? new(zone) SkippedCodeFunctions() : NULL;
670 MarkingVisitor mark(isolate, heap_, page_space, &marking_stack, 780 UnsyncMarkingVisitor mark(isolate, heap_, page_space, &marking_stack,
671 &delay_set, skipped_code_functions); 781 &delay_set, skipped_code_functions);
672 IterateRoots(isolate, &mark, visit_prologue_weak_persistent_handles); 782 IterateRoots(isolate, &mark, visit_prologue_weak_persistent_handles,
783 0, 1);
673 mark.DrainMarkingStack(); 784 mark.DrainMarkingStack();
674 IterateWeakReferences(isolate, &mark); 785 IterateWeakReferences(isolate, &mark);
675 MarkingWeakVisitor mark_weak; 786 MarkingWeakVisitor mark_weak;
676 IterateWeakRoots(isolate, &mark_weak, 787 IterateWeakRoots(isolate, &mark_weak,
677 !visit_prologue_weak_persistent_handles); 788 !visit_prologue_weak_persistent_handles);
678 // All marking done; detach code, etc. 789 // All marking done; detach code, etc.
679 FinalizeResultsFrom(&mark); 790 FinalizeResultsFrom(&mark);
680 } else { 791 } else {
681 if (num_tasks > 1) { 792 ThreadBarrier barrier(num_tasks + 1);
682 // TODO(koda): Support multiple: 793 // Used to coordinate draining among tasks; all start out as 'busy'.
683 // 1. non-concurrent tasks, after splitting root iteration work, then 794 uintptr_t num_busy = num_tasks;
684 // 2. concurrent tasks, after synchronizing headers. 795 // Phase 1: Iterate over roots and drain marking stack in tasks.
685 FATAL("Multiple marking tasks not yet supported"); 796 for (intptr_t i = 0; i < num_tasks; ++i) {
797 MarkTask* mark_task =
798 new MarkTask(this, isolate, heap_, page_space, &marking_stack,
799 &delay_set, &barrier, collect_code,
800 visit_prologue_weak_persistent_handles,
801 i, num_tasks, &num_busy);
802 ThreadPool* pool = Dart::thread_pool();
803 pool->Run(mark_task);
686 } 804 }
687 ThreadBarrier barrier(num_tasks + 1); // +1 for the main thread.
688 // Phase 1: Populate and drain marking stack in task.
689 MarkTask* mark_task =
690 new MarkTask(this, isolate, heap_, page_space, &marking_stack,
691 &delay_set, &barrier, collect_code,
692 visit_prologue_weak_persistent_handles);
693 ThreadPool* pool = Dart::thread_pool();
694 pool->Run(mark_task);
695 barrier.Sync(); 805 barrier.Sync();
806
696 // Phase 2: Weak processing and follow-up marking on main thread. 807 // Phase 2: Weak processing and follow-up marking on main thread.
697 SkippedCodeFunctions* skipped_code_functions = 808 SkippedCodeFunctions* skipped_code_functions =
698 collect_code ? new(zone) SkippedCodeFunctions() : NULL; 809 collect_code ? new(zone) SkippedCodeFunctions() : NULL;
699 MarkingVisitor mark(isolate, heap_, page_space, &marking_stack, 810 SyncMarkingVisitor mark(isolate, heap_, page_space, &marking_stack,
700 &delay_set, skipped_code_functions); 811 &delay_set, skipped_code_functions);
701 IterateWeakReferences(isolate, &mark); 812 IterateWeakReferences(isolate, &mark);
702 MarkingWeakVisitor mark_weak; 813 MarkingWeakVisitor mark_weak;
703 IterateWeakRoots(isolate, &mark_weak, 814 IterateWeakRoots(isolate, &mark_weak,
704 !visit_prologue_weak_persistent_handles); 815 !visit_prologue_weak_persistent_handles);
705 barrier.Sync(); 816 barrier.Sync();
817
706 // Phase 3: Finalize results from all markers (detach code, etc.). 818 // Phase 3: Finalize results from all markers (detach code, etc.).
819 if (FLAG_log_marker_tasks) {
820 THR_Print("Main thread marked %" Pd " bytes.\n",
821 mark.marked_bytes());
822 }
707 FinalizeResultsFrom(&mark); 823 FinalizeResultsFrom(&mark);
708 barrier.Exit(); 824 barrier.Exit();
709 } 825 }
710 delay_set.ClearReferences(); 826 delay_set.ClearReferences();
711 ProcessWeakTables(page_space); 827 ProcessWeakTables(page_space);
712 ProcessObjectIdTable(isolate); 828 ProcessObjectIdTable(isolate);
713 } 829 }
714 Epilogue(isolate, invoke_api_callbacks); 830 Epilogue(isolate, invoke_api_callbacks);
715 } 831 }
716 832
717 } // namespace dart 833 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/gc_marker.h ('k') | runtime/vm/heap_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698