Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds guarded ref and mut access for UnsafeCell #116

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

mod unsafe_cell;

pub use self::unsafe_cell::UnsafeCell;
pub use self::unsafe_cell::{UnsafeCell, UnsafeCellMutToken, UnsafeCellRefToken};
66 changes: 65 additions & 1 deletion src/cell/unsafe_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,30 @@ use crate::rt;
/// `with` and `with_mut`. Both functions take a closure in order to track the
/// start and end of the access to the underlying cell.
#[derive(Debug)]
pub struct UnsafeCell<T> {
pub struct UnsafeCell<T: ?Sized> {
/// Causality associated with the cell
state: rt::Cell,
data: std::cell::UnsafeCell<T>,
}

/// Token for tracking immutable access to the wrapped value.
///
/// This token must be held for as long as the access lasts.
#[derive(Debug)]
pub struct UnsafeCellRefToken<T: ?Sized> {
data: *const T,
token: rt::CellRefToken,
}

/// Token for tracking mutable access to the wrapped value.
///
/// This token must be held for as long as the access lasts.
#[derive(Debug)]
pub struct UnsafeCellMutToken<T: ?Sized> {
data: *mut T,
token: rt::CellMutToken,
}

impl<T> UnsafeCell<T> {
/// Constructs a new instance of `UnsafeCell` which will wrap the specified value.
#[cfg_attr(loom_nightly, track_caller)]
Expand All @@ -23,7 +41,9 @@ impl<T> UnsafeCell<T> {
data: std::cell::UnsafeCell::new(data),
}
}
}

impl<T: ?Sized> UnsafeCell<T> {
/// Get an immutable pointer to the wrapped value.
///
/// # Panics
Expand Down Expand Up @@ -52,6 +72,36 @@ impl<T> UnsafeCell<T> {
{
self.state.with_mut(location!(), || f(self.data.get()))
}

/// Get an `UnsafeCellRefToken` that provides scoped immutable access to the
/// wrapped value.
///
/// # Panics
///
/// This function will panic if the access is not valid under the Rust memory
/// model.
pub unsafe fn guard_ref_access(&self) -> UnsafeCellRefToken<T> {
let token = self.state.guard_ref_access(location!());
UnsafeCellRefToken {
data: self.data.get() as *const T,
token,
}
}

/// Get an `UnsafeCellMutToken` that provides scoped mutable access to the
/// wrapped value.
///
/// # Panics
///
/// This function will panic if the access is not valid under the Rust memory
/// model.
pub unsafe fn guard_mut_access(&self) -> UnsafeCellMutToken<T> {
let token = self.state.guard_mut_access(location!());
UnsafeCellMutToken {
data: self.data.get(),
token,
}
}
}

impl<T: Default> Default for UnsafeCell<T> {
Expand All @@ -65,3 +115,17 @@ impl<T> From<T> for UnsafeCell<T> {
UnsafeCell::new(src)
}
}

impl<T: ?Sized> UnsafeCellRefToken<T> {
/// Get an immutable pointer to the wrapped value.
pub const fn get(&self) -> *const T {
self.data
}
}

impl<T: ?Sized> UnsafeCellMutToken<T> {
/// Get a mutable pointer to the wrapped value.
pub const fn get(&self) -> *mut T {
self.data
}
}
132 changes: 80 additions & 52 deletions src/rt/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ pub(crate) struct Cell {
state: object::Ref<State>,
}

/// Tracks immutable access to `Cell`
#[derive(Debug)]
pub(crate) struct CellRefToken {
state: object::Ref<State>,
}

/// Tracks mutable access to `Cell`
#[derive(Debug)]
pub(crate) struct CellMutToken {
state: object::Ref<State>,
}

#[derive(Debug)]
pub(super) struct State {
/// Where the cell was created
Expand Down Expand Up @@ -43,78 +55,94 @@ impl Cell {
}

pub(crate) fn with<R>(&self, location: Location, f: impl FnOnce() -> R) -> R {
struct Reset {
state: object::Ref<State>,
}
// Enter the read closure
let _token = CellRefToken::new(self.state, location);

impl Drop for Reset {
fn drop(&mut self) {
rt::execution(|execution| {
let state = self.state.get_mut(&mut execution.objects);
f()
}

assert!(state.is_reading > 0);
assert!(!state.is_writing);
pub(crate) fn with_mut<R>(&self, location: Location, f: impl FnOnce() -> R) -> R {
// Enter the write closure
let _token = CellMutToken::new(self.state, location);

state.is_reading -= 1;
f()
}

if !std::thread::panicking() {
state.track_read(&execution.threads);
}
})
}
}
pub(crate) fn guard_ref_access(&self, location: Location) -> CellRefToken {
CellRefToken::new(self.state, location)
}

// Enter the read closure
let _reset = rt::synchronize(|execution| {
let state = self.state.get_mut(&mut execution.objects);
pub(crate) fn guard_mut_access(&self, location: Location) -> CellMutToken {
CellMutToken::new(self.state, location)
}
}

assert!(!state.is_writing, "currently writing to cell");
impl CellRefToken {
fn new(state: object::Ref<State>, location: Location) -> Self {
rt::synchronize(|execution| {
let state_obj = state.get_mut(&mut execution.objects);

state.is_reading += 1;
state.read_locations.track(location, &execution.threads);
state.track_read(&execution.threads);
assert!(!state_obj.is_writing, "currently writing to cell");

Reset { state: self.state }
});
state_obj.is_reading += 1;
state_obj.read_locations.track(location, &execution.threads);
state_obj.track_read(&execution.threads);

f()
Self { state }
})
}
}

pub(crate) fn with_mut<R>(&self, location: Location, f: impl FnOnce() -> R) -> R {
struct Reset(object::Ref<State>);

impl Drop for Reset {
fn drop(&mut self) {
rt::execution(|execution| {
let state = self.0.get_mut(&mut execution.objects);
impl Drop for CellRefToken {
fn drop(&mut self) {
rt::execution(|execution| {
let state_obj = self.state.get_mut(&mut execution.objects);

assert!(state.is_writing);
assert!(state.is_reading == 0);
assert!(state_obj.is_reading > 0);
assert!(!state_obj.is_writing);

state.is_writing = false;
state_obj.is_reading -= 1;

if !std::thread::panicking() {
state.track_write(&execution.threads);
}
})
if !std::thread::panicking() {
state_obj.track_read(&execution.threads);
}
}
})
}
}

// Enter the read closure
let _reset = rt::synchronize(|execution| {
let state = self.state.get_mut(&mut execution.objects);
impl CellMutToken {
fn new(state: object::Ref<State>, location: Location) -> Self {
rt::synchronize(|execution| {
let state_obj = state.get_mut(&mut execution.objects);

assert!(state.is_reading == 0, "currently reading from cell");
assert!(!state.is_writing, "currently writing to cell");
assert!(state_obj.is_reading == 0, "currently reading from cell");
assert!(!state_obj.is_writing, "currently writing to cell");

state.is_writing = true;
state.write_locations.track(location, &execution.threads);
state.track_write(&execution.threads);
state_obj.is_writing = true;
state_obj
.write_locations
.track(location, &execution.threads);
state_obj.track_write(&execution.threads);

Reset(self.state)
});
Self { state }
})
}
}

f()
impl Drop for CellMutToken {
fn drop(&mut self) {
rt::execution(|execution| {
let state_obj = self.state.get_mut(&mut execution.objects);

assert!(state_obj.is_writing);
assert!(state_obj.is_reading == 0);

state_obj.is_writing = false;

if !std::thread::panicking() {
state_obj.track_write(&execution.threads);
}
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ mod location;
pub(crate) use self::location::Location;

mod cell;
pub(crate) use self::cell::Cell;
pub(crate) use self::cell::{Cell, CellMutToken, CellRefToken};

mod condvar;
pub(crate) use self::condvar::Condvar;
Expand Down