use std::any::TypeId;
use std::cell::{Cell, RefCell};
use rustc_hash::FxHashMap;
use super::invalidation::{SignalTrackingContext, SubscriberRegistry};
use super::owner::{OwnerArena, OwnerId};
use super::runtime::{EffectId, EffectReads, Runtime, SignalId};
use super::storage::SignalStorage;
use crate::deferred::DeferredQueue;
thread_local! {
static REACTIVE: ReactiveState = ReactiveState::default();
}
pub(crate) fn with_reactive<R>(f: impl FnOnce(&ReactiveState) -> R) -> R {
REACTIVE.with(f)
}
#[derive(Default)]
pub(crate) struct ReactiveState {
pub(crate) owners: RefCell<OwnerArena>,
pub(crate) current_owner: Cell<Option<OwnerId>>,
pub(crate) root_owner: Cell<Option<OwnerId>>,
pub(crate) pending_disposals: DeferredQueue<OwnerId>,
pub(crate) tracking_context: RefCell<Vec<SignalTrackingContext>>,
pub(crate) storage: RefCell<SignalStorage>,
pub(crate) runtime: RefCell<Runtime>,
pub(crate) effect_tracking: RefCell<Vec<(EffectId, EffectReads)>>,
pub(crate) subscribers: RefCell<SubscriberRegistry>,
pub(crate) globals: RefCell<FxHashMap<usize, (SignalId, TypeId)>>,
pub(crate) batch_depth: Cell<u32>,
pub(crate) flushing: Cell<bool>,
}
pub(crate) fn reset() {
with_reactive(|reactive| {
let ReactiveState {
owners,
current_owner,
root_owner,
pending_disposals,
tracking_context,
storage,
runtime,
effect_tracking,
subscribers,
globals,
batch_depth,
flushing,
} = reactive;
let successor = storage.borrow().successor();
drop(storage.replace(successor));
runtime.take();
owners.take();
pending_disposals.clear();
subscribers.take();
globals.take();
effect_tracking.take();
tracking_context.take();
current_owner.take();
root_owner.take();
batch_depth.take();
flushing.take();
});
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reactive::owner::{dispose_owner, with_owner};
use crate::reactive::signal::create_signal;
#[test]
fn a_disposal_queued_while_the_runtime_is_being_forgotten_is_forgotten_too() {
#[derive(Clone)]
struct DisposesOnDrop(OwnerId);
impl Drop for DisposesOnDrop {
fn drop(&mut self) {
dispose_owner(self.0);
}
}
crate::reactive::owner::create_root_owner();
let ((), scope) = with_owner(|| ());
let _stored = create_signal(DisposesOnDrop(scope));
reset();
assert!(
with_reactive(|reactive| reactive.pending_disposals.is_empty()),
"a value dropped by the reset queued a disposal the reset had already \
emptied — and owner ids restart from zero, so it names a live owner \
in the next App"
);
}
}