Skip to content

Commit

Permalink
threadlocal test (#5)
Browse files Browse the repository at this point in the history
ArcShiftCell-improvement and tests
  • Loading branch information
avl authored Aug 18, 2024
1 parent 6617c2a commit 286a82c
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 2 deletions.
45 changes: 43 additions & 2 deletions arcshift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ use std::alloc::Layout;
#[allow(unused)]
use std::backtrace::Backtrace;
use std::cell::{Cell, UnsafeCell};
use std::fmt::Formatter;
use std::fmt::{Debug, Display, Formatter};
use std::marker::PhantomData;
use std::mem;
use std::mem::{ManuallyDrop, MaybeUninit};
Expand Down Expand Up @@ -384,7 +384,30 @@ impl<T: 'static> Clone for ArcShiftCell<T> {
}
}
}

/// Error type representing the case that an operation was attempted from within
/// a 'get'-function closure.
pub struct RecursionDetected;

impl Debug for RecursionDetected {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "RecursionDetected")
}
}

impl Display for RecursionDetected {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "RecursionDetected")
}
}

impl std::error::Error for RecursionDetected {}

impl<T: 'static> ArcShiftCell<T> {
/// Create a new ArcShiftCell with the given value.
pub fn new(value: T) -> ArcShiftCell<T> {
ArcShiftCell::from_arcshift(ArcShift::new(value))
}
/// Creates an ArcShiftCell from an ArcShift-instance.
/// The payload is not cloned, the two pointers keep pointing to the same object.
pub fn from_arcshift(input: ArcShift<T>) -> ArcShiftCell<T> {
Expand All @@ -398,8 +421,9 @@ impl<T: 'static> ArcShiftCell<T> {
/// This method is very fast, basically the speed of a regular reference, unless
/// the value has been modified by calling one of the update-methods.
///
/// This method will drop older values which are no longer needed
/// This method will do a reload (drop older values which are no longer needed).
/// This method is reentrant - you are allowed to call it from within the closure 'f'.
/// However, only the outermost invocation will cause a reload.
pub fn get(&self, f: impl FnOnce(&T)) {
self.recursion.set(self.recursion.get() + 1);
let val = if self.recursion.get() == 1 {
Expand All @@ -415,6 +439,23 @@ impl<T: 'static> ArcShiftCell<T> {
self.recursion.set(self.recursion.get() - 1);
}

/// Assign the given ArcShift to this instance.
/// This does not copy the value T, it replaces the ArcShift instance of Self
/// with a clone of 'other'. It does not clone T, only the ArcShift holding it.
///
/// This returns Err if recursion is detected, and has no effect in this case.
/// Recursion occurs if 'assign' is called from within the closure supplied to
/// the 'ArcShiftCell::get'-function.
pub fn assign(&self, other: &ArcShift<T>) -> Result<(), RecursionDetected> {
if self.recursion.get() == 0 {
// SAFETY:
// Getting the inner value is safe, no other thread can be accessing it now
*unsafe { &mut *self.inner.get() } = other.clone();
Ok(())
} else {
Err(RecursionDetected)
}
}
/// Reload this ArcShiftCell-instance.
/// This allows dropping heap blocks kept alive by this instance of
/// ArcShiftCell to be dropped.
Expand Down
43 changes: 43 additions & 0 deletions arcshift/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,29 @@ fn simple_unsized_str() {
assert_eq!(shift.get(), "hello");
})
}
use std::cell::{Cell, RefCell};

thread_local! {
pub static THREADLOCAL_FOO: ArcShiftCell<String> = ArcShiftCell::new(String::new());
}

#[cfg(not(any(loom, feature = "shuttle")))]
//This test doesn't work in shuttle or loom, since the lazy drop of the threadlocal ends up happening outside of the shuttle model
#[test]
fn simple_threadlocal_cell() {
model(|| {
let shift = ArcShift::new("hello".to_string());
THREADLOCAL_FOO.with(|local| {
local.assign(&shift).unwrap();
});
THREADLOCAL_FOO.with(|local| {
local.get(|value| {
assert_eq!(value, "hello");
});
});
debug_println!("Drop");
})
}

#[test]
fn simple_cell() {
Expand Down Expand Up @@ -147,6 +170,7 @@ fn simple_cell() {
owner.validate();
});
}

#[test]
fn simple_cell_recursion() {
model(|| {
Expand All @@ -156,6 +180,7 @@ fn simple_cell_recursion() {
let cell = ArcShiftCell::from_arcshift(root.clone());
cell.get(|val| {
assert_eq!(val.str(), "root");
assert!(cell.assign(&ArcShift::new(owner.create("dummy"))).is_err());
cell.get(|val| {
assert_eq!(val.str(), "root");
root.update(owner.create("B"));
Expand All @@ -170,7 +195,25 @@ fn simple_cell_recursion() {
owner.validate();
});
}
#[test]
fn simple_cell_assign() {
model(|| {
let owner = SpyOwner2::new();
{
let cell = ArcShiftCell::new(owner.create("original"));
let new_value = ArcShift::new(owner.create("new"));

cell.get(|val| {
assert_eq!(val.str(), "original");
assert!(cell.assign(&ArcShift::new(owner.create("dummy"))).is_err());
});

cell.assign(&new_value).unwrap();

cell.get(|val| assert_eq!(val.str(), "new"));
}
});
}
#[test]
fn simple_rcu() {
model(|| {
Expand Down

0 comments on commit 286a82c

Please sign in to comment.