Coroutines or goroutines?

I have already skimmed the topic with two posts:

The next promised post should have been about the same matter, tackled using D.E.Knuth's MMIX, mainly because of the GO and PUSHGO instructions.

Well, GO… In the (long) meantime, I have tried to taste Go, Google's «open source programming language that makes it easy to build simple, reliable, and efficient software».

I like it and it blends well with these articles, because of its goroutines” and channels.

So, let's see how it would be in a high level language1 like Go, kindly providing us all we need to make things work. It will be easier to follow Tatham's article closer, in particular decompressor and parser written with the aid of crBegin, crReturn and crFinish macros.

Something like this example, where there's a quirk2 and I am not sure it's the proper way3 to solve the problem, but it seems reasonable.

Now there are not two “pieces of code” which pass control from one to another and viceversa: in fact, we have routines (a producer and a consumer, if we speak in terms of their roles) running concurrently and sending/receiving data through a “channel” — the producer reads input and sends each decoded byte to the channel; the consumer reads from (and waits on) the channel. The producer does not send the decoded byte only: it says also when something went wrong or the end of the input stream was reached. The consumer uses this information to stop the get-from-channel loop.

Indeed, a single goroutine is enough and makes it possible to drop the explicit synchronization: main function won't end until the parser function exits, and the parser will exit when the consumer will send an EOF through the channel.

My final attempt is as follows.

  1. With respect to 68k, x86 and MMIX assembly…

  2. Since the goroutines need their time to finish their work, in the main we need to wait until they end — otherwise, when we exit the main, goroutines get killed, and likely it will happen soon before input stream dries. We need some kind of synchronization. I have used sync.WaitGroup: the counter is incremented by 2 (because we have 2 goroutines) in the main, with wg.Add(2); then each goroutine calls wg.Done() when it has finished (this happens when an error occurs or input stream reaches end of file).

  3. Intuitively, first I did wg.Add(1) in each goroutine; but it didn't work, likely since the main executed wg.Wait() before one of those goroutines stepped on wg.Add(1). So, the main has to set things properly before.

No comments:

Post a Comment