46

I've seen the TVar is a simple container, while the TMVar is the same as an MVar, meaning it has a lock etc, but within the STM monad. I am wondering why would that be necessary, as the idea of the STM is to make locks unnecessary.

So which is the one to use if you, say have a type like [Handle] a list of socket handles that you want to use between threads made by forkIO?

Lanbo
  • 15,118
  • 16
  • 70
  • 147

2 Answers2

44

It's not really a matter of locking, it's about what the reference means:

  • TVar is a mutable reference within STM, representing general shared state. You create it holding a value, you can read and write to it, etc. It's very much akin to IORef or STRef (which are the same thing anyway).

  • TMVar is a reference to a slot that threads can use to communicate. It can be created holding a value, or empty. You can put a value into it, which if already filled blocks until someone else empties it; or you can take a value from it, which if already empty blocks until someone fills it. It's obviously akin to an MVar, but for many common uses it might be simpler to think of it as a single-element queue used for a communicating producer/consumer pair.

In short, TVar is general shared state, use it if you want atomic updates to data from arbitrary places. TMVar is a synchronization primitive, use it if you want a thread to wait until something becomes available, while another waits for something to be needed.

Also note TChan, which is implemented roughly as two TVars holding locations in a linked list where each forward link is also a TVar, and works as an unbounded queue for communication.

All of these can be used in slightly different ways, of course--you can peek at the value of a TMVar without removing it, for instance, if you want a scenario where multiple threads all wait for a single resource to become available but it's never "used up".

C. A. McCann
  • 76,893
  • 19
  • 209
  • 302
19

The differences between TVar and TMVar are not so large as they look -- definitely not comparable to the differences between IORef and MVar.

While MVar does indeed provide some locking for thread-safety, TMVar does nothing interesting! (no additional locking) Everything important is already implemented with STM and TVar, so TMVar a is just a short-hand for TVar (Maybe a) equipped with some nice functions (some of which block using the retry function).

Whether blocking with retry is compatible with the spirit of STM and whether it eliminates some of the STM's advantages (no deadlocks etc.) is a separate question and I would love to see someone more experienced to answer it.

Rotsor
  • 13,655
  • 6
  • 43
  • 57
  • 1
    How would `retry` cause deadlocks? It rolls back the current transaction, then blocks until the situation changes somehow. It doesn't (in fact, can't) block inside a transaction. – C. A. McCann Aug 03 '11 at 00:57
  • 6
    Although it won't deadlock in the middle of a transaction, one can create transactions that can never succeed in a way similar to how classical deadlocks are created, like two transactions waiting for each other's results. It's not immediately obvious how this is different from deadlock in practice. – Rotsor Aug 03 '11 at 01:04
  • 5
    Ah, right. I believe the actual term for that is, appropriately enough, "livelock". It's more a kind of resource starvation than it is like a deadlock, though. Whereas deadlocks can easily result from unrelated uses of the same resources, I believe the optimistic nature of STM makes livelocks unlikely unless there's a direct conflict or very high overall contention. – C. A. McCann Aug 03 '11 at 01:24