Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread blocked indefinitely in an MVar operation

I have been attempting to debug a problem when using multiple MVars, however to no luck.

My code uses two MVars: one to store the servers current state, and another to pass network events to and from the client threads. However after connecting and disconnecting several times, the server stops sending data upon new clients connecting (presumably because the network events MVar is emptied for whatever reason) and eventually trips up with the error: *** Exception: thread blocked indefinitely in an MVar operation

I have concluded the following in trying to debug this issue over the last few days:

  1. The functions used to modify the MVar(s) wont throw exceptions
  2. The problem does not occur until a client either connects, or connects then disconnects
  3. The issue seems to occur randomly (sometimes several clients can connect then disconnect, other times it happens right away)

I have isolated the problem to three files:

  1. https://github.com/Mattiemus/IMC-Server/blob/master/IMC.hs (the exception gets thrown in sense)
  2. https://github.com/Mattiemus/IMC-Server/blob/master/IMC/Networking/Server.hs (Modifined within application handleClient, and cleanupClient)
  3. https://github.com/Mattiemus/IMC-Server/blob/master/IMC/Utilities/Concurrency.hs (functions which push and pop to a list stored in an MVar)

I am totally out of ideas, as I only use modifyMVar and withMVar (so surely it should never be totally left empty) - my only assumption is that maybe an Exception is being thrown while modifying the MVar, however I think this is highly unlikely.

Any help is appreciated, this problem has been bugging me for some time now.

like image 965
Mattiemus Avatar asked Jun 17 '15 02:06

Mattiemus


2 Answers

For anyone who might stumble on this, some additional information thread blocked indefinitely in an MVar operation is not really that smart. It happens when every thread that contains a reference to the MVar is trying to read (or write) to that location, has died, or is waiting on another primitive that is blocked forever. eg thread 1 is trying to read MVar a and waiting on thread 2 which is either dead, also trying to read MVar a, or trying to read MVar b which can only be written to in thread 1.

The following code happily hangs forever:

do
  a <- newEmptyMVar
  forkIO (readMVar a >>= print)
  putMVar a $ last $ repeat 0
like image 68
John F. Miller Avatar answered Nov 15 '22 00:11

John F. Miller


Three days later and its solved: Was actually unrelated to either the networking or concurrency code, and infact caused by my incorrect re-implementation of Yampas dpSwitch in Netwire. Corrected code posted below for anyone wishing to implement this function:

dpSwitch :: (Monoid e, Applicative m, Monad m, T.Traversable col) => (forall wire. a -> col wire -> col (b, wire))
     -> col (Wire s e m b c)
     -> Wire s e m (a, col c) (Event d)
     -> (col (Wire s e m b c) -> d -> Wire s e m a (col c))
     -> Wire s e m a (col c)
dpSwitch route wireCol switchEvtGen continuation = WGen $ gen wireCol switchEvtGen
where
    gen wires switchEvtGenWire _ (Left x) = return (Left mempty, WGen $ gen wires switchEvtGenWire)
    gen wires switchEvtGenWire ws (Right x) = do            
        let routings = route x wires
        wireSteps <- T.sequenceA (fmap (\(wireInput, wire) -> stepWire wire ws (Right wireInput)) routings)
        let wireOutputs = T.sequenceA (fmap fst wireSteps)
            steppedWires = fmap snd wireSteps
        case wireOutputs of
            Left wireInhibitedOutput -> return (Left wireInhibitedOutput, WGen $ gen steppedWires switchEvtGenWire)
            Right wireResultOutput -> do
                (event, steppedSwitchEvtGenWire) <- stepWire switchEvtGenWire ws (Right (x, wireResultOutput))
                case event of
                    Left eventInhibited -> return (Left eventInhibited, WGen $ gen steppedWires steppedSwitchEvtGenWire)
                    Right NoEvent -> return (wireOutputs, WGen $ gen steppedWires steppedSwitchEvtGenWire)
                    Right (Event e) -> return (wireOutputs, continuation steppedWires e)
like image 43
Mattiemus Avatar answered Nov 15 '22 02:11

Mattiemus