If I understand correctly (both your code and the implementation of Channel), channels provide some level of order in concurrent code. For example, in 3 different fibers all call receive on the same channel, they will get values in the order in which they called receive.
This happens because the receivers waiting on a channel are held in a queue (implemented as a linked list). When you call receive and there aren’t any values in the channel buffer, the receiving fiber gets pushed into the end of the queue.
So sending to a channel, receiving from a channel, and the values in the channel are all separate FIFO queues. The first sending fiber will succeed in sending the first value to the first receiving fiber every single time.
It looks like once you’ve received SIZE elements out of the channel in generate, the main fiber completes. When the main fiber completes, the entire program exits. For example, the following code will exit despite work still being done in another fiber:
spawn { loop {} }
If all of your application’s work is being done in other fibers, you may need to put the main fiber to sleep indefinitely:
spawn { loop {} }
sleep # Let the other fiber do its work
This happens in multithreaded code, too. If the main thread is done, the entire program exits. This is different from Node.js, for example, where if any setTimeout callbacks are still enqueued, the app will continue until there are none left to execute.