daemonic_error 1.0.0

Errors that compose, predict, and leave receipts - Compose: algebraic combination (in active development) - Predict: Glass/Severity - Receipts: audit trail, position, checksum - Reflection: Runtime Reflection through TopologySegment (in active development)
//! This module implements a lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
//! It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync` traits.

use core::cell::Cell;
use core::fmt;
use core::mem::ManuallyDrop;

/// The value representing a locked state for the `Cell`.
const LOCKED: bool = true;

/// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
/// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
pub struct Lock<T> {
	/// Indicates if synchronization is used via `mode_union.sync` if it's `Sync`, or if a
	/// not thread safe cell is used via `mode_union.no_sync` if it's `NoSync`.
	/// This is set on initialization and never changed.
	mode: Mode,
	
	mode_union: ModeUnion,
	data: core::cell::UnsafeCell<T>,
}

union ModeUnion {
	/// Indicates if the cell is locked. Only used if `Lock.mode` is `NoSync`.
	no_sync: ManuallyDrop<Cell<bool>>,
	
	/// A lock implementation that's only used if `Lock.mode` is `Sync`.
	sync: ManuallyDrop<RawMutex>,
}
impl<T> Lock<T> {
	#[inline(always)]
	pub fn new(inner: T) -> Self {
		let (mode, mode_union) = if unlikely(mode::might_be_dyn_thread_safe()) {
			// Create the lock with synchronization enabled using the `RawMutex` type.
			(Mode::Sync, ModeUnion { sync: ManuallyDrop::new(RawMutex::INIT) })
		} else {
			// Create the lock with synchronization disabled.
			(Mode::NoSync, ModeUnion { no_sync: ManuallyDrop::new(Cell::new(!LOCKED)) })
		};
		Lock { mode, mode_union, data: UnsafeCell::new(inner) }
	}
	
	#[inline(always)]
	pub fn into_inner(self) -> T {
		self.data.into_inner()
	}
	
	#[inline(always)]
	pub fn get_mut(&mut self) -> &mut T {
		self.data.get_mut()
	}
	
	#[inline(always)]
	pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
		let mode = self.mode;
		// SAFETY: This is safe since the union fields are used in accordance with `self.mode`.
		match mode {
			Mode::NoSync => {
				let cell = unsafe { &self.mode_union.no_sync };
				let was_unlocked = cell.get() != LOCKED;
				if was_unlocked {
					cell.set(LOCKED);
				}
				was_unlocked
			}
			Mode::Sync => unsafe { self.mode_union.sync.try_lock() },
		}
			.then(|| LockGuard { lock: self, marker: PhantomData, mode })
	}
	
	/// This acquires the lock assuming synchronization is in a specific mode.
	///
	/// Safety
	/// This method must only be called with `Mode::Sync` if `might_be_dyn_thread_safe` was
	/// true on lock creation.
	#[inline(always)]
	#[track_caller]
	pub unsafe fn lock_assume(&self, mode: Mode) -> LockGuard<'_, T> {
		#[inline(never)]
		#[track_caller]
		#[cold]
		fn lock_held() -> ! {
			panic!("lock was already held")
		}
		
		// SAFETY: This is safe since the union fields are used in accordance with `mode`
		// which also must match `self.mode` due to the safety precondition.
		unsafe {
			match mode {
				Mode::NoSync => {
					if unlikely(self.mode_union.no_sync.replace(LOCKED) == LOCKED) {
						lock_held()
					}
				}
				Mode::Sync => self.mode_union.sync.lock(),
			}
		}
		LockGuard { lock: self, marker: PhantomData, mode }
	}
	
	#[inline(always)]
	#[track_caller]
	pub fn lock(&self) -> LockGuard<'_, T> {
		unsafe { self.lock_assume(self.mode) }
	}
}

unsafe impl<T: DynSend> DynSend for Lock<T> {}
unsafe impl<T: DynSend> DynSync for Lock<T> {}

impl<T> Lock<T> {
	#[inline(always)]
	#[track_caller]
	pub fn with_lock<F: FnOnce(&mut T) -> R, R>(&self, f: F) -> R {
		f(&mut *self.lock())
	}
	
	#[inline(always)]
	#[track_caller]
	pub fn borrow(&self) -> LockGuard<'_, T> {
		self.lock()
	}
	
	#[inline(always)]
	#[track_caller]
	pub fn borrow_mut(&self) -> LockGuard<'_, T> {
		self.lock()
	}
}

impl<T: Default> Default for Lock<T> {
	#[inline]
	fn default() -> Self {
		Lock::new(T::default())
	}
}

impl<T: fmt::Debug> fmt::Debug for Lock<T> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self.try_lock() {
			Some(guard) => f.debug_struct("Lock").field("data", &&*guard).finish(),
			None => {
				struct LockedPlaceholder;
				impl fmt::Debug for LockedPlaceholder {
					fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
						f.write_str("<locked>")
					}
				}
				
				f.debug_struct("Lock").field("data", &LockedPlaceholder).finish()
			}
		}
	}
}