Data.Atomics

atomic-primops-0.8.8: A safe approach to CAS and other atomic ops in Haskell.

Safe Haskell | None
Language | Haskell2010

Data.Atomics

Contents

- Types for atomic operations
- Atomic operations on IORefs
- Atomic operations on mutable arrays
- Atomic operations on byte arrays
- Atomic operations on raw MutVars
- Memory barriers
- Deprecated Functions

Description

Provides atomic memory operations on IORefs and Mutable Arrays.

Pointer equality need not be maintained by a Haskell compiler. For example, Int values will frequently be boxed and unboxed, changing the pointer identity of the thunk. To deal with this, the compare-and-swap (CAS) approach used in this module is uses a sealed representation of pointers into the Haskell heap (`Tickets`). Currently, the user cannot coin new tickets, rather a `Ticket` provides evidence of a past observation, and grants permission to make a future change.

Synopsis

- data Ticket a
- peekTicket :: Ticket a -> a
- readForCAS :: IORef a -> IO (Ticket a)
- casIORef :: IORef a -> Ticket a -> a -> IO (Bool, Ticket a)
- casIORef2 :: IORef a -> Ticket a -> Ticket a -> IO (Bool, Ticket a)
- atomicModifyIORefCAS :: IORef a -> (a -> (a, b)) -> IO b
- atomicModifyIORefCAS_ :: IORef t -> (t -> t) -> IO ()
- casArrayElem :: MutableArray RealWorld a -> Int -> Ticket a -> a -> IO (Bool, Ticket a)
- casArrayElem2 :: MutableArray RealWorld a -> Int -> Ticket a -> Ticket a -> IO (Bool, Ticket a)
- readArrayElem :: MutableArray RealWorld a -> Int -> IO (Ticket a)
- casByteArrayInt :: MutableByteArray RealWorld -> Int -> Int -> Int -> IO Int
- fetchAddIntArray :: MutableByteArray RealWorld -> Int -> Int -> IO Int
- fetchSubIntArray :: MutableByteArray RealWorld -> Int -> Int -> IO Int
- fetchAndIntArray :: MutableByteArray RealWorld -> Int -> Int -> IO Int
- fetchNandIntArray :: MutableByteArray RealWorld -> Int -> Int -> IO Int
- fetchOrIntArray :: MutableByteArray RealWorld -> Int -> Int -> IO Int
- fetchXorIntArray :: MutableByteArray RealWorld -> Int -> Int -> IO Int
- readMutVarForCAS :: MutVar# RealWorld a -> IO (Ticket a)
- casMutVar :: MutVar# RealWorld a -> Ticket a -> a -> IO (Bool, Ticket a)
- casMutVar2 :: MutVar# RealWorld a -> Ticket a -> Ticket a -> IO (Bool, Ticket a)
- storeLoadBarrier :: IO ()
- loadLoadBarrier :: IO ()
- writeBarrier :: IO ()
- fetchAddByteArrayInt :: MutableByteArray RealWorld -> Int -> Int -> IO Int

# Types for atomic operations

data Ticket a #

When performing compare-and-swaps, the ticket encapsulates proof that a thread observed a specific previous value of a mutable variable. It is provided in lieu of the "old" value to compare-and-swap.

Design note: `Ticket`s exist to hide objects from the GHC compiler, which can normally perform many optimizations that change pointer equality. A Ticket, on the other hand, is a first-class object that can be handled by the user, but will not have its pointer identity changed by compiler optimizations (but will of course, change addresses during garbage collection).

#### Instances

Instances details

Show (Ticket a) # |
Instance details Defined in Data.Atomics.Internal Methods showsPrec :: Int -> Ticket a -> ShowS # show :: Ticket a -> String # showList :: [Ticket a] -> ShowS #
Eq (Ticket a) # |
Instance details Defined in Data.Atomics.Internal Methods (==) :: Ticket a -> Ticket a -> Bool # (/=) :: Ticket a -> Ticket a -> Bool #

peekTicket :: Ticket a -> a #

A ticket contains or can get the usable Haskell value. This function does just that.

# Atomic operations on IORefs

readForCAS :: IORef a -> IO (Ticket a) #

Ordinary processor load instruction (non-atomic, not implying any memory barriers).

The difference between this function and `readIORef`, is that it returns a ticket, for use in future compare-and-swap operations.

casIORef #

Arguments

:: IORef a | The `IORef` containing a value `current`
-> Ticket a | A ticket for the `old` value
-> a | The `new` value to replace `current` if `old == current`
-> IO (Bool, Ticket a) | Success flag, plus ticket for the NEXT operation.

Performs a machine-level compare and swap (CAS) operation on an `IORef`. Returns a tuple containing a `Bool` which is `True` when a swap is performed, along with the most `current` value from the `IORef`. Note that this differs from the more common CAS behavior, which is to return the old value before the CAS occured.

The reason for the difference is the ticket API. This function always returns the ticket that you should use in your next CAS attempt. In case of success, this ticket corresponds to the `new` value which you yourself installed in the `IORef`, whereas in the case of failure it represents the preexisting value currently in the IORef.

Note "compare" here means pointer equality in the sense of `reallyUnsafePtrEquality#`. However, the ticket API absolves the user of this module from needing to worry about the pointer equality of their values, which in general requires reasoning about the details of the Haskell implementation (GHC).

By convention this function is strict in the "new" value argument. This isn't absolutely necesary, but we think it's a bad habit to use unevaluated thunks in this context.

casIORef2 #

Arguments

:: IORef a |
-> Ticket a | A ticket for the `old` value
-> Ticket a | A ticket for the `new` value
-> IO (Bool, Ticket a) |

This variant takes two tickets, i.e. the `new` value is a ticket rather than an arbitrary, lifted, Haskell value.

atomicModifyIORefCAS #

Arguments

:: IORef a | Mutable location to modify
-> (a -> (a, b)) | Computation runs one or more times (speculation)
-> IO b |

A drop-in replacement for `atomicModifyIORef` that optimistically attempts to compute the new value and CAS it into place without introducing new thunks or locking anything. Note that this is more STRICT than its standard counterpart and will only place evaluated (WHNF) values in the IORef.

The upside is that sometimes we see a performance benefit. The downside is that this version is speculative -- when it retries, it must reexecute the compution.

atomicModifyIORefCAS_ :: IORef t -> (t -> t) -> IO () #

A simpler version that modifies the state but does not return anything.

# Atomic operations on mutable arrays

casArrayElem :: MutableArray RealWorld a -> Int -> Ticket a -> a -> IO (Bool, Ticket a) #

Compare-and-swap. Follows the same rules as `casIORef`, returning the ticket for then next operation.

By convention this is WHNF strict in the "new" value provided.

casArrayElem2 :: MutableArray RealWorld a -> Int -> Ticket a -> Ticket a -> IO (Bool, Ticket a) #

This variant takes two tickets: the `new` value is a ticket rather than an arbitrary, lifted, Haskell value.

readArrayElem :: MutableArray RealWorld a -> Int -> IO (Ticket a) #

Ordinary processor load instruction (non-atomic, not implying any memory barriers).

# Atomic operations on byte arrays

casByteArrayInt :: MutableByteArray RealWorld -> Int -> Int -> Int -> IO Int #

Compare and swap on word-sized chunks of a byte-array. For indexing purposes the bytearray is treated as an array of words (`Int`s). Note that UNLIKE `casIORef` and `casArrayTicketed`, this does not need to operate on tickets.

Further, this version always returns the old value, that was read from the array during the CAS operation. That is, it follows the normal protocol for CAS operations (and matches the underlying instruction on most architectures).

Implies a full memory barrier.

fetchAddIntArray #

Arguments

:: MutableByteArray RealWorld |
-> Int | The offset into the array
-> Int | The value to be added
-> IO Int | The value *before* the addition

Atomically add to a word of memory within a `MutableByteArray`, returning the value *before* the operation. Implies a full memory barrier.

fetchSubIntArray #

Arguments

:: MutableByteArray RealWorld |
-> Int | The offset into the array
-> Int | The value to be subtracted
-> IO Int | The value *before* the addition

Atomically subtract to a word of memory within a `MutableByteArray`, returning the value *before* the operation. Implies a full memory barrier.

fetchAndIntArray #

Arguments

:: MutableByteArray RealWorld |
-> Int | The offset into the array
-> Int | The value to be AND-ed
-> IO Int | The value *before* the addition

Atomically bitwise AND to a word of memory within a `MutableByteArray`, returning the value *before* the operation. Implies a full memory barrier.

fetchNandIntArray #

Arguments

:: MutableByteArray RealWorld |
-> Int | The offset into the array
-> Int | The value to be NAND-ed
-> IO Int | The value *before* the addition

Atomically bitwise NAND to a word of memory within a `MutableByteArray`, returning the value *before* the operation. Implies a full memory barrier.

fetchOrIntArray #

Arguments

:: MutableByteArray RealWorld |
-> Int | The offset into the array
-> Int | The value to be OR-ed
-> IO Int | The value *before* the addition

Atomically bitwise OR to a word of memory within a `MutableByteArray`, returning the value *before* the operation. Implies a full memory barrier.

fetchXorIntArray #

Arguments

:: MutableByteArray RealWorld |
-> Int | The offset into the array
-> Int | The value to be XOR-ed
-> IO Int | The value *before* the addition

Atomically bitwise XOR to a word of memory within a `MutableByteArray`, returning the value *before* the operation. Implies a full memory barrier.

# Atomic operations on raw MutVars

A lower-level version of the IORef interface.

readMutVarForCAS :: MutVar# RealWorld a -> IO (Ticket a) #

Like `readForCAS`, but for `MutVar#`.

casMutVar :: MutVar# RealWorld a -> Ticket a -> a -> IO (Bool, Ticket a) #

MutVar counterpart of `casIORef`.

By convention this is WHNF strict in the "new" value provided.

casMutVar2 :: MutVar# RealWorld a -> Ticket a -> Ticket a -> IO (Bool, Ticket a) #

This variant takes two tickets, i.e. the `new` value is a ticket rather than an arbitrary, lifted, Haskell value.

# Memory barriers

storeLoadBarrier :: IO () #

A memory barrier that prevents future loads occurring before preceding stores.

loadLoadBarrier :: IO () #

A memory barrier that prevents future loads occurring before earlier loads.

writeBarrier :: IO () #

A memory barrier that prevents future stores occurring before preceding stores.

# Deprecated Functions

fetchAddByteArrayInt :: MutableByteArray RealWorld -> Int -> Int -> IO Int #

Deprecated: Replaced by fetchAddIntArray which returns the OLD value

Atomically add to a word of memory within a `MutableByteArray`.

This function returns the NEW value of the location after the increment. Thus, it is a bit misnamed, and in other contexts might be called "add-and-fetch", such as in GCC's `__sync_add_and_fetch`.