use std::{
cell::RefCell,
rc::Rc,
task::{Context, Poll, Waker},
};
use crate::{utils::RcStatus, ShareValue};
#[derive(Debug)]
enum WakerStatus {
Unregistered,
Registered(Waker),
Updated,
}
impl WakerStatus {
#[inline]
fn notify_changed(&mut self) {
if let WakerStatus::Registered(w) = std::mem::replace(self, WakerStatus::Updated) {
w.wake()
}
}
}
struct SharedStateInner<T> {
value: RefCell<T>,
waker_status: RefCell<WakerStatus>,
}
impl<T> SharedStateInner<T> {
fn borrow_mut_waker_status<R>(
self: &mut Rc<Self>,
f: impl FnOnce(&mut WakerStatus, RcStatus) -> R,
) -> R {
if let Some(this) = Rc::get_mut(self) {
f(this.waker_status.get_mut(), RcStatus::Owned)
} else {
f(&mut self.waker_status.borrow_mut(), RcStatus::Shared)
}
}
}
pub struct SharedState<T> {
inner: Rc<SharedStateInner<T>>,
}
impl<T> Drop for SharedState<T> {
fn drop(&mut self) {
if Rc::strong_count(&self.inner) <= 2 {
self.inner.borrow_mut_waker_status(|waker, _| match waker {
WakerStatus::Unregistered => {}
WakerStatus::Registered(_) => {
if let WakerStatus::Registered(w) =
std::mem::replace(waker, WakerStatus::Unregistered)
{
w.wake()
}
}
WakerStatus::Updated => {}
});
}
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for SharedState<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut d = f.debug_tuple("SharedState");
match self.inner.value.try_borrow() {
Ok(v) => {
d.field(&v);
}
Err(_) => {
d.field(&"borrowed");
}
}
d.finish()
}
}
impl<T> Clone for SharedState<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<T> SharedState<T> {
#[inline]
pub fn new(initial_value: T) -> Self {
Self {
inner: Rc::new(SharedStateInner {
value: RefCell::new(initial_value),
waker_status: RefCell::new(WakerStatus::Updated),
}),
}
}
#[inline]
pub fn notify_changed(&self) {
self.inner.waker_status.borrow_mut().notify_changed();
}
pub fn map_mut_and_notify_if<R>(&self, f: impl FnOnce(&mut T) -> (R, bool)) -> R {
let (r, changed) = f(&mut self.inner.value.borrow_mut());
if changed {
self.notify_changed();
}
r
}
pub(super) fn impl_poll_next_update(&mut self, cx: &mut Context<'_>) -> Poll<bool> {
self.inner.borrow_mut_waker_status(|waker, status| {
match waker {
WakerStatus::Unregistered => {
if status.is_owned() {
Poll::Ready(false)
} else {
*waker = WakerStatus::Registered(cx.waker().clone());
Poll::Pending
}
}
WakerStatus::Registered(w) => {
if status.is_owned() {
Poll::Ready(false)
} else {
*w = cx.waker().clone();
Poll::Pending
}
}
WakerStatus::Updated => Poll::Ready(true),
}
})
}
#[inline]
pub(super) fn mark_as_unregistered(&mut self) {
self.inner.borrow_mut_waker_status(|waker_status, _| {
*waker_status = WakerStatus::Unregistered;
});
}
}
impl<T> ShareValue for SharedState<T> {
type Value = T;
#[inline]
fn is_shared(&self) -> bool {
Rc::strong_count(&self.inner) != 0
}
#[inline]
fn get(&self) -> T
where
T: Copy,
{
*self.inner.value.borrow()
}
#[inline]
fn get_cloned(&self) -> T
where
T: Clone,
{
self.inner.value.borrow().clone()
}
#[inline]
fn replace(&self, new_value: T) -> T {
self.map_mut(|v| std::mem::replace(v, new_value))
}
#[inline]
fn map<R>(&self, f: impl FnOnce(&T) -> R) -> R {
f(&self.inner.value.borrow())
}
#[inline]
fn map_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
self.notify_changed();
f(&mut self.inner.value.borrow_mut())
}
fn equivalent_to(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.inner, &other.inner)
}
}
hooks_core::impl_hook![
type For<T> = SharedState<T>;
fn unmount() {}
#[inline]
fn poll_next_update(self, cx: _) {
self.get_mut().impl_poll_next_update(cx)
}
#[inline]
fn use_hook(self) -> &'hook Self {
let this = self.get_mut();
this.mark_as_unregistered();
this
}
];