Foundation
Loading...
Searching...
No Matches
AtomicStack.hpp
Go to the documentation of this file.
1#pragma once
2#include "Allocator.hpp"
3#include "Atomic.hpp"
4namespace Foundation::Core
5{
14 template <typename T>
16 {
17 public:
18 struct Node;
19 struct alignas(2 * sizeof(Node*)) PTag
20 {
21 Node* p{nullptr};
23 };
24 struct Node
25 {
28 };
29
30 private:
33
34 public:
46 template <typename U>
47 void Push(U&& value)
48 {
49 Node* node = static_cast<Node*>(mAlloc->Allocate(sizeof(Node), alignof(Node)));
50 node->data = std::forward<U>(value);
51 // Node* old_top = m_top.load(std::memory_order_relaxed);
52 // -> Doesn't avoid https://en.wikipedia.org/wiki/ABA_problem - a pushes and a pop then a push
53 // effectively make the new pointer the same again on linear allocators, but it's a different node.
54 // CAS later won't solve this. We need to tag the pointer.
55 PTag old_top = mTop.load(std::memory_order_relaxed);
56 node->next = old_top;
57 // -> Tag is incremented on every modification of the head pointer.
58 while (true)
59 {
61 if (mTop.compare_exchange_weak(old_top, new_top, std::memory_order_acquire, std::memory_order_relaxed))
62 {
63 node->next = old_top;
64 return; // OK. Acquired the head and we're in
65 }
66 }
67 }
74 bool Pop(T& out)
75 {
76 PTag old_top = mTop.load(std::memory_order_relaxed);
77 while (old_top.p != nullptr)
78 {
79 // Same as above
80 PTag new_top{old_top.p->next.p, old_top.tag + 1};
81 if (mTop.compare_exchange_weak(old_top, new_top, std::memory_order_acquire, std::memory_order_relaxed))
82 {
83 out = std::move(old_top.p->data);
84 old_top.p->~Node();
86 return true; // OK. Acquired the head and we're out
87 }
88 }
89 return false; // Empty
90 }
92 {
93 T val;
94 while (Pop(val))
95 ;
96 }
97 };
98} // namespace Foundation::Core
General Purpose Allocator (GPA) interface.
Definition Allocator.hpp:24
virtual void Deallocate(pointer ptr)=0
virtual pointer Allocate(size_type size, size_t alignment=alignof(std::max_align_t))=0
Atomic, unbounded LIFO stack with lock-free push and pop operations.
Definition AtomicStack.hpp:16
AtomicStack(Allocator *alloc)
Construct the Stack.
Definition AtomicStack.hpp:39
Atomic< PTag > mTop
Definition AtomicStack.hpp:31
bool Pop(T &out)
Pop a value from the stack.
Definition AtomicStack.hpp:74
void Push(U &&value)
Push a value onto the stack.
Definition AtomicStack.hpp:47
~AtomicStack()
Definition AtomicStack.hpp:91
Allocator * mAlloc
Definition AtomicStack.hpp:32
Lock-free atomic primitives and implementations of data structures.
Definition Allocator.hpp:5
std::atomic< T > Atomic
Alias of std::atomic<T>.
Definition Atomic.hpp:26
T * Construct(Allocator *resource, Args &&...args)
Convenience placement new with object of type T using a Foundation::Core::Allocator.
Definition Allocator.hpp:149
Definition AtomicStack.hpp:25
PTag next
Definition AtomicStack.hpp:26
T data
Definition AtomicStack.hpp:27
Definition AtomicStack.hpp:20
uintptr_t tag
Definition AtomicStack.hpp:22
Node * p
Definition AtomicStack.hpp:21