[Haskell-cafe] IORef memory leak

Gregory Collins greg at gregorycollins.net
Fri Oct 15 16:31:11 EDT 2010

Evan Laforge <qdunkan at gmail.com> writes:

> The only workaround I could find is to immediately read the value back
> out and 'seq' on it, but it's ugly.

Yep! C'est la vie unfortunately.

The way atomicModifyIORef works is that the new value isn't actually
evaluated at all; GHC just swaps the old value with a thunk which will
do the modification when the value is demanded.

It's done like that so that the atomic modification can be done with a
compare-and-swap CPU instruction; a fully-fledged lock would have to be
taken otherwise, because your function could do an unbounded amount of
work. While that's happening, other mutator threads could be writing
into your memory cell, having read the same old value you did, and then
*splat*, the souffle is ruined.

Once you're taking a lock, you've got yourself an MVar. This is why
IORefs are generally (always?) faster than MVars under contention; the
lighter-weight lock mechanism means mutator threads don't block, if the
CAS fails atomicModifyIORef just tries again in a busy loop. (I think!)

Of course, the mutator threads themselves then tend to bump into each
other or do redundant work when it's time to evaluate the thunks (GHC
tries to avoid this using "thunk blackholing"). Contention issues here
have gotten radically better in recent versions of GHC I think.

Forgive me if I've gotten anything wrong here, I think Simon Marlow
might be the only person who *really* understands how all this stuff
works. :)

> So two questions:
> writeIORef doesn't have this problem.  If I am just writing a simple
> value, is writeIORef atomic?  In other words, can I replace
> 'atomicModifyIORef r (const (x, ())' with 'writeIORef r x'?
> Any reason to not do solution 1 above?

Well if you're not inspecting or using the old value then it's safe to
just blow it away, yes.


Gregory Collins <greg at gregorycollins.net>

More information about the Haskell-Cafe mailing list