D-Bus
1.10.12
|
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 DBusPollable 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 DBusPollable 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 DBusPollable 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 DBusPollable 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 */