From 28dc2b4edcff3fca5da3c4252e0a615239d9a56c Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 27 Aug 2026 12:56:12 +1200 Subject: [PATCH 1/3] Add io_uring submission backpressure regression test Assisted-By: devx/efad4cc8-cf12-4d6e-8c55-6f2c3a6683d2 --- ext/io/event/selector/uring.c | 39 +++++++++++++++++++++++++++++++-- test/io/event/selector/uring.rb | 23 +++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 test/io/event/selector/uring.rb diff --git a/ext/io/event/selector/uring.c b/ext/io/event/selector/uring.c index 9b3b959a..05a12a3a 100644 --- a/ext/io/event/selector/uring.c +++ b/ext/io/event/selector/uring.c @@ -495,12 +495,14 @@ void IO_Event_Selector_URing_dump_completion_queue(struct IO_Event_Selector_URin } // Flush the submission queue, optionally yielding if unsuccessful. +typedef int (*io_uring_submit_function)(struct io_uring *ring); + static -int io_uring_submit_all(struct IO_Event_Selector_URing *selector, bool yield) { +int io_uring_submit_all_with(struct IO_Event_Selector_URing *selector, bool yield, io_uring_submit_function submit) { struct io_uring *ring = &selector->ring; while (io_uring_sq_ready(ring) > 0) { - int result = io_uring_submit(&selector->ring); + int result = submit(&selector->ring); if (result == -EBUSY || result == -EAGAIN) { if (yield) IO_Event_Selector_yield(&selector->backend); @@ -514,6 +516,11 @@ int io_uring_submit_all(struct IO_Event_Selector_URing *selector, bool yield) { return 0; } +static +int io_uring_submit_all(struct IO_Event_Selector_URing *selector, bool yield) { + return io_uring_submit_all_with(selector, yield, io_uring_submit); +} + // Flush the submission queue if pending operations are present. static int io_uring_submit_flush(struct IO_Event_Selector_URing *selector) { @@ -1760,6 +1767,32 @@ static int IO_Event_Selector_URing_supported_p(void) { return 1; } +static int IO_Event_Selector_URing_test_submission_count = 0; + +static int IO_Event_Selector_URing_test_submission_backpressure(struct io_uring *ring) { + (void)ring; + + if (++IO_Event_Selector_URing_test_submission_count > 1) { + rb_raise(rb_eRuntimeError, "submission was retried before processing completions"); + } + + return -EAGAIN; +} + +static VALUE IO_Event_Selector_URing_test_submission_backpressure_p(VALUE self) { + struct IO_Event_Selector_URing *selector = NULL; + TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector); + + if (!io_uring_get_sqe(&selector->ring)) { + rb_raise(rb_eRuntimeError, "could not prepare test submission"); + } + + IO_Event_Selector_URing_test_submission_count = 0; + int result = io_uring_submit_all_with(selector, false, IO_Event_Selector_URing_test_submission_backpressure); + + return rb_ary_new_from_args(2, INT2NUM(result), INT2NUM(IO_Event_Selector_URing_test_submission_count)); +} + void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) { if (!IO_Event_Selector_URing_supported_p()) { return; @@ -1803,4 +1836,6 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) { rb_define_method(IO_Event_Selector_URing, "io_close", IO_Event_Selector_URing_io_close, 1); rb_define_method(IO_Event_Selector_URing, "process_wait", IO_Event_Selector_URing_process_wait, 3); + + rb_define_private_method(IO_Event_Selector_URing, "test_submission_backpressure", IO_Event_Selector_URing_test_submission_backpressure_p, 0); } diff --git a/test/io/event/selector/uring.rb b/test/io/event/selector/uring.rb new file mode 100644 index 00000000..01bfcfaa --- /dev/null +++ b/test/io/event/selector/uring.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2026, by Samuel Williams. + +require "io/event" + +return unless defined?(IO::Event::Selector::URing) + +describe IO::Event::Selector::URing do + let(:selector) {subject.new(Fiber.current)} + + after do + selector&.close + end + + it "returns control when submission encounters backpressure" do + result, submissions = selector.send(:test_submission_backpressure) + + expect(result).to be == -Errno::EAGAIN::Errno + expect(submissions).to be == 1 + end +end From af49011aa3a83e1d02fb1eed6133ba24fc79ea5d Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 27 Aug 2026 12:56:22 +1200 Subject: [PATCH 2/3] Return to the event loop on submission backpressure Assisted-By: devx/efad4cc8-cf12-4d6e-8c55-6f2c3a6683d2 --- ext/io/event/selector/uring.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/io/event/selector/uring.c b/ext/io/event/selector/uring.c index 05a12a3a..6dd87542 100644 --- a/ext/io/event/selector/uring.c +++ b/ext/io/event/selector/uring.c @@ -506,6 +506,7 @@ int io_uring_submit_all_with(struct IO_Event_Selector_URing *selector, bool yiel if (result == -EBUSY || result == -EAGAIN) { if (yield) IO_Event_Selector_yield(&selector->backend); + return result; } else if (result < 0) { rb_syserr_fail(-result, "io_uring_submit_all:io_uring_submit"); return result; From 2c2be4beeb2370837cf49a8b259826b62d2b01fc Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 27 Aug 2026 13:01:36 +1200 Subject: [PATCH 3/3] Fix test formatting Assisted-By: devx/efad4cc8-cf12-4d6e-8c55-6f2c3a6683d2 --- test/io/event/selector/uring.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/io/event/selector/uring.rb b/test/io/event/selector/uring.rb index 01bfcfaa..bb12ed4c 100644 --- a/test/io/event/selector/uring.rb +++ b/test/io/event/selector/uring.rb @@ -9,14 +9,14 @@ describe IO::Event::Selector::URing do let(:selector) {subject.new(Fiber.current)} - + after do selector&.close end - + it "returns control when submission encounters backpressure" do result, submissions = selector.send(:test_submission_backpressure) - + expect(result).to be == -Errno::EAGAIN::Errno expect(submissions).to be == 1 end