Skip to main content

rustc_data_structures/sync/
lock.rs

1//! This module implements a lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
2//! It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync` traits.
3
4use std::{fmt, hint};
5
6#[derive(#[automatically_derived]
impl ::core::clone::Clone for Mode {
    #[inline]
    fn clone(&self) -> Mode { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Mode { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for Mode {
    #[inline]
    fn eq(&self, other: &Mode) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq)]
7pub enum Mode {
8    NoSync,
9    Sync,
10}
11
12use std::cell::{Cell, UnsafeCell};
13use std::marker::PhantomData;
14use std::mem::ManuallyDrop;
15use std::ops::{Deref, DerefMut};
16
17use parking_lot::RawMutex;
18use parking_lot::lock_api::RawMutex as _;
19
20use crate::sync::{DynSend, DynSync, mode};
21
22/// A guard holding mutable access to a `Lock` which is in a locked state.
23#[must_use = "if unused the Lock will immediately unlock"]
24pub struct LockGuard<'a, T> {
25    lock: &'a Lock<T>,
26    marker: PhantomData<&'a mut T>,
27
28    /// The synchronization mode of the lock. This is explicitly passed to let LLVM relate it
29    /// to the original lock operation.
30    mode: Mode,
31}
32
33impl<'a, T: 'a> Deref for LockGuard<'a, T> {
34    type Target = T;
35    #[inline]
36    fn deref(&self) -> &T {
37        // SAFETY: We have shared access to the mutable access owned by this type,
38        // so we can give out a shared reference.
39        unsafe { &*self.lock.data.get() }
40    }
41}
42
43impl<'a, T: 'a> DerefMut for LockGuard<'a, T> {
44    #[inline]
45    fn deref_mut(&mut self) -> &mut T {
46        // SAFETY: We have mutable access to the data so we can give out a mutable reference.
47        unsafe { &mut *self.lock.data.get() }
48    }
49}
50
51impl<'a, T: 'a> Drop for LockGuard<'a, T> {
52    #[inline]
53    fn drop(&mut self) {
54        // SAFETY (union access): We get `self.mode` from the lock operation so it is consistent
55        // with the `lock.mode` state. This means we access the right union fields.
56        match self.mode {
57            Mode::NoSync => {
58                let cell = unsafe { &self.lock.mode_union.no_sync };
59                if true {
    if !cell.get() {
        ::core::panicking::panic("assertion failed: cell.get()")
    };
};debug_assert!(cell.get());
60                cell.set(false);
61            }
62            // SAFETY (unlock): We know that the lock is locked as this type is a proof of that.
63            Mode::Sync => unsafe { self.lock.mode_union.sync.unlock() },
64        }
65    }
66}
67
68union ModeUnion {
69    /// Indicates if the cell is locked. Only used if `Lock.mode` is `NoSync`.
70    no_sync: ManuallyDrop<Cell<bool>>,
71
72    /// A lock implementation that's only used if `Lock.mode` is `Sync`.
73    sync: ManuallyDrop<RawMutex>,
74}
75
76/// The value representing a locked state for the `Cell`.
77const LOCKED: bool = true;
78
79/// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
80/// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
81pub struct Lock<T> {
82    /// Indicates if synchronization is used via `mode_union.sync` if it's `Sync`, or if a
83    /// not thread safe cell is used via `mode_union.no_sync` if it's `NoSync`.
84    /// This is set on initialization and never changed.
85    mode: Mode,
86
87    mode_union: ModeUnion,
88    data: UnsafeCell<T>,
89}
90
91impl<T> Lock<T> {
92    #[inline(always)]
93    pub fn new(inner: T) -> Self {
94        let (mode, mode_union) = if mode::might_be_dyn_thread_safe() {
95            hint::cold_path();
96            // Create the lock with synchronization enabled using the `RawMutex` type.
97            (Mode::Sync, ModeUnion { sync: ManuallyDrop::new(RawMutex::INIT) })
98        } else {
99            // Create the lock with synchronization disabled.
100            (Mode::NoSync, ModeUnion { no_sync: ManuallyDrop::new(Cell::new(!LOCKED)) })
101        };
102        Lock { mode, mode_union, data: UnsafeCell::new(inner) }
103    }
104
105    #[inline(always)]
106    pub fn into_inner(self) -> T {
107        self.data.into_inner()
108    }
109
110    #[inline(always)]
111    pub fn get_mut(&mut self) -> &mut T {
112        self.data.get_mut()
113    }
114
115    #[inline(always)]
116    pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
117        let mode = self.mode;
118        // SAFETY: This is safe since the union fields are used in accordance with `self.mode`.
119        match mode {
120            Mode::NoSync => {
121                let cell = unsafe { &self.mode_union.no_sync };
122                let was_unlocked = cell.get() != LOCKED;
123                if was_unlocked {
124                    cell.set(LOCKED);
125                }
126                was_unlocked
127            }
128            Mode::Sync => unsafe { self.mode_union.sync.try_lock() },
129        }
130        .then(|| LockGuard { lock: self, marker: PhantomData, mode })
131    }
132
133    /// This acquires the lock assuming synchronization is in a specific mode.
134    ///
135    /// Safety
136    /// This method must only be called with `Mode::Sync` if `might_be_dyn_thread_safe` was
137    /// true on lock creation.
138    #[inline(always)]
139    #[track_caller]
140    pub unsafe fn lock_assume(&self, mode: Mode) -> LockGuard<'_, T> {
141        #[inline(never)]
142        #[track_caller]
143        #[cold]
144        fn lock_held() -> ! {
145            { ::core::panicking::panic_fmt(format_args!("lock was already held")); }panic!("lock was already held")
146        }
147
148        // SAFETY: This is safe since the union fields are used in accordance with `mode`
149        // which also must match `self.mode` due to the safety precondition.
150        unsafe {
151            match mode {
152                Mode::NoSync => {
153                    if self.mode_union.no_sync.replace(LOCKED) == LOCKED {
154                        hint::cold_path();
155                        lock_held()
156                    }
157                }
158                Mode::Sync => self.mode_union.sync.lock(),
159            }
160        }
161        LockGuard { lock: self, marker: PhantomData, mode }
162    }
163
164    #[inline(always)]
165    #[track_caller]
166    pub fn lock(&self) -> LockGuard<'_, T> {
167        unsafe { self.lock_assume(self.mode) }
168    }
169}
170
171unsafe impl<T: DynSend> DynSend for Lock<T> {}
172unsafe impl<T: DynSend> DynSync for Lock<T> {}
173
174impl<T> Lock<T> {
175    #[inline(always)]
176    #[track_caller]
177    pub fn with_lock<F: FnOnce(&mut T) -> R, R>(&self, f: F) -> R {
178        f(&mut *self.lock())
179    }
180
181    #[inline(always)]
182    #[track_caller]
183    pub fn borrow(&self) -> LockGuard<'_, T> {
184        self.lock()
185    }
186
187    #[inline(always)]
188    #[track_caller]
189    pub fn borrow_mut(&self) -> LockGuard<'_, T> {
190        self.lock()
191    }
192}
193
194impl<T: Default> Default for Lock<T> {
195    #[inline]
196    fn default() -> Self {
197        Lock::new(T::default())
198    }
199}
200
201impl<T: fmt::Debug> fmt::Debug for Lock<T> {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        match self.try_lock() {
204            Some(guard) => f.debug_struct("Lock").field("data", &&*guard).finish(),
205            None => {
206                struct LockedPlaceholder;
207                impl fmt::Debug for LockedPlaceholder {
208                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209                        f.write_str("<locked>")
210                    }
211                }
212
213                f.debug_struct("Lock").field("data", &LockedPlaceholder).finish()
214            }
215        }
216    }
217}