Lock-Free Data Structures using STMs in Haskell

I recently read the STM paper on lock-free data structures [1] which I found very informative in my quest to learn how to use STM. However, there are a few things I do not fully understand and was hoping someone might be able to explain further. In the STM version of the ArrayBlockingQueue, the following type is defined: data ArrayBlockingQueueSTM e = ArrayBlockingQueueSTM { shead :: TVar Int, stail :: TVar Int, sused :: TVar Int, slen :: Int, sa :: Array Int (TVar e) } It's unclear to me why the Array's elements must be wrapped in TVars. Why aren't the TVars on shead, stail, and sused sufficient? Here is the only function that reads from the queue: readHeadElementSTM :: ArrayBlockingQueueSTM e -> Bool -> Bool -> STM (Maybe e) readHeadElementSTM abq remove block = do u <- readTVar (sused abq) if u == 0 then if block then retry else return Nothing else do h <- readTVar (ihead abq) let tv = sa abq ! h -- Why are the array elements wrapped in TVars? e <- readTVar tv if remove then do let len = slen abq let newh = h `mod` len writeTVar (shead abq) $! newh writeTVar (sused abq) $! (u-1) else return () return (Just e) It is not immediately obvious to me why the elements need to be wrapped in TVars. Could someone help elaborate? The other question is in regards to section 2 where STM is introduced. The authors define the following: decT :: TVar Int -> IO () decT v = atomically (do x <- readTVar v if x == 0 then retry else return () writeTVar v (x-1)) And then go on to show how easy it is to compose STM types with this function: decPair v1 v1 :: TVar Int -> TVar Int -> IO () decPair v1 v2 = atomically (decT v1 `orElse` decT v2) Will this actually compile? I was under the impression that 'orElse' could only combine STM types, not IO () types. Thank you, Pete [1] Anthony Discolo, Tim Harris, Simon Marlow, Simon Peyton Jones, and Satnam Singh. Lock-free data structures using STMs in Haskell. In Eighth International Symposium on Functional and Logic Programming (FLOPS.06), April 2006.

Pete Kazmier wrote:
data ArrayBlockingQueueSTM e = ArrayBlockingQueueSTM { [...] sa :: Array Int (TVar e) }
It's unclear to me why the Array's elements must be wrapped in TVars.
To allow them to be modified. You can't otherwise modify the elements of an array without going into the ST monad.
decPair v1 v1 :: TVar Int -> TVar Int -> IO () decPair v1 v2 = atomically (decT v1 `orElse` decT v2)
Will this actually compile? I was under the impression that 'orElse' could only combine STM types, not IO () types.
The type of atomically is STM a -> IO a.

decPair v1 v1 :: TVar Int -> TVar Int -> IO () decPair v1 v2 = atomically (decT v1 `orElse` decT v2)
Will this actually compile? I was under the impression that 'orElse' could only combine STM types, not IO () types.
The type of atomically is STM a -> IO a.
But orElse :: STM a -> STM a -> STM a decT can be of type TVar Int -> STM () if you leave out the atomically. -- Ariel J. Birnbaum

Bryan O'Sullivan
Pete Kazmier wrote:
data ArrayBlockingQueueSTM e = ArrayBlockingQueueSTM { [...] sa :: Array Int (TVar e) }
It's unclear to me why the Array's elements must be wrapped in TVars.
To allow them to be modified. You can't otherwise modify the elements of an array without going into the ST monad.
Thanks! I forgot about the whole immutable thing :-) Haven't used arrays yet while learning Haskell!
participants (3)
-
Ariel J. Birnbaum
-
Bryan O'Sullivan
-
Pete Kazmier