Reputation: 4513
Let's say I have an atom:
(def my-atom (atom nil))
Then I initialize it as follows:
(defn init-atom [init-value]
(when (nil? @my-atom)
(reset! my-atom init-value)))
If init-atom
is called concurrently from different threads, I race condition may occur. I'm looking for a way to safely and correctly initialize an atom. Anything there?
UPD:
Actually I'm initializing it as follows:
(defn init-atom [produce-init-fn]
(when (nil? @my-atom)
(reset! my-atom (produce-init-fn)])))
produce-init-fn
may contain side effects.
Upvotes: 2
Views: 262
Reputation: 13185
The following will make sure that the atom is initialized only once:
(defn init-atom [init-value]
(swap! my-atom #(when (nil? %) init-value)))
Atom and swap!
semantics guarantee that the function passed to swap!
will be executed atomically.
If you pass a function producing the init value then it won't work as swap! might invoke the function multiple times in case of conflicting transactions. Then you need to use some kind of locking like in the other answer:
(let [o (Object.)]
(defn init-atom [init-value-fn]
(locking o
(swap! my-atom #(when (nil? %) (init-value-fn))))))
init-value-fn
still might be called more than once if there are other concurrent transactions with my-atom
.
If you need to support lazy initialization and the init-value-fn
is known upfront and the same for all the threads you can just wrap it into delay
and then it will be called only once and its result will be cached and reused:
(def my-init-value (delay init-value-fn))
(defn init-atom []
(swap! my-atom #(when (nil? %) @my-init-value)))
Upvotes: 4
Reputation: 10682
This should do the trick:
(let [o (Object.)]
(defn init-atom [init-value]
(locking o
(when (nil? @my-atom)
(reset! my-atom init-value)))))
Upvotes: 1