Search code examples
haskellthread-safetyioref

When sharing an IORef, is it safe to read with readIORef as long as I'm writing with atomicModifyIORef?


If I share an IORef among multiple threads, and use atomicModifyIORef to write to it:

atomicModifyIORef ref (\_ -> (new, ()))

Is it safe to read the value with plain old readIORef? Or is there a chance readIORef will return the old value in another thread after atomicModifyIORef has modified it?

I think that's what the documentation implies:

atomicModifyIORef acts as a barrier to reordering. Multiple atomicModifyIORef operations occur in strict program order. An atomicModifyIORef is never observed to take place ahead of any earlier (in program order) IORef operations, or after any later IORef operations.

I just want to be sure.


Solution

  • atomicModifyIORef guarantees that nothing happens between the atomic read and the following atomic write, thus making the whole operation atomic. The comment that you quoted just states that no atomicModifyIORefs will ever happen in parallel, and that the optimizer won't try to reorder the statements to optimize the program (separate reads and writes can safely be moved around in some cases; for example in a' <- read a; b' <- read b; write c $ a' + b', the reads can safely be reordered)

    readIORef is already atomic, since it only performs one operation.

    However, you are discussing a different issue. Yes, if the atomicModify happens at t=3ms and the read happens at t=4ms, you will get the modified value. However; threads aren't guaranteed to run in parallel, so if you do (pseudocode):

    forkIO $ do
      sleep 100 ms
      atomicModify
    sleep 1000 ms
    read
    

    ... there's no guarantee that the read will happen after the modify (it's extremely unlikely on modern OS's, though), because the operating system might decide to schedule the new short-lived thread in such a way that it doesn't happen in parallel.