Skip to content

[Native Image] native-image exits while JVM is waiting #12116

@borkdude

Description

@borkdude

Describe the Issue

For a given Clojure program, the JVM waits but native-image exits on all OSes except linux + musl.
I believe it has something to do with non-daemon threads.

Using the latest version of GraalVM can resolve many issues.

GraalVM Version

$ java -version
java version "26" 2026-03-17
Java(TM) SE Runtime Environment Oracle GraalVM 26-dev+11.1 (build 26+11-jvmci-b01)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 26-dev+11.1 (build 26+11-jvmci-b01, mixed mode, sharing)

Operating System and Version

all supported OSes

Troubleshooting Confirmation

Run Command

./repro

Expected Behavior

I expect the native-image to behave in a similar way as the JVM and across OSes. In the case of this program, it should wait.

Actual Behavior

The native-image exits immediately except on musl.

Steps to Reproduce

Compile the below Clojure program to native-image. For ease of building I provide the uberjar. The native-image instructions are:

native-image -jar dude-1.0.0-standalone.jar --initialize-at-build-time=clojure,repro --verbose repro
(ns repro.repro
  (:gen-class))

(defn- set-daemon-agent-executor
  "Set Clojure's send-off agent executor (also affects futures). This is almost
  an exact rewrite of the Clojure's executor, but the Threads are created as
  daemons."
  []
  (let [thread-counter (atom 0)
        thread-factory (reify java.util.concurrent.ThreadFactory
                         (newThread [_ runnable]
                           (let [name (format "CLI-agent-send-off-pool-%d"
                                              (first (swap-vals! thread-counter inc)))]
                             (doto (Thread. runnable)
                               (.setDaemon true) ;; DIFFERENT
                               (.setName name)))))
        executor (java.util.concurrent.Executors/newCachedThreadPool thread-factory)]
    (set-agent-send-off-executor! executor)))

(def ^:dynamic *foo* 1)
(def a (agent nil))

(defn -main [& _args]
  (set-daemon-agent-executor)
  (prn (-> (java.lang.ProcessHandle/current) (.pid)))
  (binding [*foo* 2]
    (send-off a (fn [_] *foo*)))
  (await a)
  (prn @a))

uberjar.zip

Additional Context

No response

Run-Time Log Output and Error Messages

No response

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions