NAME

Proc::Spawner - start children with posix_spawn(3) and run the one event loop (EXPERIMENTAL)

SYNOPSIS

my $pid = Proc::Spawner::start('/bin/ls', ['ls', '-l'], undef, { 1 => fileno $w, 3 => undef },
                               chdir => '/tmp', sigdefault => ['PIPE'], on_exit => sub { ... });
Proc::Spawner::wait_until(sub { $done });

# the raw call, without the loop:
my $pid = Proc::Spawner::spawn('ls', ['ls', '-l'], undef,
                            [[dup2 => fileno($w), 1], [close => 3], [chdir => '/tmp']],
                            {search => 1, sigdefault => [POSIX::SIGPIPE()]})
  // die "spawn: $!";

DESCRIPTION

The comments at the top of the module describe start and the loop.

spawn(FILE, \@ARGV, \@ENV_OR_UNDEF, \@ACTIONS, \%ATTR, \%UNSUPPORTED) (here an alias of the XSUB __posix_spawn) calls posix_spawn (or posix_spawnp with search => 1; pidfd_spawn or pidfd_spawnp with pidfd => 1) and returns the pid (and with pidfd, the pidfd too), or the empty list with $! set. \@ENV is a list of "NAME=value" strings; undef passes environ.

Actions, applied in the child in order: [dup2 => FROM, TO], [close => FD], [open => FD, PATH, FLAGS, MODE], [chdir => DIR], [fchdir => FD], [closefrom => FD], [tcsetpgrp => FD] (the last four are glibc extensions).

Attributes: search; pidfd; sigdefault => [SIGNUMS] (reset to the default action in the child); sigmask => [SIGNUMS] (the child's signal mask, [] for an empty one); pgroup => PGID (0: a new group led by the child); setsid; resetids; sched => [POLICY, PRIORITY]; cgroup => FD.

\%UNSUPPORTED (optional) says, per option, what a port that lacks it (a future Windows one) does: 'drop' it or 'fail'. Options not listed take the value in %Proc::Spawner::UNSUPPORTED, where '*' covers the rest (default 'fail', which fails the call with ENOTSUP). It applies with posix_spawn too, to the extensions this build's libc lacks: chdir, fchdir, closefrom, tcsetpgrp, pidfd and cgroup. They are functions, not macros, so Makefile.PL finds them by compiling a call to each (in glibc they came with 2.29, 2.29, 2.34, 2.35, 2.39 and 2.39). A dropped pidfd returns only the pid.

LIMITATIONS

Process-wide state is flipped for a while, so these parts are bullet-proof only in non-threaded execution: the umask, set around start's call (umask => N), and the SIGCHLD action, taken over while wait_until runs. posix_spawn cannot make the child ignore a signal, nor set its resource limits or nice value. Only Linux with glibc 2.39 has been tested.

AUTHOR

Ilya Zakharevich

COPYRIGHT AND LICENCE

Copyright (C) 2026 by Ilya Zakharevich

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.22.2 or, at your option, any later version of Perl 5 you may have available.