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:
Atom
wrapping a vector
as they are producedMy 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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With