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). | |
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.
| |
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 |