pvar-1.0.0.0: src/Data/Primitive/PVar.hs
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ >= 800
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
#endif
-- |
-- Module : Data.Primitive.PVar
-- Copyright : (c) Alexey Kuleshevich 2020
-- License : BSD3
-- Maintainer : Alexey Kuleshevich <lehins@yandex.ru>
-- Stability : experimental
-- Portability : non-portable
--
module Data.Primitive.PVar
( -- | `PVar` has significantly better performance characteristics over
-- `Data.IORef.IORef`, `Data.STRef.STRef` and `Data.Primitive.MutVar.MutVar`. This is
-- because value is mutated directly in memory instead of following an extra
-- pointer. Besides better performance there is another consequence of direct
-- mutation, namely the value is always evaluated to normal form when being written
-- into a `PVar`
PVar
, RW
-- * Creation
, newPVar
, withPVarST
-- * Mutable Operations
, readPVar
, writePVar
, modifyPVar
, modifyPVar_
, fetchModifyPVar
, modifyFetchPVar
, modifyPVarM
, modifyPVarM_
, fetchModifyPVarM
, modifyFetchPVarM
, swapPVars_
, swapPVars
, copyPVar
, sizeOfPVar
, alignmentPVar
-- * Pinned memory
--
-- $pinned
, newPinnedPVar
, newAlignedPinnedPVar
, withPtrPVar
, withStorablePVar
, withAlignedStorablePVar
, copyPVarToPtr
, toForeignPtrPVar
, isPinnedPVar
, peekPrim
, pokePrim
-- -- * Numeric infix operations
-- , (=+)
-- , (=-)
-- , (=*)
-- , (=/)
-- , (=%)
-- * Atomic operations
, atomicModifyIntPVar
, atomicModifyIntPVar_
, atomicFetchModifyIntPVar
, atomicModifyFetchIntPVar
, atomicReadIntPVar
, atomicWriteIntPVar
, casIntPVar
, atomicAddIntPVar
, atomicSubIntPVar
, atomicAndIntPVar
, atomicNandIntPVar
, atomicOrIntPVar
, atomicXorIntPVar
, atomicNotIntPVar
-- * Re-exports
, Prim
, MonadPrim
, PrimMonad(PrimState)
, RealWorld
, sizeOf
, alignment
, ST
, runST
, S.Storable(peek, poke)
) where
import Control.Monad (void)
import Control.Monad.Primitive (MonadPrim, PrimMonad(primitive), PrimState, primitive_,
touch)
import Control.Monad.ST (ST, runST)
import Data.Primitive.PVar.Internal
import Data.Primitive.PVar.Unsafe
import Data.Primitive.Types
import qualified Foreign.Storable as S
import GHC.Exts
import GHC.ForeignPtr
-- $pinned
-- In theory it is unsafe to mix `S.Storable` and `Prim` operations on the same chunk of
-- memory, because some instances can have different memory layouts for the same
-- type. This is highly uncommon in practice and if you are intermixing the two concepts
-- together you probably already know what you are doing.
-- | Synonym for `RealWorld`
type RW = RealWorld
-- | Run an `ST` action on a mutable variable.
--
-- @since 0.1.0
withPVarST ::
Prim p
=> p -- ^ Initial value assigned to the mutable variable
-> (forall s. PVar p s -> ST s a) -- ^ Action to run
-> a -- ^ Result produced by the `ST` action
withPVarST x st = runST (newPVar x >>= st)
{-# INLINE withPVarST #-}
-- | Apply an action to the `Ptr` that references the mutable variable, but only if it is
-- backed by pinned memory, cause otherwise it would be unsafe.
--
-- @since 0.1.0
withPtrPVar :: (MonadPrim s m, Prim a) => PVar a n -> (Ptr a -> m b) -> m (Maybe b)
withPtrPVar pvar f =
case toPtrPVar pvar of
Nothing -> return Nothing
Just ptr -> do
r <- f ptr
touch pvar
return $ Just r
{-# INLINE withPtrPVar #-}
-- | Convert `PVar` into a `ForeignPtr`, but only if it is backed by pinned memory.
--
-- @since 0.1.0
toForeignPtrPVar :: PVar a s -> Maybe (ForeignPtr a)
toForeignPtrPVar pvar
| isPinnedPVar pvar = Just $ unsafeToForeignPtrPVar pvar
| otherwise = Nothing
{-# INLINE toForeignPtrPVar #-}
-- | Copy contents of one mutable variable `PVar` into another
--
-- @since 0.1.0
copyPVar ::
(MonadPrim s m, Prim a)
=> PVar a s -- ^ Source variable
-> PVar a s -- ^ Destination variable
-> m ()
copyPVar pvar@(PVar mbas#) (PVar mbad#) =
primitive_ (copyMutableByteArray# mbas# 0# mbad# 0# (sizeOfPVar# pvar))
{-# INLINE copyPVar #-}
-- | Copy contents of a mutable variable `PVar` into a pointer `Ptr`
--
-- @since 0.1.0
copyPVarToPtr :: (MonadPrim s m, Prim a) => PVar a s -> Ptr a -> m ()
copyPVarToPtr pvar@(PVar mbas#) (Ptr addr#) =
primitive_ (copyMutableByteArrayToAddr# mbas# 0# addr# (sizeOfPVar# pvar))
{-# INLINE copyPVarToPtr #-}
-- | Apply a pure function to the contents of a mutable variable. Returns the artifact of
-- computation.
--
-- @since 0.2.0
modifyPVar :: (MonadPrim s m, Prim a) => PVar a s -> (a -> (a, b)) -> m b
modifyPVar pvar f = modifyPVarM pvar (return . f)
{-# INLINE modifyPVar #-}
-- | Apply a pure function to the contents of a mutable variable.
--
-- @since 0.1.0
modifyPVar_ :: (MonadPrim s m, Prim a) => PVar a s -> (a -> a) -> m ()
modifyPVar_ pvar f = modifyPVarM_ pvar (return . f)
{-# INLINE modifyPVar_ #-}
-- | Apply a pure function to the contents of a mutable variable. Returns the old value.
--
-- @since 0.2.0
fetchModifyPVar :: (MonadPrim s m, Prim a) => PVar a s -> (a -> a) -> m a
fetchModifyPVar pvar f = fetchModifyPVarM pvar (return . f)
{-# INLINE fetchModifyPVar #-}
-- | Apply a pure function to the contents of a mutable variable. Returns the new value.
--
-- @since 0.2.0
modifyFetchPVar :: (MonadPrim s m, Prim a) => PVar a s -> (a -> a) -> m a
modifyFetchPVar pvar f = modifyFetchPVarM pvar (return . f)
{-# INLINE modifyFetchPVar #-}
-- | Apply a monadic action to the contents of a mutable variable. Returns the artifact of
-- computation.
--
-- @since 0.2.0
modifyPVarM :: (MonadPrim s m, Prim a) => PVar a s -> (a -> m (a, b)) -> m b
modifyPVarM pvar f = do
a <- readPVar pvar
(a', b) <- f a
b <$ writePVar pvar a'
{-# INLINE modifyPVarM #-}
-- | Apply a monadic action to the contents of a mutable variable. Returns the old value.
--
-- @since 0.2.0
fetchModifyPVarM :: (MonadPrim s m, Prim a) => PVar a s -> (a -> m a) -> m a
fetchModifyPVarM pvar f = do
a <- readPVar pvar
a <$ (writePVar pvar =<< f a)
{-# INLINE fetchModifyPVarM #-}
-- | Apply a monadic action to the contents of a mutable variable. Returns the new value.
--
-- @since 0.2.0
modifyFetchPVarM :: (MonadPrim s m, Prim a) => PVar a s -> (a -> m a) -> m a
modifyFetchPVarM pvar f = do
a <- readPVar pvar
a' <- f a
a' <$ writePVar pvar a'
{-# INLINE modifyFetchPVarM #-}
-- | Apply a monadic action to the contents of a mutable variable.
--
-- @since 0.1.0
modifyPVarM_ :: (MonadPrim s m, Prim a) => PVar a s -> (a -> m a) -> m ()
modifyPVarM_ pvar f = readPVar pvar >>= f >>= writePVar pvar
{-# INLINE modifyPVarM_ #-}
-- | Swap contents of two mutable variables. Returns their old values.
--
-- @since 0.1.0
swapPVars :: (MonadPrim s m, Prim a) => PVar a s -> PVar a s -> m (a, a)
swapPVars pvar1 pvar2 = do
a1 <- readPVar pvar1
a2 <- fetchModifyPVar pvar2 (const a1)
(a1, a2) <$ writePVar pvar1 a2
{-# INLINE swapPVars #-}
-- | Swap contents of two mutable variables.
--
-- @since 0.1.0
swapPVars_ :: (MonadPrim s m, Prim a) => PVar a s -> PVar a s -> m ()
swapPVars_ pvar1 pvar2 = void $ swapPVars pvar1 pvar2
{-# INLINE swapPVars_ #-}
-- TODO: Come up with a concrete interface for numerics
-- (=+) :: (MonadPrim s m, Prim a, Num a) => PVar (PrimState m) a -> a -> m ()
-- (=+) pvar a = modifyPVar_ pvar (+ a)
-- {-# INLINE (=+) #-}
-- (=-) :: (MonadPrim s m, Prim a, Num a) => PVar (PrimState m) a -> a -> m ()
-- (=-) pvar a = modifyPVar_ pvar (subtract a)
-- {-# INLINE (=-) #-}
-- (=*) :: (MonadPrim s m, Prim a, Num a) => PVar (PrimState m) a -> a -> m ()
-- (=*) pvar a = modifyPVar_ pvar (* a)
-- {-# INLINE (=*) #-}
-- (=/) :: (MonadPrim s m, Prim a, Fractional a) => PVar (PrimState m) a -> a -> m ()
-- (=/) pvar a = modifyPVar_ pvar (/ a)
-- {-# INLINE (=/) #-}
-- -- | C like modulo operator
-- (=%) :: (MonadPrim s m, Prim a, Integral a) => PVar (PrimState m) a -> a -> m ()
-- (=%) pvar a = modifyPVar_ pvar (`mod` a)
-- {-# INLINE (=%) #-}
-- | Apply an action to the newly allocated `PVar` and to the `Ptr` that references
-- it. Memory allocated with number of bytes specified by @`S.sizeOf` a@ is allocated and
-- pinned, therefore it is safe to operate directly with the pointer as well as over
-- FFI. Returning the pointer from the supplied action would be very unsafe, therefore
-- return the `PVar` if you still need it afterwards, garbage collector will cleanup the
-- memory when it is no longer needed.
--
-- @since 0.1.0
withStorablePVar ::
(MonadPrim s m, S.Storable a)
=> a -- ^ Initial value
-> (PVar a s -> Ptr a -> m b) -- ^ Action to run
-> m b
withStorablePVar a f = do
pvar <- rawStorablePVar
runWithPokedPtr pvar a f
{-# INLINE withStorablePVar #-}
-- | Same `withStorablePVar`, except memory is aligned according to `S.alignment`.
--
-- @since 0.1.0
withAlignedStorablePVar ::
(MonadPrim s m, S.Storable a)
=> a -- ^ Initial value
-> (PVar a s -> Ptr a -> m b) -- ^ Action to run
-> m b
withAlignedStorablePVar a f = do
pvar <- rawAlignedStorablePVar
runWithPokedPtr pvar a f
{-# INLINE withAlignedStorablePVar #-}
-- | Read a value from `PVar` atomically. Implies a full memory barrier.
--
-- @since 0.1.0
atomicReadIntPVar :: MonadPrim s m => PVar Int s -> m Int
atomicReadIntPVar (PVar mba#) =
primitive $ \s# ->
case atomicReadIntArray# mba# 0# s# of
(# s'#, i# #) -> (# s'#, I# i# #)
{-# INLINE atomicReadIntPVar #-}
-- | Write a value into an `PVar` atomically. Implies a full memory barrier.
--
-- @since 0.1.0
atomicWriteIntPVar :: MonadPrim s m => PVar Int s -> Int -> m ()
atomicWriteIntPVar (PVar mba#) a = primitive_ (atomicWriteIntArray# mba# 0# (unI# a))
{-# INLINE atomicWriteIntPVar #-}
-- | Apply a function to an integer element of a `PVar` atomically. Implies a full memory
-- barrier. Returns the new value.
--
-- @since 0.2.0
atomicFetchModifyIntPVar ::
MonadPrim s m => PVar Int s -> (Int -> Int) -> m Int
atomicFetchModifyIntPVar pvar f =
atomicModifyIntPVar pvar $ \a ->
let a' = f a
in a' `seq` (a', a)
{-# INLINE atomicFetchModifyIntPVar #-}
-- | Apply a function to an integer element of a `PVar` atomically. Implies a full memory
-- barrier. Returns the new value.
--
-- @since 0.2.0
atomicModifyFetchIntPVar ::
MonadPrim s m => PVar Int s -> (Int -> Int) -> m Int
atomicModifyFetchIntPVar pvar f =
atomicModifyIntPVar pvar $ \a ->
let a' = f a
in a' `seq` (a', a')
{-# INLINE atomicModifyFetchIntPVar #-}
-- | Compare and swap. This is also a function that is used to implement
-- `atomicModifyIntPVar`. Implies a full memory barrier.
--
-- @since 0.1.0
casIntPVar ::
MonadPrim s m
=> PVar Int s -- ^ Variable to mutate
-> Int -- ^ Old expected value
-> Int -- ^ New value
-> m Int -- ^ Old actual value
casIntPVar (PVar mba#) old new =
primitive $ \s# ->
case casIntArray# mba# 0# (unI# old) (unI# new) s# of
(# s'#, i'# #) -> (# s'#, I# i'# #)
{-# INLINE casIntPVar #-}
-- | Add two numbers, corresponds to @(`+`)@ done atomically. Returns the previous value of
-- the mutable variable. Implies a full memory barrier.
--
-- @since 0.1.0
atomicAddIntPVar :: MonadPrim s m => PVar Int s -> Int -> m Int
atomicAddIntPVar (PVar mba#) a =
primitive $ \s# ->
case fetchAddIntArray# mba# 0# (unI# a) s# of
(# s'#, p# #) -> (# s'#, I# p# #)
{-# INLINE atomicAddIntPVar #-}
-- | Subtract two numbers, corresponds to @(`-`)@ done atomically. Returns the
-- previous value of the mutable variable. Implies a full memory barrier.
--
-- @since 0.1.0
atomicSubIntPVar :: MonadPrim s m => PVar Int s -> Int -> m Int
atomicSubIntPVar (PVar mba#) a =
primitive $ \s# ->
case fetchSubIntArray# mba# 0# (unI# a) s# of
(# s'#, p# #) -> (# s'#, I# p# #)
{-# INLINE atomicSubIntPVar #-}
-- | Binary conjuction (AND), corresponds to @(`Data.Bits..&.`)@ done atomically. Returns the previous
-- value of the mutable variable. Implies a full memory barrier.
--
-- @since 0.1.0
atomicAndIntPVar :: MonadPrim s m => PVar Int s -> Int -> m Int
atomicAndIntPVar (PVar mba#) a =
primitive $ \s# ->
case fetchAndIntArray# mba# 0# (unI# a) s# of
(# s'#, p# #) -> (# s'#, I# p# #)
{-# INLINE atomicAndIntPVar #-}
-- | Binary negation of conjuction (NAND), corresponds to @\\x y -> `Data.Bits.complement` (x
-- `Data.Bits..&.` y)@ done atomically. Returns the previous value of the mutable variable. Implies
-- a full memory barrier.
--
-- @since 0.1.0
atomicNandIntPVar :: MonadPrim s m => PVar Int s -> Int -> m Int
atomicNandIntPVar (PVar mba#) a =
primitive $ \s# ->
case fetchNandIntArray# mba# 0# (unI# a) s# of
(# s'#, p# #) -> (# s'#, I# p# #)
{-# INLINE atomicNandIntPVar #-}
-- | Binary disjunction (OR), corresponds to @(`Data.Bits..|.`)@ done atomically. Returns the previous
-- value of the mutable variable. Implies a full memory barrier.
--
-- @since 0.1.0
atomicOrIntPVar :: MonadPrim s m => PVar Int s -> Int -> m Int
atomicOrIntPVar (PVar mba#) a =
primitive $ \s# ->
case fetchOrIntArray# mba# 0# (unI# a) s# of
(# s'#, p# #) -> (# s'#, I# p# #)
{-# INLINE atomicOrIntPVar #-}
-- | Binary exclusive disjunction (XOR), corresponds to @`Data.Bits.xor`@ done atomically. Returns the
-- previous value of the mutable variable. Implies a full memory barrier.
--
-- @since 0.1.0
atomicXorIntPVar :: MonadPrim s m => PVar Int s -> Int -> m Int
atomicXorIntPVar (PVar mba#) a =
primitive $ \s# ->
case fetchXorIntArray# mba# 0# (unI# a) s# of
(# s'#, p# #) -> (# s'#, I# p# #)
{-# INLINE atomicXorIntPVar #-}
-- | Binary negation (NOT), corresponds to ones' @`Data.Bits.complement`@ done atomically. Returns the
-- previous value of the mutable variable. Implies a full memory barrier.
--
-- @since 0.1.0
atomicNotIntPVar :: MonadPrim s m => PVar Int s -> m Int
atomicNotIntPVar (PVar mba#) =
primitive $ \s# ->
case fetchXorIntArray# mba# 0# fullInt# s# of
(# s'#, p# #) -> (# s'#, I# p# #)
where
fullInt# =
case maxBound :: Word of
W# w# -> word2Int# w#
{-# INLINE atomicNotIntPVar #-}