diff --git a/examples/swipl-module-example/src/lib.rs b/examples/swipl-module-example/src/lib.rs index 15564582a1..74a7c457b1 100644 --- a/examples/swipl-module-example/src/lib.rs +++ b/examples/swipl-module-example/src/lib.rs @@ -3,6 +3,8 @@ use swipl::prelude::*; use std::cmp::Ordering; use std::io::{self, Write}; use std::sync::Arc; +use std::thread::sleep; +use std::time::Duration; predicates! { semidet fn unify_with_foo(_context, term) { @@ -112,6 +114,18 @@ predicates! { context.try_or_die(writeln!(stream, "こんにちは! 🫡")) } + + semidet fn sleep_n_secs(context, n_term) { + let n: u64 = n_term.get_ex()?; + for _ in 0..n { + sleep(Duration::from_secs(1)); + // this handle_signals ensures that every second the user + // is able to safely interrupt this sleep. + context.handle_signals()?; + } + + Ok(()) + } } #[arc_blob("moo")] @@ -161,4 +175,6 @@ pub extern "C" fn install() { register_unify_with_bar_baz(); register_stream_write_hello(); + + register_sleep_n_secs(); } diff --git a/swipl/src/context.rs b/swipl/src/context.rs index 73b26b878d..8d8e05fa49 100644 --- a/swipl/src/context.rs +++ b/swipl/src/context.rs @@ -301,6 +301,32 @@ impl<'a, T: ContextType> Context<'a, T> { WritablePrologStream::new(current_output) } } + + /// Handle any outstanding synchronous signals, including user interrupts. + /// + /// This could return PrologError::Exception, which should + /// normally be propagated all the way back to SWI-Prolog. In + /// particular, the raised exception will be a term containing the + /// atom '$aborted' for the case where a user interrupts. + /// + /// This should be regularly called for long-running foreign code. + /// SWI-Prolog handles signals synchronously at safe points, and + /// therefore will never do so while a foreign predicate is + /// running. The most visible outcome of this is that long-running + /// predicates can normally not be interrupted by the user when + /// they press ctrl-c once. + /// + /// On success, the number of outstanding signals handled is + /// returned. + pub fn handle_signals(&self) -> PrologResult { + let result: i32 = unsafe { PL_handle_signals() }; + + if result == -1 { + Err(PrologError::Exception) + } else { + Ok(result as u32) + } + } } trait ContextParent {