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