talloc: use the system pytalloc-util for python3 as well
[sfrench/samba-autobuild/.git] / lib / tevent / tevent_poll.c
index cda028a964bf8c723eb211571af9f1762eac18d9..09d85fa322adff298eb6a1f5e7c72bccd6121881 100644 (file)
 #include "tevent_internal.h"
 
 struct poll_event_context {
+       /* a pointer back to the generic event_context */
+       struct tevent_context *ev;
+
+       /*
+        * A DLIST for fresh fde's added by poll_event_add_fd but not
+        * picked up yet by poll_event_loop_once
+        */
+       struct tevent_fd *fresh;
+       /*
+        * A DLIST for disabled fde's.
+        */
+       struct tevent_fd *disabled;
+       /*
+        * one or more events were deleted or disabled
+        */
+       bool deleted;
+
        /*
         * These two arrays are maintained together.
         */
        struct pollfd *fds;
-       struct tevent_fd **fd_events;
-       int num_fds;
+       struct tevent_fd **fdes;
+       unsigned num_fds;
 
-       /* information for exiting from the event loop */
-       int exit_code;
+       /*
+        * Signal fd to wake the poll() thread
+        */
+       int signal_fd;
 };
 
+static int poll_event_context_destructor(struct poll_event_context *poll_ev)
+{
+       struct tevent_fd *fd, *fn;
+
+       for (fd = poll_ev->fresh; fd; fd = fn) {
+               fn = fd->next;
+               fd->event_ctx = NULL;
+               DLIST_REMOVE(poll_ev->fresh, fd);
+       }
+
+       for (fd = poll_ev->disabled; fd; fd = fn) {
+               fn = fd->next;
+               fd->event_ctx = NULL;
+               DLIST_REMOVE(poll_ev->disabled, fd);
+       }
+
+       if (poll_ev->signal_fd == -1) {
+               /*
+                * Non-threaded, no signal pipe
+                */
+               return 0;
+       }
+
+       close(poll_ev->signal_fd);
+       poll_ev->signal_fd = -1;
+
+       if (poll_ev->num_fds == 0) {
+               return 0;
+       }
+       if (poll_ev->fds[0].fd != -1) {
+               close(poll_ev->fds[0].fd);
+               poll_ev->fds[0].fd = -1;
+       }
+       return 0;
+}
+
 /*
-  create a select_event_context structure.
+  create a poll_event_context structure.
 */
 static int poll_event_context_init(struct tevent_context *ev)
 {
        struct poll_event_context *poll_ev;
 
+       /*
+        * we might be called during tevent_re_initialise()
+        * which means we need to free our old additional_data
+        * in order to detach old fd events from the
+        * poll_ev->fresh list
+        */
+       TALLOC_FREE(ev->additional_data);
+
        poll_ev = talloc_zero(ev, struct poll_event_context);
        if (poll_ev == NULL) {
                return -1;
        }
+       poll_ev->ev = ev;
+       poll_ev->signal_fd = -1;
        ev->additional_data = poll_ev;
+       talloc_set_destructor(poll_ev, poll_event_context_destructor);
+       return 0;
+}
+
+static bool set_nonblock(int fd)
+{
+       int val;
+
+       val = fcntl(fd, F_GETFL, 0);
+       if (val == -1) {
+               return false;
+       }
+       val |= O_NONBLOCK;
+
+       return (fcntl(fd, F_SETFL, val) != -1);
+}
+
+static int poll_event_context_init_mt(struct tevent_context *ev)
+{
+       struct poll_event_context *poll_ev;
+       struct pollfd *pfd;
+       int fds[2];
+       int ret;
+
+       ret = poll_event_context_init(ev);
+       if (ret == -1) {
+               return ret;
+       }
+
+       poll_ev = talloc_get_type_abort(
+               ev->additional_data, struct poll_event_context);
+
+       poll_ev->fds = talloc_zero(poll_ev, struct pollfd);
+       if (poll_ev->fds == NULL) {
+               return -1;
+       }
+
+       ret = pipe(fds);
+       if (ret == -1) {
+               return -1;
+       }
+
+       if (!set_nonblock(fds[0]) || !set_nonblock(fds[1])) {
+               close(fds[0]);
+               close(fds[1]);
+               return -1;
+       }
+
+       poll_ev->signal_fd = fds[1];
+
+       pfd = &poll_ev->fds[0];
+       pfd->fd = fds[0];
+       pfd->events = (POLLIN|POLLHUP);
+
+       poll_ev->num_fds = 1;
+
+       talloc_set_destructor(poll_ev, poll_event_context_destructor);
+
        return 0;
 }
 
+static void poll_event_wake_pollthread(struct poll_event_context *poll_ev)
+{
+       char c;
+       ssize_t ret;
+
+       if (poll_ev->signal_fd == -1) {
+               return;
+       }
+       c = 0;
+       do {
+               ret = write(poll_ev->signal_fd, &c, sizeof(c));
+       } while ((ret == -1) && (errno == EINTR));
+}
+
+static void poll_event_drain_signal_fd(struct poll_event_context *poll_ev)
+{
+       char buf[16];
+       ssize_t ret;
+       int fd;
+
+       if (poll_ev->signal_fd == -1) {
+               return;
+       }
+
+       if (poll_ev->num_fds < 1) {
+               return;
+       }
+       fd = poll_ev->fds[0].fd;
+
+       do {
+               ret = read(fd, buf, sizeof(buf));
+       } while (ret == sizeof(buf));
+}
+
 /*
   destroy an fd_event
 */
 static int poll_event_fd_destructor(struct tevent_fd *fde)
 {
        struct tevent_context *ev = fde->event_ctx;
-       struct poll_event_context *poll_ev = NULL;
-       struct tevent_fd *moved_fde;
-       long del_idx;
+       struct poll_event_context *poll_ev;
+       uint64_t del_idx = fde->additional_flags;
 
        if (ev == NULL) {
                goto done;
@@ -73,21 +229,60 @@ static int poll_event_fd_destructor(struct tevent_fd *fde)
        poll_ev = talloc_get_type_abort(
                ev->additional_data, struct poll_event_context);
 
-       /*
-        * Assume a void * can carry enough bits to hold num_fds.
-        */
-       del_idx = (long)(fde->additional_data);
+       if (del_idx == UINT64_MAX) {
+               struct tevent_fd **listp =
+                       (struct tevent_fd **)fde->additional_data;
 
-       moved_fde = poll_ev->fd_events[poll_ev->num_fds-1];
-       poll_ev->fd_events[del_idx] = moved_fde;
-       poll_ev->fds[del_idx] = poll_ev->fds[poll_ev->num_fds-1];
-       moved_fde->additional_data = (void *)del_idx;
+               DLIST_REMOVE((*listp), fde);
+               goto done;
+       }
 
-       poll_ev->num_fds -= 1;
+       poll_ev->fdes[del_idx] = NULL;
+       poll_ev->deleted = true;
+       poll_event_wake_pollthread(poll_ev);
 done:
        return tevent_common_fd_destructor(fde);
 }
 
+static void poll_event_schedule_immediate(struct tevent_immediate *im,
+                                         struct tevent_context *ev,
+                                         tevent_immediate_handler_t handler,
+                                         void *private_data,
+                                         const char *handler_name,
+                                         const char *location)
+{
+       struct poll_event_context *poll_ev = talloc_get_type_abort(
+               ev->additional_data, struct poll_event_context);
+
+       tevent_common_schedule_immediate(im, ev, handler, private_data,
+                                        handler_name, location);
+       poll_event_wake_pollthread(poll_ev);
+}
+
+/*
+  Private function called by "standard" backend fallback.
+  Note this only allows fallback to "poll" backend, not "poll-mt".
+*/
+_PRIVATE_ void tevent_poll_event_add_fd_internal(struct tevent_context *ev,
+                                                struct tevent_fd *fde)
+{
+       struct poll_event_context *poll_ev = talloc_get_type_abort(
+               ev->additional_data, struct poll_event_context);
+       struct tevent_fd **listp;
+
+       if (fde->flags != 0) {
+               listp = &poll_ev->fresh;
+       } else {
+               listp = &poll_ev->disabled;
+       }
+
+       fde->additional_flags   = UINT64_MAX;
+       fde->additional_data    = listp;
+
+       DLIST_ADD((*listp), fde);
+       talloc_set_destructor(fde, poll_event_fd_destructor);
+}
+
 /*
   add a fd based event
   return NULL on failure (memory allocation error)
@@ -102,63 +297,34 @@ static struct tevent_fd *poll_event_add_fd(struct tevent_context *ev,
 {
        struct poll_event_context *poll_ev = talloc_get_type_abort(
                ev->additional_data, struct poll_event_context);
-       struct pollfd *pfd;
        struct tevent_fd *fde;
 
-       fde = tevent_common_add_fd(ev, mem_ctx, fd, flags,
-                                  handler, private_data,
-                                  handler_name, location);
-       if (fde == NULL) {
+       if (fd < 0) {
                return NULL;
        }
 
-       /* we allocate 16 slots to avoid a lot of reallocations */
-       if (talloc_array_length(poll_ev->fds) == poll_ev->num_fds) {
-               struct pollfd *tmp_fds;
-               struct tevent_fd **tmp_fd_events;
-               tmp_fds = talloc_realloc(
-                       poll_ev, poll_ev->fds, struct pollfd,
-                       poll_ev->num_fds + 16);
-               if (tmp_fds == NULL) {
-                       TALLOC_FREE(fde);
-                       return NULL;
-               }
-               poll_ev->fds = tmp_fds;
-
-               tmp_fd_events = talloc_realloc(
-                       poll_ev, poll_ev->fd_events, struct tevent_fd *,
-                       poll_ev->num_fds + 16);
-               if (tmp_fd_events == NULL) {
-                       TALLOC_FREE(fde);
-                       return NULL;
-               }
-               poll_ev->fd_events = tmp_fd_events;
-       }
-
-       pfd = &poll_ev->fds[poll_ev->num_fds];
-
-       pfd->fd = fd;
-
-       pfd->events = 0;
-       pfd->revents = 0;
-
-       if (flags & TEVENT_FD_READ) {
-               pfd->events |= (POLLIN|POLLHUP);
-       }
-       if (flags & TEVENT_FD_WRITE) {
-               pfd->events |= (POLLOUT);
+       fde = talloc(mem_ctx ? mem_ctx : ev, struct tevent_fd);
+       if (fde == NULL) {
+               return NULL;
        }
+       fde->event_ctx          = ev;
+       fde->fd                 = fd;
+       fde->flags              = flags;
+       fde->handler            = handler;
+       fde->close_fn           = NULL;
+       fde->private_data       = private_data;
+       fde->handler_name       = handler_name;
+       fde->location           = location;
+       fde->additional_flags   = UINT64_MAX;
+       fde->additional_data    = NULL;
+
+       tevent_poll_event_add_fd_internal(ev, fde);
+       poll_event_wake_pollthread(poll_ev);
 
        /*
-        * Assume a void * can carry enough bits to hold num_fds.
+        * poll_event_loop_poll will take care of the rest in
+        * poll_event_setup_fresh
         */
-       fde->additional_data = (void *)(long)poll_ev->num_fds;
-       poll_ev->fd_events[poll_ev->num_fds] = fde;
-
-       poll_ev->num_fds += 1;
-
-       talloc_set_destructor(fde, poll_event_fd_destructor);
-
        return fde;
 }
 
@@ -167,10 +333,46 @@ static struct tevent_fd *poll_event_add_fd(struct tevent_context *ev,
 */
 static void poll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
 {
-       struct poll_event_context *poll_ev = talloc_get_type_abort(
-               fde->event_ctx->additional_data, struct poll_event_context);
-       long idx;
-       uint16_t pollflags = 0;
+       struct tevent_context *ev = fde->event_ctx;
+       struct poll_event_context *poll_ev;
+       uint64_t idx = fde->additional_flags;
+       uint16_t pollflags;
+
+       if (ev == NULL) {
+               return;
+       }
+       poll_ev = talloc_get_type_abort(
+               ev->additional_data, struct poll_event_context);
+
+       fde->flags = flags;
+
+       if (idx == UINT64_MAX) {
+               struct tevent_fd **listp =
+                       (struct tevent_fd **)fde->additional_data;
+
+               /*
+                * We move it between the fresh and disabled lists.
+                */
+               DLIST_REMOVE((*listp), fde);
+               tevent_poll_event_add_fd_internal(ev, fde);
+               poll_event_wake_pollthread(poll_ev);
+               return;
+       }
+
+       if (fde->flags == 0) {
+               /*
+                * We need to remove it from the array
+                * and move it to the disabled list.
+                */
+               poll_ev->fdes[idx] = NULL;
+               poll_ev->deleted = true;
+               DLIST_REMOVE(ev->fd_events, fde);
+               tevent_poll_event_add_fd_internal(ev, fde);
+               poll_event_wake_pollthread(poll_ev);
+               return;
+       }
+
+       pollflags = 0;
 
        if (flags & TEVENT_FD_READ) {
                pollflags |= (POLLIN|POLLHUP);
@@ -178,24 +380,126 @@ static void poll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
        if (flags & TEVENT_FD_WRITE) {
                pollflags |= (POLLOUT);
        }
-
-       idx = (long)(fde->additional_data);
        poll_ev->fds[idx].events = pollflags;
 
-       fde->flags = flags;
+       poll_event_wake_pollthread(poll_ev);
+}
+
+static bool poll_event_setup_fresh(struct tevent_context *ev,
+                                  struct poll_event_context *poll_ev)
+{
+       struct tevent_fd *fde, *next;
+       unsigned num_fresh, num_fds;
+
+       if (poll_ev->deleted) {
+               unsigned first_fd = (poll_ev->signal_fd != -1) ? 1 : 0;
+               unsigned i;
+
+               for (i=first_fd; i < poll_ev->num_fds;) {
+                       fde = poll_ev->fdes[i];
+                       if (fde != NULL) {
+                               i++;
+                               continue;
+                       }
+
+                       /*
+                        * This fde was talloc_free()'ed. Delete it
+                        * from the arrays
+                        */
+                       poll_ev->num_fds -= 1;
+                       if (poll_ev->num_fds == i) {
+                               break;
+                       }
+                       poll_ev->fds[i] = poll_ev->fds[poll_ev->num_fds];
+                       poll_ev->fdes[i] = poll_ev->fdes[poll_ev->num_fds];
+                       if (poll_ev->fdes[i] != NULL) {
+                               poll_ev->fdes[i]->additional_flags = i;
+                       }
+               }
+               poll_ev->deleted = false;
+       }
+
+       if (poll_ev->fresh == NULL) {
+               return true;
+       }
+
+       num_fresh = 0;
+       for (fde = poll_ev->fresh; fde; fde = fde->next) {
+               num_fresh += 1;
+       }
+       num_fds = poll_ev->num_fds + num_fresh;
+
+       /*
+        * We check the length of fdes here. It is the last one
+        * enlarged, so if the realloc for poll_fd->fdes fails,
+        * poll_fd->fds will have at least the size of poll_fd->fdes
+        */
+
+       if (num_fds >= talloc_array_length(poll_ev->fdes)) {
+               struct pollfd *tmp_fds;
+               struct tevent_fd **tmp_fdes;
+               unsigned array_length;
+
+               array_length = (num_fds + 15) & ~15; /* round up to 16 */
+
+               tmp_fds = talloc_realloc(
+                       poll_ev, poll_ev->fds, struct pollfd, array_length);
+               if (tmp_fds == NULL) {
+                       return false;
+               }
+               poll_ev->fds = tmp_fds;
+
+               tmp_fdes = talloc_realloc(
+                       poll_ev, poll_ev->fdes, struct tevent_fd *,
+                       array_length);
+               if (tmp_fdes == NULL) {
+                       return false;
+               }
+               poll_ev->fdes = tmp_fdes;
+       }
+
+       for (fde = poll_ev->fresh; fde; fde = next) {
+               struct pollfd *pfd;
+
+               pfd = &poll_ev->fds[poll_ev->num_fds];
+
+               pfd->fd = fde->fd;
+               pfd->events = 0;
+               pfd->revents = 0;
+
+               if (fde->flags & TEVENT_FD_READ) {
+                       pfd->events |= (POLLIN|POLLHUP);
+               }
+               if (fde->flags & TEVENT_FD_WRITE) {
+                       pfd->events |= (POLLOUT);
+               }
+
+               fde->additional_flags = poll_ev->num_fds;
+               poll_ev->fdes[poll_ev->num_fds] = fde;
+
+               next = fde->next;
+               DLIST_REMOVE(poll_ev->fresh, fde);
+               DLIST_ADD(ev->fd_events, fde);
+
+               poll_ev->num_fds += 1;
+       }
+       return true;
 }
 
 /*
-  event loop handling using select()
+  event loop handling using poll()
 */
 static int poll_event_loop_poll(struct tevent_context *ev,
                                struct timeval *tvalp)
 {
        struct poll_event_context *poll_ev = talloc_get_type_abort(
                ev->additional_data, struct poll_event_context);
-       struct tevent_fd *fde;
        int pollrtn;
        int timeout = -1;
+       int poll_errno;
+       struct tevent_fd *fde = NULL;
+       struct tevent_fd *next = NULL;
+       unsigned i;
 
        if (ev->signal_events && tevent_common_check_signal(ev)) {
                return 0;
@@ -206,54 +510,121 @@ static int poll_event_loop_poll(struct tevent_context *ev,
                timeout += (tvalp->tv_usec + 999) / 1000;
        }
 
+       poll_event_drain_signal_fd(poll_ev);
+
+       if (!poll_event_setup_fresh(ev, poll_ev)) {
+               return -1;
+       }
+
+       tevent_trace_point_callback(poll_ev->ev, TEVENT_TRACE_BEFORE_WAIT);
        pollrtn = poll(poll_ev->fds, poll_ev->num_fds, timeout);
+       poll_errno = errno;
+       tevent_trace_point_callback(poll_ev->ev, TEVENT_TRACE_AFTER_WAIT);
 
-       if (pollrtn == -1 && errno == EINTR && ev->signal_events) {
+       if (pollrtn == -1 && poll_errno == EINTR && ev->signal_events) {
                tevent_common_check_signal(ev);
                return 0;
        }
 
-       if (pollrtn == -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. */
-               tevent_debug(ev, TEVENT_DEBUG_FATAL,
-                            "ERROR: EBADF on poll_event_loop_once\n");
-               poll_ev->exit_code = EBADF;
-               return -1;
-       }
-
        if (pollrtn == 0 && tvalp) {
                /* we don't care about a possible delay here */
                tevent_common_loop_timer_delay(ev);
                return 0;
        }
 
-       if (pollrtn > 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 (fde = ev->fd_events; fde; fde = fde->next) {
-                       struct pollfd *pfd;
-                       long pfd_idx;
-                       uint16_t flags = 0;
+       if (pollrtn <= 0) {
+               /*
+                * No fd's ready
+                */
+               return 0;
+       }
 
-                       pfd_idx = (long)(fde->additional_data);
+       /* 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 */
 
-                       pfd = &poll_ev->fds[pfd_idx];
+       for (fde = ev->fd_events; fde; fde = next) {
+               uint64_t idx = fde->additional_flags;
+               struct pollfd *pfd;
+               uint16_t flags = 0;
 
-                       if (pfd->revents & (POLLIN|POLLHUP|POLLERR)) {
-                               flags |= TEVENT_FD_READ;
-                       }
-                       if (pfd->revents & POLLOUT) {
-                               flags |= TEVENT_FD_WRITE;
+               next = fde->next;
+
+               if (idx == UINT64_MAX) {
+                       continue;
+               }
+
+               pfd = &poll_ev->fds[idx];
+
+               if (pfd->revents & POLLNVAL) {
+                       /*
+                        * 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.
+                        *
+                        * We ignore it here to match the epoll
+                        * behavior.
+                        */
+                       tevent_debug(ev, TEVENT_DEBUG_ERROR,
+                                    "POLLNVAL on fde[%p] fd[%d] - disabling\n",
+                                    fde, pfd->fd);
+                       poll_ev->fdes[idx] = NULL;
+                       poll_ev->deleted = true;
+                       DLIST_REMOVE(ev->fd_events, fde);
+                       fde->event_ctx = NULL;
+                       continue;
+               }
+
+               if (pfd->revents & (POLLHUP|POLLERR)) {
+                       /* If we only wait for TEVENT_FD_WRITE, we
+                          should not tell the event handler about it,
+                          and remove the writable flag, as we only
+                          report errors when waiting for read events
+                          to match the select behavior. */
+                       if (!(fde->flags & TEVENT_FD_READ)) {
+                               TEVENT_FD_NOT_WRITEABLE(fde);
+                               continue;
                        }
-                       if (flags != 0) {
-                               fde->handler(ev, fde, flags,
-                                            fde->private_data);
-                               break;
+                       flags |= TEVENT_FD_READ;
+               }
+               if (pfd->revents & POLLIN) {
+                       flags |= TEVENT_FD_READ;
+               }
+               if (pfd->revents & POLLOUT) {
+                       flags |= TEVENT_FD_WRITE;
+               }
+               /*
+                * Note that fde->flags could be changed when using
+                * the poll_mt backend together with threads,
+                * that why we need to check pfd->revents and fde->flags
+                */
+               flags &= fde->flags;
+               if (flags != 0) {
+                       DLIST_DEMOTE(ev->fd_events, fde);
+                       fde->handler(ev, fde, flags, fde->private_data);
+                       return 0;
+               }
+       }
+
+       for (i = 0; i < poll_ev->num_fds; i++) {
+               if (poll_ev->fds[i].revents & POLLNVAL) {
+                       /*
+                        * 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 or
+                        * a race in the poll_mt usage.
+                        */
+                       fde = poll_ev->fdes[i];
+                       tevent_debug(ev, TEVENT_DEBUG_WARNING,
+                                    "POLLNVAL on dangling fd[%d] fde[%p] - disabling\n",
+                                    poll_ev->fds[i].fd, fde);
+                       poll_ev->fdes[i] = NULL;
+                       poll_ev->deleted = true;
+                       if (fde != NULL) {
+                               DLIST_REMOVE(ev->fd_events, fde);
+                               fde->event_ctx = NULL;
                        }
                }
        }
@@ -274,6 +645,10 @@ static int poll_event_loop_once(struct tevent_context *ev,
                return 0;
        }
 
+       if (ev->threaded_contexts != NULL) {
+               tevent_common_threaded_activate_immediate(ev);
+       }
+
        if (ev->immediate_events &&
            tevent_common_loop_immediate(ev)) {
                return 0;
@@ -287,20 +662,65 @@ static int poll_event_loop_once(struct tevent_context *ev,
        return poll_event_loop_poll(ev, &tval);
 }
 
+static int poll_event_loop_wait(struct tevent_context *ev,
+                               const char *location)
+{
+       struct poll_event_context *poll_ev = talloc_get_type_abort(
+               ev->additional_data, struct poll_event_context);
+
+       /*
+        * loop as long as we have events pending
+        */
+       while (tevent_common_have_events(ev) ||
+              poll_ev->fresh ||
+              poll_ev->disabled) {
+               int ret;
+               ret = _tevent_loop_once(ev, location);
+               if (ret != 0) {
+                       tevent_debug(ev, TEVENT_DEBUG_FATAL,
+                                    "_tevent_loop_once() failed: %d - %s\n",
+                                    ret, strerror(errno));
+                       return ret;
+               }
+       }
+
+       tevent_debug(ev, TEVENT_DEBUG_WARNING,
+                    "poll_event_loop_wait() out of events\n");
+       return 0;
+}
+
 static const struct tevent_ops poll_event_ops = {
        .context_init           = poll_event_context_init,
        .add_fd                 = poll_event_add_fd,
        .set_fd_close_fn        = tevent_common_fd_set_close_fn,
        .get_fd_flags           = tevent_common_fd_get_flags,
        .set_fd_flags           = poll_event_set_fd_flags,
-       .add_timer              = tevent_common_add_timer,
+       .add_timer              = tevent_common_add_timer_v2,
        .schedule_immediate     = tevent_common_schedule_immediate,
        .add_signal             = tevent_common_add_signal,
        .loop_once              = poll_event_loop_once,
-       .loop_wait              = tevent_common_loop_wait,
+       .loop_wait              = poll_event_loop_wait,
 };
 
 _PRIVATE_ bool tevent_poll_init(void)
 {
        return tevent_register_backend("poll", &poll_event_ops);
 }
+
+static const struct tevent_ops poll_event_mt_ops = {
+       .context_init           = poll_event_context_init_mt,
+       .add_fd                 = poll_event_add_fd,
+       .set_fd_close_fn        = tevent_common_fd_set_close_fn,
+       .get_fd_flags           = tevent_common_fd_get_flags,
+       .set_fd_flags           = poll_event_set_fd_flags,
+       .add_timer              = tevent_common_add_timer_v2,
+       .schedule_immediate     = poll_event_schedule_immediate,
+       .add_signal             = tevent_common_add_signal,
+       .loop_once              = poll_event_loop_once,
+       .loop_wait              = poll_event_loop_wait,
+};
+
+_PRIVATE_ bool tevent_poll_mt_init(void)
+{
+       return tevent_register_backend("poll_mt", &poll_event_mt_ops);
+}