[stm] strange behavior with TQueue and STM's alternative instance

Simon Marlow marlowsd at gmail.com
Mon Dec 10 14:12:32 CET 2012


On 10/12/12 12:23, Bas van Dijk wrote:
> On 10 December 2012 12:51, Simon Marlow <marlowsd at gmail.com> wrote:
>> On 09/12/12 21:24, Bas van Dijk wrote:
>>>
>>> On 9 December 2012 21:52, Bas van Dijk <v.dijk.bas at gmail.com> wrote:
>>>>
>>>> On 9 December 2012 20:50, Bas van Dijk <v.dijk.bas at gmail.com> wrote:
>>>>>
>>>>> On 9 December 2012 16:48, Patrick Palka <patrick at parcs.ath.cx> wrote:
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> I'm getting strange behavior when using the 'many' combinator to read
>>>>>> zero
>>>>>> or more items off of a TQueue with readTQueue. The script that exhibits
>>>>>> this
>>>>>> behavior is as follows:
>>>>>>
>>>>>> import Control.Concurrent.STM
>>>>>> import Control.Concurrent
>>>>>> import Control.Monad
>>>>>> import Control.Applicative
>>>>>>
>>>>>> main = do
>>>>>>       q <- newTQueueIO
>>>>>>       atomically $ writeTQueue q True
>>>>>>       atomically $ writeTQueue q False
>>>>>>       forever $ do
>>>>>>           xs <- atomically $ many $ readTQueue q
>>>>>>           print xs
>>>>>>           threadDelay 500000
>>>>>>
>>>>>>
>>>>>> I'd expect the output of the script to be:
>>>>>> [True,False]
>>>>>> []
>>>>>> []
>>>>>> ...
>>>>>>
>>>>>> However, that is not the case: the actual output of the script is:
>>>>>> [True,False]
>>>>>> [True,False]
>>>>>> [True,False]
>>>>>> ...
>>>>>>
>>>>>> This means that TQueue is incompatible with TChan, since if TQueue is
>>>>>> replaced by TChan then the script behaves as one would expect.
>>>>>>
>>>>>> If 1 element (say, True) is written into the TQueue instead of 2, then
>>>>>> the
>>>>>> output of the script is:
>>>>>> [True]
>>>>>> []
>>>>>> []
>>>>>> ...
>>>>>>
>>>>>> Which is expected behavior, but inconsistent with the behavior when the
>>>>>> TQueue has 2 or more elements in it.
>>>>>>
>>>>>> Is this considered a bug, or undocumented behavior of TQueue?
>>>>>>
>>>>>> _______________________________________________
>>>>>> Libraries mailing list
>>>>>> Libraries at haskell.org
>>>>>> http://www.haskell.org/mailman/listinfo/libraries
>>>>>>
>>>>>
>>>>> This is puzzling me. It looks like a bug in STM. The following code
>>>>> should have the same behavior. Interestingly, when I remove the
>>>>> 'readTVar write' marked "Remove me!!!" I get the desired behavior:
>>>>>
>>>>> import Control.Concurrent.STM
>>>>>       (STM, atomically, retry, TVar, newTVarIO, readTVar, writeTVar)
>>>>> import Control.Concurrent
>>>>> import Control.Monad
>>>>> import Control.Applicative
>>>>>
>>>>> main = do
>>>>>     q@(TQueue read write) <- newTQueueIO
>>>>>     atomically $ writeTQueue q True
>>>>>     atomically $ writeTQueue q False
>>>>>     forever $ do
>>>>>       xs <- atomically $
>>>>>         (((:) <$> readTQueue q <*>
>>>>>           (((:) <$> readTQueue q <*>
>>>>>             (((:) <$> (do readTVar read
>>>>>                           readTVar write -- Remove me!!!
>>>>>                           retry
>>>>>                       ) <*> error "..."
>>>>>              ) <|> pure []
>>>>>             )
>>>>>            ) <|> pure []
>>>>>           )
>>>>>          ) <|> pure []
>>>>>         )
>>>>>       print xs
>>>>>       threadDelay 500000
>>>>>
>>>>> data TQueue a = TQueue {-# UNPACK #-} !(TVar [a])
>>>>>                          {-# UNPACK #-} !(TVar [a])
>>>>>
>>>>> newTQueueIO :: IO (TQueue a)
>>>>> newTQueueIO = do
>>>>>     read  <- newTVarIO []
>>>>>     write <- newTVarIO []
>>>>>     return (TQueue read write)
>>>>>
>>>>> writeTQueue :: TQueue a -> a -> STM ()
>>>>> writeTQueue (TQueue _read write) a = do
>>>>>     listend <- readTVar write
>>>>>     writeTVar write (a:listend)
>>>>>
>>>>> readTQueue :: Show a => TQueue a -> STM a
>>>>> readTQueue (TQueue read write) = do
>>>>>     xs <- readTVar read
>>>>>     case xs of
>>>>>       (x:xs') -> do writeTVar read xs'
>>>>>                     return x
>>>>>       [] -> do ys <- readTVar write
>>>>>                case ys of
>>>>>                  [] -> retry
>>>>>                  _  -> case reverse ys of
>>>>>                          [] -> error "readTQueue"
>>>>>                          (z:zs) -> do writeTVar write []
>>>>>                                       writeTVar read zs
>>>>>                                       return z
>>>>>
>>>>> Bas
>>>>
>>>>
>>>> This is a simplified program which behaves in the same non-expected way:
>>>>
>>>> main = do
>>>>     t <- newTVarIO 1
>>>>     atomically $ do
>>>>       writeTVar t 2
>>>>       ((readTVar t >> retry) `orElse` return ()) `orElse` return ()
>>>>     atomically (readTVar t) >>= print
>>>>     threadDelay 500000
>>>>
>>>> This program prints 1 while I expect it to print 2.
>>>>
>>>> It prints 2 when I remove the outer `orElse`:
>>>>
>>>> main = do
>>>>     t <- newTVarIO 1
>>>>     atomically $ do
>>>>       writeTVar t 2
>>>>       ((readTVar t >> retry) `orElse` return ())
>>>>     atomically (readTVar t) >>= print
>>>>     threadDelay 500000
>>>>
>>>> It also prints 2 when I remove the readTVar:
>>>>
>>>> main = do
>>>>     t <- newTVarIO 1
>>>>     atomically $ do
>>>>       writeTVar t 2
>>>>       (retry `orElse` return ()) `orElse` return ()
>>>>     atomically (readTVar t) >>= print
>>>>     threadDelay 500000
>>>>
>>>> Bas
>>>
>>>
>>> The simplest program I can come up with which shows the same behavior:
>>>
>>> $ cat stmTest.hs
>>> import Control.Concurrent.STM
>>> main = do
>>>     x <- atomically $ do
>>>            t <- newTVar 1
>>>            writeTVar t 2
>>>            ((readTVar t >> retry) `orElse` return ()) `orElse` return ()
>>>            readTVar t
>>>     print x
>>>
>>> $ ghc --make stmTest.hs -fforce-recomp -threaded -o stmTest && ./stmTest
>>> [1 of 1] Compiling Main             ( stmTest.hs, stmTest.o )
>>> Linking stmTest ...
>>> 1
>>
>>
>> Nice bug!  I have a fix, will try to get it into GHC 7.6.2.
>>
>> Cheers,
>>          Simon
>>
>>
>
> Great!
>
> I would like to understand what went wrong. I looked at the retry and
> catchRetry PrimOps but didn't understand them well enough to figure
> out the bug. Do you have a link to your patch?

Sure: 
http://hackage.haskell.org/trac/ghc/changeset/f184d9caffa09750ef6a374a7987b9213d6db28e

Cheers,
	Simon




More information about the Libraries mailing list