Conversation
- 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>
Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com>
CMakeLists.txt
Outdated
| option(WITH_MBEDTLS "with mbedtls library" OFF) | ||
|
|
||
| option(WITH_KCP "compile event/kcp" OFF) | ||
| option(WITH_IO_URING "with io_uring" OFF) |
There was a problem hiding this comment.
Done in 6113c31. WITH_IO_URING option and -luring linking are now wrapped in if(CMAKE_SYSTEM_NAME MATCHES "Linux"), matching the pattern used for WITH_WEPOLL on Windows.
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>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. |
There was a problem hiding this comment.
Pull request overview
Adds an io_uring-based event-loop backend for Linux and integrates it into libhv’s existing iowatcher backend selection and build configuration.
Changes:
- Introduces
event/io_uring.cimplementing theiowatcherinterface using liburing’s poll operations. - Adds
WITH_IO_URINGbuild/config options and links liburing when enabled (CMake + configure/make). - Updates backend auto-selection on Linux and exposes
"io_uring"viahio_engine().
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| hconfig.h.in | Adds WITH_IO_URING to generated config header. |
| event/iowatcher.h | Auto-selects EVENT_IO_URING on Linux when enabled. |
| event/io_uring.c | New io_uring backend implementation. |
| event/hloop.h | Adds "io_uring" engine string. |
| event/hloop.c | Adds "io_uring" engine string (non-header implementation). |
| event/README.md | Documents the new backend file in the event module tree. |
| docs/PLAN.md | Updates plan/done list to include io_uring. |
| configure | Adds --with-io_uring option handling. |
| config.ini | Adds WITH_IO_URING config knob. |
| Makefile.in | Links -luring when WITH_IO_URING=yes. |
| CMakeLists.txt | Adds WITH_IO_URING option and links uring when enabled. |
| .gitignore | Ignores build-* directories. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
event/io_uring.c
Outdated
| struct io_uring_sqe* sqe; | ||
| if (io->events != 0) { | ||
| // Cancel the existing poll request first | ||
| 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 | ||
| 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); |
There was a problem hiding this comment.
When io_uring_get_sqe returns NULL, this function returns -1, but callers (e.g., hio_add) don't check the return value and will still set io->events. This can silently leave an fd unarmed. Consider ensuring SQE availability (submit/flush + retry, or increase ring size) and/or changing the call path to only update io->events on success.
| // 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); | ||
| } |
There was a problem hiding this comment.
iowatcher_del_event returns -1 when io_uring_get_sqe fails, but hio_del doesn't check this and will still clear io->events. That can desynchronize the loop's idea of what is armed vs. what's actually registered in io_uring; handle SQE exhaustion here (submit/flush + retry) and avoid returning an unhandled error.
event/io_uring.c
Outdated
| if (io == NULL) continue; | ||
|
|
||
| if (cqe->res < 0) { | ||
| io->revents |= HV_READ; |
There was a problem hiding this comment.
On cqe->res < 0 (poll request failed), the code sets only HV_READ. If the fd is registered for write-only (e.g. connect uses HV_WRITE), the pending callback will run but hio_handle_events will ignore the event, potentially stalling the connection/error handling. Consider mapping this failure to io->revents |= (io->events ? io->events : HV_RDWR) (or at least set both READ and WRITE) so the appropriate handler runs.
| io->revents |= HV_READ; | |
| unsigned ev = io->events ? io->events : HV_RDWR; | |
| io->revents |= ev; |
| // 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); | ||
| } |
There was a problem hiding this comment.
io_uring_submit is only called when nevents > 0, but this loop can queue re-arm SQEs even when no HV_READ/HV_WRITE bits were set (e.g., if cqe->res contains only poll bits you don't map to hv events). In that case the re-arm SQEs remain unsubmitted and the loop can block with nfds > 0 but no active polls. Track whether any SQEs were queued and submit based on that instead of nevents.
|
|
||
| ifeq ($(WITH_IO_URING), yes) | ||
| LDFLAGS += -luring | ||
| endif |
There was a problem hiding this comment.
The -luring linker flag is added whenever WITH_IO_URING=yes, regardless of platform. Since liburing is Linux-specific, enabling this flag on non-Linux targets will break linking; consider additionally guarding this block with ifeq ($(OS), Linux) (or equivalent) so the option is only effective on Linux.
| ifeq ($(WITH_IO_URING), yes) | |
| LDFLAGS += -luring | |
| endif | |
| ifeq ($(OS), Linux) | |
| ifeq ($(WITH_IO_URING), yes) | |
| LDFLAGS += -luring | |
| endif | |
| endif |
| @@ -165,6 +169,10 @@ if(WITH_MBEDTLS) | |||
| set(LIBS ${LIBS} mbedtls mbedx509 mbedcrypto) | |||
There was a problem hiding this comment.
LIBS unconditionally appends uring when WITH_IO_URING is ON. Since liburing is Linux-only, enabling this option on non-Linux platforms will fail at link time; consider guarding this with a Linux check (e.g. if(CMAKE_SYSTEM_NAME STREQUAL "Linux")) and emitting a clear configuration error otherwise.
| set(LIBS ${LIBS} mbedtls mbedx509 mbedcrypto) | |
| 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() |
event/io_uring.c
Outdated
|
|
||
| int iowatcher_add_event(hloop_t* loop, int fd, int events) { | ||
| if (loop->iowatcher == NULL) { | ||
| iowatcher_init(loop); |
There was a problem hiding this comment.
iowatcher_init(loop) can return a negative error (when io_uring_queue_init fails), but iowatcher_add_event ignores that return value and immediately dereferences loop->iowatcher. This can crash on init failure; handle the error (e.g., return it) before using loop->iowatcher.
| iowatcher_init(loop); | |
| int ret = iowatcher_init(loop); | |
| if (ret < 0) { | |
| return ret; | |
| } |
|
@copilot apply changes based on the comments in this thread |
- 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>
Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com>
Applied all review suggestions in 95c4a6d:
|
|
@copilot 编译报错 |
- 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>
Fixed in 10aba51. The build errors were caused by two liburing compatibility issues:
|
|
@copilot io_uring.c需要使用-std=gnu99才能编译通过,使用-std=c99不行 |
…ility 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>
Fixed in bcb6f28. Added
Other source files continue to compile with |
Makefile.in
Outdated
| ifeq ($(OS), Linux) | ||
| ifeq ($(WITH_IO_URING), yes) | ||
| event/io_uring.o: event/io_uring.c | ||
| $(CC) $(CPPFLAGS) $(CFLAGS) -std=gnu99 -c $< -o $@ |
There was a problem hiding this comment.
单独设置不太好看,能改成如果是linux gcc编译器,就全部使用-std=gnu99吗,cmake里也改下
There was a problem hiding this comment.
Done in 800e971. Changed to use -std=gnu99 globally for all C files on Linux:
- CMakeLists.txt:
set(CMAKE_C_EXTENSIONS ON)when Linux + GCC, which produces-std=gnu99 - Makefile.in:
-std=gnu99on Linux,-std=c99on other platforms
Removed the per-file set_source_files_properties and the explicit io_uring.o rule.
…uring.c Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com>
event/io_uring.c- io_uring backend implementation using liburing poll mode (IORING_OP_POLL_ADD)event/iowatcher.h- AddEVENT_IO_URINGto backend selection logicevent/hloop.c- AddEVENT_IO_URINGcase tohio_engine()functionevent/hloop.h- AddEVENT_IO_URINGto the engine name commentCMakeLists.txt- AddWITH_IO_URINGoption (Linux-only) and link liburinghconfig.h.in- AddWITH_IO_URINGcmakedefineconfigure- Add--with-io_uringoption and checkconfig.ini- AddWITH_IO_URING=nodefaultMakefile.in- Add io_uring linking when enabledevent/README.md- Add io_uring.c entrydocs/PLAN.md- Move io_uring from Plan to DoneWITH_IO_URINGoption to Linux platform only in CMakeLists.txtio_uring_get_sqe_safe()helper to handle SQE exhaustion (submit + retry)iowatcher_init()failure iniowatcher_add_event()(return error)io->eventsinstead of justHV_READfor proper write-only fd handlingsqe_queuedflag for re-arm submissions instead of relying onnevents-luringwith Linux check inMakefile.inFATAL_ERRORfor non-LinuxWITH_IO_URINGinCMakeLists.txtio_uring_for_each_cqewithio_uring_peek_cqeloopio_uring_cqe_get_data64/io_uring_sqe_set_data64with portable equivalents-std=gnu99globally on Linux GCC (CMakeCMAKE_C_EXTENSIONS ON, Makefile.in-std=gnu99) instead of-std=c99Original prompt
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.