Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC

Commits:

12 changed files:

Changes:

  • changelog.d/clc-341-strict-mutable-data-types
    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
    +}

  • libraries/base/base.cabal.in
    ... ... @@ -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
    

  • libraries/base/changelog.md
    ... ... @@ -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))
    

  • libraries/base/src/Control/Concurrent/Chan.hs
    ... ... @@ -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
    

  • libraries/base/src/Control/Concurrent/Chan/Strict.hs
    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

  • libraries/base/src/Control/Concurrent/MVar.hs
    ... ... @@ -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
    

  • libraries/base/src/Control/Concurrent/MVar/Strict.hs
    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

  • libraries/base/src/Data/IORef.hs
    ... ... @@ -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
    

  • libraries/base/src/Data/IORef/Strict.hs
    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)

  • testsuite/tests/interface-stability/base-exports.stdout
    No preview for this file type
  • testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
    No preview for this file type
  • testsuite/tests/interface-stability/base-exports.stdout-mingw32
    No preview for this file type