From 30ea33c349f44c1903ae8302341f7aa37614dddb Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Thu, 9 Nov 2017 02:27:01 +0100
Subject: [PATCH] relax the lifetime constraint of RingBuffer.split

also

- add a "`split` freezes the ring buffer" compile fail test
- hide compile-fail doc tests
- add scoped threads tests
---
 Cargo.toml              |  3 ++
 blacklist.txt           |  5 ++-
 src/cfail.rs            | 80 +++++++++++++++++++++++++++++++++++++++++
 src/lib.rs              | 71 ++----------------------------------
 src/ring_buffer/spsc.rs | 20 ++++++-----
 tests/tsan.rs           | 25 +++++++++++++
 6 files changed, 126 insertions(+), 78 deletions(-)
 create mode 100644 src/cfail.rs

diff --git a/Cargo.toml b/Cargo.toml
index 13b4ab7..983d302 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 54defd9..bf051e5 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 0000000..50c3a92
--- /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 e8f83cb..f36ef00 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 4c93f42..488c07a 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 dcef81c..6680cd2 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();
+}
-- 
GitLab