From d7eafb1b2adfeaa53975155f344e3ef9ca6fb34d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 3 Feb 2011 17:04:02 +0000 Subject: [PATCH 1/2] Choose between poll and epoll at runtime If we're compiled against glibc 2.9 or later, but then run on Linux 2.6.26 or earlier (for instance, a Debian 6 chroot on a Debian 5 host), epoll_create1 won't work and we'll have to fall back to poll anyway. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=33337 Bug-NB: NB#225019 --- configure.in | 3 + dbus/Makefile.am | 4 +- dbus/dbus-socket-set-epoll.c | 109 ++++++++++++++++++++++++++++-------------- dbus/dbus-socket-set-poll.c | 100 ++++++++++++++++++++++++-------------- dbus/dbus-socket-set.c | 47 ++++++++++++++++++ dbus/dbus-socket-set.h | 98 ++++++++++++++++++++++++++++++------- 6 files changed, 267 insertions(+), 94 deletions(-) create mode 100644 dbus/dbus-socket-set.c diff --git a/configure.in b/configure.in index 593845c..cdcc4e2 100644 --- a/configure.in +++ b/configure.in @@ -933,6 +933,9 @@ 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 diff --git a/dbus/Makefile.am b/dbus/Makefile.am index 7ef2b8e..9bd6e6d 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -108,8 +108,6 @@ endif if HAVE_LINUX_EPOLL DBUS_UTIL_arch_sources += dbus-socket-set-epoll.c -else -DBUS_UTIL_arch_sources += dbus-socket-set-poll.c endif dbusinclude_HEADERS= \ @@ -244,6 +242,8 @@ DBUS_UTIL_SOURCES= \ 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-socket-set-epoll.c b/dbus/dbus-socket-set-epoll.c index 0c6745f..b5c3209 100644 --- a/dbus/dbus-socket-set-epoll.c +++ b/dbus/dbus-socket-set-epoll.c @@ -38,15 +38,54 @@ #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_new (int size_hint) +_dbus_socket_set_epoll_new (void) { - int epfd = epoll_create1 (EPOLL_CLOEXEC); + DBusSocketSetEpoll *self; + + self = dbus_new0 (DBusSocketSetEpoll, 1); - if (epfd == -1) + if (self == NULL) return NULL; - return _DBUS_INT_TO_POINTER (epfd); + self->parent.cls = &_dbus_socket_set_epoll_class; + + self->epfd = epoll_create1 (EPOLL_CLOEXEC); + + if (self->epfd == -1) + { + socket_set_epoll_free ((DBusSocketSet *) self); + return NULL; + } + + return (DBusSocketSet *) self; } static uint32_t @@ -79,13 +118,13 @@ epoll_events_to_watch_flags (uint32_t events) return flags; } -dbus_bool_t -_dbus_socket_set_add (DBusSocketSet *self, +static dbus_bool_t +socket_set_epoll_add (DBusSocketSet *set, int fd, unsigned int flags, dbus_bool_t enabled) { - int epfd = _DBUS_POINTER_TO_INT (self); + DBusSocketSetEpoll *self = socket_set_epoll_cast (set); struct epoll_event event; int err; @@ -98,11 +137,11 @@ _dbus_socket_set_add (DBusSocketSet *self, else { /* We need to add *something* to reserve space in the kernel's data - * structures: see _dbus_socket_set_disable for more details */ + * structures: see socket_set_epoll_disable for more details */ event.events = EPOLLET; } - if (epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &event) == 0) + if (epoll_ctl (self->epfd, EPOLL_CTL_ADD, fd, &event) == 0) return TRUE; /* Anything except ENOMEM, ENOSPC means we have an internal error. */ @@ -132,19 +171,19 @@ _dbus_socket_set_add (DBusSocketSet *self, return FALSE; } -void -_dbus_socket_set_enable (DBusSocketSet *self, +static void +socket_set_epoll_enable (DBusSocketSet *set, int fd, unsigned int flags) { - int epfd = _DBUS_POINTER_TO_INT (self); + 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 (epfd, EPOLL_CTL_MOD, fd, &event) == 0) + if (epoll_ctl (self->epfd, EPOLL_CTL_MOD, fd, &event) == 0) return; err = errno; @@ -172,16 +211,16 @@ _dbus_socket_set_enable (DBusSocketSet *self, } } -void -_dbus_socket_set_disable (DBusSocketSet *self, +static void +socket_set_epoll_disable (DBusSocketSet *set, int fd) { - int epfd = _DBUS_POINTER_TO_INT (self); + 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 _dbus_socket_set_enable, + * 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 @@ -199,7 +238,7 @@ _dbus_socket_set_disable (DBusSocketSet *self, event.data.fd = fd; event.events = EPOLLET; - if (epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &event) == 0) + if (epoll_ctl (self->epfd, EPOLL_CTL_MOD, fd, &event) == 0) return; err = errno; @@ -207,14 +246,14 @@ _dbus_socket_set_disable (DBusSocketSet *self, strerror (err)); } -void -_dbus_socket_set_remove (DBusSocketSet *self, +static void +socket_set_epoll_remove (DBusSocketSet *set, int fd) { - int epfd = _DBUS_POINTER_TO_INT (self); + DBusSocketSetEpoll *self = socket_set_epoll_cast (set); int err; - if (epoll_ctl (epfd, EPOLL_CTL_DEL, fd, NULL) == 0) + if (epoll_ctl (self->epfd, EPOLL_CTL_DEL, fd, NULL) == 0) return; err = errno; @@ -225,22 +264,20 @@ _dbus_socket_set_remove (DBusSocketSet *self, * but it makes things simpler... */ #define N_STACK_DESCRIPTORS 64 -/** 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. */ -int -_dbus_socket_set_poll (DBusSocketSet *self, +static int +socket_set_epoll_poll (DBusSocketSet *set, DBusSocketEvent *revents, int max_events, int timeout_ms) { - int epfd = _DBUS_POINTER_TO_INT (self); + 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 (epfd, events, + n_ready = epoll_wait (self->epfd, events, MIN (_DBUS_N_ELEMENTS (events), max_events), timeout_ms); @@ -256,14 +293,14 @@ _dbus_socket_set_poll (DBusSocketSet *self, return n_ready; } -/* this is safe to call on a partially-allocated socket set */ -void -_dbus_socket_set_free (DBusSocketSet *self) -{ - int epfd = _DBUS_POINTER_TO_INT (self); - - close (epfd); -} +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 diff --git a/dbus/dbus-socket-set-poll.c b/dbus/dbus-socket-set-poll.c index 918c965..65a1fd2 100644 --- a/dbus/dbus-socket-set-poll.c +++ b/dbus/dbus-socket-set-poll.c @@ -32,12 +32,13 @@ #ifndef DOXYGEN_SHOULD_SKIP_THIS -struct DBusSocketSet { +typedef struct { + DBusSocketSet parent; DBusPollFD *fds; int n_fds; int n_reserved; int n_allocated; -}; +} DBusSocketSetPoll; #define REALLOC_INCREMENT 8 #define MINIMUM_SIZE 8 @@ -49,19 +50,38 @@ struct DBusSocketSet { #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_new (int size_hint) +_dbus_socket_set_poll_new (int size_hint) { - DBusSocketSet *ret; + DBusSocketSetPoll *ret; if (size_hint <= 0) size_hint = DEFAULT_SIZE_HINT; - ret = dbus_new0 (DBusSocketSet, 1); + 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; @@ -69,14 +89,14 @@ _dbus_socket_set_new (int size_hint) if (ret->fds == NULL) { - /* _dbus_socket_set_free specifically supports half-constructed + /* socket_set_poll_free specifically supports half-constructed * socket sets */ - _dbus_socket_set_free (ret); + socket_set_poll_free ((DBusSocketSet *) ret); return NULL; } _dbus_verbose ("new socket set at %p\n", ret); - return ret; + return (DBusSocketSet *) ret; } static short @@ -92,12 +112,13 @@ watch_flags_to_poll_events (unsigned int flags) return events; } -dbus_bool_t -_dbus_socket_set_add (DBusSocketSet *self, - int fd, - unsigned int flags, - dbus_bool_t enabled) +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; @@ -138,11 +159,12 @@ _dbus_socket_set_add (DBusSocketSet *self, return TRUE; } -void -_dbus_socket_set_enable (DBusSocketSet *self, - int fd, - unsigned int flags) +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++) @@ -163,10 +185,11 @@ _dbus_socket_set_enable (DBusSocketSet *self, self->n_fds++; } -void -_dbus_socket_set_disable (DBusSocketSet *self, - int fd) +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++) @@ -185,11 +208,13 @@ _dbus_socket_set_disable (DBusSocketSet *self, } } -void -_dbus_socket_set_remove (DBusSocketSet *self, - int fd) +static void +socket_set_poll_remove (DBusSocketSet *set, + int fd) { - _dbus_socket_set_disable (self, 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", @@ -240,12 +265,13 @@ watch_flags_from_poll_revents (short revents) /** 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. */ -int -_dbus_socket_set_poll (DBusSocketSet *self, - DBusSocketEvent *revents, - int max_events, - int timeout_ms) +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; @@ -282,13 +308,13 @@ _dbus_socket_set_poll (DBusSocketSet *self, return n_events; } -/* this is safe to call on a partially-allocated socket set */ -void -_dbus_socket_set_free (DBusSocketSet *self) -{ - dbus_free (self->fds); - dbus_free (self); - _dbus_verbose ("freed socket set %p\n", self); -} +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 0000000..210d600 --- /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 +#include + +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 index 4a1112b..3b71a92 100644 --- a/dbus/dbus-socket-set.h +++ b/dbus/dbus-socket-set.h @@ -37,26 +37,86 @@ typedef struct { 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); -void _dbus_socket_set_free (DBusSocketSet *self); - -dbus_bool_t _dbus_socket_set_add (DBusSocketSet *self, - int fd, - unsigned int flags, - dbus_bool_t enabled); -void _dbus_socket_set_remove (DBusSocketSet *self, - int fd); - -void _dbus_socket_set_enable (DBusSocketSet *self, - int fd, - unsigned int flags); -void _dbus_socket_set_disable (DBusSocketSet *self, - int fd); - -int _dbus_socket_set_poll (DBusSocketSet *self, - DBusSocketEvent *revents, - int max_events, - int timeout_ms); + +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 */ -- 1.7.2.3