2 Unix SMB/CIFS implementation.
4 main select loop and event handling - kqueue implementation
6 Copyright (C) Stefan Metzmacher 2013
8 ** NOTE! The following LGPL license applies to the tevent
9 ** library. This does NOT imply that all of Samba is released
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Lesser General Public
14 License as published by the Free Software Foundation; either
15 version 3 of the License, or (at your option) any later version.
17 This library is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 Lesser General Public License for more details.
22 You should have received a copy of the GNU Lesser General Public
23 License along with this library; if not, see <http://www.gnu.org/licenses/>.
27 #include "system/filesys.h"
28 #include "system/time.h"
29 #ifdef HAVE_SYS_EVENT_H
30 #include <sys/event.h>
33 #include "tevent_internal.h"
34 #include "tevent_util.h"
36 struct kqueue_event_context {
37 /* a pointer back to the generic event_context */
38 struct tevent_context *ev;
43 bool panic_force_replay;
45 bool (*panic_fallback)(struct tevent_context *ev, bool replay);
48 #define KQUEUE_ADDITIONAL_FD_FLAG_HAS_READ (1<<0)
49 #define KQUEUE_ADDITIONAL_FD_FLAG_DISABLED_READ (1<<1)
50 #define KQUEUE_ADDITIONAL_FD_FLAG_HAS_WRITE (1<<2)
51 #define KQUEUE_ADDITIONAL_FD_FLAG_DISABLED_WRITE (1<<3)
52 #define KQUEUE_ADDITIONAL_FD_FLAG_REPORT_ERROR (1<<4)
53 #define KQUEUE_ADDITIONAL_FD_FLAG_GOT_ERROR (1<<5)
56 called to set the panic fallback function.
58 _PRIVATE_ bool tevent_kqueue_set_panic_fallback(struct tevent_context *ev,
59 bool (*panic_fallback)(struct tevent_context *ev,
62 struct kqueue_event_context *kqueue_ev;
64 if (ev->additional_data == NULL) {
68 kqueue_ev = talloc_get_type(ev->additional_data,
69 struct kqueue_event_context);
70 if (kqueue_ev == NULL) {
73 kqueue_ev->panic_fallback = panic_fallback;
78 called when a epoll call fails
80 static void kqueue_panic(struct kqueue_event_context *kqueue_ev,
81 const char *reason, bool replay)
83 struct tevent_context *ev = kqueue_ev->ev;
84 bool (*panic_fallback)(struct tevent_context *ev, bool replay);
86 panic_fallback = kqueue_ev->panic_fallback;
88 if (kqueue_ev->panic_state != NULL) {
89 *kqueue_ev->panic_state = true;
92 if (kqueue_ev->panic_force_replay) {
96 TALLOC_FREE(ev->additional_data);
98 if (panic_fallback == NULL) {
99 tevent_debug(ev, TEVENT_DEBUG_FATAL,
100 "%s (%s) replay[%u] - calling abort()\n",
101 reason, strerror(errno), (unsigned)replay);
105 tevent_debug(ev, TEVENT_DEBUG_ERROR,
106 "%s (%s) replay[%u] - calling panic_fallback\n",
107 reason, strerror(errno), (unsigned)replay);
109 if (!panic_fallback(ev, replay)) {
110 /* Fallback failed. */
111 tevent_debug(ev, TEVENT_DEBUG_FATAL,
112 "%s (%s) replay[%u] - calling abort()\n",
113 reason, strerror(errno), (unsigned)replay);
118 static int kqueue_ctx_destructor(struct kqueue_event_context *kqueue_ev)
120 kqueue_ev->ev->additional_data = NULL;
122 if (kqueue_ev->kqueue_fd != -1) {
123 close(kqueue_ev->kqueue_fd);
125 kqueue_ev->kqueue_fd = -1;
130 static int kqueue_event_context_init(struct tevent_context *ev)
132 struct kqueue_event_context *kqueue_ev;
135 * We might be called during tevent_re_initialise()
136 * which means we need to free our old additional_data.
138 TALLOC_FREE(ev->additional_data);
140 kqueue_ev = talloc_zero(ev, struct kqueue_event_context);
141 if (kqueue_ev == NULL) {
145 kqueue_ev->pid = getpid();
146 kqueue_ev->kqueue_fd = -1;
148 talloc_set_destructor(kqueue_ev, kqueue_ctx_destructor);
150 kqueue_ev->kqueue_fd = kqueue();
151 if (kqueue_ev->kqueue_fd == -1) {
152 TALLOC_FREE(kqueue_ev);
153 tevent_debug(ev, TEVENT_DEBUG_FATAL,
154 "Failed to create kqueue() handle.\n");
158 if (!ev_set_close_on_exec(kqueue_ev->kqueue_fd)) {
159 tevent_debug(kqueue_ev->ev, TEVENT_DEBUG_WARNING,
160 "kqueue_fd pid[%d] - failed to set close-on-exec, "
161 "file descriptor may be leaked to children.\n",
165 ev->additional_data = kqueue_ev;
169 static void kqueue_update_fd_event(struct kqueue_event_context *kqueue_ev,
170 struct tevent_fd *fde);
172 static void kqueue_check_reopen(struct kqueue_event_context *kqueue_ev)
174 struct tevent_fd *fde;
175 bool *caller_panic_state = kqueue_ev->panic_state;
176 bool panic_triggered = false;
178 if (kqueue_ev->pid == getpid()) {
181 kqueue_ev->pid = getpid();
183 close(kqueue_ev->kqueue_fd);
184 kqueue_ev->kqueue_fd = kqueue();
185 if (kqueue_ev->kqueue_fd == -1) {
186 kqueue_panic(kqueue_ev, "kqueue() failed", false);
190 if (!ev_set_close_on_exec(kqueue_ev->kqueue_fd)) {
191 tevent_debug(kqueue_ev->ev, TEVENT_DEBUG_WARNING,
192 "kqueue_fd pid[%d] - failed to set close-on-exec, "
193 "file descriptor may be leaked to children.\n",
197 kqueue_ev->panic_state = &panic_triggered;
198 for (fde=kqueue_ev->ev->fd_events;fde;fde=fde->next) {
199 fde->additional_flags &= KQUEUE_ADDITIONAL_FD_FLAG_GOT_ERROR;
200 kqueue_update_fd_event(kqueue_ev, fde);
202 if (panic_triggered) {
203 if (caller_panic_state != NULL) {
204 *caller_panic_state = true;
209 kqueue_ev->panic_state = NULL;
212 static void kqueue_update_fd_event(struct kqueue_event_context *kqueue_ev,
213 struct tevent_fd *fde)
215 bool add_read = false;
216 bool enable_read = false;
217 bool disable_read = false;
218 bool delete_read = false;
219 bool add_write = false;
220 bool enable_write = false;
221 bool disable_write = false;
222 bool delete_write = false;
223 bool got_error = (fde->additional_flags & KQUEUE_ADDITIONAL_FD_FLAG_GOT_ERROR);
227 if (fde->additional_flags & KQUEUE_ADDITIONAL_FD_FLAG_DISABLED_WRITE) {
230 } else if (fde->flags & TEVENT_FD_WRITE) {
232 } else if (fde->flags == 0) {
235 } else if (fde->additional_flags & KQUEUE_ADDITIONAL_FD_FLAG_HAS_WRITE) {
238 } else if (fde->flags == 0) {
240 } else if (!(fde->flags & TEVENT_FD_WRITE)) {
241 disable_write = true;
246 } else if (fde->flags & TEVENT_FD_WRITE) {
251 if (fde->additional_flags & KQUEUE_ADDITIONAL_FD_FLAG_DISABLED_READ) {
252 if (fde->flags & TEVENT_FD_READ) {
254 } else if (fde->flags == 0) {
257 } else if (fde->additional_flags & KQUEUE_ADDITIONAL_FD_FLAG_HAS_READ) {
258 if (fde->flags == 0) {
260 } else if (!(fde->flags & TEVENT_FD_READ)) {
264 if (fde->flags & TEVENT_FD_READ) {
270 EV_SET(&kev, fde->fd, EVFILT_WRITE, EV_ADD,
271 0 /* fflags */, NULL /* data */,
273 ret = kevent(kqueue_ev->kqueue_fd, &kev, 1, NULL, 0, NULL);
275 kqueue_panic(kqueue_ev, "EVFILT_WRITE EV_ADD failed",
280 fde->additional_flags |= KQUEUE_ADDITIONAL_FD_FLAG_HAS_WRITE;
281 } else if (enable_write) {
282 EV_SET(&kev, fde->fd, EVFILT_WRITE, EV_ENABLE,
283 0 /* fflags */, NULL /* data */,
285 ret = kevent(kqueue_ev->kqueue_fd, &kev, 1, NULL, 0, NULL);
287 kqueue_panic(kqueue_ev, "EVFILT_WRITE EV_ENABLE failed",
292 fde->additional_flags &= ~KQUEUE_ADDITIONAL_FD_FLAG_DISABLED_WRITE;
293 } else if (disable_write) {
294 EV_SET(&kev, fde->fd, EVFILT_WRITE, EV_DISABLE,
295 0 /* fflags */, NULL /* data */,
297 ret = kevent(kqueue_ev->kqueue_fd, &kev, 1, NULL, 0, NULL);
299 kqueue_panic(kqueue_ev, "EVFILT_WRITE EV_DISABLE failed",
304 fde->additional_flags |= KQUEUE_ADDITIONAL_FD_FLAG_DISABLED_WRITE;
305 } else if (delete_write) {
306 EV_SET(&kev, fde->fd, EVFILT_WRITE, EV_DELETE,
307 0 /* fflags */, NULL /* data */,
309 ret = kevent(kqueue_ev->kqueue_fd, &kev, 1, NULL, 0, NULL);
311 kqueue_panic(kqueue_ev, "EVFILT_WRITE EV_DEL failed",
316 fde->additional_flags &= ~KQUEUE_ADDITIONAL_FD_FLAG_HAS_WRITE;
317 fde->additional_flags &= ~KQUEUE_ADDITIONAL_FD_FLAG_DISABLED_WRITE;
321 EV_SET(&kev, fde->fd, EVFILT_READ, EV_ADD,
322 0 /* fflags */, NULL /* data */,
324 ret = kevent(kqueue_ev->kqueue_fd, &kev, 1, NULL, 0, NULL);
326 kqueue_panic(kqueue_ev, "EVFILT_READ EV_ADD failed",
331 fde->additional_flags |= KQUEUE_ADDITIONAL_FD_FLAG_HAS_READ;
332 fde->additional_flags |= KQUEUE_ADDITIONAL_FD_FLAG_REPORT_ERROR;
333 } else if (enable_read) {
334 EV_SET(&kev, fde->fd, EVFILT_READ, EV_ENABLE,
335 0 /* fflags */, NULL /* data */,
337 ret = kevent(kqueue_ev->kqueue_fd, &kev, 1, NULL, 0, NULL);
339 kqueue_panic(kqueue_ev, "EVFILT_READ EV_ENABLE failed",
344 fde->additional_flags &= ~KQUEUE_ADDITIONAL_FD_FLAG_DISABLED_READ;
345 fde->additional_flags |= KQUEUE_ADDITIONAL_FD_FLAG_REPORT_ERROR;
346 } else if (disable_read) {
347 EV_SET(&kev, fde->fd, EVFILT_READ, EV_DISABLE,
348 0 /* fflags */, NULL /* data */,
350 ret = kevent(kqueue_ev->kqueue_fd, &kev, 1, NULL, 0, NULL);
352 kqueue_panic(kqueue_ev, "EVFILT_READ EV_DISABLE failed",
357 fde->additional_flags |= KQUEUE_ADDITIONAL_FD_FLAG_DISABLED_READ;
358 fde->additional_flags &= ~KQUEUE_ADDITIONAL_FD_FLAG_REPORT_ERROR;
359 } else if (delete_read) {
360 EV_SET(&kev, fde->fd, EVFILT_READ, EV_DELETE,
361 0 /* fflags */, NULL /* data */,
363 ret = kevent(kqueue_ev->kqueue_fd, &kev, 1, NULL, 0, NULL);
365 kqueue_panic(kqueue_ev, "EVFILT_READ EV_DEL failed",
370 fde->additional_flags &= ~KQUEUE_ADDITIONAL_FD_FLAG_HAS_READ;
371 fde->additional_flags &= ~KQUEUE_ADDITIONAL_FD_FLAG_DISABLED_READ;
372 fde->additional_flags &= ~KQUEUE_ADDITIONAL_FD_FLAG_REPORT_ERROR;
379 static int kqueue_event_fd_destructor(struct tevent_fd *fde)
381 struct tevent_context *ev = fde->event_ctx;
382 struct kqueue_event_context *kqueue_ev = NULL;
383 bool panic_triggered = false;
384 int flags = fde->flags;
387 return tevent_common_fd_destructor(fde);
390 kqueue_ev = talloc_get_type_abort(ev->additional_data,
391 struct kqueue_event_context);
394 * we must remove the event from the list
395 * otherwise a panic fallback handler may
396 * reuse invalid memory
398 DLIST_REMOVE(ev->fd_events, fde);
400 kqueue_ev->panic_state = &panic_triggered;
401 kqueue_check_reopen(kqueue_ev);
402 if (panic_triggered) {
403 return tevent_common_fd_destructor(fde);
407 kqueue_update_fd_event(kqueue_ev, fde);
409 if (panic_triggered) {
410 return tevent_common_fd_destructor(fde);
412 kqueue_ev->panic_state = NULL;
414 return tevent_common_fd_destructor(fde);
417 static struct tevent_fd *kqueue_event_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
418 int fd, uint16_t flags,
419 tevent_fd_handler_t handler,
421 const char *handler_name,
422 const char *location)
424 struct kqueue_event_context *kqueue_ev =
425 talloc_get_type_abort(ev->additional_data,
426 struct kqueue_event_context);
427 struct tevent_fd *fde;
428 bool panic_triggered = false;
430 fde = tevent_common_add_fd(ev, mem_ctx, fd, flags,
431 handler, private_data,
432 handler_name, location);
436 talloc_set_destructor(fde, kqueue_event_fd_destructor);
438 kqueue_ev->panic_state = &panic_triggered;
439 kqueue_check_reopen(kqueue_ev);
440 if (panic_triggered) {
443 kqueue_ev->panic_state = NULL;
445 kqueue_update_fd_event(kqueue_ev, fde);
451 set the fd event flags
453 static void kqueue_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
455 struct tevent_context *ev;
456 struct kqueue_event_context *kqueue_ev;
457 bool panic_triggered = false;
459 if (fde->flags == flags) return;
462 kqueue_ev = talloc_get_type_abort(ev->additional_data,
463 struct kqueue_event_context);
467 kqueue_ev->panic_state = &panic_triggered;
468 kqueue_check_reopen(kqueue_ev);
469 if (panic_triggered) {
472 kqueue_ev->panic_state = NULL;
474 kqueue_update_fd_event(kqueue_ev, fde);
478 event loop handling using kqueue
480 static int kqueue_event_loop(struct kqueue_event_context *kqueue_ev, struct timeval *tvalp)
484 struct kevent kevs[MAXEVENTS];
486 const struct timespec *timeout = NULL;
489 if (kqueue_ev->ev->signal_events &&
490 tevent_common_check_signal(kqueue_ev->ev)) {
495 _ts.tv_sec = tvalp->tv_sec;
496 _ts.tv_nsec = tvalp->tv_usec * 1000;
500 tevent_trace_point_callback(kqueue_ev->ev, TEVENT_TRACE_BEFORE_WAIT);
501 ret = kevent(kqueue_ev->kqueue_fd, NULL, 0, kevs, MAXEVENTS, timeout);
502 kevent_errno = errno;
503 tevent_trace_point_callback(kqueue_ev->ev, TEVENT_TRACE_AFTER_WAIT);
505 if (ret == -1 && kevent_errno == EINTR && kqueue_ev->ev->signal_events) {
506 if (tevent_common_check_signal(kqueue_ev->ev)) {
511 if (ret == -1 && kevent_errno != EINTR) {
512 kqueue_panic(kqueue_ev, "kevent() failed", true);
516 if (ret == 0 && tvalp) {
517 /* we don't care about a possible delay here */
518 tevent_common_loop_timer_delay(kqueue_ev->ev);
522 for (i=0;i<ret;i++) {
523 struct tevent_fd *fde = talloc_get_type(kevs[i].udata,
528 kqueue_panic(kqueue_ev, "kevent() gave bad data",
533 if (kevs[i].flags & EV_EOF) {
534 fde->additional_flags |= KQUEUE_ADDITIONAL_FD_FLAG_GOT_ERROR;
536 * if we only wait for TEVENT_FD_WRITE, we should not
537 * tell the event handler about it, and remove the
538 * EVFILT_WRITE filter, as we only report errors when
539 * waiting for read events, to match the select()
542 if (!(fde->additional_flags & KQUEUE_ADDITIONAL_FD_FLAG_REPORT_ERROR)) {
543 kqueue_update_fd_event(kqueue_ev, fde);
546 flags |= TEVENT_FD_READ;
548 if (kevs[i].filter == EVFILT_READ) {
549 flags |= TEVENT_FD_READ;
551 if (kevs[i].filter == EVFILT_WRITE) {
552 flags |= TEVENT_FD_WRITE;
556 * make sure we only pass the flags
557 * the handler is expecting.
561 fde->handler(kqueue_ev->ev, fde, flags, fde->private_data);
570 do a single event loop using the events defined in ev
572 static int kqueue_event_loop_once(struct tevent_context *ev, const char *location)
574 struct kqueue_event_context *kqueue_ev = talloc_get_type(ev->additional_data,
575 struct kqueue_event_context);
577 bool panic_triggered = false;
579 if (ev->signal_events &&
580 tevent_common_check_signal(ev)) {
584 if (ev->immediate_events &&
585 tevent_common_loop_immediate(ev)) {
589 tval = tevent_common_loop_timer_delay(ev);
590 if (tevent_timeval_is_zero(&tval)) {
594 kqueue_ev->panic_state = &panic_triggered;
595 kqueue_ev->panic_force_replay = true;
596 kqueue_check_reopen(kqueue_ev);
597 if (panic_triggered) {
601 kqueue_ev->panic_force_replay = false;
602 kqueue_ev->panic_state = NULL;
604 return kqueue_event_loop(kqueue_ev, &tval);
607 static const struct tevent_ops kqueue_event_ops = {
608 .context_init = kqueue_event_context_init,
609 .add_fd = kqueue_event_add_fd,
610 .set_fd_close_fn = tevent_common_fd_set_close_fn,
611 .get_fd_flags = tevent_common_fd_get_flags,
612 .set_fd_flags = kqueue_event_set_fd_flags,
613 .add_timer = tevent_common_add_timer_v2,
614 .schedule_immediate = tevent_common_schedule_immediate,
615 .add_signal = tevent_common_add_signal,
616 .loop_once = kqueue_event_loop_once,
617 .loop_wait = tevent_common_loop_wait,
620 _PRIVATE_ bool tevent_kqueue_init(void)
622 return tevent_register_backend("kqueue", &kqueue_event_ops);