From 88f92bb49e65406f8299b3bcc376160eaf18a94f Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 29 Nov 2023 10:40:09 -0800 Subject: [PATCH] feat(tricky-pipe): user-provided MPSC allocations (#37) This commit adds code for constructing a `tricky-pipe` from a user-provided `Arc` and `Box<[Cell]>`, allowing nicer integration with mnemOS' async allocator. --- source/tricky-pipe/src/loom.rs | 135 +++++++++++++++----- source/tricky-pipe/src/mpsc/arc_impl.rs | 50 ++++++-- source/tricky-pipe/src/mpsc/channel_core.rs | 2 +- source/tricky-pipe/src/mpsc/mod.rs | 2 +- source/tricky-pipe/src/oneshot.rs | 2 +- source/tricky-pipe/src/serbox.rs | 2 +- 6 files changed, 143 insertions(+), 50 deletions(-) diff --git a/source/tricky-pipe/src/loom.rs b/source/tricky-pipe/src/loom.rs index 6be2cb7..ebfe80b 100644 --- a/source/tricky-pipe/src/loom.rs +++ b/source/tricky-pipe/src/loom.rs @@ -1,6 +1,18 @@ #[allow(unused_imports)] pub(crate) use self::inner::*; +pub(crate) trait CellWith { + fn with(&self, f: F) -> R + where + F: FnOnce(*const T) -> R; + + fn with_mut(&self, f: F) -> R + where + F: FnOnce(*mut T) -> R; + + fn get_mut(&self) -> cell::MutPtr; +} + #[cfg(all(loom, test))] mod inner { #![allow(dead_code)] @@ -76,6 +88,34 @@ mod inner { #[cfg(test)] pub(crate) use loom::{cell, future, hint, thread}; + #[cfg(test)] + pub(crate) mod cell { + pub(crate) use super::super::CellWith; + pub(crate) use loom::cell::*; + impl CellWith for UnsafeCell { + #[inline(always)] + fn with(&self, f: F) -> R + where + F: FnOnce(*const T) -> R, + { + f(self.get()) + } + + #[inline(always)] + fn with_mut(&self, f: F) -> R + where + F: FnOnce(*mut T) -> R, + { + f(self.get()) + } + + #[inline(always)] + fn get_mut(&self) -> MutPtr { + loom::cell::UnsafeCell::get_mut(self) + } + } + } + #[cfg(test)] pub(crate) fn model(f: impl Fn() + Send + Sync + 'static) { static ITERS: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(1); @@ -222,60 +262,85 @@ mod inner { } pub(crate) mod cell { - #[derive(Debug)] - pub(crate) struct UnsafeCell(core::cell::UnsafeCell); - - impl UnsafeCell { - pub const fn new(data: T) -> UnsafeCell { - UnsafeCell(core::cell::UnsafeCell::new(data)) - } - } + pub(crate) use super::super::CellWith; + pub(crate) use core::cell::UnsafeCell; - impl UnsafeCell { + impl CellWith for UnsafeCell { #[inline(always)] - pub fn with(&self, f: F) -> R + fn with(&self, f: F) -> R where F: FnOnce(*const T) -> R, { - f(self.0.get()) + f(self.get()) } #[inline(always)] - pub fn with_mut(&self, f: F) -> R + fn with_mut(&self, f: F) -> R where F: FnOnce(*mut T) -> R, { - f(self.0.get()) + f(self.get()) } #[inline(always)] - pub(crate) fn get(&self) -> ConstPtr { - ConstPtr(self.0.get()) - } - - #[inline(always)] - pub(crate) fn get_mut(&self) -> MutPtr { - MutPtr(self.0.get()) + fn get_mut(&self) -> MutPtr { + MutPtr(self.get()) } } + // #[derive(Debug)] + // pub(crate) struct UnsafeCell(core::cell::UnsafeCell); - #[derive(Debug)] - pub(crate) struct ConstPtr(*const T); + // impl UnsafeCell { + // pub const fn new(data: T) -> UnsafeCell { + // UnsafeCell(core::cell::UnsafeCell::new(data)) + // } + // } - impl ConstPtr { - #[inline(always)] - pub(crate) unsafe fn deref(&self) -> &T { - &*self.0 - } + // impl UnsafeCell { + // #[inline(always)] + // pub fn with(&self, f: F) -> R + // where + // F: FnOnce(*const T) -> R, + // { + // f(self.0.get()) + // } - #[inline(always)] - pub fn with(&self, f: F) -> R - where - F: FnOnce(*const T) -> R, - { - f(self.0) - } - } + // #[inline(always)] + // pub fn with_mut(&self, f: F) -> R + // where + // F: FnOnce(*mut T) -> R, + // { + // f(self.0.get()) + // } + + // #[inline(always)] + // pub(crate) fn get(&self) -> ConstPtr { + // ConstPtr(self.0.get()) + // } + + // #[inline(always)] + // pub(crate) fn get_mut(&self) -> MutPtr { + // MutPtr(self.0.get()) + // } + // } + + // #[derive(Debug)] + // pub(crate) struct ConstPtr(*const T); + + // impl ConstPtr { + // #[inline(always)] + // pub(crate) unsafe fn deref(&self) -> &T { + // &*self.0 + // } + + // #[inline(always)] + // pub fn with(&self, f: F) -> R + // where + // F: FnOnce(*const T) -> R, + // { + // f(self.0) + // } + // } #[derive(Debug)] pub(crate) struct MutPtr(*mut T); diff --git a/source/tricky-pipe/src/mpsc/arc_impl.rs b/source/tricky-pipe/src/mpsc/arc_impl.rs index 733205c..159cc9f 100644 --- a/source/tricky-pipe/src/mpsc/arc_impl.rs +++ b/source/tricky-pipe/src/mpsc/arc_impl.rs @@ -1,5 +1,8 @@ use super::*; -use crate::loom::sync::Arc; +use crate::{ + loom::{cell::CellWith, sync::Arc}, + mpsc::channel_core::MAX_CAPACITY, +}; use alloc::boxed::Box; use super::channel_core::{Core, CoreVtable}; @@ -11,12 +14,15 @@ use super::channel_core::{Core, CoreVtable}; // TODO(eliza): we should probably replace the use of `Arc` here with manual ref // counting, since the `Core` tracks the number of senders and receivers // already. But, I was in a hurry to get a prototype working... -pub struct TrickyPipe(Arc>) +pub struct TrickyPipe(Arc>) where T: 'static, E: Clone + 'static; -struct Inner +/// The shared state of a [`TrickyPipe`]. +/// +/// This is needed only when constructing new `tricky-pipe`s from a custom allocation. +pub struct SharedState where T: 'static, E: Clone + 'static, @@ -39,8 +45,9 @@ impl TrickyPipe { /// NOTE: `CAPACITY` MUST be a power of two, and must also be <= the number of bits /// in a `usize`, e.g. <= 64 on a 64-bit system. // TODO(eliza): we would need to add a mnemos-alloc version of this... + #[must_use] pub fn new(capacity: u8) -> Self { - Self(Arc::new(Inner { + Self(Arc::new(SharedState { core: Core::new(capacity), elements: (0..capacity) .map(|_| UnsafeCell::new(MaybeUninit::uninit())) @@ -84,23 +91,23 @@ impl TrickyPipe { unsafe fn get_core(ptr: *const ()) -> *const Core { unsafe { - let ptr = ptr.cast::>(); + let ptr = ptr.cast::>(); ptr::addr_of!((*ptr).core) } } unsafe fn get_elems(ptr: *const ()) -> ErasedSlice { - let ptr = ptr.cast::>(); + let ptr = ptr.cast::>(); ErasedSlice::erase(&(*ptr).elements) } unsafe fn erased_clone(ptr: *const ()) { test_println!("erased_clone({ptr:p})"); - Arc::increment_strong_count(ptr.cast::>()) + Arc::increment_strong_count(ptr.cast::>()) } unsafe fn erased_drop(ptr: *const ()) { - let arc = Arc::from_raw(ptr.cast::>()); + let arc = Arc::from_raw(ptr.cast::>()); test_println!(refs = Arc::strong_count(&arc), "erased_drop({ptr:p})"); drop(arc) } @@ -183,11 +190,32 @@ impl Drop for TrickyPipe { unsafe impl Send for TrickyPipe {} unsafe impl Sync for TrickyPipe {} -// === impl Inner === +// === impl SharedState === + +impl SharedState { + /// Construct a new `SharedState` from an array of `UnsafeCell`s. + /// + /// This may be used to construct a new [`TrickyPipe`]`` from an + /// [`Arc`]`<`[`SharedState`]`>`, allowing the use of custom allocations + /// for constructing a `TrickyPipe`. + #[cfg(not(loom))] + pub fn new(elements: Box<[core::cell::UnsafeCell>]>) -> Self { + let capacity = elements.len(); + assert!( + capacity <= MAX_CAPACITY, + "capacity must be <= {}", + MAX_CAPACITY + ); + Self { + core: Core::new(capacity as u8), + elements, + } + } +} -impl Drop for Inner { +impl Drop for SharedState { fn drop(&mut self) { - test_span!("Inner::drop"); + test_span!("SharedState::drop"); // TODO(eliza): there is probably a more efficient way to implement this // rather than by using `try_dequeue`, since we know that we have diff --git a/source/tricky-pipe/src/mpsc/channel_core.rs b/source/tricky-pipe/src/mpsc/channel_core.rs index 8a0de65..a57bbeb 100644 --- a/source/tricky-pipe/src/mpsc/channel_core.rs +++ b/source/tricky-pipe/src/mpsc/channel_core.rs @@ -1,6 +1,6 @@ use super::error::*; use crate::loom::{ - cell::UnsafeCell, + cell::{CellWith, UnsafeCell}, hint, sync::atomic::{AtomicU16, AtomicUsize, Ordering::*}, }; diff --git a/source/tricky-pipe/src/mpsc/mod.rs b/source/tricky-pipe/src/mpsc/mod.rs index c7696ad..2d4ab07 100644 --- a/source/tricky-pipe/src/mpsc/mod.rs +++ b/source/tricky-pipe/src/mpsc/mod.rs @@ -3,7 +3,7 @@ use self::{ channel_core::{DeserVtable, ErasedPipe, ErasedSlice, Reservation, SerVtable, TypedPipe}, error::*, }; -use crate::loom::cell::{self, UnsafeCell}; +use crate::loom::cell::{self, CellWith, UnsafeCell}; use core::{ fmt, future::Future, diff --git a/source/tricky-pipe/src/oneshot.rs b/source/tricky-pipe/src/oneshot.rs index 5a06d6e..32ae503 100644 --- a/source/tricky-pipe/src/oneshot.rs +++ b/source/tricky-pipe/src/oneshot.rs @@ -107,7 +107,7 @@ #![warn(missing_debug_implementations)] use crate::{ loom::{ - cell::UnsafeCell, + cell::{CellWith, UnsafeCell}, hint, sync::atomic::{AtomicU8, Ordering::*}, }, diff --git a/source/tricky-pipe/src/serbox.rs b/source/tricky-pipe/src/serbox.rs index c7d3edf..9d752e7 100644 --- a/source/tricky-pipe/src/serbox.rs +++ b/source/tricky-pipe/src/serbox.rs @@ -2,7 +2,7 @@ #![warn(missing_debug_implementations)] use crate::{ loom::{ - cell::UnsafeCell, + cell::{CellWith, UnsafeCell}, sync::atomic::{AtomicU8, Ordering::*}, }, typeinfo::TypeInfo,