resource-pool-0.4.0.0: src/Data/Pool/Introspection.hs
-- | A variant of "Data.Pool" with introspection capabilities.
module Data.Pool.Introspection
( -- * Pool
Pool
, LocalPool
, newPool
-- ** Configuration
, PoolConfig
, defaultPoolConfig
, setNumStripes
-- * Resource management
, Resource(..)
, Acquisition(..)
, withResource
, takeResource
, tryWithResource
, tryTakeResource
, putResource
, destroyResource
, destroyAllResources
) where
import Control.Concurrent
import Control.Exception
import GHC.Clock
import GHC.Generics (Generic)
import Data.Pool.Internal
-- | A resource taken from the pool along with additional information.
data Resource a = Resource
{ resource :: a
, stripeNumber :: !Int
, availableResources :: !Int
, acquisition :: !Acquisition
, acquisitionTime :: !Double
, creationTime :: !(Maybe Double)
} deriving (Eq, Show, Generic)
-- | Describes how a resource was acquired from the pool.
data Acquisition
= Immediate
-- ^ A resource was taken from the pool immediately.
| Delayed
-- ^ The thread had to wait until a resource was released.
deriving (Eq, Show, Generic)
-- | 'Data.Pool.withResource' with introspection capabilities.
withResource :: Pool a -> (Resource a -> IO r) -> IO r
withResource pool act = mask $ \unmask -> do
(res, localPool) <- takeResource pool
r <- unmask (act res) `onException` destroyResource pool localPool (resource res)
putResource localPool (resource res)
pure r
-- | 'Data.Pool.takeResource' with introspection capabilities.
takeResource :: Pool a -> IO (Resource a, LocalPool a)
takeResource pool = mask_ $ do
t1 <- getMonotonicTime
lp <- getLocalPool (localPools pool)
stripe <- takeMVar (stripeVar lp)
if available stripe == 0
then do
q <- newEmptyMVar
putMVar (stripeVar lp) $! stripe { queueR = Queue q (queueR stripe) }
waitForResource (stripeVar lp) q >>= \case
Just a -> do
t2 <- getMonotonicTime
let res = Resource
{ resource = a
, stripeNumber = stripeId lp
, availableResources = 0
, acquisition = Delayed
, acquisitionTime = t2 - t1
, creationTime = Nothing
}
pure (res, lp)
Nothing -> do
t2 <- getMonotonicTime
a <- createResource (poolConfig pool) `onException` restoreSize (stripeVar lp)
t3 <- getMonotonicTime
let res = Resource
{ resource = a
, stripeNumber = stripeId lp
, availableResources = 0
, acquisition = Delayed
, acquisitionTime = t2 - t1
, creationTime = Just $! t3 - t2
}
pure (res, lp)
else takeAvailableResource pool t1 lp stripe
-- | A variant of 'withResource' that doesn't execute the action and returns
-- 'Nothing' instead of blocking if the local pool is exhausted.
tryWithResource :: Pool a -> (Resource a -> IO r) -> IO (Maybe r)
tryWithResource pool act = mask $ \unmask -> tryTakeResource pool >>= \case
Just (res, localPool) -> do
r <- unmask (act res) `onException` destroyResource pool localPool (resource res)
putResource localPool (resource res)
pure (Just r)
Nothing -> pure Nothing
-- | A variant of 'takeResource' that returns 'Nothing' instead of blocking if
-- the local pool is exhausted.
tryTakeResource :: Pool a -> IO (Maybe (Resource a, LocalPool a))
tryTakeResource pool = mask_ $ do
t1 <- getMonotonicTime
lp <- getLocalPool (localPools pool)
stripe <- takeMVar (stripeVar lp)
if available stripe == 0
then do
putMVar (stripeVar lp) stripe
pure Nothing
else Just <$> takeAvailableResource pool t1 lp stripe
----------------------------------------
-- Helpers
takeAvailableResource
:: Pool a
-> Double
-> LocalPool a
-> Stripe a
-> IO (Resource a, LocalPool a)
takeAvailableResource pool t1 lp stripe = case cache stripe of
[] -> do
let newAvailable = available stripe - 1
putMVar (stripeVar lp) $! stripe { available = newAvailable }
t2 <- getMonotonicTime
a <- createResource (poolConfig pool) `onException` restoreSize (stripeVar lp)
t3 <- getMonotonicTime
let res = Resource
{ resource = a
, stripeNumber = stripeId lp
, availableResources = newAvailable
, acquisition = Immediate
, acquisitionTime = t2 - t1
, creationTime = Just $! t3 - t2
}
pure (res, lp)
Entry a _ : as -> do
let newAvailable = available stripe - 1
putMVar (stripeVar lp) $! stripe { available = newAvailable, cache = as }
t2 <- getMonotonicTime
let res = Resource
{ resource = a
, stripeNumber = stripeId lp
, availableResources = newAvailable
, acquisition = Immediate
, acquisitionTime = t2 - t1
, creationTime = Nothing
}
pure (res, lp)