19

I'm having a little trouble understanding the basic difference between the IORef type and the MVar type in Haskell. Can someone help me out with this? They appear to solve the same problem. MVar seems to be targeted at multithreading, but IORef has the atomicModifyIORef function.

Thanks!

Robin Green
  • 32,079
  • 16
  • 104
  • 187
Litherum
  • 22,564
  • 3
  • 23
  • 27
  • 3
    MVars represent a queue-like abstraction which can take at most one element with blocking semantics, whereas an IORef has always one element in it. – hvr Mar 07 '11 at 07:39

1 Answers1

20

MVar is, like you said, targeted at multithreading while IORef can be used both as mutable variable in a single threaded program or as a synchronization construct in a multithreaded program.

IORef can be used together with atomicModifyIORef to get compare-and-swap (CAS) behavior: writers and readers can synchronize on a single pure value, stored in the IORef. Readers use readIORef to read a value and writers use atomicModifyIORef to write a value. Note that atomicModifyIORef doesn't let writers perform any side effects inside the critical section (i.e. they can only use a pure function when atomically changing the value).

MVar allows you to implement arbitrary critical sections (using withMVar), that may contain side effects. They can also be used just like an IORef (as described in the previous paragraph), but at a higher cost.

If you want an intuition for what kind of semantic IORef implements its the same as the CAS semantics Rich Hickey describes in a talk on Clojure's concurrency model: http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey

Edit: In addition, you cannot run into deadlocks using IORef (but there still might be contention, causing retries).

tibbe
  • 8,809
  • 7
  • 36
  • 64
  • I implemented database connection pool for use in web app. It requires only CAS actions and there's only one object it works with: the list of connections. I am using MVar to hold a Pool. Would you recommend to change it to IORef ? You are saying MVar incurs "a higher cost". Are there any benchmarks ? – Vagif Verdi Mar 07 '11 at 17:17
  • I would use an `IORef`. This is what we use in the GHC I/O manager to store an `IntMap` that maps file descriptors to callbacks. Remember that `IORefs` (and `MVars`) are lazy so you'd have to do something like: `do !_ <- atomicModifyIORef ref (\ m -> let m' = insert k v m in (m', m')` if you want the result to be evaluated in the writer instead of the reader. I don't have any benchmarks handy. – tibbe Mar 07 '11 at 19:55
  • @tibbe is there any thumb-rule for when one should use an `IORef` and when one should use an `MVar`? – Saurabh Nanda Jul 02 '19 at 06:13