nationstates-0.2.0.2: NationStates/RateLimit.hs
-- | Simple rate limiting combinator.
module NationStates.RateLimit (
RateLimit(),
newRateLimit,
rateLimit,
) where
import Control.Concurrent
import Control.Exception
import System.Clock
data RateLimit = RateLimit {
rateLock :: !(MVar TimeSpec),
rateDelay :: !TimeSpec
}
-- | Create a new rate limiter with the specified delay.
--
-- The rate limiter is thread-safe, and can be shared between threads.
newRateLimit
:: Rational
-- ^ Delay, in seconds
-> IO RateLimit
newRateLimit delay' = do
lock <- newMVar $! negate delay
return RateLimit {
rateLock = lock,
rateDelay = delay
}
where
delay = fromInteger . ceiling $ delay' * 1000 * 1000 * 1000
-- | Run the given action, pausing as necessary to keep under the rate limit.
rateLimit :: RateLimit -> IO a -> IO a
rateLimit RateLimit { rateLock = lock, rateDelay = delay } action =
bracketOnError
(takeMVar lock)
(tryPutMVar lock)
(\prev -> do
now <- getTime Monotonic
threadDelay' $ prev + delay - now
action `finally` (putMVar lock =<< getTime Monotonic))
threadDelay' :: TimeSpec -> IO ()
threadDelay' t = threadDelay . fromInteger $ timeSpecAsNanoSecs t `div` 1000