Chromium Code Reviews| 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..a18d656d068dfa80e94c8b9debedc15d29df8982 |
| --- /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) and two deques (shared and bailout). |
|
Hannes Payer (out of office)
2017/05/02 15:53:20
it is implemented using two deques.
ulan
2017/05/02 16:33:03
Done.
|
| +// |
| +// 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_ |