1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Reference-counted handles to slots in a [`SlotMap`](slotmap::SlotMap).
//!
//! This crate tracks the strong reference count to each slot and reports slots that have been
//! released when their last reference is dropped. It does not store values, which makes it a
//! building block for data structures that need reference-counted identity: entity maps, focus
//! tracking, resource tables.
//!
//! # Overview
//!
//! - [`Registry`] maintains an atomic strong count for each slot, keyed by a [`slotmap::Key`]. A
//! slot is created with a count of 1. When its count reaches 0, the slot is removed and its key
//! pushed to a release queue to be drained with [`Registry::take_released`].
//! - [`Handle`] is a strong reference to a slot. Cloning a handle increments the count and dropping
//! it decrements the count. Dropping the last handle releases the slot. A handle can carry
//! metadata `M`, such as a `TypeId` for dynamically typed entities. The metadata is cloned with
//! the handle and doesn't take part in equality, ordering, or hashing.
//! - [`WeakHandle`] is a weak reference to a slot. It does not contribute to the strong reference
//! count and does not keep its slot alive. [`WeakHandle::upgrade`] returns a strong handle only
//! if the slot is still alive. The liveness check and increment are performed as an atomic step
//! so a slot that is being released cannot be revived.
//!
//! The internal `SlotMap` uses versioned keys to allow reusing indexes. Therefore, a stale key or
//! weak handle can never resolve to a newer slot that occupies the same index in the `SlotMap`.
//!
//! # Usage
//!
//! A registry is shared as an `Arc<RwLock<Registry<K>>>`. Handles hold a weak reference to the
//! registry that created them. Cloning and dropping a handle attempts to upgrade the weak reference
//! for the operation. If the upgrade fails then there is no effect on the reference counters.
//!
//! Values associated with slots are stored by the owners of the registry, for example in a
//! [`SecondaryMap`](slotmap::SecondaryMap) indexed by [`Handle::key`]. The owner should drain the
//! release queue periodically, removing data associated with each released key and performing any
//! cleanup that is required. The `entitymap` example shows this pattern.
//!
//! A separate registry should be used for each key type. [`Handle::check_context`] compares
//! registries by pointer identity, which detects a handle being used with a registry other than the
//! one that created it.
//!
//! # Locking
//!
//! Handles are `Send` and `Sync` when their metadata is `Send` and `Sync`. This allows them to be
//! cloned and dropped on any thread. Reference counters are atomic, but handles access the registry
//! through its lock. Cloning and upgrading acquire a read lock. Dropping acquires an upgradable
//! read lock which is upgraded to a write lock when the last reference is being dropped. Inserting
//! and draining the release queue acquire a write lock.
//!
//! Handles must not be cloned, dropped, or inserted while the calling thread holds a guard on the
//! registry's lock as this might deadlock. [`Registry::take_released`] returns an iterator that
//! borrows the registry mutably. The iterator should be collected before continuing with operations
//! that might create or drop handles.
//!
//! # Leak Detection
//!
//! When the `recorder` feature is enabled every handle is given a unique, monotonically increasing
//! identifier and records a backtrace of where it was created or cloned. (The feature is always
//! enabled for this crate's tests.) [`Registry`] provides snapshots and assertions for verifying
//! that handles have been dropped. Dropping a registry that still has live handles panics with a
//! report listing each leaked handle and its creation backtrace.
//!
//! Recording is intended for debugging and testing. A backtrace is captured on every creation and
//! clone, and every clone and drop acquires the registry's write lock. Recording is not indented
//! for use in production.
//!
//! # Example
//!
//! ```rust
//! use parking_lot::RwLock;
//! use refslots::{Handle, Registry};
//! use std::sync::Arc;
//!
//! slotmap::new_key_type! { struct FocusId; }
//!
//! let registry = Arc::new(RwLock::new(Registry::<FocusId>::default()));
//!
//! let handle = Handle::insert(®istry, ());
//! let weak = handle.downgrade();
//! drop(handle);
//! assert!(weak.upgrade().is_none());
//!
//! let released: Vec<FocusId> = registry.write().take_released().collect();
//! assert_eq!(released.len(), 1);
//! ```
pub use Handle;
pub use Registry;
pub use WeakHandle;
pub use ;