NAME

Linux::Event::Fork - Child process management integrated with Linux::Event

SYNOPSIS

use v5.36;
use Linux::Event;
use Linux::Event::Fork;

my $loop   = Linux::Event->new;
my $forker = Linux::Event::Fork->new($loop,
  max_children => 4,  # 0 = unlimited
);

my $h = $forker->spawn(
  cmd => [ $^X, '-we', 'print "hello\n"; exit 0' ],

  on_stdout => sub ($child, $chunk) {
    print $chunk;
  },

  on_exit => sub ($child, $exit) {
    printf "pid=%d exited=%d code=%d\n",
      $exit->pid, $exit->exited, ($exit->exited ? $exit->code : -1);
    $loop->stop;
  },
);

if ($h->isa('Linux::Event::Fork::Request')) {
  warn "queued\n";
}

$loop->run;

DESCRIPTION

Linux::Event::Fork runs child processes while integrating their lifecycle and I/O streams (stdout/stderr/stdin) with a Linux::Event loop.

Features:

  • Nonblocking stdout/stderr capture (chunk callbacks)

  • Optional streaming stdin (parent -> child)

  • Timeout support with optional escalation to SIGKILL

  • Bounded parallelism (max_children) with queueing

  • Drain callback when all work completes

  • Cancel queued requests (bulk or per-request)

  • Introspection (running/queued/max_children)

EXECUTION MODEL

All on_* callbacks run in the parent process, inside the event loop.

Only the child => sub { ... } callback runs in the child process.

Stream directions:

stdin   : parent -> child
stdout  : child  -> parent
stderr  : child  -> parent

There is no "on_stdin" callback. Stdin is a write stream to the child.

CONSTRUCTOR

new($loop, %args)

my $forker = Linux::Event::Fork->new($loop,
  max_children => 4,   # optional (default 0 = unlimited)
);

Constructs a forker bound to a specific event loop.

Arguments:

max_children => $n

Maximum number of concurrently running children. 0 means unlimited.

METHODS

loop

my $loop = $forker->loop;

Returns the underlying Linux::Event loop.

spawn(%spec)

my $h = $forker->spawn(%spec);

Starts a child immediately if capacity allows, otherwise enqueues the request.

Returns either:

spawn options

Exactly one of:

cmd => \@argv

Execs the given argv in the child.

child => sub { ... }

Runs the coderef in the child process. If it returns, the child exits with 127.

Optional:

on_start => sub ($child) { ... }

Called in the parent after the child handle is created (and before the loop has necessarily observed any I/O).

on_stdout => sub ($child, $chunk) { ... }

Called in the parent when the child writes to stdout.

on_stderr => sub ($child, $chunk) { ... }

Called in the parent when the child writes to stderr.

on_exit => sub ($child, $exit) { ... }

Called in the parent after the child has fully exited. $exit is a Linux::Event::Fork::Exit.

capture_stdout => $bool

Force stdout capture on/off. Default: true if on_stdout is provided, otherwise false.

capture_stderr => $bool

Force stderr capture on/off. Default: true if on_stderr is provided, otherwise false.

stdin => $string

If provided, writes this string to the child's stdin after start.

stdin_pipe => $bool

If true, keeps stdin open for streaming writes using the child handle. If false (default), stdin is closed after the initial stdin write (if any).

timeout => $seconds

Soft timeout. When it fires: calls on_timeout (if any) and sends SIGTERM.

on_timeout => sub ($child) { ... }

Called in the parent when timeout fires.

timeout_kill => $seconds

If set, after SIGTERM waits this many seconds and then sends SIGKILL if still alive.

cwd => $dir

Changes working directory in the child before exec/callback.

umask => $mask

Sets umask in the child before exec/callback.

clear_env => $bool

If true, clears %ENV in the child before applying env.

env => \%env

Merges these variables into %ENV in the child before exec/callback.

tag => $string

Opaque tag stored on the child/request handles.

data => $scalar

Opaque user data stored on the child/request handles.

max_children([$n])

my $n = $forker->max_children;
$forker->max_children(8);

Get or set the concurrency limit.

0 means unlimited.

Increasing the limit may immediately start queued requests. Decreasing the limit does not affect running children; it only limits future starts.

running

my $n = $forker->running;

Number of children currently running (tracked for capacity control).

queued

my $n = $forker->queued;

Number of queued requests waiting for capacity.

drain(on_done => sub ($forker) { ... })

$forker->drain(on_done => sub ($forker) {
  ...
});

Registers a callback that fires once when:

running == 0
AND
queue is empty

If already drained at registration time, the callback fires immediately on the next opportunity inside the loop.

cancel_queued([$predicate])

my $n = $forker->cancel_queued;
my $n = $forker->cancel_queued(sub ($req) { ... });

Cancels queued requests. If a predicate is provided, only queued requests for which the predicate returns true are canceled.

Returns the number canceled.

RETURN OBJECTS

Linux::Event::Fork::Child

Represents a running (or exited) child process.

See Linux::Event::Fork::Child.

Linux::Event::Fork::Request

Represents a queued spawn request that has not yet started.

See Linux::Event::Fork::Request.

CAPACITY AND QUEUE MODEL

When max_children is non-zero:

running < max_children   -> spawn immediately (Child)
running >= max_children  -> enqueue (Request)

When a child exits, capacity is released and queued requests start FIFO.

Changing max_children at runtime affects future starts, and increasing the limit may immediately start queued requests.

WHAT THIS MODULE IS NOT

This is not a supervisor, scheduler, or promise framework.

It is a deterministic process management layer for child processes built directly on Linux::Event.

AUTHOR

Joshua S. Day

LICENSE

Same terms as Perl itself.