source: branches/samba-3.5.x/lib/tevent/tevent.c@ 1075

Last change on this file since 1075 was 429, checked in by Silvan Scherrer, 15 years ago

Samba 3.5.x: trunk update to 3.5.2

File size: 15.0 KB
Line 
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
67struct 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 */
74static struct tevent_ops_list *tevent_backends = NULL;
75static char *tevent_default_backend = NULL;
76
77/*
78 register an events backend
79*/
80bool 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 */
104void 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*/
114static 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*/
126const 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
142int tevent_common_context_destructor(struct tevent_context *ev)
143{
144 struct tevent_fd *fd, *fn;
145 struct tevent_timer *te, *tn;
146 struct tevent_immediate *ie, *in;
147 struct tevent_signal *se, *sn;
148
149 if (ev->pipe_fde) {
150 talloc_free(ev->pipe_fde);
151 close(ev->pipe_fds[0]);
152 close(ev->pipe_fds[1]);
153 ev->pipe_fde = NULL;
154 }
155
156 for (fd = ev->fd_events; fd; fd = fn) {
157 fn = fd->next;
158 fd->event_ctx = NULL;
159 DLIST_REMOVE(ev->fd_events, fd);
160 }
161
162 for (te = ev->timer_events; te; te = tn) {
163 tn = te->next;
164 te->event_ctx = NULL;
165 DLIST_REMOVE(ev->timer_events, te);
166 }
167
168 for (ie = ev->immediate_events; ie; ie = in) {
169 in = ie->next;
170 ie->event_ctx = NULL;
171 ie->cancel_fn = NULL;
172 DLIST_REMOVE(ev->immediate_events, ie);
173 }
174
175 for (se = ev->signal_events; se; se = sn) {
176 sn = se->next;
177 se->event_ctx = NULL;
178 DLIST_REMOVE(ev->signal_events, se);
179 /*
180 * This is important, Otherwise signals
181 * are handled twice in child. eg, SIGHUP.
182 * one added in parent, and another one in
183 * the child. -- BoYang
184 */
185 tevent_cleanup_pending_signal_handlers(se);
186 }
187
188 return 0;
189}
190
191/*
192 create a event_context structure for a specific implemementation.
193 This must be the first events call, and all subsequent calls pass
194 this event_context as the first element. Event handlers also
195 receive this as their first argument.
196
197 This function is for allowing third-party-applications to hook in gluecode
198 to their own event loop code, so that they can make async usage of our client libs
199
200 NOTE: use tevent_context_init() inside of samba!
201*/
202static struct tevent_context *tevent_context_init_ops(TALLOC_CTX *mem_ctx,
203 const struct tevent_ops *ops)
204{
205 struct tevent_context *ev;
206 int ret;
207
208 ev = talloc_zero(mem_ctx, struct tevent_context);
209 if (!ev) return NULL;
210
211 talloc_set_destructor(ev, tevent_common_context_destructor);
212
213 ev->ops = ops;
214
215 ret = ev->ops->context_init(ev);
216 if (ret != 0) {
217 talloc_free(ev);
218 return NULL;
219 }
220
221 return ev;
222}
223
224/*
225 create a event_context structure. This must be the first events
226 call, and all subsequent calls pass this event_context as the first
227 element. Event handlers also receive this as their first argument.
228*/
229struct tevent_context *tevent_context_init_byname(TALLOC_CTX *mem_ctx,
230 const char *name)
231{
232 struct tevent_ops_list *e;
233
234 tevent_backend_init();
235
236 if (name == NULL) {
237 name = tevent_default_backend;
238 }
239 if (name == NULL) {
240 name = "standard";
241 }
242
243 for (e=tevent_backends;e;e=e->next) {
244 if (strcmp(name, e->name) == 0) {
245 return tevent_context_init_ops(mem_ctx, e->ops);
246 }
247 }
248 return NULL;
249}
250
251
252/*
253 create a event_context structure. This must be the first events
254 call, and all subsequent calls pass this event_context as the first
255 element. Event handlers also receive this as their first argument.
256*/
257struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx)
258{
259 return tevent_context_init_byname(mem_ctx, NULL);
260}
261
262/*
263 add a fd based event
264 return NULL on failure (memory allocation error)
265
266 if flags contains TEVENT_FD_AUTOCLOSE then the fd will be closed when
267 the returned fd_event context is freed
268*/
269struct tevent_fd *_tevent_add_fd(struct tevent_context *ev,
270 TALLOC_CTX *mem_ctx,
271 int fd,
272 uint16_t flags,
273 tevent_fd_handler_t handler,
274 void *private_data,
275 const char *handler_name,
276 const char *location)
277{
278 return ev->ops->add_fd(ev, mem_ctx, fd, flags, handler, private_data,
279 handler_name, location);
280}
281
282/*
283 set a close function on the fd event
284*/
285void tevent_fd_set_close_fn(struct tevent_fd *fde,
286 tevent_fd_close_fn_t close_fn)
287{
288 if (!fde) return;
289 if (!fde->event_ctx) return;
290 fde->event_ctx->ops->set_fd_close_fn(fde, close_fn);
291}
292
293static void tevent_fd_auto_close_fn(struct tevent_context *ev,
294 struct tevent_fd *fde,
295 int fd,
296 void *private_data)
297{
298 close(fd);
299}
300
301void tevent_fd_set_auto_close(struct tevent_fd *fde)
302{
303 tevent_fd_set_close_fn(fde, tevent_fd_auto_close_fn);
304}
305
306/*
307 return the fd event flags
308*/
309uint16_t tevent_fd_get_flags(struct tevent_fd *fde)
310{
311 if (!fde) return 0;
312 if (!fde->event_ctx) return 0;
313 return fde->event_ctx->ops->get_fd_flags(fde);
314}
315
316/*
317 set the fd event flags
318*/
319void tevent_fd_set_flags(struct tevent_fd *fde, uint16_t flags)
320{
321 if (!fde) return;
322 if (!fde->event_ctx) return;
323 fde->event_ctx->ops->set_fd_flags(fde, flags);
324}
325
326bool tevent_signal_support(struct tevent_context *ev)
327{
328 if (ev->ops->add_signal) {
329 return true;
330 }
331 return false;
332}
333
334static void (*tevent_abort_fn)(const char *reason);
335
336void tevent_set_abort_fn(void (*abort_fn)(const char *reason))
337{
338 tevent_abort_fn = abort_fn;
339}
340
341static void tevent_abort(struct tevent_context *ev, const char *reason)
342{
343 tevent_debug(ev, TEVENT_DEBUG_FATAL,
344 "abort: %s\n", reason);
345
346 if (!tevent_abort_fn) {
347 abort();
348 }
349
350 tevent_abort_fn(reason);
351}
352
353/*
354 add a timer event
355 return NULL on failure
356*/
357struct tevent_timer *_tevent_add_timer(struct tevent_context *ev,
358 TALLOC_CTX *mem_ctx,
359 struct timeval next_event,
360 tevent_timer_handler_t handler,
361 void *private_data,
362 const char *handler_name,
363 const char *location)
364{
365 return ev->ops->add_timer(ev, mem_ctx, next_event, handler, private_data,
366 handler_name, location);
367}
368
369/*
370 allocate an immediate event
371 return NULL on failure (memory allocation error)
372*/
373struct tevent_immediate *_tevent_create_immediate(TALLOC_CTX *mem_ctx,
374 const char *location)
375{
376 struct tevent_immediate *im;
377
378 im = talloc(mem_ctx, struct tevent_immediate);
379 if (im == NULL) return NULL;
380
381 im->prev = NULL;
382 im->next = NULL;
383 im->event_ctx = NULL;
384 im->create_location = location;
385 im->handler = NULL;
386 im->private_data = NULL;
387 im->handler_name = NULL;
388 im->schedule_location = NULL;
389 im->cancel_fn = NULL;
390 im->additional_data = NULL;
391
392 return im;
393}
394
395/*
396 schedule an immediate event
397 return NULL on failure
398*/
399void _tevent_schedule_immediate(struct tevent_immediate *im,
400 struct tevent_context *ev,
401 tevent_immediate_handler_t handler,
402 void *private_data,
403 const char *handler_name,
404 const char *location)
405{
406 ev->ops->schedule_immediate(im, ev, handler, private_data,
407 handler_name, location);
408}
409
410/*
411 add a signal event
412
413 sa_flags are flags to sigaction(2)
414
415 return NULL on failure
416*/
417struct tevent_signal *_tevent_add_signal(struct tevent_context *ev,
418 TALLOC_CTX *mem_ctx,
419 int signum,
420 int sa_flags,
421 tevent_signal_handler_t handler,
422 void *private_data,
423 const char *handler_name,
424 const char *location)
425{
426 return ev->ops->add_signal(ev, mem_ctx, signum, sa_flags, handler, private_data,
427 handler_name, location);
428}
429
430void tevent_loop_allow_nesting(struct tevent_context *ev)
431{
432 ev->nesting.allowed = true;
433}
434
435void tevent_loop_set_nesting_hook(struct tevent_context *ev,
436 tevent_nesting_hook hook,
437 void *private_data)
438{
439 if (ev->nesting.hook_fn &&
440 (ev->nesting.hook_fn != hook ||
441 ev->nesting.hook_private != private_data)) {
442 /* the way the nesting hook code is currently written
443 we cannot support two different nesting hooks at the
444 same time. */
445 tevent_abort(ev, "tevent: Violation of nesting hook rules\n");
446 }
447 ev->nesting.hook_fn = hook;
448 ev->nesting.hook_private = private_data;
449}
450
451static void tevent_abort_nesting(struct tevent_context *ev, const char *location)
452{
453 const char *reason;
454
455 reason = talloc_asprintf(NULL, "tevent_loop_once() nesting at %s",
456 location);
457 if (!reason) {
458 reason = "tevent_loop_once() nesting";
459 }
460
461 tevent_abort(ev, reason);
462}
463
464/*
465 do a single event loop using the events defined in ev
466*/
467int _tevent_loop_once(struct tevent_context *ev, const char *location)
468{
469 int ret;
470 void *nesting_stack_ptr = NULL;
471
472 ev->nesting.level++;
473
474 if (ev->nesting.level > 1) {
475 if (!ev->nesting.allowed) {
476 tevent_abort_nesting(ev, location);
477 errno = ELOOP;
478 return -1;
479 }
480 }
481 if (ev->nesting.level > 0) {
482 if (ev->nesting.hook_fn) {
483 int ret2;
484 ret2 = ev->nesting.hook_fn(ev,
485 ev->nesting.hook_private,
486 ev->nesting.level,
487 true,
488 (void *)&nesting_stack_ptr,
489 location);
490 if (ret2 != 0) {
491 ret = ret2;
492 goto done;
493 }
494 }
495 }
496
497 ret = ev->ops->loop_once(ev, location);
498
499 if (ev->nesting.level > 0) {
500 if (ev->nesting.hook_fn) {
501 int ret2;
502 ret2 = ev->nesting.hook_fn(ev,
503 ev->nesting.hook_private,
504 ev->nesting.level,
505 false,
506 (void *)&nesting_stack_ptr,
507 location);
508 if (ret2 != 0) {
509 ret = ret2;
510 goto done;
511 }
512 }
513 }
514
515done:
516 ev->nesting.level--;
517 return ret;
518}
519
520/*
521 this is a performance optimization for the samba4 nested event loop problems
522*/
523int _tevent_loop_until(struct tevent_context *ev,
524 bool (*finished)(void *private_data),
525 void *private_data,
526 const char *location)
527{
528 int ret = 0;
529 void *nesting_stack_ptr = NULL;
530
531 ev->nesting.level++;
532
533 if (ev->nesting.level > 1) {
534 if (!ev->nesting.allowed) {
535 tevent_abort_nesting(ev, location);
536 errno = ELOOP;
537 return -1;
538 }
539 }
540 if (ev->nesting.level > 0) {
541 if (ev->nesting.hook_fn) {
542 int ret2;
543 ret2 = ev->nesting.hook_fn(ev,
544 ev->nesting.hook_private,
545 ev->nesting.level,
546 true,
547 (void *)&nesting_stack_ptr,
548 location);
549 if (ret2 != 0) {
550 ret = ret2;
551 goto done;
552 }
553 }
554 }
555
556 while (!finished(private_data)) {
557 ret = ev->ops->loop_once(ev, location);
558 if (ret != 0) {
559 break;
560 }
561 }
562
563 if (ev->nesting.level > 0) {
564 if (ev->nesting.hook_fn) {
565 int ret2;
566 ret2 = ev->nesting.hook_fn(ev,
567 ev->nesting.hook_private,
568 ev->nesting.level,
569 false,
570 (void *)&nesting_stack_ptr,
571 location);
572 if (ret2 != 0) {
573 ret = ret2;
574 goto done;
575 }
576 }
577 }
578
579done:
580 ev->nesting.level--;
581 return ret;
582}
583
584/*
585 return on failure or (with 0) if all fd events are removed
586*/
587int tevent_common_loop_wait(struct tevent_context *ev,
588 const char *location)
589{
590 /*
591 * loop as long as we have events pending
592 */
593 while (ev->fd_events ||
594 ev->timer_events ||
595 ev->immediate_events ||
596 ev->signal_events) {
597 int ret;
598 ret = _tevent_loop_once(ev, location);
599 if (ret != 0) {
600 tevent_debug(ev, TEVENT_DEBUG_FATAL,
601 "_tevent_loop_once() failed: %d - %s\n",
602 ret, strerror(errno));
603 return ret;
604 }
605 }
606
607 tevent_debug(ev, TEVENT_DEBUG_WARNING,
608 "tevent_common_loop_wait() out of events\n");
609 return 0;
610}
611
612/*
613 return on failure or (with 0) if all fd events are removed
614*/
615int _tevent_loop_wait(struct tevent_context *ev, const char *location)
616{
617 return ev->ops->loop_wait(ev, location);
618}
Note: See TracBrowser for help on using the repository browser.