Index: src/heap/concurrent-marking-deque.h |
diff --git a/src/heap/concurrent-marking-deque.h b/src/heap/concurrent-marking-deque.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..daa0e2ba4c1ce9129c648eb7ba64881748e30bbb |
--- /dev/null |
+++ b/src/heap/concurrent-marking-deque.h |
@@ -0,0 +1,177 @@ |
+// Copyright 2017 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#ifndef V8_HEAP_CONCURRENT_MARKING_DEQUE_ |
+#define V8_HEAP_CONCURRENT_MARKING_DEQUE_ |
+ |
+#include <deque> |
+ |
+#include "src/base/platform/mutex.h" |
+ |
+namespace v8 { |
+namespace internal { |
+ |
+class Heap; |
+class Isolate; |
+class HeapObject; |
+ |
+enum class MarkingThread { kMain, kConcurrent }; |
+ |
+enum class TargetDeque { kShared, kBailout }; |
+ |
+// The concurrent marking deque supports deque operations for two threads: |
+// main and concurrent. It is implemented using two deques: shared and bailout. |
+// |
+// The concurrent thread can use the push and pop operations with the |
+// MarkingThread::kConcurrent argument. All other operations are intended |
+// to be used by the main thread only. |
+// |
+// The interface of the concurrent marking deque for the main thread matches |
+// that of the sequential marking deque, so they can be easily switched |
+// at compile time without updating the main thread call-sites. |
+// |
+// The shared deque is shared between the main thread and the concurrent |
+// thread, so both threads can push to and pop from the shared deque. |
+// The bailout deque stores objects that cannot be processed by the concurrent |
+// thread. Only the concurrent thread can push to it and only the main thread |
+// can pop from it. |
+class ConcurrentMarkingDeque { |
+ public: |
+ // The heap parameter is needed to match the interface |
+ // of the sequential marking deque. |
+ explicit ConcurrentMarkingDeque(Heap* heap) {} |
+ |
+ // Pushes the object into the specified deque assuming that the function is |
+ // called on the specified thread. The main thread can push only to the shared |
+ // deque. The concurrent thread can push to both deques. |
+ bool Push(HeapObject* object, MarkingThread thread = MarkingThread::kMain, |
+ TargetDeque target = TargetDeque::kShared) { |
+ DCHECK_IMPLIES(thread == MarkingThread::kMain, |
+ target == TargetDeque::kShared); |
+ switch (target) { |
+ case TargetDeque::kShared: |
+ shared_deque_.Push(object); |
+ break; |
+ case TargetDeque::kBailout: |
+ bailout_deque_.Push(object); |
+ break; |
+ } |
+ return true; |
+ } |
+ |
+ // Pops an object from the bailout or shared deque assuming that the function |
+ // is called on the specified thread. The main thread first tries to pop the |
+ // bailout deque. If the deque is empty then it tries the shared deque. |
+ // If the shared deque is also empty, then the function returns nullptr. |
+ // The concurrent thread pops only from the shared deque. |
+ HeapObject* Pop(MarkingThread thread = MarkingThread::kMain) { |
+ if (thread == MarkingThread::kMain) { |
+ HeapObject* result = bailout_deque_.Pop(); |
+ if (result != nullptr) return result; |
+ } |
+ return shared_deque_.Pop(); |
+ } |
+ |
+ // All the following operations can used only by the main thread. |
+ void Clear() { |
+ bailout_deque_.Clear(); |
+ shared_deque_.Clear(); |
+ } |
+ |
+ bool IsFull() { return false; } |
+ |
+ bool IsEmpty() { return bailout_deque_.IsEmpty() && shared_deque_.IsEmpty(); } |
+ |
+ int Size() { return bailout_deque_.Size() + shared_deque_.Size(); } |
+ |
+ // This is used for a large array with a progress bar. |
+ // For simpicity, unshift to the bailout deque so that the concurrent thread |
+ // does not see such objects. |
+ bool Unshift(HeapObject* object) { |
+ bailout_deque_.Unshift(object); |
+ return true; |
+ } |
+ |
+ // Calls the specified callback on each element of the deques and replaces |
+ // the element with the result of the callback. If the callback returns |
+ // nullptr then the element is removed from the deque. |
+ // The callback must accept HeapObject* and return HeapObject*. |
+ template <typename Callback> |
+ void Update(Callback callback) { |
+ bailout_deque_.Update(callback); |
+ shared_deque_.Update(callback); |
+ } |
+ |
+ // These empty functions are needed to match the interface |
+ // of the sequential marking deque. |
+ void SetUp() {} |
+ void TearDown() {} |
+ void StartUsing() {} |
+ void StopUsing() {} |
+ void ClearOverflowed() {} |
+ void SetOverflowed() {} |
+ bool overflowed() const { return false; } |
+ |
+ private: |
+ // Simple, slow, and thread-safe deque that forwards all operations to |
+ // a lock-protected std::deque. |
+ class Deque { |
+ public: |
+ Deque() { cache_padding_[0] = 0; } |
+ void Clear() { |
+ base::LockGuard<base::Mutex> guard(&mutex_); |
+ return deque_.clear(); |
+ } |
+ bool IsEmpty() { |
+ base::LockGuard<base::Mutex> guard(&mutex_); |
+ return deque_.empty(); |
+ } |
+ int Size() { |
+ base::LockGuard<base::Mutex> guard(&mutex_); |
+ return static_cast<int>(deque_.size()); |
+ } |
+ void Push(HeapObject* object) { |
+ base::LockGuard<base::Mutex> guard(&mutex_); |
+ deque_.push_back(object); |
+ } |
+ HeapObject* Pop() { |
+ base::LockGuard<base::Mutex> guard(&mutex_); |
+ if (deque_.empty()) return nullptr; |
+ HeapObject* result = deque_.back(); |
+ deque_.pop_back(); |
+ return result; |
+ } |
+ void Unshift(HeapObject* object) { |
+ base::LockGuard<base::Mutex> guard(&mutex_); |
+ deque_.push_front(object); |
+ } |
+ template <typename Callback> |
+ void Update(Callback callback) { |
+ base::LockGuard<base::Mutex> guard(&mutex_); |
+ std::deque<HeapObject*> new_deque; |
+ for (auto object : deque_) { |
+ HeapObject* new_object = callback(object); |
+ if (new_object) { |
+ new_deque.push_back(new_object); |
+ } |
+ } |
+ deque_.swap(new_deque); |
+ } |
+ |
+ private: |
+ base::Mutex mutex_; |
+ std::deque<HeapObject*> deque_; |
+ // Ensure that two deques do not share the same cache line. |
+ static int const kCachePadding = 64; |
+ char cache_padding_[kCachePadding]; |
+ }; |
+ Deque bailout_deque_; |
+ Deque shared_deque_; |
+ DISALLOW_COPY_AND_ASSIGN(ConcurrentMarkingDeque); |
+}; |
+ |
+} // namespace internal |
+} // namespace v8 |
+ |
+#endif // V8_CONCURRENT_MARKING_DEQUE_ |