10namespace Stroika::Foundation::Containers::LockFreeDataStructures {
18 class forward_list<T>::ForwardIterator_ {
19 friend class forward_list;
21 using iterator_category = std::forward_iterator_tag;
29 : current{kTerminalSentinel_}
32 ForwardIterator_ (node_* n)
33 : current{n != kTerminalSentinel_ ? increment_reference_count_ (n) : kTerminalSentinel_}
36 ForwardIterator_ (
const ForwardIterator_& other)
37 : current{other.current != kTerminalSentinel_ ? increment_reference_count_ (other.current) : kTerminalSentinel_}
40 ForwardIterator_ (ForwardIterator_&& other) noexcept
41 : current{kTerminalSentinel_}
43 std::swap (current, other.current);
47 if (current == kTerminalSentinel_) {
50 decrement_reference_count_ (current);
52 ForwardIterator_&
operator= (ForwardIterator_
const& other)
54 if (current != kTerminalSentinel_) {
55 decrement_reference_count_ (current);
57 current = other.current != kTerminalSentinel_ ? increment_reference_count_ (other.current) : kTerminalSentinel_;
61 ForwardIterator_&
operator= (
const V& rhs)
63 if (current != kTerminalSentinel_) {
64 decrement_reference_count_ (current);
66 current = rhs.current != kTerminalSentinel_ ? increment_reference_count_ (rhs.current) : kTerminalSentinel_;
71 if (current == kTerminalSentinel_) {
72 throw std::logic_error{
"invalid iterator"};
74 return current->value;
78 if (current == kTerminalSentinel_) {
79 throw std::logic_error{
"invalid iterator"};
81 return ¤t->value;
83 ForwardIterator_& operator++ ()
85 Assert (current != kTerminalSentinel_);
86 node_* temp = new_ownership_ (current->next);
87 std::swap (current, temp);
88 if (temp != kTerminalSentinel_) {
89 decrement_reference_count_ (temp);
93 ForwardIterator_ operator++ (
int)
95 Assert (current != kTerminalSentinel_);
96 ForwardIterator_ temp = *
this;
100 friend void swap (ForwardIterator_& a, ForwardIterator_& b)
noexcept
103 swap (a.current, b.current);
105 operator ForwardIterator_<const T> ()
const
107 return ForwardIterator_<const T> (current);
109 bool operator== (
const ForwardIterator_& rhs)
const
111 return current == rhs.current;
121 template <
typename T>
122 class forward_list<T>::node_ {
124 friend class ForwardIterator_<T>;
126 std::atomic<node_*> next;
127 std::atomic<int> referenceCount;
129 node_ (T
const& value)
131 , next{kTerminalSentinel_}
136 : value{
std::move (value)}
137 , next{kTerminalSentinel_}
141 template <
typename... U>
142 node_ (U&&... params)
143 : value{
std::forward<U> (params)...}
144 , next{kTerminalSentinel_}
150 node_* n = owner_lock_ (next);
151 if (n != kTerminalSentinel_) {
152 decrement_reference_count_ (n);
153 next.store (kTerminalSentinel_, std::memory_order_relaxed);
163 template <
typename T>
164 inline forward_list<T>::forward_list ()
165 : fFirst_{kTerminalSentinel_}
168 template <
typename T>
169 inline forward_list<T>::forward_list (forward_list
const& src)
172 std::atomic<node_*>* nextPtr = &fFirst_;
173 for (
auto const& v : src) {
174 std::atomic<node_*>& next = *nextPtr;
175 node_* newNode =
new node_{v};
176 next.store (newNode);
177 nextPtr = &newNode->next;
180 template <
typename T>
181 inline forward_list<T>::forward_list (forward_list&& src) noexcept
184 exchange_ (src.fFirst_, fFirst_);
186 template <
typename T>
187 inline forward_list<T>::~forward_list ()
191 template <
typename T>
194 return fFirst_.load () == kTerminalSentinel_;
196 template <
typename T>
199 node_* current = kTerminalSentinel_;
201 exchange_ (fFirst_, current);
202 if (current == kTerminalSentinel_) {
208 std::vector<node_*> nodes;
209 while (current != kTerminalSentinel_) {
210 nodes.push_back (current);
211 node_* temp = kTerminalSentinel_;
213 exchange_ (current->next, temp);
216 for (
auto i = nodes.rbegin (); i != nodes.rend (); ++i) {
217 decrement_reference_count_ (*i);
219 return nodes.size ();
221 template <
typename T>
224 std::list<node_*> nodes;
226 node_* i = kTerminalSentinel_;
227 exchange_ (fFirst_, i);
230 node_* temp = kSpinSentinel_;
231 exchange_ (i->next, temp);
235 for (
auto const& i = nodes.begin (); i != nodes.end (); ++i) {
236 decrement_reference_count_ (*i);
237 i->next.store (kTerminalSentinel_, std::memory_order_relaxed);
239 return nodes.size ();
241 template <
typename T>
246 template <
typename T>
251 template <
typename T>
254 auto* nodePtr =
new node_{value};
255 return insert_node_ (fFirst_, nodePtr);
257 template <
typename T>
260 return insert_node_ (fFirst_,
new node_{std::move (value)});
262 template <
typename T>
263 template <
typename... U>
264 inline auto forward_list<T>::emplace_front (U&&... params) -> reference
266 return *insert_node_ (fFirst_,
new node_{std::forward<U> (params)...});
268 template <
typename T>
272 return remove_node_ (fFirst_, &r) ? r : std::optional<T>{};
274 template <
typename T>
277 node_* n = new_ownership_ (fFirst_);
279 if (n != kTerminalSentinel_) {
280 decrement_reference_count_ (n);
284 template <
typename T>
288 std::atomic<node_*>& nonConstFirst = *
const_cast<std::atomic<node_*>*
> (&fFirst_);
289 node_* n = new_ownership_ (nonConstFirst);
290 if (n == kTerminalSentinel_) {
293 const_iterator result{n};
294 decrement_reference_count_ (n);
297 template <
typename T>
301 return const_cast<forward_list const*
> (
this)->begin ();
303 template <
typename T>
308 template <
typename T>
311 return const_iterator{};
313 template <
typename T>
316 return const_iterator{};
318 template <
typename T>
321 return insert_node_ (position.current->next,
new node_{value});
323 template <
typename T>
326 return insert_node_ (position.current->next,
new node_{value});
328 template <
typename T>
329 auto forward_list<T>::insert_after (const_iterator pos,
int count,
const T& value) -> iterator
333 iterator result = pos = insert_after (pos, value);
334 for (
int i = 1; i < count; ++i) {
335 pos = insert_after (pos, value);
339 template <
typename T>
340 template <
typename InputIt>
341 auto forward_list<T>::insert_after (const_iterator pos, InputIt first, InputIt last) -> iterator
343 while (first != last) {
344 pos = insert_after (pos, first);
349 template <
typename T>
350 inline auto forward_list<T>::insert_after (const_iterator pos, std::initializer_list<T> ilist) -> iterator
352 return insert_after (pos, ilist.begin (), ilist.end ());
354 template <
typename T>
355 template <
typename... U>
356 inline auto forward_list<T>::emplace_after (const_iterator position, U&&... params) -> iterator
358 return insert_node_ (position,
new node_{std::forward (params)...});
360 template <
typename T>
363 node_* n = separate_ (position.current->next);
370 template <
typename T>
373 return remove_node_ (position.current->next, value);
375 template <
typename T>
378 exchange_ (fFirst_, other.fFirst_);
380 template <
typename T>
383 node_* x = owner_lock_ (atomic_ptr);
384 if (x == kTerminalSentinel_) {
385 if (atomic_ptr.load () == kTerminalSentinel_)
387 node_* temp = kTerminalSentinel_;
388 owner_unlock_ (atomic_ptr, temp);
392 node_* y = owner_lock_ (x->next);
393 owner_unlock_ (atomic_ptr, y);
394 x->next.store (kTerminalSentinel_);
395 decrement_reference_count_ (x);
398 template <
typename T>
399 inline auto forward_list<T>::insert_node_ (std::atomic<node_*>& atomic_ptr, node_* n) -> iterator
403 exchange_ (n->next, atomic_ptr);
406 template <
typename T>
407 inline auto forward_list<T>::separate_ (std::atomic<node_*>& atomic_ptr) -> node_*
409 node_* oldNext = kTerminalSentinel_;
410 exchange_ (atomic_ptr, oldNext);
413 template <
typename T>
414 inline void forward_list<T>::decrement_reference_count_ (node_*& n)
416 Assert (n !=
nullptr);
417 Assert (n != kTerminalSentinel_);
418 Assert (n != kSpinSentinel_);
419 if (n->referenceCount-- == 1) {
424 template <
typename T>
425 inline auto forward_list<T>::increment_reference_count_ (node_* n) -> node_*
427 Assert (n !=
nullptr);
428 Assert (n != kTerminalSentinel_);
429 Assert (n != kSpinSentinel_);
433 template <
typename T>
434 inline auto forward_list<T>::spin_get_ (
const std::atomic<node_*>& n) -> node_*
438 if (p != kSpinSentinel_) {
443 template <
typename T>
444 void forward_list<T>::exchange_ (std::atomic<node_*>& left, node_*& right)
446 Assert (right !=
nullptr);
447 Assert (right != kSpinSentinel_);
448 node_* n = left.load ();
450 n = spin_get_ (left);
451 }
while (!left.compare_exchange_weak (n, right));
452 Assert (n !=
nullptr);
455 template <
typename T>
456 void forward_list<T>::exchange_ (std::atomic<node_*>& left, std::atomic<node_*>& right)
458 node_* temp = owner_lock_ (left);
459 exchange_ (right, temp);
460 if (temp != kTerminalSentinel_) {
461 owner_unlock_ (left, temp);
464 left.store (kTerminalSentinel_);
467 template <
typename T>
468 auto forward_list<T>::owner_lock_ (std::atomic<node_*>& atomic_ptr) -> node_*
470 node_* n = atomic_ptr.load ();
472 n = spin_get_ (atomic_ptr);
473 }
while (!atomic_ptr.compare_exchange_weak (n, kSpinSentinel_));
475 if (n == kTerminalSentinel_) {
477 atomic_ptr.store (kTerminalSentinel_, std::memory_order_relaxed);
478 return kTerminalSentinel_;
482 template <
typename T>
483 inline void forward_list<T>::owner_unlock_ (std::atomic<node_*>& atomic_ptr, node_*& n)
485 Assert (n !=
nullptr);
486 Assert (n != kSpinSentinel_);
487 Assert (atomic_ptr.load (std::memory_order_relaxed) == kSpinSentinel_);
488 atomic_ptr.store (n, std::memory_order_relaxed);
491 template <
typename T>
492 auto forward_list<T>::new_ownership_ (std::atomic<node_*>& atomic_ptr) -> node_*
494 node_* temp = owner_lock_ (atomic_ptr);
495 if (temp == kTerminalSentinel_) {
496 return kTerminalSentinel_;
498 node_* result = temp != kTerminalSentinel_ ? increment_reference_count_ (temp) : kTerminalSentinel_;
499 owner_unlock_ (atomic_ptr, temp);
506 template <
typename T>
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