ipc/chromium/src/third_party/libevent/evport.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 * Submitted by David Pacheco (dp.spambait@gmail.com)
michael@0 3 *
michael@0 4 * Copyright 2006-2007 Niels Provos
michael@0 5 * Copyright 2007-2012 Niels Provos and Nick Mathewson
michael@0 6 *
michael@0 7 * Redistribution and use in source and binary forms, with or without
michael@0 8 * modification, are permitted provided that the following conditions
michael@0 9 * are met:
michael@0 10 * 1. Redistributions of source code must retain the above copyright
michael@0 11 * notice, this list of conditions and the following disclaimer.
michael@0 12 * 2. Redistributions in binary form must reproduce the above copyright
michael@0 13 * notice, this list of conditions and the following disclaimer in the
michael@0 14 * documentation and/or other materials provided with the distribution.
michael@0 15 * 3. The name of the author may not be used to endorse or promote products
michael@0 16 * derived from this software without specific prior written permission.
michael@0 17 *
michael@0 18 * THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``AS IS'' AND ANY
michael@0 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
michael@0 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
michael@0 21 * DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY
michael@0 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
michael@0 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
michael@0 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
michael@0 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
michael@0 27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 28 */
michael@0 29
michael@0 30 /*
michael@0 31 * Copyright (c) 2007 Sun Microsystems. All rights reserved.
michael@0 32 * Use is subject to license terms.
michael@0 33 */
michael@0 34
michael@0 35 /*
michael@0 36 * evport.c: event backend using Solaris 10 event ports. See port_create(3C).
michael@0 37 * This implementation is loosely modeled after the one used for select(2) (in
michael@0 38 * select.c).
michael@0 39 *
michael@0 40 * The outstanding events are tracked in a data structure called evport_data.
michael@0 41 * Each entry in the ed_fds array corresponds to a file descriptor, and contains
michael@0 42 * pointers to the read and write events that correspond to that fd. (That is,
michael@0 43 * when the file is readable, the "read" event should handle it, etc.)
michael@0 44 *
michael@0 45 * evport_add and evport_del update this data structure. evport_dispatch uses it
michael@0 46 * to determine where to callback when an event occurs (which it gets from
michael@0 47 * port_getn).
michael@0 48 *
michael@0 49 * Helper functions are used: grow() grows the file descriptor array as
michael@0 50 * necessary when large fd's come in. reassociate() takes care of maintaining
michael@0 51 * the proper file-descriptor/event-port associations.
michael@0 52 *
michael@0 53 * As in the select(2) implementation, signals are handled by evsignal.
michael@0 54 */
michael@0 55
michael@0 56 #include "event2/event-config.h"
michael@0 57
michael@0 58 #include <sys/time.h>
michael@0 59 #include <sys/queue.h>
michael@0 60 #include <errno.h>
michael@0 61 #include <poll.h>
michael@0 62 #include <port.h>
michael@0 63 #include <signal.h>
michael@0 64 #include <stdio.h>
michael@0 65 #include <stdlib.h>
michael@0 66 #include <string.h>
michael@0 67 #include <time.h>
michael@0 68 #include <unistd.h>
michael@0 69
michael@0 70 #include "event2/thread.h"
michael@0 71
michael@0 72 #include "evthread-internal.h"
michael@0 73 #include "event-internal.h"
michael@0 74 #include "log-internal.h"
michael@0 75 #include "evsignal-internal.h"
michael@0 76 #include "evmap-internal.h"
michael@0 77
michael@0 78 /*
michael@0 79 * Default value for ed_nevents, which is the maximum file descriptor number we
michael@0 80 * can handle. If an event comes in for a file descriptor F > nevents, we will
michael@0 81 * grow the array of file descriptors, doubling its size.
michael@0 82 */
michael@0 83 #define DEFAULT_NFDS 16
michael@0 84
michael@0 85
michael@0 86 /*
michael@0 87 * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on
michael@0 88 * any particular call. You can speed things up by increasing this, but it will
michael@0 89 * (obviously) require more memory.
michael@0 90 */
michael@0 91 #define EVENTS_PER_GETN 8
michael@0 92
michael@0 93 /*
michael@0 94 * Per-file-descriptor information about what events we're subscribed to. These
michael@0 95 * fields are NULL if no event is subscribed to either of them.
michael@0 96 */
michael@0 97
michael@0 98 struct fd_info {
michael@0 99 short fdi_what; /* combinations of EV_READ and EV_WRITE */
michael@0 100 };
michael@0 101
michael@0 102 #define FDI_HAS_READ(fdi) ((fdi)->fdi_what & EV_READ)
michael@0 103 #define FDI_HAS_WRITE(fdi) ((fdi)->fdi_what & EV_WRITE)
michael@0 104 #define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi))
michael@0 105 #define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \
michael@0 106 (FDI_HAS_WRITE(fdi) ? POLLOUT : 0)
michael@0 107
michael@0 108 struct evport_data {
michael@0 109 int ed_port; /* event port for system events */
michael@0 110 int ed_nevents; /* number of allocated fdi's */
michael@0 111 struct fd_info *ed_fds; /* allocated fdi table */
michael@0 112 /* fdi's that we need to reassoc */
michael@0 113 int ed_pending[EVENTS_PER_GETN]; /* fd's with pending events */
michael@0 114 };
michael@0 115
michael@0 116 static void* evport_init(struct event_base *);
michael@0 117 static int evport_add(struct event_base *, int fd, short old, short events, void *);
michael@0 118 static int evport_del(struct event_base *, int fd, short old, short events, void *);
michael@0 119 static int evport_dispatch(struct event_base *, struct timeval *);
michael@0 120 static void evport_dealloc(struct event_base *);
michael@0 121
michael@0 122 const struct eventop evportops = {
michael@0 123 "evport",
michael@0 124 evport_init,
michael@0 125 evport_add,
michael@0 126 evport_del,
michael@0 127 evport_dispatch,
michael@0 128 evport_dealloc,
michael@0 129 1, /* need reinit */
michael@0 130 0, /* features */
michael@0 131 0, /* fdinfo length */
michael@0 132 };
michael@0 133
michael@0 134 /*
michael@0 135 * Initialize the event port implementation.
michael@0 136 */
michael@0 137
michael@0 138 static void*
michael@0 139 evport_init(struct event_base *base)
michael@0 140 {
michael@0 141 struct evport_data *evpd;
michael@0 142 int i;
michael@0 143
michael@0 144 if (!(evpd = mm_calloc(1, sizeof(struct evport_data))))
michael@0 145 return (NULL);
michael@0 146
michael@0 147 if ((evpd->ed_port = port_create()) == -1) {
michael@0 148 mm_free(evpd);
michael@0 149 return (NULL);
michael@0 150 }
michael@0 151
michael@0 152 /*
michael@0 153 * Initialize file descriptor structure
michael@0 154 */
michael@0 155 evpd->ed_fds = mm_calloc(DEFAULT_NFDS, sizeof(struct fd_info));
michael@0 156 if (evpd->ed_fds == NULL) {
michael@0 157 close(evpd->ed_port);
michael@0 158 mm_free(evpd);
michael@0 159 return (NULL);
michael@0 160 }
michael@0 161 evpd->ed_nevents = DEFAULT_NFDS;
michael@0 162 for (i = 0; i < EVENTS_PER_GETN; i++)
michael@0 163 evpd->ed_pending[i] = -1;
michael@0 164
michael@0 165 evsig_init(base);
michael@0 166
michael@0 167 return (evpd);
michael@0 168 }
michael@0 169
michael@0 170 #ifdef CHECK_INVARIANTS
michael@0 171 /*
michael@0 172 * Checks some basic properties about the evport_data structure. Because it
michael@0 173 * checks all file descriptors, this function can be expensive when the maximum
michael@0 174 * file descriptor ever used is rather large.
michael@0 175 */
michael@0 176
michael@0 177 static void
michael@0 178 check_evportop(struct evport_data *evpd)
michael@0 179 {
michael@0 180 EVUTIL_ASSERT(evpd);
michael@0 181 EVUTIL_ASSERT(evpd->ed_nevents > 0);
michael@0 182 EVUTIL_ASSERT(evpd->ed_port > 0);
michael@0 183 EVUTIL_ASSERT(evpd->ed_fds > 0);
michael@0 184 }
michael@0 185
michael@0 186 /*
michael@0 187 * Verifies very basic integrity of a given port_event.
michael@0 188 */
michael@0 189 static void
michael@0 190 check_event(port_event_t* pevt)
michael@0 191 {
michael@0 192 /*
michael@0 193 * We've only registered for PORT_SOURCE_FD events. The only
michael@0 194 * other thing we can legitimately receive is PORT_SOURCE_ALERT,
michael@0 195 * but since we're not using port_alert either, we can assume
michael@0 196 * PORT_SOURCE_FD.
michael@0 197 */
michael@0 198 EVUTIL_ASSERT(pevt->portev_source == PORT_SOURCE_FD);
michael@0 199 EVUTIL_ASSERT(pevt->portev_user == NULL);
michael@0 200 }
michael@0 201
michael@0 202 #else
michael@0 203 #define check_evportop(epop)
michael@0 204 #define check_event(pevt)
michael@0 205 #endif /* CHECK_INVARIANTS */
michael@0 206
michael@0 207 /*
michael@0 208 * Doubles the size of the allocated file descriptor array.
michael@0 209 */
michael@0 210 static int
michael@0 211 grow(struct evport_data *epdp, int factor)
michael@0 212 {
michael@0 213 struct fd_info *tmp;
michael@0 214 int oldsize = epdp->ed_nevents;
michael@0 215 int newsize = factor * oldsize;
michael@0 216 EVUTIL_ASSERT(factor > 1);
michael@0 217
michael@0 218 check_evportop(epdp);
michael@0 219
michael@0 220 tmp = mm_realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize);
michael@0 221 if (NULL == tmp)
michael@0 222 return -1;
michael@0 223 epdp->ed_fds = tmp;
michael@0 224 memset((char*) (epdp->ed_fds + oldsize), 0,
michael@0 225 (newsize - oldsize)*sizeof(struct fd_info));
michael@0 226 epdp->ed_nevents = newsize;
michael@0 227
michael@0 228 check_evportop(epdp);
michael@0 229
michael@0 230 return 0;
michael@0 231 }
michael@0 232
michael@0 233
michael@0 234 /*
michael@0 235 * (Re)associates the given file descriptor with the event port. The OS events
michael@0 236 * are specified (implicitly) from the fd_info struct.
michael@0 237 */
michael@0 238 static int
michael@0 239 reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd)
michael@0 240 {
michael@0 241 int sysevents = FDI_TO_SYSEVENTS(fdip);
michael@0 242
michael@0 243 if (sysevents != 0) {
michael@0 244 if (port_associate(epdp->ed_port, PORT_SOURCE_FD,
michael@0 245 fd, sysevents, NULL) == -1) {
michael@0 246 event_warn("port_associate");
michael@0 247 return (-1);
michael@0 248 }
michael@0 249 }
michael@0 250
michael@0 251 check_evportop(epdp);
michael@0 252
michael@0 253 return (0);
michael@0 254 }
michael@0 255
michael@0 256 /*
michael@0 257 * Main event loop - polls port_getn for some number of events, and processes
michael@0 258 * them.
michael@0 259 */
michael@0 260
michael@0 261 static int
michael@0 262 evport_dispatch(struct event_base *base, struct timeval *tv)
michael@0 263 {
michael@0 264 int i, res;
michael@0 265 struct evport_data *epdp = base->evbase;
michael@0 266 port_event_t pevtlist[EVENTS_PER_GETN];
michael@0 267
michael@0 268 /*
michael@0 269 * port_getn will block until it has at least nevents events. It will
michael@0 270 * also return how many it's given us (which may be more than we asked
michael@0 271 * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in
michael@0 272 * nevents.
michael@0 273 */
michael@0 274 int nevents = 1;
michael@0 275
michael@0 276 /*
michael@0 277 * We have to convert a struct timeval to a struct timespec
michael@0 278 * (only difference is nanoseconds vs. microseconds). If no time-based
michael@0 279 * events are active, we should wait for I/O (and tv == NULL).
michael@0 280 */
michael@0 281 struct timespec ts;
michael@0 282 struct timespec *ts_p = NULL;
michael@0 283 if (tv != NULL) {
michael@0 284 ts.tv_sec = tv->tv_sec;
michael@0 285 ts.tv_nsec = tv->tv_usec * 1000;
michael@0 286 ts_p = &ts;
michael@0 287 }
michael@0 288
michael@0 289 /*
michael@0 290 * Before doing anything else, we need to reassociate the events we hit
michael@0 291 * last time which need reassociation. See comment at the end of the
michael@0 292 * loop below.
michael@0 293 */
michael@0 294 for (i = 0; i < EVENTS_PER_GETN; ++i) {
michael@0 295 struct fd_info *fdi = NULL;
michael@0 296 if (epdp->ed_pending[i] != -1) {
michael@0 297 fdi = &(epdp->ed_fds[epdp->ed_pending[i]]);
michael@0 298 }
michael@0 299
michael@0 300 if (fdi != NULL && FDI_HAS_EVENTS(fdi)) {
michael@0 301 int fd = epdp->ed_pending[i];
michael@0 302 reassociate(epdp, fdi, fd);
michael@0 303 epdp->ed_pending[i] = -1;
michael@0 304 }
michael@0 305 }
michael@0 306
michael@0 307 EVBASE_RELEASE_LOCK(base, th_base_lock);
michael@0 308
michael@0 309 res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN,
michael@0 310 (unsigned int *) &nevents, ts_p);
michael@0 311
michael@0 312 EVBASE_ACQUIRE_LOCK(base, th_base_lock);
michael@0 313
michael@0 314 if (res == -1) {
michael@0 315 if (errno == EINTR || errno == EAGAIN) {
michael@0 316 return (0);
michael@0 317 } else if (errno == ETIME) {
michael@0 318 if (nevents == 0)
michael@0 319 return (0);
michael@0 320 } else {
michael@0 321 event_warn("port_getn");
michael@0 322 return (-1);
michael@0 323 }
michael@0 324 }
michael@0 325
michael@0 326 event_debug(("%s: port_getn reports %d events", __func__, nevents));
michael@0 327
michael@0 328 for (i = 0; i < nevents; ++i) {
michael@0 329 struct fd_info *fdi;
michael@0 330 port_event_t *pevt = &pevtlist[i];
michael@0 331 int fd = (int) pevt->portev_object;
michael@0 332
michael@0 333 check_evportop(epdp);
michael@0 334 check_event(pevt);
michael@0 335 epdp->ed_pending[i] = fd;
michael@0 336
michael@0 337 /*
michael@0 338 * Figure out what kind of event it was
michael@0 339 * (because we have to pass this to the callback)
michael@0 340 */
michael@0 341 res = 0;
michael@0 342 if (pevt->portev_events & (POLLERR|POLLHUP)) {
michael@0 343 res = EV_READ | EV_WRITE;
michael@0 344 } else {
michael@0 345 if (pevt->portev_events & POLLIN)
michael@0 346 res |= EV_READ;
michael@0 347 if (pevt->portev_events & POLLOUT)
michael@0 348 res |= EV_WRITE;
michael@0 349 }
michael@0 350
michael@0 351 /*
michael@0 352 * Check for the error situations or a hangup situation
michael@0 353 */
michael@0 354 if (pevt->portev_events & (POLLERR|POLLHUP|POLLNVAL))
michael@0 355 res |= EV_READ|EV_WRITE;
michael@0 356
michael@0 357 EVUTIL_ASSERT(epdp->ed_nevents > fd);
michael@0 358 fdi = &(epdp->ed_fds[fd]);
michael@0 359
michael@0 360 evmap_io_active(base, fd, res);
michael@0 361 } /* end of all events gotten */
michael@0 362
michael@0 363 check_evportop(epdp);
michael@0 364
michael@0 365 return (0);
michael@0 366 }
michael@0 367
michael@0 368
michael@0 369 /*
michael@0 370 * Adds the given event (so that you will be notified when it happens via
michael@0 371 * the callback function).
michael@0 372 */
michael@0 373
michael@0 374 static int
michael@0 375 evport_add(struct event_base *base, int fd, short old, short events, void *p)
michael@0 376 {
michael@0 377 struct evport_data *evpd = base->evbase;
michael@0 378 struct fd_info *fdi;
michael@0 379 int factor;
michael@0 380 (void)p;
michael@0 381
michael@0 382 check_evportop(evpd);
michael@0 383
michael@0 384 /*
michael@0 385 * If necessary, grow the file descriptor info table
michael@0 386 */
michael@0 387
michael@0 388 factor = 1;
michael@0 389 while (fd >= factor * evpd->ed_nevents)
michael@0 390 factor *= 2;
michael@0 391
michael@0 392 if (factor > 1) {
michael@0 393 if (-1 == grow(evpd, factor)) {
michael@0 394 return (-1);
michael@0 395 }
michael@0 396 }
michael@0 397
michael@0 398 fdi = &evpd->ed_fds[fd];
michael@0 399 fdi->fdi_what |= events;
michael@0 400
michael@0 401 return reassociate(evpd, fdi, fd);
michael@0 402 }
michael@0 403
michael@0 404 /*
michael@0 405 * Removes the given event from the list of events to wait for.
michael@0 406 */
michael@0 407
michael@0 408 static int
michael@0 409 evport_del(struct event_base *base, int fd, short old, short events, void *p)
michael@0 410 {
michael@0 411 struct evport_data *evpd = base->evbase;
michael@0 412 struct fd_info *fdi;
michael@0 413 int i;
michael@0 414 int associated = 1;
michael@0 415 (void)p;
michael@0 416
michael@0 417 check_evportop(evpd);
michael@0 418
michael@0 419 if (evpd->ed_nevents < fd) {
michael@0 420 return (-1);
michael@0 421 }
michael@0 422
michael@0 423 for (i = 0; i < EVENTS_PER_GETN; ++i) {
michael@0 424 if (evpd->ed_pending[i] == fd) {
michael@0 425 associated = 0;
michael@0 426 break;
michael@0 427 }
michael@0 428 }
michael@0 429
michael@0 430 fdi = &evpd->ed_fds[fd];
michael@0 431 if (events & EV_READ)
michael@0 432 fdi->fdi_what &= ~EV_READ;
michael@0 433 if (events & EV_WRITE)
michael@0 434 fdi->fdi_what &= ~EV_WRITE;
michael@0 435
michael@0 436 if (associated) {
michael@0 437 if (!FDI_HAS_EVENTS(fdi) &&
michael@0 438 port_dissociate(evpd->ed_port, PORT_SOURCE_FD, fd) == -1) {
michael@0 439 /*
michael@0 440 * Ignore EBADFD error the fd could have been closed
michael@0 441 * before event_del() was called.
michael@0 442 */
michael@0 443 if (errno != EBADFD) {
michael@0 444 event_warn("port_dissociate");
michael@0 445 return (-1);
michael@0 446 }
michael@0 447 } else {
michael@0 448 if (FDI_HAS_EVENTS(fdi)) {
michael@0 449 return (reassociate(evpd, fdi, fd));
michael@0 450 }
michael@0 451 }
michael@0 452 } else {
michael@0 453 if ((fdi->fdi_what & (EV_READ|EV_WRITE)) == 0) {
michael@0 454 evpd->ed_pending[i] = -1;
michael@0 455 }
michael@0 456 }
michael@0 457 return 0;
michael@0 458 }
michael@0 459
michael@0 460
michael@0 461 static void
michael@0 462 evport_dealloc(struct event_base *base)
michael@0 463 {
michael@0 464 struct evport_data *evpd = base->evbase;
michael@0 465
michael@0 466 evsig_dealloc(base);
michael@0 467
michael@0 468 close(evpd->ed_port);
michael@0 469
michael@0 470 if (evpd->ed_fds)
michael@0 471 mm_free(evpd->ed_fds);
michael@0 472 mm_free(evpd);
michael@0 473 }

mercurial