6#include <boost/atomic/atomic.hpp>
13template<
class T>
using Atomic = boost::atomic<T>;
27 "Required: std::atomic<T*>& atomic_next_ptrref (T&)");
33 return head_.
load() ==
nullptr;
39 AtomicTPtr &last_nextptr = atomic_next_ptrref (last);
41 T *exchange = head_.
load();
43 last_nextptr = exchange;
45 const bool was_empty = exchange ==
nullptr;
59 T *exchange = head_.
load();
70 T *current =
pop_all(), *prev =
nullptr;;
73 AtomicTPtr &el_nextptr = atomic_next_ptrref (current);
94template<
typename Node>
100 thead.next = (Node*) TAIL_;
108 Head nhead, ohead = head_.load();
111 nhead.aba_counter = ohead.aba_counter;
112 nhead.next =
nullptr;
114 while (!head_.compare_exchange_weak (ohead, nhead));
115 ohead = head_.load();
116 assert (ohead.next ==
nullptr);
121 const Head ohead = head_.load();
122 return ohead.next == (Node*) TAIL_;
127 assert (node && !node->intr_ptr_);
128 Head nhead, ohead = head_.load();
132 node->intr_ptr_ = ohead.next;
133 nhead.aba_counter = ohead.aba_counter;
136 while (!head_.compare_exchange_weak (ohead, nhead));
137 const bool was_empty = ohead.next == (Node*) TAIL_;
144 Head nhead, ohead = head_.load();
149 if (node == (Node*) TAIL_)
151 nhead.next = node->intr_ptr_;
152 nhead.aba_counter = ohead.aba_counter + 1;
154 while (!head_.compare_exchange_weak (ohead, nhead));
155 node->intr_ptr_ =
nullptr;
162 const Head ohead = head_.load();
167 Node *next =
nullptr;
171 static constexpr uintptr_t TAIL_ = ~uintptr_t (0);
177template<
typename Value,
template<
class>
class GrowOnlyAllocator =
Loft::Allocator>
180 bool empty()
const {
return stack_.empty(); }
185 Node *node = node_realloc (
nullptr);
187 return stack_.push (node);
199 Node *node = stack_.pop();
202 value = std::move (node->value);
210 Atomic<Node*> intr_ptr_ =
nullptr;
212 MpmcStack<Node> stack_;
214 node_realloc (Node *node)
216 static constexpr GrowOnlyAllocator<Node> grow_only_alloc;
217 static_assert (GrowOnlyAllocator<Node>::allows_read_after_free ==
true);
219 grow_only_alloc.deallocate (node, 1);
222 node = grow_only_alloc.allocate (1);
238 size_t u_ = ~size_t (0), s_ = 0;
239 AtomicU64& ubits()
const {
return const_cast<Iter*
> (
this)->atomics_->u64 (u_); }
240 size_t usize()
const {
return atomics_ ? atomics_->usize() : 0; }
242 explicit Iter (
AtomicBits *a,
size_t p) : atomics_ (a), u_ (p >> 6), s_ (p & 63) {}
245 Iter& operator= (
const Iter&) =
default;
246 size_t position ()
const {
return u_ + s_; }
247 bool is_set ()
const {
return valid() && ubits().
load() & (
uint64 (1) << s_); }
248 bool done ()
const {
return !valid(); }
249 bool valid ()
const {
return atomics_ && u_ < usize(); }
251 bool set (
bool toggle);
252 bool xor_ (
bool toggle);
254 Iter& operator= (
bool toggle) { set (toggle);
return *
this; }
255 Iter& operator^= (
bool toggle) { xor_ (toggle);
return *
this; }
256 Iter& operator|= (
bool toggle) {
if (toggle) set (1);
return *
this; }
257 Iter& operator&= (
bool toggle) {
if (!toggle) set (0);
return *
this; }
258 Iter& operator++ () {
if (!done()) { s_ = (s_ + 1) & 63; u_ += s_ == 0; }
return *
this;}
259 Iter& operator++ (
int) {
return this->operator++(); }
260 bool operator== (
const Iter &o)
const {
return (done() && o.done()) || position() == o.position(); }
261 bool operator!= (
const Iter &o)
const {
return !operator== (o); }
262 bool operator== (
bool b)
const {
return b == is_set(); }
263 bool operator!= (
bool b)
const {
return b != is_set(); }
264 explicit operator bool ()
const {
return is_set(); }
268 explicit AtomicBits (
const AtomicBits&) =
delete;
269 size_t usize ()
const {
return base().
size(); }
270 size_t size ()
const {
return 64 * usize(); }
271 uint64 u64 (
size_t upos)
const {
return base()[upos]; }
272 AtomicU64& u64 (
size_t upos) {
return base()[upos]; }
273 AtomicBits& operator= (
const AtomicBits&) =
delete;
274 Iter iter (
size_t pos) {
return Iter (
this, pos); }
275 Iter begin () {
return iter (0); }
276 Iter end ()
const {
return {}; }
277 bool all (
bool toggle)
const;
278 bool any (
bool toggle)
const;
279 bool set (
size_t pos,
bool toggle) {
return iter (pos).set (toggle); }
280 bool operator[] (
size_t pos)
const {
return const_cast<AtomicBits*
> (
this)->iter (pos).is_set(); }
281 Iter operator[] (
size_t pos) {
return iter (pos); }
285AtomicBits::all (
bool toggle)
const
287 const uint64 match = toggle * ~uint64 (0);
288 for (
size_t u = 0; u < usize(); u++)
289 if (u64 (u) != match)
295AtomicBits::any (
bool toggle)
const
298 for (
size_t u = 0; u < usize(); u++)
302 for (
size_t u = 0; u < usize(); u++)
303 if (u64 (u) != ~uint64 (0))
310AtomicBits::Iter::set (
bool toggle)
322AtomicBits::Iter::clear()
331AtomicBits::Iter::xor_ (
bool toggle)
335 last = ubits().fetch_xor (toggle ?
uint64 (1) << s_ : 0);
340inline AtomicBits::Iter&
351 while (!ubits().load() && u_ < usize());
Iter & big_inc1()
Increment iterator by 1, allow big increments skipping zero bits.
Vector of atomic bits, operates in blocks of 64 bits.
Lock-free stack with atomic push() and pop_all operations.
T * pop_reversed()
Atomically pop all elements from the stack in FIFO order.
T * pop_all()
Atomically pop all elements from the stack in LIFO order.
bool push_chain(T *first, T *last)
Atomically push linked nodes first->…->last onto the stack, returns was_empty.
bool push(T *el)
Atomically push el onto the stack, returns was_empty.
bool empty() const
Check if poppingreturns null.
Allocator for STL containers.
T compare_exchange_strong(T... args)
T construct_at(T... args)
#define ASE_ASSERT_RETURN(expr,...)
Return from the current function if expr evaluates to false and issue an assertion warning.
#define ASE_RETURN_UNLESS(cond,...)
Return silently if cond does not evaluate to true, with return value ...
#define ASE_ISLIKELY(expr)
Compiler hint to optimize for expr evaluating to true.
The Anklang C++ API namespace.
boost::atomic< T > Atomic
Substitute for std::atomic<> with fixes for GCC.
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
Thread-safe, lock-free stack based on MpmcStack and an Allocator with allows_read_after_free.
bool push(const Value &value)
Add a copy of value to top of the stack, returns if the stack was empty (true).
bool empty() const
Return true if the stack holds no items.
bool pop(Value &value)
Pop value from top of the stack, returns if value was reassigned (true), otherwise the stack was empt...
bool push(Value &&value)
Add value to top of the stack, returns if the stack was empty (true).
Multi-producer, multi-consumer stack for non-reclaimable memory nodes.
Value type used to interface with various property types.