I think, I don't understand dupChan, here is a pic

Hi, all. I’m studying concurrency and I think, I don’t understand dupChan or even worse, the channels itself. Could you point me what is wrong in my observations?

I’ve drew several pics to understand how channels build and how they work and if you see mistakes, could you point me to it, please?

//////////////////////////////
creating channel
//////////////////////////////

newChan :: IO (Chan a)
newChan = do
	hole <- newEmptyMVar		-- MVar (ChItem Int)
    read <- newMVar hole		-- MVar (MVar (ChItem Int))
    write <- newMVar hole		-- MVar (MVar (ChItem Int))
    return $ Chan read write
main :: IO ()
main = newChan >> return ()


//////////////////////////////////
writing to a channel
//////////////////////////////////

writeChan :: Chan a -> a -> IO ()
writeChan (Chan _ w) v = do
	newHole <- newEmptyMVar				
    oldHole <- takeMVar w				
    putMVar w newHole					
    putMVar oldHole (ChItem v newHole)		
main :: IO ()
main = do
    ch <- newChan
    writeChan ch 2 >> return ()

main :: IO ()
main = do
    ch <- newChan
    writeChan ch 2
    writeChan ch 8 >> return ()


////////////////////////////////////////
reading from a channel
////////////////////////////////////////

readChan :: Chan a -> IO a
readChan (Chan r _) = do
	hole <- takeMVar r
    takeMVar hole >>= \(ChItem v next) -> putMVar r next >> return v
main :: IO ()
main = do
    ch <- newChan
    writeChan ch 2
    writeChan ch 8
    readChan ch >>= print -- 2


//////////////////////////////////////////////////////////
and now, my headache - dupChan
//////////////////////////////////////////////////////////

dupChan :: Chan a -> IO (Chan a)
dupChan (Chan _ w) = do
	hole <- readMVar w
    newRead <- newMVar hole
    return $ Chan newRead w
main :: IO ()
main = do
    ch <- newChan
    writeChan ch 2
    writeChan ch 8
    readChan ch >>= print -- 2
    
    dupChan ch >> return ()

However, here I totally don’t understand what’s going on, if I want to read data from the dupChan I can’t, it seems that there is block happened

main :: IO ()
main = do
   x <- newChan
   writeChan x 8
   y <- dupChan2 x
   readChan y >>= print -- blocked, even it has value in read stream

What’s wrong with my picture of dupChan? Sorry for English, if so :confused:

5 Likes
  1. Nice graphics! You may have a few people here asking for your help with documentation.

  2. In your graphics, you’re using generic terms like “Shared MVar”, “Shared MVar of Item” and so on - try using the names of the bindings instead. So for dupChan these would be w, hole and newRead.

(If this seems vague…none of us want this Discourse to be “black-listed” by educators for providing answers to homework questions: we would rather help people to achieve that “Aha!” moment :-)

1 Like

I think both you and @atravers misunderstand what dupChan is supposed to do.

For me the documentation is pretty clear:

Duplicate a Chan: the duplicate channel begins empty, but data written to either channel from then on will be available from both. Hence this creates a kind of broadcast channel, where data written by anyone is seen by everyone else.

So the reason y <- dupChan x; readChan y hangs is because the duplicated channel starts empty.

And @atravers, I don’t think it is easy to write dupChan in terms of chanToList and listToChan.

1 Like

but, how it can be an empty if we put write copy as a read?

dupChan :: Chan a -> IO (Chan a)
dupChan (Chan _ w) = do
    hole <- readMVar w      -- take a copy of the content of write stream
-- :t hole :: MVar (ChItem a)
    newRead <- newMVar hole -- create a new one read with the content of the hole
-- :t MVar (MVar (ChItem a))
    return $ Chan newRead w

I’d recommend looking at the chapter about channels in Parallel and Concurrent Haskell:

It includes a useful picture:

So, if we create a new channel where both the read and the write vars point to the write end of another channel then there are no items between the read and write ends so it is empty.

2 Likes

So none of the data already on the original channel is duplicated? Yeah, that’s definitely not what I was expecting of something with the prefix dup…just imagine if e.g. dupFile only started copying the original file from “the tail end”, as more data was appended to either the original or new file! So yes, I was duped by that name. But then again, Prelude.seq isn’t actually sequential - a detail which has also caused confusion: dupChan isn’t alone.

Anyway, the suggestion about defining listToChan and chanToList has been removed.

[UPDATE 3] wow, I’ve understood it, LOL! and again, I have to check it out :slight_smile:

[UPDATE 2] no, I didn’t get it, @jaror could you look at my question, please?

[UPDATE] LOL, don’t answer on it, I think I get it, I have to check it out

sorry, but I spent all my yesterday’s spare time and still don’t get it

you said:

both the read and the write vars point to the write end of another channel then there are no items between the read and write ends so it is empty

however, while I was writing that question, I got insight and I would like to ask your advice or vision to understand that

Could you look on that pic? I’m interesting in red area

when we do readMVar, we get the copy of the whole chain right to the read MVar or just to MVar that in the Item structure?

well, I’ve managed to know what’s happening with dupChan under the hood

this reply is for future me or for people who don’t get it as I were

Mistake was in notion that we copy full path of write stream, that red square:

The right answer: we copy only MVar and nothing else, like here:

and now, a little bit of the code

data Chan a = Chan (MVar (Stream a)) (MVar (Stream a))
type Stream a = MVar (Item a)
data Item a = Item a (Stream a)

main :: IO ()
main = do
    ch <- newChan
    writeChan ch 2
    writeChan ch 8
 -- let's look in data behind read and write end
 -- Chan r w - r :: MVar (MVar (Item 2 (MVar (Item 8 (MVar _)))))
 --            w :: MVar _
 -- that's why when we copy write side, we don't have an access to the items and so on
 -- MVar is empty here :)
1 Like