[GHC] #10241: BlockedIndefinitelyOnMVar thrown to the thread which is not blocked indefinitely

GHC ghc-devs at haskell.org
Sat Apr 4 11:23:09 UTC 2015


#10241: BlockedIndefinitelyOnMVar thrown to the thread which is not blocked
indefinitely
-------------------------------------+-------------------------------------
              Reporter:  asukamirai  |             Owner:  simonmar
                  Type:  bug         |            Status:  new
              Priority:  normal      |         Milestone:
             Component:  Runtime     |           Version:  7.8.3
  System                             |  Operating System:  Unknown/Multiple
              Keywords:              |   Type of failure:  Incorrect result
          Architecture:  x86_64      |  at runtime
  (amd64)                            |        Blocked By:
             Test Case:              |   Related Tickets:
              Blocking:              |
Differential Revisions:              |
-------------------------------------+-------------------------------------
 BlockedIndefinatelyOnMVar exception is thrown to the main thread in below
 source code although the thread is not blocked indefinitely.

 {{{#!hs
 module Main where

 import qualified Control.Concurrent.MVar as MV
 import qualified Control.Concurrent      as CC
 import qualified Control.Exception       as E

 main :: IO ()
 main = do
     -- call this thread "threadA"
     mvar1 <- MV.newEmptyMVar :: IO (MV.MVar ())
     mvar2 <- MV.newEmptyMVar :: IO (MV.MVar ())
     _ <- CC.forkIO $ do
         -- call this thread "threadB"
         MV.takeMVar mvar1 `E.catch` errorHandler1
         putStrLn "after error catch"
         CC.threadDelay 1000000
         MV.putMVar mvar2 ()
         putStrLn "after putMVar"
     MV.readMVar mvar2 `E.catch` errorHandler2
     putStrLn "after readMVar"
     CC.threadDelay 5000000
     where
         errorHandler1 :: E.BlockedIndefinitelyOnMVar -> IO ()
         errorHandler1 e = putStrLn $ "errorHandler1 : " ++ show e
         errorHandler2 :: E.BlockedIndefinitelyOnMVar -> IO ()
         errorHandler2 e = putStrLn $ "errorHandler2 : " ++ show e
 }}}

 Save above as "mvar.hs" and run by ghc as below.

 {{{
 > runhaskell mvar.hs
 errorHandler1 : thread blocked indefinitely in an MVar operation
 errorHandler2 : thread blocked indefinitely in an MVar operation
 after error catch
 after readMVar
 after putMVar
 }}}

 BlockedIndefinitelyOnMVar thrown for mvar1 is correct. It will be caught
 by errorHandler1 and "threadB" can continue to put the value to mvar2. It
 means that "threadA" can wait for the value of mvar2 and it is not blocked
 indefinately.
 However, BlockedIndefinitelyOnMVar is thrown for mvar2 on "threadA" before
 "threadB" puts value to the mvar2. I think it is incorrect.

 ----

 I tested another case that adding "CC.threadDelay 10000000" before
 "readMVar" as below.

 {{{#!hs
 module Main where

 import qualified Control.Concurrent.MVar as MV
 import qualified Control.Concurrent      as CC
 import qualified Control.Exception       as E

 main :: IO ()
 main = do
     -- call this thread "threadA"
     mvar1 <- MV.newEmptyMVar :: IO (MV.MVar ())
     mvar2 <- MV.newEmptyMVar :: IO (MV.MVar ())
     _ <- CC.forkIO $ do
         -- call this thread "threadB"
         MV.takeMVar mvar1 `E.catch` errorHandler1
         putStrLn "after error catch"
         CC.threadDelay 1000000
         MV.putMVar mvar2 ()
         putStrLn "after putMVar"
     CC.threadDelay 10000000 -- <-- this line is added
     MV.readMVar mvar2 `E.catch` errorHandler2
     putStrLn "after readMVar"
     CC.threadDelay 5000000
     where
         errorHandler1 :: E.BlockedIndefinitelyOnMVar -> IO ()
         errorHandler1 e = putStrLn $ "errorHandler1 : " ++ show e
         errorHandler2 :: E.BlockedIndefinitelyOnMVar -> IO ()
         errorHandler2 e = putStrLn $ "errorHandler2 : " ++ show e
 }}}

 And it will run correctly (BlockedIndefinitelyOnMVar is not thrown for
 mvar2).

 {{{
 > runhaskell mvar.hs
 errorHandler1 : thread blocked indefinitely in an MVar operation
 after error catch
 after putMVar
 after readMVar
 }}}

 ----

 I found this behavior is same on STM / BlockedIndefinitelyOnSTM.

 {{{#!hs
 module Main where

 import qualified Control.Concurrent.STM as STM
 import qualified Control.Concurrent     as CC
 import qualified Control.Exception      as E

 main :: IO ()
 main = do
     tmv1 <- STM.newEmptyTMVarIO :: IO (STM.TMVar ())
     tmv2 <- STM.newEmptyTMVarIO :: IO (STM.TMVar ())
     _ <- CC.forkIO $ do
         STM.atomically (STM.takeTMVar tmv1) `E.catch` errorHandler1
         putStrLn "after error catch"
         CC.threadDelay 1000000
         STM.atomically $ STM.putTMVar tmv2 ()
         putStrLn "after putTMVar"
     STM.atomically (STM.readTMVar tmv2) `E.catch` errorHandler2
     putStrLn "after readTMVar"
     CC.threadDelay 5000000
     where
         errorHandler1 :: E.BlockedIndefinitelyOnSTM -> IO ()
         errorHandler1 e = putStrLn $ "errorHandler1 : " ++ show e
         errorHandler2 :: E.BlockedIndefinitelyOnSTM -> IO ()
         errorHandler2 e = putStrLn $ "errorHandler2 : " ++ show e
 }}}

 {{{
 > runhaskell stm.hs
 errorHandler1 : thread blocked indefinitely in an STM transaction
 errorHandler2 : thread blocked indefinitely in an STM transaction
 after error catch
 after readTMVar
 after putTMVar
 }}}

 ----

 I tested this in below versions/OSs and got same result (exception thrown
 for mvar2/tmv2).

 ghc7.8.3 on Windows7
 ghc7.8.3 on lubuntu14.04 on VirtualBox on Windows7
 ghc7.8.4 on lubuntu14.04 on VirtualBox on Windows7
 ghc7.10.1 on lubuntu14.04 on VirtualBox on Windows7

 Similar report https://ghc.haskell.org/trac/ghc/ticket/8804 found but not
 the same.
 (In this case, the reference to the MVar is not weak)

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/10241>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list