Chromium Code Reviews| Index: runtime/vm/gc_marker.cc |
| diff --git a/runtime/vm/gc_marker.cc b/runtime/vm/gc_marker.cc |
| index 234bf973476eb04b5b2b17c9dc278fa104761486..a416692e6cf3b002fa420f14eb72520c77ea64a0 100644 |
| --- a/runtime/vm/gc_marker.cc |
| +++ b/runtime/vm/gc_marker.cc |
| @@ -15,6 +15,7 @@ |
| #include "vm/pages.h" |
| #include "vm/raw_object.h" |
| #include "vm/stack_frame.h" |
| +#include "vm/thread_pool.h" |
| #include "vm/visitor.h" |
| #include "vm/object_id_ring.h" |
| @@ -120,12 +121,63 @@ class MarkingStack : public ValueObject { |
| }; |
| +class DelaySet { |
| + private: |
| + typedef std::multimap<RawObject*, RawWeakProperty*> Map; |
| + typedef std::pair<RawObject*, RawWeakProperty*> MapEntry; |
| + |
| + public: |
| + DelaySet() : mutex_(new Mutex()) {} |
| + ~DelaySet() { delete mutex_; } |
| + |
| + // Returns 'true' if this inserted a new key (not just updated the value). |
| + bool Insert(RawWeakProperty* raw_weak) { |
| + MutexLocker ml(mutex_); |
| + RawObject* raw_key = raw_weak->ptr()->key_; |
| + bool new_key = (delay_set_.find(raw_key) == delay_set_.end()); |
| + delay_set_.insert(std::make_pair(raw_key, raw_weak)); |
| + return new_key; |
| + } |
| + |
| + void ClearAll() { |
| + MutexLocker ml(mutex_); |
| + Map::iterator it = delay_set_.begin(); |
| + for (; it != delay_set_.end(); ++it) { |
| + WeakProperty::Clear(it->second); |
| + } |
| + } |
| + |
| + // Visit all values with a key equal to raw_obj. |
| + void VisitValuesForKey(RawObject* raw_obj, ObjectPointerVisitor* visitor) { |
| + // Extract the range into a temporary vector to iterate over it |
| + // while delay_set_ may be modified. |
| + std::vector<MapEntry> temp_copy; |
| + { |
| + MutexLocker ml(mutex_); |
| + std::pair<Map::iterator, Map::iterator> ret = |
| + delay_set_.equal_range(raw_obj); |
| + temp_copy.insert(temp_copy.end(), ret.first, ret.second); |
| + delay_set_.erase(ret.first, ret.second); |
| + } |
| + for (std::vector<MapEntry>::iterator it = temp_copy.begin(); |
| + it != temp_copy.end(); ++it) { |
| + it->second->VisitPointers(visitor); |
| + } |
| + } |
| + |
| + private: |
| + Map delay_set_; |
| + Mutex* mutex_; |
| +}; |
| + |
| + |
| class MarkingVisitor : public ObjectPointerVisitor { |
| public: |
| MarkingVisitor(Isolate* isolate, |
| Heap* heap, |
| PageSpace* page_space, |
| MarkingStack* marking_stack, |
| + DelaySet* delay_set, |
| bool visit_function_code) |
| : ObjectPointerVisitor(isolate), |
| thread_(Thread::Current()), |
| @@ -134,12 +186,18 @@ class MarkingVisitor : public ObjectPointerVisitor { |
| class_table_(isolate->class_table()), |
| page_space_(page_space), |
| marking_stack_(marking_stack), |
| + delay_set_(delay_set), |
| visiting_old_object_(NULL), |
| visit_function_code_(visit_function_code) { |
| ASSERT(heap_ != vm_heap_); |
| ASSERT(thread_->isolate() == isolate); |
| } |
| + ~MarkingVisitor() { |
| + // 'Finalize' should be explicitly called before destruction. |
| + ASSERT(marking_stack_ == NULL); |
| + } |
| + |
| MarkingStack* marking_stack() const { return marking_stack_; } |
| void VisitPointers(RawObject** first, RawObject** last) { |
| @@ -154,26 +212,45 @@ class MarkingVisitor : public ObjectPointerVisitor { |
| return &skipped_code_functions_; |
| } |
| - void DelayWeakProperty(RawWeakProperty* raw_weak) { |
| + // Returns the mark bit. Sets the watch bit if unmarked. (The prior value of |
| + // the watched bit is returned in 'watched_before' for validation purposes.) |
| + // TODO(koda): When synchronizing header bits, this goes in a single CAS loop. |
| + static bool EnsureWatchedIfWhite(RawObject* obj, bool* watched_before) { |
| + if (obj->IsMarked()) { |
| + return false; |
| + } |
| + if (!obj->IsWatched()) { |
| + *watched_before = false; |
| + obj->SetWatchedBitUnsynchronized(); |
| + } else { |
| + *watched_before = true; |
| + } |
| + return true; |
| + } |
| + |
| + void ProcessWeakProperty(RawWeakProperty* raw_weak) { |
| + // The fate of the weak property is determined by its key. |
| RawObject* raw_key = raw_weak->ptr()->key_; |
| - DelaySet::iterator it = delay_set_.find(raw_key); |
| - if (it != delay_set_.end()) { |
| - ASSERT(raw_key->IsWatched()); |
| + bool watched_before = false; |
| + if (raw_key->IsHeapObject() && |
| + raw_key->IsOldObject() && |
| + EnsureWatchedIfWhite(raw_key, &watched_before)) { |
| + // Key is white. Delay the weak property. |
| + bool new_key = delay_set_->Insert(raw_weak); |
| + ASSERT(new_key == !watched_before); |
| } else { |
| - ASSERT(!raw_key->IsWatched()); |
| - raw_key->SetWatchedBitUnsynchronized(); |
| + // Key is gray or black. Make the weak property black. |
| + raw_weak->VisitPointers(this); |
| } |
| - delay_set_.insert(std::make_pair(raw_key, raw_weak)); |
| } |
| + // Called when all marking is complete. |
| void Finalize() { |
| - DelaySet::iterator it = delay_set_.begin(); |
| - for (; it != delay_set_.end(); ++it) { |
| - WeakProperty::Clear(it->second); |
| - } |
| if (!visit_function_code_) { |
| DetachCode(); |
| } |
| + // Fail fast on attempts to mark after finalizing. |
| + marking_stack_ = NULL; |
| } |
| void VisitingOldObject(RawObject* obj) { |
| @@ -195,17 +272,7 @@ class MarkingVisitor : public ObjectPointerVisitor { |
| raw_obj->ClearRememberedBitUnsynchronized(); |
| raw_obj->ClearWatchedBitUnsynchronized(); |
| if (is_watched) { |
| - std::pair<DelaySet::iterator, DelaySet::iterator> ret; |
| - // Visit all elements with a key equal to raw_obj. |
| - ret = delay_set_.equal_range(raw_obj); |
| - // Create a copy of the range in a temporary vector to iterate over it |
| - // while delay_set_ may be modified. |
| - std::vector<DelaySetEntry> temp_copy(ret.first, ret.second); |
| - delay_set_.erase(ret.first, ret.second); |
| - for (std::vector<DelaySetEntry>::iterator it = temp_copy.begin(); |
| - it != temp_copy.end(); ++it) { |
| - it->second->VisitPointers(this); |
| - } |
| + delay_set_->VisitValuesForKey(raw_obj, this); |
| } |
| marking_stack_->Push(raw_obj); |
| } |
| @@ -285,6 +352,8 @@ class MarkingVisitor : public ObjectPointerVisitor { |
| ISL_Print(" total detached unoptimized: %" Pd "\n", |
| unoptimized_code_count); |
| } |
| + // Clean up (even if the array is currently Zone-allocated). |
| + skipped_code_functions_.Clear(); |
| } |
| Thread* thread_; |
| @@ -293,10 +362,8 @@ class MarkingVisitor : public ObjectPointerVisitor { |
| ClassTable* class_table_; |
| PageSpace* page_space_; |
| MarkingStack* marking_stack_; |
| + DelaySet* delay_set_; |
| RawObject* visiting_old_object_; |
| - typedef std::multimap<RawObject*, RawWeakProperty*> DelaySet; |
| - typedef std::pair<RawObject*, RawWeakProperty*> DelaySetEntry; |
| - DelaySet delay_set_; |
| const bool visit_function_code_; |
| GrowableArray<RawFunction*> skipped_code_functions_; |
| @@ -449,29 +516,13 @@ void GCMarker::DrainMarkingStack(Isolate* isolate, |
| } else { |
| RawWeakProperty* raw_weak = reinterpret_cast<RawWeakProperty*>(raw_obj); |
| marked_bytes_ += raw_weak->Size(); |
| - ProcessWeakProperty(raw_weak, visitor); |
| + visitor->ProcessWeakProperty(raw_weak); |
| } |
| } |
| visitor->VisitingOldObject(NULL); |
| } |
| -void GCMarker::ProcessWeakProperty(RawWeakProperty* raw_weak, |
| - MarkingVisitor* visitor) { |
| - // The fate of the weak property is determined by its key. |
| - RawObject* raw_key = raw_weak->ptr()->key_; |
| - if (raw_key->IsHeapObject() && |
| - raw_key->IsOldObject() && |
| - !raw_key->IsMarked()) { |
| - // Key is white. Delay the weak property. |
| - visitor->DelayWeakProperty(raw_weak); |
| - } else { |
| - // Key is gray or black. Make the weak property black. |
| - raw_weak->VisitPointers(visitor); |
| - } |
| -} |
| - |
| - |
| void GCMarker::ProcessWeakTables(PageSpace* page_space) { |
| for (int sel = 0; |
| sel < Heap::kNumWeakSelectors; |
| @@ -519,6 +570,43 @@ void GCMarker::ProcessObjectIdTable(Isolate* isolate) { |
| } |
| +class DrainTask : public ThreadPool::Task { |
| + public: |
| + DrainTask(GCMarker* marker, |
| + Isolate* isolate, |
| + PageSpace* page_space, |
| + MarkingVisitor* visitor) |
| + : marker_(marker), |
| + isolate_(isolate), |
| + page_space_(page_space), |
| + visitor_(visitor) { |
| + MonitorLocker ml(page_space->tasks_lock()); |
| + page_space->set_tasks(page_space->tasks() + 1); |
| + ml.NotifyAll(); |
|
Ivan Posva
2015/06/16 17:21:00
Why NotifyAll()? Do you really intend to wakeup al
koda
2015/06/16 19:41:44
Removed.
|
| + } |
| + |
| + virtual ~DrainTask() {} |
| + |
| + virtual void Run() { |
| + Thread::EnterIsolateAsHelper(isolate_); |
| + marker_->DrainMarkingStack(isolate_, visitor_); |
| + // This task is done. Notify the original thread. |
| + { |
| + MonitorLocker ml(page_space_->tasks_lock()); |
| + page_space_->set_tasks(page_space_->tasks() - 1); |
| + ml.NotifyAll(); |
|
Ivan Posva
2015/06/16 17:21:00
ditto
koda
2015/06/16 19:41:43
Changed to Notify.
|
| + } |
| + Thread::ExitIsolateAsHelper(); |
| + } |
| + |
| + private: |
| + GCMarker* marker_; |
| + Isolate* isolate_; |
| + PageSpace* page_space_; |
| + MarkingVisitor* visitor_; |
| +}; |
| + |
| + |
| void GCMarker::MarkObjects(Isolate* isolate, |
| PageSpace* page_space, |
| bool invoke_api_callbacks, |
| @@ -530,14 +618,40 @@ void GCMarker::MarkObjects(Isolate* isolate, |
| { |
| StackZone zone(isolate); |
| MarkingStack marking_stack; |
| - MarkingVisitor mark( |
| - isolate, heap_, page_space, &marking_stack, visit_function_code); |
| - IterateRoots(isolate, &mark, !invoke_api_callbacks); |
| - DrainMarkingStack(isolate, &mark); |
| - IterateWeakReferences(isolate, &mark); |
| + DelaySet delay_set; |
| + const int kNumMarkers = 2; |
| + GrowableArray<MarkingVisitor*> markers; |
| + for (int i = 0; i < kNumMarkers; ++i) { |
|
Ivan Posva
2015/06/16 17:21:00
It would be much more readable if the DrainTask wo
koda
2015/06/16 19:41:44
Yes, but ThreadPool deletes each Task after it has
|
| + markers.Add(new MarkingVisitor( |
| + isolate, heap_, page_space, &marking_stack, &delay_set, |
| + visit_function_code)); |
| + } |
| + // Arbitrarily pick a visitor for the roots. |
| + MarkingVisitor* roots_visitor = markers[0]; |
| + // No parallelism/concurrcy yet, but use a separate visitor to drain the |
| + // marking stack on a separate thread, just to show we can. |
| + MarkingVisitor* main_visitor = markers[1]; |
| + IterateRoots(isolate, roots_visitor, !invoke_api_callbacks); |
| + Dart::thread_pool()->Run( |
| + new DrainTask(this, isolate, page_space, main_visitor)); |
| + // Wait for task to finish. |
| + // TODO(koda): Implement counting barrier utility class and use it here. |
| + { |
| + MonitorLocker locker(page_space->tasks_lock()); |
| + while (page_space->tasks() > 1) { |
| + locker.Wait(); |
| + } |
| + } |
| + IterateWeakReferences(isolate, roots_visitor); |
| MarkingWeakVisitor mark_weak; |
| IterateWeakRoots(isolate, &mark_weak, invoke_api_callbacks); |
| - mark.Finalize(); |
| + // All marking is done. |
| + for (int i = 0; i < kNumMarkers; ++i) { |
| + MarkingVisitor* marker = markers.RemoveLast(); |
| + marker->Finalize(); |
| + delete marker; |
| + } |
| + delay_set.ClearAll(); |
|
Ivan Posva
2015/06/16 17:21:00
ClearAll is ambiguous. How about ClearReferences()
koda
2015/06/16 19:41:44
Done.
|
| ProcessWeakTables(page_space); |
| ProcessObjectIdTable(isolate); |
| } |