Skip to content

unsafe impl Send+Sync for Submitter, SubmissionQueue, CompletionQueue #283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions io-uring-test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ fn test<S: squeue::EntryMarker, C: cqueue::EntryMarker>(
// 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(())
Expand Down
20 changes: 20 additions & 0 deletions io-uring-test/src/tests/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::Test;
use io_uring::IoUring;
use io_uring::{cqueue, squeue};

pub fn test_sendness<S: squeue::EntryMarker, C: cqueue::EntryMarker>(
ring: &mut IoUring<S, C>,
_test: &Test,
) -> anyhow::Result<()> {
fn assert_send<T: 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(())
}
1 change: 1 addition & 0 deletions io-uring-test/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod api;
pub mod cancel;
pub mod fs;
pub mod futex;
Expand Down
12 changes: 11 additions & 1 deletion src/cqueue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,20 @@ pub struct CompletionQueue<'a, E: EntryMarker = Entry> {
queue: &'a Inner<E>,
}

/// 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<Entry> + private::Sealed {
pub trait EntryMarker: Send + Clone + Debug + Into<Entry> + private::Sealed {
const BUILD_FLAGS: u32;
}

Expand Down
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S: squeue::EntryMarker, C: cqueue::EntryMarker> Send for IoUring<S, C> {}
/// 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<S: squeue::EntryMarker, C: cqueue::EntryMarker> Sync for IoUring<S, C> {}

impl IoUring<squeue::Entry, cqueue::Entry> {
Expand Down
13 changes: 12 additions & 1 deletion src/squeue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,21 @@ pub struct SubmissionQueue<'a, E: EntryMarker = Entry> {
queue: &'a Inner<E>,
}

/// 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<Entry> + private::Sealed {
pub trait EntryMarker: Send + Clone + Debug + From<Entry> + private::Sealed {
const BUILD_FLAGS: u32;
}

Expand Down
7 changes: 7 additions & 0 deletions src/submit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down