r12123: handle fde == NULL at the correct level
[bbaumbach/samba-autobuild/.git] / source4 / lib / events / events.c
index 64cf8f41bfc15bbc36dabd200461721eb859378a..7aa8d680fcbf6f036ac7def9077e9cc122c93adb 100644 (file)
   After that you can add/remove events that you are interested in
   using event_add_*() and talloc_free()
 
-  Finally, you call event_loop_wait() to block waiting for one of the
-  events to occor. In normal operation event_loop_wait() will loop
+  Finally, you call event_loop_wait_once() to block waiting for one of the
+  events to occor or event_loop_wait() which will loop
   forever.
 
 */
 
 #include "includes.h"
-#include "system/time.h"
-#include "system/select.h"
-#include "dlinklist.h"
 #include "lib/events/events.h"
-
-/* use epoll if it is available */
-#if defined(HAVE_EPOLL_CREATE) && defined(HAVE_SYS_EPOLL_H)
-#define WITH_EPOLL 1
-#endif
-
-#if WITH_EPOLL
-/* this #undef is needed on my SuSE 9.2 Box with glibc-devel-2.3.3-118 */
-#undef u32
-#include <sys/epoll.h>
-#endif
-
-struct event_context { 
-       /* list of filedescriptor events */
-       struct fd_event {
-               struct event_context *event_ctx;
-               struct fd_event *next, *prev;
-               int fd;
-               uint16_t flags; /* see EVENT_FD_* flags */
-               event_fd_handler_t handler;
-               void *private;
-       } *fd_events;
-
-       /* list of timed events */
-       struct timed_event {
-               struct event_context *event_ctx;
-               struct timed_event *next, *prev;
-               struct timeval next_event;
-               event_timed_handler_t handler;
-               void *private;
-       } *timed_events;
-
-       /* the maximum file descriptor number in fd_events */
-       int maxfd;
-
-       /* information for exiting from the event loop */
-       int exit_code;
-
-       /* this is changed by the destructors for the fd event
-          type. It is used to detect event destruction by event
-          handlers, which means the code that is calling the event
-          handler needs to assume that the linked list is no longer
-          valid
-       */
-       uint32_t destruction_count;
-
-#if WITH_EPOLL
-       /* when using epoll this is the handle from epoll_create */
-       int epoll_fd;
-#endif
-};
-
+#include "lib/events/events_internal.h"
 
 /*
-  destroy an event context
-*/
-static int event_context_destructor(void *ptr)
-{
-#if WITH_EPOLL
-       struct event_context *ev = talloc_get_type(ptr, struct event_context);
-       if (ev->epoll_fd != -1) {
-               close(ev->epoll_fd);
-               ev->epoll_fd = -1;
-       }
-#endif
-       return 0;
-}
+  create a event_context structure for a specific implemementation.
+  This must be the first events call, and all subsequent calls pass
+  this event_context as the first element. Event handlers also
+  receive this as their first argument.
 
-/*
-  create a event_context structure. This must be the first events
-  call, and all subsequent calls pass this event_context as the first
-  element. Event handlers also receive this as their first argument.
+  This function is for allowing third-party-applications to hook in gluecode
+  to their own event loop code, so that they can make async usage of our client libs
+
+  NOTE: use event_context_init() inside of samba!
 */
-struct event_context *event_context_init(TALLOC_CTX *mem_ctx)
+struct event_context *event_context_init_ops(TALLOC_CTX *mem_ctx, const struct event_ops *ops, void *private_data)
 {
        struct event_context *ev;
+       int ret;
 
        ev = talloc_zero(mem_ctx, struct event_context);
        if (!ev) return NULL;
 
-#if WITH_EPOLL
-       ev->epoll_fd = epoll_create(64);
-#endif
-
-       talloc_set_destructor(ev, event_context_destructor);
-
-       return ev;
-}
-
+       ev->ops = ops;
 
-/*
-  recalculate the maxfd
-*/
-static void calc_maxfd(struct event_context *ev)
-{
-       struct fd_event *e;
-       ev->maxfd = 0;
-       for (e=ev->fd_events; e; e=e->next) {
-               if (e->fd > ev->maxfd) {
-                       ev->maxfd = e->fd;
-               }
+       ret = ev->ops->context_init(ev, private_data);
+       if (ret != 0) {
+               talloc_free(ev);
+               return NULL;
        }
-}
-
-
-/* to mark the ev->maxfd invalid
- * this means we need to recalculate it
- */
-#define EVENT_INVALID_MAXFD (-1)
-
 
-#if WITH_EPOLL
-/*
-  called when a epoll call fails, and we should fallback
-  to using select
-*/
-static void epoll_fallback_to_select(struct event_context *ev, const char *reason)
-{
-       DEBUG(0,("%s (%s) - falling back to select()\n", reason, strerror(errno)));
-       close(ev->epoll_fd);
-       ev->epoll_fd = -1;
-}
-#endif
-
-
-#if WITH_EPOLL
-/*
-  map from EVENT_FD_* to EPOLLIN/EPOLLOUT
-*/
-static uint32_t epoll_map_flags(uint16_t flags)
-{
-       uint32_t ret = 0;
-       if (flags & EVENT_FD_READ) ret |= EPOLLIN;
-       if (flags & EVENT_FD_WRITE) ret |= EPOLLOUT;
-       return ret;
+       return ev;
 }
-#endif
 
 /*
-  destroy an fd_event
+  create a event_context structure. This must be the first events
+  call, and all subsequent calls pass this event_context as the first
+  element. Event handlers also receive this as their first argument.
 */
-static int event_fd_destructor(void *ptr)
+struct event_context *event_context_init(TALLOC_CTX *mem_ctx)
 {
-       struct fd_event *fde = talloc_get_type(ptr, struct fd_event);
-       struct event_context *ev = fde->event_ctx;
-
-       if (ev->maxfd == fde->fd) {
-               ev->maxfd = EVENT_INVALID_MAXFD;
-       }
-       DLIST_REMOVE(ev->fd_events, fde);
-       ev->destruction_count++;
-#if WITH_EPOLL
-       if (ev->epoll_fd != -1) {
-               struct epoll_event event;
-               ZERO_STRUCT(event);
-               event.events = epoll_map_flags(fde->flags);
-               event.data.ptr = fde;
-               epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, fde->fd, &event);
-       }
-#endif
-       return 0;
+       const struct event_ops *ops = event_standard_get_ops();
+       return event_context_init_ops(mem_ctx, ops, NULL);
 }
 
 /*
@@ -228,313 +105,71 @@ static int event_fd_destructor(void *ptr)
 */
 struct fd_event *event_add_fd(struct event_context *ev, TALLOC_CTX *mem_ctx,
                              int fd, uint16_t flags, event_fd_handler_t handler,
-                             void *private)
+                             void *private_data)
 {
-       struct fd_event *e = talloc(ev, struct fd_event);
-       if (!e) return NULL;
-
-       e->event_ctx = ev;
-       e->fd        = fd;
-       e->flags     = flags;
-       e->handler   = handler;
-       e->private   = private;
-
-       DLIST_ADD(ev->fd_events, e);
-
-       if (e->fd > ev->maxfd) {
-               ev->maxfd = e->fd;
-       }
-
-       talloc_set_destructor(e, event_fd_destructor);
-       if (mem_ctx) {
-               talloc_steal(mem_ctx, e);
-       }
-
-#if WITH_EPOLL
-       if (ev->epoll_fd != -1) {
-               struct epoll_event event;
-               ZERO_STRUCT(event);
-               event.events = epoll_map_flags(flags);
-               event.data.ptr = e;
-               if (epoll_ctl(ev->epoll_fd, EPOLL_CTL_ADD, e->fd, &event) != 0) {
-                       epoll_fallback_to_select(ev, "EPOLL_CTL_ADD failed");
-               }
-       }
-#endif
-
-       return e;
+       return ev->ops->add_fd(ev, mem_ctx, fd, flags, handler, private_data);
 }
 
-
 /*
   return the fd event flags
 */
-uint16_t event_fd_flags(struct fd_event *fde)
+uint16_t event_get_fd_flags(struct fd_event *fde)
 {
-       return fde?fde->flags:0;
+       if (!fde) return 0;
+       return fde->event_ctx->ops->get_fd_flags(fde);
 }
 
 /*
   set the fd event flags
 */
-void event_fd_setflags(struct fd_event *fde, uint16_t flags)
-{
-#if WITH_EPOLL
-       struct event_context *ev;
-       if (fde == NULL || 
-           fde->flags == flags) {
-               return;
-       }
-       ev = fde->event_ctx;
-       if (ev->epoll_fd != -1) {
-               struct epoll_event event;
-               ZERO_STRUCT(event);
-               event.events = epoll_map_flags(flags);
-               event.data.ptr = fde;
-               if (epoll_ctl(ev->epoll_fd, EPOLL_CTL_MOD, fde->fd, &event) != 0) {
-                       epoll_fallback_to_select(ev, "EPOLL_CTL_MOD failed");
-               }
-       }
-#endif
-       if (fde) {
-               fde->flags = flags;
-       }
-}
-
-/*
-  destroy a timed event
-*/
-static int event_timed_destructor(void *ptr)
+void event_set_fd_flags(struct fd_event *fde, uint16_t flags)
 {
-       struct timed_event *te = talloc_get_type(ptr, struct timed_event);
-       DLIST_REMOVE(te->event_ctx->timed_events, te);
-       return 0;
+       if (!fde) return;
+       fde->event_ctx->ops->set_fd_flags(fde, flags);
 }
 
 /*
   add a timed event
-  return NULL on failure (memory allocation error)
+  return NULL on failure
 */
 struct timed_event *event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ctx,
                                    struct timeval next_event, 
                                    event_timed_handler_t handler, 
-                                   void *private
+                                   void *private_data)
 {
-       struct timed_event *te, *e;
-
-       e = talloc(mem_ctx?mem_ctx:ev, struct timed_event);
-       if (e == NULL) return NULL;
-
-       e->event_ctx  = ev;
-       e->next_event = next_event;
-       e->handler    = handler;
-       e->private    = private;
-
-       /* keep the list ordered */
-       if (ev->timed_events == NULL || 
-           timeval_compare(&e->next_event, &ev->timed_events->next_event) > 0) {
-               DLIST_ADD(ev->timed_events, e);
-       } else {
-               for (te=ev->timed_events;te && te->next;te=te->next) {
-                       if (!timeval_is_zero(&te->next->next_event) &&
-                           timeval_compare(&te->next->next_event, &e->next_event) < 0) {
-                               break;
-                       }
-               }
-               DLIST_ADD_AFTER(ev->timed_events, e, te);
-       }
-
-       talloc_set_destructor(e, event_timed_destructor);
-
-       return e;
+       return ev->ops->add_timed(ev, mem_ctx, next_event, handler, private_data);
 }
 
 /*
-  a timer has gone off - call it
+  do a single event loop using the events defined in ev 
 */
-static void event_loop_timer(struct event_context *ev)
+int event_loop_once(struct event_context *ev)
 {
-       struct timeval t = timeval_current();
-       struct timed_event *te = ev->timed_events;
-
-       te->next_event = timeval_zero();
-
-       te->handler(ev, te, t, te->private);
-
-       /* note the care taken to prevent referencing a event
-          that could have been freed by the handler */
-       if (ev->timed_events && timeval_is_zero(&ev->timed_events->next_event)) {
-               talloc_free(ev->timed_events);
-       }
+       return ev->ops->loop_once(ev);
 }
 
-#if WITH_EPOLL
 /*
-  event loop handling using epoll
+  return on failure or (with 0) if all fd events are removed
 */
-static int event_loop_epoll(struct event_context *ev, struct timeval *tvalp)
-{
-       int ret, i;
-       const int maxevents = 8;
-       struct epoll_event events[maxevents];
-       uint32_t destruction_count = ev->destruction_count;
-       int timeout = -1;
-
-       if (tvalp) {
-               timeout = (tvalp->tv_usec / 1000) + (tvalp->tv_sec*1000);
-       }
-
-       ret = epoll_wait(ev->epoll_fd, events, maxevents, timeout);
-
-       if (ret == -1 && errno != EINTR) {
-               epoll_fallback_to_select(ev, "epoll_wait() failed");
-               return -1;
-       }
-
-       if (ret == 0 && tvalp) {
-               event_loop_timer(ev);
-               return 0;
-       }
-
-       for (i=0;i<ret;i++) {
-               struct fd_event *fde = talloc_get_type(events[i].data.ptr, 
-                                                      struct fd_event);
-               uint16_t flags = 0;
-
-               if (fde == NULL) {
-                       epoll_fallback_to_select(ev, "epoll_wait() gave bad data");
-                       return -1;
-               }
-               if (events[i].events & EPOLLIN) flags |= EVENT_FD_READ;
-               if (events[i].events & EPOLLOUT) flags |= EVENT_FD_WRITE;
-               if (flags) {
-                       fde->handler(ev, fde, flags, fde->private);
-                       if (destruction_count != ev->destruction_count) {
-                               break;
-                       }
-               }
-       }
-
-       return 0;
-}              
-#endif
-
-/*
-  event loop handling using select()
-*/
-static int event_loop_select(struct event_context *ev, struct timeval *tvalp)
-{
-       fd_set r_fds, w_fds;
-       int selrtn;
-       uint32_t destruction_count = ev->destruction_count;
-       struct fd_event *fe;
-
-       /* we maybe need to recalculate the maxfd */
-       if (ev->maxfd == EVENT_INVALID_MAXFD) {
-               calc_maxfd(ev);
-       }
-               
-       FD_ZERO(&r_fds);
-       FD_ZERO(&w_fds);
-
-       /* setup any fd events */
-       for (fe=ev->fd_events; fe; ) {
-               struct fd_event *next = fe->next;
-               if (fe->flags & EVENT_FD_READ) {
-                       FD_SET(fe->fd, &r_fds);
-               }
-               if (fe->flags & EVENT_FD_WRITE) {
-                       FD_SET(fe->fd, &w_fds);
-               }
-               fe = next;
-       }
-
-       selrtn = select(ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp);
-               
-       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 event_loop_once\n"));
-               ev->exit_code = EBADF;
-               return -1;
-       }
-
-       if (selrtn == 0 && tvalp) {
-               event_loop_timer(ev);
-               return 0;
-       }
-
-       if (selrtn > 0) {
-               /* at least one file descriptor is ready - check
-                  which ones and call the handler, being careful to allow
-                  the handler to remove itself when called */
-               for (fe=ev->fd_events; fe; fe=fe->next) {
-                       uint16_t flags = 0;
-                       if (FD_ISSET(fe->fd, &r_fds)) flags |= EVENT_FD_READ;
-                       if (FD_ISSET(fe->fd, &w_fds)) flags |= EVENT_FD_WRITE;
-                       if (flags) {
-                               fe->handler(ev, fe, flags, fe->private);
-                               if (destruction_count != ev->destruction_count) {
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       return 0;
-}              
-
-/*
-  do a single event loop using the events defined in ev 
-*/
-int event_loop_once(struct event_context *ev)
+int event_loop_wait(struct event_context *ev)
 {
-       struct timeval tval, *tvalp;
-
-       tvalp = NULL;
-               
-       /* work out the right timeout for all timed events */
-       if (ev->timed_events) {
-               struct timeval t = timeval_current();
-               tval = timeval_diff(&ev->timed_events->next_event, &t);
-               tvalp = &tval;
-               if (timeval_is_zero(tvalp)) {
-                       event_loop_timer(ev);
-                       return 0;
-               }
-       }
-
-#if WITH_EPOLL
-       if (ev->epoll_fd != -1) {
-               if (event_loop_epoll(ev, tvalp) == 0) {
-                       return 0;
-               }
-       }
-#endif
-
-       return event_loop_select(ev, tvalp);
+       return ev->ops->loop_wait(ev);
 }
 
 /*
-  go into an event loop using the events defined in ev this function
-  will return with the specified code if one of the handlers calls
-  event_loop_exit()
+  find an event context that is a parent of the given memory context,
+  or create a new event context as a child of the given context if
+  none is found
 
-  also return (with code 0) if all fd events are removed
+  This should be used in preference to event_context_init() in places
+  where you would prefer to use the existing event context if possible
+  (which is most situations)
 */
-int event_loop_wait(struct event_context *ev)
+struct event_context *event_context_find(TALLOC_CTX *mem_ctx)
 {
-       ev->exit_code = 0;
-       ev->maxfd = EVENT_INVALID_MAXFD;
-
-       while (ev->fd_events && ev->exit_code == 0) {
-               if (event_loop_once(ev) != 0) {
-                       break;
-               }
+       struct event_context *ev = talloc_find_parent_bytype(mem_ctx, struct event_context);
+       if (ev == NULL) {
+               ev = event_context_init(mem_ctx);
        }
-
-       return ev->exit_code;
+       return ev;
 }