rustc_data_structures/sync/
freeze.rs

1use std::cell::UnsafeCell;
2use std::intrinsics::likely;
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(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 likely(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            None
68        }
69    }
70
71    #[inline]
72    pub fn read(&self) -> FreezeReadGuard<'_, T> {
73        FreezeReadGuard {
74            _lock_guard: if self.frozen.load(Ordering::Acquire) {
75                None
76            } else {
77                Some(self.lock.read())
78            },
79            data: unsafe { NonNull::new_unchecked(self.data.get()) },
80        }
81    }
82
83    #[inline]
84    pub fn borrow(&self) -> FreezeReadGuard<'_, T> {
85        self.read()
86    }
87
88    #[inline]
89    #[track_caller]
90    pub fn write(&self) -> FreezeWriteGuard<'_, T> {
91        self.try_write().expect("still mutable")
92    }
93
94    #[inline]
95    pub fn try_write(&self) -> Option<FreezeWriteGuard<'_, T>> {
96        let _lock_guard = self.lock.write();
97        // Use relaxed ordering since we're in the write lock.
98        if self.frozen.load(Ordering::Relaxed) {
99            None
100        } else {
101            Some(FreezeWriteGuard {
102                _lock_guard,
103                data: unsafe { NonNull::new_unchecked(self.data.get()) },
104                frozen: &self.frozen,
105                marker: PhantomData,
106            })
107        }
108    }
109
110    #[inline]
111    pub fn freeze(&self) -> &T {
112        if !self.frozen.load(Ordering::Acquire) {
113            // Get the lock to ensure no concurrent writes and that we release the latest write.
114            let _lock = self.lock.write();
115            self.frozen.store(true, Ordering::Release);
116        }
117
118        // SAFETY: This is frozen so the data cannot be modified and shared access is sound.
119        unsafe { &*self.data.get() }
120    }
121}
122
123/// A guard holding shared access to a `FreezeLock` which is in a locked state or frozen.
124#[must_use = "if unused the FreezeLock may immediately unlock"]
125pub struct FreezeReadGuard<'a, T: ?Sized> {
126    _lock_guard: Option<ReadGuard<'a, ()>>,
127    data: NonNull<T>,
128}
129
130impl<'a, T: ?Sized + 'a> Deref for FreezeReadGuard<'a, T> {
131    type Target = T;
132    #[inline]
133    fn deref(&self) -> &T {
134        // SAFETY: If the lock is not frozen, `_lock_guard` holds the lock to the `UnsafeCell` so
135        // this has shared access until the `FreezeReadGuard` is dropped. If the lock is frozen,
136        // the data cannot be modified and shared access is sound.
137        unsafe { &*self.data.as_ptr() }
138    }
139}
140
141impl<'a, T: ?Sized> FreezeReadGuard<'a, T> {
142    #[inline]
143    pub fn map<U: ?Sized>(this: Self, f: impl FnOnce(&T) -> &U) -> FreezeReadGuard<'a, U> {
144        FreezeReadGuard { data: NonNull::from(f(&*this)), _lock_guard: this._lock_guard }
145    }
146}
147
148/// A guard holding mutable access to a `FreezeLock` which is in a locked state or frozen.
149#[must_use = "if unused the FreezeLock may immediately unlock"]
150pub struct FreezeWriteGuard<'a, T: ?Sized> {
151    _lock_guard: WriteGuard<'a, ()>,
152    frozen: &'a AtomicBool,
153    data: NonNull<T>,
154    marker: PhantomData<&'a mut T>,
155}
156
157impl<'a, T> FreezeWriteGuard<'a, T> {
158    pub fn freeze(self) -> &'a T {
159        self.frozen.store(true, Ordering::Release);
160
161        // SAFETY: This is frozen so the data cannot be modified and shared access is sound.
162        unsafe { &*self.data.as_ptr() }
163    }
164}
165
166impl<'a, T: ?Sized> FreezeWriteGuard<'a, T> {
167    #[inline]
168    pub fn map<U: ?Sized>(
169        mut this: Self,
170        f: impl FnOnce(&mut T) -> &mut U,
171    ) -> FreezeWriteGuard<'a, U> {
172        FreezeWriteGuard {
173            data: NonNull::from(f(&mut *this)),
174            _lock_guard: this._lock_guard,
175            frozen: this.frozen,
176            marker: PhantomData,
177        }
178    }
179}
180
181impl<'a, T: ?Sized + 'a> Deref for FreezeWriteGuard<'a, T> {
182    type Target = T;
183    #[inline]
184    fn deref(&self) -> &T {
185        // SAFETY: `self._lock_guard` holds the lock to the `UnsafeCell` so this has shared access.
186        unsafe { &*self.data.as_ptr() }
187    }
188}
189
190impl<'a, T: ?Sized + 'a> DerefMut for FreezeWriteGuard<'a, T> {
191    #[inline]
192    fn deref_mut(&mut self) -> &mut T {
193        // SAFETY: `self._lock_guard` holds the lock to the `UnsafeCell` so this has mutable access.
194        unsafe { &mut *self.data.as_ptr() }
195    }
196}