lock_cell/
lib.rs

1// Copyright (c) 2023, George Burton <burtonageo@gmail.com>
2//
3// SPDX-License-Identifier: 0BSD
4
5#![cfg_attr(not(feature = "enable_std"), no_std)]
6#![warn(
7    clippy::cargo,
8    clippy::complexity,
9    clippy::pedantic,
10    clippy::perf,
11    clippy::style,
12    clippy::suspicious,
13    clippy::undocumented_unsafe_blocks
14)]
15
16//! This crate provides the [`LockCell<T>`] and other supportings types.
17//!
18//! A `LockCell` is a cell type which provides dynamic mutation using interior
19//! mutability. It is similar to [`RefCell<T>`], except that it only allows
20//! a single borrow type (a lock). Locking a `LockCell` allows mutating its
21//! contents freely.
22//!
23//! A `LockCell` can only be used in a single threaded context - it cannot be shared
24//! across different threads. Generally, a `LockCell` will be stored in a [`Rc<T>`]
25//! so that it can be shared.
26//!
27//! Whether you use a `LockCell` or a `RefCell` depends on the structure and behavior of
28//! your program. Generally, if you have a lot of writers and readers, using a `LockCell`
29//! may be better, as it ensures that writers are less likely to be starved.
30//!
31//! The [`Sync`] equivalent of a `LockCell` is [`Mutex<T>`].
32//!
33//! # Features
34//!
35//! * The `enable_std` feature enables the standard library. This provides an implementation of
36//!   [`std::error::Error`] for the [`TryLockError`] type. This feature is enabled by default.
37//!
38//! * The `debug_lockcell` feature tracks the location of each `lock()` call in the `LockCell`,
39//!   allowing the developer to compare the first lock location in their file to the panicking
40//!   lock location, aiding in debugging.
41//!
42//! [`LockCell<T>`]: ./struct.LockCell.html
43//! [`RefCell<T>`]: http://doc.rust-lang.org/std/cell/struct.RefCell.html
44//! [`Rc<T>`]: https://doc.rust-lang.org/std/rc/struct.Rc.html
45//! [`Mutex<T>`]: http://doc.rust-lang.org/std/sync/struct.Mutex.html
46//! [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
47//! [`std::error::Error`]: https://doc.rust-lang.org/std/error/trait.Error.html
48//! [`TryLockError`]: ./struct.TryLockError.html
49
50#[cfg(feature = "debug_lockcell")]
51use core::panic::Location;
52use core::{
53    borrow::{Borrow, BorrowMut},
54    cell::{Cell, UnsafeCell},
55    cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
56    convert::{AsMut, AsRef, TryFrom},
57    fmt,
58    hash::{Hash, Hasher},
59    marker::PhantomData,
60    mem::{self, ManuallyDrop},
61    ops::{Deref, DerefMut, FnOnce},
62};
63#[cfg(feature = "enable_std")]
64use std::error::Error as StdError;
65
66/// A mutable memory location with dynamically checked borrow rules.
67///
68/// See the [module level documentation] for more.
69///
70/// [module level documentation]: ./index.html
71pub struct LockCell<T: ?Sized> {
72    /// Used to track the lock state of the `LockCell`.
73    is_locked: Cell<bool>,
74    /// Stores where the `LockCell` was first locked. This is used as
75    /// part of the `debug_lockcell` feature to help debug double locks.
76    #[cfg(feature = "debug_lockcell")]
77    first_locked_at: Cell<Option<&'static Location<'static>>>,
78    /// The inner value of the `LockCell`.
79    value: UnsafeCell<T>,
80}
81
82impl<T> LockCell<T> {
83    /// Create a new `LockCell` with the given `value`.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// use lock_cell::LockCell;
89    /// # fn main() {
90    /// let cell = LockCell::new("I could be anything!".to_string());
91    /// # let _ = cell;
92    /// # }
93    /// ```
94    #[must_use]
95    #[inline]
96    pub const fn new(value: T) -> Self {
97        Self {
98            is_locked: Cell::new(false),
99            #[cfg(feature = "debug_lockcell")]
100            first_locked_at: Cell::new(None),
101            value: UnsafeCell::new(value),
102        }
103    }
104
105    /// Consumes the `LockCell`, returning the inner value.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use lock_cell::LockCell;
111    /// # fn main() {
112    /// let cell = LockCell::new(5);
113    ///
114    /// let five = cell.into_inner();
115    ///
116    /// assert_eq!(five, 5);
117    /// # }
118    /// ```
119    #[must_use]
120    #[inline]
121    pub fn into_inner(self) -> T {
122        self.value.into_inner()
123    }
124
125    /// Swaps the wrapped values of `self` and `rhs`.
126    ///
127    /// This function corresponds to [`std::mem::swap`].
128    ///
129    /// # Panics
130    ///
131    /// Panics if either `LockCell` is locked, or if `self` and `rhs` point to the same
132    /// `LockCell`.
133    ///
134    /// # Examples
135    ///
136    /// ```
137    /// use lock_cell::LockCell;
138    /// # fn main() {
139    /// let cell_1 = LockCell::new(3);
140    /// let cell_2 = LockCell::new(24);
141    ///
142    /// cell_1.swap(&cell_2);
143    ///
144    /// assert_eq!(cell_1.into_inner(), 24);
145    /// assert_eq!(cell_2.into_inner(), 3);
146    /// # }
147    /// ```
148    ///
149    /// [`std::mem::swap`]: https://doc.rust-lang.org/std/mem/fn.swap.html
150    #[track_caller]
151    #[inline]
152    pub fn swap(&self, rhs: &LockCell<T>) {
153        mem::swap(&mut *self.lock(), &mut *rhs.lock());
154    }
155
156    /// Sets the value in this `LockCell` to `new_value`, returning the previous value
157    /// in the `LockCell`.
158    ///
159    /// # Panics
160    ///
161    /// This method will panic if the cell is locked.
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// use lock_cell::LockCell;
167    /// # fn main() {
168    /// let cell = LockCell::new(5);
169    ///
170    /// let old_value = cell.replace(6);
171    ///
172    /// assert_eq!(old_value, 5);
173    ///
174    /// assert_eq!(cell.into_inner(), 6);
175    /// # }
176    /// ```
177    #[inline]
178    #[track_caller]
179    pub fn replace(&self, new_value: T) -> T {
180        let mut lock = self.lock();
181        mem::replace(&mut *lock, new_value)
182    }
183
184    /// Replaces the wrapped value with a new value computed from the function `f`,
185    /// returning the old value without deinitializing either.
186    ///
187    /// # Panics
188    ///
189    /// This method will panic if the `LockCell` is locked.
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// use lock_cell::LockCell;
195    /// # fn main() {
196    /// let cell = LockCell::new(5);
197    /// let old_value = cell.replace_with(|old| {
198    ///     *old += 1;
199    ///     *old + 1
200    /// });
201    ///
202    /// assert_eq!(old_value, 6);
203    ///
204    /// assert_eq!(cell.into_inner(), 7);
205    /// # }
206    /// ```
207    #[inline]
208    #[track_caller]
209    pub fn replace_with<F>(&self, f: F) -> T
210    where
211        F: FnOnce(&mut T) -> T,
212    {
213        let mut lock = self.lock();
214        let replacement = f(&mut *lock);
215        mem::replace(&mut *lock, replacement)
216    }
217
218    /// Replaces the value in this `LockCell` with the [`Default::default()`] value,
219    /// returning the previous value in the `LockCell`.
220    ///
221    /// # Panics
222    ///
223    /// This method will panic if the cell is locked.
224    ///
225    /// # Examples
226    ///
227    /// ```
228    /// use lock_cell::LockCell;
229    /// # fn main() {
230    /// let cell = LockCell::new(5);
231    ///
232    /// let old_value = cell.take();
233    ///
234    /// assert_eq!(old_value, 5);
235    ///
236    /// assert_eq!(cell.into_inner(), 0);
237    /// # }
238    /// ```
239    ///
240    /// [`Default::default()`]: https://doc.rust-lang.org/std/default/trait.Default.html
241    #[inline]
242    #[track_caller]
243    pub fn take(&self) -> T
244    where
245        T: Default,
246    {
247        self.replace(Default::default())
248    }
249}
250
251impl<T: ?Sized> LockCell<T> {
252    /// Attempt to lock the `LockCell`.
253    ///
254    /// # Notes
255    ///
256    /// If this `LockCell` is not locked, the function succeeds and will return a
257    /// guard which provides mutable access to the inner value.
258    ///
259    /// # Errors
260    ///
261    /// If the `LockCell` is already locked, this function will fail and will
262    /// return a [`TryLockError`].
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// # use lock_cell::{LockCell, TryLockError};
268    /// # fn main() -> Result<(), TryLockError> {
269    /// let cell = LockCell::new(21);
270    ///
271    /// let first_access = cell.try_lock();
272    /// assert!(first_access.is_ok());
273    ///
274    /// let first_lock = first_access?;
275    /// assert_eq!(*first_lock, 21);
276    ///
277    /// let second_access = cell.try_lock();
278    /// assert!(second_access.is_err());
279    /// # Ok(())
280    /// # }
281    /// ```
282    ///
283    /// [`TryLockError`]: ./struct.TryLockError.html
284    #[inline]
285    #[track_caller]
286    pub fn try_lock(&self) -> Result<LockGuard<'_, T>, TryLockError> {
287        if self.is_locked.replace(true) {
288            return Err(TryLockError::new(self));
289        }
290
291        #[cfg(feature = "debug_lockcell")]
292        {
293            self.first_locked_at.set(Some(Location::caller()));
294        }
295
296        Ok(LockGuard {
297            value: self.value.get(),
298            is_locked: &self.is_locked,
299            #[cfg(feature = "debug_lockcell")]
300            locked_at: &self.first_locked_at,
301            _boo: PhantomData,
302        })
303    }
304
305    /// Lock the given `LockCell`, returning a [`LockGuard`] which can be used to access
306    /// the value.
307    ///
308    /// The `LockCell` will be locked until the returned [`LockGuard`] goes out of scope.
309    /// The cell can only have a single lock at a time active.
310    ///
311    /// # Panics
312    ///
313    /// This method will panic if the `LockCell` is already locked.
314    ///
315    /// To avoid this, you can use the [`try_lock()`] method to return a `Result` to
316    /// check if the lock succeeded, or you can use the [`is_locked()`] method to check
317    /// ahead of time if the lock will succeed.
318    ///
319    /// # Examples
320    ///
321    /// ```
322    /// use lock_cell::LockCell;
323    /// # fn main() {
324    /// let cell = LockCell::new("Hello".to_string());
325    ///
326    /// let lock = cell.lock();
327    ///
328    /// assert_eq!(&*lock, "Hello");
329    /// # }
330    /// ```
331    ///
332    /// [`LockGuard`]: ./struct.LockGuard.html
333    /// [`try_lock()`]: ./struct.LockCell.html#method.try_lock
334    /// [`is_locked()`]: ./struct.LockCell.html#method.is_locked
335    #[inline]
336    #[track_caller]
337    pub fn lock(&self) -> LockGuard<'_, T> {
338        self.try_lock().expect("already locked")
339    }
340
341    /// Returns whether this `LockCell` is currently locked.
342    ///
343    /// # Examples
344    ///
345    /// ```
346    /// use lock_cell::LockCell;
347    /// # fn main() {
348    /// let cell = LockCell::new(5);
349    ///
350    /// assert!(!cell.is_locked());
351    ///
352    /// let lock = cell.lock();
353    ///
354    /// assert!(cell.is_locked());
355    /// # let _ = lock;
356    /// # }
357    /// ```
358    #[must_use]
359    #[inline]
360    pub fn is_locked(&self) -> bool {
361        self.is_locked.get()
362    }
363
364    /// Provides mutable access to the inner value.
365    ///
366    /// As this requires exclusive access to the `LockCell`, no locking is
367    /// required to provide exclusive access to the value.
368    ///
369    /// # Examples
370    ///
371    /// ```
372    /// use lock_cell::LockCell;
373    /// # fn main() {
374    /// let mut cell = LockCell::new(54);
375    ///
376    /// *cell.get_mut() = 20;
377    ///
378    /// assert_eq!(cell.into_inner(), 20);
379    /// # }
380    /// ```
381    #[must_use]
382    #[inline]
383    pub fn get_mut(&mut self) -> &mut T {
384        self.value.get_mut()
385    }
386
387    /// Return a raw pointer to the underlying data in this `LockCell`.
388    ///
389    /// # Notes
390    ///
391    /// This function does not lock the `LockCell`. Therefore, any mutations made through
392    /// the returned pointer must be synchronized in some other way, or undefined behaviour
393    /// may occur.
394    /// 
395    /// # Examples
396    ///
397    /// ```
398    /// use lock_cell::LockCell;
399    /// # fn main() {
400    /// let cell = LockCell::new(5);
401    ///
402    /// let ptr = cell.as_ptr();
403    /// # }
404    /// ```
405    #[must_use]
406    #[inline]
407    pub fn as_ptr(&self) -> *mut T {
408        self.value.get()
409    }
410
411    /// Resets the lock state, in case that any [`LockGuard`]s have been leaked.
412    ///
413    /// This method takes `self` by `&mut` to ensure that there are no other borrows
414    /// of the `LockCell` in flight.
415    ///
416    /// # Examples
417    ///
418    /// ```
419    /// # use core::mem;
420    /// use lock_cell::LockCell;
421    /// # fn main() {
422    /// let mut cell = LockCell::new(12);
423    ///
424    /// let mut lock = cell.lock();
425    ///
426    /// *lock = 54;
427    ///
428    /// mem::forget(lock);
429    ///
430    /// assert!(cell.is_locked());
431    ///
432    /// cell.reset_lock();
433    ///
434    /// assert!(!cell.is_locked());
435    ///
436    /// assert_eq!(cell.into_inner(), 54);
437    /// # }
438    /// ```
439    ///
440    /// [`LockGuard`]: ./struct.LockGuard.html
441    #[inline]
442    pub fn reset_lock(&mut self) -> &mut T {
443        self.is_locked.set(false);
444
445        #[cfg(feature = "debug_lockcell")]
446        {
447            self.first_locked_at.set(None);
448        }
449
450        self.get_mut()
451    }
452}
453
454impl<T: fmt::Debug> fmt::Debug for LockCell<T> {
455    #[inline]
456    fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
457        let lock_result = self.try_lock();
458        let value: &dyn fmt::Debug = if let Ok(value) = lock_result.as_deref() {
459            value
460        } else {
461            struct LockedPlaceholder;
462            impl fmt::Debug for LockedPlaceholder {
463                #[inline]
464                fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
465                    fmtr.write_str("<locked>")
466                }
467            }
468
469            const PLACEHOLDER: LockedPlaceholder = LockedPlaceholder;
470            &PLACEHOLDER
471        };
472
473        fmtr.debug_struct("LockCell").field("value", value).finish()
474    }
475}
476
477impl<T: Default> Default for LockCell<T> {
478    #[inline]
479    fn default() -> Self {
480        Self::new(Default::default())
481    }
482}
483
484impl<T> From<T> for LockCell<T> {
485    #[inline]
486    fn from(value: T) -> Self {
487        Self::new(value)
488    }
489}
490
491/// A `LockGuard` provides exclusive access to the inner value of a [`LockCell<T>`].
492///
493/// An instance of this type can be constructed from a `LockCell` using the [`LockCell::try_lock()`]
494/// or [`LockCell::lock()`] methods.
495///
496/// See the [module level documentation] for more.
497///
498/// [`LockCell<T>`]: ./struct.LockCell.html
499/// [`LockCell::try_lock()`]: ./struct.LockCell.html#method.try_lock
500/// [`LockCell::lock()`]: ./struct.LockCell.html#method.lock
501/// [module level documentation]: ./index.html
502#[must_use]
503pub struct LockGuard<'lock, T: ?Sized> {
504    /// The location of the original value in the `LockCell`.
505    value: *mut T,
506    /// The lock state of the `LockCell`.
507    is_locked: &'lock Cell<bool>,
508    /// The location where the original `LockCell` was first locked.
509    ///
510    /// The `LockGuard` will reset this value when it is dropped.
511    #[cfg(feature = "debug_lockcell")]
512    locked_at: &'lock Cell<Option<&'static Location<'static>>>,
513    /// Phantom data.
514    _boo: PhantomData<&'lock UnsafeCell<T>>,
515}
516
517impl<'lock, T: ?Sized> LockGuard<'lock, T> {
518    /// Applies the given `func` to the contents `LockGuard` to return a new `LockGuard` which
519    /// points to a sub-part of the original data.
520    ///
521    /// # Examples
522    ///
523    /// ```
524    /// use lock_cell::{LockCell, LockGuard};
525    /// # fn main() {
526    /// let cell = LockCell::<(i32, i32)>::default();
527    /// let lock = cell.lock();
528    ///
529    /// let mut value = LockGuard::map(lock, |(_, ref mut val)| val);
530    /// *value = 21;
531    /// drop(value);
532    ///
533    /// let tuple = cell.into_inner();
534    /// assert_eq!(tuple.1, 21);
535    /// # }
536    /// ```
537    #[inline]
538    pub fn map<F, U: ?Sized>(this: Self, func: F) -> LockGuard<'lock, U>
539    where
540        F: FnOnce(&mut T) -> &mut U,
541    {
542        let mut this = ManuallyDrop::new(this);
543
544        LockGuard {
545            // SAFETY:
546            // The `value` ptr has been created from a valid `LockCell`, so it always valid.
547            value: unsafe { func(&mut *this.value) } as *mut _,
548            #[cfg(feature = "debug_lockcell")]
549            locked_at: this.locked_at,
550            is_locked: this.is_locked,
551            _boo: PhantomData,
552        }
553    }
554
555    /// Applies the given `func` to the contents of `LockGuard` to return an optional reference
556    /// to a part of the original data.
557    ///
558    /// # Errors
559    ///
560    /// If `func` returns `None`, then the original guard will be returned in the `Err` variant
561    /// of the return value.
562    ///
563    /// # Examples
564    ///
565    /// ```
566    /// use lock_cell::{LockCell, LockGuard};
567    /// # fn main() {
568    /// let cell = LockCell::new(Some(0));
569    /// let lock = cell.lock();
570    ///
571    /// let mut value = match LockGuard::filter_map(lock, |value| value.as_mut()) {
572    ///     Ok(inner) => inner,
573    ///     Err(old_lock) => panic!("Unexpectedly empty value: {:?}", old_lock),
574    /// };
575    /// *value = 5;
576    /// drop(value);
577    ///
578    /// let old_value = cell.replace(None);
579    /// assert_eq!(old_value, Some(5));
580    ///
581    /// let lock = cell.lock();
582    /// let value = match LockGuard::filter_map(lock, |value| value.as_mut()) {
583    ///     Ok(inner) => panic!("Unexpected value is present: {:?}", inner),
584    ///     Err(old_lock) => old_lock,
585    /// };
586    ///
587    /// assert_eq!(*value, None);
588    /// # }
589    /// ```
590    #[inline]
591    pub fn filter_map<F, U: ?Sized>(this: Self, func: F) -> Result<LockGuard<'lock, U>, Self>
592    where
593        F: FnOnce(&mut T) -> Option<&mut U>,
594    {
595        let mut this = ManuallyDrop::new(this);
596
597        // SAFETY:
598        // The `value` ptr has been created from a valid `LockCell`, so it always valid.
599        let value = match unsafe { func(&mut *this.value) } {
600            Some(value) => value as *mut _,
601            _ => return Err(ManuallyDrop::into_inner(this)),
602        };
603
604        Ok(LockGuard {
605            // SAFETY: value has been created from a reference so it is always valid.
606            value: unsafe { &mut *value },
607            #[cfg(feature = "debug_lockcell")]
608            locked_at: this.locked_at,
609            is_locked: this.is_locked,
610            _boo: PhantomData,
611        })
612    }
613}
614
615impl<'lock, T> TryFrom<&'lock LockCell<T>> for LockGuard<'lock, T> {
616    type Error = TryLockError;
617    #[inline]
618    #[track_caller]
619    fn try_from(lock_cell: &'lock LockCell<T>) -> Result<Self, Self::Error> {
620        lock_cell.try_lock()
621    }
622}
623
624impl<'lock, T: ?Sized> Drop for LockGuard<'lock, T> {
625    #[inline]
626    fn drop(&mut self) {
627        self.is_locked.set(false);
628        #[cfg(feature = "debug_lockcell")]
629        {
630            self.locked_at.set(None);
631        }
632    }
633}
634
635impl<'lock, T: ?Sized> AsRef<T> for LockGuard<'lock, T> {
636    #[inline]
637    fn as_ref(&self) -> &T {
638        // SAFETY:
639        // The `value` ptr has been created from a valid `LockCell`, so it always valid.
640        unsafe { &*self.value }
641    }
642}
643
644impl<'lock, T: ?Sized> AsMut<T> for LockGuard<'lock, T> {
645    #[inline]
646    fn as_mut(&mut self) -> &mut T {
647        // SAFETY:
648        // The `value` ptr has been created from a valid `LockCell`, so it always valid.
649        unsafe { &mut *self.value }
650    }
651}
652
653impl<'lock, T: ?Sized> Borrow<T> for LockGuard<'lock, T> {
654    #[inline]
655    fn borrow(&self) -> &T {
656        // SAFETY:
657        // The `value` ptr has been created from a valid `LockCell`, so it always valid.
658        unsafe { &*self.value }
659    }
660}
661
662impl<'lock, T: ?Sized> BorrowMut<T> for LockGuard<'lock, T> {
663    #[inline]
664    fn borrow_mut(&mut self) -> &mut T {
665        // SAFETY:
666        // The `value` ptr has been created from a valid `LockCell`, so it always valid.
667        unsafe { &mut *self.value }
668    }
669}
670
671impl<'lock, T: ?Sized> Deref for LockGuard<'lock, T> {
672    type Target = T;
673    #[inline]
674    fn deref(&self) -> &T {
675        // SAFETY:
676        // The `value` ptr has been created from a valid `LockCell`, so it always valid.
677        unsafe { &*self.value }
678    }
679}
680
681impl<'lock, T: ?Sized> DerefMut for LockGuard<'lock, T> {
682    #[inline]
683    fn deref_mut(&mut self) -> &mut T {
684        // SAFETY:
685        // The `value` ptr has been created from a valid `LockCell`, so it always valid.
686        unsafe { &mut *self.value }
687    }
688}
689
690impl<'lock, T: fmt::Debug + ?Sized> fmt::Debug for LockGuard<'lock, T> {
691    #[inline]
692    fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
693        fmtr.debug_struct("LockGuard").field("value", self).finish()
694    }
695}
696
697impl<'lock, T: fmt::Display + ?Sized> fmt::Display for LockGuard<'lock, T> {
698    #[inline]
699    fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
700        <T as fmt::Display>::fmt(self, fmtr)
701    }
702}
703
704impl<'lock, T: ?Sized + Hash> Hash for LockGuard<'lock, T> {
705    #[inline]
706    fn hash<H: Hasher>(&self, state: &mut H) {
707        <T as Hash>::hash(self, state);
708    }
709}
710
711impl<'lock, T: ?Sized + PartialEq> PartialEq<T> for LockGuard<'lock, T> {
712    #[inline]
713    fn eq(&self, other: &T) -> bool {
714        <T as PartialEq>::eq(self, other)
715    }
716}
717
718impl<'other, 'lock, T: ?Sized + PartialEq> PartialEq<LockGuard<'other, T>> for LockGuard<'lock, T> {
719    #[inline]
720    fn eq(&self, other: &LockGuard<'other, T>) -> bool {
721        <T as PartialEq>::eq(self, other)
722    }
723}
724
725impl<'lock, T: ?Sized + Eq> Eq for LockGuard<'lock, T> {}
726
727impl<'lock, T: ?Sized + PartialOrd> PartialOrd<T> for LockGuard<'lock, T> {
728    #[inline]
729    fn partial_cmp(&self, other: &T) -> Option<Ordering> {
730        <T as PartialOrd>::partial_cmp(self, other)
731    }
732}
733
734impl<'other, 'lock, T: ?Sized + PartialOrd> PartialOrd<LockGuard<'other, T>> for LockGuard<'lock, T> {
735    #[inline]
736    fn partial_cmp(&self, other: &LockGuard<'other, T>) -> Option<Ordering> {
737        <T as PartialOrd>::partial_cmp(self, other)
738    }
739}
740
741impl<'lock, T: ?Sized + Ord> Ord for LockGuard<'lock, T> {
742    #[inline]
743    fn cmp(&self, other: &LockGuard<'lock, T>) -> Ordering {
744        <T as Ord>::cmp(self, other)
745    }
746}
747
748/// An error returned from the [`LockCell::try_lock()`] method to indicate
749/// that the `LockCell` could not be locked.
750///
751/// [`LockCell::try_lock()`]: ./struct.LockCell.html#method.try_lock
752#[non_exhaustive]
753pub struct TryLockError {
754    /// The location where the `LockCell` was first locked.
755    #[cfg(feature = "debug_lockcell")]
756    first_lock_location: &'static Location<'static>,
757    /// The latest location where the `LockCell` was locked. This should provide
758    /// the location of the erroneous lock.
759    #[cfg(feature = "debug_lockcell")]
760    latest_lock_location: &'static Location<'static>,
761    _priv: (),
762}
763
764impl TryLockError {
765    /// Create a new `TryLockError` from the given caller location.
766    #[cfg_attr(not(feature = "debug_lockcell"), allow(unused_variables))]
767    #[track_caller]
768    #[inline]
769    fn new<T: ?Sized>(cell: &LockCell<T>) -> Self {
770        TryLockError {
771            #[cfg(feature = "debug_lockcell")]
772            first_lock_location: cell
773                .first_locked_at
774                .get()
775                .expect("Cell must be already locked"),
776            #[cfg(feature = "debug_lockcell")]
777            latest_lock_location: Location::caller(),
778            _priv: (),
779        }
780    }
781}
782
783impl fmt::Debug for TryLockError {
784    #[inline]
785    fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
786        let mut builder = fmtr.debug_struct("TryLockError");
787
788        #[cfg(feature = "debug_lockcell")]
789        {
790            builder.field("first_locked_at", &self.first_lock_location);
791            builder.field("last_locked_at", &self.latest_lock_location);
792        }
793
794        builder.finish_non_exhaustive()
795    }
796}
797
798impl fmt::Display for TryLockError {
799    #[inline]
800    fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
801        #[cfg(feature = "debug_lockcell")]
802        {
803            write!(
804                fmtr,
805                "first lock at {} conflicts with lock at {}",
806                self.first_lock_location, self.latest_lock_location,
807            )
808        }
809
810        #[cfg(not(feature = "debug_lockcell"))]
811        {
812            fmtr.write_str("already locked")
813        }
814    }
815}
816
817#[cfg(feature = "enable_std")]
818impl StdError for TryLockError {}
819
820#[cfg(test)]
821mod tests {
822    use super::*;
823
824    #[test]
825    fn locking() {
826        let mut mtx = LockCell::new(23);
827
828        let mut lk = mtx.lock();
829        assert_eq!(*lk, 23);
830        assert!(mtx.is_locked());
831
832        *lk = 32;
833        assert_eq!(*lk, 32);
834
835        assert!(mtx.try_lock().is_err());
836        drop(lk);
837        assert_eq!(*mtx.get_mut(), 32);
838    }
839
840    #[test]
841    fn lock_map() {
842        #[derive(Default, Debug)]
843        struct TestData {
844            x: i32,
845            y: i32,
846        }
847
848        let mtx = LockCell::<TestData>::default();
849        let mut lk = LockGuard::map(mtx.lock(), |test_data| &mut test_data.y);
850        *lk = 42;
851        drop(lk);
852
853        let lk = mtx.lock();
854        let mut lk = match LockGuard::filter_map(lk, |data| Some(&mut data.x)) {
855            Ok(new_lk) => new_lk,
856            Err(old_lk) => panic!("{:?}", old_lk),
857        };
858        assert!(mtx.is_locked());
859        *lk = 21;
860        assert_eq!(*lk, 21);
861        match LockGuard::filter_map(lk, |_| -> Option<&mut i32> { None }) {
862            Ok(new_lk) => panic!("Unexpected lock guard found: {:?}", new_lk),
863            Err(_) => {}
864        }
865
866        let data = mtx.into_inner();
867        assert_eq!(data.x, 21);
868        assert_eq!(data.y, 42);
869    }
870}