tevent: add tevent_loop_until()
[samba.git] / lib / tevent / tevent.c
1 /* 
2    Unix SMB/CIFS implementation.
3    main select loop and event handling
4    Copyright (C) Andrew Tridgell 2003
5    Copyright (C) Stefan Metzmacher 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 /*
26   PLEASE READ THIS BEFORE MODIFYING!
27
28   This module is a general abstraction for the main select loop and
29   event handling. Do not ever put any localised hacks in here, instead
30   register one of the possible event types and implement that event
31   somewhere else.
32
33   There are 2 types of event handling that are handled in this module:
34
35   1) a file descriptor becoming readable or writeable. This is mostly
36      used for network sockets, but can be used for any type of file
37      descriptor. You may only register one handler for each file
38      descriptor/io combination or you will get unpredictable results
39      (this means that you can have a handler for read events, and a
40      separate handler for write events, but not two handlers that are
41      both handling read events)
42
43   2) a timed event. You can register an event that happens at a
44      specific time.  You can register as many of these as you
45      like. They are single shot - add a new timed event in the event
46      handler to get another event.
47
48   To setup a set of events you first need to create a event_context
49   structure using the function tevent_context_init(); This returns a
50   'struct tevent_context' that you use in all subsequent calls.
51
52   After that you can add/remove events that you are interested in
53   using tevent_add_*() and talloc_free()
54
55   Finally, you call tevent_loop_wait_once() to block waiting for one of the
56   events to occor or tevent_loop_wait() which will loop
57   forever.
58
59 */
60 #include "replace.h"
61 #include "system/filesys.h"
62 #define TEVENT_DEPRECATED 1
63 #include "tevent.h"
64 #include "tevent_internal.h"
65 #include "tevent_util.h"
66
67 struct tevent_ops_list {
68         struct tevent_ops_list *next, *prev;
69         const char *name;
70         const struct tevent_ops *ops;
71 };
72
73 /* list of registered event backends */
74 static struct tevent_ops_list *tevent_backends = NULL;
75 static char *tevent_default_backend = NULL;
76
77 /*
78   register an events backend
79 */
80 bool tevent_register_backend(const char *name, const struct tevent_ops *ops)
81 {
82         struct tevent_ops_list *e;
83
84         for (e = tevent_backends; e != NULL; e = e->next) {
85                 if (0 == strcmp(e->name, name)) {
86                         /* already registered, skip it */
87                         return true;
88                 }
89         }
90
91         e = talloc(talloc_autofree_context(), struct tevent_ops_list);
92         if (e == NULL) return false;
93
94         e->name = name;
95         e->ops = ops;
96         DLIST_ADD(tevent_backends, e);
97
98         return true;
99 }
100
101 /*
102   set the default event backend
103  */
104 void tevent_set_default_backend(const char *backend)
105 {
106         talloc_free(tevent_default_backend);
107         tevent_default_backend = talloc_strdup(talloc_autofree_context(),
108                                                backend);
109 }
110
111 /*
112   initialise backends if not already done
113 */
114 static void tevent_backend_init(void)
115 {
116         tevent_select_init();
117         tevent_standard_init();
118 #ifdef HAVE_EPOLL
119         tevent_epoll_init();
120 #endif
121 }
122
123 /*
124   list available backends
125 */
126 const char **tevent_backend_list(TALLOC_CTX *mem_ctx)
127 {
128         const char **list = NULL;
129         struct tevent_ops_list *e;
130
131         tevent_backend_init();
132
133         for (e=tevent_backends;e;e=e->next) {
134                 list = ev_str_list_add(list, e->name);
135         }
136
137         talloc_steal(mem_ctx, list);
138
139         return list;
140 }
141
142 int tevent_common_context_destructor(struct tevent_context *ev)
143 {
144         struct tevent_fd *fd, *fn;
145         struct tevent_timer *te, *tn;
146         struct tevent_signal *se, *sn;
147
148         if (ev->pipe_fde) {
149                 talloc_free(ev->pipe_fde);
150                 ev->pipe_fde = NULL;
151         }
152
153         for (fd = ev->fd_events; fd; fd = fn) {
154                 fn = fd->next;
155                 fd->event_ctx = NULL;
156                 DLIST_REMOVE(ev->fd_events, fd);
157         }
158
159         for (te = ev->timer_events; te; te = tn) {
160                 tn = te->next;
161                 te->event_ctx = NULL;
162                 DLIST_REMOVE(ev->timer_events, te);
163         }
164
165         for (se = ev->signal_events; se; se = sn) {
166                 sn = se->next;
167                 se->event_ctx = NULL;
168                 DLIST_REMOVE(ev->signal_events, se);
169         }
170
171         return 0;
172 }
173
174 /*
175   create a event_context structure for a specific implemementation.
176   This must be the first events call, and all subsequent calls pass
177   this event_context as the first element. Event handlers also
178   receive this as their first argument.
179
180   This function is for allowing third-party-applications to hook in gluecode
181   to their own event loop code, so that they can make async usage of our client libs
182
183   NOTE: use tevent_context_init() inside of samba!
184 */
185 static struct tevent_context *tevent_context_init_ops(TALLOC_CTX *mem_ctx,
186                                                       const struct tevent_ops *ops)
187 {
188         struct tevent_context *ev;
189         int ret;
190
191         ev = talloc_zero(mem_ctx, struct tevent_context);
192         if (!ev) return NULL;
193
194         talloc_set_destructor(ev, tevent_common_context_destructor);
195
196         ev->ops = ops;
197
198         ret = ev->ops->context_init(ev);
199         if (ret != 0) {
200                 talloc_free(ev);
201                 return NULL;
202         }
203
204         return ev;
205 }
206
207 /*
208   create a event_context structure. This must be the first events
209   call, and all subsequent calls pass this event_context as the first
210   element. Event handlers also receive this as their first argument.
211 */
212 struct tevent_context *tevent_context_init_byname(TALLOC_CTX *mem_ctx,
213                                                   const char *name)
214 {
215         struct tevent_ops_list *e;
216
217         tevent_backend_init();
218
219         if (name == NULL) {
220                 name = tevent_default_backend;
221         }
222         if (name == NULL) {
223                 name = "standard";
224         }
225
226         for (e=tevent_backends;e;e=e->next) {
227                 if (strcmp(name, e->name) == 0) {
228                         return tevent_context_init_ops(mem_ctx, e->ops);
229                 }
230         }
231         return NULL;
232 }
233
234
235 /*
236   create a event_context structure. This must be the first events
237   call, and all subsequent calls pass this event_context as the first
238   element. Event handlers also receive this as their first argument.
239 */
240 struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx)
241 {
242         return tevent_context_init_byname(mem_ctx, NULL);
243 }
244
245 /*
246   add a fd based event
247   return NULL on failure (memory allocation error)
248
249   if flags contains TEVENT_FD_AUTOCLOSE then the fd will be closed when
250   the returned fd_event context is freed
251 */
252 struct tevent_fd *_tevent_add_fd(struct tevent_context *ev,
253                                  TALLOC_CTX *mem_ctx,
254                                  int fd,
255                                  uint16_t flags,
256                                  tevent_fd_handler_t handler,
257                                  void *private_data,
258                                  const char *handler_name,
259                                  const char *location)
260 {
261         return ev->ops->add_fd(ev, mem_ctx, fd, flags, handler, private_data,
262                                handler_name, location);
263 }
264
265 /*
266   set a close function on the fd event
267 */
268 void tevent_fd_set_close_fn(struct tevent_fd *fde,
269                             tevent_fd_close_fn_t close_fn)
270 {
271         if (!fde) return;
272         if (!fde->event_ctx) return;
273         fde->event_ctx->ops->set_fd_close_fn(fde, close_fn);
274 }
275
276 static void tevent_fd_auto_close_fn(struct tevent_context *ev,
277                                     struct tevent_fd *fde,
278                                     int fd,
279                                     void *private_data)
280 {
281         close(fd);
282 }
283
284 void tevent_fd_set_auto_close(struct tevent_fd *fde)
285 {
286         tevent_fd_set_close_fn(fde, tevent_fd_auto_close_fn);
287 }
288
289 /*
290   return the fd event flags
291 */
292 uint16_t tevent_fd_get_flags(struct tevent_fd *fde)
293 {
294         if (!fde) return 0;
295         if (!fde->event_ctx) return 0;
296         return fde->event_ctx->ops->get_fd_flags(fde);
297 }
298
299 /*
300   set the fd event flags
301 */
302 void tevent_fd_set_flags(struct tevent_fd *fde, uint16_t flags)
303 {
304         if (!fde) return;
305         if (!fde->event_ctx) return;
306         fde->event_ctx->ops->set_fd_flags(fde, flags);
307 }
308
309 bool tevent_signal_support(struct tevent_context *ev)
310 {
311         if (ev->ops->add_signal) {
312                 return true;
313         }
314         return false;
315 }
316
317 static void (*tevent_abort_fn)(const char *reason);
318
319 void tevent_set_abort_fn(void (*abort_fn)(const char *reason))
320 {
321         tevent_abort_fn = abort_fn;
322 }
323
324 static void tevent_abort(struct tevent_context *ev, const char *reason)
325 {
326         tevent_debug(ev, TEVENT_DEBUG_FATAL,
327                      "abort: %s\n", reason);
328
329         if (!tevent_abort_fn) {
330                 abort();
331         }
332
333         tevent_abort_fn(reason);
334 }
335
336 /*
337   add a timer event
338   return NULL on failure
339 */
340 struct tevent_timer *_tevent_add_timer(struct tevent_context *ev,
341                                        TALLOC_CTX *mem_ctx,
342                                        struct timeval next_event,
343                                        tevent_timer_handler_t handler,
344                                        void *private_data,
345                                        const char *handler_name,
346                                        const char *location)
347 {
348         return ev->ops->add_timer(ev, mem_ctx, next_event, handler, private_data,
349                                   handler_name, location);
350 }
351
352 /*
353   add a signal event
354
355   sa_flags are flags to sigaction(2)
356
357   return NULL on failure
358 */
359 struct tevent_signal *_tevent_add_signal(struct tevent_context *ev,
360                                          TALLOC_CTX *mem_ctx,
361                                          int signum,
362                                          int sa_flags,
363                                          tevent_signal_handler_t handler,
364                                          void *private_data,
365                                          const char *handler_name,
366                                          const char *location)
367 {
368         return ev->ops->add_signal(ev, mem_ctx, signum, sa_flags, handler, private_data,
369                                    handler_name, location);
370 }
371
372 void tevent_loop_allow_nesting(struct tevent_context *ev)
373 {
374         ev->nesting.allowed = true;
375 }
376
377 void tevent_loop_set_nesting_hook(struct tevent_context *ev,
378                                   tevent_nesting_hook hook,
379                                   void *private_data)
380 {
381         ev->nesting.hook_fn = hook;
382         ev->nesting.hook_private = private_data;
383 }
384
385 static void tevent_abort_nesting(struct tevent_context *ev, const char *location)
386 {
387         const char *reason;
388
389         reason = talloc_asprintf(NULL, "tevent_loop_once() nesting at %s",
390                                  location);
391         if (!reason) {
392                 reason = "tevent_loop_once() nesting";
393         }
394
395         tevent_abort(ev, reason);
396 }
397
398 /*
399   do a single event loop using the events defined in ev 
400 */
401 int _tevent_loop_once(struct tevent_context *ev, const char *location)
402 {
403         int ret;
404         void *nesting_stack_ptr = NULL;
405
406         ev->nesting.level++;
407
408         if (ev->nesting.level > 1) {
409                 if (!ev->nesting.allowed) {
410                         tevent_abort_nesting(ev, location);
411                         errno = ELOOP;
412                         return -1;
413                 }
414                 if (ev->nesting.hook_fn) {
415                         int ret2;
416                         ret2 = ev->nesting.hook_fn(ev,
417                                                    ev->nesting.hook_private,
418                                                    ev->nesting.level,
419                                                    true,
420                                                    (void *)&nesting_stack_ptr,
421                                                    location);
422                         if (ret2 != 0) {
423                                 ret = ret2;
424                                 goto done;
425                         }
426                 }
427         }
428
429         ret = ev->ops->loop_once(ev, location);
430
431         if (ev->nesting.level > 1) {
432                 if (ev->nesting.hook_fn) {
433                         int ret2;
434                         ret2 = ev->nesting.hook_fn(ev,
435                                                    ev->nesting.hook_private,
436                                                    ev->nesting.level,
437                                                    false,
438                                                    (void *)&nesting_stack_ptr,
439                                                    location);
440                         if (ret2 != 0) {
441                                 ret = ret2;
442                                 goto done;
443                         }
444                 }
445         }
446
447 done:
448         ev->nesting.level--;
449         return ret;
450 }
451
452 /*
453   this is a performance optimization for the samba4 nested event loop problems
454 */
455 int _tevent_loop_until(struct tevent_context *ev,
456                        bool (*finished)(void *private_data),
457                        void *private_data,
458                        const char *location)
459 {
460         int ret;
461         void *nesting_stack_ptr = NULL;
462
463         ev->nesting.level++;
464
465         if (ev->nesting.level > 1) {
466                 if (!ev->nesting.allowed) {
467                         tevent_abort_nesting(ev, location);
468                         errno = ELOOP;
469                         return -1;
470                 }
471                 if (ev->nesting.hook_fn) {
472                         int ret2;
473                         ret2 = ev->nesting.hook_fn(ev,
474                                                    ev->nesting.hook_private,
475                                                    ev->nesting.level,
476                                                    true,
477                                                    (void *)&nesting_stack_ptr,
478                                                    location);
479                         if (ret2 != 0) {
480                                 ret = ret2;
481                                 goto done;
482                         }
483                 }
484         }
485
486         while (!finished(private_data)) {
487                 ret = ev->ops->loop_once(ev, location);
488                 if (ret != 0) {
489                         break;
490                 }
491         }
492
493         if (ev->nesting.level > 1) {
494                 if (ev->nesting.hook_fn) {
495                         int ret2;
496                         ret2 = ev->nesting.hook_fn(ev,
497                                                    ev->nesting.hook_private,
498                                                    ev->nesting.level,
499                                                    false,
500                                                    (void *)&nesting_stack_ptr,
501                                                    location);
502                         if (ret2 != 0) {
503                                 ret = ret2;
504                                 goto done;
505                         }
506                 }
507         }
508
509 done:
510         ev->nesting.level--;
511         return ret;
512 }
513
514 /*
515   return on failure or (with 0) if all fd events are removed
516 */
517 int _tevent_loop_wait(struct tevent_context *ev, const char *location)
518 {
519         return ev->ops->loop_wait(ev, location);
520 }