Reputation: 5843
I've read this SO question and http://clojure.org/refs, but I am still confused about how exactly ref-set works. (To some extent the two documents kind of lead me to believe two different things...)
Suppose that I have a transaction in Clojure that looks like this:
(def flag (ref false))
(dosync
(long-computation-that-does-not-read-or-write-flag)
(ref-set flag true))
Suppose that in the middle of the long computation, somebody else modifies flag. Will that cause my transaction to retry when I try to ref-set flag?
I could imagine the answer might be yes, since clojure.org says transactions guarantee that "No changes will have been made by any other transactions to any Refs that have been ref-set/altered/ensured by this transaction"
.
But I could also imagine the answer to be no, since I never read flag, and the clojure.org page suggests that "All *reads* of Refs will see a consistent snapshot of the 'Ref world' as of the starting point of the transaction"
. This is also what the linked SO answer would lead me to believe.
And a followup: supposing that instead of (ref-set flag true)
, I had done one of these:
(alter flag (fn [_] true))
(let [ignored @flag] (ref-set flag true))
I assume that both of those would constitute a read of flag, and so the transaction would have to retry?
Upvotes: 3
Views: 190
Reputation: 70211
Calling ref-set
means that you have included flag
in the tracked references for this transaction. Thus, a concurrent write to flag
in some other transaction will cause a conflict and a retry.
Both of the followups modify flag (via alter
and ref-set
) and thus have the same result. The important thing here is not the read of flag, it's the write. If a transaction contains a read of a ref without a write, the transaction can succeed even if the read ref changes in a concurrent transaction. However, ensure can be used to include a read in the tracked references for a transaction (thus causing concurrent changes to fail).
Upvotes: 3