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}