HORIZON HASKELLDocslts/ghc-10.0.xc62e4de2026-10-19Search names, modules, packages, or :: a typeCtrl K

GHC 10.0.0.20260917 · lts/ghc-10.0.x · c62e4de · 2026-10-19

Moduleconcurrency-1.11.0.3Haskell2010

Control.Concurrent.Classy.CRef

Deprecated. Import Control.Concurrent.Classy.IORef instead

Deprecated re-exports of IORef functions under the old CRef names.

  • 1 type
  • 12 values

CRefs

10 declarations
valuenewCRefN :: MonadConc m => String -> a -> m (CRef m a)
#

Create a new reference, but it is given a name which may be used to present more useful debugging information.

valuewriteCRef :: MonadConc m => CRef m a -> a -> m ()
#

Write a new value into an CRef, without imposing a memory barrier. This means that relaxed memory effects can be observed.

valuemodifyCRef :: MonadConc m => CRef m a -> (a -> a) -> m ()
#

Mutate the contents of a CRef.

Be warned that modifyCRef does not apply the function strictly. This means if the program calls modifyCRef many times, but seldomly uses the value, thunks will pile up in memory resulting in a space leak.

valueatomicModifyCRef :: MonadConc m => CRef m a -> (a -> (a, b)) -> m b
#

Atomically modify the value stored in a reference. This imposes a full memory barrier.

valueatomicWriteCRef :: MonadConc m => CRef m a -> a -> m ()
#

Replace the value stored in a reference, with the barrier-to-reordering property that atomicModifyIORef has.

Compare-and-swap

valuecasCRef
  1. :: MonadConc m
  2. => CRef m a
  3. -> Ticket m a
  4. -> a
  5. -> m (Bool, Ticket m a)
#

Perform a machine-level compare-and-swap (CAS) operation on a CRef. Returns an indication of success and a Ticket for the most current value in the CRef. This is strict in the "new" value argument.

Memory Model

0 declarations

In a concurrent program, CRef operations may appear out-of-order to another thread, depending on the memory model of the underlying processor architecture. For example, on x86 (which uses total store order), loads can move ahead of stores. Consider this example:

crefs :: MonadConc m => m (Bool, Bool)
crefs = do
  r1 <- newCRef False
  r2 <- newCRef False

  x <- spawn $ writeCRef r1 True >> readCRef r2
  y <- spawn $ writeCRef r2 True >> readCRef r1

  (,) <$> readMVar x <*> readMVar y

Under a sequentially consistent memory model the possible results are (True, True), (True, False), and (False, True). Under total or partial store order, (False, False) is also a possible result, even though there is no interleaving of the threads which can lead to this.

We can see this by testing with different memory models:

> autocheckWay defaultWay SequentialConsistency relaxed
[pass] Never Deadlocks
[pass] No Exceptions
[fail] Consistent Result
       (False,True) S0---------S1----S0--S2----S0--

       (True,True) S0---------S1-P2----S1---S0---

       (True,False) S0---------S2----S1----S0---
False
> autocheckWay defaultWay TotalStoreOrder  relaxed
[pass] Never Deadlocks
[pass] No Exceptions
[fail] Consistent Result
        (False,True) S0---------S1----S0--S2----S0--

        (False,False) S0---------S1--P2----S1--S0---

        (True,False) S0---------S2----S1----S0---

        (True,True) S0---------S1-C-S2----S1---S0---
False

Traces for non-sequentially-consistent memory models show where writes to CRefs are committed, which makes a write visible to all threads rather than just the one which performed the write. Only writeCRef is broken up into separate write and commit steps, atomicModifyCRef is still atomic and imposes a memory barrier.