packages feed

unliftio-messagebox-1.0.0: src/UnliftIO/MessageBox/Util/Fresh.hs

-- | Threadsafe, shared, atomic counters
--
-- This is based on "Data.Atomics.Counter".
module UnliftIO.MessageBox.Util.Fresh
  ( fresh,
    incrementAndGet,
    newCounterVar,
    HasCounterVar (getCounterVar),
    CounterVar (),
  )
where

import Control.Monad.Reader (MonadReader, asks)
import Data.Atomics.Counter
  ( AtomicCounter,
    incrCounter,
    newCounter,
  )
import Data.Coerce (Coercible, coerce)
import UnliftIO (MonadIO (..))

-- | A threadsafe atomic a

-- | Atomically increment and get the value of the 'Counter'
-- for type @a@ that must be present in the @env@.
{-# INLINE fresh #-}
fresh ::
  forall a env m.
  ( MonadReader env m,
    MonadIO m,
    HasCounterVar a env,
    Coercible a Int
  ) =>
  m a
fresh =
  asks (getCounterVar @a) >>= incrementAndGet

-- | Atomically increment and get the value of the 'Counter'
-- for type @a@ that must be present in the @env@.
{-# INLINE incrementAndGet #-}
incrementAndGet ::
  forall a m.
  ( MonadIO m,
    Coercible a Int
  ) =>
  CounterVar a -> m a
incrementAndGet (MkCounterVar !atomicCounter) =
  coerce <$> liftIO (incrCounter 1 atomicCounter)


-- | Create a new 'CounterVar' starting at @0@.
{-# INLINE newCounterVar #-}
newCounterVar ::
  forall a m.
  MonadIO m =>
  m (CounterVar a)
newCounterVar =
  MkCounterVar <$> liftIO (newCounter 0)

-- | An 'AtomicCounter'.
newtype CounterVar a = MkCounterVar AtomicCounter

-- | A type class for @MonadReader@ based
-- applications.
class HasCounterVar a env | env -> a where
  getCounterVar :: env -> CounterVar a

instance HasCounterVar t (CounterVar t) where
  getCounterVar = id