|
MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
|
Lock-free, unbounded, multi-producer collection of in-flight items. More...
#include <SweepList.hpp>
Inheritance diagram for MayaFlux::Memory::SweepList< T >:
Collaboration diagram for MayaFlux::Memory::SweepList< T >:Classes | |
| struct | Link |
Public Member Functions | |
| size_t | approx_size () const noexcept |
| Approximate number of items currently held. | |
| template<typename Finish > requires std::invocable<Finish, T&> | |
| void | drain (Finish finish) |
| Blocking pass that finalizes every item currently held. | |
| bool | empty () const noexcept |
| Whether the list currently holds no items. | |
| SweepList & | operator= (const SweepList &)=delete |
| SweepList & | operator= (SweepList &&)=delete |
| void | push (T value) noexcept |
Publishes value for later sweep() or drain(). | |
| template<typename Pred , typename Finish > requires std::invocable<Pred, T&> && std::invocable<Finish, T&> | |
| void | sweep (Pred ready, Finish finish) |
| Non-blocking pass over every currently held item. | |
| SweepList () noexcept=default | |
| SweepList (const SweepList &)=delete | |
| SweepList (SweepList &&)=delete | |
| ~SweepList () | |
| Drains and discards any remaining items without inspecting them. | |
Private Member Functions | |
| void | reattach (Link *survivors) noexcept |
Private Attributes | |
| std::atomic< size_t > | m_count { 0 } |
| std::atomic< Link * > | m_head { nullptr } |
Lock-free, unbounded, multi-producer collection of in-flight items.
Holds items whose readiness is checked out of order and reclaimed out of order, such as std::future<T> handles for asynchronous work. Unlike RingBuffer's lock-free policies, which require a fixed power-of-two capacity, SweepList grows without bound: every push() allocates one link, and every reclaimed item frees its link. There is no capacity to exhaust and no size policy to choose.
The concurrency shape is "any thread may push, any thread may sweep." There is no reserved single-consumer role: sweep() and drain() both take the entire current chain atomically via a single exchange(), so once a thread holds that chain no other thread can observe or mutate any link in it. This sidesteps the classic hazard of lock-free list removal, where unlinking one link out of the middle of a shared list races against concurrent traversal, by never unlinking a single link. A sweep always takes everything, decides per item, and republishes the survivors as one new chain.
push() is wait-free under the same CAS-retry bound as any Treiber stack. sweep() and drain() are lock-free: no thread blocks on another thread's mutex, though a CAS loop may retry under contention from concurrent pushes.
Ownership of each link is a std::unique_ptr everywhere except the single word actually under CAS. That word, m_head, is necessarily a raw pointer: no atomic smart pointer gives genuine lock-free compare-exchange. The standard library's own std::atomic<std::shared_ptr<T>> is typically implemented with an internal lock on every major implementation, since a shared_ptr CAS would need to update an object pointer and a control-block pointer as one indivisible step, which no hardware CAS instruction does. Using it here would silently reintroduce a lock behind an API that no longer looks like one, defeating the reason this type exists. m_head is therefore raw for the same reason LiveArena's backing storage is raw: a narrowly scoped exception at the exact point the type system cannot help, never exposed past this class's own implementation.
approx_size() is named approx deliberately. Concurrent pushes and sweeps can make it transiently stale, the same caveat RingBuffer's MPSCPolicy documents for its own size().
| T | Element type. Move-only types such as std::future<T> are fully supported; SweepList never copies an element. |
Definition at line 68 of file SweepList.hpp.