Skip to main content

rustc_data_structures/sync/
freeze.rs

1use std::cell::UnsafeCell;
2use std::hint;
3use std::marker::PhantomData;
4use std::ops::{Deref, DerefMut};
5use std::ptr::NonNull;
6use std::sync::atomic::{AtomicBool, Ordering};
7
8use crate::sync::{DynSend, DynSync, ReadGuard, RwLock, WriteGuard};
9
10/// A type which allows mutation using a lock until
11/// the value is frozen and can be accessed lock-free.
12///
13/// Unlike `RwLock`, it can be used to prevent mutation past a point.
14#[derive(#[automatically_derived]
impl<T: ::core::default::Default> ::core::default::Default for FreezeLock<T> {
    #[inline]
    fn default() -> FreezeLock<T> {
        FreezeLock {
            data: ::core::default::Default::default(),
            frozen: ::core::default::Default::default(),
            lock: ::core::default::Default::default(),
        }
    }
}Default)]
15pub struct FreezeLock<T> {
16    data: UnsafeCell<T>,
17    frozen: AtomicBool,
18
19    /// This lock protects writes to the `data` and `frozen` fields.
20    lock: RwLock<()>,
21}
22
23unsafe impl<T: DynSync + DynSend> DynSync for FreezeLock<T> {}
24
25impl<T> FreezeLock<T> {
26    #[inline]
27    pub fn new(value: T) -> Self {
28        Self::with(value, false)
29    }
30
31    #[inline]
32    pub fn frozen(value: T) -> Self {
33        Self::with(value, true)
34    }
35
36    #[inline]
37    pub fn with(value: T, frozen: bool) -> Self {
38        Self {
39            data: UnsafeCell::new(value),
40            frozen: AtomicBool::new(frozen),
41            lock: RwLock::new(()),
42        }
43    }
44
45    /// Clones the inner value along with the frozen state.
46    #[inline]
47    pub fn clone(&self) -> Self
48    where
49        T: Clone,
50    {
51        let lock = self.read();
52        Self::with(lock.clone(), self.is_frozen())
53    }
54
55    #[inline]
56    pub fn is_frozen(&self) -> bool {
57        self.frozen.load(Ordering::Acquire)
58    }
59
60    /// Get the inner value if frozen.
61    #[inline]
62    pub fn get(&self) -> Option<&T> {
63        if self.frozen.load(Ordering::Acquire) {
64            // SAFETY: This is frozen so the data cannot be modified.
65            unsafe { Some(&*self.data.get()) }
66        } else {
67            hint::cold_path();
68            None
69        }
70    }
71
72    #[inline]
73    pub fn read(&self) -> FreezeReadGuard<'_, T> {
74        FreezeReadGuard {
75            _lock_guard: if self.frozen.load(Ordering::Acquire) {
76                None
77            } else {
78                Some(self.lock.read())
79            },
80            data: unsafe { NonNull::new_unchecked(self.data.get()) },
81        }
82    }
83
84    #[inline]
85    pub fn borrow(&self) -> FreezeReadGuard<'_, T> {
86        self.read()
87    }
88
89    #[inline]
90    #[track_caller]
91    pub fn write(&self) -> FreezeWriteGuard<'_, T> {
92        self.try_write().expect("data should not be frozen if we're still attempting to mutate it")
93    }
94
95    #[inline]
96    pub fn try_write(&self) -> Option<FreezeWriteGuard<'_, T>> {
97        let _lock_guard = self.lock.write();
98        // Use relaxed ordering since we're in the write lock.
99        if self.frozen.load(Ordering::Relaxed) {
100            None
101        } else {
102            Some(FreezeWriteGuard {
103                _lock_guard,
104                data: unsafe { NonNull::new_unchecked(self.data.get()) },
105                frozen: &self.frozen,
106                marker: PhantomData,
107            })
108        }
109    }
110
111    #[inline]
112    pub fn freeze(&self) -> &T {
113        if !self.frozen.load(Ordering::Acquire) {
114            // Get the lock to ensure no concurrent writes and that we release the latest write.
115            let _lock = self.lock.write();
116            self.frozen.store(true, Ordering::Release);
117        }
118
119        // SAFETY: This is frozen so the data cannot be modified and shared access is sound.
120        unsafe { &*self.data.get() }
121    }
122}
123
124/// A guard holding shared access to a `FreezeLock` which is in a locked state or frozen.
125#[must_use = "if unused the FreezeLock may immediately unlock"]
126pub struct FreezeReadGuard<'a, T: ?Sized> {
127    _lock_guard: Option<ReadGuard<'a, ()>>,
128    data: NonNull<T>,
129}
130
131impl<'a, T: ?Sized + 'a> Deref for FreezeReadGuard<'a, T> {
132    type Target = T;
133    #[inline]
134    fn deref(&self) -> &T {
135        // SAFETY: If the lock is not frozen, `_lock_guard` holds the lock to the `UnsafeCell` so
136        // this has shared access until the `FreezeReadGuard` is dropped. If the lock is frozen,
137        // the data cannot be modified and shared access is sound.
138        unsafe { &*self.data.as_ptr() }
139    }
140}
141
142impl<'a, T: ?Sized> FreezeReadGuard<'a, T> {
143    #[inline]
144    pub fn map<U: ?Sized>(this: Self, f: impl FnOnce(&T) -> &U) -> FreezeReadGuard<'a, U> {
145        FreezeReadGuard { data: NonNull::from(f(&*this)), _lock_guard: this._lock_guard }
146    }
147}
148
149/// A guard holding mutable access to a `FreezeLock` which is in a locked state or frozen.
150#[must_use = "if unused the FreezeLock may immediately unlock"]
151pub struct FreezeWriteGuard<'a, T: ?Sized> {
152    _lock_guard: WriteGuard<'a, ()>,
153    frozen: &'a AtomicBool,
154    data: NonNull<T>,
155    marker: PhantomData<&'a mut T>,
156}
157
158impl<'a, T> FreezeWriteGuard<'a, T> {
159    pub fn freeze(self) -> &'a T {
160        self.frozen.store(true, Ordering::Release);
161
162        // SAFETY: This is frozen so the data cannot be modified and shared access is sound.
163        unsafe { &*self.data.as_ptr() }
164    }
165}
166
167impl<'a, T: ?Sized> FreezeWriteGuard<'a, T> {
168    #[inline]
169    pub fn map<U: ?Sized>(
170        mut this: Self,
171        f: impl FnOnce(&mut T) -> &mut U,
172    ) -> FreezeWriteGuard<'a, U> {
173        FreezeWriteGuard {
174            data: NonNull::from(f(&mut *this)),
175            _lock_guard: this._lock_guard,
176            frozen: this.frozen,
177            marker: PhantomData,
178        }
179    }
180}
181
182impl<'a, T: ?Sized + 'a> Deref for FreezeWriteGuard<'a, T> {
183    type Target = T;
184    #[inline]
185    fn deref(&self) -> &T {
186        // SAFETY: `self._lock_guard` holds the lock to the `UnsafeCell` so this has shared access.
187        unsafe { &*self.data.as_ptr() }
188    }
189}
190
191impl<'a, T: ?Sized + 'a> DerefMut for FreezeWriteGuard<'a, T> {
192    #[inline]
193    fn deref_mut(&mut self) -> &mut T {
194        // SAFETY: `self._lock_guard` holds the lock to the `UnsafeCell` so this has mutable access.
195        unsafe { &mut *self.data.as_ptr() }
196    }
197}