jolt.ffi: a :blocking option for collect-safe foreign calls

A library binding a blocking native call (accept/recv/connect/...) needs it
emitted __collect_safe so the thread deactivates for the call and doesn't pin
the stop-the-world collector. foreign-fn / defcfn take an optional trailing
:blocking; the backend emits (foreign-procedure __collect_safe ...). Needed for
the ring-janet-adapter socket-server port. ffi-binding-test asserts a thread
parked in a :blocking call doesn't block (collect).
This commit is contained in:
Yogthos 2026-06-22 12:00:14 -04:00
parent db9bed226f
commit 2a64e65a1c
5 changed files with 93 additions and 74 deletions

View file

@ -395,17 +395,21 @@
;; jolt.ffi/__cfn (jolt-ffi): the low-level foreign-function form a jolt library
;; uses (via the jolt.ffi/foreign-fn macro) to bind native code. Shape:
;; (jolt.ffi/__cfn "c_symbol" [:argtype ...] :rettype)
;; (jolt.ffi/__cfn "c_symbol" [:argtype ...] :rettype) ; non-blocking
;; (jolt.ffi/__cfn "c_symbol" [:argtype ...] :rettype :blocking) ; may block
;; The C symbol is a string literal and the types are literal keywords, read here
;; at compile time; the Chez back end lowers it to a real `foreign-procedure`
;; (typed marshaling, no runtime eval). A leaf IR node.
;; (typed marshaling, no runtime eval). A :blocking call is emitted __collect_safe
;; so it deactivates the thread for the call — a blocking call (accept/recv/...)
;; must not pin the stop-the-world collector. A leaf IR node.
(defn- analyze-ffi-fn [ctx items env]
(when (not= 4 (count items))
(throw (str "jolt.ffi/foreign-fn expects (foreign-fn \"sym\" [argtypes] rettype)")))
(when-not (<= 4 (count items) 5)
(throw (str "jolt.ffi/foreign-fn expects (foreign-fn \"sym\" [argtypes] rettype [:blocking])")))
{:op :ffi-fn
:csym (nth items 1)
:argtypes (mapv name (form-vec-items (nth items 2)))
:rettype (name (nth items 3))})
:rettype (name (nth items 3))
:blocking (and (= 5 (count items)) (= "blocking" (name (nth items 4))))})
;; The `.` special form: `(. target member arg*)` — member access / method call.
;; A symbol member whose name starts with "-" is a field read; otherwise it is a

View file

@ -292,7 +292,7 @@
(defn- ffi-type->chez [t]
(or (ffi-types t) (throw (ex-info (str "jolt.ffi: unknown foreign type :" t) {}))))
(defn- emit-ffi-fn [node]
(str "(foreign-procedure " (chez-str-lit (:csym node))
(str "(foreign-procedure " (when (:blocking node) "__collect_safe ") (chez-str-lit (:csym node))
" (" (str/join " " (map ffi-type->chez (:argtypes node))) ") "
(ffi-type->chez (:rettype node)) ")"))