Sam Estep
Sam Estep

Reputation: 13294

Why does lein run hang?

I have created a Leiningen project for Exercise 2 from here. My code looks like this:

(ns random-quotes.core
  (:require [clojure.string :as str])
  (:gen-class))

(defn word-count [s]
  (frequencies (str/split (first (str/split s #"\n")) #"\s")))

(def quote-url "http://www.braveclojure.com/random-quote")

(def total-word-count (atom {}))

(defn update-word-count []
  (future
    (swap! total-word-count
           (partial merge-with +)
           (word-count (slurp quote-url)))))

(defn quote-word-count [n]
  (doseq [quote-future (doall (repeatedly n update-word-count))]
    @quote-future)
  @total-word-count)

(defn -main [n]
  (doseq [entry (sort-by val (quote-word-count (bigdec n)))]
    (println entry)))

All pretty straightforward. When I run, e.g., (-main 5) in lein repl, it runs, prints, and returns as expected. However, when I try lein run 5 instead, it runs and prints but never exits, so I am forced to use Ctrl+C to get my terminal back.

Any idea why this happens?

Upvotes: 2

Views: 909

Answers (1)

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91534

Clojure has a thread pool that it keeps running for use by the agents. Because those threads are still alive, the JVM can't tell that you're program is done. It's just sitting there waiting for the agents to exit. You can make them finish by calling (shutdown-agents) at the end of your program as described here. Futures use agents.

clojure.core/future-call calls an agent like this:

(let [f (binding-conveyor-fn f)
      fut (.submit clojure.lang.Agent/soloExecutor ^Callable f)]

which actually starts your code running. You would not be the only one to voice some criticism of this, and we all hope a more elegant solution is found.

Upvotes: 3

Related Questions