tevent_coroutine: let tevent_coroutine_yield() check for subreq == NULL, to make...
[metze/samba/wip.git] / lib / util / tevent_coroutine.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Coroutine abstraction for tevent.
5
6    Copyright (C) Stefan Metzmacher 2009
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "replace.h"
23 #include <tevent.h>
24 #include "tevent_coroutine.h"
25 #include <pcl.h>
26
27 /*
28
29 typedef void *coroutine_t;
30
31
32 coroutine_t co_create(void (*func)(void *), void *data, void *stack, int size);
33 void co_delete(coroutine_t coro);
34 void co_call(coroutine_t coro);
35 void co_resume(void);
36 void co_exit_to(coroutine_t coro);
37 void co_exit(void);
38 coroutine_t co_current(void);
39 */
40
41 struct tevent_coroutine;
42
43 struct tevent_coroutine_result {
44         enum {
45                 TEVENT_COROUTINE_INIT = 0,
46                 TEVENT_COROUTINE_IN_PROGRESS,
47                 TEVENT_COROUTINE_YIELD,
48                 TEVENT_COROUTINE_DONE,
49                 TEVENT_COROUTINE_NO_MEMORY,
50                 TEVENT_COROUTINE_USER_ERROR
51         } state;
52         uint64_t error;
53         const char *location;
54         const char *return_location;
55 };
56
57 struct tevent_coroutine {
58         struct tevent_req *req;
59         struct tevent_context *ev;
60         const char *create_location;
61         tevent_coroutine_fn_t fn;
62         struct tevent_coroutine_result result;
63         coroutine_t coro;
64         uint8_t stack[4196];
65 };
66
67 static void tevent_coroutine_body(void *data);
68
69 struct tevent_coroutine *_tevent_coroutine_create(
70                         struct tevent_req *req,
71                         struct tevent_context *ev,
72                         tevent_coroutine_fn_t fn,
73                         const char *location)
74 {
75         void *state = _tevent_req_data(req);
76         struct tevent_coroutine *tco;
77
78         tco = talloc(state, struct tevent_coroutine);
79         if (tco == NULL) {
80                 return NULL;
81         }
82
83         tco->req = req;
84         tco->ev = ev;
85         tco->create_location = location;
86         tco->fn = fn;
87         ZERO_STRUCT(tco->result);
88         tco->coro = co_create(tevent_coroutine_body, tco,
89                               tco->stack, sizeof(tco->stack));
90         if (tco->coro == NULL) {
91                 talloc_free(tco);
92                 return NULL;
93         }
94
95         return tco;
96 }
97
98 static void tevent_coroutine_body(void *data)
99 {
100         struct tevent_coroutine *tco = talloc_get_type_abort(data,
101                                        struct tevent_coroutine);
102         void *fn_state = _tevent_req_data(tco->req);
103         struct tevent_coroutine_result *res;
104
105         /* now call the real implementation */
106         res = tco->fn(tco, tco->ev, fn_state);
107
108         /*
109          * now destroy the coroutine_t handle and jump back to
110          * tevent_coroutine_run() after co_call().
111          */
112         tco->coro = NULL;
113         co_exit();
114 }
115
116 void tevent_coroutine_run(struct tevent_coroutine *tco)
117 {
118         coroutine_t cur = co_current();
119         if (tco->coro == cur) {
120                 /* this should never happen */
121                 abort();
122         }
123
124         tco->result.state = TEVENT_COROUTINE_IN_PROGRESS;
125         /*
126          * now start or continue the given coroutine
127          * on its own stack. co_call() returns when
128          * the coroutine has called co_resume() (via tevent_coroutine_yield())
129          * or co_exit() (via tevent_coroutine_return()
130          */
131         co_call(tco->coro);
132         /* now we're back on the original stack */
133
134         switch (tco->result.state) {
135         case TEVENT_COROUTINE_INIT:
136         case TEVENT_COROUTINE_IN_PROGRESS:
137                 /* this should never be reached */
138                 abort();
139
140         case TEVENT_COROUTINE_YIELD:
141                 /* we'll resume later */
142                 return;
143
144         case TEVENT_COROUTINE_DONE:
145                 _tevent_req_done(tco->req, tco->result.location);
146                 return;
147
148         case TEVENT_COROUTINE_NO_MEMORY:
149                 _tevent_req_nomem(NULL, tco->req, tco->result.location);
150                 return;
151
152         case TEVENT_COROUTINE_USER_ERROR:
153                 _tevent_req_error(tco->req, tco->result.error,
154                                   tco->result.location);
155                 return;
156         }
157
158         /* this should never be reached */
159         abort();
160 }
161
162 static void tevent_coroutine_yield_callback(struct tevent_req *subreq);
163
164 void _tevent_coroutine_yield(struct tevent_coroutine *tco,
165                              struct tevent_req *subreq,
166                              const char *location)
167 {
168         if (subreq == NULL) {
169                 tco->result.state = TEVENT_COROUTINE_NO_MEMORY;
170                 tco->result.location = location;
171                 tco->result.return_location = location;
172                 /*
173                  * this jumps back to tevent_coroutine_run() after
174                  * co_call().
175                  */
176                 tco->coro = NULL;
177                 co_exit();
178                 return;
179         }
180
181         tco->result.state = TEVENT_COROUTINE_YIELD;
182         tco->result.location = location;
183
184         tevent_req_set_callback(subreq,
185                                 tevent_coroutine_yield_callback,
186                                 tco);
187
188         /*
189          * this jumps back to tevent_coroutine_run() after
190          * co_call().
191          */
192         co_resume();
193 }
194
195 static void tevent_coroutine_yield_callback(struct tevent_req *subreq)
196 {
197         struct tevent_coroutine *tco = tevent_req_callback_data(subreq,
198                                        struct tevent_coroutine);
199         tevent_coroutine_run(tco);
200 }
201
202 void _tevent_coroutine_done(struct tevent_coroutine *tco,
203                             const char *location)
204 {
205         tco->result.state = TEVENT_COROUTINE_DONE;
206         tco->result.location = location;
207 }
208
209 bool _tevent_coroutine_nomem(const void *ptr,
210                              struct tevent_coroutine *tco,
211                              const char *location)
212 {
213         if (ptr != NULL) {
214                 return false;
215         }
216
217         tco->result.state = TEVENT_COROUTINE_NO_MEMORY;
218         tco->result.location = location;
219         return true;
220 }
221
222 bool _tevent_coroutine_error(struct tevent_coroutine *tco,
223                              uint64_t error,
224                              const char *location)
225 {
226         if (error == 0) {
227                 return false;
228         }
229
230         tco->result.state = TEVENT_COROUTINE_USER_ERROR;
231         tco->result.error = error;
232         tco->result.location = location;
233         return true;
234 }
235
236 struct tevent_coroutine_result *_tevent_coroutine_return(
237                         struct tevent_coroutine *tco,
238                         const char *location)
239 {
240         tco->result.return_location = location;
241         return &tco->result;
242 }
243