线程在MVar操作中无限期地被阻塞

Mat*_*mus 8 concurrency haskell network-programming

我一直试图在使用多个MVar时调试问题,但是没有运气.

我的代码使用两个MVar:一个用于存储服务器的当前状态,另一个用于向客户端线程传递网络事件.但是,在多次连接和断开连接后,服务器会在新客户端连接时停止发送数据(可能是因为网络事件MVar因任何原因而被清空)并最终因错误而跳闸: *** Exception: thread blocked indefinitely in an MVar operation

我在过去几天尝试调试此问题时得出以下结论:

  1. 用于修改MVar的函数不会抛出异常
  2. 在客户端连接或连接然后断开连接之前,不会发生此问题
  3. 问题似乎是随机发生的(有时几个客户端可以连接然后断开连接,有时它会立即发生)

我已将问题隔离到三个文件:

  1. https://github.com/Mattiemus/IMC-Server/blob/master/IMC.hs(抛出异常sense)
  2. https://github.com/Mattiemus/IMC-Server/blob/master/IMC/Networking/Server.hs(修改在application handleClient,和cleanupClient)
  3. https://github.com/Mattiemus/IMC-Server/blob/master/IMC/Utilities/Concurrency.hs(推送和弹出存储在MVar中的列表的函数)

我完全没有想法,因为我只使用modifyMVar和withMVar(所以它肯定永远不会完全留空) - 我唯一的假设是在修改MVar时可能会抛出异常,但我认为这是不太可能的.

任何帮助表示赞赏,这个问题一直困扰着我一段时间.

Joh*_*ler 5

对于任何可能偶然发现这一点的人来说,一些附加信息thread blocked indefinitely in an MVar operation并不是那么明智。当包含对 MVar 的引用的每个线程都尝试读取(或写入)该位置、已死亡或正在等待永远被阻止的另一个原语时,就会发生这种情况。例如,线程 1 正在尝试读取 MVara并等待线程 2,该线程 2 要么已死a,也尝试读取 MVar ,要么尝试读取b只能在线程 1 中写入的 MVar 。

下面的代码很高兴永远挂起:

do
  a <- newEmptyMVar
  forkIO (readMVar a >>= print)
  putMVar a $ last $ repeat 0
Run Code Online (Sandbox Code Playgroud)


Mat*_*mus 4

三天后问题得到解决:实际上与网络或并发代码无关,事实上是我dpSwitch在 Netwire 中错误地重新实现了 Yampas 造成的。下面发布了更正的代码,供希望实现此功能的任何人使用:

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)
Run Code Online (Sandbox Code Playgroud)