NAME
Acme::Parataxis::Monitor - Observe Fiber and Actor Terminations without Ownership
SYNOPSIS
use v5.40;
use Acme::Parataxis qw[async fiber await_sleep];
use Acme::Parataxis::Monitor;
async {
my $worker = fiber {
await_sleep(50);
die "Worker failed catastrophically\n";
};
# Monitor the worker instead of awaiting it directly:
my $watch = Acme::Parataxis::Monitor->new($worker);
# Awaiting the monitor yields the exit error as a value; it does NOT rethrow:
if ( my $err = $watch->await ) {
say "Captured worker death: $err";
}
say "Scheduler run continues uninterrupted!";
};
DESCRIPTION
An Acme::Parataxis::Monitor provides non-intrusive observation of another concurrent entity (a fiber, future, or actor), modeled after Erlang process monitors. It resolves exactly once when the target terminates: to undef if the target exited cleanly, or to the target's fatal exception if it crashed.
Crucially, a monitor delivers crashes as plain return values, not rethrown exceptions. Direct awaiting of a failing fiber (via $fiber->await) propagates the exception up the awaiting stack and aborts the scheduler unless wrapped in an eval. Awaiting a monitor allows a coordinator fiber to inspect crashes safely without risking its own unwinding.
Key characteristics:
No ownership: A monitor does not manage or control the target's lifecycle; it has no
stop()method and cannot cancel the target.Zero scheduler impact: Monitors attach to existing completion hooks (
on_readyon fibers and futures;on_deathon actors).Safe against race conditions: Monitoring a target that has already terminated resolves the monitor immediately during
new().Weak back-references: While the monitor retains a strong reference to the target, the target's internal callback list holds only a weak reference to the monitor. Dropping all references to a monitor lets it garbage-collect cleanly without lingering in the target's callback list.
CONSTRUCTOR
new( $target )
my $m1 = Acme::Parataxis::Monitor->new($fiber);
my $m2 = Acme::Parataxis::Monitor->new( target => $actor );
my $m3 = Acme::Parataxis::Monitor->new( fid => $fiber_id );
Creates and activates a monitor. Accepts a blessed target object or a numeric fiber ID.
Target must be a blessed object implementing
on_ready(fibers, futures) oron_death(actors).If passing
fid, the ID must correspond to a live, currently registered fiber.If the target has already completed, the monitor resolves immediately upon construction.
Croaks if passed invalid arguments, an unknown fiber ID, or an unblessed reference.
METHODS
await( )
my $err = $watch->await;
Suspends the calling fiber until the watched target terminates.
If the target exited cleanly: returns
undef.If the target crashed: returns the exception.
Unlike standard Future await, this method never throws. Must be called from inside a scheduled fiber.
result( )
my $err = $watch->result;
Returns the resolution error without parking. Croaks with "Future not ready" if the target is still running.
error( )
my $err = $watch->error;
Non-blocking inspection. Returns the target's fatal exception if it crashed, or undef if the target exited cleanly or is still running. Check "is_ready( )" to distinguish between a running target and a clean exit.
is_ready( )
if ( $watch->is_ready ) {
say "Target has terminated";
}
Returns true if the watched target has finished execution. is_done is an alias.
is_done( )
if ( $watch->is_done ) {
say "Target has terminated";
}
Returns true if the watched target has finished execution. Alias for is_ready( ).
target( )
Returns the underlying watched object (fiber, future, or actor).
fid( )
Returns the numeric fiber ID of the target at the time the monitor was established, or undef if the target is not a fiber.
on_ready( sub ($monitor) { ... } )
$watch->on_ready( sub ($m) {
say "Subject finished. Error: " . ( $m->result // 'none' );
} );
Registers a callback to fire when the target terminates. The callback receives the monitor instance. If the target has already terminated, the callback runs immediately inline.
Recipes
Safely harvesting an untrusted pool of fibers
my @fibers = map { fiber { risky_operation($_) } } 1 .. 10;
my @monitors = map { Acme::Parataxis::Monitor->new($_) } @fibers;
my @crashes;
for my $m (@monitors) {
if ( my $err = $m->await ) {
push @crashes, $err;
}
}
say "Completed with ${\scalar @crashes} failures out of 10";
Watching for worker actor death
While supervised actors restart under supervisors, unmanaged actors can be monitored to trigger failover logic:
my $worker = Acme::Parataxis::Actor->spawn( sub ( $self, $msg ) { ... } );
my $mon = Acme::Parataxis::Monitor->new($worker);
fiber {
my $err = $mon->await;
warn "Actor stopped unexpectedly: $err" if $err;
};
Behavior Notes
Resolution is strictly the termination status
A monitor has no concept of a "successful payload." Even if a watched fiber returns a meaningful value (such as
42),$monitor->awaitresolves toundef. Use$fiber->awaitwhen you want the return value, and use a monitor when you want pure termination observation.Actors survive message handler dies
Unless spawned with
supervised => 1, an actor survives exceptions thrown inside its handler; the error is returned to the caller'saskfuture while the actor continues processing messages. Because the actor does not die, a monitor attached to it will not fire. A monitor on an actor only resolves whenstop()is called or the actor suffers a fatal crash.Do not monitor the current fiber
Attempting to monitor the calling fiber (e.g.
Monitor->new( fid => Acme::Parataxis->current_fid )->await) causes the fiber to wait on its own termination, triggering an immediate deadlock.
SEE ALSO
Acme::Parataxis, Acme::Parataxis::Future, Acme::Parataxis::Actor, Acme::Parataxis::Nursery
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.