I'm learning about concurrency in Clojure.
I ran into a claim (by Stuart Serra?) at http://dev.clojure.org/display/design/Scheduled+Events, stating:
- Clojure functions cannot use time for control flow without blocking or Java interop
- Java interop (ScheduledThreadPoolExecutor) is not aware of thread-local bindings
I don't understand these claims and kindly ask for clarification, perhaps an example. Specifically:
Many thanks!
Ok, I think I got it.
Suppose you try this:
(def pool (atom nil))
(defn- thread-pool []
(or @pool
(reset! pool (ScheduledThreadPoolExecutor. 1))))
(def ^:dynamic *t* 0)
(binding [*t* 1]
(future (println "first example:" *t*)))
(binding [*t* 1]
(.schedule (thread-pool) (fn [] (println "second example:" *t*)) 0
TimeUnit/SECONDS))
(binding [*t* 1]
(.schedule (thread-pool) (bound-fn [] (println "third example:" *t*)) 0
TimeUnit/SECONDS))
The output will be:
first example: 1
second example: 0
third example: 1
In the first case, the future macro wraps the body with the private function binding-conveyor-fn, which preserves the calling thread's bindings frame in the lexical scope, and restores it before calling the wrapped function.
In the third case, bound-fn pushes the calling thread's bindings onto the frame, executes the function body, and pops the bindings.
In the second case, no one saves per-thread bindings - a Java class certainly doesn't know about them, and so we drop to the root value of the t Var.
I hope someone out there finds this interesting.