timers-0.2.0.0: src/Control/Concurrent/Timer/Lifted.hs
{-# LANGUAGE FlexibleContexts #-}
module Control.Concurrent.Timer.Lifted
( Timer
, TimerIO
, oneShotTimer
, oneShotStart
, oneShotRestart
, repeatedTimer
, repeatedStart
, repeatedRestart
, newTimer
, stopTimer
) where
------------------------------------------------------------------------------
import Control.Applicative
import Control.Concurrent.Lifted (ThreadId, fork, killThread)
import Control.Concurrent.MVar.Lifted (newMVar, tryTakeMVar, putMVar, modifyMVar_)
import Control.Concurrent.Suspend.Lifted (Delay, suspend)
import Control.Monad
import Control.Monad.Base (MonadBase)
import Control.Monad.Trans.Control (MonadBaseControl)
------------------------------------------------------------------------------
import Control.Concurrent.Timer.Types (Timer(..), TimerImmutable(..))
------------------------------------------------------------------------------
-- | Attempts to start a timer.
-- The started timer will have the given delay and action associated and will be one-shot timer.
--
-- If the timer was already initialized, it the previous timer will be stoped (the thread killed) and the timer will be started anew.
--
-- Returns True if the strat was successful,
-- otherwise (e.g. other thread is attempting to manipulate the timer) returns False.
oneShotStart :: MonadBaseControl IO m
=> Timer m
-> m () -- ^ The action the timer will start with.
-> Delay -- ^ The dealy the timer will start with.
-> m Bool
oneShotStart (Timer mvmtim) a d = do
mtim <- tryTakeMVar mvmtim
case mtim of
Just (Just (TimerImmutable _ _ tid)) -> do
killThread tid
oneShotTimerImmutable a d >>= putMVar mvmtim . Just
return True
Just (Nothing) -> do
oneShotTimerImmutable a d >>= putMVar mvmtim . Just
return True
Nothing -> return False
{-# INLINEABLE oneShotStart #-}
-- | Attempts to start a timer.
-- The started timer will have the given delay and action associated and will be repeated timer.
--
-- If the timer was already initialized, it the previous timer will be stoped (the thread killed) and the timer will be started anew.
--
-- Returns True if the strat was successful,
-- otherwise (e.g. other thread is attempting to manipulate the timer) returns False.
repeatedStart :: MonadBaseControl IO m
=> Timer m
-> m () -- ^ The action the timer will start with.
-> Delay -- ^ The dealy the timer will start with.
-> m Bool
repeatedStart (Timer mvmtim) a d = do
mtim <- tryTakeMVar mvmtim
case mtim of
Just (Just (TimerImmutable _ _ tid)) -> do
killThread tid
repeatedTimerImmutable a d >>= putMVar mvmtim . Just
return True
Just (Nothing) -> do
repeatedTimerImmutable a d >>= putMVar mvmtim . Just
return True
Nothing -> return False
{-# INLINEABLE repeatedStart #-}
-- | Attempts to restart already initialized timer.
-- The restarted timer will have the same delay and action associated and will be one-shot timer.
--
-- Returns True if the restrat was successful,
-- otherwise (e.g. other thread is attempting to manipulate the timer or the timer was not initialized) returns False.
oneShotRestart :: MonadBaseControl IO m
=> Timer m
-> m Bool
oneShotRestart (Timer mvmtim) = do
mtim <- tryTakeMVar mvmtim
case mtim of
Just (Just (TimerImmutable a d tid)) -> do
killThread tid
oneShotTimerImmutable a d >>= putMVar mvmtim . Just
return True
_ -> return False
{-# INLINEABLE oneShotRestart #-}
-- | Attempts to restart already initialized timer.
-- The restarted timer will have the same delay and action associated and will be one-shot timer.
--
-- Returns True if the restrat was successful,
-- otherwise (e.g. other thread is attempting to manipulate the timer or the timer was not initialized) returns False.
repeatedRestart :: MonadBaseControl IO m
=> Timer m
-> m Bool
repeatedRestart (Timer mvmtim) = do
mtim <- tryTakeMVar mvmtim
case mtim of
Just (Just (TimerImmutable a d tid)) -> do
killThread tid
repeatedTimerImmutable a d >>= putMVar mvmtim . Just
return True
_ -> return False
{-# INLINEABLE repeatedRestart #-}
-- | Executes the the given action once after the given delay elapsed, no sooner, maybe later.
oneShotTimer :: MonadBaseControl IO m
=> m () -- ^ The action to be executed.
-> Delay -- ^ The (minimal) time until the execution in microseconds.
-> m (Timer m)
oneShotTimer a d = Timer <$> (oneShotTimerImmutable a d >>= newMVar . Just)
{-# INLINE oneShotTimer #-}
-- | Executes the the given action repeatedly with at least the given delay between executions.
repeatedTimer :: MonadBaseControl IO m
=> m () -- ^ The action to be executed.
-> Delay -- ^ The (minimal) delay between executions.
-> m (Timer m)
repeatedTimer a d = Timer <$> (repeatedTimerImmutable a d >>= newMVar . Just)
{-# INLINE repeatedTimer #-}
-- | This function is blocking. It waits until it can stop the timer
-- (until there is a value in the MVar), then it kills the timer's thread.
--
-- After this action completes, the Timer is not innitialized anymore (the MVar contains Nothing).
stopTimer :: MonadBaseControl IO m
=> Timer m
-> m ()
stopTimer (Timer mvmtim) = modifyMVar_ mvmtim $
maybe (return Nothing)
(\(TimerImmutable _ _ tid) -> killThread tid >> return Nothing)
{-# INLINE stopTimer #-}
-- | Creates a new timer. This does not start the timer.
newTimer :: MonadBase IO m
=> m (Timer m)
newTimer = Timer <$> newMVar Nothing
{-# INLINE newTimer #-}
------------------------------------------------------------------------------
-- | Utility
type TimerIO = Timer IO
-- | Forks a new thread that runs the supplied action
-- (at least) after the given delay and stores the action,
-- delay and thread id in the immutable TimerImmutable value.
oneShotTimerImmutable :: MonadBaseControl IO m
=> m () -- ^ The action to be executed.
-> Delay -- ^ The (minimal) time until the execution in microseconds.
-> m (TimerImmutable m)
oneShotTimerImmutable a d = TimerImmutable a d <$> oneShotAction a d
{-# INLINE oneShotTimerImmutable #-}
-- | Forks a new thread that repeats the supplied action
-- with (at least) the given delay between each execution and stores the action,
-- delay and thread id in the immutable TimerImmutable value.
repeatedTimerImmutable :: MonadBaseControl IO m
=> m () -- ^ The action to be executed.
-> Delay -- ^ The (minimal) time until the execution in microseconds.
-> m (TimerImmutable m)
repeatedTimerImmutable a d = TimerImmutable a d <$> repeatedAction a d
{-# INLINE repeatedTimerImmutable #-}
-- | Forks a new thread that runs the supplied action
-- (at least) after the given delay.
oneShotAction :: MonadBaseControl IO m
=> m ()
-> Delay
-> m ThreadId
oneShotAction action delay = fork (suspend delay >> action)
{-# INLINE oneShotAction #-}
-- | Forks a new thread that repeats the supplied action
-- with (at least) the given delay between each execution.
repeatedAction :: MonadBaseControl IO m
=> m ()
-> Delay
-> m ThreadId
repeatedAction action delay = fork (forever $ suspend delay >> action)
{-# INLINE repeatedAction #-}