From: Javier Sagredo Date: Wed, 17 Jun 2026 10:57:33 +0000 (+0200) Subject: Make sleep/1 interruptible by Ctrl-C X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=844f8e23474d698f41d8ba48a5bc788cd54b4735;p=scryer-prolog.git 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. --- 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 e79517aa..8703e279 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -6968,7 +6968,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)) { @@ -6981,9 +6981,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)]