floem_reactive/
signal.rs

1use std::{
2    any::Any,
3    cell::{Ref, RefCell, RefMut},
4    collections::HashSet,
5    fmt,
6    marker::PhantomData,
7    rc::Rc,
8    sync::Arc,
9};
10
11#[cfg(debug_assertions)]
12use std::cell::Cell;
13#[cfg(debug_assertions)]
14use std::panic::Location;
15
16use parking_lot::{Mutex, MutexGuard};
17
18use crate::{
19    id::Id,
20    read::{SignalRead, SignalTrack, SignalWith},
21    runtime::{Runtime, RUNTIME},
22    storage::{Storage, SyncStorage, UnsyncStorage},
23    sync_runtime::{SyncSignal, SYNC_RUNTIME},
24    write::SignalWrite,
25    SignalGet, SignalUpdate,
26};
27
28#[derive(Debug)]
29pub(crate) struct TrackedRefCell<T> {
30    inner: RefCell<T>,
31    #[cfg(debug_assertions)]
32    shared_borrows: Cell<usize>,
33    #[cfg(debug_assertions)]
34    has_mut_borrow: Cell<bool>,
35    #[cfg(debug_assertions)]
36    holder: Cell<Option<&'static Location<'static>>>,
37}
38
39impl<T> TrackedRefCell<T> {
40    #[cfg_attr(debug_assertions, track_caller)]
41    pub(crate) fn new(value: T) -> Self {
42        Self {
43            inner: RefCell::new(value),
44            #[cfg(debug_assertions)]
45            shared_borrows: Cell::new(0),
46            #[cfg(debug_assertions)]
47            has_mut_borrow: Cell::new(false),
48            #[cfg(debug_assertions)]
49            holder: Cell::new(None),
50        }
51    }
52
53    #[cfg_attr(debug_assertions, track_caller)]
54    pub(crate) fn borrow(&self) -> TrackedRef<'_, T> {
55        #[cfg(debug_assertions)]
56        return self.borrow_at(Location::caller());
57        #[cfg(not(debug_assertions))]
58        return TrackedRef {
59            inner: self.inner.borrow(),
60        };
61    }
62
63    #[cfg_attr(debug_assertions, track_caller)]
64    pub(crate) fn borrow_mut(&self) -> TrackedRefMut<'_, T> {
65        #[cfg(debug_assertions)]
66        return self.borrow_mut_at(Location::caller());
67        #[cfg(not(debug_assertions))]
68        return TrackedRefMut {
69            inner: self.inner.borrow_mut(),
70        };
71    }
72
73    #[cfg(debug_assertions)]
74    pub(crate) fn borrow_at(&self, caller: &'static Location<'static>) -> TrackedRef<'_, T> {
75        let inner = self
76            .inner
77            .try_borrow()
78            .unwrap_or_else(|_| self.panic_conflict(caller));
79        let shared = self.shared_borrows.get();
80        if shared == 0 && !self.has_mut_borrow.get() {
81            self.holder.set(Some(caller));
82        }
83        self.shared_borrows.set(shared + 1);
84        TrackedRef { inner, cell: self }
85    }
86
87    #[cfg(debug_assertions)]
88    pub(crate) fn borrow_mut_at(&self, caller: &'static Location<'static>) -> TrackedRefMut<'_, T> {
89        let inner = self
90            .inner
91            .try_borrow_mut()
92            .unwrap_or_else(|_| self.panic_conflict(caller));
93        if self.shared_borrows.get() == 0 && !self.has_mut_borrow.get() {
94            self.holder.set(Some(caller));
95        }
96        self.has_mut_borrow.set(true);
97        TrackedRefMut { inner, cell: self }
98    }
99
100    #[cfg(debug_assertions)]
101    fn release_shared(&self) {
102        let shared = self.shared_borrows.get().saturating_sub(1);
103        self.shared_borrows.set(shared);
104        if shared == 0 && !self.has_mut_borrow.get() {
105            self.holder.set(None);
106        }
107    }
108
109    #[cfg(debug_assertions)]
110    fn release_mut(&self) {
111        self.has_mut_borrow.set(false);
112        if self.shared_borrows.get() == 0 {
113            self.holder.set(None);
114        }
115    }
116
117    #[cfg(debug_assertions)]
118    fn panic_conflict(&self, caller: &'static Location<'static>) -> ! {
119        match self.holder.get() {
120            Some(loc) => panic!(
121                "signal value already borrowed at {}:{} (attempted at {}:{})",
122                loc.file(),
123                loc.line(),
124                caller.file(),
125                caller.line()
126            ),
127            None => panic!(
128                "signal value already borrowed (attempted at {}:{})",
129                caller.file(),
130                caller.line()
131            ),
132        }
133    }
134}
135
136pub struct TrackedRef<'a, T> {
137    inner: Ref<'a, T>,
138    #[cfg(debug_assertions)]
139    cell: &'a TrackedRefCell<T>,
140}
141
142impl<'a, T> Drop for TrackedRef<'a, T> {
143    fn drop(&mut self) {
144        #[cfg(debug_assertions)]
145        self.cell.release_shared();
146    }
147}
148
149impl<'a, T> std::ops::Deref for TrackedRef<'a, T> {
150    type Target = T;
151    fn deref(&self) -> &Self::Target {
152        &self.inner
153    }
154}
155
156pub struct TrackedRefMut<'a, T> {
157    inner: RefMut<'a, T>,
158    #[cfg(debug_assertions)]
159    cell: &'a TrackedRefCell<T>,
160}
161
162impl<'a, T> Drop for TrackedRefMut<'a, T> {
163    fn drop(&mut self) {
164        #[cfg(debug_assertions)]
165        self.cell.release_mut();
166    }
167}
168
169impl<'a, T> std::ops::Deref for TrackedRefMut<'a, T> {
170    type Target = T;
171    fn deref(&self) -> &Self::Target {
172        &self.inner
173    }
174}
175
176impl<'a, T> std::ops::DerefMut for TrackedRefMut<'a, T> {
177    fn deref_mut(&mut self) -> &mut Self::Target {
178        &mut self.inner
179    }
180}
181
182pub type SyncRwSignal<T> = RwSignal<T, SyncStorage>;
183pub type SyncReadSignal<T> = ReadSignal<T, SyncStorage>;
184pub type SyncWriteSignal<T> = WriteSignal<T, SyncStorage>;
185
186impl<T, S> SignalTrack<T> for RwSignal<T, S> {
187    fn id(&self) -> Id {
188        self.id
189    }
190}
191
192impl<T, S> SignalTrack<T> for ReadSignal<T, S> {
193    fn id(&self) -> Id {
194        self.id
195    }
196}
197
198impl<T: Any + 'static> Storage<T> for UnsyncStorage {
199    fn create(value: T) -> Id {
200        SignalState::new(value)
201    }
202
203    fn get(id: Id) -> Option<Self::Signal> {
204        id.signal()
205    }
206
207    type Signal = SignalState;
208}
209
210impl<T: Any + Send + Sync + 'static> Storage<T> for SyncStorage {
211    fn create(value: T) -> Id {
212        SignalState::new_sync(value)
213    }
214
215    fn get(id: Id) -> Option<Self::Signal> {
216        id.signal()
217            .or_else(|| SYNC_RUNTIME.get_signal(&id).map(|s| s.into()))
218    }
219
220    type Signal = SignalState;
221}
222
223/// A read write Signal which can act as both a Getter and a Setter
224pub struct RwSignal<T, S = UnsyncStorage> {
225    pub(crate) id: Id,
226    pub(crate) ty: PhantomData<T>,
227    pub(crate) st: PhantomData<S>,
228}
229
230impl<T, S> RwSignal<T, S> {
231    pub fn id(&self) -> Id {
232        self.id
233    }
234}
235
236impl<T, S> Copy for RwSignal<T, S> {}
237
238impl<T, S> Clone for RwSignal<T, S> {
239    fn clone(&self) -> Self {
240        *self
241    }
242}
243
244impl<T, S> Eq for RwSignal<T, S> {}
245
246impl<T, S> PartialEq for RwSignal<T, S> {
247    fn eq(&self, other: &Self) -> bool {
248        self.id == other.id
249    }
250}
251
252impl<T, S> fmt::Debug for RwSignal<T, S> {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        let mut s = f.debug_struct("RwSignal");
255        s.field("id", &self.id);
256        s.field("ty", &self.ty);
257        s.finish()
258    }
259}
260
261impl<T: Default + 'static> Default for RwSignal<T> {
262    fn default() -> Self {
263        RwSignal::new(T::default())
264    }
265}
266
267impl<T, S> RwSignal<T, S> {
268    /// Create a Getter of this Signal
269    pub fn read_only(&self) -> ReadSignal<T, S> {
270        ReadSignal {
271            id: self.id,
272            ty: PhantomData,
273            st: PhantomData,
274        }
275    }
276
277    /// Create a Setter of this Signal
278    pub fn write_only(&self) -> WriteSignal<T, S> {
279        WriteSignal {
280            id: self.id,
281            ty: PhantomData,
282            st: PhantomData,
283        }
284    }
285}
286
287impl<T: Send + Sync + 'static> RwSignal<T, SyncStorage> {
288    /// Creates a sync signal. When called off the UI thread, the signal is left
289    /// unscoped, so callers must ensure it is disposed manually.
290    pub fn new_sync(value: T) -> Self {
291        let id = SignalState::new_sync(value);
292        if Runtime::is_ui_thread() {
293            id.set_scope();
294        }
295        RwSignal {
296            id,
297            ty: PhantomData,
298            st: PhantomData,
299        }
300    }
301    /// Creates a sync signal with separate read/write handles. Off-UI calls
302    /// leave the signal unscoped; the caller is responsible for disposal.
303    pub fn new_sync_split(value: T) -> (ReadSignal<T, SyncStorage>, WriteSignal<T, SyncStorage>) {
304        let sig = Self::new_sync(value);
305        (sig.read_only(), sig.write_only())
306    }
307}
308
309impl<T: 'static> RwSignal<T, UnsyncStorage> {
310    #[cfg_attr(debug_assertions, track_caller)]
311    pub fn new(value: T) -> Self {
312        Runtime::assert_ui_thread();
313        let id = <UnsyncStorage as Storage<T>>::create(value);
314        id.set_scope();
315        RwSignal {
316            id,
317            ty: PhantomData,
318            st: PhantomData,
319        }
320    }
321    #[cfg_attr(debug_assertions, track_caller)]
322    pub fn new_split(value: T) -> (ReadSignal<T, UnsyncStorage>, WriteSignal<T, UnsyncStorage>) {
323        let sig = Self::new(value);
324        (sig.read_only(), sig.write_only())
325    }
326}
327
328/// Creates a new RwSignal which can act both as a setter and a getter.
329///
330/// Accessing the signal value in an Effect will make the Effect subscribe
331/// to the value change of the Signal. And whenever the signal value changes,
332/// it will trigger an effect run.
333#[deprecated(
334    since = "0.2.0",
335    note = "Use RwSignal::new for sync signals or RwSignal::new_local for local ones"
336)]
337#[cfg_attr(debug_assertions, track_caller)]
338pub fn create_rw_signal<T>(value: T) -> RwSignal<T>
339where
340    T: Any + 'static,
341{
342    RwSignal::new(value)
343}
344
345/// A getter only Signal
346pub struct ReadSignal<T, S = UnsyncStorage> {
347    pub(crate) id: Id,
348    pub(crate) ty: PhantomData<T>,
349    pub(crate) st: PhantomData<S>,
350}
351
352impl<T, S> ReadSignal<T, S> {
353    pub fn id(&self) -> Id {
354        self.id
355    }
356}
357
358impl<T, S> Copy for ReadSignal<T, S> {}
359
360impl<T, S> Clone for ReadSignal<T, S> {
361    fn clone(&self) -> Self {
362        *self
363    }
364}
365
366impl<T, S> Eq for ReadSignal<T, S> {}
367
368impl<T, S> PartialEq for ReadSignal<T, S> {
369    fn eq(&self, other: &Self) -> bool {
370        self.id == other.id
371    }
372}
373
374/// A setter only Signal
375pub struct WriteSignal<T, S = UnsyncStorage> {
376    pub(crate) id: Id,
377    pub(crate) ty: PhantomData<T>,
378    pub(crate) st: PhantomData<S>,
379}
380
381impl<T, S> WriteSignal<T, S> {
382    pub fn id(&self) -> Id {
383        self.id
384    }
385}
386
387impl<T, S> Copy for WriteSignal<T, S> {}
388
389impl<T, S> Clone for WriteSignal<T, S> {
390    fn clone(&self) -> Self {
391        *self
392    }
393}
394
395impl<T, S> Eq for WriteSignal<T, S> {}
396
397impl<T, S> PartialEq for WriteSignal<T, S> {
398    fn eq(&self, other: &Self) -> bool {
399        self.id == other.id
400    }
401}
402
403/// Creates a new setter and getter Signal.
404///
405/// Accessing the signal value in an Effect will make the Effect subscribe
406/// to the value change of the Signal. And whenever the signal value changes,
407/// it will trigger an effect run.
408#[deprecated(
409    since = "0.2.0",
410    note = "Use RwSignal::new for sync signals or RwSignal::new_local for local ones"
411)]
412pub fn create_signal<T>(value: T) -> (ReadSignal<T, UnsyncStorage>, WriteSignal<T, UnsyncStorage>)
413where
414    T: Any + 'static,
415{
416    let id = SignalState::new(value);
417    (
418        ReadSignal {
419            id,
420            ty: PhantomData,
421            st: PhantomData,
422        },
423        WriteSignal {
424            id,
425            ty: PhantomData,
426            st: PhantomData,
427        },
428    )
429}
430
431/// Internal state for a signal; stores the value and subscriber set.
432#[derive(Clone)]
433pub(crate) struct SignalState {
434    pub(crate) id: Id,
435    pub(crate) value: SignalValue,
436    pub(crate) subscribers: Arc<Mutex<HashSet<Id>>>,
437}
438
439#[derive(Clone)]
440pub(crate) enum SignalValue {
441    Sync(Arc<dyn Any + Send + Sync>),
442    Local(Rc<dyn Any>),
443}
444
445#[allow(dead_code)]
446pub enum SignalBorrow<'a, T> {
447    Sync(MutexGuard<'a, T>),
448    Local(TrackedRef<'a, T>),
449}
450
451impl SignalState {
452    #[allow(clippy::new_ret_no_self)]
453    #[cfg_attr(debug_assertions, track_caller)]
454    pub fn new<T>(value: T) -> Id
455    where
456        T: Any + 'static,
457    {
458        Runtime::assert_ui_thread();
459        let id = Id::next();
460        let value = TrackedRefCell::new(value);
461        let signal = SignalState {
462            id,
463            subscribers: Arc::new(Mutex::new(HashSet::new())),
464            value: SignalValue::Local(Rc::new(value)),
465        };
466        id.add_signal(signal);
467        id
468    }
469
470    pub fn new_sync<T>(value: T) -> Id
471    where
472        T: Any + Send + Sync + 'static,
473    {
474        let id = Id::next();
475        let value = Arc::new(Mutex::new(value));
476        let subscribers = Arc::new(Mutex::new(HashSet::new()));
477        // Sync signals live in the global sync runtime; we don't store them in the TLS runtime.
478        SYNC_RUNTIME.insert_signal(
479            id,
480            SyncSignal {
481                id,
482                value,
483                subscribers,
484            },
485        );
486        id
487    }
488
489    #[deprecated(
490        since = "0.2.0",
491        note = "Use SignalState::new_sync for sync signals or SignalState::new for local ones"
492    )]
493    #[allow(dead_code)]
494    pub fn create<T>(value: T) -> Id
495    where
496        T: Any + Send + Sync + 'static,
497    {
498        Self::new_sync(value)
499    }
500
501    #[allow(dead_code)]
502    #[cfg_attr(debug_assertions, track_caller)]
503    pub fn borrow<T: 'static>(&self) -> SignalBorrow<'_, T> {
504        match &self.value {
505            SignalValue::Sync(v) => {
506                let v = v
507                    .as_ref()
508                    .downcast_ref::<Mutex<T>>()
509                    .expect("to downcast signal type");
510                SignalBorrow::Sync(v.lock())
511            }
512            SignalValue::Local(v) => {
513                let v = v
514                    .as_ref()
515                    .downcast_ref::<TrackedRefCell<T>>()
516                    .expect("to downcast signal type");
517                #[cfg(debug_assertions)]
518                {
519                    SignalBorrow::Local(v.borrow_at(Location::caller()))
520                }
521                #[cfg(not(debug_assertions))]
522                {
523                    SignalBorrow::Local(v.borrow())
524                }
525            }
526        }
527    }
528
529    #[cfg_attr(debug_assertions, track_caller)]
530    pub(crate) fn get_untracked<T: Clone + 'static>(&self) -> T {
531        match &self.value {
532            SignalValue::Sync(v) => {
533                let v = v
534                    .as_ref()
535                    .downcast_ref::<Mutex<T>>()
536                    .expect("to downcast signal type");
537                v.lock().clone()
538            }
539            SignalValue::Local(v) => {
540                let v = v
541                    .as_ref()
542                    .downcast_ref::<TrackedRefCell<T>>()
543                    .expect("to downcast signal type");
544                #[cfg(debug_assertions)]
545                {
546                    v.borrow_at(Location::caller()).clone()
547                }
548                #[cfg(not(debug_assertions))]
549                {
550                    v.borrow().clone()
551                }
552            }
553        }
554    }
555
556    #[cfg_attr(debug_assertions, track_caller)]
557    pub(crate) fn get<T: Clone + 'static>(&self) -> T {
558        self.subscribe();
559        self.get_untracked()
560    }
561
562    #[cfg_attr(debug_assertions, track_caller)]
563    pub(crate) fn with_untracked<O, T: 'static>(&self, f: impl FnOnce(&T) -> O) -> O {
564        match &self.value {
565            SignalValue::Sync(v) => {
566                let v = v
567                    .as_ref()
568                    .downcast_ref::<Mutex<T>>()
569                    .expect("to downcast signal type");
570                f(&v.lock())
571            }
572            SignalValue::Local(v) => {
573                let v = v
574                    .as_ref()
575                    .downcast_ref::<TrackedRefCell<T>>()
576                    .expect("to downcast signal type");
577                #[cfg(debug_assertions)]
578                {
579                    f(&v.borrow_at(Location::caller()))
580                }
581                #[cfg(not(debug_assertions))]
582                {
583                    f(&v.borrow())
584                }
585            }
586        }
587    }
588
589    #[cfg_attr(debug_assertions, track_caller)]
590    pub(crate) fn with<O, T: 'static>(&self, f: impl FnOnce(&T) -> O) -> O {
591        self.subscribe();
592        self.with_untracked(f)
593    }
594
595    pub(crate) fn update_value_sync<U, T: Send + Sync + 'static>(
596        &self,
597        f: impl FnOnce(&mut T) -> U,
598    ) -> U {
599        let value = self.as_sync::<T>();
600        let mut guard = value.lock();
601        let result = f(&mut *guard);
602        drop(guard);
603        self.run_effects();
604        result
605    }
606
607    #[cfg_attr(debug_assertions, track_caller)]
608    pub(crate) fn update_value_local<U, T: 'static>(&self, f: impl FnOnce(&mut T) -> U) -> U {
609        let value = self.as_local::<T>();
610        #[cfg(debug_assertions)]
611        let mut guard = value.borrow_mut_at(Location::caller());
612        #[cfg(not(debug_assertions))]
613        let mut guard = value.borrow_mut();
614        let result = f(&mut *guard);
615        drop(guard);
616        self.run_effects();
617        result
618    }
619
620    pub(crate) fn subscriber_ids(&self) -> HashSet<Id> {
621        self.subscribers.lock().iter().copied().collect()
622    }
623
624    pub(crate) fn run_effects(&self) {
625        let ids: Vec<_> = self.subscriber_ids().into_iter().collect();
626        let on_ui_thread = Runtime::is_ui_thread();
627
628        if !on_ui_thread {
629            SYNC_RUNTIME.enqueue_effects(ids);
630            return;
631        }
632
633        RUNTIME.with(|r| {
634            for id in &ids {
635                r.add_pending_effect(*id);
636            }
637            if !r.batching.get() {
638                r.run_pending_effects();
639            }
640        });
641    }
642
643    pub(crate) fn subscribe(&self) {
644        RUNTIME.with(|runtime| {
645            if let Some(effect) = runtime.current_effect.borrow().as_ref() {
646                self.subscribers.lock().insert(effect.id());
647                effect.add_observer(self.id);
648            }
649        });
650    }
651
652    pub(crate) fn as_sync<T: Send + Sync + 'static>(&self) -> Arc<Mutex<T>> {
653        match &self.value {
654            SignalValue::Sync(v) => v
655                .clone()
656                .downcast::<Mutex<T>>()
657                .expect("to downcast signal type"),
658            SignalValue::Local(_) => unreachable!("expected sync signal storage"),
659        }
660    }
661
662    pub(crate) fn as_local<T: 'static>(&self) -> Rc<TrackedRefCell<T>> {
663        match &self.value {
664            SignalValue::Local(v) => v
665                .clone()
666                .downcast::<TrackedRefCell<T>>()
667                .expect("to downcast signal type"),
668            SignalValue::Sync(_) => unreachable!("expected local signal storage"),
669        }
670    }
671}
672
673// Sync storage trait impls (requires Send + Sync)
674impl<T: Clone + Send + Sync> SignalGet<T> for RwSignal<T, SyncStorage> {
675    fn id(&self) -> Id {
676        self.id
677    }
678}
679
680impl<T: Send + Sync> SignalWith<T> for RwSignal<T, SyncStorage> {
681    fn id(&self) -> Id {
682        self.id
683    }
684}
685
686impl<T: Send + Sync> SignalRead<T> for RwSignal<T, SyncStorage> {
687    fn id(&self) -> Id {
688        self.id
689    }
690
691    fn try_read(&self) -> Option<crate::read::ReadRef<'_, T>>
692    where
693        T: 'static,
694    {
695        self.id().signal().map(|signal| {
696            signal.subscribe();
697            crate::read::ReadRef::Sync(crate::read::SyncReadRef::new(signal.as_sync::<T>()))
698        })
699    }
700
701    fn try_read_untracked(&self) -> Option<crate::read::ReadRef<'_, T>>
702    where
703        T: 'static,
704    {
705        self.id().signal().map(|signal| {
706            crate::read::ReadRef::Sync(crate::read::SyncReadRef::new(signal.as_sync::<T>()))
707        })
708    }
709}
710
711impl<T: Send + Sync> SignalUpdate<T> for RwSignal<T, SyncStorage> {
712    fn id(&self) -> Id {
713        self.id
714    }
715
716    fn set(&self, new_value: T)
717    where
718        T: 'static,
719    {
720        if let Some(signal) = self.id().signal() {
721            signal.update_value_sync(|v| *v = new_value);
722        }
723    }
724
725    fn update(&self, f: impl FnOnce(&mut T))
726    where
727        T: 'static,
728    {
729        if let Some(signal) = self.id().signal() {
730            signal.update_value_sync(f);
731        }
732    }
733
734    fn try_update<O>(&self, f: impl FnOnce(&mut T) -> O) -> Option<O>
735    where
736        T: 'static,
737    {
738        self.id().signal().map(|signal| signal.update_value_sync(f))
739    }
740}
741
742impl<T: Send + Sync> SignalWrite<T> for RwSignal<T, SyncStorage> {
743    fn id(&self) -> Id {
744        self.id
745    }
746
747    fn write(&self) -> crate::write::WriteRef<'_, T>
748    where
749        T: 'static,
750    {
751        self.try_write().unwrap()
752    }
753
754    fn try_write(&self) -> Option<crate::write::WriteRef<'_, T>>
755    where
756        T: 'static,
757    {
758        self.id().signal().map(|signal| {
759            crate::write::WriteRef::Sync(crate::write::SyncWriteRef::new(
760                signal.id,
761                signal.as_sync::<T>(),
762            ))
763        })
764    }
765}
766
767impl<T: Clone + Send + Sync> SignalGet<T> for ReadSignal<T, SyncStorage> {
768    fn id(&self) -> Id {
769        self.id
770    }
771}
772
773impl<T: Send + Sync> SignalWith<T> for ReadSignal<T, SyncStorage> {
774    fn id(&self) -> Id {
775        self.id
776    }
777}
778
779impl<T: Send + Sync> SignalRead<T> for ReadSignal<T, SyncStorage> {
780    fn id(&self) -> Id {
781        self.id
782    }
783
784    fn try_read(&self) -> Option<crate::read::ReadRef<'_, T>>
785    where
786        T: 'static,
787    {
788        self.id().signal().map(|signal| {
789            signal.subscribe();
790            crate::read::ReadRef::Sync(crate::read::SyncReadRef::new(signal.as_sync::<T>()))
791        })
792    }
793
794    fn try_read_untracked(&self) -> Option<crate::read::ReadRef<'_, T>>
795    where
796        T: 'static,
797    {
798        self.id().signal().map(|signal| {
799            crate::read::ReadRef::Sync(crate::read::SyncReadRef::new(signal.as_sync::<T>()))
800        })
801    }
802}
803
804impl<T: Send + Sync> SignalUpdate<T> for WriteSignal<T, SyncStorage> {
805    fn id(&self) -> Id {
806        self.id
807    }
808
809    fn set(&self, new_value: T)
810    where
811        T: 'static,
812    {
813        if let Some(signal) = self.id().signal() {
814            signal.update_value_sync(|v| *v = new_value);
815        }
816    }
817
818    fn update(&self, f: impl FnOnce(&mut T))
819    where
820        T: 'static,
821    {
822        if let Some(signal) = self.id().signal() {
823            signal.update_value_sync(f);
824        }
825    }
826
827    fn try_update<O>(&self, f: impl FnOnce(&mut T) -> O) -> Option<O>
828    where
829        T: 'static,
830    {
831        self.id().signal().map(|signal| signal.update_value_sync(f))
832    }
833}
834
835impl<T: Send + Sync> SignalWrite<T> for WriteSignal<T, SyncStorage> {
836    fn id(&self) -> Id {
837        self.id
838    }
839
840    fn try_write(&self) -> Option<crate::write::WriteRef<'_, T>>
841    where
842        T: 'static,
843    {
844        self.id().signal().map(|signal| {
845            crate::write::WriteRef::Sync(crate::write::SyncWriteRef::new(
846                signal.id,
847                signal.as_sync::<T>(),
848            ))
849        })
850    }
851}
852
853// Unsync storage trait impls (no Send + Sync required)
854impl<T: Clone> SignalGet<T> for RwSignal<T, UnsyncStorage> {
855    fn id(&self) -> Id {
856        self.id
857    }
858}
859
860impl<T> SignalWith<T> for RwSignal<T, UnsyncStorage> {
861    fn id(&self) -> Id {
862        self.id
863    }
864}
865
866impl<T> SignalRead<T> for RwSignal<T, UnsyncStorage> {
867    fn id(&self) -> Id {
868        self.id
869    }
870
871    #[cfg_attr(debug_assertions, track_caller)]
872    fn try_read(&self) -> Option<crate::read::ReadRef<'_, T>>
873    where
874        T: 'static,
875    {
876        Runtime::assert_ui_thread();
877        self.id().signal().map(|signal| {
878            signal.subscribe();
879            crate::read::ReadRef::Local(crate::read::LocalReadRef::new(signal.as_local::<T>()))
880        })
881    }
882
883    fn try_read_untracked(&self) -> Option<crate::read::ReadRef<'_, T>>
884    where
885        T: 'static,
886    {
887        Runtime::assert_ui_thread();
888        self.id().signal().map(|signal| {
889            crate::read::ReadRef::Local(crate::read::LocalReadRef::new(signal.as_local::<T>()))
890        })
891    }
892}
893
894impl<T> SignalUpdate<T> for RwSignal<T, UnsyncStorage> {
895    fn id(&self) -> Id {
896        self.id
897    }
898
899    #[cfg_attr(debug_assertions, track_caller)]
900    fn set(&self, new_value: T)
901    where
902        T: 'static,
903    {
904        Runtime::assert_ui_thread();
905        if let Some(signal) = self.id().signal() {
906            signal.update_value_local(|v| *v = new_value);
907        }
908    }
909
910    #[cfg_attr(debug_assertions, track_caller)]
911    fn update(&self, f: impl FnOnce(&mut T))
912    where
913        T: 'static,
914    {
915        Runtime::assert_ui_thread();
916        if let Some(signal) = self.id().signal() {
917            signal.update_value_local(f);
918        }
919    }
920
921    #[cfg_attr(debug_assertions, track_caller)]
922    fn try_update<O>(&self, f: impl FnOnce(&mut T) -> O) -> Option<O>
923    where
924        T: 'static,
925    {
926        Runtime::assert_ui_thread();
927        self.id()
928            .signal()
929            .map(|signal| signal.update_value_local(f))
930    }
931}
932
933impl<T> SignalWrite<T> for RwSignal<T, UnsyncStorage> {
934    fn id(&self) -> Id {
935        self.id
936    }
937
938    #[cfg_attr(debug_assertions, track_caller)]
939    fn write(&self) -> crate::write::WriteRef<'_, T>
940    where
941        T: 'static,
942    {
943        Runtime::assert_ui_thread();
944        self.try_write().unwrap()
945    }
946
947    #[cfg_attr(debug_assertions, track_caller)]
948    fn try_write(&self) -> Option<crate::write::WriteRef<'_, T>>
949    where
950        T: 'static,
951    {
952        Runtime::assert_ui_thread();
953        self.id().signal().map(|signal| {
954            crate::write::WriteRef::Local(crate::write::LocalWriteRef::new(
955                signal.id,
956                signal.as_local::<T>(),
957            ))
958        })
959    }
960}
961
962impl<T> SignalUpdate<T> for WriteSignal<T, UnsyncStorage> {
963    fn id(&self) -> Id {
964        self.id
965    }
966
967    #[cfg_attr(debug_assertions, track_caller)]
968    fn set(&self, new_value: T)
969    where
970        T: 'static,
971    {
972        Runtime::assert_ui_thread();
973        if let Some(signal) = self.id().signal() {
974            signal.update_value_local(|v| *v = new_value);
975        }
976    }
977
978    #[cfg_attr(debug_assertions, track_caller)]
979    fn update(&self, f: impl FnOnce(&mut T))
980    where
981        T: 'static,
982    {
983        Runtime::assert_ui_thread();
984        if let Some(signal) = self.id().signal() {
985            signal.update_value_local(f);
986        }
987    }
988
989    #[cfg_attr(debug_assertions, track_caller)]
990    fn try_update<O>(&self, f: impl FnOnce(&mut T) -> O) -> Option<O>
991    where
992        T: 'static,
993    {
994        Runtime::assert_ui_thread();
995        self.id()
996            .signal()
997            .map(|signal| signal.update_value_local(f))
998    }
999}
1000
1001impl<T> SignalWrite<T> for WriteSignal<T, UnsyncStorage> {
1002    fn id(&self) -> Id {
1003        self.id
1004    }
1005
1006    #[cfg_attr(debug_assertions, track_caller)]
1007    fn try_write(&self) -> Option<crate::write::WriteRef<'_, T>>
1008    where
1009        T: 'static,
1010    {
1011        Runtime::assert_ui_thread();
1012        self.id().signal().map(|signal| {
1013            crate::write::WriteRef::Local(crate::write::LocalWriteRef::new(
1014                signal.id,
1015                signal.as_local::<T>(),
1016            ))
1017        })
1018    }
1019}
1020
1021impl<T: Clone> SignalGet<T> for ReadSignal<T, UnsyncStorage> {
1022    fn id(&self) -> Id {
1023        self.id
1024    }
1025}
1026
1027impl<T> SignalWith<T> for ReadSignal<T, UnsyncStorage> {
1028    fn id(&self) -> Id {
1029        self.id
1030    }
1031}
1032
1033impl<T> SignalRead<T> for ReadSignal<T, UnsyncStorage> {
1034    fn id(&self) -> Id {
1035        self.id
1036    }
1037
1038    #[cfg_attr(debug_assertions, track_caller)]
1039    fn try_read(&self) -> Option<crate::read::ReadRef<'_, T>>
1040    where
1041        T: 'static,
1042    {
1043        Runtime::assert_ui_thread();
1044        self.id().signal().map(|signal| {
1045            signal.subscribe();
1046            crate::read::ReadRef::Local(crate::read::LocalReadRef::new(signal.as_local::<T>()))
1047        })
1048    }
1049
1050    fn try_read_untracked(&self) -> Option<crate::read::ReadRef<'_, T>>
1051    where
1052        T: 'static,
1053    {
1054        Runtime::assert_ui_thread();
1055        self.id().signal().map(|signal| {
1056            crate::read::ReadRef::Local(crate::read::LocalReadRef::new(signal.as_local::<T>()))
1057        })
1058    }
1059}