如何在不杀死yesod-websocket连接的情况下停止侦听消息?

时间:2018-03-21 08:22:25

标签: haskell yesod

我正在使用yesod-websockets编写应用程序,每当收到“开始”消息时,我需要运行一个连续生成数据并将其发送到客户端的线程,直到客户端告诉它停止。制作人也可以自行停止制作数据。

当生产者停止时(无论它是否自行停止生产,或者客户端告诉它停止),它会返回主循环并等待接收另一条“开始”消息。

代码看起来像这样(下面的runnable minimal repro):

wsApp :: WebSocketsT Handler ()
wsApp = 
  forever $ do
    msg <- receiveMsg
    case msg of
      StartMsg -> do
        race_
          (produceData)
          (whileM ((/= StopMsg) <$> receiveMsg))

问题是如果produceData自行停止,那么运行receiveMsg的线程将被取消,这会导致websocket连接被关闭。

  

21 / Mar / 2018:08:21:06 +0000 [错误#yesod]来自Warp的异常:ConnectionClosed @(app-0.0.0-5bzI9Onrk2fFepGGsdocDz:Application src / Application.hs:122:15)

有没有办法取消正在侦听连接的线程,而不会终止连接?

这是一个最小的复制品:

wsApp :: WebSocketsT Handler ()
wsApp = forever $ do
  race_
    (receiveData :: WebSocketsT Handler Text)
    (pure ())
  $logDebug "Trying again"

第二个线程将完成,第一个线程将被取消,导致连接被终止。

1 个答案:

答案 0 :(得分:0)

由于我找不到更优雅的解决方案,我最终使用IORef Bool来同步websockets线程和生产者线程。

wsApp :: WebSocketsT Handler ()
wsApp = do
  producing <- newIORef False
  forever $ do
    msg <- receiveMsg
    case msg of
      StartMsg -> do
        whenM (not <$> readIORef producing) $ do
          atomicWriteIORef producing True
          void . async $ produceData producing
      StopMsg -> atomicWriteIORef producing False

produceData :: IORef Bool -> WebSocketsT Handler ()
produceData producing =
  whenM (readIORef producing) $
    case produce of
      Nothing -> atomicWriteIORef producing False
      Just x  -> sendMsg x >> produceData producing