Remove Sync bound from Local (#5483)

# Objective

Currently, `Local` has a `Sync` bound. Theoretically this is unnecessary as a local can only ever be accessed from its own system, ensuring exclusive access on one thread. This PR removes this restriction.

## Solution

- By removing the `Resource` bound from `Local` and adding the new `SyncCell` threading primative, `Local` can have the `Sync` bound removed.

## Changelog

### Added

- Added `SyncCell` to `bevy_utils`

### Changed

- Removed `Resource` bound from `Local`
- `Local` is now wrapped in a `SyncCell`

## Migration Guide

- Any code relying on `Local<T>` having `T: Resource` may have to be changed, but this is unlikely.

Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com>
This commit is contained in:
PROMETHIA-27 2022-09-12 04:15:55 +00:00
parent 301ecf65ba
commit 05afbc6815
3 changed files with 49 additions and 8 deletions

View file

@ -15,6 +15,7 @@ pub use bevy_ecs_macros::Resource;
pub use bevy_ecs_macros::SystemParam;
use bevy_ecs_macros::{all_tuples, impl_param_set};
use bevy_ptr::UnsafeCellDeref;
use bevy_utils::synccell::SyncCell;
use std::{
borrow::Cow,
fmt::Debug,
@ -674,10 +675,10 @@ impl<'w, 's> SystemParamFetch<'w, 's> for WorldState {
/// // .add_system(reset_to_system(my_config))
/// # assert_is_system(reset_to_system(Config(10)));
/// ```
pub struct Local<'a, T: FromWorld + Send + Sync + 'static>(&'a mut T);
pub struct Local<'a, T: FromWorld + Send + 'static>(&'a mut T);
// SAFETY: Local only accesses internal state
unsafe impl<T: Send + Sync + 'static> ReadOnlySystemParamFetch for LocalState<T> {}
unsafe impl<T: Send + 'static> ReadOnlySystemParamFetch for LocalState<T> {}
impl<'a, T: FromWorld + Send + Sync + 'static> Debug for Local<'a, T>
where
@ -706,20 +707,20 @@ impl<'a, T: FromWorld + Send + Sync + 'static> DerefMut for Local<'a, T> {
/// The [`SystemParamState`] of [`Local<T>`].
#[doc(hidden)]
pub struct LocalState<T: Send + Sync + 'static>(T);
pub struct LocalState<T: Send + 'static>(SyncCell<T>);
impl<'a, T: Send + Sync + 'static + FromWorld> SystemParam for Local<'a, T> {
impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> {
type Fetch = LocalState<T>;
}
// SAFETY: only local state is accessed
unsafe impl<T: FromWorld + Send + Sync + 'static> SystemParamState for LocalState<T> {
unsafe impl<T: FromWorld + Send + 'static> SystemParamState for LocalState<T> {
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self {
Self(T::from_world(world))
Self(SyncCell::new(T::from_world(world)))
}
}
impl<'w, 's, T: Send + Sync + 'static + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> {
impl<'w, 's, T: FromWorld + Send + 'static> SystemParamFetch<'w, 's> for LocalState<T> {
type Item = Local<'s, T>;
#[inline]
@ -729,7 +730,7 @@ impl<'w, 's, T: Send + Sync + 'static + FromWorld> SystemParamFetch<'w, 's> for
_world: &'w World,
_change_tick: u32,
) -> Self::Item {
Local(&mut state.0)
Local(state.0.get())
}
}

View file

@ -6,6 +6,7 @@ pub mod futures;
pub mod label;
mod short_names;
pub use short_names::get_short_name;
pub mod synccell;
mod default;
mod float_ord;

View file

@ -0,0 +1,39 @@
/// See [`Exclusive`](https://github.com/rust-lang/rust/issues/98407) for stdlib's upcoming implementation,
/// which should replace this one entirely.
///
/// Provides a wrapper that allows making any type unconditionally [`Sync`] by only providing mutable access.
#[repr(transparent)]
pub struct SyncCell<T: ?Sized> {
inner: T,
}
impl<T: Sized> SyncCell<T> {
/// Construct a new instance of a `SyncCell` from the given value.
pub fn new(inner: T) -> Self {
Self { inner }
}
/// Deconstruct this `SyncCell` into its inner value.
pub fn to_inner(Self { inner }: Self) -> T {
inner
}
}
impl<T: ?Sized> SyncCell<T> {
/// Get a reference to this `SyncCell`'s inner value.
pub fn get(&mut self) -> &mut T {
&mut self.inner
}
/// Build a mutable reference to a `SyncCell` from a mutable reference
/// to its inner value, to skip constructing with [`new()`](SyncCell::new()).
pub fn from_mut(r: &'_ mut T) -> &'_ mut SyncCell<T> {
// SAFETY: repr is transparent, so refs have the same layout; and `SyncCell` properties are `&mut`-agnostic
unsafe { &mut *(r as *mut T as *mut SyncCell<T>) }
}
}
// SAFETY: `Sync` only allows multithreaded access via immutable reference.
// As `SyncCell` requires an exclusive reference to access the wrapped value,
// marking this type as `Sync` does not actually allow threaded access to the inner value.
unsafe impl<T: ?Sized> Sync for SyncCell<T> {}