From 9acc8a43b44565c100fe53f23058d46828886ad6 Mon Sep 17 00:00:00 2001 From: "Justin R. Cutler" Date: Sun, 10 Apr 2016 21:45:20 -0400 Subject: [PATCH] Adding coroutine_defer() (with test) --- src/coroutine.c | 49 ++++++++++++++++++++++++++++++++++++++++++ test/coroutine.c | 13 +++++++++++ threadless/coroutine.h | 17 +++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/src/coroutine.c b/src/coroutine.c index ea35c02..a12f296 100644 --- a/src/coroutine.c +++ b/src/coroutine.c @@ -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; +} diff --git a/test/coroutine.c b/test/coroutine.c index 46d6658..934bcdf 100644 --- a/test/coroutine.c +++ b/test/coroutine.c @@ -52,10 +52,23 @@ static void *fibonacci_generator(coroutine_t *coro, void *data) } +static void deferred_puts(void *data) +{ + puts(data); +} + + static void *output_coroutine(coroutine_t *coro, void *data) { size_t *value = data; + if (coroutine_defer(coro, deferred_puts, "deferred output 0")) { + return NULL; + } + if (coroutine_defer(coro, deferred_puts, "deferred output 1")) { + return NULL; + } + while (NULL != value) { printf("%zu\n", *value); value = coroutine_yield(coro, value); diff --git a/threadless/coroutine.h b/threadless/coroutine.h index b018a58..f4de52b 100644 --- a/threadless/coroutine.h +++ b/threadless/coroutine.h @@ -29,6 +29,11 @@ typedef struct coroutine coroutine_t; */ typedef void *(coroutine_function_t)(coroutine_t *coro, void *data); +/** Coroutine deferred function type + * @param[in,out] data user-defined data + */ +typedef void (coroutine_deferred_function_t)(void *data); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -77,6 +82,18 @@ void *coroutine_resume(coroutine_t *coro, void *value); */ void *coroutine_yield(coroutine_t *coro, void *value); +/** Defer a function call until coroutine termination + * @param[in,out] coro coroutine + * @param function function to call when @p coro terminates + * @param data user-defined point to pass to @p function + * @retval 0 success + * @retval -1 error + * @post @p function will be called with @p data when @p coro terminates + * @note Registered functions will be called in reverse order of registration + */ +int coroutine_defer(coroutine_t *coro, coroutine_deferred_function_t *function, + void *data); + #ifdef __cplusplus } #endif /* __cplusplus */