Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
-
f8cfb1a5
by Hécate Kleidukos at 2026-10-04T19:36:39+02:00
12 changed files:
- + changelog.d/clc-341-strict-mutable-data-types
- libraries/base/base.cabal.in
- libraries/base/changelog.md
- libraries/base/src/Control/Concurrent/Chan.hs
- + libraries/base/src/Control/Concurrent/Chan/Strict.hs
- libraries/base/src/Control/Concurrent/MVar.hs
- + libraries/base/src/Control/Concurrent/MVar/Strict.hs
- libraries/base/src/Data/IORef.hs
- + libraries/base/src/Data/IORef/Strict.hs
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
Changes:
| 1 | +section: base
|
|
| 2 | +synopsis: Add strict variants to mutable types (IORef, Chan, MVar)
|
|
| 3 | +issues: #27343
|
|
| 4 | +mrs: !15450
|
|
| 5 | + |
|
| 6 | +description: {
|
|
| 7 | + These new types prevent users from (easily) mis-using existing and dangerous APIs (writeIORef, modifyIORef, modifyMVar, etc) which can lead to space leaks when stored in mutable containers.
|
|
| 8 | + They also prevent invisible coercions to and from their lazy equivalents by not exposing their constructors.
|
|
| 9 | +} |
| ... | ... | @@ -41,6 +41,7 @@ Library |
| 41 | 41 | Control.Applicative
|
| 42 | 42 | , Control.Concurrent
|
| 43 | 43 | , Control.Concurrent.Chan
|
| 44 | + , Control.Concurrent.Chan.Strict
|
|
| 44 | 45 | , Control.Concurrent.QSem
|
| 45 | 46 | , Control.Concurrent.QSemN
|
| 46 | 47 | , Control.Monad.IO.Class
|
| ... | ... | @@ -80,6 +81,7 @@ Library |
| 80 | 81 | , Control.Arrow
|
| 81 | 82 | , Control.Category
|
| 82 | 83 | , Control.Concurrent.MVar
|
| 84 | + , Control.Concurrent.MVar.Strict
|
|
| 83 | 85 | , Control.Exception
|
| 84 | 86 | , Control.Exception.Annotation
|
| 85 | 87 | , Control.Exception.Backtrace
|
| ... | ... | @@ -109,6 +111,7 @@ Library |
| 109 | 111 | , Data.Functor.Const
|
| 110 | 112 | , Data.Functor.Identity
|
| 111 | 113 | , Data.IORef
|
| 114 | + , Data.IORef.Strict
|
|
| 112 | 115 | , Data.Int
|
| 113 | 116 | , Data.Ix
|
| 114 | 117 | , Data.Kind
|
| ... | ... | @@ -12,6 +12,7 @@ |
| 12 | 12 | * Add `withEmptyCallStack` to `GHC.Stack`. ([CLC proposal #428](https://github.com/haskell/core-libraries-committee/issues/428))
|
| 13 | 13 | * Add new `Data.RealFloat` module re-exporting `RealFloat` from `GHC.Float` ([CLC proposal #394](https://github.com/haskell/core-libraries-committee/issues/394))
|
| 14 | 14 | * Add `Infinity`, `NegInfinity`, and `NaN` pattern synonyms to `Data.RealFloat` ([CLC proposal #394](https://github.com/haskell/core-libraries-committee/issues/394))
|
| 15 | + * Add strict variants to mutable types (IORef, Chan, MVar). ([CLC Proposal #341](https://github.com/haskell/core-libraries-committee/issues/341))
|
|
| 15 | 16 | |
| 16 | 17 | ## 4.23.0.0 *TBA*
|
| 17 | 18 | * Add `System.IO.hGetNewlineMode`. ([CLC proposal #370](https://github.com/haskell/core-libraries-committee/issues/370))
|
| ... | ... | @@ -18,6 +18,8 @@ |
| 18 | 18 | -- @stm@ (software transactional memory) library has a more robust implementation
|
| 19 | 19 | -- of channels called @TChan@s.
|
| 20 | 20 | --
|
| 21 | +-- A variant that forces its contents to weak head normal form is available in
|
|
| 22 | +-- "Control.Concurrent.Chan.Strict".
|
|
| 21 | 23 | -----------------------------------------------------------------------------
|
| 22 | 24 | |
| 23 | 25 | module Control.Concurrent.Chan
|
| 1 | +-- | Unbounded channels whose contents are always in weak head normal form
|
|
| 2 | +-- (WHNF).
|
|
| 3 | +--
|
|
| 4 | +-- This module is a thin wrapper around "Control.Concurrent.Chan": each
|
|
| 5 | +-- operation has the same semantics as its counterpart there, except that the
|
|
| 6 | +-- value written to the channel is forced to WHNF first.
|
|
| 7 | +-- "Control.Concurrent.Chan" is the reference documentation for these
|
|
| 8 | +-- operations; only the differences are repeated here.
|
|
| 9 | +--
|
|
| 10 | +-- The caveats of the lazy module apply unchanged. In particular a 'Chan' is
|
|
| 11 | +-- /unbounded/: a producer that outruns its consumer makes the channel grow
|
|
| 12 | +-- without limit. The @stm@ package offers channels with a different
|
|
| 13 | +-- concurrency model, and @TBQueue@ in particular is bounded.
|
|
| 14 | +--
|
|
| 15 | +-- Note that WHNF is not deep: writing @Just <thunk>@ to a
|
|
| 16 | +-- @'Chan' (Maybe a)@ still writes a thunk inside the @Just@.
|
|
| 17 | +module Control.Concurrent.Chan.Strict
|
|
| 18 | + ( Chan
|
|
| 19 | + |
|
| 20 | + -- * Operations
|
|
| 21 | + , newChan
|
|
| 22 | + , writeChan
|
|
| 23 | + , readChan
|
|
| 24 | + , dupChan
|
|
| 25 | + , getChanContents
|
|
| 26 | + , writeList2Chan
|
|
| 27 | + ) where
|
|
| 28 | + |
|
| 29 | +import Data.Foldable (mapM_)
|
|
| 30 | +import Data.Functor
|
|
| 31 | +import GHC.Base
|
|
| 32 | +import qualified Control.Concurrent.Chan as Lazy
|
|
| 33 | + |
|
| 34 | +-- | An unbounded FIFO channel, wrapping @Chan@ from "Control.Concurrent.Chan".
|
|
| 35 | +--
|
|
| 36 | +-- The values it holds are always in WHNF: every operation of this module forces
|
|
| 37 | +-- a value before it is written.
|
|
| 38 | +newtype Chan a = Chan (Lazy.Chan a)
|
|
| 39 | + deriving Eq
|
|
| 40 | + |
|
| 41 | +-- | Build and return a new instance of 'Chan'.
|
|
| 42 | +--
|
|
| 43 | +-- See @newChan@ in "Control.Concurrent.Chan" for the full semantics.
|
|
| 44 | +newChan :: IO (Chan a)
|
|
| 45 | +newChan = Chan <$> Lazy.newChan
|
|
| 46 | + |
|
| 47 | +-- | Write a value to a 'Chan'.
|
|
| 48 | +--
|
|
| 49 | +-- See @writeChan@ in "Control.Concurrent.Chan" for the full semantics.
|
|
| 50 | +--
|
|
| 51 | +-- The value is forced to WHNF before it is written.
|
|
| 52 | +writeChan :: Chan a -> a -> IO ()
|
|
| 53 | +writeChan (Chan chan) a = Lazy.writeChan chan =<< (pure $! a)
|
|
| 54 | + |
|
| 55 | +-- | Read the next value from the 'Chan'. Blocks when the channel is empty.
|
|
| 56 | +--
|
|
| 57 | +-- See @readChan@ in "Control.Concurrent.Chan" for the full semantics, in
|
|
| 58 | +-- particular for the fairness guarantees and for the
|
|
| 59 | +-- 'Control.Exception.BlockedIndefinitelyOnMVar' exception.
|
|
| 60 | +readChan :: Chan a -> IO a
|
|
| 61 | +readChan (Chan chan) = Lazy.readChan chan
|
|
| 62 | + |
|
| 63 | +-- | Duplicate a 'Chan'.
|
|
| 64 | +--
|
|
| 65 | +-- See @dupChan@ in "Control.Concurrent.Chan" for the full semantics.
|
|
| 66 | +dupChan :: Chan a -> IO (Chan a)
|
|
| 67 | +dupChan (Chan chan) = Chan <$> Lazy.dupChan chan
|
|
| 68 | + |
|
| 69 | +-- | Return a lazy list representing the contents of the supplied 'Chan'.
|
|
| 70 | +--
|
|
| 71 | +-- See @getChanContents@ in "Control.Concurrent.Chan" for the full semantics.
|
|
| 72 | +getChanContents :: Chan a -> IO [a]
|
|
| 73 | +getChanContents (Chan chan) = Lazy.getChanContents chan
|
|
| 74 | + |
|
| 75 | +-- | Write an entire list of items to a 'Chan'.
|
|
| 76 | +--
|
|
| 77 | +-- See @writeList2Chan@ in "Control.Concurrent.Chan" for the full semantics.
|
|
| 78 | +--
|
|
| 79 | +-- Evaluates the list elements to WHNF.
|
|
| 80 | +writeList2Chan :: Chan a -> [a] -> IO ()
|
|
| 81 | +writeList2Chan = mapM_ . writeChan |
| ... | ... | @@ -65,6 +65,11 @@ |
| 65 | 65 | -- in an 'MVar' to the appropriate normal form, or utilize a strict
|
| 66 | 66 | -- @MVar@ provided by the [strict-concurrency](https://hackage.haskell.org/package/strict-concurrency) package.
|
| 67 | 67 | --
|
| 68 | +-- A variant that forces its contents to weak head normal form is available in
|
|
| 69 | +-- "Control.Concurrent.MVar.Strict". Note that weak head normal form is often
|
|
| 70 | +-- not the appropriate normal form: it does not force the fields of the
|
|
| 71 | +-- constructor it exposes.
|
|
| 72 | +--
|
|
| 68 | 73 | -- === Ordering
|
| 69 | 74 | --
|
| 70 | 75 | -- 'MVar' operations are always observed to take place in the order
|
| 1 | +{-# LANGUAGE DerivingStrategies #-}
|
|
| 2 | +{-# LANGUAGE TupleSections #-}
|
|
| 3 | + |
|
| 4 | +-- | Synchronising variables whose contents are always in weak head normal form
|
|
| 5 | +-- (WHNF).
|
|
| 6 | +--
|
|
| 7 | +-- This module is a thin wrapper around "Control.Concurrent.MVar": each
|
|
| 8 | +-- operation has the same semantics as its counterpart there, except that the
|
|
| 9 | +-- value put in the variable is forced to WHNF first.
|
|
| 10 | +-- "Control.Concurrent.MVar" is the reference documentation for these
|
|
| 11 | +-- operations — for the fairness and ordering guarantees, for the atomicity
|
|
| 12 | +-- caveats of the \"bigger\" functions, and for worked examples; only the
|
|
| 13 | +-- differences are repeated here.
|
|
| 14 | +--
|
|
| 15 | +-- Note that WHNF is not deep: putting @Just <thunk>@ in an
|
|
| 16 | +-- @'MVar' (Maybe a)@ still puts a thunk inside the @Just@.
|
|
| 17 | + |
|
| 18 | +module Control.Concurrent.MVar.Strict
|
|
| 19 | + ( MVar
|
|
| 20 | + |
|
| 21 | + -- * Operations
|
|
| 22 | + , newEmptyMVar
|
|
| 23 | + , newMVar
|
|
| 24 | + , takeMVar
|
|
| 25 | + , putMVar
|
|
| 26 | + , readMVar
|
|
| 27 | + , swapMVar
|
|
| 28 | + , tryTakeMVar
|
|
| 29 | + , tryPutMVar
|
|
| 30 | + , tryReadMVar
|
|
| 31 | + , isEmptyMVar
|
|
| 32 | + , withMVar
|
|
| 33 | + , withMVarMasked
|
|
| 34 | + , modifyMVar_
|
|
| 35 | + , modifyMVar
|
|
| 36 | + , modifyMVarMasked_
|
|
| 37 | + , modifyMVarMasked
|
|
| 38 | + , mkWeakMVar
|
|
| 39 | + ) where
|
|
| 40 | + |
|
| 41 | +import Data.Functor
|
|
| 42 | +import GHC.Base
|
|
| 43 | +import GHC.Weak (Weak)
|
|
| 44 | +import qualified Control.Concurrent.MVar as Lazy
|
|
| 45 | + |
|
| 46 | +-- | A synchronising variable, wrapping @MVar@ from "Control.Concurrent.MVar".
|
|
| 47 | +-- It can be thought of as a box, which may be empty or full.
|
|
| 48 | +--
|
|
| 49 | +-- The value it holds is always in WHNF: every operation of this module forces
|
|
| 50 | +-- the value before it is put in the box.
|
|
| 51 | +newtype MVar a = MVar (Lazy.MVar a)
|
|
| 52 | + deriving stock (Eq)
|
|
| 53 | + |
|
| 54 | +-- | Create an 'MVar' which is initially empty.
|
|
| 55 | +--
|
|
| 56 | +-- See @newEmptyMVar@ in "Control.Concurrent.MVar" for the full semantics.
|
|
| 57 | +newEmptyMVar :: IO (MVar a)
|
|
| 58 | +newEmptyMVar = MVar <$> Lazy.newEmptyMVar
|
|
| 59 | + |
|
| 60 | +-- | Create an 'MVar' which contains the supplied value.
|
|
| 61 | +--
|
|
| 62 | +-- See @newMVar@ in "Control.Concurrent.MVar" for the full semantics.
|
|
| 63 | +--
|
|
| 64 | +-- The initial value is forced to WHNF before it is stored.
|
|
| 65 | +newMVar :: a -> IO (MVar a)
|
|
| 66 | +newMVar a = fmap MVar . Lazy.newMVar =<< (pure $! a)
|
|
| 67 | + |
|
| 68 | +-- | Return the contents of the 'MVar', leaving it empty. Blocks while the
|
|
| 69 | +-- 'MVar' is empty.
|
|
| 70 | +--
|
|
| 71 | +-- See @takeMVar@ in "Control.Concurrent.MVar" for the full semantics, in
|
|
| 72 | +-- particular for the single-wakeup and FIFO guarantees.
|
|
| 73 | +takeMVar :: MVar a -> IO a
|
|
| 74 | +takeMVar (MVar var) = Lazy.takeMVar var
|
|
| 75 | + |
|
| 76 | +-- | Atomically read the contents of an 'MVar'. Blocks while the 'MVar' is
|
|
| 77 | +-- empty.
|
|
| 78 | +--
|
|
| 79 | +-- See @readMVar@ in "Control.Concurrent.MVar" for the full semantics, in
|
|
| 80 | +-- particular for the multiple-wakeup guarantee.
|
|
| 81 | +readMVar :: MVar a -> IO a
|
|
| 82 | +readMVar (MVar var) = Lazy.readMVar var
|
|
| 83 | + |
|
| 84 | +-- | Put a value into an 'MVar'. Blocks while the 'MVar' is full.
|
|
| 85 | +--
|
|
| 86 | +-- See @putMVar@ in "Control.Concurrent.MVar" for the full semantics, in
|
|
| 87 | +-- particular for the single-wakeup and FIFO guarantees.
|
|
| 88 | +--
|
|
| 89 | +-- The value is forced to WHNF before it is put.
|
|
| 90 | +putMVar :: MVar a -> a -> IO ()
|
|
| 91 | +putMVar (MVar var) a = Lazy.putMVar var =<< (pure $! a)
|
|
| 92 | + |
|
| 93 | +-- | A non-blocking version of 'takeMVar'.
|
|
| 94 | +--
|
|
| 95 | +-- See @tryTakeMVar@ in "Control.Concurrent.MVar" for the full semantics.
|
|
| 96 | +tryTakeMVar :: MVar a -> IO (Maybe a)
|
|
| 97 | +tryTakeMVar (MVar var) = Lazy.tryTakeMVar var
|
|
| 98 | + |
|
| 99 | +-- | A non-blocking version of 'putMVar'.
|
|
| 100 | +--
|
|
| 101 | +-- See @tryPutMVar@ in "Control.Concurrent.MVar" for the full semantics.
|
|
| 102 | +--
|
|
| 103 | +-- The value is forced to WHNF before it is put. Note that it is forced whether
|
|
| 104 | +-- or not the 'MVar' turns out to be empty.
|
|
| 105 | +tryPutMVar :: MVar a -> a -> IO Bool
|
|
| 106 | +tryPutMVar (MVar var) a = Lazy.tryPutMVar var =<< (pure $! a)
|
|
| 107 | + |
|
| 108 | +-- | Take a value from an 'MVar', put a new value into the 'MVar' and return the
|
|
| 109 | +-- value taken.
|
|
| 110 | +--
|
|
| 111 | +-- See @swapMVar@ in "Control.Concurrent.MVar" for the full semantics, in
|
|
| 112 | +-- particular for its atomicity caveat.
|
|
| 113 | +--
|
|
| 114 | +-- The new value is forced to WHNF before it is put.
|
|
| 115 | +swapMVar :: MVar a -> a -> IO a
|
|
| 116 | +swapMVar (MVar var) a = Lazy.swapMVar var =<< (pure $! a)
|
|
| 117 | + |
|
| 118 | +-- | A non-blocking version of 'readMVar'.
|
|
| 119 | +--
|
|
| 120 | +-- See @tryReadMVar@ in "Control.Concurrent.MVar" for the full semantics.
|
|
| 121 | +tryReadMVar :: MVar a -> IO (Maybe a)
|
|
| 122 | +tryReadMVar (MVar var) = Lazy.tryReadMVar var
|
|
| 123 | + |
|
| 124 | +-- | Check whether a given 'MVar' is empty.
|
|
| 125 | +--
|
|
| 126 | +-- See @isEmptyMVar@ in "Control.Concurrent.MVar" for the full semantics. The
|
|
| 127 | +-- result is only a snapshot, so prefer 'tryTakeMVar' where possible.
|
|
| 128 | +isEmptyMVar :: MVar a -> IO Bool
|
|
| 129 | +isEmptyMVar (MVar var) = Lazy.isEmptyMVar var
|
|
| 130 | + |
|
| 131 | +-- | An exception-safe wrapper for operating on the contents of an 'MVar'.
|
|
| 132 | +--
|
|
| 133 | +-- See @withMVar@ in "Control.Concurrent.MVar" for the full semantics, in
|
|
| 134 | +-- particular for its atomicity caveat.
|
|
| 135 | +withMVar :: MVar a -> (a -> IO b) -> IO b
|
|
| 136 | +withMVar (MVar var) action = Lazy.withMVar var action
|
|
| 137 | +{-# INLINE withMVar #-}
|
|
| 138 | + |
|
| 139 | +-- | Like 'withMVar', but the @IO@ action in the second argument is executed
|
|
| 140 | +-- with asynchronous exceptions masked.
|
|
| 141 | +--
|
|
| 142 | +-- See @withMVarMasked@ in "Control.Concurrent.MVar" for the full semantics.
|
|
| 143 | +withMVarMasked :: MVar a -> (a -> IO b) -> IO b
|
|
| 144 | +withMVarMasked (MVar var) action = Lazy.withMVarMasked var action
|
|
| 145 | +{-# INLINE withMVarMasked #-}
|
|
| 146 | + |
|
| 147 | +-- | An exception-safe wrapper for modifying the contents of an 'MVar'.
|
|
| 148 | +--
|
|
| 149 | +-- See @modifyMVar_@ in "Control.Concurrent.MVar" for the full semantics, in
|
|
| 150 | +-- particular for its atomicity caveat.
|
|
| 151 | +--
|
|
| 152 | +-- The new value is forced to WHNF before it is put back.
|
|
| 153 | +modifyMVar_ :: MVar a -> (a -> IO a) -> IO ()
|
|
| 154 | +modifyMVar_ (MVar var) action = Lazy.modifyMVar_ var $ \a0 -> do
|
|
| 155 | + a <- action a0
|
|
| 156 | + pure $! a
|
|
| 157 | +{-# INLINE modifyMVar_ #-}
|
|
| 158 | + |
|
| 159 | +-- | A slight variation on 'modifyMVar_' that allows a value to be returned
|
|
| 160 | +-- (@b@) in addition to the modified value of the 'MVar'.
|
|
| 161 | +--
|
|
| 162 | +-- See @modifyMVar@ in "Control.Concurrent.MVar" for the full semantics.
|
|
| 163 | +--
|
|
| 164 | +-- The new value is forced to WHNF before it is put back. The returned value is
|
|
| 165 | +-- not forced.
|
|
| 166 | +modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b
|
|
| 167 | +modifyMVar (MVar var) action = Lazy.modifyMVar var $ \a0 -> do
|
|
| 168 | + (a, b) <- action a0
|
|
| 169 | + (, b) <$> (pure $! a)
|
|
| 170 | +{-# INLINE modifyMVar #-}
|
|
| 171 | + |
|
| 172 | +-- | Like 'modifyMVar_', but the @IO@ action in the second argument is executed
|
|
| 173 | +-- with asynchronous exceptions masked.
|
|
| 174 | +--
|
|
| 175 | +-- See @modifyMVarMasked_@ in "Control.Concurrent.MVar" for the full semantics.
|
|
| 176 | +--
|
|
| 177 | +-- The new value is forced to WHNF before it is put back.
|
|
| 178 | +modifyMVarMasked_ :: MVar a -> (a -> IO a) -> IO ()
|
|
| 179 | +modifyMVarMasked_ (MVar var) action = Lazy.modifyMVarMasked_ var $ \a0 -> do
|
|
| 180 | + a <- action a0
|
|
| 181 | + pure $! a
|
|
| 182 | +{-# INLINE modifyMVarMasked_ #-}
|
|
| 183 | + |
|
| 184 | +-- | Like 'modifyMVar', but the @IO@ action in the second argument is executed
|
|
| 185 | +-- with asynchronous exceptions masked.
|
|
| 186 | +--
|
|
| 187 | +-- See @modifyMVarMasked@ in "Control.Concurrent.MVar" for the full semantics.
|
|
| 188 | +--
|
|
| 189 | +-- The new value is forced to WHNF before it is put back. The returned value is
|
|
| 190 | +-- not forced.
|
|
| 191 | +modifyMVarMasked :: MVar a -> (a -> IO (a, b)) -> IO b
|
|
| 192 | +modifyMVarMasked (MVar var) action = Lazy.modifyMVarMasked var $ \a0 -> do
|
|
| 193 | + (a, b) <- action a0
|
|
| 194 | + (, b) <$> (pure $! a)
|
|
| 195 | +{-# INLINE modifyMVarMasked #-}
|
|
| 196 | + |
|
| 197 | +-- | Make a 'Weak' pointer to an 'MVar', using the second argument as a
|
|
| 198 | +-- finalizer to run when the 'MVar' is garbage-collected.
|
|
| 199 | +--
|
|
| 200 | +-- See @mkWeakMVar@ in "Control.Concurrent.MVar" for the full semantics.
|
|
| 201 | +mkWeakMVar :: MVar a -> IO () -> IO (Weak (MVar a))
|
|
| 202 | +mkWeakMVar (MVar var) finalizer = coerce <$> Lazy.mkWeakMVar var finalizer |
| ... | ... | @@ -12,6 +12,8 @@ |
| 12 | 12 | --
|
| 13 | 13 | -- Mutable references in the IO monad.
|
| 14 | 14 | --
|
| 15 | +-- A variant that forces its contents to weak head normal form is available in
|
|
| 16 | +-- "Data.IORef.Strict".
|
|
| 15 | 17 | |
| 16 | 18 | module Data.IORef
|
| 17 | 19 | (-- * IORefs
|
| 1 | +{-# LANGUAGE DerivingStrategies #-}
|
|
| 2 | + |
|
| 3 | +-- | Mutable references in the IO monad whose contents are always in weak head
|
|
| 4 | +-- normal form (WHNF).
|
|
| 5 | +--
|
|
| 6 | +-- This module is a thin wrapper around "Data.IORef": each operation has the
|
|
| 7 | +-- same semantics as its counterpart there, except that the value put in the
|
|
| 8 | +-- reference is forced to WHNF first. "Data.IORef" is the reference
|
|
| 9 | +-- documentation for these operations; only the differences are repeated here.
|
|
| 10 | +--
|
|
| 11 | +-- Note that WHNF is not deep: storing @Just <thunk>@ in an
|
|
| 12 | +-- @'IORef' (Maybe a)@ still stores a thunk inside the @Just@.
|
|
| 13 | + |
|
| 14 | +module Data.IORef.Strict
|
|
| 15 | + ( IORef
|
|
| 16 | + |
|
| 17 | + -- * Operations
|
|
| 18 | + , newIORef
|
|
| 19 | + , readIORef
|
|
| 20 | + , writeIORef
|
|
| 21 | + , modifyIORef
|
|
| 22 | + , atomicModifyIORef
|
|
| 23 | + , atomicWriteIORef
|
|
| 24 | + , mkWeakIORef
|
|
| 25 | + ) where
|
|
| 26 | + |
|
| 27 | +import GHC.Base
|
|
| 28 | +import GHC.Weak (Weak)
|
|
| 29 | +import qualified Data.IORef as Lazy
|
|
| 30 | + |
|
| 31 | +-- | A mutable reference in the IO monad, wrapping @IORef@ from "Data.IORef".
|
|
| 32 | +--
|
|
| 33 | +-- The value it holds is always in WHNF: every operation of this module forces
|
|
| 34 | +-- the value before it is written.
|
|
| 35 | +newtype IORef a = IORef (Lazy.IORef a)
|
|
| 36 | + deriving stock (Eq)
|
|
| 37 | + |
|
| 38 | +-- | Build a new 'IORef'.
|
|
| 39 | +--
|
|
| 40 | +-- See @newIORef@ in "Data.IORef" for the full semantics.
|
|
| 41 | +--
|
|
| 42 | +-- The initial value is forced to WHNF before it is stored.
|
|
| 43 | +newIORef :: a -> IO (IORef a)
|
|
| 44 | +newIORef a = fmap IORef . Lazy.newIORef =<< (pure $! a)
|
|
| 45 | + |
|
| 46 | +-- | Read the value of an 'IORef'.
|
|
| 47 | +--
|
|
| 48 | +-- See @readIORef@ in "Data.IORef" for the full semantics, in particular for the
|
|
| 49 | +-- memory model that applies to reads and writes.
|
|
| 50 | +readIORef :: IORef a -> IO a
|
|
| 51 | +readIORef (IORef var) = Lazy.readIORef var
|
|
| 52 | + |
|
| 53 | +-- | Write a new value into an 'IORef'.
|
|
| 54 | +--
|
|
| 55 | +-- See @writeIORef@ in "Data.IORef" for the full semantics. This function does
|
|
| 56 | +-- not create a memory barrier; use 'atomicWriteIORef' when that matters.
|
|
| 57 | +--
|
|
| 58 | +-- The new value is forced to WHNF before it is stored.
|
|
| 59 | +writeIORef :: IORef a -> a -> IO ()
|
|
| 60 | +writeIORef (IORef var) a = Lazy.writeIORef var =<< (pure $! a)
|
|
| 61 | + |
|
| 62 | +-- | Mutate the contents of an 'IORef', combining 'readIORef' and 'writeIORef'.
|
|
| 63 | +--
|
|
| 64 | +-- See @modifyIORef@ in "Data.IORef" for the full semantics. This is not an
|
|
| 65 | +-- atomic update; use 'atomicModifyIORef' in a multithreaded environment.
|
|
| 66 | +--
|
|
| 67 | +-- The new value is forced to WHNF before it is stored.
|
|
| 68 | +modifyIORef :: IORef a -> (a -> a) -> IO ()
|
|
| 69 | +modifyIORef (IORef var) f = Lazy.modifyIORef' var f
|
|
| 70 | + |
|
| 71 | +-- | Atomically modify the contents of an 'IORef'.
|
|
| 72 | +--
|
|
| 73 | +-- See @atomicModifyIORef@ in "Data.IORef" for the full semantics, in particular
|
|
| 74 | +-- for the atomicity and memory-barrier guarantees.
|
|
| 75 | +--
|
|
| 76 | +-- Both the new value and the returned value are forced to WHNF.
|
|
| 77 | +atomicModifyIORef :: IORef a -> (a -> (a, b)) -> IO b
|
|
| 78 | +atomicModifyIORef (IORef var) f = Lazy.atomicModifyIORef' var f
|
|
| 79 | + |
|
| 80 | +-- | Variant of 'writeIORef' that imposes a reordering barrier.
|
|
| 81 | +--
|
|
| 82 | +-- See @atomicWriteIORef@ in "Data.IORef" for the full semantics.
|
|
| 83 | +--
|
|
| 84 | +-- The new value is forced to WHNF before it is stored.
|
|
| 85 | +atomicWriteIORef :: IORef a -> a -> IO ()
|
|
| 86 | +atomicWriteIORef (IORef var) a = Lazy.atomicWriteIORef var =<< (pure $! a)
|
|
| 87 | + |
|
| 88 | +-- | Make a 'Weak' pointer to an 'IORef', using the second argument as a
|
|
| 89 | +-- finalizer to run when the 'IORef' is garbage-collected.
|
|
| 90 | +--
|
|
| 91 | +-- See @mkWeakIORef@ in "Data.IORef" for the full semantics.
|
|
| 92 | +mkWeakIORef :: IORef a -> IO () -> IO (Weak (IORef a))
|
|
| 93 | +mkWeakIORef (IORef var) finalizer = fmap coerce (Lazy.mkWeakIORef var finalizer) |