Neil Brown
I think there needs to be some differentiation here between the implementation of STM, and the programmer's use of STM.
The implementation of STM does effectively use locks (from memory, it's this paper that explains it:
Ignoring the paper in the interest of laz...expedience, I guess the crucial part is committing the transactions - you'd either need locks or to single-thread the committing.
The use of STM does not involve locks, and one of STM's main advantages is that it hides explicit locks from the user. If you have retry in STM (as Haskell does, but not all other implementations do) then you can write deadlocking code with it, and indeed you can simulate mutexes and so on using retry, hence allowing you to use your own constructed locks with STM. So in using STM you can deadlock, and you can make some locks to use if you want, but it's not required.
So the naïve attempt at doing this would be something like: thread = do -- grab "lock 1" t <- readTVar lock when t retry writeTVar lock True -- grab "lock 2" t2 <- readTVar lock2 when t2 retry writeTVar writeTVar lock2 True -- do something writeTVar lock2 False writeTVar lock False and another one with the locks reversed. But that won't work of course, since the 'retry' will rollback the taking of lock 1 as well. So do I need to split this up into separate STM transactions and orchestrate the locking from the IO monad? -k -- If I haven't seen further, it is by standing in the footprints of giants