{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} -- | Allocate resources from a pool, guaranteeing resource handling via the -- ResourceT transformer. module Data.Conduit.Pool ( ManagedResource (..) , takeResource , takeResourceCheck , P.Pool , P.createPool , P.withResource , withResourceTimeout , withResourceT ) where import qualified Data.Pool as P import Control.Monad (liftM) import Control.Monad.Trans.Resource import Control.Monad.IO.Class (liftIO) import qualified Data.IORef as I import Control.Exception (onException, mask) import System.Timeout (timeout) import Control.Monad.Trans.Control (control) -- | The result of taking a resource. data ManagedResource m a = ManagedResource { mrValue :: a -- ^ The actual resource. , mrReuse :: Bool -> m () -- ^ Let's you specify whether the resource should be returned to the pool -- (via 'P.putResource') or destroyed (via 'P.destroyResource') on release. -- This defaults to destruction, in case of exceptions. , mrRelease :: m () -- ^ Release this resource, either destroying it or returning it to the -- pool. } -- | Like 'P.withResource', but uses 'MonadResource' instead of 'MonadBaseControl'. -- -- Since 0.1.1 withResourceT :: MonadResource m => P.Pool a -> (a -> m b) -> m b withResourceT pool f = do mr <- takeResource pool b <- f $ mrValue mr mrReuse mr True mrRelease mr return b -- | Like 'P.withResource', but times out the operation if resource -- allocation does not complete within the given timeout period. -- -- Since 0.1.2 withResourceTimeout :: #if MIN_VERSION_monad_control(0,3,0) (MonadBaseControl IO m) #else (MonadControlIO m) #endif => Int -- ^ Timeout period in microseconds -> P.Pool a -> (a -> m b) -> m (Maybe b) {-# SPECIALIZE withResourceTimeout :: Int -> P.Pool a -> (a -> IO b) -> IO (Maybe b) #-} withResourceTimeout ms pool act = control $ \runInIO -> mask $ \restore -> do mres <- timeout ms $ P.takeResource pool case mres of Nothing -> runInIO $ return Nothing Just (resource, local) -> do ret <- restore (runInIO (liftM Just $ act resource)) `onException` P.destroyResource pool local resource P.putResource local resource return ret #if __GLASGOW_HASKELL__ >= 700 {-# INLINABLE withResourceTimeout #-} #endif -- | Take a resource from the pool and register a release action. takeResource :: MonadResource m => P.Pool a -> m (ManagedResource m a) takeResource pool = do onRelRef <- liftIO $ I.newIORef False (relKey, (a, _)) <- allocate (P.takeResource pool) (\(a, local) -> do onRel <- I.readIORef onRelRef if onRel then P.putResource local a else P.destroyResource pool local a) return ManagedResource { mrValue = a , mrReuse = liftIO . I.writeIORef onRelRef , mrRelease = release relKey } -- | Same as 'takeResource', but apply some action to check if a resource is -- still valid. takeResourceCheck :: MonadResource m => P.Pool a -> (a -> m Bool) -> m (ManagedResource m a) takeResourceCheck pool check = do mr <- takeResource pool isValid <- check $ mrValue mr if isValid then return mr else do mrRelease mr takeResourceCheck pool check