clojurefuture

Using claypoole thread pools without pre-evaluating everything


I want to pass a form to be evaluated to a target thread pool as a future via the claypoole library in clojure.

I tried to pass off the body of a function to a thread pool, and I was expecting that body to be evaluated on a thread rather than inline.

The code I have looks like this:

(defn in-thread [thread & body] (claypoole/future thread body))

Which gets called like:

(defonce pool (claypoole/threadpool 20))

(defn do-something [pool]
  (in-thread pool
    (Thread/sleep 5000)
    (println "foo")))

After some testing, I've come to the conclusion that that body is being evaluated when I call in-thread, making my whole solution not actually do anything meaningful. I know there's some sort of basic concept I'm missing, but every time I try to look into macros my head starts to spin.

How would I make the in-thread function into a macro that passes off the body into the thread pool to execute, rather than the result?


Solution

  • When you use a function - that is, a construct created with defn/fn - all its arguments will be evaluated before the function itself is called. So in your case, the in-thread function gets called with the results of (Thread/sleep ...) and (println ...), and both are nil.

    In order to pass forms around without actually evaluating them, you either have to quote those forms and use eval later or use a macro, which is a much more common approach.

    In your case, it would be enough to replace the definition of in-thread with:

    (defmacro in-thread [thread & body]
      `(claypoole/future ~thread ~@body))
    

    However, notice that it becomes just a wrapper around claypoole/future that doesn't do anything by itself. And claypoole/future itself is already a macro - you don't even need in-thread! All you need is already implemented by claypoole/future, so you can just use this:

    (defonce pool (claypoole/threadpool 20))
    
    (defn do-something [pool]
      (claypoole/future pool
        (Thread/sleep 5000)
        (println "foo")))