Skip to content

Commit

Permalink
feat(tricky-pipe): user-provided MPSC allocations (#37)
Browse files Browse the repository at this point in the history
This commit adds code for constructing a `tricky-pipe` from a
user-provided `Arc` and `Box<[Cell<T>]>`, allowing nicer integration
with mnemOS' async allocator.
  • Loading branch information
hawkw committed Nov 29, 2023
1 parent 39cc397 commit 88f92bb
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 50 deletions.
135 changes: 100 additions & 35 deletions source/tricky-pipe/src/loom.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
#[allow(unused_imports)]
pub(crate) use self::inner::*;

pub(crate) trait CellWith<T> {
fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(*const T) -> R;

fn with_mut<F, R>(&self, f: F) -> R
where
F: FnOnce(*mut T) -> R;

fn get_mut(&self) -> cell::MutPtr<T>;
}

#[cfg(all(loom, test))]
mod inner {
#![allow(dead_code)]
Expand Down Expand Up @@ -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<T> CellWith<T> for UnsafeCell<T> {
#[inline(always)]
fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(*const T) -> R,
{
f(self.get())
}

#[inline(always)]
fn with_mut<F, R>(&self, f: F) -> R
where
F: FnOnce(*mut T) -> R,
{
f(self.get())
}

#[inline(always)]
fn get_mut(&self) -> MutPtr<T> {
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);
Expand Down Expand Up @@ -222,60 +262,85 @@ mod inner {
}

pub(crate) mod cell {
#[derive(Debug)]
pub(crate) struct UnsafeCell<T: ?Sized>(core::cell::UnsafeCell<T>);

impl<T> UnsafeCell<T> {
pub const fn new(data: T) -> UnsafeCell<T> {
UnsafeCell(core::cell::UnsafeCell::new(data))
}
}
pub(crate) use super::super::CellWith;
pub(crate) use core::cell::UnsafeCell;

impl<T: ?Sized> UnsafeCell<T> {
impl<T> CellWith<T> for UnsafeCell<T> {
#[inline(always)]
pub fn with<F, R>(&self, f: F) -> R
fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(*const T) -> R,
{
f(self.0.get())
f(self.get())
}

#[inline(always)]
pub fn with_mut<F, R>(&self, f: F) -> R
fn with_mut<F, R>(&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<T> {
ConstPtr(self.0.get())
}

#[inline(always)]
pub(crate) fn get_mut(&self) -> MutPtr<T> {
MutPtr(self.0.get())
fn get_mut(&self) -> MutPtr<T> {
MutPtr(self.get())
}
}
// #[derive(Debug)]
// pub(crate) struct UnsafeCell<T: ?Sized>(core::cell::UnsafeCell<T>);

#[derive(Debug)]
pub(crate) struct ConstPtr<T: ?Sized>(*const T);
// impl<T> UnsafeCell<T> {
// pub const fn new(data: T) -> UnsafeCell<T> {
// UnsafeCell(core::cell::UnsafeCell::new(data))
// }
// }

impl<T: ?Sized> ConstPtr<T> {
#[inline(always)]
pub(crate) unsafe fn deref(&self) -> &T {
&*self.0
}
// impl<T: ?Sized> UnsafeCell<T> {
// #[inline(always)]
// pub fn with<F, R>(&self, f: F) -> R
// where
// F: FnOnce(*const T) -> R,
// {
// f(self.0.get())
// }

#[inline(always)]
pub fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(*const T) -> R,
{
f(self.0)
}
}
// #[inline(always)]
// pub fn with_mut<F, R>(&self, f: F) -> R
// where
// F: FnOnce(*mut T) -> R,
// {
// f(self.0.get())
// }

// #[inline(always)]
// pub(crate) fn get(&self) -> ConstPtr<T> {
// ConstPtr(self.0.get())
// }

// #[inline(always)]
// pub(crate) fn get_mut(&self) -> MutPtr<T> {
// MutPtr(self.0.get())
// }
// }

// #[derive(Debug)]
// pub(crate) struct ConstPtr<T: ?Sized>(*const T);

// impl<T: ?Sized> ConstPtr<T> {
// #[inline(always)]
// pub(crate) unsafe fn deref(&self) -> &T {
// &*self.0
// }

// #[inline(always)]
// pub fn with<F, R>(&self, f: F) -> R
// where
// F: FnOnce(*const T) -> R,
// {
// f(self.0)
// }
// }

#[derive(Debug)]
pub(crate) struct MutPtr<T: ?Sized>(*mut T);
Expand Down
50 changes: 39 additions & 11 deletions source/tricky-pipe/src/mpsc/arc_impl.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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<T, E = ()>(Arc<Inner<T, E>>)
pub struct TrickyPipe<T, E = ()>(Arc<SharedState<T, E>>)
where
T: 'static,
E: Clone + 'static;

struct Inner<T, E>
/// The shared state of a [`TrickyPipe`].
///
/// This is needed only when constructing new `tricky-pipe`s from a custom allocation.
pub struct SharedState<T, E>
where
T: 'static,
E: Clone + 'static,
Expand All @@ -39,8 +45,9 @@ impl<T: 'static, E: Clone + 'static> TrickyPipe<T, E> {
/// 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()))
Expand Down Expand Up @@ -84,23 +91,23 @@ impl<T: 'static, E: Clone + 'static> TrickyPipe<T, E> {

unsafe fn get_core(ptr: *const ()) -> *const Core<E> {
unsafe {
let ptr = ptr.cast::<Inner<T, E>>();
let ptr = ptr.cast::<SharedState<T, E>>();
ptr::addr_of!((*ptr).core)
}
}

unsafe fn get_elems(ptr: *const ()) -> ErasedSlice {
let ptr = ptr.cast::<Inner<T, E>>();
let ptr = ptr.cast::<SharedState<T, E>>();
ErasedSlice::erase(&(*ptr).elements)
}

unsafe fn erased_clone(ptr: *const ()) {
test_println!("erased_clone({ptr:p})");
Arc::increment_strong_count(ptr.cast::<Inner<T, E>>())
Arc::increment_strong_count(ptr.cast::<SharedState<T, E>>())
}

unsafe fn erased_drop(ptr: *const ()) {
let arc = Arc::from_raw(ptr.cast::<Inner<T, E>>());
let arc = Arc::from_raw(ptr.cast::<SharedState<T, E>>());
test_println!(refs = Arc::strong_count(&arc), "erased_drop({ptr:p})");
drop(arc)
}
Expand Down Expand Up @@ -183,11 +190,32 @@ impl<T, E: Clone> Drop for TrickyPipe<T, E> {
unsafe impl<T: Send, E: Clone + Send + Sync> Send for TrickyPipe<T, E> {}
unsafe impl<T: Send, E: Clone + Send + Sync> Sync for TrickyPipe<T, E> {}

// === impl Inner ===
// === impl SharedState ===

impl<T, E: Clone> SharedState<T, E> {
/// Construct a new `SharedState` from an array of `UnsafeCell`s.
///
/// This may be used to construct a new [`TrickyPipe`]`<T>` from an
/// [`Arc`]`<`[`SharedState`]`<T>>`, allowing the use of custom allocations
/// for constructing a `TrickyPipe`.
#[cfg(not(loom))]
pub fn new(elements: Box<[core::cell::UnsafeCell<core::mem::MaybeUninit<T>>]>) -> Self {
let capacity = elements.len();
assert!(
capacity <= MAX_CAPACITY,
"capacity must be <= {}",
MAX_CAPACITY
);
Self {
core: Core::new(capacity as u8),
elements,
}
}
}

impl<T, E: Clone> Drop for Inner<T, E> {
impl<T, E: Clone> Drop for SharedState<T, E> {
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
Expand Down
2 changes: 1 addition & 1 deletion source/tricky-pipe/src/mpsc/channel_core.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::error::*;
use crate::loom::{
cell::UnsafeCell,
cell::{CellWith, UnsafeCell},
hint,
sync::atomic::{AtomicU16, AtomicUsize, Ordering::*},
};
Expand Down
2 changes: 1 addition & 1 deletion source/tricky-pipe/src/mpsc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion source/tricky-pipe/src/oneshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
#![warn(missing_debug_implementations)]
use crate::{
loom::{
cell::UnsafeCell,
cell::{CellWith, UnsafeCell},
hint,
sync::atomic::{AtomicU8, Ordering::*},
},
Expand Down
2 changes: 1 addition & 1 deletion source/tricky-pipe/src/serbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![warn(missing_debug_implementations)]
use crate::{
loom::{
cell::UnsafeCell,
cell::{CellWith, UnsafeCell},
sync::atomic::{AtomicU8, Ordering::*},
},
typeinfo::TypeInfo,
Expand Down

0 comments on commit 88f92bb

Please sign in to comment.