
Krzysztof Skrzętnicki wrote:
2009/5/27 Bertram Felgenhauer
: I wrote:
Krzysztof Skrzętnicki wrote:
The code for modifying the counter: (\ msg -> atomicModifyIORef ioref (\ cnt -> (cntMsg cnt msg,())))
atomicModifyIORef does not force the new value of the IORef. If the previous contents of the IORef is x, the new contents will be a thunk,
(\ cnt -> (cntMsg cnt msg,())) x
Sorry, it's slightly worse than that. The contents becomes
sel_0 (\ cnt -> (cntMsg cnt msg, ())) x
where sel_0 is basically an RTS internal version of fst.
Instead of reading the new value of the IORef, you could also force the old one:
atomicModifyIORef ioref (\ cnt -> (cntMsg cnt msg, msg)) >>= (return $!)
Thanks for the tip, although it seems tricky to get it right. I wonder why there is no strict version of atomicModifyIORef?
Something like this? -- | Stricter version of 'atomicModifyIORef', which prevents building -- up huge thunks in the 'IORef' due to repeated modification. -- Unlike 'atomicModifyIORef', 'atomicModifyIORef'' may block. atomicModifyIORef' :: IORef a -> (a -> (a, b)) -> IO b atomicModifyIORef' ioref f = do res <- atomicModifyIORef ioref f new <- readIORef ioref new `seq` return res (The step that may block is forcing the new value - if another thread is already evaluating part of the thunk, the currently executing thread will block, waiting for the other thread to finish.)
Dually there might be a strict version of IORef datatype.
One interesting feature of atomicModifyIORef is that its implementation is lock-free, and never blocks (which affects exception handling): replacing the old value by the new value is done with compare-and-swap operation in a tight loop. Each iteration executes very quickly because all it does is replace the reference to the old value in the new thunk. With a strict IORef, the time window between reading the old value and storing the new value would become arbitrarily large, because you'd have to force the new value before exchanging it with the old value. So a reasonable implementation would have to use locks instead, I think, making atomicModifyIORef more expensive, and less useful in contexts that block exceptions. Bertram