diff --git a/Cargo.toml b/Cargo.toml index 13b4ab795a7a857400a51d97e63d8c8e71b604e3..983d302e07b1b587a426c74961a3b0bdca904914 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,6 @@ version = "0.2.0" [dependencies] untagged-option = "0.1.1" + +[dev-dependencies] +scoped_threadpool = "0.1.8" diff --git a/blacklist.txt b/blacklist.txt index 54defd9d6fbc0c8ce18d2594088fda6363886ad9..bf051e53d43c7b2770a383860ce0b20c0a5e929d 100644 --- a/blacklist.txt +++ b/blacklist.txt @@ -1,4 +1,7 @@ -# false positives from thread::spawn (?) +# false positives in thread::spawn (?) race:*dealloc race:*drop_slow* race:__call_tls_dtors + +# false positives in scoped_threadpool (?) +race:*drop* diff --git a/src/cfail.rs b/src/cfail.rs new file mode 100644 index 0000000000000000000000000000000000000000..50c3a920747f2bfd3a756decc7da33cbb42a0ce9 --- /dev/null +++ b/src/cfail.rs @@ -0,0 +1,80 @@ +//! Compile fail tests +//! +//! # `Send`-ness +//! +//! Collections of `Send`-able things are `Send` +//! +//! ``` +//! use heapless::{RingBuffer, Vec}; +//! use heapless::ring_buffer::{Consumer, Producer}; +//! +//! struct IsSend; +//! +//! unsafe impl Send for IsSend {} +//! +//! fn is_send<T>() where T: Send {} +//! +//! is_send::<Consumer<IsSend, [IsSend; 4]>>(); +//! is_send::<Producer<IsSend, [IsSend; 4]>>(); +//! is_send::<RingBuffer<IsSend, [IsSend; 4]>>(); +//! is_send::<Vec<IsSend, [IsSend; 4]>>(); +//! ``` +//! +//! Collections of non-`Send`-able things are *not* `Send` +//! +//! ``` compile_fail +//! use std::marker::PhantomData; +//! use heapless::ring_buffer::Consumer; +//! +//! type NotSend = PhantomData<*const ()>; +//! +//! fn is_send<T>() where T: Send {} +//! +//! is_send::<Consumer<NotSend, [NotSend; 4]>>(); +//! ``` +//! +//! ``` compile_fail +//! use std::marker::PhantomData; +//! use heapless::ring_buffer::Producer; +//! +//! type NotSend = PhantomData<*const ()>; +//! +//! fn is_send<T>() where T: Send {} +//! +//! is_send::<Producer<NotSend, [NotSend; 4]>>(); +//! ``` +//! +//! ``` compile_fail +//! use std::marker::PhantomData; +//! use heapless::RingBuffer; +//! +//! type NotSend = PhantomData<*const ()>; +//! +//! fn is_send<T>() where T: Send {} +//! +//! is_send::<RingBuffer<NotSend, [NotSend; 4]>>(); +//! ``` +//! +//! ``` compile_fail +//! use std::marker::PhantomData; +//! use heapless::Vec; +//! +//! type NotSend = PhantomData<*const ()>; +//! +//! fn is_send<T>() where T: Send {} +//! +//! is_send::<Vec<NotSend, [NotSend; 4]>>(); +//! ``` +//! +//! # Freeze +//! +//! Splitting a `RingBuffer` should invalidate the original reference. +//! +//! ``` compile_fail +//! use heapless::RingBuffer; +//! +//! let mut rb: RingBuffer<u8, [u8; 4]> = RingBuffer::new(); +//! +//! let (p, c) = rb.split(); +//! rb.enqueue(0).unwrap(); +//! ``` diff --git a/src/lib.rs b/src/lib.rs index e8f83cbd160859606a248ba5ae1451b1ea7924f2..f36ef00d20d5b6e5c5c10280dae62fab1e5afb00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,8 +36,6 @@ //! //! ### Single producer single consumer mode //! -//! For use in *single core* systems like microcontrollers -//! //! ``` //! use heapless::RingBuffer; //! @@ -77,72 +75,6 @@ //! // .. //! } //! ``` -//! -//! # `Send`-ness -//! -//! Collections of `Send`-able things are `Send` -//! -//! ``` -//! use heapless::{RingBuffer, Vec}; -//! use heapless::ring_buffer::{Consumer, Producer}; -//! -//! struct IsSend; -//! -//! unsafe impl Send for IsSend {} -//! -//! fn is_send<T>() where T: Send {} -//! -//! is_send::<Consumer<IsSend, [IsSend; 4]>>(); -//! is_send::<Producer<IsSend, [IsSend; 4]>>(); -//! is_send::<RingBuffer<IsSend, [IsSend; 4]>>(); -//! is_send::<Vec<IsSend, [IsSend; 4]>>(); -//! ``` -//! -//! Collections of not `Send`-able things are *not* `Send` -//! -//! ``` compile_fail -//! use std::marker::PhantomData; -//! use heapless::ring_buffer::Consumer; -//! -//! type NotSend = PhantomData<*const ()>; -//! -//! fn is_send<T>() where T: Send {} -//! -//! is_send::<Consumer<NotSend, [NotSend; 4]>>(); -//! ``` -//! -//! ``` compile_fail -//! use std::marker::PhantomData; -//! use heapless::ring_buffer::Producer; -//! -//! type NotSend = PhantomData<*const ()>; -//! -//! fn is_send<T>() where T: Send {} -//! -//! is_send::<Producer<NotSend, [NotSend; 4]>>(); -//! ``` -//! -//! ``` compile_fail -//! use std::marker::PhantomData; -//! use heapless::RingBuffer; -//! -//! type NotSend = PhantomData<*const ()>; -//! -//! fn is_send<T>() where T: Send {} -//! -//! is_send::<RingBuffer<NotSend, [NotSend; 4]>>(); -//! ``` -//! -//! ``` compile_fail -//! use std::marker::PhantomData; -//! use heapless::Vec; -//! -//! type NotSend = PhantomData<*const ()>; -//! -//! fn is_send<T>() where T: Send {} -//! -//! is_send::<Vec<NotSend, [NotSend; 4]>>(); -//! ``` #![deny(missing_docs)] #![feature(const_fn)] @@ -157,8 +89,9 @@ extern crate untagged_option; pub use vec::Vec; pub use ring_buffer::RingBuffer; -pub mod ring_buffer; +mod cfail; mod vec; +pub mod ring_buffer; /// Error raised when the buffer is full #[derive(Clone, Copy, Debug, Eq, PartialEq)] diff --git a/src/ring_buffer/spsc.rs b/src/ring_buffer/spsc.rs index 4c93f429dae84c820f7277ad90115a1f092452ff..488c07a87a91a8d34f90a0bf0f55536c4dc2dd5d 100644 --- a/src/ring_buffer/spsc.rs +++ b/src/ring_buffer/spsc.rs @@ -1,5 +1,5 @@ use core::ptr::{self, Shared}; -use core::marker::Unsize; +use core::marker::{PhantomData, Unsize}; use BufferFullError; use ring_buffer::RingBuffer; @@ -9,13 +9,15 @@ where A: Unsize<[T]>, { /// Splits a statically allocated ring buffer into producer and consumer end points - pub fn split(&'static mut self) -> (Producer<T, A>, Consumer<T, A>) { + pub fn split(&mut self) -> (Producer<T, A>, Consumer<T, A>) { ( Producer { rb: unsafe { Shared::new_unchecked(self) }, + _marker: PhantomData, }, Consumer { rb: unsafe { Shared::new_unchecked(self) }, + _marker: PhantomData, }, ) } @@ -23,15 +25,16 @@ where /// A ring buffer "consumer"; it can dequeue items from the ring buffer // NOTE the consumer semantically owns the `head` pointer of the ring buffer -pub struct Consumer<T, A> +pub struct Consumer<'a, T, A> where A: Unsize<[T]>, { // XXX do we need to use `Shared` (for soundness) here? rb: Shared<RingBuffer<T, A>>, + _marker: PhantomData<&'a ()>, } -impl<T, A> Consumer<T, A> +impl<'a, T, A> Consumer<'a, T, A> where A: Unsize<[T]>, { @@ -54,7 +57,7 @@ where } } -unsafe impl<T, A> Send for Consumer<T, A> +unsafe impl<'a, T, A> Send for Consumer<'a, T, A> where A: Unsize<[T]>, T: Send, @@ -63,15 +66,16 @@ where /// A ring buffer "producer"; it can enqueue items into the ring buffer // NOTE the producer semantically owns the `tail` pointer of the ring buffer -pub struct Producer<T, A> +pub struct Producer<'a, T, A> where A: Unsize<[T]>, { // XXX do we need to use `Shared` (for soundness) here? rb: Shared<RingBuffer<T, A>>, + _marker: PhantomData<&'a ()>, } -impl<T, A> Producer<T, A> +impl<'a, T, A> Producer<'a, T, A> where A: Unsize<[T]>, { @@ -99,7 +103,7 @@ where } } -unsafe impl<T, A> Send for Producer<T, A> +unsafe impl<'a, T, A> Send for Producer<'a, T, A> where A: Unsize<[T]>, T: Send, diff --git a/tests/tsan.rs b/tests/tsan.rs index dcef81cf40f75e56866d93c1de9decd4abd2356d..6680cd248500fb6a910f18e3c992541e434f58fc 100644 --- a/tests/tsan.rs +++ b/tests/tsan.rs @@ -1,10 +1,12 @@ #![deny(warnings)] extern crate heapless; +extern crate scoped_threadpool; use std::thread; use heapless::RingBuffer; +use scoped_threadpool::Pool; #[test] fn once() { @@ -48,3 +50,26 @@ fn twice() { c.dequeue().unwrap(); }); } + +#[test] +fn scoped() { + let mut rb: RingBuffer<i32, [i32; 4]> = RingBuffer::new(); + + rb.enqueue(0).unwrap(); + + { + let (mut p, mut c) = rb.split(); + + Pool::new(2).scoped(move |scope| { + scope.execute(move || { + p.enqueue(1).unwrap(); + }); + + scope.execute(move || { + c.dequeue().unwrap(); + }); + }); + } + + rb.dequeue().unwrap(); +}