]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added IO_DIR_NOTIFY and IO_FILE_NOTIFY conditions to io_add(). IO_DIR_NOTIFY
authorTimo Sirainen <tss@iki.fi>
Sun, 24 Aug 2003 12:43:53 +0000 (15:43 +0300)
committerTimo Sirainen <tss@iki.fi>
Sun, 24 Aug 2003 12:43:53 +0000 (15:43 +0300)
is now implemented for Linux dnotify.

--HG--
branch : HEAD

configure.in
src/lib/Makefile.am
src/lib/ioloop-internal.h
src/lib/ioloop-notify-dn.c [new file with mode: 0644]
src/lib/ioloop-notify-none.c [new file with mode: 0644]
src/lib/ioloop-poll.c
src/lib/ioloop-select.c
src/lib/ioloop.c
src/lib/ioloop.h

index 40516c1eed49e746551bf743f73e93b313c2f33a..e372e2cc41faa3287dcbcecb313cac922b0bbe7d 100644 (file)
@@ -225,6 +225,21 @@ AC_CHECK_FUNC(poll, [
        AC_DEFINE(IOLOOP_SELECT,, Implement I/O loop with select())
 ])
 
+dnl * dnotify?
+AC_TRY_COMPILE([
+  #define _GNU_SOURCE
+  #include <fcntl.h>
+  #include <signal.h>
+  #include <unistd.h>
+], [
+  fcntl(0, F_SETSIG, SIGRTMIN);
+  fcntl(0, F_NOTIFY, DN_CREATE | DN_DELETE | DN_RENAME | DN_MULTISHOT);
+], [
+  AC_DEFINE(IOLOOP_NOTIFY_DNOTIFY,, Use Linux dnotify)
+], [
+  AC_DEFINE(IOLOOP_NOTIFY_NONE,, No special notify support)
+])
+
 dnl * OS specific options
 case "$host_os" in
        hpux*)
index 5b597eed06fef43a7138c81a75d7a298fafcffcd..f6307ad9a1e72970ae51a0b0922ac1d316410c38 100644 (file)
@@ -25,6 +25,8 @@ liblib_a_SOURCES = \
        istream-file.c \
        istream-mmap.c \
        ioloop.c \
+       ioloop-notify-none.c \
+       ioloop-notify-dn.c \
        ioloop-poll.c \
        ioloop-select.c \
        lib.c \
index 6a2613118a18293dc7ca40e7ccea27be8d36b499..0f05fadf6b696e1a544eb5dbbceb0702f7e65782 100644 (file)
@@ -9,7 +9,8 @@ struct ioloop {
        pool_t pool;
        int highest_fd;
 
-       struct io *ios; /* sorted by priority */
+       struct io *ios;
+       struct io *notifys, *event_io;
        struct timeout *timeouts; /* sorted by next_run */
 
         struct ioloop_handler_data *handler_data;
@@ -52,10 +53,17 @@ void io_destroy(struct ioloop *ioloop, struct io **io_p);
 void timeout_destroy(struct ioloop *ioloop, struct timeout **timeout_p);
 
 /* I/O handler calls */
-void io_loop_handle_add(struct ioloop *ioloop, int fd, int condition);
-void io_loop_handle_remove(struct ioloop *ioloop, int fd, int condition);
+void io_loop_handle_add(struct ioloop *ioloop, int fd,
+                       enum io_condition condition);
+void io_loop_handle_remove(struct ioloop *ioloop, int fd,
+                          enum io_condition condition);
 
 void io_loop_handler_init(struct ioloop *ioloop);
 void io_loop_handler_deinit(struct ioloop *ioloop);
 
+struct io *io_loop_notify_add(struct ioloop *ioloop, int fd,
+                             enum io_condition condition,
+                             io_callback_t *callback, void *context);
+void io_loop_notify_remove(struct ioloop *ioloop, struct io *io);
+
 #endif
diff --git a/src/lib/ioloop-notify-dn.c b/src/lib/ioloop-notify-dn.c
new file mode 100644 (file)
index 0000000..d2c77e0
--- /dev/null
@@ -0,0 +1,138 @@
+/* Copyright (C) 2003 Timo Sirainen */
+
+/* Logic is pretty much based on dnotify by Oskar Liljeblad. */
+
+#define _GNU_SOURCE
+#include "lib.h"
+
+#ifdef IOLOOP_NOTIFY_DNOTIFY
+
+#include "ioloop-internal.h"
+#include "write-full.h"
+
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static int event_pipe[2] = { -1, -1 };
+
+static void sigrt_handler(int signo __attr_unused__, siginfo_t *si,
+                         void *data __attr_unused__)
+{
+       if (write_full(event_pipe[1], &si->si_fd, sizeof(int)) < 0)
+               i_fatal("write_full(event_pipe) failed: %m");
+}
+
+static void event_callback(void *context)
+{
+       struct ioloop *ioloop = context;
+       struct io *io;
+       int fd, ret;
+
+       ret = read(event_pipe[0], &fd, sizeof(fd));
+       if (ret < 0)
+               i_fatal("read(event_pipe) failed: %m");
+       if (ret != sizeof(fd))
+               i_fatal("read(event_pipe) returned %d != %d", ret, sizeof(fd));
+
+       if (gettimeofday(&ioloop_timeval, &ioloop_timezone) < 0)
+               i_fatal("gettimeofday(): %m");
+       ioloop_time = ioloop_timeval.tv_sec;
+
+       for (io = ioloop->notifys; io != NULL; io = io->next) {
+               if (io->fd == fd) {
+                       io->callback(io->context);
+                       break;
+               }
+       }
+}
+
+static int dn_init(void)
+{
+       struct sigaction act;
+
+       if (pipe(event_pipe) < 0) {
+               i_error("pipe() failed: %m");
+               return FALSE;
+       }
+
+       act.sa_sigaction = sigrt_handler;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER;
+
+       if (sigaction(SIGRTMIN, &act, NULL) < 0) {
+               i_error("sigaction(SIGRTMIN) failed: %m");
+               close(event_pipe[0]);
+               close(event_pipe[1]);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+struct io *io_loop_notify_add(struct ioloop *ioloop, int fd,
+                             enum io_condition condition,
+                             io_callback_t *callback, void *context)
+{
+       struct io *io;
+
+       if ((condition & IO_FILE_NOTIFY) != 0)
+               return NULL;
+
+       if (event_pipe[0] == -1) {
+               if (!dn_init())
+                       return NULL;
+       }
+       if (ioloop->event_io == NULL) {
+               ioloop->event_io =
+                       io_add(event_pipe[0], IO_READ, event_callback, ioloop);
+       }
+
+       if (fcntl(fd, F_SETSIG, SIGRTMIN) < 0) {
+               i_error("fcntl(F_SETSIG) failed: %m");
+               return FALSE;
+       }
+       if (fcntl(fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_RENAME |
+                 DN_MULTISHOT) < 0) {
+               i_error("fcntl(F_NOTIFY) failed: %m");
+               (void)fcntl(fd, F_SETSIG, 0);
+               return FALSE;
+       }
+
+       io = p_new(ioloop->pool, struct io, 1);
+       io->fd = fd;
+        io->condition = condition;
+
+       io->callback = callback;
+        io->context = context;
+
+       io->next = ioloop->notifys;
+       ioloop->notifys = io;
+       return io;
+}
+
+void io_loop_notify_remove(struct ioloop *ioloop, struct io *io)
+{
+       struct io **io_p;
+
+       for (io_p = &ioloop->notifys; *io_p != NULL; io_p = &(*io_p)->next) {
+               if (*io_p == io) {
+                       *io_p = io->next;
+                       break;
+               }
+       }
+
+       if (fcntl(io->fd, F_SETSIG, 0) < 0)
+               i_error("fcntl(F_SETSIG, 0) failed: %m");
+       if (fcntl(io->fd, F_NOTIFY, 0) < 0)
+               i_error("fcntl(F_NOTIFY, 0) failed: %m");
+
+       p_free(ioloop->pool, io);
+
+       if (ioloop->notifys == NULL) {
+               io_remove(ioloop->event_io);
+               ioloop->event_io = NULL;
+       }
+}
+
+#endif
diff --git a/src/lib/ioloop-notify-none.c b/src/lib/ioloop-notify-none.c
new file mode 100644 (file)
index 0000000..c2b6ec3
--- /dev/null
@@ -0,0 +1,22 @@
+/* Copyright (C) 2003 Timo Sirainen */
+
+#include "lib.h"
+#include "ioloop-internal.h"
+
+#ifdef IOLOOP_NOTIFY_NONE
+
+struct io *io_loop_notify_add(struct ioloop *ioloop __attr_unused__,
+                             int fd __attr_unused__,
+                             enum io_condition condition __attr_unused__,
+                             io_callback_t *callback __attr_unused__,
+                             void *context __attr_unused__)
+{
+       return FALSE;
+}
+
+void io_loop_notify_remove(struct ioloop *ioloop __attr_unused__,
+                          struct io *io __attr_unused__)
+{
+}
+
+#endif
index bb1600ceba6fb5ff9037bf8028af7d545f848bdc..dab47e8dfcc51c1c12739651c8eed76b91e16e76 100644 (file)
@@ -68,7 +68,8 @@ void io_loop_handler_deinit(struct ioloop *ioloop)
 #define IO_POLL_INPUT (POLLIN|POLLPRI|POLLERR|POLLHUP|POLLNVAL)
 #define IO_POLL_OUTPUT (POLLOUT|POLLERR|POLLHUP|POLLNVAL)
 
-void io_loop_handle_add(struct ioloop *ioloop, int fd, int condition)
+void io_loop_handle_add(struct ioloop *ioloop, int fd,
+                       enum io_condition condition)
 {
        struct ioloop_handler_data *data = ioloop->handler_data;
        unsigned int old_size;
@@ -119,7 +120,8 @@ void io_loop_handle_add(struct ioloop *ioloop, int fd, int condition)
                data->fds[index].events |= IO_POLL_OUTPUT;
 }
 
-void io_loop_handle_remove(struct ioloop *ioloop, int fd, int condition)
+void io_loop_handle_remove(struct ioloop *ioloop, int fd,
+                          enum io_condition condition)
 {
        struct ioloop_handler_data *data = ioloop->handler_data;
        int index;
index 719eede2e8f5b227e95acf7e402364ff92b0884f..210d106251cece06599d242322057f7a9e6536f6 100644 (file)
@@ -53,7 +53,8 @@ void io_loop_handler_deinit(struct ioloop *ioloop)
         p_free(ioloop->pool, ioloop->handler_data);
 }
 
-void io_loop_handle_add(struct ioloop *ioloop, int fd, int condition)
+void io_loop_handle_add(struct ioloop *ioloop, int fd,
+                       enum io_condition condition)
 {
        i_assert(fd >= 0);
 
@@ -66,7 +67,8 @@ void io_loop_handle_add(struct ioloop *ioloop, int fd, int condition)
                FD_SET(fd, &ioloop->handler_data->write_fds);
 }
 
-void io_loop_handle_remove(struct ioloop *ioloop, int fd, int condition)
+void io_loop_handle_remove(struct ioloop *ioloop, int fd,
+                          enum io_condition condition)
 {
        i_assert(fd >= 0 && fd < FD_SETSIZE);
 
@@ -84,8 +86,8 @@ void io_loop_handler_run(struct ioloop *ioloop)
 {
        struct timeval tv;
        struct io *io, **io_p;
-        unsigned int t_id;
-       int ret, fd, condition;
+       unsigned int t_id;
+       int ret;
 
        /* get the time left for next timeout task */
        io_loop_get_wait_time(ioloop->timeouts, &tv, NULL);
@@ -117,10 +119,7 @@ void io_loop_handler_run(struct ioloop *ioloop)
 
                i_assert(io->fd >= 0);
 
-               fd = io->fd;
-               condition = io->condition;
-
-               if (io_check_condition(fd, condition)) {
+               if (io_check_condition(io->fd, io->condition)) {
                        ret--;
 
                        t_id = t_push();
index 735de50fe7a4bbf90fe7aeef48dab4db8d3c3a9e..aa443a3f6e7d78ffa9a009a1a2f6893cd1f50fc9 100644 (file)
@@ -56,13 +56,19 @@ static void update_highest_fd(struct ioloop *ioloop)
        }
 }
 
-struct io *io_add(int fd, int condition, io_callback_t *callback, void *context)
+struct io *io_add(int fd, enum io_condition condition,
+                 io_callback_t *callback, void *context)
 {
        struct io *io, **io_p;
 
        i_assert(fd >= 0);
        i_assert(callback != NULL);
 
+       if ((condition & IO_NOTIFY_MASK) != 0) {
+               return io_loop_notify_add(current_ioloop, fd, condition,
+                                         callback, context);
+       }
+
        io = p_new(current_ioloop->pool, struct io, 1);
        io->fd = fd;
         io->condition = condition;
@@ -71,9 +77,9 @@ struct io *io_add(int fd, int condition, io_callback_t *callback, void *context)
         io->context = context;
 
        if (io->fd > current_ioloop->highest_fd)
-                current_ioloop->highest_fd = io->fd;
+               current_ioloop->highest_fd = io->fd;
 
-        io_loop_handle_add(current_ioloop, io->fd, io->condition);
+       io_loop_handle_add(current_ioloop, io->fd, io->condition);
 
        /* have to append it, or io_destroy() breaks */
         io_p = &current_ioloop->ios;
@@ -87,14 +93,20 @@ void io_remove(struct io *io)
 {
        i_assert(io != NULL);
        i_assert(io->fd >= 0);
+
+       if ((io->condition & IO_NOTIFY_MASK) != 0) {
+               io_loop_notify_remove(current_ioloop, io);
+               return;
+       }
+
        i_assert(io->fd <= current_ioloop->highest_fd);
 
-        /* notify the real I/O handler */
+       /* notify the real I/O handler */
        io_loop_handle_remove(current_ioloop, io->fd, io->condition);
 
-        /* check if we removed the highest fd */
+       /* check if we removed the highest fd */
        if (io->fd == current_ioloop->highest_fd)
-                update_highest_fd(current_ioloop);
+               update_highest_fd(current_ioloop);
 
        io->destroyed = TRUE;
        io->fd = -1;
index e3ebdb8e74cb9e2efcbd83338a71c8914d5fd56d..3f0230160332b79c532599185d2afb0a15cc4f49 100644 (file)
@@ -4,13 +4,19 @@
 #include <sys/time.h>
 #include <time.h>
 
-#define IO_READ                        (1 << 0)
-#define IO_WRITE               (1 << 1)
-
 struct io;
 struct timeout;
 struct ioloop;
 
+enum io_condition {
+       IO_READ         = 0x01,
+       IO_WRITE        = 0x02,
+       IO_DIR_NOTIFY   = 0x04,
+       IO_FILE_NOTIFY  = 0x08,
+
+       IO_NOTIFY_MASK  = IO_DIR_NOTIFY | IO_FILE_NOTIFY
+};
+
 typedef void io_callback_t(void *context);
 typedef void timeout_callback_t(void *context);
 
@@ -22,8 +28,12 @@ extern struct timezone ioloop_timezone;
 
 /* I/O listeners - you can create different handlers for IO_READ and IO_WRITE,
    but make sure you don't create multiple handlers of same type, it's not
-   checked and removing one will stop the other from working as well. */
-struct io *io_add(int fd, int condition, io_callback_t *callback, void *context);
+   checked and removing one will stop the other from working as well.
+
+   If IO_DIR_NOTIFY or IO_FILE_NOTIFY isn't supported by operating system
+   directly, this function returns NULL. */
+struct io *io_add(int fd, enum io_condition condition,
+                 io_callback_t *callback, void *context);
 void io_remove(struct io *io);
 
 /* Timeout handlers */