@@ -21,6 +21,9 @@ module Data.Atomics
2121
2222 -- * Atomic operations on mutable arrays
2323 casArrayElem , casArrayElem2 , readArrayElem ,
24+ atomicModifyArrayElem_ ,
25+ atomicModifyArrayElem ,
26+ atomicModifyArrayElem' ,
2427
2528 -- * Atomic operations on byte arrays
2629 casByteArrayInt ,
@@ -62,6 +65,7 @@ import GHC.Prim
6265import GHC.Base (Int (I #))
6366import GHC.IO (IO (IO ))
6467-- import GHC.Word (Word(W#))
68+ import System.IO.Unsafe (unsafeDupablePerformIO )
6569
6670
6771#if MIN_VERSION_base(4,8,0)
@@ -70,6 +74,8 @@ import Data.Bits
7074import Data.Primitive.ByteArray (readByteArray )
7175#endif
7276
77+ import GHC.Exts (lazy )
78+
7379#ifdef DEBUG_ATOMICS
7480#warning "Activating DEBUG_ATOMICS... NOINLINE's and more"
7581{-# NOINLINE seal #-}
@@ -134,6 +140,73 @@ casArrayElem2 (MutableArray arr#) (I# i#) old new = IO$ \s1# ->
134140 case casArrayTicketed# arr# i# old new s1# of
135141 (# s2# , x# , res # ) -> (# s2# , (x# ==# 0 # , res) # )
136142
143+ -- | A version of 'atomicModifyIORef' for arrays that returns
144+ -- /both/ the new value and the result. This function is very
145+ -- lazy; in particular,
146+ --
147+ -- @ atomicModifyArrayElem_ mary i (const undefined) @
148+ --
149+ -- will succeed, although both the new element and the result will
150+ -- be undefined.
151+ --
152+ atomicModifyArrayElem_ :: forall a b . MutableArray RealWorld a
153+ -> Int
154+ -> (a -> (a , b ))
155+ -> IO (a , b )
156+ atomicModifyArrayElem_ mary i fn = do
157+ original <- readArrayElem mary i
158+ oldref <- newIORef original
159+ let
160+ nr@ (new, _) = unsafeDupablePerformIO $ fn . peekTicket <$> readIORef oldref
161+ loop :: Ticket a -> IO (a , b )
162+ loop tick = do
163+ (b,tick') <- casArrayElem2 mary i tick (seal new)
164+ -- We must be *lazy* here;
165+ -- neither new nor nr may be
166+ -- forced until the CAS succeeds.
167+ if b
168+ then do
169+ -- lazy to prevent demand analysis from forcing it early.
170+ return (lazy nr)
171+ else do
172+ writeIORef oldref tick'
173+ loop tick'
174+ loop original
175+
176+ -- | A version of 'atomicModifyIORef' for arrays. Unlike 'atomicModifyIORef',
177+ -- the user function is applied eagerly. In particular,
178+ --
179+ -- @atomicModifyArrayElem mary i (const undefined)@
180+ --
181+ -- will throw an exception immediately.
182+ atomicModifyArrayElem :: forall a b . MutableArray RealWorld a
183+ -> Int
184+ -> (a -> (a , b ))
185+ -> IO b
186+ -- We should ideally implement this in CMM to avoid the extra
187+ -- IORef and such.
188+ atomicModifyArrayElem mary i fn = do
189+ (_new, res) <- atomicModifyArrayElem_ mary i fn
190+ return res
191+
192+ -- | A version of 'atomicModifyArrayElem' that forces the stored
193+ -- value to WHNF. This is *lazier* than 'atomicModifyIORef''; in
194+ -- particular, it does not force the result value.
195+ --
196+ -- @
197+ -- atomicModifyArrayElem' mary i f =
198+ -- atomicModifyArrayElem mary i (\a -> case f a of (!a', b) -> (a', b))
199+ -- @
200+ atomicModifyArrayElem' :: forall a b . MutableArray RealWorld a
201+ -> Int
202+ -> (a -> (a , b ))
203+ -> IO b
204+ atomicModifyArrayElem' mary i fn = do
205+ (new, res) <- atomicModifyArrayElem_ mary i fn
206+ evaluate new
207+ return res
208+
209+
137210-- | Ordinary processor load instruction (non-atomic, not implying any memory barriers).
138211readArrayElem :: forall a . MutableArray RealWorld a -> Int -> IO (Ticket a )
139212-- readArrayElem = unsafeCoerce# readArray#
0 commit comments