Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |