net: harden epoll-wait against EINTR + EDN improper-list write

ober

95f42c95ca06b6553dc5e2cafe79c766a970ff9d

diff --git a/jerboa-native-rs/src/epoll.rs b/jerboa-native-rs/src/epoll.rs
index e44a7c7..211d569 100644
--- a/jerboa-native-rs/src/epoll.rs
+++ b/jerboa-native-rs/src/epoll.rs
@@ -55,16 +55,21 @@ pub extern "C" fn jerboa_epoll_wait(
             libc::epoll_event { events: 0, u64: 0 };
             max_events as usize
         ];
-        let n = unsafe {
-            libc::epoll_wait(epfd, events.as_mut_ptr(), max_events, timeout_ms)
+        let n = loop {
+            let r = unsafe {
+                libc::epoll_wait(epfd, events.as_mut_ptr(), max_events, timeout_ms)
+            };
+            if r < 0 {
+                let err = std::io::Error::last_os_error();
+                if err.raw_os_error() == Some(libc::EINTR) {
+                    // Interrupted by signal — retry transparently.
+                    continue;
+                }
+                crate::panic::set_last_error(format!("epoll_wait: {}", err));
+                return -1;
+            }
+            break r;
         };
-        if n < 0 {
-            crate::panic::set_last_error(format!(
-                "epoll_wait: {}",
-                std::io::Error::last_os_error()
-            ));
-            return -1;
-        }
         // Copy results to output buffer: each entry is (fd:i32, events:u32) = 8 bytes
         let out = unsafe {
             std::slice::from_raw_parts_mut(events_out, (max_events as usize) * 8)
diff --git a/jerboa-native-rs/src/lib.rs b/jerboa-native-rs/src/lib.rs
index e90fc6d..a27b353 100644
--- a/jerboa-native-rs/src/lib.rs
+++ b/jerboa-native-rs/src/lib.rs
@@ -39,9 +39,9 @@ mod wasm;
 #[cfg(feature = "spidermonkey")]
 mod wasm_sm;
 
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "android"))]
 mod epoll;
-#[cfg(all(target_os = "linux", feature = "tls"))]
+#[cfg(all(any(target_os = "linux", target_os = "android"), feature = "tls"))]
 mod http_parse;
 #[cfg(target_os = "linux")]
 mod inotify_native;
diff --git a/lib/std/net/io.sls b/lib/std/net/io.sls
index 53402c7..d111522 100644
--- a/lib/std/net/io.sls
+++ b/lib/std/net/io.sls
@@ -222,7 +222,11 @@
         (when (io-poller-running? poller)
           ;; Block until events (or wakefd signal).
           ;; Use 100ms timeout as safety net so we re-check running?.
-          (let ([events (epoll-wait epfd *max-events* 100)])
+          ;; epoll_wait can return -1/EINTR on Termux when a signal
+          ;; arrives mid-syscall; treat that as an empty event list and
+          ;; re-enter the loop instead of letting the poller die.
+          (let ([events (guard (exn [#t '()])
+                          (epoll-wait epfd *max-events* 100))])
             (for-each
               (lambda (ev)
                 (let ([fd (car ev)]
diff --git a/lib/std/text/edn.sls b/lib/std/text/edn.sls
index ce837b5..35fd498 100644
--- a/lib/std/text/edn.sls
+++ b/lib/std/text/edn.sls
@@ -323,10 +323,18 @@
   (define (write-edn-list lst port)
     (put-char port #\()
     (let loop ([l lst] [first? #t])
-      (unless (null? l)
-        (unless first? (put-char port #\space))
-        (write-edn (car l) port)
-        (loop (cdr l) #f)))
+      (cond
+        [(null? l) (void)]
+        [(pair? l)
+         (unless first? (put-char port #\space))
+         (write-edn (car l) port)
+         (loop (cdr l) #f)]
+        [else
+         ;; Improper / dotted pair: emit final element as if it were the
+         ;; second member of a 2-list.  Lossy for cons cells with non-list
+         ;; cdrs, but keeps EDN spec-compliant and avoids a crash.
+         (unless first? (put-char port #\space))
+         (write-edn l port)]))
     (put-char port #\)))
 
   (define (write-edn-vector vec port)