mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Wait for SIGCONT when resuming the TUI (#51192)
## What changed Install a temporary `SIGCONT` handler and unblock the signal during `Ctrl+Z` suspension. After sending `SIGTSTP`, wait for the handler to confirm resumption, returning a timeout error if no confirmation arrives within the bounded wait. Restore the previous signal handler and thread signal mask, resume terminal stderr handling, and reapply terminal modes even when suspension fails. Propagate signal setup, delivery, and restoration errors to the caller. GitOrigin-RevId: 1a7e10e982b1f6b2c7c7d75362a01989409b3b4d
This commit is contained in:
@@ -202,13 +202,78 @@ impl PreparedResumeAction {
|
||||
|
||||
/// Deliver SIGTSTP after restoring terminal state, then re-applies terminal modes once resumed.
|
||||
fn suspend_process() -> Result<()> {
|
||||
static JOB_RESUMED: AtomicBool = AtomicBool::new(false);
|
||||
extern "C" fn record_job_resumed(_signal: libc::c_int) {
|
||||
JOB_RESUMED.store(true, Ordering::Release);
|
||||
}
|
||||
super::restore()?;
|
||||
super::terminal_stderr::pause()?;
|
||||
unsafe {
|
||||
libc::kill(/*pid*/ 0, libc::SIGTSTP)
|
||||
// SAFETY: both actions are initialized before they are passed to libc.
|
||||
let (mut action, mut previous_action): (libc::sigaction, libc::sigaction) =
|
||||
unsafe { std::mem::zeroed() };
|
||||
let (mut continue_set, mut previous_mask): (libc::sigset_t, libc::sigset_t) =
|
||||
unsafe { std::mem::zeroed() };
|
||||
action.sa_sigaction = record_job_resumed as *const () as libc::sighandler_t;
|
||||
action.sa_flags = libc::SA_RESTART;
|
||||
let action_installed = unsafe {
|
||||
libc::sigemptyset(&mut action.sa_mask);
|
||||
libc::sigaction(libc::SIGCONT, &action, &mut previous_action) == 0
|
||||
};
|
||||
// After the process resumes, reapply terminal modes so drawing can continue.
|
||||
super::terminal_stderr::resume()?;
|
||||
super::set_modes()?;
|
||||
Ok(())
|
||||
let unblock_result = if action_installed {
|
||||
unsafe {
|
||||
libc::sigemptyset(&mut continue_set);
|
||||
libc::sigaddset(&mut continue_set, libc::SIGCONT);
|
||||
libc::pthread_sigmask(libc::SIG_UNBLOCK, &continue_set, &mut previous_mask)
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let suspend_result = if !action_installed {
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else if unblock_result != 0 {
|
||||
Err(std::io::Error::from_raw_os_error(unblock_result))
|
||||
} else if unsafe {
|
||||
JOB_RESUMED.store(false, Ordering::Relaxed);
|
||||
libc::kill(/*pid*/ 0, libc::SIGTSTP)
|
||||
} == 0
|
||||
{
|
||||
for _ in 0..1000 {
|
||||
if JOB_RESUMED.load(Ordering::Acquire) {
|
||||
break;
|
||||
}
|
||||
std::thread::park_timeout(std::time::Duration::from_millis(1));
|
||||
}
|
||||
if JOB_RESUMED.load(Ordering::Acquire) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::ErrorKind::TimedOut.into())
|
||||
}
|
||||
} else {
|
||||
Err(std::io::Error::last_os_error())
|
||||
};
|
||||
let restore_mask_result = if action_installed && unblock_result == 0 {
|
||||
let result = unsafe {
|
||||
libc::pthread_sigmask(libc::SIG_SETMASK, &previous_mask, std::ptr::null_mut())
|
||||
};
|
||||
if result == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::from_raw_os_error(result))
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
// SAFETY: previous_action was initialized by the successful sigaction call above.
|
||||
let restore_result = if !action_installed
|
||||
|| unsafe { libc::sigaction(libc::SIGCONT, &previous_action, std::ptr::null_mut()) } == 0
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::last_os_error())
|
||||
};
|
||||
suspend_result
|
||||
.and(restore_mask_result)
|
||||
.and(restore_result)
|
||||
.and(super::terminal_stderr::resume())
|
||||
.and(super::set_modes())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user