festung-0.9.1.1: src/Festung/Concurrency/Utils.hs
module Festung.Concurrency.Utils
( copyMVar
, readAnyMVar
, readMVarTimeout
, readChanTimeout
, forkIOForSure
) where
import Control.Concurrent
import Control.Monad
-- | Copy (by using readMVar not takeMVar) an MVar into another MVar
copyMVar :: (a -> b) -> MVar a -> MVar b -> IO ()
copyMVar f src dst = putMVar dst =<< fmap f (readMVar src)
-- | Read the first MVar that gets filled.
--
-- If both MVar get filled at the same time, or are already filled, the result of this
-- function is undefined.
readAnyMVar :: MVar a -> MVar b -> IO (Either a b)
readAnyMVar a b = do
result <- newEmptyMVar
first <- forkIO $ copyMVar Left a result
second <- forkIO $ copyMVar Right b result
takeMVar result
<* mapM_ killThread [first, second]
-- | Read the MVar with timeout
--
-- Try to read the MVar and return @'Nothing' if the MVar doesn't get filled
-- within a certain amount of microseconds.
readMVarTimeout :: Int -> MVar a -> IO (Maybe a)
readMVarTimeout timeout mvar = do
timeoutMVar <- newEmptyMVar
timeoutThread <- forkIO . void $ do threadDelay timeout
tryPutMVar timeoutMVar ()
result <- readAnyMVar timeoutMVar mvar
killThread timeoutThread
return $ case result of
Left () -> Nothing
Right e -> Just e
-- | Read the next value from a @'Chan' within a certain time
--
-- Note: If this times out, nothing guarantee that the value hasn't been read
-- from the @'Chan' and discarded.
readChanTimeout :: Int -> Chan a -> IO (Maybe a)
readChanTimeout timeout chan = do
mvar <- newEmptyMVar
reader <- forkIO (putMVar mvar =<< readChan chan)
readMVarTimeout timeout mvar
<* killThread reader
-- | Like forkIO but make sure the green thread is started
forkIOForSure :: IO () -> IO ThreadId
forkIOForSure action = do
mvar <- newEmptyMVar
forkIO (putMVar mvar () >> action)
<* readMVar mvar