zng_handle/
lib.rs

1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/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 id dropped if the handle is dropped"]
36#[repr(transparent)]
37pub struct Handle<D: Send + Sync>(Arc<HandleState<D>>);
38struct HandleState<D> {
39    state: AtomicU8,
40    data: D,
41}
42impl<D: Send + Sync> Handle<D> {
43    /// Create a handle with owner pair.
44    pub fn new(data: D) -> (HandleOwner<D>, Handle<D>) {
45        let handle = Handle(Arc::new(HandleState {
46            state: AtomicU8::new(NONE),
47            data,
48        }));
49        (HandleOwner(handle.clone()), handle)
50    }
51
52    /// Create a handle to nothing, the handle always in the *dropped* state.
53    ///
54    /// Note that `Option<Handle<D>>` takes up the same space as `Handle<D>` and avoids an allocation.
55    pub fn dummy(data: D) -> Self {
56        Handle(Arc::new(HandleState {
57            state: AtomicU8::new(FORCE_DROP),
58            data,
59        }))
60    }
61
62    /// Reference the attached data.
63    pub fn data(&self) -> &D {
64        &self.0.data
65    }
66
67    /// Mark the handle as permanent and drops this clone of it. This causes the resource to stay in memory
68    /// until the app exits, no need to hold a handle somewhere.
69    pub fn perm(self) {
70        self.0.state.fetch_or(PERMANENT, Ordering::Relaxed);
71    }
72
73    /// If [`perm`](Self::perm) was called in another clone of this handle.
74    ///
75    /// If `true` the resource will stay in memory for the duration of the app, unless [`force_drop`](Self::force_drop)
76    /// is also called.
77    pub fn is_permanent(&self) -> bool {
78        self.0.state.load(Ordering::Relaxed) == PERMANENT
79    }
80
81    /// Force drops the handle, meaning the resource will be dropped even if there are other handles active.
82    pub fn force_drop(self) {
83        self.0.state.store(FORCE_DROP, Ordering::Relaxed);
84    }
85
86    /// If the handle is in *dropped* state.
87    ///
88    /// The handle is considered dropped when all handle and clones are dropped or when [`force_drop`](Handle::force_drop)
89    /// was called in any of the clones.
90    ///
91    /// Note that in this method it can only be because [`force_drop`](Handle::force_drop) was called.
92    pub fn is_dropped(&self) -> bool {
93        self.0.state.load(Ordering::Relaxed) == FORCE_DROP
94    }
95
96    /// Create a [`WeakHandle`] to this handle.
97    pub fn downgrade(&self) -> WeakHandle<D> {
98        WeakHandle(Arc::downgrade(&self.0))
99    }
100}
101impl<D: Send + Sync> Clone for Handle<D> {
102    fn clone(&self) -> Self {
103        Handle(Arc::clone(&self.0))
104    }
105}
106impl<D: Send + Sync> PartialEq for Handle<D> {
107    fn eq(&self, other: &Self) -> bool {
108        Arc::ptr_eq(&self.0, &other.0)
109    }
110}
111impl<D: Send + Sync> Eq for Handle<D> {}
112impl<D: Send + Sync> Hash for Handle<D> {
113    fn hash<H: Hasher>(&self, state: &mut H) {
114        let ptr = Arc::as_ptr(&self.0) as usize;
115        ptr.hash(state);
116    }
117}
118impl<D: Send + Sync> Drop for Handle<D> {
119    fn drop(&mut self) {
120        if !self.is_permanent() && Arc::strong_count(&self.0) == 2 {
121            // if we are about to drop the last handle and it is not permanent, force-drop
122            // this causes potential weak-handles to not reanimate a dropping resource because
123            // of the handle that HandleOwner holds.
124            self.0.state.store(FORCE_DROP, Ordering::Relaxed);
125        }
126    }
127}
128impl<D: Send + Sync> fmt::Debug for Handle<D> {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        if self.is_permanent() {
131            write!(f, "permanent")
132        } else if self.is_dropped() {
133            write!(f, "dropped")
134        } else {
135            write!(f, "holding")
136        }
137    }
138}
139
140/// A weak reference to a [`Handle`].
141pub struct WeakHandle<D: Send + Sync>(Weak<HandleState<D>>);
142impl<D: Send + Sync> WeakHandle<D> {
143    /// New weak handle that does not upgrade.
144    pub fn new() -> Self {
145        WeakHandle(Weak::new())
146    }
147
148    /// Get a live handle if it was not dropped or force-dropped.
149    pub fn upgrade(&self) -> Option<Handle<D>> {
150        if let Some(arc) = self.0.upgrade() {
151            let handle = Handle(arc);
152            if handle.is_dropped() { None } else { Some(handle) }
153        } else {
154            None
155        }
156    }
157}
158impl<D: Send + Sync> Default for WeakHandle<D> {
159    fn default() -> Self {
160        Self::new()
161    }
162}
163impl<D: Send + Sync> Clone for WeakHandle<D> {
164    fn clone(&self) -> Self {
165        WeakHandle(self.0.clone())
166    }
167}
168impl<D: Send + Sync> PartialEq for WeakHandle<D> {
169    fn eq(&self, other: &Self) -> bool {
170        Weak::ptr_eq(&self.0, &other.0)
171    }
172}
173impl<D: Send + Sync> Eq for WeakHandle<D> {}
174impl<D: Send + Sync> Hash for WeakHandle<D> {
175    fn hash<H: Hasher>(&self, state: &mut H) {
176        let ptr = self.0.as_ptr() as usize;
177        ptr.hash(state);
178    }
179}
180impl<D: Send + Sync> fmt::Debug for WeakHandle<D> {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        if self.0.strong_count() > 0 {
183            write!(f, "can-upgrade")
184        } else {
185            write!(f, "dropped")
186        }
187    }
188}
189
190/// A [`Handle`] owner.
191///
192/// Use [`Handle::new`] to create.
193///
194/// Dropping the [`HandleOwner`] marks all active handles as *force-drop*.
195pub struct HandleOwner<D: Send + Sync>(Handle<D>);
196impl<D: Send + Sync> HandleOwner<D> {
197    /// If the handle is in *dropped* state.
198    ///
199    /// The handle is considered dropped when all handle and clones are dropped or when [`force_drop`](Handle::force_drop)
200    /// was called in any of the clones.
201    pub fn is_dropped(&self) -> bool {
202        let state = self.0.0.state.load(Ordering::Relaxed);
203        state == FORCE_DROP || (state != PERMANENT && Arc::strong_count(&self.0.0) <= 1)
204    }
205
206    /*
207    /// New handle owner in the dropped state.
208    pub fn dropped(data: D) -> HandleOwner<D> {
209        HandleOwner(Handle(Arc::new(HandleState {
210            state: AtomicU8::new(FORCE_DROP),
211            data,
212        })))
213    }
214
215    /// Gets a new handle and resets the state if it was *force-drop*.
216    ///
217    /// Note that handles are permanently dropped when the last handle is dropped.
218    pub fn reanimate(&self) -> Handle<D> {
219        if self.is_dropped() {
220            self.0 .0.state.store(NONE, Ordering::Relaxed);
221        }
222        self.0.clone()
223    }
224
225    */
226
227    /// Gets an weak handle that may-not be able to upgrade.
228    pub fn weak_handle(&self) -> WeakHandle<D> {
229        self.0.downgrade()
230    }
231
232    /// Reference the attached data.
233    pub fn data(&self) -> &D {
234        self.0.data()
235    }
236}
237impl<D: Send + Sync> Drop for HandleOwner<D> {
238    fn drop(&mut self) {
239        self.0.0.state.store(FORCE_DROP, Ordering::Relaxed);
240    }
241}
242
243const NONE: u8 = 0;
244const PERMANENT: u8 = 0b01;
245const FORCE_DROP: u8 = 0b11;