1use std::{
3 any::{Any, TypeId},
4 cell::{Cell, RefCell},
5 collections::BTreeMap,
6 rc::{Rc, Weak},
7};
8
9type Callback = Box<dyn FnOnce()>;
10
11#[derive(Clone, Copy, PartialEq, Eq)]
12enum Status {
13 Prepared,
14 Active,
15 Disposed,
16}
17
18struct Inner {
19 status: Cell<Status>,
20 committed: Cell<bool>,
21 activated: Cell<bool>,
23 #[cfg(feature = "dom")]
25 mount_ready: Cell<bool>,
26 parent: Option<Weak<Inner>>,
27 children: RefCell<Vec<Weak<Inner>>>,
28 dead_children: Cell<usize>,
29 next: Cell<u64>,
30 activate: RefCell<BTreeMap<u64, Callback>>,
31 cleanup: RefCell<BTreeMap<u64, Callback>>,
32 contexts: RefCell<BTreeMap<TypeId, Rc<dyn Any>>>,
33}
34
35impl Drop for Inner {
38 fn drop(&mut self) {
39 if let Some(parent) = self.parent.as_ref().and_then(Weak::upgrade) {
40 parent.dead_children.set(parent.dead_children.get() + 1);
41 }
42 }
43}
44
45pub trait ContextKey: 'static {
58 type Value: 'static;
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum ContextError {
64 Disposed,
65 AlreadyProvided,
66}
67
68impl std::fmt::Display for ContextError {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 f.write_str(match self {
71 Self::Disposed => "cannot provide context on a disposed owner",
72 Self::AlreadyProvided => "this owner already provides the context key",
73 })
74 }
75}
76
77impl std::error::Error for ContextError {}
78
79#[must_use = "dropping the owner disposes its work"]
82pub struct Owner(Rc<Inner>);
83
84#[derive(Clone)]
86pub struct OwnerHandle(Weak<Inner>);
87
88#[must_use = "retain the registration until the callback is no longer needed"]
90pub struct Registration {
91 owner: Weak<Inner>,
92 id: u64,
93 activation: bool,
94}
95
96impl Drop for Registration {
97 fn drop(&mut self) {
98 if let Some(owner) = self.owner.upgrade() {
99 let removed = if self.activation {
100 owner.activate.borrow_mut().remove(&self.id)
101 } else {
102 owner.cleanup.borrow_mut().remove(&self.id)
103 };
104 drop(removed);
105 }
106 }
107}
108
109impl Default for Owner {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115impl Owner {
116 pub fn new() -> Self {
118 Self::create(None)
119 }
120
121 pub fn child(parent: &OwnerHandle) -> Self {
124 Self::create(Some(parent.0.clone()))
125 }
126
127 fn create(parent: Option<Weak<Inner>>) -> Self {
128 let owner = Self(Rc::new(Inner {
129 status: Cell::new(Status::Prepared),
130 committed: Cell::new(false),
131 activated: Cell::new(false),
132 #[cfg(feature = "dom")]
133 mount_ready: Cell::new(false),
134 parent,
135 children: RefCell::new(Vec::new()),
136 dead_children: Cell::new(0),
137 next: Cell::new(0),
138 activate: RefCell::new(BTreeMap::new()),
139 cleanup: RefCell::new(BTreeMap::new()),
140 contexts: RefCell::new(BTreeMap::new()),
141 }));
142 if let Some(parent) = &owner.0.parent {
143 if let Some(parent) = parent
144 .upgrade()
145 .filter(|p| p.status.get() != Status::Disposed)
146 {
147 let mut children = parent.children.borrow_mut();
148 if parent.dead_children.get() >= 32
152 && parent.dead_children.get() >= children.len() / 2
153 {
154 children.retain(|child| child.strong_count() != 0);
155 parent.dead_children.set(0);
156 }
157 children.push(Rc::downgrade(&owner.0));
158 } else {
159 owner.0.status.set(Status::Disposed);
160 }
161 }
162 owner
163 }
164
165 pub fn handle(&self) -> OwnerHandle {
166 OwnerHandle(Rc::downgrade(&self.0))
167 }
168
169 pub fn commit(&self) {
171 self.0.committed.set(true);
172 activate(&self.0);
173 }
174
175 #[cfg(feature = "dom")]
177 pub(crate) fn is_active(&self) -> bool {
178 self.0.status.get() == Status::Active
179 }
180
181 #[cfg(feature = "dom")]
182 pub(crate) fn is_disposed(&self) -> bool {
183 self.0.status.get() == Status::Disposed
184 }
185
186 #[cfg(feature = "dom")]
187 pub(crate) fn mark_mount_ready(&self) {
188 self.0.mount_ready.set(true);
189 }
190
191 #[cfg(any(feature = "dom", test))]
192 pub(crate) fn was_activated(&self) -> bool {
193 self.0.activated.get()
194 }
195
196 pub fn dispose(&self) {
199 let mut callbacks = Vec::new();
200 invalidate(&self.0, &mut callbacks);
201 for callback in callbacks {
202 callback();
203 }
204 }
205}
206
207impl Drop for Owner {
208 fn drop(&mut self) {
209 self.dispose();
210 }
211}
212
213fn activate(inner: &Rc<Inner>) {
214 if inner.status.get() != Status::Prepared || !inner.committed.get() {
215 return;
216 }
217 if let Some(parent) = &inner.parent {
218 if !parent
219 .upgrade()
220 .is_some_and(|p| p.status.get() == Status::Active)
221 {
222 return;
223 }
224 }
225 inner.status.set(Status::Active);
226 inner.activated.set(true);
227 let callbacks = inner.activate.take();
228 for callback in callbacks.into_values() {
229 if inner.status.get() == Status::Active {
230 callback();
231 }
232 }
233 let children = inner.children.borrow().clone();
234 for child in children.into_iter().filter_map(|child| child.upgrade()) {
235 activate(&child);
236 }
237}
238
239fn invalidate(inner: &Rc<Inner>, callbacks: &mut Vec<Callback>) {
240 if inner.status.replace(Status::Disposed) == Status::Disposed {
241 return;
242 }
243 if !inner.activate.borrow().is_empty() {
246 let activations = inner.activate.take();
247 callbacks.push(Box::new(move || drop(activations)));
248 }
249 if !inner.cleanup.borrow().is_empty() {
250 callbacks.extend(inner.cleanup.take().into_values());
251 }
252 if !inner.children.borrow().is_empty() {
253 for child in inner
254 .children
255 .take()
256 .into_iter()
257 .filter_map(|c| c.upgrade())
258 {
259 invalidate(&child, callbacks);
260 }
261 }
262 if !inner.contexts.borrow().is_empty() {
265 let contexts = inner.contexts.take();
266 callbacks.push(Box::new(move || drop(contexts)));
267 }
268}
269
270impl OwnerHandle {
271 pub fn guarded<A, R, F: FnMut(A) -> R>(
275 &self,
276 mut callback: F,
277 ) -> impl FnMut(A) -> Option<R> + use<A, R, F> {
278 let owner = self.clone();
279 move |argument| {
280 owner
281 .is_active()
282 .then(|| crate::untrack(|| callback(argument)))
283 }
284 }
285 pub fn provide<K: ContextKey>(&self, value: K::Value) -> Result<(), ContextError> {
288 let Some(inner) = self
289 .0
290 .upgrade()
291 .filter(|inner| inner.status.get() != Status::Disposed)
292 else {
293 return Err(ContextError::Disposed);
294 };
295 let mut contexts = inner.contexts.borrow_mut();
297 if contexts.contains_key(&TypeId::of::<K>()) {
298 return Err(ContextError::AlreadyProvided);
299 }
300 contexts.insert(TypeId::of::<K>(), Rc::new(value));
301 Ok(())
302 }
303
304 pub fn context<K: ContextKey>(&self) -> Option<Rc<K::Value>> {
309 let mut current = self.0.upgrade();
310 while let Some(inner) = current {
311 if inner.status.get() == Status::Disposed {
312 return None;
313 }
314 let value = inner.contexts.borrow().get(&TypeId::of::<K>()).cloned();
315 if let Some(value) = value {
316 return Some(
317 value
318 .downcast::<K::Value>()
319 .expect("context key and value type agree"),
320 );
321 }
322 current = inner.parent.as_ref().and_then(Weak::upgrade);
323 }
324 None
325 }
326
327 pub fn is_child_of(&self, parent: &Self) -> bool {
329 self.0
330 .upgrade()
331 .is_some_and(|inner| inner.parent.as_ref().is_some_and(|p| p.ptr_eq(&parent.0)))
332 }
333 #[cfg(feature = "dom")]
334 pub(crate) fn is_within(&self, ancestor: &Self) -> bool {
335 let mut cursor = Some(self.0.clone());
336 while let Some(owner) = cursor {
337 if owner.ptr_eq(&ancestor.0) {
338 return true;
339 }
340 cursor = owner.upgrade().and_then(|owner| owner.parent.clone());
341 }
342 false
343 }
344 pub fn is_active(&self) -> bool {
345 self.0
346 .upgrade()
347 .is_some_and(|p| p.status.get() == Status::Active)
348 }
349 pub fn is_disposed(&self) -> bool {
350 self.0
351 .upgrade()
352 .is_none_or(|p| p.status.get() == Status::Disposed)
353 }
354 #[cfg(feature = "dom")]
355 pub(crate) fn is_mount_ready(&self) -> bool {
356 self.0.upgrade().is_some_and(|p| p.mount_ready.get())
357 }
358 pub fn on_activate(&self, callback: impl FnOnce() + 'static) -> Registration {
360 self.register(Box::new(callback), true)
361 }
362 pub fn on_cleanup(&self, callback: impl FnOnce() + 'static) -> Registration {
364 self.register(Box::new(callback), false)
365 }
366 fn register(&self, callback: Callback, activation: bool) -> Registration {
367 let mut registration = Registration {
368 owner: self.0.clone(),
369 id: 0,
370 activation,
371 };
372 let Some(inner) = self.0.upgrade() else {
373 if !activation {
374 callback();
375 }
376 return registration;
377 };
378 let id = inner
379 .next
380 .get()
381 .checked_add(1)
382 .expect("owner registration overflow");
383 inner.next.set(id);
384 match (activation, inner.status.get()) {
385 (true, Status::Active) | (false, Status::Disposed) => callback(),
386 (true, Status::Disposed) => {}
387 (true, Status::Prepared) => {
388 inner.activate.borrow_mut().insert(id, callback);
389 }
390 (false, _) => {
391 inner.cleanup.borrow_mut().insert(id, callback);
392 }
393 }
394 registration.id = id;
395 registration
396 }
397}
398
399#[cfg(test)]
400mod registry_tests {
401 use super::*;
402
403 #[test]
404 fn transient_children_are_compacted_without_losing_survivors_or_order() {
405 let parent = Owner::new();
406 let first = Owner::child(&parent.handle());
407 for _ in 0..10_000 {
408 drop(Owner::child(&parent.handle()));
409 }
410 let last = Owner::child(&parent.handle());
411 assert!(parent.0.children.borrow().len() <= 34);
412 let calls = Rc::new(RefCell::new(Vec::new()));
413 let registrations: Vec<_> = [&first, &last]
414 .into_iter()
415 .enumerate()
416 .map(|(id, owner)| {
417 let calls = calls.clone();
418 owner.commit();
419 owner
420 .handle()
421 .on_activate(move || calls.borrow_mut().push(id))
422 })
423 .collect();
424 parent.commit();
425 assert_eq!(*calls.borrow(), [0, 1]);
426 parent.dispose();
427 assert!(first.handle().is_disposed());
428 assert!(last.handle().is_disposed());
429 drop(registrations);
430 }
431}
432
433#[cfg(test)]
434mod activation_history_tests {
435 use super::*;
436
437 #[test]
438 fn activation_history_survives_disposal_and_does_not_confuse_commit_with_activation() {
439 let parent = Owner::new();
440 let child = Owner::child(&parent.handle());
441 assert!(!parent.was_activated());
442 child.commit();
443 assert!(!child.was_activated());
444 parent.commit();
445 assert!(parent.was_activated());
446 assert!(child.was_activated());
447 parent.dispose();
448 assert!(parent.was_activated());
449 assert!(child.was_activated());
450 child.commit();
451 assert!(child.was_activated());
452 assert!(child.handle().is_disposed());
453 }
454
455 #[test]
456 fn failed_preparation_and_disposed_ancestors_never_claim_activation() {
457 let parent = Owner::new();
458 let child = Owner::child(&parent.handle());
459 child.commit();
460 parent.dispose();
461 assert!(!parent.was_activated());
462 assert!(!child.was_activated());
463 let late = Owner::child(&parent.handle());
464 late.commit();
465 assert!(!late.was_activated());
466 let orphan = Owner::child(&Owner::new().handle());
467 orphan.commit();
468 assert!(!orphan.was_activated());
469 }
470
471 #[test]
472 fn parent_disposal_during_activation_does_not_claim_its_waiting_child() {
473 let parent = Rc::new(Owner::new());
474 let child = Owner::child(&parent.handle());
475 child.commit();
476 let weak = Rc::downgrade(&parent);
477 let _registration = parent.handle().on_activate(move || {
478 weak.upgrade().unwrap().dispose();
479 });
480 parent.commit();
481 assert!(parent.was_activated());
482 assert!(!child.was_activated());
483 assert!(child.handle().is_disposed());
484 }
485
486 #[test]
487 fn history_is_set_before_self_disposal_in_an_activation_callback() {
488 let owner = Rc::new(Owner::new());
489 let weak = Rc::downgrade(&owner);
490 let observed = Rc::new(Cell::new(false));
491 let observed_in_callback = observed.clone();
492 let _registration = owner.handle().on_activate(move || {
493 let owner = weak.upgrade().unwrap();
494 observed_in_callback.set(owner.was_activated());
495 owner.dispose();
496 });
497 owner.commit();
498 assert!(observed.get());
499 assert!(owner.was_activated());
500 assert!(owner.handle().is_disposed());
501 }
502}