On Wed, Dec 1, 2010 at 12:55 AM, Alex Osborne <a...@meshy.org> wrote: > Ken Wesson <kwess...@gmail.com> writes: > >> I put a println in a swap! function once and the world didn't end. > > For a println retries are usually harmless
Retries? We were discussing an atom here, not a ref. A dosync transaction may be retried. The obvious implementation for swap! would be (locking inner_value (set! inner_value.contents (apply fun inner_value.contents))) (warning: pseudocode, I assume this stuff would actually be implemented in Atom.java or someplace like that in Java rather than in Clojure). > it just means you'll > occasionally get doubled output. But it's not a good thing for a > general-purpose runonce fn whose entire reason for existence is to > ensure something is called only once. ;-) If swap! does sometimes run its argument twice then you'd need something slightly more sophisticated, like: (let [owned (Object.)] (swap! the-atom #(if (= % sentinel) owned %)) (when (= @the-atom owned) (do-once-fn) (reset! the-atom nil))) Here, the swap changes the atom to the unique object "owned" if and only if it's still holding the sentinel value. If afterward the atom's value is owned, we've claimed ownership of the atom and we call do-once-fn. If anyone else beat us to the punch the atom will hold either a different "owned" object or else nil. This works if we're not actually interested in the return value of the function, just in making it execute exactly once. If we need the result: (let [owned (Object.)] (swap! the-atom #(if (= % sentinel) owned %)) (if (= @the-atom owned) (reset! the-atom (do-once-fn)) (loop [] (if (= (.getClass @the-atom) Object) (do (Thread/sleep 10) (recur)) (@the-atom))))) The interesting bit here deals with the case that the atom holds someone else's "owned" object instead of an actual result. The loop sleeps 10ms and checks repeatedly until the do-once-fn (running in some other thread) has completed and the atom's final value been assigned. It does assume that the result of do-once-fn will not be an unspecialized java.lang.Object. The locking version is probably cleaner than the above, but when no return value is needed, the code further above is short and especially concise; both are lock-free (given that swap! and reset! are lock-free, which, if they perform retries, is presumably the case). > The guarantee of atomicity is *dependent* on the function not having > side-effects. How so? (Other than that it would probably be bad to reference the atom explicitly inside of the swap! function.) > Atoms do not lock. If multiple threads enter swap! simultaneously, they > *will* each execute f. One will win and the others will retry. Eh. Why this implementation? To optimize for the common case that there isn't any contention? -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en