lib: Remove "num_contexts" from poll_funcs_state
[metze/samba/wip.git] / lib / poll_funcs / poll_funcs_tevent.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Copyright (C) Volker Lendecke 2013,2014
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "poll_funcs_tevent.h"
20 #include "tevent.h"
21 #include "system/select.h"
22 #include "lib/util/dlinklist.h"
23
24 /*
25  * A poll_watch is asked for by the engine using this library via
26  * funcs->watch_new(). It represents interest in "fd" becoming readable or
27  * writable.
28  */
29
30 struct poll_watch {
31         struct poll_funcs_state *state;
32         size_t slot;            /* index into state->watches[] */
33         int fd;
34         int events;
35         void (*callback)(struct poll_watch *w, int fd, short events,
36                          void *private_data);
37         void *private_data;
38 };
39
40 struct poll_funcs_state {
41         /*
42          * "watches" is the array of all watches that we have handed out via
43          * funcs->watch_new(). The "watches" array can contain NULL pointers.
44          */
45         struct poll_watch **watches;
46
47         /*
48          * "contexts is the array of tevent_contexts that serve
49          * "watches". "contexts" can contain NULL pointers.
50          */
51         struct poll_funcs_tevent_context **contexts;
52 };
53
54 struct poll_funcs_tevent_context {
55         struct poll_funcs_tevent_handle *handles;
56         struct poll_funcs_state *state;
57         unsigned slot;          /* index into state->contexts[] */
58         struct tevent_context *ev;
59         struct tevent_fd **fdes; /* same indexes as state->watches[] */
60 };
61
62 /*
63  * poll_funcs_tevent_register() hands out a struct poll_funcs_tevent_handle as
64  * a void *. poll_funcs_tevent_register allows tevent_contexts to be
65  * registered multiple times, and we can't add a tevent_fd for the same fd's
66  * multiple times. So we have to share one poll_funcs_tevent_context.
67  */
68 struct poll_funcs_tevent_handle {
69         struct poll_funcs_tevent_handle *prev, *next;
70         struct poll_funcs_tevent_context *ctx;
71 };
72
73 static uint16_t poll_events_to_tevent(short events)
74 {
75         uint16_t ret = 0;
76
77         if (events & POLLIN) {
78                 ret |= TEVENT_FD_READ;
79         }
80         if (events & POLLOUT) {
81                 ret |= TEVENT_FD_WRITE;
82         }
83         return ret;
84 }
85
86 static short tevent_to_poll_events(uint16_t flags)
87 {
88         short ret = 0;
89
90         if (flags & TEVENT_FD_READ) {
91                 ret |= POLLIN;
92         }
93         if (flags & TEVENT_FD_WRITE) {
94                 ret |= POLLOUT;
95         }
96         return ret;
97 }
98
99 /*
100  * Find or create a free slot in state->watches[]
101  */
102 static bool poll_funcs_watch_find_slot(struct poll_funcs_state *state,
103                                        size_t *slot)
104 {
105         struct poll_watch **watches;
106         size_t i, num_watches, num_contexts;
107
108         num_watches = talloc_array_length(state->watches);
109
110         for (i=0; i<num_watches; i++) {
111                 if (state->watches[i] == NULL) {
112                         *slot = i;
113                         return true;
114                 }
115         }
116
117         watches = talloc_realloc(state, state->watches, struct poll_watch *,
118                                  num_watches + 1);
119         if (watches == NULL) {
120                 return false;
121         }
122         watches[num_watches] = NULL;
123         state->watches = watches;
124
125         num_contexts = talloc_array_length(state->contexts);
126
127         for (i=0; i<num_contexts; i++) {
128                 struct tevent_fd **fdes;
129                 struct poll_funcs_tevent_context *c = state->contexts[i];
130                 if (c == NULL) {
131                         continue;
132                 }
133                 fdes = talloc_realloc(c, c->fdes, struct tevent_fd *,
134                                       num_watches + 1);
135                 if (fdes == NULL) {
136                         state->watches = talloc_realloc(
137                                 state, state->watches, struct poll_watch *,
138                                 num_watches);
139                         return false;
140                 }
141                 c->fdes = fdes;
142
143                 fdes[num_watches] = NULL;
144         }
145
146         *slot = num_watches;
147
148         return true;
149 }
150
151 static void poll_funcs_fde_handler(struct tevent_context *ev,
152                                    struct tevent_fd *fde, uint16_t flags,
153                                    void *private_data);
154 static int poll_watch_destructor(struct poll_watch *w);
155
156 static struct poll_watch *tevent_watch_new(
157         const struct poll_funcs *funcs, int fd, short events,
158         void (*callback)(struct poll_watch *w, int fd, short events,
159                          void *private_data),
160         void *private_data)
161 {
162         struct poll_funcs_state *state = talloc_get_type_abort(
163                 funcs->private_data, struct poll_funcs_state);
164         struct poll_watch *w;
165         size_t i, slot, num_contexts;
166
167         if (!poll_funcs_watch_find_slot(state, &slot)) {
168                 return NULL;
169         }
170
171         w = talloc(state->watches, struct poll_watch);
172         if (w == NULL) {
173                 return NULL;
174         }
175         w->state = state;
176         w->slot = slot;
177         w->fd = fd;
178         w->events = poll_events_to_tevent(events);
179         w->fd = fd;
180         w->callback = callback;
181         w->private_data = private_data;
182         state->watches[slot] = w;
183
184         talloc_set_destructor(w, poll_watch_destructor);
185
186         num_contexts = talloc_array_length(state->contexts);
187
188         for (i=0; i<num_contexts; i++) {
189                 struct poll_funcs_tevent_context *c = state->contexts[i];
190                 if (c == NULL) {
191                         continue;
192                 }
193                 c->fdes[slot] = tevent_add_fd(c->ev, c->fdes, w->fd, w->events,
194                                               poll_funcs_fde_handler, w);
195                 if (c->fdes[slot] == NULL) {
196                         goto fail;
197                 }
198         }
199         return w;
200
201 fail:
202         TALLOC_FREE(w);
203         return NULL;
204 }
205
206 static int poll_watch_destructor(struct poll_watch *w)
207 {
208         struct poll_funcs_state *state = w->state;
209         size_t num_contexts = talloc_array_length(state->contexts);
210         size_t slot = w->slot;
211         size_t i;
212
213         TALLOC_FREE(state->watches[slot]);
214
215         for (i=0; i<num_contexts; i++) {
216                 struct poll_funcs_tevent_context *c = state->contexts[i];
217                 if (c == NULL) {
218                         continue;
219                 }
220                 TALLOC_FREE(c->fdes[slot]);
221         }
222
223         return 0;
224 }
225
226 static void tevent_watch_update(struct poll_watch *w, short events)
227 {
228         struct poll_funcs_state *state = w->state;
229         size_t num_contexts = talloc_array_length(state->contexts);
230         size_t slot = w->slot;
231         size_t i;
232
233         w->events = poll_events_to_tevent(events);
234
235         for (i=0; i<num_contexts; i++) {
236                 struct poll_funcs_tevent_context *c = state->contexts[i];
237                 if (c == NULL) {
238                         continue;
239                 }
240                 tevent_fd_set_flags(c->fdes[slot], w->events);
241         }
242 }
243
244 static short tevent_watch_get_events(struct poll_watch *w)
245 {
246         return tevent_to_poll_events(w->events);
247 }
248
249 static void tevent_watch_free(struct poll_watch *w)
250 {
251         TALLOC_FREE(w);
252 }
253
254 static struct poll_timeout *tevent_timeout_new(
255         const struct poll_funcs *funcs, const struct timeval tv,
256         void (*callback)(struct poll_timeout *t, void *private_data),
257         void *private_data)
258 {
259         /* not implemented yet */
260         return NULL;
261 }
262
263 static void tevent_timeout_update(struct poll_timeout *t,
264                                   const struct timeval tv)
265 {
266         return;
267 }
268
269 static void tevent_timeout_free(struct poll_timeout *t)
270 {
271         return;
272 }
273
274 static int poll_funcs_state_destructor(struct poll_funcs_state *state);
275
276 struct poll_funcs *poll_funcs_init_tevent(TALLOC_CTX *mem_ctx)
277 {
278         struct poll_funcs *f;
279         struct poll_funcs_state *state;
280
281         f = talloc(mem_ctx, struct poll_funcs);
282         if (f == NULL) {
283                 return NULL;
284         }
285         state = talloc_zero(f, struct poll_funcs_state);
286         if (state == NULL) {
287                 TALLOC_FREE(f);
288                 return NULL;
289         }
290         talloc_set_destructor(state, poll_funcs_state_destructor);
291
292         f->watch_new = tevent_watch_new;
293         f->watch_update = tevent_watch_update;
294         f->watch_get_events = tevent_watch_get_events;
295         f->watch_free = tevent_watch_free;
296         f->timeout_new = tevent_timeout_new;
297         f->timeout_update = tevent_timeout_update;
298         f->timeout_free = tevent_timeout_free;
299         f->private_data = state;
300         return f;
301 }
302
303 static int poll_funcs_state_destructor(struct poll_funcs_state *state)
304 {
305         size_t num_watches = talloc_array_length(state->watches);
306         size_t i;
307         /*
308          * Make sure the watches are cleared before the contexts. The watches
309          * have destructors attached to them that clean up the fde's
310          */
311         for (i=0; i<num_watches; i++) {
312                 TALLOC_FREE(state->watches[i]);
313         }
314         return 0;
315 }
316
317 /*
318  * Find or create a free slot in state->contexts[]
319  */
320 static bool poll_funcs_context_slot_find(struct poll_funcs_state *state,
321                                          struct tevent_context *ev,
322                                          size_t *slot)
323 {
324         struct poll_funcs_tevent_context **contexts;
325         size_t num_contexts = talloc_array_length(state->contexts);
326         size_t i;
327
328         for (i=0; i<num_contexts; i++) {
329                 struct poll_funcs_tevent_context *ctx = state->contexts[i];
330
331                 if ((ctx == NULL) || (ctx->ev == ev)) {
332                         *slot = i;
333                         return true;
334                 }
335         }
336
337         contexts = talloc_realloc(state, state->contexts,
338                                   struct poll_funcs_tevent_context *,
339                                   num_contexts + 1);
340         if (contexts == NULL) {
341                 return false;
342         }
343         state->contexts = contexts;
344         state->contexts[num_contexts] = NULL;
345
346         *slot = num_contexts;
347
348         return true;
349 }
350
351 static int poll_funcs_tevent_context_destructor(
352         struct poll_funcs_tevent_context *ctx);
353
354 static struct poll_funcs_tevent_context *poll_funcs_tevent_context_new(
355         TALLOC_CTX *mem_ctx, struct poll_funcs_state *state,
356         struct tevent_context *ev, unsigned slot)
357 {
358         struct poll_funcs_tevent_context *ctx;
359         size_t num_watches = talloc_array_length(state->watches);
360         size_t i;
361
362         ctx = talloc(mem_ctx, struct poll_funcs_tevent_context);
363         if (ctx == NULL) {
364                 return NULL;
365         }
366
367         ctx->handles = NULL;
368         ctx->state = state;
369         ctx->ev = ev;
370         ctx->slot = slot;
371
372         ctx->fdes = talloc_array(ctx, struct tevent_fd *, num_watches);
373         if (ctx->fdes == NULL) {
374                 goto fail;
375         }
376
377         for (i=0; i<num_watches; i++) {
378                 struct poll_watch *w = state->watches[i];
379
380                 if (w == NULL) {
381                         ctx->fdes[i] = NULL;
382                         continue;
383                 }
384                 ctx->fdes[i] = tevent_add_fd(ev, ctx->fdes, w->fd, w->events,
385                                              poll_funcs_fde_handler, w);
386                 if (ctx->fdes[i] == NULL) {
387                         goto fail;
388                 }
389         }
390         talloc_set_destructor(ctx, poll_funcs_tevent_context_destructor);
391         return ctx;
392 fail:
393         TALLOC_FREE(ctx);
394         return NULL;
395 }
396
397 static int poll_funcs_tevent_context_destructor(
398         struct poll_funcs_tevent_context *ctx)
399 {
400         struct poll_funcs_tevent_handle *h;
401
402         ctx->state->contexts[ctx->slot] = NULL;
403
404         for (h = ctx->handles; h != NULL; h = h->next) {
405                 h->ctx = NULL;
406         }
407
408         return 0;
409 }
410
411 static void poll_funcs_fde_handler(struct tevent_context *ev,
412                                    struct tevent_fd *fde, uint16_t flags,
413                                    void *private_data)
414 {
415         struct poll_watch *w = talloc_get_type_abort(
416                 private_data, struct poll_watch);
417         short events = tevent_to_poll_events(flags);
418         w->callback(w, w->fd, events, w->private_data);
419 }
420
421 static int poll_funcs_tevent_handle_destructor(
422         struct poll_funcs_tevent_handle *handle);
423
424 void *poll_funcs_tevent_register(TALLOC_CTX *mem_ctx, struct poll_funcs *f,
425                                  struct tevent_context *ev)
426 {
427         struct poll_funcs_state *state = talloc_get_type_abort(
428                 f->private_data, struct poll_funcs_state);
429         struct poll_funcs_tevent_handle *handle;
430         size_t slot;
431
432         handle = talloc(mem_ctx, struct poll_funcs_tevent_handle);
433         if (handle == NULL) {
434                 return NULL;
435         }
436
437         if (!poll_funcs_context_slot_find(state, ev, &slot)) {
438                 goto fail;
439         }
440         if (state->contexts[slot] == NULL) {
441                 state->contexts[slot] = poll_funcs_tevent_context_new(
442                         state->contexts, state, ev, slot);
443                 if (state->contexts[slot] == NULL) {
444                         goto fail;
445                 }
446         }
447
448         handle->ctx = state->contexts[slot];
449         DLIST_ADD(handle->ctx->handles, handle);
450         talloc_set_destructor(handle, poll_funcs_tevent_handle_destructor);
451         return handle;
452 fail:
453         TALLOC_FREE(handle);
454         return NULL;
455 }
456
457 static int poll_funcs_tevent_handle_destructor(
458         struct poll_funcs_tevent_handle *handle)
459 {
460         if (handle->ctx == NULL) {
461                 return 0;
462         }
463         if (handle->ctx->handles == NULL) {
464                 abort();
465         }
466
467         DLIST_REMOVE(handle->ctx->handles, handle);
468
469         if (handle->ctx->handles == NULL) {
470                 TALLOC_FREE(handle->ctx);
471         }
472         return 0;
473 }