From 862d9f477c93946c08d69e4bf0c602a722957b3c Mon Sep 17 00:00:00 2001 From: radkesvat <134321679+radkesvat@users.noreply.github.com> Date: Tue, 20 Oct 2026 00:10:54 +0000 Subject: [PATCH] Fixed Windows wepoll initialization failure detection --- event/epoll.c | 4 +++ unittest/CMakeLists.txt | 9 +++++ unittest/epoll_init_test.c | 70 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 unittest/epoll_init_test.c diff --git a/event/epoll.c b/event/epoll.c index b7b28214b..010cb57cc 100644 --- a/event/epoll.c +++ b/event/epoll.c @@ -28,7 +28,11 @@ typedef struct epoll_ctx_s { int iowatcher_init(hloop_t* loop) { if (loop->iowatcher) return 0; epoll_handle_t epfd = epoll_create(EVENTS_INIT_SIZE); +#ifdef OS_WIN + if (epfd == NULL) { +#else if (epfd < 0) { +#endif hloge("epoll_create failed: %d", socket_errno()); return -1; } diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index d3a67a4ee..6e17408b3 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -163,6 +163,14 @@ set(HTTP_JS_UNITTEST_TARGETS ${HTTP_JS_UNITTEST_TARGETS} http_js_mqtt_test) endif() endif() +# ------event: Windows epoll initialization------ +if(WIN32 AND WITH_WEPOLL) +add_executable(epoll_init_test epoll_init_test.c ../event/wepoll/wepoll.c) +target_include_directories(epoll_init_test PRIVATE .. ../base ../ssl ../event) +target_link_libraries(epoll_init_test ${HV_LIBRARIES}) +set(EVENT_UNITTEST_TARGETS epoll_init_test) +endif() + # ------event: async dns------ add_executable(hdns_test hdns_test.c) target_include_directories(hdns_test PRIVATE .. ../base ../ssl ../event) @@ -231,6 +239,7 @@ add_custom_target(unittest DEPENDS ${HTTP_UNITTEST_TARGETS} ${HTTP_LUA_UNITTEST_TARGETS} ${HTTP_JS_UNITTEST_TARGETS} + ${EVENT_UNITTEST_TARGETS} hdns_test hdns_benchmark ${REDIS_UNITTEST_TARGETS} diff --git a/unittest/epoll_init_test.c b/unittest/epoll_init_test.c new file mode 100644 index 000000000..d8419884e --- /dev/null +++ b/unittest/epoll_init_test.c @@ -0,0 +1,70 @@ +/* Exercise failed Windows backend initialization and retry. Only the first + * creation fails artificially; successful ports and cleanup use real wepoll. */ +#include "hevent.h" +#include "wepoll/wepoll.h" + +static unsigned int create_calls; +static int fail_next_create; + +static HANDLE test_epoll_create(int size) { + ++create_calls; + if (fail_next_create) { + fail_next_create = 0; + errno = ENOMEM; + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + return epoll_create(size); +} + +/* Keep the backend under test separate from the linked library's watcher. */ +#define epoll_create test_epoll_create +#define iowatcher_init test_iowatcher_init +#define iowatcher_cleanup test_iowatcher_cleanup +#define iowatcher_add_event test_iowatcher_add_event +#define iowatcher_del_event test_iowatcher_del_event +#define iowatcher_poll_events test_iowatcher_poll_events +#include "../event/epoll.c" +#undef epoll_create +#undef iowatcher_init +#undef iowatcher_cleanup +#undef iowatcher_add_event +#undef iowatcher_del_event +#undef iowatcher_poll_events + +/* Assertions must remain active in optimized builds. */ +static void require(int condition, const char *message) { + if (!condition) { + fprintf(stderr, "epoll_init_test: %s (errno=%d, winerror=%lu)\n", message, errno, (unsigned long)GetLastError()); + exit(EXIT_FAILURE); + } +} + +int main(void) { + hloop_t loop; + HANDLE port; + DWORD flags; + memset(&loop, 0, sizeof(loop)); + + fail_next_create = 1; + require(test_iowatcher_init(&loop) == -1, "failed epoll_create was reported as successful initialization"); + require(loop.iowatcher == NULL, "failed initialization published an invalid context"); + require(create_calls == 1, "creation failure did not reach the backend"); + + require(test_iowatcher_init(&loop) == 0, "initialization did not recover after creation failure"); + require(create_calls == 2 && loop.iowatcher != NULL, "retry did not create a new port"); + port = ((epoll_ctx_t *)loop.iowatcher)->epfd; + require(port != NULL && GetHandleInformation(port, &flags), "retry did not create a valid native port"); + require(test_iowatcher_init(&loop) == 0 && create_calls == 2, "initialization was not idempotent"); + require(test_iowatcher_cleanup(&loop) == 0 && loop.iowatcher == NULL, "cleanup retained the port context"); + require(!GetHandleInformation(port, &flags) && GetLastError() == ERROR_INVALID_HANDLE, "cleanup did not close the native port"); + require(test_iowatcher_cleanup(&loop) == 0, "repeated cleanup failed"); + + require(test_iowatcher_init(&loop) == 0 && create_calls == 3, "ordinary creation after cleanup failed"); + port = ((epoll_ctx_t *)loop.iowatcher)->epfd; + require(port != NULL && GetHandleInformation(port, &flags), "ordinary creation did not produce a valid native port"); + require(test_iowatcher_cleanup(&loop) == 0 && loop.iowatcher == NULL, "ordinary port cleanup failed"); + require(!GetHandleInformation(port, &flags) && GetLastError() == ERROR_INVALID_HANDLE, "ordinary cleanup did not close the native port"); + puts("epoll_init_test passed"); + return 0; +}