Skip to content

Commit

Permalink
Add wepoll support to light up the epoll backend on Windows
Browse files Browse the repository at this point in the history
libevent is lacking a scalable backend on Windows. Let's leverage the wepoll
library until Windows comes up with an epoll/kqueue compete user mode API.

- All regress tests pass for standard wepoll
- These 2 tests fail intermittently for changelist wepoll, so disabling
  changelist wepoll for now
     http/cancel_inactive_server
     http/stream_in
- verify target on Windows runs tests for both wepoll and win32 backends
- wepoll backend preferred over win32 backend
- wepoll version 1.5.6

v2: cleaner backend abstraction. Disallow wepoll on MinGW/Cygwin.
v3: Add wepoll.h to dist
v4: Make sure wepoll source files are excluded from cygwin/mingw builds
v5: Keep win32 as default backend on windows.
v6: Include wepoll in mingw builds. Verified that regress tests pass w/ WEPOLL backend.
v7: Enable wepoll on mingw when building with cmake
v8: Add wepoll testrunner for autotools test target
  • Loading branch information
nigriMSFT authored and azat committed May 8, 2020
1 parent 06a1192 commit 83ef321
Show file tree
Hide file tree
Showing 11 changed files with 2,461 additions and 20 deletions.
18 changes: 15 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,9 @@ set(EVENT_INCLUDES ${EVENT_INCLUDES} stdio.h)
CHECK_SYMBOLS_EXIST("${SYMBOLS_TO_CHECK}" "${EVENT_INCLUDES}" "EVENT")
unset(SYMBOLS_TO_CHECK)
set(EVENT__HAVE_EPOLL ${EVENT__HAVE_EPOLL_CREATE})
if(WIN32 AND NOT CYGWIN)
set(EVENT__HAVE_WEPOLL 1)
endif()

# Get the gethostbyname_r prototype.
if(EVENT__HAVE_GETHOSTBYNAME_R)
Expand Down Expand Up @@ -839,6 +842,12 @@ if(EVENT__HAVE_EPOLL)
list(APPEND SRC_CORE epoll.c)
endif()

if(EVENT__HAVE_WEPOLL)
list(APPEND SRC_CORE
epoll.c
wepoll.c)
endif()

if(EVENT__HAVE_EVENT_PORTS)
list(APPEND SRC_CORE evport.c)
endif()
Expand Down Expand Up @@ -1240,6 +1249,10 @@ if (NOT EVENT__DISABLE_TESTS)
list(APPEND BACKENDS DEVPOLL)
endif()

if (EVENT__HAVE_WEPOLL)
list(APPEND BACKENDS WEPOLL)
endif()

if (WIN32)
list(APPEND BACKENDS WIN32)
endif()
Expand Down Expand Up @@ -1382,13 +1395,12 @@ if (NOT EVENT__DISABLE_TESTS)
# If they are set in the shell the tests are running using simply "ctest" or "make test" will fail)
if (WIN32)
# Windows doesn't have "unset". But you can use "set VAR=" instead.
# We need to guard against the possibility taht EVENT_NOWIN32 is set, and all test failing
# since no event backend being available.
file(TO_NATIVE_PATH ${CMAKE_CTEST_COMMAND} WINDOWS_CTEST_COMMAND)

file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/tmp/verify_tests.bat
"
set EVENT_NOWIN32=
set EVENT_NOWEPOLL=
\"${WINDOWS_CTEST_COMMAND}\"
")

Expand All @@ -1410,7 +1422,7 @@ if (NOT EVENT__DISABLE_TESTS)
add_custom_target(verify COMMAND "${VERIFY_PATH}"
DEPENDS event ${ALL_TESTPROGS})
else()
# On some platforms doing exec(unset) as CMake does won't work, so make sure
# On some platforms doing exec(unset) as CMake doesn't work, so make sure
# we run the unset command in a shell instead.
# First we write the script contents.
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/tmp/verify_tests.sh
Expand Down
31 changes: 31 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,34 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

==============================

The wepoll module is available under the following, sometimes called the
"FreeBSD" license:

Copyright 2012-2020, Bert Belder <[email protected]>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

* 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, 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 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7 changes: 6 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ endif
if EVPORT_BACKEND
SYS_SRC += evport.c
endif
if WEPOLL_BACKEND
SYS_SRC += epoll.c
SYS_SRC += wepoll.c
endif
if SIGNAL_SUPPORT
SYS_SRC += signal.c
endif
Expand Down Expand Up @@ -323,7 +327,8 @@ noinst_HEADERS += \
strlcpy-internal.h \
time-internal.h \
util-internal.h \
openssl-compat.h
openssl-compat.h \
wepoll.h

EVENT1_HDRS = \
include/evdns.h \
Expand Down
8 changes: 7 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -660,10 +660,16 @@ if test "x$haveeventports" = "xyes" ; then
fi
AM_CONDITIONAL(EVPORT_BACKEND, [test "x$haveeventports" = "xyes"])

havewepoll=no
if test "x$bwin32" = "xtrue"; then
needsignal=yes
if test "x$cygwin" = "xfalse"; then
havewepoll=yes
AC_DEFINE(HAVE_WEPOLL, 1,
[Define if your system supports the wepoll module])
fi
fi

AM_CONDITIONAL(WEPOLL_BACKEND, [test "x$havewepoll" = "xyes"])
AM_CONDITIONAL(SIGNAL_SUPPORT, [test "x$needsignal" = "xyes"])

AC_TYPE_PID_T
Expand Down
71 changes: 57 additions & 14 deletions epoll.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,18 @@
#include "event2/event-config.h"
#include "evconfig-private.h"

#ifdef EVENT__HAVE_EPOLL
#if defined EVENT__HAVE_EPOLL || defined EVENT__HAVE_WEPOLL

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>

#ifdef EVENT__HAVE_WEPOLL
#include "wepoll.h"
#define EPOLLET 0
#else
#include <sys/types.h>
#include <sys/resource.h>
#ifdef EVENT__HAVE_SYS_TIME_H
Expand All @@ -38,10 +47,6 @@
#include <sys/queue.h>
#include <sys/epoll.h>
#include <signal.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#ifdef EVENT__HAVE_FCNTL_H
Expand All @@ -50,6 +55,7 @@
#ifdef EVENT__HAVE_SYS_TIMERFD_H
#include <sys/timerfd.h>
#endif
#endif

#include "event-internal.h"
#include "evsignal-internal.h"
Expand Down Expand Up @@ -83,10 +89,20 @@
#define USING_TIMERFD
#endif

#ifdef EVENT__HAVE_WEPOLL
typedef HANDLE epoll_handle;
#define INVALID_EPOLL_HANDLE NULL
static void close_epoll_handle(HANDLE h) { epoll_close(h); }
#else
typedef int epoll_handle;
#define INVALID_EPOLL_HANDLE -1
static void close_epoll_handle(int h) { close(h); }
#endif

struct epollop {
struct epoll_event *events;
int nevents;
int epfd;
epoll_handle epfd;
#ifdef USING_TIMERFD
int timerfd;
#endif
Expand Down Expand Up @@ -114,6 +130,19 @@ static int epoll_nochangelist_add(struct event_base *base, evutil_socket_t fd,
static int epoll_nochangelist_del(struct event_base *base, evutil_socket_t fd,
short old, short events, void *p);

#ifdef EVENT__HAVE_WEPOLL
const struct eventop wepollops = {
"wepoll",
epoll_init,
epoll_nochangelist_add,
epoll_nochangelist_del,
epoll_dispatch,
epoll_dealloc,
1, /* need reinit */
EV_FEATURE_O1|EV_FEATURE_EARLY_CLOSE,
0
};
#else
const struct eventop epollops = {
"epoll",
epoll_init,
Expand All @@ -125,6 +154,8 @@ const struct eventop epollops = {
EV_FEATURE_ET|EV_FEATURE_O1|EV_FEATURE_EARLY_CLOSE,
0
};
#endif


#define INITIAL_NEVENT 32
#define MAX_NEVENT 4096
Expand All @@ -140,26 +171,28 @@ const struct eventop epollops = {
static void *
epoll_init(struct event_base *base)
{
int epfd = -1;
epoll_handle epfd = INVALID_EPOLL_HANDLE;
struct epollop *epollop;

#ifdef EVENT__HAVE_EPOLL_CREATE1
/* First, try the shiny new epoll_create1 interface, if we have it. */
epfd = epoll_create1(EPOLL_CLOEXEC);
#endif
if (epfd == -1) {
if (epfd == INVALID_EPOLL_HANDLE) {
/* Initialize the kernel queue using the old interface. (The
size field is ignored since 2.6.8.) */
if ((epfd = epoll_create(32000)) == -1) {
if ((epfd = epoll_create(32000)) == INVALID_EPOLL_HANDLE) {
if (errno != ENOSYS)
event_warn("epoll_create");
return (NULL);
}
#ifndef EVENT__HAVE_WEPOLL
evutil_make_socket_closeonexec(epfd);
#endif
}

if (!(epollop = mm_calloc(1, sizeof(struct epollop)))) {
close(epfd);
close_epoll_handle(epfd);
return (NULL);
}

Expand All @@ -169,17 +202,19 @@ epoll_init(struct event_base *base)
epollop->events = mm_calloc(INITIAL_NEVENT, sizeof(struct epoll_event));
if (epollop->events == NULL) {
mm_free(epollop);
close(epfd);
close_epoll_handle(epfd);
return (NULL);
}
epollop->nevents = INITIAL_NEVENT;

#ifndef EVENT__HAVE_WEPOLL
if ((base->flags & EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST) != 0 ||
((base->flags & EVENT_BASE_FLAG_IGNORE_ENV) == 0 &&
evutil_getenv_("EVENT_EPOLL_USE_CHANGELIST") != NULL)) {

base->evsel = &epollops_changelist;
}
#endif

#ifdef USING_TIMERFD
/*
Expand Down Expand Up @@ -286,7 +321,11 @@ epoll_apply_one_change(struct event_base *base,
events |= EPOLLET;

memset(&epev, 0, sizeof(epev));
#ifdef EVENT__HAVE_WEPOLL
epev.data.sock = ch->fd;
#else
epev.data.fd = ch->fd;
#endif
epev.events = events;
if (epoll_ctl(epollop->epfd, op, ch->fd, &epev) == 0) {
event_debug((PRINT_CHANGES(op, epev.events, ch, "okay")));
Expand Down Expand Up @@ -503,7 +542,11 @@ epoll_dispatch(struct event_base *base, struct timeval *tv)
if (!ev)
continue;

#ifdef EVENT__HAVE_WEPOLL
evmap_io_active_(base, events[i].data.sock, ev);
#else
evmap_io_active_(base, events[i].data.fd, ev | EV_ET);
#endif
}

if (res == epollop->nevents && epollop->nevents < MAX_NEVENT) {
Expand Down Expand Up @@ -532,8 +575,8 @@ epoll_dealloc(struct event_base *base)
evsig_dealloc_(base);
if (epollop->events)
mm_free(epollop->events);
if (epollop->epfd >= 0)
close(epollop->epfd);
if (epollop->epfd != INVALID_EPOLL_HANDLE)
close_epoll_handle(epollop->epfd);
#ifdef USING_TIMERFD
if (epollop->timerfd >= 0)
close(epollop->timerfd);
Expand All @@ -543,4 +586,4 @@ epoll_dealloc(struct event_base *base)
mm_free(epollop);
}

#endif /* EVENT__HAVE_EPOLL */
#endif /* defined EVENT__HAVE_EPOLL || defined EVENT__HAVE_WEPOLL */
3 changes: 3 additions & 0 deletions event-config.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@
/* Define to 1 if you have the `epoll_ctl' function. */
#cmakedefine EVENT__HAVE_EPOLL_CTL 1

/* Define if your system supports the wepoll module */
#cmakedefine EVENT__HAVE_WEPOLL 1

/* Define to 1 if you have the `eventfd' function. */
#cmakedefine EVENT__HAVE_EVENTFD 1

Expand Down
6 changes: 6 additions & 0 deletions event.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ extern const struct eventop kqops;
#ifdef EVENT__HAVE_DEVPOLL
extern const struct eventop devpollops;
#endif
#ifdef EVENT__HAVE_WEPOLL
extern const struct eventop wepollops;
#endif
#ifdef _WIN32
extern const struct eventop win32ops;
#endif
Expand All @@ -122,6 +125,9 @@ static const struct eventop *eventops[] = {
#endif
#ifdef _WIN32
&win32ops,
#endif
#ifdef EVENT__HAVE_WEPOLL
&wepollops,
#endif
NULL
};
Expand Down
3 changes: 3 additions & 0 deletions test/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ TESTS = \
test_runner_devpoll \
test_runner_poll \
test_runner_win32 \
test_runner_wepoll \
test_runner_timerfd \
test_runner_changelist \
test_runner_timerfd_changelist
Expand All @@ -71,6 +72,8 @@ test_runner_poll: $(top_srcdir)/test/test.sh
$(top_srcdir)/test/test.sh -b POLL
test_runner_win32: $(top_srcdir)/test/test.sh
$(top_srcdir)/test/test.sh -b WIN32
test_runner_wepoll: $(top_srcdir)/test/test.sh
$(top_srcdir)/test/test.sh -b WEPOLL
test_runner_timerfd: $(top_srcdir)/test/test.sh
$(top_srcdir)/test/test.sh -b "" -t
test_runner_changelist: $(top_srcdir)/test/test.sh
Expand Down
2 changes: 1 addition & 1 deletion test/test.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/sh

BACKENDS="EVPORT KQUEUE EPOLL DEVPOLL POLL SELECT WIN32"
BACKENDS="EVPORT KQUEUE EPOLL DEVPOLL POLL SELECT WIN32 WEPOLL"
TESTS="test-eof test-closed test-weof test-time test-changelist test-fdleak"
FAILED=no
TEST_OUTPUT_FILE=${TEST_OUTPUT_FILE:-/dev/null}
Expand Down
Loading

0 comments on commit 83ef321

Please sign in to comment.