tevent_poll: Decouple poll_ev->fds handling from adding/removing fds
[metze/samba/wip.git] / lib / tevent / tevent_poll.c
1 /*
2    Unix SMB/CIFS implementation.
3    main select loop and event handling
4    Copyright (C) Andrew Tridgell        2003-2005
5    Copyright (C) Stefan Metzmacher      2005-2009
6
7      ** NOTE! The following LGPL license applies to the tevent
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 3 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20
21    You should have received a copy of the GNU Lesser General Public
22    License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "replace.h"
26 #include "system/filesys.h"
27 #include "system/select.h"
28 #include "tevent.h"
29 #include "tevent_util.h"
30 #include "tevent_internal.h"
31
32 struct poll_event_context {
33         /* a pointer back to the generic event_context */
34         struct tevent_context *ev;
35
36         /*
37          * A DLIST for fresh fde's
38          */
39         struct tevent_fd *fresh;
40
41         /*
42          * These two arrays are maintained together.
43          */
44         struct pollfd *fds;
45         struct tevent_fd **fdes;
46         unsigned num_fds;
47
48         /* information for exiting from the event loop */
49         int exit_code;
50 };
51
52 /*
53   create a select_event_context structure.
54 */
55 static int poll_event_context_init(struct tevent_context *ev)
56 {
57         struct poll_event_context *poll_ev;
58
59         poll_ev = talloc_zero(ev, struct poll_event_context);
60         if (poll_ev == NULL) {
61                 return -1;
62         }
63         poll_ev->ev = ev;
64         ev->additional_data = poll_ev;
65         return 0;
66 }
67
68 /*
69   destroy an fd_event
70 */
71 static int poll_event_fd_destructor(struct tevent_fd *fde)
72 {
73         struct tevent_context *ev = fde->event_ctx;
74         struct poll_event_context *poll_ev = NULL;
75         uint64_t del_idx = fde->additional_flags;
76
77         if (ev == NULL) {
78                 goto done;
79         }
80
81         poll_ev = talloc_get_type_abort(
82                 ev->additional_data, struct poll_event_context);
83
84         poll_ev->fdes[del_idx] = NULL;
85 done:
86         return tevent_common_fd_destructor(fde);
87 }
88
89 static int poll_fresh_fde_destructor(struct tevent_fd *fde)
90 {
91         struct poll_event_context *poll_ev = talloc_get_type_abort(
92                 fde->event_ctx->additional_data, struct poll_event_context);
93         DLIST_REMOVE(poll_ev->fresh, fde);
94         return 0;
95 }
96
97 /*
98   add a fd based event
99   return NULL on failure (memory allocation error)
100 */
101 static struct tevent_fd *poll_event_add_fd(struct tevent_context *ev,
102                                            TALLOC_CTX *mem_ctx,
103                                            int fd, uint16_t flags,
104                                            tevent_fd_handler_t handler,
105                                            void *private_data,
106                                            const char *handler_name,
107                                            const char *location)
108 {
109         struct poll_event_context *poll_ev = talloc_get_type_abort(
110                 ev->additional_data, struct poll_event_context);
111         struct tevent_fd *fde;
112
113         if (fd < 0) {
114                 return NULL;
115         }
116
117         fde = talloc(mem_ctx ? mem_ctx : ev, struct tevent_fd);
118         if (fde == NULL) {
119                 return NULL;
120         }
121         fde->event_ctx          = ev;
122         fde->fd                 = fd;
123         fde->flags              = flags;
124         fde->handler            = handler;
125         fde->close_fn           = NULL;
126         fde->private_data       = private_data;
127         fde->handler_name       = handler_name;
128         fde->location           = location;
129         fde->additional_flags   = 0;
130         fde->additional_data    = NULL;
131
132         DLIST_ADD(poll_ev->fresh, fde);
133         talloc_set_destructor(fde, poll_fresh_fde_destructor);
134
135         /*
136          * poll_event_loop_poll will take care of the rest in
137          * poll_event_setup_fresh
138          */
139         return fde;
140 }
141
142 /*
143   set the fd event flags
144 */
145 static void poll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
146 {
147         struct poll_event_context *poll_ev = talloc_get_type_abort(
148                 fde->event_ctx->additional_data, struct poll_event_context);
149         uint64_t idx = fde->additional_flags;
150         uint16_t pollflags = 0;
151
152         if (flags & TEVENT_FD_READ) {
153                 pollflags |= (POLLIN|POLLHUP);
154         }
155         if (flags & TEVENT_FD_WRITE) {
156                 pollflags |= (POLLOUT);
157         }
158
159         poll_ev->fds[idx].events = pollflags;
160
161         fde->flags = flags;
162 }
163
164 static bool poll_event_setup_fresh(struct tevent_context *ev,
165                                    struct poll_event_context *poll_ev)
166 {
167         struct tevent_fd *fde, *next;
168         unsigned num_fresh, num_fds;
169
170         if (poll_ev->fresh == NULL) {
171                 return true;
172         }
173
174         num_fresh = 0;
175         for (fde = poll_ev->fresh; fde; fde = fde->next) {
176                 num_fresh += 1;
177         }
178         num_fds = poll_ev->num_fds + num_fresh;
179
180         /*
181          * We check the length of fdes here. It is the last one
182          * enlarged, so if the realloc for poll_fd->fdes fails,
183          * poll_fd->fds will have at least the size of poll_fd->fdes
184          */
185
186         if (num_fds >= talloc_array_length(poll_ev->fdes)) {
187                 struct pollfd *tmp_fds;
188                 struct tevent_fd **tmp_fdes;
189                 unsigned array_length;
190
191                 array_length = (num_fds + 15) & ~15; /* round up to 16 */
192
193                 tmp_fds = talloc_realloc(
194                         poll_ev, poll_ev->fds, struct pollfd, array_length);
195                 if (tmp_fds == NULL) {
196                         return false;
197                 }
198                 poll_ev->fds = tmp_fds;
199
200                 tmp_fdes = talloc_realloc(
201                         poll_ev, poll_ev->fdes, struct tevent_fd *,
202                         array_length);
203                 if (tmp_fdes == NULL) {
204                         return false;
205                 }
206                 poll_ev->fdes = tmp_fdes;
207         }
208
209         for (fde = poll_ev->fresh; fde; fde = next) {
210                 struct pollfd *pfd;
211
212                 pfd = &poll_ev->fds[poll_ev->num_fds];
213
214                 pfd->fd = fde->fd;
215                 pfd->events = 0;
216                 pfd->revents = 0;
217
218                 if (fde->flags & TEVENT_FD_READ) {
219                         pfd->events |= (POLLIN|POLLHUP);
220                 }
221                 if (fde->flags & TEVENT_FD_WRITE) {
222                         pfd->events |= (POLLOUT);
223                 }
224
225                 fde->additional_flags = poll_ev->num_fds;
226                 poll_ev->fdes[poll_ev->num_fds] = fde;
227
228                 next = fde->next;
229                 DLIST_REMOVE(poll_ev->fresh, fde);
230                 DLIST_ADD(ev->fd_events, fde);
231
232                 talloc_set_destructor(fde, poll_event_fd_destructor);
233
234                 poll_ev->num_fds += 1;
235         }
236         return true;
237 }
238
239 /*
240   event loop handling using poll()
241 */
242 static int poll_event_loop_poll(struct tevent_context *ev,
243                                 struct timeval *tvalp)
244 {
245         struct poll_event_context *poll_ev = talloc_get_type_abort(
246                 ev->additional_data, struct poll_event_context);
247         int pollrtn;
248         int timeout = -1;
249         unsigned i;
250
251         if (ev->signal_events && tevent_common_check_signal(ev)) {
252                 return 0;
253         }
254
255         if (tvalp != NULL) {
256                 timeout = tvalp->tv_sec * 1000;
257                 timeout += (tvalp->tv_usec + 999) / 1000;
258         }
259
260         if (!poll_event_setup_fresh(ev, poll_ev)) {
261                 return -1;
262         }
263
264         tevent_trace_point_callback(poll_ev->ev, TEVENT_TRACE_BEFORE_WAIT);
265         pollrtn = poll(poll_ev->fds, poll_ev->num_fds, timeout);
266         tevent_trace_point_callback(poll_ev->ev, TEVENT_TRACE_AFTER_WAIT);
267
268         if (pollrtn == -1 && errno == EINTR && ev->signal_events) {
269                 tevent_common_check_signal(ev);
270                 return 0;
271         }
272
273         if (pollrtn == 0 && tvalp) {
274                 /* we don't care about a possible delay here */
275                 tevent_common_loop_timer_delay(ev);
276                 return 0;
277         }
278
279         if (pollrtn <= 0) {
280                 /*
281                  * No fd's ready
282                  */
283                 return 0;
284         }
285
286         /* at least one file descriptor is ready - check
287            which ones and call the handler, being careful to allow
288            the handler to remove itself when called */
289
290         for (i=0; i<poll_ev->num_fds; i++) {
291                 struct pollfd *pfd;
292                 struct tevent_fd *fde;
293                 uint16_t flags = 0;
294
295                 fde = poll_ev->fdes[i];
296                 if (fde == NULL) {
297                         /*
298                          * This fde was talloc_free()'ed. Delete it
299                          * from the arrays
300                          */
301                         poll_ev->num_fds -= 1;
302                         poll_ev->fds[i] = poll_ev->fds[poll_ev->num_fds];
303                         poll_ev->fdes[i] = poll_ev->fdes[poll_ev->num_fds];
304                         if (poll_ev->fdes[i] != NULL) {
305                                 poll_ev->fdes[i]->additional_flags = i;
306                         }
307                         continue;
308                 }
309
310                 pfd = &poll_ev->fds[i];
311
312                 if (pfd->revents & (POLLHUP|POLLERR)) {
313                         /* If we only wait for TEVENT_FD_WRITE, we
314                            should not tell the event handler about it,
315                            and remove the writable flag, as we only
316                            report errors when waiting for read events
317                            to match the select behavior. */
318                         if (!(fde->flags & TEVENT_FD_READ)) {
319                                 TEVENT_FD_NOT_WRITEABLE(fde);
320                                 continue;
321                         }
322                         flags |= TEVENT_FD_READ;
323                 }
324                 if (pfd->revents & POLLIN) {
325                         flags |= TEVENT_FD_READ;
326                 }
327                 if (pfd->revents & POLLOUT) {
328                         flags |= TEVENT_FD_WRITE;
329                 }
330                 if (flags != 0) {
331                         fde->handler(ev, fde, flags, fde->private_data);
332                         break;
333                 }
334         }
335
336         return 0;
337 }
338
339 /*
340   do a single event loop using the events defined in ev
341 */
342 static int poll_event_loop_once(struct tevent_context *ev,
343                                 const char *location)
344 {
345         struct timeval tval;
346
347         if (ev->signal_events &&
348             tevent_common_check_signal(ev)) {
349                 return 0;
350         }
351
352         if (ev->immediate_events &&
353             tevent_common_loop_immediate(ev)) {
354                 return 0;
355         }
356
357         tval = tevent_common_loop_timer_delay(ev);
358         if (tevent_timeval_is_zero(&tval)) {
359                 return 0;
360         }
361
362         return poll_event_loop_poll(ev, &tval);
363 }
364
365 static const struct tevent_ops poll_event_ops = {
366         .context_init           = poll_event_context_init,
367         .add_fd                 = poll_event_add_fd,
368         .set_fd_close_fn        = tevent_common_fd_set_close_fn,
369         .get_fd_flags           = tevent_common_fd_get_flags,
370         .set_fd_flags           = poll_event_set_fd_flags,
371         .add_timer              = tevent_common_add_timer,
372         .schedule_immediate     = tevent_common_schedule_immediate,
373         .add_signal             = tevent_common_add_signal,
374         .loop_once              = poll_event_loop_once,
375         .loop_wait              = tevent_common_loop_wait,
376 };
377
378 _PRIVATE_ bool tevent_poll_init(void)
379 {
380         return tevent_register_backend("poll", &poll_event_ops);
381 }