summaryrefslogtreecommitdiff
path: root/libraries/base/Data/IORef.hs
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/base/Data/IORef.hs')
-rw-r--r--libraries/base/Data/IORef.hs162
1 files changed, 162 insertions, 0 deletions
diff --git a/libraries/base/Data/IORef.hs b/libraries/base/Data/IORef.hs
new file mode 100644
index 0000000000..0e5717cb7c
--- /dev/null
+++ b/libraries/base/Data/IORef.hs
@@ -0,0 +1,162 @@
+{-# LANGUAGE Trustworthy #-}
+{-# LANGUAGE CPP, NoImplicitPrelude, MagicHash, UnboxedTuples #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : Data.IORef
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer : libraries@haskell.org
+-- Stability : experimental
+-- Portability : portable
+--
+-- Mutable references in the IO monad.
+--
+-----------------------------------------------------------------------------
+
+module Data.IORef
+ (
+ -- * IORefs
+ IORef, -- abstract, instance of: Eq, Typeable
+ newIORef,
+ readIORef,
+ writeIORef,
+ modifyIORef,
+ modifyIORef',
+ atomicModifyIORef,
+ atomicModifyIORef',
+ atomicWriteIORef,
+
+#if !defined(__PARALLEL_HASKELL__)
+ mkWeakIORef,
+#endif
+ -- ** Memory Model
+
+ -- $memmodel
+
+ ) where
+
+import GHC.Base
+import GHC.STRef
+import GHC.IORef hiding (atomicModifyIORef)
+import qualified GHC.IORef
+#if !defined(__PARALLEL_HASKELL__)
+import GHC.Weak
+#endif
+
+#if !defined(__PARALLEL_HASKELL__)
+-- |Make a 'Weak' pointer to an 'IORef', using the second argument as a finalizer
+-- to run when 'IORef' is garbage-collected
+mkWeakIORef :: IORef a -> IO () -> IO (Weak (IORef a))
+mkWeakIORef r@(IORef (STRef r#)) f = IO $ \s ->
+ case mkWeak# r# r f s of (# s1, w #) -> (# s1, Weak w #)
+#endif
+
+-- |Mutate the contents of an 'IORef'.
+--
+-- Be warned that 'modifyIORef' does not apply the function strictly. This
+-- means if the program calls 'modifyIORef' many times, but seldomly uses the
+-- value, thunks will pile up in memory resulting in a space leak. This is a
+-- common mistake made when using an IORef as a counter. For example, the
+-- following will likely produce a stack overflow:
+--
+-- >ref <- newIORef 0
+-- >replicateM_ 1000000 $ modifyIORef ref (+1)
+-- >readIORef ref >>= print
+--
+-- To avoid this problem, use 'modifyIORef'' instead.
+modifyIORef :: IORef a -> (a -> a) -> IO ()
+modifyIORef ref f = readIORef ref >>= writeIORef ref . f
+
+-- |Strict version of 'modifyIORef'
+--
+-- /Since: 4.6.0.0/
+modifyIORef' :: IORef a -> (a -> a) -> IO ()
+modifyIORef' ref f = do
+ x <- readIORef ref
+ let x' = f x
+ x' `seq` writeIORef ref x'
+
+-- |Atomically modifies the contents of an 'IORef'.
+--
+-- This function is useful for using 'IORef' in a safe way in a multithreaded
+-- program. If you only have one 'IORef', then using 'atomicModifyIORef' to
+-- access and modify it will prevent race conditions.
+--
+-- Extending the atomicity to multiple 'IORef's is problematic, so it
+-- is recommended that if you need to do anything more complicated
+-- then using 'Control.Concurrent.MVar.MVar' instead is a good idea.
+--
+-- 'atomicModifyIORef' does not apply the function strictly. This is important
+-- to know even if all you are doing is replacing the value. For example, this
+-- will leak memory:
+--
+-- >ref <- newIORef '1'
+-- >forever $ atomicModifyIORef ref (\_ -> ('2', ()))
+--
+-- Use 'atomicModifyIORef'' or 'atomicWriteIORef' to avoid this problem.
+--
+atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b
+atomicModifyIORef = GHC.IORef.atomicModifyIORef
+
+-- | Strict version of 'atomicModifyIORef'. This forces both the value stored
+-- in the 'IORef' as well as the value returned.
+--
+-- /Since: 4.6.0.0/
+atomicModifyIORef' :: IORef a -> (a -> (a,b)) -> IO b
+atomicModifyIORef' ref f = do
+ b <- atomicModifyIORef ref
+ (\x -> let (a, b) = f x
+ in (a, a `seq` b))
+ b `seq` return b
+
+-- | Variant of 'writeIORef' with the \"barrier to reordering\" property that
+-- 'atomicModifyIORef' has.
+--
+-- /Since: 4.6.0.0/
+atomicWriteIORef :: IORef a -> a -> IO ()
+atomicWriteIORef ref a = do
+ x <- atomicModifyIORef ref (\_ -> (a, ()))
+ x `seq` return ()
+
+{- $memmodel
+
+ In a concurrent program, 'IORef' operations may appear out-of-order
+ to another thread, depending on the memory model of the underlying
+ processor architecture. For example, on x86, loads can move ahead
+ of stores, so in the following example:
+
+> maybePrint :: IORef Bool -> IORef Bool -> IO ()
+> maybePrint myRef yourRef = do
+> writeIORef myRef True
+> yourVal <- readIORef yourRef
+> unless yourVal $ putStrLn "critical section"
+>
+> main :: IO ()
+> main = do
+> r1 <- newIORef False
+> r2 <- newIORef False
+> forkIO $ maybePrint r1 r2
+> forkIO $ maybePrint r2 r1
+> threadDelay 1000000
+
+ it is possible that the string @"critical section"@ is printed
+ twice, even though there is no interleaving of the operations of the
+ two threads that allows that outcome. The memory model of x86
+ allows 'readIORef' to happen before the earlier 'writeIORef'.
+
+ The implementation is required to ensure that reordering of memory
+ operations cannot cause type-correct code to go wrong. In
+ particular, when inspecting the value read from an 'IORef', the
+ memory writes that created that value must have occurred from the
+ point of view of the current thread.
+
+ 'atomicModifyIORef' acts as a barrier to reordering. Multiple
+ 'atomicModifyIORef' operations occur in strict program order. An
+ 'atomicModifyIORef' is never observed to take place ahead of any
+ earlier (in program order) 'IORef' operations, or after any later
+ 'IORef' operations.
+
+-}
+