use std::ptr::NonNull;
use std::sync::{Condvar, Mutex, MutexGuard, PoisonError};
pub struct Status<T> {
mutex: Mutex<T>,
condvar: Condvar,
}
impl<T> Status<T> {
pub fn new(t: T) -> Self {
Self {
mutex: Mutex::new(t),
condvar: Condvar::new(),
}
}
pub fn try_notify_one(&self, t: T) -> Result<(), PoisonError<MutexGuard<'_, T>>> {
*self.mutex.lock()? = t;
self.condvar.notify_one();
Ok(())
}
pub fn notify_all(&self, t: T) {
*self.mutex.lock().unwrap() = t;
self.condvar.notify_all();
}
pub fn wait_while(&self, predicate: impl FnMut(&mut T) -> bool) -> MutexGuard<'_, T> {
self.condvar
.wait_while(self.mutex.lock().unwrap(), predicate)
.unwrap()
}
}
pub trait LifetimeParameterized {
type T<'a>: ?Sized;
}
pub struct DynLifetimeView<T: LifetimeParameterized> {
ptr: Option<NonNull<T::T<'static>>>,
}
impl<T: LifetimeParameterized> DynLifetimeView<T> {
pub fn empty() -> Self {
Self { ptr: None }
}
#[allow(clippy::unnecessary_cast)]
pub fn set(&mut self, value: &T::T<'_>) {
self.ptr = NonNull::new(NonNull::from(value).as_ptr() as *mut T::T<'static>);
}
pub fn clear(&mut self) {
self.ptr = None;
}
#[allow(clippy::unnecessary_cast)]
pub unsafe fn get<'a>(&self) -> Option<&'a T::T<'a>> {
self.ptr.map(|static_ptr| {
let ptr = static_ptr.as_ptr() as *mut T::T<'a>;
unsafe { &*ptr }
})
}
}
unsafe impl<T: LifetimeParameterized> Send for DynLifetimeView<T> where for<'a> T::T<'a>: Sync {}
unsafe impl<T: LifetimeParameterized> Sync for DynLifetimeView<T> where for<'a> T::T<'a>: Sync {}
#[cfg(test)]
mod test {
use super::*;
use std::sync::{Arc, Barrier, RwLock};
impl LifetimeParameterized for i32 {
type T<'a> = Self;
}
#[test]
fn view_basic_usage() {
let mut view = DynLifetimeView::<i32>::empty();
let mut foo = 42;
view.set(&foo);
let bar = unsafe { view.get().unwrap() };
assert_eq!(*bar, 42);
foo = 1;
view.set(&foo);
let bar = unsafe { view.get().unwrap() };
assert_eq!(*bar, 1);
let abc = 123;
view.set(&abc);
let bar = unsafe { view.get().unwrap() };
assert_eq!(*bar, 123);
}
#[test]
fn view_multi_threaded() {
const NUM_THREADS: usize = 2;
let view = Arc::new(RwLock::new(DynLifetimeView::<i32>::empty()));
let steps: Arc<[_; 6]> = Arc::new(std::array::from_fn(|_| Barrier::new(NUM_THREADS + 1)));
let main = std::thread::spawn({
let view = view.clone();
let steps = steps.clone();
move || {
let mut foo = 42;
view.write().unwrap().set(&foo);
steps[0].wait();
steps[1].wait();
foo = 1;
view.write().unwrap().set(&foo);
steps[2].wait();
steps[3].wait();
let abc = 123;
view.write().unwrap().set(&abc);
steps[4].wait();
steps[5].wait();
}
});
let threads: [_; NUM_THREADS] = std::array::from_fn(move |_| {
std::thread::spawn({
let view = view.clone();
let steps = steps.clone();
move || {
steps[0].wait();
let guard = view.read().unwrap();
let reference = unsafe { guard.get().unwrap() };
assert_eq!(*reference, 42);
drop(guard);
steps[1].wait();
steps[2].wait();
let guard = view.read().unwrap();
let reference = unsafe { guard.get().unwrap() };
assert_eq!(*reference, 1);
drop(guard);
steps[3].wait();
steps[4].wait();
let guard = view.read().unwrap();
let reference = unsafe { guard.get().unwrap() };
assert_eq!(*reference, 123);
drop(guard);
steps[5].wait();
}
})
});
main.join().unwrap();
for t in threads {
t.join().unwrap();
}
}
#[ignore]
#[cfg_attr(coverage_nightly, coverage(off))]
#[test]
#[allow(unused_assignments)]
fn view_bad_mut() {
let mut view = DynLifetimeView::<i32>::empty();
let mut foo = 42;
view.set(&foo);
let bar = unsafe { view.get().unwrap() };
foo = 1;
assert_eq!(*bar, 1);
}
#[ignore]
#[cfg_attr(coverage_nightly, coverage(off))]
#[test]
fn view_bad_lifetime() {
let mut view = DynLifetimeView::<i32>::empty();
{
let foo = 42;
view.set(&foo);
}
let bar = unsafe { view.get().unwrap() };
assert_ne!(*bar, 42);
}
impl LifetimeParameterized for &i32 {
type T<'a> = &'a i32;
}
#[test]
fn dyn_lifetime_view_basic_usage() {
let mut view = DynLifetimeView::<&i32>::empty();
let x = 42;
let mut foo = &x;
view.set(&foo);
let bar = unsafe { view.get().unwrap() };
assert_eq!(**bar, 42);
let y = 1;
foo = &y;
view.set(&foo);
let bar = unsafe { view.get().unwrap() };
assert_eq!(**bar, 1);
let z = 123;
let abc = &z;
view.set(&abc);
let bar = unsafe { view.get().unwrap() };
assert_eq!(**bar, 123);
}
#[test]
fn dyn_lifetime_view_multi_threaded() {
const NUM_THREADS: usize = 2;
let view = Arc::new(RwLock::new(DynLifetimeView::<&i32>::empty()));
let steps: Arc<[_; 6]> = Arc::new(std::array::from_fn(|_| Barrier::new(NUM_THREADS + 1)));
let main = std::thread::spawn({
let view = view.clone();
let steps = steps.clone();
move || {
let x = 42;
let mut foo = &x;
view.write().unwrap().set(&foo);
steps[0].wait();
steps[1].wait();
let y = 1;
foo = &y;
view.write().unwrap().set(&foo);
steps[2].wait();
steps[3].wait();
let z = 123;
let abc = &z;
view.write().unwrap().set(&abc);
steps[4].wait();
steps[5].wait();
}
});
let threads: [_; NUM_THREADS] = std::array::from_fn(move |_| {
std::thread::spawn({
let view = view.clone();
let steps = steps.clone();
move || {
steps[0].wait();
let guard = view.read().unwrap();
let reference = unsafe { guard.get().unwrap() };
assert_eq!(**reference, 42);
drop(guard);
steps[1].wait();
steps[2].wait();
let guard = view.read().unwrap();
let reference = unsafe { guard.get().unwrap() };
assert_eq!(**reference, 1);
drop(guard);
steps[3].wait();
steps[4].wait();
let guard = view.read().unwrap();
let reference = unsafe { guard.get().unwrap() };
assert_eq!(**reference, 123);
drop(guard);
steps[5].wait();
}
})
});
main.join().unwrap();
for t in threads {
t.join().unwrap();
}
}
#[ignore]
#[cfg_attr(coverage_nightly, coverage(off))]
#[test]
#[allow(unused_assignments)]
fn dyn_lifetime_view_bad_mut() {
let mut view = DynLifetimeView::<&i32>::empty();
let x = 42;
let mut foo = &x;
view.set(&foo);
let bar = unsafe { view.get().unwrap() };
let y = 1;
foo = &y;
assert_eq!(**bar, 1);
}
#[ignore]
#[cfg_attr(coverage_nightly, coverage(off))]
#[test]
fn dyn_lifetime_view_bad_lifetime() {
let x = 42;
let mut view = DynLifetimeView::<&i32>::empty();
{
let foo = &x;
view.set(&foo);
}
let bar = unsafe { view.get().unwrap() };
assert_eq!(**bar, 42);
}
}