bio
 
Loading...
Searching...
No Matches
Coroutine

Cooperative scheduling. More...

Modules

 Signal
 Waiting for an event to complete.
 
 Monitor
 Monitor a coroutine for termination.
 

Data Structures

struct  bio_coro_t
 Handle to a coroutine. More...
 
struct  bio_monitor_t
 Handle to a monitor. More...
 
struct  bio_cls_t
 Coroutine-local storage (CLS) specification. More...
 
struct  bio_coro_options_t
 Coroutine spawn options. More...
 

Typedefs

typedef void(* bio_entrypoint_t) (void *userdata)
 Entrypoint for a coroutine.
 

Enumerations

enum  bio_coro_state_t { BIO_CORO_READY , BIO_CORO_RUNNING , BIO_CORO_WAITING , BIO_CORO_DEAD }
 State of a coroutine. More...
 

Functions

bio_coro_t bio_spawn_ex (bio_entrypoint_t entrypoint, void *userdata, const bio_coro_options_t *options)
 Spawn a new coroutine.
 
static bio_coro_t bio_spawn (bio_entrypoint_t entrypoint, void *userdata)
 Spawn a new coroutine.
 
bio_coro_state_t bio_coro_state (bio_coro_t coro)
 Check the state of a coroutine.
 
bio_coro_t bio_current_coro (void)
 Get the handle of the currently running coroutine.
 
void bio_yield (void)
 Let a different coroutine run.
 
void bio_set_coro_data (void *data, const bio_tag_t *tag)
 Associate the calling coroutine with arbitrary data and a tag.
 
void * bio_get_coro_data (bio_coro_t coro, const bio_tag_t *tag)
 Retrieve the data associated with a coroutine.
 
void bio_set_coro_name (const char *name)
 Give a human-readable name to the calling coroutine.
 
const char * bio_get_coro_name (bio_coro_t coro)
 Get a human-readable name for the coroutine if it has one.
 
void * bio_get_cls (const bio_cls_t *cls)
 Get a coroutine-local storage (CLS) object.
 

Detailed Description

Cooperative scheduling.

At the heart of bio is a cooperative scheduling system. Coroutines are generally cheaper to spawn and context switch compared to threads.

Instead of using callbacks, all I/O calls in bio will block the calling coroutine. A different coroutine will be scheduled to run and the original coroutine will only resume once the original call return.

Coroutines can interact with one another through:

Typedef Documentation

◆ bio_entrypoint_t

typedef void(* bio_entrypoint_t) (void *userdata)

Entrypoint for a coroutine.

See also
bio_spawn

Enumeration Type Documentation

◆ bio_coro_state_t

State of a coroutine.

Enumerator
BIO_CORO_READY 

The coroutine is ready to run but not running yet.

BIO_CORO_RUNNING 

The coroutine is the currently running coroutine that called bio_coro_state.

BIO_CORO_WAITING 

The coroutine is waiting for a signal.

BIO_CORO_DEAD 

The coroutine has terminated.

Function Documentation

◆ bio_coro_state()

bio_coro_state_t bio_coro_state ( bio_coro_t  coro)

Check the state of a coroutine.

◆ bio_current_coro()

bio_coro_t bio_current_coro ( void  )

Get the handle of the currently running coroutine.

◆ bio_get_cls()

void * bio_get_cls ( const bio_cls_t cls)

Get a coroutine-local storage (CLS) object.

This is used when something needs to be (lazily) created once in each coroutine. The CLS spec should be a global variable in a .c/.cpp file and passed as a pointer:

typedef struct {
int foo;
} my_cls_t;
static void
init_my_cls(void* ptr) {
my_cls_t* cls = ptr;
*cls = (my_cls_t){ .foo = 42 };
}
static const bio_cls_t MY_CLS = {
.size = sizeof(my_cls_t),
.init = &init_my_cls,
};
static void entrypoint(void* userdata) {
my_cls_t* cls = bio_get_cls(&MY_CLS);
assert(cls->foo == 42); // Initialized
my_cls_t* cls2 = bio_get_cls(&MY_CLS);
assert(cls == cls2); // The same object
}
void * bio_get_cls(const bio_cls_t *cls)
Get a coroutine-local storage (CLS) object.
Coroutine-local storage (CLS) specification.
Definition bio.h:317
size_t size
Size of the data.
Definition bio.h:318

The memory for the CLS object will only be allocated the first time a coroutine calls this function. The optional bio_cls_t::init callback will be invoked to initialize this memory block.

Subsequent calls to bio_get_cls from the same coroutine with the same cls pointer will return the same object.

When a coroutine terminates, the optional bio_cls_t::cleanup callback will be invoked. The associated memory will be automatically freed.

Internally, the default logger use CLS to allocate a private format buffer for each coroutine. This avoids the problem of corrupting log content when a coroutine inevitably gets context-switched since logging would involve some sort of I/O.

Parameters
clsThe specification for the CLS. Using the same pointer will return the same CLS object.
Returns
The CLS object

◆ bio_get_coro_data()

void * bio_get_coro_data ( bio_coro_t  coro,
const bio_tag_t tag 
)

Retrieve the data associated with a coroutine.

This returns the data that was set with bio_set_coro_data.

If bio_set_coro_data was never called by the target coroutine, this will return NULL.

If the tag is not the same as what was previously passed to bio_set_coro_data, this will return NULL.

If the targeted coro has terminated, this will also return NULL.

Typically, the associated data is stack-allocated by the owning coroutine. Since it is safe to call this on a dead coroutine, it is unnecessary and even slightly less efficient to check a coroutine's liveness before calling this function.

Parameters
coroThe coroutine to retrieve data from
tagA unique tag
Returns
The associated data or NULL
See also
bio_set_coro_data

◆ bio_get_coro_name()

const char * bio_get_coro_name ( bio_coro_t  coro)

Get a human-readable name for the coroutine if it has one.

This may return NULL if a name was not given.

See also
bio_set_coro_name

◆ bio_set_coro_data()

void bio_set_coro_data ( void *  data,
const bio_tag_t tag 
)

Associate the calling coroutine with arbitrary data and a tag.

This data can later be retrieved by any coroutine, provided that they pass the same tag to bio_get_coro_data. This allows a coroutine to quickly expose its states to others.

"Module-private" data can be implemented by declaring the tag as a static global variable in a .c/.cpp file. Hence, only functions in the same file can call bio_set_coro_data and bio_get_coro_data.

This is provided as an additional runtime safety check. Since all bio_coro_t handles are interchangable and the user data is just an untyped void* pointer, it is possible to incorrectly pass the wrong coroutine to the wrong function that does not know how to cast the pointer back to the correct concrete type, resulting in undefined behaviour. The tag acts as a type check and access check on the data.

Parameters
dataAn arbitrary pointer
tagA unique tag
See also
bio_get_coro_data
bio_tag_t

◆ bio_set_coro_name()

void bio_set_coro_name ( const char *  name)

Give a human-readable name to the calling coroutine.

Using the default logger, this name will be displayed instead of the coroutine handle's numeric representation (e.g: main instead of 1:0).

It is by design that a coroutine can only name itself, hence the lack of a coro argument in this function.

See also
bio_get_coro_name

◆ bio_spawn()

static bio_coro_t bio_spawn ( bio_entrypoint_t  entrypoint,
void *  userdata 
)
inlinestatic

Spawn a new coroutine.

This is equivalent to:

bio_spawn_ex(entrypoint, userdata, NULL);
bio_coro_t bio_spawn_ex(bio_entrypoint_t entrypoint, void *userdata, const bio_coro_options_t *options)
Spawn a new coroutine.
Parameters
entrypointThe entrypoint for the coroutine.
userdataData to pass to the entrypoint.
Returns
A new coroutine handle.

◆ bio_spawn_ex()

bio_coro_t bio_spawn_ex ( bio_entrypoint_t  entrypoint,
void *  userdata,
const bio_coro_options_t options 
)

Spawn a new coroutine.

Parameters
entrypointThe entrypoint for the coroutine.
userdataData to pass to the entrypoint.
optionsOption for the new coroutine, can be NULL.
Returns
A new coroutine handle.

◆ bio_yield()

void bio_yield ( void  )

Let a different coroutine run.

Since cooperative scheduling is used in the main thread, if a coroutine is doing something intensive, it could block other coroutines from running. Calling this function will let other coroutines run. However, it is probably a better idea to use the async thread pool instead.

Another use for this would be to implement a busy wait.