weird, it actually ping-pongs, every other run of the (time …) the transaction in the future locks the ref writing so you get
"Elapsed time: 10006.505 msecs" "Elapsed time: 0.358 msecs" "Elapsed time: 9038.18 msecs" "Elapsed time: 0.348 msecs" "Elapsed time: 26250.854 msecs" On Mon, Aug 29, 2011 at 6:13 PM, Kevin Downey <redc...@gmail.com> wrote: > https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LockingTransaction.java#L424 > is definitely the line that causes the write lock to be acquired. > > I get the same behavior on master(1.3) and 1.2.1: > > the first time I run the code on a particular ref it locks for writing > while the first transaction is running, but subsequent runs do not. > > 1.3 > > user=> (def x (ref 0)) > #'user/x > user=> (time (do (future (dosync (alter x inc) (Thread/sleep 10000))) > (dosync (alter x dec)))) > "Elapsed time: 10007.789 msecs" > 0 > user=> (time (do (future (dosync (alter x inc) (Thread/sleep 10000))) > (dosync (alter x dec)))) > "Elapsed time: 0.409 msecs" > -1 > user=> > > 1.2 > > Clojure 1.2.1 > user=> (def x (ref 0)) > #'user/x > user=> (time (do (future (dosync (alter x inc) (Thread/sleep 10000))) > (dosync (alter x dec)))) > "Elapsed time: 10007.026 msecs" > 0 > user=> (time (do (future (dosync (alter x inc) (Thread/sleep 10000))) > (dosync (alter x dec)))) > "Elapsed time: 0.391 msecs" > -1 > user=> > > > if I had to guess, I'd say that until a ref has some initial number of > history entries (1?) to satisfy readers it falls back to locking, but > I really have no clue > > On Mon, Aug 29, 2011 at 4:03 PM, Laurent PETIT <laurent.pe...@gmail.com> > wrote: >> 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? > -- 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