|
| __JAFFAR_COMMON_INLINE__ void | reserve (const size_t capacity) |
| |
| __JAFFAR_COMMON_INLINE__ void | clear () |
| |
| __JAFFAR_COMMON_INLINE__ void | push_back_no_lock (T element) |
| |
| __JAFFAR_COMMON_INLINE__ size_t | pop_front_get_batch (T *elements, const size_t maxCount) |
| |
| __JAFFAR_COMMON_INLINE__ bool | pop_front_get (T &element) |
| |
| __JAFFAR_COMMON_INLINE__ bool | pop_back_get (T &element) |
| |
| __JAFFAR_COMMON_INLINE__ size_t | wasSize () const |
| |
template<class T>
class jaffarCommon::concurrent::DrainBuffer< T >
A fixed-capacity buffer specialized for a fill-once / drain-from-both-ends lifecycle, with a lock-free concurrent drain phase.
It is built for the pattern where one phase fills the buffer single-threaded (no contention), a barrier follows, and then many threads concurrently consume the elements – most pulling batches from the front, a few pulling single elements from the back – until it is empty. There is never a push concurrent with a pop. (This is exactly how a best-first search step works: the ordered set of states for the step is laid down once, then worker threads drain it.)
Versus a mutex-guarded std::deque this wins three ways: the fill is a plain sequential store with no per-element allocation; the storage is contiguous, so the concurrent drain is cache- and prefetcher-friendly; and front/back claiming is lock-free (a single CAS) instead of taking a mutex. Both ends are claimed by advancing counters packed into one 64-bit atomic, so a front and a back claim can never alias the same slot. Indices only grow within a step and are reset (clear()) only while the buffer is quiescent, so there is no ABA hazard.
- Note
- Capacity is fixed at reserve() time; the fill phase must not exceed it. Capacity is limited to UINT32_MAX elements (the claim counters are 32-bit halves).