5 #ifndef V8_HEAP_WORKLIST_H_ 6 #define V8_HEAP_WORKLIST_H_ 11 #include "src/base/atomic-utils.h" 12 #include "src/base/logging.h" 13 #include "src/base/macros.h" 14 #include "src/base/platform/mutex.h" 15 #include "testing/gtest/include/gtest/gtest_prod.h" 27 template <
typename EntryType,
int SEGMENT_SIZE>
33 : worklist_(worklist), task_id_(task_id) {}
36 bool Push(EntryType entry) {
return worklist_->Push(task_id_, entry); }
39 bool Pop(EntryType* entry) {
return worklist_->Pop(task_id_, entry); }
42 bool IsLocalEmpty() {
return worklist_->IsLocalEmpty(task_id_); }
46 bool IsEmpty() {
return worklist_->IsEmpty(); }
48 bool IsGlobalPoolEmpty() {
return worklist_->IsGlobalPoolEmpty(); }
50 size_t LocalPushSegmentSize() {
51 return worklist_->LocalPushSegmentSize(task_id_);
59 static const int kMaxNumTasks = 8;
60 static const size_t kSegmentCapacity = SEGMENT_SIZE;
64 explicit Worklist(
int num_tasks) : num_tasks_(num_tasks) {
65 DCHECK_LE(num_tasks, kMaxNumTasks);
66 for (
int i = 0;
i < num_tasks_;
i++) {
67 private_push_segment(
i) = NewSegment();
68 private_pop_segment(
i) = NewSegment();
74 for (
int i = 0;
i < num_tasks_;
i++) {
75 DCHECK_NOT_NULL(private_push_segment(
i));
76 DCHECK_NOT_NULL(private_pop_segment(
i));
77 delete private_push_segment(
i);
78 delete private_pop_segment(
i);
84 void Swap(Worklist<EntryType, SEGMENT_SIZE>& other) {
85 CHECK(AreLocalsEmpty());
86 CHECK(other.AreLocalsEmpty());
88 global_pool_.Swap(other.global_pool_);
91 bool Push(
int task_id, EntryType entry) {
92 DCHECK_LT(task_id, num_tasks_);
93 DCHECK_NOT_NULL(private_push_segment(task_id));
94 if (!private_push_segment(task_id)->Push(entry)) {
95 PublishPushSegmentToGlobal(task_id);
96 bool success = private_push_segment(task_id)->Push(entry);
103 bool Pop(
int task_id, EntryType* entry) {
104 DCHECK_LT(task_id, num_tasks_);
105 DCHECK_NOT_NULL(private_pop_segment(task_id));
106 if (!private_pop_segment(task_id)->Pop(entry)) {
107 if (!private_push_segment(task_id)->IsEmpty()) {
108 Segment* tmp = private_pop_segment(task_id);
109 private_pop_segment(task_id) = private_push_segment(task_id);
110 private_push_segment(task_id) = tmp;
111 }
else if (!StealPopSegmentFromGlobal(task_id)) {
114 bool success = private_pop_segment(task_id)->Pop(entry);
121 size_t LocalPushSegmentSize(
int task_id) {
122 return private_push_segment(task_id)->Size();
125 bool IsLocalEmpty(
int task_id) {
126 return private_pop_segment(task_id)->IsEmpty() &&
127 private_push_segment(task_id)->IsEmpty();
130 bool IsGlobalPoolEmpty() {
return global_pool_.IsEmpty(); }
133 if (!AreLocalsEmpty())
return false;
134 return global_pool_.IsEmpty();
137 bool AreLocalsEmpty() {
138 for (
int i = 0;
i < num_tasks_;
i++) {
139 if (!IsLocalEmpty(
i))
return false;
144 size_t LocalSize(
int task_id) {
145 return private_pop_segment(task_id)->Size() +
146 private_push_segment(task_id)->Size();
153 for (
int i = 0;
i < num_tasks_;
i++) {
154 private_pop_segment(
i)->Clear();
155 private_push_segment(
i)->Clear();
157 global_pool_.Clear();
168 template <
typename Callback>
169 void Update(Callback callback) {
170 for (
int i = 0;
i < num_tasks_;
i++) {
171 private_pop_segment(
i)->Update(callback);
172 private_push_segment(
i)->Update(callback);
174 global_pool_.Update(callback);
182 template <
typename Callback>
183 void Iterate(Callback callback) {
184 for (
int i = 0;
i < num_tasks_;
i++) {
185 private_pop_segment(
i)->Iterate(callback);
186 private_push_segment(
i)->Iterate(callback);
188 global_pool_.Iterate(callback);
191 template <
typename Callback>
192 void IterateGlobalPool(Callback callback) {
193 global_pool_.Iterate(callback);
196 void FlushToGlobal(
int task_id) {
197 PublishPushSegmentToGlobal(task_id);
198 PublishPopSegmentToGlobal(task_id);
201 void MergeGlobalPool(Worklist* other) {
202 auto pair = other->global_pool_.Extract();
203 global_pool_.MergeList(pair.first, pair.second);
207 FRIEND_TEST(WorkListTest, SegmentCreate);
208 FRIEND_TEST(WorkListTest, SegmentPush);
209 FRIEND_TEST(WorkListTest, SegmentPushPop);
210 FRIEND_TEST(WorkListTest, SegmentIsEmpty);
211 FRIEND_TEST(WorkListTest, SegmentIsFull);
212 FRIEND_TEST(WorkListTest, SegmentClear);
213 FRIEND_TEST(WorkListTest, SegmentFullPushFails);
214 FRIEND_TEST(WorkListTest, SegmentEmptyPopFails);
215 FRIEND_TEST(WorkListTest, SegmentUpdateFalse);
216 FRIEND_TEST(WorkListTest, SegmentUpdate);
220 static const size_t kCapacity = kSegmentCapacity;
222 Segment() : index_(0) {}
224 bool Push(EntryType entry) {
225 if (IsFull())
return false;
226 entries_[index_++] = entry;
230 bool Pop(EntryType* entry) {
231 if (IsEmpty())
return false;
232 *entry = entries_[--index_];
236 size_t Size()
const {
return index_; }
237 bool IsEmpty()
const {
return index_ == 0; }
238 bool IsFull()
const {
return index_ == kCapacity; }
239 void Clear() { index_ = 0; }
241 template <
typename Callback>
242 void Update(Callback callback) {
243 size_t new_index = 0;
244 for (
size_t i = 0;
i < index_;
i++) {
245 if (callback(entries_[
i], &entries_[new_index])) {
252 template <
typename Callback>
253 void Iterate(Callback callback)
const {
254 for (
size_t i = 0;
i < index_;
i++) {
255 callback(entries_[
i]);
259 Segment* next()
const {
return next_; }
260 void set_next(Segment* segment) { next_ = segment; }
265 EntryType entries_[kCapacity];
268 struct PrivateSegmentHolder {
269 Segment* private_push_segment;
270 Segment* private_pop_segment;
271 char cache_line_padding[64];
276 GlobalPool() : top_(nullptr) {}
279 void Swap(GlobalPool& other) {
280 Segment* temp = top_;
285 V8_INLINE
void Push(Segment* segment) {
286 base::MutexGuard guard(&lock_);
287 segment->set_next(top_);
291 V8_INLINE
bool Pop(Segment** segment) {
292 base::MutexGuard guard(&lock_);
293 if (top_ !=
nullptr) {
295 set_top(top_->next());
301 V8_INLINE
bool IsEmpty() {
302 return base::AsAtomicPointer::Relaxed_Load(&top_) ==
nullptr;
306 base::MutexGuard guard(&lock_);
307 Segment* current = top_;
308 while (current !=
nullptr) {
309 Segment* tmp = current;
310 current = current->next();
317 template <
typename Callback>
318 void Update(Callback callback) {
319 base::MutexGuard guard(&lock_);
320 Segment* prev =
nullptr;
321 Segment* current = top_;
322 while (current !=
nullptr) {
323 current->Update(callback);
324 if (current->IsEmpty()) {
325 if (prev ==
nullptr) {
326 top_ = current->next();
328 prev->set_next(current->next());
330 Segment* tmp = current;
331 current = current->next();
335 current = current->next();
341 template <
typename Callback>
342 void Iterate(Callback callback) {
343 base::MutexGuard guard(&lock_);
344 for (Segment* current = top_; current !=
nullptr;
345 current = current->next()) {
346 current->Iterate(callback);
350 std::pair<Segment*, Segment*> Extract() {
351 Segment* top =
nullptr;
353 base::MutexGuard guard(&lock_);
354 if (top_ ==
nullptr)
return std::make_pair(
nullptr,
nullptr);
359 while (end->next() !=
nullptr) end = end->next();
360 return std::make_pair(top, end);
363 void MergeList(Segment* start, Segment* end) {
364 if (start ==
nullptr)
return;
366 base::MutexGuard guard(&lock_);
373 void set_top(Segment* segment) {
374 base::AsAtomicPointer::Relaxed_Store(&top_, segment);
381 V8_INLINE Segment*& private_push_segment(
int task_id) {
382 return private_segments_[task_id].private_push_segment;
385 V8_INLINE Segment*& private_pop_segment(
int task_id) {
386 return private_segments_[task_id].private_pop_segment;
389 V8_INLINE
void PublishPushSegmentToGlobal(
int task_id) {
390 if (!private_push_segment(task_id)->IsEmpty()) {
391 global_pool_.Push(private_push_segment(task_id));
392 private_push_segment(task_id) = NewSegment();
396 V8_INLINE
void PublishPopSegmentToGlobal(
int task_id) {
397 if (!private_pop_segment(task_id)->IsEmpty()) {
398 global_pool_.Push(private_pop_segment(task_id));
399 private_pop_segment(task_id) = NewSegment();
403 V8_INLINE
bool StealPopSegmentFromGlobal(
int task_id) {
404 if (global_pool_.IsEmpty())
return false;
405 Segment* new_segment =
nullptr;
406 if (global_pool_.Pop(&new_segment)) {
407 delete private_pop_segment(task_id);
408 private_pop_segment(task_id) = new_segment;
414 V8_INLINE Segment* NewSegment() {
416 return new Segment();
419 PrivateSegmentHolder private_segments_[kMaxNumTasks];
420 GlobalPool global_pool_;
427 #endif // V8_HEAP_WORKLIST_H_