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