use crate::sync::{AtomicBool, ReadGuard, RwLock, WriteGuard}; #[cfg(parallel_compiler)] use crate::sync::{DynSend, DynSync}; use std::{ cell::UnsafeCell, intrinsics::likely, marker::PhantomData, ops::{Deref, DerefMut}, ptr::NonNull, sync::atomic::Ordering, }; /// A type which allows mutation using a lock until /// the value is frozen and can be accessed lock-free. /// /// Unlike `RwLock`, it can be used to prevent mutation past a point. #[derive(Default)] pub struct FreezeLock { data: UnsafeCell, frozen: AtomicBool, /// This lock protects writes to the `data` and `frozen` fields. lock: RwLock<()>, } #[cfg(parallel_compiler)] unsafe impl DynSync for FreezeLock {} impl FreezeLock { #[inline] pub fn new(value: T) -> Self { Self::with(value, false) } #[inline] pub fn frozen(value: T) -> Self { Self::with(value, true) } #[inline] pub fn with(value: T, frozen: bool) -> Self { Self { data: UnsafeCell::new(value), frozen: AtomicBool::new(frozen), lock: RwLock::new(()), } } /// Clones the inner value along with the frozen state. #[inline] pub fn clone(&self) -> Self where T: Clone, { let lock = self.read(); Self::with(lock.clone(), self.is_frozen()) } #[inline] pub fn is_frozen(&self) -> bool { self.frozen.load(Ordering::Acquire) } /// Get the inner value if frozen. #[inline] pub fn get(&self) -> Option<&T> { if likely(self.frozen.load(Ordering::Acquire)) { // SAFETY: This is frozen so the data cannot be modified. unsafe { Some(&*self.data.get()) } } else { None } } #[inline] pub fn read(&self) -> FreezeReadGuard<'_, T> { FreezeReadGuard { _lock_guard: if self.frozen.load(Ordering::Acquire) { None } else { Some(self.lock.read()) }, data: unsafe { NonNull::new_unchecked(self.data.get()) }, } } #[inline] pub fn borrow(&self) -> FreezeReadGuard<'_, T> { self.read() } #[inline] #[track_caller] pub fn write(&self) -> FreezeWriteGuard<'_, T> { self.try_write().expect("still mutable") } #[inline] pub fn try_write(&self) -> Option> { let _lock_guard = self.lock.write(); // Use relaxed ordering since we're in the write lock. if self.frozen.load(Ordering::Relaxed) { None } else { Some(FreezeWriteGuard { _lock_guard, data: unsafe { NonNull::new_unchecked(self.data.get()) }, frozen: &self.frozen, marker: PhantomData, }) } } #[inline] pub fn freeze(&self) -> &T { if !self.frozen.load(Ordering::Acquire) { // Get the lock to ensure no concurrent writes and that we release the latest write. let _lock = self.lock.write(); self.frozen.store(true, Ordering::Release); } // SAFETY: This is frozen so the data cannot be modified and shared access is sound. unsafe { &*self.data.get() } } } /// A guard holding shared access to a `FreezeLock` which is in a locked state or frozen. #[must_use = "if unused the FreezeLock may immediately unlock"] pub struct FreezeReadGuard<'a, T: ?Sized> { _lock_guard: Option>, data: NonNull, } impl<'a, T: ?Sized + 'a> Deref for FreezeReadGuard<'a, T> { type Target = T; #[inline] fn deref(&self) -> &T { // SAFETY: If the lock is not frozen, `_lock_guard` holds the lock to the `UnsafeCell` so // this has shared access until the `FreezeReadGuard` is dropped. If the lock is frozen, // the data cannot be modified and shared access is sound. unsafe { &*self.data.as_ptr() } } } impl<'a, T: ?Sized> FreezeReadGuard<'a, T> { #[inline] pub fn map(this: Self, f: impl FnOnce(&T) -> &U) -> FreezeReadGuard<'a, U> { FreezeReadGuard { data: NonNull::from(f(&*this)), _lock_guard: this._lock_guard } } } /// A guard holding mutable access to a `FreezeLock` which is in a locked state or frozen. #[must_use = "if unused the FreezeLock may immediately unlock"] pub struct FreezeWriteGuard<'a, T: ?Sized> { _lock_guard: WriteGuard<'a, ()>, frozen: &'a AtomicBool, data: NonNull, marker: PhantomData<&'a mut T>, } impl<'a, T> FreezeWriteGuard<'a, T> { pub fn freeze(self) -> &'a T { self.frozen.store(true, Ordering::Release); // SAFETY: This is frozen so the data cannot be modified and shared access is sound. unsafe { &*self.data.as_ptr() } } } impl<'a, T: ?Sized> FreezeWriteGuard<'a, T> { #[inline] pub fn map( mut this: Self, f: impl FnOnce(&mut T) -> &mut U, ) -> FreezeWriteGuard<'a, U> { FreezeWriteGuard { data: NonNull::from(f(&mut *this)), _lock_guard: this._lock_guard, frozen: this.frozen, marker: PhantomData, } } } impl<'a, T: ?Sized + 'a> Deref for FreezeWriteGuard<'a, T> { type Target = T; #[inline] fn deref(&self) -> &T { // SAFETY: `self._lock_guard` holds the lock to the `UnsafeCell` so this has shared access. unsafe { &*self.data.as_ptr() } } } impl<'a, T: ?Sized + 'a> DerefMut for FreezeWriteGuard<'a, T> { #[inline] fn deref_mut(&mut self) -> &mut T { // SAFETY: `self._lock_guard` holds the lock to the `UnsafeCell` so this has mutable access. unsafe { &mut *self.data.as_ptr() } } }