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);
    ...
}

API

Coroutine

void Coroutine_StartSystem();

Start the coroutine system on this thread. When you've finished with Coroutine call Coroutine_Stop(). Coroutine can be started & stopped many times on one thread. The total stack allowed for all coroutines running on a thread is the size of the call stack on that thread.

void Coroutine_StopSystem();

Stop the coroutine system on this thread.

Coroutine_Start

void *(*)(void *param)

The entry function for a coroutine. The param is the value passed to Coroutine_Continue, and the void * return value can be accessed through the Coroutine object.

Coroutine *Coroutine_New(Coroutine_Start start);

Create a new Coroutine. The Coroutine system must be started to create a Coroutine. The stack size available to the coroutine will be COROUTINE_STACK_SIZE defined in coroutine.h. When you have finished with your Coroutine, use Coroutine_Delete() to delete it.

void Coroutine_Run_Coroutine(Coroutine cor, void value);

Run the Coroutine and return when it returns. This is how to start coroutines running in the coroutine system. It is an error for the run coroutine to return before all other coroutines have completed.

void Coroutine_Run(Coroutine_Start start, void value);

Convenience wrapper for Coroutine_Run_Coroutine which creates the Coroutine and retrieves its result.

void Coroutine_Delete(Coroutine *cor);

Use Coroutine_Delete() to delete a coroutine when it is no longer needed.

void Coroutine_Continue(Coroutine cor, void value, bool early);

Continue the given Coroutine. value is passed to the coroutine, as param to the start function, or as the return value from Coroutine_Yield. early determines whether the continued coroutine is added to the head of tail of the list of runable coroutines.

void Coroutine_Yield(void value, Coroutine_YieldCallback on_yield, void *this);

Yield value from the current coroutine; this coroutine is moved to the list of coroutines waiting to be continued. The next runable coroutine is run - either by its start routine being called with value as its param, or by valuebeing returned from its Coroutine_Yield().

void Coroutine_GetValue(Coroutine cor);

Return the Coroutine's value - the value last yielded, or returned by its start routine.

Coroutine *Coroutine_GetActive();

Return whihc coroutine is currently running, ie the caller's Coroutine.

bool Coroutine_IsRunning(Coroutine *cor);

Return whether the given coroutine is still running - it may be running, ready to run, or waiting to be continued, but won't have returned from its start function.