Understanding behavior of BlockedIndefinitelyOnMVar exception
Brandon Simmons
brandon.m.simmons at gmail.com
Tue Jul 26 23:18:49 CEST 2011
On Tue, Jul 26, 2011 at 1:25 AM, Edward Z. Yang <ezyang at mit.edu> wrote:
> Hello Brandon,
>
> The answer is subtle, and has to do with what references are kept in code,
> which make an object considered reachable. Essentially, the main thread
> itself keeps the MVar live while it still has forking to do, so that
> it cannot get garbage collected and trigger these errors.
Ah, okay. That seems like an obvious explanation for the exceptions
to be raised at the same time in the forked threads.
>
> Here is a simple demonstrative program:
>
> main = do
> lock <- newMVar ()
> forkIO (takeMVar lock)
> forkIO (takeMVar lock)
> forkIO (takeMVar lock)
>
(snip)
>
> But in the meantime (esp. between invocation 2 and 3), the MVar cannot be
> garbage collected, because it is live on the stack.
>
> Could GHC have been more clever in this case? Not in general, since deciding
> whether or not a reference will actually be used or not boils down to the
> halting problem.
>
> loop = threadDelay 100 >> loop -- prevent blackholing from discovering this
> main = do
> lock <- newEmptyMVar
> t1 <- newEmptyMVar
> forkIO (takeMVar lock >> putMVar t1 ())
> forkIO (loop `finally` putMVar lock ())
> takeMVar t1
>
> Maybe we could do something where MVar references are known to be writer ends
> or read ends, and let the garbage collector know that an MVar with only read
> ends left is a deadlocked one. However, this would be a very imprecise
> analysis, and would not help in your original code (since all of your remaining
> threads had the possibility of writing to the MVar: it doesn't become clear
> that they can't until they all hit their takeMVar statements.)
I think this is the crux of what I was confused about. I had assumed
read vs. write was being taken into account by the runtime in raising
BlockedIndefinitelyOnMVar. This makes it obvious:
loop = threadDelay 100 >> loop -- prevent blackholing from discovering this
main = do
lock <- newEmptyMVar
forkIO (loop `finally` takeMVar lock)
takeMVar lock
Given that, I still can't say I understand what is happening in my
original code. I'll try to work out an even simpler example on my own.
Thanks for the thoughtful response,
Brandon
>
> Cheers,
> Edward
>
More information about the Glasgow-haskell-users
mailing list