Avoid including events_util.h in events_internal.h since the latter is
[tprouty/samba.git] / source / lib / events / events_standard.c
index 5d59f1b885cbaea7c46f5cc99e121b918fb27228..11128d7623d23c3862ea133c042896297de8dd09 100644 (file)
@@ -6,7 +6,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -15,8 +15,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 /*
     at runtime we fallback to select()
 */
 
-#include "includes.h"
+#include "replace.h"
 #include "system/filesys.h"
-#include "system/select.h" /* needed for WITH_EPOLL */
-#include "dlinklist.h"
-#include "lib/events/events.h"
-#include "lib/events/events_internal.h"
+#include "system/network.h"
+#include "system/select.h" /* needed for HAVE_EVENTS_EPOLL */
+#include "events.h"
+#include "events_util.h"
+#include "events_internal.h"
 
 struct std_event_context {
        /* a pointer back to the generic event_context */
@@ -42,9 +42,6 @@ struct std_event_context {
        /* list of filedescriptor events */
        struct fd_event *fd_events;
 
-       /* list of timed events */
-       struct timed_event *timed_events;
-
        /* the maximum file descriptor number in fd_events */
        int maxfd;
 
@@ -61,19 +58,22 @@ struct std_event_context {
 
        /* when using epoll this is the handle from epoll_create */
        int epoll_fd;
-};
 
-static void std_event_loop_timer(struct std_event_context *std_ev);
+       /* our pid at the time the epoll_fd was created */
+       pid_t pid;
+};
 
 /* use epoll if it is available */
-#if WITH_EPOLL
+#if HAVE_EVENTS_EPOLL
 /*
   called when a epoll call fails, and we should fallback
   to using select
 */
 static void epoll_fallback_to_select(struct std_event_context *std_ev, const char *reason)
 {
-       DEBUG(0,("%s (%s) - falling back to select()\n", reason, strerror(errno)));
+       ev_debug(std_ev->ev, EV_DEBUG_FATAL,
+                "%s (%s) - falling back to select()\n",
+                reason, strerror(errno));
        close(std_ev->epoll_fd);
        std_ev->epoll_fd = -1;
        talloc_set_destructor(std_ev, NULL);
@@ -95,7 +95,9 @@ static uint32_t epoll_map_flags(uint16_t flags)
 */
 static int epoll_ctx_destructor(struct std_event_context *std_ev)
 {
-       close(std_ev->epoll_fd);
+       if (std_ev->epoll_fd != -1) {
+               close(std_ev->epoll_fd);
+       }
        std_ev->epoll_fd = -1;
        return 0;
 }
@@ -103,13 +105,41 @@ static int epoll_ctx_destructor(struct std_event_context *std_ev)
 /*
  init the epoll fd
 */
-static void epoll_init_ctx(struct std_event_context *std_ev, BOOL try_epoll)
+static void epoll_init_ctx(struct std_event_context *std_ev)
 {
-       if (!try_epoll) return;
        std_ev->epoll_fd = epoll_create(64);
+       std_ev->pid = getpid();
        talloc_set_destructor(std_ev, epoll_ctx_destructor);
 }
 
+static void epoll_add_event(struct std_event_context *std_ev, struct fd_event *fde);
+
+/*
+  reopen the epoll handle when our pid changes
+  see http://junkcode.samba.org/ftp/unpacked/junkcode/epoll_fork.c for an 
+  demonstration of why this is needed
+ */
+static void epoll_check_reopen(struct std_event_context *std_ev)
+{
+       struct fd_event *fde;
+
+       if (std_ev->pid == getpid()) {
+               return;
+       }
+
+       close(std_ev->epoll_fd);
+       std_ev->epoll_fd = epoll_create(64);
+       if (std_ev->epoll_fd == -1) {
+               ev_debug(std_ev->ev, EV_DEBUG_FATAL,
+                        "Failed to recreate epoll handle after fork\n");
+               return;
+       }
+       std_ev->pid = getpid();
+       for (fde=std_ev->fd_events;fde;fde=fde->next) {
+               epoll_add_event(std_ev, fde);
+       }
+}
+
 #define EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT     (1<<0)
 #define EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR  (1<<1)
 #define EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR     (1<<2)
@@ -186,9 +216,9 @@ static void epoll_mod_event(struct std_event_context *std_ev, struct fd_event *f
 
 static void epoll_change_event(struct std_event_context *std_ev, struct fd_event *fde)
 {
-       BOOL got_error = (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR);
-       BOOL want_read = (fde->flags & EVENT_FD_READ);
-       BOOL want_write= (fde->flags & EVENT_FD_WRITE);
+       bool got_error = (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR);
+       bool want_read = (fde->flags & EVENT_FD_READ);
+       bool want_write= (fde->flags & EVENT_FD_WRITE);
 
        if (std_ev->epoll_fd == -1) return;
 
@@ -225,7 +255,7 @@ static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tv
        int ret, i;
 #define MAXEVENTS 8
        struct epoll_event events[MAXEVENTS];
-       uint32_t destruction_count = std_ev->destruction_count;
+       uint32_t destruction_count = ++std_ev->destruction_count;
        int timeout = -1;
 
        if (std_ev->epoll_fd == -1) return -1;
@@ -235,15 +265,27 @@ static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tv
                timeout = ((tvalp->tv_usec+999) / 1000) + (tvalp->tv_sec*1000);
        }
 
+       if (std_ev->ev->num_signal_handlers && 
+           common_event_check_signal(std_ev->ev)) {
+               return 0;
+       }
+
        ret = epoll_wait(std_ev->epoll_fd, events, MAXEVENTS, timeout);
 
+       if (ret == -1 && errno == EINTR && std_ev->ev->num_signal_handlers) {
+               if (common_event_check_signal(std_ev->ev)) {
+                       return 0;
+               }
+       }
+
        if (ret == -1 && errno != EINTR) {
                epoll_fallback_to_select(std_ev, "epoll_wait() failed");
                return -1;
        }
 
        if (ret == 0 && tvalp) {
-               std_event_loop_timer(std_ev);
+               /* we don't care about a possible delay here */
+               common_event_loop_timer_delay(std_ev->ev);
                return 0;
        }
 
@@ -283,28 +325,27 @@ static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tv
        return 0;
 }
 #else
-#define epoll_init_ctx(std_ev,try_epoll) if (try_epoll) {/* fix unused variable warning*/}
+#define epoll_init_ctx(std_ev
 #define epoll_add_event(std_ev,fde)
 #define epoll_del_event(std_ev,fde)
 #define epoll_change_event(std_ev,fde)
 #define epoll_event_loop(std_ev,tvalp) (-1)
+#define epoll_check_reopen(std_ev)
 #endif
 
 /*
   create a std_event_context structure.
 */
-static int std_event_context_init(struct event_context *ev, void *private_data)
+static int std_event_context_init(struct event_context *ev)
 {
        struct std_event_context *std_ev;
-       BOOL *_try_epoll = private_data;
-       BOOL try_epoll = (_try_epoll == NULL ? True : *_try_epoll);
 
        std_ev = talloc_zero(ev, struct std_event_context);
        if (!std_ev) return -1;
        std_ev->ev = ev;
        std_ev->epoll_fd = -1;
 
-       epoll_init_ctx(std_ev, try_epoll);
+       epoll_init_ctx(std_ev);
 
        ev->additional_data = std_ev;
        return 0;
@@ -340,6 +381,8 @@ static int std_event_fd_destructor(struct fd_event *fde)
        struct std_event_context *std_ev = talloc_get_type(ev->additional_data,
                                                           struct std_event_context);
 
+       epoll_check_reopen(std_ev);
+
        if (std_ev->maxfd == fde->fd) {
                std_ev->maxfd = EVENT_INVALID_MAXFD;
        }
@@ -349,6 +392,11 @@ static int std_event_fd_destructor(struct fd_event *fde)
 
        epoll_del_event(std_ev, fde);
 
+       if (fde->flags & EVENT_FD_AUTOCLOSE) {
+               close(fde->fd);
+               fde->fd = -1;
+       }
+
        return 0;
 }
 
@@ -365,6 +413,8 @@ static struct fd_event *std_event_add_fd(struct event_context *ev, TALLOC_CTX *m
                                                           struct std_event_context);
        struct fd_event *fde;
 
+       epoll_check_reopen(std_ev);
+
        fde = talloc(mem_ctx?mem_ctx:ev, struct fd_event);
        if (!fde) return NULL;
 
@@ -377,7 +427,8 @@ static struct fd_event *std_event_add_fd(struct event_context *ev, TALLOC_CTX *m
        fde->additional_data    = NULL;
 
        DLIST_ADD(std_ev->fd_events, fde);
-       if (fde->fd > std_ev->maxfd) {
+       if ((std_ev->maxfd != EVENT_INVALID_MAXFD)
+           && (fde->fd > std_ev->maxfd)) {
                std_ev->maxfd = fde->fd;
        }
        talloc_set_destructor(fde, std_event_fd_destructor);
@@ -411,94 +462,9 @@ static void std_event_set_fd_flags(struct fd_event *fde, uint16_t flags)
 
        fde->flags = flags;
 
-       epoll_change_event(std_ev, fde);
-}
-
-/*
-  destroy a timed event
-*/
-static int std_event_timed_destructor(struct timed_event *te)
-{
-       struct std_event_context *std_ev = talloc_get_type(te->event_ctx->additional_data,
-                                                          struct std_event_context);
-       DLIST_REMOVE(std_ev->timed_events, te);
-       return 0;
-}
-
-static int std_event_timed_deny_destructor(struct timed_event *te)
-{
-       return -1;
-}
-
-/*
-  add a timed event
-  return NULL on failure (memory allocation error)
-*/
-static struct timed_event *std_event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ctx,
-                                              struct timeval next_event, 
-                                              event_timed_handler_t handler, 
-                                              void *private_data) 
-{
-       struct std_event_context *std_ev = talloc_get_type(ev->additional_data,
-                                                          struct std_event_context);
-       struct timed_event *te, *last_te, *cur_te;
-
-       te = talloc(mem_ctx?mem_ctx:ev, struct timed_event);
-       if (te == NULL) return NULL;
-
-       te->event_ctx           = ev;
-       te->next_event          = next_event;
-       te->handler             = handler;
-       te->private_data        = private_data;
-       te->additional_data     = NULL;
-
-       /* keep the list ordered */
-       last_te = NULL;
-       for (cur_te = std_ev->timed_events; cur_te; cur_te = cur_te->next) {
-               /* if the new event comes before the current one break */
-               if (!timeval_is_zero(&cur_te->next_event) &&
-                   timeval_compare(&te->next_event,
-                                   &cur_te->next_event) < 0) {
-                       break;
-               }
-
-               last_te = cur_te;
-       }
-
-       DLIST_ADD_AFTER(std_ev->timed_events, te, last_te);
-
-       talloc_set_destructor(te, std_event_timed_destructor);
-
-       return te;
-}
-
-/*
-  a timer has gone off - call it
-*/
-static void std_event_loop_timer(struct std_event_context *std_ev)
-{
-       struct timeval t = timeval_current();
-       struct timed_event *te = std_ev->timed_events;
-
-       if (te == NULL) {
-               return;
-       }
-
-       /* deny the handler to free the event */
-       talloc_set_destructor(te, std_event_timed_deny_destructor);
-
-       /* We need to remove the timer from the list before calling the
-        * handler because in a semi-async inner event loop called from the
-        * handler we don't want to come across this event again -- vl */
-       DLIST_REMOVE(std_ev->timed_events, te);
+       epoll_check_reopen(std_ev);
 
-       te->handler(std_ev->ev, te, t, te->private_data);
-
-       /* The destructor isn't necessary anymore, we've already removed the
-        * event from the list. */
-       talloc_set_destructor(te, NULL);
-
-       talloc_free(te);
+       epoll_change_event(std_ev, fde);
 }
 
 /*
@@ -509,7 +475,7 @@ static int std_event_loop_select(struct std_event_context *std_ev, struct timeva
        fd_set r_fds, w_fds;
        struct fd_event *fde;
        int selrtn;
-       uint32_t destruction_count = std_ev->destruction_count;
+       uint32_t destruction_count = ++std_ev->destruction_count;
 
        /* we maybe need to recalculate the maxfd */
        if (std_ev->maxfd == EVENT_INVALID_MAXFD) {
@@ -529,21 +495,34 @@ static int std_event_loop_select(struct std_event_context *std_ev, struct timeva
                }
        }
 
+       if (std_ev->ev->num_signal_handlers && 
+           common_event_check_signal(std_ev->ev)) {
+               return 0;
+       }
+
        selrtn = select(std_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp);
 
+       if (selrtn == -1 && errno == EINTR && 
+           std_ev->ev->num_signal_handlers) {
+               common_event_check_signal(std_ev->ev);
+               return 0;
+       }
+
        if (selrtn == -1 && errno == EBADF) {
                /* the socket is dead! this should never
                   happen as the socket should have first been
                   made readable and that should have removed
                   the event, so this must be a bug. This is a
                   fatal error. */
-               DEBUG(0,("ERROR: EBADF on std_event_loop_once\n"));
+               ev_debug(std_ev->ev, EV_DEBUG_FATAL,
+                        "ERROR: EBADF on std_event_loop_once\n");
                std_ev->exit_code = EBADF;
                return -1;
        }
 
        if (selrtn == 0 && tvalp) {
-               std_event_loop_timer(std_ev);
+               /* we don't care about a possible delay here */
+               common_event_loop_timer_delay(std_ev->ev);
                return 0;
        }
 
@@ -577,21 +556,13 @@ static int std_event_loop_once(struct event_context *ev)
                                                           struct std_event_context);
        struct timeval tval;
 
-       /* work out the right timeout for all timed events */
-       if (std_ev->timed_events) {
-               struct timeval t = timeval_current();
-               tval = timeval_until(&t, &std_ev->timed_events->next_event);
-               if (timeval_is_zero(&tval)) {
-                       std_event_loop_timer(std_ev);
-                       return 0;
-               }
-       } else {
-               /* have a default tick time of 30 seconds. This guarantees
-                  that code that uses its own timeout checking will be
-                  able to proceeed eventually */
-               tval = timeval_set(30, 0);
+       tval = common_event_loop_timer_delay(ev);
+       if (ev_timeval_is_zero(&tval)) {
+               return 0;
        }
 
+       epoll_check_reopen(std_ev);
+
        if (epoll_event_loop(std_ev, &tval) == 0) {
                return 0;
        }
@@ -622,12 +593,15 @@ static const struct event_ops std_event_ops = {
        .add_fd         = std_event_add_fd,
        .get_fd_flags   = std_event_get_fd_flags,
        .set_fd_flags   = std_event_set_fd_flags,
-       .add_timed      = std_event_add_timed,
+       .add_timed      = common_event_add_timed,
+       .add_signal     = common_event_add_signal,
        .loop_once      = std_event_loop_once,
        .loop_wait      = std_event_loop_wait,
 };
 
-const struct event_ops *event_standard_get_ops(void)
+
+bool events_standard_init(void)
 {
-       return &std_event_ops;
+       return event_register_backend("standard", &std_event_ops);
 }
+