{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} module Control.Concurrent.Async.Timer.Internal ( Timer(..) , TimerConf(..) , TimerException(..) , defaultTimerConf , timerThread , timerConfSetInitDelay , timerConfSetInterval , timerWait ) where import Control.Concurrent.Lifted import Control.Exception.Safe import Control.Monad import Control.Monad.Base import Control.Monad.Trans.Control -- | Timer specific exception; only used for a graceful termination -- mechanism for timer threads. data TimerException = TimerEnd deriving (Typeable, Show) instance Exception TimerException -- | This is the type of timer handle, which will be provided to the -- IO action to be executed within 'withAsyncTimer'. The user can use -- 'timerWait' on this timer to delay execution until the next timer -- synchronization event. newtype Timer = Timer { timerMVar :: MVar () } -- | Type of a timer configuration. data TimerConf = TimerConf { _timerConfInitDelay :: Int , _timerConfInterval :: Int } -- | This exception handler acts on exceptions of type -- 'TimerException'. What it essentially does is providing a mechanism -- for graceful termination of timer threads by simply ignoring the -- TimerEnd exception. timerHandler :: Monad m => Handler m () timerHandler = Handler $ \case TimerEnd -> return () -- | Sleep 'dt' milliseconds. millisleep :: MonadBase IO m => Int -> m () millisleep dt = threadDelay (fromIntegral dt * 10 ^ 3) -- | Default timer configuration specifies no initial delay and an -- interval delay of 1s. defaultTimerConf :: TimerConf defaultTimerConf = TimerConf { _timerConfInitDelay = 0 , _timerConfInterval = 1000 } -- | Set the initial delay in the provided timer configuration. timerConfSetInitDelay :: Int -> TimerConf -> TimerConf timerConfSetInitDelay n conf = conf { _timerConfInitDelay = n } -- | Set the interval delay in the provided timer configuration. timerConfSetInterval :: Int -> TimerConf -> TimerConf timerConfSetInterval n conf = conf { _timerConfInterval = n } -- | IO action to be executed within in a timer thread. timerThread :: (MonadBaseControl IO m, MonadCatch m) => Int -> Int -> MVar () -> m () timerThread initDelay intervalDelay syncMVar = catches (timerLoop initDelay intervalDelay syncMVar) [timerHandler] -- | Timer loop to be executed within in a timer thread. timerLoop :: (MonadBaseControl IO m) => Int -> Int -> MVar () -> m () timerLoop initDelay intervalDelay syncMVar = do millisleep initDelay forever $ putMVar syncMVar () >> millisleep intervalDelay -- | Wait for the next synchronization event on the givem timer. timerWait :: MonadBaseControl IO m => Timer -> m () timerWait = void . takeMVar . timerMVar