简体   繁体   English

如何通过每次传入重置的超时来管道?

[英]How would I pipe with a timeout that resets with each incoming?

The withTimeout function is suppose to pipe ConsoleEvent with a CeTimeout sent every s :: Int seconds if nothing has been received. withTimeout函数假设管道ConsoleEvent ,如果没有收到任何内容,则每隔s :: Int秒发送一次CeTimeout Instead it fails to send the CeTimeout events at the appropriate times. 相反,它无法在适当的时间发送CeTimeout事件。 One CeTimeout event is replaced for other events if greater than s seconds have passed with the original event being lost. 如果大于s秒且原始事件丢失,则会将一个CeTimeout事件替换为其他事件。 Also instead of one CeTimeout event, it should be n*s CeTimeout events with n counting for each s second period that has passed. 此外,而不是一个CeTimeout事件,应该是n*s CeTimeout与事件n每个计数s已经过去了2期。 Where is the mistake, and what would be the correction? 错误在哪里,纠正是什么? Thanks! 谢谢!

withTimeout :: (MonadIO t) => Int -> Pipe ConsoleEvent ConsoleEvent t ()
withTimeout ((* 1000000) -> s) = join . liftIO $ work
  where
    work :: (MonadIO t) => IO (Pipe ConsoleEvent ConsoleEvent t ()) 
    work =
      do
        (oSent, iKept) <- spawn $ bounded 1
        (oKept, iSent) <- spawn $ unbounded
        (oTimeout, iTimeout) <- spawn $ bounded 1

        tid <- launchTimeout oTimeout >>= newMVar

        forkIO $ do
          runEffect . forever $ fromInput iKept >-> factorTimeout tid oTimeout >-> toOutput oKept

        forkIO $ do
          runEffect . forever $ fromInput iTimeout >-> toOutput oKept

        return $ do
          await >>= (liftIO . guardedSend oSent)
          (liftIO . guardedRecv $ iSent) >>= yield

    guardedSend :: Output ConsoleEvent -> ConsoleEvent -> IO ()
    guardedSend o ce =
      (atomically $ send o ce) >>= \case
        True -> return ()
        otherwise -> die $ "withTimeout can not send"

    guardedRecv :: Input ConsoleEvent -> IO ConsoleEvent
    guardedRecv i =
      (atomically $ recv i) >>= \case
        Just a -> return a
        otherwise -> die $ "withTimeout can not recv"

    launchTimeout :: Output ConsoleEvent -> IO ThreadId
    launchTimeout o =
      forkIO . forever $ do
        threadDelay $ s
        (atomically $ send o CeTimeout) >>= \case
          True -> return ()
          otherwise -> die "withTimeout can not send timeout"

    relaunchTimeout :: Output ConsoleEvent -> ThreadId -> IO ThreadId
    relaunchTimeout o oldTid = 
      do
        tid <- launchTimeout o
        killThread oldTid
        return tid

    factorTimeout :: MVar ThreadId -> Output ConsoleEvent -> Pipe ConsoleEvent ConsoleEvent IO ()
    factorTimeout v o =
      do
        ce <- await
        liftIO . modifyMVar_ v $ relaunchTimeout o
        yield ce

Here is a fully executable script . 这是一个完全可执行的脚本

It seems like a Pipe will only allow one yield per await . 这似乎是一个Pipe将只允许一个yieldawait This means that a CeTimeout can not arbitrarily be sent down the pipe because nothing came into the pipe to cause the flow. 这意味着CeTimeout不能随意向管道发送,因为没有任何东西进入管道导致流动。 I will have to go through the source to confirm this; 我将不得不通过消息来源证实这一点; in the meantime this function has been refactored to return a Pipe and a Producer instead of just a Pipe . 与此同时,此函数已重构为返回PipeProducer而不仅仅是Pipe The Producer can then be joined back in the calling function. Producer然后可以在调用函数中加入回来。 The initial plan was to return just a Pipe so that the calling function would not have to do any additional work to make timeouts work. 最初的计划是只返回一个Pipe这样调用函数就不需要做任何额外的工作来使超时工作。 That would have been a more self contained solution. 那本来是一个更独立的解决方案。 This alternative is nice in that it is more explicit. 这个替代方案很好,因为它更明确。 The timeouts won't look like they are appearing out of thin air to someone that is not familiar with the pipeline. 对于不熟悉管道的人来说,超时看起来并不像是显得空洞。

withTimeout :: (MonadIO t) => Int -> IO (Pipe ConsoleEvent ConsoleEvent t (), Producer ConsoleEvent t ())
withTimeout ((* 1000000) -> s) =
  do
    (oTimeout, iTimeout) <- spawn $ bounded 1
    vTid <- launchTimeout oTimeout >>= newMVar

    return (factorTimeout vTid oTimeout, fromInput iTimeout)
  where
    launchTimeout :: Output ConsoleEvent -> IO ThreadId
    launchTimeout o =
      forkIO . forever $ do
        threadDelay $ s
        (atomically $ send o CeTimeout) >>= \case
          True -> return ()
          otherwise -> die "withTimeout can not send timeout"

    relaunchTimeout :: Output ConsoleEvent -> ThreadId -> IO ThreadId
    relaunchTimeout o oldTid = 
      do
        tid <- launchTimeout o
        killThread oldTid
        return tid

    factorTimeout :: (MonadIO t) => MVar ThreadId -> Output ConsoleEvent -> Pipe ConsoleEvent ConsoleEvent t ()
    factorTimeout v o =
      do
        ce <- await
        liftIO . modifyMVar_ v $ relaunchTimeout o
        yield ce

main :: IO ()
main =
  do
    hSetBuffering stdin NoBuffering
    hSetEcho stdin False

    exitSemaphore <- newEmptyMVar
    (o1, i1) <- spawn $ bounded 1
    (o2, i2) <- spawn $ bounded 1

    (timeoutTrap, timeoutRender) <- withTimeout 2

    runEffect $ yield CeBegan >-> toOutput o1

    forkIO $ do
      runEffect . forever $ chars >-> toOutput o1
      putMVar exitSemaphore ()

    -- other inputs would be piped to o1 here

    forkIO $ do
      runEffect . forever $ fromInput i1 >-> timeoutTrap >-> toOutput o2
      putMVar exitSemaphore ()

    forkIO $ do
      runEffect . forever $ timeoutRender >-> toOutput o2
      putMVar exitSemaphore ()

    forkIO $ do
      -- logic would be done before dumpPipe
      runEffect . forever $ fromInput i2 >-> dumpPipe >-> (await >> return ())
      putMVar exitSemaphore ()

    takeMVar exitSemaphore

Here is a fully executable script . 这是一个完全可执行的脚本

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM