Interruptibility and thread safeness with readMVar

Simon Marlow
Mon, 28 Oct 2002 10:00:09 -0000

> How does the ghc run-time system determine if an operation is=20
> interruptible
> when within "block" function scope? Explanations I've read in various
> papers/web pages state that an operation is interruptible,=20
> even within a the
> scope of the "block" function, if that operation can potentially block
> waiting for input/output.

That's correct.

> Take the readMVar function. In my implementation
> of ghc, readMVar is defined as follows.
> readMVar :: MVar a -> IO a
> readMVar m =3D
>   block $ do
>     a <- takeMVar m
>     putMVar m a
>     return a
> Since, by the above definition of interruptible, both=20
> takeMVar and putMVar
> are both interruptible operations, isn't there a possibility that, on
> receipt of an exception while blocked on the "putMVar m a"=20
> operation, "MVar
> m" would be left in an inconsistent state (i.e. empty)? Am I right in
> thinking that readMVar should only ever be used when no other=20
> thread can
> potentially access "MVar m"? Or have I completely missed the point?

Yes, that's right.  The safetey of readMVar depends on there being no
malicious threads doing putMVar without first doing takeMVar (in fact
this condition extends to many MVar operations).

> My second question concerns readMVar directly. Is this=20
> function intended to
> be an atomic operation in the context of multiple threads=20
> accessing the
> "MVar m" parameter (i.e. thread safe)? Presumably, if a=20
> context switch takes
> place after takeMVar but before putMVar, there is a=20
> possibility that another
> thread will successfully evaluate putMVar on "MVar m",=20
> inserting a different
> value. Is this intended? I suppose it's necessary to resort=20
> to some kind of
> cunning ploy of using more MVars/threads to control access to=20
> MVar m if we
> wan't to guarantee readMVar's thread safeness? If so, does=20
> anyone have such an implementation?

The assumption is that there are no threads attempting to do putMVar
without first doing takeMVar (the same condition as above).

> Both of these questions are a result of trying to implement a=20
> function that
> will atomically (in the presence of both other threads and=20
> asynchronous
> exceptions) transfer the contents of one MVar to another (or=20
> leave both
> MVars in their original states of the operation fails).

In order to do this, you'll need to be clear about what another thread
in the system is allowed to do with the MVars involved.  This will
affect whether you need extra locking (more MVars) or not.

Also, doing atomic operations on two MVars simultaneously is tricky,
because there is a risk of deadlock with another thread doing the same
operation on the same MVars but in the opposite order.  Usually some
ordering is applied to the MVars to avoid this problem.