-- | Perform an IO action, and place its result in a 'TMVar'.  See
-- also "Control.Concurrent.MVarIO" for an 'MVar' version.
module Control.Concurrent.STM.TMVarIO (run, runOrTakeTMVar) where

import Data.Functor
import Control.Concurrent
import Control.Concurrent.STM

-- | @'run' action@ returns a 'TMVar' immediately. The result of
-- @action@ will be placed in said 'TMVar'. If the 'TMVar' is full
-- when @action@ completes, the return value is lost (the action does
-- not wait for an empty 'TMVar').
run :: IO a -> IO (TMVar a)
run action = newEmptyTMVarIO >>= \tm ->
             forkIO (run' action tm) >>
             return tm

-- | This version of 'run' takes an additional 'TMVar', and returns
-- its content /or/ the result of the IO action, depending on which is
-- available first. Note that the action is /not/ interrupted if the
-- 'TMVar' is the winner, so you may want to make sure it doesn't
-- stick around forever.
--
-- The function was made to support
-- 'System.Posix.IO.Select.STM.selectOrTakeTMVar', the reason this
-- library exists, and may not be useful outside of that context.
runOrTakeTMVar :: IO a -> TMVar b -> IO (Either a b)
runOrTakeTMVar action tm1 = run action >>= \tm2 ->
                            atomically ((Left <$> takeTMVar tm2) `orElse` (Right <$> takeTMVar tm1))

run' :: IO a -> TMVar a -> IO ()
run' action tm = action >>= \ret ->
                 atomically (tryPutTMVar tm ret) >>
                 return ()