1use std::cell::RefCell;
2use std::rc::Rc;
3use std::sync::atomic::{AtomicUsize, Ordering};
4
5use crate::reactive;
6
7pub type SubId = usize;
8
9type SubCallback<T> = Rc<dyn Fn(&T)>;
11
12static NEXT_SIGNAL_ID: AtomicUsize = AtomicUsize::new(1);
13
14pub struct Signal<T: 'static>(Rc<RefCell<Inner<T>>>);
15
16impl<T> Clone for Signal<T> {
17 fn clone(&self) -> Self {
18 Self(self.0.clone())
19 }
20}
21
22struct Inner<T> {
23 id: usize,
24 value: T,
25 subs: Vec<Option<SubCallback<T>>>,
26 free_list: Vec<SubId>,
27 subscriber_count: usize,
28}
29
30impl<T> Signal<T> {
31 pub fn new(value: T) -> Self {
32 let id = NEXT_SIGNAL_ID.fetch_add(1, Ordering::Relaxed);
33 Self(Rc::new(RefCell::new(Inner {
34 id,
35 value,
36 subs: Vec::new(),
37 free_list: Vec::new(),
38 subscriber_count: 0,
39 })))
40 }
41
42 pub fn id(&self) -> usize {
43 self.0.borrow().id
44 }
45
46 pub fn get(&self) -> T
47 where
48 T: Clone,
49 {
50 let inner = self.0.borrow();
51 reactive::register_signal_read(inner.id);
52 inner.value.clone()
53 }
54
55 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
58 let inner = self.0.borrow();
59 reactive::register_signal_read(inner.id);
60 f(&inner.value)
61 }
62
63 pub fn set_neq(&self, v: T)
66 where
67 T: PartialEq + Clone,
68 {
69 let (id, old) = {
70 let mut inner = self.0.borrow_mut();
71 if inner.value == v {
72 return;
73 }
74 let old = std::mem::replace(&mut inner.value, v);
75 (inner.id, old)
76 };
77 drop(old);
78 self.notify_and_request_frame(id);
79 }
80
81 pub fn set(&self, v: T)
87 where
88 T: Clone,
89 {
90 let (id, old) = {
91 let mut inner = self.0.borrow_mut();
92 let old = std::mem::replace(&mut inner.value, v);
93 (inner.id, old)
94 };
95 drop(old);
96 self.notify_and_request_frame(id);
97 }
98
99 pub fn update<F: FnOnce(&mut T)>(&self, f: F)
100 where
101 T: Clone,
102 {
103 let id = {
104 let mut inner = self.0.borrow_mut();
105 f(&mut inner.value);
106 inner.id
107 };
108 self.notify_and_request_frame(id);
109 }
110
111 fn notify_and_request_frame(&self, id: usize)
112 where
113 T: Clone,
114 {
115 let subscriber_data = {
116 let inner = match self.0.try_borrow() {
117 Ok(b) => Some(b),
118 Err(_) => {
119 log::warn!("Signal notify: inner already borrowed, skipping notify");
120 None
121 }
122 };
123 inner.map(|inner| {
124 if inner.subscriber_count == 0 {
125 None
126 } else {
127 let cbs = inner
128 .subs
129 .iter()
130 .filter_map(|s| s.clone())
131 .collect::<Vec<_>>();
132 Some((cbs, inner.value.clone()))
133 }
134 })
135 };
136 let Some((cbs, snapshot)) = subscriber_data.flatten() else {
137 reactive::signal_changed(id);
138 crate::signal_fired();
139 crate::request_frame();
140 return;
141 };
142 reactive::without_observer(|| {
143 for cb in cbs {
144 let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| cb(&snapshot)));
145 if let Err(e) = res {
146 let msg = e
147 .downcast_ref::<String>()
148 .map(|s| s.as_str())
149 .or_else(|| e.downcast_ref::<&str>().copied())
150 .unwrap_or("unknown");
151 log::error!("Signal subscriber panicked: {msg}");
152 }
153 }
154 });
155
156 reactive::signal_changed(id);
157 crate::signal_fired();
158 crate::request_frame();
159 }
160
161 pub fn subscribe(&self, f: impl Fn(&T) + 'static) -> SubId {
162 let (id, old) = {
163 let mut inner = self.0.borrow_mut();
164 inner.subscriber_count += 1;
165 if let Some(free_id) = inner.free_list.pop() {
166 let old = inner.subs[free_id].replace(Rc::new(f));
167 (free_id, old)
168 } else {
169 inner.subs.push(Some(Rc::new(f)));
170 (inner.subs.len() - 1, None)
171 }
172 };
173 drop(old);
174 id
175 }
176
177 pub fn unsubscribe(&self, id: SubId) -> bool {
179 let old = {
180 let mut inner = self.0.borrow_mut();
181 if id < inner.subs.len() && inner.subs[id].is_some() {
182 let old = inner.subs[id].take();
183 inner.subscriber_count = inner.subscriber_count.saturating_sub(1);
184 inner.free_list.push(id);
185 while inner.subs.last().is_some_and(|s| s.is_none()) {
186 let popped = inner.subs.len() - 1;
187 inner.subs.pop();
188 if let Some(pos) = inner.free_list.iter().position(|&x| x == popped) {
189 inner.free_list.swap_remove(pos);
190 }
191 }
192 old
193 } else {
194 None
195 }
196 };
197 let removed = old.is_some();
198 drop(old);
199 removed
200 }
201
202 pub fn subscribe_guard(&self, f: impl Fn(&T) + 'static) -> SubGuard<T> {
204 let id = self.subscribe(f);
205 SubGuard {
206 sig: self.clone(),
207 id,
208 }
209 }
210}
211
212pub fn signal<T>(t: T) -> Signal<T> {
213 Signal::new(t)
214}
215
216pub struct SubGuard<T: 'static> {
218 sig: crate::Signal<T>,
219 id: SubId,
220}
221impl<T> Drop for SubGuard<T> {
222 fn drop(&mut self) {
223 let _ = self.sig.unsubscribe(self.id);
224 }
225}