Skip to content

Ignore SIGPIPE process-wide — broken-pipe writes must not kill the server (Refs #320) - #328

Merged
kiki830621 merged 1 commit into
mainfrom
idd/320-sigpipe-disposition
Aug 1, 2026
Merged

Ignore SIGPIPE process-wide — broken-pipe writes must not kill the server (Refs #320)#328
kiki830621 merged 1 commit into
mainfrom
idd/320-sigpipe-disposition

Conversation

@kiki830621

Copy link
Copy Markdown
Member

Refs #320

Summary

No SIGPIPE disposition was ever set, so a broken-pipe write (any of ~26 stderr sites, or the stdout transport) killed the server in the kernel — unreachable by Swift error handling. signal(SIGPIPE, SIG_IGN) now installs at startup; broken-pipe writes become EPIPE errnos the throwing write sites already swallow.

Diagnosis-ordered safety sequence: (1) verified stdin-EOF is the clean-shutdown path before changing anything; (2) applied the fix; (3) verified live that broken-pipe stdout + stdin EOF still exits rc=0 (no spin).

Verification

  • SigpipeDispositionTests: delivers SIGPIPE to the real spawned binary — pre-fix dies by signal 13 (2/2), post-fix survives (2/2). Deterministic and load-independent.
  • Deliberately NOT asserted: bounded exit-after-EOF. Interleaved A/B showed 4s→>40s variance on the unmodified binary (pre-existing; likely the startup sync's 45s guard) — filed separately rather than baked into a flaky assertion. An initial non-interleaved measurement wrongly implicated this fix; interleaving dissolved it into load noise (recorded for honesty).
  • Full suite: 1069 tests, 0 failures.
  • Plan-tier deliberation skipped under unattended /idd-all (marked). Codex lens unavailable until Aug 5 — Claude-only verify round.

Coordination

main.swift is also touched by unmerged PR #307 (adds a --version case in the switch; this PR adds signal() above it) — different regions, trivial adjacency at merge.

🤖 Generated by /idd-all. Do NOT add a GitHub close trailer — run /idd-close after merge.

…the server

The default disposition killed the process in the kernel before write()
returned to Swift — unreachable by try?/do-catch — for any of ~26 stderr
sites or the stdout stdio transport (verified: delivered SIGPIPE → death
by signal 13, 2/2 on the pre-fix binary).

Sequenced per the diagnosis to avoid trading death-on-disconnect for
spin-on-disconnect:
 1. verified stdin-EOF is the governing clean-shutdown path (rc=0,
    independent of stdout state) BEFORE the change,
 2. installed signal(SIGPIPE, SIG_IGN) in main.swift before any transport,
 3. verified live that broken-pipe stdout + stdin EOF still exits rc=0.

SigpipeDispositionTests pins the disposition by delivering SIGPIPE to the
real spawned binary — deterministic and load-independent (pre 2/2 dead,
post 2/2 alive), unlike a bounded exit-after-EOF assertion, which
interleaved A/B showed would flake on pre-existing latency variance
(4s->40s+ on the unmodified binary; recorded as a separate observation).

Measurement honesty: an initial non-interleaved A/B wrongly suggested the
fix slowed shutdown; interleaving dissolved the signal into load noise.

Plan-tier note: implemented under unattended /idd-all — Plan deliberation
skipped per orchestrator contract, marked here.

1069 tests, 0 failures.

Refs #320
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.

1 participant