#![allow(unused)]
use std::cell::Cell;
use std::marker::PhantomData;
use crate::loom_exports::sync::Arc;
use crate::loom_exports::sync::atomic::{self, AtomicUsize, Ordering};
pub(crate) trait TearableAtomic: Sync {
type Value;
fn tearable_load(&self) -> Self::Value;
fn tearable_store(&self, value: Self::Value);
}
struct Inner<T: TearableAtomic> {
tearable: T,
sequence: AtomicUsize,
}
pub(crate) struct SyncCell<T: TearableAtomic> {
inner: Arc<Inner<T>>,
_non_sync_phantom: PhantomData<Cell<()>>,
}
impl<T: TearableAtomic> SyncCell<T> {
pub(crate) fn new(tearable: T) -> Self {
Self {
inner: Arc::new(Inner {
tearable,
sequence: AtomicUsize::new(0),
}),
_non_sync_phantom: PhantomData,
}
}
pub(crate) fn read(&self) -> T::Value {
self.inner.tearable.tearable_load()
}
pub(crate) fn write(&self, value: T::Value) {
let seq = self.inner.sequence.load(Ordering::Relaxed);
self.inner
.sequence
.store(seq.wrapping_add(1), Ordering::Relaxed);
atomic::fence(Ordering::Release);
self.inner.tearable.tearable_store(value);
self.inner
.sequence
.store(seq.wrapping_add(2), Ordering::Release);
}
pub(crate) fn reader(&self) -> SyncCellReader<T> {
SyncCellReader {
inner: self.inner.clone(),
}
}
}
pub(crate) struct SyncCellReader<T: TearableAtomic> {
inner: Arc<Inner<T>>,
}
impl<T: TearableAtomic> SyncCellReader<T> {
pub(crate) fn try_read(&self) -> Result<T::Value, SyncCellReadError> {
let seq = self.inner.sequence.load(Ordering::Acquire);
if seq & 1 != 0 {
return Err(SyncCellReadError {});
}
let value = self.inner.tearable.tearable_load();
atomic::fence(Ordering::Acquire);
let new_seq = self.inner.sequence.load(Ordering::Relaxed);
if new_seq == seq {
Ok(value)
} else {
Err(SyncCellReadError {})
}
}
pub(crate) fn read(&self) -> T::Value {
loop {
if let Ok(value) = self.try_read() {
return value;
}
}
}
}
impl<T: TearableAtomic> Clone for SyncCellReader<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct SyncCellReadError {}
#[cfg(all(test, nexosim_loom))]
mod tests {
use super::*;
use loom::lazy_static;
use loom::model::Builder;
use loom::sync::atomic::AtomicBool;
use loom::thread;
struct TestTearable<const N: usize> {
inner: [AtomicUsize; N],
}
impl<const N: usize> TestTearable<N> {
fn new(value: [usize; N]) -> Self {
let inner: Vec<_> = value.into_iter().map(|v| AtomicUsize::new(v)).collect();
Self {
inner: inner.try_into().unwrap(),
}
}
}
impl<const N: usize> TearableAtomic for TestTearable<N> {
type Value = [usize; N];
fn tearable_load(&self) -> Self::Value {
let mut value = [0usize; N];
for i in 0..N {
value[i] = self.inner[i].load(Ordering::Relaxed);
}
value
}
fn tearable_store(&self, value: Self::Value) {
for i in 0..N {
self.inner[i].store(value[i], Ordering::Relaxed);
}
}
}
#[test]
fn loom_sync_cell_race() {
const DEFAULT_PREEMPTION_BOUND: usize = 4;
const VALUE1: [usize; 3] = [1, 2, 3];
const VALUE2: [usize; 3] = [4, 5, 6];
const VALUE3: [usize; 3] = [7, 8, 9];
let mut builder = Builder::new();
if builder.preemption_bound.is_none() {
builder.preemption_bound = Some(DEFAULT_PREEMPTION_BOUND);
}
builder.check(move || {
let tearable = TestTearable::new(VALUE1);
let cell = SyncCell::new(tearable);
let reader = cell.reader();
let th = thread::spawn(move || {
if let Ok(v) = reader.try_read() {
assert!(v == VALUE1 || v == VALUE2 || v == VALUE3, "v = {:?}", v);
}
});
cell.write(VALUE2);
cell.write(VALUE3);
th.join().unwrap();
});
}
#[test]
fn loom_sync_cell_synchronized() {
const DEFAULT_PREEMPTION_BOUND: usize = 4;
const VALUE1: [usize; 3] = [1, 2, 3];
const VALUE2: [usize; 3] = [4, 5, 6];
const VALUE3: [usize; 3] = [7, 8, 9];
let mut builder = Builder::new();
if builder.preemption_bound.is_none() {
builder.preemption_bound = Some(DEFAULT_PREEMPTION_BOUND);
}
builder.check(move || {
lazy_static! {
static ref NEW_VALUE_FLAG: AtomicBool = AtomicBool::new(false);
};
let tearable = TestTearable::new(VALUE1);
let cell = SyncCell::new(tearable);
let reader = cell.reader();
let th = thread::spawn(move || {
if NEW_VALUE_FLAG.load(Ordering::Acquire) {
let v = reader
.try_read()
.expect("read should always succeed when synchronized");
assert!(v == VALUE2 || v == VALUE3, "v = {:?}", v);
NEW_VALUE_FLAG.store(false, Ordering::Release);
if NEW_VALUE_FLAG.load(Ordering::Acquire) {
let v = reader
.try_read()
.expect("read should always succeed when synchronized");
assert_eq!(v, VALUE3);
}
}
});
cell.write(VALUE2);
NEW_VALUE_FLAG.store(true, Ordering::Release);
if !NEW_VALUE_FLAG.load(Ordering::Acquire) {
cell.write(VALUE3);
NEW_VALUE_FLAG.store(true, Ordering::Release);
}
th.join().unwrap();
});
}
}