From 7816f95894abcf760e24247a5a066fda462a0cc1 Mon Sep 17 00:00:00 2001 From: Javier Sagredo Date: Wed, 17 Jun 2026 23:58:29 +0200 Subject: [PATCH] Make sleep/1 interruptible by Ctrl-C sleep/1 previously called a single blocking std::thread::sleep for the whole duration, which runs to completion regardless of SIGINT. The interrupt flag was only polled by the dispatch loop every 256 instructions, so a sleep-dominated loop kept running for a nondeterministic number of extra iterations after Ctrl-C. Sleep in 50ms slices and poll interrupt_occured() between them, throwing the interrupt exception promptly when the flag is set. --- src/machine/dispatch.rs | 4 ++-- src/machine/system_calls.rs | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index e356bc38..ae3c1025 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4909,11 +4909,11 @@ impl Machine { step_or_fail!(self.machine_st, self.machine_st.p = self.machine_st.cp); } &Instruction::CallSleep => { - self.sleep(); + try_or_throw!(self.machine_st, self.sleep(), continue); self.machine_st.p += 1; } &Instruction::ExecuteSleep => { - self.sleep(); + try_or_throw!(self.machine_st, self.sleep(), continue); self.machine_st.p = self.machine_st.cp; } &Instruction::CallSocketClientOpen => { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 0e36d1ad..0baa9b93 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -6959,7 +6959,7 @@ impl Machine { } #[inline(always)] - pub(crate) fn sleep(&mut self) { + pub(crate) fn sleep(&mut self) -> CallResult { let time = self.deref_register(1); let time = match Number::try_from((time, &self.machine_st.arena.f64_tbl)) { @@ -6972,9 +6972,27 @@ impl Machine { }; let duration = Duration::new(1, 0); - let duration = duration.mul_f64(time); + let mut remaining = duration.mul_f64(time); - std::thread::sleep(duration); + // Sleep in small slices, checking for a pending interrupt (e.g. Ctrl-C) + // between each one. A single blocking `std::thread::sleep` would run to + // completion regardless of SIGINT, leaving the interrupt unobserved + // until well after the sleep returns. + let slice = Duration::from_millis(50); + + while !remaining.is_zero() { + let step = remaining.min(slice); + std::thread::sleep(step); + remaining -= step; + + if self.interrupt_occured() { + let err = self.machine_st.interrupt_error(); + let src = functor_stub(atom!("repl"), 0); + return Err(self.machine_st.error_form(err, src)); + } + } + + Ok(()) } #[inline(always)] -- 2.54.0