Back to the future (to get its value)

Everyone wants to exploit concurrency these days, in order to get the best out of the new multi-core processors that are just around the corner. One of the patterns for concurrency is fork-join parallelism, where a computation is launched on another thread and the main thread comes back for the value at a later point in time.

Java offers a type called a future for handling this kind of parallelism, and this type is surfaced into Clojure via the future macro. Future takes a series of forms and executes them as a future. In does this by wrapping the forms with a fn, and then submits this to the ExecutorService in the static soloExecutor field of the Agent class.
(defmacro future
  [& body] `(future-call (fn [] ~@body)))

This returns a future which is wrapped for appropriate interfaces to make it accessible using the @/deref function.
(defn future-call
  [#^Callable f]
  (let [fut (.submit clojure.lang.Agent/soloExecutor f)]
    (proxy [clojure.lang.IDeref java.util.concurrent.Future] []
      (deref [] (.get fut))
      (get ([] (.get fut))
           ([timeout unit] (.get fut timeout unit)))
      (isCancelled [] (.isCancelled fut))
      (isDone [] (.isDone fut))
      (cancel [interrupt?] (.cancel fut interrupt?)))))

This operation will block if the computation isn’t yet finished. Hence in the example below we assigned the future via a def so that the printer didn’t wait until the value was available in order to print it.

user=> (def a (future (. Thread (sleep 10000)) 20))
user=> (. a isDone)
user=> @a
user=> (. a isDone)

Futures can be cancelled
user=> (def a (future (. Thread (sleep 10000)) 20))
user=> (. a (cancel true))
user=> (. a isDone)
user=> @a
java.util.concurrent.CancellationException (NO_SOURCE_FILE:0)

Futures are used to implement some other higher order operations that allow parallel execution. pmap takes a function and some sequences and applies the function to elements of the sequences(s), generating a lazy-seq which uses to calculate the values in parallel.
user=> (pmap (fn [x y] (+ x y)) ‘(1 2) ‘(3 4))
(4 6)
user=> (pmap (fn [x y] (+ x y)) ‘(1 2 3 4 5 6) ‘(3 4 5 6 7 8))
(4 6 8 10 12 14)

By printing inside the function we can see the calculation as it proceeds – the results will now be a list of nil values as returned by println.
user=> (pmap (fn [x] (println x)) ‘(1 2 3 4 5 6 7))

nil nil 7
nil nil nil nil)

pmap is used to implement the function pcalls and the macro pvalues which call a sequence of functions and a group of values respectively.
user=> (pcalls (fn[] (println 1)) (fn[] (println 2)))

nil nil)
user=> (pvalues (print 1) (print 2))
1(2nil nil)

This entry was posted in Computers and Internet. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s