fixed a logic bug in the tevent nesting code
[Samba.git] / lib / tevent / tevent.c
blob56fd6aec7aa54d2957f8bdcaf7e9e80a80655023
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
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
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.
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.
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/>.
26 PLEASE READ THIS BEFORE MODIFYING!
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.
33 There are 2 types of event handling that are handled in this module:
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)
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.
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.
52 After that you can add/remove events that you are interested in
53 using tevent_add_*() and talloc_free()
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.
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"
67 struct tevent_ops_list {
68 struct tevent_ops_list *next, *prev;
69 const char *name;
70 const struct tevent_ops *ops;
73 /* list of registered event backends */
74 static struct tevent_ops_list *tevent_backends = NULL;
75 static char *tevent_default_backend = NULL;
78 register an events backend
80 bool tevent_register_backend(const char *name, const struct tevent_ops *ops)
82 struct tevent_ops_list *e;
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;
91 e = talloc(talloc_autofree_context(), struct tevent_ops_list);
92 if (e == NULL) return false;
94 e->name = name;
95 e->ops = ops;
96 DLIST_ADD(tevent_backends, e);
98 return true;
102 set the default event backend
104 void tevent_set_default_backend(const char *backend)
106 talloc_free(tevent_default_backend);
107 tevent_default_backend = talloc_strdup(talloc_autofree_context(),
108 backend);
112 initialise backends if not already done
114 static void tevent_backend_init(void)
116 tevent_select_init();
117 tevent_standard_init();
118 #ifdef HAVE_EPOLL
119 tevent_epoll_init();
120 #endif
124 list available backends
126 const char **tevent_backend_list(TALLOC_CTX *mem_ctx)
128 const char **list = NULL;
129 struct tevent_ops_list *e;
131 tevent_backend_init();
133 for (e=tevent_backends;e;e=e->next) {
134 list = ev_str_list_add(list, e->name);
137 talloc_steal(mem_ctx, list);
139 return list;
142 int tevent_common_context_destructor(struct tevent_context *ev)
144 struct tevent_fd *fd, *fn;
145 struct tevent_timer *te, *tn;
146 struct tevent_immediate *ie, *in;
147 struct tevent_signal *se, *sn;
149 if (ev->pipe_fde) {
150 talloc_free(ev->pipe_fde);
151 ev->pipe_fde = NULL;
154 for (fd = ev->fd_events; fd; fd = fn) {
155 fn = fd->next;
156 fd->event_ctx = NULL;
157 DLIST_REMOVE(ev->fd_events, fd);
160 for (te = ev->timer_events; te; te = tn) {
161 tn = te->next;
162 te->event_ctx = NULL;
163 DLIST_REMOVE(ev->timer_events, te);
166 for (ie = ev->immediate_events; ie; ie = in) {
167 in = ie->next;
168 ie->event_ctx = NULL;
169 ie->cancel_fn = NULL;
170 DLIST_REMOVE(ev->immediate_events, ie);
173 for (se = ev->signal_events; se; se = sn) {
174 sn = se->next;
175 se->event_ctx = NULL;
176 DLIST_REMOVE(ev->signal_events, se);
179 return 0;
183 create a event_context structure for a specific implemementation.
184 This must be the first events call, and all subsequent calls pass
185 this event_context as the first element. Event handlers also
186 receive this as their first argument.
188 This function is for allowing third-party-applications to hook in gluecode
189 to their own event loop code, so that they can make async usage of our client libs
191 NOTE: use tevent_context_init() inside of samba!
193 static struct tevent_context *tevent_context_init_ops(TALLOC_CTX *mem_ctx,
194 const struct tevent_ops *ops)
196 struct tevent_context *ev;
197 int ret;
199 ev = talloc_zero(mem_ctx, struct tevent_context);
200 if (!ev) return NULL;
202 talloc_set_destructor(ev, tevent_common_context_destructor);
204 ev->ops = ops;
206 ret = ev->ops->context_init(ev);
207 if (ret != 0) {
208 talloc_free(ev);
209 return NULL;
212 return ev;
216 create a event_context structure. This must be the first events
217 call, and all subsequent calls pass this event_context as the first
218 element. Event handlers also receive this as their first argument.
220 struct tevent_context *tevent_context_init_byname(TALLOC_CTX *mem_ctx,
221 const char *name)
223 struct tevent_ops_list *e;
225 tevent_backend_init();
227 if (name == NULL) {
228 name = tevent_default_backend;
230 if (name == NULL) {
231 name = "standard";
234 for (e=tevent_backends;e;e=e->next) {
235 if (strcmp(name, e->name) == 0) {
236 return tevent_context_init_ops(mem_ctx, e->ops);
239 return NULL;
244 create a event_context structure. This must be the first events
245 call, and all subsequent calls pass this event_context as the first
246 element. Event handlers also receive this as their first argument.
248 struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx)
250 return tevent_context_init_byname(mem_ctx, NULL);
254 add a fd based event
255 return NULL on failure (memory allocation error)
257 if flags contains TEVENT_FD_AUTOCLOSE then the fd will be closed when
258 the returned fd_event context is freed
260 struct tevent_fd *_tevent_add_fd(struct tevent_context *ev,
261 TALLOC_CTX *mem_ctx,
262 int fd,
263 uint16_t flags,
264 tevent_fd_handler_t handler,
265 void *private_data,
266 const char *handler_name,
267 const char *location)
269 return ev->ops->add_fd(ev, mem_ctx, fd, flags, handler, private_data,
270 handler_name, location);
274 set a close function on the fd event
276 void tevent_fd_set_close_fn(struct tevent_fd *fde,
277 tevent_fd_close_fn_t close_fn)
279 if (!fde) return;
280 if (!fde->event_ctx) return;
281 fde->event_ctx->ops->set_fd_close_fn(fde, close_fn);
284 static void tevent_fd_auto_close_fn(struct tevent_context *ev,
285 struct tevent_fd *fde,
286 int fd,
287 void *private_data)
289 close(fd);
292 void tevent_fd_set_auto_close(struct tevent_fd *fde)
294 tevent_fd_set_close_fn(fde, tevent_fd_auto_close_fn);
298 return the fd event flags
300 uint16_t tevent_fd_get_flags(struct tevent_fd *fde)
302 if (!fde) return 0;
303 if (!fde->event_ctx) return 0;
304 return fde->event_ctx->ops->get_fd_flags(fde);
308 set the fd event flags
310 void tevent_fd_set_flags(struct tevent_fd *fde, uint16_t flags)
312 if (!fde) return;
313 if (!fde->event_ctx) return;
314 fde->event_ctx->ops->set_fd_flags(fde, flags);
317 bool tevent_signal_support(struct tevent_context *ev)
319 if (ev->ops->add_signal) {
320 return true;
322 return false;
325 static void (*tevent_abort_fn)(const char *reason);
327 void tevent_set_abort_fn(void (*abort_fn)(const char *reason))
329 tevent_abort_fn = abort_fn;
332 static void tevent_abort(struct tevent_context *ev, const char *reason)
334 tevent_debug(ev, TEVENT_DEBUG_FATAL,
335 "abort: %s\n", reason);
337 if (!tevent_abort_fn) {
338 abort();
341 tevent_abort_fn(reason);
345 add a timer event
346 return NULL on failure
348 struct tevent_timer *_tevent_add_timer(struct tevent_context *ev,
349 TALLOC_CTX *mem_ctx,
350 struct timeval next_event,
351 tevent_timer_handler_t handler,
352 void *private_data,
353 const char *handler_name,
354 const char *location)
356 return ev->ops->add_timer(ev, mem_ctx, next_event, handler, private_data,
357 handler_name, location);
361 allocate an immediate event
362 return NULL on failure (memory allocation error)
364 struct tevent_immediate *_tevent_create_immediate(TALLOC_CTX *mem_ctx,
365 const char *location)
367 struct tevent_immediate *im;
369 im = talloc(mem_ctx, struct tevent_immediate);
370 if (im == NULL) return NULL;
372 im->prev = NULL;
373 im->next = NULL;
374 im->event_ctx = NULL;
375 im->create_location = location;
376 im->handler = NULL;
377 im->private_data = NULL;
378 im->handler_name = NULL;
379 im->schedule_location = NULL;
380 im->cancel_fn = NULL;
381 im->additional_data = NULL;
383 return im;
387 schedule an immediate event
388 return NULL on failure
390 void _tevent_schedule_immediate(struct tevent_immediate *im,
391 struct tevent_context *ev,
392 tevent_immediate_handler_t handler,
393 void *private_data,
394 const char *handler_name,
395 const char *location)
397 ev->ops->schedule_immediate(im, ev, handler, private_data,
398 handler_name, location);
402 add a signal event
404 sa_flags are flags to sigaction(2)
406 return NULL on failure
408 struct tevent_signal *_tevent_add_signal(struct tevent_context *ev,
409 TALLOC_CTX *mem_ctx,
410 int signum,
411 int sa_flags,
412 tevent_signal_handler_t handler,
413 void *private_data,
414 const char *handler_name,
415 const char *location)
417 return ev->ops->add_signal(ev, mem_ctx, signum, sa_flags, handler, private_data,
418 handler_name, location);
421 void tevent_loop_allow_nesting(struct tevent_context *ev)
423 ev->nesting.allowed = true;
426 void tevent_loop_set_nesting_hook(struct tevent_context *ev,
427 tevent_nesting_hook hook,
428 void *private_data)
430 if (ev->nesting.hook_fn &&
431 (ev->nesting.hook_fn != hook ||
432 ev->nesting.hook_private != private_data)) {
433 /* the way the nesting hook code is currently written
434 we cannot support two different nesting hooks at the
435 same time. */
436 tevent_abort(ev, "tevent: Violation of nesting hook rules\n");
438 ev->nesting.hook_fn = hook;
439 ev->nesting.hook_private = private_data;
442 static void tevent_abort_nesting(struct tevent_context *ev, const char *location)
444 const char *reason;
446 reason = talloc_asprintf(NULL, "tevent_loop_once() nesting at %s",
447 location);
448 if (!reason) {
449 reason = "tevent_loop_once() nesting";
452 tevent_abort(ev, reason);
456 do a single event loop using the events defined in ev
458 int _tevent_loop_once(struct tevent_context *ev, const char *location)
460 int ret;
461 void *nesting_stack_ptr = NULL;
463 ev->nesting.level++;
465 if (ev->nesting.level > 1) {
466 if (!ev->nesting.allowed) {
467 tevent_abort_nesting(ev, location);
468 errno = ELOOP;
469 return -1;
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;
486 ret = ev->ops->loop_once(ev, location);
488 if (ev->nesting.level > 1) {
489 if (ev->nesting.hook_fn) {
490 int ret2;
491 ret2 = ev->nesting.hook_fn(ev,
492 ev->nesting.hook_private,
493 ev->nesting.level,
494 false,
495 (void *)&nesting_stack_ptr,
496 location);
497 if (ret2 != 0) {
498 ret = ret2;
499 goto done;
504 done:
505 ev->nesting.level--;
506 return ret;
510 this is a performance optimization for the samba4 nested event loop problems
512 int _tevent_loop_until(struct tevent_context *ev,
513 bool (*finished)(void *private_data),
514 void *private_data,
515 const char *location)
517 int ret = 0;
518 void *nesting_stack_ptr = NULL;
520 ev->nesting.level++;
522 if (ev->nesting.level > 1) {
523 if (!ev->nesting.allowed) {
524 tevent_abort_nesting(ev, location);
525 errno = ELOOP;
526 return -1;
528 if (ev->nesting.hook_fn) {
529 int ret2;
530 ret2 = ev->nesting.hook_fn(ev,
531 ev->nesting.hook_private,
532 ev->nesting.level,
533 true,
534 (void *)&nesting_stack_ptr,
535 location);
536 if (ret2 != 0) {
537 ret = ret2;
538 goto done;
543 while (!finished(private_data)) {
544 ret = ev->ops->loop_once(ev, location);
545 if (ret != 0) {
546 break;
550 if (ev->nesting.level > 1) {
551 if (ev->nesting.hook_fn) {
552 int ret2;
553 ret2 = ev->nesting.hook_fn(ev,
554 ev->nesting.hook_private,
555 ev->nesting.level,
556 false,
557 (void *)&nesting_stack_ptr,
558 location);
559 if (ret2 != 0) {
560 ret = ret2;
561 goto done;
566 done:
567 ev->nesting.level--;
568 return ret;
572 return on failure or (with 0) if all fd events are removed
574 int tevent_common_loop_wait(struct tevent_context *ev,
575 const char *location)
578 * loop as long as we have events pending
580 while (ev->fd_events ||
581 ev->timer_events ||
582 ev->immediate_events ||
583 ev->signal_events) {
584 int ret;
585 ret = _tevent_loop_once(ev, location);
586 if (ret != 0) {
587 tevent_debug(ev, TEVENT_DEBUG_FATAL,
588 "_tevent_loop_once() failed: %d - %s\n",
589 ret, strerror(errno));
590 return ret;
594 tevent_debug(ev, TEVENT_DEBUG_WARNING,
595 "tevent_common_loop_wait() out of events\n");
596 return 0;
600 return on failure or (with 0) if all fd events are removed
602 int _tevent_loop_wait(struct tevent_context *ev, const char *location)
604 int ret;
605 ev->nesting.level++;
606 ret = ev->ops->loop_wait(ev, location);
607 ev->nesting.level--;
608 return ret;