Files
snmalloc/src/snmalloc/ds/mpmcstack.h
Schrodinger ZHU Yifan 2cc74eac10 [proxy](2/n) provide proxy layers for type traits (#717)
* [proxy](2/n) provide proxy layers for type traits

* fix after rebasing
2025-01-06 17:07:49 +00:00

100 lines
2.1 KiB
C++

#pragma once
#include "../ds_core/ds_core.h"
#include "aba.h"
#include "allocconfig.h"
namespace snmalloc
{
template<class T, Construction c = RequiresInit>
class MPMCStack
{
using ABAT = ABA<T, c>;
private:
alignas(CACHELINE_SIZE) ABAT stack;
#ifdef SNMALLOC_THREAD_SANITIZER_ENABLED
__attribute__((no_sanitize("thread"))) static T*
racy_read(stl::Atomic<T*>& ptr)
{
// reinterpret_cast is required as TSAN still instruments
// stl::Atomic operations, even if you disable TSAN on
// the function.
return *reinterpret_cast<T**>(&ptr);
}
#else
static T* racy_read(stl::Atomic<T*>& ptr)
{
return ptr.load(stl::memory_order_relaxed);
}
#endif
public:
constexpr MPMCStack() = default;
void push(T* item)
{
static_assert(
stl::is_same_v<decltype(T::next), stl::Atomic<T*>>,
"T->next must be an stl::Atomic<T*>");
return push(item, item);
}
void push(T* first, T* last)
{
// Pushes an item on the stack.
auto cmp = stack.read();
do
{
auto top = cmp.ptr();
last->next.store(top, stl::memory_order_release);
} while (!cmp.store_conditional(first));
}
T* pop()
{
// Returns the next item. If the returned value is decommitted, it is
// possible for the read of top->next to segfault.
auto cmp = stack.read();
T* top;
T* next;
do
{
top = cmp.ptr();
if (top == nullptr)
break;
// The following read can race with non-atomic accesses
// this is undefined behaviour. There is no way to use
// CAS sensibly that conforms to the standard with optimistic
// concurrency.
next = racy_read(top->next);
} while (!cmp.store_conditional(next));
return top;
}
T* pop_all()
{
// Returns all items as a linked list, leaving an empty stack.
auto cmp = stack.read();
T* top;
do
{
top = cmp.ptr();
if (top == nullptr)
break;
} while (!cmp.store_conditional(nullptr));
return top;
}
};
} // namespace snmalloc