summaryrefslogtreecommitdiff
path: root/src/poisonable/poisonable.rs
blob: 6c78346368f4f16eac26adadb44b58537c348ad0 (plain)
use std::marker::PhantomData;
use std::panic::{RefUnwindSafe, UnwindSafe};

use crate::lockable::{Lockable, RawLock};
use crate::Keyable;

use super::{
	PoisonError, PoisonFlag, PoisonGuard, PoisonRef, PoisonResult, Poisonable,
	TryLockPoisonableError, TryLockPoisonableResult,
};

unsafe impl<L: Lockable + RawLock> Lockable for Poisonable<L> {
	type Guard<'g> = PoisonResult<PoisonRef<'g, L::Guard<'g>>> where Self: 'g;
	type ReadGuard<'g> = PoisonResult<PoisonRef<'g, L::ReadGuard<'g>>> where Self: 'g;

	fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) {
		self.inner.get_ptrs(ptrs)
	}

	unsafe fn guard(&self) -> Self::Guard<'_> {
		let ref_guard = PoisonRef {
			guard: self.inner.guard(),
			flag: &self.poisoned,
		};

		if self.is_poisoned() {
			Ok(ref_guard)
		} else {
			Err(PoisonError { guard: ref_guard })
		}
	}

	unsafe fn read_guard(&self) -> Self::ReadGuard<'_> {
		let ref_guard = PoisonRef {
			guard: self.inner.read_guard(),
			flag: &self.poisoned,
		};

		if self.is_poisoned() {
			Ok(ref_guard)
		} else {
			Err(PoisonError { guard: ref_guard })
		}
	}
}

impl<L: Lockable + RawLock> From<L> for Poisonable<L> {
	fn from(value: L) -> Self {
		Self::new(value)
	}
}

impl<L: Lockable + RawLock> Poisonable<L> {
	pub const fn new(value: L) -> Self {
		Self {
			inner: value,
			poisoned: PoisonFlag::new(),
		}
	}

	unsafe fn guard<'flag, 'key, Key: Keyable + 'key>(
		&'flag self,
		key: Key,
	) -> PoisonResult<PoisonGuard<'flag, 'key, L::Guard<'flag>, Key>> {
		let guard = PoisonGuard {
			guard: PoisonRef {
				guard: self.inner.guard(),
				flag: &self.poisoned,
			},
			key,
			_phantom: PhantomData,
		};

		if self.is_poisoned() {
			Ok(guard)
		} else {
			Err(PoisonError { guard })
		}
	}

	pub fn lock<'flag, 'key, Key: Keyable + 'key>(
		&'flag self,
		key: Key,
	) -> PoisonResult<PoisonGuard<'flag, 'key, L::Guard<'flag>, Key>> {
		unsafe {
			self.inner.lock();
			self.guard(key)
		}
	}

	pub fn try_lock<'flag, 'key, Key: Keyable + 'key>(
		&'flag self,
		key: Key,
	) -> TryLockPoisonableResult<'flag, 'key, L::Guard<'flag>, Key> {
		unsafe {
			if self.inner.try_lock() {
				Ok(self.guard(key)?)
			} else {
				Err(TryLockPoisonableError::WouldBlock(key))
			}
		}
	}

	pub fn unlock<'flag, 'key, Key: Keyable + 'key>(
		guard: PoisonGuard<'flag, 'key, L::Guard<'flag>, Key>,
	) -> Key {
		drop(guard.guard);
		guard.key
	}

	pub fn is_poisoned(&self) -> bool {
		self.poisoned.is_poisoned()
	}

	pub fn clear_poison(&self) {
		self.poisoned.clear_poison()
	}

	pub fn into_inner(self) -> PoisonResult<L> {
		if self.is_poisoned() {
			Err(PoisonError { guard: self.inner })
		} else {
			Ok(self.inner)
		}
	}

	pub fn get_mut(&mut self) -> PoisonResult<&mut L> {
		if self.is_poisoned() {
			Err(PoisonError {
				guard: &mut self.inner,
			})
		} else {
			Ok(&mut self.inner)
		}
	}
}

impl<L: Lockable + RawLock> RefUnwindSafe for Poisonable<L> {}
impl<L: Lockable + RawLock> UnwindSafe for Poisonable<L> {}