summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2012-02-07 15:03:56 +0000
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2012-02-07 15:04:25 +0000
commit88498b706a39bbe520f9591d8d52b54fb1f8e378 (patch)
tree5243eb3c97b8039d0ff43cd7121a632bbdb9d3bc
parentb816cb6e900073c6a6126687f2102dfd8e594e68 (diff)
parent3f4ed9def33c359142c340f28345755ca37663f2 (diff)
downloaddbus-88498b706a39bbe520f9591d8d52b54fb1f8e378.tar.gz
Merge branch 'socket-set-33337'
Reviewed-by: Will Thompson <will.thompson@collabora.co.uk> Bug: https://bugs.freedesktop.org/show_bug.cgi?id=33337
-rw-r--r--bus/activation.c12
-rw-r--r--bus/bus.c14
-rw-r--r--bus/connection.c11
-rw-r--r--bus/test.c9
-rw-r--r--configure.ac30
-rw-r--r--dbus/Makefile.am7
-rw-r--r--dbus/dbus-connection.h1
-rw-r--r--dbus/dbus-mainloop.c314
-rw-r--r--dbus/dbus-mainloop.h2
-rw-r--r--dbus/dbus-socket-set-epoll.c371
-rw-r--r--dbus/dbus-socket-set-poll.c320
-rw-r--r--dbus/dbus-socket-set.c47
-rw-r--r--dbus/dbus-socket-set.h122
-rw-r--r--dbus/dbus-watch.h2
-rw-r--r--test/test-utils.c27
15 files changed, 1124 insertions, 165 deletions
diff --git a/bus/activation.c b/bus/activation.c
index 2744e214..3dfba787 100644
--- a/bus/activation.c
+++ b/bus/activation.c
@@ -1427,6 +1427,16 @@ remove_babysitter_watch (DBusWatch *watch,
watch);
}
+static void
+toggle_babysitter_watch (DBusWatch *watch,
+ void *data)
+{
+ BusPendingActivation *pending_activation = data;
+
+ _dbus_loop_toggle_watch (bus_context_get_loop (pending_activation->activation->context),
+ watch);
+}
+
static dbus_bool_t
pending_activation_timed_out (void *data)
{
@@ -2110,7 +2120,7 @@ bus_activation_activate_service (BusActivation *activation,
if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
add_babysitter_watch,
remove_babysitter_watch,
- NULL,
+ toggle_babysitter_watch,
pending_activation,
NULL))
{
diff --git a/bus/bus.c b/bus/bus.c
index 358a9106..c7308b79 100644
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -126,6 +126,18 @@ remove_server_watch (DBusWatch *watch,
_dbus_loop_remove_watch (context->loop, watch);
}
+static void
+toggle_server_watch (DBusWatch *watch,
+ void *data)
+{
+ DBusServer *server = data;
+ BusContext *context;
+
+ context = server_get_context (server);
+
+ _dbus_loop_toggle_watch (context->loop, watch);
+}
+
static dbus_bool_t
add_server_timeout (DBusTimeout *timeout,
void *data)
@@ -228,7 +240,7 @@ setup_server (BusContext *context,
if (!dbus_server_set_watch_functions (server,
add_server_watch,
remove_server_watch,
- NULL,
+ toggle_server_watch,
server,
NULL))
{
diff --git a/bus/connection.c b/bus/connection.c
index 26839529..97e5f64b 100644
--- a/bus/connection.c
+++ b/bus/connection.c
@@ -327,6 +327,15 @@ remove_connection_watch (DBusWatch *watch,
_dbus_loop_remove_watch (connection_get_loop (connection), watch);
}
+static void
+toggle_connection_watch (DBusWatch *watch,
+ void *data)
+{
+ DBusConnection *connection = data;
+
+ _dbus_loop_toggle_watch (connection_get_loop (connection), watch);
+}
+
static dbus_bool_t
add_connection_timeout (DBusTimeout *timeout,
void *data)
@@ -631,7 +640,7 @@ bus_connections_setup_connection (BusConnections *connections,
if (!dbus_connection_set_watch_functions (connection,
add_connection_watch,
remove_connection_watch,
- NULL,
+ toggle_connection_watch,
connection,
NULL))
goto out;
diff --git a/bus/test.c b/bus/test.c
index e1b8a130..9a0c1967 100644
--- a/bus/test.c
+++ b/bus/test.c
@@ -50,6 +50,13 @@ remove_client_watch (DBusWatch *watch,
_dbus_loop_remove_watch (client_loop, watch);
}
+static void
+toggle_client_watch (DBusWatch *watch,
+ void *data)
+{
+ _dbus_loop_toggle_watch (client_loop, watch);
+}
+
static dbus_bool_t
add_client_timeout (DBusTimeout *timeout,
void *data)
@@ -112,7 +119,7 @@ bus_setup_debug_client (DBusConnection *connection)
if (!dbus_connection_set_watch_functions (connection,
add_client_watch,
remove_client_watch,
- NULL,
+ toggle_client_watch,
connection,
NULL))
goto out;
diff --git a/configure.ac b/configure.ac
index 3ca922b8..6ecea15e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1036,6 +1036,36 @@ fi
AM_CONDITIONAL(DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX, test x$have_dnotify = xyes)
+# For simplicity, we require the userland API for epoll_create1 at
+# compile-time (glibc 2.9), but we'll run on kernels that turn out
+# not to have it at runtime.
+AC_ARG_ENABLE([epoll],
+ [AS_HELP_STRING([--enable-epoll],[use epoll(4) on Linux])],
+ [enable_epoll=$enableval], [enable_epoll=auto])
+if test x$enable_epoll = xno; then
+ have_linux_epoll=no
+else
+ AC_MSG_CHECKING([for Linux epoll(4)])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [
+ #ifndef __linux__
+ #error This is not Linux
+ #endif
+ #include <sys/epoll.h>
+ ],
+ [epoll_create1 (EPOLL_CLOEXEC);])],
+ [have_linux_epoll=yes],
+ [have_linux_epoll=no])
+ AC_MSG_RESULT([$have_linux_epoll])
+fi
+if test x$enable_epoll,$have_linux_epoll = xyes,no; then
+ AC_MSG_ERROR([epoll support explicitly enabled but not available])
+fi
+if test x$have_linux_epoll = xyes; then
+ AC_DEFINE([DBUS_HAVE_LINUX_EPOLL], 1, [Define to use epoll(4) on Linux])
+fi
+AM_CONDITIONAL([HAVE_LINUX_EPOLL], [test x$have_linux_epoll = xyes])
+
# kqueue checks
if test x$enable_kqueue = xno ; then
have_kqueue=no
diff --git a/dbus/Makefile.am b/dbus/Makefile.am
index d3d1b95f..1c8fe8c8 100644
--- a/dbus/Makefile.am
+++ b/dbus/Makefile.am
@@ -111,6 +111,10 @@ DBUS_UTIL_arch_sources = \
dbus-spawn.c
endif
+if HAVE_LINUX_EPOLL
+DBUS_UTIL_arch_sources += dbus-socket-set-epoll.c
+endif
+
dbusinclude_HEADERS= \
dbus.h \
dbus-address.h \
@@ -239,6 +243,9 @@ DBUS_UTIL_SOURCES= \
dbus-shell.c \
dbus-shell.h \
$(DBUS_UTIL_arch_sources) \
+ dbus-socket-set.h \
+ dbus-socket-set.c \
+ dbus-socket-set-poll.c \
dbus-spawn.h \
dbus-string-util.c \
dbus-sysdeps-util.c \
diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h
index 3e2a7d8d..fe4d04ef 100644
--- a/dbus/dbus-connection.h
+++ b/dbus/dbus-connection.h
@@ -69,6 +69,7 @@ typedef enum
* state passed to
* dbus_watch_handle()).
*/
+ /* Internal to libdbus, there is also _DBUS_WATCH_NVAL in dbus-watch.h */
} DBusWatchFlags;
/**
diff --git a/dbus/dbus-mainloop.c b/dbus/dbus-mainloop.c
index 1a046a1f..579614de 100644
--- a/dbus/dbus-mainloop.c
+++ b/dbus/dbus-mainloop.c
@@ -1,7 +1,8 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-mainloop.c Main loop utility
*
- * Copyright (C) 2003, 2004 Red Hat, Inc.
+ * Copyright © 2003, 2004 Red Hat, Inc.
+ * Copyright © 2011 Nokia Corporation
*
* Licensed under the Academic Free License version 2.1
*
@@ -28,7 +29,7 @@
#include <dbus/dbus-hash.h>
#include <dbus/dbus-list.h>
-#include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-socket-set.h>
#include <dbus/dbus-watch.h>
#define MAINLOOP_SPEW 0
@@ -59,44 +60,18 @@ struct DBusLoop
int refcount;
/** fd => dbus_malloc'd DBusList ** of references to DBusWatch */
DBusHashTable *watches;
+ DBusSocketSet *socket_set;
DBusList *timeouts;
int callback_list_serial;
int watch_count;
int timeout_count;
int depth; /**< number of recursive runs */
DBusList *need_dispatch;
+ /** TRUE if we will skip a watch next time because it was OOM; becomes
+ * FALSE between polling, and dealing with the results of the poll */
+ unsigned oom_watch_pending : 1;
};
-static short
-watch_flags_to_poll_events (unsigned int flags)
-{
- short events = 0;
-
- if (flags & DBUS_WATCH_READABLE)
- events |= _DBUS_POLLIN;
- if (flags & DBUS_WATCH_WRITABLE)
- events |= _DBUS_POLLOUT;
-
- return events;
-}
-
-static unsigned int
-watch_flags_from_poll_revents (short revents)
-{
- unsigned int condition = 0;
-
- if (revents & _DBUS_POLLIN)
- condition |= DBUS_WATCH_READABLE;
- if (revents & _DBUS_POLLOUT)
- condition |= DBUS_WATCH_WRITABLE;
- if (revents & _DBUS_POLLHUP)
- condition |= DBUS_WATCH_HANGUP;
- if (revents & _DBUS_POLLERR)
- condition |= DBUS_WATCH_ERROR;
-
- return condition;
-}
-
typedef struct
{
DBusTimeout *timeout;
@@ -161,8 +136,16 @@ _dbus_loop_new (void)
loop->watches = _dbus_hash_table_new (DBUS_HASH_INT, NULL,
free_watch_table_entry);
- if (loop->watches == NULL)
+ loop->socket_set = _dbus_socket_set_new (0);
+
+ if (loop->watches == NULL || loop->socket_set == NULL)
{
+ if (loop->watches != NULL)
+ _dbus_hash_table_unref (loop->watches);
+
+ if (loop->socket_set != NULL)
+ _dbus_socket_set_free (loop->socket_set);
+
dbus_free (loop);
return NULL;
}
@@ -200,6 +183,7 @@ _dbus_loop_unref (DBusLoop *loop)
}
_dbus_hash_table_unref (loop->watches);
+ _dbus_socket_set_free (loop->socket_set);
dbus_free (loop);
}
}
@@ -250,20 +234,57 @@ cull_watches_for_invalid_fd (DBusLoop *loop,
_dbus_hash_table_remove_int (loop->watches, fd);
}
-static void
+static dbus_bool_t
gc_watch_table_entry (DBusLoop *loop,
DBusList **watches,
int fd)
{
/* If watches is already NULL we have nothing to do */
if (watches == NULL)
- return;
+ return FALSE;
/* We can't GC hash table entries if they're non-empty lists */
if (*watches != NULL)
- return;
+ return FALSE;
_dbus_hash_table_remove_int (loop->watches, fd);
+ return TRUE;
+}
+
+static void
+refresh_watches_for_fd (DBusLoop *loop,
+ DBusList **watches,
+ int fd)
+{
+ DBusList *link;
+ unsigned int flags = 0;
+ dbus_bool_t interested;
+
+ _dbus_assert (fd != -1);
+
+ if (watches == NULL)
+ watches = _dbus_hash_table_lookup_int (loop->watches, fd);
+
+ /* we allocated this in the first _dbus_loop_add_watch for the fd, and keep
+ * it until there are none left */
+ _dbus_assert (watches != NULL);
+
+ for (link = _dbus_list_get_first_link (watches);
+ link != NULL;
+ link = _dbus_list_get_next_link (watches, link))
+ {
+ if (dbus_watch_get_enabled (link->data) &&
+ !_dbus_watch_get_oom_last_time (link->data))
+ {
+ flags |= dbus_watch_get_flags (link->data);
+ interested = TRUE;
+ }
+ }
+
+ if (interested)
+ _dbus_socket_set_enable (loop->socket_set, fd, flags);
+ else
+ _dbus_socket_set_disable (loop->socket_set, fd);
}
dbus_bool_t
@@ -281,23 +302,43 @@ _dbus_loop_add_watch (DBusLoop *loop,
if (watches == NULL)
return FALSE;
- if (_dbus_list_append (watches, _dbus_watch_ref (watch)))
- {
- loop->callback_list_serial += 1;
- loop->watch_count += 1;
- }
- else
+ if (!_dbus_list_append (watches, _dbus_watch_ref (watch)))
{
_dbus_watch_unref (watch);
gc_watch_table_entry (loop, watches, fd);
- return FALSE;
- }
+ return FALSE;
+ }
+
+ if (_dbus_list_length_is_one (watches))
+ {
+ if (!_dbus_socket_set_add (loop->socket_set, fd,
+ dbus_watch_get_flags (watch),
+ dbus_watch_get_enabled (watch)))
+ {
+ _dbus_hash_table_remove_int (loop->watches, fd);
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* we're modifying, not adding, which can't fail with OOM */
+ refresh_watches_for_fd (loop, watches, fd);
+ }
+ loop->callback_list_serial += 1;
+ loop->watch_count += 1;
return TRUE;
}
void
+_dbus_loop_toggle_watch (DBusLoop *loop,
+ DBusWatch *watch)
+{
+ refresh_watches_for_fd (loop, NULL, dbus_watch_get_socket (watch));
+}
+
+void
_dbus_loop_remove_watch (DBusLoop *loop,
DBusWatch *watch)
{
@@ -329,8 +370,11 @@ _dbus_loop_remove_watch (DBusLoop *loop,
_dbus_watch_unref (this);
/* if that was the last watch for that fd, drop the hash table
- * entry too */
- gc_watch_table_entry (loop, watches, fd);
+ * entry, and stop reserving space for it in the socket set */
+ if (gc_watch_table_entry (loop, watches, fd))
+ {
+ _dbus_socket_set_remove (loop->socket_set, fd);
+ }
return;
}
@@ -548,23 +592,16 @@ _dbus_loop_iterate (DBusLoop *loop,
{
#define N_STACK_DESCRIPTORS 64
dbus_bool_t retval;
- DBusPollFD *fds;
- DBusPollFD stack_fds[N_STACK_DESCRIPTORS];
- int n_fds;
+ DBusSocketEvent ready_fds[N_STACK_DESCRIPTORS];
int i;
DBusList *link;
int n_ready;
int initial_serial;
long timeout;
- dbus_bool_t oom_watch_pending;
int orig_depth;
- DBusHashIter hash_iter;
retval = FALSE;
- fds = NULL;
- n_fds = 0;
- oom_watch_pending = FALSE;
orig_depth = loop->depth;
#if MAINLOOP_SPEW
@@ -576,87 +613,6 @@ _dbus_loop_iterate (DBusLoop *loop,
loop->timeouts == NULL)
goto next_iteration;
- if (loop->watch_count > N_STACK_DESCRIPTORS)
- {
- fds = dbus_new0 (DBusPollFD, loop->watch_count);
-
- while (fds == NULL)
- {
- _dbus_wait_for_memory ();
- fds = dbus_new0 (DBusPollFD, loop->watch_count);
- }
- }
- else
- {
- fds = stack_fds;
- }
-
- /* fill our array of fds and watches */
- n_fds = 0;
- _dbus_hash_iter_init (loop->watches, &hash_iter);
-
- while (_dbus_hash_iter_next (&hash_iter))
- {
- DBusList **watches;
- unsigned int flags;
- int fd;
-
- fd = _dbus_hash_iter_get_int_key (&hash_iter);
- watches = _dbus_hash_iter_get_value (&hash_iter);
- flags = 0;
-
- for (link = _dbus_list_get_first_link (watches);
- link != NULL;
- link = _dbus_list_get_next_link (watches, link))
- {
- DBusWatch *watch = link->data;
-
- if (_dbus_watch_get_oom_last_time (watch))
- {
- /* we skip this one this time, but reenable it next time,
- * and have a timeout on this iteration
- */
- _dbus_watch_set_oom_last_time (watch, FALSE);
- oom_watch_pending = TRUE;
-
- retval = TRUE; /* return TRUE here to keep the loop going,
- * since we don't know the watch is inactive
- */
-
-#if MAINLOOP_SPEW
- _dbus_verbose (" skipping watch on fd %d as it was out of memory last time\n",
- fd);
-#endif
- }
- else if (dbus_watch_get_enabled (watch))
- {
- flags |= dbus_watch_get_flags (watch);
- }
- }
-
- if (flags != 0)
- {
- fds[n_fds].fd = fd;
- fds[n_fds].revents = 0;
- fds[n_fds].events = watch_flags_to_poll_events (flags);
-
-#if MAINLOOP_SPEW
- _dbus_verbose (" polling watch on fd %d %s\n",
- loop->fds[loop->n_fds].fd, watch_flags_to_string (flags));
-#endif
-
- n_fds += 1;
- }
- else
- {
-#if MAINLOOP_SPEW
- _dbus_verbose (" skipping disabled watch on fd %d %s\n",
- fd,
- watch_flags_to_string (dbus_watch_get_flags (watch)));
-#endif
- }
- }
-
timeout = -1;
if (loop->timeout_count > 0)
{
@@ -712,17 +668,58 @@ _dbus_loop_iterate (DBusLoop *loop,
#endif
}
- /* if a watch is OOM, don't wait longer than the OOM
+ /* if a watch was OOM last time, don't wait longer than the OOM
* wait to re-enable it
*/
- if (oom_watch_pending)
+ if (loop->oom_watch_pending)
timeout = MIN (timeout, _dbus_get_oom_wait ());
#if MAINLOOP_SPEW
_dbus_verbose (" polling on %d descriptors timeout %ld\n", n_fds, timeout);
#endif
-
- n_ready = _dbus_poll (fds, n_fds, timeout);
+
+ n_ready = _dbus_socket_set_poll (loop->socket_set, ready_fds,
+ _DBUS_N_ELEMENTS (ready_fds), timeout);
+
+ /* re-enable any watches we skipped this time */
+ if (loop->oom_watch_pending)
+ {
+ DBusHashIter hash_iter;
+
+ loop->oom_watch_pending = FALSE;
+
+ _dbus_hash_iter_init (loop->watches, &hash_iter);
+
+ while (_dbus_hash_iter_next (&hash_iter))
+ {
+ DBusList **watches;
+ int fd;
+ dbus_bool_t changed;
+
+ changed = FALSE;
+ fd = _dbus_hash_iter_get_int_key (&hash_iter);
+ watches = _dbus_hash_iter_get_value (&hash_iter);
+
+ for (link = _dbus_list_get_first_link (watches);
+ link != NULL;
+ link = _dbus_list_get_next_link (watches, link))
+ {
+ DBusWatch *watch = link->data;
+
+ if (_dbus_watch_get_oom_last_time (watch))
+ {
+ _dbus_watch_set_oom_last_time (watch, FALSE);
+ changed = TRUE;
+ }
+ }
+
+ if (changed)
+ refresh_watches_for_fd (loop, watches, fd);
+ }
+
+ retval = TRUE; /* return TRUE here to keep the loop going,
+ * since we don't know the watch was inactive */
+ }
initial_serial = loop->callback_list_serial;
@@ -785,14 +782,15 @@ _dbus_loop_iterate (DBusLoop *loop,
link = next;
}
}
-
+
if (n_ready > 0)
{
- for (i = 0; i < n_fds; i++)
+ for (i = 0; i < n_ready; i++)
{
DBusList **watches;
DBusList *next;
unsigned int condition;
+ dbus_bool_t any_oom;
/* FIXME I think this "restart if we change the watches"
* approach could result in starving watches
@@ -804,16 +802,16 @@ _dbus_loop_iterate (DBusLoop *loop,
if (loop->depth != orig_depth)
goto next_iteration;
- if (fds[i].revents == 0)
- continue;
+ _dbus_assert (ready_fds[i].flags != 0);
- if (_DBUS_UNLIKELY (fds[i].revents & _DBUS_POLLNVAL))
+ if (_DBUS_UNLIKELY (ready_fds[i].flags & _DBUS_WATCH_NVAL))
{
- cull_watches_for_invalid_fd (loop, fds[i].fd);
+ cull_watches_for_invalid_fd (loop, ready_fds[i].fd);
goto next_iteration;
}
- condition = watch_flags_from_poll_revents (fds[i].revents);
+ condition = ready_fds[i].flags;
+ _dbus_assert ((condition & _DBUS_WATCH_NVAL) == 0);
/* condition may still be 0 if we got some
* weird POLLFOO thing like POLLWRBAND
@@ -821,11 +819,14 @@ _dbus_loop_iterate (DBusLoop *loop,
if (condition == 0)
continue;
- watches = _dbus_hash_table_lookup_int (loop->watches, fds[i].fd);
+ watches = _dbus_hash_table_lookup_int (loop->watches,
+ ready_fds[i].fd);
if (watches == NULL)
continue;
+ any_oom = FALSE;
+
for (link = _dbus_list_get_first_link (watches);
link != NULL;
link = next)
@@ -843,6 +844,8 @@ _dbus_loop_iterate (DBusLoop *loop,
if (oom)
{
_dbus_watch_set_oom_last_time (watch, TRUE);
+ loop->oom_watch_pending = TRUE;
+ any_oom = TRUE;
}
#if MAINLOOP_SPEW
@@ -853,13 +856,19 @@ _dbus_loop_iterate (DBusLoop *loop,
/* We re-check this every time, in case the callback
* added/removed watches, which might make our position in
* the linked list invalid. See the FIXME above. */
- if (initial_serial != loop->callback_list_serial)
- goto next_iteration;
+ if (initial_serial != loop->callback_list_serial ||
+ loop->depth != orig_depth)
+ {
+ if (any_oom)
+ refresh_watches_for_fd (loop, NULL, ready_fds[i].fd);
- if (loop->depth != orig_depth)
- goto next_iteration;
+ goto next_iteration;
+ }
}
}
+
+ if (any_oom)
+ refresh_watches_for_fd (loop, watches, ready_fds[i].fd);
}
}
@@ -867,9 +876,6 @@ _dbus_loop_iterate (DBusLoop *loop,
#if MAINLOOP_SPEW
_dbus_verbose (" moving to next iteration\n");
#endif
-
- if (fds && fds != stack_fds)
- dbus_free (fds);
if (_dbus_loop_dispatch (loop))
retval = TRUE;
diff --git a/dbus/dbus-mainloop.h b/dbus/dbus-mainloop.h
index a3417adb..a76cb6f0 100644
--- a/dbus/dbus-mainloop.h
+++ b/dbus/dbus-mainloop.h
@@ -41,6 +41,8 @@ dbus_bool_t _dbus_loop_add_watch (DBusLoop *loop,
DBusWatch *watch);
void _dbus_loop_remove_watch (DBusLoop *loop,
DBusWatch *watch);
+void _dbus_loop_toggle_watch (DBusLoop *loop,
+ DBusWatch *watch);
dbus_bool_t _dbus_loop_add_timeout (DBusLoop *loop,
DBusTimeout *timeout);
void _dbus_loop_remove_timeout (DBusLoop *loop,
diff --git a/dbus/dbus-socket-set-epoll.c b/dbus/dbus-socket-set-epoll.c
new file mode 100644
index 00000000..4cd9a563
--- /dev/null
+++ b/dbus/dbus-socket-set-epoll.c
@@ -0,0 +1,371 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-socket-set-epoll.c - a socket set implemented via Linux epoll(4)
+ *
+ * Copyright © 2011 Nokia Corporation
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ */
+
+#include <config.h>
+#include "dbus-socket-set.h"
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-sysdeps.h>
+
+#ifndef __linux__
+# error This file is for Linux epoll(4)
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+typedef struct {
+ DBusSocketSet parent;
+ int epfd;
+} DBusSocketSetEpoll;
+
+static inline DBusSocketSetEpoll *
+socket_set_epoll_cast (DBusSocketSet *set)
+{
+ _dbus_assert (set->cls == &_dbus_socket_set_epoll_class);
+ return (DBusSocketSetEpoll *) set;
+}
+
+/* this is safe to call on a partially-allocated socket set */
+static void
+socket_set_epoll_free (DBusSocketSet *set)
+{
+ DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
+
+ if (self == NULL)
+ return;
+
+ if (self->epfd != -1)
+ close (self->epfd);
+
+ dbus_free (self);
+}
+
+DBusSocketSet *
+_dbus_socket_set_epoll_new (void)
+{
+ DBusSocketSetEpoll *self;
+
+ self = dbus_new0 (DBusSocketSetEpoll, 1);
+
+ if (self == NULL)
+ return NULL;
+
+ self->parent.cls = &_dbus_socket_set_epoll_class;
+
+ self->epfd = epoll_create1 (EPOLL_CLOEXEC);
+
+ if (self->epfd == -1)
+ {
+ int flags;
+
+ /* the size hint is ignored unless you have a rather old kernel,
+ * but must be positive on some versions, so just pick something
+ * arbitrary; it's a hint, not a limit */
+ self->epfd = epoll_create (42);
+
+ flags = fcntl (self->epfd, F_GETFD, 0);
+
+ if (flags != -1)
+ fcntl (self->epfd, F_SETFD, flags | FD_CLOEXEC);
+ }
+
+ if (self->epfd == -1)
+ {
+ socket_set_epoll_free ((DBusSocketSet *) self);
+ return NULL;
+ }
+
+ return (DBusSocketSet *) self;
+}
+
+static uint32_t
+watch_flags_to_epoll_events (unsigned int flags)
+{
+ uint32_t events = 0;
+
+ if (flags & DBUS_WATCH_READABLE)
+ events |= EPOLLIN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ events |= EPOLLOUT;
+
+ return events;
+}
+
+static unsigned int
+epoll_events_to_watch_flags (uint32_t events)
+{
+ short flags = 0;
+
+ if (events & EPOLLIN)
+ flags |= DBUS_WATCH_READABLE;
+ if (events & EPOLLOUT)
+ flags |= DBUS_WATCH_WRITABLE;
+ if (events & EPOLLHUP)
+ flags |= DBUS_WATCH_HANGUP;
+ if (events & EPOLLERR)
+ flags |= DBUS_WATCH_ERROR;
+
+ return flags;
+}
+
+static dbus_bool_t
+socket_set_epoll_add (DBusSocketSet *set,
+ int fd,
+ unsigned int flags,
+ dbus_bool_t enabled)
+{
+ DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
+ struct epoll_event event;
+ int err;
+
+ event.data.fd = fd;
+
+ if (enabled)
+ {
+ event.events = watch_flags_to_epoll_events (flags);
+ }
+ else
+ {
+ /* We need to add *something* to reserve space in the kernel's data
+ * structures: see socket_set_epoll_disable for more details */
+ event.events = EPOLLET;
+ }
+
+ if (epoll_ctl (self->epfd, EPOLL_CTL_ADD, fd, &event) == 0)
+ return TRUE;
+
+ /* Anything except ENOMEM, ENOSPC means we have an internal error. */
+ err = errno;
+ switch (err)
+ {
+ case ENOMEM:
+ case ENOSPC:
+ /* be silent: this is basically OOM, which our callers are expected
+ * to cope with */
+ break;
+
+ case EBADF:
+ _dbus_warn ("Bad fd %d\n", fd);
+ break;
+
+ case EEXIST:
+ _dbus_warn ("fd %d added and then added again\n", fd);
+ break;
+
+ default:
+ _dbus_warn ("Misc error when trying to watch fd %d: %s\n", fd,
+ strerror (err));
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+socket_set_epoll_enable (DBusSocketSet *set,
+ int fd,
+ unsigned int flags)
+{
+ DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
+ struct epoll_event event;
+ int err;
+
+ event.data.fd = fd;
+ event.events = watch_flags_to_epoll_events (flags);
+
+ if (epoll_ctl (self->epfd, EPOLL_CTL_MOD, fd, &event) == 0)
+ return;
+
+ err = errno;
+
+ /* Enabling a file descriptor isn't allowed to fail, even for OOM, so we
+ * do our best to avoid all of these. */
+ switch (err)
+ {
+ case EBADF:
+ _dbus_warn ("Bad fd %d\n", fd);
+ break;
+
+ case ENOENT:
+ _dbus_warn ("fd %d enabled before it was added\n", fd);
+ break;
+
+ case ENOMEM:
+ _dbus_warn ("Insufficient memory to change watch for fd %d\n", fd);
+ break;
+
+ default:
+ _dbus_warn ("Misc error when trying to watch fd %d: %s\n", fd,
+ strerror (err));
+ break;
+ }
+}
+
+static void
+socket_set_epoll_disable (DBusSocketSet *set,
+ int fd)
+{
+ DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
+ struct epoll_event event;
+ int err;
+
+ /* The naive thing to do would be EPOLL_CTL_DEL, but that'll probably
+ * free resources in the kernel. When we come to do socket_set_epoll_enable,
+ * there might not be enough resources to bring it back!
+ *
+ * The next idea you might have is to set the flags to 0. However, events
+ * always trigger on EPOLLERR and EPOLLHUP, even if libdbus isn't actually
+ * delivering them to a DBusWatch. Because epoll is level-triggered by
+ * default, we'll busy-loop on an unhandled error or hangup; not good.
+ *
+ * So, let's set it to be edge-triggered: then the worst case is that
+ * we return from poll immediately on one iteration, ignore it because no
+ * watch is enabled, then go back to normal. When we re-enable a watch
+ * we'll switch back to level-triggered and be notified again (verified to
+ * work on 2.6.32). Compile this file with -DTEST_BEHAVIOUR_OF_EPOLLET for
+ * test code.
+ */
+ event.data.fd = fd;
+ event.events = EPOLLET;
+
+ if (epoll_ctl (self->epfd, EPOLL_CTL_MOD, fd, &event) == 0)
+ return;
+
+ err = errno;
+ _dbus_warn ("Error when trying to watch fd %d: %s\n", fd,
+ strerror (err));
+}
+
+static void
+socket_set_epoll_remove (DBusSocketSet *set,
+ int fd)
+{
+ DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
+ int err;
+ /* Kernels < 2.6.9 require a non-NULL struct pointer, even though its
+ * contents are ignored */
+ struct epoll_event dummy = { 0 };
+
+ if (epoll_ctl (self->epfd, EPOLL_CTL_DEL, fd, &dummy) == 0)
+ return;
+
+ err = errno;
+ _dbus_warn ("Error when trying to remove fd %d: %s\n", fd, strerror (err));
+}
+
+/* Optimally, this should be the same as in DBusLoop: we use it to translate
+ * between struct epoll_event and DBusSocketEvent without allocating heap
+ * memory. */
+#define N_STACK_DESCRIPTORS 64
+
+static int
+socket_set_epoll_poll (DBusSocketSet *set,
+ DBusSocketEvent *revents,
+ int max_events,
+ int timeout_ms)
+{
+ DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
+ struct epoll_event events[N_STACK_DESCRIPTORS];
+ int n_ready;
+ int i;
+
+ _dbus_assert (max_events > 0);
+
+ n_ready = epoll_wait (self->epfd, events,
+ MIN (_DBUS_N_ELEMENTS (events), max_events),
+ timeout_ms);
+
+ if (n_ready <= 0)
+ return n_ready;
+
+ for (i = 0; i < n_ready; i++)
+ {
+ revents[i].fd = events[i].data.fd;
+ revents[i].flags = epoll_events_to_watch_flags (events[i].events);
+ }
+
+ return n_ready;
+}
+
+DBusSocketSetClass _dbus_socket_set_epoll_class = {
+ socket_set_epoll_free,
+ socket_set_epoll_add,
+ socket_set_epoll_remove,
+ socket_set_epoll_enable,
+ socket_set_epoll_disable,
+ socket_set_epoll_poll
+};
+
+#ifdef TEST_BEHAVIOUR_OF_EPOLLET
+/* usage: cat /dev/null | ./epoll
+ *
+ * desired output:
+ * ctl ADD: 0
+ * wait for HUP, edge-triggered: 1
+ * wait for HUP again: 0
+ * ctl MOD: 0
+ * wait for HUP: 1
+ */
+
+#include <sys/epoll.h>
+
+#include <stdio.h>
+
+int
+main (void)
+{
+ struct epoll_event input;
+ struct epoll_event output;
+ int epfd = epoll_create1 (EPOLL_CLOEXEC);
+ int fd = 0; /* stdin */
+ int ret;
+
+ input.events = EPOLLHUP | EPOLLET;
+ ret = epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &input);
+ printf ("ctl ADD: %d\n", ret);
+
+ ret = epoll_wait (epfd, &output, 1, -1);
+ printf ("wait for HUP, edge-triggered: %d\n", ret);
+
+ ret = epoll_wait (epfd, &output, 1, 1);
+ printf ("wait for HUP again: %d\n", ret);
+
+ input.events = EPOLLHUP;
+ ret = epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &input);
+ printf ("ctl MOD: %d\n", ret);
+
+ ret = epoll_wait (epfd, &output, 1, -1);
+ printf ("wait for HUP: %d\n", ret);
+
+ return 0;
+}
+
+#endif /* TEST_BEHAVIOUR_OF_EPOLLET */
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
diff --git a/dbus/dbus-socket-set-poll.c b/dbus/dbus-socket-set-poll.c
new file mode 100644
index 00000000..65a1fd23
--- /dev/null
+++ b/dbus/dbus-socket-set-poll.c
@@ -0,0 +1,320 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-socket-set-poll.c - a socket set implemented via _dbus_poll
+ *
+ * Copyright © 2011 Nokia Corporation
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ */
+
+#include <config.h>
+#include "dbus-socket-set.h"
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-watch.h>
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+typedef struct {
+ DBusSocketSet parent;
+ DBusPollFD *fds;
+ int n_fds;
+ int n_reserved;
+ int n_allocated;
+} DBusSocketSetPoll;
+
+#define REALLOC_INCREMENT 8
+#define MINIMUM_SIZE 8
+
+/* If we're in the regression tests, force reallocation to happen sooner */
+#ifdef DBUS_BUILD_TESTS
+#define DEFAULT_SIZE_HINT 1
+#else
+#define DEFAULT_SIZE_HINT MINIMUM_SIZE
+#endif
+
+static inline DBusSocketSetPoll *
+socket_set_poll_cast (DBusSocketSet *set)
+{
+ _dbus_assert (set->cls == &_dbus_socket_set_poll_class);
+ return (DBusSocketSetPoll *) set;
+}
+
+/* this is safe to call on a partially-allocated socket set */
+static void
+socket_set_poll_free (DBusSocketSet *set)
+{
+ DBusSocketSetPoll *self = socket_set_poll_cast (set);
+
+ dbus_free (self->fds);
+ dbus_free (self);
+ _dbus_verbose ("freed socket set %p\n", self);
+}
+
+DBusSocketSet *
+_dbus_socket_set_poll_new (int size_hint)
+{
+ DBusSocketSetPoll *ret;
+
+ if (size_hint <= 0)
+ size_hint = DEFAULT_SIZE_HINT;
+
+ ret = dbus_new0 (DBusSocketSetPoll, 1);
+
+ if (ret == NULL)
+ return NULL;
+
+ ret->parent.cls = &_dbus_socket_set_poll_class;
+ ret->n_fds = 0;
+ ret->n_allocated = size_hint;
+
+ ret->fds = dbus_new0 (DBusPollFD, size_hint);
+
+ if (ret->fds == NULL)
+ {
+ /* socket_set_poll_free specifically supports half-constructed
+ * socket sets */
+ socket_set_poll_free ((DBusSocketSet *) ret);
+ return NULL;
+ }
+
+ _dbus_verbose ("new socket set at %p\n", ret);
+ return (DBusSocketSet *) ret;
+}
+
+static short
+watch_flags_to_poll_events (unsigned int flags)
+{
+ short events = 0;
+
+ if (flags & DBUS_WATCH_READABLE)
+ events |= _DBUS_POLLIN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ events |= _DBUS_POLLOUT;
+
+ return events;
+}
+
+static dbus_bool_t
+socket_set_poll_add (DBusSocketSet *set,
+ int fd,
+ unsigned int flags,
+ dbus_bool_t enabled)
+{
+ DBusSocketSetPoll *self = socket_set_poll_cast (set);
+#ifndef DBUS_DISABLE_ASSERT
+ int i;
+
+ for (i = 0; i < self->n_fds; i++)
+ _dbus_assert (self->fds[i].fd != fd);
+#endif
+
+ if (self->n_reserved >= self->n_allocated)
+ {
+ DBusPollFD *new_fds = dbus_realloc (self->fds,
+ sizeof (DBusPollFD) * (self->n_allocated + REALLOC_INCREMENT));
+
+ _dbus_verbose ("inflating set %p from %d en/%d res/%d alloc to %d\n",
+ self, self->n_fds, self->n_reserved, self->n_allocated,
+ self->n_allocated + REALLOC_INCREMENT);
+
+ if (new_fds == NULL)
+ return FALSE;
+
+ self->fds = new_fds;
+ self->n_allocated += REALLOC_INCREMENT;
+ }
+
+ _dbus_verbose ("before adding fd %d to %p, %d en/%d res/%d alloc\n",
+ fd, self, self->n_fds, self->n_reserved, self->n_allocated);
+ _dbus_assert (self->n_reserved >= self->n_fds);
+ _dbus_assert (self->n_allocated > self->n_reserved);
+
+ self->n_reserved++;
+
+ if (enabled)
+ {
+ self->fds[self->n_fds].fd = fd;
+ self->fds[self->n_fds].events = watch_flags_to_poll_events (flags);
+ self->n_fds++;
+ }
+
+ return TRUE;
+}
+
+static void
+socket_set_poll_enable (DBusSocketSet *set,
+ int fd,
+ unsigned int flags)
+{
+ DBusSocketSetPoll *self = socket_set_poll_cast (set);
+ int i;
+
+ for (i = 0; i < self->n_fds; i++)
+ {
+ if (self->fds[i].fd == fd)
+ {
+ self->fds[i].events = watch_flags_to_poll_events (flags);
+ return;
+ }
+ }
+
+ /* we allocated space when the socket was added */
+ _dbus_assert (self->n_fds < self->n_reserved);
+ _dbus_assert (self->n_reserved <= self->n_allocated);
+
+ self->fds[self->n_fds].fd = fd;
+ self->fds[self->n_fds].events = watch_flags_to_poll_events (flags);
+ self->n_fds++;
+}
+
+static void
+socket_set_poll_disable (DBusSocketSet *set,
+ int fd)
+{
+ DBusSocketSetPoll *self = socket_set_poll_cast (set);
+ int i;
+
+ for (i = 0; i < self->n_fds; i++)
+ {
+ if (self->fds[i].fd == fd)
+ {
+ if (i != self->n_fds - 1)
+ {
+ self->fds[i].fd = self->fds[self->n_fds - 1].fd;
+ self->fds[i].events = self->fds[self->n_fds - 1].events;
+ }
+
+ self->n_fds--;
+ return;
+ }
+ }
+}
+
+static void
+socket_set_poll_remove (DBusSocketSet *set,
+ int fd)
+{
+ DBusSocketSetPoll *self = socket_set_poll_cast (set);
+
+ socket_set_poll_disable (set, fd);
+ self->n_reserved--;
+
+ _dbus_verbose ("after removing fd %d from %p, %d en/%d res/%d alloc\n",
+ fd, self, self->n_fds, self->n_reserved, self->n_allocated);
+ _dbus_assert (self->n_fds <= self->n_reserved);
+ _dbus_assert (self->n_reserved <= self->n_allocated);
+
+ if (self->n_reserved + MINIMUM_SIZE < self->n_allocated / 2)
+ {
+ /* Our array is twice as big as it needs to be - deflate it until it's
+ * only slightly larger than the number reserved. */
+ DBusPollFD *new_fds = dbus_realloc (self->fds,
+ sizeof (DBusPollFD) * (self->n_reserved + MINIMUM_SIZE));
+
+ _dbus_verbose ("before deflating %p, %d en/%d res/%d alloc\n",
+ self, self->n_fds, self->n_reserved, self->n_allocated);
+
+ if (_DBUS_UNLIKELY (new_fds == NULL))
+ {
+ /* Weird. Oh well, never mind, the too-big array is untouched */
+ return;
+ }
+
+ self->fds = new_fds;
+ self->n_allocated = self->n_reserved;
+ }
+}
+
+static unsigned int
+watch_flags_from_poll_revents (short revents)
+{
+ unsigned int condition = 0;
+
+ if (revents & _DBUS_POLLIN)
+ condition |= DBUS_WATCH_READABLE;
+ if (revents & _DBUS_POLLOUT)
+ condition |= DBUS_WATCH_WRITABLE;
+ if (revents & _DBUS_POLLHUP)
+ condition |= DBUS_WATCH_HANGUP;
+ if (revents & _DBUS_POLLERR)
+ condition |= DBUS_WATCH_ERROR;
+
+ if (_DBUS_UNLIKELY (revents & _DBUS_POLLNVAL))
+ condition |= _DBUS_WATCH_NVAL;
+
+ return condition;
+}
+
+/** This is basically Linux's epoll_wait(2) implemented in terms of poll(2);
+ * it returns results into a caller-supplied buffer so we can be reentrant. */
+static int
+socket_set_poll_poll (DBusSocketSet *set,
+ DBusSocketEvent *revents,
+ int max_events,
+ int timeout_ms)
+{
+ DBusSocketSetPoll *self = socket_set_poll_cast (set);
+ int i;
+ int n_events;
+ int n_ready;
+
+ _dbus_assert (max_events > 0);
+
+ for (i = 0; i < self->n_fds; i++)
+ self->fds[i].revents = 0;
+
+ n_ready = _dbus_poll (self->fds, self->n_fds, timeout_ms);
+
+ if (n_ready <= 0)
+ return n_ready;
+
+ n_events = 0;
+
+ for (i = 0; i < self->n_fds; i++)
+ {
+ if (self->fds[i].revents != 0)
+ {
+ revents[n_events].fd = self->fds[i].fd;
+ revents[n_events].flags = watch_flags_from_poll_revents (self->fds[i].revents);
+
+ n_events += 1;
+
+ /* We ignore events beyond max_events because we have nowhere to
+ * put them. _dbus_poll is level-triggered, so we'll just be told
+ * about them next time round the main loop anyway. */
+ if (n_events == max_events)
+ return n_events;
+ }
+ }
+
+ return n_events;
+}
+
+DBusSocketSetClass _dbus_socket_set_poll_class = {
+ socket_set_poll_free,
+ socket_set_poll_add,
+ socket_set_poll_remove,
+ socket_set_poll_enable,
+ socket_set_poll_disable,
+ socket_set_poll_poll
+};
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
diff --git a/dbus/dbus-socket-set.c b/dbus/dbus-socket-set.c
new file mode 100644
index 00000000..210d600e
--- /dev/null
+++ b/dbus/dbus-socket-set.c
@@ -0,0 +1,47 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * dbus-socket-set.c - used to bolt file descriptors onto a bus
+ *
+ * Copyright © 2011 Nokia Corporation
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ */
+
+#include <config.h>
+#include <dbus/dbus-socket-set.h>
+
+DBusSocketSet *
+_dbus_socket_set_new (int size_hint)
+{
+ DBusSocketSet *ret;
+
+#ifdef DBUS_HAVE_LINUX_EPOLL
+ ret = _dbus_socket_set_epoll_new ();
+
+ if (ret != NULL)
+ return ret;
+#endif
+
+ ret = _dbus_socket_set_poll_new (size_hint);
+
+ if (ret != NULL)
+ return ret;
+
+ return NULL;
+}
diff --git a/dbus/dbus-socket-set.h b/dbus/dbus-socket-set.h
new file mode 100644
index 00000000..3b71a925
--- /dev/null
+++ b/dbus/dbus-socket-set.h
@@ -0,0 +1,122 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * dbus-socket-set.h - used to bolt file descriptors onto a bus
+ *
+ * Copyright © 2011 Nokia Corporation
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ */
+
+#ifndef DBUS_SOCKET_SET_H
+#define DBUS_SOCKET_SET_H
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include <dbus/dbus.h>
+
+typedef struct {
+ int fd;
+ unsigned int flags;
+} DBusSocketEvent;
+
+typedef struct DBusSocketSet DBusSocketSet;
+
+typedef struct DBusSocketSetClass DBusSocketSetClass;
+struct DBusSocketSetClass {
+ void (*free) (DBusSocketSet *self);
+ dbus_bool_t (*add) (DBusSocketSet *self,
+ int fd,
+ unsigned int flags,
+ dbus_bool_t enabled);
+ void (*remove) (DBusSocketSet *self,
+ int fd);
+ void (*enable) (DBusSocketSet *self,
+ int fd,
+ unsigned int flags);
+ void (*disable) (DBusSocketSet *self,
+ int fd);
+ int (*poll) (DBusSocketSet *self,
+ DBusSocketEvent *revents,
+ int max_events,
+ int timeout_ms);
+};
+
+struct DBusSocketSet {
+ DBusSocketSetClass *cls;
+};
+
+DBusSocketSet *_dbus_socket_set_new (int size_hint);
+
+static inline void
+_dbus_socket_set_free (DBusSocketSet *self)
+{
+ (self->cls->free) (self);
+}
+
+static inline dbus_bool_t
+_dbus_socket_set_add (DBusSocketSet *self,
+ int fd,
+ unsigned int flags,
+ dbus_bool_t enabled)
+{
+ return (self->cls->add) (self, fd, flags, enabled);
+}
+
+static inline void
+_dbus_socket_set_remove (DBusSocketSet *self,
+ int fd)
+{
+ (self->cls->remove) (self, fd);
+}
+
+static inline void
+_dbus_socket_set_enable (DBusSocketSet *self,
+ int fd,
+ unsigned int flags)
+{
+ (self->cls->enable) (self, fd, flags);
+}
+
+static inline void
+_dbus_socket_set_disable (DBusSocketSet *self,
+ int fd)
+{
+ (self->cls->disable) (self, fd);
+}
+
+
+static inline int
+_dbus_socket_set_poll (DBusSocketSet *self,
+ DBusSocketEvent *revents,
+ int max_events,
+ int timeout_ms)
+{
+ return (self->cls->poll) (self, revents, max_events, timeout_ms);
+}
+
+/* concrete implementations, not necessarily built on all platforms */
+
+extern DBusSocketSetClass _dbus_socket_set_poll_class;
+extern DBusSocketSetClass _dbus_socket_set_epoll_class;
+
+DBusSocketSet *_dbus_socket_set_poll_new (int size_hint);
+DBusSocketSet *_dbus_socket_set_epoll_new (void);
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+#endif /* multiple-inclusion guard */
diff --git a/dbus/dbus-watch.h b/dbus/dbus-watch.h
index dd23b862..c5832141 100644
--- a/dbus/dbus-watch.h
+++ b/dbus/dbus-watch.h
@@ -37,6 +37,8 @@ DBUS_BEGIN_DECLS
typedef struct DBusWatchList DBusWatchList;
+#define _DBUS_WATCH_NVAL (1<<4)
+
/** function to run when the watch is handled */
typedef dbus_bool_t (* DBusWatchHandler) (DBusWatch *watch,
unsigned int flags,
diff --git a/test/test-utils.c b/test/test-utils.c
index 4fd84fe8..c3c3ed34 100644
--- a/test/test-utils.c
+++ b/test/test-utils.c
@@ -26,6 +26,15 @@ remove_watch (DBusWatch *watch,
_dbus_loop_remove_watch (cd->loop, watch);
}
+static void
+toggle_watch (DBusWatch *watch,
+ void *data)
+{
+ CData *cd = data;
+
+ _dbus_loop_toggle_watch (cd->loop, watch);
+}
+
static dbus_bool_t
add_timeout (DBusTimeout *timeout,
void *data)
@@ -103,15 +112,10 @@ test_connection_setup (DBusLoop *loop,
if (cd == NULL)
goto nomem;
- /* Because dbus-mainloop.c checks dbus_timeout_get_enabled(),
- * dbus_watch_get_enabled() directly, we don't have to provide
- * "toggled" callbacks.
- */
-
if (!dbus_connection_set_watch_functions (connection,
add_watch,
remove_watch,
- NULL,
+ toggle_watch,
cd, cdata_free))
goto nomem;
@@ -213,6 +217,15 @@ add_server_watch (DBusWatch *watch,
}
static void
+toggle_server_watch (DBusWatch *watch,
+ void *data)
+{
+ ServerData *context = data;
+
+ _dbus_loop_toggle_watch (context->loop, watch);
+}
+
+static void
remove_server_watch (DBusWatch *watch,
void *data)
{
@@ -252,7 +265,7 @@ test_server_setup (DBusLoop *loop,
if (!dbus_server_set_watch_functions (server,
add_server_watch,
remove_server_watch,
- NULL,
+ toggle_server_watch,
sd,
serverdata_free))
{