| #include "generator.h" |
| #include "coroutine.h" |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include "cor_thread_local.h" |
|
| enum { |
| Generator_Running, |
| Generator_Deleting, |
| Generator_Complete |
| }; |
|
| struct Generator { |
| Coroutine *coroutine; |
| Coroutine *caller; |
| void *(*start)(void *); |
| void *param; |
| char state; |
| }; |
|
| _Cor_thread_local Generator *g_active_generator = NULL; |
|
| static void on_yield_gen(void *this){ |
| Generator *gen = (Generator *)this; |
| Coroutine_Continue(gen->caller, Coroutine_GetValue(gen->coroutine), true); |
| } |
|
| static void on_yield_gen_caller(void *this){ |
| (void)this; |
| } |
|
| static void *do_start(void *this){ |
| Generator *gen = (Generator *)this; |
| g_active_generator = this; |
| assert(gen->state == Generator_Running || gen->state == Generator_Deleting); |
| void *value; |
| if (gen->state == Generator_Running){ |
| g_active_generator = gen; |
| value = gen->start(gen->param); |
| g_active_generator = NULL; |
| } else { |
| // we are being deleted |
| value = NULL; |
| } |
| gen->state = Generator_Complete; |
| Coroutine_Continue(gen->caller, value, true); |
| return value; |
| } |
|
| Generator *Generator_New(void *(*start)(void *), void *param){ |
| Generator *gen = malloc(sizeof(Generator)); |
| gen->start = start; |
| gen->param = param; |
| gen->coroutine = Coroutine_New(do_start); |
| gen->state = Generator_Running; |
| return gen; |
| } |
|
| void Generator_Delete(Generator *gen){ |
| assert(gen->state != Generator_Deleting); |
| if (gen->state == Generator_Running){ |
| gen->state = Generator_Deleting; |
| gen->caller = Coroutine_GetActive(); |
| Coroutine_Continue(gen->coroutine, gen, true); |
| Coroutine_Yield(NULL, on_yield_gen_caller, gen); |
| } |
| assert(gen->state == Generator_Complete); |
| Coroutine_Delete(gen->coroutine); |
| free(gen); |
| } |
|
| bool Generator_Next(Generator *gen, void **value){ |
| assert(gen->state != Generator_Deleting); |
| if (gen->state == Generator_Complete){ |
| return false; |
| } |
| gen->caller = Coroutine_GetActive(); |
| Coroutine_Continue(gen->coroutine, gen, true); |
| *value = Coroutine_Yield(NULL, on_yield_gen_caller, gen); |
| return Coroutine_IsRunning(gen->coroutine); |
| } |
|
| bool Generator_Yield(void *value) |
| { |
| assert(g_active_generator); |
| Generator *gen = g_active_generator; |
| g_active_generator = NULL; |
| Coroutine_Yield(value, on_yield_gen, gen); |
| g_active_generator = gen; |
| return gen->state == Generator_Running; |
| } |
|