[英]How would I pipe with a timeout that resets with each incoming?
withTimeout
函數假設管道ConsoleEvent
,如果沒有收到任何內容,則每隔s :: Int
秒發送一次CeTimeout
。 相反,它無法在適當的時間發送CeTimeout
事件。 如果大於s
秒且原始事件丟失,則會將一個CeTimeout
事件替換為其他事件。 此外,而不是一個CeTimeout
事件,應該是n*s
CeTimeout
與事件n
每個計數s
已經過去了2期。 錯誤在哪里,糾正是什么? 謝謝!
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
這是一個完全可執行的腳本 。
這似乎是一個Pipe
將只允許一個yield
每await
。 這意味着CeTimeout
不能隨意向管道發送,因為沒有任何東西進入管道導致流動。 我將不得不通過消息來源證實這一點; 與此同時,此函數已重構為返回Pipe
和Producer
而不僅僅是Pipe
。 該Producer
然后可以在調用函數中加入回來。 最初的計划是只返回一個Pipe
這樣調用函數就不需要做任何額外的工作來使超時工作。 那本來是一個更獨立的解決方案。 這個替代方案很好,因為它更明確。 對於不熟悉管道的人來說,超時看起來並不像是顯得空洞。
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
這是一個完全可執行的腳本 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.