Softpanorama
(slightly skeptical) Open Source Software Educational Society

May the source be with you, but remember the KISS principle ;-)

Google   


Coroutines in C

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.

Building Coroutines

[Feb 02, 2002] Coroutines in C by Simon Tatham

Introduction

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:

 

    /* Decompression code */
    while (1) {
        c = getchar();
        if (c == EOF)
            break;
        if (c == 0xFF) {
            len = getchar();
            c = getchar();
            while (len--)
                emit(c);
        } else
            emit(c);
    }
    emit(EOF);
    /* Parser code */
    while (1) {
        c = getchar();
        if (c == EOF)
            break;
        if (isalpha(c)) {
            do {
                add_to_token(c);
                c = getchar();
            } while (isalpha(c));
            got_token(WORD);
        }
        add_to_token(c);
        got_token(PUNCT);
    }

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.

 

C Coroutines

CORO(2)                    C Coroutines                   CORO(2)

NAME
       co_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);
DESCRIPTION
       The 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.

(CUG 385)BCC+ TSR, DOSThread, and Coroutine classes

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-2007 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