{-# LANGUAGE DeriveGeneric #-}

-- |
-- A utility module which lets you put a concurrency limit to an IO action.
module Control.Concurrent.Throttled
  ( Throttle
  , newThrottle
  , throttled
  )  where

--------------------------------------------------------------------------------
import Control.Monad (when)
import Control.Exception (finally)
import Control.Concurrent.STM
--------------------------------------------------------------------------------

newtype Throttle
  = Throttle (TVar Int)

newThrottle :: Int -> IO Throttle
newThrottle lim =
  Throttle <$> newTVarIO (max lim 1)

throttled :: Throttle -> IO a -> IO a
throttled (Throttle tv) act = do
  atomically $ do
    r <- readTVar tv
    when (r == 0) retry
    writeTVar tv (r - 1)
  finally act $
    atomically $ modifyTVar tv (+ 1)