lib/util: add tevent_coroutine infrastructure based on Portable Coroutine Library
[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         struct tevent_immediate *trigger;
62         tevent_coroutine_fn_t fn;
63         struct tevent_coroutine_result result;
64         coroutine_t coro;
65         uint8_t stack[4196];
66 };
67
68 static void tevent_coroutine_body(void *data);
69
70 static void tevent_couroutine_start_callback(struct tevent_context *ctx,
71                                              struct tevent_immediate *im,
72                                              void *private_data);
73
74 struct tevent_coroutine *_tevent_coroutine_create(
75                         struct tevent_req *req,
76                         struct tevent_context *ev,
77                         tevent_coroutine_fn_t fn,
78                         const char *location)
79 {
80         void *state = _tevent_req_data(req);
81         struct tevent_coroutine *tco;
82
83         tco = talloc(state, struct tevent_coroutine);
84         if (tco == NULL) {
85                 return NULL;
86         }
87
88         tco->req = req;
89         tco->ev = ev;
90         tco->create_location = location;
91         tco->trigger = tevent_create_immediate(tco);
92         if (tco->trigger == NULL) {
93                 talloc_free(tco);
94                 return NULL;
95         }
96         tco->fn = fn;
97         ZERO_STRUCT(tco->result);
98         tco->coro = co_create(tevent_coroutine_body, tco,
99                               tco->stack, sizeof(tco->stack));
100         if (tco->coro == NULL) {
101                 talloc_free(tco);
102                 return NULL;
103         }
104
105         tevent_schedule_immediate(tco->trigger,
106                                   tevent_coroutine_start_callback,
107                                   tco);
108         return tco;
109 }
110
111 static void tevent_coroutine_body(void *data)
112 {
113         struct tevent_coroutine *tco = talloc_get_type_abort(data,
114                                        struct tevent_coroutine);
115         void *fn_state = _tevent_req_data(tco->req);
116         struct tevent_coroutine_result *res;
117
118         /* now call the real implementation */
119         res = tco->fn(tco, tco->ev, fn_state);
120
121         /*
122          * now destroy the coroutine_t handle and jump back to
123          * tevent_coroutine_run() after co_call().
124          */
125         tco->coro = NULL;
126         co_exit();
127 }
128
129 static void tevent_coroutine_run(struct tevent_coroutine *tco)
130 {
131         coroutine_t cur = co_current();
132         if (tco->coro == cur) {
133                 /* this should never happen */
134                 abort();
135         }
136
137         tco->result.state = TEVENT_COROUTINE_IN_PROGRESS;
138         /*
139          * now start or continue the given coroutine
140          * on its own stack. co_call() returns when
141          * the coroutine has called co_resume() (via tevent_coroutine_yield())
142          * or co_exit() (via tevent_coroutine_return()
143          */
144         co_call(tco->coro);
145         /* now we're back on the original stack */
146
147         switch (tco->result.state) {
148         case TEVENT_COROUTINE_INIT:
149         case TEVENT_COROUTINE_IN_PROGRESS:
150                 /* this should never be reached */
151                 abort();
152
153         case TEVENT_COROUTINE_YIELD:
154                 /* we'll resume later */
155                 return;
156
157         case TEVENT_COROUTINE_DONE:
158                 _tevent_req_done(tco->req, tco->result.location);
159                 return;
160
161         case TEVENT_COROUTINE_NO_MEMORY:
162                 _tevent_req_nomem(NULL, tco->req, tco->result.location);
163                 return;
164
165         case TEVENT_COROUTINE_USER_ERROR:
166                 _tevent_req_error(tco->req, tco->result.error,
167                                   tco->result.location);
168                 return;
169         }
170
171         /* this should never be reached */
172         abort();
173 }
174
175 static void tevent_couroutine_start_callback(struct tevent_context *ctx,
176                                              struct tevent_immediate *im,
177                                              void *private_data)
178 {
179         struct tevent_coroutine *tco = talloc_get_type_abort(private_data,
180                                        struct tevent_coroutine);
181         tevent_coroutine_run(tco);
182 }
183
184 static void tevent_coroutine_yield_callback(struct tevent_req *subreq);
185
186 bool _tevent_coroutine_yield(struct tevent_coroutine *tco,
187                              struct tevent_req *subreq,
188                              const char *location)
189 {
190         bool ok;
191
192         ok = _tevent_coroutine_nomem(subreq, tco, location);
193         if (!ok) {
194                 return false;
195         }
196
197         tco->result.state = TEVENT_COROUTINE_YIELD;
198         tco->result.location = location;
199
200         tevent_req_set_callback(subreq,
201                                 tevent_coroutine_yield_callback,
202                                 tco);
203
204         /*
205          * this jumps back to tevent_coroutine_run() after
206          * co_call().
207          */
208         co_resume();
209
210         return true;
211 }
212
213 static void tevent_coroutine_yield_callback(struct tevent_req *subreq)
214 {
215         struct tevent_coroutine *tco = tevent_req_callback_data(subreq,
216                                        struct tevent_coroutine);
217         tevent_coroutine_run(tco);
218 }
219
220 void _tevent_coroutine_done(struct tevent_coroutine *tco,
221                             const char *location)
222 {
223         tco->result.state = TEVENT_COROUTINE_DONE;
224         tco->result.location = location;
225 }
226
227 bool _tevent_coroutine_nomem(const void *ptr,
228                              struct tevent_coroutine *tco,
229                              const char *location)
230 {
231         if (ptr != NULL) {
232                 return false;
233         }
234
235         tco->result.state = TEVENT_COROUTINE_NO_MEMORY;
236         tco->result.location = location;
237         return true;
238 }
239
240 bool _tevent_coroutine_error(struct tevent_coroutine *tco,
241                              uint64_t error,
242                              const char *location)
243 {
244         if (error == 0) {
245                 return false;
246         }
247
248         tco->result.state = TEVENT_COROUTINE_USER_ERROR;
249         tco->result.error = error;
250         tco->result.location = location;
251         return true;
252 }
253
254 struct tevent_coroutine_result *_tevent_coroutine_return(
255                         struct tevent_coroutine *tco,
256                         const char *location)
257 {
258         tco->result.return_location = location;
259         return &tco->result;
260 }
261