packages feed

primitive-primvar-0.0.0.0: src/Data/Primitive/PrimVar.hs

-- SPDX-License-Identifier: CC0-1.0
{-
Each contributor licenses you to do everything with this work that
would otherwise infringe any patent claims they can license or become
able to license.
-}
{-|
`PrimVar` is internally a `PrimArray` of one element,
but with a convenient `MutVar`-like API.
-}
module Data.Primitive.PrimVar
  ( -- * Types
    PrimVar(..)
    -- * Allocation
  , newPrimVar
  , newPinnedPrimVar
  , newAlignedPinnedPrimVar
    -- * Access
  , readPrimVar
  , writePrimVar
  , modifyPrimVar
    -- * Information
  , samePrimVar
  , primVarContents
  , isPrimVarPinned
  ) where

import Data.Functor ( ($>) )
import Foreign (Ptr)

import Control.Monad.Primitive ( PrimMonad(PrimState) )
import Data.Primitive
  ( MutablePrimArray
  , Prim
  , newPrimArray
  , newPinnedPrimArray
  , newAlignedPinnedPrimArray
  , readPrimArray
  , writePrimArray
  , sameMutablePrimArray
  , mutablePrimArrayContents
  , isMutablePrimArrayPinned
  )

newtype PrimVar s a = PrimVar (MutablePrimArray s a)

newPrimVar
  :: (Prim a, PrimMonad m)
  => a -> m (PrimVar (PrimState m) a)
-- ^ Create a new `PrimVar` with the specified initial value.
newPrimVar a =
  newPrimArray 1 >>= \arr ->
    writePrimArray arr 0 a $> PrimVar arr

newPinnedPrimVar
  :: (Prim a, PrimMonad m)
  => a -> m (PrimVar (PrimState m) a)
-- ^ Create a new /pinned/ `PrimVar` with the specified initial value.
-- The garbage collector is guaranteed not to move it.
newPinnedPrimVar a =
  newPinnedPrimArray 1 >>= \arr ->
    writePrimArray arr 0 a $> PrimVar arr

newAlignedPinnedPrimVar
  :: (Prim a, PrimMonad m)
  => a -> m (PrimVar (PrimState m) a)
-- ^ Create a new /pinned/ `PrimVar` with the specified initial value
-- and with the alignment given by its Prim instance.
-- The garbage collector is guaranteed not to move it.
newAlignedPinnedPrimVar a =
  newAlignedPinnedPrimArray 1 >>= \arr ->
    writePrimArray arr 0 a $> PrimVar arr

readPrimVar
  :: (Prim a, PrimMonad m)
  => PrimVar (PrimState m) a -> m a
-- ^ Read the value of a `PrimVar`.
readPrimVar (PrimVar v) = readPrimArray v 0

writePrimVar
  :: (Prim a, PrimMonad m)
  => PrimVar (PrimState m) a -> a -> m ()
-- ^ Write a new value into a `PrimVar`.
writePrimVar (PrimVar v) = writePrimArray v 0

modifyPrimVar
  :: (Prim a, PrimMonad m)
  => PrimVar (PrimState m) a -> (a -> a) -> m ()
-- ^ Mutate the contents of a `PrimVar`.
modifyPrimVar v f =
  readPrimVar v >>= \a ->
    writePrimVar v (f a)

samePrimVar :: PrimVar s a -> PrimVar s a -> Bool
-- ^ Check if two `PrimVar`s refer to the same memory block.
samePrimVar (PrimVar v0) (PrimVar v1) =
  sameMutablePrimArray v0 v1

primVarContents :: PrimVar s a -> Ptr a
-- ^ Yield a pointer to a `PrimVar`'s data.
-- This operation is only safe on pinned `PrimVar`s
-- allocated by `newPinnedPrimVar` or `newAlignedPinnedPrimVar`.
primVarContents (PrimVar v) =
  mutablePrimArrayContents v

isPrimVarPinned :: PrimVar s a -> Bool
-- ^ Check whether or not a `PrimVar` is pinned.
isPrimVarPinned (PrimVar v) =
  isMutablePrimArrayPinned v