zng_handle/
lib.rs

1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3//!
4//! Resource handle type.
5//!
6//! # Crate
7//!
8#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12use std::hash::Hash;
13use std::{
14    fmt,
15    hash::Hasher,
16    sync::{
17        Arc, Weak,
18        atomic::{AtomicU8, Ordering},
19    },
20};
21
22/// Represents a resource handle.
23///
24/// The resource stays in memory as long as a handle clone is alive. After the handle
25/// is dropped the resource will be removed after an indeterminate time at the discretion
26/// of the resource manager.
27///
28/// You can *forget* a handle by calling [`perm`](Self::perm), this releases the handle memory
29/// but the resource stays alive for the duration of the app, unlike calling [`std::mem::forget`] no memory is leaked.
30///
31/// Any handle can also [`force_drop`](Self::force_drop), meaning that even if there are various handles active the
32/// resource will be dropped regardless.
33///
34/// The parameter type `D` is any [`Sync`] data type that will be shared using the handle.
35#[must_use = "the resource is dropped if the handle is dropped"]
36#[repr(transparent)]
37pub struct Handle<D: Send + Sync>(Arc<HandleState<D>>);
38struct HandleState<D> {
39    // only use the enum `State` variant values to set this field
40    state: AtomicU8,
41    data: D,
42}
43impl<D: Send + Sync> Handle<D> {
44    /// Create a handle with owner pair.
45    pub fn new(data: D) -> (HandleOwner<D>, Handle<D>) {
46        let handle = Handle(Arc::new(HandleState {
47            state: AtomicU8::new(State::None as u8),
48            data,
49        }));
50        (HandleOwner(handle.clone()), handle)
51    }
52
53    /// Create a handle to nothing, the handle always in the *dropped* state.
54    ///
55    /// Note that `Option<Handle<D>>` takes up the same space as `Handle<D>` and avoids an allocation.
56    pub fn dummy(data: D) -> Self {
57        Handle(Arc::new(HandleState {
58            state: AtomicU8::new(State::ForceDrop as u8),
59            data,
60        }))
61    }
62
63    /// Reference the attached data.
64    pub fn data(&self) -> &D {
65        &self.0.data
66    }
67
68    /// Mark the handle as permanent and drops this clone of it. This causes the resource to stay in memory
69    /// until the app exits, no need to hold a handle somewhere.
70    ///
71    /// Note that a clone can still force drop a perm handle and perm does nothing if the handle is already force dropped.
72    pub fn perm(self) {
73        let _ = self.0.state.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |s| {
74            if s != State::ForceDrop as u8 {
75                Some(State::Permanent as u8)
76            } else {
77                None
78            }
79        });
80    }
81
82    /// If [`perm`](Self::perm) was called in another clone of this handle.
83    ///
84    /// If `true` the resource will stay in memory for the duration of the app, unless [`force_drop`](Self::force_drop)
85    /// is also called.
86    pub fn is_permanent(&self) -> bool {
87        self.0.state.load(Ordering::Relaxed) == State::Permanent as u8
88    }
89
90    /// Force drops the handle, meaning the resource will be dropped even if there are other handles active.
91    pub fn force_drop(self) {
92        self.0.state.store(State::ForceDrop as u8, Ordering::Relaxed);
93    }
94
95    /// If the handle is in *dropped* state.
96    ///
97    /// The handle is considered dropped when all handle and clones are dropped or when [`force_drop`](Handle::force_drop)
98    /// was called in any of the clones.
99    ///
100    /// Note that in this method it can only be because [`force_drop`](Handle::force_drop) was called.
101    pub fn is_dropped(&self) -> bool {
102        self.0.state.load(Ordering::Relaxed) == State::ForceDrop as u8
103    }
104
105    /// Create a [`WeakHandle`] to this handle.
106    pub fn downgrade(&self) -> WeakHandle<D> {
107        WeakHandle(Arc::downgrade(&self.0))
108    }
109}
110impl<D: Send + Sync> Clone for Handle<D> {
111    fn clone(&self) -> Self {
112        Handle(Arc::clone(&self.0))
113    }
114}
115impl<D: Send + Sync> PartialEq for Handle<D> {
116    fn eq(&self, other: &Self) -> bool {
117        Arc::ptr_eq(&self.0, &other.0)
118    }
119}
120impl<D: Send + Sync> Eq for Handle<D> {}
121impl<D: Send + Sync> Hash for Handle<D> {
122    fn hash<H: Hasher>(&self, state: &mut H) {
123        let ptr = Arc::as_ptr(&self.0) as usize;
124        ptr.hash(state);
125    }
126}
127impl<D: Send + Sync> Drop for Handle<D> {
128    fn drop(&mut self) {
129        if !self.is_permanent() && Arc::strong_count(&self.0) == 2 {
130            // if we are about to drop the last handle and it is not permanent, force-drop
131            // this causes potential weak-handles to not reanimate a dropping resource because
132            // of the handle that HandleOwner holds.
133            self.0.state.store(State::ForceDrop as u8, Ordering::Relaxed);
134        }
135    }
136}
137impl<D: Send + Sync> fmt::Debug for Handle<D> {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        if self.is_permanent() {
140            write!(f, "permanent")
141        } else if self.is_dropped() {
142            write!(f, "dropped")
143        } else {
144            write!(f, "holding")
145        }
146    }
147}
148
149/// A weak reference to a [`Handle`].
150pub struct WeakHandle<D: Send + Sync>(Weak<HandleState<D>>);
151impl<D: Send + Sync> WeakHandle<D> {
152    /// New weak handle that does not upgrade.
153    pub fn new() -> Self {
154        WeakHandle(Weak::new())
155    }
156
157    /// Get a live handle if it was not dropped or force-dropped.
158    pub fn upgrade(&self) -> Option<Handle<D>> {
159        if let Some(arc) = self.0.upgrade() {
160            let handle = Handle(arc);
161            if handle.is_dropped() { None } else { Some(handle) }
162        } else {
163            None
164        }
165    }
166}
167impl<D: Send + Sync> Default for WeakHandle<D> {
168    fn default() -> Self {
169        Self::new()
170    }
171}
172impl<D: Send + Sync> Clone for WeakHandle<D> {
173    fn clone(&self) -> Self {
174        WeakHandle(self.0.clone())
175    }
176}
177impl<D: Send + Sync> PartialEq for WeakHandle<D> {
178    fn eq(&self, other: &Self) -> bool {
179        Weak::ptr_eq(&self.0, &other.0)
180    }
181}
182impl<D: Send + Sync> Eq for WeakHandle<D> {}
183impl<D: Send + Sync> Hash for WeakHandle<D> {
184    fn hash<H: Hasher>(&self, state: &mut H) {
185        let ptr = self.0.as_ptr() as usize;
186        ptr.hash(state);
187    }
188}
189impl<D: Send + Sync> fmt::Debug for WeakHandle<D> {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        if self.0.strong_count() > 0 {
192            write!(f, "can-upgrade")
193        } else {
194            write!(f, "dropped")
195        }
196    }
197}
198
199/// A [`Handle`] owner.
200///
201/// Use [`Handle::new`] to create.
202///
203/// Dropping the [`HandleOwner`] marks all handles as force dropped.
204pub struct HandleOwner<D: Send + Sync>(Handle<D>);
205impl<D: Send + Sync> HandleOwner<D> {
206    /// If the handle is in *dropped* state.
207    ///
208    /// The handle is considered dropped when all handle and clones are dropped or when [`force_drop`](Handle::force_drop)
209    /// was called in any of the clones.
210    pub fn is_dropped(&self) -> bool {
211        let state = self.0.0.state.load(Ordering::Relaxed);
212        state == State::ForceDrop as u8 || (state != State::Permanent as u8 && Arc::strong_count(&self.0.0) <= 1)
213    }
214
215    /// Gets an weak handle that may-not be able to upgrade.
216    pub fn weak_handle(&self) -> WeakHandle<D> {
217        self.0.downgrade()
218    }
219
220    /// Reference the attached data.
221    pub fn data(&self) -> &D {
222        self.0.data()
223    }
224}
225impl<D: Send + Sync> Drop for HandleOwner<D> {
226    fn drop(&mut self) {
227        self.0.0.state.store(State::ForceDrop as u8, Ordering::Relaxed);
228    }
229}
230
231#[repr(u8)]
232enum State {
233    None = 0,
234    Permanent = 1,
235    ForceDrop = 2,
236}