From ca703bf507f30f66a4a6da4516a686edc1816f1d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:54:04 +0000 Subject: [PATCH 1/9] Initial plan From 5cada929f6acf01bdd6275e4df16f00dc95c4bf3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:05:45 +0000 Subject: [PATCH 2/9] Add io_uring event loop backend support using liburing - Create event/io_uring.c: io_uring backend using IORING_OP_POLL_ADD - Modify event/iowatcher.h: Add EVENT_IO_URING to backend selection - Modify event/hloop.c and hloop.h: Add io_uring engine name - Modify CMakeLists.txt: Add WITH_IO_URING option and liburing linking - Modify hconfig.h.in: Add WITH_IO_URING cmakedefine - Modify configure: Add --with-io_uring option - Modify config.ini: Add WITH_IO_URING=no default - Modify Makefile.in: Add -luring linking when enabled - Update event/README.md and docs/PLAN.md Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- .gitignore | 1 + CMakeLists.txt | 5 ++ Makefile.in | 4 + config.ini | 3 + configure | 4 + docs/PLAN.md | 3 +- event/README.md | 1 + event/hloop.c | 2 + event/hloop.h | 2 + event/io_uring.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++ event/iowatcher.h | 7 +- hconfig.h.in | 1 + 12 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 event/io_uring.c diff --git a/.gitignore b/.gitignore index fe0d5f4aa..5822f761f 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ dist test *_test build +build-* config.mk hconfig.h html/uploads diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e58fad02..4fa06a83d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ option(WITH_GNUTLS "with gnutls library" OFF) option(WITH_MBEDTLS "with mbedtls library" OFF) option(WITH_KCP "compile event/kcp" OFF) +option(WITH_IO_URING "with io_uring" OFF) if(WIN32 OR MINGW) option(WITH_WEPOLL "compile event/wepoll -> use iocp" ON) @@ -165,6 +166,10 @@ if(WITH_MBEDTLS) set(LIBS ${LIBS} mbedtls mbedx509 mbedcrypto) endif() +if(WITH_IO_URING) + set(LIBS ${LIBS} uring) +endif() + if(WIN32 OR MINGW) add_definitions(-DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_WARNINGS -D_WIN32_WINNT=0x0600) set(LIBS ${LIBS} secur32 crypt32 winmm iphlpapi ws2_32) diff --git a/Makefile.in b/Makefile.in index cceb29d8f..dba67e18b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -193,6 +193,10 @@ endif endif endif +ifeq ($(WITH_IO_URING), yes) + LDFLAGS += -luring +endif + LDFLAGS += $(addprefix -L, $(LIBDIRS)) LDFLAGS += $(addprefix -l, $(LIBS)) diff --git a/config.ini b/config.ini index e3145a666..f9a5a43b4 100644 --- a/config.ini +++ b/config.ini @@ -38,3 +38,6 @@ WITH_MBEDTLS=no # rudp WITH_KCP=no + +# event +WITH_IO_URING=no diff --git a/configure b/configure index 535599d0e..396762389 100755 --- a/configure +++ b/configure @@ -45,6 +45,9 @@ dependencies: rudp: --with-kcp compile with kcp? (DEFAULT: $WITH_KCP) +event: + --with-io_uring compile with io_uring? (DEFAULT: $WITH_IO_URING) + END } @@ -298,6 +301,7 @@ option=WITH_MBEDTLS && check_option option=ENABLE_UDS && check_option option=USE_MULTIMAP && check_option option=WITH_KCP && check_option +option=WITH_IO_URING && check_option # end confile cat << END >> $confile diff --git a/docs/PLAN.md b/docs/PLAN.md index b389a39a1..69dac01b9 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -1,7 +1,7 @@ ## Done - base: cross platfrom infrastructure -- event: select/poll/epoll/wepoll/kqueue/port +- event: select/poll/epoll/wepoll/kqueue/port/io_uring - ssl: openssl/gnutls/mbedtls/wintls/appletls - rudp: KCP - evpp: c++ EventLoop interface similar to muduo and evpp @@ -22,7 +22,6 @@ - hrpc = libhv + protobuf - rudp: FEC, ARQ, UDT, QUIC - kcptun -- have a taste of io_uring - coroutine - cppsocket.io - IM-libhv diff --git a/event/README.md b/event/README.md index d92834284..308aabeb3 100644 --- a/event/README.md +++ b/event/README.md @@ -11,6 +11,7 @@ ├── select.c EVENT_SELECT实现 ├── poll.c EVENT_POLL实现 ├── epoll.c EVENT_EPOLL实现 (for OS_LINUX) +├── io_uring.c EVENT_IO_URING实现 (for OS_LINUX, with liburing) ├── iocp.c EVENT_IOCP实现 (for OS_WIN) ├── kqueue.c EVENT_KQUEUE实现(for OS_BSD/OS_MAC) ├── evport.c EVENT_PORT实现 (for OS_SOLARIS) diff --git a/event/hloop.c b/event/hloop.c index d0e042c3e..5999daf90 100644 --- a/event/hloop.c +++ b/event/hloop.c @@ -771,6 +771,8 @@ const char* hio_engine() { return "iocp"; #elif defined(EVENT_PORT) return "evport"; +#elif defined(EVENT_IO_URING) + return "io_uring"; #else return "noevent"; #endif diff --git a/event/hloop.h b/event/hloop.h index f070fea26..3e268fc3b 100644 --- a/event/hloop.h +++ b/event/hloop.h @@ -233,6 +233,8 @@ const char* hio_engine() { return "iocp"; #elif defined(EVENT_PORT) return "evport"; +#elif defined(EVENT_IO_URING) + return "io_uring"; #else return "noevent"; #endif diff --git a/event/io_uring.c b/event/io_uring.c new file mode 100644 index 000000000..3fd2ef5b8 --- /dev/null +++ b/event/io_uring.c @@ -0,0 +1,208 @@ +#include "iowatcher.h" + +#ifdef EVENT_IO_URING +#include "hplatform.h" +#include "hdef.h" +#include "hevent.h" + +#include +#include + +#define IO_URING_ENTRIES 1024 +#define IO_URING_CANCEL_TAG UINT64_MAX + +typedef struct io_uring_ctx_s { + struct io_uring ring; + int nfds; +} io_uring_ctx_t; + +int iowatcher_init(hloop_t* loop) { + if (loop->iowatcher) return 0; + io_uring_ctx_t* ctx; + HV_ALLOC_SIZEOF(ctx); + int ret = io_uring_queue_init(IO_URING_ENTRIES, &ctx->ring, 0); + if (ret < 0) { + HV_FREE(ctx); + return ret; + } + ctx->nfds = 0; + loop->iowatcher = ctx; + return 0; +} + +int iowatcher_cleanup(hloop_t* loop) { + if (loop->iowatcher == NULL) return 0; + io_uring_ctx_t* ctx = (io_uring_ctx_t*)loop->iowatcher; + io_uring_queue_exit(&ctx->ring); + HV_FREE(loop->iowatcher); + return 0; +} + +int iowatcher_add_event(hloop_t* loop, int fd, int events) { + if (loop->iowatcher == NULL) { + iowatcher_init(loop); + } + io_uring_ctx_t* ctx = (io_uring_ctx_t*)loop->iowatcher; + hio_t* io = loop->ios.ptr[fd]; + + unsigned poll_mask = 0; + // pre events + if (io->events & HV_READ) { + poll_mask |= POLLIN; + } + if (io->events & HV_WRITE) { + poll_mask |= POLLOUT; + } + // now events + if (events & HV_READ) { + poll_mask |= POLLIN; + } + if (events & HV_WRITE) { + poll_mask |= POLLOUT; + } + + if (io->events != 0) { + // Cancel the existing poll request first + struct io_uring_sqe* sqe = io_uring_get_sqe(&ctx->ring); + if (sqe == NULL) return -1; + io_uring_prep_poll_remove(sqe, (uint64_t)fd); + io_uring_sqe_set_data64(sqe, IO_URING_CANCEL_TAG); + } else { + ctx->nfds++; + } + + // Add poll for the combined events + struct io_uring_sqe* sqe = io_uring_get_sqe(&ctx->ring); + if (sqe == NULL) return -1; + io_uring_prep_poll_add(sqe, fd, poll_mask); + io_uring_sqe_set_data64(sqe, (uint64_t)fd); + + io_uring_submit(&ctx->ring); + return 0; +} + +int iowatcher_del_event(hloop_t* loop, int fd, int events) { + io_uring_ctx_t* ctx = (io_uring_ctx_t*)loop->iowatcher; + if (ctx == NULL) return 0; + hio_t* io = loop->ios.ptr[fd]; + + // Calculate remaining events + unsigned poll_mask = 0; + // pre events + if (io->events & HV_READ) { + poll_mask |= POLLIN; + } + if (io->events & HV_WRITE) { + poll_mask |= POLLOUT; + } + // now events + if (events & HV_READ) { + poll_mask &= ~POLLIN; + } + if (events & HV_WRITE) { + poll_mask &= ~POLLOUT; + } + + // Cancel existing poll + struct io_uring_sqe* sqe = io_uring_get_sqe(&ctx->ring); + if (sqe == NULL) return -1; + io_uring_prep_poll_remove(sqe, (uint64_t)fd); + io_uring_sqe_set_data64(sqe, IO_URING_CANCEL_TAG); + + if (poll_mask == 0) { + ctx->nfds--; + } else { + // Re-add with remaining events + sqe = io_uring_get_sqe(&ctx->ring); + if (sqe == NULL) return -1; + io_uring_prep_poll_add(sqe, fd, poll_mask); + io_uring_sqe_set_data64(sqe, (uint64_t)fd); + } + + io_uring_submit(&ctx->ring); + return 0; +} + +int iowatcher_poll_events(hloop_t* loop, int timeout) { + io_uring_ctx_t* ctx = (io_uring_ctx_t*)loop->iowatcher; + if (ctx == NULL) return 0; + if (ctx->nfds == 0) return 0; + + struct __kernel_timespec ts; + struct __kernel_timespec* tp = NULL; + if (timeout != INFINITE) { + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000LL; + tp = &ts; + } + + struct io_uring_cqe* cqe; + int ret; + if (tp) { + ret = io_uring_wait_cqe_timeout(&ctx->ring, &cqe, tp); + } else { + ret = io_uring_wait_cqe(&ctx->ring, &cqe); + } + if (ret < 0) { + if (ret == -ETIME || ret == -EINTR) { + return 0; + } + perror("io_uring"); + return ret; + } + + int nevents = 0; + unsigned head; + unsigned ncqes = 0; + io_uring_for_each_cqe(&ctx->ring, head, cqe) { + ncqes++; + uint64_t data = io_uring_cqe_get_data64(cqe); + if (data == IO_URING_CANCEL_TAG) { + continue; + } + + int fd = (int)data; + if (fd < 0 || fd >= loop->ios.maxsize) continue; + hio_t* io = loop->ios.ptr[fd]; + if (io == NULL) continue; + + if (cqe->res < 0) { + io->revents |= HV_READ; + EVENT_PENDING(io); + ++nevents; + } else { + int revents = cqe->res; + if (revents & (POLLIN | POLLHUP | POLLERR)) { + io->revents |= HV_READ; + } + if (revents & (POLLOUT | POLLHUP | POLLERR)) { + io->revents |= HV_WRITE; + } + if (io->revents) { + EVENT_PENDING(io); + ++nevents; + } + } + + // io_uring POLL_ADD is one-shot, re-arm for the same events + unsigned remask = 0; + if (io->events & HV_READ) remask |= POLLIN; + if (io->events & HV_WRITE) remask |= POLLOUT; + if (remask) { + struct io_uring_sqe* sqe = io_uring_get_sqe(&ctx->ring); + if (sqe) { + io_uring_prep_poll_add(sqe, fd, remask); + io_uring_sqe_set_data64(sqe, (uint64_t)fd); + } + } + } + + io_uring_cq_advance(&ctx->ring, ncqes); + + if (nevents > 0) { + io_uring_submit(&ctx->ring); + } + + return nevents; +} +#endif diff --git a/event/iowatcher.h b/event/iowatcher.h index 1c6853d6b..5432bc9df 100644 --- a/event/iowatcher.h +++ b/event/iowatcher.h @@ -10,6 +10,7 @@ !defined(EVENT_KQUEUE) && \ !defined(EVENT_IOCP) && \ !defined(EVENT_PORT) && \ + !defined(EVENT_IO_URING) && \ !defined(EVENT_NOEVENT) #ifdef OS_WIN #if WITH_WEPOLL @@ -18,7 +19,11 @@ #define EVENT_POLL // WSAPoll #endif #elif defined(OS_LINUX) -#define EVENT_EPOLL + #if WITH_IO_URING + #define EVENT_IO_URING + #else + #define EVENT_EPOLL + #endif #elif defined(OS_MAC) #define EVENT_KQUEUE #elif defined(OS_BSD) diff --git a/hconfig.h.in b/hconfig.h.in index 700f9e7dc..4880666a8 100644 --- a/hconfig.h.in +++ b/hconfig.h.in @@ -98,5 +98,6 @@ #cmakedefine WITH_WEPOLL 1 #cmakedefine WITH_KCP 1 +#cmakedefine WITH_IO_URING 1 #endif // HV_CONFIG_H_ From e7acef277757106d0f7b4e67d7c6082b98e19b21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:08:29 +0000 Subject: [PATCH 3/9] Address code review: fix sqe variable scope, improve error message Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- event/io_uring.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/event/io_uring.c b/event/io_uring.c index 3fd2ef5b8..43c5ebbda 100644 --- a/event/io_uring.c +++ b/event/io_uring.c @@ -61,9 +61,10 @@ int iowatcher_add_event(hloop_t* loop, int fd, int events) { poll_mask |= POLLOUT; } + struct io_uring_sqe* sqe; if (io->events != 0) { // Cancel the existing poll request first - struct io_uring_sqe* sqe = io_uring_get_sqe(&ctx->ring); + sqe = io_uring_get_sqe(&ctx->ring); if (sqe == NULL) return -1; io_uring_prep_poll_remove(sqe, (uint64_t)fd); io_uring_sqe_set_data64(sqe, IO_URING_CANCEL_TAG); @@ -72,7 +73,7 @@ int iowatcher_add_event(hloop_t* loop, int fd, int events) { } // Add poll for the combined events - struct io_uring_sqe* sqe = io_uring_get_sqe(&ctx->ring); + sqe = io_uring_get_sqe(&ctx->ring); if (sqe == NULL) return -1; io_uring_prep_poll_add(sqe, fd, poll_mask); io_uring_sqe_set_data64(sqe, (uint64_t)fd); @@ -147,7 +148,7 @@ int iowatcher_poll_events(hloop_t* loop, int timeout) { if (ret == -ETIME || ret == -EINTR) { return 0; } - perror("io_uring"); + perror("io_uring_wait_cqe"); return ret; } From 6113c3183dcee578d13c770437ec4979b3ff0304 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 03:42:21 +0000 Subject: [PATCH 4/9] Restrict WITH_IO_URING option to Linux platform only Wrap the WITH_IO_URING cmake option and liburing linking inside CMAKE_SYSTEM_NAME matches "Linux" checks, mirroring how WITH_WEPOLL is restricted to Windows. Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fa06a83d..a5203598c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,10 @@ option(WITH_GNUTLS "with gnutls library" OFF) option(WITH_MBEDTLS "with mbedtls library" OFF) option(WITH_KCP "compile event/kcp" OFF) -option(WITH_IO_URING "with io_uring" OFF) + +if(CMAKE_SYSTEM_NAME MATCHES "Linux") + option(WITH_IO_URING "with io_uring" OFF) +endif() if(WIN32 OR MINGW) option(WITH_WEPOLL "compile event/wepoll -> use iocp" ON) @@ -166,7 +169,7 @@ if(WITH_MBEDTLS) set(LIBS ${LIBS} mbedtls mbedx509 mbedcrypto) endif() -if(WITH_IO_URING) +if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND WITH_IO_URING) set(LIBS ${LIBS} uring) endif() From 95c4a6d0b92db9b3eadd40151c1ec96a9136e4df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 07:16:24 +0000 Subject: [PATCH 5/9] Apply review suggestions to io_uring backend - Add io_uring_get_sqe_safe() helper that flushes and retries on SQE exhaustion - Handle iowatcher_init() failure in iowatcher_add_event() - Map poll errors to io->events (not just HV_READ) so write-only fds get notified - Track sqe_queued flag for re-arm submissions instead of relying on nevents - Guard -luring with Linux check in Makefile.in - Add FATAL_ERROR for non-Linux WITH_IO_URING in CMakeLists.txt Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- CMakeLists.txt | 5 ++++- Makefile.in | 2 ++ event/io_uring.c | 31 +++++++++++++++++++++++-------- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5203598c..db9e28db7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,7 +169,10 @@ if(WITH_MBEDTLS) set(LIBS ${LIBS} mbedtls mbedx509 mbedcrypto) endif() -if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND WITH_IO_URING) +if(WITH_IO_URING) + if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "WITH_IO_URING is only supported on Linux because liburing is Linux-only.") + endif() set(LIBS ${LIBS} uring) endif() diff --git a/Makefile.in b/Makefile.in index dba67e18b..08983a7b2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -193,9 +193,11 @@ endif endif endif +ifeq ($(OS), Linux) ifeq ($(WITH_IO_URING), yes) LDFLAGS += -luring endif +endif LDFLAGS += $(addprefix -L, $(LIBDIRS)) LDFLAGS += $(addprefix -l, $(LIBS)) diff --git a/event/io_uring.c b/event/io_uring.c index 43c5ebbda..1f63a28bd 100644 --- a/event/io_uring.c +++ b/event/io_uring.c @@ -38,9 +38,22 @@ int iowatcher_cleanup(hloop_t* loop) { return 0; } +static struct io_uring_sqe* io_uring_get_sqe_safe(struct io_uring* ring) { + struct io_uring_sqe* sqe = io_uring_get_sqe(ring); + if (sqe == NULL) { + // SQ is full, flush pending submissions and retry + io_uring_submit(ring); + sqe = io_uring_get_sqe(ring); + } + return sqe; +} + int iowatcher_add_event(hloop_t* loop, int fd, int events) { if (loop->iowatcher == NULL) { - iowatcher_init(loop); + int ret = iowatcher_init(loop); + if (ret < 0) { + return ret; + } } io_uring_ctx_t* ctx = (io_uring_ctx_t*)loop->iowatcher; hio_t* io = loop->ios.ptr[fd]; @@ -64,7 +77,7 @@ int iowatcher_add_event(hloop_t* loop, int fd, int events) { struct io_uring_sqe* sqe; if (io->events != 0) { // Cancel the existing poll request first - sqe = io_uring_get_sqe(&ctx->ring); + sqe = io_uring_get_sqe_safe(&ctx->ring); if (sqe == NULL) return -1; io_uring_prep_poll_remove(sqe, (uint64_t)fd); io_uring_sqe_set_data64(sqe, IO_URING_CANCEL_TAG); @@ -73,7 +86,7 @@ int iowatcher_add_event(hloop_t* loop, int fd, int events) { } // Add poll for the combined events - sqe = io_uring_get_sqe(&ctx->ring); + sqe = io_uring_get_sqe_safe(&ctx->ring); if (sqe == NULL) return -1; io_uring_prep_poll_add(sqe, fd, poll_mask); io_uring_sqe_set_data64(sqe, (uint64_t)fd); @@ -105,7 +118,7 @@ int iowatcher_del_event(hloop_t* loop, int fd, int events) { } // Cancel existing poll - struct io_uring_sqe* sqe = io_uring_get_sqe(&ctx->ring); + struct io_uring_sqe* sqe = io_uring_get_sqe_safe(&ctx->ring); if (sqe == NULL) return -1; io_uring_prep_poll_remove(sqe, (uint64_t)fd); io_uring_sqe_set_data64(sqe, IO_URING_CANCEL_TAG); @@ -114,7 +127,7 @@ int iowatcher_del_event(hloop_t* loop, int fd, int events) { ctx->nfds--; } else { // Re-add with remaining events - sqe = io_uring_get_sqe(&ctx->ring); + sqe = io_uring_get_sqe_safe(&ctx->ring); if (sqe == NULL) return -1; io_uring_prep_poll_add(sqe, fd, poll_mask); io_uring_sqe_set_data64(sqe, (uint64_t)fd); @@ -153,6 +166,7 @@ int iowatcher_poll_events(hloop_t* loop, int timeout) { } int nevents = 0; + int sqe_queued = 0; unsigned head; unsigned ncqes = 0; io_uring_for_each_cqe(&ctx->ring, head, cqe) { @@ -168,7 +182,7 @@ int iowatcher_poll_events(hloop_t* loop, int timeout) { if (io == NULL) continue; if (cqe->res < 0) { - io->revents |= HV_READ; + io->revents |= (io->events ? io->events : HV_RDWR); EVENT_PENDING(io); ++nevents; } else { @@ -190,17 +204,18 @@ int iowatcher_poll_events(hloop_t* loop, int timeout) { if (io->events & HV_READ) remask |= POLLIN; if (io->events & HV_WRITE) remask |= POLLOUT; if (remask) { - struct io_uring_sqe* sqe = io_uring_get_sqe(&ctx->ring); + struct io_uring_sqe* sqe = io_uring_get_sqe_safe(&ctx->ring); if (sqe) { io_uring_prep_poll_add(sqe, fd, remask); io_uring_sqe_set_data64(sqe, (uint64_t)fd); + sqe_queued = 1; } } } io_uring_cq_advance(&ctx->ring, ncqes); - if (nevents > 0) { + if (sqe_queued) { io_uring_submit(&ctx->ring); } From a7173208ec55774125939ea21c0c410b92bc22a5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 07:20:09 +0000 Subject: [PATCH 6/9] Add clarifying comment for error event mapping in io_uring backend Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- event/io_uring.c | 1 + 1 file changed, 1 insertion(+) diff --git a/event/io_uring.c b/event/io_uring.c index 1f63a28bd..171e1b426 100644 --- a/event/io_uring.c +++ b/event/io_uring.c @@ -182,6 +182,7 @@ int iowatcher_poll_events(hloop_t* loop, int timeout) { if (io == NULL) continue; if (cqe->res < 0) { + // Poll request failed: notify registered events, or both if none registered io->revents |= (io->events ? io->events : HV_RDWR); EVENT_PENDING(io); ++nevents; From 10aba5111b721ba27297aa80957428cf9f889d15 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 08:19:27 +0000 Subject: [PATCH 7/9] Fix io_uring.c build: use portable liburing APIs for older versions - Replace io_uring_for_each_cqe (uses typeof GNU extension) with io_uring_cq_ready + io_uring_peek_cqe loop - Replace io_uring_cqe_get_data64/io_uring_sqe_set_data64 (liburing 2.2+) with io_uring_cqe_get_data/io_uring_sqe_set_data (pointer-based) - Change IO_URING_CANCEL_TAG from UINT64_MAX to pointer sentinel Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- event/io_uring.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/event/io_uring.c b/event/io_uring.c index 171e1b426..0b677d126 100644 --- a/event/io_uring.c +++ b/event/io_uring.c @@ -9,7 +9,7 @@ #include #define IO_URING_ENTRIES 1024 -#define IO_URING_CANCEL_TAG UINT64_MAX +#define IO_URING_CANCEL_TAG ((void*)(uintptr_t)-1) typedef struct io_uring_ctx_s { struct io_uring ring; @@ -80,7 +80,7 @@ int iowatcher_add_event(hloop_t* loop, int fd, int events) { sqe = io_uring_get_sqe_safe(&ctx->ring); if (sqe == NULL) return -1; io_uring_prep_poll_remove(sqe, (uint64_t)fd); - io_uring_sqe_set_data64(sqe, IO_URING_CANCEL_TAG); + io_uring_sqe_set_data(sqe, IO_URING_CANCEL_TAG); } else { ctx->nfds++; } @@ -89,7 +89,7 @@ int iowatcher_add_event(hloop_t* loop, int fd, int events) { sqe = io_uring_get_sqe_safe(&ctx->ring); if (sqe == NULL) return -1; io_uring_prep_poll_add(sqe, fd, poll_mask); - io_uring_sqe_set_data64(sqe, (uint64_t)fd); + io_uring_sqe_set_data(sqe, (void*)(uintptr_t)fd); io_uring_submit(&ctx->ring); return 0; @@ -121,7 +121,7 @@ int iowatcher_del_event(hloop_t* loop, int fd, int events) { struct io_uring_sqe* sqe = io_uring_get_sqe_safe(&ctx->ring); if (sqe == NULL) return -1; io_uring_prep_poll_remove(sqe, (uint64_t)fd); - io_uring_sqe_set_data64(sqe, IO_URING_CANCEL_TAG); + io_uring_sqe_set_data(sqe, IO_URING_CANCEL_TAG); if (poll_mask == 0) { ctx->nfds--; @@ -130,7 +130,7 @@ int iowatcher_del_event(hloop_t* loop, int fd, int events) { sqe = io_uring_get_sqe_safe(&ctx->ring); if (sqe == NULL) return -1; io_uring_prep_poll_add(sqe, fd, poll_mask); - io_uring_sqe_set_data64(sqe, (uint64_t)fd); + io_uring_sqe_set_data(sqe, (void*)(uintptr_t)fd); } io_uring_submit(&ctx->ring); @@ -167,19 +167,26 @@ int iowatcher_poll_events(hloop_t* loop, int timeout) { int nevents = 0; int sqe_queued = 0; - unsigned head; - unsigned ncqes = 0; - io_uring_for_each_cqe(&ctx->ring, head, cqe) { - ncqes++; - uint64_t data = io_uring_cqe_get_data64(cqe); + unsigned nready = io_uring_cq_ready(&ctx->ring); + unsigned i; + for (i = 0; i < nready; ++i) { + if (io_uring_peek_cqe(&ctx->ring, &cqe) != 0) break; + void* data = io_uring_cqe_get_data(cqe); if (data == IO_URING_CANCEL_TAG) { + io_uring_cqe_seen(&ctx->ring, cqe); continue; } - int fd = (int)data; - if (fd < 0 || fd >= loop->ios.maxsize) continue; + int fd = (int)(uintptr_t)data; + if (fd < 0 || fd >= loop->ios.maxsize) { + io_uring_cqe_seen(&ctx->ring, cqe); + continue; + } hio_t* io = loop->ios.ptr[fd]; - if (io == NULL) continue; + if (io == NULL) { + io_uring_cqe_seen(&ctx->ring, cqe); + continue; + } if (cqe->res < 0) { // Poll request failed: notify registered events, or both if none registered @@ -200,6 +207,8 @@ int iowatcher_poll_events(hloop_t* loop, int timeout) { } } + io_uring_cqe_seen(&ctx->ring, cqe); + // io_uring POLL_ADD is one-shot, re-arm for the same events unsigned remask = 0; if (io->events & HV_READ) remask |= POLLIN; @@ -208,14 +217,12 @@ int iowatcher_poll_events(hloop_t* loop, int timeout) { struct io_uring_sqe* sqe = io_uring_get_sqe_safe(&ctx->ring); if (sqe) { io_uring_prep_poll_add(sqe, fd, remask); - io_uring_sqe_set_data64(sqe, (uint64_t)fd); + io_uring_sqe_set_data(sqe, (void*)(uintptr_t)fd); sqe_queued = 1; } } } - io_uring_cq_advance(&ctx->ring, ncqes); - if (sqe_queued) { io_uring_submit(&ctx->ring); } From bcb6f2811d6134d1e87af38bf777e0e1081f3d08 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 08:39:42 +0000 Subject: [PATCH 8/9] Compile event/io_uring.c with -std=gnu99 for liburing header compatibility liburing headers use GNU C extensions (typeof, statement expressions) that are not available with -std=c99. Override the C standard to gnu99 specifically for io_uring.c in both CMake and Makefile build systems. Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- CMakeLists.txt | 2 ++ Makefile.in | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index db9e28db7..153139679 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,8 @@ if(WITH_IO_URING) message(FATAL_ERROR "WITH_IO_URING is only supported on Linux because liburing is Linux-only.") endif() set(LIBS ${LIBS} uring) + # liburing headers require GNU C extensions (typeof, statement expressions, etc.) + set_source_files_properties(event/io_uring.c PROPERTIES COMPILE_OPTIONS "-std=gnu99") endif() if(WIN32 OR MINGW) diff --git a/Makefile.in b/Makefile.in index 08983a7b2..bc6bcf8c7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -199,6 +199,14 @@ ifeq ($(WITH_IO_URING), yes) endif endif +# liburing headers require GNU C extensions (typeof, statement expressions, etc.) +ifeq ($(OS), Linux) +ifeq ($(WITH_IO_URING), yes) +event/io_uring.o: event/io_uring.c + $(CC) $(CPPFLAGS) $(CFLAGS) -std=gnu99 -c $< -o $@ +endif +endif + LDFLAGS += $(addprefix -L, $(LIBDIRS)) LDFLAGS += $(addprefix -l, $(LIBS)) From 800e9718436224495379c38c88542f725b210418 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 09:01:27 +0000 Subject: [PATCH 9/9] Use -std=gnu99 globally on Linux instead of per-file override for io_uring.c Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- CMakeLists.txt | 5 +++-- Makefile.in | 11 ++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 153139679..8d8502e34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,9 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/hconfig.h.in ${CMAKE_CURRENT_SOURCE_D # see Makefile.in set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED True) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_C_COMPILER_ID STREQUAL "GNU") + set(CMAKE_C_EXTENSIONS ON) +endif() set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) @@ -174,8 +177,6 @@ if(WITH_IO_URING) message(FATAL_ERROR "WITH_IO_URING is only supported on Linux because liburing is Linux-only.") endif() set(LIBS ${LIBS} uring) - # liburing headers require GNU C extensions (typeof, statement expressions, etc.) - set_source_files_properties(event/io_uring.c PROPERTIES COMPILE_OPTIONS "-std=gnu99") endif() if(WIN32 OR MINGW) diff --git a/Makefile.in b/Makefile.in index bc6bcf8c7..1b807ed38 100644 --- a/Makefile.in +++ b/Makefile.in @@ -87,8 +87,12 @@ endif endif ifeq ($(findstring -std, $(CFLAGS)), ) +ifeq ($(OS), Linux) +override CFLAGS += -std=gnu99 +else override CFLAGS += -std=c99 endif +endif ifeq ($(findstring -std, $(CXXFLAGS)), ) override CXXFLAGS += -std=c++11 @@ -199,13 +203,6 @@ ifeq ($(WITH_IO_URING), yes) endif endif -# liburing headers require GNU C extensions (typeof, statement expressions, etc.) -ifeq ($(OS), Linux) -ifeq ($(WITH_IO_URING), yes) -event/io_uring.o: event/io_uring.c - $(CC) $(CPPFLAGS) $(CFLAGS) -std=gnu99 -c $< -o $@ -endif -endif LDFLAGS += $(addprefix -L, $(LIBDIRS)) LDFLAGS += $(addprefix -l, $(LIBS))