commute documentation detail

6 views
Skip to first unread message

Mark Volkmann

unread,
Aug 2, 2009, 3:20:54 PM8/2/09
to clo...@googlegroups.com
The doc for commute says "At the commit point of the transaction, sets
the value of ref to be:
(apply fun most-recently-committed-value-of-ref args)".

Looking at the source code in LockingTransaction.java, that doesn't
seem to always be the case. I see this:
f.fn.applyTo(RT.cons(vals.get(ref), f.args))

vals is a HashMap of in-transaction values, not most recently committed values.

Inside a transaction, Refs get in-transaction values if you do a
ref-set or alter on them.
Within a transaction you can ref-set or alter a Ref and then do a
commute on the same Ref.
You can't reverse the order. Once you do a commute on a Ref, you can't
do a ref-set or alter on it in the same transaction.

During transaction commit and immediately before the commute functions
for the Ref are called, it determines whether the Ref has an
in-transaction value, meaning that ref-set or alter was called on it.
If it does not then the most recently committed value for the Ref is
used for the in-transaction value.

So I think it is only partially true to say that when commute
functions are rerun during a commit, they use the most recently
committed values of Refs. That only happens if ref-set and alter were
not used on the Ref before the call to commute.

Am I understanding this correctly?

--
R. Mark Volkmann
Object Computing, Inc.

Mark Volkmann

unread,
Aug 2, 2009, 6:21:56 PM8/2/09
to Clojure
I've been thinking about this more. Suppose I do something like this.

(def my-ref (ref 1))

(defn f1 []
(dosync
(ref-set my-ref 10)))

(defn f2 []
(dosync
(ref-set my-ref 5)
(commute my-ref #(inc %))))

(let [t1 (Thread. f1)
t2 (Thread. f2)]
(.start t1)
(.start t2)
(.join t1)
(.join t2))

(println "my-ref =" @my-ref)

Is there any way that my-ref will have a value of 11 at the end?
That's what would happen if the transaction in f1 committed while the
transaction in f2 was running because according to the commute doc
string, it would use the latest committed value. What I see in
LockingTransaction.java though tells me that because the transaction
in f2 does a ref-set before calling commute, when the commute function
runs a second time during the commit, it will use the in-transaction
value of 5 instead and result in a value of 6 for my-ref instead of
11.

Rich Hickey

unread,
Aug 2, 2009, 6:45:45 PM8/2/09
to clo...@googlegroups.com

Not really. If this transaction is succeeding, and it has written to a
ref prior to its commute, then those writes *are* the most recently
committed value, committed now as a prior part of the same
transaction.

Rich

Mark Volkmann

unread,
Aug 2, 2009, 8:54:36 PM8/2/09
to clo...@googlegroups.com

Thanks for explaining that! I see what you're saying, but I also see
how someone, like me ;-), could be confused by the doc string.

When the code in question is running, the commit hasn't completed yet.
It doesn't seem correct to say "those writes *are* the most recently
committed value", but rather "those writes *will become* the most
recently committed value. I hope I'm not just splitting hairs, but it
seems that "most-recently-committed-value-of-ref" implies that its a
value that can be seen now by all threads.

Maybe this is splitting hairs too, but if the code in a commute
function throws an exception when it is called during the commit then
values set before the commutes in the transaction body really will not
become the most recently committed value.

That said, I can't think of a concise way to explain this in the doc string.

Rich Hickey

unread,
Aug 2, 2009, 9:34:58 PM8/2/09
to clo...@googlegroups.com

Nor will the commute happen.

Rich

Christophe Grand

unread,
Aug 3, 2009, 5:44:41 AM8/3/09
to clo...@googlegroups.com
I think Mark's code exhibits a bug:

(def my-ref (ref 1))

(dosync
  (ref-set my-ref 5)
  (commute my-ref inc)) ; returns 6

(println "my-ref =" @my-ref) ; prints 7

since my-ref is ref-set, my-ref is in LockingTransaction/sets but its current value in LockingTransaction/vals is set by commute to 6.
At the commit point, the commute function is rerun with the latest in-transaction value (6) and then 7 is committed...

See the attached patch for a potential fix.

Christophe
--
Professional: http://cgrand.net/ (fr)
On Clojure: http://clj-me.blogspot.com/ (en)
0001-fixes-commute-after-set.patch

Rich Hickey

unread,
Aug 3, 2009, 9:25:12 AM8/3/09
to clo...@googlegroups.com
On Mon, Aug 3, 2009 at 5:44 AM, Christophe Grand<chris...@cgrand.net> wrote:
> I think Mark's code exhibits a bug:
>
> (def my-ref (ref 1))
>
> (dosync
>   (ref-set my-ref 5)
>   (commute my-ref inc)) ; returns 6
>
> (println "my-ref =" @my-ref) ; prints 7
>
> since my-ref is ref-set, my-ref is in LockingTransaction/sets but its
> current value in LockingTransaction/vals is set by commute to 6.
> At the commit point, the commute function is rerun with the latest
> in-transaction value (6) and then 7 is committed...
>
> See the attached patch for a potential fix.
>

Patch applied to master and 1.0.x - thanks!

Rich

Reply all
Reply to author
Forward
0 new messages