use core::{
cell::UnsafeCell,
cmp,
fmt::{self, Debug, Display},
sync::atomic::Ordering,
};
use crate::{
borrow::{is_borrowed, is_writing, new_lock, AtomicBorrow, AtomicBorrowMut, Lock},
refs::{Ref, RefMut},
};
pub struct AtomicCell<T: ?Sized> {
lock: Lock,
value: UnsafeCell<T>,
}
unsafe impl<T> Send for AtomicCell<T> where T: Send {}
unsafe impl<T> Sync for AtomicCell<T> where T: Send + Sync {}
impl<T> AtomicCell<T> {
#[inline]
pub const fn new(value: T) -> Self {
AtomicCell {
value: UnsafeCell::new(value),
lock: new_lock(),
}
}
#[inline(always)]
pub fn into_inner(self) -> T {
self.value.into_inner()
}
#[inline(always)]
pub fn replace(&self, t: T) -> T {
core::mem::replace(&mut *self.borrow_mut(), t)
}
#[inline]
pub fn replace_with<F: FnOnce(&mut T) -> T>(&self, f: F) -> T {
match self.try_borrow_mut() {
None => failed_to_borrow_mut(),
Some(mut borrow) => {
let t = f(&mut *borrow);
core::mem::replace(&mut *borrow, t)
}
}
}
#[inline]
pub fn swap(&self, other: &Self) {
match self.try_borrow_mut() {
None => failed_to_borrow_mut(),
Some(mut borrow) => match other.try_borrow_mut() {
None => failed_to_borrow_mut(),
Some(mut other_borrow) => {
core::mem::swap(&mut *borrow, &mut *other_borrow);
}
},
}
}
}
impl<T> AtomicCell<T>
where
T: ?Sized,
{
#[inline]
pub fn try_borrow(&self) -> Option<Ref<'_, T>> {
match AtomicBorrow::try_new(&self.lock) {
None => None,
Some(borrow) => {
let r = unsafe {
&*self.value.get()
};
Some(Ref::with_borrow(r, borrow))
}
}
}
#[inline(always)]
#[track_caller]
pub fn borrow(&self) -> Ref<'_, T> {
match self.try_borrow() {
None => failed_to_borrow(),
Some(r) => r,
}
}
#[inline]
pub fn try_borrow_mut(&self) -> Option<RefMut<'_, T>> {
match AtomicBorrowMut::try_new(&self.lock) {
None => None,
Some(borrow) => {
let r = unsafe {
&mut *self.value.get()
};
Some(RefMut::with_borrow(r, borrow))
}
}
}
#[inline(always)]
#[track_caller]
pub fn borrow_mut(&self) -> RefMut<'_, T> {
match self.try_borrow_mut() {
None => failed_to_borrow_mut(),
Some(r) => r,
}
}
#[inline]
pub const fn as_ptr(&self) -> *mut T {
self.value.get()
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}
#[inline]
pub fn undo_leak(&mut self) -> &mut T {
*self.lock.get_mut() = 0;
self.value.get_mut()
}
#[inline]
pub unsafe fn try_borrow_unguarded(&self) -> Option<&T> {
if is_writing(self.lock.load(Ordering::Relaxed)) {
None
} else {
Some(&*self.value.get())
}
}
#[inline]
pub unsafe fn try_borrow_unguarded_mut(&self) -> Option<&mut T> {
let val = self.lock.load(Ordering::Relaxed);
if is_borrowed(val) {
None
} else {
Some(&mut *self.value.get())
}
}
}
impl<T> AtomicCell<T>
where
T: Default,
{
#[inline]
pub fn take(&self) -> T {
let mut r = self.borrow_mut();
core::mem::take(&mut *r)
}
}
impl<T> Clone for AtomicCell<T>
where
T: Clone,
{
#[inline]
fn clone(&self) -> Self {
let r = self.borrow();
let t = Clone::clone(&*r);
AtomicCell::new(t)
}
#[inline]
fn clone_from(&mut self, other: &Self) {
self.get_mut().clone_from(&other.borrow());
}
}
impl<T> Debug for AtomicCell<T>
where
T: Debug,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&*self.borrow(), f)
}
}
impl<T> Display for AtomicCell<T>
where
T: Display,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&*self.borrow(), f)
}
}
impl<T> From<T> for AtomicCell<T> {
#[inline]
fn from(t: T) -> Self {
AtomicCell::new(t)
}
}
impl<T, U> PartialEq<AtomicCell<U>> for AtomicCell<T>
where
T: PartialEq<U>,
{
fn eq(&self, other: &AtomicCell<U>) -> bool {
self.borrow().eq(&other.borrow())
}
}
impl<T> Eq for AtomicCell<T> where T: Eq {}
impl<T, U> PartialOrd<AtomicCell<U>> for AtomicCell<T>
where
T: PartialOrd<U>,
{
fn partial_cmp(&self, other: &AtomicCell<U>) -> Option<cmp::Ordering> {
self.borrow().partial_cmp(&other.borrow())
}
}
impl<T> Ord for AtomicCell<T>
where
T: Ord,
{
fn cmp(&self, other: &AtomicCell<T>) -> cmp::Ordering {
self.borrow().cmp(&other.borrow())
}
}
#[inline(never)]
#[track_caller]
#[cold]
const fn failed_to_borrow() -> ! {
panic!("Failed to borrow AtomicCell immutably");
}
#[inline(never)]
#[track_caller]
#[cold]
const fn failed_to_borrow_mut() -> ! {
panic!("Failed to borrow AtomicCell mutably");
}