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被阻挡的确切原因是什么?