Module: sys.process
Source: ./sys/process.reef
Overview
sys/process - Process spawning and management
Provides functions for spawning child processes, executing shell commands, and managing process lifecycle.
Example - Run a command and wait: match process_spawn_shell("ls -la") Option_Some(pid) => match process_wait_status(pid) Result_Ok(ExitStatus_Exited(code)) => println("exited ${code}") end Result_Ok(ExitStatus_Signaled(sig, core)) => println("killed by ${sig}") end Result_Err(e) => println("wait failed") end end match end Option_None() => println("could not spawn") end end match
Example - Non-blocking wait: match process_try_wait_status(pid) Result_Ok(Option_None()) => println("still running") end Result_Ok(Option_Some(st)) => println("done, shell code ${st.shell_code()}") end Result_Err(SysError_NoChild()) => println("not our child") end Result_Err(SysError_InvalidPid(p)) => println("bad pid ${p}") end Result_Err(SysError_Errno(n)) => println("wait failed: errno ${n}") end end match
Wait guarantees (0.11):
- The runtime never calls waitpid(-1, ...) on its own behalf. The only calls that wait for "any child" are process_wait_any*, which you call explicitly. Every other wait names the pid it was given.
- The runtime never installs a SIGCHLD handler or changes SIGCHLD's disposition. A program that does (e.g. a supervisor) owns reaping.
- Blocking waits retry EINTR (the GC's stop-the-world signal interrupts them), so they only fail for a real reason. tests/check_waitpid_sites.sh in reef-runtime guards the first two.
Deprecated in 0.11, removed in 0.12: process_wait (int), process_try_wait (bool), process_wait_any, process_wait_any_nohang, WaitAnyResult, process_exit_code, process_exited_normally, process_was_signaled, process_term_signal and ProcessCapture.exit_code. They read thread-local "last wait" state; the *_status calls return the outcome as a value instead.
Example - Kill a process: import sys.signal match process_spawn_shell("sleep 100") Option_Some(pid) => process_kill(pid, SIGTERM()) end Option_None() => end end match
Types
ExitStatus (enum)
How a reaped child ended. Stopped/continued children are never reported: the wait calls do not pass WUNTRACED/WCONTINUED.
Variants:
Exited(int)Signaled(int, bool)
SysError (enum)
Why a wait failed. NoChild (ECHILD) is its own case because it is the one callers act on: the pid is not our child, was already reaped, or (wait_any) there are no children left.
Variants:
NoChildInvalidPid(int)Errno(int)
ChildExit
A child reaped by process_wait_any_status / _nohang_status.
Fields:
| Name | Type |
|---|---|
pid |
int |
status |
ExitStatus |
ForkResult (enum)
The three outcomes of a fork, kept apart by the type.
ROADMAP §8.2: process_fork returned the POSIX int -- child pid in the
parent, 0 in the child, -1 on error -- and a caller that writes the obvious
if pid == 0 then child else parent treats a FAILED fork as the parent.
Measured in this tree before the change: 4 of the 5 callers did exactly
that. The next step is usually a wait on that pid, and waitpid(-1) waits for
ANY child, so the failure turns into a hang or the wrong process being
reaped rather than an error.
An Option cannot express this: Child is not an absence, it is one of three real answers. The enum makes the failure impossible to fall through.
Variants:
Parent(int)ChildFailed
ProcessCapture
Fields:
| Name | Type |
|---|---|
status |
ExitStatus |
exit_code |
int |
stdout_data |
string |
stderr_data |
string |
CaptureResult (enum)
Variants:
Captured(ProcessCapture)Failed
WaitAnyResult (enum)
The three outcomes of a non-blocking wait for any child. This is an enum rather than an Option because "no child is ready yet" and "the wait failed" are different answers that both used to arrive as a number: 0 and -1 respectively, one loop-again and one give-up, distinguished only by a caller who remembered to test for both.
Variants:
Exited(int)NoneReadyFailed
Functions
fn process_spawn_shell(command: string): option.Option[int]
Spawn a shell command. The child PID, or None if the shell could not be spawned. ROADMAP 8.2: this returned -1, a value a caller had to remember to test before passing it to process_wait -- where -1 means "any child", not "that child".
fn exit_status_from_raw(raw: int): ExitStatus
fn sys_error_from_neg(rc: int): SysError
fn exit_status_core_supported(): bool
True if this build can report Signaled(_, core = true) at all.
fn process_wait_status(pid: int): result.Result[ExitStatus, SysError]
Wait for one child to exit (blocking; EINTR is retried). pid <= 0 is Err(InvalidPid): use process_wait_any_status for "any".
fn process_try_wait_status(pid: int): result.Result[option.Option[ExitStatus], SysError]
Non-blocking wait for one child: Ok(None) while it is still running, Ok(Some(status)) once it has been reaped, Err when the wait failed. pid <= 0 is Err(InvalidPid).
fn process_wait_any_status(): result.Result[ChildExit, SysError]
Wait for ANY child (blocking; waitpid(-1)). Err(NoChild) when there are no children left.
fn process_wait_any_nohang_status(): result.Result[option.Option[ChildExit], SysError]
Non-blocking wait for ANY child: Ok(None) when children exist but none has exited, Err(NoChild) when there are none.
fn process_wait(pid: int): int
DEPRECATED: use process_wait_status. Wait for a process to exit (blocking). Returns the shell code -- the exit code, or 128 + signal for a signal death (before 0.11 a signal death returned -1) -- or -1 only when the wait itself failed, including pid <= 0 (never passed on as waitpid(-1)). The getters below describe the same child.
fn process_try_wait(pid: int): bool
DEPRECATED: use process_try_wait_status. Non-blocking wait - check if process has exited Returns false only while the process is still running. true means it is not running any more: it was reaped now (the status getters report how it ended), or the wait failed (not our child, or already reaped; the getters then report neither a normal exit nor a signal). pid <= 0 is a failed wait (true); it is never passed to waitpid.
fn process_kill(pid: int, signum: int): bool
Send a signal to a process Returns true on success
fn process_exit_code(): int
DEPRECATED: match on the ExitStatus from the *_status calls. Get exit code from last wait operation
fn process_exited_normally(): bool
DEPRECATED: match on the ExitStatus from the *_status calls. Check if last waited process exited normally (vs signaled)
fn process_was_signaled(): bool
DEPRECATED: match on the ExitStatus from the *_status calls. Check if last waited process was killed by a signal
fn process_term_signal(): int
DEPRECATED: match on the ExitStatus from the *_status calls. Get the signal that terminated the process (if signaled)
fn getpid(): int
Get current process ID
fn getppid(): int
Get parent process ID
fn getuid(): int
fn process_fork(): ForkResult
Fork the current process.
fn process_spawn(program: string, argv: [string]): option.Option[int]
Spawn a program with the given argv vector (POSIX execve semantics).
argv[0] is conventionally the program name; the runtime does NOT
prepend program for you. If you want Python/Go-style auto-prepend,
use process_run instead.
The child PID, or None if the spawn failed. ROADMAP §8.2: this returned -1,
which a caller had to remember to test before using the value as a pid.
fn process_exec(program: string, argv: [string]): int
Replace current process with program (POSIX execve semantics; does not
return on success). argv[0] is conventionally the program name; the
runtime does NOT prepend program for you. For the higher-level
wrapper, use process_run_exec.
fn process_run(program: string, arg_list: [string]): option.Option[int]
high-level wrapper matching Python subprocess.run / Go exec.Command convention. Builds argv as [program, ...arg_list] so callers pass only the trailing arguments and don't have to think about argv[0]. Returns child PID on success, -1 on error.
Parameter is named arg_list (not args) because consumers
commonly alias import sys.optparse as args, which collides with a
parameter named args. The compiler rejects that collision, but the
rename keeps the stdlib defensive.
fn process_run_exec(program: string, arg_list: [string]): int
Exec variant of process_run — replace current process, auto-prepending program as argv[0]. Does not return on success.
fn copy_capture_buf(buf: pointer, len: int): string
fn capture_from_runtime(program: string, argv: [string]): CaptureResult
fn process_spawn_fds(program: string, argv: [string], stdin_fd: int, stdout_fd: int, stderr_fd: int): option.Option[int]
Spawn with redirected stdio fds. -1 means inherit the parent's fd. POSIX argv (caller supplies argv[0]).
fn process_run_capture(program: string, arg_list: [string]): CaptureResult
Run program with auto-prepended argv[0]; capture stdout + stderr + exit.
fn process_spawn_capture(program: string, argv: [string]): CaptureResult
Capture with full POSIX argv (caller supplies argv[0]).
fn process_setsid(): option.Option[int]
Create a new session (setsid). The new session ID, or None if the call failed -- setsid fails when the caller is already a process-group leader, which is the ordinary case in a parent that forgot to fork first.
fn process_getpgid(pid: int): option.Option[int]
Get process group ID. None if the pid does not exist or is not visible to this process.
fn process_setpgid(pid: int, pgid: int): bool
Set process group ID. True on success. Mirrors process_kill, which already reports success as a bool: the successful return carries no information beyond "it worked" (setpgid returns 0), so there is no value for an Option to hold.
fn process_wait_any(): option.Option[int]
DEPRECATED: use process_wait_any_status. Wait for any child process (blocking). The PID of the child that exited, or None if there are no children left to wait for (ECHILD) or the wait failed.
fn process_wait_any_nohang(): WaitAnyResult
DEPRECATED: use process_wait_any_nohang_status. Non-blocking wait for any child.
fn killpg(pgrp: int, sig: int): bool
Send signal to a process group. True on success.
fn umask(mask: int): int
Set file creation mask Returns previous mask value
Procedures
proc exit_now(code: int)
Immediately exit process without cleanup Use this in forked child processes to avoid running GC/AO cleanup
Generated by reefc doc