On Wed, Aug 19, 2009 at 12:18 PM, Rich Hickey<richhic...@gmail.com> wrote: > > On Mon, Aug 17, 2009 at 4:16 PM, Mark Volkmann<r.mark.volkm...@gmail.com> > wrote: >> >> On Mon, Aug 17, 2009 at 2:47 PM, Michael Reid<kid.me...@gmail.com> wrote: >>> >>> Hi, >>> >>> Ditto on the ordering example. Clojure can't infer which order your >>> code needs to run any more than it can figure out what your code is >>> supposed to do. >> >> Okay. I was pretty sure that was the case, but just wanted to verify >> that there wasn't something special going on that I was missing. >> >>> On the deadlock question, it is my understanding from a prior post by >>> Rich that Clojure's STM acquires locks in such a way that deadlocks >>> cannot occur (as I recall, the locks are sorted by numeric ID an >>> acquired/released in that order). If a deadlock occurs in the STM, my >>> understanding is that would be a bug. >>> >>> So yes, the Clojure STM should guarantee that a deadlock situation >>> does not occur. >> >> I think it's a common misconception that STM in general just manages >> locks for you and basically uses them in the same way you would if you >> were hand-coding lock-based concurrency. > > How could that possibly be the case? Lock strategies are open and > arbitrary. Also, some STMs don't use locks at all. What an STM that > uses locks should do is use them correctly. Every time you hand-code > lock-based concurrency you risk getting it wrong. If an STM > implementation gets it wrong, it gets fixed and everyone benefits from > the fix.
I agree with everything you said above. I'm explaining what I have witnessed as a misconception. For example, a C++ guy I know said that STM implementation "use locks like crazy". I'm trying to explain that that isn't the case in the Clojure STM implementation. >> I can say with certainty >> after studying the Clojure STM implementation for over a month that >> that isn't the case. > > What exactly are you saying? I'm saying that the Clojure STM implemenation doesn't "use locks like crazy". For example, a lock is not held for every Ref that is modified in a transaction. Instead, the Ref gets its tinfo field set to indicate that a transaction has modified it, but hasn't yet committed the change. >> Refs in Clojure STM each have a ReentrantReadWriteLock object. Any >> number of concurrent transactions can hold the read lock for a Ref OR >> a single transaction can hold the write lock for a Ref. The only time >> one of these locks is held for the duration of a transaction is when >> you call ensure on a Ref. In that case it will hold a read lock until >> either the Ref is modified in the transaction or until the transaction >> commits. There is no circumstance under which a write lock will be >> held for the duration of a transaction. Write locks are obtained only >> briefly when certain things occur during a transaction and then >> quickly released. They are acquired again when the transaction commits >> and then released when the commit completes. The way that one >> transaction knows that another one has modified a Ref and hasn't yet >> committed is that the tinfo field of the Ref is set to refer to an >> object that describes the transaction that modified the Ref (including >> the order in which it started relative to others and its current >> status). >> >> So locks are not sorted by a numeric ID. > > This is an oversimplification and simply wrong. You'll need to spend > more time studying the implementation. In particular, commuting uses > an ordered lock strategy. Is there something specific that I said above that is wrong, because based on my reading of the code I think it is all correct. I just discovered what you are talking about with ordered locking for commutes! See my next comment below. >> It is transaction starts, >> tries and commits that are numbered that way. It's a beautiful and >> complicated strategy ... but it leaves me wondering whether there is >> any possibility of deadlock. I don't see how it could happen. I'm just >> asking in case I overlooked something. > > Deadlock can be precluded by avoiding mutually incompatible multi-lock > acquisitions, and/or utilizing non-forever-blocking (timeout-based) > lock acquisition. In Clojure's STM several strategies are combined. > ensures use mutually compatible readlocks, and can thus hold them over > time. Writes use write locks, but - a) try to obtain them only with a > timeout, and b) don't hold more than one at a time other than during a > commit, c) use a tagging system during the body of the transaction. > So, they obtain a write lock temporarily, then tag as owned by this > transaction, then release. On commit, everything written to will have > been successfully tagged, and those locks can then be obtained in any > order, since any other writing transaction vying for the same refs > will have failed. Thus there can be no lock A wait for B/lock B wait > for A deadlocks. Commutes always proceed (without tagging), but need > to coordinate with other writes and commutes on commit. Thus commute > locks are obtained in order Ah ... I just saw a detail I had overlooked. The commutes are stored in a TreeMap, not a HashMap. So that's where the order comes from. They are sorted on the Ref ids which are assigned sequentially as the Ref objects are created. > (with timeouts to avoid clashes with > writes), contrary to what you've said, based upon comparability of > refs utilizing numeric IDs. This ensures that multiple commutes > involving overlapping sets of refs use compatible lock orders. Cool! I get that now. > These are well known techniques for safe manual lock based > concurrency, simply automated by Clojure's STM, so I don't know what > you are talking about above. I've done a ton of manual concurrency and > have used all of these techniques. That said, there are many ways to > combine these and others and the implementation is subject to change. > Marrying your understanding of STM to understanding this particular > implementation is tenuous. Not that there's anything wrong with trying > to understand it, but going from the specific to the general is > dangerous, and many details *are likely to change* as it gets refined. > > If a transaction can't succeed in delivering the semantics, there will > be retries, which are not problems but the expected behavior. Livelock > would be possible, but is avoided by the retry limit. If you encounter > the retry limit it means you will have to re-architect your units of > work (sizes, durations, degrees of contention, commute vs set), and > would have to make similar changes with any strategy. Thanks for the feedback Rich! -- R. Mark Volkmann Object Computing, Inc. --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---