Skip to content

feat(ffi): surface the terminating reason (timeout/signal/kill) through the C ABI and SDKs#136

Merged
congwang-mk merged 1 commit into
multikernel:mainfrom
dzerik:feat/exit-reason-abi
Jul 10, 2026
Merged

feat(ffi): surface the terminating reason (timeout/signal/kill) through the C ABI and SDKs#136
congwang-mk merged 1 commit into
multikernel:mainfrom
dzerik:feat/exit-reason-abi

Conversation

@dzerik

@dzerik dzerik commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Closes #131.

sandlock_result_exit_code flattens Signal/Killed/Timeout to -1, so a
caller can't tell a timeout from a signal from a normal exit. This adds the
systemd-style reason taxonomy you approved ("go for it", minus the oom-kill):
Linux bottoms both a timeout and an OOM kill out in SIGKILL, and telling OOM
apart needs cgroup v2 memory.events introspection, so KILLED (SIGKILL, no
recoverable number) and TIMEOUT (sandlock-enforced) are the honest split.

C ABI

  • sandlock_exit_reason enum { EXITED, SIGNALED, KILLED, TIMEOUT }
    (#[repr(u32)], matching the sibling enums).
  • sandlock_result_reason / sandlock_result_signal, plus the dry_run pair.
    signal is the number for SIGNALED, else -1; a null result is KILLED/-1.

Bindings (parity)

  • Python: ExitReason IntEnum + Result.reason/.signal and DryRunResult,
    wired into every native-result path (run / wait / spawn / reduce /
    Process.wait / Pipeline / GatherPipeline / dry_run). The fragile
    exit_code == -1 timeout heuristic is replaced by reason == TIMEOUT.
  • Go: ExitReason + Result.Reason/.Signal via readResult and DryRun.

Tests

  • Python + Go: exited / timeout / signaled (SIGTERM=15) / killed (SIGKILL, no number).
  • Rust FFI unit test pins all four ExitStatus variants incl. KILLED and the
    null-result contract.

Full CI (Rust + Go + Python 3.10–3.12, low-ABI, cbindgen header check) is green.

…e C ABI and SDKs (multikernel#131)

sandlock_result_exit_code flattens Signal/Killed/Timeout to -1, so a caller can't
tell a timeout from a signal from a normal exit. Add a systemd-style reason
taxonomy — deliberately WITHOUT an oom-kill reason: Linux bottoms both a timeout
and an OOM kill out in SIGKILL, and telling OOM apart needs cgroup v2
memory.events introspection, so KILLED (SIGKILL, no recoverable number) and
TIMEOUT (sandlock-enforced) are the honest split we can report.

- C ABI: `sandlock_exit_reason` enum { EXITED, SIGNALED, KILLED, TIMEOUT }
  (#[repr(u32)]) + `sandlock_result_reason`/`_signal` and the dry-run pair.
- Python: `ExitReason` IntEnum + `Result.reason`/`.signal` (and DryRunResult),
  wired into every native-result path (run/wait/spawn/reduce/Process.wait/
  Pipeline/GatherPipeline/dry_run); the fragile `exit_code == -1` timeout
  heuristic is replaced by `reason == TIMEOUT`. reason defaults to None on the
  error paths that never produced a native result.
- Go: `ExitReason` + `Result.Reason`/`.Signal` via readResult and DryRun.
- Tests: Python + Go cover exited/timeout/signaled(SIGTERM)/killed(SIGKILL);
  a Rust FFI unit test pins all four ExitStatus variants incl. KILLED and the
  null-result contract.
@congwang-mk congwang-mk merged commit c4dfd04 into multikernel:main Jul 10, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Surface the terminating reason (timeout / signal / kill) through the C ABI and SDK Result

2 participants