diff --git a/io-uring-test/src/main.rs b/io-uring-test/src/main.rs index a1868c0a..c05d3630 100644 --- a/io-uring-test/src/main.rs +++ b/io-uring-test/src/main.rs @@ -168,6 +168,9 @@ fn test( // regression test tests::regression::test_issue154(&mut ring, &test)?; + // api tests + tests::api::test_sendness(&mut ring, &test)?; + println!("Test count: {}", test.count.get()); Ok(()) diff --git a/io-uring-test/src/tests/api.rs b/io-uring-test/src/tests/api.rs new file mode 100644 index 00000000..38643d1c --- /dev/null +++ b/io-uring-test/src/tests/api.rs @@ -0,0 +1,20 @@ +use crate::Test; +use io_uring::IoUring; +use io_uring::{cqueue, squeue}; + +pub fn test_sendness( + ring: &mut IoUring, + _test: &Test, +) -> anyhow::Result<()> { + fn assert_send(t: T) -> T { + t + } + + let ring = assert_send(ring); + + let (submitter, sq, cq) = ring.split(); + assert_send(submitter); + assert_send(sq); + assert_send(cq); + Ok(()) +} diff --git a/io-uring-test/src/tests/mod.rs b/io-uring-test/src/tests/mod.rs index 9d03131f..7af1aa4e 100644 --- a/io-uring-test/src/tests/mod.rs +++ b/io-uring-test/src/tests/mod.rs @@ -1,3 +1,4 @@ +pub mod api; pub mod cancel; pub mod fs; pub mod futex; diff --git a/src/cqueue.rs b/src/cqueue.rs index c37daa50..079702f6 100644 --- a/src/cqueue.rs +++ b/src/cqueue.rs @@ -29,10 +29,20 @@ pub struct CompletionQueue<'a, E: EntryMarker = Entry> { queue: &'a Inner, } +/// SAFETY: there isn't anything thread-local about the completion queue memory; +unsafe impl<'a, E: EntryMarker> Send for CompletionQueue<'a, E> {} +/// SAFETY: mutating methods take `&mut self`, thereby eliminating data races between +/// different userspace borrowers at compile time via standard borrowing rules. +/// (There are `unsafe` methods like `borrow_shared()` that allow violating this.) +/// +/// The various pointers to atomics are pointers to shared state between +/// userspace and kernel, and orthogonal to this unsafe impl. +unsafe impl<'a, E: EntryMarker> Sync for CompletionQueue<'a, E> {} + /// A completion queue entry (CQE), representing a complete I/O operation. /// /// This is implemented for [`Entry`] and [`Entry32`]. -pub trait EntryMarker: Clone + Debug + Into + private::Sealed { +pub trait EntryMarker: Send + Clone + Debug + Into + private::Sealed { const BUILD_FLAGS: u32; } diff --git a/src/lib.rs b/src/lib.rs index 440ae0ba..0c3b51a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,7 +69,13 @@ where #[derive(Clone)] pub struct Parameters(sys::io_uring_params); +/// SAFETY: there isn't anything thread-local about an io_uring instance. +/// (We do not attempt to model `IORING_SETUP_SINGLE_ISSUER` in the type system.) unsafe impl Send for IoUring {} +/// SAFETY: mutating methods take `&mut self`, thereby eliminating data races between +/// different userspace borrowers at compile time via standard borrowing rules. +/// (There are `unsafe` methods like `submission_shared()` and `completion_shared()` +/// that allow violating this.) unsafe impl Sync for IoUring {} impl IoUring { diff --git a/src/squeue.rs b/src/squeue.rs index 5bd5f67d..1824b0ae 100644 --- a/src/squeue.rs +++ b/src/squeue.rs @@ -28,10 +28,21 @@ pub struct SubmissionQueue<'a, E: EntryMarker = Entry> { queue: &'a Inner, } +/// SAFETY: there isn't anything thread-local about the submission queue; +/// (We do not attempt to model `IORING_SETUP_SINGLE_ISSUER` in the type system.) +unsafe impl<'a, E: EntryMarker> Send for SubmissionQueue<'a, E> {} +/// SAFETY: mutating methods take `&mut self`, thereby eliminating data races between +/// different userspace borrowers at compile time via standard borrowing rules. +/// (There are `unsafe` methods like `borrow_shared()` that allow violating this.) +/// +/// The various pointers to atomics are pointers to shared state between +/// userspace and kernel, and orthogonal to this unsafe impl. +unsafe impl<'a, E: EntryMarker> Sync for SubmissionQueue<'a, E> {} + /// A submission queue entry (SQE), representing a request for an I/O operation. /// /// This is implemented for [`Entry`] and [`Entry128`]. -pub trait EntryMarker: Clone + Debug + From + private::Sealed { +pub trait EntryMarker: Send + Clone + Debug + From + private::Sealed { const BUILD_FLAGS: u32; } diff --git a/src/submit.rs b/src/submit.rs index 0f41c74e..faa8e92d 100644 --- a/src/submit.rs +++ b/src/submit.rs @@ -26,6 +26,13 @@ pub struct Submitter<'a> { sq_flags: *const atomic::AtomicU32, } +/// SAFETY: there isn't anyhting thread-local about submission that would make Send memory unsafe. +/// (We do not attempt to model `IORING_SETUP_SINGLE_ISSUER` in the type system.) +unsafe impl<'a> Send for Submitter<'a> {} +/// SAFETY: [`Submitter`] methods do not manipulate any state themselves (the kernel might do that, +/// during io_uring_enter, but that's orthogonal to this unsafe impl). +unsafe impl<'a> Sync for Submitter<'a> {} + impl<'a> Submitter<'a> { #[inline] pub(crate) const fn new(