#![no_std]
#![deny(missing_docs)]
use core::cell::RefCell;
use core::ops::{Deref, DerefMut};
pub mod prelude {
pub use crate::Mutex;
macro_rules! lock {
($e:ident, $fun:block) => {
$e.lock(|$e| $fun )
};
($e:ident, $($es:ident),+, $fun:block) => {
$e.lock(|$e| lock!($($es),*, $fun))
};
}
macro_rules! make_tuple_impl {
($name:ident, $($es:ident),+) => {
pub trait $name {
$(
type $es;
)*
fn lock<R>(&mut self, f: impl FnOnce($(&mut Self::$es),*) -> R) -> R;
}
impl<$($es),+> $name for ($($es,)+)
where
$($es: crate::Mutex),*
{
$(
type $es = $es::Data;
)*
#[allow(non_snake_case)]
fn lock<R>(&mut self, f: impl FnOnce($(&mut Self::$es),*) -> R) -> R {
let ($(
$es,
)*) = self;
lock!($($es),*, { f($($es),*) })
}
}
};
}
make_tuple_impl!(TupleExt01, T1);
make_tuple_impl!(TupleExt02, T1, T2);
make_tuple_impl!(TupleExt03, T1, T2, T3);
make_tuple_impl!(TupleExt04, T1, T2, T3, T4);
make_tuple_impl!(TupleExt05, T1, T2, T3, T4, T5);
make_tuple_impl!(TupleExt06, T1, T2, T3, T4, T5, T6);
make_tuple_impl!(TupleExt07, T1, T2, T3, T4, T5, T6, T7);
make_tuple_impl!(TupleExt08, T1, T2, T3, T4, T5, T6, T7, T8);
make_tuple_impl!(TupleExt09, T1, T2, T3, T4, T5, T6, T7, T8, T9);
make_tuple_impl!(TupleExt10, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
make_tuple_impl!(TupleExt11, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
make_tuple_impl!(TupleExt12, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
}
pub trait Mutex {
type Data;
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R;
}
impl<L> Mutex for &'_ mut L
where
L: Mutex,
{
type Data = L::Data;
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
L::lock(self, f)
}
}
impl<T> Mutex for &'_ RefCell<T> {
type Data = T;
fn lock<R>(&mut self, f: impl FnOnce(&mut T) -> R) -> R {
f(&mut self.borrow_mut())
}
}
#[derive(Debug)]
pub struct Exclusive<'a, T>(&'a mut T);
impl<'a, T> Exclusive<'a, T> {
pub fn new(data: &'a mut T) -> Self {
Exclusive(data)
}
pub fn into_inner(self) -> &'a mut T {
self.0
}
}
impl<'a, T> From<&'a mut T> for Exclusive<'a, T> {
fn from(data: &'a mut T) -> Self {
Exclusive(data)
}
}
impl<'a, T> Deref for Exclusive<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.0
}
}
impl<'a, T> DerefMut for Exclusive<'a, T> {
fn deref_mut(&mut self) -> &mut T {
self.0
}
}
impl<'a, T> Mutex for Exclusive<'a, T> {
type Data = T;
fn lock<R>(&mut self, f: impl FnOnce(&mut T) -> R) -> R {
f(self.0)
}
}
#[cfg(test)]
#[allow(dead_code)]
mod tests {
use crate::prelude::*;
use crate::Exclusive;
fn compile_test_single_move(mut a: impl Mutex<Data = i32>) {
a.lock(|a| {
*a += 1;
});
}
fn compile_test_single_reference(a: &mut impl Mutex<Data = i32>) {
a.lock(|a| {
*a += 1;
});
}
fn compile_test_double_move(mut a: impl Mutex<Data = i32>, mut b: impl Mutex<Data = i32>) {
a.lock(|a| {
*a += 1;
});
b.lock(|b| {
*b += 1;
});
(a, b).lock(|a, b| {
*a += 1;
*b += 1;
});
}
fn compile_test_double_reference(
a: &mut impl Mutex<Data = i32>,
b: &mut impl Mutex<Data = i32>,
) {
a.lock(|a| {
*a += 1;
});
b.lock(|b| {
*b += 1;
});
(a, b).lock(|a, b| {
*a += 1;
*b += 1;
});
}
fn compile_test_move_and_reference(
mut a: impl Mutex<Data = i32>,
b: &mut impl Mutex<Data = i32>,
) {
a.lock(|a| {
*a += 1;
});
b.lock(|b| {
*b += 1;
});
(a, b).lock(|a, b| {
*a += 1;
*b += 1;
});
}
#[test]
fn refcell_lock() {
let a = core::cell::RefCell::new(0);
let b = core::cell::RefCell::new(0);
(&a).lock(|a| {
*a += 1;
});
(&b).lock(|b| {
*b += 1;
});
(&a, &b).lock(|a, b| {
*a += 1;
*b += 1;
});
}
#[test]
fn exclusive() {
let mut var = 0;
let mut excl = Exclusive(&mut var);
excl.lock(|val| *val += 1);
assert_eq!(*excl.into_inner(), 1);
}
}