Skip to main content

reactive_graph/
diagnostics.rs

1//! By default, attempting to [`Track`](crate::traits::Track) a signal when you are not in a
2//! reactive tracking context will cause a warning when you are in debug mode.
3//!
4//! In some cases, this warning is a false positive. For example, inside an event listener in a
5//! user interface, you never want to read from a signal reactively; the event listener should run
6//! when the event fires, not when a signal read in the event listener changes.
7//!
8//! This module provides utilities to suppress those warnings by entering a
9//! [`SpecialNonReactiveZone`].
10
11/// Marks an execution block that is known not to be reactive, and suppresses warnings.
12#[derive(Debug)]
13pub struct SpecialNonReactiveZone;
14
15/// Exits the "special non-reactive zone" when dropped.
16#[derive(Debug)]
17pub struct SpecialNonReactiveZoneGuard(bool);
18
19use pin_project_lite::pin_project;
20use std::{
21    cell::Cell,
22    future::Future,
23    pin::Pin,
24    task::{Context, Poll},
25};
26
27thread_local! {
28    static IS_SPECIAL_ZONE: Cell<bool> = const { Cell::new(false) };
29}
30
31impl SpecialNonReactiveZone {
32    /// Suppresses warnings about non-reactive accesses until the guard is dropped.
33    pub fn enter() -> SpecialNonReactiveZoneGuard {
34        let prev = IS_SPECIAL_ZONE.replace(true);
35        SpecialNonReactiveZoneGuard(prev)
36    }
37
38    #[cfg(all(debug_assertions, feature = "effects"))]
39    #[inline(always)]
40    pub(crate) fn is_inside() -> bool {
41        if cfg!(debug_assertions) {
42            IS_SPECIAL_ZONE.get()
43        } else {
44            false
45        }
46    }
47}
48
49impl Drop for SpecialNonReactiveZoneGuard {
50    fn drop(&mut self) {
51        IS_SPECIAL_ZONE.set(self.0);
52    }
53}
54
55pin_project! {
56    #[doc(hidden)]
57    pub struct SpecialNonReactiveFuture<Fut> {
58        #[pin]
59        inner: Fut
60    }
61}
62
63impl<Fut> SpecialNonReactiveFuture<Fut> {
64    pub fn new(inner: Fut) -> Self {
65        Self { inner }
66    }
67}
68
69impl<Fut> Future for SpecialNonReactiveFuture<Fut>
70where
71    Fut: Future,
72{
73    type Output = Fut::Output;
74
75    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
76        #[cfg(debug_assertions)]
77        let _rw = SpecialNonReactiveZone::enter();
78        let this = self.project();
79        this.inner.poll(cx)
80    }
81}
82
83thread_local! {
84    static SUPPRESS_RESOURCE_LOAD: Cell<bool> = const { Cell::new(false) };
85}
86
87#[doc(hidden)]
88pub fn suppress_resource_load(suppress: bool) {
89    SUPPRESS_RESOURCE_LOAD.with(|w| w.set(suppress));
90}
91
92#[doc(hidden)]
93pub fn is_suppressing_resource_load() -> bool {
94    SUPPRESS_RESOURCE_LOAD.with(|w| w.get())
95}