Be warned that modifyIORef does not apply the function strictly. This
means if the program calls modifyIORef many times, but seldomly uses the
value, thunks will pile up in memory resulting in a space leak. This is a
common mistake made when using an IORef as a counter. For example, the
following will likely produce a stack overflow:

Make a Weak pointer to an IORef, using the second argument as a finalizer
to run when IORef is garbage-collected

Memory Model

In a concurrent program, IORef operations may appear out-of-order
to another thread, depending on the memory model of the underlying
processor architecture. For example, on x86, loads can move ahead
of stores, so in the following example:

it is possible that the string "critical section" is printed
twice, even though there is no interleaving of the operations of the
two threads that allows that outcome. The memory model of x86
allows readIORef to happen before the earlier writeIORef.

The implementation is required to ensure that reordering of memory
operations cannot cause type-correct code to go wrong. In
particular, when inspecting the value read from an IORef, the
memory writes that created that value must have occurred from the
point of view of the current thread.