OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #ifndef V8_HEAP_CONCURRENT_MARKING_DEQUE_ |
| 6 #define V8_HEAP_CONCURRENT_MARKING_DEQUE_ |
| 7 |
| 8 #include <deque> |
| 9 |
| 10 #include "src/base/platform/mutex.h" |
| 11 |
| 12 namespace v8 { |
| 13 namespace internal { |
| 14 |
| 15 class Heap; |
| 16 class Isolate; |
| 17 class HeapObject; |
| 18 |
| 19 enum class MarkingThread { kMain, kConcurrent }; |
| 20 |
| 21 enum class TargetDeque { kShared, kBailout }; |
| 22 |
| 23 // The concurrent marking deque supports deque operations for two threads |
| 24 // (main and concurrent) and two deques (shared and bailout). |
| 25 // |
| 26 // The concurrent thread can use the push and pop operations with the |
| 27 // MarkingThread::kConcurrent argument. All other operations are intended |
| 28 // to be used by the main thread only. |
| 29 // |
| 30 // The interface of the concurrent marking deque for the main thread matches |
| 31 // that of the sequential marking deque, so they can be easily switched |
| 32 // at compile time without updating the main thread call-sites. |
| 33 // |
| 34 // The shared deque is shared between the main thread and the concurrent |
| 35 // thread, so both threads can push to and pop from the shared deque. |
| 36 // The bailout deque stores objects that cannot be processed by the concurrent |
| 37 // thread. Only the concurrent thread can push to it and only the main thread |
| 38 // can pop from it. |
| 39 class ConcurrentMarkingDeque { |
| 40 public: |
| 41 // The heap parameter is needed to match the interface |
| 42 // of the sequential marking deque. |
| 43 explicit ConcurrentMarkingDeque(Heap* heap) {} |
| 44 |
| 45 // Pushes the object into the specified deque assuming that the function is |
| 46 // called on the specified thread. The main thread can push only to the shared |
| 47 // deque. The concurrent thread can push to both deques. |
| 48 bool Push(HeapObject* object, MarkingThread thread = MarkingThread::kMain, |
| 49 TargetDeque target = TargetDeque::kShared) { |
| 50 DCHECK_IMPLIES(thread == MarkingThread::kMain, |
| 51 target == TargetDeque::kShared); |
| 52 switch (target) { |
| 53 case TargetDeque::kShared: |
| 54 shared_deque_.Push(object); |
| 55 break; |
| 56 case TargetDeque::kBailout: |
| 57 bailout_deque_.Push(object); |
| 58 break; |
| 59 } |
| 60 return true; |
| 61 } |
| 62 |
| 63 // Pops an object from the bailout or shared deque assuming that the function |
| 64 // is called on the specified thread. The main thread first tries to pop the |
| 65 // bailout deque. If the deque is empty then it tries the shared deque. |
| 66 // If the shared deque is also empty, then the function returns nullptr. |
| 67 // The concurrent thread pops only from the shared deque. |
| 68 HeapObject* Pop(MarkingThread thread = MarkingThread::kMain) { |
| 69 if (thread == MarkingThread::kMain) { |
| 70 HeapObject* result = bailout_deque_.Pop(); |
| 71 if (result != nullptr) return result; |
| 72 } |
| 73 return shared_deque_.Pop(); |
| 74 } |
| 75 |
| 76 // All the following operations can used only by the main thread. |
| 77 void Clear() { |
| 78 bailout_deque_.Clear(); |
| 79 shared_deque_.Clear(); |
| 80 } |
| 81 |
| 82 bool IsFull() { return false; } |
| 83 |
| 84 bool IsEmpty() { return bailout_deque_.IsEmpty() && shared_deque_.IsEmpty(); } |
| 85 |
| 86 int Size() { return bailout_deque_.Size() + shared_deque_.Size(); } |
| 87 |
| 88 // This is used for a large array with a progress bar. |
| 89 // For simpicity, unshift to the bailout deque so that the concurrent thread |
| 90 // does not see such objects. |
| 91 bool Unshift(HeapObject* object) { |
| 92 bailout_deque_.Unshift(object); |
| 93 return true; |
| 94 } |
| 95 |
| 96 // Calls the specified callback on each element of the deques and replaces |
| 97 // the element with the result of the callback. If the callback returns |
| 98 // nullptr then the element is removed from the deque. |
| 99 // The callback must accept HeapObject* and return HeapObject*. |
| 100 template <typename Callback> |
| 101 void Update(Callback callback) { |
| 102 bailout_deque_.Update(callback); |
| 103 shared_deque_.Update(callback); |
| 104 } |
| 105 |
| 106 // These empty functions are needed to match the interface |
| 107 // of the sequential marking deque. |
| 108 void SetUp() {} |
| 109 void TearDown() {} |
| 110 void StartUsing() {} |
| 111 void StopUsing() {} |
| 112 void ClearOverflowed() {} |
| 113 void SetOverflowed() {} |
| 114 bool overflowed() const { return false; } |
| 115 |
| 116 private: |
| 117 // Simple, slow, and thread-safe deque that forwards all operations to |
| 118 // a lock-protected std::deque. |
| 119 class Deque { |
| 120 public: |
| 121 Deque() { cache_padding_[0] = 0; } |
| 122 void Clear() { |
| 123 base::LockGuard<base::Mutex> guard(&mutex_); |
| 124 return deque_.clear(); |
| 125 } |
| 126 bool IsEmpty() { |
| 127 base::LockGuard<base::Mutex> guard(&mutex_); |
| 128 return deque_.empty(); |
| 129 } |
| 130 int Size() { |
| 131 base::LockGuard<base::Mutex> guard(&mutex_); |
| 132 return static_cast<int>(deque_.size()); |
| 133 } |
| 134 void Push(HeapObject* object) { |
| 135 base::LockGuard<base::Mutex> guard(&mutex_); |
| 136 deque_.push_back(object); |
| 137 } |
| 138 HeapObject* Pop() { |
| 139 base::LockGuard<base::Mutex> guard(&mutex_); |
| 140 if (deque_.empty()) return nullptr; |
| 141 HeapObject* result = deque_.back(); |
| 142 deque_.pop_back(); |
| 143 return result; |
| 144 } |
| 145 void Unshift(HeapObject* object) { |
| 146 base::LockGuard<base::Mutex> guard(&mutex_); |
| 147 deque_.push_front(object); |
| 148 } |
| 149 template <typename Callback> |
| 150 void Update(Callback callback) { |
| 151 base::LockGuard<base::Mutex> guard(&mutex_); |
| 152 std::deque<HeapObject*> new_deque; |
| 153 for (auto object : deque_) { |
| 154 HeapObject* new_object = callback(object); |
| 155 if (new_object) { |
| 156 new_deque.push_back(new_object); |
| 157 } |
| 158 } |
| 159 deque_.swap(new_deque); |
| 160 } |
| 161 |
| 162 private: |
| 163 base::Mutex mutex_; |
| 164 std::deque<HeapObject*> deque_; |
| 165 // Ensure that two deques do not share the same cache line. |
| 166 static int const kCachePadding = 64; |
| 167 char cache_padding_[kCachePadding]; |
| 168 }; |
| 169 Deque bailout_deque_; |
| 170 Deque shared_deque_; |
| 171 DISALLOW_COPY_AND_ASSIGN(ConcurrentMarkingDeque); |
| 172 }; |
| 173 |
| 174 } // namespace internal |
| 175 } // namespace v8 |
| 176 |
| 177 #endif // V8_CONCURRENT_MARKING_DEQUE_ |
OLD | NEW |