Make blocking socket FFI collect-safe; fix http-client temp-file race
Two thread-safety bugs in the native FFI layer. The HTTP server's accept/recv/send were plain foreign-procedures. A thread inside a foreign call stays active for the stop-the-world collector, so the accept loop sitting idle in accept() froze GC for the whole process whenever another thread (a future, an async block) allocated. Mark the three blocking calls __collect_safe so the thread deactivates for the call's duration — collection proceeds while the accept thread waits. The args are an fd and foreign-alloc'd buffers (outside the Scheme heap), so a collection mid-call has nothing to move. jolt.http-client built its -D header-file path from an unguarded (set! counter (+ counter 1)) and counter mod 90000, with no per-process component. Concurrent requests could compute the same path and clobber each other's headers. Use a mutex-guarded monotonic counter plus the pid. test/chez/ffi-server-test.ss exercises both (a (collect) while the server is idle in accept(), temp-path uniqueness across threads, and a live request) and is wired into the gate as `make ffi`.
This commit is contained in:
parent
4114766c71
commit
6f433a1b3c
4 changed files with 97 additions and 8 deletions
|
|
@ -41,11 +41,17 @@
|
|||
(else n))))
|
||||
(else (jolt-str-render-one ct))))
|
||||
|
||||
;; A per-request header file path, unique across processes (PID) and threads (a
|
||||
;; mutex-guarded monotonic counter). The previous unguarded `set!` + `mod 90000`
|
||||
;; raced: concurrent callers could compute the same path and clobber each other's
|
||||
;; -D header dump. getpid is a fast, non-blocking foreign call.
|
||||
(define c-getpid (begin (load-shared-object #f) (foreign-procedure "getpid" () int)))
|
||||
(define hc-tmp-mutex (make-mutex))
|
||||
(define hc-tmp-counter 0)
|
||||
(define (hc-tmp-path)
|
||||
(set! hc-tmp-counter (+ hc-tmp-counter 1))
|
||||
(string-append (or (getenv "TMPDIR") "/tmp") "/jolt-http-"
|
||||
(number->string (* 100000 (+ 1 (modulo hc-tmp-counter 90000)))) ".hdr"))
|
||||
(let ((n (with-mutex hc-tmp-mutex (set! hc-tmp-counter (+ hc-tmp-counter 1)) hc-tmp-counter)))
|
||||
(string-append (or (getenv "TMPDIR") "/tmp") "/jolt-http-"
|
||||
(number->string (c-getpid)) "-" (number->string n) ".hdr")))
|
||||
|
||||
(define (hc-trim s)
|
||||
(let* ((n (string-length s))
|
||||
|
|
|
|||
|
|
@ -11,11 +11,18 @@
|
|||
(define c-socket (foreign-procedure "socket" (int int int) int))
|
||||
(define c-bind (foreign-procedure "bind" (int void* int) int))
|
||||
(define c-listen (foreign-procedure "listen" (int int) int))
|
||||
(define c-accept (foreign-procedure "accept" (int void* void*) int))
|
||||
(define c-setsockopt (foreign-procedure "setsockopt" (int int int void* int) int))
|
||||
(define c-recv (foreign-procedure "recv" (int void* size_t int) ssize_t))
|
||||
(define c-send (foreign-procedure "send" (int void* size_t int) ssize_t))
|
||||
(define c-close (foreign-procedure "close" (int) int))
|
||||
;; accept/recv/send can BLOCK (accept indefinitely while idle). A thread inside a
|
||||
;; plain foreign call stays "active" and stalls the stop-the-world collector for
|
||||
;; every thread, so the accept loop would freeze GC process-wide whenever a future
|
||||
;; or async block allocates while no request is in flight. __collect_safe
|
||||
;; deactivates the calling thread for the call's duration so collection proceeds.
|
||||
;; Safe here: the only arguments are an fd and foreign-alloc'd buffers (outside the
|
||||
;; Scheme heap), so a collection during the call has nothing to move.
|
||||
(define c-accept (foreign-procedure __collect_safe "accept" (int void* void*) int))
|
||||
(define c-recv (foreign-procedure __collect_safe "recv" (int void* size_t int) ssize_t))
|
||||
(define c-send (foreign-procedure __collect_safe "send" (int void* size_t int) ssize_t))
|
||||
|
||||
(define AF_INET 2) (define SOCK_STREAM 1)
|
||||
;; SOL_SOCKET / SO_REUSEADDR differ by platform: macOS uses 0xffff / 4, Linux 1 / 2.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue