(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());