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

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