Opened 15 months ago

Closed 15 months ago

Last modified 15 months ago

#8804 closed bug (invalid)

BlockedIndefinitelyOnMVar thrown for an MVar which is still weakly accessible from another thread

Reported by: bholst Owned by: simonmar
Priority: normal Milestone:
Component: Runtime System Version: 7.6.3
Keywords: Cc: simonmar
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Incorrect result at runtime Test Case:
Blocked By: Blocking:
Related Tickets: Differential Revisions:

Description

GHC's runtime system throws a BlockedIndefinatelyOnMVar exception
when the thread blocking an the MVar is the only one having a direct access to it. It assumes that there will be nothing written into the MVar in the future.
This would be the correct behaviour if there were no weak references.
The runtime system even throws the Exception when another thread still has a weak reference to the MVar. Consider the following example:

import Control.Concurrent
import System.Mem.Weak
import Data.Maybe
import Control.Monad

main = do
  m <- newEmptyMVar
  w <- mkWeakMVar m (return ())
  forkIO $ do
    threadDelay 1000000
    n <- deRefWeak w
    when (isJust n) $ putMVar (fromJust n) ()
  takeMVar m

At the time of takeMVar the forked thread has a weak reference to
the MVar and it will put a value in it. However, the runtime system throws the Exception:

% ghc BlockingOnMVar.hs -threaded
Linking BlockingOnMVar ...
% ./BlockingOnMVar +RTS -N2      
BlockingOnMVar: thread blocked indefinitely in an MVar operation

Change History (3)

comment:1 Changed 15 months ago by simonmar

  • Resolution set to invalid
  • Status changed from new to closed

This is correct: remember that the weak reference is weak, so it doesn't keep the MVar alive, and hence the thread is also not reachable.

comment:2 Changed 15 months ago by bholst

You mean the waiting thread is garbage collected in this example and that's why the MVar will never be filled?

Then keeping the thread reachable through another MVar should do the trick?

import Control.Concurrent
import System.Mem.Weak
import Data.Maybe
import Control.Monad

main = do
  my <- myThreadId
  m <- newEmptyMVar
  w <- mkWeakMVar m (return ())
  o <- newEmptyMVar
  _ <- forkIO $ do
    threadDelay 1000000
    n <- deRefWeak w
    when (isJust n) $ putMVar (fromJust n) ()
    putMVar o ()
  takeMVar m
  takeMVar o

This program has exactly the same output.

I understand that the MVar m is not reachable from the other thread, but nevertheless the main thread is not blocked indefinately on this MVar.

comment:3 Changed 15 months ago by ezyang

I suspect you typoed, because my becomes dead immediately in your code. An MVar will not keep a thread alive unless the thread is blocked on it. If you force the thread to be live it will not get the exception raised, since the thread now retains the MVar.

import Control.Concurrent
import System.Mem.Weak
import Data.Maybe
import Control.Monad
import Foreign.StablePtr

main = do
  my <- myThreadId
  newStablePtr my
  m <- newEmptyMVar
  w <- mkWeakMVar m (return ())
  _ <- forkIO $ do
    threadDelay 1000000
    n <- deRefWeak w
    when (isJust n) $ putMVar (fromJust n) ()
  takeMVar m
Note: See TracTickets for help on using tickets.