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

Reply via email to