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:

  • NoChild
  • InvalidPid(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)
  • Child
  • Failed

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)
  • NoneReady
  • Failed

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