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:
I have isolated the problem to three files:
sense
)application
handleClient
, and cleanupClient
)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.
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
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With