What is the precise reason I got blocked on STM?

I have the following Haskell code which is supposed to implement some STM-based queue:

{-# LANGUAGE ScopedTypeVariables #-}
module Main where

import           Control.Concurrent.Async
import           Control.Concurrent.STM
import           Control.Exception
import           Control.Monad            (forever)
import           Hevents.Eff
import           System.IO

withStore :: (FileStorage -> IO a) -> IO a
withStore = bracket (openFileStorage "test.store") closeFileStorage

data Op = Op String (TMVar Int)

storerun :: TBQueue Op -> IO ()
storerun q = do
  h <- openFile "store.test" ReadWriteMode
  hSetBuffering h NoBuffering
  forever $ do
    Op s v <- atomically $ readTBQueue q
    hPutStrLn h s
    atomically $ putTMVar v (length s)


main :: IO ()
main = do
  q <- newTBQueueIO 100
  _ <- async $ storerun q
  storeInput q
  where
    storeInput q = forever $ do
      putStrLn "pushing"
      l <- getLine
      v <- newEmptyTMVarIO
      r <- atomically $ do
        writeTBQueue q (Op l v)
        takeTMVar v
      putStrLn $ "got " ++ show r

When ran this code raises a BlockedIndefinitelyOnSTM exception. If I change the storeInput function to the following:

    storeInput q = forever $ do
      putStrLn "pushing"
      l <- getLine
      v <- atomically $ do
        v <- newEmptyTMVar
        writeTBQueue q (Op l v)
        return v
      r <- atomically $ takeTMVar v
      putStrLn $ "got " ++ show r

Program runs fine.

My understanding of what can cause this exception is that a variable involved in a STM transaction has been somehow garbage-collected is only visible in a single thread which is retry ing and is thus deadlocked because the content of the transactional variable won't ever change.

In my code, the v variable inside Op structure is created in one thread, passed to another thread using a transactional queue, then used by the other thread, and it seems there is no reason for it to be ever garbage-collected in any thread.

Hence it is not clear to me why precisely this code is failing.


Transactions are atomic. The problem lies here:

r <- atomically $ do
        writeTBQueue q (Op l v) -- (1)
        takeTMVar v             -- (2)

This will block unless another thread performs a putTMVar between (1) and (2). Atomicity prevents that.

In a transaction, you can not "send information" to another transaction, and expect a "reply" from that. This would require the former transaction to be (logically) performed before the latter one, and vice versa, which is impossible.

链接地址: http://www.djcxy.com/p/81464.html

上一篇: 在STM事务中取消屏蔽异步异常

下一篇: STM被阻挡的确切原因是什么?