is now implemented for Linux dnotify.
--HG--
branch : HEAD
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*)
istream-file.c \
istream-mmap.c \
ioloop.c \
+ ioloop-notify-none.c \
+ ioloop-notify-dn.c \
ioloop-poll.c \
ioloop-select.c \
lib.c \
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;
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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
#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;
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;
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);
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);
{
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);
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();
}
}
-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;
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;
{
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;
#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);
/* 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 */