D-Bus 1.6.12

dbus-socket-set-epoll.c

00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 /* dbus-socket-set-epoll.c - a socket set implemented via Linux epoll(4)
00003  *
00004  * Copyright © 2011 Nokia Corporation
00005  *
00006  * Licensed under the Academic Free License version 2.1
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00021  * MA  02110-1301  USA
00022  *
00023  */
00024 
00025 #include <config.h>
00026 #include "dbus-socket-set.h"
00027 
00028 #include <dbus/dbus-internals.h>
00029 #include <dbus/dbus-sysdeps.h>
00030 
00031 #ifndef __linux__
00032 # error This file is for Linux epoll(4)
00033 #endif
00034 
00035 #include <errno.h>
00036 #include <fcntl.h>
00037 #include <sys/epoll.h>
00038 #include <unistd.h>
00039 
00040 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00041 
00042 typedef struct {
00043     DBusSocketSet parent;
00044     int epfd;
00045 } DBusSocketSetEpoll;
00046 
00047 static inline DBusSocketSetEpoll *
00048 socket_set_epoll_cast (DBusSocketSet *set)
00049 {
00050   _dbus_assert (set->cls == &_dbus_socket_set_epoll_class);
00051   return (DBusSocketSetEpoll *) set;
00052 }
00053 
00054 /* this is safe to call on a partially-allocated socket set */
00055 static void
00056 socket_set_epoll_free (DBusSocketSet *set)
00057 {
00058   DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
00059 
00060   if (self == NULL)
00061     return;
00062 
00063   if (self->epfd != -1)
00064     close (self->epfd);
00065 
00066   dbus_free (self);
00067 }
00068 
00069 DBusSocketSet *
00070 _dbus_socket_set_epoll_new (void)
00071 {
00072   DBusSocketSetEpoll *self;
00073 
00074   self = dbus_new0 (DBusSocketSetEpoll, 1);
00075 
00076   if (self == NULL)
00077     return NULL;
00078 
00079   self->parent.cls = &_dbus_socket_set_epoll_class;
00080 
00081   self->epfd = epoll_create1 (EPOLL_CLOEXEC);
00082 
00083   if (self->epfd == -1)
00084     {
00085       int flags;
00086 
00087       /* the size hint is ignored unless you have a rather old kernel,
00088        * but must be positive on some versions, so just pick something
00089        * arbitrary; it's a hint, not a limit */
00090       self->epfd = epoll_create (42);
00091 
00092       flags = fcntl (self->epfd, F_GETFD, 0);
00093 
00094       if (flags != -1)
00095         fcntl (self->epfd, F_SETFD, flags | FD_CLOEXEC);
00096     }
00097 
00098   if (self->epfd == -1)
00099     {
00100       socket_set_epoll_free ((DBusSocketSet *) self);
00101       return NULL;
00102     }
00103 
00104   return (DBusSocketSet *) self;
00105 }
00106 
00107 static uint32_t
00108 watch_flags_to_epoll_events (unsigned int flags)
00109 {
00110   uint32_t events = 0;
00111 
00112   if (flags & DBUS_WATCH_READABLE)
00113     events |= EPOLLIN;
00114   if (flags & DBUS_WATCH_WRITABLE)
00115     events |= EPOLLOUT;
00116 
00117   return events;
00118 }
00119 
00120 static unsigned int
00121 epoll_events_to_watch_flags (uint32_t events)
00122 {
00123   short flags = 0;
00124 
00125   if (events & EPOLLIN)
00126     flags |= DBUS_WATCH_READABLE;
00127   if (events & EPOLLOUT)
00128     flags |= DBUS_WATCH_WRITABLE;
00129   if (events & EPOLLHUP)
00130     flags |= DBUS_WATCH_HANGUP;
00131   if (events & EPOLLERR)
00132     flags |= DBUS_WATCH_ERROR;
00133 
00134   return flags;
00135 }
00136 
00137 static dbus_bool_t
00138 socket_set_epoll_add (DBusSocketSet  *set,
00139                       int             fd,
00140                       unsigned int    flags,
00141                       dbus_bool_t     enabled)
00142 {
00143   DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
00144   struct epoll_event event;
00145   int err;
00146 
00147   event.data.fd = fd;
00148 
00149   if (enabled)
00150     {
00151       event.events = watch_flags_to_epoll_events (flags);
00152     }
00153   else
00154     {
00155       /* We need to add *something* to reserve space in the kernel's data
00156        * structures: see socket_set_epoll_disable for more details */
00157       event.events = EPOLLET;
00158     }
00159 
00160   if (epoll_ctl (self->epfd, EPOLL_CTL_ADD, fd, &event) == 0)
00161     return TRUE;
00162 
00163   /* Anything except ENOMEM, ENOSPC means we have an internal error. */
00164   err = errno;
00165   switch (err)
00166     {
00167       case ENOMEM:
00168       case ENOSPC:
00169         /* be silent: this is basically OOM, which our callers are expected
00170          * to cope with */
00171         break;
00172 
00173       case EBADF:
00174         _dbus_warn ("Bad fd %d\n", fd);
00175         break;
00176 
00177       case EEXIST:
00178         _dbus_warn ("fd %d added and then added again\n", fd);
00179         break;
00180 
00181       default:
00182         _dbus_warn ("Misc error when trying to watch fd %d: %s\n", fd,
00183                     strerror (err));
00184         break;
00185     }
00186 
00187   return FALSE;
00188 }
00189 
00190 static void
00191 socket_set_epoll_enable (DBusSocketSet  *set,
00192                          int             fd,
00193                          unsigned int    flags)
00194 {
00195   DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
00196   struct epoll_event event;
00197   int err;
00198 
00199   event.data.fd = fd;
00200   event.events = watch_flags_to_epoll_events (flags);
00201 
00202   if (epoll_ctl (self->epfd, EPOLL_CTL_MOD, fd, &event) == 0)
00203     return;
00204 
00205   err = errno;
00206 
00207   /* Enabling a file descriptor isn't allowed to fail, even for OOM, so we
00208    * do our best to avoid all of these. */
00209   switch (err)
00210     {
00211       case EBADF:
00212         _dbus_warn ("Bad fd %d\n", fd);
00213         break;
00214 
00215       case ENOENT:
00216         _dbus_warn ("fd %d enabled before it was added\n", fd);
00217         break;
00218 
00219       case ENOMEM:
00220         _dbus_warn ("Insufficient memory to change watch for fd %d\n", fd);
00221         break;
00222 
00223       default:
00224         _dbus_warn ("Misc error when trying to watch fd %d: %s\n", fd,
00225                     strerror (err));
00226         break;
00227     }
00228 }
00229 
00230 static void
00231 socket_set_epoll_disable (DBusSocketSet  *set,
00232                           int             fd)
00233 {
00234   DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
00235   struct epoll_event event;
00236   int err;
00237 
00238   /* The naive thing to do would be EPOLL_CTL_DEL, but that'll probably
00239    * free resources in the kernel. When we come to do socket_set_epoll_enable,
00240    * there might not be enough resources to bring it back!
00241    *
00242    * The next idea you might have is to set the flags to 0. However, events
00243    * always trigger on EPOLLERR and EPOLLHUP, even if libdbus isn't actually
00244    * delivering them to a DBusWatch. Because epoll is level-triggered by
00245    * default, we'll busy-loop on an unhandled error or hangup; not good.
00246    *
00247    * So, let's set it to be edge-triggered: then the worst case is that
00248    * we return from poll immediately on one iteration, ignore it because no
00249    * watch is enabled, then go back to normal. When we re-enable a watch
00250    * we'll switch back to level-triggered and be notified again (verified to
00251    * work on 2.6.32). Compile this file with -DTEST_BEHAVIOUR_OF_EPOLLET for
00252    * test code.
00253    */
00254   event.data.fd = fd;
00255   event.events = EPOLLET;
00256 
00257   if (epoll_ctl (self->epfd, EPOLL_CTL_MOD, fd, &event) == 0)
00258     return;
00259 
00260   err = errno;
00261   _dbus_warn ("Error when trying to watch fd %d: %s\n", fd,
00262               strerror (err));
00263 }
00264 
00265 static void
00266 socket_set_epoll_remove (DBusSocketSet  *set,
00267                          int             fd)
00268 {
00269   DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
00270   int err;
00271   /* Kernels < 2.6.9 require a non-NULL struct pointer, even though its
00272    * contents are ignored */
00273   struct epoll_event dummy = { 0 };
00274 
00275   if (epoll_ctl (self->epfd, EPOLL_CTL_DEL, fd, &dummy) == 0)
00276     return;
00277 
00278   err = errno;
00279   _dbus_warn ("Error when trying to remove fd %d: %s\n", fd, strerror (err));
00280 }
00281 
00282 /* Optimally, this should be the same as in DBusLoop: we use it to translate
00283  * between struct epoll_event and DBusSocketEvent without allocating heap
00284  * memory. */
00285 #define N_STACK_DESCRIPTORS 64
00286 
00287 static int
00288 socket_set_epoll_poll (DBusSocketSet   *set,
00289                        DBusSocketEvent *revents,
00290                        int              max_events,
00291                        int              timeout_ms)
00292 {
00293   DBusSocketSetEpoll *self = socket_set_epoll_cast (set);
00294   struct epoll_event events[N_STACK_DESCRIPTORS];
00295   int n_ready;
00296   int i;
00297 
00298   _dbus_assert (max_events > 0);
00299 
00300   n_ready = epoll_wait (self->epfd, events,
00301                         MIN (_DBUS_N_ELEMENTS (events), max_events),
00302                         timeout_ms);
00303 
00304   if (n_ready <= 0)
00305     return n_ready;
00306 
00307   for (i = 0; i < n_ready; i++)
00308     {
00309       revents[i].fd = events[i].data.fd;
00310       revents[i].flags = epoll_events_to_watch_flags (events[i].events);
00311     }
00312 
00313   return n_ready;
00314 }
00315 
00316 DBusSocketSetClass _dbus_socket_set_epoll_class = {
00317     socket_set_epoll_free,
00318     socket_set_epoll_add,
00319     socket_set_epoll_remove,
00320     socket_set_epoll_enable,
00321     socket_set_epoll_disable,
00322     socket_set_epoll_poll
00323 };
00324 
00325 #ifdef TEST_BEHAVIOUR_OF_EPOLLET
00326 /* usage: cat /dev/null | ./epoll
00327  *
00328  * desired output:
00329  * ctl ADD: 0
00330  * wait for HUP, edge-triggered: 1
00331  * wait for HUP again: 0
00332  * ctl MOD: 0
00333  * wait for HUP: 1
00334  */
00335 
00336 #include <sys/epoll.h>
00337 
00338 #include <stdio.h>
00339 
00340 int
00341 main (void)
00342 {
00343   struct epoll_event input;
00344   struct epoll_event output;
00345   int epfd = epoll_create1 (EPOLL_CLOEXEC);
00346   int fd = 0; /* stdin */
00347   int ret;
00348 
00349   input.events = EPOLLHUP | EPOLLET;
00350   ret = epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &input);
00351   printf ("ctl ADD: %d\n", ret);
00352 
00353   ret = epoll_wait (epfd, &output, 1, -1);
00354   printf ("wait for HUP, edge-triggered: %d\n", ret);
00355 
00356   ret = epoll_wait (epfd, &output, 1, 1);
00357   printf ("wait for HUP again: %d\n", ret);
00358 
00359   input.events = EPOLLHUP;
00360   ret = epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &input);
00361   printf ("ctl MOD: %d\n", ret);
00362 
00363   ret = epoll_wait (epfd, &output, 1, -1);
00364   printf ("wait for HUP: %d\n", ret);
00365 
00366   return 0;
00367 }
00368 
00369 #endif /* TEST_BEHAVIOUR_OF_EPOLLET */
00370 
00371 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */