haskellconcurrencyfunctional-programmingghcpreemption

How can GHC's fairness guarantee not show up if a thread is descheduled while it is not holding the MVar?


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.


Solution

  • The same paragraph you are quoting also includes

    The stdout handle is represented by an MVar, [...]

    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.