Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternate version of swap! also returning swapped out value

I talked about this a bit on IRC's #clojure channel today but would like to go more in detail here. Basically, in order to better understand atoms, swap!, deref and Clojure concurrency as a whole, I'd like to try to write a function which not only returns the value that was swapped-in using swap!, but also the value that was swapped out.

(def foo (atom 42))

.
.
.

((fn [a]
  (do
    (println "swapped out: " @a)
    (println "swapped in: "(swap! a rand-int)))) foo)

may print:

swapped out:  42
swapped in:   14

However if another thread does swap! the same atom between the @a deref and the call to swap! then I may be swapping out a value that is not 42.

How can I write a function which gives back correctly both values (the swapped out and the swapped in)?

I don't care about the various values that the atom does change to: all I want to know is what was the value swapped out.

Can this be written using code that is guaranteed not to deadlock and if so why?

like image 478
Cedric Martin Avatar asked Nov 29 '22 02:11

Cedric Martin


2 Answers

Clojure's swap! is just a spinning compare-and-set. You can define an alternate version that returns whatever you like:

(defn alternate-swap [atom f & args]
  (loop []
    (let [old @atom
          new (apply f old args)]
      (if (compare-and-set! atom old new)
        [old new]  ; return value
        (recur)))))
like image 131
Stuart Sierra Avatar answered Dec 04 '22 01:12

Stuart Sierra


Atoms are un-coordinated so it seems likely that any attempt to do this outside of the swapping function it's self will likely fail. You could write a function that you call instead of swap! which constructs a function that saves the existing value before applying the real function, and then pass this constructed function to swap!.

user> (def foo (atom  []))
#'user/foo
user> (defn save-n-swap! [a f & args] 
         (swap! a (fn [old-val] 
                    (let [new-val (apply f (cons old-val args))] 
                       (println "swapped out: " old-val "\n" "swapped in: " new-val) 
                       new-val)))) 
#'user/save-n-swap!
user> (save-n-swap! foo conj 4) 
swapped out:  [] 
 swapped in:  [4]
[4] 
user> (save-n-swap! foo conj 4) 
swapped out:  [4]
 swapped in:  [4 4]
[4 4] 

This example prints it, It would also make sense to push them to a changelog stored in another atom

like image 21
Arthur Ulfeldt Avatar answered Dec 04 '22 02:12

Arthur Ulfeldt