lib/events: use mem_context given to event_context_init().
[tprouty/samba.git] / source / lib / events.c
index 6db9930c300685e6b0be442c11a00f24b6f6877b..8134a7ac1c2a9503f4696a5df21a873077f3903a 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,
    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/>.
 */
 
 #include "includes.h"
 
-static struct timed_event *timed_events;
+struct timed_event {
+       struct timed_event *next, *prev;
+       struct event_context *event_ctx;
+       struct timeval when;
+       const char *event_name;
+       void (*handler)(struct event_context *event_ctx,
+                       struct timed_event *te,
+                       const struct timeval *now,
+                       void *private_data);
+       void *private_data;
+};
+
+struct fd_event {
+       struct fd_event *prev, *next;
+       struct event_context *event_ctx;
+       int fd;
+       uint16_t flags; /* see EVENT_FD_* flags */
+       void (*handler)(struct event_context *event_ctx,
+                       struct fd_event *event,
+                       uint16 flags,
+                       void *private_data);
+       void *private_data;
+};
+
+#define EVENT_FD_WRITEABLE(fde) \
+       event_set_fd_flags(fde, event_get_fd_flags(fde) | EVENT_FD_WRITE)
+#define EVENT_FD_READABLE(fde) \
+       event_set_fd_flags(fde, event_get_fd_flags(fde) | EVENT_FD_READ)
+
+#define EVENT_FD_NOT_WRITEABLE(fde) \
+       event_set_fd_flags(fde, event_get_fd_flags(fde) & ~EVENT_FD_WRITE)
+#define EVENT_FD_NOT_READABLE(fde) \
+       event_set_fd_flags(fde, event_get_fd_flags(fde) & ~EVENT_FD_READ)
+
+struct event_context {
+       struct timed_event *timed_events;
+       struct fd_event *fd_events;
+};
 
 static int timed_event_destructor(struct timed_event *te)
 {
        DEBUG(10, ("Destroying timed event %lx \"%s\"\n", (unsigned long)te,
                te->event_name));
-       DLIST_REMOVE(timed_events, te);
+       DLIST_REMOVE(te->event_ctx->timed_events, te);
        return 0;
 }
 
+/****************************************************************************
+ Add te by time.
+****************************************************************************/
+
+static void add_event_by_time(struct timed_event *te)
+{
+       struct event_context *ctx = te->event_ctx;
+       struct timed_event *last_te, *cur_te;
+
+       /* Keep the list ordered by time. We must preserve this. */
+       last_te = NULL;
+       for (cur_te = ctx->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->when) &&
+                               timeval_compare(&te->when, &cur_te->when) < 0) {
+                       break;
+               }
+               last_te = cur_te;
+       }
+
+       DLIST_ADD_AFTER(ctx->timed_events, te, last_te);
+}
+
 /****************************************************************************
  Schedule a function for future calling, cancel with TALLOC_FREE().
  It's the responsibility of the handler to call TALLOC_FREE() on the event
  handed to it.
 ****************************************************************************/
 
-struct timed_event *add_timed_event(TALLOC_CTX *mem_ctx,
+struct timed_event *event_add_timed(struct event_context *event_ctx,
+                               TALLOC_CTX *mem_ctx,
                                struct timeval when,
                                const char *event_name,
-                               void (*handler)(struct timed_event *te,
+                               void (*handler)(struct event_context *event_ctx,
+                                               struct timed_event *te,
                                                const struct timeval *now,
                                                void *private_data),
                                void *private_data)
 {
-       struct timed_event *te, *last_te, *cur_te;
+       struct timed_event *te;
 
        te = TALLOC_P(mem_ctx, struct timed_event);
        if (te == NULL) {
@@ -53,23 +114,14 @@ struct timed_event *add_timed_event(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
+       te->event_ctx = event_ctx;
        te->when = when;
        te->event_name = event_name;
        te->handler = handler;
        te->private_data = private_data;
 
-       /* keep the list ordered */
-       last_te = NULL;
-       for (cur_te = 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->when) &&
-                               timeval_compare(&te->when, &cur_te->when) < 0) {
-                       break;
-               }
-               last_te = cur_te;
-       }
+       add_event_by_time(te);
 
-       DLIST_ADD_AFTER(timed_events, te, last_te);
        talloc_set_destructor(te, timed_event_destructor);
 
        DEBUG(10, ("Added timed event \"%s\": %lx\n", event_name,
@@ -77,44 +129,298 @@ struct timed_event *add_timed_event(TALLOC_CTX *mem_ctx,
        return te;
 }
 
-void run_events(void)
+static int fd_event_destructor(struct fd_event *fde)
 {
-       struct timeval now;
+       struct event_context *event_ctx = fde->event_ctx;
 
-       if (timed_events == NULL) {
-               /* No syscall if there are no events */
-               DEBUG(11, ("run_events: No events\n"));
-               return;
+       DLIST_REMOVE(event_ctx->fd_events, fde);
+       return 0;
+}
+
+struct fd_event *event_add_fd(struct event_context *event_ctx,
+                             TALLOC_CTX *mem_ctx,
+                             int fd, uint16_t flags,
+                             void (*handler)(struct event_context *event_ctx,
+                                             struct fd_event *event,
+                                             uint16 flags,
+                                             void *private_data),
+                             void *private_data)
+{
+       struct fd_event *fde;
+
+       if (!(fde = TALLOC_P(mem_ctx, struct fd_event))) {
+               return NULL;
        }
 
-       GetTimeOfDay(&now);
+       fde->event_ctx = event_ctx;
+       fde->fd = fd;
+       fde->flags = flags;
+       fde->handler = handler;
+       fde->private_data = private_data;
 
-       if (timeval_compare(&now, &timed_events->when) < 0) {
-               /* Nothing to do yet */
-               DEBUG(11, ("run_events: Nothing to do\n"));
-               return;
+       DLIST_ADD(event_ctx->fd_events, fde);
+
+       talloc_set_destructor(fde, fd_event_destructor);
+       return fde;
+}
+
+void event_fd_set_writeable(struct fd_event *fde)
+{
+       fde->flags |= EVENT_FD_WRITE;
+}
+
+void event_fd_set_not_writeable(struct fd_event *fde)
+{
+       fde->flags &= ~EVENT_FD_WRITE;
+}
+
+void event_fd_set_readable(struct fd_event *fde)
+{
+       fde->flags |= EVENT_FD_READ;
+}
+
+void event_fd_set_not_readable(struct fd_event *fde)
+{
+       fde->flags &= ~EVENT_FD_READ;
+}
+
+/*
+ * Return if there's something in the queue
+ */
+
+bool event_add_to_select_args(struct event_context *event_ctx,
+                             const struct timeval *now,
+                             fd_set *read_fds, fd_set *write_fds,
+                             struct timeval *timeout, int *maxfd)
+{
+       struct fd_event *fde;
+       struct timeval diff;
+       bool ret = False;
+
+       for (fde = event_ctx->fd_events; fde; fde = fde->next) {
+               if (fde->flags & EVENT_FD_READ) {
+                       FD_SET(fde->fd, read_fds);
+                       ret = True;
+               }
+               if (fde->flags & EVENT_FD_WRITE) {
+                       FD_SET(fde->fd, write_fds);
+                       ret = True;
+               }
+
+               if ((fde->flags & (EVENT_FD_READ|EVENT_FD_WRITE))
+                   && (fde->fd > *maxfd)) {
+                       *maxfd = fde->fd;
+               }
+       }
+
+       if (event_ctx->timed_events == NULL) {
+               return ret;
        }
 
-       DEBUG(10, ("Running event \"%s\" %lx\n", timed_events->event_name,
-               (unsigned long)timed_events));
+       diff = timeval_until(now, &event_ctx->timed_events->when);
+       *timeout = timeval_min(timeout, &diff);
 
-       timed_events->handler(timed_events, &now, timed_events->private_data);
-       return;
+       return True;
 }
 
-struct timeval *get_timed_events_timeout(struct timeval *to_ret)
+bool events_pending(struct event_context *event_ctx)
+{
+       struct fd_event *fde;
+
+       if (event_ctx->timed_events != NULL) {
+               return True;
+       }
+       for (fde = event_ctx->fd_events; fde; fde = fde->next) {
+               if (fde->flags & (EVENT_FD_READ|EVENT_FD_WRITE)) {
+                       return True;
+               }
+       }
+       return False;
+}
+
+bool run_events(struct event_context *event_ctx,
+               int selrtn, fd_set *read_fds, fd_set *write_fds)
+{
+       bool fired = False;
+       struct fd_event *fde, *next;
+
+       /* Run all events that are pending, not just one (as we
+          did previously. */
+
+       while (event_ctx->timed_events) {
+               struct timeval now;
+               GetTimeOfDay(&now);
+
+               if (timeval_compare(
+                           &now, &event_ctx->timed_events->when) < 0) {
+                       /* Nothing to do yet */
+                       DEBUG(11, ("run_events: Nothing to do\n"));
+                       break;
+               }
+
+               DEBUG(10, ("Running event \"%s\" %lx\n",
+                          event_ctx->timed_events->event_name,
+                          (unsigned long)event_ctx->timed_events));
+
+               event_ctx->timed_events->handler(
+                       event_ctx,
+                       event_ctx->timed_events, &now,
+                       event_ctx->timed_events->private_data);
+
+               fired = True;
+       }
+
+       if (fired) {
+               /*
+                * We might have changed the socket status during the timed
+                * events, return to run select again.
+                */
+               return True;
+       }
+
+       if (selrtn == 0) {
+               /*
+                * No fd ready
+                */
+               return fired;
+       }
+
+       for (fde = event_ctx->fd_events; fde; fde = next) {
+               uint16 flags = 0;
+
+               next = fde->next;
+               if (FD_ISSET(fde->fd, read_fds)) flags |= EVENT_FD_READ;
+               if (FD_ISSET(fde->fd, write_fds)) flags |= EVENT_FD_WRITE;
+
+               if (flags & fde->flags) {
+                       fde->handler(event_ctx, fde, flags, fde->private_data);
+                       fired = True;
+               }
+       }
+
+       return fired;
+}
+
+
+struct timeval *get_timed_events_timeout(struct event_context *event_ctx,
+                                        struct timeval *to_ret)
 {
        struct timeval now;
 
-       if (timed_events == NULL) {
+       if (event_ctx->timed_events == NULL) {
                return NULL;
        }
 
        now = timeval_current();
-       *to_ret = timeval_until(&now, &timed_events->when);
+       *to_ret = timeval_until(&now, &event_ctx->timed_events->when);
 
        DEBUG(10, ("timed_events_timeout: %d/%d\n", (int)to_ret->tv_sec,
                (int)to_ret->tv_usec));
 
        return to_ret;
 }
+
+int event_loop_once(struct event_context *ev)
+{
+       struct timeval now, to;
+       fd_set r_fds, w_fds;
+       int maxfd = 0;
+       int ret;
+
+       FD_ZERO(&r_fds);
+       FD_ZERO(&w_fds);
+
+       to.tv_sec = 9999;       /* Max timeout */
+       to.tv_usec = 0;
+
+       GetTimeOfDay(&now);
+
+       if (!event_add_to_select_args(ev, &now, &r_fds, &w_fds, &to, &maxfd)) {
+               return -1;
+       }
+
+       if (timeval_is_zero(&to)) {
+               run_events(ev, 0, NULL, NULL);
+               return 0;
+       }
+
+       ret = sys_select(maxfd, &r_fds, &w_fds, NULL, &to);
+
+       if (ret == -1 && errno != EINTR) {
+               return -1;
+       }
+
+       run_events(ev, ret, &r_fds, &w_fds);
+       return 0;
+}
+
+struct event_context *event_context_init(TALLOC_CTX *mem_ctx)
+{
+       return TALLOC_ZERO_P(mem_ctx, struct event_context);
+}
+
+int set_event_dispatch_time(struct event_context *event_ctx,
+                           const char *event_name, struct timeval when)
+{
+       struct timed_event *te;
+
+       for (te = event_ctx->timed_events; te; te = te->next) {
+               if (strcmp(event_name, te->event_name) == 0) {
+                       DLIST_REMOVE(event_ctx->timed_events, te);
+                       te->when = when;
+                       add_event_by_time(te);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/* Returns 1 if event was found and cancelled, 0 otherwise. */
+
+int cancel_named_event(struct event_context *event_ctx,
+                      const char *event_name)
+{
+       struct timed_event *te;
+
+       for (te = event_ctx->timed_events; te; te = te->next) {
+               if (strcmp(event_name, te->event_name) == 0) {
+                       TALLOC_FREE(te);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+void dump_event_list(struct event_context *event_ctx)
+{
+       struct timed_event *te;
+       struct fd_event *fe;
+       struct timeval evt, now;
+
+       if (!event_ctx) {
+               return;
+       }
+
+       now = timeval_current();
+
+       DEBUG(10,("dump_event_list:\n"));
+
+       for (te = event_ctx->timed_events; te; te = te->next) {
+
+               evt = timeval_until(&now, &te->when);
+
+               DEBUGADD(10,("Timed Event \"%s\" %lx handled in %d seconds (at %s)\n",
+                          te->event_name,
+                          (unsigned long)te,
+                          (int)evt.tv_sec,
+                          http_timestring(te->when.tv_sec)));
+       }
+
+       for (fe = event_ctx->fd_events; fe; fe = fe->next) {
+
+               DEBUGADD(10,("FD Event %d %lx, flags: 0x%04x\n",
+                          fe->fd,
+                          (unsigned long)fe,
+                          fe->flags));
+       }
+}