Skip to content

Commit

Permalink
Make storage Send + Sync if item is only Send, bump version
Browse files Browse the repository at this point in the history
  • Loading branch information
agerasev committed May 15, 2024
1 parent ba69a52 commit 6a689ea
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 13 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ readme = "README.md"
license = "MIT/Apache-2.0"

[workspace.dependencies]
ringbuf = { path = ".", version = "0.4.0" }
ringbuf = { path = ".", version = "0.4.1" }

[workspace]
members = ["async", "blocking"]

[package]
name = "ringbuf"
version = "0.4.0"
version = "0.4.1"
edition.workspace = true
authors.workspace = true
description = "Lock-free SPSC FIFO ring buffer with direct access to inner data"
Expand Down
2 changes: 1 addition & 1 deletion async/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "async-ringbuf"
version = "0.2.0"
version = "0.2.1"
edition.workspace = true
authors.workspace = true
description = "Async SPSC FIFO ring buffer"
Expand Down
26 changes: 22 additions & 4 deletions src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub unsafe trait Storage {
/// # Safety
///
/// Slice must not overlab with existing mutable slices.
///
/// Non-`Sync` items must not be accessed concurrently.
unsafe fn slice(&self, range: Range<usize>) -> &[MaybeUninit<Self::Item>] {
slice::from_raw_parts(self.as_ptr().add(range.start), range.len())
}
Expand All @@ -57,8 +59,8 @@ pub struct Ref<'a, T> {
ptr: *mut MaybeUninit<T>,
len: usize,
}
unsafe impl<'a, T> Send for Ref<'a, T> where T: Sync {}
unsafe impl<'a, T> Sync for Ref<'a, T> where T: Sync {}
unsafe impl<'a, T> Send for Ref<'a, T> where T: Send {}
unsafe impl<'a, T> Sync for Ref<'a, T> where T: Send {}
unsafe impl<'a, T> Storage for Ref<'a, T> {
type Item = T;
#[inline]
Expand Down Expand Up @@ -88,7 +90,7 @@ impl<'a, T> From<Ref<'a, T>> for &'a mut [MaybeUninit<T>] {
pub struct Owning<T: ?Sized> {
data: UnsafeCell<T>,
}
unsafe impl<T: ?Sized> Sync for Owning<T> where T: Sync {}
unsafe impl<T: ?Sized> Sync for Owning<T> where T: Send {}
impl<T> From<T> for Owning<T> {
fn from(value: T) -> Self {
Self {
Expand Down Expand Up @@ -136,7 +138,7 @@ pub struct Heap<T> {
#[cfg(feature = "alloc")]
unsafe impl<T> Send for Heap<T> where T: Send {}
#[cfg(feature = "alloc")]
unsafe impl<T> Sync for Heap<T> where T: Sync {}
unsafe impl<T> Sync for Heap<T> where T: Send {}
#[cfg(feature = "alloc")]
unsafe impl<T> Storage for Heap<T> {
type Item = T;
Expand Down Expand Up @@ -188,3 +190,19 @@ impl<T> Drop for Heap<T> {
drop(unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(self.ptr, self.len)) });
}
}

#[cfg(test)]
mod tests {
use super::*;
use core::{cell::Cell, marker::PhantomData};

struct Check<S: Storage + Send + Sync + ?Sized>(PhantomData<S>);

#[allow(dead_code)]
fn check_send_sync() {
let _: Check<Ref<Cell<i32>>>;
let _: Check<Array<Cell<i32>, 4>>;
let _: Check<Slice<Cell<i32>>>;
let _: Check<Heap<Cell<i32>>>;
}
}
46 changes: 40 additions & 6 deletions src/tests/shared.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use crate::{storage::Heap, traits::*, SharedRb};
use std::{thread, thread::sleep, time::Duration, vec::Vec};
use std::{cell::Cell, thread, thread::sleep, time::Duration, vec::Vec};

fn yield_() {
sleep(Duration::from_millis(1));
}

#[cfg(feature = "std")]
#[test]
fn concurrent() {
fn yield_() {
sleep(Duration::from_millis(1));
}

const MSG: &[u8] = b"The quick brown fox jumps over the lazy dog\0";
let rb = SharedRb::<Heap<u8>>::new(4);
let (mut prod, mut cons) = rb.split();
Expand All @@ -34,3 +33,38 @@ fn concurrent() {
pjh.join().unwrap();
assert_eq!(cjh.join().unwrap(), MSG);
}

#[test]
fn non_sync() {
const N: i32 = 256;

let rb = SharedRb::<Heap<Cell<i32>>>::new(4);
let (mut prod, mut cons) = rb.split();

let pjh = thread::spawn({
move || {
for i in 0..N {
while prod.try_push(Cell::new(i)).is_err() {
yield_();
}
}
}
});

let cjh = thread::spawn(move || {
for i in 0..N {
assert_eq!(
i,
loop {
match cons.try_pop() {
Some(i) => break i.get(),
None => yield_(),
}
}
);
}
});

pjh.join().unwrap();
cjh.join().unwrap();
}
2 changes: 2 additions & 0 deletions src/traits/observer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub trait Observer {
/// # Safety
///
/// Slice must not overlap with any mutable slice existing at the same time.
///
/// Non-`Sync` items must not be accessed from multiple threads at the same time.
unsafe fn unsafe_slices(&self, start: usize, end: usize) -> (&[MaybeUninit<Self::Item>], &[MaybeUninit<Self::Item>]);

/// Get mutable slice between `start` and `end` indices.
Expand Down

0 comments on commit 6a689ea

Please sign in to comment.