|
Softpanorama |
May the source be with you, but remember the KISS principle ;-)
|
| Old News ;-) | Recommended Links | Pipes | Coroutines in assembler | Coroutines in C++ | Python | Java | vi/vim | Etc |
Coroutines are closely connected with the notion of pipes and are subroutines, with neither the caller nor the callee being "in charge". Instead, they allow program-controlled interleaving of instructions generated by both. Suppose A calls B. Then B wants to allow A to perform some more computation. B can "resume A", which then runs until it "resumes B". Then A can execute until it needs data from B, which might produce part of that data, and resume A, to examine or compute with the part produced so far. Coroutines have been exploited in the past in compilers, where the "parser" asks the "lexer" to run until the lexer has to stop (say at end of line). The lexer then resumes the parser to process that line's data, and is itself resumed to continue reading input characters. The text also shows an example of a tree-comparison problem solved logically by coroutines. Their advantage is that the cooperative behavior allows the "high-level" program to terminate the computation early, before the companion routine "completes" its assigned task. I have also used them to simulate parallel computation, when I want to build my own control over the task scheduling process.
The simplest way to simulate coroutines n C, is to use C's "setjmp()" and "longjmp()" library procedures. These procedures are intended for use in setting exception-handler routines. However, they have the property that they create concrete realizations of a "stopped" task -- an instruction counter, along with a variable reference context is stored when a setjmp occurs, and is resumed when a longjmp to the saved item is performed. The longjmp(Buf, Return) causes the setjmp(Buf) to return (again), this time returning value Return, instead of the 0 setjmp(Buf) returns when it is called.
[Feb 02, 2002] Coroutines in C by Simon Tatham
Structuring a large program is always a difficult job. One of the particular problems that often comes up is this: if you have a piece of code producing data, and another piece of code consuming it, which should be the caller and which should be the callee?
Here is a very simple piece of run-length decompression code, and an equally simple piece of parser code:
|
|
Each of these code fragments is very
simple, and easy to read and understand. One produces a character at a time by
calling emit();
the other consumes a character at a time by calling
getchar(). If only the calls
to emit() and
the calls to getchar()
could be made to feed data to each other, it would be simple to connect the two
fragments together so that the output from the decompressor went straight to the
parser.
In many modern operating systems, you could
do this using pipes between two processes or two threads.
emit() in the decompressor
writes to a pipe, and getchar()
in the parser reads from the other end of the same pipe. Simple and robust, but
also heavyweight and not portable. Typically you don't want to have to divide
your program into threads for a task this simple.
In this article I offer a creative solution to this sort of structure problem.
CORO(2) C Coroutines CORO(2) NAMEco_create, co_call, co_resume, co_delete, co_exit_to, co_exit - C coroutine management SYNOPSIS#include <coro.h> extern struct coroutine *co_current; extern struct coroutine co_main[]; struct coroutine *co_create(void *func, void *stack, int stacksize); void co_delete(struct coroutine *co); void *co_call(struct coroutine *co, void *data); void *co_resume(void *data); void *co_exit_to(struct coroutine *co, void *data); void *co_exit(void *data);DESCRIPTIONThe coro library implements the low level functionality for coroutines. For a definition of the term coroutine see The Art of Computer Programming by Donald E. Knuth. In short, you may think of coroutines as a very simple cooperative multitasking environment where the switch from one task to another is done explicitly by a function call. And, coroutines are fast. Switching from one coroutine to another takes only a couple of assembler instructions more than a normal function call. This document defines an API for the low level handling of coroutines i.e. creating and deleting coroutines and switching between them. Higher level functionality (scheduler, etc.) is not covered here.
From John English, at the University of Brighton (England), comes a trilogy of highly useful class libraries for Borland C++. The TSR class presents a framework for writing memory-resident DOS programs (TSRs). The DOSThread class presents a framework for writing DOS applications consisting of multiple "threads" in a self-contained preemptive multitasker. The Coroutine class presents a cooperative non-premptive framework for sharing the CPU. Version 1.00 (March 1993) of all three BCC+ class libraries is now available in a single CUG library volume #389.
COROUTINES - the C co-routine package.
Copyright © 1996-2008 by Dr. Nikolai Bezroukov. www.softpanorama.org was created as a service to the UN Sustainable Development Networking Programme (SDNP) in the author free time. Submit comments This document is an industrial compilation designed and created exclusively for educational use and is placed under the copyright of the Open Content License(OPL). Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine.
Standard disclaimer: The statements, views and opinions presented on this web page are those of the author and are not endorsed by, nor do they necessarily reflect, the opinions of the author present and former employers, SDNP or any other organization the author may be associated with. We do not warrant the correctness of the information provided or its fitness for any purpose.
Last modified: February 28, 2008