1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2 /* dbus-socket-set-poll.c - a socket set implemented via _dbus_poll 3 * 4 * Copyright 2011 Nokia Corporation 5 * 6 * Licensed under the Academic Free License version 2.1 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 * MA 02110-1301 USA 22 * 23 */ 24 25 #include <config.h> 26 #include "dbus-socket-set.h" 27 28 #include <dbus/dbus-internals.h> 29 #include <dbus/dbus-list.h> 30 #include <dbus/dbus-sysdeps.h> 31 #include <dbus/dbus-watch.h> 32 33 #ifndef DOXYGEN_SHOULD_SKIP_THIS 34 35 typedef struct { 36 DBusSocketSet parent; 37 DBusPollFD *fds; 38 int n_fds; 39 int n_reserved; 40 int n_allocated; 41 } DBusSocketSetPoll; 42 43 #define REALLOC_INCREMENT 8 44 #define MINIMUM_SIZE 8 45 46 /* If we're in the regression tests, force reallocation to happen sooner */ 47 #ifdef DBUS_BUILD_TESTS 48 #define DEFAULT_SIZE_HINT 1 49 #else 50 #define DEFAULT_SIZE_HINT MINIMUM_SIZE 51 #endif 52 53 static inline DBusSocketSetPoll * 54 socket_set_poll_cast (DBusSocketSet *set) 55 { 56 _dbus_assert (set->cls == &_dbus_socket_set_poll_class); 57 return (DBusSocketSetPoll *) set; 58 } 59 60 /* this is safe to call on a partially-allocated socket set */ 61 static void 62 socket_set_poll_free (DBusSocketSet *set) 63 { 64 DBusSocketSetPoll *self = socket_set_poll_cast (set); 65 66 dbus_free (self->fds); 67 dbus_free (self); 68 _dbus_verbose ("freed socket set %p\n", self); 69 } 70 71 DBusSocketSet * 72 _dbus_socket_set_poll_new (int size_hint) 73 { 74 DBusSocketSetPoll *ret; 75 76 if (size_hint <= 0) 77 size_hint = DEFAULT_SIZE_HINT; 78 79 ret = dbus_new0 (DBusSocketSetPoll, 1); 80 81 if (ret == NULL) 82 return NULL; 83 84 ret->parent.cls = &_dbus_socket_set_poll_class; 85 ret->n_fds = 0; 86 ret->n_allocated = size_hint; 87 88 ret->fds = dbus_new0 (DBusPollFD, size_hint); 89 90 if (ret->fds == NULL) 91 { 92 /* socket_set_poll_free specifically supports half-constructed 93 * socket sets */ 94 socket_set_poll_free ((DBusSocketSet *) ret); 95 return NULL; 96 } 97 98 _dbus_verbose ("new socket set at %p\n", ret); 99 return (DBusSocketSet *) ret; 100 } 101 102 static short 103 watch_flags_to_poll_events (unsigned int flags) 104 { 105 short events = 0; 106 107 if (flags & DBUS_WATCH_READABLE) 108 events |= _DBUS_POLLIN; 109 if (flags & DBUS_WATCH_WRITABLE) 110 events |= _DBUS_POLLOUT; 111 112 return events; 113 } 114 115 static dbus_bool_t 116 socket_set_poll_add (DBusSocketSet *set, 117 int fd, 118 unsigned int flags, 119 dbus_bool_t enabled) 120 { 121 DBusSocketSetPoll *self = socket_set_poll_cast (set); 122 #ifndef DBUS_DISABLE_ASSERT 123 int i; 124 125 for (i = 0; i < self->n_fds; i++) 126 _dbus_assert (self->fds[i].fd != fd); 127 #endif 128 129 if (self->n_reserved >= self->n_allocated) 130 { 131 DBusPollFD *new_fds = dbus_realloc (self->fds, 132 sizeof (DBusPollFD) * (self->n_allocated + REALLOC_INCREMENT)); 133 134 _dbus_verbose ("inflating set %p from %d en/%d res/%d alloc to %d\n", 135 self, self->n_fds, self->n_reserved, self->n_allocated, 136 self->n_allocated + REALLOC_INCREMENT); 137 138 if (new_fds == NULL) 139 return FALSE; 140 141 self->fds = new_fds; 142 self->n_allocated += REALLOC_INCREMENT; 143 } 144 145 _dbus_verbose ("before adding fd %d to %p, %d en/%d res/%d alloc\n", 146 fd, self, self->n_fds, self->n_reserved, self->n_allocated); 147 _dbus_assert (self->n_reserved >= self->n_fds); 148 _dbus_assert (self->n_allocated > self->n_reserved); 149 150 self->n_reserved++; 151 152 if (enabled) 153 { 154 self->fds[self->n_fds].fd = fd; 155 self->fds[self->n_fds].events = watch_flags_to_poll_events (flags); 156 self->n_fds++; 157 } 158 159 return TRUE; 160 } 161 162 static void 163 socket_set_poll_enable (DBusSocketSet *set, 164 int fd, 165 unsigned int flags) 166 { 167 DBusSocketSetPoll *self = socket_set_poll_cast (set); 168 int i; 169 170 for (i = 0; i < self->n_fds; i++) 171 { 172 if (self->fds[i].fd == fd) 173 { 174 self->fds[i].events = watch_flags_to_poll_events (flags); 175 return; 176 } 177 } 178 179 /* we allocated space when the socket was added */ 180 _dbus_assert (self->n_fds < self->n_reserved); 181 _dbus_assert (self->n_reserved <= self->n_allocated); 182 183 self->fds[self->n_fds].fd = fd; 184 self->fds[self->n_fds].events = watch_flags_to_poll_events (flags); 185 self->n_fds++; 186 } 187 188 static void 189 socket_set_poll_disable (DBusSocketSet *set, 190 int fd) 191 { 192 DBusSocketSetPoll *self = socket_set_poll_cast (set); 193 int i; 194 195 for (i = 0; i < self->n_fds; i++) 196 { 197 if (self->fds[i].fd == fd) 198 { 199 if (i != self->n_fds - 1) 200 { 201 self->fds[i].fd = self->fds[self->n_fds - 1].fd; 202 self->fds[i].events = self->fds[self->n_fds - 1].events; 203 } 204 205 self->n_fds--; 206 return; 207 } 208 } 209 } 210 211 static void 212 socket_set_poll_remove (DBusSocketSet *set, 213 int fd) 214 { 215 DBusSocketSetPoll *self = socket_set_poll_cast (set); 216 217 socket_set_poll_disable (set, fd); 218 self->n_reserved--; 219 220 _dbus_verbose ("after removing fd %d from %p, %d en/%d res/%d alloc\n", 221 fd, self, self->n_fds, self->n_reserved, self->n_allocated); 222 _dbus_assert (self->n_fds <= self->n_reserved); 223 _dbus_assert (self->n_reserved <= self->n_allocated); 224 225 if (self->n_reserved + MINIMUM_SIZE < self->n_allocated / 2) 226 { 227 /* Our array is twice as big as it needs to be - deflate it until it's 228 * only slightly larger than the number reserved. */ 229 DBusPollFD *new_fds = dbus_realloc (self->fds, 230 sizeof (DBusPollFD) * (self->n_reserved + MINIMUM_SIZE)); 231 232 _dbus_verbose ("before deflating %p, %d en/%d res/%d alloc\n", 233 self, self->n_fds, self->n_reserved, self->n_allocated); 234 235 if (_DBUS_UNLIKELY (new_fds == NULL)) 236 { 237 /* Weird. Oh well, never mind, the too-big array is untouched */ 238 return; 239 } 240 241 self->fds = new_fds; 242 self->n_allocated = self->n_reserved; 243 } 244 } 245 246 static unsigned int 247 watch_flags_from_poll_revents (short revents) 248 { 249 unsigned int condition = 0; 250 251 if (revents & _DBUS_POLLIN) 252 condition |= DBUS_WATCH_READABLE; 253 if (revents & _DBUS_POLLOUT) 254 condition |= DBUS_WATCH_WRITABLE; 255 if (revents & _DBUS_POLLHUP) 256 condition |= DBUS_WATCH_HANGUP; 257 if (revents & _DBUS_POLLERR) 258 condition |= DBUS_WATCH_ERROR; 259 260 if (_DBUS_UNLIKELY (revents & _DBUS_POLLNVAL)) 261 condition |= _DBUS_WATCH_NVAL; 262 263 return condition; 264 } 265 266 /** This is basically Linux's epoll_wait(2) implemented in terms of poll(2); 267 * it returns results into a caller-supplied buffer so we can be reentrant. */ 268 static int 269 socket_set_poll_poll (DBusSocketSet *set, 270 DBusSocketEvent *revents, 271 int max_events, 272 int timeout_ms) 273 { 274 DBusSocketSetPoll *self = socket_set_poll_cast (set); 275 int i; 276 int n_events; 277 int n_ready; 278 279 _dbus_assert (max_events > 0); 280 281 for (i = 0; i < self->n_fds; i++) 282 self->fds[i].revents = 0; 283 284 n_ready = _dbus_poll (self->fds, self->n_fds, timeout_ms); 285 286 if (n_ready <= 0) 287 return n_ready; 288 289 n_events = 0; 290 291 for (i = 0; i < self->n_fds; i++) 292 { 293 if (self->fds[i].revents != 0) 294 { 295 revents[n_events].fd = self->fds[i].fd; 296 revents[n_events].flags = watch_flags_from_poll_revents (self->fds[i].revents); 297 298 n_events += 1; 299 300 /* We ignore events beyond max_events because we have nowhere to 301 * put them. _dbus_poll is level-triggered, so we'll just be told 302 * about them next time round the main loop anyway. */ 303 if (n_events == max_events) 304 return n_events; 305 } 306 } 307 308 return n_events; 309 } 310 311 DBusSocketSetClass _dbus_socket_set_poll_class = { 312 socket_set_poll_free, 313 socket_set_poll_add, 314 socket_set_poll_remove, 315 socket_set_poll_enable, 316 socket_set_poll_disable, 317 socket_set_poll_poll 318 }; 319 320 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */ 321