use parking_lot::{
MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard,
};
use rustc_hash::FxHashMap;
use std::sync::Arc;
type LinkUpdateMap = FxHashMap<usize, (usize, Arc<dyn Send + Sync + Fn()>)>;
pub(crate) struct Link<T>(RwLock<(T, LinkUpdateMap)>);
impl<T> Link<T> {
pub(crate) fn new(t: T) -> Self {
Self(RwLock::new((t, FxHashMap::default())))
}
pub(crate) fn add_listener<F: FnOnce() -> Arc<dyn Send + Sync + Fn()>>(&self, id: usize, f: F) {
self.0.write().1.entry(id).or_insert_with(|| (0, f())).0 += 1;
}
pub(crate) fn drop_listener(&self, id: usize) {
let mut p = self.0.write();
let c = if let Some((c, _)) = p.1.get_mut(&id) {
*c -= 1;
*c
} else {
1
};
if c == 0 {
p.1.remove(&id);
}
}
pub(crate) fn needs_update(&self) {
for (_id, (_, u)) in self.0.read().1.iter().filter(|&(_, &(ct, _))| ct > 0) {
u()
}
}
pub(crate) fn borrow(&self) -> MappedRwLockReadGuard<T> {
RwLockReadGuard::map(self.0.read(), |(r, _)| r)
}
pub(crate) fn borrow_mut(&self) -> MappedRwLockWriteGuard<T> {
RwLockWriteGuard::map(self.0.write(), |(r, _)| r)
}
}
#[cfg(feature = "debug")]
impl<T: std::fmt::Debug> std::fmt::Debug for Link<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Ok(me) = (self.0).try_borrow() {
write!(f, "Link({:?})", me.0)
} else {
f.write_str("Link::AlreadyBorrowed")
}
}
}
pub struct Shareable<T>(pub(crate) Option<Arc<Link<T>>>);
impl<T> Shareable<T> {
pub const fn new() -> Self {
Self(None)
}
}
#[cfg(feature = "debug")]
impl<T: std::fmt::Debug> std::fmt::Debug for Shareable<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(me) = &self.0 {
write!(f, "Shareable::Initialized({:?})", me)
} else {
write!(f, "Shareable::Uninitialized")
}
}
}
#[macro_export]
macro_rules! shareable {
($(#[$meta:meta])*$vis:vis $IDENT:ident: $Ty:ty = $($init:tt)*) => {
$(#[$meta])*
#[derive(Clone, Copy)]
$vis struct $IDENT;
impl $IDENT {
pub fn use_rw<'a, P>(self,cx: &$crate::reexported::Scope<'a, P>) -> &'a mut $crate::Shared<$Ty, $crate::RW> {
$crate::shared::Static::_use_rw(self, cx)
}
pub fn use_w<'a, P>(self,cx: &$crate::reexported::Scope<'a, P>) -> &'a mut $crate::Shared<$Ty, $crate::W> {
$crate::shared::Static::_use_w(self, cx)
}
pub fn share(self) -> $crate::Shared<$Ty, $crate::W> {
$crate::shared::Static::_share(self)
}
}
const _: () = {
#[allow(non_upper_case_globals)]
static $IDENT: $crate::reexported::Mutex<$crate::shared::Shareable<$Ty>> = $crate::reexported::Mutex::new($crate::shared::Shareable::new());
#[doc(hidden)]
impl $crate::shared::Static for $IDENT {
type Type = $Ty;
fn _share(self) -> $crate::Shared<$Ty, $crate::W> {
$crate::Shared::from_shareable(&mut $IDENT.lock(), || {$($init)*})
}
fn _use_rw<'a, P>(self,cx: &$crate::reexported::Scope<'a, P>) -> &'a mut $crate::Shared<$Ty, $crate::RW> {
$crate::Shared::init(cx, &mut $IDENT.lock(), || {$($init)*}, $crate::RW)
}
fn _use_w<'a, P>(self,cx: &$crate::reexported::Scope<'a, P>) -> &'a mut $crate::Shared<$Ty, $crate::W> {
$crate::Shared::init(cx, &mut $IDENT.lock(), || {$($init)*}, $crate::W)
}
}
};
};
}
#[doc(hidden)]
pub trait Static {
type Type;
fn _share(self) -> Shared<Self::Type, super::W>;
fn _use_rw<'a, P>(
self,
cx: &dioxus_core::Scope<'a, P>,
) -> &'a mut Shared<Self::Type, super::RW>;
fn _use_w<'a, P>(self, cx: &dioxus_core::Scope<'a, P>) -> &'a mut Shared<Self::Type, super::W>;
}
pub struct Shared<T: 'static, B: 'static> {
pub(crate) link: Arc<Link<T>>,
pub id: Option<usize>,
__: std::marker::PhantomData<B>,
}
impl<T: 'static, B: 'static> Clone for Shared<T, B> {
fn clone(&self) -> Self {
if let Some(id) = self.id {
self.link.add_listener(id, || Arc::new(|| {}))
}
Self {
link: self.link.clone(),
id: self.id,
__: std::marker::PhantomData,
}
}
}
impl<T: 'static, B: 'static + super::Flag> Shared<T, B> {
pub fn init<'a, P, F: FnOnce() -> T>(
cx: &dioxus_core::Scope<'a, P>,
opt: &mut Shareable<T>,
f: F,
_: B,
) -> &'a mut Self {
let id = cx.scope_id().0;
cx.use_hook(|| {
let mut r: Shared<T, super::W> = Shared::from_shareable(opt, f);
if B::READ {
r.id = Some(id);
r.link.add_listener(id, || cx.schedule_update());
}
unsafe { std::mem::transmute::<_, Self>(r) }
})
}
pub fn write(&self) -> MappedRwLockWriteGuard<T> {
self.link.needs_update();
self.link.borrow_mut()
}
pub fn write_silent(&self) -> MappedRwLockWriteGuard<T> {
self.link.borrow_mut()
}
pub fn needs_update(&self) {
self.link.needs_update();
}
pub fn set(&self, t: T)
where
T: PartialEq,
{
if *self.link.borrow() != t {
*self.write() = t;
}
}
pub fn set_with<F: Fn(&T) -> T>(&self, f: F)
where
T: PartialEq,
{
let prev = self.link.borrow();
let updated = f(&prev);
if *prev != updated {
drop(prev);
*self.write() = updated;
}
}
pub fn read(&self) -> MappedRwLockReadGuard<T> {
self.link.borrow()
}
pub fn listeners(&self) -> String {
format!(
"{:?}",
self.link
.0
.read()
.1
.iter()
.map(|(&i, &(j, _))| (i, j))
.collect::<Vec<_>>()
)
}
}
impl<T: 'static> Shared<T, super::W> {
pub(crate) fn from_link(link: Arc<Link<T>>) -> Self {
Self {
link,
id: None,
__: std::marker::PhantomData,
}
}
#[doc(hidden)]
pub fn from_shareable<F: FnOnce() -> T>(opt: &mut Shareable<T>, f: F) -> Self {
if let Some(p) = opt.0.as_ref() {
Shared {
link: p.clone(),
id: None,
__: std::marker::PhantomData,
}
} else {
let r = Shared {
link: Arc::new(Link::new(f())),
id: None,
__: std::marker::PhantomData,
};
opt.0 = Some(r.link.clone());
r
}
}
}
impl<T: 'static, B: 'static> Drop for Shared<T, B> {
fn drop(&mut self) {
if let Some(id) = self.id {
self.link.drop_listener(id);
}
}
}