use crate::clone::PClone;
use crate::alloc::MemPool;
use crate::stm::{Journal, Logger};
use crate::*;
use std::cell::UnsafeCell;
use std::cmp::Ordering;
use std::marker::PhantomData;
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::{fmt, mem, ptr};
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))]
use crate::cell::TCell;
/// A persistent mutable memory location with recoverability
///
/// This is one of the safe ways to provide interior mutability for pointer
/// wrappers. It takes a log, if it was not already taken, before updating the
/// value.
///
/// Using [`get()`](#method.get) function, you can obtain a copy of data. To
/// update data, you can use [`set()`](#method.set) which writes a log to the
/// given journal before mutation.
///
/// It does not implement [`Sync`], so it is not possible to share `PCell`
/// between threads. To provide thread-safe interior mutability, use
/// [`PMutex`].
///
/// [`PCell`] is a compact version of `PCell` tha can be find in the pool
/// module.
///
/// [`Sync`]: std::marker::Sync
/// [`PMutex`]: ../sync/mutex/struct.PMutex.html
/// [`PCell`]: ../alloc/default/type.PCell.html
///
pub struct PCell<T: PSafe + ?Sized, A: MemPool> {
heap: PhantomData<A>,
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))]
temp: TCell<Option<*mut T>, A>,
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))]
value: UnsafeCell<T>,
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))]
value: UnsafeCell<(u8, T)>,
}
unsafe impl<T: PSafe + Send + ?Sized, A: MemPool> Send for PCell<T, A> {}
impl<T: PSafe + ?Sized, A: MemPool> RefUnwindSafe for PCell<T, A> {}
impl<T: PSafe + ?Sized, A: MemPool> UnwindSafe for PCell<T, A> {}
unsafe impl<T: PSafe + ?Sized, A: MemPool> TxInSafe for PCell<T, A> {}
unsafe impl<T: PSafe + ?Sized, A: MemPool> PSafe for PCell<T, A> {}
impl<T: ?Sized, A: MemPool> !TxOutSafe for PCell<T, A> {}
impl<T: ?Sized, A: MemPool> !Sync for PCell<T, A> {}
impl<T: ?Sized, A: MemPool> !PSend for PCell<T, A> {}
impl<T: PSafe + Default, A: MemPool> Default for PCell<T, A> {
fn default() -> Self {
PCell {
heap: PhantomData,
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))]
temp: TCell::new_invalid(None),
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))]
value: UnsafeCell::new(T::default()),
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))]
value: UnsafeCell::new((0, T::default())),
}
}
}
impl<T: PSafe + PartialEq + Copy, A: MemPool> PartialEq for PCell<T, A> {
#[inline]
fn eq(&self, other: &PCell<T, A>) -> bool {
self.get() == other.get()
}
}
impl<T: PSafe + Eq + Copy, A: MemPool> Eq for PCell<T, A> {}
impl<T: PSafe + PartialOrd + Copy, A: MemPool> PartialOrd for PCell<T, A> {
#[inline]
fn partial_cmp(&self, other: &PCell<T, A>) -> Option<Ordering> {
self.get().partial_cmp(&other.get())
}
#[inline]
fn lt(&self, other: &PCell<T, A>) -> bool {
self.get() < other.get()
}
#[inline]
fn le(&self, other: &PCell<T, A>) -> bool {
self.get() <= other.get()
}
#[inline]
fn gt(&self, other: &PCell<T, A>) -> bool {
self.get() > other.get()
}
#[inline]
fn ge(&self, other: &PCell<T, A>) -> bool {
self.get() >= other.get()
}
}
impl<T: PSafe + Ord + Copy, A: MemPool> Ord for PCell<T, A> {
#[inline]
fn cmp(&self, other: &PCell<T, A>) -> Ordering {
self.get().cmp(&other.get())
}
}
impl<T: PSafe, A: MemPool> PCell<T, A> {
/// Creates a new `PCell` containing the given value.
///
/// # Examples
///
/// ```
/// use corundum::alloc::heap::*;
///
/// Heap::transaction(|j| {
/// let c = PCell::new(5);
/// }).unwrap();
/// ```
#[inline]
pub const fn new(value: T) -> PCell<T, A> {
PCell {
heap: PhantomData,
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))]
temp: TCell::new_invalid(None),
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))]
value: UnsafeCell::new(value),
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))]
value: UnsafeCell::new((0, value)),
}
}
/// Sets the contained value.
///
/// # Examples
///
/// ```
/// use corundum::alloc::heap::*;
/// use corundum::boxed::Pbox;
/// use corundum::cell::PCell;
///
/// Heap::transaction(|j| {
/// let c = Pbox::new(PCell::new(5), j);
/// c.set(10, j);
/// }).unwrap();
/// ```
///
/// # Errors
///
/// If `PCell` is not in the persistent memory, it will raise an 'invalid
/// address' error. To make sure that the `PCell` is in the persistent
/// memory, use dynamic allocation using [`Pbox`] as shown above.
///
/// [`Pbox`]: ../../boxed/struct.Pbox.html
#[inline]
#[track_caller]
pub fn set(&self, val: T, journal: &Journal<A>) {
let old = self.replace(val, journal);
drop(old);
}
/// Swaps the values of two Cells.
///
/// Difference with `std::mem::swap` is that this function doesn't require
/// `&mut` reference. It takes a log of both sides, if required, and then
/// swaps the values.
///
/// # Examples
///
/// ```
/// use corundum::default::*;
///
/// let _pool = Allocator::open_no_root("foo.pool", O_CF).unwrap();
///
/// Allocator::transaction(|j| {
/// let c1 = Pbox::new(PCell::new(5i32), j);
/// let c2 = Pbox::new(PCell::new(10i32), j);
/// c1.swap(&c2, j);
/// assert_eq!(10, c1.get());
/// assert_eq!(5, c2.get());
/// }).unwrap();
/// ```
#[inline]
pub fn swap(&self, other: &Self, journal: &Journal<A>) {
let this = unsafe { self.as_mut() };
let that = unsafe { other.as_mut() };
if ptr::eq(this, that) {
return;
}
self.create_log(journal);
other.create_log(journal);
// SAFETY: This can be risky if called from separate threads, but `PCell`
// is `!Sync` so this won't happen. This also won't invalidate any
// pointers since `PCell` makes sure nothing else will be pointing into
// either of these `PCell`s.
unsafe {
ptr::swap(this, that);
}
}
#[inline]
#[track_caller]
/// Replaces the contained value, and returns it.
///
/// # Examples
///
/// ```
/// use corundum::alloc::heap::*;
/// use corundum::boxed::Pbox;
/// use corundum::cell::PCell;
///
/// Heap::transaction(|j| {
/// let cell = Pbox::new(PCell::new(5), j);
/// assert_eq!(cell.get(), 5);
/// assert_eq!(cell.replace(10, j), 5);
/// assert_eq!(cell.get(), 10);
/// }).unwrap();
/// ```
pub fn replace(&self, val: T, journal: &Journal<A>) -> T {
// SAFETY: This can cause data races if called from a separate thread,
// but `PCell` is `!Sync` so this won't happen.
self.create_log(journal);
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))] {
if let Some(tmp) = *self.temp {
mem::replace(unsafe { &mut *tmp }, val)
} else {
mem::replace(unsafe { &mut *self.value.get() }, val)
}
}
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))]
mem::replace(unsafe { &mut (*self.value.get()).1 }, val)
}
/// Unwraps the value.
///
/// # Examples
///
/// ```
/// use corundum::alloc::heap::*;
///
/// Heap::transaction(|j| {
/// let c = PCell::new(5);
/// let five = c.into_inner();
///
/// assert_eq!(five, 5);
/// }).unwrap();
/// ```
pub fn into_inner(self) -> T {
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))] {
self.value.into_inner()
}
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))] {
self.value.into_inner().1
}
}
#[inline]
fn self_mut(&self) -> &mut Self {
unsafe { &mut *(self as *const Self as *mut Self) }
}
}
impl<T: PSafe, A: MemPool> PCell<T, A> {
/// Returns a copy of the contained value.
///
/// # Examples
///
/// ```
/// use corundum::alloc::heap::*;
///
/// Heap::transaction(|j| {
/// let c = PCell::new(5);
/// let five = c.get();
///
/// assert_eq!(five, 5);
/// }).unwrap();
/// ```
#[inline]
pub fn get(&self) -> T where T: Copy {
// SAFETY: This can cause data races if called from a separate thread,
// but `PCell` is `!Sync` so this won't happen.
unsafe {
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))] {
if let Some(tmp) = *self.temp {
*tmp
} else {
*self.value.get()
}
}
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))] {
(*self.value.get()).1
}
}
}
/// Updates the contained value using a function and returns the new value.
///
/// # Examples
///
/// ```
/// #![feature(cell_update)]
///
/// use corundum::alloc::heap::*;
/// use corundum::boxed::Pbox;
///
/// Heap::transaction(|j| {
/// let c = Pbox::new(PCell::new(5), j);
/// let new = c.update(|x| x + 1, j);
///
/// assert_eq!(new, 6);
/// assert_eq!(c.get(), 6);
/// }).unwrap();
/// ```
#[inline]
pub fn update<F>(&self, f: F, journal: &Journal<A>) -> T
where
F: FnOnce(T) -> T,
T: Copy
{
let old = self.get();
let new = f(old);
self.set(new, journal);
new
}
}
impl<T: PSafe + ?Sized, A: MemPool> PCell<T, A> {
#[inline]
#[track_caller]
pub(crate) fn create_log(&self, journal: &Journal<A>) {
unsafe {
let inner = &mut *self.value.get();
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))] {
if self.temp.is_none() {
if let Some(p) = journal.draft(inner) {
self.temp.as_mut().replace(p);
}
}
}
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))] {
use crate::ptr::Ptr;
use crate::stm::Notifier;
if inner.0 == 0 {
assert!(A::valid(inner), "The object is not in the pool's valid range");
inner.1.create_log(journal, Notifier::NonAtomic(Ptr::from_ref(&inner.0)));
}
}
}
}
/// Returns a mutable reference to the underlying data.
///
/// This call borrows `PCell` mutably (at compile-time) which guarantees
/// that we possess the only reference.
///
/// # Examples
///
/// ```
/// use corundum::alloc::heap::*;
/// use corundum::boxed::Pbox;
/// use corundum::cell::PCell;
///
/// Heap::transaction(|j| {
/// let mut c = Pbox::new(PCell::new(5), j);
/// let mut n = c.get_mut(j);
/// *n += 1;
///
/// assert_eq!(c.get(), 6);
/// }).unwrap();
/// ```
#[inline]
pub fn get_mut(&mut self, journal: &Journal<A>) -> &mut T {
// SAFETY: This can cause data races if called from a separate thread,
// but `PCell` is `!Sync` so this won't happen, and `&mut` guarantees
// unique access.
self.create_log(journal);
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))] unsafe {
if let Some(tmp) = *self.temp {
&mut *tmp
} else {
&mut *self.value.get()
}
}
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))] {
unsafe { &mut (*self.value.get()).1 }
}
}
/// Returns a mutable reference to the underlying data without taking a log
///
/// # Safety
///
/// This function violates borrow rules as it allows multiple mutable
/// references.
///
/// # Examples
///
/// ```
/// use corundum::default::*;
/// use corundum::cell::PCell;
///
/// type P = Allocator;
///
/// let root = P::open::<PCell<i32,P>>("foo.pool", O_CF).unwrap();
///
/// unsafe {
/// let mut data = root.as_mut();
/// *data = 20;
/// }
///
/// ```
#[inline]
pub unsafe fn as_mut(&self) -> &mut T {
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))] {
if let Some(tmp) = *self.temp {
&mut *tmp
} else {
&mut *self.value.get()
}
}
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))] {
&mut (*self.value.get()).1
}
}
}
impl<T: PSafe + Default, A: MemPool> PCell<T, A> {
/// Takes the value of the cell, leaving `Default::default()` in its place.
///
/// # Examples
///
/// ```
/// use corundum::alloc::heap::*;
/// use corundum::boxed::Pbox;
///
/// Heap::transaction(|j| {
/// let c = Pbox::new(PCell::new(5), j);
/// let five = c.take(j);
///
/// assert_eq!(five, 5);
/// assert_eq!(c.get(), 0);
/// }).unwrap();
/// ```
#[track_caller]
pub fn take(&self, journal: &Journal<A>) -> T {
self.replace(Default::default(), journal)
}
}
impl<T: fmt::Debug + PSafe + Copy, A: MemPool> fmt::Debug for PCell<T, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))] {
unsafe { (*self.value.get()).fmt(f) }
}
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))] {
unsafe { (*self.value.get()).1.fmt(f) }
}
}
}
impl<T: PSafe + Logger<A> + Copy, A: MemPool> PClone<A> for PCell<T, A> {
#[inline]
fn pclone(&self, _j: &Journal<A>) -> PCell<T, A> {
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))] {
unsafe { PCell::new(*self.value.get()) }
}
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))] {
unsafe { PCell::new((*self.value.get()).1) }
}
}
}
impl<T: PSafe + Logger<A> + Copy, A: MemPool> Clone for PCell<T, A> {
#[inline]
fn clone(&self) -> PCell<T, A> {
#[cfg(any(feature = "use_pspd", feature = "use_vspd"))] {
unsafe { PCell::new((*self.value.get()).clone()) }
}
#[cfg(not(any(feature = "use_pspd", feature = "use_vspd")))] {
unsafe { PCell::new((*self.value.get()).1.clone()) }
}
}
}