packages feed

primitive-atomic (empty) → 0.1.0.0

raw patch · 10 files changed

+428/−0 lines, 10 filesdep +basedep +primitivedep +primitive-atomicsetup-changed

Dependencies added: base, primitive, primitive-atomic

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for primitive-atomic++## 0.1.0.0 -- 2019-04-25++* First version.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2019, Andrew Martin++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * 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.++    * Neither the name of Andrew Martin nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"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 COPYRIGHT+OWNER 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ primitive-atomic.cabal view
@@ -0,0 +1,38 @@+cabal-version: 2.2+name: primitive-atomic+version: 0.1.0.0+synopsis: Wrappers for primops around atomic operations+homepage: https://github.com/andrewthad/primitive-atomic+license: BSD-3-Clause+license-file: LICENSE+author: Andrew Martin+maintainer: andrew.thaddeus@gmail.com+copyright: 2019 Andrew Martin+category: Data+build-type: Simple+extra-source-files: CHANGELOG.md++library+  exposed-modules:+    Data.Primitive.Array.Atomic+    Data.Primitive.Unlifted.Atomic+    Data.Primitive.ByteArray.Atomic+    Data.Primitive.Class.Atomic+    Data.Primitive.PrimArray.Atomic+  build-depends:+    , base >=4.11.1 && <5+    , primitive >= 0.6.4+  hs-source-dirs: src+  default-language: Haskell2010+  ghc-options: -Wall++test-suite unit+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: Unit.hs+  build-depends:+    , base+    , primitive-atomic+    , primitive+  ghc-options: -Wall -O2+  default-language: Haskell2010
+ src/Data/Primitive/Array/Atomic.hs view
@@ -0,0 +1,32 @@+{-# language MagicHash #-}+{-# language UnboxedTuples #-}++module Data.Primitive.Array.Atomic+  ( casArray+  ) where++import Control.Monad.Primitive (PrimMonad,PrimState,primitive)+import Data.Primitive (MutableArray(..))+import GHC.Exts (Int(I#),casArray#,isTrue#,(==#))++-- | Given an array, an offset in Int units, 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.+--+-- Note that lifted values in GHC have limited guarantees concerning+-- pointer equality. In particular, data constructor applications of+-- single-constructor data types may be mangled by GHC Core optimizations.+-- Users of this function are expected to understand how to make+-- pointer equality survive GHC's optimization passes.+casArray :: PrimMonad m+  => MutableArray (PrimState m) a -- ^ prim array+  -> Int -- ^ index+  -> a -- ^ expected old value+  -> a -- ^ new value+  -> m (Bool,a)+{-# INLINE casArray #-}+casArray (MutableArray arr#) (I# i#) old new =+  primitive $ \s0 -> case casArray# arr# i# old new s0 of+    (# s1, n, r #) -> (# s1, (isTrue# (n ==# 0# ),r) #)
+ src/Data/Primitive/ByteArray/Atomic.hs view
@@ -0,0 +1,112 @@+{-# language MagicHash #-}+{-# language UnboxedTuples #-}++module Data.Primitive.ByteArray.Atomic+  ( casByteArray+  , fetchAddByteArray+  , fetchSubByteArray+  , fetchAndByteArray+  , fetchNandByteArray+  , fetchOrByteArray+  , fetchXorByteArray+  ) where++import Control.Monad.Primitive (PrimMonad,PrimState,primitive)+import Data.Primitive (MutableByteArray(..))+import Data.Primitive.Class.Atomic (PrimMach,primMachToInt#,primMachFromInt#)+import GHC.Exts++-- | Given an array, an offset in Int units, 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.+casByteArray :: (PrimMonad m, PrimMach a)+  => MutableByteArray (PrimState m) -- ^ array+  -> Int -- ^ index+  -> a -- ^ expected old value+  -> a -- ^ new value+  -> m a+{-# INLINE casByteArray #-}+casByteArray (MutableByteArray arr#) (I# i#) old new =+  primitive $ \s0 -> case casIntArray# arr# i# (primMachToInt# old) (primMachToInt# new) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, 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.+fetchAddByteArray :: (PrimMonad m, PrimMach a)+  => MutableByteArray (PrimState m)+  -> Int -- ^ index+  -> a -- ^ value to add to the element+  -> m a+{-# INLINE fetchAddByteArray #-}+fetchAddByteArray (MutableByteArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchAddIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, and a value to subtract, atomically+-- subtract the value to the element. Returns the value of the element before the+-- operation. Implies a full memory barrier.+fetchSubByteArray :: (PrimMonad m, PrimMach a)+  => MutableByteArray (PrimState m)+  -> Int -- ^ index+  -> a -- ^ value to subtract from the element+  -> m a+{-# INLINE fetchSubByteArray #-}+fetchSubByteArray (MutableByteArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchSubIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, and a value to @AND@, atomically+-- @AND@ the value to the element. Returns the value of the element before the+-- operation. Implies a full memory barrier.+fetchAndByteArray :: (PrimMonad m, PrimMach a)+  => MutableByteArray (PrimState m)+  -> Int -- ^ index+  -> a -- ^ value to @AND@ with the element+  -> m a+{-# INLINE fetchAndByteArray #-}+fetchAndByteArray (MutableByteArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchAndIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, and a value to @NAND@, atomically+-- @NAND@ the value to the element. Returns the value of the element before the+-- operation. Implies a full memory barrier.+fetchNandByteArray :: (PrimMonad m, PrimMach a)+  => MutableByteArray (PrimState m)+  -> Int -- ^ index+  -> a -- ^ value to @NAND@ with the element+  -> m a+{-# INLINE fetchNandByteArray #-}+fetchNandByteArray (MutableByteArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchNandIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, and a value to @OR@, atomically+-- @OR@ the value to the element. Returns the value of the element before the+-- operation. Implies a full memory barrier.+fetchOrByteArray :: (PrimMonad m, PrimMach a)+  => MutableByteArray (PrimState m)+  -> Int -- ^ index+  -> a -- ^ value to @OR@ with the element+  -> m a+{-# INLINE fetchOrByteArray #-}+fetchOrByteArray (MutableByteArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchOrIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, and a value to @XOR@, atomically+-- @XOR@ the value to the element. Returns the value of the element before the+-- operation. Implies a full memory barrier.+fetchXorByteArray :: (PrimMonad m, PrimMach a)+  => MutableByteArray (PrimState m)+  -> Int -- ^ index+  -> a -- ^ value to @XOR@ with the element+  -> m a+{-# INLINE fetchXorByteArray #-}+fetchXorByteArray (MutableByteArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchXorIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++
+ src/Data/Primitive/Class/Atomic.hs view
@@ -0,0 +1,24 @@+{-# language MagicHash #-}+{-# language UnboxedTuples #-}++module Data.Primitive.Class.Atomic+  ( PrimMach(..)+  ) where++import Data.Primitive (Prim)+import GHC.Exts++-- | Class of types supporting primitive operations that are isomorphic to+-- a machine integer. Such types support compare-and-swap and other atomic+-- operations.+class Prim a => PrimMach a where+  primMachToInt# :: a -> Int#+  primMachFromInt# :: Int# -> a++instance PrimMach Int where+  primMachToInt# (I# i) = i+  primMachFromInt# = I#++instance PrimMach Word where+  primMachToInt# (W# i) = word2Int# i+  primMachFromInt# i = W# (int2Word# i)
+ src/Data/Primitive/PrimArray/Atomic.hs view
@@ -0,0 +1,111 @@+{-# language MagicHash #-}+{-# language UnboxedTuples #-}++module Data.Primitive.PrimArray.Atomic+  ( casPrimArray+  , fetchAddPrimArray+  , fetchSubPrimArray+  , fetchAndPrimArray+  , fetchNandPrimArray+  , fetchOrPrimArray+  , fetchXorPrimArray+  ) where++import Control.Monad.Primitive (PrimMonad,PrimState,primitive)+import Data.Primitive (MutablePrimArray(..))+import Data.Primitive.Class.Atomic (PrimMach,primMachToInt#,primMachFromInt#)+import GHC.Exts++-- | Given an array, an offset in Int units, 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.+casPrimArray :: (PrimMonad m, PrimMach a)+  => MutablePrimArray (PrimState m) a -- ^ prim array+  -> Int -- ^ index+  -> a -- ^ expected old value+  -> a -- ^ new value+  -> m a+{-# INLINE casPrimArray #-}+casPrimArray (MutablePrimArray arr#) (I# i#) old new =+  primitive $ \s0 -> case casIntArray# arr# i# (primMachToInt# old) (primMachToInt# new) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, 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.+fetchAddPrimArray :: (PrimMonad m, PrimMach a)+  => MutablePrimArray (PrimState m) a+  -> Int -- ^ index+  -> a -- ^ value to add to the element+  -> m a+{-# INLINE fetchAddPrimArray #-}+fetchAddPrimArray (MutablePrimArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchAddIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, and a value to subtract, atomically+-- subtract the value to the element. Returns the value of the element before the+-- operation. Implies a full memory barrier.+fetchSubPrimArray :: (PrimMonad m, PrimMach a)+  => MutablePrimArray (PrimState m) a+  -> Int -- ^ index+  -> a -- ^ value to subtract from the element+  -> m a+{-# INLINE fetchSubPrimArray #-}+fetchSubPrimArray (MutablePrimArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchSubIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, and a value to @AND@, atomically+-- @AND@ the value to the element. Returns the value of the element before the+-- operation. Implies a full memory barrier.+fetchAndPrimArray :: (PrimMonad m, PrimMach a)+  => MutablePrimArray (PrimState m) a+  -> Int -- ^ index+  -> a -- ^ value to @AND@ with the element+  -> m a+{-# INLINE fetchAndPrimArray #-}+fetchAndPrimArray (MutablePrimArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchAndIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, and a value to @NAND@, atomically+-- @NAND@ the value to the element. Returns the value of the element before the+-- operation. Implies a full memory barrier.+fetchNandPrimArray :: (PrimMonad m, PrimMach a)+  => MutablePrimArray (PrimState m) a+  -> Int -- ^ index+  -> a -- ^ value to @NAND@ with the element+  -> m a+{-# INLINE fetchNandPrimArray #-}+fetchNandPrimArray (MutablePrimArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchNandIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, and a value to @OR@, atomically+-- @OR@ the value to the element. Returns the value of the element before the+-- operation. Implies a full memory barrier.+fetchOrPrimArray :: (PrimMonad m, PrimMach a)+  => MutablePrimArray (PrimState m) a+  -> Int -- ^ index+  -> a -- ^ value to @OR@ with the element+  -> m a+{-# INLINE fetchOrPrimArray #-}+fetchOrPrimArray (MutablePrimArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchOrIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)++-- | Given an array, and offset in Int units, and a value to @XOR@, atomically+-- @XOR@ the value to the element. Returns the value of the element before the+-- operation. Implies a full memory barrier.+fetchXorPrimArray :: (PrimMonad m, PrimMach a)+  => MutablePrimArray (PrimState m) a+  -> Int -- ^ index+  -> a -- ^ value to @XOR@ with the element+  -> m a+{-# INLINE fetchXorPrimArray #-}+fetchXorPrimArray (MutablePrimArray arr#) (I# i#) val =+  primitive $ \s0 -> case fetchXorIntArray# arr# i# (primMachToInt# val) s0 of+    (# s1, r #) -> (# s1, primMachFromInt# r #)+
+ src/Data/Primitive/Unlifted/Atomic.hs view
@@ -0,0 +1,40 @@+{-# language BangPatterns #-}+{-# language MagicHash #-}+{-# language UnboxedTuples #-}+{-# language ScopedTypeVariables #-}++module Data.Primitive.Unlifted.Atomic+  ( casUnliftedArray+  ) where++import Control.Monad.Primitive (PrimMonad,PrimState,primitive)+import Data.Primitive (MutableUnliftedArray(..),PrimUnlifted)+import Data.Primitive (toArrayArray#,fromArrayArray#)+import GHC.Exts (Any,MutableArrayArray#,MutableArray#,ArrayArray#,Int(I#))+import GHC.Exts (casArray#,isTrue#,(==#),unsafeCoerce#)++-- | Given an array, an offset, 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.+--+-- Some unlifted types, in particular the ones that correspond to mutable+-- resources, have good guarantees about pointer equality. With these+-- types, this function is much easier to reason about than @casArray@.+casUnliftedArray :: forall m a. (PrimMonad m, PrimUnlifted a)+  => MutableUnliftedArray (PrimState m) a -- ^ prim array+  -> Int -- ^ index+  -> a -- ^ expected old value+  -> a -- ^ new value+  -> m (Bool,a)+{-# INLINE casUnliftedArray #-}+casUnliftedArray (MutableUnliftedArray arr#) (I# i#) old new =+  -- All of this unsafeCoercing is really nasty business. This will go away+  -- once https://github.com/ghc-proposals/ghc-proposals/pull/203 happens.+  primitive $ \s0 ->+    let !uold = (unsafeCoerce# :: ArrayArray# -> Any) (toArrayArray# old)+        !unew = (unsafeCoerce# :: ArrayArray# -> Any) (toArrayArray# new)+     in case casArray# ((unsafeCoerce# :: MutableArrayArray# (PrimState m) -> MutableArray# (PrimState m) Any) arr#) i# uold unew s0 of+          (# s1, n, ur #) -> (# s1, (isTrue# (n ==# 0# ),fromArrayArray# ((unsafeCoerce# :: Any -> ArrayArray#) ur)) #)+
+ test/Unit.hs view
@@ -0,0 +1,34 @@+{-# language MultiWayIf #-}++import Data.Primitive.Unlifted.Atomic+import Data.Primitive+import Control.Monad (when)++main :: IO ()+main = do+  putStrLn "Start"+  putStrLn "A"+  testA+  putStrLn "Finished"++testA :: IO ()+testA = do+  arr <- newUnliftedArray 20 =<< newByteArray 16+  x <- newByteArray 24+  y <- newByteArray 32+  z <- newByteArray 40+  writeUnliftedArray arr 0 x+  writeUnliftedArray arr 1 y+  writeUnliftedArray arr 2 z+  (success0,old0) <- casUnliftedArray arr 1 z x+  when (not (sameMutableByteArray old0 y)) $ do+    fail "old0 /= y"+  when (success0 == True) $ do+    fail "success0 == True"+  (success1,old1) <- casUnliftedArray arr 0 x z+  if | sameMutableByteArray old1 z -> pure ()+     | sameMutableByteArray old1 y -> fail "old1 == y"+     | sameMutableByteArray old1 x -> fail "old1 == x"+     | otherwise -> fail "old1 /= z"+  when (success1 == False) $ do+    fail "success1 == False"