From f34934dbc7667140e49c88c36ca5832f48856ea8 Mon Sep 17 00:00:00 2001 From: Teddy DeRego Date: Sat, 25 Jul 2020 10:32:58 -0700 Subject: [PATCH 1/8] Split allocation-agnostic code into 'common' module. --- core/src/bbbuffer.rs | 650 ++--------------------------------- core/src/common.rs | 792 +++++++++++++++++++++++++++++++++++++++++++ core/src/framed.rs | 4 +- core/src/lib.rs | 2 + 4 files changed, 815 insertions(+), 633 deletions(-) create mode 100644 core/src/common.rs diff --git a/core/src/bbbuffer.rs b/core/src/bbbuffer.rs index ef04f28..ba7408d 100644 --- a/core/src/bbbuffer.rs +++ b/core/src/bbbuffer.rs @@ -1,20 +1,14 @@ +pub use crate::common::{ConstBBBuffer, GrantR, GrantW}; use crate::{ + common::{self, atomic}, framed::{FrameConsumer, FrameProducer}, Error, Result, }; use core::{ - cell::UnsafeCell, - cmp::min, marker::PhantomData, - mem::{forget, transmute, MaybeUninit}, - ops::{Deref, DerefMut}, ptr::NonNull, result::Result as CoreResult, - slice::from_raw_parts_mut, - sync::atomic::{ - AtomicBool, AtomicUsize, - Ordering::{AcqRel, Acquire, Release}, - }, + sync::atomic::Ordering::{AcqRel, Acquire, Release}, }; pub use generic_array::typenum::consts; use generic_array::{ArrayLength, GenericArray}; @@ -26,8 +20,6 @@ pub struct BBBuffer>( #[doc(hidden)] pub ConstBBBuffer>, ); -unsafe impl Sync for ConstBBBuffer {} - impl<'a, N> BBBuffer where N: ArrayLength, @@ -80,11 +72,15 @@ where Ok(( Producer { - bbq: nn1, + inner: common::Producer { + bbq: nn1, + }, pd: PhantomData, }, Consumer { - bbq: nn2, + inner: common::Consumer { + bbq: nn2, + }, pd: PhantomData, }, )) @@ -151,8 +147,8 @@ where // can assume the buffer has been split, because // Are these our producers and consumers? - let our_prod = prod.bbq.as_ptr() as *const Self == self; - let our_cons = cons.bbq.as_ptr() as *const Self == self; + let our_prod = prod.inner.bbq.as_ptr() as *const Self == self; + let our_cons = cons.inner.bbq.as_ptr() as *const Self == self; if !(our_prod && our_cons) { // Can't release, not our producer and consumer @@ -203,100 +199,6 @@ where } } -/// `const-fn` version BBBuffer -/// -/// NOTE: This is only necessary to use when creating a `BBBuffer` at static -/// scope, and is generally never used directly. This process is necessary to -/// work around current limitations in `const fn`, and will be replaced in -/// the future. -pub struct ConstBBBuffer { - buf: UnsafeCell>, - - /// Where the next byte will be written - write: AtomicUsize, - - /// Where the next byte will be read from - read: AtomicUsize, - - /// Used in the inverted case to mark the end of the - /// readable streak. Otherwise will == sizeof::(). - /// Writer is responsible for placing this at the correct - /// place when entering an inverted condition, and Reader - /// is responsible for moving it back to sizeof::() - /// when exiting the inverted condition - last: AtomicUsize, - - /// Used by the Writer to remember what bytes are currently - /// allowed to be written to, but are not yet ready to be - /// read from - reserve: AtomicUsize, - - /// Is there an active read grant? - read_in_progress: AtomicBool, - - /// Is there an active write grant? - write_in_progress: AtomicBool, - - /// Have we already split? - already_split: AtomicBool, -} - -impl ConstBBBuffer { - /// Create a new constant inner portion of a `BBBuffer`. - /// - /// NOTE: This is only necessary to use when creating a `BBBuffer` at static - /// scope, and is generally never used directly. This process is necessary to - /// work around current limitations in `const fn`, and will be replaced in - /// the future. - /// - /// ```rust,no_run - /// use bbqueue::{BBBuffer, ConstBBBuffer, consts::*}; - /// - /// static BUF: BBBuffer = BBBuffer( ConstBBBuffer::new() ); - /// - /// fn main() { - /// let (prod, cons) = BUF.try_split().unwrap(); - /// } - /// ``` - pub const fn new() -> Self { - Self { - // This will not be initialized until we split the buffer - buf: UnsafeCell::new(MaybeUninit::uninit()), - - /// Owned by the writer - write: AtomicUsize::new(0), - - /// Owned by the reader - read: AtomicUsize::new(0), - - /// Cooperatively owned - /// - /// NOTE: This should generally be initialized as size_of::(), however - /// this would prevent the structure from being entirely zero-initialized, - /// and can cause the .data section to be much larger than necessary. By - /// forcing the `last` pointer to be zero initially, we place the structure - /// in an "inverted" condition, which will be resolved on the first commited - /// bytes that are written to the structure. - /// - /// When read == last == write, no bytes will be allowed to be read (good), but - /// write grants can be given out (also good). - last: AtomicUsize::new(0), - - /// Owned by the Writer, "private" - reserve: AtomicUsize::new(0), - - /// Owned by the Reader, "private" - read_in_progress: AtomicBool::new(false), - - /// Owned by the Writer, "private" - write_in_progress: AtomicBool::new(false), - - /// We haven't split at the start - already_split: AtomicBool::new(false), - } - } -} - /// `Producer` is the primary interface for pushing data into a `BBBuffer`. /// There are various methods for obtaining a grant to write to the buffer, with /// different potential tradeoffs. As all grants are required to be a contiguous @@ -325,7 +227,7 @@ pub struct Producer<'a, N> where N: ArrayLength, { - bbq: NonNull>, + inner: common::Producer, pd: PhantomData<&'a ()>, } @@ -367,63 +269,8 @@ where /// # bbqtest(); /// # } /// ``` - pub fn grant_exact(&mut self, sz: usize) -> Result> { - let inner = unsafe { &self.bbq.as_ref().0 }; - - if atomic::swap(&inner.write_in_progress, true, AcqRel) { - return Err(Error::GrantInProgress); - } - - // Writer component. Must never write to `read`, - // be careful writing to `load` - let write = inner.write.load(Acquire); - let read = inner.read.load(Acquire); - let max = N::to_usize(); - let already_inverted = write < read; - - let start = if already_inverted { - if (write + sz) < read { - // Inverted, room is still available - write - } else { - // Inverted, no room is available - inner.write_in_progress.store(false, Release); - return Err(Error::InsufficientSize); - } - } else { - if write + sz <= max { - // Non inverted condition - write - } else { - // Not inverted, but need to go inverted - - // NOTE: We check sz < read, NOT <=, because - // write must never == read in an inverted condition, since - // we will then not be able to tell if we are inverted or not - if sz < read { - // Invertible situation - 0 - } else { - // Not invertible, no space - inner.write_in_progress.store(false, Release); - return Err(Error::InsufficientSize); - } - } - }; - - // Safe write, only viewed by this task - inner.reserve.store(start + sz, Release); - - // This is sound, as UnsafeCell, MaybeUninit, and GenericArray - // are all `#[repr(Transparent)] - let start_of_buf_ptr = inner.buf.get().cast::(); - let grant_slice = - unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(start as isize), sz) }; - - Ok(GrantW { - buf: grant_slice, - bbq: self.bbq, - }) + pub fn grant_exact(&'a mut self, sz: usize) -> Result> { + self.inner.grant_exact(sz) } /// Request a writable, contiguous section of memory of up to @@ -464,68 +311,8 @@ where /// # bbqtest(); /// # } /// ``` - pub fn grant_max_remaining(&mut self, mut sz: usize) -> Result> { - let inner = unsafe { &self.bbq.as_ref().0 }; - - if atomic::swap(&inner.write_in_progress, true, AcqRel) { - return Err(Error::GrantInProgress); - } - - // Writer component. Must never write to `read`, - // be careful writing to `load` - let write = inner.write.load(Acquire); - let read = inner.read.load(Acquire); - let max = N::to_usize(); - - let already_inverted = write < read; - - let start = if already_inverted { - // In inverted case, read is always > write - let remain = read - write - 1; - - if remain != 0 { - sz = min(remain, sz); - write - } else { - // Inverted, no room is available - inner.write_in_progress.store(false, Release); - return Err(Error::InsufficientSize); - } - } else { - if write != max { - // Some (or all) room remaining in un-inverted case - sz = min(max - write, sz); - write - } else { - // Not inverted, but need to go inverted - - // NOTE: We check read > 1, NOT read >= 1, because - // write must never == read in an inverted condition, since - // we will then not be able to tell if we are inverted or not - if read > 1 { - sz = min(read - 1, sz); - 0 - } else { - // Not invertible, no space - inner.write_in_progress.store(false, Release); - return Err(Error::InsufficientSize); - } - } - }; - - // Safe write, only viewed by this task - inner.reserve.store(start + sz, Release); - - // This is sound, as UnsafeCell, MaybeUninit, and GenericArray - // are all `#[repr(Transparent)] - let start_of_buf_ptr = inner.buf.get().cast::(); - let grant_slice = - unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(start as isize), sz) }; - - Ok(GrantW { - buf: grant_slice, - bbq: self.bbq, - }) + pub fn grant_max_remaining(&'a mut self, sz: usize) -> Result> { + self.inner.grant_max_remaining(sz) } } @@ -534,7 +321,7 @@ pub struct Consumer<'a, N> where N: ArrayLength, { - bbq: NonNull>, + inner: common::Consumer, pd: PhantomData<&'a ()>, } @@ -574,53 +361,8 @@ where /// # bbqtest(); /// # } /// ``` - pub fn read(&mut self) -> Result> { - let inner = unsafe { &self.bbq.as_ref().0 }; - - if atomic::swap(&inner.read_in_progress, true, AcqRel) { - return Err(Error::GrantInProgress); - } - - let write = inner.write.load(Acquire); - let last = inner.last.load(Acquire); - let mut read = inner.read.load(Acquire); - - // Resolve the inverted case or end of read - if (read == last) && (write < read) { - read = 0; - // This has some room for error, the other thread reads this - // Impact to Grant: - // Grant checks if read < write to see if inverted. If not inverted, but - // no space left, Grant will initiate an inversion, but will not trigger it - // Impact to Commit: - // Commit does not check read, but if Grant has started an inversion, - // grant could move Last to the prior write position - // MOVING READ BACKWARDS! - inner.read.store(0, Release); - } - - let sz = if write < read { - // Inverted, only believe last - last - } else { - // Not inverted, only believe write - write - } - read; - - if sz == 0 { - inner.read_in_progress.store(false, Release); - return Err(Error::InsufficientSize); - } - - // This is sound, as UnsafeCell, MaybeUninit, and GenericArray - // are all `#[repr(Transparent)] - let start_of_buf_ptr = inner.buf.get().cast::(); - let grant_slice = unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(read as isize), sz) }; - - Ok(GrantR { - buf: grant_slice, - bbq: self.bbq, - }) + pub fn read(&'a mut self) -> Result> { + self.inner.read() } } @@ -681,357 +423,3 @@ where } } -/// A structure representing a contiguous region of memory that -/// may be written to, and potentially "committed" to the queue. -/// -/// NOTE: If the grant is dropped without explicitly commiting -/// the contents, then no bytes will be comitted for writing. -/// If the `thumbv6` feature is selected, dropping the grant -/// without committing it takes a short critical section, -#[derive(Debug, PartialEq)] -pub struct GrantW<'a, N> -where - N: ArrayLength, -{ - pub(crate) buf: &'a mut [u8], - bbq: NonNull>, -} - -unsafe impl<'a, N> Send for GrantW<'a, N> where N: ArrayLength {} - -/// A structure representing a contiguous region of memory that -/// may be read from, and potentially "released" (or cleared) -/// from the queue -/// -/// NOTE: If the grant is dropped without explicitly releasing -/// the contents, then no bytes will be released as read. -/// If the `thumbv6` feature is selected, dropping the grant -/// without releasing it takes a short critical section, -#[derive(Debug, PartialEq)] -pub struct GrantR<'a, N> -where - N: ArrayLength, -{ - pub(crate) buf: &'a mut [u8], - bbq: NonNull>, -} - -unsafe impl<'a, N> Send for GrantR<'a, N> where N: ArrayLength {} - -impl<'a, N> GrantW<'a, N> -where - N: ArrayLength, -{ - /// Finalizes a writable grant given by `grant()` or `grant_max()`. - /// This makes the data available to be read via `read()`. This consumes - /// the grant. - /// - /// If `used` is larger than the given grant, the maximum amount will - /// be commited - /// - /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical - /// section while committing. - pub fn commit(mut self, used: usize) { - self.commit_inner(used); - forget(self); - } - - /// Obtain access to the inner buffer for writing - /// - /// ```rust - /// # // bbqueue test shim! - /// # fn bbqtest() { - /// use bbqueue::{BBBuffer, consts::*}; - /// - /// // Create and split a new buffer of 6 elements - /// let buffer: BBBuffer = BBBuffer::new(); - /// let (mut prod, mut cons) = buffer.try_split().unwrap(); - /// - /// // Successfully obtain and commit a grant of four bytes - /// let mut grant = prod.grant_max_remaining(4).unwrap(); - /// grant.buf().copy_from_slice(&[1, 2, 3, 4]); - /// grant.commit(4); - /// # // bbqueue test shim! - /// # } - /// # - /// # fn main() { - /// # #[cfg(not(feature = "thumbv6"))] - /// # bbqtest(); - /// # } - /// ``` - pub fn buf(&mut self) -> &mut [u8] { - self.buf - } - - /// Sometimes, it's not possible for the lifetimes to check out. For example, - /// if you need to hand this buffer to a function that expects to receive a - /// `&'static mut [u8]`, it is not possible for the inner reference to outlive the - /// grant itself. - /// - /// You MUST guarantee that in no cases, the reference that is returned here outlives - /// the grant itself. Once the grant has been released, referencing the data contained - /// WILL cause undefined behavior. - /// - /// Additionally, you must ensure that a separate reference to this data is not created - /// to this data, e.g. using `DerefMut` or the `buf()` method of this grant. - pub unsafe fn as_static_mut_buf(&mut self) -> &'static mut [u8] { - transmute::<&mut [u8], &'static mut [u8]>(self.buf) - } - - #[inline(always)] - fn commit_inner(&mut self, used: usize) { - let inner = unsafe { &self.bbq.as_ref().0 }; - - // Writer component. Must never write to READ, - // be careful writing to LAST - - // Saturate the grant commit - let len = self.buf.len(); - let used = min(len, used); - - let write = inner.write.load(Acquire); - atomic::fetch_sub(&inner.reserve, len - used, AcqRel); - - let max = N::to_usize(); - let last = inner.last.load(Acquire); - let new_write = inner.reserve.load(Acquire); - - if (new_write < write) && (write != max) { - // We have already wrapped, but we are skipping some bytes at the end of the ring. - // Mark `last` where the write pointer used to be to hold the line here - inner.last.store(write, Release); - } else if new_write > last { - // We're about to pass the last pointer, which was previously the artificial - // end of the ring. Now that we've passed it, we can "unlock" the section - // that was previously skipped. - // - // Since new_write is strictly larger than last, it is safe to move this as - // the other thread will still be halted by the (about to be updated) write - // value - inner.last.store(max, Release); - } - // else: If new_write == last, either: - // * last == max, so no need to write, OR - // * If we write in the end chunk again, we'll update last to max next time - // * If we write to the start chunk in a wrap, we'll update last when we - // move write backwards - - // Write must be updated AFTER last, otherwise read could think it was - // time to invert early! - inner.write.store(new_write, Release); - - // Allow subsequent grants - inner.write_in_progress.store(false, Release); - } -} - -impl<'a, N> GrantR<'a, N> -where - N: ArrayLength, -{ - /// Release a sequence of bytes from the buffer, allowing the space - /// to be used by later writes. This consumes the grant. - /// - /// If `used` is larger than the given grant, the full grant will - /// be released. - /// - /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical - /// section while releasing. - pub fn release(mut self, used: usize) { - // Saturate the grant release - let used = min(self.buf.len(), used); - - self.release_inner(used); - forget(self); - } - - pub(crate) fn shrink(&mut self, len: usize) { - let mut new_buf: &mut [u8] = &mut []; - core::mem::swap(&mut self.buf, &mut new_buf); - let (new, _) = new_buf.split_at_mut(len); - self.buf = new; - } - - /// Obtain access to the inner buffer for reading - /// - /// ``` - /// # // bbqueue test shim! - /// # fn bbqtest() { - /// use bbqueue::{BBBuffer, consts::*}; - /// - /// // Create and split a new buffer of 6 elements - /// let buffer: BBBuffer = BBBuffer::new(); - /// let (mut prod, mut cons) = buffer.try_split().unwrap(); - /// - /// // Successfully obtain and commit a grant of four bytes - /// let mut grant = prod.grant_max_remaining(4).unwrap(); - /// grant.buf().copy_from_slice(&[1, 2, 3, 4]); - /// grant.commit(4); - /// - /// // Obtain a read grant, and copy to a buffer - /// let mut grant = cons.read().unwrap(); - /// let mut buf = [0u8; 4]; - /// buf.copy_from_slice(grant.buf()); - /// assert_eq!(&buf, &[1, 2, 3, 4]); - /// # // bbqueue test shim! - /// # } - /// # - /// # fn main() { - /// # #[cfg(not(feature = "thumbv6"))] - /// # bbqtest(); - /// # } - /// ``` - pub fn buf(&self) -> &[u8] { - self.buf - } - - /// Obtain mutable access to the read grant - /// - /// This is useful if you are performing in-place operations - /// on an incoming packet, such as decryption - pub fn buf_mut(&mut self) -> &mut [u8] { - self.buf - } - - /// Sometimes, it's not possible for the lifetimes to check out. For example, - /// if you need to hand this buffer to a function that expects to receive a - /// `&'static [u8]`, it is not possible for the inner reference to outlive the - /// grant itself. - /// - /// You MUST guarantee that in no cases, the reference that is returned here outlives - /// the grant itself. Once the grant has been released, referencing the data contained - /// WILL cause undefined behavior. - /// - /// Additionally, you must ensure that a separate reference to this data is not created - /// to this data, e.g. using `Deref` or the `buf()` method of this grant. - pub unsafe fn as_static_buf(&self) -> &'static [u8] { - transmute::<&[u8], &'static [u8]>(self.buf) - } - - #[inline(always)] - pub(crate) fn release_inner(&mut self, used: usize) { - let inner = unsafe { &self.bbq.as_ref().0 }; - - // This should always be checked by the public interfaces - debug_assert!(used <= self.buf.len()); - - // This should be fine, purely incrementing - let _ = atomic::fetch_add(&inner.read, used, Release); - - inner.read_in_progress.store(false, Release); - } -} - -impl<'a, N> Drop for GrantW<'a, N> -where - N: ArrayLength, -{ - fn drop(&mut self) { - self.commit_inner(0) - } -} - -impl<'a, N> Drop for GrantR<'a, N> -where - N: ArrayLength, -{ - fn drop(&mut self) { - self.release_inner(0) - } -} - -impl<'a, N> Deref for GrantW<'a, N> -where - N: ArrayLength, -{ - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - self.buf - } -} - -impl<'a, N> DerefMut for GrantW<'a, N> -where - N: ArrayLength, -{ - fn deref_mut(&mut self) -> &mut [u8] { - self.buf - } -} - -impl<'a, N> Deref for GrantR<'a, N> -where - N: ArrayLength, -{ - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - self.buf - } -} - -impl<'a, N> DerefMut for GrantR<'a, N> -where - N: ArrayLength, -{ - fn deref_mut(&mut self) -> &mut [u8] { - self.buf - } -} - -#[cfg(feature = "thumbv6")] -mod atomic { - use core::sync::atomic::{ - AtomicBool, AtomicUsize, - Ordering::{self, Acquire, Release}, - }; - use cortex_m::interrupt::free; - - #[inline(always)] - pub fn fetch_add(atomic: &AtomicUsize, val: usize, _order: Ordering) -> usize { - free(|_| { - let prev = atomic.load(Acquire); - atomic.store(prev.wrapping_add(val), Release); - prev - }) - } - - #[inline(always)] - pub fn fetch_sub(atomic: &AtomicUsize, val: usize, _order: Ordering) -> usize { - free(|_| { - let prev = atomic.load(Acquire); - atomic.store(prev.wrapping_sub(val), Release); - prev - }) - } - - #[inline(always)] - pub fn swap(atomic: &AtomicBool, val: bool, _order: Ordering) -> bool { - free(|_| { - let prev = atomic.load(Acquire); - atomic.store(val, Release); - prev - }) - } -} - -#[cfg(not(feature = "thumbv6"))] -mod atomic { - use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; - - #[inline(always)] - pub fn fetch_add(atomic: &AtomicUsize, val: usize, order: Ordering) -> usize { - atomic.fetch_add(val, order) - } - - #[inline(always)] - pub fn fetch_sub(atomic: &AtomicUsize, val: usize, order: Ordering) -> usize { - atomic.fetch_sub(val, order) - } - - #[inline(always)] - pub fn swap(atomic: &AtomicBool, val: bool, order: Ordering) -> bool { - atomic.swap(val, order) - } -} diff --git a/core/src/common.rs b/core/src/common.rs new file mode 100644 index 0000000..9c533f4 --- /dev/null +++ b/core/src/common.rs @@ -0,0 +1,792 @@ +use crate::{ + Error, Result, +}; +use core::{ + cell::UnsafeCell, + cmp::min, + mem::{forget, transmute, MaybeUninit}, + ops::{Deref, DerefMut}, + ptr::NonNull, + slice::from_raw_parts_mut, + sync::atomic::{ + AtomicBool, AtomicUsize, + Ordering::{AcqRel, Acquire, Release}, + }, +}; +use generic_array::ArrayLength; + +/// `const-fn` version BBBuffer +/// +/// NOTE: This is only necessary to use when creating a `BBBuffer` at static +/// scope, and is generally never used directly. This process is necessary to +/// work around current limitations in `const fn`, and will be replaced in +/// the future. +pub struct ConstBBBuffer { + pub(crate) buf: UnsafeCell>, + + /// Where the next byte will be written + pub(crate) write: AtomicUsize, + + /// Where the next byte will be read from + pub(crate) read: AtomicUsize, + + /// Used in the inverted case to mark the end of the + /// readable streak. Otherwise will == sizeof::(). + /// Writer is responsible for placing this at the correct + /// place when entering an inverted condition, and Reader + /// is responsible for moving it back to sizeof::() + /// when exiting the inverted condition + pub(crate) last: AtomicUsize, + + /// Used by the Writer to remember what bytes are currently + /// allowed to be written to, but are not yet ready to be + /// read from + pub(crate) reserve: AtomicUsize, + + /// Is there an active read grant? + pub(crate) read_in_progress: AtomicBool, + + /// Is there an active write grant? + pub(crate) write_in_progress: AtomicBool, + + /// Have we already split? + pub(crate) already_split: AtomicBool, +} + +impl ConstBBBuffer { + /// Create a new constant inner portion of a `BBBuffer`. + /// + /// NOTE: This is only necessary to use when creating a `BBBuffer` at static + /// scope, and is generally never used directly. This process is necessary to + /// work around current limitations in `const fn`, and will be replaced in + /// the future. + /// + /// ```rust,no_run + /// use bbqueue::{BBBuffer, ConstBBBuffer, consts::*}; + /// + /// static BUF: BBBuffer = BBBuffer( ConstBBBuffer::new() ); + /// + /// fn main() { + /// let (prod, cons) = BUF.try_split().unwrap(); + /// } + /// ``` + pub const fn new() -> Self { + Self { + // This will not be initialized until we split the buffer + buf: UnsafeCell::new(MaybeUninit::uninit()), + + /// Owned by the writer + write: AtomicUsize::new(0), + + /// Owned by the reader + read: AtomicUsize::new(0), + + /// Cooperatively owned + /// + /// NOTE: This should generally be initialized as size_of::(), however + /// this would prevent the structure from being entirely zero-initialized, + /// and can cause the .data section to be much larger than necessary. By + /// forcing the `last` pointer to be zero initially, we place the structure + /// in an "inverted" condition, which will be resolved on the first commited + /// bytes that are written to the structure. + /// + /// When read == last == write, no bytes will be allowed to be read (good), but + /// write grants can be given out (also good). + last: AtomicUsize::new(0), + + /// Owned by the Writer, "private" + reserve: AtomicUsize::new(0), + + /// Owned by the Reader, "private" + read_in_progress: AtomicBool::new(false), + + /// Owned by the Writer, "private" + write_in_progress: AtomicBool::new(false), + + /// We haven't split at the start + already_split: AtomicBool::new(false), + } + } +} + +unsafe impl Sync for ConstBBBuffer {} + +/// `Producer` is the primary interface for pushing data into a `BBBuffer`. +/// There are various methods for obtaining a grant to write to the buffer, with +/// different potential tradeoffs. As all grants are required to be a contiguous +/// range of data, different strategies are sometimes useful when making the decision +/// between maximizing usage of the buffer, and ensuring a given grant is successful. +/// +/// As a short summary of currently possible grants: +/// +/// * `grant_exact(N)` +/// * User will receive a grant `sz == N` (or receive an error) +/// * This may cause a wraparound if a grant of size N is not available +/// at the end of the ring. +/// * If this grant caused a wraparound, the bytes that were "skipped" at the +/// end of the ring will not be available until the reader reaches them, +/// regardless of whether the grant commited any data or not. +/// * Maximum possible waste due to skipping: `N - 1` bytes +/// * `grant_max_remaining(N)` +/// * User will receive a grant `0 < sz <= N` (or receive an error) +/// * This will only cause a wrap to the beginning of the ring if exactly +/// zero bytes are available at the end of the ring. +/// * Maximum possible waste due to skipping: 0 bytes +/// +/// See [this github issue](https://github.com/jamesmunns/bbqueue/issues/38) for a +/// discussion of grant methods that could be added in the future. +pub struct Producer +where + N: ArrayLength, +{ + pub(crate) bbq: NonNull>, +} + +unsafe impl Send for Producer where N: ArrayLength {} + +impl Producer +where + N: ArrayLength, +{ + /// Request a writable, contiguous section of memory of exactly + /// `sz` bytes. If the buffer size requested is not available, + /// an error will be returned. + /// + /// This method may cause the buffer to wrap around early if the + /// requested space is not available at the end of the buffer, but + /// is available at the beginning + /// + /// ```rust + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::{BBBuffer, consts::*}; + /// + /// // Create and split a new buffer of 6 elements + /// let buffer: BBBuffer = BBBuffer::new(); + /// let (mut prod, cons) = buffer.try_split().unwrap(); + /// + /// // Successfully obtain and commit a grant of four bytes + /// let mut grant = prod.grant_exact(4).unwrap(); + /// assert_eq!(grant.buf().len(), 4); + /// grant.commit(4); + /// + /// // Try to obtain a grant of three bytes + /// assert!(prod.grant_exact(3).is_err()); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn grant_exact<'a>(&'a mut self, sz: usize) -> Result> { + let inner = unsafe { &self.bbq.as_ref() }; + + if atomic::swap(&inner.write_in_progress, true, AcqRel) { + return Err(Error::GrantInProgress); + } + + // Writer component. Must never write to `read`, + // be careful writing to `load` + let write = inner.write.load(Acquire); + let read = inner.read.load(Acquire); + let max = N::to_usize(); + let already_inverted = write < read; + + let start = if already_inverted { + if (write + sz) < read { + // Inverted, room is still available + write + } else { + // Inverted, no room is available + inner.write_in_progress.store(false, Release); + return Err(Error::InsufficientSize); + } + } else { + if write + sz <= max { + // Non inverted condition + write + } else { + // Not inverted, but need to go inverted + + // NOTE: We check sz < read, NOT <=, because + // write must never == read in an inverted condition, since + // we will then not be able to tell if we are inverted or not + if sz < read { + // Invertible situation + 0 + } else { + // Not invertible, no space + inner.write_in_progress.store(false, Release); + return Err(Error::InsufficientSize); + } + } + }; + + // Safe write, only viewed by this task + inner.reserve.store(start + sz, Release); + + // This is sound, as UnsafeCell, MaybeUninit, and GenericArray + // are all `#[repr(Transparent)] + let start_of_buf_ptr = inner.buf.get().cast::(); + let grant_slice = + unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(start as isize), sz) }; + + Ok(GrantW { + buf: grant_slice, + bbq: self.bbq, + }) + } + + /// Request a writable, contiguous section of memory of up to + /// `sz` bytes. If a buffer of size `sz` is not available without + /// wrapping, but some space (0 < available < sz) is available without + /// wrapping, then a grant will be given for the remaining size at the + /// end of the buffer. If no space is available for writing, an error + /// will be returned. + /// + /// ``` + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::{BBBuffer, consts::*}; + /// + /// // Create and split a new buffer of 6 elements + /// let buffer: BBBuffer = BBBuffer::new(); + /// let (mut prod, mut cons) = buffer.try_split().unwrap(); + /// + /// // Successfully obtain and commit a grant of four bytes + /// let mut grant = prod.grant_max_remaining(4).unwrap(); + /// assert_eq!(grant.buf().len(), 4); + /// grant.commit(4); + /// + /// // Release the four initial commited bytes + /// let mut grant = cons.read().unwrap(); + /// assert_eq!(grant.buf().len(), 4); + /// grant.release(4); + /// + /// // Try to obtain a grant of three bytes, get two bytes + /// let mut grant = prod.grant_max_remaining(3).unwrap(); + /// assert_eq!(grant.buf().len(), 2); + /// grant.commit(2); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn grant_max_remaining<'a>(&'a mut self, mut sz: usize) -> Result> { + let inner = unsafe { &self.bbq.as_ref() }; + + if atomic::swap(&inner.write_in_progress, true, AcqRel) { + return Err(Error::GrantInProgress); + } + + // Writer component. Must never write to `read`, + // be careful writing to `load` + let write = inner.write.load(Acquire); + let read = inner.read.load(Acquire); + let max = N::to_usize(); + + let already_inverted = write < read; + + let start = if already_inverted { + // In inverted case, read is always > write + let remain = read - write - 1; + + if remain != 0 { + sz = min(remain, sz); + write + } else { + // Inverted, no room is available + inner.write_in_progress.store(false, Release); + return Err(Error::InsufficientSize); + } + } else { + if write != max { + // Some (or all) room remaining in un-inverted case + sz = min(max - write, sz); + write + } else { + // Not inverted, but need to go inverted + + // NOTE: We check read > 1, NOT read >= 1, because + // write must never == read in an inverted condition, since + // we will then not be able to tell if we are inverted or not + if read > 1 { + sz = min(read - 1, sz); + 0 + } else { + // Not invertible, no space + inner.write_in_progress.store(false, Release); + return Err(Error::InsufficientSize); + } + } + }; + + // Safe write, only viewed by this task + inner.reserve.store(start + sz, Release); + + // This is sound, as UnsafeCell, MaybeUninit, and GenericArray + // are all `#[repr(Transparent)] + let start_of_buf_ptr = inner.buf.get().cast::(); + let grant_slice = + unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(start as isize), sz) }; + + Ok(GrantW { + buf: grant_slice, + bbq: self.bbq, + }) + } +} + +/// `Consumer` is the primary interface for reading data from a `BBBuffer`. +pub struct Consumer +where + N: ArrayLength, +{ + pub(crate) bbq: NonNull>, +} + +unsafe impl Send for Consumer where N: ArrayLength {} + +impl Consumer +where + N: ArrayLength, +{ + /// Obtains a contiguous slice of committed bytes. This slice may not + /// contain ALL available bytes, if the writer has wrapped around. The + /// remaining bytes will be available after all readable bytes are + /// released + /// + /// ```rust + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::{BBBuffer, consts::*}; + /// + /// // Create and split a new buffer of 6 elements + /// let buffer: BBBuffer = BBBuffer::new(); + /// let (mut prod, mut cons) = buffer.try_split().unwrap(); + /// + /// // Successfully obtain and commit a grant of four bytes + /// let mut grant = prod.grant_max_remaining(4).unwrap(); + /// assert_eq!(grant.buf().len(), 4); + /// grant.commit(4); + /// + /// // Obtain a read grant + /// let mut grant = cons.read().unwrap(); + /// assert_eq!(grant.buf().len(), 4); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn read<'a>(&'a mut self) -> Result> { + let inner = unsafe { &self.bbq.as_ref() }; + + if atomic::swap(&inner.read_in_progress, true, AcqRel) { + return Err(Error::GrantInProgress); + } + + let write = inner.write.load(Acquire); + let last = inner.last.load(Acquire); + let mut read = inner.read.load(Acquire); + + // Resolve the inverted case or end of read + if (read == last) && (write < read) { + read = 0; + // This has some room for error, the other thread reads this + // Impact to Grant: + // Grant checks if read < write to see if inverted. If not inverted, but + // no space left, Grant will initiate an inversion, but will not trigger it + // Impact to Commit: + // Commit does not check read, but if Grant has started an inversion, + // grant could move Last to the prior write position + // MOVING READ BACKWARDS! + inner.read.store(0, Release); + } + + let sz = if write < read { + // Inverted, only believe last + last + } else { + // Not inverted, only believe write + write + } - read; + + if sz == 0 { + inner.read_in_progress.store(false, Release); + return Err(Error::InsufficientSize); + } + + // This is sound, as UnsafeCell, MaybeUninit, and GenericArray + // are all `#[repr(Transparent)] + let start_of_buf_ptr = inner.buf.get().cast::(); + let grant_slice = unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(read as isize), sz) }; + + Ok(GrantR { + buf: grant_slice, + bbq: self.bbq, + }) + } +} + +/// A structure representing a contiguous region of memory that +/// may be written to, and potentially "committed" to the queue. +/// +/// NOTE: If the grant is dropped without explicitly commiting +/// the contents, then no bytes will be comitted for writing. +/// If the `thumbv6` feature is selected, dropping the grant +/// without committing it takes a short critical section, +#[derive(Debug, PartialEq)] +pub struct GrantW<'a, N> +where + N: ArrayLength, +{ + pub(crate) buf: &'a mut [u8], + pub(crate) bbq: NonNull>, +} + +unsafe impl<'a, N> Send for GrantW<'a, N> where N: ArrayLength {} + +/// A structure representing a contiguous region of memory that +/// may be read from, and potentially "released" (or cleared) +/// from the queue +/// +/// NOTE: If the grant is dropped without explicitly releasing +/// the contents, then no bytes will be released as read. +/// If the `thumbv6` feature is selected, dropping the grant +/// without releasing it takes a short critical section, +#[derive(Debug, PartialEq)] +pub struct GrantR<'a, N> +where + N: ArrayLength, +{ + pub(crate) buf: &'a mut [u8], + pub(crate) bbq: NonNull>, +} + +unsafe impl<'a, N> Send for GrantR<'a, N> where N: ArrayLength {} + +impl<'a, N> GrantW<'a, N> +where + N: ArrayLength, +{ + /// Finalizes a writable grant given by `grant()` or `grant_max()`. + /// This makes the data available to be read via `read()`. This consumes + /// the grant. + /// + /// If `used` is larger than the given grant, the maximum amount will + /// be commited + /// + /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical + /// section while committing. + pub fn commit(mut self, used: usize) { + self.commit_inner(used); + forget(self); + } + + /// Obtain access to the inner buffer for writing + /// + /// ```rust + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::{BBBuffer, consts::*}; + /// + /// // Create and split a new buffer of 6 elements + /// let buffer: BBBuffer = BBBuffer::new(); + /// let (mut prod, mut cons) = buffer.try_split().unwrap(); + /// + /// // Successfully obtain and commit a grant of four bytes + /// let mut grant = prod.grant_max_remaining(4).unwrap(); + /// grant.buf().copy_from_slice(&[1, 2, 3, 4]); + /// grant.commit(4); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn buf(&mut self) -> &mut [u8] { + self.buf + } + + /// Sometimes, it's not possible for the lifetimes to check out. For example, + /// if you need to hand this buffer to a function that expects to receive a + /// `&'static mut [u8]`, it is not possible for the inner reference to outlive the + /// grant itself. + /// + /// You MUST guarantee that in no cases, the reference that is returned here outlives + /// the grant itself. Once the grant has been released, referencing the data contained + /// WILL cause undefined behavior. + /// + /// Additionally, you must ensure that a separate reference to this data is not created + /// to this data, e.g. using `DerefMut` or the `buf()` method of this grant. + pub unsafe fn as_static_mut_buf(&mut self) -> &'static mut [u8] { + transmute::<&mut [u8], &'static mut [u8]>(self.buf) + } + + #[inline(always)] + fn commit_inner(&mut self, used: usize) { + let inner = unsafe { &self.bbq.as_ref() }; + + // Writer component. Must never write to READ, + // be careful writing to LAST + + // Saturate the grant commit + let len = self.buf.len(); + let used = min(len, used); + + let write = inner.write.load(Acquire); + atomic::fetch_sub(&inner.reserve, len - used, AcqRel); + + let max = N::to_usize(); + let last = inner.last.load(Acquire); + let new_write = inner.reserve.load(Acquire); + + if (new_write < write) && (write != max) { + // We have already wrapped, but we are skipping some bytes at the end of the ring. + // Mark `last` where the write pointer used to be to hold the line here + inner.last.store(write, Release); + } else if new_write > last { + // We're about to pass the last pointer, which was previously the artificial + // end of the ring. Now that we've passed it, we can "unlock" the section + // that was previously skipped. + // + // Since new_write is strictly larger than last, it is safe to move this as + // the other thread will still be halted by the (about to be updated) write + // value + inner.last.store(max, Release); + } + // else: If new_write == last, either: + // * last == max, so no need to write, OR + // * If we write in the end chunk again, we'll update last to max next time + // * If we write to the start chunk in a wrap, we'll update last when we + // move write backwards + + // Write must be updated AFTER last, otherwise read could think it was + // time to invert early! + inner.write.store(new_write, Release); + + // Allow subsequent grants + inner.write_in_progress.store(false, Release); + } +} + +impl<'a, N> GrantR<'a, N> +where + N: ArrayLength, +{ + /// Release a sequence of bytes from the buffer, allowing the space + /// to be used by later writes. This consumes the grant. + /// + /// If `used` is larger than the given grant, the full grant will + /// be released. + /// + /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical + /// section while releasing. + pub fn release(mut self, used: usize) { + // Saturate the grant release + let used = min(self.buf.len(), used); + + self.release_inner(used); + forget(self); + } + + pub(crate) fn shrink(&mut self, len: usize) { + let mut new_buf: &mut [u8] = &mut []; + core::mem::swap(&mut self.buf, &mut new_buf); + let (new, _) = new_buf.split_at_mut(len); + self.buf = new; + } + + /// Obtain access to the inner buffer for reading + /// + /// ``` + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::{BBBuffer, consts::*}; + /// + /// // Create and split a new buffer of 6 elements + /// let buffer: BBBuffer = BBBuffer::new(); + /// let (mut prod, mut cons) = buffer.try_split().unwrap(); + /// + /// // Successfully obtain and commit a grant of four bytes + /// let mut grant = prod.grant_max_remaining(4).unwrap(); + /// grant.buf().copy_from_slice(&[1, 2, 3, 4]); + /// grant.commit(4); + /// + /// // Obtain a read grant, and copy to a buffer + /// let mut grant = cons.read().unwrap(); + /// let mut buf = [0u8; 4]; + /// buf.copy_from_slice(grant.buf()); + /// assert_eq!(&buf, &[1, 2, 3, 4]); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn buf(&self) -> &[u8] { + self.buf + } + + /// Obtain mutable access to the read grant + /// + /// This is useful if you are performing in-place operations + /// on an incoming packet, such as decryption + pub fn buf_mut(&mut self) -> &mut [u8] { + self.buf + } + + /// Sometimes, it's not possible for the lifetimes to check out. For example, + /// if you need to hand this buffer to a function that expects to receive a + /// `&'static [u8]`, it is not possible for the inner reference to outlive the + /// grant itself. + /// + /// You MUST guarantee that in no cases, the reference that is returned here outlives + /// the grant itself. Once the grant has been released, referencing the data contained + /// WILL cause undefined behavior. + /// + /// Additionally, you must ensure that a separate reference to this data is not created + /// to this data, e.g. using `Deref` or the `buf()` method of this grant. + pub unsafe fn as_static_buf(&self) -> &'static [u8] { + transmute::<&[u8], &'static [u8]>(self.buf) + } + + #[inline(always)] + pub(crate) fn release_inner(&mut self, used: usize) { + let inner = unsafe { &self.bbq.as_ref() }; + + // This should always be checked by the public interfaces + debug_assert!(used <= self.buf.len()); + + // This should be fine, purely incrementing + let _ = atomic::fetch_add(&inner.read, used, Release); + + inner.read_in_progress.store(false, Release); + } +} + +impl<'a, N> Drop for GrantW<'a, N> +where + N: ArrayLength, +{ + fn drop(&mut self) { + self.commit_inner(0) + } +} + +impl<'a, N> Drop for GrantR<'a, N> +where + N: ArrayLength, +{ + fn drop(&mut self) { + self.release_inner(0) + } +} + +impl<'a, N> Deref for GrantW<'a, N> +where + N: ArrayLength, +{ + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.buf + } +} + +impl<'a, N> DerefMut for GrantW<'a, N> +where + N: ArrayLength, +{ + fn deref_mut(&mut self) -> &mut [u8] { + self.buf + } +} + +impl<'a, N> Deref for GrantR<'a, N> +where + N: ArrayLength, +{ + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.buf + } +} + +impl<'a, N> DerefMut for GrantR<'a, N> +where + N: ArrayLength, +{ + fn deref_mut(&mut self) -> &mut [u8] { + self.buf + } +} + +#[cfg(feature = "thumbv6")] +pub mod atomic { + use core::sync::atomic::{ + AtomicBool, AtomicUsize, + Ordering::{self, Acquire, Release}, + }; + use cortex_m::interrupt::free; + + #[inline(always)] + pub fn fetch_add(atomic: &AtomicUsize, val: usize, _order: Ordering) -> usize { + free(|_| { + let prev = atomic.load(Acquire); + atomic.store(prev.wrapping_add(val), Release); + prev + }) + } + + #[inline(always)] + pub fn fetch_sub(atomic: &AtomicUsize, val: usize, _order: Ordering) -> usize { + free(|_| { + let prev = atomic.load(Acquire); + atomic.store(prev.wrapping_sub(val), Release); + prev + }) + } + + #[inline(always)] + pub fn swap(atomic: &AtomicBool, val: bool, _order: Ordering) -> bool { + free(|_| { + let prev = atomic.load(Acquire); + atomic.store(val, Release); + prev + }) + } +} + +#[cfg(not(feature = "thumbv6"))] +pub mod atomic { + use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; + + #[inline(always)] + pub fn fetch_add(atomic: &AtomicUsize, val: usize, order: Ordering) -> usize { + atomic.fetch_add(val, order) + } + + #[inline(always)] + pub fn fetch_sub(atomic: &AtomicUsize, val: usize, order: Ordering) -> usize { + atomic.fetch_sub(val, order) + } + + #[inline(always)] + pub fn swap(atomic: &AtomicBool, val: bool, order: Ordering) -> bool { + atomic.swap(val, order) + } +} diff --git a/core/src/framed.rs b/core/src/framed.rs index 0bddbf3..d0a5b18 100644 --- a/core/src/framed.rs +++ b/core/src/framed.rs @@ -99,7 +99,7 @@ where /// /// This size does not include the size of the frame header. The exact size /// of the frame can be set on `commit`. - pub fn grant(&mut self, max_sz: usize) -> Result> { + pub fn grant(&'a mut self, max_sz: usize) -> Result> { let hdr_len = encoded_len(max_sz); Ok(FrameGrantW { grant_w: self.producer.grant_exact(max_sz + hdr_len)?, @@ -121,7 +121,7 @@ where N: ArrayLength, { /// Obtain the next available frame, if any - pub fn read(&mut self) -> Option> { + pub fn read(&'a mut self) -> Option> { // Get all available bytes. We never wrap a frame around, // so if a header is available, the whole frame will be. let mut grant_r = self.consumer.read().ok()?; diff --git a/core/src/lib.rs b/core/src/lib.rs index ed7048e..c3bf1e5 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -108,6 +108,8 @@ mod bbbuffer; pub use bbbuffer::*; +mod common; + /// There are no longer separate `atomic` and `cm_mutex` modules. You can just use the types at the /// crate root. #[deprecated( From c1c14df3e469f242341130fffd6503c601be2dac Mon Sep 17 00:00:00 2001 From: Teddy DeRego Date: Sat, 25 Jul 2020 11:38:27 -0700 Subject: [PATCH 2/8] Fix tests --- core/src/bbbuffer.rs | 21 ++++++++++----------- core/src/common.rs | 40 +++++++++++++++++----------------------- core/src/lib.rs | 2 ++ 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/core/src/bbbuffer.rs b/core/src/bbbuffer.rs index ba7408d..80f994f 100644 --- a/core/src/bbbuffer.rs +++ b/core/src/bbbuffer.rs @@ -67,19 +67,18 @@ where let mu_ptr = self.0.buf.get(); (*mu_ptr).as_mut_ptr().write_bytes(0u8, 1); - let nn1 = NonNull::new_unchecked(self as *const _ as *mut _); - let nn2 = NonNull::new_unchecked(self as *const _ as *mut _); + let nn = NonNull::new_unchecked(&self.0 as *const _ as *mut _); Ok(( Producer { inner: common::Producer { - bbq: nn1, + bbq: nn, }, pd: PhantomData, }, Consumer { inner: common::Consumer { - bbq: nn2, + bbq: nn, }, pd: PhantomData, }, @@ -147,8 +146,8 @@ where // can assume the buffer has been split, because // Are these our producers and consumers? - let our_prod = prod.inner.bbq.as_ptr() as *const Self == self; - let our_cons = cons.inner.bbq.as_ptr() as *const Self == self; + let our_prod = prod.inner.bbq.as_ptr() as *const ConstBBBuffer> == &self.0; + let our_cons = cons.inner.bbq.as_ptr() as *const ConstBBBuffer> == &self.0; if !(our_prod && our_cons) { // Can't release, not our producer and consumer @@ -233,7 +232,7 @@ where unsafe impl<'a, N> Send for Producer<'a, N> where N: ArrayLength {} -impl<'a, N> Producer<'a, N> +impl<'a, N: 'a> Producer<'a, N> where N: ArrayLength, { @@ -269,7 +268,7 @@ where /// # bbqtest(); /// # } /// ``` - pub fn grant_exact(&'a mut self, sz: usize) -> Result> { + pub fn grant_exact(&mut self, sz: usize) -> Result> { self.inner.grant_exact(sz) } @@ -311,7 +310,7 @@ where /// # bbqtest(); /// # } /// ``` - pub fn grant_max_remaining(&'a mut self, sz: usize) -> Result> { + pub fn grant_max_remaining(&mut self, sz: usize) -> Result> { self.inner.grant_max_remaining(sz) } } @@ -327,7 +326,7 @@ where unsafe impl<'a, N> Send for Consumer<'a, N> where N: ArrayLength {} -impl<'a, N> Consumer<'a, N> +impl<'a, N: 'a> Consumer<'a, N> where N: ArrayLength, { @@ -361,7 +360,7 @@ where /// # bbqtest(); /// # } /// ``` - pub fn read(&'a mut self) -> Result> { + pub fn read(&mut self) -> Result> { self.inner.read() } } diff --git a/core/src/common.rs b/core/src/common.rs index 9c533f4..e0fcac6 100644 --- a/core/src/common.rs +++ b/core/src/common.rs @@ -7,13 +7,12 @@ use core::{ mem::{forget, transmute, MaybeUninit}, ops::{Deref, DerefMut}, ptr::NonNull, - slice::from_raw_parts_mut, sync::atomic::{ AtomicBool, AtomicUsize, Ordering::{AcqRel, Acquire, Release}, }, }; -use generic_array::ArrayLength; +use generic_array::{ArrayLength, GenericArray}; /// `const-fn` version BBBuffer /// @@ -139,12 +138,12 @@ pub struct Producer where N: ArrayLength, { - pub(crate) bbq: NonNull>, + pub(crate) bbq: NonNull>>, } unsafe impl Send for Producer where N: ArrayLength {} -impl Producer +impl<'a, N: 'a> Producer where N: ArrayLength, { @@ -180,7 +179,7 @@ where /// # bbqtest(); /// # } /// ``` - pub fn grant_exact<'a>(&'a mut self, sz: usize) -> Result> { + pub fn grant_exact(&mut self, sz: usize) -> Result> { let inner = unsafe { &self.bbq.as_ref() }; if atomic::swap(&inner.write_in_progress, true, AcqRel) { @@ -227,11 +226,9 @@ where // Safe write, only viewed by this task inner.reserve.store(start + sz, Release); - // This is sound, as UnsafeCell, MaybeUninit, and GenericArray - // are all `#[repr(Transparent)] - let start_of_buf_ptr = inner.buf.get().cast::(); let grant_slice = - unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(start as isize), sz) }; + &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } + .as_mut_slice()[start .. start + sz]; Ok(GrantW { buf: grant_slice, @@ -277,7 +274,7 @@ where /// # bbqtest(); /// # } /// ``` - pub fn grant_max_remaining<'a>(&'a mut self, mut sz: usize) -> Result> { + pub fn grant_max_remaining(&mut self, mut sz: usize) -> Result> { let inner = unsafe { &self.bbq.as_ref() }; if atomic::swap(&inner.write_in_progress, true, AcqRel) { @@ -329,11 +326,9 @@ where // Safe write, only viewed by this task inner.reserve.store(start + sz, Release); - // This is sound, as UnsafeCell, MaybeUninit, and GenericArray - // are all `#[repr(Transparent)] - let start_of_buf_ptr = inner.buf.get().cast::(); let grant_slice = - unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(start as isize), sz) }; + &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } + .as_mut_slice()[start .. start + sz]; Ok(GrantW { buf: grant_slice, @@ -347,12 +342,12 @@ pub struct Consumer where N: ArrayLength, { - pub(crate) bbq: NonNull>, + pub(crate) bbq: NonNull>>, } unsafe impl Send for Consumer where N: ArrayLength {} -impl Consumer +impl<'a, N: 'a> Consumer where N: ArrayLength, { @@ -386,7 +381,7 @@ where /// # bbqtest(); /// # } /// ``` - pub fn read<'a>(&'a mut self) -> Result> { + pub fn read(&mut self) -> Result> { let inner = unsafe { &self.bbq.as_ref() }; if atomic::swap(&inner.read_in_progress, true, AcqRel) { @@ -424,10 +419,9 @@ where return Err(Error::InsufficientSize); } - // This is sound, as UnsafeCell, MaybeUninit, and GenericArray - // are all `#[repr(Transparent)] - let start_of_buf_ptr = inner.buf.get().cast::(); - let grant_slice = unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(read as isize), sz) }; + let grant_slice = + &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } + .as_mut_slice()[read .. read + sz]; Ok(GrantR { buf: grant_slice, @@ -449,7 +443,7 @@ where N: ArrayLength, { pub(crate) buf: &'a mut [u8], - pub(crate) bbq: NonNull>, + pub(crate) bbq: NonNull>>, } unsafe impl<'a, N> Send for GrantW<'a, N> where N: ArrayLength {} @@ -468,7 +462,7 @@ where N: ArrayLength, { pub(crate) buf: &'a mut [u8], - pub(crate) bbq: NonNull>, + pub(crate) bbq: NonNull>>, } unsafe impl<'a, N> Send for GrantR<'a, N> where N: ArrayLength {} diff --git a/core/src/lib.rs b/core/src/lib.rs index c3bf1e5..9050ca1 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(maybe_uninit_ref)] + //! # BBQueue //! //! BBQueue, short for "BipBuffer Queue", is a Single Producer Single Consumer, From ab7c86144dd5398d73784084b292febfb9d22943 Mon Sep 17 00:00:00 2001 From: Teddy DeRego Date: Sat, 25 Jul 2020 11:54:15 -0700 Subject: [PATCH 3/8] Switch back to pointer casting for 'grant_slice' to avoid unstable maybe_uninit_ref feature. --- core/src/common.rs | 31 +++++++++++++++++++++++++------ core/src/lib.rs | 2 -- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/core/src/common.rs b/core/src/common.rs index e0fcac6..8ca6651 100644 --- a/core/src/common.rs +++ b/core/src/common.rs @@ -7,6 +7,7 @@ use core::{ mem::{forget, transmute, MaybeUninit}, ops::{Deref, DerefMut}, ptr::NonNull, + slice::from_raw_parts_mut, sync::atomic::{ AtomicBool, AtomicUsize, Ordering::{AcqRel, Acquire, Release}, @@ -226,9 +227,15 @@ where // Safe write, only viewed by this task inner.reserve.store(start + sz, Release); + // This is sound, as UnsafeCell, MaybeUninit, and GenericArray + // are all `#[repr(Transparent)] + let start_of_buf_ptr = inner.buf.get().cast::(); let grant_slice = - &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } - .as_mut_slice()[start .. start + sz]; + unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(start as isize), sz) }; + // TODO feature(maybe_uninit_ref) + //let grant_slice = + // &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } + // .as_mut_slice()[start .. start + sz]; Ok(GrantW { buf: grant_slice, @@ -326,9 +333,15 @@ where // Safe write, only viewed by this task inner.reserve.store(start + sz, Release); + // This is sound, as UnsafeCell, MaybeUninit, and GenericArray + // are all `#[repr(Transparent)] + let start_of_buf_ptr = inner.buf.get().cast::(); let grant_slice = - &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } - .as_mut_slice()[start .. start + sz]; + unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(start as isize), sz) }; + // TODO feature(maybe_uninit_ref) + //let grant_slice = + // &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } + // .as_mut_slice()[start .. start + sz]; Ok(GrantW { buf: grant_slice, @@ -419,9 +432,15 @@ where return Err(Error::InsufficientSize); } + // This is sound, as UnsafeCell, MaybeUninit, and GenericArray + // are all `#[repr(Transparent)] + let start_of_buf_ptr = inner.buf.get().cast::(); let grant_slice = - &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } - .as_mut_slice()[read .. read + sz]; + unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(read as isize), sz) }; + // TODO feature(maybe_uninit_ref) + //let grant_slice = + // &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } + // .as_mut_slice()[read .. read + sz]; Ok(GrantR { buf: grant_slice, diff --git a/core/src/lib.rs b/core/src/lib.rs index 9050ca1..c3bf1e5 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(maybe_uninit_ref)] - //! # BBQueue //! //! BBQueue, short for "BipBuffer Queue", is a Single Producer Single Consumer, From 2e6b5ec71a15efe5198868a315f0a4740d64ff80 Mon Sep 17 00:00:00 2001 From: Teddy DeRego Date: Sat, 25 Jul 2020 12:55:23 -0700 Subject: [PATCH 4/8] First pass heap-allocated flavor of BBBuffer. --- core/src/heap.rs | 362 +++++++++++++++++++++++++++++++++++++++++++++++ core/src/lib.rs | 2 + 2 files changed, 364 insertions(+) create mode 100644 core/src/heap.rs diff --git a/core/src/heap.rs b/core/src/heap.rs new file mode 100644 index 0000000..f70477a --- /dev/null +++ b/core/src/heap.rs @@ -0,0 +1,362 @@ +//! Heap-allocated flavor of BBQueue. + +pub use crate::common::{ConstBBBuffer, GrantR, GrantW}; +use crate::{ + common::{self, atomic}, + Error, Result, +}; +use std::sync::Arc; +use core::{ + ptr::NonNull, + sync::atomic::{ + AtomicBool, + Ordering::AcqRel, + }, +}; +pub use generic_array::typenum::consts; +use generic_array::{ArrayLength, GenericArray}; + +/// A backing structure for a BBQueue. Can be used to create either +/// a BBQueue or a split Producer/Consumer pair +pub struct BBBuffer>( + // Underlying data storage + #[doc(hidden)] pub Box>>, +); + +impl BBBuffer +where + N: ArrayLength, +{ + /// Attempt to split the `BBBuffer` into `Consumer` and `Producer` halves to gain access to the + /// buffer. If buffer has already been split, an error will be returned. + /// + /// NOTE: When splitting, the underlying buffer will be explicitly initialized + /// to zero. This may take a measurable amount of time, depending on the size + /// of the buffer. This is necessary to prevent undefined behavior. If the buffer + /// is placed at `static` scope within the `.bss` region, the explicit initialization + /// will be elided (as it is already performed as part of memory initialization) + /// + /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical section + /// while splitting. + /// + /// ```rust + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::consts::*; + /// use bbqueue::heap::BBBuffer; + /// + /// // Create and split a new buffer + /// let buffer: BBBuffer = BBBuffer::new(); + /// let (prod, cons) = buffer.try_split().unwrap(); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn try_split(self) -> Result<(Producer, Consumer)> { + if atomic::swap(&self.0.already_split, true, AcqRel) { + return Err(Error::AlreadySplit); + } + + unsafe { + // Explicitly zero the data to avoid undefined behavior. + // This is required, because we hand out references to the buffers, + // which mean that creating them as references is technically UB for now + let mu_ptr = self.0.buf.get(); + (*mu_ptr).as_mut_ptr().write_bytes(0u8, 1); + + let nn: NonNull<_> = Box::leak(self.0).into(); + let dealloc_on_drop = Arc::new(AtomicBool::new(false)); + + Ok(( + Producer { + inner: common::Producer { + bbq: nn, + }, + dealloc_on_drop: dealloc_on_drop.clone(), + }, + Consumer { + inner: common::Consumer { + bbq: nn, + }, + dealloc_on_drop: dealloc_on_drop.clone(), + }, + )) + } + } + + /* + /// Attempt to split the `BBBuffer` into `FrameConsumer` and `FrameProducer` halves + /// to gain access to the buffer. If buffer has already been split, an error + /// will be returned. + /// + /// NOTE: When splitting, the underlying buffer will be explicitly initialized + /// to zero. This may take a measurable amount of time, depending on the size + /// of the buffer. This is necessary to prevent undefined behavior. If the buffer + /// is placed at `static` scope within the `.bss` region, the explicit initialization + /// will be elided (as it is already performed as part of memory initialization) + /// + /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical + /// section while splitting. + pub fn try_split_framed(self) -> Result<(FrameProducer<'a, N>, FrameConsumer<'a, N>)> { + let (producer, consumer) = self.try_split()?; + Ok((FrameProducer { producer }, FrameConsumer { consumer })) + }*/ +} + +/// `Producer` is the primary interface for pushing data into a `BBBuffer`. +/// There are various methods for obtaining a grant to write to the buffer, with +/// different potential tradeoffs. As all grants are required to be a contiguous +/// range of data, different strategies are sometimes useful when making the decision +/// between maximizing usage of the buffer, and ensuring a given grant is successful. +/// +/// As a short summary of currently possible grants: +/// +/// * `grant_exact(N)` +/// * User will receive a grant `sz == N` (or receive an error) +/// * This may cause a wraparound if a grant of size N is not available +/// at the end of the ring. +/// * If this grant caused a wraparound, the bytes that were "skipped" at the +/// end of the ring will not be available until the reader reaches them, +/// regardless of whether the grant commited any data or not. +/// * Maximum possible waste due to skipping: `N - 1` bytes +/// * `grant_max_remaining(N)` +/// * User will receive a grant `0 < sz <= N` (or receive an error) +/// * This will only cause a wrap to the beginning of the ring if exactly +/// zero bytes are available at the end of the ring. +/// * Maximum possible waste due to skipping: 0 bytes +/// +/// See [this github issue](https://github.com/jamesmunns/bbqueue/issues/38) for a +/// discussion of grant methods that could be added in the future. +pub struct Producer +where + N: ArrayLength, +{ + inner: common::Producer, + dealloc_on_drop: Arc, +} + +unsafe impl Send for Producer where N: ArrayLength {} + +impl Producer +where + N: ArrayLength, +{ + /// Request a writable, contiguous section of memory of exactly + /// `sz` bytes. If the buffer size requested is not available, + /// an error will be returned. + /// + /// This method may cause the buffer to wrap around early if the + /// requested space is not available at the end of the buffer, but + /// is available at the beginning + /// + /// ```rust + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::consts::*; + /// use bbqueue::heap::BBBuffer; + /// + /// // Create and split a new buffer of 6 elements + /// let buffer: BBBuffer = BBBuffer::new(); + /// let (mut prod, cons) = buffer.try_split().unwrap(); + /// + /// // Successfully obtain and commit a grant of four bytes + /// let mut grant = prod.grant_exact(4).unwrap(); + /// assert_eq!(grant.buf().len(), 4); + /// grant.commit(4); + /// + /// // Try to obtain a grant of three bytes + /// assert!(prod.grant_exact(3).is_err()); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn grant_exact<'a>(&'a mut self, sz: usize) -> Result> { + self.inner.grant_exact(sz) + } + + /// Request a writable, contiguous section of memory of up to + /// `sz` bytes. If a buffer of size `sz` is not available without + /// wrapping, but some space (0 < available < sz) is available without + /// wrapping, then a grant will be given for the remaining size at the + /// end of the buffer. If no space is available for writing, an error + /// will be returned. + /// + /// ``` + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::consts::*; + /// use bbqueue::heap::BBBuffer; + /// + /// // Create and split a new buffer of 6 elements + /// let buffer: BBBuffer = BBBuffer::new(); + /// let (mut prod, mut cons) = buffer.try_split().unwrap(); + /// + /// // Successfully obtain and commit a grant of four bytes + /// let mut grant = prod.grant_max_remaining(4).unwrap(); + /// assert_eq!(grant.buf().len(), 4); + /// grant.commit(4); + /// + /// // Release the four initial commited bytes + /// let mut grant = cons.read().unwrap(); + /// assert_eq!(grant.buf().len(), 4); + /// grant.release(4); + /// + /// // Try to obtain a grant of three bytes, get two bytes + /// let mut grant = prod.grant_max_remaining(3).unwrap(); + /// assert_eq!(grant.buf().len(), 2); + /// grant.commit(2); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn grant_max_remaining<'a>(&'a mut self, sz: usize) -> Result> { + self.inner.grant_max_remaining(sz) + } +} + +/// `Consumer` is the primary interface for reading data from a `BBBuffer`. +pub struct Consumer +where + N: ArrayLength, +{ + inner: common::Consumer, + dealloc_on_drop: Arc, +} + +unsafe impl Send for Consumer where N: ArrayLength {} + +impl Consumer +where + N: ArrayLength, +{ + /// Obtains a contiguous slice of committed bytes. This slice may not + /// contain ALL available bytes, if the writer has wrapped around. The + /// remaining bytes will be available after all readable bytes are + /// released + /// + /// ```rust + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::consts::*; + /// use bbqueue::heap::BBBuffer; + /// + /// // Create and split a new buffer of 6 elements + /// let buffer: BBBuffer = BBBuffer::new(); + /// let (mut prod, mut cons) = buffer.try_split().unwrap(); + /// + /// // Successfully obtain and commit a grant of four bytes + /// let mut grant = prod.grant_max_remaining(4).unwrap(); + /// grant.buf().copy_from_slice(&[1, 2, 3, 4]); + /// grant.commit(4); + /// + /// // Obtain a read grant, and copy to a buffer + /// let mut grant = cons.read().unwrap(); + /// let mut buf = [0u8; 4]; + /// buf.copy_from_slice(grant.buf()); + /// assert_eq!(&buf, &[1, 2, 3, 4]); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn read<'a>(&'a mut self) -> Result> { + self.inner.read() + } +} + +impl Drop for Producer +where + N: ArrayLength, +{ + fn drop(&mut self) { + if atomic::swap(&self.dealloc_on_drop, true, AcqRel) { + unsafe { Box::from_raw(self.inner.bbq.as_ptr()); } + } + } +} + +impl Drop for Consumer +where + N: ArrayLength, +{ + fn drop(&mut self) { + if atomic::swap(&self.dealloc_on_drop, true, AcqRel) { + unsafe { Box::from_raw(self.inner.bbq.as_ptr()); } + } + } +} + +impl BBBuffer +where + N: ArrayLength, +{ + /// Returns the size of the backing storage. + /// + /// This is the maximum number of bytes that can be stored in this queue. + /// + /// ```rust + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::consts::*; + /// use bbqueue::heap::BBBuffer; + /// + /// // Create a new buffer of 6 elements + /// let buffer: BBBuffer = BBBuffer::new(); + /// assert_eq!(buffer.capacity(), 6); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn capacity(&self) -> usize { + N::to_usize() + } +} + +impl BBBuffer +where + N: ArrayLength, +{ + /// Create a new bbqueue + /// + /// NOTE: For creating a bbqueue in static context, see `ConstBBBuffer::new()`. + /// + /// ```rust + /// # // bbqueue test shim! + /// # fn bbqtest() { + /// use bbqueue::consts::*; + /// use bbqueue::heap::BBBuffer; + /// + /// // Create a new buffer of 6 elements + /// let buffer: BBBuffer = BBBuffer::new(); + /// # // bbqueue test shim! + /// # } + /// # + /// # fn main() { + /// # #[cfg(not(feature = "thumbv6"))] + /// # bbqtest(); + /// # } + /// ``` + pub fn new() -> Self { + Self(Box::new(ConstBBBuffer::new())) + } +} + diff --git a/core/src/lib.rs b/core/src/lib.rs index c3bf1e5..41010ec 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -109,6 +109,8 @@ mod bbbuffer; pub use bbbuffer::*; mod common; +#[cfg(feature = "std")] +pub mod heap; /// There are no longer separate `atomic` and `cm_mutex` modules. You can just use the types at the /// crate root. From cf24da413a7a5a942e0d30ab2bea9a685d71cb00 Mon Sep 17 00:00:00 2001 From: Teddy DeRego Date: Sat, 25 Jul 2020 14:33:04 -0700 Subject: [PATCH 5/8] Switch to 'alloc' crate for 'heap' module instead of using 'std'. --- core/Cargo.toml | 1 + core/src/heap.rs | 5 ++++- core/src/lib.rs | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 41e9fdf..97c1286 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -24,6 +24,7 @@ thumbv6 = ["cortex-m"] # TODO: These don't really do anything anymore atomic = [] std = [] +alloc = [] [package.metadata.docs.rs] diff --git a/core/src/heap.rs b/core/src/heap.rs index f70477a..17f8b2e 100644 --- a/core/src/heap.rs +++ b/core/src/heap.rs @@ -5,7 +5,10 @@ use crate::{ common::{self, atomic}, Error, Result, }; -use std::sync::Arc; +use alloc::{ + boxed::Box, + sync::Arc, +}; use core::{ ptr::NonNull, sync::atomic::{ diff --git a/core/src/lib.rs b/core/src/lib.rs index 41010ec..cf4c302 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -105,11 +105,14 @@ #![deny(missing_docs)] #![deny(warnings)] +#[cfg(feature = "alloc")] +extern crate alloc; + mod bbbuffer; pub use bbbuffer::*; mod common; -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] pub mod heap; /// There are no longer separate `atomic` and `cm_mutex` modules. You can just use the types at the From 7f0416ec57d9db2842a660d5a556675457e63a8c Mon Sep 17 00:00:00 2001 From: Teddy DeRego Date: Sat, 25 Jul 2020 14:36:04 -0700 Subject: [PATCH 6/8] Remove lifetime requirement from N parameter in producer/consumer types. Not required when using pointer-casting to get underlying slice when creating grants. --- core/src/bbbuffer.rs | 4 ++-- core/src/common.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/bbbuffer.rs b/core/src/bbbuffer.rs index 80f994f..9814e94 100644 --- a/core/src/bbbuffer.rs +++ b/core/src/bbbuffer.rs @@ -232,7 +232,7 @@ where unsafe impl<'a, N> Send for Producer<'a, N> where N: ArrayLength {} -impl<'a, N: 'a> Producer<'a, N> +impl<'a, N> Producer<'a, N> where N: ArrayLength, { @@ -326,7 +326,7 @@ where unsafe impl<'a, N> Send for Consumer<'a, N> where N: ArrayLength {} -impl<'a, N: 'a> Consumer<'a, N> +impl<'a, N> Consumer<'a, N> where N: ArrayLength, { diff --git a/core/src/common.rs b/core/src/common.rs index 8ca6651..b6eb598 100644 --- a/core/src/common.rs +++ b/core/src/common.rs @@ -144,7 +144,7 @@ where unsafe impl Send for Producer where N: ArrayLength {} -impl<'a, N: 'a> Producer +impl<'a, N> Producer where N: ArrayLength, { @@ -360,7 +360,7 @@ where unsafe impl Send for Consumer where N: ArrayLength {} -impl<'a, N: 'a> Consumer +impl<'a, N> Consumer where N: ArrayLength, { From 7e1ecd80780e5c0d1a41165f44858fede55e1022 Mon Sep 17 00:00:00 2001 From: Teddy DeRego Date: Sat, 25 Jul 2020 14:40:00 -0700 Subject: [PATCH 7/8] cargo fmt --- core/src/bbbuffer.rs | 15 ++++++--------- core/src/common.rs | 12 ++++-------- core/src/heap.rs | 27 ++++++++++----------------- 3 files changed, 20 insertions(+), 34 deletions(-) diff --git a/core/src/bbbuffer.rs b/core/src/bbbuffer.rs index 9814e94..bad4db6 100644 --- a/core/src/bbbuffer.rs +++ b/core/src/bbbuffer.rs @@ -71,15 +71,11 @@ where Ok(( Producer { - inner: common::Producer { - bbq: nn, - }, + inner: common::Producer { bbq: nn }, pd: PhantomData, }, Consumer { - inner: common::Consumer { - bbq: nn, - }, + inner: common::Consumer { bbq: nn }, pd: PhantomData, }, )) @@ -146,8 +142,10 @@ where // can assume the buffer has been split, because // Are these our producers and consumers? - let our_prod = prod.inner.bbq.as_ptr() as *const ConstBBBuffer> == &self.0; - let our_cons = cons.inner.bbq.as_ptr() as *const ConstBBBuffer> == &self.0; + let our_prod = + prod.inner.bbq.as_ptr() as *const ConstBBBuffer> == &self.0; + let our_cons = + cons.inner.bbq.as_ptr() as *const ConstBBBuffer> == &self.0; if !(our_prod && our_cons) { // Can't release, not our producer and consumer @@ -421,4 +419,3 @@ where Self(ConstBBBuffer::new()) } } - diff --git a/core/src/common.rs b/core/src/common.rs index b6eb598..439150e 100644 --- a/core/src/common.rs +++ b/core/src/common.rs @@ -1,6 +1,4 @@ -use crate::{ - Error, Result, -}; +use crate::{Error, Result}; use core::{ cell::UnsafeCell, cmp::min, @@ -435,12 +433,10 @@ where // This is sound, as UnsafeCell, MaybeUninit, and GenericArray // are all `#[repr(Transparent)] let start_of_buf_ptr = inner.buf.get().cast::(); - let grant_slice = - unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(read as isize), sz) }; + let grant_slice = unsafe { from_raw_parts_mut(start_of_buf_ptr.offset(read as isize), sz) }; // TODO feature(maybe_uninit_ref) - //let grant_slice = - // &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } - // .as_mut_slice()[read .. read + sz]; + //let grant_slice = &mut unsafe { inner.buf.get().as_mut().unwrap().get_mut() } + // .as_mut_slice()[read..read + sz]; Ok(GrantR { buf: grant_slice, diff --git a/core/src/heap.rs b/core/src/heap.rs index 17f8b2e..2362700 100644 --- a/core/src/heap.rs +++ b/core/src/heap.rs @@ -5,16 +5,10 @@ use crate::{ common::{self, atomic}, Error, Result, }; -use alloc::{ - boxed::Box, - sync::Arc, -}; +use alloc::{boxed::Box, sync::Arc}; use core::{ ptr::NonNull, - sync::atomic::{ - AtomicBool, - Ordering::AcqRel, - }, + sync::atomic::{AtomicBool, Ordering::AcqRel}, }; pub use generic_array::typenum::consts; use generic_array::{ArrayLength, GenericArray}; @@ -76,15 +70,11 @@ where Ok(( Producer { - inner: common::Producer { - bbq: nn, - }, + inner: common::Producer { bbq: nn }, dealloc_on_drop: dealloc_on_drop.clone(), }, Consumer { - inner: common::Consumer { - bbq: nn, - }, + inner: common::Consumer { bbq: nn }, dealloc_on_drop: dealloc_on_drop.clone(), }, )) @@ -288,7 +278,9 @@ where { fn drop(&mut self) { if atomic::swap(&self.dealloc_on_drop, true, AcqRel) { - unsafe { Box::from_raw(self.inner.bbq.as_ptr()); } + unsafe { + Box::from_raw(self.inner.bbq.as_ptr()); + } } } } @@ -299,7 +291,9 @@ where { fn drop(&mut self) { if atomic::swap(&self.dealloc_on_drop, true, AcqRel) { - unsafe { Box::from_raw(self.inner.bbq.as_ptr()); } + unsafe { + Box::from_raw(self.inner.bbq.as_ptr()); + } } } } @@ -362,4 +356,3 @@ where Self(Box::new(ConstBBBuffer::new())) } } - From 7cdfbd1f79dd69bc80d381366133dd95b7f3a39f Mon Sep 17 00:00:00 2001 From: Teddy DeRego Date: Sat, 25 Jul 2020 14:46:45 -0700 Subject: [PATCH 8/8] Make bbqtest pass --- core/src/framed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/framed.rs b/core/src/framed.rs index d0a5b18..0bddbf3 100644 --- a/core/src/framed.rs +++ b/core/src/framed.rs @@ -99,7 +99,7 @@ where /// /// This size does not include the size of the frame header. The exact size /// of the frame can be set on `commit`. - pub fn grant(&'a mut self, max_sz: usize) -> Result> { + pub fn grant(&mut self, max_sz: usize) -> Result> { let hdr_len = encoded_len(max_sz); Ok(FrameGrantW { grant_w: self.producer.grant_exact(max_sz + hdr_len)?, @@ -121,7 +121,7 @@ where N: ArrayLength, { /// Obtain the next available frame, if any - pub fn read(&'a mut self) -> Option> { + pub fn read(&mut self) -> Option> { // Get all available bytes. We never wrap a frame around, // so if a header is available, the whole frame will be. let mut grant_r = self.consumer.read().ok()?;