mirror of
https://github.com/jrcutler/threadless.io.git
synced 2024-07-07 10:35:49 +00:00
Adding coroutine_defer() (with test)
This commit is contained in:
parent
aca3db2b06
commit
9acc8a43b4
@ -29,6 +29,13 @@ enum {
|
|||||||
COROUTINE_ENDED = 1,
|
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 {
|
struct coroutine {
|
||||||
allocation_t allocation;
|
allocation_t allocation;
|
||||||
@ -36,6 +43,7 @@ struct coroutine {
|
|||||||
ucontext_t caller;
|
ucontext_t caller;
|
||||||
void *data;
|
void *data;
|
||||||
int status;
|
int status;
|
||||||
|
deferred_t *deferred;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -78,6 +86,7 @@ coroutine_t *coroutine_create(allocator_t *allocator,
|
|||||||
coro = allocation.memory;
|
coro = allocation.memory;
|
||||||
memset(coro, 0, alloc_size);
|
memset(coro, 0, alloc_size);
|
||||||
coro->allocation = allocation;
|
coro->allocation = allocation;
|
||||||
|
coro->deferred = NULL;
|
||||||
(void) getcontext(&coro->context);
|
(void) getcontext(&coro->context);
|
||||||
coro->context.uc_stack.ss_sp = coro + 1;
|
coro->context.uc_stack.ss_sp = coro + 1;
|
||||||
coro->context.uc_stack.ss_size = stack_size;
|
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)
|
void coroutine_destroy(coroutine_t *coro)
|
||||||
{
|
{
|
||||||
if (NULL != coro) {
|
if (NULL != coro) {
|
||||||
|
coroutine_run_deferred(coro);
|
||||||
allocation_t allocation = coro->allocation;
|
allocation_t allocation = coro->allocation;
|
||||||
allocation_free(&allocation);
|
allocation_free(&allocation);
|
||||||
}
|
}
|
||||||
@ -129,3 +151,30 @@ void *coroutine_yield(coroutine_t *coro, void *value)
|
|||||||
(void) swapcontext(&coro->context, &coro->caller);
|
(void) swapcontext(&coro->context, &coro->caller);
|
||||||
return coro->data;
|
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;
|
||||||
|
}
|
||||||
|
@ -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)
|
static void *output_coroutine(coroutine_t *coro, void *data)
|
||||||
{
|
{
|
||||||
size_t *value = 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) {
|
while (NULL != value) {
|
||||||
printf("%zu\n", *value);
|
printf("%zu\n", *value);
|
||||||
value = coroutine_yield(coro, value);
|
value = coroutine_yield(coro, value);
|
||||||
|
@ -29,6 +29,11 @@ typedef struct coroutine coroutine_t;
|
|||||||
*/
|
*/
|
||||||
typedef void *(coroutine_function_t)(coroutine_t *coro, void *data);
|
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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
@ -77,6 +82,18 @@ void *coroutine_resume(coroutine_t *coro, void *value);
|
|||||||
*/
|
*/
|
||||||
void *coroutine_yield(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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user