s3-events: make the old timed events compatible with tevent
[ira/wip.git] / source3 / lib / events.c
index 74f30a709abdd313d9af89ef97d626fa2b3fbedd..75aa250726699ca896fe1953c1a3df8ee1aef566 100644 (file)
@@ -70,6 +70,11 @@ bool event_add_to_select_args(struct tevent_context *ev,
                }
        }
 
+       if (ev->immediate_events != NULL) {
+               *timeout = timeval_zero();
+               return true;
+       }
+
        if (ev->timer_events == NULL) {
                return ret;
        }
@@ -83,45 +88,46 @@ bool event_add_to_select_args(struct tevent_context *ev,
 bool run_events(struct tevent_context *ev,
                int selrtn, fd_set *read_fds, fd_set *write_fds)
 {
-       bool fired = false;
-       struct tevent_fd *fde, *next;
+       struct tevent_fd *fde;
+       struct timeval now;
 
        if (ev->signal_events &&
            tevent_common_check_signal(ev)) {
                return true;
        }
 
-       /* Run all events that are pending, not just one (as we
-          did previously. */
+       if (ev->immediate_events &&
+           tevent_common_loop_immediate(ev)) {
+               return true;
+       }
 
-       while (ev->timer_events) {
-               struct timeval now;
-               GetTimeOfDay(&now);
+       GetTimeOfDay(&now);
 
-               if (timeval_compare(
-                           &now, &ev->timer_events->next_event) < 0) {
-                       /* Nothing to do yet */
-                       DEBUG(11, ("run_events: Nothing to do\n"));
-                       break;
-               }
+       if ((ev->timer_events != NULL)
+           && (timeval_compare(&now, &ev->timer_events->next_event) >= 0)) {
+               /* this older events system did not auto-free timed
+                  events on running them, and had a race condition
+                  where the event could be called twice if the
+                  talloc_free of the te happened after the callback
+                  made a call which invoked the event loop. To avoid
+                  this while still allowing old code which frees the
+                  te, we need to create a temporary context which
+                  will be used to ensure the te is freed. We also
+                  remove the te from the timed event list before we
+                  call the handler, to ensure we can't loop */
 
-               DEBUG(10, ("Running event \"%s\" %p\n",
-                          ev->timer_events->handler_name,
-                          ev->timer_events));
+               struct tevent_timer *te = ev->timer_events;
+               TALLOC_CTX *tmp_ctx = talloc_new(ev);
 
-               ev->timer_events->handler(
-                       ev,
-                       ev->timer_events, now,
-                       ev->timer_events->private_data);
+               DEBUG(10, ("Running timed event \"%s\" %p\n",
+                          ev->timer_events->handler_name, ev->timer_events));
 
-               fired = true;
-       }
+               DLIST_REMOVE(ev->timer_events, te);
+               talloc_steal(tmp_ctx, te);
 
-       if (fired) {
-               /*
-                * We might have changed the socket status during the timed
-                * events, return to run select again.
-                */
+               te->handler(ev, te, now, te->private_data);
+
+               talloc_free(tmp_ctx);
                return true;
        }
 
@@ -129,23 +135,22 @@ bool run_events(struct tevent_context *ev,
                /*
                 * No fd ready
                 */
-               return fired;
+               return false;
        }
 
-       for (fde = ev->fd_events; fde; fde = next) {
+       for (fde = ev->fd_events; fde; 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(ev, fde, flags, fde->private_data);
-                       fired = true;
+                       return true;
                }
        }
 
-       return fired;
+       return false;
 }
 
 
@@ -154,9 +159,13 @@ struct timeval *get_timed_events_timeout(struct tevent_context *ev,
 {
        struct timeval now;
 
-       if (ev->timer_events == NULL) {
+       if ((ev->timer_events == NULL) && (ev->immediate_events == NULL)) {
                return NULL;
        }
+       if (ev->immediate_events != NULL) {
+               *to_ret = timeval_zero();
+               return to_ret;
+       }
 
        now = timeval_current();
        *to_ret = timeval_until(&now, &ev->timer_events->next_event);
@@ -167,7 +176,7 @@ struct timeval *get_timed_events_timeout(struct tevent_context *ev,
        return to_ret;
 }
 
-static int s3_event_loop_once(struct tevent_context *ev)
+static int s3_event_loop_once(struct tevent_context *ev, const char *location)
 {
        struct timeval now, to;
        fd_set r_fds, w_fds;
@@ -180,17 +189,16 @@ static int s3_event_loop_once(struct tevent_context *ev)
        to.tv_sec = 9999;       /* Max timeout */
        to.tv_usec = 0;
 
+       if (run_events(ev, 0, NULL, NULL)) {
+               return 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+1, &r_fds, &w_fds, NULL, &to);
 
        if (ret == -1 && errno != EINTR) {
@@ -204,17 +212,6 @@ static int s3_event_loop_once(struct tevent_context *ev)
        return 0;
 }
 
-static int s3_event_loop_wait(struct tevent_context *ev)
-{
-       int ret = 0;
-
-       while (ret == 0) {
-               ret = s3_event_loop_once(ev);
-       }
-
-       return ret;
-}
-
 void event_context_reinit(struct tevent_context *ev)
 {
        tevent_common_context_destructor(ev);
@@ -261,15 +258,16 @@ void dump_event_list(struct tevent_context *ev)
 }
 
 static const struct tevent_ops s3_event_ops = {
-       .context_init   = s3_event_context_init,
-       .add_fd         = tevent_common_add_fd,
-       .set_fd_close_fn= tevent_common_fd_set_close_fn,
-       .get_fd_flags   = tevent_common_fd_get_flags,
-       .set_fd_flags   = tevent_common_fd_set_flags,
-       .add_timer      = tevent_common_add_timer,
-       .add_signal     = tevent_common_add_signal,
-       .loop_once      = s3_event_loop_once,
-       .loop_wait      = s3_event_loop_wait,
+       .context_init           = s3_event_context_init,
+       .add_fd                 = tevent_common_add_fd,
+       .set_fd_close_fn        = tevent_common_fd_set_close_fn,
+       .get_fd_flags           = tevent_common_fd_get_flags,
+       .set_fd_flags           = tevent_common_fd_set_flags,
+       .add_timer              = tevent_common_add_timer,
+       .schedule_immediate     = tevent_common_schedule_immediate,
+       .add_signal             = tevent_common_add_signal,
+       .loop_once              = s3_event_loop_once,
+       .loop_wait              = tevent_common_loop_wait,
 };
 
 static bool s3_tevent_init(void)
@@ -305,12 +303,13 @@ static void s3_event_debug(void *context, enum tevent_debug_level level,
                samba_level = 2;
                break;
        case TEVENT_DEBUG_TRACE:
-               samba_level = 5;
+               samba_level = 11;
                break;
 
        };
-       vasprintf(&s, fmt, ap);
-       if (!s) return;
+       if (vasprintf(&s, fmt, ap) == -1) {
+               return;
+       }
        DEBUG(samba_level, ("s3_event: %s", s));
        free(s);
 }