1 contributor
93 lines2.4 KB
1#include "generator.h"
2#include "coroutine.h"
3#include <stdlib.h>
4#include <stdio.h>
5#include <assert.h>
6#include "cor_thread_local.h"
7
8enum {
9 Generator_Running,
10 Generator_Deleting,
11 Generator_Complete
12};
13
14struct Generator {
15 Coroutine *coroutine;
16 Coroutine *caller;
17 void *(*start)(void *);
18 void *param;
19 char state;
20};
21
22_Cor_thread_local Generator *g_active_generator = NULL;
23
24static void on_yield_gen(void *this){
25 Generator *gen = (Generator *)this;
26 Coroutine_Continue(gen->caller, Coroutine_GetValue(gen->coroutine), true);
27}
28
29static void on_yield_gen_caller(void *this){
30 (void)this;
31}
32
33static void *do_start(void *this){
34 Generator *gen = (Generator *)this;
35 g_active_generator = this;
36 assert(gen->state == Generator_Running || gen->state == Generator_Deleting);
37 void *value;
38 if (gen->state == Generator_Running){
39 g_active_generator = gen;
40 value = gen->start(gen->param);
41 g_active_generator = NULL;
42 } else {
43 // we are being deleted
44 value = NULL;
45 }
46 gen->state = Generator_Complete;
47 Coroutine_Continue(gen->caller, value, true);
48 return value;
49}
50
51Generator *Generator_New(void *(*start)(void *), void *param){
52 Generator *gen = malloc(sizeof(Generator));
53 gen->start = start;
54 gen->param = param;
55 gen->coroutine = Coroutine_New(do_start);
56 gen->state = Generator_Running;
57 return gen;
58}
59
60void Generator_Delete(Generator *gen){
61 assert(gen->state != Generator_Deleting);
62 if (gen->state == Generator_Running){
63 gen->state = Generator_Deleting;
64 gen->caller = Coroutine_GetActive();
65 Coroutine_Continue(gen->coroutine, gen, true);
66 Coroutine_Yield(NULL, on_yield_gen_caller, gen);
67 }
68 assert(gen->state == Generator_Complete);
69 Coroutine_Delete(gen->coroutine);
70 free(gen);
71}
72
73bool Generator_Next(Generator *gen, void **value){
74 assert(gen->state != Generator_Deleting);
75 if (gen->state == Generator_Complete){
76 return false;
77 }
78 gen->caller = Coroutine_GetActive();
79 Coroutine_Continue(gen->coroutine, gen, true);
80 *value = Coroutine_Yield(NULL, on_yield_gen_caller, gen);
81 return Coroutine_IsRunning(gen->coroutine);
82}
83
84bool Generator_Yield(void *value)
85{
86 assert(g_active_generator);
87 Generator *gen = g_active_generator;
88 g_active_generator = NULL;
89 Coroutine_Yield(value, on_yield_gen, gen);
90 g_active_generator = gen;
91 return gen->state == Generator_Running;
92}
93