-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Timeout #11
base: master
Are you sure you want to change the base?
WIP: Timeout #11
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
-- This module is intended for internal use only, and may change without warning | ||
-- in subsequent releases. | ||
{-# OPTIONS_HADDOCK not-home #-} | ||
{-# LANGUAGE DeriveAnyClass #-} | ||
module Data.Pool.Internal where | ||
|
||
import Control.Concurrent | ||
|
@@ -12,6 +13,7 @@ import Data.IORef | |
import Data.Primitive.SmallArray | ||
import GHC.Clock | ||
import qualified Data.List as L | ||
import System.Timeout (timeout) | ||
|
||
-- | Striped resource pool based on "Control.Concurrent.QSem". | ||
-- | ||
|
@@ -24,6 +26,9 @@ data Pool a = Pool | |
, reaperRef :: !(IORef ()) | ||
} | ||
|
||
getPoolTimeoutConfig :: Pool a -> Maybe TimeoutConfig | ||
getPoolTimeoutConfig = poolTimeoutConfig . poolConfig | ||
|
||
-- | A single, capability-local pool. | ||
data LocalPool a = LocalPool | ||
{ stripeId :: !Int | ||
|
@@ -71,8 +76,20 @@ data PoolConfig a = PoolConfig | |
-- capabilities and rounded up. Therefore the pool might end up creating up to | ||
-- @N - 1@ resources more in total than specified, where @N@ is the number of | ||
-- capabilities. | ||
, poolTimeoutConfig :: Maybe TimeoutConfig | ||
-- ^ Optional timeout for waiting for a resource | ||
} | ||
|
||
data TimeoutConfig = TimeoutConfig | ||
{ acquireResourceTimeout :: Int | ||
-- ^ Time to await, microseconds | ||
, timeoutLabel :: String | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't it be better to provide a value that should be thrown as exception here instead of a label? Then you can also get rid of the |
||
-- ^ Label for TimeoutException | ||
} | ||
|
||
newtype TimeoutException = TimeoutException String | ||
deriving (Show, Exception) | ||
|
||
-- | Create a new striped resource pool. | ||
-- | ||
-- The number of stripes is equal to the number of capabilities. | ||
|
@@ -87,6 +104,8 @@ newPool pc = do | |
error "poolCacheTTL must be at least 0.5" | ||
when (poolMaxResources pc < 1) $ do | ||
error "poolMaxResources must be at least 1" | ||
when (maybe False (< 0) (acquireResourceTimeout <$> poolTimeoutConfig pc)) $ do | ||
error "acquireResourceTimeout must be at least 0" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd argue it should be more than 0 :) |
||
numStripes <- getNumCapabilities | ||
when (numStripes < 1) $ do | ||
error "numStripes must be at least 1" | ||
|
@@ -175,8 +194,8 @@ getLocalPool pools = do | |
pure $ pools `indexSmallArray` (cid `rem` sizeofSmallArray pools) | ||
|
||
-- | Wait for the resource to be put into a given 'MVar'. | ||
waitForResource :: MVar (Stripe a) -> MVar (Maybe a) -> IO (Maybe a) | ||
waitForResource mstripe q = takeMVar q `onException` cleanup | ||
waitForResource :: Maybe TimeoutConfig -> MVar (Stripe a) -> MVar (Maybe a) -> IO (Maybe a) | ||
waitForResource timeoutConfig mstripe q = limitByTime (takeMVar q) `onException` cleanup | ||
where | ||
cleanup = uninterruptibleMask_ $ do -- Note [signal uninterruptible] | ||
stripe <- takeMVar mstripe | ||
|
@@ -192,6 +211,13 @@ waitForResource mstripe q = takeMVar q `onException` cleanup | |
putMVar q $ error "unreachable" | ||
pure stripe | ||
putMVar mstripe newStripe | ||
limitByTime = case timeoutConfig of | ||
Just cfg -> timeout (acquireResourceTimeout cfg) >=> throwOnTimeout cfg | ||
Nothing -> id | ||
throwOnTimeout cfg = | ||
\case Just a -> pure a | ||
Nothing -> throwIO $ TimeoutException (timeoutLabel cfg) | ||
|
||
|
||
-- | If an exception is received while a resource is being created, restore the | ||
-- original size of the stripe. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd argue we can do without this helper function.