NAME
Acme::Parataxis::Signal - One-Shot Event Flags with Waiter Queues
SYNOPSIS
use v5.40;
use Acme::Parataxis qw[async fiber];
use Acme::Parataxis::Signal;
# Initialize unsignalled (count => false):
my $ready = Acme::Parataxis::Signal->new( count => false );
async {
fiber {
say 'Worker: waiting for ready signal...';
$ready->wait; # parks until signalled
say 'Worker: unblocked and running!';
};
say 'Main: completing setup...';
$ready->send; # wakes the worker fiber
};
DESCRIPTION
An Acme::Parataxis::Signal represents a single notification flag accompanied by a FIFO queue of waiters. Calling send signals the event, waking a parked fiber or saving the notification for the next fiber that arrives.
A key design feature of signals is early firing safety: if send is called before any fiber has called wait, the signal is remembered. When a fiber eventually calls wait, it consumes the remembered signal and proceeds immediately without parking. This eliminates initialization race conditions where a notifier fires before its listeners are spawned.
send()releases one waiter, or records the signal if no waiters exist.broadcast()wakes all currently queued waiters simultaneously, clearing any remembered signal.
METHODS
new( [ count => $bool = true ] )
my $ready = Acme::Parataxis::Signal->new( count => false ); # unsignalled (common case)
my $fired = Acme::Parataxis::Signal->new; # pre-signalled
Constructs a new signal.
Important Default: count defaults to true (pre-signalled). In this state, the first wait() will not block. For the standard pattern where waiters must park until an event occurs, pass count => false.
send( )
$signal->send;
Delivers the signal to the longest-waiting entity (waking a parked fiber or executing a registered callback). If no waiters are currently queued, the signal is stored (setting count to true) so the next wait() will return immediately. Returns true.
broadcast( )
$signal->broadcast;
Wakes every currently queued waiter in a single pass. Unlike send(), an unconsumed broadcast is not remembered: if no waiters are present, the notification is discarded, and any previously banked signal is cleared (resetting count to false). Returns true.
wait( [$callback] )
$signal->wait; # parks the calling fiber until signalled
$signal->wait(\&on_done); # registers a callback without parking
Without arguments: If a signal is already pending, consumes it (resetting
countto false) and returns immediately. Otherwise, parks the calling fiber until a signal arrives. Always returnsundef. Must run inside a scheduled fiber unless a signal is already pending.With a CODE reference: Registers
$callbackto run when the signal fires, returning immediately without parking. If a signal is already pending, the callback executes inline beforewait()returns. Safe to call outside a fiber.
awaited( )
my $num_waiters = $signal->awaited;
Returns the total count of currently queued waiters (parked fibers plus registered callbacks). Returns 0 when no waiters are waiting.
count( )
Returns true if a signal has been banked and is currently pending consumption; false otherwise.
remove_waiter( $waiter )
$signal->remove_waiter($cb_or_fid);
Removes a specific callback or fiber ID from the wait queue without triggering it. Used during cancellation and cleanup.
Recipes
Fan-out broadcast notification
Use broadcast() to release multiple concurrent workers on a single event:
async {
my $start_signal = Acme::Parataxis::Signal->new( count => false );
for my $id ( 1 .. 3 ) {
fiber {
$start_signal->wait;
say "Worker $id running!";
};
}
await_sleep(50);
say "Starting all workers:";
$start_signal->broadcast; # unblocks all 3 workers at once
};
Non-blocking callback triggers
Register callbacks to execute immediately upon notification without spawning intermediate fibers:
my $done = Acme::Parataxis::Signal->new( count => false );
$done->wait( sub {
say "Cleanup callback executed inline on send()";
} );
# Later, in any fiber:
$done->send;
Behavior Notes
broadcast( ) cancels banked signals
broadcast() clears any pending signal flag even if no waiters were queued. If you need to clear the wait queue while preserving a banked signal, invoke send() repeatedly for each waiter instead.
Single wake-up per send( )
Calling send() multiple times while no fibers are waiting will only bank a single notification. The signal records that it fired, not how many times it fired. For event counting, use Acme::Parataxis::Semaphore or Acme::Parataxis::Channel.
Inline callback execution
Callbacks registered with wait(\&cb) run synchronously inside send() or broadcast(), in the context of the signaling fiber. If a registered callback dies, the exception propagates directly out of send(), interrupting the broadcast loop and stranding any remaining queued waiters.
SEE ALSO
Acme::Parataxis, Acme::Parataxis::Semaphore, Acme::Parataxis::Future
AUTHOR
Sanko Robinson https://github.com/sanko
LICENSE
Copyright (C) Sanko Robinson.
This library is free software; you can redistribute it and/or modify it under the terms found in the Artistic License 2.