diff --git a/io-uring-test/src/main.rs b/io-uring-test/src/main.rs index 088c774f..830307d8 100644 --- a/io-uring-test/src/main.rs +++ b/io-uring-test/src/main.rs @@ -115,6 +115,7 @@ fn test( tests::timeout::test_timeout_cancel(&mut ring, &test)?; tests::timeout::test_timeout_abs(&mut ring, &test)?; tests::timeout::test_timeout_submit_args(&mut ring, &test)?; + tests::timeout::test_timeout_submit_args_min_wait(&mut ring, &test)?; // net tests::net::test_tcp_write_read(&mut ring, &test)?; diff --git a/io-uring-test/src/tests/timeout.rs b/io-uring-test/src/tests/timeout.rs index 18ff34ea..13231a4e 100644 --- a/io-uring-test/src/tests/timeout.rs +++ b/io-uring-test/src/tests/timeout.rs @@ -381,3 +381,55 @@ pub fn test_timeout_submit_args( Ok(()) } + +pub fn test_timeout_submit_args_min_wait( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require! { + test; + ring.params().is_feature_ext_arg(); + ring.params().is_feature_min_timeout(); + }; + + println!("test timeout_submit_args_min_wait"); + + let ts = types::Timespec::new().sec(2); + let args = types::SubmitArgs::new() + .timespec(&ts) + .min_wait_usec(1_000_000); + + // timeout + + let start = Instant::now(); + match ring.submitter().submit_with_args(2, &args) { + Ok(_) => panic!(), + Err(ref err) if err.raw_os_error() == Some(libc::ETIME) => (), + Err(err) => return Err(err.into()), + } + assert_eq!(start.elapsed().as_secs(), 2); + + assert!(ring.completion().next().is_none()); + + // no timeout + + let nop_e = opcode::Nop::new(); + + unsafe { + ring.submission() + .push(&nop_e.build().user_data(0x1d).into()) + .expect("queue is full"); + } + + let start = Instant::now(); + ring.submitter().submit_with_args(2, &args)?; + assert_eq!(start.elapsed().as_secs(), 1); + + let cqes: Vec = ring.completion().map(Into::into).collect(); + + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data(), 0x1d); + assert_eq!(cqes[0].result(), 0); + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index ed5a73ea..c525e833 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -597,6 +597,14 @@ impl Parameters { self.0.features & sys::IORING_FEAT_RECVSEND_BUNDLE != 0 } + /// If this flag is set, applications can use the + /// [`SubmitArgs::min_wait_usec`](types::SubmitArgs::min_wait_usec) method to specify a timeout + /// after which the kernel will return as soon as a single completion is received instead of + /// waiting for the minimum specified by the application. Available since kernel 6.12. + pub fn is_feature_min_timeout(&self) -> bool { + self.0.features & sys::IORING_FEAT_MIN_TIMEOUT != 0 + } + /// The number of submission queue entries allocated. pub fn sq_entries(&self) -> u32 { self.0.sq_entries diff --git a/src/types.rs b/src/types.rs index fc67febf..6bb778b3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -239,7 +239,7 @@ impl<'prev, 'now> SubmitArgs<'prev, 'now> { let args = sys::io_uring_getevents_arg { sigmask: 0, sigmask_sz: 0, - pad: 0, + min_wait_usec: 0, ts: 0, }; @@ -262,6 +262,22 @@ impl<'prev, 'now> SubmitArgs<'prev, 'now> { } } + /// Sets a timeout in microseconds to start waiting for a minimum of a single completion. + /// + /// Once the timeout expires, the kernel will return when a single completion has been received + /// instead of waiting for the minimum amount of completions specified by the `want` parameter + /// in the call to [`Submitter::submit_and_wait`](crate::Submitter::submit_and_wait) or + /// [`Submitter::submit_with_args`](crate::Submitter::submit_with_args). + /// + /// Available since 6.12. Use the + /// [`Parameters::is_feature_min_timeout`](crate::Parameters::is_feature_min_timeout) method to + /// check for availability. + #[inline] + pub fn min_wait_usec(mut self, min_wait_usec: u32) -> Self { + self.args.min_wait_usec = min_wait_usec; + self + } + #[inline] pub fn timespec<'new>(mut self, timespec: &'new Timespec) -> SubmitArgs<'now, 'new> { self.args.ts = cast_ptr(timespec) as _;