It was possible to make this work with ouroboros:
use ouroboros::self_referencing;
use std::{
any::Any,
collections::HashMap,
marker::PhantomData,
ops::{Deref, DerefMut},
sync::{atomic::AtomicUsize, RwLock, RwLockReadGuard, RwLockWriteGuard},
thread::spawn,
};
#[derive(Default)]
struct Context {
next_id: AtomicUsize,
signals: RwLock<HashMap<usize, RwLock<StoredSignal>>>,
}
impl Context {
pub fn create_signal<T: Any + Send + Sync>(&'static self, data: T) -> Signal<T> {
let id = self
.next_id
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let signal = StoredSignal {
id,
data: Box::new(data),
};
self.signals
.write()
.unwrap()
.insert(id, RwLock::new(signal));
Signal {
cx: self,
id,
_ty: PhantomData,
}
}
}
struct StoredSignal {
id: usize,
data: Box<dyn Any + Send + Sync>,
}
#[derive(Clone, Copy)]
struct Signal<T> {
cx: &'static Context,
id: usize,
_ty: PhantomData<T>,
}
impl<T> Signal<T> {
pub fn get(&self) -> SignalLock<T> {
let lock = SignalLockBuilder {
map_lock: self.cx.signals.read().unwrap(),
lock_builder: |map_lock| map_lock.get(&self.id).unwrap().read().unwrap(),
ty: PhantomData,
}
.build();
lock
}
pub fn get_mut(&self) -> SignalLockMut<T> {
SignalLockMut(
SignalLockMutInnerBuilder {
map_lock: self.cx.signals.read().unwrap(),
lock_builder: |map_lock| map_lock.get(&self.id).unwrap().write().unwrap(),
deref_mut_called: false,
ty: PhantomData,
}
.build(),
)
}
}
#[self_referencing]
struct SignalLock<'a, T> {
map_lock: RwLockReadGuard<'a, HashMap<usize, RwLock<StoredSignal>>>,
#[borrows(map_lock)]
#[covariant]
lock: RwLockReadGuard<'this, StoredSignal>,
ty: PhantomData<T>,
}
impl<T: 'static> Deref for SignalLock<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.borrow_lock().data.downcast_ref().unwrap()
}
}
#[self_referencing]
struct SignalLockMutInner<'a, T> {
map_lock: RwLockReadGuard<'a, HashMap<usize, RwLock<StoredSignal>>>,
#[borrows(map_lock)]
#[covariant]
lock: RwLockWriteGuard<'this, StoredSignal>,
deref_mut_called: bool,
ty: PhantomData<T>,
}
struct SignalLockMut<'a, T>(SignalLockMutInner<'a, T>);
impl<T: 'static> Deref for SignalLockMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.borrow_lock().data.downcast_ref().unwrap()
}
}
impl<T: 'static> DerefMut for SignalLockMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.with_deref_mut_called_mut(|called| *called = true);
self.0
.with_lock_mut(|lock| lock.data.downcast_mut().unwrap())
}
}
impl<'a, T> Drop for SignalLockMut<'a, T> {
fn drop(&mut self) {
if *self.0.borrow_deref_mut_called() {
println!("DerefMut was called {}", self.0.borrow_lock().id);
}
}
}
fn main() {
let cx = Box::leak(Box::new(Context::default()));
let signal: Signal<i32> = cx.create_signal(100);
dbg!(*signal.get());
dbg!(*signal.get_mut());
dbg!(*signal.get());
*signal.get_mut() = 200;
dbg!(*signal.get());
let s = cx.create_signal(100);
dbg!(*s.get());
let t = spawn(move || {
dbg!(*s.get());
});
dbg!(*s.get());
*s.get_mut() = 300;
t.join().unwrap();
}