In Parallel and Concurrent Programming in Haskell by Simon Marlow, chapter 7 starts at page 125 with this example,
import Control.Concurrent
import Control.Monad
import System.IO
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
_ <- forkIO (replicateM_ 100000 (putChar 'A'))
replicateM_ 100000 (putChar 'B')
which has output like this
BBBBBBBABABABAAAAAAA
(which is incidentally the output I really got when changing 100000
to 10
in the snippet above and running the program).
At the end of the chapter, page 140, the following comment is made (my emphasis):
[…] This is an example of the fairess guarantee in practice. […]. Hnce this leads to perfect alternation between the two threads. The only way that the alternation pattern can be broken is if one thread is descheduled while it is not holding the
MVar
. Indeed, this does happen from time to time as a result of preemption, and we see the occasional long string of a single letter in the output.
Based on the output I showed about, yes, I can see the alternation being broken, but I don't quite understand the explanation above. There's not even an MVar
in the first example.
The same paragraph you are quoting also includes
The
stdout
handle is represented by anMVar
, [...]
That's where the MVar is: it's stdout
. You can look at the source for Handle to confirm:
data Handle
= FileHandle
FilePath
!(MVar Handle__)
| DuplexHandle
FilePath
!(MVar Handle__) -- The read side
!(MVar Handle__) -- The write side
As for how you could fail to get perfectly interleaved outputs if one of the threads is preempted, it's hard to know what I could say to you that's not already in the chapter you're reading. The thread that's preempted stops printing for a while, because it gets de-scheduled while not holding any locks. Until such time as it gets scheduled again, the other thread can write freely.