NAME
Acme::Parataxis::Local - Per-Fiber Storage Slots and Context Propagation
SYNOPSIS
use v5.40;
use Acme::Parataxis qw[async fiber];
use Acme::Parataxis::Local;
my $request_id = Acme::Parataxis::Local->new;
my $trace_id = Acme::Parataxis::Local->new( inherit => 1 );
async {
$request_id->set(42);
$trace_id->set('span-100');
fiber {
# Inherited slot carries the parent's value at spawn time:
say "Child trace: " . $trace_id->get; # span-100
# Uninherited slot starts empty (undef):
say "Child request: " . ($request_id->get // 'unset'); # unset
$request_id->set(99);
say "Child modified: " . $request_id->get; # 99
};
# Parent's slot remains untouched:
say "Parent request: " . $request_id->get; # 42
};
DESCRIPTION
Acme::Parataxis::Local provides fiber-local storage, analogous to thread-local storage in multi-threaded runtimes. A single Local instance represents a variable that resolves to a private cell for each individual fiber.
Fibers never share or leak cells:
Writing to a
Localin a child fiber never overwrites the parent's or siblings' values.Unlike Perl's dynamic
local, the value follows the fiber's execution lifetime rather than the block scope. It survives any number ofawaitandyieldboundaries.When a fiber terminates and is reaped, its local cells are cleaned up automatically, avoiding memory leaks in long-running processes.
Inheritance and Trace Propagation
Constructing a slot with inherit => 1 enables automatic context propagation. When a fiber spawns a child fiber via fiber { ... }, spawn(), an actor, or a nursery, all active inherited slots are shallow-copied into the child's cell before its first statement runs.
This is the standard mechanism for passing request IDs, tracing spans, and security contexts down an asynchronous call tree without manually threading parameters through every helper function. Once copied, the child owns its cell outright; subsequent writes by parent or child do not cross the boundary.
METHODS
new( [%opts] )
my $plain = Acme::Parataxis::Local->new;
my $seeded = Acme::Parataxis::Local->new( default => 'anonymous' );
my $traced = Acme::Parataxis::Local->new( inherit => 1 );
Constructs a new fiber-local slot. Arguments are keyword-only:
default => $valThe fallback value returned by
get()in fibers that have not explicitly set a value. Default isundef.inherit => $boolIf true, child fibers spawned via
spawn(),fiber, nurseries, or actors inherit a snapshot of the parent's current value at spawn time. Default is false.
get( )
my $val = $slot->get;
Returns the value stored in the calling fiber's private cell, or the configured default if unwritten.
Must be called from within a scheduled fiber (croaks with Local-get must be called from inside a scheduled fiber> otherwise).
set( $value )
$slot->set('req-123');
Stores $value in the calling fiber's private cell and returns that value. The write is strictly private to this fiber.
Must be called from within a scheduled fiber (croaks otherwise).
Recipes
Contextual logging without parameter threading
use v5.40;
use Acme::Parataxis qw[async fiber];
use Acme::Parataxis::Local;
my $CURRENT_USER = Acme::Parataxis::Local->new( default => 'guest', inherit => 1 );
sub log_action ($action) {
my $user = $CURRENT_USER->get;
say "[$user] $action";
}
async {
$CURRENT_USER->set('alice');
log_action('logging in'); # [alice] logging in
fiber {
# Inherited context carries into background work:
log_action('processing queue'); # [alice] processing queue
};
};
Isolated fiber-scoped resource handles
my $DB_CONN = Acme::Parataxis::Local->new;
sub get_db () {
my $dbh = $DB_CONN->get;
unless ($dbh) {
$dbh = create_database_handle();
$DB_CONN->set($dbh);
}
return $dbh;
}
# Concurrent fibers calling get_db() each maintain an isolated connection.
Behavior Notes
Top-level
asyncblocks do not inheritInheritance is wired into
spawn(). Becauseasync { ... }andrun()initialize a brand-new scheduler run directly rather than spawning off a parent fiber, they do not inherit slots from an enclosing context. Pass values into anasyncblock explicitly or wrap the work in an internalfiber { ... }.Writing
undefoverrides the defaultdefaultis only consulted when a fiber has never calledset(). Calling$slot->set(undef)stores an explicit undefined value, and subsequentget()calls returnundef, not the default.Unique slot identity
Each instantiated
Localobject receives a monotonically increasing process-wide ID. Two distinctLocalinstances with identical configurations remain completely isolated from one another.
SEE ALSO
Acme::Parataxis, 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.