|
|
|
@ -29,6 +29,13 @@ enum {
|
|
|
|
|
COROUTINE_ENDED = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct deferred deferred_t;
|
|
|
|
|
struct deferred {
|
|
|
|
|
allocation_t allocation;
|
|
|
|
|
coroutine_deferred_function_t *function;
|
|
|
|
|
void *data;
|
|
|
|
|
deferred_t *next;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct coroutine {
|
|
|
|
|
allocation_t allocation;
|
|
|
|
@ -36,6 +43,7 @@ struct coroutine {
|
|
|
|
|
ucontext_t caller;
|
|
|
|
|
void *data;
|
|
|
|
|
int status;
|
|
|
|
|
deferred_t *deferred;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -78,6 +86,7 @@ coroutine_t *coroutine_create(allocator_t *allocator,
|
|
|
|
|
coro = allocation.memory;
|
|
|
|
|
memset(coro, 0, alloc_size);
|
|
|
|
|
coro->allocation = allocation;
|
|
|
|
|
coro->deferred = NULL;
|
|
|
|
|
(void) getcontext(&coro->context);
|
|
|
|
|
coro->context.uc_stack.ss_sp = coro + 1;
|
|
|
|
|
coro->context.uc_stack.ss_size = stack_size;
|
|
|
|
@ -94,9 +103,22 @@ fail:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void coroutine_run_deferred(coroutine_t *coro)
|
|
|
|
|
{
|
|
|
|
|
deferred_t *deferred = coro->deferred;
|
|
|
|
|
while (NULL != deferred) {
|
|
|
|
|
allocation_t allocation = deferred->allocation;
|
|
|
|
|
deferred->function(deferred->data);
|
|
|
|
|
deferred = deferred->next;
|
|
|
|
|
allocation_free(&allocation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void coroutine_destroy(coroutine_t *coro)
|
|
|
|
|
{
|
|
|
|
|
if (NULL != coro) {
|
|
|
|
|
coroutine_run_deferred(coro);
|
|
|
|
|
allocation_t allocation = coro->allocation;
|
|
|
|
|
allocation_free(&allocation);
|
|
|
|
|
}
|
|
|
|
@ -129,3 +151,30 @@ void *coroutine_yield(coroutine_t *coro, void *value)
|
|
|
|
|
(void) swapcontext(&coro->context, &coro->caller);
|
|
|
|
|
return coro->data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int coroutine_defer(coroutine_t *coro, coroutine_deferred_function_t *function,
|
|
|
|
|
void *data)
|
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
|
deferred_t *deferred = NULL;
|
|
|
|
|
allocation_t allocation;
|
|
|
|
|
|
|
|
|
|
/* allocate memory */
|
|
|
|
|
allocation_init(&allocation, coro->allocation.allocator);
|
|
|
|
|
error = allocation_realloc_array(&allocation, 1, sizeof(*deferred));
|
|
|
|
|
|
|
|
|
|
if (!error) {
|
|
|
|
|
/* initialize deferred work */
|
|
|
|
|
deferred = allocation.memory;
|
|
|
|
|
deferred->allocation = allocation;
|
|
|
|
|
deferred->function = function;
|
|
|
|
|
deferred->data = data;
|
|
|
|
|
|
|
|
|
|
/* push deferred work to front of list */
|
|
|
|
|
deferred->next = coro->deferred;
|
|
|
|
|
coro->deferred = deferred;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|