Skip to content

Commit

Permalink
Rollup merge of #95438 - m-ou-se:sync-unsafe-cell, r=joshtriplett
Browse files Browse the repository at this point in the history
Add SyncUnsafeCell.

This adds `SyncUnsafeCell`, which is just `UnsafeCell` except it implements `Sync`.

This was first proposed under the name `RacyUnsafeCell` here: #53639 (comment) and here: #53639 (comment) and here: #53639 (comment)

It allows you to create an UnsafeCell that is Sync without having to wrap it in a struct first (and then implement Sync for that struct).

E.g. `static X: SyncUnsafeCell<i32>`. Using a regular `UnsafeCell` as `static` is not possible, because it isn't `Sync`. We have a language workaround for it called `static mut`, but it's nice to be able to use the proper type for such unsafety instead.

It also makes implementing synchronization primitives based on unsafe cells slightly less verbose, because by using `SyncUnsafeCell` for `UnsafeCell`s that are shared between threads, you don't need a separate `impl<..> Sync for ..`. Using this type also clearly documents that the cell is expected to be accessed from multiple threads.
  • Loading branch information
Dylan-DPC authored Apr 4, 2022
2 parents 73148ee + f225808 commit 4d7d9d4
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 4 deletions.
103 changes: 100 additions & 3 deletions library/core/src/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1990,9 +1990,106 @@ impl<T> const From<T> for UnsafeCell<T> {
#[unstable(feature = "coerce_unsized", issue = "27732")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<UnsafeCell<U>> for UnsafeCell<T> {}

/// [`UnsafeCell`], but [`Sync`].
///
/// This is just an `UnsafeCell`, except it implements `Sync`
/// if `T` implements `Sync`.
///
/// `UnsafeCell` doesn't implement `Sync`, to prevent accidental mis-use.
/// You can use `SyncUnsafeCell` instead of `UnsafeCell` to allow it to be
/// shared between threads, if that's intentional.
/// Providing proper synchronization is still the task of the user,
/// making this type just as unsafe to use.
///
/// See [`UnsafeCell`] for details.
#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
#[repr(transparent)]
pub struct SyncUnsafeCell<T: ?Sized> {
value: UnsafeCell<T>,
}

#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
unsafe impl<T: ?Sized + Sync> Sync for SyncUnsafeCell<T> {}

#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
impl<T> SyncUnsafeCell<T> {
/// Constructs a new instance of `SyncUnsafeCell` which will wrap the specified value.
#[inline]
pub const fn new(value: T) -> Self {
Self { value: UnsafeCell { value } }
}

/// Unwraps the value.
#[inline]
pub const fn into_inner(self) -> T {
self.value.into_inner()
}
}

#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
impl<T: ?Sized> SyncUnsafeCell<T> {
/// Gets a mutable pointer to the wrapped value.
///
/// This can be cast to a pointer of any kind.
/// Ensure that the access is unique (no active references, mutable or not)
/// when casting to `&mut T`, and ensure that there are no mutations
/// or mutable aliases going on when casting to `&T`
#[inline]
pub const fn get(&self) -> *mut T {
self.value.get()
}

/// Returns a mutable reference to the underlying data.
///
/// This call borrows the `SyncUnsafeCell` mutably (at compile-time) which
/// guarantees that we possess the only reference.
#[inline]
pub const fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}

/// Gets a mutable pointer to the wrapped value.
///
/// See [`UnsafeCell::get`] for details.
#[inline]
pub const fn raw_get(this: *const Self) -> *mut T {
// We can just cast the pointer from `SyncUnsafeCell<T>` to `T` because
// of #[repr(transparent)] on both SyncUnsafeCell and UnsafeCell.
// See UnsafeCell::raw_get.
this as *const T as *mut T
}
}

#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
impl<T: Default> Default for SyncUnsafeCell<T> {
/// Creates an `SyncUnsafeCell`, with the `Default` value for T.
fn default() -> SyncUnsafeCell<T> {
SyncUnsafeCell::new(Default::default())
}
}

#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T> const From<T> for SyncUnsafeCell<T> {
/// Creates a new `SyncUnsafeCell<T>` containing the given value.
fn from(t: T) -> SyncUnsafeCell<T> {
SyncUnsafeCell::new(t)
}
}

#[unstable(feature = "coerce_unsized", issue = "27732")]
//#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<SyncUnsafeCell<U>> for SyncUnsafeCell<T> {}

#[allow(unused)]
fn assert_coerce_unsized(a: UnsafeCell<&i32>, b: Cell<&i32>, c: RefCell<&i32>) {
fn assert_coerce_unsized(
a: UnsafeCell<&i32>,
b: SyncUnsafeCell<&i32>,
c: Cell<&i32>,
d: RefCell<&i32>,
) {
let _: UnsafeCell<&dyn Send> = a;
let _: Cell<&dyn Send> = b;
let _: RefCell<&dyn Send> = c;
let _: SyncUnsafeCell<&dyn Send> = b;
let _: Cell<&dyn Send> = c;
let _: RefCell<&dyn Send> = d;
}
9 changes: 8 additions & 1 deletion library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![stable(feature = "rust1", since = "1.0.0")]

use crate::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
use crate::cell::{Cell, Ref, RefCell, RefMut, SyncUnsafeCell, UnsafeCell};
use crate::char::EscapeDebugExtArgs;
use crate::marker::PhantomData;
use crate::mem;
Expand Down Expand Up @@ -2400,6 +2400,13 @@ impl<T: ?Sized> Debug for UnsafeCell<T> {
}
}

#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
impl<T: ?Sized> Debug for SyncUnsafeCell<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("SyncUnsafeCell").finish_non_exhaustive()
}
}

// If you expected tests to be here, look instead at the core/tests/fmt.rs file,
// it's a lot easier than creating all of the rt::Piece structures here.
// There are also tests in the alloc crate, for those that need allocations.
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
#![feature(const_type_id)]
#![feature(const_type_name)]
#![feature(const_default_impls)]
#![feature(const_unsafecell_get_mut)]
#![feature(core_panic)]
#![feature(duration_consts_float)]
#![feature(maybe_uninit_uninit_array)]
Expand Down

0 comments on commit 4d7d9d4

Please sign in to comment.