Skip to main content

rosin_core/
reactive.rs

1#![allow(clippy::mutable_key_type)]
2#![allow(clippy::type_complexity)]
3//! Provides values that allow Rosin to track data dependencies automatically.
4//! - Use [`Var<T>`] for owned values inside your application state.
5//! - Use [`WeakVar<T>`] to give non-owning handles for those values to callbacks.
6//!
7//! Rosin records which variables are read while building the UI. When a [`Var`] is
8//! later written, it can efficiently update only the parts of the UI that depended on it.
9//!
10//! A [`WeakVar`] becomes invalid once the owning [`Var`] is dropped; most operations then return `None`.
11//!
12//! Dependency tracking is cheap, so these types can be used extensively.
13//!
14//! **Deadlock note:** like other synchronization primitives, accessing [`Var`]s across
15//! threads in an inconsistent order can cause lock inversion, potentially leading to deadlocks.
16//! Always acquire locks in a consistent order to prevent this.
17//!
18//! ## Example
19//! ```ignore
20//! struct State {
21//!     count: Var<u32>,
22//! }
23//!
24//! fn view(state: &State, ui: &mut Ui<State, WindowHandle>) {
25//!     let count = state.count.downgrade();
26//!
27//!     ui.node().children(move |ui| {
28//!         label(ui, id!(), count);
29//!
30//!         button(ui, id!(), "Count", move |_, _| {
31//!             if let Some(mut c) = count.write() {
32//!                 *c += 1;
33//!             }
34//!         });
35//!     });
36//! }
37//! ```
38//!
39//! ### `serde` feature
40//!
41//! When the `serde` feature is enabled, [`Var`] and [`WeakVar`] can be serialized and deserialized,
42//! but that must be done inside of a `serde_impl::serde_scope` in order to preserve dependencies.
43//!
44//! A [`WeakVar`] must be serialized or deserialized in the same scope as its associated [`Var`]. Scopes cannot be nested.
45
46use std::{
47    any::Any,
48    cell::{OnceCell, RefCell},
49    collections::HashMap,
50    fmt,
51    hash::{Hash, Hasher},
52    marker::PhantomData,
53    ops::{Deref, DerefMut},
54    rc::Rc,
55    sync::OnceLock,
56};
57
58use crate::sync::*;
59
60fn fmt_var_debug<T: fmt::Debug + Send + Sync + 'static>(slot: &Slot, generation: u64, struct_name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61    let mut ds = f.debug_struct(struct_name);
62
63    if slot.generation.load(Ordering::Acquire) != generation {
64        return ds.field("status", &"dropped").finish_non_exhaustive();
65    }
66
67    let Some(guard) = slot.value.try_read() else {
68        return ds.field("status", &"locked").finish_non_exhaustive();
69    };
70
71    if let Some(any_val) = guard.as_ref() {
72        if let Some(value) = any_val.downcast_ref::<T>() {
73            ds.field("value", value).finish_non_exhaustive()
74        } else {
75            ds.field("status", &"type_mismatch")
76                .field("expected", &std::any::type_name::<T>())
77                .finish_non_exhaustive()
78        }
79    } else {
80        ds.field("status", &"dropped").finish_non_exhaustive()
81    }
82}
83
84#[derive(Debug, Clone, Copy)]
85pub(crate) struct VarKey {
86    slot: &'static Slot,
87    generation: u64,
88}
89
90impl Eq for VarKey {}
91impl PartialEq for VarKey {
92    fn eq(&self, other: &Self) -> bool {
93        std::ptr::eq(self.slot, other.slot) && self.generation == other.generation
94    }
95}
96
97impl Hash for VarKey {
98    fn hash<H: Hasher>(&self, state: &mut H) {
99        std::ptr::hash(self.slot, state);
100        self.generation.hash(state);
101    }
102}
103
104/// A reactive value that affects on-screen content.
105///
106/// Viewports track when a [`Var`] is read, automatically determining which parts of the UI it affects.
107/// When the variable is modified, the required updates will be applied to the screen.
108///
109/// This derefs to [`WeakVar`] a non-owning, Copy handle.
110///
111/// Anything visible and dynamic should be stored in a [`Var`].
112///
113/// **Deadlock note:** like other synchronization primitives, accessing [`Var`]s across
114/// threads in an inconsistent order can cause lock inversion, potentially leading to deadlocks.
115/// Always acquire locks in a consistent order to prevent this.
116pub struct Var<T: Send + Sync + 'static>(pub(crate) WeakVar<T>);
117
118impl<T: Send + Sync + 'static> Deref for Var<T> {
119    type Target = WeakVar<T>;
120
121    fn deref(&self) -> &Self::Target {
122        &self.0
123    }
124}
125
126impl<T: Send + Sync + 'static> Drop for Var<T> {
127    fn drop(&mut self) {
128        let registry = self.0.registry;
129        let slot = self.0.slot;
130        let prev = slot.generation.fetch_add(1, Ordering::Release);
131        registry.write_count.fetch_add(1, Ordering::Release);
132
133        // If a WeakVar has a lock on the data, it will handle the cleanup when it's done.
134        slot.attempt_cleanup(registry, prev + 1);
135    }
136}
137
138impl<T: Send + Sync + 'static> From<T> for Var<T> {
139    fn from(value: T) -> Self {
140        Self::new(value)
141    }
142}
143
144impl<T: Send + Sync + Default + 'static> Default for Var<T> {
145    fn default() -> Self {
146        Self::new(T::default())
147    }
148}
149
150impl<T: fmt::Debug + Send + Sync + 'static> fmt::Debug for Var<T> {
151    /// Debug formatting is side-effect free, so viewports won't register the value as having been read from.
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        fmt_var_debug::<T>(self.slot, self.generation, "Var", f)
154    }
155}
156
157impl<T: Send + Sync + 'static> Var<T> {
158    /// Creates a new [`Var`] in the global registry with the specified initial value.
159    pub fn new(value: T) -> Self {
160        Self::new_in(Registry::global(), value)
161    }
162
163    /// Creates a new [`Var`] in the provided registry with the specified initial value.
164    pub(crate) fn new_in(registry: &'static Registry, value: T) -> Self {
165        let (slot, generation) = registry.alloc_slot();
166        *slot.value.write() = Some(Box::new(value));
167        slot.version.store(0, Ordering::Release);
168
169        Var(WeakVar {
170            registry,
171            slot,
172            generation,
173            ty: PhantomData,
174        })
175    }
176
177    /// Returns a read guard to the value if the [`Var`] is alive, marking it as read from.
178    pub fn read<'a>(&'a self) -> VarReadGuard<'a, T> {
179        WeakVar::read(self).unwrap() // Unwrap ok: we have a Var, so we know the value hasn't been dropped.
180    }
181
182    /// Returns a write guard to the value if the [`Var`] is alive.
183    ///
184    /// The guard handles marking the [`Var`] as written to and read from when it is dropped.
185    pub fn write<'a>(&'a self) -> VarWriteGuard<'a, T> {
186        WeakVar::write(self).unwrap() // Unwrap ok: we have a Var, so we know the value hasn't been dropped.
187    }
188
189    /// Returns the current version of the variable.
190    pub fn get_version(&self) -> u64 {
191        WeakVar::get_version(self).unwrap() // Unwrap ok: we have a Var, so we know the value hasn't been dropped.
192    }
193
194    /// Returns a clone of the stored value if it is still alive, marking it as read from.
195    pub fn get(&self) -> T
196    where
197        T: Clone,
198    {
199        WeakVar::get(self).unwrap() // Unwrap ok: we have a Var, so we know the value hasn't been dropped.
200    }
201
202    /// Sets the value of the variable, but only bumps the version if the value actually changed.
203    pub fn set(&self, new: T)
204    where
205        T: PartialEq,
206    {
207        WeakVar::set(self, new).unwrap() // Unwrap ok: we have a Var, so we know the value hasn't been dropped.
208    }
209
210    /// Replaces the value in the registry and returns the old value.
211    pub fn replace(&self, new: T) -> T {
212        WeakVar::replace(self, new).unwrap() // Unwrap ok: we have a Var, so we know the value hasn't been dropped.
213    }
214
215    /// Takes the current value, leaving [`Default::default()`] in its place.
216    pub fn take(&self) -> T
217    where
218        T: Default,
219    {
220        WeakVar::take(self).unwrap() // Unwrap ok: we have a Var, so we know the value hasn't been dropped.
221    }
222
223    /// Returns a [`WeakVar`] that references the same value without taking ownership.
224    ///
225    /// This is useful for storing or passing a handle to the value without keeping it alive.
226    /// The returned [`WeakVar`] can be cheaply copied and used to access or update the value
227    /// as long as the original [`Var`] is still alive.
228    pub fn downgrade(&self) -> WeakVar<T> {
229        self.0
230    }
231}
232
233/// A handle to a [`Var`] that implements Copy.
234///
235/// Intended to be stored in callbacks.
236pub struct WeakVar<T: Send + Sync + 'static> {
237    pub(crate) registry: &'static Registry,
238    pub(crate) slot: &'static Slot,
239    pub(crate) generation: u64,
240    pub(crate) ty: PhantomData<T>,
241}
242
243// Impl Copy even if `T` doesn't
244impl<T: Send + Sync + 'static> Copy for WeakVar<T> {}
245impl<T: Send + Sync + 'static> Clone for WeakVar<T> {
246    fn clone(&self) -> Self {
247        *self
248    }
249}
250
251impl<T: Send + Sync + 'static> Eq for WeakVar<T> {}
252impl<T: Send + Sync + 'static> PartialEq for WeakVar<T> {
253    fn eq(&self, other: &Self) -> bool {
254        std::ptr::eq(self.slot, other.slot) && self.generation == other.generation
255    }
256}
257
258impl<T: fmt::Debug + Send + Sync + 'static> fmt::Debug for WeakVar<T> {
259    /// Debug formatting is side-effect free, so viewports won't register the value as having been read from.
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        fmt_var_debug::<T>(self.slot, self.generation, "WeakVar", f)
262    }
263}
264
265impl<T: Send + Sync + 'static> WeakVar<T> {
266    /// Returns an opaque key representing the associated [`Var`].
267    pub(crate) fn get_key(&self) -> VarKey {
268        VarKey {
269            slot: self.slot,
270            generation: self.generation,
271        }
272    }
273
274    /// Checks if the associated Var has been dropped.
275    pub fn is_alive(&self) -> bool {
276        self.slot.generation.load(Ordering::Acquire) == self.generation
277    }
278
279    /// Returns the current version of the variable.
280    pub fn get_version(&self) -> Option<u64> {
281        if self.is_alive() {
282            Some(self.slot.version.load(Ordering::Acquire))
283        } else {
284            None
285        }
286    }
287
288    /// Returns a read guard to the value if the [`Var`] is alive, marking it as read from.
289    ///
290    /// Returns [`None`] if the [`Var`] has been destroyed.
291    pub fn read<'a>(&'a self) -> Option<VarReadGuard<'a, T>> {
292        let guard = self.slot.value.read();
293
294        if self.slot.generation.load(Ordering::Acquire) != self.generation {
295            drop(guard);
296            self.slot.attempt_cleanup(self.registry, self.generation + 1);
297            return None;
298        }
299
300        let guard = RwLockReadGuard::try_map(guard, |opt: &Option<Box<dyn Any + Send + Sync>>| {
301            let boxed = opt.as_ref()?;
302            (boxed.as_ref() as &dyn Any).downcast_ref::<T>()
303        })
304        .ok()?;
305
306        Some(VarReadGuard {
307            meta: VarGuardMeta {
308                registry: self.registry,
309                slot: self.slot,
310                generation: self.generation,
311            },
312            guard: Some(guard),
313            armed: true,
314            _marker: PhantomData,
315        })
316    }
317
318    /// Returns a write guard to the value if the [`Var`] is alive.
319    ///
320    /// The guard handles marking the [`Var`] as written to and read from when it is dropped.
321    ///
322    /// Returns [`None`] if the [`Var`] has been destroyed.
323    pub fn write<'a>(&'a self) -> Option<VarWriteGuard<'a, T>> {
324        let guard = self.slot.value.write();
325
326        if self.slot.generation.load(Ordering::Acquire) != self.generation {
327            drop(guard);
328            self.slot.attempt_cleanup(self.registry, self.generation + 1);
329            return None;
330        }
331
332        let guard = RwLockWriteGuard::try_map(guard, |opt: &mut Option<Box<dyn Any + Send + Sync>>| {
333            let boxed = opt.as_mut()?;
334            (boxed.as_mut() as &mut dyn Any).downcast_mut::<T>()
335        })
336        .ok()?;
337
338        Some(VarWriteGuard {
339            meta: VarGuardMeta {
340                registry: self.registry,
341                slot: self.slot,
342                generation: self.generation,
343            },
344            guard: Some(guard),
345            changed: true,
346            armed: true,
347            _marker: PhantomData,
348        })
349    }
350
351    /// Returns a clone of the stored value if it is still alive, marking it as read from. Returns [`None`] otherwise.
352    pub fn get(&self) -> Option<T>
353    where
354        T: Clone,
355    {
356        self.read().map(|guard| (*guard).clone())
357    }
358
359    /// Returns the value if the [`Var`] is alive, or `default` if it has been dropped.
360    ///
361    /// This method evaluates `default` eagerly, even if the value is alive.
362    /// Use [`WeakVar::get_or_else`] to evaluate the default lazily.
363    pub fn get_or(&self, default: T) -> T
364    where
365        T: Clone,
366    {
367        self.get().unwrap_or(default)
368    }
369
370    /// Returns a clone of the value if alive, or computes a default from the given closure.
371    ///
372    /// Unlike [`WeakVar::get_or`], this only evaluates `default` if the [`Var`] is dead.
373    pub fn get_or_else(&self, default: impl FnOnce() -> T) -> T
374    where
375        T: Clone,
376    {
377        if let Some(val) = self.get() { val } else { default() }
378    }
379
380    /// Sets the value of the variable, but only bumps the version if the value actually changed.
381    ///
382    /// Returns `None` if the [`Var`] has been destroyed, `Some(())` otherwise.
383    pub fn set(&self, new: T) -> Option<()>
384    where
385        T: PartialEq,
386    {
387        let mut guard = self.write()?;
388        if *guard != new {
389            *guard = new;
390        } else {
391            guard.cancel_change();
392        }
393        Some(())
394    }
395
396    /// Replaces the value in the registry and returns the old value.
397    pub fn replace(&self, new: T) -> Option<T> {
398        let mut guard = self.write()?;
399        Some(std::mem::replace(&mut *guard, new))
400    }
401
402    /// Takes the current value, leaving [`Default::default()`] in its place.
403    pub fn take(&self) -> Option<T>
404    where
405        T: Default,
406    {
407        let mut guard = self.write()?;
408        Some(std::mem::take(&mut *guard))
409    }
410
411    /// Marks a [`Var`] as if it had been read from.
412    ///
413    /// This is a no-op if the [`Var`] has been destroyed.
414    pub fn mark_read(&self) {
415        if self.is_alive() {
416            let key = self.get_key();
417            let version = self.slot.version.load(Ordering::Acquire);
418            notify_scopes(key, version);
419        }
420    }
421}
422
423/// Used to track when [`Var`]s have been read from and written to.
424///
425/// Most apps will not need to use this.
426#[derive(Debug, Default, Clone)]
427pub struct DependencyMap {
428    pub(crate) deps: HashMap<VarKey, u64>,
429}
430
431impl DependencyMap {
432    pub(crate) fn record(&mut self, key: VarKey, version: u64) {
433        self.deps.entry(key).and_modify(|v| *v = std::cmp::max(*v, version)).or_insert(version);
434    }
435
436    /// Removes all dependencies from the map.
437    pub fn clear(&mut self) {
438        self.deps.clear();
439    }
440
441    /// Returns the map with all dependencies removed.
442    pub fn cleared(mut self) -> Self {
443        self.deps.clear();
444        self
445    }
446
447    /// Returns `true` as soon as it finds any var in the map that's been dropped, or whose current_version != last_seen.
448    ///
449    /// Does not update the last seen versions.
450    pub fn any_changed(&self) -> bool {
451        for (key, last_seen) in self.deps.iter() {
452            let current = key.slot.version.load(Ordering::Acquire);
453            if key.slot.generation.load(Ordering::Acquire) != key.generation {
454                return true;
455            }
456            if current != *last_seen {
457                return true;
458            }
459        }
460        false
461    }
462
463    /// Returns `true` if any var in the map changed since last time this function was called, or if any var was dropped.
464    ///
465    /// Updates the last seen versions and removes dropped vars.
466    pub fn any_changed_update(&mut self) -> bool {
467        let mut changed = false;
468
469        self.deps.retain(|key, last_seen| {
470            // If the var was dropped, remove it and report changed.
471            if key.slot.generation.load(Ordering::Acquire) != key.generation {
472                changed = true;
473                return false;
474            }
475
476            // Otherwise update the stored value if it changed.
477            let current = key.slot.version.load(Ordering::Acquire);
478            if current != *last_seen {
479                changed = true;
480                *last_seen = current;
481            }
482
483            true
484        });
485
486        changed
487    }
488
489    /// Marks all dependencies in the map as read from.
490    pub fn mark_read(&self) {
491        for key in self.deps.keys() {
492            // Only mark if still alive
493            if key.slot.generation.load(Ordering::Acquire) == key.generation {
494                let version = key.slot.version.load(Ordering::Acquire);
495                notify_scopes(*key, version);
496            }
497        }
498    }
499
500    /// Any [`Var`] that is read from during the provided closure will be added to the dependency map.
501    /// Nested scopes are supported; any reads will be logged in all scopes.
502    ///
503    /// NOTE: Dependencies are only recorded when a read or write guard drops,
504    /// so it's important that guards don't escape the scope.
505    pub fn read_scope(self, func: impl FnMut()) -> Self {
506        scopes_with(|stack| stack.borrow_mut().push(self));
507
508        // If the provided closure panics, clean up so the stack is never in an inconsistent state.
509        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(func));
510
511        let deps = scopes_with(|stack| stack.borrow_mut().pop().unwrap()); // Unwrap ok: we pushed an element on to the stack, so we know it will be there to pop.
512
513        if let Err(panic) = result {
514            std::panic::resume_unwind(panic);
515        }
516
517        deps
518    }
519}
520
521/// Identity metadata for a Var, stored in guards.
522///
523/// We intentionally don't store `WeakVar<T>` in mapped guards.
524/// After mapping, `T` would no longer represent the actual stored type inside the slot.
525#[derive(Clone, Copy)]
526struct VarGuardMeta {
527    registry: &'static Registry,
528    slot: &'static Slot,
529    generation: u64,
530}
531
532impl VarGuardMeta {
533    #[inline]
534    fn key(&self) -> VarKey {
535        VarKey {
536            slot: self.slot,
537            generation: self.generation,
538        }
539    }
540}
541
542/// A read lock guard returned by [`Var::read`] or [`WeakVar::read`].
543///
544/// While the guard is alive, it holds a shared lock on the underlying value.
545/// When the guard is dropped, the variable is registered as having been read
546/// so viewports can track dependencies.
547///
548/// Mapped guards produced by [`VarReadGuard::map`] and friends preserve the same
549/// behavior: dependency tracking happens when the final mapped guard is dropped.
550pub struct VarReadGuard<'a, T: ?Sized + Send + Sync + 'static> {
551    meta: VarGuardMeta,
552    guard: Option<MappedRwLockReadGuard<'a, T>>,
553    armed: bool,
554    _marker: PhantomData<*const ()>, // !Send + !Sync
555}
556
557impl<'a, T: ?Sized + Send + Sync + 'static> Deref for VarReadGuard<'a, T> {
558    type Target = T;
559    fn deref(&self) -> &Self::Target {
560        self.guard.as_ref().unwrap() // Unwrap ok: The inner guard is only `None` after being consumed by `Drop`.
561    }
562}
563
564impl<'a, T: ?Sized + Send + Sync + 'static> Drop for VarReadGuard<'a, T> {
565    fn drop(&mut self) {
566        if !self.armed {
567            return;
568        }
569
570        let version = self.meta.slot.version.load(Ordering::Acquire);
571        drop(self.guard.take());
572        notify_scopes(self.meta.key(), version);
573        if self.meta.slot.generation.load(Ordering::Acquire) != self.meta.generation {
574            self.meta.slot.attempt_cleanup(self.meta.registry, self.meta.generation + 1);
575        }
576    }
577}
578
579impl<'a, T: ?Sized + Send + Sync + 'static> VarReadGuard<'a, T> {
580    /// Maps a read guard to a subfield, like `RwLockReadGuard::map`.
581    ///
582    /// This is an associated function that needs to be used as `VarReadGuard::map(...)`.
583    pub fn map<U: ?Sized + Send + Sync + 'static>(mut this: Self, f: impl FnOnce(&T) -> &U) -> VarReadGuard<'a, U> {
584        let meta = this.meta;
585        let guard = this.guard.take().expect("mapped guard is missing");
586
587        // If `f` panics, `this` will be dropped with `armed = true`, notifying once.
588        let guard = MappedRwLockReadGuard::map(guard, f);
589
590        // Disarm so we don't double-notify on Drop.
591        this.armed = false;
592
593        VarReadGuard {
594            meta,
595            guard: Some(guard),
596            armed: true,
597            _marker: PhantomData,
598        }
599    }
600
601    /// Fallible map, like `RwLockReadGuard::try_map`.
602    ///
603    /// This is an associated function that needs to be used as `VarReadGuard::try_map(...)`.
604    pub fn try_map<U: ?Sized + Send + Sync + 'static>(mut this: Self, f: impl FnOnce(&T) -> Option<&U>) -> Result<VarReadGuard<'a, U>, VarReadGuard<'a, T>> {
605        let meta = this.meta;
606        let guard = this.guard.take().expect("mapped guard is missing");
607
608        match MappedRwLockReadGuard::try_map(guard, f) {
609            Ok(mapped) => {
610                // Disarm so we don't double-notify on Drop.
611                this.armed = false;
612
613                Ok(VarReadGuard {
614                    meta,
615                    guard: Some(mapped),
616                    armed: true,
617                    _marker: PhantomData,
618                })
619            }
620            Err(original) => {
621                // Put the original guard back and return it.
622                this.guard = Some(original);
623                Err(this)
624            }
625        }
626    }
627
628    /// Fallible map, like `RwLockReadGuard::try_map`, but returns the error produced by the mapping function.
629    ///
630    /// This is an associated function that needs to be used as `VarReadGuard::try_map_or_err(...)`.
631    pub fn try_map_or_err<U: ?Sized + Send + Sync + 'static, E>(
632        mut this: Self,
633        f: impl FnOnce(&T) -> Result<&U, E>,
634    ) -> Result<VarReadGuard<'a, U>, (VarReadGuard<'a, T>, E)> {
635        let meta = this.meta;
636        let guard = this.guard.take().expect("mapped guard is missing");
637
638        let mut err: Option<E> = None;
639
640        let mapped = MappedRwLockReadGuard::try_map(guard, |t| match f(t) {
641            Ok(r) => Some(r),
642            Err(e) => {
643                err = Some(e);
644                None
645            }
646        });
647
648        match mapped {
649            Ok(mapped) => {
650                // Disarm so we don't double-notify on Drop.
651                this.armed = false;
652
653                Ok(VarReadGuard {
654                    meta,
655                    guard: Some(mapped),
656                    armed: true,
657                    _marker: PhantomData,
658                })
659            }
660            Err(original) => {
661                // Put the original guard back and return it along with the error.
662                this.guard = Some(original);
663                let e = err.expect("try_map_or_err failed without producing an error");
664                Err((this, e))
665            }
666        }
667    }
668}
669
670/// A write lock guard returned by [`Var::write`] or [`WeakVar::write`].
671///
672/// While the guard is alive, it holds an exclusive lock on the underlying value.
673/// When the guard is dropped, the variable is registered as having been read
674/// and, by default, written, which bumps the variable's version and triggers
675/// dependent UI updates.
676///
677/// If no observable change occurred, call [`VarWriteGuard::cancel_change`]
678/// before dropping the guard to prevent the version bump.
679///
680/// Mapped guards produced by [`VarWriteGuard::map`] and friends preserve the same
681/// behavior: the write is committed (unless canceled) when the final mapped guard
682/// is dropped.
683pub struct VarWriteGuard<'a, T: ?Sized + Send + Sync + 'static> {
684    meta: VarGuardMeta,
685    guard: Option<MappedRwLockWriteGuard<'a, T>>,
686    changed: bool,
687    armed: bool,
688    _marker: PhantomData<*const ()>, // !Send + !Sync
689}
690
691impl<'a, T: ?Sized + Send + Sync + 'static> Deref for VarWriteGuard<'a, T> {
692    type Target = T;
693    fn deref(&self) -> &Self::Target {
694        self.guard.as_ref().unwrap() // Unwrap ok: The inner guard is only `None` after being consumed by `Drop`.
695    }
696}
697
698impl<'a, T: ?Sized + Send + Sync + 'static> DerefMut for VarWriteGuard<'a, T> {
699    fn deref_mut(&mut self) -> &mut Self::Target {
700        self.guard.as_mut().unwrap() // Unwrap ok: The inner guard is only `None` after being consumed by `Drop`.
701    }
702}
703
704impl<'a, T: ?Sized + Send + Sync + 'static> Drop for VarWriteGuard<'a, T> {
705    fn drop(&mut self) {
706        if !self.armed {
707            return;
708        }
709
710        let version = if self.changed {
711            self.meta.registry.write_count.fetch_add(1, Ordering::Release);
712            self.meta.slot.version.fetch_add(1, Ordering::Release) + 1
713        } else {
714            self.meta.slot.version.load(Ordering::Acquire)
715        };
716
717        drop(self.guard.take());
718        notify_scopes(self.meta.key(), version);
719        if self.meta.slot.generation.load(Ordering::Acquire) != self.meta.generation {
720            self.meta.slot.attempt_cleanup(self.meta.registry, self.meta.generation + 1);
721        }
722    }
723}
724
725impl<'a, T: ?Sized + Send + Sync + 'static> VarWriteGuard<'a, T> {
726    /// Prevents the dependency tracking system from registering a change.
727    ///
728    /// When this is called, the [`Var`]'s version number will not be incremented upon `Drop`.
729    pub fn cancel_change(&mut self) {
730        self.changed = false;
731    }
732
733    /// Maps a write guard to a subfield, like `RwLockWriteGuard::map`.
734    ///
735    /// This is an associated function that needs to be used as `VarWriteGuard::map(...)`.
736    pub fn map<U: ?Sized + Send + Sync + 'static>(mut this: Self, f: impl FnOnce(&mut T) -> &mut U) -> VarWriteGuard<'a, U> {
737        let meta = this.meta;
738        let changed = this.changed;
739        let guard = this.guard.take().expect("mapped guard is missing");
740
741        // If `f` panics, `this` will be dropped with `armed = true`, notifying once.
742        let guard = MappedRwLockWriteGuard::map(guard, f);
743
744        // Disarm so we don't double-notify on Drop.
745        this.armed = false;
746
747        VarWriteGuard {
748            meta,
749            guard: Some(guard),
750            changed,
751            armed: true,
752            _marker: PhantomData,
753        }
754    }
755
756    /// Fallible map, like `RwLockWriteGuard::try_map`.
757    ///
758    /// This is an associated function that needs to be used as `VarWriteGuard::try_map(...)`.
759    pub fn try_map<U: ?Sized + Send + Sync + 'static>(
760        mut this: Self,
761        f: impl FnOnce(&mut T) -> Option<&mut U>,
762    ) -> Result<VarWriteGuard<'a, U>, VarWriteGuard<'a, T>> {
763        let meta = this.meta;
764        let changed = this.changed;
765        let guard = this.guard.take().expect("mapped guard is missing");
766
767        match MappedRwLockWriteGuard::try_map(guard, f) {
768            Ok(mapped) => {
769                // Disarm so we don't double-notify on Drop.
770                this.armed = false;
771
772                Ok(VarWriteGuard {
773                    meta,
774                    guard: Some(mapped),
775                    changed,
776                    armed: true,
777                    _marker: PhantomData,
778                })
779            }
780            Err(original) => {
781                // Put the original guard back and return it.
782                this.guard = Some(original);
783                Err(this)
784            }
785        }
786    }
787
788    /// Fallible map, like `RwLockWriteGuard::try_map`, but returns the error produced by the mapping function.
789    ///
790    /// This is an associated function that needs to be used as `VarWriteGuard::try_map_or_err(...)`.
791    pub fn try_map_or_err<U: ?Sized + Send + Sync + 'static, E>(
792        mut this: Self,
793        f: impl FnOnce(&mut T) -> Result<&mut U, E>,
794    ) -> Result<VarWriteGuard<'a, U>, (VarWriteGuard<'a, T>, E)> {
795        let meta = this.meta;
796        let changed = this.changed;
797        let guard = this.guard.take().expect("mapped guard is missing");
798
799        let mut err: Option<E> = None;
800
801        let mapped = MappedRwLockWriteGuard::try_map(guard, |t| match f(t) {
802            Ok(r) => Some(r),
803            Err(e) => {
804                err = Some(e);
805                None
806            }
807        });
808
809        match mapped {
810            Ok(mapped) => {
811                // Disarm so we don't double-notify on Drop.
812                this.armed = false;
813
814                Ok(VarWriteGuard {
815                    meta,
816                    guard: Some(mapped),
817                    changed,
818                    armed: true,
819                    _marker: PhantomData,
820                })
821            }
822            Err(original) => {
823                // Put the original guard back and return it along with the error.
824                this.guard = Some(original);
825                let e = err.expect("try_map_or_err failed without producing an error");
826                Err((this, e))
827            }
828        }
829    }
830}
831
832#[derive(Debug)]
833pub(crate) struct Slot {
834    pub(crate) generation: AtomicU64,
835    pub(crate) version: AtomicU64,
836    pub(crate) value: RwLock<Option<Box<dyn Any + Send + Sync>>>,
837}
838
839impl Slot {
840    fn new() -> Self {
841        Self {
842            generation: AtomicU64::new(0),
843            version: AtomicU64::new(0),
844            value: RwLock::new(None),
845        }
846    }
847
848    fn attempt_cleanup(&'static self, registry: &'static Registry, expected_gen: u64) {
849        // We only recycle the slot if we are the thread that transitions the value from Some -> None.
850        if let Some(mut guard) = self.value.try_write()
851            && self.generation.load(Ordering::Acquire) == expected_gen
852            && guard.is_some()
853        {
854            let value = guard.take();
855            drop(guard);
856
857            struct RecycleGuard {
858                registry: &'static Registry,
859                slot: &'static Slot,
860            }
861
862            impl Drop for RecycleGuard {
863                fn drop(&mut self) {
864                    self.registry.recycle_slot(self.slot);
865                }
866            }
867
868            let _recycle_guard = RecycleGuard { registry, slot: self };
869            drop(value);
870        }
871    }
872}
873
874#[cfg(not(loom))]
875std::thread_local! {
876    /// A stack of all current read_scopes used by viewports for dependency tracking.
877    static READ_SCOPES: OnceCell<Rc<RefCell<Vec<DependencyMap>>>> = const { OnceCell::new() };
878}
879
880// Loom doesn't support const in thread_local macro
881#[cfg(loom)]
882loom::thread_local! {
883    static READ_SCOPES: OnceCell<Rc<RefCell<Vec<DependencyMap>>>> = OnceCell::new();
884}
885
886/// Notify read scopes of a dependency.
887fn notify_scopes(key: VarKey, version: u64) {
888    scopes_with(|stack| {
889        for vars in stack.borrow_mut().iter_mut() {
890            vars.record(key, version);
891        }
892    });
893}
894
895/// Access the thread-local stack of read scopes.
896fn scopes_with<F, R>(f: F) -> R
897where
898    F: FnOnce(&Rc<RefCell<Vec<DependencyMap>>>) -> R,
899{
900    READ_SCOPES.with(|once| f(once.get_or_init(|| Rc::new(RefCell::new(Vec::new())))))
901}
902
903/// Returns a clone of the current thread-local `READ_SCOPES` Rc, initializing it if needed.
904/// This is used internally by the hot-reload feature.
905#[doc(hidden)]
906pub fn read_scopes_rc() -> Rc<RefCell<Vec<DependencyMap>>> {
907    scopes_with(|stack| stack.clone())
908}
909
910/// Tries to initialize the thread-local `READ_SCOPES`, returning `true` if it succeeded.
911/// This is used internally by the hot-reload feature.
912#[doc(hidden)]
913pub fn try_init_read_scopes(rc: Rc<RefCell<Vec<DependencyMap>>>) -> bool {
914    READ_SCOPES.with(|cell| cell.set(rc).is_ok())
915}
916
917/// The container for all reactive variables. Most code won't need to interact with this directly.
918#[doc(hidden)]
919pub struct Registry {
920    free_slots: Mutex<Vec<&'static Slot>>,
921    write_count: AtomicU64,
922}
923
924static GLOBAL_REGISTRY: OnceLock<&'static Registry> = OnceLock::new();
925
926impl Default for Registry {
927    fn default() -> Self {
928        Self {
929            free_slots: Mutex::new(Vec::new()),
930            write_count: AtomicU64::new(0),
931        }
932    }
933}
934
935impl Registry {
936    /// Gets a reference to the global [`Registry`] instance, initializing it if needed.
937    pub fn global() -> &'static Self {
938        GLOBAL_REGISTRY.get_or_init(|| Box::leak(Box::new(Self::default())))
939    }
940
941    /// Initialize the global [`Registry`] instance, if it isn't already.
942    pub fn set_global(&'static self) -> bool {
943        GLOBAL_REGISTRY.set(self).is_ok()
944    }
945
946    /// Returns the total number of committed writes for all variables in the registry.
947    pub fn write_count(&self) -> u64 {
948        self.write_count.load(Ordering::Acquire)
949    }
950
951    /// Returns an empty [`Slot`] and generation counter.
952    fn alloc_slot(&self) -> (&'static Slot, u64) {
953        let mut free = self.free_slots.lock();
954        if let Some(slot) = free.pop() {
955            let next_gen = slot.generation.fetch_add(1, Ordering::Release) + 1;
956            (slot, next_gen)
957        } else {
958            let slot = Box::leak(Box::new(Slot::new()));
959            (slot, 0)
960        }
961    }
962
963    /// Queues up a [`Slot`] to be re-used later.
964    fn recycle_slot(&self, slot: &'static Slot) {
965        let mut free = self.free_slots.lock();
966        free.push(slot);
967    }
968}
969
970#[cfg(feature = "serde")]
971pub mod serde_impl {
972    use super::*;
973    use serde::{Deserialize, Deserializer, Serialize, Serializer, de, ser};
974    use std::cell::RefCell;
975
976    type SlotId = u64;
977
978    struct DeEntry {
979        slot: &'static Slot,
980        pending_generation: u64,
981        initialized: bool,
982    }
983
984    struct SerdeContext {
985        ser_map: HashMap<VarKey, SlotId>,
986        de_map: HashMap<SlotId, DeEntry>,
987        next_id: SlotId,
988    }
989
990    impl SerdeContext {
991        fn new() -> Self {
992            Self {
993                ser_map: HashMap::new(),
994                de_map: HashMap::new(),
995                next_id: 1, // 0 reserved for dead vars
996            }
997        }
998
999        fn cleanup_uninitialized(self) {
1000            let registry = Registry::global();
1001
1002            for entry in self.de_map.into_values() {
1003                if entry.initialized {
1004                    continue;
1005                }
1006
1007                entry.slot.generation.store(entry.pending_generation + 1, Ordering::Release);
1008                *entry.slot.value.write() = None;
1009                entry.slot.version.store(0, Ordering::Release);
1010                registry.recycle_slot(entry.slot);
1011            }
1012        }
1013    }
1014
1015    thread_local! {
1016        static CONTEXT: RefCell<Option<SerdeContext>> = const { RefCell::new(None) };
1017    }
1018
1019    // TODO - this can be made more testable by accepting a registry and storing it in the SerdeContext
1020    pub fn serde_scope<R>(f: impl FnOnce() -> R) -> R {
1021        CONTEXT.with(|ctx| {
1022            let mut borrow = ctx.borrow_mut();
1023            if borrow.is_some() {
1024                panic!("nested serde_scope is not supported");
1025            }
1026            *borrow = Some(SerdeContext::new());
1027        });
1028
1029        struct ScopeGuard;
1030        impl Drop for ScopeGuard {
1031            fn drop(&mut self) {
1032                CONTEXT.with(|ctx| {
1033                    let mut borrow = ctx.borrow_mut();
1034                    if let Some(ctx) = borrow.take() {
1035                        ctx.cleanup_uninitialized();
1036                    }
1037                });
1038            }
1039        }
1040
1041        let _guard = ScopeGuard;
1042        f()
1043    }
1044
1045    fn resolve_id<S: Serializer, T>(v: &WeakVar<T>) -> Result<SlotId, S::Error>
1046    where
1047        T: Send + Sync + 'static,
1048    {
1049        if !v.is_alive() {
1050            return Ok(0);
1051        }
1052
1053        CONTEXT.with(|cell| {
1054            let mut borrow = cell.borrow_mut();
1055            let ctx = borrow.as_mut().ok_or_else(|| ser::Error::custom("Serialize called outside of a scope"))?;
1056
1057            let key = v.get_key();
1058            Ok(*ctx.ser_map.entry(key).or_insert_with(|| {
1059                let id = ctx.next_id;
1060                ctx.next_id += 1;
1061                id
1062            }))
1063        })
1064    }
1065
1066    fn resolve_handle<'de, D: Deserializer<'de>, T>(id: SlotId) -> Result<WeakVar<T>, D::Error>
1067    where
1068        T: Send + Sync + 'static,
1069    {
1070        let registry = Registry::global();
1071
1072        if id == 0 {
1073            static DEAD_SLOT: OnceLock<Slot> = OnceLock::new();
1074            let slot = DEAD_SLOT.get_or_init(|| Slot {
1075                generation: AtomicU64::new(1),
1076                version: AtomicU64::new(0),
1077                value: RwLock::new(None),
1078            });
1079
1080            return Ok(WeakVar {
1081                registry,
1082                slot,
1083                generation: 0,
1084                ty: PhantomData,
1085            });
1086        }
1087
1088        let (slot, pending_generation) = CONTEXT.with(|cell| {
1089            let mut borrow = cell.borrow_mut();
1090            let ctx = borrow.as_mut().ok_or_else(|| de::Error::custom("Deserialize called outside of a scope"))?;
1091
1092            if let Some(entry) = ctx.de_map.get(&id) {
1093                Ok((entry.slot, entry.pending_generation))
1094            } else {
1095                let (slot, generation) = registry.alloc_slot();
1096                let pending_generation = generation + 1;
1097
1098                ctx.de_map.insert(
1099                    id,
1100                    DeEntry {
1101                        slot,
1102                        pending_generation,
1103                        initialized: false,
1104                    },
1105                );
1106
1107                Ok((slot, pending_generation))
1108            }
1109        })?;
1110
1111        Ok(WeakVar {
1112            registry,
1113            slot,
1114            generation: pending_generation,
1115            ty: PhantomData,
1116        })
1117    }
1118
1119    impl<T> Serialize for Var<T>
1120    where
1121        T: Serialize + Send + Sync + 'static,
1122    {
1123        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1124        where
1125            S: Serializer,
1126        {
1127            if let Some(guard) = self.0.read() {
1128                let id = resolve_id::<S, T>(&self.0)?;
1129                (id, Some(&*guard)).serialize(serializer)
1130            } else {
1131                (0u64, Option::<T>::None).serialize(serializer)
1132            }
1133        }
1134    }
1135
1136    impl<'de, T> Deserialize<'de> for Var<T>
1137    where
1138        T: Deserialize<'de> + Send + Sync + 'static,
1139    {
1140        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1141        where
1142            D: Deserializer<'de>,
1143        {
1144            let (id, maybe_value) = <(SlotId, Option<T>)>::deserialize(deserializer)?;
1145
1146            let Some(val) = maybe_value else {
1147                let handle = resolve_handle::<D, T>(0)?;
1148                return Ok(Var(handle));
1149            };
1150
1151            let handle = resolve_handle::<D, T>(id)?;
1152            *handle.slot.value.write() = Some(Box::new(val));
1153            handle.slot.version.store(0, Ordering::Release);
1154            handle.slot.generation.store(handle.generation, Ordering::Release);
1155
1156            CONTEXT.with(|cell| {
1157                let mut borrow = cell.borrow_mut();
1158                let ctx = borrow.as_mut().expect("Deserialize called outside of a scope");
1159                if let Some(entry) = ctx.de_map.get_mut(&id) {
1160                    entry.initialized = true;
1161                }
1162            });
1163
1164            Ok(Var(handle))
1165        }
1166    }
1167
1168    impl<T> Serialize for WeakVar<T>
1169    where
1170        T: Send + Sync + 'static,
1171    {
1172        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1173        where
1174            S: Serializer,
1175        {
1176            resolve_id::<S, T>(self)?.serialize(serializer)
1177        }
1178    }
1179
1180    impl<'de, T> Deserialize<'de> for WeakVar<T>
1181    where
1182        T: Send + Sync + 'static,
1183    {
1184        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1185        where
1186            D: Deserializer<'de>,
1187        {
1188            let id = SlotId::deserialize(deserializer)?;
1189            resolve_handle::<D, T>(id)
1190        }
1191    }
1192}