mirror of
https://github.com/mikeyobrien/ralph-orchestrator.git
synced 2026-04-25 15:15:57 +03:00
[GH-ISSUE #76] Bug: Process continues executing after Ctrl+C due to interrupt handler race condition #28
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/ralph-orchestrator#28
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @LuoAndOrder on GitHub (Jan 20, 2026).
Original GitHub issue: https://github.com/mikeyobrien/ralph-orchestrator/issues/76
Issue: Process continues executing after Ctrl+C due to interrupt handler race condition
Summary
When running
ralph runand sending an interrupt signal (Ctrl+C), the process continues executing and printing output to the terminal instead of terminating cleanly. This occurs because two interrupt handlers race against each other, and when the main loop's handler wins, the PTY executor's cleanup code is skipped.Environment
-i) and non-TUI modes affectedSteps to Reproduce
Start ralph in run mode:
Wait for the agent to start producing output
Press Ctrl+C to interrupt
Observe: Output continues to be printed to the terminal after the interrupt
Expected Behavior
Actual Behavior
Root Cause Analysis
The Race Condition
Two interrupt handlers compete when Ctrl+C is pressed:
1. Main loop interrupt handler (
crates/ralph-cli/src/main.rs:1852-1872):2. PTY executor interrupt handler (
crates/ralph-adapters/src/pty_executor.rs:388-395):What Happens When Main Loop Wins
When the main loop's
tokio::select!picks the interrupt branch:execute_futureis dropped (cancelled), not awaitedshould_terminateflag is never setstd::threadreader (spawned atpty_executor.rs:334) continues runningtui_output_txchannelcleanup_tui()is calledWhy the Reader Thread Continues
The reader thread is a native OS thread (
std::thread::spawn), not a tokio task. It only stops when:reader.read()returns EOF (child process died)blocking_send()fails (channel receiver dropped)should_terminateflag is checked and trueSince the PTY executor's cleanup is skipped, the flag is never set. The reader continues its loop until the child dies from
killpg()and the PTY returns EOF.Timing Issue with TUI Cleanup
Proposed Solution
Recommended Fix: Remove Duplicate Interrupt Handler
Remove the main loop's interrupt handler and let the PTY executor handle interrupts exclusively.
Before:
After:
Rationale
should_terminateflag is set properlyAlternative: If Safety Net is Deemed Necessary
If the main loop interrupt handler is kept as a safety net, it should:
Files Affected
crates/ralph-cli/src/main.rs- Main loop interrupt handler (lines 1850-1874)crates/ralph-adapters/src/pty_executor.rs- PTY executor interrupt handlingAcceptance Criteria
cargo testpassescargo clippypassesralph run)ralph run -i)Test Plan
Manual Testing
Related Issues
tasks/fix-termination-cleanup.code-task.md(completed) - Terminal state corruption on aborttasks/fix-ctrl-c-freeze.code-task.md(completed) - TUI freeze after double Ctrl+CLabels
bug,signal-handling,pty,tui@mikeyobrien commented on GitHub (Jan 20, 2026):
Fixed in v2.1.0! 🎉
The TUI refactor addressed the interrupt handling race condition by restructuring how iterations are managed and cleaned up.