]> Repositorios git - scryer-prolog.git/commitdiff
Make tcp_accept poll INTERRUPT
authorJavier Sagredo <[email protected]>
Fri, 29 May 2026 00:44:36 +0000 (02:44 +0200)
committerJavier Sagredo <[email protected]>
Fri, 29 May 2026 01:18:24 +0000 (03:18 +0200)
src/machine/system_calls.rs

index f66d7c321ec4f5c1c48cba6e3350bcdaeefa70e8..68e3f79e8885ed78c1bfd7bb97c1e925acea06dd 100644 (file)
@@ -7127,7 +7127,44 @@ impl Machine {
             (HeapCellValueTag::Cons, cons_ptr) => {
                 match_untyped_arena_ptr!(cons_ptr,
                      (ArenaHeaderTag::TcpListener, tcp_listener) => {
-                         match tcp_listener.accept().ok() {
+                         // Poll accept() in non-blocking mode so that a SIGINT
+                         // delivered while we're waiting actually lands as a
+                         // catchable '$interrupt_thrown' exception in Prolog,
+                         // instead of being stuck behind a blocking syscall.
+                         let _ = tcp_listener.set_nonblocking(true);
+                         let accepted: Option<_> = 'poll: loop {
+                             match tcp_listener.accept() {
+                                 Ok(pair) => break 'poll Some(pair),
+                                 Err(ref e) if e.kind() == ErrorKind::WouldBlock => {
+                                     let interrupted = machine::INTERRUPT.load(
+                                         std::sync::atomic::Ordering::Relaxed);
+                                     if interrupted
+                                         && machine::INTERRUPT.compare_exchange(
+                                             true,
+                                             false,
+                                             std::sync::atomic::Ordering::Relaxed,
+                                             std::sync::atomic::Ordering::Relaxed,
+                                         ).is_ok()
+                                     {
+                                         let _ = tcp_listener.set_nonblocking(false);
+                                         // Return an Err so the dispatch site's
+                                         // try_or_throw! macro performs the throw
+                                         // + backtrack + continue dance correctly
+                                         // (calling throw+backtrack here and then
+                                         //  returning Ok(()) would let the dispatch
+                                         //  step over the unwound program counter
+                                         //  with step_or_fail!).
+                                         let err = self.machine_st.interrupt_error();
+                                         let src = functor_stub(atom!("repl"), 0);
+                                         return Err(self.machine_st.error_form(err, src));
+                                     }
+                                     std::thread::sleep(std::time::Duration::from_millis(100));
+                                 }
+                                 Err(_) => break 'poll None,
+                             }
+                         };
+                         let _ = tcp_listener.set_nonblocking(false);
+                         match accepted {
                              Some((tcp_stream, socket_addr)) => {
                                  let client = AtomTable::build_with(&self.machine_st.atom_tbl, &socket_addr.to_string());