`forkIO` and` putMVar`: what happens under the hood?

I hope someone can help me understand why the following code generates the result below. The code is in the Concurrency chapter in Simon Marlowe's book (link below).

Based on the description of the various functions, I suggested that the second putMVar function should be blocked, given that (i) both putMVar functions are part of the same thread, and (ii) the value has already been assigned. Clearly, this is not so. It would be great to understand what is happening here "under the hood."

(Note: the book uses the notation do , but I prefer the notation >>= , because I think this is more straightforward - hence the version of the code below.)

Book Link

 import Control.Concurrent main :: IO () main = newEmptyMVar >>= \m -> forkIO (putMVar m 'x' >>= \_ -> putMVar m 'y') >>= \_ -> takeMVar m >>= print >>= \_ -> takeMVar m >>= print 

Code output above:

 % ./mvar2 'x' 'y' 
+6
source share
1 answer

For myself, here is the code in do notation.

 main :: IO () main = do m <- newEmptyMVar forkIO $ do putMVar m 'x' putMVar m 'y' x <- takeMVar m print x y <- takeMVar m print y 

We have a background thread and a main thread that performs simultaneous transmission through a small piece of memory, MVar , called m .

MVar semantics are: a MVar can be empty or full. If you want to read MVar and it is empty, you have to wait until it is full. If you readMVar , then you simply enable the value stored in full MVar as soon as you can. If you takeMVar , then you decide the value, and then immediately clear it after reading.

On the other hand, when you putMVar to put a new value in MVar , you will immediately succeed if MVar empty. If it is full, you must wait until it becomes empty.

Since there is a wait on the read and write sides, the threads are synchronized in the emptiness and completeness of MVar .

So, in this example, we can present many possible linearized stories about how execution is performed. Fortunately, they all work the same way. Let the BG background thread and the main thread MN called.

 t = 1 : MN makes a new, empty MVar called 'm' t = 2 : BG puts 'x' in 'm' making it full t = 3 : BG attempts to put 'y' in 'm', but since 'm' is full BG blocks t = 4 : MN attempts to read 'm' and succeeds as it is full t = 5 : BG now places 'y' into the newly empty 'm' t = 6 : BG dies t = 6 : MN prints the value it previously read t = 7 : MN attempts to read 'm' and succeeds as it is full t = 8 : MN prints the value it previously read t = 9 : MN dies 

As we can see, BG cannot put more values ​​in MVar than it can read MN . This gives the printed semantics that you have observed.

+10
source

Source: https://habr.com/ru/post/978008/


All Articles