Skip to content

Commit d144763

Browse files
PROMETHIA-27nicopap
authored andcommitted
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 <[email protected]>
1 parent 6ac5800 commit d144763

File tree

3 files changed

+49
-8
lines changed

3 files changed

+49
-8
lines changed

crates/bevy_ecs/src/system/system_param.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub use bevy_ecs_macros::Resource;
1515
pub use bevy_ecs_macros::SystemParam;
1616
use bevy_ecs_macros::{all_tuples, impl_param_set};
1717
use bevy_ptr::UnsafeCellDeref;
18+
use bevy_utils::synccell::SyncCell;
1819
use std::{
1920
borrow::Cow,
2021
fmt::Debug,
@@ -674,10 +675,10 @@ impl<'w, 's> SystemParamFetch<'w, 's> for WorldState {
674675
/// // .add_system(reset_to_system(my_config))
675676
/// # assert_is_system(reset_to_system(Config(10)));
676677
/// ```
677-
pub struct Local<'a, T: FromWorld + Send + Sync + 'static>(&'a mut T);
678+
pub struct Local<'a, T: FromWorld + Send + 'static>(&'a mut T);
678679

679680
// SAFETY: Local only accesses internal state
680-
unsafe impl<T: Send + Sync + 'static> ReadOnlySystemParamFetch for LocalState<T> {}
681+
unsafe impl<T: Send + 'static> ReadOnlySystemParamFetch for LocalState<T> {}
681682

682683
impl<'a, T: FromWorld + Send + Sync + 'static> Debug for Local<'a, T>
683684
where
@@ -706,20 +707,20 @@ impl<'a, T: FromWorld + Send + Sync + 'static> DerefMut for Local<'a, T> {
706707

707708
/// The [`SystemParamState`] of [`Local<T>`].
708709
#[doc(hidden)]
709-
pub struct LocalState<T: Send + Sync + 'static>(T);
710+
pub struct LocalState<T: Send + 'static>(SyncCell<T>);
710711

711-
impl<'a, T: Send + Sync + 'static + FromWorld> SystemParam for Local<'a, T> {
712+
impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> {
712713
type Fetch = LocalState<T>;
713714
}
714715

715716
// SAFETY: only local state is accessed
716-
unsafe impl<T: FromWorld + Send + Sync + 'static> SystemParamState for LocalState<T> {
717+
unsafe impl<T: FromWorld + Send + 'static> SystemParamState for LocalState<T> {
717718
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self {
718-
Self(T::from_world(world))
719+
Self(SyncCell::new(T::from_world(world)))
719720
}
720721
}
721722

722-
impl<'w, 's, T: Send + Sync + 'static + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> {
723+
impl<'w, 's, T: FromWorld + Send + 'static> SystemParamFetch<'w, 's> for LocalState<T> {
723724
type Item = Local<'s, T>;
724725

725726
#[inline]
@@ -729,7 +730,7 @@ impl<'w, 's, T: Send + Sync + 'static + FromWorld> SystemParamFetch<'w, 's> for
729730
_world: &'w World,
730731
_change_tick: u32,
731732
) -> Self::Item {
732-
Local(&mut state.0)
733+
Local(state.0.get())
733734
}
734735
}
735736

crates/bevy_utils/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod futures;
66
pub mod label;
77
mod short_names;
88
pub use short_names::get_short_name;
9+
pub mod synccell;
910

1011
mod default;
1112
mod float_ord;

crates/bevy_utils/src/synccell.rs

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/// See [`Exclusive`](https://github.com/rust-lang/rust/issues/98407) for stdlib's upcoming implementation,
2+
/// which should replace this one entirely.
3+
///
4+
/// Provides a wrapper that allows making any type unconditionally [`Sync`] by only providing mutable access.
5+
#[repr(transparent)]
6+
pub struct SyncCell<T: ?Sized> {
7+
inner: T,
8+
}
9+
10+
impl<T: Sized> SyncCell<T> {
11+
/// Construct a new instance of a `SyncCell` from the given value.
12+
pub fn new(inner: T) -> Self {
13+
Self { inner }
14+
}
15+
16+
/// Deconstruct this `SyncCell` into its inner value.
17+
pub fn to_inner(Self { inner }: Self) -> T {
18+
inner
19+
}
20+
}
21+
22+
impl<T: ?Sized> SyncCell<T> {
23+
/// Get a reference to this `SyncCell`'s inner value.
24+
pub fn get(&mut self) -> &mut T {
25+
&mut self.inner
26+
}
27+
28+
/// Build a mutable reference to a `SyncCell` from a mutable reference
29+
/// to its inner value, to skip constructing with [`new()`](SyncCell::new()).
30+
pub fn from_mut(r: &'_ mut T) -> &'_ mut SyncCell<T> {
31+
// SAFETY: repr is transparent, so refs have the same layout; and `SyncCell` properties are `&mut`-agnostic
32+
unsafe { &mut *(r as *mut T as *mut SyncCell<T>) }
33+
}
34+
}
35+
36+
// SAFETY: `Sync` only allows multithreaded access via immutable reference.
37+
// As `SyncCell` requires an exclusive reference to access the wrapped value,
38+
// marking this type as `Sync` does not actually allow threaded access to the inner value.
39+
unsafe impl<T: ?Sized> Sync for SyncCell<T> {}

0 commit comments

Comments
 (0)