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