From fbc16c8fe2a00242a89084e6413a3238df6fffc9 Mon Sep 17 00:00:00 2001 From: Kyle Fleming Date: Tue, 7 Apr 2020 16:36:33 -0700 Subject: [PATCH 1/2] Allow T in UnsafeCell to be non-Sized --- src/cell/unsafe_cell.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cell/unsafe_cell.rs b/src/cell/unsafe_cell.rs index 186c5eab..4f0284e8 100644 --- a/src/cell/unsafe_cell.rs +++ b/src/cell/unsafe_cell.rs @@ -6,7 +6,7 @@ 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 { +pub struct UnsafeCell { /// Causality associated with the cell state: rt::Cell, data: std::cell::UnsafeCell, @@ -23,7 +23,9 @@ impl UnsafeCell { data: std::cell::UnsafeCell::new(data), } } +} +impl UnsafeCell { /// Get an immutable pointer to the wrapped value. /// /// # Panics From 1ce97fbd7a0a409f2fb4b998a0a9b0655cae0351 Mon Sep 17 00:00:00 2001 From: Kyle Fleming Date: Tue, 7 Apr 2020 16:44:28 -0700 Subject: [PATCH 2/2] Add UnsafeCell guarded ref and mut access --- src/cell/mod.rs | 2 +- src/cell/unsafe_cell.rs | 62 +++++++++++++++++++ src/rt/cell.rs | 132 ++++++++++++++++++++++++---------------- src/rt/mod.rs | 2 +- 4 files changed, 144 insertions(+), 54 deletions(-) diff --git a/src/cell/mod.rs b/src/cell/mod.rs index 4fb2ab72..a0271cc9 100644 --- a/src/cell/mod.rs +++ b/src/cell/mod.rs @@ -2,4 +2,4 @@ mod unsafe_cell; -pub use self::unsafe_cell::UnsafeCell; +pub use self::unsafe_cell::{UnsafeCell, UnsafeCellMutToken, UnsafeCellRefToken}; diff --git a/src/cell/unsafe_cell.rs b/src/cell/unsafe_cell.rs index 4f0284e8..b0ca3fba 100644 --- a/src/cell/unsafe_cell.rs +++ b/src/cell/unsafe_cell.rs @@ -12,6 +12,24 @@ pub struct UnsafeCell { data: std::cell::UnsafeCell, } +/// 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 { + 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 { + data: *mut T, + token: rt::CellMutToken, +} + impl UnsafeCell { /// Constructs a new instance of `UnsafeCell` which will wrap the specified value. #[cfg_attr(loom_nightly, track_caller)] @@ -54,6 +72,36 @@ impl UnsafeCell { { 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 { + 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 { + let token = self.state.guard_mut_access(location!()); + UnsafeCellMutToken { + data: self.data.get(), + token, + } + } } impl Default for UnsafeCell { @@ -67,3 +115,17 @@ impl From for UnsafeCell { UnsafeCell::new(src) } } + +impl UnsafeCellRefToken { + /// Get an immutable pointer to the wrapped value. + pub const fn get(&self) -> *const T { + self.data + } +} + +impl UnsafeCellMutToken { + /// Get a mutable pointer to the wrapped value. + pub const fn get(&self) -> *mut T { + self.data + } +} diff --git a/src/rt/cell.rs b/src/rt/cell.rs index 8abe544b..7382fc06 100644 --- a/src/rt/cell.rs +++ b/src/rt/cell.rs @@ -7,6 +7,18 @@ pub(crate) struct Cell { state: object::Ref, } +/// Tracks immutable access to `Cell` +#[derive(Debug)] +pub(crate) struct CellRefToken { + state: object::Ref, +} + +/// Tracks mutable access to `Cell` +#[derive(Debug)] +pub(crate) struct CellMutToken { + state: object::Ref, +} + #[derive(Debug)] pub(super) struct State { /// Where the cell was created @@ -43,78 +55,94 @@ impl Cell { } pub(crate) fn with(&self, location: Location, f: impl FnOnce() -> R) -> R { - struct Reset { - state: object::Ref, - } + // 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(&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, 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(&self, location: Location, f: impl FnOnce() -> R) -> R { - struct Reset(object::Ref); - - 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, 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); + } + }) } } diff --git a/src/rt/mod.rs b/src/rt/mod.rs index 52fd55bf..5b9b9f25 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -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;