{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE DeriveDataTypeable #-} -- | Module supporting adding timeouts to a given service. module Glue.Timeout( TimeoutOptions , TimeoutException(..) , defaultTimeoutOptions , addTimeout , timeoutDescription , timeoutLimitMs ) where import Data.Typeable import Glue.Types import Control.Concurrent.Lifted import Control.Exception.Lifted import Control.Monad.Trans.Control -- | Options for determining behaviour of services with a timeout. data TimeoutOptions = TimeoutOptions { timeoutDescription :: String -- ^ Description added to the 'TimeoutException' thrown when the timeout is exceeded. , timeoutLimitMs :: Int -- ^ Timeout in milliseconds. } -- | Default instance of 'TimeoutOptions' with a timeout of 30 seconds. defaultTimeoutOptions :: TimeoutOptions defaultTimeoutOptions = TimeoutOptions { timeoutDescription = "Service call timed out.", timeoutLimitMs = 30000 } -- | Exception thrown when the timeout is exceeded. data TimeoutException = TimeoutException String deriving (Eq, Show, Typeable) instance Exception TimeoutException -- | Function for producing services protected with a timeout. addTimeout :: (MonadBaseControl IO m) => TimeoutOptions -- ^ Options to configure the timeout. -> BasicService m a b -- ^ Service to protect with a timeout. -> BasicService m a b addTimeout options service = (\request -> do currentThreadId <- myThreadId timeoutThreadId <- fork $ do threadDelay (1000 * timeoutLimitMs options) throwTo currentThreadId (TimeoutException $ timeoutDescription options) finally (service request) (killThread timeoutThreadId))