impure-containers-0.5.1: src/ImpureContainers/PrimRef.hs
--------------------------------------------------------------------------------
-- License:
-- Copyright 2015 Edward Kmett
-- Copyright 2018 Remy Goldschmidt
--
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
--
-- 1. Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
-- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-- DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
-- ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE Unsafe #-}
--------------------------------------------------------------------------------
-- | Unboxed primitive references.
--
-- Note: Edward Kmett wrote everything in this module. It was sitting
-- unpackaged on GitHub, so I took it and published it as a part of
-- this package.
module ImpureContainers.PrimRef
( -- * 'PrimRef'
PrimRef
-- ** Creation
, new
, newPinned
, newAlignedPinned
-- ** Simple functions
, read
, write
, contents
-- ** Atomic mutators
, atomicReadInt
, atomicWriteInt
, casInt
, fetchAddInt
, fetchSubInt
, fetchAndInt
, fetchNandInt
, fetchOrInt
, fetchXorInt
-- ** Unsafe functions
, unsafeToMByteArray
, unsafeFromMByteArray
) where
--------------------------------------------------------------------------------
import Prelude ()
import Control.Applicative (pure)
import Data.Eq (Eq ((==)))
import Data.Function (($))
import Control.Monad.Primitive
(PrimMonad, PrimState, primitive, primitive_)
#if MIN_VERSION_primitive(0, 7, 0)
import Data.Primitive (Prim, Ptr, alignment, sizeOf)
import Data.Word (Word8)
#else
import Data.Primitive (Addr, Prim, alignment, sizeOf)
#endif
import qualified GHC.Prim as GHC.Prim
import GHC.Types (Int (I#))
import ImpureContainers.MByteArray (MByteArray)
import qualified ImpureContainers.MByteArray as MByteArray
--------------------------------------------------------------------------------
-- FIXME: when GHC supports creating newtypes in kind #, we should create a
-- type called PrimRef# to reduce indirections even further.
-- see https://ghc.haskell.org/trac/ghc/ticket/1311 for progress.
-- | FIXME: doc
newtype PrimRef s a
= MkPrimRef (MByteArray s)
type role PrimRef nominal nominal
--------------------------------------------------------------------------------
-- | Create a primitive reference.
new
:: (PrimMonad m, Prim a)
=> a
-- ^ FIXME: doc
-> m (PrimRef (PrimState m) a)
-- ^ FIXME: doc
new a = do
m <- MByteArray.new (sizeOf a)
MByteArray.write m 0 a
pure (MkPrimRef m)
{-# INLINE new #-}
-- | Create a pinned primitive reference.
newPinned
:: (PrimMonad m, Prim a)
=> a
-- ^ FIXME: doc
-> m (PrimRef (PrimState m) a)
-- ^ FIXME: doc
newPinned a = do
m <- MByteArray.newPinned (sizeOf a)
MByteArray.write m 0 a
pure (MkPrimRef m)
{-# INLINE newPinned #-}
-- | Create a pinned primitive reference with the appropriate alignment for
-- its contents.
newAlignedPinned
:: (PrimMonad m, Prim a)
=> a
-- ^ FIXME: doc
-> m (PrimRef (PrimState m) a)
-- ^ FIXME: doc
newAlignedPinned a = do
m <- MByteArray.newAlignedPinned (sizeOf a) (alignment a)
MByteArray.write m 0 a
pure (MkPrimRef m)
{-# INLINE newAlignedPinned #-}
-- | Read a primitive value from the reference
read
:: (PrimMonad m, Prim a)
=> PrimRef (PrimState m) a
-- ^ FIXME: doc
-> m a
-- ^ FIXME: doc
read (MkPrimRef m) = MByteArray.read m 0
{-# INLINE read #-}
-- | Write a primitive value to the reference
write
:: (PrimMonad m, Prim a)
=> PrimRef (PrimState m) a
-- ^ FIXME: doc
-> a
-- ^ FIXME: doc
-> m ()
-- ^ FIXME: doc
write (MkPrimRef m) a = MByteArray.write m 0 a
{-# INLINE write #-}
-- | FIXME: doc
instance Eq (PrimRef s a) where
(MkPrimRef m) == (MkPrimRef n) = MByteArray.same m n
{-# INLINE (==) #-}
-- | Yield a pointer to the data of a 'PrimRef'.
--
-- This operation is only safe on pinned byte arrays allocated by
-- 'newPinned' or 'newAlignedPinned'.
contents
:: PrimRef s a
-- ^ FIXME: doc
#if MIN_VERSION_primitive(0, 7, 0)
-> Ptr Word8
#else
-> Addr
#endif
-- ^ FIXME: doc
contents (MkPrimRef m) = MByteArray.contents m
{-# INLINE contents #-}
--------------------------------------------------------------------------------
-- | Given a reference, read an element.
--
-- Implies a full memory barrier.
atomicReadInt
:: (PrimMonad m)
=> PrimRef (PrimState m) Int
-- ^ A primitive reference.
-> m Int
-- ^ A 'PrimMonad' action that reads the primitive reference and returns
-- its current value as an 'Int'.
atomicReadInt (MkPrimRef mba) = primitive $ \s -> do
let m = MByteArray.underlyingMutableByteArray# mba
case GHC.Prim.atomicReadIntArray# m 0# s of
(# s', result #) -> (# s', I# result #)
-- | Given a reference, write an element.
--
-- Implies a full memory barrier.
atomicWriteInt
:: (PrimMonad m)
=> PrimRef (PrimState m) Int
-- ^ A primitive reference.
-> Int
-- ^ The new value the primitive reference should take on.
-> m ()
-- ^ A 'PrimMonad' action that writes the given 'Int' to the given
-- primitive reference.
atomicWriteInt (MkPrimRef mba) (I# x) = primitive_ $ \s -> do
let m = MByteArray.underlyingMutableByteArray# mba
GHC.Prim.atomicWriteIntArray# m 0# x s
-- | Given a primitive reference, the expected old value, and the new value,
-- perform an 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.
--
-- Implies a full memory barrier.
casInt
:: (PrimMonad m)
=> PrimRef (PrimState m) Int
-- ^ A primitive reference.
-> Int
-- ^ The expected old value.
-> Int
-- ^ The new value.
-> m Int
-- ^ A 'PrimMonad' action that atomically writes the new value to the
-- primitive reference if the current value matches the expected old value,
-- and then returns the old value of the primitive reference.
casInt (MkPrimRef mba) (I# oldValue) (I# newValue) = primitive $ \s -> do
let m = MByteArray.underlyingMutableByteArray# mba
case GHC.Prim.casIntArray# m 0# oldValue newValue s of
(# s', result #) -> (# s', I# result #)
-- | Given a reference, and a value to add, atomically add the value to the
-- element.
--
-- Returns the value of the element before the operation.
--
-- Implies a full memory barrier.
fetchAddInt
:: (PrimMonad m)
=> PrimRef (PrimState m) Int
-- ^ A primitive reference.
-> Int
-- ^ An 'Int' to add to the current value of the primitive reference.
-> m Int
-- ^ A 'PrimMonad' action that atomically sets the value of the given
-- primitive reference to the result of adding the given 'Int' to its
-- current value, and then returns the value the primitive reference
-- had before this modification.
fetchAddInt (MkPrimRef mba) (I# x) = primitive $ \s -> do
let m = MByteArray.underlyingMutableByteArray# mba
case GHC.Prim.fetchAddIntArray# m 0# x s of
(# s', result #) -> (# s', I# result #)
-- | Given a reference and a value to subtract, atomically subtract the value
-- from the element.
--
-- Returns the value of the element before the operation.
--
-- Implies a full memory barrier.
fetchSubInt
:: (PrimMonad m)
=> PrimRef (PrimState m) Int
-- ^ A primitive reference.
-> Int
-- ^ An 'Int' to subtract from the current value of the primitive reference.
-> m Int
-- ^ A 'PrimMonad' action that atomically sets the value of the given
-- primitive reference to the result of subtracting the given 'Int' from
-- its current value, and then returns the value the primitive reference
-- had before this modification.
fetchSubInt (MkPrimRef mba) (I# x) = primitive $ \s -> do
let m = MByteArray.underlyingMutableByteArray# mba
case GHC.Prim.fetchSubIntArray# m 0# x s of
(# s', result #) -> (# s', I# result #)
-- | Given a reference and a value with which to bitwise AND, atomically AND
-- the value with the element.
--
-- Returns the value of the element before the operation.
--
-- Implies a full memory barrier.
fetchAndInt
:: (PrimMonad m)
=> PrimRef (PrimState m) Int
-- ^ A primitive reference.
-> Int
-- ^ An 'Int' to AND with the current value of the primitive reference.
-> m Int
-- ^ A 'PrimMonad' action that atomically sets the value of the given
-- primitive reference to the result of bitwise ANDing the given 'Int'
-- with the primitive reference's current value, and then returns the value
-- the primitive reference had before this modification.
fetchAndInt (MkPrimRef mba) (I# x) = primitive $ \s -> do
let m = MByteArray.underlyingMutableByteArray# mba
case GHC.Prim.fetchAndIntArray# m 0# x s of
(# s', result #) -> (# s', I# result #)
-- | Given a reference and a value with which to bitwise NAND, atomically NAND
-- the value with the element.
--
-- Returns the value of the element before the operation.
--
-- Implies a full memory barrier.
fetchNandInt
:: (PrimMonad m)
=> PrimRef (PrimState m) Int
-- ^ A primitive reference.
-> Int
-- ^ An 'Int' to NAND with the current value of the primitive reference.
-> m Int
-- ^ A 'PrimMonad' action that atomically sets the value of the given
-- primitive reference to the result of bitwise NANDing the given 'Int'
-- with the primitive reference's current value, and then returns the value
-- the primitive reference had before this modification.
fetchNandInt (MkPrimRef mba) (I# x) = primitive $ \s -> do
let m = MByteArray.underlyingMutableByteArray# mba
case GHC.Prim.fetchNandIntArray# m 0# x s of
(# s', result #) -> (# s', I# result #)
-- | Given a reference and a value with which to bitwise OR, atomically OR
-- the value with the element.
--
-- Returns the value of the element before the operation.
--
-- Implies a full memory barrier.
fetchOrInt
:: (PrimMonad m)
=> PrimRef (PrimState m) Int
-- ^ A primitive reference.
-> Int
-- ^ An 'Int' to OR with the current value of the primitive reference.
-> m Int
-- ^ A 'PrimMonad' action that atomically sets the value of the given
-- primitive reference to the result of bitwise ORing the given 'Int'
-- with the primitive reference's current value, and then returns the value
-- the primitive reference had before this modification.
fetchOrInt (MkPrimRef mba) (I# x) = primitive $ \s -> do
let m = MByteArray.underlyingMutableByteArray# mba
case GHC.Prim.fetchOrIntArray# m 0# x s of
(# s', result #) -> (# s', I# result #)
-- | Given a reference, and a value with which to bitwise XOR, atomically XOR
-- the value with the element.
--
-- Returns the value of the element before the operation.
--
-- Implies a full memory barrier.
fetchXorInt
:: (PrimMonad m)
=> PrimRef (PrimState m) Int
-- ^ A primitive reference.
-> Int
-- ^ An 'Int' to XOR with the current value of the primitive reference.
-> m Int
-- ^ A 'PrimMonad' action that atomically sets the value of the given
-- primitive reference to the result of bitwise XORing the given 'Int'
-- with the primitive reference's current value, and then returns the value
-- the primitive reference had before this modification.
fetchXorInt (MkPrimRef mba) (I# x) = primitive $ \s -> do
let m = MByteArray.underlyingMutableByteArray# mba
case GHC.Prim.fetchXorIntArray# m 0# x s of
(# s', result #) -> (# s', I# result #)
--------------------------------------------------------------------------------
-- | FIXME: doc
unsafeToMByteArray
:: PrimRef s a
-- ^ FIXME: doc
-> MByteArray s
-- ^ FIXME: doc
unsafeToMByteArray (MkPrimRef mba) = mba
{-# INLINE unsafeToMByteArray #-}
-- | FIXME: doc
unsafeFromMByteArray
:: MByteArray s
-- ^ FIXME: doc
-> PrimRef s a
-- ^ FIXME: doc
unsafeFromMByteArray = MkPrimRef
{-# INLINE unsafeFromMByteArray #-}
--------------------------------------------------------------------------------