Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the state of an atom, and reset it atomically

Tags:

atomic

clojure

I'm writing a Mandelbrot Set implementation, and to speed up finding out which points go off into infinity, I decided to try using an ExecutorService to check the points in parallel.

Basically the plan is:

  • Calculate all the points I need to find
  • Give each point to the service
  • Have the service dump its results into an Atom wrapping a vector as they are produced
  • Have the drawing code periodically grab the produced results, and clear the queue

My problem is with the last point. How can I safely grab the previous results from an atom, and reset it?

I thought about the simple way of just:

(def draw-queue-A (atom []))

(defn grab-and-clear-queue []
  (let [results @draw-queue-A]
    (reset! draw-queue-A [])
    results)) 

But this looks unsafe. If something is added between the dereference and the reset!, it will be lost.

The atrocious abomination I've settled on at the moment is:

(defn grab-and-clear-queue []
  (let [results (atom [])]
    (swap! draw-queue-A
           (fn [res] (reset! results res)
                     []))
    results))

But using an atom just to retrieve the results seems ridiculous.

How can I sanely retrieve the contents of an atom, and reset it without potentially losing any results?

like image 855
Carcigenicate Avatar asked Apr 25 '17 00:04

Carcigenicate


1 Answers

There is currently a JIRA ticket dealing with this very request. In the meantime, this does what you want, and is similar to what's in the patch, though I only browsed the code:

(defn reset-return-old!
  [atm new-value]
  (let [old-value @atm]
    (if (compare-and-set! atm old-value new-value)
      (do
        (.notifyWatches atm old-value new-value)
        old-value)
      (recur atm new-value))))

We rely on the CAS semantics, just as swap! does. We effectively spin to guarantee that we were not interrupted between the read and the CAS (although I suppose this still falls prey to the ABA problem, but I think in this context that's not important).

I'm notifying watches above -- if you have none, for your purposes, you can eliminate it and the do block to simplify further.

like image 176
Josh Avatar answered Nov 03 '22 01:11

Josh