NAME
DBIO::Forked::Future - Loop-free, pipe-backed Future for DBIO::Forked
VERSION
version 0.900001
DESCRIPTION
A loop-free Future implementation over a pipe read fd, with no Future, no Future::IO and no event loop. It is what DBIO::Forked::Storage hands back from its *_async methods.
It fulfils both the minimal core DBIO::Future contract (then / catch / get / is_ready / is_failed) AND the fuller, DBIO::Future::Immediate-compatible surface (the done / fail / needs_all class constructors, plus and_then), so it works as a live future_class for core's ResultSet async helpers (all_async / first_async / single_async / count_async / create_async), which build futures via $storage->future_class->done(@rows) and ->fail($err).
The model is deliberately simple, matching Model A (one forked child per query):
is_ready is an EOF-clean, non-blocking drain.
IO::Select->can_read(0)goes true as soon as the child writes anything, not when it is done, so a bare peek would be premature. Insteadis_readyreads whatever is available without blocking and accumulates it across calls; it returns true only once the child closes its write end (sysreadreturns 0 = EOF), at which point the blob is thawed, cached and the child reaped.get blocks until EOF,
Storable-thaws the accumulated blob,waitpid-reaps the child, and returns the rows -- or re-throws the child's error. It is idempotent: the result is cached, so a secondgetdoes not read or reap again.then / catch / and_then compose lazily. A derived future stores its callbacks and a reference to its source; it resolves (running the callback synchronously, since the source is by then ready) the first time it is forced via
get, or viais_ready/is_failedonce the source is ready. No callback runs before its source has resolved, and nothing blocks unlessgetis called.done / fail / needs_all are immediate class constructors (no fork): a settled future is a pre-resolved leaf with no child, so it flows through the same resolution machinery.
needs_allblocks on each input viagetin turn (the children already run in parallel).
METHODS
new
my $future = DBIO::Forked::Future->new(read_fh => $fh, pid => $pid);
Construct a leaf future bound to the pipe read handle read_fh and the forked child pid. Both are optional so the object can be built before the fork wiring is in place.
done
my $f = DBIO::Forked::Future->done(@values);
Class method: an immediately-resolved successful future carrying @values, with no fork and no pipe. This is the constructor core's ResultSet async helpers call as $storage->future_class->done(@rows).
fail
my $f = DBIO::Forked::Future->fail($error);
Class method: an immediately-resolved failed future; "get" re-throws $error. The ResultSet async helpers call $storage->future_class->fail($err).
needs_all
my $f = DBIO::Forked::Future->needs_all(@futures);
Class method: resolves once ALL @futures have resolved, collecting their values in order; fails as soon as any one fails, with that future's error. Under Model A the forked children already run in parallel; needs_all just blocks on each in turn via "get". Because collection is serial and each child holds its result in the pipe until read, a batch of many large results can stall on the pipe-buffer ceiling (see docs/adr/0003); fine for modest result sets.
then
my $g = $future->then(sub { my @result = @_; ... });
Success continuation. Returns a new DBIO::Forked::Future that, when forced, runs the callback with this future's resolved values and resolves to the callback's return values. On failure the callback is skipped and the failure propagates (unless an optional second $on_fail argument is given).
catch
my $g = $future->catch(sub { my $error = shift; ... });
Failure continuation. Returns a new DBIO::Forked::Future; on failure it runs the callback with the error and resolves to its return values, on success it passes the values through unchanged.
and_then
my $g = $future->and_then(sub { my @r = @_; return DBIO::Forked::Future->done(...) });
Like "then", but the callback is expected to return a future, which is flattened into the chain (no future-wrapping-a-future). Mirrors "and_then" in DBIO::Future::Immediate. (then already flattens a returned future, so and_then is then with future-returning intent made explicit.)
get
my @result = $future->get;
Block until resolved, then return the resolved values -- or croak with the child's error if it failed. Idempotent. Like "get" in DBIO::Future::Immediate, in scalar context it returns the first value (not the count).
is_ready
if ($future->is_ready) { ... }
Non-blocking. True once resolved (the child has finished and its blob been read, or the source of a derived future has resolved). Never blocks.
is_failed
if ($future->is_failed) { ... }
Non-blocking. True once resolved and the result is an error.
STATUS
Full future_class surface implemented: the minimal DBIO::Future contract plus the DBIO::Future::Immediate-compatible done / fail / needs_all / and_then. "then" / "catch" / "and_then" flatten a single returned DBIO::Forked::Future (chaining) but do not otherwise inspect nested structures.
AUTHOR
DBIO Authors
COPYRIGHT AND LICENSE
Copyright (C) 2026 DBIO Authors
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.