Object Commando languages, development and design

10Jun/103

Clojure Futures

Futures in Clojure are basically a way that you can execute some bit of code on a background thread. I was using it as a way to allow timeouts for long running code. In this entry I'll give a run down on how to use futures.

Future Basics

It is pretty easy to start using futures in Clojure. Most of the function calls start with (future...). First I'll start by creating a future that calls a long running sleep function:

(def f
  (future
    (println "Starting to sleep...")
    (Thread/sleep 600000)
    (println "Done sleeping.")))

There are two ways via Clojure to create a future, the first is with the macro future (used above), the other is the future-call function. The future macro is just syntax sugar around future-call. Whatever you pass in to future with be wrapped in a no-arg function and passed to future-call. The future-call function just invokes the passed in function on background type of thread. The above code, even though it sleeps for an hour will return immediately. The object returned is a future (intentionally not getting into details about what is returned right now). This returned future allows you to peek inside the execution of the code passed in and get information like is it done, or has it been canceled:

(future-done? f)
=> false

(future-cancelled? f)
=> false

You can then decide to cancel the future:

(future-cancel f)
=>true

(future-cancel f)
=>false

The above will return false when it was not able to cancel the future. As an example, if we try to cancel the future again, it will return false, because it has already been canceled. Another set of useful functions is getting the value returned by the executing future. Let's say we're still computing Fibonacci numbers in the slow, recursive way:

(defn fib [x]
  (cond (zero? x) 0
            (= 1 x) 1
            :else
	    (+ (fib (- x 1)) (fib (- x 2)))))

Computing (fib 40) on my laptop takes about 10 seconds. Below is code to execute the code in a future and then use deref to pull the value out:

(def f (future-call #(fib 40)))
=> #'user/f

(time @f)
=> "Elapsed time: 8075.452326 msecs"
102334155

(time @f)
=> "Elapsed time: 0.082342 msecs"
102334155

The first deref (@) call will block until the future has completed running and then return the value. So timing the deref, it took about 8 seconds until returning the value. But if it has already computed the value, it will just return that computed value. So the second deref returns very quickly.

Timeouts

One thing that is hidden from users of futures in Clojure is what is actually returned when calling (future...). I say that it's hidden because when you use future-cancel, future-done? etc, it doesn't matter what kind of object is returned from the future function call. It's some object that you are able to pass to these other functions and it just works. Where you actually do need to know more about the implementation of this is when you want to have your future timeout. The object returned by future-call is an implementation of java.util.concurrent.Future. With this information, you can use the Java APIs for the Future. Below is some code that creates a future that will timeout before it finishes:

(def f (future-call #(fib 50)))
(.get f 10 (java.util.concurrent.TimeUnit/SECONDS))

The last line does the same thing that we did before when we deref'd the future, but this time there is a timeout on how long we will wait for that deref to happen. If the timeout is reached (in this case 10 seconds), it will throw a java.util.concurrent.TimeoutException. Armed with the knowledge that the future is actually a java.util.concurrent.Future, the deref is just a more Clojure way of calling the get method on the future:

(def f (future-call #(fib 40)))
=> #'user/f

(time (.get f))
=> "Elapsed time: 10713.176672 msecs"
102334155

(time (.get f))
=> "Elapsed time: 0.075569 msecs"
102334155

Hey, where did my console output go?

I use Emacs and Slime for my development environment. One thing that I noticed was that code running in a future does not write to the console like code running from the Slime REPL. This is because code running in the Slime REPL gets the bound variables from the REPL thread, whereas the future code runs in a different thread that does not have those variables bound. bound-fn makes it easy to fix this problem:

(def f (future-call #(println "Hello World")))
=>#'user/f

(def f (future-call (bound-fn [] (println "Hello World"))))
=>Hello World
#'user/f
Filed under: Clojure Leave a comment
Comments (3) Trackbacks (0)
  1. In (.get f 10 (java.util.concurrent.TimeUnit/SECONDS))
    should that be a .set instead of a .get?

  2. No, what (.get f 10 (java.util.concurrent.TimeUnit/SECONDS)) is saying is give me the value returned by the future f, but I’m only willing to wait 10 seconds for it. It will not actually modify the future, it just changes how long you should block waiting for an answer. You can actually execute the above many times (if the future is long running enough).

  3. In case anyone is reading this and scratching their head as I was – in Clojure 1.3 the exception thrown is no longer a TimeoutException but a RuntimeException wrapping a TimeoutException.


Leave a comment

(required)

No trackbacks yet.