Skip to content

Instantly share code, notes, and snippets.

@valyagolev
Created May 28, 2023 01:49
Show Gist options
  • Save valyagolev/22eaad3c015e3722768bab8f8463ee59 to your computer and use it in GitHub Desktop.
Save valyagolev/22eaad3c015e3722768bab8f8463ee59 to your computer and use it in GitHub Desktop.
bi-directional comm between dioxus and the outside
use std::{
mem::ManuallyDrop,
ops::{Deref, DerefMut},
sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard},
};
use dioxus::prelude::Scope;
pub struct UiAtomSubscription<T> {
atom: Arc<UiAtom<T>>,
subscription: Arc<dyn Fn() + Send + Sync>,
}
impl<T> Drop for UiAtomSubscription<T> {
fn drop(&mut self) {
self.atom.unsubscribe(&self.subscription)
}
}
pub fn use_ui_atom<T: 'static>(cx: Scope, atom: &Arc<UiAtom<T>>) {
cx.use_hook(|| {
let update = cx.schedule_update();
atom.subscribe(update.clone());
UiAtomSubscription {
atom: atom.clone(),
subscription: update,
}
});
}
pub struct UiAtom<T> {
value: RwLock<T>,
subscribers: Mutex<Vec<Arc<dyn Fn() + Send + Sync>>>,
}
pub struct UiAtomGuard<'a, T> {
value: ManuallyDrop<RwLockWriteGuard<'a, T>>,
atom: &'a UiAtom<T>,
}
impl<T> UiAtom<T> {
pub fn new(value: T) -> Self {
Self {
value: RwLock::new(value),
subscribers: Mutex::new(vec![]),
}
}
pub fn read(&self) -> RwLockReadGuard<T> {
self.value.read().unwrap()
}
pub fn write<'a>(&'a self) -> UiAtomGuard<'a, T> {
UiAtomGuard {
atom: self,
value: ManuallyDrop::new(self.value.write().unwrap()),
}
}
pub fn subscribe(&self, subscriber: Arc<dyn Fn() + Send + Sync>) {
self.subscribers.lock().unwrap().push(subscriber);
}
pub fn unsubscribe(&self, subscriber: &Arc<dyn Fn() + Send + Sync>) {
let mut subs = self.subscribers.lock().unwrap();
let prev_len = subs.len();
subs.retain(|s| !Arc::ptr_eq(s, subscriber));
assert!(prev_len == subs.len() + 1);
}
}
impl<T> Drop for UiAtomGuard<'_, T> {
fn drop(&mut self) {
let guard = unsafe { ManuallyDrop::take(&mut self.value) };
drop(guard);
for subscriber in self.atom.subscribers.lock().unwrap().iter() {
subscriber();
}
}
}
impl<T> Deref for UiAtomGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<T> DerefMut for UiAtomGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ui_atom() {
let atom = Arc::new(UiAtom::new(0));
println!("1");
assert!(*atom.read() == 0);
println!("2");
*atom.write() += 1;
assert!(*atom.read() == 1);
println!("3");
*atom.write() = 21;
assert!(*atom.read() == 21);
println!("4");
let s0: Arc<(dyn Fn() + Send + Sync + 'static)> = {
let atom = atom.clone();
let a = Mutex::new(0);
let s0 = {
let atom = atom.clone();
Arc::new(move || {
let mut v = a.lock().unwrap();
*v += 1;
println!("Sub0: {} value={}", v, atom.read());
})
};
atom.clone().subscribe(s0.clone());
s0
};
println!("5");
assert!(*atom.read() == 21);
*atom.write() = 123;
assert!(*atom.read() == 123);
println!("6");
*atom.write() = 456;
println!("7");
assert!(*atom.read() == 456);
println!("8");
{
let atom = atom.clone();
let a = Mutex::new(0);
atom.clone().subscribe(Arc::new(move || {
let mut v = a.lock().unwrap();
*v += 1;
println!("Sub1: {} value={}", v, atom.read());
}));
}
assert!(*atom.read() == 456);
*atom.write() = 123;
assert!(*atom.read() == 123);
println!("16");
*atom.write() = 456;
println!("17");
assert!(*atom.read() == 456);
println!("18");
atom.unsubscribe(&s0);
println!("19");
*atom.write() = 456;
println!("20");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment