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 {
121 Self::create(None)
122 }
123
124 pub fn child(parent: &OwnerHandle) -> Self {
127 Self::create(Some(parent.0.clone()))
128 }
129
130 fn create(parent: Option<Weak<Inner>>) -> Self {
131 let owner = Self(Rc::new(Inner {
132 status: Cell::new(Status::Prepared),
133 committed: Cell::new(false),
134 activated: Cell::new(false),
135 #[cfg(feature = "dom")]
136 mount_ready: Cell::new(false),
137 parent,
138 children: RefCell::new(Vec::new()),
139 dead_children: Cell::new(0),
140 next: Cell::new(0),
141 activate: RefCell::new(BTreeMap::new()),
142 cleanup: RefCell::new(BTreeMap::new()),
143 contexts: RefCell::new(BTreeMap::new()),
144 }));
145 if let Some(parent) = &owner.0.parent {
146 if let Some(parent) = parent
147 .upgrade()
148 .filter(|p| p.status.get() != Status::Disposed)
149 {
150 let mut children = parent.children.borrow_mut();
151 if parent.dead_children.get() >= 32
155 && parent.dead_children.get() >= children.len() / 2
156 {
157 children.retain(|child| child.strong_count() != 0);
158 parent.dead_children.set(0);
159 }
160 children.push(Rc::downgrade(&owner.0));
161 } else {
162 owner.0.status.set(Status::Disposed);
163 }
164 }
165 owner
166 }
167
168 pub fn handle(&self) -> OwnerHandle {
169 OwnerHandle(Rc::downgrade(&self.0))
170 }
171
172 pub fn commit(&self) {
176 self.0.committed.set(true);
177 activate(&self.0);
178 }
179
180 #[cfg(feature = "dom")]
182 pub(crate) fn is_active(&self) -> bool {
183 self.0.status.get() == Status::Active
184 }
185
186 #[cfg(feature = "dom")]
187 pub(crate) fn is_disposed(&self) -> bool {
188 self.0.status.get() == Status::Disposed
189 }
190
191 #[cfg(feature = "dom")]
192 pub(crate) fn mark_mount_ready(&self) {
193 self.0.mount_ready.set(true);
194 }
195
196 #[cfg(any(feature = "dom", test))]
197 pub(crate) fn was_activated(&self) -> bool {
198 self.0.activated.get()
199 }
200
201 pub fn dispose(&self) {
204 let mut callbacks = Vec::new();
205 invalidate(&self.0, &mut callbacks);
206 for callback in callbacks {
207 callback();
208 }
209 }
210}
211
212impl Drop for Owner {
213 fn drop(&mut self) {
214 self.dispose();
215 }
216}
217
218fn activate(inner: &Rc<Inner>) {
219 if inner.status.get() != Status::Prepared || !inner.committed.get() {
220 return;
221 }
222 if let Some(parent) = &inner.parent {
223 if !parent
224 .upgrade()
225 .is_some_and(|p| p.status.get() == Status::Active)
226 {
227 return;
228 }
229 }
230 inner.status.set(Status::Active);
231 inner.activated.set(true);
232 let callbacks = inner.activate.take();
233 for callback in callbacks.into_values() {
234 if inner.status.get() == Status::Active {
235 callback();
236 }
237 }
238 let children = inner.children.borrow().clone();
239 for child in children.into_iter().filter_map(|child| child.upgrade()) {
240 activate(&child);
241 }
242}
243
244fn invalidate(inner: &Rc<Inner>, callbacks: &mut Vec<Callback>) {
245 if inner.status.replace(Status::Disposed) == Status::Disposed {
246 return;
247 }
248 if !inner.activate.borrow().is_empty() {
251 let activations = inner.activate.take();
252 callbacks.push(Box::new(move || drop(activations)));
253 }
254 if !inner.cleanup.borrow().is_empty() {
255 callbacks.extend(inner.cleanup.take().into_values());
256 }
257 if !inner.children.borrow().is_empty() {
258 for child in inner
259 .children
260 .take()
261 .into_iter()
262 .filter_map(|c| c.upgrade())
263 {
264 invalidate(&child, callbacks);
265 }
266 }
267 if !inner.contexts.borrow().is_empty() {
270 let contexts = inner.contexts.take();
271 callbacks.push(Box::new(move || drop(contexts)));
272 }
273}
274
275impl OwnerHandle {
276 pub fn guarded<A, R, F: FnMut(A) -> R>(
280 &self,
281 mut callback: F,
282 ) -> impl FnMut(A) -> Option<R> + use<A, R, F> {
283 let owner = self.clone();
284 move |argument| {
285 owner
286 .is_active()
287 .then(|| crate::untrack(|| callback(argument)))
288 }
289 }
290 pub fn provide<K: ContextKey>(&self, value: K::Value) -> Result<(), ContextError> {
293 let Some(inner) = self
294 .0
295 .upgrade()
296 .filter(|inner| inner.status.get() != Status::Disposed)
297 else {
298 return Err(ContextError::Disposed);
299 };
300 let mut contexts = inner.contexts.borrow_mut();
302 if contexts.contains_key(&TypeId::of::<K>()) {
303 return Err(ContextError::AlreadyProvided);
304 }
305 contexts.insert(TypeId::of::<K>(), Rc::new(value));
306 Ok(())
307 }
308
309 pub fn context<K: ContextKey>(&self) -> Option<Rc<K::Value>> {
314 let mut current = self.0.upgrade();
315 while let Some(inner) = current {
316 if inner.status.get() == Status::Disposed {
317 return None;
318 }
319 let value = inner.contexts.borrow().get(&TypeId::of::<K>()).cloned();
320 if let Some(value) = value {
321 return Some(
322 value
323 .downcast::<K::Value>()
324 .expect("context key and value type agree"),
325 );
326 }
327 current = inner.parent.as_ref().and_then(Weak::upgrade);
328 }
329 None
330 }
331
332 pub fn is_child_of(&self, parent: &Self) -> bool {
334 self.0
335 .upgrade()
336 .is_some_and(|inner| inner.parent.as_ref().is_some_and(|p| p.ptr_eq(&parent.0)))
337 }
338 #[cfg(feature = "dom")]
339 pub(crate) fn is_within(&self, ancestor: &Self) -> bool {
340 let mut cursor = Some(self.0.clone());
341 while let Some(owner) = cursor {
342 if owner.ptr_eq(&ancestor.0) {
343 return true;
344 }
345 cursor = owner.upgrade().and_then(|owner| owner.parent.clone());
346 }
347 false
348 }
349 pub fn is_active(&self) -> bool {
350 self.0
351 .upgrade()
352 .is_some_and(|p| p.status.get() == Status::Active)
353 }
354 pub fn is_disposed(&self) -> bool {
355 self.0
356 .upgrade()
357 .is_none_or(|p| p.status.get() == Status::Disposed)
358 }
359 #[cfg(feature = "dom")]
360 pub(crate) fn is_mount_ready(&self) -> bool {
361 self.0.upgrade().is_some_and(|p| p.mount_ready.get())
362 }
363 pub fn on_activate(&self, callback: impl FnOnce() + 'static) -> Registration {
365 self.register(Box::new(callback), true)
366 }
367 pub fn on_cleanup(&self, callback: impl FnOnce() + 'static) -> Registration {
369 self.register(Box::new(callback), false)
370 }
371 fn register(&self, callback: Callback, activation: bool) -> Registration {
372 let mut registration = Registration {
373 owner: self.0.clone(),
374 id: 0,
375 activation,
376 };
377 let Some(inner) = self.0.upgrade() else {
378 if !activation {
379 callback();
380 }
381 return registration;
382 };
383 let id = inner
384 .next
385 .get()
386 .checked_add(1)
387 .expect("owner registration overflow");
388 inner.next.set(id);
389 match (activation, inner.status.get()) {
390 (true, Status::Active) | (false, Status::Disposed) => callback(),
391 (true, Status::Disposed) => {}
392 (true, Status::Prepared) => {
393 inner.activate.borrow_mut().insert(id, callback);
394 }
395 (false, _) => {
396 inner.cleanup.borrow_mut().insert(id, callback);
397 }
398 }
399 registration.id = id;
400 registration
401 }
402}
403
404#[cfg(test)]
405mod registry_tests {
406 use super::*;
407
408 #[test]
409 fn transient_children_are_compacted_without_losing_survivors_or_order() {
410 let parent = Owner::new();
411 let first = Owner::child(&parent.handle());
412 for _ in 0..10_000 {
413 drop(Owner::child(&parent.handle()));
414 }
415 let last = Owner::child(&parent.handle());
416 assert!(parent.0.children.borrow().len() <= 34);
417 let calls = Rc::new(RefCell::new(Vec::new()));
418 let registrations: Vec<_> = [&first, &last]
419 .into_iter()
420 .enumerate()
421 .map(|(id, owner)| {
422 let calls = calls.clone();
423 owner.commit();
424 owner
425 .handle()
426 .on_activate(move || calls.borrow_mut().push(id))
427 })
428 .collect();
429 parent.commit();
430 assert_eq!(*calls.borrow(), [0, 1]);
431 parent.dispose();
432 assert!(first.handle().is_disposed());
433 assert!(last.handle().is_disposed());
434 drop(registrations);
435 }
436}
437
438#[cfg(test)]
439mod activation_history_tests {
440 use super::*;
441
442 #[test]
443 fn activation_history_survives_disposal_and_does_not_confuse_commit_with_activation() {
444 let parent = Owner::new();
445 let child = Owner::child(&parent.handle());
446 assert!(!parent.was_activated());
447 child.commit();
448 assert!(!child.was_activated());
449 parent.commit();
450 assert!(parent.was_activated());
451 assert!(child.was_activated());
452 parent.dispose();
453 assert!(parent.was_activated());
454 assert!(child.was_activated());
455 child.commit();
456 assert!(child.was_activated());
457 assert!(child.handle().is_disposed());
458 }
459
460 #[test]
461 fn failed_preparation_and_disposed_ancestors_never_claim_activation() {
462 let parent = Owner::new();
463 let child = Owner::child(&parent.handle());
464 child.commit();
465 parent.dispose();
466 assert!(!parent.was_activated());
467 assert!(!child.was_activated());
468 let late = Owner::child(&parent.handle());
469 late.commit();
470 assert!(!late.was_activated());
471 let orphan = Owner::child(&Owner::new().handle());
472 orphan.commit();
473 assert!(!orphan.was_activated());
474 }
475
476 #[test]
477 fn parent_disposal_during_activation_does_not_claim_its_waiting_child() {
478 let parent = Rc::new(Owner::new());
479 let child = Owner::child(&parent.handle());
480 child.commit();
481 let weak = Rc::downgrade(&parent);
482 let _registration = parent.handle().on_activate(move || {
483 weak.upgrade().unwrap().dispose();
484 });
485 parent.commit();
486 assert!(parent.was_activated());
487 assert!(!child.was_activated());
488 assert!(child.handle().is_disposed());
489 }
490
491 #[test]
492 fn history_is_set_before_self_disposal_in_an_activation_callback() {
493 let owner = Rc::new(Owner::new());
494 let weak = Rc::downgrade(&owner);
495 let observed = Rc::new(Cell::new(false));
496 let observed_in_callback = observed.clone();
497 let _registration = owner.handle().on_activate(move || {
498 let owner = weak.upgrade().unwrap();
499 observed_in_callback.set(owner.was_activated());
500 owner.dispose();
501 });
502 owner.commit();
503 assert!(observed.get());
504 assert!(owner.was_activated());
505 assert!(owner.handle().is_disposed());
506 }
507}