2016-04-01 13:42:12 +00:00
|
|
|
/* threadless.io
|
|
|
|
* Copyright (c) 2016 Justin R. Cutler
|
|
|
|
* Licensed under the MIT License. See LICENSE file in the project root for
|
|
|
|
* full license information.
|
|
|
|
*/
|
2016-04-02 20:37:31 +00:00
|
|
|
/** @file
|
|
|
|
* coroutine interface implementation
|
|
|
|
* @author Justin R. Cutler <justin.r.cutler@gmail.com>
|
|
|
|
*/
|
2016-04-01 13:42:12 +00:00
|
|
|
|
2016-03-31 06:31:46 +00:00
|
|
|
/* stack_t (in ucontext.h) */
|
|
|
|
#define _BSD_SOURCE
|
|
|
|
|
|
|
|
/* errno, ENOMEM */
|
|
|
|
#include <errno.h>
|
2016-04-03 01:10:40 +00:00
|
|
|
/* memset */
|
|
|
|
#include <string.h>
|
2016-03-31 06:31:46 +00:00
|
|
|
|
|
|
|
/* ucontext_t, getcontext, makecontext, swapcontext */
|
|
|
|
#include <ucontext.h>
|
|
|
|
|
2016-04-04 00:52:32 +00:00
|
|
|
/* allocator_t allocation_t, allocation_init, allocation_realloc_array */
|
|
|
|
#include <threadless/allocation.h>
|
2016-03-31 06:31:46 +00:00
|
|
|
/* ... */
|
|
|
|
#include <threadless/coroutine.h>
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
COROUTINE_ENDED = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct coroutine {
|
2016-04-04 00:52:32 +00:00
|
|
|
allocation_t allocation;
|
|
|
|
ucontext_t context;
|
|
|
|
ucontext_t caller;
|
|
|
|
void *data;
|
|
|
|
int status;
|
2016-03-31 06:31:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-04-02 20:35:45 +00:00
|
|
|
static void coroutine_entry_point(coroutine_t *, coroutine_function_t *)
|
2016-03-31 06:31:46 +00:00
|
|
|
__attribute__ ((noreturn));
|
2016-04-02 20:35:45 +00:00
|
|
|
static void coroutine_entry_point(coroutine_t *c,
|
|
|
|
coroutine_function_t *function)
|
2016-03-31 06:31:46 +00:00
|
|
|
{
|
|
|
|
/* run function until it returns */
|
|
|
|
void *retval = function(c, c->data);
|
|
|
|
|
|
|
|
/* mark as ended */
|
|
|
|
c->status |= COROUTINE_ENDED;
|
|
|
|
|
|
|
|
/* yield final return value (forever) */
|
|
|
|
for (;;) {
|
|
|
|
(void) coroutine_yield(c, retval);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-03 01:10:40 +00:00
|
|
|
coroutine_t *coroutine_create(allocator_t *allocator,
|
|
|
|
coroutine_function_t *function, size_t stack_size)
|
2016-03-31 06:31:46 +00:00
|
|
|
{
|
2016-04-03 01:10:40 +00:00
|
|
|
coroutine_t *coro = NULL;
|
|
|
|
size_t alloc_size = sizeof(*coro) + stack_size;
|
|
|
|
|
|
|
|
if (alloc_size < stack_size) {
|
2016-03-31 06:31:46 +00:00
|
|
|
/* integer overflow */
|
|
|
|
errno = ENOMEM;
|
2016-04-03 01:10:40 +00:00
|
|
|
goto fail;
|
2016-03-31 06:31:46 +00:00
|
|
|
}
|
|
|
|
|
2016-04-04 00:52:32 +00:00
|
|
|
allocation_t allocation;
|
|
|
|
allocation_init(&allocation, allocator);
|
|
|
|
if (allocation_realloc_array(&allocation, 1, alloc_size)) {
|
2016-03-31 06:31:46 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2016-04-04 00:52:32 +00:00
|
|
|
coro = allocation.memory;
|
2016-04-03 01:10:40 +00:00
|
|
|
memset(coro, 0, alloc_size);
|
2016-04-04 00:52:32 +00:00
|
|
|
coro->allocation = allocation;
|
2016-03-31 06:31:46 +00:00
|
|
|
(void) getcontext(&coro->context);
|
2016-04-03 01:10:40 +00:00
|
|
|
coro->context.uc_stack.ss_sp = coro + 1;
|
|
|
|
coro->context.uc_stack.ss_size = stack_size;
|
2016-03-31 06:31:46 +00:00
|
|
|
|
|
|
|
makecontext(&coro->context, (void (*)(void)) coroutine_entry_point, 2,
|
|
|
|
coro, function);
|
|
|
|
|
|
|
|
return coro;
|
|
|
|
|
|
|
|
fail:
|
2016-04-03 01:10:40 +00:00
|
|
|
coroutine_destroy(coro);
|
2016-03-31 06:31:46 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-02 20:35:45 +00:00
|
|
|
void coroutine_destroy(coroutine_t *coro)
|
2016-03-31 06:31:46 +00:00
|
|
|
{
|
2016-04-04 00:52:32 +00:00
|
|
|
if (NULL != coro) {
|
|
|
|
allocation_t allocation = coro->allocation;
|
|
|
|
allocation_free(&allocation);
|
2016-03-31 06:31:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-02 20:35:45 +00:00
|
|
|
bool coroutine_ended(const coroutine_t *coro)
|
2016-03-31 06:31:46 +00:00
|
|
|
{
|
|
|
|
return (NULL == coro) || !!(coro->status & COROUTINE_ENDED);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-02 20:35:45 +00:00
|
|
|
void *coroutine_resume(coroutine_t *coro, void *value)
|
2016-03-31 06:31:46 +00:00
|
|
|
{
|
|
|
|
if (NULL == coro) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
coro->data = value;
|
|
|
|
(void) swapcontext(&coro->caller, &coro->context);
|
|
|
|
return coro->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-02 20:35:45 +00:00
|
|
|
void *coroutine_yield(coroutine_t *coro, void *value)
|
2016-03-31 06:31:46 +00:00
|
|
|
{
|
|
|
|
if (NULL == coro) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
coro->data = value;
|
|
|
|
(void) swapcontext(&coro->context, &coro->caller);
|
|
|
|
return coro->data;
|
|
|
|
}
|