v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
threaded-list.h
Go to the documentation of this file.
1// Copyright 2018 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_BASE_THREADED_LIST_H_
6#define V8_BASE_THREADED_LIST_H_
7
8#include <iterator>
9
11#include "src/base/macros.h"
12
13namespace v8 {
14namespace base {
15
16template <typename T>
18 static T** next(T* t) { return t->next(); }
19 static T** start(T** t) { return t; }
20 static T* const* start(T* const* t) { return t; }
21};
22
23// Represents a linked list that threads through the nodes in the linked list.
24// Entries in the list are pointers to nodes. By default nodes need to have a
25// T** next() method that returns the location where the next value is stored.
26// The kSupportsUnsafeInsertion flag defines whether the list supports insertion
27// of new elements into the list by just rewiring the next pointers without
28// updating the list object itself. Such an insertion might invalidate the
29// pointer to list tail and thus requires additional steps to recover the
30// pointer to the tail.
31// The default can be overwritten by providing a ThreadedTraits class.
32template <typename T, typename BaseClass,
33 typename TLTraits = ThreadedListTraits<T>,
34 bool kSupportsUnsafeInsertion = false>
35class ThreadedListBase final : public BaseClass {
36 public:
37 ThreadedListBase() : head_(nullptr), tail_(&head_) {}
40
41 void Add(T* v) {
44 DCHECK_NULL(*TLTraits::next(v));
45 *tail_ = v;
46 tail_ = TLTraits::next(v);
47 // Check that only one element was added (and that hasn't created a cycle).
49 }
50
51 void AddFront(T* v) {
52 DCHECK_NULL(*TLTraits::next(v));
54 T** const next = TLTraits::next(v);
55
56 *next = head_;
57 if (head_ == nullptr) tail_ = next;
58 head_ = v;
59 }
60
61 // This temporarily breaks the tail_ invariant, and it should only be called
62 // if we support unsafe insertions.
63 static void AddAfter(T* after_this, T* v) {
64 DCHECK(kSupportsUnsafeInsertion);
65 DCHECK_NULL(*TLTraits::next(v));
66 *TLTraits::next(v) = *TLTraits::next(after_this);
67 *TLTraits::next(after_this) = v;
68 }
69
70 void DropHead() {
72
73 T* old_head = head_;
74 head_ = *TLTraits::next(head_);
75 if (head_ == nullptr) tail_ = &head_;
76 *TLTraits::next(old_head) = nullptr;
77 }
78
79 bool Contains(T* v) {
80 for (Iterator it = begin(); it != end(); ++it) {
81 if (*it == v) return true;
82 }
83 return false;
84 }
85
86 void Append(ThreadedListBase&& list) {
87 if (list.is_empty()) return;
88
90 *tail_ = list.head_;
91 tail_ = list.tail_;
92 list.Clear();
93 }
94
96 if (list.head_ == nullptr) return;
97
99 T* new_head = list.head_;
100 *list.tail_ = head_;
101 if (head_ == nullptr) {
102 tail_ = list.tail_;
103 }
104 head_ = new_head;
105 list.Clear();
106 }
107
108 // This is only valid if {v} is in the current list.
109 void TruncateAt(ThreadedListBase* rem, T* v) {
110 CHECK_NOT_NULL(rem);
112 CHECK(rem->is_empty());
113 Iterator it = begin();
114 T* last = nullptr;
115 for (; it != end(); ++it) {
116 if (*it == v) {
117 break;
118 }
119 last = *it;
120 }
121 CHECK_EQ(v, *it);
122
123 // Remaining list.
124 rem->head_ = v;
125 rem->tail_ = tail_;
126
127 if (last == nullptr) {
128 // The head must point to v, so we return the empty list.
129 CHECK_EQ(head_, v);
130 Clear();
131 } else {
132 tail_ = TLTraits::next(last);
133 *tail_ = nullptr;
134 }
135 }
136
137 void Clear() {
138 head_ = nullptr;
139 tail_ = &head_;
140 }
141
143 head_ = other.head_;
144 tail_ = other.head_ ? other.tail_ : &head_;
145#ifdef DEBUG
146 other.Clear();
147#endif
148 return *this;
149 }
150
152 : head_(other.head_),
153 tail_(other.head_ ? other.tail_ : &head_) {
154#ifdef DEBUG
155 other.Clear();
156#endif
157 }
158
159 bool Remove(T* v) {
160 T* current = first();
161 if (current == v) {
162 DropHead();
163 return true;
164 }
165
167 while (current != nullptr) {
168 T* next = *TLTraits::next(current);
169 if (next == v) {
170 *TLTraits::next(current) = *TLTraits::next(next);
171 *TLTraits::next(next) = nullptr;
172
173 if (TLTraits::next(next) == tail_) {
174 tail_ = TLTraits::next(current);
175 }
176 return true;
177 }
178 current = next;
179 }
180 return false;
181 }
182
183 class Iterator final {
184 public:
185 using iterator_category = std::forward_iterator_tag;
186 using difference_type = std::ptrdiff_t;
187 using value_type = T*;
190
191 public:
193 entry_ = TLTraits::next(*entry_);
194 return *this;
195 }
196 bool operator==(const Iterator& other) const {
197 return entry_ == other.entry_;
198 }
199 bool operator!=(const Iterator& other) const {
200 return entry_ != other.entry_;
201 }
202 T*& operator*() { return *entry_; }
203 T* operator->() { return *entry_; }
204 Iterator& operator=(T* entry) {
205 T* next = *TLTraits::next(*entry_);
206 *TLTraits::next(entry) = next;
207 *entry_ = entry;
208 return *this;
209 }
210
211 bool is_null() { return entry_ == nullptr; }
212
213 void InsertBefore(T* value) {
214 T* old_entry_value = *entry_;
215 *entry_ = value;
216 entry_ = TLTraits::next(value);
217 *entry_ = old_entry_value;
218 }
219
220 Iterator() : entry_(nullptr) {}
221
222 private:
223 explicit Iterator(T** entry) : entry_(entry) {}
224
226
227 friend class ThreadedListBase;
228 };
229
230 class ConstIterator final {
231 public:
232 using iterator_category = std::forward_iterator_tag;
233 using difference_type = std::ptrdiff_t;
234 using value_type = T*;
235 using reference = const value_type;
236 using pointer = const value_type*;
237
238 // Allow implicit conversion to const iterator.
239 // NOLINTNEXTLINE
241
242 public:
244 entry_ = TLTraits::next(*entry_);
245 return *this;
246 }
247 bool operator==(const ConstIterator& other) const {
248 return entry_ == other.entry_;
249 }
250 bool operator!=(const ConstIterator& other) const {
251 return entry_ != other.entry_;
252 }
253 const T* operator*() const { return *entry_; }
254
255 private:
256 explicit ConstIterator(T* const* entry) : entry_(entry) {}
257
258 T* const* entry_;
259
260 friend class ThreadedListBase;
261 };
262
263 Iterator begin() { return Iterator(TLTraits::start(&head_)); }
266 return Iterator(tail_);
267 }
268
269 ConstIterator begin() const { return ConstIterator(TLTraits::start(&head_)); }
272 return ConstIterator(tail_);
273 }
274
275 // Rewinds the list's tail to the reset point, i.e., cutting of the rest of
276 // the list, including the reset_point.
277 void Rewind(Iterator reset_point) {
278 tail_ = reset_point.entry_;
279 *tail_ = nullptr;
280 }
281
282 // Moves the tail of the from_list, starting at the from_location, to the end
283 // of this list.
284 void MoveTail(ThreadedListBase* from_list, Iterator from_location) {
285 if (from_list->end() != from_location) {
287 *tail_ = *from_location;
288 tail_ = from_list->tail_;
289 from_list->Rewind(from_location);
290 }
291 }
292
293 // Removes the element at `it`, and returns a new iterator pointing to the
294 // element following the removed element (if `it` was pointing to the last
295 // element, then `end()` is returned). The head and the tail are updated. `it`
296 // should not be `end()`. Iterators that are currently on the same element as
297 // `it` are invalidated. Other iterators are not affected. If the last element
298 // is removed, existing `end()` iterators will be invalidated.
300 if (*it.entry_ == head_) {
301 DropHead();
302 return begin();
303 } else if (tail_ == TLTraits::next(*it.entry_)) {
304 tail_ = it.entry_;
305 *it.entry_ = nullptr;
306 return end();
307 } else {
308 T* old_entry = *it.entry_;
309 *it.entry_ = *TLTraits::next(*it.entry_);
310 *TLTraits::next(old_entry) = nullptr;
311 return Iterator(it.entry_);
312 }
313 }
314
315 bool is_empty() const { return head_ == nullptr; }
316
317 T* first() const { return head_; }
318
319 // Slow. For testing purposes.
321 int result = 0;
322 for (Iterator t = begin(); t != end(); ++t) ++result;
323 return result;
324 }
325
326 T* AtForTest(int i) {
327 Iterator t = begin();
328 while (i-- > 0) ++t;
329 return *t;
330 }
331
332 bool Verify() const {
333 T* last = this->first();
334 if (last == nullptr) {
336 } else {
337 while (*TLTraits::next(last) != nullptr) {
338 last = *TLTraits::next(last);
339 }
340 CHECK_EQ(TLTraits::next(last), tail_);
341 }
342 return true;
343 }
344
345 inline void EnsureValidTail() const {
346 if (!kSupportsUnsafeInsertion) {
347 DCHECK_EQ(*tail_, nullptr);
348 return;
349 }
350 // If kSupportsUnsafeInsertion, then we support adding a new element by
351 // using the pointer to a certain element. E.g., imagine list A -> B -> C,
352 // we can add D after B, by just moving the pointer of B to D and D to
353 // whatever B used to point to. We do not need to know the beginning of the
354 // list (ie. to have a pointer to the ThreadList class). This however might
355 // break the tail_ invariant. We ensure this here, by manually looking for
356 // the tail of the list.
357 if (*tail_ == nullptr) return;
358 T* last = *tail_;
359 if (last != nullptr) {
360 while (*TLTraits::next(last) != nullptr) {
361 last = *TLTraits::next(last);
362 }
363 tail_ = TLTraits::next(last);
364 }
365 }
366
367 private:
369 mutable T** tail_; // We need to ensure a valid `tail_` even when using a
370 // const Iterator.
371};
372
373struct EmptyBase {};
374
375// Check ThreadedListBase::EnsureValidTail.
376static constexpr bool kUnsafeInsertion = true;
377
378template <typename T, typename TLTraits = ThreadedListTraits<T>>
380
381template <typename T, typename TLTraits = ThreadedListTraits<T>>
384
385} // namespace base
386} // namespace v8
387
388#endif // V8_BASE_THREADED_LIST_H_
#define T
bool operator!=(const ConstIterator &other) const
std::forward_iterator_tag iterator_category
bool operator==(const ConstIterator &other) const
bool operator!=(const Iterator &other) const
bool operator==(const Iterator &other) const
std::forward_iterator_tag iterator_category
void TruncateAt(ThreadedListBase *rem, T *v)
ConstIterator end() const
Iterator RemoveAt(Iterator it)
ThreadedListBase & operator=(ThreadedListBase &&other) V8_NOEXCEPT
ThreadedListBase(ThreadedListBase &&other) V8_NOEXCEPT
ConstIterator begin() const
void Prepend(ThreadedListBase &&list)
ThreadedListBase(const ThreadedListBase &)=delete
ThreadedListBase & operator=(const ThreadedListBase &)=delete
void MoveTail(ThreadedListBase *from_list, Iterator from_location)
void Append(ThreadedListBase &&list)
void Rewind(Iterator reset_point)
static void AddAfter(T *after_this, T *v)
ZoneVector< RpoNumber > & result
static constexpr bool kUnsafeInsertion
#define V8_NOEXCEPT
#define DCHECK_NULL(val)
Definition logging.h:491
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define CHECK_NOT_NULL(val)
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
static T *const * start(T *const *t)
std::unique_ptr< ValueMirror > value