diff options
Diffstat (limited to 'event.c')
-rw-r--r-- | event.c | 1055 |
1 files changed, 1055 insertions, 0 deletions
@@ -0,0 +1,1055 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "syshead.h" + +#include "buffer.h" +#include "error.h" +#include "integer.h" +#include "event.h" + +#include "memdbg.h" + +/* + * Some OSes will prefer select() over poll() + * when both are available. + */ +#if defined(TARGET_DARWIN) +#define SELECT_PREFERRED_OVER_POLL +#endif + +/* + * All non-windows OSes are assumed to have select() + */ +#ifdef WIN32 +#define SELECT 0 +#else +#define SELECT 1 +#endif + +/* + * This should be set to the highest file descriptor + * which can be used in one of the FD_ macros. + */ +#ifdef FD_SETSIZE +#define SELECT_MAX_FDS FD_SETSIZE +#else +#define SELECT_MAX_FDS 256 +#endif + +static inline int +tv_to_ms_timeout (const struct timeval *tv) +{ + if (tv->tv_sec == 0 && tv->tv_usec == 0) + return 0; + else + return max_int (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000, 1); +} + +#ifdef WIN32 + +struct we_set +{ + struct event_set_functions func; + bool fast; + HANDLE *events; + struct event_set_return *esr; + int n_events; + int capacity; +}; + +static inline void +we_set_event (struct we_set *wes, int i, event_t event, unsigned int rwflags, void *arg) +{ + ASSERT (i >= 0 && i < wes->capacity); + + if (rwflags == EVENT_READ) + { + ASSERT (event->read != NULL); + wes->events[i] = event->read; + } + else if (rwflags == EVENT_WRITE) + { + ASSERT (event->write != NULL); + wes->events[i] = event->write; + } + else + msg (M_FATAL, "fatal error in we_set_events: rwflags=%d", rwflags); + + wes->esr[i].rwflags = rwflags; + wes->esr[i].arg = arg; +} + +static inline bool +we_append_event (struct we_set *wes, event_t event, unsigned int rwflags, void *arg) +{ + if (rwflags & EVENT_WRITE) + { + if (wes->n_events < wes->capacity) + { + we_set_event (wes, wes->n_events, event, EVENT_WRITE, arg); + ++wes->n_events; + } + else + return false; + } + if (rwflags & EVENT_READ) + { + if (wes->n_events < wes->capacity) + { + we_set_event (wes, wes->n_events, event, EVENT_READ, arg); + ++wes->n_events; + } + else + return false; + } + return true; +} + +static void +we_del_event (struct we_set *wes, event_t event) +{ + int i, j = 0; + const int len = wes->n_events; + + for (i = 0; i < len; ++i) + { + const HANDLE h = wes->events[i]; + if (h == event->read || h == event->write) + --wes->n_events; + else + { + if (i != j) + { + wes->events[j] = wes->events[i]; + wes->esr[j] = wes->esr[i]; + } + ++j; + } + } +} + +static void +we_del_index (struct we_set *wes, int index) +{ + int i; + ASSERT (index >= 0 && index < wes->n_events); + for (i = index; i < wes->n_events - 1; ++i) + { + wes->events[i] = wes->events[i+1]; + wes->esr[i] = wes->esr[i+1]; + } + --wes->n_events; +} + +static void +we_get_rw_indices (struct we_set *wes, event_t event, int *ri, int *wi) +{ + int i; + *ri = *wi = -1; + for (i = 0; i < wes->n_events; ++i) + { + const HANDLE h = wes->events[i]; + if (h == event->read) + { + ASSERT (*ri == -1); + *ri = i; + } + else if (h == event->write) + { + ASSERT (*wi == -1); + *wi = i; + } + } +} + +static void +we_free (struct event_set *es) +{ + struct we_set *wes = (struct we_set *) es; + free (wes->events); + free (wes->esr); + free (wes); +} + +static void +we_reset (struct event_set *es) +{ + struct we_set *wes = (struct we_set *) es; + ASSERT (wes->fast); + wes->n_events = 0; +} + +static void +we_del (struct event_set *es, event_t event) +{ + struct we_set *wes = (struct we_set *) es; + ASSERT (!wes->fast); + we_del_event (wes, event); +} + +static void +we_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg) +{ + struct we_set *wes = (struct we_set *) es; + + dmsg (D_EVENT_WAIT, "WE_CTL n=%d ev=0x%08x rwflags=0x%04x arg=" ptr_format, + wes->n_events, + (unsigned int)event, + rwflags, + (ptr_type)arg); + + if (wes->fast) + { + if (!we_append_event (wes, event, rwflags, arg)) + goto err; + } + else + { + int ri, wi; + int one = -1; + int n = 0; + + we_get_rw_indices (wes, event, &ri, &wi); + if (wi >= 0) + { + one = wi; + ++n; + } + if (ri >= 0) + { + one = ri; + ++n; + } + switch (rwflags) + { + case 0: + switch (n) + { + case 0: + break; + case 1: + we_del_index (wes, one); + break; + case 2: + we_del_event (wes, event); + break; + default: + ASSERT (0); + } + break; + case EVENT_READ: + switch (n) + { + case 0: + if (!we_append_event (wes, event, EVENT_READ, arg)) + goto err; + break; + case 1: + we_set_event (wes, one, event, EVENT_READ, arg); + break; + case 2: + we_del_index (wes, wi); + break; + default: + ASSERT (0); + } + break; + case EVENT_WRITE: + switch (n) + { + case 0: + if (!we_append_event (wes, event, EVENT_WRITE, arg)) + goto err; + break; + case 1: + we_set_event (wes, one, event, EVENT_WRITE, arg); + break; + case 2: + we_del_index (wes, ri); + break; + default: + ASSERT (0); + } + break; + case EVENT_READ|EVENT_WRITE: + switch (n) + { + case 0: + if (!we_append_event (wes, event, EVENT_READ|EVENT_WRITE, arg)) + goto err; + break; + case 1: + if (ri == -1) + { + ASSERT (wi != -1); + if (!we_append_event (wes, event, EVENT_READ, arg)) + goto err; + } + else if (wi == -1) + { + if (!we_append_event (wes, event, EVENT_WRITE, arg)) + goto err; + } + else + ASSERT (0); + break; + case 2: + break; + default: + ASSERT (0); + } + break; + default: + msg (M_FATAL, "fatal error in we_ctl: rwflags=%d", rwflags); + } + } + return; + + err: + msg (D_EVENT_ERRORS, "Error: Windows resource limit WSA_MAXIMUM_WAIT_EVENTS (%d) has been exceeded", WSA_MAXIMUM_WAIT_EVENTS); +} + +static int +we_wait (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) +{ + struct we_set *wes = (struct we_set *) es; + const int timeout = tv_to_ms_timeout (tv); + DWORD status; + + dmsg (D_EVENT_WAIT, "WE_WAIT enter n=%d to=%d", wes->n_events, timeout); + +#ifdef ENABLE_DEBUG + if (check_debug_level (D_EVENT_WAIT)) { + int i; + for (i = 0; i < wes->n_events; ++i) + dmsg (D_EVENT_WAIT, "[%d] ev=0x%08x rwflags=0x%04x arg=" ptr_format, + i, + (unsigned int)wes->events[i], + wes->esr[i].rwflags, + (ptr_type)wes->esr[i].arg); + } +#endif + + /* + * First poll our event list with 0 timeout + */ + status = WSAWaitForMultipleEvents( + (DWORD) wes->n_events, + wes->events, + FALSE, + (DWORD) 0, + FALSE); + + /* + * If at least one event is already set, we must + * individually poll the whole list. + */ + if (status >= WSA_WAIT_EVENT_0 && status < WSA_WAIT_EVENT_0 + (DWORD) wes->n_events) + { + int i; + int j = 0; + for (i = 0; i < wes->n_events; ++i) + { + if (j >= outlen) + break; + if (WaitForSingleObject (wes->events[i], 0) == WAIT_OBJECT_0) + { + *out = wes->esr[i]; + dmsg (D_EVENT_WAIT, "WE_WAIT leave [%d,%d] rwflags=0x%04x arg=" ptr_format, + i, j, out->rwflags, (ptr_type)out->arg); + ++j; + ++out; + } + } + return j; + } + else + { + /* + * If caller specified timeout > 0, we know at this point + * that no events are set, so wait only for the first event + * (or timeout) and return at most one event_set_return object. + * + * If caller specified timeout == 0, the second call to + * WSAWaitForMultipleEvents would be redundant -- just + * return 0 indicating timeout. + */ + if (timeout > 0) + status = WSAWaitForMultipleEvents( + (DWORD) wes->n_events, + wes->events, + FALSE, + (DWORD) timeout, + FALSE); + + if (outlen >= 1 && status >= WSA_WAIT_EVENT_0 && status < WSA_WAIT_EVENT_0 + (DWORD) wes->n_events) + { + *out = wes->esr[status - WSA_WAIT_EVENT_0]; + dmsg (D_EVENT_WAIT, "WE_WAIT leave rwflags=0x%04x arg=" ptr_format, + out->rwflags, (ptr_type)out->arg); + return 1; + } + else if (status == WSA_WAIT_TIMEOUT) + return 0; + else + return -1; + } +} + +static struct event_set * +we_init (int *maxevents, unsigned int flags) +{ + struct we_set *wes; + + dmsg (D_EVENT_WAIT, "WE_INIT maxevents=%d flags=0x%08x", *maxevents, flags); + + ALLOC_OBJ_CLEAR (wes, struct we_set); + + /* set dispatch functions */ + wes->func.free = we_free; + wes->func.reset = we_reset; + wes->func.del = we_del; + wes->func.ctl = we_ctl; + wes->func.wait = we_wait; + + if (flags & EVENT_METHOD_FAST) + wes->fast = true; + wes->n_events = 0; + + /* Figure our event capacity */ + ASSERT (*maxevents > 0); + wes->capacity = min_int (*maxevents * 2, WSA_MAXIMUM_WAIT_EVENTS); + *maxevents = min_int (*maxevents, WSA_MAXIMUM_WAIT_EVENTS); + + /* Allocate space for Win32 event handles */ + ALLOC_ARRAY_CLEAR (wes->events, HANDLE, wes->capacity); + + /* Allocate space for event_set_return objects */ + ALLOC_ARRAY_CLEAR (wes->esr, struct event_set_return, wes->capacity); + + dmsg (D_EVENT_WAIT, "WE_INIT maxevents=%d capacity=%d", + *maxevents, wes->capacity); + + return (struct event_set *) wes; +} + +#endif /* WIN32 */ + +#if EPOLL + +struct ep_set +{ + struct event_set_functions func; + bool fast; + int epfd; + int maxevents; + struct epoll_event *events; +}; + +static void +ep_free (struct event_set *es) +{ + struct ep_set *eps = (struct ep_set *) es; + close (eps->epfd); + free (eps->events); + free (eps); +} + +static void +ep_reset (struct event_set *es) +{ + const struct ep_set *eps = (struct ep_set *) es; + ASSERT (eps->fast); +} + +static void +ep_del (struct event_set *es, event_t event) +{ + struct epoll_event ev; + struct ep_set *eps = (struct ep_set *) es; + + dmsg (D_EVENT_WAIT, "EP_DEL ev=%d", (int)event); + + ASSERT (!eps->fast); + CLEAR (ev); + epoll_ctl (eps->epfd, EPOLL_CTL_DEL, event, &ev); +} + +static void +ep_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg) +{ + struct ep_set *eps = (struct ep_set *) es; + struct epoll_event ev; + + CLEAR (ev); + + ev.data.ptr = arg; + if (rwflags & EVENT_READ) + ev.events |= EPOLLIN; + if (rwflags & EVENT_WRITE) + ev.events |= EPOLLOUT; + + dmsg (D_EVENT_WAIT, "EP_CTL fd=%d rwflags=0x%04x ev=0x%08x arg=" ptr_format, + (int)event, + rwflags, + (unsigned int)ev.events, + (ptr_type)ev.data.ptr); + + if (epoll_ctl (eps->epfd, EPOLL_CTL_MOD, event, &ev) < 0) + { + if (errno == ENOENT) + { + if (epoll_ctl (eps->epfd, EPOLL_CTL_ADD, event, &ev) < 0) + msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_ADD failed"); + } + else + msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_MOD failed"); + } +} + +static int +ep_wait (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) +{ + struct ep_set *eps = (struct ep_set *) es; + int stat; + + if (outlen > eps->maxevents) + outlen = eps->maxevents; + + stat = epoll_wait (eps->epfd, eps->events, outlen, tv_to_ms_timeout (tv)); + ASSERT (stat <= outlen); + + if (stat > 0) + { + int i; + const struct epoll_event *ev = eps->events; + struct event_set_return *esr = out; + for (i = 0; i < stat; ++i) + { + esr->rwflags = 0; + if (ev->events & (EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP)) + esr->rwflags |= EVENT_READ; + if (ev->events & EPOLLOUT) + esr->rwflags |= EVENT_WRITE; + esr->arg = ev->data.ptr; + dmsg (D_EVENT_WAIT, "EP_WAIT[%d] rwflags=0x%04x ev=0x%08x arg=" ptr_format, + i, esr->rwflags, ev->events, (ptr_type)ev->data.ptr); + ++ev; + ++esr; + } + } + return stat; +} + +static struct event_set * +ep_init (int *maxevents, unsigned int flags) +{ + struct ep_set *eps; + int fd; + + dmsg (D_EVENT_WAIT, "EP_INIT maxevents=%d flags=0x%08x", *maxevents, flags); + + /* open epoll file descriptor */ + fd = epoll_create (*maxevents); + if (fd < 0) + return NULL; + + ALLOC_OBJ_CLEAR (eps, struct ep_set); + + /* set dispatch functions */ + eps->func.free = ep_free; + eps->func.reset = ep_reset; + eps->func.del = ep_del; + eps->func.ctl = ep_ctl; + eps->func.wait = ep_wait; + + /* fast method ("sort of") corresponds to epoll one-shot */ + if (flags & EVENT_METHOD_FAST) + eps->fast = true; + + /* allocate space for epoll_wait return */ + ASSERT (*maxevents > 0); + eps->maxevents = *maxevents; + ALLOC_ARRAY_CLEAR (eps->events, struct epoll_event, eps->maxevents); + + /* set epoll control fd */ + eps->epfd = fd; + + return (struct event_set *) eps; +} +#endif /* EPOLL */ + +#if POLL + +struct po_set +{ + struct event_set_functions func; + bool fast; + struct pollfd *events; + void **args; + int n_events; + int capacity; +}; + +static void +po_free (struct event_set *es) +{ + struct po_set *pos = (struct po_set *) es; + free (pos->events); + free (pos->args); + free (pos); +} + +static void +po_reset (struct event_set *es) +{ + struct po_set *pos = (struct po_set *) es; + ASSERT (pos->fast); + pos->n_events = 0; +} + +static void +po_del (struct event_set *es, event_t event) +{ + struct po_set *pos = (struct po_set *) es; + int i; + + dmsg (D_EVENT_WAIT, "PO_DEL ev=%d", (int)event); + + ASSERT (!pos->fast); + for (i = 0; i < pos->n_events; ++i) + { + if (pos->events[i].fd == event) + { + int j; + for (j = i; j < pos->n_events - 1; ++j) + { + pos->events[j] = pos->events[j+1]; + pos->args[j] = pos->args[j+1]; + } + --pos->n_events; + break; + } + } +} + +static inline void +po_set_pollfd_events (struct pollfd *pfdp, unsigned int rwflags) +{ + pfdp->events = 0; + if (rwflags & EVENT_WRITE) + pfdp->events |= POLLOUT; + if (rwflags & EVENT_READ) + pfdp->events |= (POLLIN|POLLPRI); +} + +static inline bool +po_append_event (struct po_set *pos, event_t event, unsigned int rwflags, void *arg) +{ + if (pos->n_events < pos->capacity) + { + struct pollfd *pfdp = &pos->events[pos->n_events]; + pfdp->fd = event; + pos->args[pos->n_events] = arg; + po_set_pollfd_events (pfdp, rwflags); + ++pos->n_events; + return true; + } + else + return false; +} + +static void +po_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg) +{ + struct po_set *pos = (struct po_set *) es; + + dmsg (D_EVENT_WAIT, "PO_CTL rwflags=0x%04x ev=%d arg=" ptr_format, + rwflags, (int)event, (ptr_type)arg); + + if (pos->fast) + { + if (!po_append_event (pos, event, rwflags, arg)) + goto err; + } + else + { + int i; + for (i = 0; i < pos->n_events; ++i) + { + struct pollfd *pfdp = &pos->events[i]; + if (pfdp->fd == event) + { + pos->args[i] = arg; + po_set_pollfd_events (pfdp, rwflags); + goto done; + } + } + if (!po_append_event (pos, event, rwflags, arg)) + goto err; + } + + done: + return; + + err: + msg (D_EVENT_ERRORS, "Error: poll: too many I/O wait events"); +} + +static int +po_wait (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) +{ + struct po_set *pos = (struct po_set *) es; + int stat; + + stat = poll (pos->events, pos->n_events, tv_to_ms_timeout (tv)); + + ASSERT (stat <= pos->n_events); + + if (stat > 0) + { + int i, j=0; + const struct pollfd *pfdp = pos->events; + for (i = 0; i < pos->n_events && j < outlen; ++i) + { + if (pfdp->revents & (POLLIN|POLLPRI|POLLERR|POLLHUP|POLLOUT)) + { + out->rwflags = 0; + if (pfdp->revents & (POLLIN|POLLPRI|POLLERR|POLLHUP)) + out->rwflags |= EVENT_READ; + if (pfdp->revents & POLLOUT) + out->rwflags |= EVENT_WRITE; + out->arg = pos->args[i]; + dmsg (D_EVENT_WAIT, "PO_WAIT[%d,%d] fd=%d rev=0x%08x rwflags=0x%04x arg=" ptr_format " %s", + i, j, pfdp->fd, pfdp->revents, out->rwflags, (ptr_type)out->arg, pos->fast ? "" : "[scalable]"); + ++out; + ++j; + } + else if (pfdp->revents) + { + msg (D_EVENT_ERRORS, "Error: poll: unknown revents=0x%04x", (unsigned int)pfdp->revents); + } + ++pfdp; + } + return j; + } + return stat; +} + +static struct event_set * +po_init (int *maxevents, unsigned int flags) +{ + struct po_set *pos; + + dmsg (D_EVENT_WAIT, "PO_INIT maxevents=%d flags=0x%08x", *maxevents, flags); + + ALLOC_OBJ_CLEAR (pos, struct po_set); + + /* set dispatch functions */ + pos->func.free = po_free; + pos->func.reset = po_reset; + pos->func.del = po_del; + pos->func.ctl = po_ctl; + pos->func.wait = po_wait; + + if (flags & EVENT_METHOD_FAST) + pos->fast = true; + + pos->n_events = 0; + + /* Figure our event capacity */ + ASSERT (*maxevents > 0); + pos->capacity = *maxevents; + + /* Allocate space for pollfd structures to be passed to poll() */ + ALLOC_ARRAY_CLEAR (pos->events, struct pollfd, pos->capacity); + + /* Allocate space for event_set_return objects */ + ALLOC_ARRAY_CLEAR (pos->args, void *, pos->capacity); + + return (struct event_set *) pos; +} +#endif /* POLL */ + +#if SELECT + +struct se_set +{ + struct event_set_functions func; + bool fast; + fd_set readfds; + fd_set writefds; + void **args; /* allocated to capacity size */ + int maxfd; /* largest fd seen so far, always < capacity */ + int capacity; /* fixed largest fd + 1 */ +}; + +static void +se_free (struct event_set *es) +{ + struct se_set *ses = (struct se_set *) es; + free (ses->args); + free (ses); +} + +static void +se_reset (struct event_set *es) +{ + struct se_set *ses = (struct se_set *) es; + int i; + ASSERT (ses->fast); + + dmsg (D_EVENT_WAIT, "SE_RESET"); + + FD_ZERO (&ses->readfds); + FD_ZERO (&ses->writefds); + for (i = 0; i <= ses->maxfd; ++i) + ses->args[i] = NULL; + ses->maxfd = -1; +} + +static void +se_del (struct event_set *es, event_t event) +{ + struct se_set *ses = (struct se_set *) es; + ASSERT (!ses->fast); + + dmsg (D_EVENT_WAIT, "SE_DEL ev=%d", (int)event); + + if (event >= 0 && event < ses->capacity) + { + FD_CLR (event, &ses->readfds); + FD_CLR (event, &ses->writefds); + ses->args[event] = NULL; + } + else + msg (D_EVENT_ERRORS, "Error: select/se_del: too many I/O wait events"); + return; +} + +static void +se_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg) +{ + struct se_set *ses = (struct se_set *) es; + + dmsg (D_EVENT_WAIT, "SE_CTL rwflags=0x%04x ev=%d fast=%d cap=%d maxfd=%d arg=" ptr_format, + rwflags, (int)event, (int)ses->fast, ses->capacity, ses->maxfd, (ptr_type)arg); + + if (event >= 0 && event < ses->capacity) + { + ses->maxfd = max_int (event, ses->maxfd); + ses->args[event] = arg; + if (ses->fast) + { + if (rwflags & EVENT_READ) + FD_SET (event, &ses->readfds); + if (rwflags & EVENT_WRITE) + FD_SET (event, &ses->writefds); + } + else + { + if (rwflags & EVENT_READ) + FD_SET (event, &ses->readfds); + else + FD_CLR (event, &ses->readfds); + if (rwflags & EVENT_WRITE) + FD_SET (event, &ses->writefds); + else + FD_CLR (event, &ses->writefds); + } + } + else + { + msg (D_EVENT_ERRORS, "Error: select: too many I/O wait events, fd=%d cap=%d", + (int) event, + ses->capacity); + } +} + +static int +se_wait_return (struct se_set *ses, + fd_set *read, + fd_set *write, + struct event_set_return *out, + int outlen) +{ + int i, j = 0; + for (i = 0; i <= ses->maxfd && j < outlen; ++i) + { + const bool r = FD_ISSET (i, read); + const bool w = FD_ISSET (i, write); + if (r || w) + { + out->rwflags = 0; + if (r) + out->rwflags |= EVENT_READ; + if (w) + out->rwflags |= EVENT_WRITE; + out->arg = ses->args[i]; + dmsg (D_EVENT_WAIT, "SE_WAIT[%d,%d] rwflags=0x%04x arg=" ptr_format, + i, j, out->rwflags, (ptr_type)out->arg); + ++out; + ++j; + } + } + return j; +} + +static int +se_wait_fast (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) +{ + struct se_set *ses = (struct se_set *) es; + struct timeval tv_tmp = *tv; + int stat; + + dmsg (D_EVENT_WAIT, "SE_WAIT_FAST maxfd=%d tv=%d/%d", + ses->maxfd, + (int)tv_tmp.tv_sec, + (int)tv_tmp.tv_usec); + + stat = select (ses->maxfd + 1, &ses->readfds, &ses->writefds, NULL, &tv_tmp); + + if (stat > 0) + stat = se_wait_return (ses, &ses->readfds, &ses->writefds, out, outlen); + + return stat; +} + +static int +se_wait_scalable (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) +{ + struct se_set *ses = (struct se_set *) es; + struct timeval tv_tmp = *tv; + fd_set read = ses->readfds; + fd_set write = ses->writefds; + int stat; + + dmsg (D_EVENT_WAIT, "SE_WAIT_SCALEABLE maxfd=%d tv=%d/%d", + ses->maxfd, (int)tv_tmp.tv_sec, (int)tv_tmp.tv_usec); + + stat = select (ses->maxfd + 1, &read, &write, NULL, &tv_tmp); + + if (stat > 0) + stat = se_wait_return (ses, &read, &write, out, outlen); + + return stat; +} + +static struct event_set * +se_init (int *maxevents, unsigned int flags) +{ + struct se_set *ses; + + dmsg (D_EVENT_WAIT, "SE_INIT maxevents=%d flags=0x%08x", *maxevents, flags); + + ALLOC_OBJ_CLEAR (ses, struct se_set); + + /* set dispatch functions */ + ses->func.free = se_free; + ses->func.reset = se_reset; + ses->func.del = se_del; + ses->func.ctl = se_ctl; + ses->func.wait = se_wait_scalable; + + if (flags & EVENT_METHOD_FAST) + { + ses->fast = true; + ses->func.wait = se_wait_fast; + } + + /* Select needs to be passed this value + 1 */ + ses->maxfd = -1; + + /* Set our event capacity */ + ASSERT (*maxevents > 0); + *maxevents = min_int (*maxevents, SELECT_MAX_FDS); + ses->capacity = SELECT_MAX_FDS; + + /* Allocate space for event_set_return void * args */ + ALLOC_ARRAY_CLEAR (ses->args, void *, ses->capacity); + + return (struct event_set *) ses; +} +#endif /* SELECT */ + +static struct event_set * +event_set_init_simple (int *maxevents, unsigned int flags) +{ + struct event_set *ret = NULL; +#ifdef WIN32 + ret = we_init (maxevents, flags); +#elif POLL && SELECT +#if 0 /* Define to 1 if EVENT_METHOD_US_TIMEOUT should cause select to be favored over poll */ + if (flags & EVENT_METHOD_US_TIMEOUT) + ret = se_init (maxevents, flags); +#endif +# ifdef SELECT_PREFERRED_OVER_POLL + if (!ret) + ret = se_init (maxevents, flags); + if (!ret) + ret = po_init (maxevents, flags); +# else + if (!ret) + ret = po_init (maxevents, flags); + if (!ret) + ret = se_init (maxevents, flags); +# endif +#elif POLL + ret = po_init (maxevents, flags); +#elif SELECT + ret = se_init (maxevents, flags); +#else +#error At least one of poll, select, or WSAWaitForMultipleEvents must be supported by the kernel +#endif + ASSERT (ret); + return ret; +} + +static struct event_set * +event_set_init_scalable (int *maxevents, unsigned int flags) +{ + struct event_set *ret = NULL; +#if EPOLL + ret = ep_init (maxevents, flags); + if (!ret) + { + msg (M_WARN, "Note: sys_epoll API is unavailable, falling back to poll/select API"); + ret = event_set_init_simple (maxevents, flags); + } +#else + ret = event_set_init_simple (maxevents, flags); +#endif + ASSERT (ret); + return ret; +} + +struct event_set * +event_set_init (int *maxevents, unsigned int flags) +{ + if (flags & EVENT_METHOD_FAST) + return event_set_init_simple (maxevents, flags); + else + return event_set_init_scalable (maxevents, flags); +} |