source: trunk/server/lib/tevent/tevent.c@ 421

Last change on this file since 421 was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

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