10

I have a top-level core.async go loop. I want it to run indefinitely, at least until I signal it to stop with CTRL-C or kill or similar. I'm currently using java.lang.Runtime/addShutdownHook like this:

(ns async-demo.core
  (:require [clojure.core.async :as async
             :refer [<! >! <!! timeout chan alt! go]]))
(defn run [] (go (loop [] (recur))))
(.addShutdownHook (Runtime/getRuntime) (Thread. #(println "SHUTDOWN")))

Here are my problems:

  1. If I start the REPL and (run) then it starts and runs in a background thread. When I exit the REPL, I don't see the desired shutdown message.

  2. However, when I run from lein run, the go loop exits immediately and displays "SHUTDOWN".

Neither is what I want.

I don't necessarily expect to find a solution that works for all JVM's. I develop on a Mac and deploy to Ubuntu, so I'd like to find a solution that works for both:

  • Mac JVM: java version "1.7.0_45" Java(TM) SE Runtime Environment (build 1.7.0_45-b18) Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

  • Ubuntu JVM: java version "1.7.0_25" OpenJDK Runtime Environment (IcedTea 2.3.10) (7u25-2.3.10-1ubuntu0.12.04.2) OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)

2

3 Answers 3

13

You can use a signalling exit-ch and do a conditional to see if you should exit the loop. The channel + conditional is better handled in clojure by alt!:

(ns async-demo.core
  (:require [clojure.core.async :refer [chan go-loop alt! timeout put!]]))

(defn run
  []
  (let [exit-ch (chan)]
    (go-loop []
      (alt!
         (timeout 10) (recur)
         exit-ch nil
    exit-ch

(let [exit-ch (run)]
  (.addShutdownHook (Runtime/getRuntime) (put! exit-ch true)))

If you just close! the channel, it won't stop the loop from running. Also, don't need to start a go block just to send a value to a channel.

Sign up to request clarification or add additional context in comments.

Comments

-1

Regarding part 1: "When I exit the REPL, I don't see the desired shutdown message." I think the shutdown thread isn't connected to lein repl's console.

Regarding part 2: After the go loop is started, it runs in background threads. Since the main thread exits after creating the go block, the program shuts down. To make the loop long-lived it needs to be put in a normal loop. (It is also way nicer to put a Thread/sleep inside!)

Comments

-2

go function returns a channel. You may want to (close! chan) in shutdown hook.

If you run lein run you need a main function that will call (run) to start go thread.

(ns async-demo.core
  (:require [clojure.core.async :as async
             :refer [<! >! <!! timeout chan alt! go close!]]))

(def ch (atom nil))

(defn run []
  (go (while true
        (<! (timeout 500))
        (prn "inside go"))))

(defn -main [& args]
  (println "Starting")
  (reset! ch (run))
  (.addShutdownHook (Runtime/getRuntime)
                    (Thread. #(do
                                (println "SHUTDOWN")
                                (close! @ch))))
  (while true
    (<!! @ch)))

4 Comments

Are you seeing the shutdown message when you (a) run from the REPL; (b) run via lein run?
There is no shutdown message in case (a) lein repl, case (b) lein run does display shutdown
If I run lein trampoline repl shutdown display when I exit with Ctrl-D or using (exit) function
I don't understand why it's an accepted answer. It's wrong because closing a channel returned by go from outside doesn't stop while-loop

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.