use super::*;
impl<T> Signal<T>
where
T: Clone + PartialEq + 'static,
{
fn slab() -> &'static SignalSlab {
unsafe { &*(*std::ptr::addr_of!(SIGNAL_SLAB)).deref().get() }
}
fn slab_mut() -> &'static mut SignalSlab {
unsafe { &mut *(*std::ptr::addr_of_mut!(SIGNAL_SLAB)).deref().get() }
}
pub fn create(value: T) -> Self {
let inner: SignalInner<T> = SignalInner::new(value, Vec::new(), true);
let idx: usize = Self::slab_mut().insert(inner);
let mut signal: Self = Self::new(0, PhantomData);
signal.set_inner(idx);
signal
}
pub fn get(&self) -> T {
let idx: usize = self.get_inner();
let Some(inner) = Self::slab_mut().get_mut::<T>(idx) else {
unreachable!("Signal handle does not resolve to a slab slot");
};
if !inner.get_alive() {
return inner.get_value().clone();
}
let tracking_id: usize = CURRENT_TRACKING_DYNAMIC_ID.load(Ordering::Relaxed);
if tracking_id != usize::MAX {
Self::push_dependent(inner, tracking_id);
}
inner.get_value().clone()
}
pub fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
let idx: usize = self.get_inner();
let Some(inner) = Self::slab_mut().get_mut::<T>(idx) else {
unreachable!("Signal handle does not resolve to a slab slot");
};
if !inner.get_alive() {
return f(inner.get_value());
}
let tracking_id: usize = CURRENT_TRACKING_DYNAMIC_ID.load(Ordering::Relaxed);
if tracking_id != usize::MAX {
Self::push_dependent(inner, tracking_id);
}
f(inner.get_value())
}
pub fn subscribe<F>(&self, callback: F) -> usize
where
F: FnMut() + 'static,
{
let Some(inner) = Self::slab_mut().get_mut::<T>(self.get_inner()) else {
return usize::MAX;
};
let id: usize = inner.get_next_listener_id();
inner.set_next_listener_id(id.wrapping_add(1));
inner.get_mut_listeners().push((id, Box::new(callback)));
id
}
pub fn unsubscribe(&self, id: usize) {
let Some(inner) = Self::slab_mut().get_mut::<T>(self.get_inner()) else {
return;
};
if inner.get_notifying() {
inner.get_mut_removed_listener_ids().push(id);
return;
}
inner
.get_mut_listeners()
.retain(|(listener_id, _): &ListenerEntry| *listener_id != id);
}
pub(crate) fn deactivate(&self) {
let idx: usize = self.get_inner();
let Some(inner) = Self::slab_mut().get_mut::<T>(idx) else {
return;
};
inner.set_alive(false);
inner.get_mut_listeners().clear();
inner.get_mut_dependents().clear();
inner.get_mut_removed_listener_ids().clear();
}
fn update(&self, value: T) -> bool {
let idx: usize = self.get_inner();
let Some(inner) = Self::slab_mut().get_mut::<T>(idx) else {
return false;
};
if !inner.get_alive() {
return false;
}
if *inner.get_value() == value {
return false;
}
inner.set_value(value);
inner.set_notifying(true);
let mut listeners: Vec<ListenerEntry> = Vec::new();
swap(inner.get_mut_listeners(), &mut listeners);
for (_id, listener) in listeners.iter_mut() {
listener();
}
if !Self::is_alive(self.get_inner()) {
if let Some(inner) = Self::slab_mut().get_mut::<T>(idx) {
inner.set_notifying(false);
inner.get_mut_removed_listener_ids().clear();
}
return true;
}
if let Some(inner) = Self::slab_mut().get_mut::<T>(idx)
&& inner.get_alive()
{
let removed: Vec<usize> = take(inner.get_mut_removed_listener_ids());
if !removed.is_empty() {
listeners.retain(|(listener_id, _): &ListenerEntry| !removed.contains(listener_id));
}
let new_listeners: &mut Vec<ListenerEntry> = inner.get_mut_listeners();
if new_listeners.is_empty() {
swap(new_listeners, &mut listeners);
} else {
listeners.append(new_listeners);
swap(new_listeners, &mut listeners);
}
inner.set_notifying(false);
}
true
}
fn push_dependent(inner: &mut SignalInner<T>, dynamic_id: usize) {
let deps: &mut Vec<usize> = inner.get_mut_dependents();
if let Some(last) = deps.last() {
if *last == dynamic_id {
return;
}
if !deps.contains(&dynamic_id) {
deps.push(dynamic_id);
}
} else {
deps.push(dynamic_id);
}
}
pub(crate) fn take_dependents(&self) -> Vec<usize> {
Self::slab_mut()
.get_mut::<T>(self.get_inner())
.map(|inner: &mut SignalInner<T>| take(inner.get_mut_dependents()))
.unwrap_or_default()
}
pub fn set(&self, value: T) {
if self.update(value) {
let dependents: Vec<usize> = self.take_dependents();
App::schedule_update(&dependents);
}
}
pub(crate) fn is_alive(idx: usize) -> bool {
Self::slab().is_alive(idx)
}
}
impl<T> Default for Signal<T>
where
T: Clone + Default + PartialEq + 'static,
{
fn default() -> Self {
Self::create(T::default())
}
}
impl<T> Clone for Signal<T>
where
T: Clone + PartialEq + 'static,
{
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Signal<T> where T: Clone + PartialEq + 'static {}
unsafe impl<T> Sync for SignalCell<T> where T: Clone + PartialEq + 'static {}
impl<T> SignalCell<T>
where
T: Clone + PartialEq + 'static,
{
pub const fn none() -> Self {
Self {
inner: UnsafeCell::new(None),
}
}
pub fn set(&self, signal: Signal<T>) {
unsafe {
let ptr: &mut Option<Signal<T>> = &mut *self.get_inner().get();
if ptr.is_none() {
*ptr = Some(signal);
}
}
}
pub fn loaded(&self) -> Option<Signal<T>> {
unsafe {
let ptr: &Option<Signal<T>> = &*self.get_inner().get();
*ptr
}
}
}
impl<T> Default for SignalCell<T>
where
T: Clone + PartialEq + 'static,
{
fn default() -> Self {
Self::new(UnsafeCell::new(None))
}
}
impl FireHandle {
pub fn new<F>(fire: F) -> Self
where
F: FnMut() + 'static,
{
let leaked: &'static mut Box<dyn FnMut()> =
Box::leak(Box::new(Box::new(fire) as Box<dyn FnMut()>));
let addr: usize = leaked as *mut Box<dyn FnMut()> as usize;
let mut handle: Self = Self { inner: 0 };
handle.set_inner(addr);
handle
}
pub unsafe fn fire(self) {
unsafe { Self::fire_at(self.get_inner()) };
}
pub unsafe fn fire_at(addr: usize) {
let ptr: *mut Box<dyn FnMut()> = addr as *mut Box<dyn FnMut()>;
unsafe { (&mut *ptr)() };
}
}
impl<F> From<F> for FireHandle
where
F: FnMut() + 'static,
{
fn from(fire: F) -> Self {
Self::new(fire)
}
}
impl From<FireHandle> for usize {
fn from(handle: FireHandle) -> Self {
handle.get_inner()
}
}
impl SignalSlab {
pub(crate) fn new() -> Self {
Self {
entries: Vec::new(),
}
}
pub(crate) fn insert<T>(&mut self, inner: SignalInner<T>) -> usize
where
T: Clone + PartialEq + 'static,
{
let boxed: Box<dyn AnySignalInner> = Box::new(inner);
let idx: usize = self.entries.len();
self.entries.push(boxed);
idx
}
pub(crate) fn get_mut<T>(&mut self, idx: usize) -> Option<&mut SignalInner<T>>
where
T: Clone + PartialEq + 'static,
{
self.entries
.get_mut(idx)?
.as_any_mut()
.downcast_mut::<SignalInner<T>>()
}
pub(crate) fn is_alive(&self, idx: usize) -> bool {
match self.entries.get(idx) {
Some(inner) => inner.alive(),
None => false,
}
}
}