Adding coroutine_defer() (with test)

This commit is contained in:
Justin R. Cutler 2016-04-10 21:45:20 -04:00
parent aca3db2b06
commit 9acc8a43b4
3 changed files with 79 additions and 0 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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 */