Right, it's "according to the spec", but it doesn't explain the behavior. as I noticed this morning http://clojure.org/refs references http://en.wikipedia.org/wiki/Multiversion_concurrency_control (the technique used to implement STM) which provides an explanation for the experienced behavior:
<quote> If a transaction (Ti) wants to write to an object, and if there is another transaction (Tk), the timestamp of Ti must precede the timestamp of Tk (i.e., TS(Ti) < TS(Tk)) for the object write operation to succeed. Which is to say a write cannot complete if there are outstanding transactions with an earlier timestamp. </quote> That's why the second transaction, which comes later in time, is the one waiting for the first transaction to complete. Dominikus On Aug 30, 9:53 am, Laurent PETIT <laurent.pe...@gmail.com> wrote: > 2011/8/30 Kevin Downey <redc...@gmail.com> > > > the two threads race to acquire the write lock and the winner runs, > > the loser retries. my guess is acquiring the write lock helps avoid > > live locks between transactions. > > Yes, and after re-readinghttp://clojure.org/refs, the current behaviour > does not seem to violate any of the 10 guaranteed listed points. > > So, while it seems unfortunate from client's code point of view, it is > correct wrt to "the current specs". > > Now I more deeply understand why one should at all price avoid long > computations from within a transaction ... > > Cheers, > > -- > Laurent > > > > > > > > > > > On Mon, Aug 29, 2011 at 10:59 PM, Laurent PETIT <laurent.pe...@gmail.com> > > wrote: > > > My tests were false. > > > Since I sent the code "at once" to the REPL, there was a race condition > > > between the start of the agent and the dosync for setting the ref's value > > to > > > 2000 ; this explains the weirdness of my test results (thank you, Mr > > Murphy, > > > for having made the tests look like beta2 behaved differently from the > > > others ...) > > > So now I see consistent behaviours across 1.2.0, beta1 and beta2. > > > But I am still perplex and confused by the behaviour ... > > > > 2011/8/30 Laurent PETIT <laurent.pe...@gmail.com> > > > >> ok so now i'm totally confused, since I cannot see why the behaviour is > > so > > >> different between beta1 and beta 2 ... > > > >> 2011/8/30 Laurent PETIT <laurent.pe...@gmail.com> > > > >>> Congratulations, you've found a bug in clojure 1.3 beta2, see: > > >>> look with my clojure 1.2.0 version : > > >>> ;; Clojure 1.2.0 > > >>> => (def thread (agent "Thread")) > > >>> (def account (ref 1000)) > > >>> (send thread > > >>> (fn [agt aref] (dosync (alter aref + 100) (Thread/sleep 8000) > > >>> agt)) > > >>> account) > > >>> (time (dosync (ref-set account 2000))) > > >>> #'user/thread > > >>> #'user/account > > >>> #<Agent@7e543cb1: "Thread"> > > >>> "Elapsed time: 0.498106 msecs" > > >>> 2000 > > >>> => ;; 10 seconds later : > > >>> => @account > > >>> 2100 > > > >>> And now with clojure 1.3 beta1 : > > >>> ;; Clojure 1.3.0-beta1 > > >>> => (def thread (agent "Thread")) > > >>> (def account (ref 1000)) > > >>> (send thread > > >>> (fn [agt aref] (dosync (alter aref + 100) (Thread/sleep 8000) > > >>> agt)) > > >>> account) > > >>> (time (dosync (ref-set account 2000))) > > >>> ;; 10 seconds later : > > >>> #'user/thread > > >>> #'user/account > > >>> #<Agent@6bf51e5c: "Thread"> > > >>> "Elapsed time: 0.270225 msecs" > > >>> 2000 > > >>> => @account > > >>> 2100 > > >>> And now with Clojure 1.3-beta2 > > >>> ;; Clojure 1.3.0-beta2 > > >>> => (def thread (agent "Thread")) > > >>> (def account (ref 1000)) > > >>> (send thread > > >>> (fn [agt aref] (dosync (alter aref + 100) (Thread/sleep 8000) > > >>> agt)) > > >>> account) > > >>> (time (dosync (ref-set account 2000))) > > >>> #'user/thread > > >>> #'user/account > > >>> #<Agent@50fba502: "Thread"> > > >>> "Elapsed time: 7957.328798 msecs" > > >>> 2000 > > >>> ;; 10 seconds later : > > >>> => @account > > >>> 2000 > > >>> ;; 10 more seconds later : > > >>> => @account > > >>> 2000 > > >>> 2011/8/30 Laurent PETIT <laurent.pe...@gmail.com> > > > >>>> 2011/8/29 Dominikus <dominikus.herzb...@gmail.com> > > > >>>>> Thanks a lot for this detailed analysis and the pointers to the Java > > >>>>> implementation, Stefan! That's excellent and very helpful. > > > >>>>> I still wonder, why the first transaction blocks write access. My > > >>>>> overall understanding of the transaction system is that changes to > > >>>>> referenced values remain in-transaction before committing them, a > > >>>>> write-lock shouldn't be needed. I'm surprised, that the second > > >>>>> transaction is the one who has to retry 71 times even though the > > first > > >>>>> transaction hasn't committed anything yet. > > > >>>> I must confess this behaviour also challenges my assumptions. > > >>>> Does this work the same whatever the clojure version used ? > > > >>>>> Cheers, > > > >>>>> Dominikus > > > >>>>> P.S.: Thanks for the idea to use 'future' to spawn a thread! > > > >>>>> On Aug 29, 5:42 pm, Stefan Kamphausen <ska2...@googlemail.com> > > wrote: > > >>>>> > The call to alter already wrote to the Ref, this requires a > > >>>>> > write-lock on > > >>>>> > the ref (see LockingTransaction.java/doSet). After that it sleeps > > a > > >>>>> > while > > >>>>> > and gets to its commit phase. The other transactions retries in > > the > > >>>>> > meantime. Consider the following code, which introduces some atoms > > >>>>> > for > > >>>>> > counting the retries > > > >>>>> > (defn tz [] > > >>>>> > (let [rr (ref 10) > > >>>>> > a1 (atom 0) > > >>>>> > a2 (atom 0)] > > >>>>> > (println "Starting future") > > >>>>> > (future > > >>>>> > (dosync > > >>>>> > (swap! a1 inc) > > >>>>> > (alter rr + 100) > > >>>>> > (Thread/sleep 8000))) > > >>>>> > (println "Sleeping a bit") > > >>>>> > (Thread/sleep 1000) > > >>>>> > (println "Another dosync") > > >>>>> > (time > > >>>>> > (dosync > > >>>>> > (swap! a2 inc) > > >>>>> > (ref-set rr 1))) > > >>>>> > [@rr @a1 @a2 (.getHistoryCount rr)])) > > > >>>>> > user> (tz) > > >>>>> > Starting future > > >>>>> > Sleeping a bit > > >>>>> > Another dosync > > >>>>> > "Elapsed time: 7001.554 msecs" > > >>>>> > [1 1 71 0] > > > >>>>> > Note, how the 71 retries nice fit the approximately 7 seconds left > > >>>>> > and the > > >>>>> > 100 miliseconds time-out (LOCK_WAIT_MSECS) for the lock used in > > >>>>> > LockingTransaction.java/tryWriteLock. > > > >>>>> > Transactions with significantly different run-times should be > > >>>>> > avoided, > > >>>>> > although there is some point at which older transactions will get > > >>>>> > their > > >>>>> > turn. This could be another explanation of the effect. Take a > > look > > >>>>> > at the > > >>>>> > barge-function in LockingTransaction.java > > > >>>>> > Hope this helps, > > >>>>> > Stefan > > > >>>>> -- > > >>>>> 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 > > > > -- > > > 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 > > > -- > > And what is good, Phaedrus, > > And what is not good— > > Need we ask anyone to tell us these things? > > > -- > > 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 -- 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