Tasks

The tasks system provides “green threads” (also known as lightweight cooperative threads, or coroutines).

Additionally, the following utilities are provided to be used along with the tasks system:

Types

w_task_t

Type of a task.

Tasks are created by w_task_prepare(), and the resources used by a task (including the stack space used by the task and the w_task_t value itself) will be automatically freed when the tasks is exited. Never deallocate a task manually, or use it after it has been exited.

w_task_func_t

Type of task functions.

w_io_task_t

Stream wrapper for asynchronous input/output.

This is a subclass of :type:w_io_t; all the stream functions can be used on objects of this type. Reading and writing data uses w_task_yield_io_read() and w_task_yield_io_write(), so instead of blocking until completion the current task will be suspended automatically.

Functions

w_task_t* w_task_prepare(w_task_func_t function, void *data, size_t stack_size)

Creates a task with a given stack_size and prepares it for running a function, passing a data pointer to the function. The task will be in paused state upon creation.

To get tasks running, the scheduler must be running, see w_task_run_scheduler().

The stack_size is always rounded up to the size of a memory page. It is possible to pass zero to get the smallest possible stack size (usually 4 kB).

w_task_t* w_task_current()

Obtains the task currently running.

Warning

This function must be called from inside a task, once the task scheduler has been started. Otherwise, calling this function is an error and the execution of the program will be aborted.

void w_task_set_is_system(w_task_t *task, bool is_system)

Set whether a task is a system task. System tasks are those which are always running.

System tasks do not prevent w_task_run_scheduler() from returning.

bool w_task_get_is_system(w_task_t *task)

Checks whether a task is a system task.

See also w_task_set_is_system().

void w_task_set_name(w_task_t *task, const char *name)

Sets the name of a task. The name of the tast is copied as-is, and it is not interpreted in any way. The ability of naming tasks is mainly provided as an aid for debugging client code.

It is possible to pass NULL as the name, which will clear any custom name previously set.

const char* w_task_get_name(w_task_t *task)

Obtains the name of a task.

If a name has not been set using w_task_set_name(), an autogenerated one of the form Task<ID> will be returned.

void w_task_run_scheduler()

Runs the task scheduler.

The scheduler will choose tasks in a round-robin fashion, and let each task run until it gives up the CPU explicitly using w_task_yield() or implicitly when waiting for input/output on a stream be means of w_task_yield_io_read() and w_task_yield_io_write().

The scheduler will keep scheduling tasks until all non-system tasks have been exited.

This function must be called in the main function of a program. Typically:

extern void process_argument (void*);

int main (int argc, char **argv) {
    while (argc--)
        w_task_prepare (process_argument, *argv++, 0);
    w_task_run_scheduler ();
    return 0;
}
void w_task_yield()

Make the current task give up the CPU, giving control back to the task scheduler, which will give other other tasks the chance to run.

void w_task_exit()

Exits the current task. This can be used to exit from a task at any point, without needing to return from the task function.

w_io_result_t w_task_yield_io_read(w_io_t *stream, void *buffer, size_t count)

Reads count bytes into the memory block at buffer from an input stream, suspending the current task as needed.

If the stream has been set as non-blocking and reading from it results in an EAGAIN or EWOULDBLOCK error, the current task will give up the CPU and wait until the data is available for reading as many times as needed, until count bytes are read, the end-of-file marker is reached, or an error is found.

w_io_result_t w_task_yield_io_write(w_io_t *stream, const void *buffer, size_t count)

Writes count bytes from the memory block at buffer to an output stream, suspending the current task as needed.

If the stream has been set as non-blocking and writing to it results in an EAGAIN or EWOULDBLOCK error, the current task will give up the CPU and wait until the stream accepts writing data as many times as needed, until count bytes are written, or an error is found.

bool w_io_task_init(w_io_task_t *wrapper, w_io_t *stream)

Initializes a stream wrapper object (possibly allocated in the stack) which wraps a stream. The wrapper behaves like the wrapped stream, suspending the current task when needed to ensure that I/O is performed asynchronously.

The return value indicates whether the stream can be wrapped. Most of the streams for which w_io_get_fd() returns a valid file descriptor can be wrapped.

w_io_t* w_io_task_open(w_io_t *stream)

Wraps a stream and returns an object that behaves like the wrapped stream, suspending the current task when needed to ensure that I/O is performed asynchronously.

Returns NULL when the stream cannot be wrapped. Most of the streams for which w_io_get_fd() returns a valid file descriptor can be wrapped.

void w_task_system()

Mark the current task as a system task.

See also w_task_set_system().

const char* w_task_name()

Obtain the name of the current task.

See also w_task_get_name().