Blame libevent/evmap.c

Packit e9ba0d
/*
Packit e9ba0d
 * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
Packit e9ba0d
 *
Packit e9ba0d
 * Redistribution and use in source and binary forms, with or without
Packit e9ba0d
 * modification, are permitted provided that the following conditions
Packit e9ba0d
 * are met:
Packit e9ba0d
 * 1. Redistributions of source code must retain the above copyright
Packit e9ba0d
 *    notice, this list of conditions and the following disclaimer.
Packit e9ba0d
 * 2. Redistributions in binary form must reproduce the above copyright
Packit e9ba0d
 *    notice, this list of conditions and the following disclaimer in the
Packit e9ba0d
 *    documentation and/or other materials provided with the distribution.
Packit e9ba0d
 * 3. The name of the author may not be used to endorse or promote products
Packit e9ba0d
 *    derived from this software without specific prior written permission.
Packit e9ba0d
 *
Packit e9ba0d
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
Packit e9ba0d
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Packit e9ba0d
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Packit e9ba0d
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit e9ba0d
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Packit e9ba0d
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit e9ba0d
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit e9ba0d
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit e9ba0d
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Packit e9ba0d
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit e9ba0d
 */
Packit e9ba0d
#include "event2/event-config.h"
Packit e9ba0d
Packit e9ba0d
#ifdef WIN32
Packit e9ba0d
#include <winsock2.h>
Packit e9ba0d
#define WIN32_LEAN_AND_MEAN
Packit e9ba0d
#include <windows.h>
Packit e9ba0d
#undef WIN32_LEAN_AND_MEAN
Packit e9ba0d
#endif
Packit e9ba0d
#include <sys/types.h>
Packit e9ba0d
#if !defined(WIN32) && defined(_EVENT_HAVE_SYS_TIME_H)
Packit e9ba0d
#include <sys/time.h>
Packit e9ba0d
#endif
Packit e9ba0d
#include <sys/queue.h>
Packit e9ba0d
#include <stdio.h>
Packit e9ba0d
#include <stdlib.h>
Packit e9ba0d
#ifndef WIN32
Packit e9ba0d
#include <unistd.h>
Packit e9ba0d
#endif
Packit e9ba0d
#include <errno.h>
Packit e9ba0d
#include <signal.h>
Packit e9ba0d
#include <string.h>
Packit e9ba0d
#include <time.h>
Packit e9ba0d
Packit e9ba0d
#include "event-internal.h"
Packit e9ba0d
#include "evmap-internal.h"
Packit e9ba0d
#include "mm-internal.h"
Packit e9ba0d
#include "changelist-internal.h"
Packit e9ba0d
Packit e9ba0d
/** An entry for an evmap_io list: notes all the events that want to read or
Packit e9ba0d
	write on a given fd, and the number of each.
Packit e9ba0d
  */
Packit e9ba0d
struct evmap_io {
Packit e9ba0d
	struct event_list events;
Packit e9ba0d
	ev_uint16_t nread;
Packit e9ba0d
	ev_uint16_t nwrite;
Packit e9ba0d
};
Packit e9ba0d
Packit e9ba0d
/* An entry for an evmap_signal list: notes all the events that want to know
Packit e9ba0d
   when a signal triggers. */
Packit e9ba0d
struct evmap_signal {
Packit e9ba0d
	struct event_list events;
Packit e9ba0d
};
Packit e9ba0d
Packit e9ba0d
/* On some platforms, fds start at 0 and increment by 1 as they are
Packit e9ba0d
   allocated, and old numbers get used.  For these platforms, we
Packit e9ba0d
   implement io maps just like signal maps: as an array of pointers to
Packit e9ba0d
   struct evmap_io.  But on other platforms (windows), sockets are not
Packit e9ba0d
   0-indexed, not necessarily consecutive, and not necessarily reused.
Packit e9ba0d
   There, we use a hashtable to implement evmap_io.
Packit e9ba0d
*/
Packit e9ba0d
#ifdef EVMAP_USE_HT
Packit e9ba0d
struct event_map_entry {
Packit e9ba0d
	HT_ENTRY(event_map_entry) map_node;
Packit e9ba0d
	evutil_socket_t fd;
Packit e9ba0d
	union { /* This is a union in case we need to make more things that can
Packit e9ba0d
			   be in the hashtable. */
Packit e9ba0d
		struct evmap_io evmap_io;
Packit e9ba0d
	} ent;
Packit e9ba0d
};
Packit e9ba0d
Packit e9ba0d
/* Helper used by the event_io_map hashtable code; tries to return a good hash
Packit e9ba0d
 * of the fd in e->fd. */
Packit e9ba0d
static inline unsigned
Packit e9ba0d
hashsocket(struct event_map_entry *e)
Packit e9ba0d
{
Packit e9ba0d
	/* On win32, in practice, the low 2-3 bits of a SOCKET seem not to
Packit e9ba0d
	 * matter.  Our hashtable implementation really likes low-order bits,
Packit e9ba0d
	 * though, so let's do the rotate-and-add trick. */
Packit e9ba0d
	unsigned h = (unsigned) e->fd;
Packit e9ba0d
	h += (h >> 2) | (h << 30);
Packit e9ba0d
	return h;
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
/* Helper used by the event_io_map hashtable code; returns true iff e1 and e2
Packit e9ba0d
 * have the same e->fd. */
Packit e9ba0d
static inline int
Packit e9ba0d
eqsocket(struct event_map_entry *e1, struct event_map_entry *e2)
Packit e9ba0d
{
Packit e9ba0d
	return e1->fd == e2->fd;
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
HT_PROTOTYPE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket)
Packit e9ba0d
HT_GENERATE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket,
Packit e9ba0d
			0.5, mm_malloc, mm_realloc, mm_free)
Packit e9ba0d
Packit e9ba0d
#define GET_IO_SLOT(x, map, slot, type)					\
Packit e9ba0d
	do {								\
Packit e9ba0d
		struct event_map_entry _key, *_ent;			\
Packit e9ba0d
		_key.fd = slot;						\
Packit e9ba0d
		_ent = HT_FIND(event_io_map, map, &_key);		\
Packit e9ba0d
		(x) = _ent ? &_ent->ent.type : NULL;			\
Packit e9ba0d
	} while (0);
Packit e9ba0d
Packit e9ba0d
#define GET_IO_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)	\
Packit e9ba0d
	do {								\
Packit e9ba0d
		struct event_map_entry _key, *_ent;			\
Packit e9ba0d
		_key.fd = slot;						\
Packit e9ba0d
		_HT_FIND_OR_INSERT(event_io_map, map_node, hashsocket, map, \
Packit e9ba0d
		    event_map_entry, &_key, ptr,			\
Packit e9ba0d
		    {							\
Packit e9ba0d
			    _ent = *ptr;				\
Packit e9ba0d
		    },							\
Packit e9ba0d
		    {							\
Packit e9ba0d
			    _ent = mm_calloc(1,sizeof(struct event_map_entry)+fdinfo_len); \
Packit e9ba0d
			    if (EVUTIL_UNLIKELY(_ent == NULL))		\
Packit e9ba0d
				    return (-1);			\
Packit e9ba0d
			    _ent->fd = slot;				\
Packit e9ba0d
			    (ctor)(&_ent->ent.type);			\
Packit e9ba0d
			    _HT_FOI_INSERT(map_node, map, &_key, _ent, ptr) \
Packit e9ba0d
				});					\
Packit e9ba0d
		(x) = &_ent->ent.type;					\
Packit e9ba0d
	} while (0)
Packit e9ba0d
Packit e9ba0d
void evmap_io_initmap(struct event_io_map *ctx)
Packit e9ba0d
{
Packit e9ba0d
	HT_INIT(event_io_map, ctx);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
void evmap_io_clear(struct event_io_map *ctx)
Packit e9ba0d
{
Packit e9ba0d
	struct event_map_entry **ent, **next, *this;
Packit e9ba0d
	for (ent = HT_START(event_io_map, ctx); ent; ent = next) {
Packit e9ba0d
		this = *ent;
Packit e9ba0d
		next = HT_NEXT_RMV(event_io_map, ctx, ent);
Packit e9ba0d
		mm_free(this);
Packit e9ba0d
	}
Packit e9ba0d
	HT_CLEAR(event_io_map, ctx); /* remove all storage held by the ctx. */
Packit e9ba0d
}
Packit e9ba0d
#endif
Packit e9ba0d
Packit e9ba0d
/* Set the variable 'x' to the field in event_map 'map' with fields of type
Packit e9ba0d
   'struct type *' corresponding to the fd or signal 'slot'.  Set 'x' to NULL
Packit e9ba0d
   if there are no entries for 'slot'.  Does no bounds-checking. */
Packit e9ba0d
#define GET_SIGNAL_SLOT(x, map, slot, type)			\
Packit e9ba0d
	(x) = (struct type *)((map)->entries[slot])
Packit e9ba0d
/* As GET_SLOT, but construct the entry for 'slot' if it is not present,
Packit e9ba0d
   by allocating enough memory for a 'struct type', and initializing the new
Packit e9ba0d
   value by calling the function 'ctor' on it.  Makes the function
Packit e9ba0d
   return -1 on allocation failure.
Packit e9ba0d
 */
Packit e9ba0d
#define GET_SIGNAL_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)	\
Packit e9ba0d
	do {								\
Packit e9ba0d
		if ((map)->entries[slot] == NULL) {			\
Packit e9ba0d
			(map)->entries[slot] =				\
Packit e9ba0d
			    mm_calloc(1,sizeof(struct type)+fdinfo_len); \
Packit e9ba0d
			if (EVUTIL_UNLIKELY((map)->entries[slot] == NULL)) \
Packit e9ba0d
				return (-1);				\
Packit e9ba0d
			(ctor)((struct type *)(map)->entries[slot]);	\
Packit e9ba0d
		}							\
Packit e9ba0d
		(x) = (struct type *)((map)->entries[slot]);		\
Packit e9ba0d
	} while (0)
Packit e9ba0d
Packit e9ba0d
/* If we aren't using hashtables, then define the IO_SLOT macros and functions
Packit e9ba0d
   as thin aliases over the SIGNAL_SLOT versions. */
Packit e9ba0d
#ifndef EVMAP_USE_HT
Packit e9ba0d
#define GET_IO_SLOT(x,map,slot,type) GET_SIGNAL_SLOT(x,map,slot,type)
Packit e9ba0d
#define GET_IO_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)	\
Packit e9ba0d
	GET_SIGNAL_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)
Packit e9ba0d
#define FDINFO_OFFSET sizeof(struct evmap_io)
Packit e9ba0d
void
Packit e9ba0d
evmap_io_initmap(struct event_io_map* ctx)
Packit e9ba0d
{
Packit e9ba0d
	evmap_signal_initmap(ctx);
Packit e9ba0d
}
Packit e9ba0d
void
Packit e9ba0d
evmap_io_clear(struct event_io_map* ctx)
Packit e9ba0d
{
Packit e9ba0d
	evmap_signal_clear(ctx);
Packit e9ba0d
}
Packit e9ba0d
#endif
Packit e9ba0d
Packit e9ba0d
Packit e9ba0d
/** Expand 'map' with new entries of width 'msize' until it is big enough
Packit e9ba0d
	to store a value in 'slot'.
Packit e9ba0d
 */
Packit e9ba0d
static int
Packit e9ba0d
evmap_make_space(struct event_signal_map *map, int slot, int msize)
Packit e9ba0d
{
Packit e9ba0d
	if (map->nentries <= slot) {
Packit e9ba0d
		int nentries = map->nentries ? map->nentries : 32;
Packit e9ba0d
		void **tmp;
Packit e9ba0d
Packit e9ba0d
		while (nentries <= slot)
Packit e9ba0d
			nentries <<= 1;
Packit e9ba0d
Packit e9ba0d
		tmp = (void **)mm_realloc(map->entries, nentries * msize);
Packit e9ba0d
		if (tmp == NULL)
Packit e9ba0d
			return (-1);
Packit e9ba0d
Packit e9ba0d
		memset(&tmp[map->nentries], 0,
Packit e9ba0d
		    (nentries - map->nentries) * msize);
Packit e9ba0d
Packit e9ba0d
		map->nentries = nentries;
Packit e9ba0d
		map->entries = tmp;
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	return (0);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
void
Packit e9ba0d
evmap_signal_initmap(struct event_signal_map *ctx)
Packit e9ba0d
{
Packit e9ba0d
	ctx->nentries = 0;
Packit e9ba0d
	ctx->entries = NULL;
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
void
Packit e9ba0d
evmap_signal_clear(struct event_signal_map *ctx)
Packit e9ba0d
{
Packit e9ba0d
	if (ctx->entries != NULL) {
Packit e9ba0d
		int i;
Packit e9ba0d
		for (i = 0; i < ctx->nentries; ++i) {
Packit e9ba0d
			if (ctx->entries[i] != NULL)
Packit e9ba0d
				mm_free(ctx->entries[i]);
Packit e9ba0d
		}
Packit e9ba0d
		mm_free(ctx->entries);
Packit e9ba0d
		ctx->entries = NULL;
Packit e9ba0d
	}
Packit e9ba0d
	ctx->nentries = 0;
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
Packit e9ba0d
/* code specific to file descriptors */
Packit e9ba0d
Packit e9ba0d
/** Constructor for struct evmap_io */
Packit e9ba0d
static void
Packit e9ba0d
evmap_io_init(struct evmap_io *entry)
Packit e9ba0d
{
Packit e9ba0d
	TAILQ_INIT(&entry->events);
Packit e9ba0d
	entry->nread = 0;
Packit e9ba0d
	entry->nwrite = 0;
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
Packit e9ba0d
/* return -1 on error, 0 on success if nothing changed in the event backend,
Packit e9ba0d
 * and 1 on success if something did. */
Packit e9ba0d
int
Packit e9ba0d
evmap_io_add(struct event_base *base, evutil_socket_t fd, struct event *ev)
Packit e9ba0d
{
Packit e9ba0d
	const struct eventop *evsel = base->evsel;
Packit e9ba0d
	struct event_io_map *io = &base->io;
Packit e9ba0d
	struct evmap_io *ctx = NULL;
Packit e9ba0d
	int nread, nwrite, retval = 0;
Packit e9ba0d
	short res = 0, old = 0;
Packit e9ba0d
	struct event *old_ev;
Packit e9ba0d
Packit e9ba0d
	EVUTIL_ASSERT(fd == ev->ev_fd);
Packit e9ba0d
Packit e9ba0d
	if (fd < 0)
Packit e9ba0d
		return 0;
Packit e9ba0d
Packit e9ba0d
#ifndef EVMAP_USE_HT
Packit e9ba0d
	if (fd >= io->nentries) {
Packit e9ba0d
		if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
Packit e9ba0d
			return (-1);
Packit e9ba0d
	}
Packit e9ba0d
#endif
Packit e9ba0d
	GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
Packit e9ba0d
						 evsel->fdinfo_len);
Packit e9ba0d
Packit e9ba0d
	nread = ctx->nread;
Packit e9ba0d
	nwrite = ctx->nwrite;
Packit e9ba0d
Packit e9ba0d
	if (nread)
Packit e9ba0d
		old |= EV_READ;
Packit e9ba0d
	if (nwrite)
Packit e9ba0d
		old |= EV_WRITE;
Packit e9ba0d
Packit e9ba0d
	if (ev->ev_events & EV_READ) {
Packit e9ba0d
		if (++nread == 1)
Packit e9ba0d
			res |= EV_READ;
Packit e9ba0d
	}
Packit e9ba0d
	if (ev->ev_events & EV_WRITE) {
Packit e9ba0d
		if (++nwrite == 1)
Packit e9ba0d
			res |= EV_WRITE;
Packit e9ba0d
	}
Packit e9ba0d
	if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff)) {
Packit e9ba0d
		event_warnx("Too many events reading or writing on fd %d",
Packit e9ba0d
		    (int)fd);
Packit e9ba0d
		return -1;
Packit e9ba0d
	}
Packit e9ba0d
	if (EVENT_DEBUG_MODE_IS_ON() &&
Packit e9ba0d
	    (old_ev = TAILQ_FIRST(&ctx->events)) &&
Packit e9ba0d
	    (old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {
Packit e9ba0d
		event_warnx("Tried to mix edge-triggered and non-edge-triggered"
Packit e9ba0d
		    " events on fd %d", (int)fd);
Packit e9ba0d
		return -1;
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	if (res) {
Packit e9ba0d
		void *extra = ((char*)ctx) + sizeof(struct evmap_io);
Packit e9ba0d
		/* XXX(niels): we cannot mix edge-triggered and
Packit e9ba0d
		 * level-triggered, we should probably assert on
Packit e9ba0d
		 * this. */
Packit e9ba0d
		if (evsel->add(base, ev->ev_fd,
Packit e9ba0d
			old, (ev->ev_events & EV_ET) | res, extra) == -1)
Packit e9ba0d
			return (-1);
Packit e9ba0d
		retval = 1;
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	ctx->nread = (ev_uint16_t) nread;
Packit e9ba0d
	ctx->nwrite = (ev_uint16_t) nwrite;
Packit e9ba0d
	TAILQ_INSERT_TAIL(&ctx->events, ev, ev_io_next);
Packit e9ba0d
Packit e9ba0d
	return (retval);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
/* return -1 on error, 0 on success if nothing changed in the event backend,
Packit e9ba0d
 * and 1 on success if something did. */
Packit e9ba0d
int
Packit e9ba0d
evmap_io_del(struct event_base *base, evutil_socket_t fd, struct event *ev)
Packit e9ba0d
{
Packit e9ba0d
	const struct eventop *evsel = base->evsel;
Packit e9ba0d
	struct event_io_map *io = &base->io;
Packit e9ba0d
	struct evmap_io *ctx;
Packit e9ba0d
	int nread, nwrite, retval = 0;
Packit e9ba0d
	short res = 0, old = 0;
Packit e9ba0d
Packit e9ba0d
	if (fd < 0)
Packit e9ba0d
		return 0;
Packit e9ba0d
Packit e9ba0d
	EVUTIL_ASSERT(fd == ev->ev_fd);
Packit e9ba0d
Packit e9ba0d
#ifndef EVMAP_USE_HT
Packit e9ba0d
	if (fd >= io->nentries)
Packit e9ba0d
		return (-1);
Packit e9ba0d
#endif
Packit e9ba0d
Packit e9ba0d
	GET_IO_SLOT(ctx, io, fd, evmap_io);
Packit e9ba0d
Packit e9ba0d
	nread = ctx->nread;
Packit e9ba0d
	nwrite = ctx->nwrite;
Packit e9ba0d
Packit e9ba0d
	if (nread)
Packit e9ba0d
		old |= EV_READ;
Packit e9ba0d
	if (nwrite)
Packit e9ba0d
		old |= EV_WRITE;
Packit e9ba0d
Packit e9ba0d
	if (ev->ev_events & EV_READ) {
Packit e9ba0d
		if (--nread == 0)
Packit e9ba0d
			res |= EV_READ;
Packit e9ba0d
		EVUTIL_ASSERT(nread >= 0);
Packit e9ba0d
	}
Packit e9ba0d
	if (ev->ev_events & EV_WRITE) {
Packit e9ba0d
		if (--nwrite == 0)
Packit e9ba0d
			res |= EV_WRITE;
Packit e9ba0d
		EVUTIL_ASSERT(nwrite >= 0);
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	if (res) {
Packit e9ba0d
		void *extra = ((char*)ctx) + sizeof(struct evmap_io);
Packit e9ba0d
		if (evsel->del(base, ev->ev_fd, old, res, extra) == -1)
Packit e9ba0d
			return (-1);
Packit e9ba0d
		retval = 1;
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	ctx->nread = nread;
Packit e9ba0d
	ctx->nwrite = nwrite;
Packit e9ba0d
	TAILQ_REMOVE(&ctx->events, ev, ev_io_next);
Packit e9ba0d
Packit e9ba0d
	return (retval);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
void
Packit e9ba0d
evmap_io_active(struct event_base *base, evutil_socket_t fd, short events)
Packit e9ba0d
{
Packit e9ba0d
	struct event_io_map *io = &base->io;
Packit e9ba0d
	struct evmap_io *ctx;
Packit e9ba0d
	struct event *ev;
Packit e9ba0d
Packit e9ba0d
#ifndef EVMAP_USE_HT
Packit e9ba0d
	EVUTIL_ASSERT(fd < io->nentries);
Packit e9ba0d
#endif
Packit e9ba0d
	GET_IO_SLOT(ctx, io, fd, evmap_io);
Packit e9ba0d
Packit e9ba0d
	EVUTIL_ASSERT(ctx);
Packit e9ba0d
	TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {
Packit e9ba0d
		if (ev->ev_events & events)
Packit e9ba0d
			event_active_nolock(ev, ev->ev_events & events, 1);
Packit e9ba0d
	}
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
/* code specific to signals */
Packit e9ba0d
Packit e9ba0d
static void
Packit e9ba0d
evmap_signal_init(struct evmap_signal *entry)
Packit e9ba0d
{
Packit e9ba0d
	TAILQ_INIT(&entry->events);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
Packit e9ba0d
int
Packit e9ba0d
evmap_signal_add(struct event_base *base, int sig, struct event *ev)
Packit e9ba0d
{
Packit e9ba0d
	const struct eventop *evsel = base->evsigsel;
Packit e9ba0d
	struct event_signal_map *map = &base->sigmap;
Packit e9ba0d
	struct evmap_signal *ctx = NULL;
Packit e9ba0d
Packit e9ba0d
	if (sig >= map->nentries) {
Packit e9ba0d
		if (evmap_make_space(
Packit e9ba0d
			map, sig, sizeof(struct evmap_signal *)) == -1)
Packit e9ba0d
			return (-1);
Packit e9ba0d
	}
Packit e9ba0d
	GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,
Packit e9ba0d
	    base->evsigsel->fdinfo_len);
Packit e9ba0d
Packit e9ba0d
	if (TAILQ_EMPTY(&ctx->events)) {
Packit e9ba0d
		if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)
Packit e9ba0d
		    == -1)
Packit e9ba0d
			return (-1);
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	TAILQ_INSERT_TAIL(&ctx->events, ev, ev_signal_next);
Packit e9ba0d
Packit e9ba0d
	return (1);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
int
Packit e9ba0d
evmap_signal_del(struct event_base *base, int sig, struct event *ev)
Packit e9ba0d
{
Packit e9ba0d
	const struct eventop *evsel = base->evsigsel;
Packit e9ba0d
	struct event_signal_map *map = &base->sigmap;
Packit e9ba0d
	struct evmap_signal *ctx;
Packit e9ba0d
Packit e9ba0d
	if (sig >= map->nentries)
Packit e9ba0d
		return (-1);
Packit e9ba0d
Packit e9ba0d
	GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
Packit e9ba0d
Packit e9ba0d
	if (TAILQ_FIRST(&ctx->events) == TAILQ_LAST(&ctx->events, event_list)) {
Packit e9ba0d
		if (evsel->del(base, ev->ev_fd, 0, EV_SIGNAL, NULL) == -1)
Packit e9ba0d
			return (-1);
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	TAILQ_REMOVE(&ctx->events, ev, ev_signal_next);
Packit e9ba0d
Packit e9ba0d
	return (1);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
void
Packit e9ba0d
evmap_signal_active(struct event_base *base, evutil_socket_t sig, int ncalls)
Packit e9ba0d
{
Packit e9ba0d
	struct event_signal_map *map = &base->sigmap;
Packit e9ba0d
	struct evmap_signal *ctx;
Packit e9ba0d
	struct event *ev;
Packit e9ba0d
Packit e9ba0d
	EVUTIL_ASSERT(sig < map->nentries);
Packit e9ba0d
	GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
Packit e9ba0d
Packit e9ba0d
	TAILQ_FOREACH(ev, &ctx->events, ev_signal_next)
Packit e9ba0d
		event_active_nolock(ev, EV_SIGNAL, ncalls);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
void *
Packit e9ba0d
evmap_io_get_fdinfo(struct event_io_map *map, evutil_socket_t fd)
Packit e9ba0d
{
Packit e9ba0d
	struct evmap_io *ctx;
Packit e9ba0d
	GET_IO_SLOT(ctx, map, fd, evmap_io);
Packit e9ba0d
	if (ctx)
Packit e9ba0d
		return ((char*)ctx) + sizeof(struct evmap_io);
Packit e9ba0d
	else
Packit e9ba0d
		return NULL;
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
/** Per-fd structure for use with changelists.  It keeps track, for each fd or
Packit e9ba0d
 * signal using the changelist, of where its entry in the changelist is.
Packit e9ba0d
 */
Packit e9ba0d
struct event_changelist_fdinfo {
Packit e9ba0d
	int idxplus1; /* this is the index +1, so that memset(0) will make it
Packit e9ba0d
		       * a no-such-element */
Packit e9ba0d
};
Packit e9ba0d
Packit e9ba0d
void
Packit e9ba0d
event_changelist_init(struct event_changelist *changelist)
Packit e9ba0d
{
Packit e9ba0d
	changelist->changes = NULL;
Packit e9ba0d
	changelist->changes_size = 0;
Packit e9ba0d
	changelist->n_changes = 0;
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
/** Helper: return the changelist_fdinfo corresponding to a given change. */
Packit e9ba0d
static inline struct event_changelist_fdinfo *
Packit e9ba0d
event_change_get_fdinfo(struct event_base *base,
Packit e9ba0d
    const struct event_change *change)
Packit e9ba0d
{
Packit e9ba0d
	char *ptr;
Packit e9ba0d
	if (change->read_change & EV_CHANGE_SIGNAL) {
Packit e9ba0d
		struct evmap_signal *ctx;
Packit e9ba0d
		GET_SIGNAL_SLOT(ctx, &base->sigmap, change->fd, evmap_signal);
Packit e9ba0d
		ptr = ((char*)ctx) + sizeof(struct evmap_signal);
Packit e9ba0d
	} else {
Packit e9ba0d
		struct evmap_io *ctx;
Packit e9ba0d
		GET_IO_SLOT(ctx, &base->io, change->fd, evmap_io);
Packit e9ba0d
		ptr = ((char*)ctx) + sizeof(struct evmap_io);
Packit e9ba0d
	}
Packit e9ba0d
	return (void*)ptr;
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
#ifdef DEBUG_CHANGELIST
Packit e9ba0d
/** Make sure that the changelist is consistent with the evmap structures. */
Packit e9ba0d
static void
Packit e9ba0d
event_changelist_check(struct event_base *base)
Packit e9ba0d
{
Packit e9ba0d
	int i;
Packit e9ba0d
	struct event_changelist *changelist = &base->changelist;
Packit e9ba0d
Packit e9ba0d
	EVUTIL_ASSERT(changelist->changes_size >= changelist->n_changes);
Packit e9ba0d
	for (i = 0; i < changelist->n_changes; ++i) {
Packit e9ba0d
		struct event_change *c = &changelist->changes[i];
Packit e9ba0d
		struct event_changelist_fdinfo *f;
Packit e9ba0d
		EVUTIL_ASSERT(c->fd >= 0);
Packit e9ba0d
		f = event_change_get_fdinfo(base, c);
Packit e9ba0d
		EVUTIL_ASSERT(f);
Packit e9ba0d
		EVUTIL_ASSERT(f->idxplus1 == i + 1);
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	for (i = 0; i < base->io.nentries; ++i) {
Packit e9ba0d
		struct evmap_io *io = base->io.entries[i];
Packit e9ba0d
		struct event_changelist_fdinfo *f;
Packit e9ba0d
		if (!io)
Packit e9ba0d
			continue;
Packit e9ba0d
		f = (void*)
Packit e9ba0d
		    ( ((char*)io) + sizeof(struct evmap_io) );
Packit e9ba0d
		if (f->idxplus1) {
Packit e9ba0d
			struct event_change *c = &changelist->changes[f->idxplus1 - 1];
Packit e9ba0d
			EVUTIL_ASSERT(c->fd == i);
Packit e9ba0d
		}
Packit e9ba0d
	}
Packit e9ba0d
}
Packit e9ba0d
#else
Packit e9ba0d
#define event_changelist_check(base)  ((void)0)
Packit e9ba0d
#endif
Packit e9ba0d
Packit e9ba0d
void
Packit e9ba0d
event_changelist_remove_all(struct event_changelist *changelist,
Packit e9ba0d
    struct event_base *base)
Packit e9ba0d
{
Packit e9ba0d
	int i;
Packit e9ba0d
Packit e9ba0d
	event_changelist_check(base);
Packit e9ba0d
Packit e9ba0d
	for (i = 0; i < changelist->n_changes; ++i) {
Packit e9ba0d
		struct event_change *ch = &changelist->changes[i];
Packit e9ba0d
		struct event_changelist_fdinfo *fdinfo =
Packit e9ba0d
		    event_change_get_fdinfo(base, ch);
Packit e9ba0d
		EVUTIL_ASSERT(fdinfo->idxplus1 == i + 1);
Packit e9ba0d
		fdinfo->idxplus1 = 0;
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	changelist->n_changes = 0;
Packit e9ba0d
Packit e9ba0d
	event_changelist_check(base);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
void
Packit e9ba0d
event_changelist_freemem(struct event_changelist *changelist)
Packit e9ba0d
{
Packit e9ba0d
	if (changelist->changes)
Packit e9ba0d
		mm_free(changelist->changes);
Packit e9ba0d
	event_changelist_init(changelist); /* zero it all out. */
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
/** Increase the size of 'changelist' to hold more changes. */
Packit e9ba0d
static int
Packit e9ba0d
event_changelist_grow(struct event_changelist *changelist)
Packit e9ba0d
{
Packit e9ba0d
	int new_size;
Packit e9ba0d
	struct event_change *new_changes;
Packit e9ba0d
	if (changelist->changes_size < 64)
Packit e9ba0d
		new_size = 64;
Packit e9ba0d
	else
Packit e9ba0d
		new_size = changelist->changes_size * 2;
Packit e9ba0d
Packit e9ba0d
	new_changes = mm_realloc(changelist->changes,
Packit e9ba0d
	    new_size * sizeof(struct event_change));
Packit e9ba0d
Packit e9ba0d
	if (EVUTIL_UNLIKELY(new_changes == NULL))
Packit e9ba0d
		return (-1);
Packit e9ba0d
Packit e9ba0d
	changelist->changes = new_changes;
Packit e9ba0d
	changelist->changes_size = new_size;
Packit e9ba0d
Packit e9ba0d
	return (0);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
/** Return a pointer to the changelist entry for the file descriptor or signal
Packit e9ba0d
 * 'fd', whose fdinfo is 'fdinfo'.  If none exists, construct it, setting its
Packit e9ba0d
 * old_events field to old_events.
Packit e9ba0d
 */
Packit e9ba0d
static struct event_change *
Packit e9ba0d
event_changelist_get_or_construct(struct event_changelist *changelist,
Packit e9ba0d
    evutil_socket_t fd,
Packit e9ba0d
    short old_events,
Packit e9ba0d
    struct event_changelist_fdinfo *fdinfo)
Packit e9ba0d
{
Packit e9ba0d
	struct event_change *change;
Packit e9ba0d
Packit e9ba0d
	if (fdinfo->idxplus1 == 0) {
Packit e9ba0d
		int idx;
Packit e9ba0d
		EVUTIL_ASSERT(changelist->n_changes <= changelist->changes_size);
Packit e9ba0d
Packit e9ba0d
		if (changelist->n_changes == changelist->changes_size) {
Packit e9ba0d
			if (event_changelist_grow(changelist) < 0)
Packit e9ba0d
				return NULL;
Packit e9ba0d
		}
Packit e9ba0d
Packit e9ba0d
		idx = changelist->n_changes++;
Packit e9ba0d
		change = &changelist->changes[idx];
Packit e9ba0d
		fdinfo->idxplus1 = idx + 1;
Packit e9ba0d
Packit e9ba0d
		memset(change, 0, sizeof(struct event_change));
Packit e9ba0d
		change->fd = fd;
Packit e9ba0d
		change->old_events = old_events;
Packit e9ba0d
	} else {
Packit e9ba0d
		change = &changelist->changes[fdinfo->idxplus1 - 1];
Packit e9ba0d
		EVUTIL_ASSERT(change->fd == fd);
Packit e9ba0d
	}
Packit e9ba0d
	return change;
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
int
Packit e9ba0d
event_changelist_add(struct event_base *base, evutil_socket_t fd, short old, short events,
Packit e9ba0d
    void *p)
Packit e9ba0d
{
Packit e9ba0d
	struct event_changelist *changelist = &base->changelist;
Packit e9ba0d
	struct event_changelist_fdinfo *fdinfo = p;
Packit e9ba0d
	struct event_change *change;
Packit e9ba0d
Packit e9ba0d
	event_changelist_check(base);
Packit e9ba0d
Packit e9ba0d
	change = event_changelist_get_or_construct(changelist, fd, old, fdinfo);
Packit e9ba0d
	if (!change)
Packit e9ba0d
		return -1;
Packit e9ba0d
Packit e9ba0d
	/* An add replaces any previous delete, but doesn't result in a no-op,
Packit e9ba0d
	 * since the delete might fail (because the fd had been closed since
Packit e9ba0d
	 * the last add, for instance. */
Packit e9ba0d
Packit e9ba0d
	if (events & (EV_READ|EV_SIGNAL)) {
Packit e9ba0d
		change->read_change = EV_CHANGE_ADD |
Packit e9ba0d
		    (events & (EV_ET|EV_PERSIST|EV_SIGNAL));
Packit e9ba0d
	}
Packit e9ba0d
	if (events & EV_WRITE) {
Packit e9ba0d
		change->write_change = EV_CHANGE_ADD |
Packit e9ba0d
		    (events & (EV_ET|EV_PERSIST|EV_SIGNAL));
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	event_changelist_check(base);
Packit e9ba0d
	return (0);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
int
Packit e9ba0d
event_changelist_del(struct event_base *base, evutil_socket_t fd, short old, short events,
Packit e9ba0d
    void *p)
Packit e9ba0d
{
Packit e9ba0d
	struct event_changelist *changelist = &base->changelist;
Packit e9ba0d
	struct event_changelist_fdinfo *fdinfo = p;
Packit e9ba0d
	struct event_change *change;
Packit e9ba0d
Packit e9ba0d
	event_changelist_check(base);
Packit e9ba0d
	change = event_changelist_get_or_construct(changelist, fd, old, fdinfo);
Packit e9ba0d
	event_changelist_check(base);
Packit e9ba0d
	if (!change)
Packit e9ba0d
		return -1;
Packit e9ba0d
Packit e9ba0d
	/* A delete removes any previous add, rather than replacing it:
Packit e9ba0d
	   on those platforms where "add, delete, dispatch" is not the same
Packit e9ba0d
	   as "no-op, dispatch", we want the no-op behavior.
Packit e9ba0d
Packit e9ba0d
	   As well as checking the current operation we should also check
Packit e9ba0d
	   the original set of events to make sure were not ignoring
Packit e9ba0d
	   the case where the add operation is present on an event that
Packit e9ba0d
	   was already set.
Packit e9ba0d
Packit e9ba0d
	   If we have a no-op item, we could remove it it from the list
Packit e9ba0d
	   entirely, but really there's not much point: skipping the no-op
Packit e9ba0d
	   change when we do the dispatch later is far cheaper than rejuggling
Packit e9ba0d
	   the array now.
Packit e9ba0d
Packit e9ba0d
	   As this stands, it also lets through deletions of events that are
Packit e9ba0d
	   not currently set.
Packit e9ba0d
	 */
Packit e9ba0d
Packit e9ba0d
	if (events & (EV_READ|EV_SIGNAL)) {
Packit e9ba0d
		if (!(change->old_events & (EV_READ | EV_SIGNAL)) &&
Packit e9ba0d
		    (change->read_change & EV_CHANGE_ADD))
Packit e9ba0d
			change->read_change = 0;
Packit e9ba0d
		else
Packit e9ba0d
			change->read_change = EV_CHANGE_DEL;
Packit e9ba0d
	}
Packit e9ba0d
	if (events & EV_WRITE) {
Packit e9ba0d
		if (!(change->old_events & EV_WRITE) &&
Packit e9ba0d
		    (change->write_change & EV_CHANGE_ADD))
Packit e9ba0d
			change->write_change = 0;
Packit e9ba0d
		else
Packit e9ba0d
			change->write_change = EV_CHANGE_DEL;
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	event_changelist_check(base);
Packit e9ba0d
	return (0);
Packit e9ba0d
}
Packit e9ba0d
Packit e9ba0d
void
Packit e9ba0d
evmap_check_integrity(struct event_base *base)
Packit e9ba0d
{
Packit e9ba0d
#define EVLIST_X_SIGFOUND 0x1000
Packit e9ba0d
#define EVLIST_X_IOFOUND 0x2000
Packit e9ba0d
Packit e9ba0d
	evutil_socket_t i;
Packit e9ba0d
	struct event *ev;
Packit e9ba0d
	struct event_io_map *io = &base->io;
Packit e9ba0d
	struct event_signal_map *sigmap = &base->sigmap;
Packit e9ba0d
#ifdef EVMAP_USE_HT
Packit e9ba0d
	struct event_map_entry **mapent;
Packit e9ba0d
#endif
Packit e9ba0d
	int nsignals, ntimers, nio;
Packit e9ba0d
	nsignals = ntimers = nio = 0;
Packit e9ba0d
Packit e9ba0d
	TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
Packit e9ba0d
		EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED);
Packit e9ba0d
		EVUTIL_ASSERT(ev->ev_flags & EVLIST_INIT);
Packit e9ba0d
		ev->ev_flags &= ~(EVLIST_X_SIGFOUND|EVLIST_X_IOFOUND);
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
#ifdef EVMAP_USE_HT
Packit e9ba0d
	HT_FOREACH(mapent, event_io_map, io) {
Packit e9ba0d
		struct evmap_io *ctx = &(*mapent)->ent.evmap_io;
Packit e9ba0d
		i = (*mapent)->fd;
Packit e9ba0d
#else
Packit e9ba0d
	for (i = 0; i < io->nentries; ++i) {
Packit e9ba0d
		struct evmap_io *ctx = io->entries[i];
Packit e9ba0d
Packit e9ba0d
		if (!ctx)
Packit e9ba0d
			continue;
Packit e9ba0d
#endif
Packit e9ba0d
Packit e9ba0d
		TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {
Packit e9ba0d
			EVUTIL_ASSERT(!(ev->ev_flags & EVLIST_X_IOFOUND));
Packit e9ba0d
			EVUTIL_ASSERT(ev->ev_fd == i);
Packit e9ba0d
			ev->ev_flags |= EVLIST_X_IOFOUND;
Packit e9ba0d
			nio++;
Packit e9ba0d
		}
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	for (i = 0; i < sigmap->nentries; ++i) {
Packit e9ba0d
		struct evmap_signal *ctx = sigmap->entries[i];
Packit e9ba0d
		if (!ctx)
Packit e9ba0d
			continue;
Packit e9ba0d
Packit e9ba0d
		TAILQ_FOREACH(ev, &ctx->events, ev_signal_next) {
Packit e9ba0d
			EVUTIL_ASSERT(!(ev->ev_flags & EVLIST_X_SIGFOUND));
Packit e9ba0d
			EVUTIL_ASSERT(ev->ev_fd == i);
Packit e9ba0d
			ev->ev_flags |= EVLIST_X_SIGFOUND;
Packit e9ba0d
			nsignals++;
Packit e9ba0d
		}
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
Packit e9ba0d
		if (ev->ev_events & (EV_READ|EV_WRITE)) {
Packit e9ba0d
			EVUTIL_ASSERT(ev->ev_flags & EVLIST_X_IOFOUND);
Packit e9ba0d
			--nio;
Packit e9ba0d
		}
Packit e9ba0d
		if (ev->ev_events & EV_SIGNAL) {
Packit e9ba0d
			EVUTIL_ASSERT(ev->ev_flags & EVLIST_X_SIGFOUND);
Packit e9ba0d
			--nsignals;
Packit e9ba0d
		}
Packit e9ba0d
	}
Packit e9ba0d
Packit e9ba0d
	EVUTIL_ASSERT(nio == 0);
Packit e9ba0d
	EVUTIL_ASSERT(nsignals == 0);
Packit e9ba0d
	/* There is no "EVUTIL_ASSERT(ntimers == 0)": eventqueue is only for
Packit e9ba0d
	 * pending signals and io events.
Packit e9ba0d
	 */
Packit e9ba0d
}