module Irc.RateLimit
( RateLimit
, newRateLimit
, tickRateLimit
) where
import Control.Concurrent
import Control.Monad
import Data.Time
data RateLimit = RateLimit
{ RateLimit -> MVar UTCTime
rateStamp :: !(MVar UTCTime)
, RateLimit -> NominalDiffTime
rateThreshold :: !NominalDiffTime
, RateLimit -> NominalDiffTime
ratePenalty :: !NominalDiffTime
}
newRateLimit ::
Rational ->
Rational ->
IO RateLimit
newRateLimit :: Rational -> Rational -> IO RateLimit
newRateLimit Rational
penalty Rational
threshold =
do now <- IO UTCTime
getCurrentTime
ref <- newMVar now
return RateLimit
{ rateStamp = ref
, rateThreshold = realToFrac (max 0 threshold)
, ratePenalty = realToFrac (max 0 penalty)
}
tickRateLimit :: RateLimit -> IO ()
tickRateLimit :: RateLimit -> IO ()
tickRateLimit RateLimit
r = MVar UTCTime -> (UTCTime -> IO UTCTime) -> IO ()
forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ (RateLimit -> MVar UTCTime
rateStamp RateLimit
r) ((UTCTime -> IO UTCTime) -> IO ())
-> (UTCTime -> IO UTCTime) -> IO ()
forall a b. (a -> b) -> a -> b
$ \UTCTime
stamp ->
do now <- IO UTCTime
getCurrentTime
let stamp' = RateLimit -> NominalDiffTime
ratePenalty RateLimit
r NominalDiffTime -> UTCTime -> UTCTime
`addUTCTime` UTCTime -> UTCTime -> UTCTime
forall a. Ord a => a -> a -> a
max UTCTime
stamp UTCTime
now
diff = UTCTime -> UTCTime -> NominalDiffTime
diffUTCTime UTCTime
stamp' UTCTime
now
excess = NominalDiffTime
diff NominalDiffTime -> NominalDiffTime -> NominalDiffTime
forall a. Num a => a -> a -> a
- RateLimit -> NominalDiffTime
rateThreshold RateLimit
r
when (excess > 0) (threadDelay (ceiling (1000000 * excess)))
return stamp'