diff options
Diffstat (limited to 'engine-ecs/src/lock.rs')
| -rw-r--r-- | engine-ecs/src/lock.rs | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/engine-ecs/src/lock.rs b/engine-ecs/src/lock.rs new file mode 100644 index 0000000..fe4e08b --- /dev/null +++ b/engine-ecs/src/lock.rs @@ -0,0 +1,259 @@ +use std::any::type_name; +use std::mem::forget; +use std::ops::{Deref, DerefMut}; + +use parking_lot::{ + MappedRwLockReadGuard, + MappedRwLockWriteGuard, + RwLock, + RwLockReadGuard, + RwLockWriteGuard, +}; + +#[derive(Debug)] +pub struct Lock<Value> +{ + inner: RwLock<Value>, + value_type_name: &'static str, +} + +impl<Value> Lock<Value> +{ + pub fn new(value: Value, value_type_name: &'static str) -> Self + { + Self { + inner: RwLock::new(value), + value_type_name, + } + } + + /// Tries to a acquire a handle to the resource with read access. + /// + /// # Errors + /// Returns `Err` if unavailable (A mutable handle is hold). + pub fn read_nonblock(&self) -> Result<ReadGuard<'_, Value>, Error> + { + let guard = self.inner.try_read().ok_or(Error::ReadUnavailable)?; + + tracing::trace!("Acquired lock to value of type {}", self.value_type_name); + + Ok(ReadGuard { + inner: guard, + value_type_name: self.value_type_name, + }) + } + + /// Tries to a acquire a handle to the resource with mutable access. + /// + /// # Errors + /// Returns `Err` if unavailable (A mutable or immutable handle is hold). + pub fn write_nonblock(&self) -> Result<WriteGuard<'_, Value>, Error> + { + let guard = self.inner.try_write().ok_or(Error::WriteUnavailable)?; + + tracing::trace!( + "Acquired mutable lock to value of type {}", + self.value_type_name + ); + + Ok(WriteGuard { + inner: guard, + value_type_name: self.value_type_name, + }) + } +} + +impl<Value: Default + 'static> Default for Lock<Value> +{ + fn default() -> Self + { + Self::new(Value::default(), type_name::<Value>()) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum Error +{ + #[error("Lock is unavailable for reading")] + ReadUnavailable, + + #[error("Lock is unavailable for writing")] + WriteUnavailable, +} + +#[derive(Debug)] +pub struct ReadGuard<'guard, Value> +{ + inner: RwLockReadGuard<'guard, Value>, + value_type_name: &'static str, +} + +impl<'guard, Value> ReadGuard<'guard, Value> +{ + pub fn try_map<NewValue>( + this: Self, + func: impl FnOnce(&Value) -> Option<&NewValue>, + ) -> Result<MappedReadGuard<'guard, NewValue>, Self> + { + let value_type_name = this.value_type_name; + + // The 'inner' field cannot be moved out of ReadGuard in a normal way since + // ReadGuard implements Drop + let inner = unsafe { std::ptr::read(&raw const this.inner) }; + forget(this); + + match RwLockReadGuard::try_map(inner, func) { + Ok(mapped_guard) => { + Ok(MappedReadGuard { inner: mapped_guard, value_type_name }) + } + Err(unmapped_guard) => Err(Self { + inner: unmapped_guard, + value_type_name, + }), + } + } +} + +impl<Value> Deref for ReadGuard<'_, Value> +{ + type Target = Value; + + fn deref(&self) -> &Self::Target + { + &self.inner + } +} + +impl<Value> Drop for ReadGuard<'_, Value> +{ + fn drop(&mut self) + { + tracing::trace!("Dropped lock to value of type {}", self.value_type_name); + } +} + +#[derive(Debug)] +pub struct MappedReadGuard<'guard, Value> +{ + inner: MappedRwLockReadGuard<'guard, Value>, + value_type_name: &'static str, +} + +impl<Value> Deref for MappedReadGuard<'_, Value> +{ + type Target = Value; + + fn deref(&self) -> &Self::Target + { + &self.inner + } +} + +impl<Value> Drop for MappedReadGuard<'_, Value> +{ + fn drop(&mut self) + { + tracing::trace!( + "Dropped mapped lock to value of type {}", + self.value_type_name + ); + } +} + +#[derive(Debug)] +pub struct WriteGuard<'guard, Value> +{ + inner: RwLockWriteGuard<'guard, Value>, + value_type_name: &'static str, +} + +impl<'guard, Value> WriteGuard<'guard, Value> +{ + pub fn try_map<NewValue>( + this: Self, + func: impl FnOnce(&mut Value) -> Option<&mut NewValue>, + ) -> Result<MappedWriteGuard<'guard, NewValue>, Self> + { + let value_type_name = this.value_type_name; + + // The 'inner' field cannot be moved out of ReadGuard in a normal way since + // ReadGuard implements Drop + let inner = unsafe { std::ptr::read(&raw const this.inner) }; + forget(this); + + match RwLockWriteGuard::try_map(inner, func) { + Ok(mapped_guard) => { + Ok(MappedWriteGuard { inner: mapped_guard, value_type_name }) + } + Err(unmapped_guard) => Err(Self { + inner: unmapped_guard, + value_type_name, + }), + } + } +} + +impl<Value> Deref for WriteGuard<'_, Value> +{ + type Target = Value; + + fn deref(&self) -> &Self::Target + { + &self.inner + } +} + +impl<Value> DerefMut for WriteGuard<'_, Value> +{ + fn deref_mut(&mut self) -> &mut Self::Target + { + &mut self.inner + } +} + +impl<Value> Drop for WriteGuard<'_, Value> +{ + fn drop(&mut self) + { + tracing::trace!( + "Dropped mutable lock to value of type {}", + self.value_type_name + ); + } +} + +#[derive(Debug)] +pub struct MappedWriteGuard<'guard, Value> +{ + inner: MappedRwLockWriteGuard<'guard, Value>, + value_type_name: &'static str, +} + +impl<Value> Deref for MappedWriteGuard<'_, Value> +{ + type Target = Value; + + fn deref(&self) -> &Self::Target + { + &self.inner + } +} + +impl<Value> DerefMut for MappedWriteGuard<'_, Value> +{ + fn deref_mut(&mut self) -> &mut Self::Target + { + &mut self.inner + } +} + +impl<Value> Drop for MappedWriteGuard<'_, Value> +{ + fn drop(&mut self) + { + tracing::trace!( + "Dropped mapped mutable lock to value of type {}", + self.value_type_name + ); + } +} |
