
How does the ghc run-time system determine if an operation is interruptible when within "block" function scope? Explanations I've read in various papers/web pages state that an operation is interruptible, 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 = block $ do a <- takeMVar m putMVar m a return a
Since, by the above definition of interruptible, both 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" 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 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 function intended to be an atomic operation in the context of multiple threads accessing the "MVar m" parameter (i.e. thread safe)? Presumably, if a context switch takes place after takeMVar but before putMVar, there is a possibility that another thread will successfully evaluate putMVar on "MVar m", inserting a different value. Is this intended? I suppose it's necessary to resort to some kind of cunning ploy of using more MVars/threads to control access to MVar m if we wan't to guarantee readMVar's thread safeness? If so, does 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 function that will atomically (in the presence of both other threads and asynchronous exceptions) transfer the contents of one MVar to another (or 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. Cheers, Simon