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;
5
6#[derive(Clone, Copy, PartialEq)]
7pub enum Mode {
8    NoSync,
9    Sync,
10}
11
12use std::cell::{Cell, UnsafeCell};
13use std::intrinsics::unlikely;
14use std::marker::PhantomData;
15use std::mem::ManuallyDrop;
16use std::ops::{Deref, DerefMut};
17
18use parking_lot::RawMutex;
19use parking_lot::lock_api::RawMutex as _;
20
21use crate::sync::{DynSend, DynSync, mode};
22
23/// A guard holding mutable access to a `Lock` which is in a locked state.
24#[must_use = "if unused the Lock will immediately unlock"]
25pub struct LockGuard<'a, T> {
26    lock: &'a Lock<T>,
27    marker: PhantomData<&'a mut T>,
28
29    /// The synchronization mode of the lock. This is explicitly passed to let LLVM relate it
30    /// to the original lock operation.
31    mode: Mode,
32}
33
34impl<'a, T: 'a> Deref for LockGuard<'a, T> {
35    type Target = T;
36    #[inline]
37    fn deref(&self) -> &T {
38        // SAFETY: We have shared access to the mutable access owned by this type,
39        // so we can give out a shared reference.
40        unsafe { &*self.lock.data.get() }
41    }
42}
43
44impl<'a, T: 'a> DerefMut for LockGuard<'a, T> {
45    #[inline]
46    fn deref_mut(&mut self) -> &mut T {
47        // SAFETY: We have mutable access to the data so we can give out a mutable reference.
48        unsafe { &mut *self.lock.data.get() }
49    }
50}
51
52impl<'a, T: 'a> Drop for LockGuard<'a, T> {
53    #[inline]
54    fn drop(&mut self) {
55        // SAFETY (union access): We get `self.mode` from the lock operation so it is consistent
56        // with the `lock.mode` state. This means we access the right union fields.
57        match self.mode {
58            Mode::NoSync => {
59                let cell = unsafe { &self.lock.mode_union.no_sync };
60                debug_assert!(cell.get());
61                cell.set(false);
62            }
63            // SAFETY (unlock): We know that the lock is locked as this type is a proof of that.
64            Mode::Sync => unsafe { self.lock.mode_union.sync.unlock() },
65        }
66    }
67}
68
69union ModeUnion {
70    /// Indicates if the cell is locked. Only used if `Lock.mode` is `NoSync`.
71    no_sync: ManuallyDrop<Cell<bool>>,
72
73    /// A lock implementation that's only used if `Lock.mode` is `Sync`.
74    sync: ManuallyDrop<RawMutex>,
75}
76
77/// The value representing a locked state for the `Cell`.
78const LOCKED: bool = true;
79
80/// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
81/// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
82pub struct Lock<T> {
83    /// Indicates if synchronization is used via `mode_union.sync` if it's `Sync`, or if a
84    /// not thread safe cell is used via `mode_union.no_sync` if it's `NoSync`.
85    /// This is set on initialization and never changed.
86    mode: Mode,
87
88    mode_union: ModeUnion,
89    data: UnsafeCell<T>,
90}
91
92impl<T> Lock<T> {
93    #[inline(always)]
94    pub fn new(inner: T) -> Self {
95        let (mode, mode_union) = if unlikely(mode::might_be_dyn_thread_safe()) {
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            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 unlikely(self.mode_union.no_sync.replace(LOCKED) == LOCKED) {
154                        lock_held()
155                    }
156                }
157                Mode::Sync => self.mode_union.sync.lock(),
158            }
159        }
160        LockGuard { lock: self, marker: PhantomData, mode }
161    }
162
163    #[inline(always)]
164    #[track_caller]
165    pub fn lock(&self) -> LockGuard<'_, T> {
166        unsafe { self.lock_assume(self.mode) }
167    }
168}
169
170unsafe impl<T: DynSend> DynSend for Lock<T> {}
171unsafe impl<T: DynSend> DynSync for Lock<T> {}
172
173impl<T> Lock<T> {
174    #[inline(always)]
175    #[track_caller]
176    pub fn with_lock<F: FnOnce(&mut T) -> R, R>(&self, f: F) -> R {
177        f(&mut *self.lock())
178    }
179
180    #[inline(always)]
181    #[track_caller]
182    pub fn borrow(&self) -> LockGuard<'_, T> {
183        self.lock()
184    }
185
186    #[inline(always)]
187    #[track_caller]
188    pub fn borrow_mut(&self) -> LockGuard<'_, T> {
189        self.lock()
190    }
191}
192
193impl<T: Default> Default for Lock<T> {
194    #[inline]
195    fn default() -> Self {
196        Lock::new(T::default())
197    }
198}
199
200impl<T: fmt::Debug> fmt::Debug for Lock<T> {
201    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202        match self.try_lock() {
203            Some(guard) => f.debug_struct("Lock").field("data", &&*guard).finish(),
204            None => {
205                struct LockedPlaceholder;
206                impl fmt::Debug for LockedPlaceholder {
207                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208                        f.write_str("<locked>")
209                    }
210                }
211
212                f.debug_struct("Lock").field("data", &LockedPlaceholder).finish()
213            }
214        }
215    }
216}