Fix nREPL server ^C shutdown crash
^C to a running `joltc --nrepl-server` aborted with "thread does not own mutex" because the accept-loop thread absorbed SIGINT in its foreign accept() call, where Chez can't run the keyboard-interrupt handler, and run-main-pump's tight condition-wait loop wasn't interruptible anyway. Block SIGINT in the primordial thread before starting the server so the accept loop inherits a blocked mask, park in a single interruptible condition-wait via the new park-until-interrupt, and run registered shutdown hooks (newest-first, each isolated) from the keyboard-interrupt handler before (exit 0). The stop fn now drops .nrepl-port via the new jolt.host/delete-file seam — clojure.java.io/delete-file doesn't exist in Jolt and silently no-ops, so .nrepl-port was never removed.
This commit is contained in:
parent
8c2bd60257
commit
46c9c7b4d9
3 changed files with 93 additions and 15 deletions
|
|
@ -200,10 +200,22 @@
|
|||
;; the GUI main loop: glimmer's run marshals its startup here via
|
||||
;; jolt.host/call-on-main-thread — on macOS GTK quartz, g_application_run
|
||||
;; must run on the main thread or AppKit aborts when it sets the main menu.
|
||||
;; Block SIGINT in this (primordial) thread before starting the server so the
|
||||
;; accept-loop future — and the conn-handler futures it spawns — inherit a
|
||||
;; blocked SIGINT mask. Without this, ^C lands on the accept loop blocked in
|
||||
;; c-accept (a foreign call), where Chez can't fire the keyboard-interrupt
|
||||
;; handler, and the server hangs. park-until-interrupt unblocks SIGINT here
|
||||
;; once its own ^C handler is installed, so ^C reaches this thread and the
|
||||
;; shutdown hooks run cleanly.
|
||||
(jolt.host/block-sigint)
|
||||
(let [stop ((resolve 'jolt.nrepl/start) port (:nrepl-middleware resolved))]
|
||||
;; park here until something calls jolt.host/stop-main-pump, then shut the
|
||||
;; server down cleanly (close the socket, remove .nrepl-port) and return.
|
||||
(jolt.host/run-main-pump)
|
||||
;; register stop so ^C (handled by park-until-interrupt) closes the socket
|
||||
;; and drops .nrepl-port on the way out.
|
||||
(jolt.host/add-shutdown-hook stop)
|
||||
;; park here until ^C (handled by park-until-interrupt's keyboard-interrupt-
|
||||
;; handler, which runs the shutdown hooks and exits). The accept loop
|
||||
;; inherited SIGINT-blocked above, so ^C is delivered to this thread.
|
||||
(jolt.host/park-until-interrupt)
|
||||
(when stop (stop))))))
|
||||
|
||||
(defn- usage []
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@
|
|||
(ffi/defcfn c-bind "bind" [:int :pointer :int] :int)
|
||||
(ffi/defcfn c-listen "listen" [:int :int] :int)
|
||||
(ffi/defcfn c-setsockopt "setsockopt" [:int :int :int :pointer :int] :int)
|
||||
(ffi/defcfn c-accept "accept" [:int :pointer :pointer] :int :blocking)
|
||||
(ffi/defcfn c-recv "recv" [:int :pointer :size_t :int] :ssize_t :blocking)
|
||||
(ffi/defcfn c-send "send" [:int :pointer :size_t :int] :ssize_t :blocking)
|
||||
(ffi/defcfn c-accept "accept" [:int :pointer :pointer] :int :blocking)
|
||||
(ffi/defcfn c-recv "recv" [:int :pointer :size_t :int] :ssize_t :blocking)
|
||||
(ffi/defcfn c-send "send" [:int :pointer :size_t :int] :ssize_t :blocking)
|
||||
(ffi/defcfn c-close "close" [:int] :int)
|
||||
|
||||
(def ^:private AF-INET 2)
|
||||
|
|
@ -243,18 +243,18 @@
|
|||
(println (str "nREPL server started on port " port " (127.0.0.1) — .nrepl-port written"))
|
||||
(when (seq middleware) (println (str ";; middleware: " (str/join " " middleware))))
|
||||
(println ";; connect your editor; ^C to stop")
|
||||
(future
|
||||
;; A stop closes fd, which makes the blocking accept() return an error; the
|
||||
;; @stopped check then breaks the loop instead of spinning on the dead fd.
|
||||
(loop []
|
||||
(future
|
||||
;; A stop closes fd, which makes the blocking accept() return an error; the
|
||||
;; @stopped check then breaks the loop instead of spinning on the dead fd.
|
||||
(loop []
|
||||
(let [conn (c-accept fd ffi/null ffi/null)]
|
||||
(when-not @stopped
|
||||
(when (>= conn 0)
|
||||
(future (try (handle-conn conn handler)
|
||||
(catch :default e (println "nrepl conn error:" (err-msg e)) (c-close conn)))))
|
||||
(recur)))))
|
||||
(fn stop []
|
||||
(when (compare-and-set! stopped false true)
|
||||
(c-close fd)
|
||||
(try (io/delete-file ".nrepl-port" true) (catch :default _ nil)))
|
||||
nil))))
|
||||
(fn stop []
|
||||
(when (compare-and-set! stopped false true)
|
||||
(c-close fd)
|
||||
(jolt.host/delete-file ".nrepl-port"))
|
||||
nil))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue