Processes

wait#

Block until any child process exits and reap it.

wait is the simplest front end to the underlying wait(2) system call. It suspends the current process until one of its children terminates, collects that child’s exit status into $? and ${^CHILD_ERROR_NATIVE}, and returns the reaped child’s PID. If there are no children to wait on, it returns -1 immediately. It is exactly equivalent to waitpid(-1, 0).

Synopsis#

my $pid = wait;

What you get back#

The PID of the child that was reaped (a positive integer), or -1 if there are no unreaped children left to wait on. The child’s status goes into $?; the raw, OS-specific status word goes into ${^CHILD_ERROR_NATIVE}.

To decode $? after a successful wait:

my $exit   = $? >> 8;       # normal exit code (0..255)
my $signal = $? & 0x7f;     # termination signal, if any
my $core   = $? & 0x80;     # core-dump flag

A -1 return does not always mean “never had any children” — it can also mean “something already reaped them for me” (see below).

Global state it touches#

  • $? — set to the reaped child’s 16-bit wait status (high byte: exit code, low byte: termination signal and core flag). Also readable as $CHILD_ERROR under use English.

  • ${^CHILD_ERROR_NATIVE} — the platform’s raw status word, for callers that want to use POSIX macros (WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG) from POSIX.

  • %SIG — if $SIG{CHLD} is set to 'IGNORE', or a handler in it calls wait itself, this call’s result changes. See Edge cases.

Examples#

Fork a worker, do something, then reap it:

my $pid = fork // die "fork: $!";
if ($pid == 0) {
    # child
    exec "/usr/bin/true" or die "exec: $!";
}
my $reaped = wait;              # blocks until the child exits
die "unexpected pid" if $reaped != $pid;
printf "child %d exited %d\n", $reaped, $? >> 8;

Reap every outstanding child in a loop. wait returns -1 once the process has none left:

while ((my $kid = wait) > 0) {
    warn "reaped $kid, status " . ($? >> 8) . "\n";
}

Distinguish normal exit from signal termination:

my $pid = wait;
if ($pid > 0) {
    if    ($? == 0)       { say "clean exit" }
    elsif ($? & 0x7f)     { say "killed by signal " . ($? & 0x7f) }
    else                  { say "exit " . ($? >> 8) }
}

Decode the native status word with POSIX macros, which is more portable than manual bit-shifting:

use POSIX ":sys_wait_h";
my $pid = wait;
if ($pid > 0 && WIFEXITED(${^CHILD_ERROR_NATIVE})) {
    say "exit code: ", WEXITSTATUS(${^CHILD_ERROR_NATIVE});
}

Non-blocking reap of every pending zombie — use waitpid with WNOHANG, not wait:

use POSIX ":sys_wait_h";
1 while waitpid(-1, WNOHANG) > 0;

Edge cases#

  • No children to wait on: returns -1 immediately. wait does not block in this case — it only blocks when at least one child is still alive and unreaped.

  • $SIG{CHLD} = 'IGNORE': on POSIX systems this asks the kernel to auto-reap children. Under this setting, wait almost always returns -1 because the children are gone before user code can call it. $? is not set. If you want both fire-and-forget children and status reporting, use a real handler, not 'IGNORE'.

  • Custom $SIG{CHLD} handler: if the handler calls wait (or waitpid) itself, a subsequent wait in the main flow will find nothing left and return -1. Decide up-front which code path does the reaping.

  • Interaction with qx// and system: both of these internally fork, exec, and wait on their own child. A $SIG{CHLD} handler that blindly calls wait can accidentally reap that internal child out from under qx// or system, leaving them unable to collect the status and typically returning -1 with $! set to ECHILD. Handlers should loop with waitpid(-1, WNOHANG) and only act on PIDs they recognise.

  • $? is only written on a successful reap. After a -1 return, $? retains whatever it had before the call. Don’t inspect it unless wait returned a positive PID.

  • Interrupted by a signal: if a signal is delivered while wait is blocked and the handler returns normally, Perl restarts the wait — you do not see EINTR at the Perl level. If the handler dies or exits, control leaves wait the usual way for that mechanism.

  • Threaded builds: wait waits for children of the current process, not children of a specific thread. In a multi-threaded program the reaping thread is whichever one called wait first.

  • Return value is a PID, not a boolean: if (wait) { ... } is true for both successful reaps and -1. Always compare to 0 or check > 0 explicitly, as in the loop form above.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • waitpid — wait for a specific child, or poll non-blockingly with WNOHANG; wait is the waitpid(-1, 0) form

  • fork — create the children that wait reaps

  • exec — what a forked child typically calls to become a different program

  • system — fork + exec + wait in one call; uses its own internal wait and sets $? for you

  • kill — send a signal to a child you no longer want to wait for

  • $? — where the reaped child’s status lands, encoded as exit-code high byte plus signal/core low byte