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}