#![cfg_attr(not(test), no_std)]
extern crate alloc;
use alloc::{boxed::Box, sync::Arc};
use atomicbox_nostd::AtomicOptionBox;
use core::{
fmt,
ops::{Deref, DerefMut},
sync::atomic::{self, Ordering},
};
pub struct Wrrm<T> {
inner: AtomicOptionBox<Arc<T>>,
}
impl<T> Wrrm<T> {
pub fn new(value: T) -> Self {
Wrrm {
inner: AtomicOptionBox::new(Some(Box::new(Arc::new(value)))),
}
}
pub fn access(&self) -> Access<T> {
let inner = loop {
if let Some(value) = self.inner.take(Ordering::AcqRel) {
let _updated = self.inner.swap(Some(value.clone()), Ordering::AcqRel);
debug_assert!(_updated.is_none());
break *value;
}
atomic::spin_loop_hint();
};
Access {
parent: self,
inner,
}
}
pub fn modify_with(&self, modification: impl FnMut(&mut T))
where
T: Clone,
{
Access::modify_with(self.access(), modification)
}
}
impl<T: Default> Default for Wrrm<T> {
fn default() -> Self {
Wrrm::new(Default::default())
}
}
impl<T> From<T> for Wrrm<T> {
fn from(value: T) -> Self {
Wrrm::new(value)
}
}
impl<T> fmt::Debug for Wrrm<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.access(), f)
}
}
pub struct Access<'a, T> {
parent: &'a Wrrm<T>,
inner: Arc<T>,
}
impl<'a, T: Clone> Access<'a, T> {
pub fn modify_with(mut me: Self, mut modification: impl FnMut(&mut T)) {
loop {
let mut modify = Modify::from(me);
modification(&mut *modify);
match Modify::apply(modify) {
Ok(()) => return,
Err(acc) => me = acc,
}
}
}
}
impl<'a, T> Deref for Access<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&*self.inner
}
}
impl<'a, T> fmt::Debug for Access<'a, T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<T as fmt::Debug>::fmt(&*self.inner, f)
}
}
pub struct Modify<'a, T> {
parent: &'a Wrrm<T>,
expected: usize,
new_value: T,
}
impl<'a, T: Clone> From<Access<'a, T>> for Modify<'a, T> {
fn from(access: Access<'a, T>) -> Self {
Modify {
parent: access.parent,
expected: Arc::as_ptr(&access.inner) as usize,
new_value: (*access.inner).clone(),
}
}
}
impl<'a, T> Modify<'a, T> {
pub fn apply(me: Self) -> Result<(), Access<'a, T>> {
loop {
if let Some(in_ptr) = me.parent.inner.take(Ordering::AcqRel) {
if Arc::as_ptr(&*in_ptr) as usize == me.expected {
let new_value = Box::new(Arc::new(me.new_value));
let _updated = me.parent.inner.swap(Some(new_value), Ordering::AcqRel);
debug_assert!(_updated.is_none());
return Ok(());
} else {
let _updated = me.parent.inner.swap(Some(in_ptr.clone()), Ordering::AcqRel);
debug_assert!(_updated.is_none());
return Err(Access {
parent: me.parent,
inner: *in_ptr,
});
}
}
atomic::spin_loop_hint();
}
}
}
impl<'a, T> Deref for Modify<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.new_value
}
}
impl<'a, T> DerefMut for Modify<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.new_value
}
}
impl<'a, T> fmt::Debug for Modify<'a, T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<T as fmt::Debug>::fmt(&self.new_value, f)
}
}
#[cfg(test)]
mod tests {
use super::Wrrm;
use std::{
sync::{Arc, Barrier},
thread,
time::Duration,
};
#[test]
fn basic() {
let val = Wrrm::from(5);
let first_access = val.access();
assert_eq!(*first_access, 5);
val.modify_with(|v| *v = 6);
assert_eq!(*val.access(), 6);
assert_eq!(*first_access, 5);
}
#[test]
fn threads() {
let val = Arc::new(Wrrm::from(5));
let barrier = Arc::new(Barrier::new(256));
for _ in 0..256 {
let val = val.clone();
let barrier = barrier.clone();
thread::spawn(move || {
barrier.wait();
val.modify_with(|v| *v += 1);
});
}
loop {
thread::sleep(Duration::from_millis(200));
if *val.access() == 261 {
break;
}
}
thread::sleep(Duration::from_secs(3));
assert_eq!(*val.access(), 261);
}
}