haskell - Subproceso bloqueado indefinidamente en una operación MVar
concurrency network-programming (3)
Creo que veo el problema, está en Server.hs. Tiene operaciones que hacen IO de red dentro de una llamada withMVar
. Ahora imagina que IO bloquea efectivamente para siempre. Tampoco obtienes una excepción que fuerza a la var a ser reemplazada, ni la operación se completa normalmente y reemplaza la var, y por lo tanto te quedas atascado.
En general, no debe hacer ninguna operación significativa en una llamada withMVar
, aunque pueda . Y si realiza estas operaciones, debe asegurarse doblemente de que efectivamente las protege con tiempo de espera, etc. de modo que esté seguro de que siempre se completan de una forma u otra.
He estado intentando depurar un problema al usar múltiples MVars, sin embargo, no tuve suerte.
Mi código utiliza dos MVars: uno para almacenar el estado actual de los servidores y otro para pasar eventos de red hacia y desde los hilos del cliente. Sin embargo, después de conectarse y desconectarse varias veces, el servidor deja de enviar datos cuando se conectan nuevos clientes (presumiblemente porque los eventos de red MVar se vacían por algún motivo) y finalmente se dispara con el error: *** Exception: thread blocked indefinitely in an MVar operation
He concluido lo siguiente al intentar depurar este problema en los últimos días:
- Las funciones utilizadas para modificar los MVar (s) no arrojarán excepciones
- El problema no ocurre hasta que un cliente se conecta o se conecta y luego se desconecta
- El problema parece ocurrir al azar (a veces varios clientes pueden conectarse y luego desconectarse, otras veces sucede de inmediato)
He aislado el problema en tres archivos:
- https://github.com/Mattiemus/IMC-Server/blob/master/IMC.hs (la excepción se arroja en
sense
) - https://github.com/Mattiemus/IMC-Server/blob/master/IMC/Networking/Server.hs (Modificado en la
application
handleClient
ycleanupClient
) - https://github.com/Mattiemus/IMC-Server/blob/master/IMC/Utilities/Concurrency.hs (funciones que presionan y aparecen en una lista almacenada en un MVar)
Estoy totalmente fuera de las ideas, ya que solo uso modifyMVar y withMVar (así que seguramente nunca debería estar totalmente vacío) - mi única suposición es que tal vez se está lanzando una excepción al modificar el MVar, sin embargo, creo que esto es muy poco probable.
Cualquier ayuda es apreciada, este problema me ha estado molestando por algún tiempo.
Para cualquiera que pueda tropezar con esto, algún thread blocked indefinitely in an MVar operation
información adicional thread blocked indefinitely in an MVar operation
no es realmente tan inteligente. Sucede cuando cada hilo que contiene una referencia al MVar está tratando de leer (o escribir) en esa ubicación, ha muerto o está esperando otra primitiva que está bloqueada para siempre. por ejemplo, el hilo 1 está tratando de leer MVar ay esperar en el hilo 2 que está muerto, también tratando de leer MVar a
, o tratando de leer MVar b
que solo se puede escribir en el hilo 1.
El siguiente código se cuelga felizmente para siempre:
do
a <- newEmptyMVar
forkIO (readMVar a >>= putStrLn)
putMVar a $ last $ repeat 0
Tres días después y se resolvió: en realidad no estaba relacionado con el código de red o de concurrencia, y de hecho fue causado por una reimplementación incorrecta de Yampas dpSwitch
en Netwire. Código corregido publicado a continuación para cualquier persona que desee implementar esta función:
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)