extern crate std;
use core::cell::UnsafeCell;
use core::fmt::Debug;
use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError};
type WriteResult<T> = Result<T, PoisonError<T>>;
type TryLockResult<T> = Result<T, TryLockError<T>>;
pub struct TempRef<'a, T: Send, F: FnMut(&mut T) + Sync> {
re: RwLockWriteGuard<'a, T>,
reset: &'a mut F,
}
impl<'a, T: Send, F: FnMut(&mut T) + Sync> TempRef<'a, T, F> {
fn new(re: RwLockWriteGuard<'a, T>, reset: &'a mut F) -> Self {
TempRef { re, reset }
}
fn write(temp: &'a Temp<T, F>) -> WriteResult<Self> {
let reset = unsafe { &mut *temp.reset.get() };
match temp.value.write() {
Ok(guard) => Ok(TempRef::new(guard, reset)),
Err(err) => Err(PoisonError::new(TempRef::new(err.into_inner(), reset))),
}
}
fn try_write(temp: &'a Temp<T, F>) -> TryLockResult<Self> {
let reset = unsafe { &mut *temp.reset.get() };
match temp.value.try_write() {
Ok(guard) => Ok(TempRef::new(guard, reset)),
Err(TryLockError::Poisoned(err)) => Err(TryLockError::Poisoned(PoisonError::new(
TempRef::new(err.into_inner(), reset),
))),
Err(TryLockError::WouldBlock) => Err(TryLockError::WouldBlock),
}
}
pub fn reset(&mut self) {
(self.reset)(&mut self.re);
}
}
impl<'a, T: Send, F: FnMut(&mut T) + Sync> core::ops::Deref for TempRef<'a, T, F> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.re
}
}
impl<'a, T: Send, F: FnMut(&mut T) + Sync> core::ops::DerefMut for TempRef<'a, T, F> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.re
}
}
impl<'a, T: Send, F: FnMut(&mut T) + Sync> Drop for TempRef<'a, T, F> {
fn drop(&mut self) {
(self.reset)(&mut self.re);
}
}
impl<'a, T: Send + Debug, F: FnMut(&mut T) + Sync> Debug for TempRef<'a, T, F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TempRef").field("value", &self.re).finish()
}
}
pub struct Temp<T: Send, F: FnMut(&mut T) + Sync> {
value: RwLock<T>,
reset: UnsafeCell<F>,
}
impl<T: Send, F: FnMut(&mut T) + Sync> Temp<T, F> {
pub const fn new(value: T, reset: F) -> Self {
Temp {
value: RwLock::new(value),
reset: UnsafeCell::new(reset),
}
}
pub fn new_with(mut value: T, mut reset: F) -> Self {
reset(&mut value);
Temp {
value: RwLock::new(value),
reset: UnsafeCell::new(reset),
}
}
pub fn read<'a>(
&'a self,
) -> Result<RwLockReadGuard<'a, T>, PoisonError<RwLockReadGuard<'a, T>>> {
self.value.read()
}
pub fn write<'a>(&'a self) -> WriteResult<TempRef<'a, T, F>> {
TempRef::write(self)
}
pub fn try_read<'a>(
&'a self,
) -> Result<RwLockReadGuard<'a, T>, TryLockError<RwLockReadGuard<'a, T>>> {
self.value.try_read()
}
pub fn try_write<'a>(&'a self) -> TryLockResult<TempRef<'a, T, F>> {
TempRef::try_write(self)
}
pub fn into_inner(self) -> WriteResult<T> {
self.value.into_inner()
}
pub fn clear_poison(&self) {
self.value.clear_poison();
}
pub fn is_poisoned(&self) -> bool {
self.value.is_poisoned()
}
pub fn reset(&self) -> WriteResult<()> {
if let Ok(mut guard) = self.value.write() {
self.get_reset()(&mut guard);
Ok(())
} else {
Err(PoisonError::new(()))
}
}
pub fn try_reset(&self) -> TryLockResult<()> {
match self.value.try_write() {
Ok(mut guard) => {
self.get_reset()(&mut guard);
Ok(())
},
Err(TryLockError::Poisoned(_)) => Err(TryLockError::Poisoned(PoisonError::new(()))),
Err(TryLockError::WouldBlock) => Err(TryLockError::WouldBlock),
}
}
#[allow(clippy::mut_from_ref)]
fn get_reset(&self) -> &mut F {
unsafe { &mut *self.reset.get() }
}
}
impl<T: Default + Send, F: FnMut(&mut T) + Sync> Temp<T, F> {
pub fn new_default(reset: F) -> Self {
Temp {
value: RwLock::new(T::default()),
reset: UnsafeCell::new(reset),
}
}
pub fn new_default_with(mut reset: F) -> Self {
let mut default = T::default();
reset(&mut default);
Temp {
value: RwLock::new(default),
reset: UnsafeCell::new(reset),
}
}
}
unsafe impl<T: Send, F: FnMut(&mut T) + Sync> Send for Temp<T, F> {}
unsafe impl<T: Send, F: FnMut(&mut T) + Sync> Sync for Temp<T, F> {}
impl<T: Debug + Send, F: FnMut(&mut T) + Sync> Debug for Temp<T, F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Temp").field("value", &self.value).finish()
}
}