atomic-counter-0.1.2: src/Control/Concurrent/Counter/Lifted/ST.hs
----------------------------------------------------------------------------
-- |
-- Module : Control.Concurrent.Counter.Lifted.ST
-- Copyright : (c) Sergey Vinokurov 2022
-- License : Apache-2.0 (see LICENSE)
-- Maintainer : serg.foo@gmail.com
--
-- Counters that support some atomic operations. Safe to use from
-- multiple threads and likely faster than using IORef or TVar for the
-- same operation (terms and conditions apply).
----------------------------------------------------------------------------
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
module Control.Concurrent.Counter.Lifted.ST
( Counter
-- * Create
, new
-- * Read/write
, get
, set
, cas
-- * Arithmetic operations
, add
, sub
-- * Bitwise operations
, and
, or
, xor
, nand
) where
import Prelude hiding (and, or)
import GHC.Exts (Int(..), Int#, State#)
import GHC.ST
import Control.Concurrent.Counter.Unlifted qualified as Unlifted
-- | Memory location that supports select few atomic operations.
--
-- Isomorphic to @STRef s Int@.
data Counter s = Counter (Unlifted.Counter s)
-- | Pointer equality
instance Eq (Counter s) where
Counter x == Counter y = Unlifted.sameCounter x y
{-# INLINE new #-}
-- | Create new counter with initial value.
new :: Int -> ST s (Counter s)
new (I# initVal) = ST $ \s1 -> case Unlifted.new initVal s1 of
(# s2, c #) -> (# s2, Counter c #)
{-# INLINE get #-}
-- | Atomically read the counter's value.
get
:: Counter s
-> ST s Int
get (Counter c) = ST $ \s1 -> case Unlifted.get c s1 of
(# s2, x #) -> (# s2, I# x #)
{-# INLINE set #-}
-- | Atomically assign new value to the counter.
set
:: Counter s
-> Int
-> ST s ()
set (Counter c) (I# x) = ST $ \s1 -> case Unlifted.set c x s1 of
(# s2 #) -> (# s2, () #)
{-# INLINE cas #-}
-- | Atomic compare and swap, i.e. write the new value if the current
-- value matches the provided old value. Returns the value of the
-- element before the operation
cas
:: Counter s
-> Int -- ^ Expected old value
-> Int -- ^ New value
-> ST s Int
cas (Counter c) (I# x) (I# y) = ST $ \s1 -> case Unlifted.cas c x y s1 of
(# s2, z #) -> (# s2, I# z #)
{-# INLINE add #-}
-- | Atomically add an amount to the counter and return its old value.
add :: Counter s -> Int -> ST s Int
add = toST Unlifted.add
{-# INLINE sub #-}
-- | Atomically subtract an amount from the counter and return its old value.
sub :: Counter s -> Int -> ST s Int
sub = toST Unlifted.sub
{-# INLINE and #-}
-- | Atomically combine old value with a new one via bitwise and. Returns old counter value.
and :: Counter s -> Int -> ST s Int
and = toST Unlifted.and
{-# INLINE or #-}
-- | Atomically combine old value with a new one via bitwise or. Returns old counter value.
or :: Counter s -> Int -> ST s Int
or = toST Unlifted.or
{-# INLINE xor #-}
-- | Atomically combine old value with a new one via bitwise xor. Returns old counter value.
xor :: Counter s -> Int -> ST s Int
xor = toST Unlifted.xor
{-# INLINE nand #-}
-- | Atomically combine old value with a new one via bitwise nand. Returns old counter value.
nand :: Counter s -> Int -> ST s Int
nand = toST Unlifted.nand
{-# INLINE toST #-}
toST
:: (Unlifted.Counter s -> Int# -> State# s -> (# State# s, Int# #))
-> Counter s -> Int -> ST s Int
toST f = \(Counter c) (I# x) -> ST $ \s1 -> case f c x s1 of
(# s2, old #) -> (# s2, I# old #)