tevent: Add in the new implementation of "standard" tevent backend.
authorJeremy Allison <jra@samba.org>
Mon, 11 Feb 2013 19:40:49 +0000 (11:40 -0800)
committerJeremy Allison <jra@samba.org>
Thu, 14 Feb 2013 18:19:38 +0000 (10:19 -0800)
Falls back cleanly from epoll -> poll, or uses poll if
epoll not available.

Signed-off-by: Jeremy Allison <jra@samba.org>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
lib/tevent/tevent_standard.c

index 938b2235727fdcc46cd45fe664e6fd03aca5e0d2..0c72226558092b6f3ab2e8db5ac2978d60ad6181 100644 (file)
@@ -2,7 +2,8 @@
    Unix SMB/CIFS implementation.
    main select loop and event handling
    Copyright (C) Andrew Tridgell       2003-2005
-   Copyright (C) Stefan Metzmacher     2005-2009
+   Copyright (C) Stefan Metzmacher      2005-2013
+   Copyright (C) Jeremy Allison         2013
 
      ** NOTE! The following LGPL license applies to the tevent
      ** library. This does NOT imply that all of Samba is released
   This is SAMBA's default event loop code
 
   - we try to use epoll if configure detected support for it
-    otherwise we use select()
+    otherwise we use poll()
   - if epoll is broken on the system or the kernel doesn't support it
-    at runtime we fallback to select()
+    at runtime we fallback to poll()
 */
 
 #include "replace.h"
-#include "system/filesys.h"
-#include "system/select.h"
 #include "tevent.h"
 #include "tevent_util.h"
 #include "tevent_internal.h"
 
+#if 0
 struct std_event_context {
        /* a pointer back to the generic event_context */
        struct tevent_context *ev;
@@ -589,10 +589,184 @@ static const struct tevent_ops std_event_ops = {
        .loop_once              = std_event_loop_once,
        .loop_wait              = tevent_common_loop_wait,
 };
+#endif
+
+struct std_event_glue {
+       const struct tevent_ops *epoll_ops;
+       const struct tevent_ops *poll_ops;
+       struct tevent_ops *glue_ops;
+       bool fallback_replay;
+};
+
+static int std_event_context_init(struct tevent_context *ev);
+
+static const struct tevent_ops std_event_ops = {
+       .context_init           = std_event_context_init,
+};
+
+/*
+  If this function gets called. epoll failed at runtime.
+  Move us to using poll instead. If we return false here,
+  caller should abort().
+*/
+static bool std_fallback_to_poll(struct tevent_context *ev, bool replay)
+{
+       void *glue_ptr = talloc_parent(ev->ops);
+       struct std_event_glue *glue =
+               talloc_get_type_abort(glue_ptr,
+               struct std_event_glue);
+       int ret;
+       struct tevent_fd *fde;
+       struct tevent_fd *fde_next;
+
+       glue->fallback_replay = replay;
+
+       /* First switch all the ops to poll. */
+       glue->epoll_ops = NULL;
+       TALLOC_FREE(ev->additional_data);
+
+       /*
+        * Set custom_ops the same as poll.
+        */
+       *glue->glue_ops = *glue->poll_ops;
+       glue->glue_ops->context_init = std_event_context_init;
 
+       /* Next initialize the poll backend. */
+       ret = glue->poll_ops->context_init(ev);
+       if (ret != 0) {
+               return false;
+       }
+
+       /*
+        * Now we have to change all the existing file descriptor
+        * events from the epoll backend to the poll backend.
+        */
+       for (fde = ev->fd_events; fde; fde = fde_next) {
+               /*
+                * We must remove this fde off the ev->fd_events list.
+                */
+               fde_next = fde->next;
+
+               /* Remove from the ev->fd_events list. */
+               DLIST_REMOVE(ev->fd_events, fde);
+
+               /* Re-add this event as a poll backend event. */
+               tevent_poll_event_add_fd_internal(ev, fde);
+       }
+
+       return true;
+}
+
+static int std_event_loop_once(struct tevent_context *ev, const char *location)
+{
+       void *glue_ptr = talloc_parent(ev->ops);
+       struct std_event_glue *glue =
+               talloc_get_type_abort(glue_ptr,
+               struct std_event_glue);
+       int ret;
+
+       ret = glue->epoll_ops->loop_once(ev, location);
+       if (glue->epoll_ops != NULL) {
+               /* No fallback */
+               return ret;
+       }
+
+       if (!glue->fallback_replay) {
+               /*
+                * The problem happened while modifying an event.
+                * An event handler was triggered in this case
+                * and there is no need to call loop_once() again.
+                */
+               return ret;
+       }
+
+       return glue->poll_ops->loop_once(ev, location);
+}
+
+/*
+  Initialize the epoll backend and allow it to call a
+  switch function if epoll fails at runtime.
+*/
+static int std_event_context_init(struct tevent_context *ev)
+{
+       struct std_event_glue *glue;
+       int ret;
+
+       /*
+        * If this is the first initialization
+        * we need to set up the allocated ops
+        * pointers.
+        */
+
+       if (ev->ops == &std_event_ops) {
+               glue = talloc_zero(ev, struct std_event_glue);
+               if (glue == NULL) {
+                       return -1;
+               }
+
+               glue->epoll_ops = tevent_find_ops_byname("epoll");
+
+               glue->poll_ops = tevent_find_ops_byname("poll");
+               if (glue->poll_ops == NULL) {
+                       return -1;
+               }
+
+               /*
+                * Allocate space for our custom ops.
+                * Allocate as a child of our epoll_ops pointer
+                * so we can easily get to it using talloc_parent.
+                */
+               glue->glue_ops = talloc_zero(glue, struct tevent_ops);
+               if (glue->glue_ops == NULL) {
+                       talloc_free(glue);
+                       return -1;
+               }
+
+               ev->ops = glue->glue_ops;
+       } else {
+               void *glue_ptr = talloc_parent(ev->ops);
+               glue = talloc_get_type_abort(glue_ptr, struct std_event_glue);
+       }
+
+       if (glue->epoll_ops != NULL) {
+               /*
+                * Set custom_ops the same as epoll,
+                * except re-init using std_event_context_init()
+                * and use std_event_loop_once() to add the
+                * ability to fallback to a poll backend on
+                * epoll runtime error.
+                */
+               *glue->glue_ops = *glue->epoll_ops;
+               glue->glue_ops->context_init = std_event_context_init;
+               glue->glue_ops->loop_once = std_event_loop_once;
+
+               ret = glue->epoll_ops->context_init(ev);
+               if (ret == -1) {
+                       goto fallback;
+               }
+#ifdef HAVE_EPOLL
+               if (!tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll)) {
+                       TALLOC_FREE(ev->additional_data);
+                       goto fallback;
+               }
+#endif
+
+               return ret;
+       }
+
+fallback:
+       glue->epoll_ops = NULL;
+
+       /*
+        * Set custom_ops the same as poll.
+        */
+       *glue->glue_ops = *glue->poll_ops;
+       glue->glue_ops->context_init = std_event_context_init;
+
+       return glue->poll_ops->context_init(ev);
+}
 
 _PRIVATE_ bool tevent_standard_init(void)
 {
        return tevent_register_backend("standard", &std_event_ops);
 }
-