mod utils;
use tuple_list::{Tuple, TupleList};
pub use self::utils::{Concat, Construct, Count, InElement, Index};
use crate::{array::Element, include::*};
#[derive(Debug)]
struct Inner<T: InElement> {
count: AtomicUsize,
place: T::Place,
}
impl<T: InElement> Inner<T> {
const LAYOUT: Layout = Layout::new::<Self>();
fn new() -> NonNull<Self> {
let memory = match Global.allocate(Self::LAYOUT) {
Ok(memory) => memory.cast::<Self>(),
Err(_) => handle_alloc_error(Self::LAYOUT),
};
let value = Self {
count: AtomicUsize::new(T::TUPLE_LIST_SIZE),
place: T::init(),
};
unsafe { memory.as_ptr().write(value) }
memory
}
unsafe fn drop_in_place(this: NonNull<Self>) -> <T::Take as TupleList>::Tuple {
let inner = unsafe { this.as_ref() };
let tuple = unsafe { T::take(&inner.place) }.into_tuple();
unsafe { Global.deallocate(this.cast(), Self::LAYOUT) };
tuple
}
}
pub type Whole<Head, Current, Tail> =
<<Head as Concat<(Current,)>>::Output as Concat<Tail>>::Output;
pub type List<Head, Current, Tail> = <Whole<Head, Current, Tail> as Tuple>::TupleList;
type Ptr<Head, Current, Tail> = NonNull<Inner<List<Head, Current, Tail>>>;
pub type Place<Head, Current, Tail> = <List<Head, Current, Tail> as InElement>::Place;
pub type TakeList<Head, Current, Tail> = <List<Head, Current, Tail> as InElement>::Take;
pub type Take<Head, Current, Tail> = <TakeList<Head, Current, Tail> as TupleList>::Tuple;
#[derive(Debug)]
pub struct Sender<Head, Current, Tail>(Ptr<Head, Current, Tail>)
where
Head: Concat<(Current,)>,
<Head as Concat<(Current,)>>::Output: Concat<Tail>,
Tail: Tuple,
<Whole<Head, Current, Tail> as Tuple>::TupleList: InElement;
unsafe impl<Head, Current, Tail> Send for Sender<Head, Current, Tail>
where
Head: Concat<(Current,)> + Send,
Current: Send,
<Head as Concat<(Current,)>>::Output: Concat<Tail>,
Tail: Tuple + Send,
<Whole<Head, Current, Tail> as Tuple>::TupleList: InElement,
{
}
pub type CountOf<T> = <<T as Tuple>::TupleList as Count>::Count;
impl<Head, Current, Tail> Sender<Head, Current, Tail>
where
Head: Concat<(Current,)>,
<Head as Concat<(Current,)>>::Output: Concat<Tail>,
Tail: Tuple,
<Whole<Head, Current, Tail> as Tuple>::TupleList: InElement,
{
unsafe fn new(inner: Ptr<Head, Current, Tail>) -> Self {
Sender(inner)
}
pub fn send(self, value: Current) -> Result<(), Take<Head, Current, Tail>>
where
<Head as Tuple>::TupleList: Count,
Place<Head, Current, Tail>: Index<CountOf<Head>, Output = Element<Current>>,
{
let pointer = self.0;
let inner = unsafe { pointer.as_ref() };
let elem: &Element<Current> = Index::<CountOf<Head>>::index(&inner.place);
unsafe { elem.place(value) };
let fetch_sub = inner.count.fetch_sub(1, Release);
mem::forget(self);
if fetch_sub == 1 {
atomic::fence(Acquire);
return Err(unsafe { Inner::drop_in_place(pointer) });
}
Ok(())
}
}
impl<Head, Current, Tail> Drop for Sender<Head, Current, Tail>
where
Head: Concat<(Current,)>,
<Head as Concat<(Current,)>>::Output: Concat<Tail>,
Tail: Tuple,
<Whole<Head, Current, Tail> as Tuple>::TupleList: InElement,
{
fn drop(&mut self) {
let pointer = self.0;
let inner = unsafe { pointer.as_ref() };
if inner.count.fetch_sub(1, Relaxed) == 1 {
atomic::fence(Acquire);
unsafe { Inner::drop_in_place(pointer) };
}
}
}
pub fn tuple<T>() -> <T::Sender as TupleList>::Tuple
where
T: Construct,
<T as Tuple>::TupleList: InElement,
{
let inner = Inner::<T::TupleList>::new();
unsafe { T::construct(inner) }.into_tuple()
}
#[cfg(test)]
mod tests {
#[cfg(not(loom))]
use std::thread;
#[cfg(loom)]
use loom::thread;
use super::tuple;
#[test]
fn send() {
fn inner() {
let (s1, s2, s3) = tuple::<(i32, u8, char)>();
let j2 = thread::spawn(|| s2.send(2));
let j3 = thread::spawn(|| s3.send('3'));
let res = s1.send(1).and(j2.join().unwrap()).and(j3.join().unwrap());
assert_eq!(res, Err((Some(1), Some(2), Some('3'))));
}
#[cfg(not(loom))]
inner();
#[cfg(loom)]
loom::model(inner);
}
#[test]
fn drop_one() {
fn inner() {
let (s1, s2, s3) = tuple::<(i32, u8, char)>();
let j2 = thread::spawn(|| {
drop(s2);
Ok(())
});
let j3 = thread::spawn(|| s3.send('3'));
let res = s1.send(1).and(j2.join().unwrap()).and(j3.join().unwrap());
if let Err(r) = res {
assert_eq!(r, (Some(1), None, Some('3')));
}
}
#[cfg(not(loom))]
inner();
#[cfg(loom)]
loom::model(inner);
}
}