talloc: use the system pytalloc-util for python3 as well
[sfrench/samba-autobuild/.git] / lib / tevent / tevent.c
index e35eb5ddc93a86c0b68a4949a3ee3611eb19c3a7..65b101f28e5715d8b576b89c655e2d2662962c5b 100644 (file)
@@ -66,6 +66,9 @@
 #include "tevent.h"
 #include "tevent_internal.h"
 #include "tevent_util.h"
+#ifdef HAVE_EVENTFD
+#include <sys/eventfd.h>
+#endif
 
 static void tevent_abort(struct tevent_context *ev, const char *reason);
 
@@ -197,6 +200,16 @@ static void tevent_atfork_prepare(void)
        }
 
        for (ev = tevent_contexts; ev != NULL; ev = ev->next) {
+               struct tevent_threaded_context *tctx;
+
+               for (tctx = ev->threaded_contexts; tctx != NULL;
+                    tctx = tctx->next) {
+                       ret = pthread_mutex_lock(&tctx->event_ctx_mutex);
+                       if (ret != 0) {
+                               tevent_abort(ev, "pthread_mutex_lock failed");
+                       }
+               }
+
                ret = pthread_mutex_lock(&ev->scheduled_mutex);
                if (ret != 0) {
                        tevent_abort(ev, "pthread_mutex_lock failed");
@@ -211,10 +224,21 @@ static void tevent_atfork_parent(void)
 
        for (ev = DLIST_TAIL(tevent_contexts); ev != NULL;
             ev = DLIST_PREV(ev)) {
+               struct tevent_threaded_context *tctx;
+
                ret = pthread_mutex_unlock(&ev->scheduled_mutex);
                if (ret != 0) {
                        tevent_abort(ev, "pthread_mutex_unlock failed");
                }
+
+               for (tctx = DLIST_TAIL(ev->threaded_contexts); tctx != NULL;
+                    tctx = DLIST_PREV(tctx)) {
+                       ret = pthread_mutex_unlock(&tctx->event_ctx_mutex);
+                       if (ret != 0) {
+                               tevent_abort(
+                                       ev, "pthread_mutex_unlock failed");
+                       }
+               }
        }
 
        ret = pthread_mutex_unlock(&tevent_contexts_mutex);
@@ -232,9 +256,15 @@ static void tevent_atfork_child(void)
             ev = DLIST_PREV(ev)) {
                struct tevent_threaded_context *tctx;
 
-               for (tctx = ev->threaded_contexts; tctx != NULL;
-                    tctx = tctx->next) {
+               for (tctx = DLIST_TAIL(ev->threaded_contexts); tctx != NULL;
+                    tctx = DLIST_PREV(tctx)) {
                        tctx->event_ctx = NULL;
+
+                       ret = pthread_mutex_unlock(&tctx->event_ctx_mutex);
+                       if (ret != 0) {
+                               tevent_abort(
+                                       ev, "pthread_mutex_unlock failed");
+                       }
                }
 
                ev->threaded_contexts = NULL;
@@ -286,18 +316,32 @@ int tevent_common_context_destructor(struct tevent_context *ev)
        if (ret != 0) {
                abort();
        }
-#endif
 
-       if (ev->threaded_contexts != NULL) {
+       while (ev->threaded_contexts != NULL) {
+               struct tevent_threaded_context *tctx = ev->threaded_contexts;
+
+               ret = pthread_mutex_lock(&tctx->event_ctx_mutex);
+               if (ret != 0) {
+                       abort();
+               }
+
                /*
-                * Threaded contexts are indicators that threads are
-                * about to send us immediates via
-                * tevent_threaded_schedule_immediate. The caller
-                * needs to make sure that the tevent context lives
-                * long enough to receive immediates from all threads.
+                * Indicate to the thread that the tevent_context is
+                * gone. The counterpart of this is in
+                * _tevent_threaded_schedule_immediate, there we read
+                * this under the threaded_context's mutex.
                 */
-               tevent_abort(ev, "threaded contexts exist");
+
+               tctx->event_ctx = NULL;
+
+               ret = pthread_mutex_unlock(&tctx->event_ctx_mutex);
+               if (ret != 0) {
+                       abort();
+               }
+
+               DLIST_REMOVE(ev->threaded_contexts, tctx);
        }
+#endif
 
        tevent_common_wakeup_fini(ev);
 
@@ -767,7 +811,7 @@ done:
 bool tevent_common_have_events(struct tevent_context *ev)
 {
        if (ev->fd_events != NULL) {
-               if (ev->fd_events != ev->pipe_fde) {
+               if (ev->fd_events != ev->wakeup_fde) {
                        return true;
                }
                if (ev->fd_events->next != NULL) {
@@ -840,10 +884,14 @@ static void wakeup_pipe_handler(struct tevent_context *ev,
 {
        ssize_t ret;
 
-       /* its non-blocking, doesn't matter if we read too much */
        do {
-               char c[16];
-               ret = read(fde->fd, c, sizeof(c));
+               /*
+                * This is the boilerplate for eventfd, but it works
+                * for pipes too. And as we don't care about the data
+                * we read, we're fine.
+                */
+               uint64_t val;
+               ret = read(fde->fd, &val, sizeof(val));
        } while (ret == -1 && errno == EINTR);
 }
 
@@ -853,55 +901,84 @@ static void wakeup_pipe_handler(struct tevent_context *ev,
 
 int tevent_common_wakeup_init(struct tevent_context *ev)
 {
-       int ret;
+       int ret, read_fd;
 
-       if (ev->pipe_fde != NULL) {
+       if (ev->wakeup_fde != NULL) {
                return 0;
        }
 
-       ret = pipe(ev->pipe_fds);
+#ifdef HAVE_EVENTFD
+       ret = eventfd(0, EFD_NONBLOCK);
        if (ret == -1) {
                return errno;
        }
-       ev_set_blocking(ev->pipe_fds[0], false);
-       ev_set_blocking(ev->pipe_fds[1], false);
+       read_fd = ev->wakeup_fd = ret;
+#else
+       {
+               int pipe_fds[2];
+               ret = pipe(pipe_fds);
+               if (ret == -1) {
+                       return errno;
+               }
+               ev->wakeup_fd = pipe_fds[1];
+               ev->wakeup_read_fd = pipe_fds[0];
 
-       ev->pipe_fde = tevent_add_fd(ev, ev, ev->pipe_fds[0],
-                                    TEVENT_FD_READ,
+               ev_set_blocking(ev->wakeup_fd, false);
+               ev_set_blocking(ev->wakeup_read_fd, false);
+
+               read_fd = ev->wakeup_read_fd;
+       }
+#endif
+
+       ev->wakeup_fde = tevent_add_fd(ev, ev, read_fd, TEVENT_FD_READ,
                                     wakeup_pipe_handler, NULL);
-       if (ev->pipe_fde == NULL) {
-               close(ev->pipe_fds[0]);
-               close(ev->pipe_fds[1]);
+       if (ev->wakeup_fde == NULL) {
+               close(ev->wakeup_fd);
+#ifndef HAVE_EVENTFD
+               close(ev->wakeup_read_fd);
+#endif
                return ENOMEM;
        }
 
        return 0;
 }
 
-int tevent_common_wakeup(struct tevent_context *ev)
+int tevent_common_wakeup_fd(int fd)
 {
        ssize_t ret;
 
-       if (ev->pipe_fds[1] == -1) {
-               return ENOTCONN;
-       }
-
        do {
+#ifdef HAVE_EVENTFD
+               uint64_t val = 1;
+               ret = write(fd, &val, sizeof(val));
+#else
                char c = '\0';
-               ret = write(ev->pipe_fds[1], &c, 1);
+               ret = write(fd, &c, 1);
+#endif
        } while ((ret == -1) && (errno == EINTR));
 
        return 0;
 }
 
+int tevent_common_wakeup(struct tevent_context *ev)
+{
+       if (ev->wakeup_fde == NULL) {
+               return ENOTCONN;
+       }
+
+       return tevent_common_wakeup_fd(ev->wakeup_fd);
+}
+
 static void tevent_common_wakeup_fini(struct tevent_context *ev)
 {
-       if (ev->pipe_fde == NULL) {
+       if (ev->wakeup_fde == NULL) {
                return;
        }
 
-       TALLOC_FREE(ev->pipe_fde);
+       TALLOC_FREE(ev->wakeup_fde);
 
-       close(ev->pipe_fds[0]);
-       close(ev->pipe_fds[1]);
+       close(ev->wakeup_fd);
+#ifndef HAVE_EVENTFD
+       close(ev->wakeup_read_fd);
+#endif
 }