On Feb 1, 2009, at 6:47 AM, Paul Barry wrote:
> My understanding of commute is that it would not restart the > transaction, it would just apply the function. So I wrote this > little test program: > > (defmacro in-thread [& body] > `(.start > (Thread. > (fn [] > (println "Thread" (.getId (Thread/currentThread)) " started") > ~...@body > (println "Thread" (.getId (Thread/currentThread)) " > finished"))))) > > (def numbers (ref [])) > > (defn conj-count [] > (dosync (commute numbers conj (inc (count @numbers))))) > > (defn conj-count-later [] > (dosync (println "Starting Transaction...") > (Thread/sleep 10000) > (conj-count))) > > The in-thread macro just runs what you give it in a thread, printing > at the beginning and the end so you know when the thread finishes. > The conj-count function just conjoins a number onto the numbers > vector, the number being one more than the current size of the > vector. Here's what happens when I run it at the repl: > > user=> #'user/numbers > user=> #'user/conj-count > user=> #'user/conj-count-later > user=> (in-thread (conj-count-later)) > nil > user=> Thread 21 started > Starting Transaction... > > user=> (conj-count) > [1] > user=> Starting Transaction... > Thread 21 finished > > user=> @numbers > [1 2] > > Once I call conj-count-later in in-thread, that starts a transaction > in a thread what will wait 10 seconds and call conj-count. While > that thread is running, in the meantime I call conj-count directly, > so the ref is modified. > > What happens next is that you can see "Starting Transaction..." > print again for the second time, which means the transaction is > executing for a second time. Next I don't call conj-count again, I > just wait for the thread to finish, which it does without restarting. > > But since all conj-count-later does is call commute (by calling conj- > count), why does the transaction restart? If conj-count called > alter instead of commute, that would be the behavior I would expect, > but I guess I don't quite understand how dosync and/or commute works. > You are not just calling commute, you are also dereferencing (@) in the same transaction. Reads may cause transaction restarts when there is not enough history to support the snapshot, as is the case here on the first call (by the time you first look at numbers with @, its value has been changed by another transaction, and initially there is no history). Per-ref history grows dynamically with need, so over time reads will be satisfied by history and not cause restarts. More fundamental to your problem is that you are not fully leveraging the fact that alter/commute pass the value of the ref to the supplied function, so you should use that rather than deref again, i.e. don't do this: (dosync (commute numbers conj (inc (count @numbers)))) do this instead: (dosync (commute numbers #(conj % (inc (count %))))) Basically any time you are saying (alter/commute x f ... @x ...) you are needlessly derefing. Let alter/commute do the deref and use the value they pass you. Rich --~--~---------~--~----~------------~-------~--~----~ 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 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 -~----------~----~----~----~------~----~------~--~---