From: Timo Sirainen Date: Sun, 24 Aug 2003 12:43:53 +0000 (+0300) Subject: Added IO_DIR_NOTIFY and IO_FILE_NOTIFY conditions to io_add(). IO_DIR_NOTIFY X-Git-Tag: 1.1.alpha1~4377 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9bf98814caf6ca9091a77e1eeb5d64bfc5c537af;p=thirdparty%2Fdovecot%2Fcore.git Added IO_DIR_NOTIFY and IO_FILE_NOTIFY conditions to io_add(). IO_DIR_NOTIFY is now implemented for Linux dnotify. --HG-- branch : HEAD --- diff --git a/configure.in b/configure.in index 40516c1eed..e372e2cc41 100644 --- a/configure.in +++ b/configure.in @@ -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 + #include + #include +], [ + 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*) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 5b597eed06..f6307ad9a1 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -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 \ diff --git a/src/lib/ioloop-internal.h b/src/lib/ioloop-internal.h index 6a2613118a..0f05fadf6b 100644 --- a/src/lib/ioloop-internal.h +++ b/src/lib/ioloop-internal.h @@ -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 index 0000000000..d2c77e0838 --- /dev/null +++ b/src/lib/ioloop-notify-dn.c @@ -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 +#include +#include + +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 index 0000000000..c2b6ec311e --- /dev/null +++ b/src/lib/ioloop-notify-none.c @@ -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 diff --git a/src/lib/ioloop-poll.c b/src/lib/ioloop-poll.c index bb1600ceba..dab47e8dfc 100644 --- a/src/lib/ioloop-poll.c +++ b/src/lib/ioloop-poll.c @@ -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; diff --git a/src/lib/ioloop-select.c b/src/lib/ioloop-select.c index 719eede2e8..210d106251 100644 --- a/src/lib/ioloop-select.c +++ b/src/lib/ioloop-select.c @@ -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(); diff --git a/src/lib/ioloop.c b/src/lib/ioloop.c index 735de50fe7..aa443a3f6e 100644 --- a/src/lib/ioloop.c +++ b/src/lib/ioloop.c @@ -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 = ¤t_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; diff --git a/src/lib/ioloop.h b/src/lib/ioloop.h index e3ebdb8e74..3f02301603 100644 --- a/src/lib/ioloop.h +++ b/src/lib/ioloop.h @@ -4,13 +4,19 @@ #include #include -#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 */