]> Repositorios git - scryer-prolog.git/commitdiff
Make sleep/1 interruptible by Ctrl-C
authorJavier Sagredo <[email protected]>
Wed, 17 Jun 2026 21:58:29 +0000 (23:58 +0200)
committerJavier Sagredo <[email protected]>
Wed, 17 Jun 2026 21:58:29 +0000 (23:58 +0200)
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
src/machine/system_calls.rs

index e356bc38b91d0a11a25f93423ff7c87de3d440f4..ae3c1025e6307009e689a9c2987e4d5756d310d8 100644 (file)
@@ -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 => {
index 0e36d1ad9cca3aa7a5667914ecc6c9509fc3a6b8..0baa9b936acfe82b2ce0fe388abc03e449afbc9c 100644 (file)
@@ -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)]