Browse Source

Adding coroutine_defer() (with test)

master
Justin R. Cutler 3 years ago
parent
commit
9acc8a43b4
3 changed files with 79 additions and 0 deletions
  1. +49
    -0
      src/coroutine.c
  2. +13
    -0
      test/coroutine.c
  3. +17
    -0
      threadless/coroutine.h

+ 49
- 0
src/coroutine.c 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;
}

+ 13
- 0
test/coroutine.c 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);

+ 17
- 0
threadless/coroutine.h 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 */

Loading…
Cancel
Save