dendron/tree/
lock.rs

1//! Tree hierarchy locking.
2
3use core::cell::Cell;
4use core::fmt;
5
6use alloc::rc::{Rc, Weak};
7
8use crate::node::Node;
9use crate::tree::TreeCore;
10
11/// An error indicating that an attempt to prohibit edit of the tree hierarchy failed.
12///
13/// Already existing edit grant is the only cause of this error.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub struct HierarchyEditProhibitionError;
16
17impl fmt::Display for HierarchyEditProhibitionError {
18    #[inline]
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        f.write_str("edif of the tree hierarchy is already granted")
21    }
22}
23
24#[cfg(feature = "std")]
25impl std::error::Error for HierarchyEditProhibitionError {}
26
27/// An error indicating that an attempt to grant edit of the tree hierarchy failed.
28///
29/// Already existing edit prohibition is the only cause of this error.
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub struct HierarchyEditGrantError;
32
33impl fmt::Display for HierarchyEditGrantError {
34    #[inline]
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        f.write_str("edit of the tree hierarchy is already prohibited")
37    }
38}
39
40#[cfg(feature = "std")]
41impl std::error::Error for HierarchyEditGrantError {}
42
43/// Manager for tree hierarchy edit grant and prohibition.
44///
45/// Each tree has just one `HierarchyLockManager`.
46///
47/// If no locks are active, such state is called "neutral".
48#[derive(Default, Debug)]
49pub struct HierarchyLockManager {
50    /// Lock count for the tree.
51    ///
52    /// Positive count represents the number of edit grants, and the negative
53    /// count represents (the negation of) the number of edit prohibitions.
54    num_grants: Cell<isize>,
55}
56
57impl HierarchyLockManager {
58    /// Returns true if there is any active prohibitions or there are no active locks.
59    ///
60    /// Note that this does not mean more prohibitions can be acquirable; the
61    /// lock number limit is not considered by this method.
62    #[inline]
63    pub(crate) fn ensure_prohibition_acquirable(
64        &self,
65    ) -> Result<(), HierarchyEditProhibitionError> {
66        if self.num_grants.get() <= 0 {
67            Ok(())
68        } else {
69            Err(HierarchyEditProhibitionError)
70        }
71    }
72
73    /// Returns true if there is any active grants or there are no active locks.
74    ///
75    /// Note that this does not mean more grants can be acquirable; the
76    /// lock number limit is not considered by this method.
77    #[inline]
78    pub(crate) fn ensure_grant_acquirable(&self) -> Result<(), HierarchyEditGrantError> {
79        if self.num_grants.get() >= 0 {
80            Ok(())
81        } else {
82            Err(HierarchyEditGrantError)
83        }
84    }
85
86    /// Increments the edit prohibitions count.
87    ///
88    /// # Failures
89    ///
90    /// Fails if edit grants are already active.
91    ///
92    /// # Panics
93    ///
94    /// Panics if the number of active edit prohibitions for the tree exceeds
95    /// `isize::MAX`. This is very unlikely to happen without leaking prohibitions.
96    fn acquire_prohibition(&self) -> Result<(), HierarchyEditProhibitionError> {
97        let old_count = self.num_grants.get();
98        if old_count > 0 {
99            return Err(HierarchyEditProhibitionError);
100        }
101        let new_count = old_count
102            .checked_sub(1)
103            .unwrap_or_else(|| panic!("[precondition] too many hierarchy edit prohibitions"));
104        self.num_grants.set(new_count);
105
106        Ok(())
107    }
108
109    /// Decrements the edit prohibitions count.
110    ///
111    /// # Panics
112    ///
113    /// Panics if no edit prohibitions are active.
114    fn release_prohibition(&self) {
115        let old_count = self.num_grants.get();
116        if old_count >= 0 {
117            panic!("[consistency] attempt to release an edit prohibition while none are active");
118        }
119        self.num_grants.set(old_count + 1);
120    }
121
122    /// Increments the edit grants count.
123    ///
124    /// # Failures
125    ///
126    /// Fails if edit prohibitions are already active.
127    ///
128    /// # Panics
129    ///
130    /// Panics if the number of active edit grants for the tree exceeds
131    /// `isize::MAX as usize + 1`. This is very unlikely to happen without
132    /// leaking grants.
133    fn acquire_grant(&self) -> Result<(), HierarchyEditGrantError> {
134        let old_count = self.num_grants.get();
135        if old_count < 0 {
136            return Err(HierarchyEditGrantError);
137        }
138        let new_count = old_count
139            .checked_add(1)
140            .unwrap_or_else(|| panic!("[precondition] too many hierarchy edit grants"));
141        self.num_grants.set(new_count);
142
143        Ok(())
144    }
145
146    /// Decrements the edit grants count.
147    ///
148    /// # Panics
149    ///
150    /// Panics if no edit grants are active.
151    fn release_grant(&self) {
152        let old_count = self.num_grants.get();
153        if old_count <= 0 {
154            panic!("[consistency] attempt to release an edit grant while none are active");
155        }
156        self.num_grants.set(old_count - 1);
157    }
158
159    /// Decrements lock count of any type, assuming it is nonzero.
160    ///
161    /// # Panics
162    ///
163    /// Panics if there is no active edit lock.
164    fn release_any_lock(&self) {
165        use core::cmp::Ordering;
166
167        match self.num_grants.get().cmp(&0) {
168            Ordering::Greater => {
169                // Grants are active.
170                let new_count = self.num_grants.get() - 1;
171                self.num_grants.set(new_count);
172            }
173            Ordering::Equal => panic!("[consistency] lock count should be nonzero"),
174            Ordering::Less => {
175                // Prohibitions are active.
176                let new_count = self.num_grants.get() + 1;
177                self.num_grants.set(new_count);
178            }
179        }
180    }
181
182    /// Transfers a lock.
183    ///
184    /// # Failures
185    ///
186    /// Fails if `other` cannot be locked with the currently active tree
187    /// hierarchy edit lock for `self`.
188    pub(super) fn transfer_single_lock_to(&self, other: &Self) -> Result<(), ()> {
189        use core::cmp::Ordering;
190
191        match self.num_grants.get().cmp(&0) {
192            Ordering::Greater => {
193                // Grants are active.
194                other.acquire_grant().map_err(|_| ())?;
195                let new_count = self.num_grants.get() - 1;
196                self.num_grants.set(new_count);
197            }
198            Ordering::Equal => {}
199            Ordering::Less => {
200                // Prohibitions are active.
201                other.acquire_prohibition().map_err(|_| ())?;
202                let new_count = self.num_grants.get() + 1;
203                self.num_grants.set(new_count);
204            }
205        }
206        Ok(())
207    }
208}
209
210/// A token to keep the tree hierarchy prohibited to be edited.
211///
212/// A prohibition can be created by [`Tree::prohibit_hierarchy_edit`] or
213/// [`FrozenNode::extract_hierarchy_edit_prohibition`].
214///
215/// [`Tree::prohibit_hierarchy_edit`]: `crate::Tree::prohibit_hierarchy_edit`
216/// [`FrozenNode::extract_hierarchy_edit_prohibition`]:
217///     `crate::FrozenNode::extract_hierarchy_edit_prohibition`
218pub struct HierarchyEditProhibition<T>(Weak<TreeCore<T>>);
219
220impl<T> Drop for HierarchyEditProhibition<T> {
221    #[inline]
222    fn drop(&mut self) {
223        if let Some(tree_core) = Weak::upgrade(&self.0) {
224            tree_core.lock_manager.release_prohibition();
225        }
226    }
227}
228
229impl<T> HierarchyEditProhibition<T> {
230    /// Creates a hierarchy edit prohibition for the given tree.
231    pub(super) fn new(tree_core: &Rc<TreeCore<T>>) -> Result<Self, HierarchyEditProhibitionError> {
232        tree_core.lock_manager.acquire_prohibition()?;
233        Ok(HierarchyEditProhibition(Rc::downgrade(tree_core)))
234    }
235
236    /// Clones the tree hierarchy edit prohibition.
237    ///
238    /// # Failures
239    ///
240    /// Fails if the number of active edit prohibitions for the tree exceeds
241    /// `isize::MAX`.
242    pub fn try_clone(&self) -> Result<Self, HierarchyEditProhibitionError> {
243        if let Some(tree_core) = Weak::upgrade(&self.0) {
244            tree_core.lock_manager.acquire_prohibition()?;
245        }
246        Ok(Self(self.0.clone()))
247    }
248
249    /// Returns true if the prohibition is valid for the tree the given node belongs to.
250    ///
251    /// # Examples
252    ///
253    /// ```
254    /// use dendron::Node;
255    ///
256    /// let node1 = Node::new_tree("foo");
257    /// let node2 = Node::new_tree("bar");
258    ///
259    /// let prohibition1 = node1.tree()
260    ///     .prohibit_hierarchy_edit()
261    ///     .expect("hierarchy edit should not yet be granted");
262    ///
263    /// assert!(prohibition1.is_valid_for_node(&node1));
264    /// assert!(!prohibition1.is_valid_for_node(&node2));
265    /// ```
266    ///
267    /// [`Tree::prohibit_hierarchy_edit`]:
268    ///     `crate::Tree::prohibit_hierarchy_edit`
269    /// [`FrozenNode::extract_hierarchy_edit_prohibition`]:
270    ///     `crate::FrozenNode::extract_hierarchy_edit_prohibition`
271    #[inline]
272    #[must_use]
273    pub fn is_valid_for_node(&self, node: &Node<T>) -> bool {
274        node.ptr_eq_tree_core_weak(&self.0)
275    }
276
277    /// Panics if the edit prohibition is not valid for the given node.
278    #[inline]
279    pub(crate) fn panic_if_invalid_for_node(&self, node: &Node<T>) {
280        if !self.is_valid_for_node(node) {
281            panic!("[precondition] the prohibition is not valid for the node of interest");
282        }
283    }
284}
285
286/// A token to keep the tree hierarchy granted to be edited.
287///
288/// A grant can be created by [`Tree::grant_hierarchy_edit`] or
289/// [`HotNode::extract_hierarchy_edit_grant`].
290///
291/// A grant can be created by [`Tree::grant_hierarchy_edit`] or
292/// [`HotNode::extract_hierarchy_edit_grant`].
293///
294/// [`Tree::grant_hierarchy_edit`]: `crate::Tree::grant_hierarchy_edit`
295/// [`HotNode::extract_hierarchy_edit_grant`]:
296///     `crate::HotNode::extract_hierarchy_edit_grant`
297pub struct HierarchyEditGrant<T>(Weak<TreeCore<T>>);
298
299impl<T> Drop for HierarchyEditGrant<T> {
300    #[inline]
301    fn drop(&mut self) {
302        if let Some(tree_core) = Weak::upgrade(&self.0) {
303            tree_core.lock_manager.release_grant();
304        }
305    }
306}
307
308impl<T> Clone for HierarchyEditGrant<T> {
309    /// Clones the tree hierarchy edit grant.
310    ///
311    /// If you want to avoid the risk of panic (even if it is very unlikely),
312    /// use [`try_clone`][`Self::try_clone`] method.
313    ///
314    /// # Panics
315    ///
316    /// Panics if the number of active edit grants for the tree will exceed
317    /// `isize::MAX`.
318    #[inline]
319    fn clone(&self) -> Self {
320        self.try_clone()
321            .expect("[precondition] too many hierarchy edit grants are active")
322    }
323}
324
325impl<T> HierarchyEditGrant<T> {
326    /// Creates a hierarchy edit grant for the given tree.
327    pub(super) fn new(tree_core: &Rc<TreeCore<T>>) -> Result<Self, HierarchyEditGrantError> {
328        tree_core.lock_manager.acquire_grant()?;
329        Ok(HierarchyEditGrant(Rc::downgrade(tree_core)))
330    }
331
332    /// Clones the tree hierarchy edit grant.
333    ///
334    /// # Failures
335    ///
336    /// Fails if the number of active edit grants for the tree exceeds
337    /// `isize::MAX`.
338    pub fn try_clone(&self) -> Result<Self, HierarchyEditGrantError> {
339        if let Some(tree_core) = Weak::upgrade(&self.0) {
340            tree_core.lock_manager.acquire_grant()?;
341        }
342        Ok(Self(self.0.clone()))
343    }
344
345    /// Returns true if the grant is valid for the tree the given node belongs to.
346    ///
347    /// # Examples
348    ///
349    /// ```
350    /// use dendron::Node;
351    ///
352    /// let node1 = Node::new_tree("foo");
353    /// let node2 = Node::new_tree("bar");
354    ///
355    /// let grant1 = node1.tree()
356    ///     .grant_hierarchy_edit()
357    ///     .expect("hierarchy edit should not yet be prohibited");
358    ///
359    /// assert!(grant1.is_valid_for_node(&node1));
360    /// assert!(!grant1.is_valid_for_node(&node2));
361    /// ```
362    ///
363    /// [`Tree::grant_hierarchy_edit`]: `crate::Tree::grant_hierarchy_edit`
364    /// [`HotNode::extract_hierarchy_edit_grant`]:
365    ///     `crate::HotNode::extract_hierarchy_edit_grant`
366    #[inline]
367    #[must_use]
368    pub fn is_valid_for_node(&self, node: &Node<T>) -> bool {
369        node.ptr_eq_tree_core_weak(&self.0)
370    }
371
372    /// Panics if the edit grant is not valid for the given node.
373    #[inline]
374    pub(crate) fn panic_if_invalid_for_node(&self, node: &Node<T>) {
375        if !self.is_valid_for_node(node) {
376            panic!("[precondition] the grant is not valid for the node of interest");
377        }
378    }
379}
380
381/// Aggregates locks that is following through a node, not following a tree directly.
382#[derive(Default, Debug)]
383pub(crate) struct LockAggregatorForNode {
384    /// The number of aggregated lock acquisition through the node.
385    ///
386    /// This aggregator does not need to remember which type of lock is active
387    /// because the backend `HierarchyLockManager` knows it.
388    aggregated_count: usize,
389}
390
391impl LockAggregatorForNode {
392    /// Returns true if the aggregator has any lock.
393    #[inline]
394    #[must_use]
395    pub(crate) fn has_lock(&self) -> bool {
396        self.aggregated_count != 0
397    }
398
399    /// Decrements the lock count.
400    ///
401    /// # Panics
402    ///
403    /// Panics if the aggregated lock count is zero.
404    pub(crate) fn decrement_count<T>(&mut self, tree_core: &Rc<TreeCore<T>>) {
405        if self.aggregated_count == 0 {
406            panic!("[consistency] attempt to decrement aggregated lock count while it is zero");
407        }
408        self.aggregated_count -= 1;
409
410        if self.aggregated_count == 0 {
411            tree_core.lock_manager.release_any_lock();
412        }
413    }
414
415    /// Increments the lock count, assuming the count is nonzero.
416    ///
417    /// # Panics
418    ///
419    /// Panics if the aggregated lock count is zero or `usize::MAX`.
420    pub(crate) fn increment_nonzero_count(&mut self) {
421        if self.aggregated_count == 0 {
422            panic!("[consistency] expected the lock aggregator already has some locks");
423        }
424        let new_count = self.aggregated_count.checked_add(1).unwrap_or_else(|| {
425            panic!("[precondition] attempt to acuqire too many locks for a node")
426        });
427        self.aggregated_count = new_count;
428    }
429
430    /// Acquires hierarchy edit prohibition.
431    pub(crate) fn acquire_edit_prohibition<T>(
432        &mut self,
433        tree_core: &Rc<TreeCore<T>>,
434    ) -> Result<(), HierarchyEditProhibitionError> {
435        tree_core.lock_manager.ensure_prohibition_acquirable()?;
436        if self.aggregated_count != 0 {
437            // Already has locks.
438            self.increment_nonzero_count();
439        } else {
440            // No active locks.
441            tree_core.lock_manager.acquire_prohibition()?;
442            self.aggregated_count = 1;
443        }
444        Ok(())
445    }
446
447    /// Acquires hierarchy edit grant.
448    pub(crate) fn acquire_edit_grant<T>(
449        &mut self,
450        tree_core: &Rc<TreeCore<T>>,
451    ) -> Result<(), HierarchyEditGrantError> {
452        tree_core.lock_manager.ensure_grant_acquirable()?;
453        if self.aggregated_count != 0 {
454            // Already has locks.
455            self.increment_nonzero_count();
456        } else {
457            // No active locks.
458            tree_core.lock_manager.acquire_grant()?;
459            self.aggregated_count = 1;
460        }
461        Ok(())
462    }
463}