std/net,std/os: classify macOS as BSD for socket/fcntl constants
ober
bd3d97d764b1326431704d3c8eec6db0c900c444
--- a/lib/std/net/tcp.ss +++ b/lib/std/net/tcp.ss @@ -77,20 +77,35 @@ (foreign-procedure "__errno_location" () void*))))) (def (get-errno) (foreign-ref 'int (c-errno-location) 0)) (def EINTR 4) - (def *freebsd?* (memq (machine-type) '(a6fb ta6fb i3fb ti3fb arm64fb))) - (def EAGAIN (if *freebsd?* 35 11)) + ;; macOS (Darwin) and FreeBSD share the BSD socket ABI: O_NONBLOCK=0x4, + ;; EAGAIN=35, SOL_SOCKET=0xffff, SO_REUSEADDR=4, and sockaddr_in carries a + ;; sin_len byte at offset 0. Linux differs on every one of these. Chez tags + ;; macOS machine-types with an "osx" suffix (e.g. tarm64osx); they were + ;; absent from the list below, so macOS silently used the LINUX constants. + ;; Worst effect: set-nonblocking! wrote O_EXCL(0x800), not O_NONBLOCK(0x4), + ;; leaving sockets BLOCKING — and a blocking c-read (a plain, non- + ;; __collect_safe foreign-procedure) then pins Chez's collector for the + ;; whole read, freezing every thread. (The errno probe above already + ;; treats osx as BSD; the socket constants just never matched.) + (def *bsd?* + (let ([mt (symbol->string (machine-type))]) + (or (memq (machine-type) '(a6fb ta6fb i3fb ti3fb arm64fb)) + (and (>= (string-length mt) 3) + (string=? (substring mt (- (string-length mt) 3) + (string-length mt)) + "osx"))))) + (def EAGAIN (if *bsd?* 35 11)) ;; fcntl constants (def F_GETFL 3) (def F_SETFL 4) - (def O_NONBLOCK - (if (memq (machine-type) '(a6fb ta6fb i3fb ti3fb arm64fb)) #x4 #x800)) + (def O_NONBLOCK (if *bsd?* #x4 #x800)) ;; Constants (def AF_INET 2) (def SOCK_STREAM 1) - (def SOL_SOCKET (if *freebsd?* #xffff 1)) - (def SO_REUSEADDR (if *freebsd?* 4 2)) + (def SOL_SOCKET (if *bsd?* #xffff 1)) + (def SO_REUSEADDR (if *bsd?* 4 2)) (def SOCKADDR_IN_SIZE 16) ;; sizeof(struct sockaddr_in) on Linux ;; GC-safe retry delay: 10ms via Chez's sleep (not a foreign call). @@ -112,9 +127,9 @@ (foreign-set! 'unsigned-8 buf i 0) (lp (+ i 1)))) ;; sin_family = AF_INET - ;; FreeBSD sockaddr_in has sin_len (uint8) at offset 0, sin_family (uint8) at offset 1 + ;; BSD/Darwin sockaddr_in has sin_len (uint8) at offset 0, sin_family (uint8) at offset 1 ;; Linux sockaddr_in has sin_family (uint16) at offset 0 - (if *freebsd?* + (if *bsd?* (begin (foreign-set! 'unsigned-8 buf 0 16) ;; sin_len = sizeof(sockaddr_in) (foreign-set! 'unsigned-8 buf 1 AF_INET)) ;; sin_family (uint8) --- a/lib/std/os/fcntl.ss +++ b/lib/std/os/fcntl.ss @@ -54,10 +54,26 @@ (def F_SETFL 4) ;; File status flags (F_GETFL/F_SETFL) + ;; macOS (Darwin), FreeBSD, and Linux use different fcntl flag bits. + ;; O_NONBLOCK/O_APPEND match between Darwin and FreeBSD, but O_CLOEXEC + ;; differs across all three (Darwin 0x1000000, FreeBSD 0x100000, Linux + ;; 0x80000). Chez tags macOS machine-types with an "osx" suffix (e.g. + ;; tarm64osx). Misclassifying macOS as Linux gave O_NONBLOCK=0x800 — which + ;; is Darwin's O_EXCL — so set-nonblocking! silently failed, leaving fds + ;; BLOCKING; a plain (non-__collect_safe) read on such an fd then pins + ;; Chez's collector for the whole read and freezes every thread. (def *freebsd?* (memq (machine-type) '(a6fb ta6fb i3fb ti3fb arm64fb))) - (def O_APPEND (if *freebsd?* #x8 #x400)) - (def O_NONBLOCK (if *freebsd?* #x4 #x800)) - (def O_CLOEXEC (if *freebsd?* #x100000 #x80000)) + (def *darwin?* + (let ([mt (symbol->string (machine-type))]) + (and (>= (string-length mt) 3) + (string=? (substring mt (- (string-length mt) 3) (string-length mt)) + "osx")))) + (def *bsd?* (or *freebsd?* *darwin?*)) + (def O_APPEND (if *bsd?* #x8 #x400)) + (def O_NONBLOCK (if *bsd?* #x4 #x800)) + (def O_CLOEXEC (cond (*darwin?* #x1000000) + (*freebsd?* #x100000) + (else #x80000))) ;; File descriptor flags (F_GETFD/F_SETFD) (def FD_CLOEXEC 1)