0 branches 0 tags

README.md

ccoroutines

Coroutines in C, using standard libraries.

What's Here

ccoroutines

The coroutine engine.

generator

Provides generators - coroutines which yield values. These are especially useful for nested searches which provide values to a for loop (eg searching for files in a directory tree).

async

Provides asynchronous tasks. Each task volunteers to be switched away-from by waiting on a Async_Future.

Note that generator and async can be used together.

Prerequisites

These rely on as much as possible on C's cross-platform comfort zone. C's standard libraries are used as far as possible, but, as threads.h is not usually supported, pthread.h has been used instead.

Style

The style is influenced by C++. For example, where possible, a Something *Something_New(a, b, c) and Something_Delete(Something *), where a Something is malloced, will have corresponding Somthing_ctor(Somthing *, a, b, c) and Something_dtor(Something *) to initialise and finalise a Something on the stack, or within another object. Using .._ctor() and .._dtor() will be faster as they avoid the malloc() and free().

Something *oneofthem = Something_New();
// use oneofthem
Something_Delete(oneofthem);

Can be also be done like this, and this will run faster:

Something oneofthem;
Something_ctor(&oneofthem);
// use oneofthem
Something_dtor(&oneofthem);

Usage

When you are using coroutines or generators:

void *myfunc(void *){
    // your function here
}

Coroutine_StartSystem();
Coroutine_Run(myfunc, (void *)myparam);
Coroutine_StopSystem();

If you also use async, then:

bool myfunc(void *myparam, void **res){
    // your async function here
}

Async_StartSystem();
void *res = NULL;
bool canceled = Async_Run(myfunc, myparam, &res);
Async_StopSystem();

While the system is started, you can make many calls to Coroutine_Run() or Async_Run(). A running system is thread local - each thread you want to use coroutines on will need to be Coroutine_StartSystem()ed or Async_StartSystem()ed.

Generators

void *yield_files(void *param){
    bool domore = true;

        // loop/call functions to find more values to yield, and when you have one:
        domore = Generator_Yield(res);
        // .. if domore is false, exit your generator - it is being destructed

    // not actually used by generators, but this is a useful convention for bubbling
    // the flag out to calling functions.
    return (void *)domore;
}

Generator gen;
Generator_ctor(&gen, yield_files, "..");
int count = 0;
void *res;
while(Generator_Next(&gen, &res)){
    // use res - a value yielded by your generator
    printf("%d) %s\n", count, (char *)res);
    free(res);

    // exit your loop early if you want to
    if (++count>16000) break;
}
Generator_dtor(&gen);

Async

To run an Async program:

Async_StartSystem();
void *res = NULL;
bool canceled = Async_Run(asyncmain, &param, &res);
Async_StopSystem();

Async runs tasks, switching between them when the current task waits on an Async_Future. The async main is also a task. The entry function for any task looks like this:

bool asyncmain(void *param, void **res){

    // do your thing here

    return canceled;
}

Tasks can complete, or be canceled. Return whether your task was canceled or not.

Within your async task, create Async_Tasks and Async_Future_Await() them when you want to wait for their result. Note that an Async_Task has a member, base, which is an Async_Future, which is what you should wait on:

bool asyncmain(void *param, void **res){
    ...
    Async_Task task1;
    Async_Task_ctor(&task1, anasynctask, &task1param);

    bool canceled = Async_Future_Await(&task1.base);

    // use the result stored in the future

    Async_Task_dtor(&task1);
    ...
}