Command Queue#
-
class CommandQueue#
Bounded multiple-producer, single-consumer queue carrying commands to the actor.
Any thread may push; only the actor thread pops. The ring is preallocated and its cells are reused, so unlike an intrusive linked-list MPSC there are no nodes to free, no ABA on node pointers, and no need for hazard pointers or epochs — the reclamation problem that makes most lock-free queues hard does not arise. The one heap object per command is allocated by the producer and normally destroyed by the actor, which a single consumer makes safe. Two paths destroy it elsewhere: a push onto a full ring destroys it on the producer, and cells own what is still pending when the ring itself is destroyed.
Bounded on purpose. A full queue makes push() fail rather than wait or grow, which hands the caller a backpressure signal instead of silently absorbing load it cannot serve.
Public Functions
-
explicit CommandQueue(int32_t capacity)#
- Parameters:
capacity – Ring slots, rounded up to a power of two and to at least kMinCapacity. Must be positive.
-
~CommandQueue() = default#
Cells own their commands, so undrained ones are freed with the ring itself. As everywhere on this class, destruction requires that all use of the queue happens-before it — which the engine provides by owning its channel through a shared_ptr, whose final release synchronizes with every thread that used it.
-
CommandQueue(CommandQueue const&) = delete#
-
CommandQueue &operator=(CommandQueue const&) = delete#
-
CommandQueue(CommandQueue&&) = delete#
-
CommandQueue &operator=(CommandQueue&&) = delete#
-
bool push(std::unique_ptr<EngineCommand> command)#
Producer, any thread: hand a command to the actor.
- Returns:
false when the ring is full, in which case the command is destroyed. Submission has no retry path — a full queue rejects the request outright — so nothing is salvaged.
-
std::unique_ptr<EngineCommand> pop()#
Consumer, actor thread only: take the next command, or nullptr when none is ready. Not the same as “empty”: a producer that has claimed the next position but not yet published it holds the line, so a later command that already succeeded stays invisible until that producer finishes. The gap is a few instructions wide, and the actor re-drains every boundary, so a command is delayed by at most one step.
-
inline bool hasPending() const noexcept#
Whether any command is waiting. The actor’s idle park asks this before sleeping.
Advisory: the counters may move between the two loads, and both wrong answers are cheap — a stale “no” is covered by the producer’s wake and the park’s timeout, a stale “yes” costs one empty pop.
-
inline int32_t capacity() const noexcept#
Public Static Attributes
-
static int32_t kMinCapacity = 2#
Smallest ring the sequence protocol can express. With a single cell, position 1 wraps onto the cell position 0 just published, whose sequence already reads as writable — a producer would overwrite an undelivered command and leave the consumer stuck a lap behind.
-
explicit CommandQueue(int32_t capacity)#