libev/ev_epoll.c

267 lines
9.0 KiB
C
Raw Normal View History

2007-10-31 14:44:14 +00:00
/*
2007-11-01 13:11:11 +00:00
* libev epoll fd activity backend
*
2011-01-05 04:21:20 +00:00
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
2007-10-31 14:44:14 +00:00
* All rights reserved.
*
2007-12-25 07:05:45 +00:00
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
2007-10-31 14:44:14 +00:00
*
2007-12-25 07:05:45 +00:00
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
2007-10-31 14:44:14 +00:00
*/
2007-12-13 16:52:50 +00:00
/*
* general notes about epoll:
*
* a) epoll silently removes fds from the fd set. as nothing tells us
* that an fd has been removed otherwise, we have to continually
* "rearm" fds that we suspect *might* have changed (same
* problem with kqueue, but much less costly there).
* b) the fact that ADD != MOD creates a lot of extra syscalls due to a)
* and seems not to have any advantage.
* c) the inability to handle fork or file descriptors (think dup)
* limits the applicability over poll, so this is not a generic
* poll replacement.
2010-03-23 23:45:12 +00:00
* d) epoll doesn't work the same as select with many file descriptors
* (such as files). while not critical, no other advanced interface
* seems to share this (rather non-unixy) limitation.
* e) epoll claims to be embeddable, but in practise you never get
2011-01-05 04:21:20 +00:00
* a ready event for the epoll fd (broken: <=2.6.26, working: >=2.6.32).
* f) epoll_ctl returning EPERM means the fd is always ready.
2007-12-13 16:52:50 +00:00
*
* lots of "weird code" and complication handling in this file is due
* to these design problems with epoll, as we try very hard to avoid
2008-10-27 13:39:18 +00:00
* epoll_ctl syscalls for common usage patterns and handle the breakage
* ensuing from receiving events for closed and otherwise long gone
* file descriptors.
2007-12-13 16:52:50 +00:00
*/
#include <sys/epoll.h>
2011-01-05 04:21:20 +00:00
#define EV_EMASK_EPERM 0x80
static void
2007-11-03 21:58:51 +00:00
epoll_modify (EV_P_ int fd, int oev, int nev)
{
2007-10-30 23:54:38 +00:00
struct epoll_event ev;
2008-10-23 04:56:49 +00:00
unsigned char oldmask;
2007-12-13 16:52:50 +00:00
/*
* we handle EPOLL_CTL_DEL by ignoring it here
* on the assumption that the fd is gone anyways
* if that is wrong, we have to handle the spurious
* event in epoll_poll.
2008-11-07 15:07:50 +00:00
* if the fd is added again, we try to ADD it, and, if that
2008-10-23 04:56:49 +00:00
* fails, we assume it still has the same eventmask.
2007-12-13 16:52:50 +00:00
*/
if (!nev)
return;
2008-10-23 04:56:49 +00:00
oldmask = anfds [fd].emask;
anfds [fd].emask = nev;
2008-10-29 10:24:23 +00:00
/* store the generation counter in the upper 32 bits, the fd in the lower 32 bits */
ev.data.u64 = (uint64_t)(uint32_t)fd
| ((uint64_t)(uint32_t)++anfds [fd].egen << 32);
2007-12-13 16:52:50 +00:00
ev.events = (nev & EV_READ ? EPOLLIN : 0)
| (nev & EV_WRITE ? EPOLLOUT : 0);
2010-04-21 13:13:17 +00:00
if (expect_true (!epoll_ctl (backend_fd, oev && oldmask != nev ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev)))
2007-12-13 16:52:50 +00:00
return;
if (expect_true (errno == ENOENT))
{
2008-10-23 04:56:49 +00:00
/* if ENOENT then the fd went away, so try to do the right thing */
2007-12-13 16:52:50 +00:00
if (!nev)
goto dec_egen;
2007-12-13 16:52:50 +00:00
if (!epoll_ctl (backend_fd, EPOLL_CTL_ADD, fd, &ev))
return;
}
else if (expect_true (errno == EEXIST))
{
2008-10-23 04:56:49 +00:00
/* EEXIST means we ignored a previous DEL, but the fd is still active */
/* if the kernel mask is the same as the new mask, we assume it hasn't changed */
if (oldmask == nev)
goto dec_egen;
if (!epoll_ctl (backend_fd, EPOLL_CTL_MOD, fd, &ev))
2007-12-13 16:52:50 +00:00
return;
}
2011-01-05 04:21:20 +00:00
else if (expect_true (errno == EPERM))
{
2011-01-08 10:25:16 +00:00
/* EPERM means the fd is always ready, but epoll is too snobbish */
/* to handle it, unlike select or poll. */
2011-01-05 04:21:20 +00:00
anfds [fd].emask = EV_EMASK_EPERM;
/* add fd to epoll_eperms, if not already inside */
if (!(oldmask & EV_EMASK_EPERM))
{
array_needsize (int, epoll_eperms, epoll_epermmax, epoll_epermcnt + 1, EMPTY2);
epoll_eperms [epoll_epermcnt++] = fd;
}
return;
}
2007-12-13 16:52:50 +00:00
fd_kill (EV_A_ fd);
dec_egen:
/* we didn't successfully call epoll_ctl, so decrement the generation counter again */
--anfds [fd].egen;
}
2007-10-31 20:46:44 +00:00
static void
2007-11-03 21:58:51 +00:00
epoll_poll (EV_P_ ev_tstamp timeout)
{
int i;
2009-07-09 09:11:20 +00:00
int eventcnt;
2011-01-10 14:05:23 +00:00
2009-07-25 10:14:34 +00:00
/* epoll wait times cannot be larger than (LONG_MAX - 999UL) / HZ msecs, which is below */
/* the default libev max wait time, however. */
2009-07-10 19:10:19 +00:00
EV_RELEASE_CB;
2011-01-10 14:05:23 +00:00
eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax,
2011-01-17 12:11:11 +00:00
epoll_epermcnt ? 0 : ev_timeout_to_ms (timeout));
2009-07-10 19:10:19 +00:00
EV_ACQUIRE_CB;
2007-12-09 02:12:43 +00:00
if (expect_false (eventcnt < 0))
2007-11-06 00:10:04 +00:00
{
if (errno != EINTR)
2008-10-29 06:32:48 +00:00
ev_syserr ("(libev) epoll_wait");
2007-11-06 00:10:04 +00:00
return;
}
for (i = 0; i < eventcnt; ++i)
2007-12-13 16:52:50 +00:00
{
struct epoll_event *ev = epoll_events + i;
2008-10-27 13:39:18 +00:00
int fd = (uint32_t)ev->data.u64; /* mask out the lower 32 bits */
int want = anfds [fd].events;
2007-12-13 16:52:50 +00:00
int got = (ev->events & (EPOLLOUT | EPOLLERR | EPOLLHUP) ? EV_WRITE : 0)
| (ev->events & (EPOLLIN | EPOLLERR | EPOLLHUP) ? EV_READ : 0);
2008-10-27 13:39:18 +00:00
/* check for spurious notification */
2010-10-22 10:50:24 +00:00
/* we assume that fd is always in range, as we never shrink the anfds array */
2008-10-29 07:09:37 +00:00
if (expect_false ((uint32_t)anfds [fd].egen != (uint32_t)(ev->data.u64 >> 32)))
2008-10-27 13:39:18 +00:00
{
/* recreate kernel state */
postfork = 1;
continue;
}
2007-12-13 16:52:50 +00:00
if (expect_false (got & ~want))
{
2008-10-23 04:56:49 +00:00
anfds [fd].emask = want;
2007-12-13 16:52:50 +00:00
/* we received an event but are not interested in it, try mod or del */
2008-10-23 04:56:49 +00:00
/* I don't think we ever need MOD, but let's handle it anyways */
2007-12-13 16:52:50 +00:00
ev->events = (want & EV_READ ? EPOLLIN : 0)
| (want & EV_WRITE ? EPOLLOUT : 0);
2009-11-24 06:37:23 +00:00
/* pre-2.6.9 kernels require a non-null pointer with EPOLL_CTL_DEL, */
/* which is fortunately easy to do for us. */
2008-10-27 13:39:18 +00:00
if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev))
{
2010-07-31 23:00:11 +00:00
postfork = 1; /* an error occurred, recreate kernel state */
2008-10-27 13:39:18 +00:00
continue;
}
2007-12-13 16:52:50 +00:00
}
fd_event (EV_A_ fd, got);
}
/* if the receive array was full, increase its size */
if (expect_false (eventcnt == epoll_eventmax))
{
2007-11-06 00:10:04 +00:00
ev_free (epoll_events);
2007-12-05 13:54:36 +00:00
epoll_eventmax = array_nextsize (sizeof (struct epoll_event), epoll_eventmax, epoll_eventmax + 1);
2007-11-06 16:51:20 +00:00
epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax);
}
2011-01-05 04:21:20 +00:00
2011-01-05 04:25:12 +00:00
/* now synthesize events for all fds where epoll fails, while select works... */
2011-01-05 04:21:20 +00:00
for (i = epoll_epermcnt; i--; )
{
int fd = epoll_eperms [i];
unsigned char events = anfds [fd].events & (EV_READ | EV_WRITE);
if (anfds [fd].emask & EV_EMASK_EPERM && events)
fd_event (EV_A_ fd, events);
else
epoll_eperms [i] = epoll_eperms [--epoll_epermcnt];
}
}
int inline_size
2007-11-03 21:58:51 +00:00
epoll_init (EV_P_ int flags)
{
2009-07-19 04:11:27 +00:00
#ifdef EPOLL_CLOEXEC
backend_fd = epoll_create1 (EPOLL_CLOEXEC);
if (backend_fd <= 0)
#endif
backend_fd = epoll_create (256);
2007-11-23 19:13:33 +00:00
if (backend_fd < 0)
2007-11-03 21:58:51 +00:00
return 0;
2007-10-31 00:24:16 +00:00
2007-11-23 19:13:33 +00:00
fcntl (backend_fd, F_SETFD, FD_CLOEXEC);
2007-12-09 03:51:18 +00:00
backend_fudge = 0.; /* kernel sources seem to indicate this to be zero */
backend_modify = epoll_modify;
backend_poll = epoll_poll;
2008-10-27 13:39:18 +00:00
epoll_eventmax = 64; /* initial number of events receivable per poll */
2007-11-06 16:51:20 +00:00
epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax);
2007-11-03 21:58:51 +00:00
return EVBACKEND_EPOLL;
}
2007-10-31 20:46:44 +00:00
void inline_size
epoll_destroy (EV_P)
{
2007-11-06 00:10:04 +00:00
ev_free (epoll_events);
2011-01-05 04:21:20 +00:00
array_free (epoll_eperm, EMPTY);
}
void inline_size
epoll_fork (EV_P)
{
2007-11-23 19:13:33 +00:00
close (backend_fd);
2007-11-06 00:52:32 +00:00
2007-11-23 19:13:33 +00:00
while ((backend_fd = epoll_create (256)) < 0)
2008-10-29 06:32:48 +00:00
ev_syserr ("(libev) epoll_create");
2007-11-06 00:52:32 +00:00
2007-11-23 19:13:33 +00:00
fcntl (backend_fd, F_SETFD, FD_CLOEXEC);
2007-11-04 18:29:44 +00:00
fd_rearm_all (EV_A);
}