From 81699895073162cd6731413711a4357dde67d661 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Sat, 19 Sep 2020 21:32:33 +0200 Subject: [PATCH 1/2] Add non-`unsafe` `.get_mut()` for `UnsafeCell` Update the tracking issue number Updated the documentation for `UnsafeCell` Address review comments Address more review comments + minor changes --- library/core/src/cell.rs | 93 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 9 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index cbbfcb4611321..44b863b220051 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1543,8 +1543,11 @@ impl fmt::Display for RefMut<'_, T> { /// allow internal mutability, such as `Cell` and `RefCell`, use `UnsafeCell` to wrap their /// internal data. There is *no* legal way to obtain aliasing `&mut`, not even with `UnsafeCell`. /// -/// The `UnsafeCell` API itself is technically very simple: it gives you a raw pointer `*mut T` to -/// its contents. It is up to _you_ as the abstraction designer to use that raw pointer correctly. +/// The `UnsafeCell` API itself is technically very simple: [`.get()`] gives you a raw pointer +/// `*mut T` to its contents. It is up to _you_ as the abstraction designer to use that raw pointer +/// correctly. +/// +/// [`.get()`]: `UnsafeCell::get` /// /// The precise Rust aliasing rules are somewhat in flux, but the main points are not contentious: /// @@ -1571,21 +1574,70 @@ impl fmt::Display for RefMut<'_, T> { /// 2. A `&mut T` reference may be released to safe code provided neither other `&mut T` nor `&T` /// co-exist with it. A `&mut T` must always be unique. /// -/// Note that while mutating or mutably aliasing the contents of an `&UnsafeCell` is -/// ok (provided you enforce the invariants some other way), it is still undefined behavior -/// to have multiple `&mut UnsafeCell` aliases. +/// Note that whilst mutating the contents of an `&UnsafeCell` (even while other +/// `&UnsafeCell` references alias the cell) is +/// ok (provided you enforce the above invariants some other way), it is still undefined behavior +/// to have multiple `&mut UnsafeCell` aliases. That is, `UnsafeCell` is a wrapper +/// designed to have a special interaction with _shared_ accesses (_i.e._, through an +/// `&UnsafeCell<_>` reference); there is no magic whatsoever when dealing with _exclusive_ +/// accesses (_e.g._, through an `&mut UnsafeCell<_>`): neither the cell nor the wrapped value +/// may be aliased for the duration of that `&mut` borrow. +/// This is showcased by the [`.get_mut()`] accessor, which is a non-`unsafe` getter that yields +/// a `&mut T`. +/// +/// [`.get_mut()`]: `UnsafeCell::get_mut` /// /// # Examples /// +/// Here is an example showcasing how to soundly mutate the contents of an `UnsafeCell<_>` despite +/// there being multiple references aliasing the cell: +/// /// ``` /// use std::cell::UnsafeCell; /// -/// # #[allow(dead_code)] -/// struct NotThreadSafe { -/// value: UnsafeCell, +/// let x: UnsafeCell = 42.into(); +/// // Get multiple / concurrent / shared references to the same `x`. +/// let (p1, p2): (&UnsafeCell, &UnsafeCell) = (&x, &x); +/// +/// unsafe { +/// // SAFETY: within this scope there are no other references to `x`'s contents, +/// // so ours is effectively unique. +/// let p1_exclusive: &mut i32 = &mut *p1.get(); // -- borrow --+ +/// *p1_exclusive += 27; // | +/// } // <---------- cannot go beyond this point -------------------+ +/// +/// unsafe { +/// // SAFETY: within this scope nobody expects to have exclusive access to `x`'s contents, +/// // so we can have multiple shared accesses concurrently. +/// let p2_shared: &i32 = &*p2.get(); +/// assert_eq!(*p2_shared, 42 + 27); +/// let p1_shared: &i32 = &*p1.get(); +/// assert_eq!(*p1_shared, *p2_shared); /// } +/// ``` +/// +/// The following example showcases the fact that exclusive access to an `UnsafeCell` +/// implies exclusive access to its `T`: /// -/// unsafe impl Sync for NotThreadSafe {} +/// ```rust +/// #![feature(unsafe_cell_get_mut)] +/// #![forbid(unsafe_code)] // with exclusive accesses, +/// // `UnsafeCell` is a transparent no-op wrapper, +/// // so no need for `unsafe` here. +/// use std::cell::UnsafeCell; +/// +/// let mut x: UnsafeCell = 42.into(); +/// +/// // Get a compile-time-checked unique reference to `x`. +/// let p_unique: &mut UnsafeCell = &mut x; +/// // With an exclusive reference, we can mutate the contents for free. +/// *p_unique.get_mut() = 0; +/// // Or, equivalently: +/// x = UnsafeCell::new(0); +/// +/// // When we own the value, we can extract the contents for free. +/// let contents: i32 = x.into_inner(); +/// assert_eq!(contents, 0); /// ``` #[lang = "unsafe_cell"] #[stable(feature = "rust1", since = "1.0.0")] @@ -1663,6 +1715,29 @@ impl UnsafeCell { self as *const UnsafeCell as *const T as *mut T } + /// Returns a mutable reference to the underlying data. + /// + /// This call borrows the `UnsafeCell` mutably (at compile-time) which + /// guarantees that we possess the only reference. + /// + /// # Examples + /// + /// ``` + /// #![feature(unsafe_cell_get_mut)] + /// use std::cell::UnsafeCell; + /// + /// let mut c = UnsafeCell::new(5); + /// *c.get_mut() += 1; + /// + /// assert_eq!(*c.get_mut(), 6); + /// ``` + #[inline] + #[unstable(feature = "unsafe_cell_get_mut", issue = "76943")] + pub fn get_mut(&mut self) -> &mut T { + // SAFETY: (outer) `&mut` guarantees unique access. + unsafe { &mut *self.get() } + } + /// Gets a mutable pointer to the wrapped value. /// The difference to [`get`] is that this function accepts a raw pointer, /// which is useful to avoid the creation of temporary references. From 5886c38112c8bb347b1cbd46c28b1ca6f8bac88d Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Sat, 19 Sep 2020 21:33:40 +0200 Subject: [PATCH 2/2] Replace unneeded `unsafe` calls to `.get()` with calls to `.get_mut()` --- library/core/src/cell.rs | 8 ++------ library/core/src/sync/atomic.rs | 6 ++---- library/std/src/lib.rs | 1 + library/std/src/sync/mutex.rs | 4 +--- library/std/src/sync/rwlock.rs | 4 +--- 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 44b863b220051..f60aa2d24c5ca 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -496,10 +496,7 @@ impl Cell { #[inline] #[stable(feature = "cell_get_mut", since = "1.11.0")] pub fn get_mut(&mut self) -> &mut T { - // SAFETY: This can cause data races if called from a separate thread, - // but `Cell` is `!Sync` so this won't happen, and `&mut` guarantees - // unique access. - unsafe { &mut *self.value.get() } + self.value.get_mut() } /// Returns a `&Cell` from a `&mut T` @@ -945,8 +942,7 @@ impl RefCell { #[inline] #[stable(feature = "cell_get_mut", since = "1.11.0")] pub fn get_mut(&mut self) -> &mut T { - // SAFETY: `&mut` guarantees unique access. - unsafe { &mut *self.value.get() } + self.value.get_mut() } /// Undo the effect of leaked guards on the borrow state of the `RefCell`. diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 9d74f537491b1..c67d6422c01ec 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -838,8 +838,7 @@ impl AtomicPtr { #[inline] #[stable(feature = "atomic_access", since = "1.15.0")] pub fn get_mut(&mut self) -> &mut *mut T { - // SAFETY: the mutable reference guarantees unique ownership. - unsafe { &mut *self.p.get() } + self.p.get_mut() } /// Get atomic access to a pointer. @@ -1275,8 +1274,7 @@ assert_eq!(some_var.load(Ordering::SeqCst), 5); #[inline] #[$stable_access] pub fn get_mut(&mut self) -> &mut $int_type { - // SAFETY: the mutable reference guarantees unique ownership. - unsafe { &mut *self.v.get() } + self.v.get_mut() } } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5333d75ec1bc5..71b29cf5af99b 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -315,6 +315,7 @@ #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(unsafe_block_in_unsafe_fn)] +#![feature(unsafe_cell_get_mut)] #![feature(unsafe_cell_raw_get)] #![feature(untagged_unions)] #![feature(unwind_attributes)] diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index 240155b06b411..a1703c731d44d 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -406,9 +406,7 @@ impl Mutex { /// ``` #[stable(feature = "mutex_get_mut", since = "1.6.0")] pub fn get_mut(&mut self) -> LockResult<&mut T> { - // We know statically that there are no other references to `self`, so - // there's no need to lock the inner mutex. - let data = unsafe { &mut *self.data.get() }; + let data = self.data.get_mut(); poison::map_result(self.poison.borrow(), |_| data) } } diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index f38d6101da0d3..d967779ce361d 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -404,9 +404,7 @@ impl RwLock { /// ``` #[stable(feature = "rwlock_get_mut", since = "1.6.0")] pub fn get_mut(&mut self) -> LockResult<&mut T> { - // We know statically that there are no other references to `self`, so - // there's no need to lock the inner lock. - let data = unsafe { &mut *self.data.get() }; + let data = self.data.get_mut(); poison::map_result(self.poison.borrow(), |_| data) } }