>>> import GHC.Internal.Data.IORef >>> r <- newIORef 0 >>> readIORef r 0 >>> writeIORef r 1 >>> readIORef r 1 >>> atomicWriteIORef r 2 >>> readIORef r 2 >>> modifyIORef' r (+ 1) >>> readIORef r 3 >>> atomicModifyIORef' r (\a -> (a + 1, ())) >>> readIORef r 4See also STRef and MVar.
>>> import GHC.Internal.Data.IORef >>> r <- newIORef 0 >>> readIORef r 0 >>> writeIORef r 1 >>> readIORef r 1 >>> atomicWriteIORef r 2 >>> readIORef r 2 >>> modifyIORef' r (+ 1) >>> readIORef r 3 >>> atomicModifyIORef' r (\a -> (a + 1, ())) >>> readIORef r 4See also STRef and MVar.
>>> import Data.IORef >>> r <- newIORef 0 >>> readIORef r 0 >>> writeIORef r 1 >>> readIORef r 1 >>> atomicWriteIORef r 2 >>> readIORef r 2 >>> modifyIORef' r (+ 1) >>> readIORef r 3 >>> atomicModifyIORef' r (\a -> (a + 1, ())) >>> readIORef r 4See also STRef and MVar.
>>> import Data.IORef >>> r <- newIORef 0 >>> readIORef r 0 >>> writeIORef r 1 >>> readIORef r 1 >>> atomicWriteIORef r 2 >>> readIORef r 2 >>> modifyIORef' r (+ 1) >>> readIORef r 3 >>> atomicModifyIORef' r (\a -> (a + 1, ())) >>> readIORef r 4See also STRef and MVar.
atomicModifyIORef ref f = do -- Begin atomic block old <- readIORef ref let r = f old new = fst r writeIORef ref new -- End atomic block case r of (_new, res) -> pure resThe actions in the section labeled "atomic block" are not subject to interference from other threads. In particular, it is impossible for the value in the IORef to change between the readIORef and writeIORef invocations. The user-supplied function is applied to the value stored in the IORef, yielding a new value to store in the IORef and a value to return. After the new value is (lazily) stored in the IORef, atomicModifyIORef forces the result pair, but does not force either component of the result. To force both components, use atomicModifyIORef'. Note that
atomicModifyIORef ref (_ -> undefined)will raise an exception in the calling thread, but will also install the bottoming value in the IORef, where it may be read by other threads. This function imposes a memory barrier, preventing reordering around the "atomic block"; see Data.IORef#memmodel for details.
atomicModifyIORef' ref f = do -- Begin atomic block old <- readIORef ref let r = f old new = fst r writeIORef ref new -- End atomic block case r of (!_new, !res) -> pure resThe actions in the "atomic block" are not subject to interference by other threads. In particular, the value in the IORef cannot change between the readIORef and writeIORef invocations. The new value is installed in the IORef before either value is forced. So
atomicModifyIORef' ref (x -> (x+1, undefined))will increment the IORef and then throw an exception in the calling thread.
atomicModifyIORef' ref (x -> (undefined, x))and
atomicModifyIORef' ref (_ -> undefined)will each raise an exception in the calling thread, but will also install the bottoming value in the IORef, where it may be read by other threads. This function imposes a memory barrier, preventing reordering around the "atomic block"; see Data.IORef#memmodel for details.
ref <- newIORef 0 replicateM_ 1000000 $ modifyIORef ref (+1) readIORef ref >>= printTo avoid this problem, use modifyIORef' instead.