@@ -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,74 @@ 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+ -- We should ideally implement this in CMM to avoid the extra
157+ -- IORef and such. I think the atomicModifyMutVar# primop should
158+ -- really have been given this type.
159+ atomicModifyArrayElem_ mary i fn = do
160+ original <- readArrayElem mary i
161+ oldref <- newIORef original
162+ let
163+ nr@ (new, _) = unsafeDupablePerformIO $ fn . peekTicket <$> readIORef oldref
164+ loop :: Ticket a -> IO (a , b )
165+ loop tick = do
166+ (b,tick') <- casArrayElem2 mary i tick (seal new)
167+ -- We must be *lazy* here;
168+ -- neither new nor nr may be
169+ -- forced until the CAS succeeds.
170+ if b
171+ then do
172+ -- lazy to prevent demand analysis from forcing it early.
173+ return (lazy nr)
174+ else do
175+ writeIORef oldref tick'
176+ loop tick'
177+ loop original
178+
179+ -- | A version of 'atomicModifyIORef' for arrays. Unlike 'atomicModifyIORef',
180+ -- the user function is applied eagerly. In particular,
181+ --
182+ -- @atomicModifyArrayElem mary i (const undefined)@
183+ --
184+ -- will throw an exception immediately.
185+ atomicModifyArrayElem :: forall a b . MutableArray RealWorld a
186+ -> Int
187+ -> (a -> (a , b ))
188+ -> IO b
189+ atomicModifyArrayElem mary i fn = do
190+ (_new, res) <- atomicModifyArrayElem_ mary i fn
191+ return res
192+
193+ -- | A version of 'atomicModifyArrayElem' that forces the stored
194+ -- value to WHNF. This is *lazier* than 'atomicModifyIORef''; in
195+ -- particular, it does not force the result value.
196+ --
197+ -- @
198+ -- atomicModifyArrayElem' mary i f =
199+ -- atomicModifyArrayElem mary i (\a -> case f a of (!a', b) -> (a', b))
200+ -- @
201+ atomicModifyArrayElem' :: forall a b . MutableArray RealWorld a
202+ -> Int
203+ -> (a -> (a , b ))
204+ -> IO b
205+ atomicModifyArrayElem' mary i fn = do
206+ (new, res) <- atomicModifyArrayElem_ mary i fn
207+ evaluate new
208+ return res
209+
210+
137211-- | Ordinary processor load instruction (non-atomic, not implying any memory barriers).
138212readArrayElem :: forall a . MutableArray RealWorld a -> Int -> IO (Ticket a )
139213-- readArrayElem = unsafeCoerce# readArray#
0 commit comments