11namespace Stroika::Foundation::Containers::LockFreeDataStructures {
19 class forward_list<T>::ForwardIterator_ {
20 friend class forward_list;
22 using iterator_category = std::forward_iterator_tag;
30 : current{kTerminalSentinel_}
33 ForwardIterator_ (node_* n)
34 : current{n != kTerminalSentinel_ ? increment_reference_count_ (n) : kTerminalSentinel_}
37 ForwardIterator_ (
const ForwardIterator_& other)
38 : current{other.current != kTerminalSentinel_ ? increment_reference_count_ (other.current) : kTerminalSentinel_}
41 ForwardIterator_ (ForwardIterator_&& other) noexcept
42 : current{kTerminalSentinel_}
44 std::swap (current, other.current);
48 if (current == kTerminalSentinel_) {
51 decrement_reference_count_ (current);
53 ForwardIterator_&
operator= (ForwardIterator_
const& other)
55 if (current != kTerminalSentinel_) {
56 decrement_reference_count_ (current);
58 current = other.current != kTerminalSentinel_ ? increment_reference_count_ (other.current) : kTerminalSentinel_;
62 ForwardIterator_&
operator= (
const V& rhs)
64 if (current != kTerminalSentinel_) {
65 decrement_reference_count_ (current);
67 current = rhs.current != kTerminalSentinel_ ? increment_reference_count_ (rhs.current) : kTerminalSentinel_;
72 if (current == kTerminalSentinel_) {
73 throw std::logic_error{
"invalid iterator"};
75 return current->value;
79 if (current == kTerminalSentinel_) {
80 throw std::logic_error{
"invalid iterator"};
82 return ¤t->value;
84 ForwardIterator_& operator++ ()
86 Assert (current != kTerminalSentinel_);
87 node_* temp = new_ownership_ (current->next);
88 std::swap (current, temp);
89 if (temp != kTerminalSentinel_) {
90 decrement_reference_count_ (temp);
94 ForwardIterator_ operator++ (
int)
96 Assert (current != kTerminalSentinel_);
97 ForwardIterator_ temp = *
this;
101 friend void swap (ForwardIterator_& a, ForwardIterator_& b)
noexcept
104 swap (a.current, b.current);
106 operator ForwardIterator_<const T> ()
const
108 return ForwardIterator_<const T> (current);
110 bool operator== (
const ForwardIterator_& rhs)
const
112 return current == rhs.current;
122 template <
typename T>
123 class forward_list<T>::node_ {
125 friend class ForwardIterator_<T>;
127 std::atomic<node_*> next;
128 std::atomic<int> referenceCount;
130 node_ (T
const& value)
132 , next{kTerminalSentinel_}
137 : value{
std::move (value)}
138 , next{kTerminalSentinel_}
142 template <
typename... U>
143 node_ (U&&... params)
144 : value{
std::forward<U> (params)...}
145 , next{kTerminalSentinel_}
151 node_* n = owner_lock_ (next);
152 if (n != kTerminalSentinel_) {
153 decrement_reference_count_ (n);
154 next.store (kTerminalSentinel_, std::memory_order_relaxed);
164 template <
typename T>
165 inline forward_list<T>::forward_list ()
166 : fFirst_{kTerminalSentinel_}
169 template <
typename T>
170 inline forward_list<T>::forward_list (forward_list
const& src)
173 std::atomic<node_*>* nextPtr = &fFirst_;
174 for (
auto const& v : src) {
175 std::atomic<node_*>& next = *nextPtr;
176 node_* newNode =
new node_{v};
177 next.store (newNode);
178 nextPtr = &newNode->next;
181 template <
typename T>
182 inline forward_list<T>::forward_list (forward_list&& src) noexcept
185 exchange_ (src.fFirst_, fFirst_);
187 template <
typename T>
188 inline forward_list<T>::~forward_list ()
192 template <
typename T>
195 return fFirst_.load () == kTerminalSentinel_;
197 template <
typename T>
200 node_* current = kTerminalSentinel_;
202 exchange_ (fFirst_, current);
203 if (current == kTerminalSentinel_) {
209 std::vector<node_*> nodes;
210 while (current != kTerminalSentinel_) {
211 nodes.push_back (current);
212 node_* temp = kTerminalSentinel_;
214 exchange_ (current->next, temp);
217 for (
auto i = nodes.rbegin (); i != nodes.rend (); ++i) {
218 decrement_reference_count_ (*i);
220 return nodes.size ();
222 template <
typename T>
225 std::list<node_*> nodes;
227 node_* i = kTerminalSentinel_;
228 exchange_ (fFirst_, i);
231 node_* temp = kSpinSentinel_;
232 exchange_ (i->next, temp);
236 for (
auto const& i = nodes.begin (); i != nodes.end (); ++i) {
237 decrement_reference_count_ (*i);
238 i->next.store (kTerminalSentinel_, std::memory_order_relaxed);
240 return nodes.size ();
242 template <
typename T>
247 template <
typename T>
252 template <
typename T>
255 auto* nodePtr =
new node_{value};
256 return insert_node_ (fFirst_, nodePtr);
258 template <
typename T>
261 return insert_node_ (fFirst_,
new node_{std::move (value)});
263 template <
typename T>
264 template <
typename... U>
265 inline auto forward_list<T>::emplace_front (U&&... params) -> reference
267 return *insert_node_ (fFirst_,
new node_{std::forward<U> (params)...});
269 template <
typename T>
273 return remove_node_ (fFirst_, &r) ? r : std::optional<T>{};
275 template <
typename T>
278 node_* n = new_ownership_ (fFirst_);
280 if (n != kTerminalSentinel_) {
281 decrement_reference_count_ (n);
285 template <
typename T>
289 std::atomic<node_*>& nonConstFirst = *
const_cast<std::atomic<node_*>*
> (&fFirst_);
290 node_* n = new_ownership_ (nonConstFirst);
291 if (n == kTerminalSentinel_) {
294 const_iterator result{n};
295 decrement_reference_count_ (n);
298 template <
typename T>
302 return const_cast<forward_list const*
> (
this)->begin ();
304 template <
typename T>
309 template <
typename T>
312 return const_iterator{};
314 template <
typename T>
317 return const_iterator{};
319 template <
typename T>
322 return insert_node_ (position.current->next,
new node_{value});
324 template <
typename T>
327 return insert_node_ (position.current->next,
new node_{value});
329 template <
typename T>
330 auto forward_list<T>::insert_after (const_iterator pos,
int count,
const T& value) -> iterator
334 iterator result = pos = insert_after (pos, value);
335 for (
int i = 1; i < count; ++i) {
336 pos = insert_after (pos, value);
340 template <
typename T>
341 template <
typename InputIt>
342 auto forward_list<T>::insert_after (const_iterator pos, InputIt first, InputIt last) -> iterator
344 while (first != last) {
345 pos = insert_after (pos, first);
350 template <
typename T>
351 inline auto forward_list<T>::insert_after (const_iterator pos, std::initializer_list<T> ilist) -> iterator
353 return insert_after (pos, ilist.begin (), ilist.end ());
355 template <
typename T>
356 template <
typename... U>
357 inline auto forward_list<T>::emplace_after (const_iterator position, U&&... params) -> iterator
359 return insert_node_ (position,
new node_{std::forward (params)...});
361 template <
typename T>
364 node_* n = separate_ (position.current->next);
371 template <
typename T>
374 return remove_node_ (position.current->next, value);
376 template <
typename T>
379 exchange_ (fFirst_, other.fFirst_);
381 template <
typename T>
384 node_* x = owner_lock_ (atomic_ptr);
385 if (x == kTerminalSentinel_) {
386 if (atomic_ptr.load () == kTerminalSentinel_)
388 node_* temp = kTerminalSentinel_;
389 owner_unlock_ (atomic_ptr, temp);
393 node_* y = owner_lock_ (x->next);
394 owner_unlock_ (atomic_ptr, y);
395 x->next.store (kTerminalSentinel_);
396 decrement_reference_count_ (x);
399 template <
typename T>
400 inline auto forward_list<T>::insert_node_ (std::atomic<node_*>& atomic_ptr, node_* n) -> iterator
404 exchange_ (n->next, atomic_ptr);
407 template <
typename T>
408 inline auto forward_list<T>::separate_ (std::atomic<node_*>& atomic_ptr) -> node_*
410 node_* oldNext = kTerminalSentinel_;
411 exchange_ (atomic_ptr, oldNext);
414 template <
typename T>
415 inline void forward_list<T>::decrement_reference_count_ (node_*& n)
417 Assert (n !=
nullptr);
418 Assert (n != kTerminalSentinel_);
419 Assert (n != kSpinSentinel_);
420 if (n->referenceCount-- == 1) {
425 template <
typename T>
426 inline auto forward_list<T>::increment_reference_count_ (node_* n) -> node_*
428 Assert (n !=
nullptr);
429 Assert (n != kTerminalSentinel_);
430 Assert (n != kSpinSentinel_);
434 template <
typename T>
435 inline auto forward_list<T>::spin_get_ (
const std::atomic<node_*>& n) -> node_*
439 if (p != kSpinSentinel_) {
444 template <
typename T>
445 void forward_list<T>::exchange_ (std::atomic<node_*>& left, node_*& right)
447 Assert (right !=
nullptr);
448 Assert (right != kSpinSentinel_);
449 node_* n = left.load ();
451 n = spin_get_ (left);
452 }
while (!left.compare_exchange_weak (n, right));
453 Assert (n !=
nullptr);
456 template <
typename T>
457 void forward_list<T>::exchange_ (std::atomic<node_*>& left, std::atomic<node_*>& right)
459 node_* temp = owner_lock_ (left);
460 exchange_ (right, temp);
461 if (temp != kTerminalSentinel_) {
462 owner_unlock_ (left, temp);
465 left.store (kTerminalSentinel_);
468 template <
typename T>
469 auto forward_list<T>::owner_lock_ (std::atomic<node_*>& atomic_ptr) -> node_*
471 node_* n = atomic_ptr.load ();
473 n = spin_get_ (atomic_ptr);
474 }
while (!atomic_ptr.compare_exchange_weak (n, kSpinSentinel_));
476 if (n == kTerminalSentinel_) {
478 atomic_ptr.store (kTerminalSentinel_, std::memory_order_relaxed);
479 return kTerminalSentinel_;
483 template <
typename T>
484 inline void forward_list<T>::owner_unlock_ (std::atomic<node_*>& atomic_ptr, node_*& n)
486 Assert (n !=
nullptr);
487 Assert (n != kSpinSentinel_);
488 Assert (atomic_ptr.load (std::memory_order_relaxed) == kSpinSentinel_);
489 atomic_ptr.store (n, std::memory_order_relaxed);
492 template <
typename T>
493 auto forward_list<T>::new_ownership_ (std::atomic<node_*>& atomic_ptr) -> node_*
495 node_* temp = owner_lock_ (atomic_ptr);
496 if (temp == kTerminalSentinel_) {
497 return kTerminalSentinel_;
499 node_* result = temp != kTerminalSentinel_ ? increment_reference_count_ (temp) : kTerminalSentinel_;
500 owner_unlock_ (atomic_ptr, temp);
507 template <
typename T>
Include this file VERY EARLY ON - before including stuff like <cstdio> - to allow use of Valgrind (so...
lock-free, threadsafe singly linked list implementation (patterned after std::forward_list) - INTERNA...
nonvirtual void swap(forward_list &other) noexcept
nonvirtual forward_list & operator=(const forward_list &rhs)=delete