1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/third_party/libevent/evport.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,473 @@ 1.4 +/* 1.5 + * Submitted by David Pacheco (dp.spambait@gmail.com) 1.6 + * 1.7 + * Copyright 2006-2007 Niels Provos 1.8 + * Copyright 2007-2012 Niels Provos and Nick Mathewson 1.9 + * 1.10 + * Redistribution and use in source and binary forms, with or without 1.11 + * modification, are permitted provided that the following conditions 1.12 + * are met: 1.13 + * 1. Redistributions of source code must retain the above copyright 1.14 + * notice, this list of conditions and the following disclaimer. 1.15 + * 2. Redistributions in binary form must reproduce the above copyright 1.16 + * notice, this list of conditions and the following disclaimer in the 1.17 + * documentation and/or other materials provided with the distribution. 1.18 + * 3. The name of the author may not be used to endorse or promote products 1.19 + * derived from this software without specific prior written permission. 1.20 + * 1.21 + * THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``AS IS'' AND ANY 1.22 + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1.23 + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1.24 + * DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY 1.25 + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1.26 + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 1.27 + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1.28 + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.29 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 1.30 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.31 + */ 1.32 + 1.33 +/* 1.34 + * Copyright (c) 2007 Sun Microsystems. All rights reserved. 1.35 + * Use is subject to license terms. 1.36 + */ 1.37 + 1.38 +/* 1.39 + * evport.c: event backend using Solaris 10 event ports. See port_create(3C). 1.40 + * This implementation is loosely modeled after the one used for select(2) (in 1.41 + * select.c). 1.42 + * 1.43 + * The outstanding events are tracked in a data structure called evport_data. 1.44 + * Each entry in the ed_fds array corresponds to a file descriptor, and contains 1.45 + * pointers to the read and write events that correspond to that fd. (That is, 1.46 + * when the file is readable, the "read" event should handle it, etc.) 1.47 + * 1.48 + * evport_add and evport_del update this data structure. evport_dispatch uses it 1.49 + * to determine where to callback when an event occurs (which it gets from 1.50 + * port_getn). 1.51 + * 1.52 + * Helper functions are used: grow() grows the file descriptor array as 1.53 + * necessary when large fd's come in. reassociate() takes care of maintaining 1.54 + * the proper file-descriptor/event-port associations. 1.55 + * 1.56 + * As in the select(2) implementation, signals are handled by evsignal. 1.57 + */ 1.58 + 1.59 +#include "event2/event-config.h" 1.60 + 1.61 +#include <sys/time.h> 1.62 +#include <sys/queue.h> 1.63 +#include <errno.h> 1.64 +#include <poll.h> 1.65 +#include <port.h> 1.66 +#include <signal.h> 1.67 +#include <stdio.h> 1.68 +#include <stdlib.h> 1.69 +#include <string.h> 1.70 +#include <time.h> 1.71 +#include <unistd.h> 1.72 + 1.73 +#include "event2/thread.h" 1.74 + 1.75 +#include "evthread-internal.h" 1.76 +#include "event-internal.h" 1.77 +#include "log-internal.h" 1.78 +#include "evsignal-internal.h" 1.79 +#include "evmap-internal.h" 1.80 + 1.81 +/* 1.82 + * Default value for ed_nevents, which is the maximum file descriptor number we 1.83 + * can handle. If an event comes in for a file descriptor F > nevents, we will 1.84 + * grow the array of file descriptors, doubling its size. 1.85 + */ 1.86 +#define DEFAULT_NFDS 16 1.87 + 1.88 + 1.89 +/* 1.90 + * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on 1.91 + * any particular call. You can speed things up by increasing this, but it will 1.92 + * (obviously) require more memory. 1.93 + */ 1.94 +#define EVENTS_PER_GETN 8 1.95 + 1.96 +/* 1.97 + * Per-file-descriptor information about what events we're subscribed to. These 1.98 + * fields are NULL if no event is subscribed to either of them. 1.99 + */ 1.100 + 1.101 +struct fd_info { 1.102 + short fdi_what; /* combinations of EV_READ and EV_WRITE */ 1.103 +}; 1.104 + 1.105 +#define FDI_HAS_READ(fdi) ((fdi)->fdi_what & EV_READ) 1.106 +#define FDI_HAS_WRITE(fdi) ((fdi)->fdi_what & EV_WRITE) 1.107 +#define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi)) 1.108 +#define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \ 1.109 + (FDI_HAS_WRITE(fdi) ? POLLOUT : 0) 1.110 + 1.111 +struct evport_data { 1.112 + int ed_port; /* event port for system events */ 1.113 + int ed_nevents; /* number of allocated fdi's */ 1.114 + struct fd_info *ed_fds; /* allocated fdi table */ 1.115 + /* fdi's that we need to reassoc */ 1.116 + int ed_pending[EVENTS_PER_GETN]; /* fd's with pending events */ 1.117 +}; 1.118 + 1.119 +static void* evport_init(struct event_base *); 1.120 +static int evport_add(struct event_base *, int fd, short old, short events, void *); 1.121 +static int evport_del(struct event_base *, int fd, short old, short events, void *); 1.122 +static int evport_dispatch(struct event_base *, struct timeval *); 1.123 +static void evport_dealloc(struct event_base *); 1.124 + 1.125 +const struct eventop evportops = { 1.126 + "evport", 1.127 + evport_init, 1.128 + evport_add, 1.129 + evport_del, 1.130 + evport_dispatch, 1.131 + evport_dealloc, 1.132 + 1, /* need reinit */ 1.133 + 0, /* features */ 1.134 + 0, /* fdinfo length */ 1.135 +}; 1.136 + 1.137 +/* 1.138 + * Initialize the event port implementation. 1.139 + */ 1.140 + 1.141 +static void* 1.142 +evport_init(struct event_base *base) 1.143 +{ 1.144 + struct evport_data *evpd; 1.145 + int i; 1.146 + 1.147 + if (!(evpd = mm_calloc(1, sizeof(struct evport_data)))) 1.148 + return (NULL); 1.149 + 1.150 + if ((evpd->ed_port = port_create()) == -1) { 1.151 + mm_free(evpd); 1.152 + return (NULL); 1.153 + } 1.154 + 1.155 + /* 1.156 + * Initialize file descriptor structure 1.157 + */ 1.158 + evpd->ed_fds = mm_calloc(DEFAULT_NFDS, sizeof(struct fd_info)); 1.159 + if (evpd->ed_fds == NULL) { 1.160 + close(evpd->ed_port); 1.161 + mm_free(evpd); 1.162 + return (NULL); 1.163 + } 1.164 + evpd->ed_nevents = DEFAULT_NFDS; 1.165 + for (i = 0; i < EVENTS_PER_GETN; i++) 1.166 + evpd->ed_pending[i] = -1; 1.167 + 1.168 + evsig_init(base); 1.169 + 1.170 + return (evpd); 1.171 +} 1.172 + 1.173 +#ifdef CHECK_INVARIANTS 1.174 +/* 1.175 + * Checks some basic properties about the evport_data structure. Because it 1.176 + * checks all file descriptors, this function can be expensive when the maximum 1.177 + * file descriptor ever used is rather large. 1.178 + */ 1.179 + 1.180 +static void 1.181 +check_evportop(struct evport_data *evpd) 1.182 +{ 1.183 + EVUTIL_ASSERT(evpd); 1.184 + EVUTIL_ASSERT(evpd->ed_nevents > 0); 1.185 + EVUTIL_ASSERT(evpd->ed_port > 0); 1.186 + EVUTIL_ASSERT(evpd->ed_fds > 0); 1.187 +} 1.188 + 1.189 +/* 1.190 + * Verifies very basic integrity of a given port_event. 1.191 + */ 1.192 +static void 1.193 +check_event(port_event_t* pevt) 1.194 +{ 1.195 + /* 1.196 + * We've only registered for PORT_SOURCE_FD events. The only 1.197 + * other thing we can legitimately receive is PORT_SOURCE_ALERT, 1.198 + * but since we're not using port_alert either, we can assume 1.199 + * PORT_SOURCE_FD. 1.200 + */ 1.201 + EVUTIL_ASSERT(pevt->portev_source == PORT_SOURCE_FD); 1.202 + EVUTIL_ASSERT(pevt->portev_user == NULL); 1.203 +} 1.204 + 1.205 +#else 1.206 +#define check_evportop(epop) 1.207 +#define check_event(pevt) 1.208 +#endif /* CHECK_INVARIANTS */ 1.209 + 1.210 +/* 1.211 + * Doubles the size of the allocated file descriptor array. 1.212 + */ 1.213 +static int 1.214 +grow(struct evport_data *epdp, int factor) 1.215 +{ 1.216 + struct fd_info *tmp; 1.217 + int oldsize = epdp->ed_nevents; 1.218 + int newsize = factor * oldsize; 1.219 + EVUTIL_ASSERT(factor > 1); 1.220 + 1.221 + check_evportop(epdp); 1.222 + 1.223 + tmp = mm_realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize); 1.224 + if (NULL == tmp) 1.225 + return -1; 1.226 + epdp->ed_fds = tmp; 1.227 + memset((char*) (epdp->ed_fds + oldsize), 0, 1.228 + (newsize - oldsize)*sizeof(struct fd_info)); 1.229 + epdp->ed_nevents = newsize; 1.230 + 1.231 + check_evportop(epdp); 1.232 + 1.233 + return 0; 1.234 +} 1.235 + 1.236 + 1.237 +/* 1.238 + * (Re)associates the given file descriptor with the event port. The OS events 1.239 + * are specified (implicitly) from the fd_info struct. 1.240 + */ 1.241 +static int 1.242 +reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd) 1.243 +{ 1.244 + int sysevents = FDI_TO_SYSEVENTS(fdip); 1.245 + 1.246 + if (sysevents != 0) { 1.247 + if (port_associate(epdp->ed_port, PORT_SOURCE_FD, 1.248 + fd, sysevents, NULL) == -1) { 1.249 + event_warn("port_associate"); 1.250 + return (-1); 1.251 + } 1.252 + } 1.253 + 1.254 + check_evportop(epdp); 1.255 + 1.256 + return (0); 1.257 +} 1.258 + 1.259 +/* 1.260 + * Main event loop - polls port_getn for some number of events, and processes 1.261 + * them. 1.262 + */ 1.263 + 1.264 +static int 1.265 +evport_dispatch(struct event_base *base, struct timeval *tv) 1.266 +{ 1.267 + int i, res; 1.268 + struct evport_data *epdp = base->evbase; 1.269 + port_event_t pevtlist[EVENTS_PER_GETN]; 1.270 + 1.271 + /* 1.272 + * port_getn will block until it has at least nevents events. It will 1.273 + * also return how many it's given us (which may be more than we asked 1.274 + * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in 1.275 + * nevents. 1.276 + */ 1.277 + int nevents = 1; 1.278 + 1.279 + /* 1.280 + * We have to convert a struct timeval to a struct timespec 1.281 + * (only difference is nanoseconds vs. microseconds). If no time-based 1.282 + * events are active, we should wait for I/O (and tv == NULL). 1.283 + */ 1.284 + struct timespec ts; 1.285 + struct timespec *ts_p = NULL; 1.286 + if (tv != NULL) { 1.287 + ts.tv_sec = tv->tv_sec; 1.288 + ts.tv_nsec = tv->tv_usec * 1000; 1.289 + ts_p = &ts; 1.290 + } 1.291 + 1.292 + /* 1.293 + * Before doing anything else, we need to reassociate the events we hit 1.294 + * last time which need reassociation. See comment at the end of the 1.295 + * loop below. 1.296 + */ 1.297 + for (i = 0; i < EVENTS_PER_GETN; ++i) { 1.298 + struct fd_info *fdi = NULL; 1.299 + if (epdp->ed_pending[i] != -1) { 1.300 + fdi = &(epdp->ed_fds[epdp->ed_pending[i]]); 1.301 + } 1.302 + 1.303 + if (fdi != NULL && FDI_HAS_EVENTS(fdi)) { 1.304 + int fd = epdp->ed_pending[i]; 1.305 + reassociate(epdp, fdi, fd); 1.306 + epdp->ed_pending[i] = -1; 1.307 + } 1.308 + } 1.309 + 1.310 + EVBASE_RELEASE_LOCK(base, th_base_lock); 1.311 + 1.312 + res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN, 1.313 + (unsigned int *) &nevents, ts_p); 1.314 + 1.315 + EVBASE_ACQUIRE_LOCK(base, th_base_lock); 1.316 + 1.317 + if (res == -1) { 1.318 + if (errno == EINTR || errno == EAGAIN) { 1.319 + return (0); 1.320 + } else if (errno == ETIME) { 1.321 + if (nevents == 0) 1.322 + return (0); 1.323 + } else { 1.324 + event_warn("port_getn"); 1.325 + return (-1); 1.326 + } 1.327 + } 1.328 + 1.329 + event_debug(("%s: port_getn reports %d events", __func__, nevents)); 1.330 + 1.331 + for (i = 0; i < nevents; ++i) { 1.332 + struct fd_info *fdi; 1.333 + port_event_t *pevt = &pevtlist[i]; 1.334 + int fd = (int) pevt->portev_object; 1.335 + 1.336 + check_evportop(epdp); 1.337 + check_event(pevt); 1.338 + epdp->ed_pending[i] = fd; 1.339 + 1.340 + /* 1.341 + * Figure out what kind of event it was 1.342 + * (because we have to pass this to the callback) 1.343 + */ 1.344 + res = 0; 1.345 + if (pevt->portev_events & (POLLERR|POLLHUP)) { 1.346 + res = EV_READ | EV_WRITE; 1.347 + } else { 1.348 + if (pevt->portev_events & POLLIN) 1.349 + res |= EV_READ; 1.350 + if (pevt->portev_events & POLLOUT) 1.351 + res |= EV_WRITE; 1.352 + } 1.353 + 1.354 + /* 1.355 + * Check for the error situations or a hangup situation 1.356 + */ 1.357 + if (pevt->portev_events & (POLLERR|POLLHUP|POLLNVAL)) 1.358 + res |= EV_READ|EV_WRITE; 1.359 + 1.360 + EVUTIL_ASSERT(epdp->ed_nevents > fd); 1.361 + fdi = &(epdp->ed_fds[fd]); 1.362 + 1.363 + evmap_io_active(base, fd, res); 1.364 + } /* end of all events gotten */ 1.365 + 1.366 + check_evportop(epdp); 1.367 + 1.368 + return (0); 1.369 +} 1.370 + 1.371 + 1.372 +/* 1.373 + * Adds the given event (so that you will be notified when it happens via 1.374 + * the callback function). 1.375 + */ 1.376 + 1.377 +static int 1.378 +evport_add(struct event_base *base, int fd, short old, short events, void *p) 1.379 +{ 1.380 + struct evport_data *evpd = base->evbase; 1.381 + struct fd_info *fdi; 1.382 + int factor; 1.383 + (void)p; 1.384 + 1.385 + check_evportop(evpd); 1.386 + 1.387 + /* 1.388 + * If necessary, grow the file descriptor info table 1.389 + */ 1.390 + 1.391 + factor = 1; 1.392 + while (fd >= factor * evpd->ed_nevents) 1.393 + factor *= 2; 1.394 + 1.395 + if (factor > 1) { 1.396 + if (-1 == grow(evpd, factor)) { 1.397 + return (-1); 1.398 + } 1.399 + } 1.400 + 1.401 + fdi = &evpd->ed_fds[fd]; 1.402 + fdi->fdi_what |= events; 1.403 + 1.404 + return reassociate(evpd, fdi, fd); 1.405 +} 1.406 + 1.407 +/* 1.408 + * Removes the given event from the list of events to wait for. 1.409 + */ 1.410 + 1.411 +static int 1.412 +evport_del(struct event_base *base, int fd, short old, short events, void *p) 1.413 +{ 1.414 + struct evport_data *evpd = base->evbase; 1.415 + struct fd_info *fdi; 1.416 + int i; 1.417 + int associated = 1; 1.418 + (void)p; 1.419 + 1.420 + check_evportop(evpd); 1.421 + 1.422 + if (evpd->ed_nevents < fd) { 1.423 + return (-1); 1.424 + } 1.425 + 1.426 + for (i = 0; i < EVENTS_PER_GETN; ++i) { 1.427 + if (evpd->ed_pending[i] == fd) { 1.428 + associated = 0; 1.429 + break; 1.430 + } 1.431 + } 1.432 + 1.433 + fdi = &evpd->ed_fds[fd]; 1.434 + if (events & EV_READ) 1.435 + fdi->fdi_what &= ~EV_READ; 1.436 + if (events & EV_WRITE) 1.437 + fdi->fdi_what &= ~EV_WRITE; 1.438 + 1.439 + if (associated) { 1.440 + if (!FDI_HAS_EVENTS(fdi) && 1.441 + port_dissociate(evpd->ed_port, PORT_SOURCE_FD, fd) == -1) { 1.442 + /* 1.443 + * Ignore EBADFD error the fd could have been closed 1.444 + * before event_del() was called. 1.445 + */ 1.446 + if (errno != EBADFD) { 1.447 + event_warn("port_dissociate"); 1.448 + return (-1); 1.449 + } 1.450 + } else { 1.451 + if (FDI_HAS_EVENTS(fdi)) { 1.452 + return (reassociate(evpd, fdi, fd)); 1.453 + } 1.454 + } 1.455 + } else { 1.456 + if ((fdi->fdi_what & (EV_READ|EV_WRITE)) == 0) { 1.457 + evpd->ed_pending[i] = -1; 1.458 + } 1.459 + } 1.460 + return 0; 1.461 +} 1.462 + 1.463 + 1.464 +static void 1.465 +evport_dealloc(struct event_base *base) 1.466 +{ 1.467 + struct evport_data *evpd = base->evbase; 1.468 + 1.469 + evsig_dealloc(base); 1.470 + 1.471 + close(evpd->ed_port); 1.472 + 1.473 + if (evpd->ed_fds) 1.474 + mm_free(evpd->ed_fds); 1.475 + mm_free(evpd); 1.476 +}