Coroutines are a bit hard to wrap one's head around. I never truly understood them or their purpose for many years. And libraries like this one are pretty abstract, so it's hard to figure out how one would use it. I get the feeling @exagonx that you maybe could use a little explanation. I know I could have used one! You seem to expect that coroutines could be a way of doing multitasking in DOS. But that's not really what they do and what they are used for.
Coroutines aren't meant to replace conventional threading. Rather they are tools for dealing with data flows (pipelines if you will). Today asynchronous programmign is all the rage and coroutines form a fundamental part of that. I'm no expert but I do use coroutines fairly regularly (in Python). Maybe some pseudo code might help explain it and how I use them. To me coroutines are most useful with what Don Knuth called the "emit operator" and also an iteration protocol, where data is emitted from one function and consumed by another. Languages that have inbuilt coroutines tend to provide this, such as C+20, C#, and Python. But we can use some kind of queue class instead to provide the data channel.
Here's a bit of generic psuedocode (not specific or even necessarily valid to libco) that hopefully makes sense:
Code: Select all
dim queue as Queue 'shared queue structure used between the coroutines
dim done_flag 'a way to indicate that the coroutine is finished emitting data
function producer(...., byref queue, byref done_flag)
while some long-running loop
read in some data
put it in the queue
co_switch(consumer_handle)
wend
done_flag = true
end function
function consumer(..., byref queue, byref done_flag)
while not all data processed:
if there's something in the queue then
remove data from queue and process it
co_switch(producer_handle)
end if
wend
end function
I've seen a lot of coroutine implementations in C that use static variables to maintain state in a function instead of using a library like libco. the problem there is that the functions are not reentrant and cannot be used in more than one place at a time. At least here in the pseudo code, as long as each producer/consumer pair had it's own queue and done flag, you can run as a many of these as you want, even in multiple threads.
To me this method of doing something that produces and consumes data has several advantages to other methods of doing this. First it avoids the hassles and overhead of threads if you were to make the producer it's own thread and the consumer it's own thread. They'd just be spinning waiting for each other. Since running of the two functions is linked via the queue anyway, threads would just be overkill. Second, it allows you to separate the producer code from the consumer code, making it easier to refactor or add new layers of functionality to the consumer without complicating the producer side. Or you could replace the producer with something else entirely, perhaps reading from a network socket instead of a file.
I'm not sure any of that made anything clearer...