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

changeset 0
6474c204b198
     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 +}

mercurial