[Haskell-cafe] Stack overflow

Bertram Felgenhauer bertram.felgenhauer at googlemail.com
Thu May 28 08:41:55 EDT 2009


Krzysztof Skrzętnicki wrote:
> 2009/5/27 Bertram Felgenhauer <bertram.felgenhauer at googlemail.com>:
> > 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


More information about the Haskell-Cafe mailing list