I’m trying to use Clojure to write a quick little script but my program won’t exit for some reason.
(ns benchmark.core
(:require [clojure.java.shell :refer :all])
(:gen-class))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(let [command "ls"
args "-al"
program-run (sh command args)]
(println (:out program-run))))
The weird thing is that In the REPL (Emacs/CIDER) I can run (-main) and it’ll print the output and then go back to waiting for the next user provided command to run. But when I run it with lein run it’ll just hang after printing.
However, I’ve confirmed that a simple (println "Hello World") exits properly
I’m not even sure how to go about debugging this. Any input would be helpful
I do not want to discourage you from updating ClojureDocs with useful info, but the issue you came across has been documented there for years now. See the 4th example for clojure.java.shell/sh here, which refers to the page on future for more details: https://clojuredocs.org/clojure.java.shell/sh
I suppose the full details could be copied to the 3 or 4 functions that are affected – when I added those notes it seemed best to have the full details on one of them, and cross-reference from the others.
The use of shutdown-agents isn’t a workaround, but the intended normal use. The issue here is sh doc-string not mentioning its usage of the agent pool.
Even better (and a good habit), to call System/exit in your scripts when you want to exit. It should also cause the agent pool to be shutdown immediately.
Oh yikes, I didn’t actually see your code comment @andy.fingerhut . I’ll add an extra entry in the Notes section so it’s more prominent. And yeah @didibus this to me is a problem with the doc string really. Thank for the pointer to System/exit
The only inconvenience here is that adding either System/exit or shutdown-agents makes it so I can’t run (-main) on the REPL b/c it kills the whole environment hahaha. The solution I guess is just not to run (-main)
I’ll keep (-main) as a non-REPL entry point for the application and have it as a simple launch wrapper that runs your applications and then kills the environment
Let me know if I missed anything guys. Appreciate the help :)))
While in xterm/bash I see something a little cleaner
$ time ls > /dev/null
real 0m0.004s
user 0m0.001s
sys 0m0.003s
This might be some other undocumented magic? Or am I doing something wrong?
(I’m just trying to make a simple benchmarking script that runs an app with different params :S)
That’s a great guess
But I just tried running (sh "which" "time") and I get the same /usr/bin/time as when I run in the terminal
However, I accidentally did find that I could replicate the weird time output on the terminal. I needed to modify the an environment variable to run a different program. So I was running something like
LD_LIBRARY_PATH=. time MyTestProgram
and then the output was funky just like in Clojure. So it’s a weirdness with time and not Clojure’s sh
Hey @geokon-gh, I ran into this same issue recently (scripting with clojure.java.shell too), and found another way to ensure the jvm exits promptly by using set-agent-send-executor! and set-agent-send-off-executor! to install daemon threadpool factories. It’s a little more code, but the nice thing is that I no longer have to ensure all possible exit points call (shutdown-agents). This goes near the top of a utility lib I use in my scripts:
;; Configure agent threadpools to use daemon threads so we don't have to
;; remember to call (shutdown-agents):
(import '[java.util.concurrent Executors ThreadFactory])
(defn- daemon-thread-factory [name-format]
(let [counter (atom 0)]
(reify ThreadFactory
(newThread [_ runnable]
(doto (Thread. runnable)
(.setName (format name-format (swap! counter inc)))
(.setDaemon true))))))
(shutdown-agents) ;; stop the default threadpools before installing our own
(set-agent-send-executor!
(Executors/newFixedThreadPool
(+ 2 (.. Runtime getRuntime availableProcessors))
(daemon-thread-factory "clojure-agent-send-daemon-pool-%d")))
(set-agent-send-off-executor!
(Executors/newCachedThreadPool
(daemon-thread-factory "clojure-agent-send-off-daemon-pool-%d")))