-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Relax Resource: Sync
bound
#6572
Conversation
Can we avoid having to specify the (Edit: Nevermind, I missed the "Future Work" section.) |
Should be ready for review now. Note that this PR is blocked by #6603 . |
@@ -371,6 +378,13 @@ impl<'a, T: Resource> SystemParam for Res<'a, T> { | |||
// conflicts with any prior access, a panic will occur. | |||
unsafe impl<T: Resource> SystemParamState for ResState<T> { | |||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { | |||
// If the resource can't be shared between threads, make sure it runs on the main thread. | |||
// FIXME: We should be able to register access as 'exclusive reads' to prevent shared access | |||
// without forcing the system to run on the main thread. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, there's no point to merging this PR as is without this. You would otherwise just use a NonSend
for the same effect.
You can treat a Res<T: !Sync>
the same as ResMut<T: !Sync>
. Move the if !T::IS_SYNC
down below, and change the non-Sync branch to add a write instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, there's no point to merging this PR as is without this. You would otherwise just use a NonSend for the same effect.
I want to keep this PR as small as possible, so it's easier to review. If this gets merged, I'll open a follow-up PR that makes the Access
changes.
These changes are easily separable, so I see no reason to roll them up into one PR. Especially since the Access
changes are just an optimization.
Also, this already allows ResMut<T: !Sync>
to be used off of the main thread.
You can treat a Res<T: !Sync> the same as ResMut<T: !Sync>. Move the if !T::IS_SYNC down below, and change the non-Sync branch to add a write instead.
This won't work, it will mess up ambiguity detection.
impl #impl_generics #bevy_ecs_path::system::Resource for #struct_name #type_generics #where_clause { | ||
// SAFETY: The trait bounds on this impl guarantee that the non-`Sync` types will not have `IS_SYNC = true`. | ||
unsafe impl #impl_generics #bevy_ecs_path::system::Resource for #struct_name #type_generics #where_clause { | ||
const IS_SYNC: bool = #is_sync; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should definitely have some compile fail tests to ensure that this breaks when this contract is broken.
Separately, we should see if the compile failures are readable by normal users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a compile fail test. The error message isn't great, but it isn't any worse than before this PR.
I tried a few ways of providing a more descriptive error message, but they all caused problems in edge cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After investigating further, the error message is actually good for concrete types. It shows up when you define the struct, and points you directly to the field that makes the struct !Sync
.
It's less good for generic types, because the error doesn't occur until you try to use the resource.
// Concrete types:
#[derive(Resource)] // error message shows up here, right where we want it.
struct MyResource(Cell<u32>);
// Generic types:
#[derive(Resource)]
struct MyResource<T>(Cell<T>);
// The error message doesn't show up until here, which is un-ideal.
fn my_system(_: Res<MyResource<u32>>) {}
IMO this isn't perfect, but it's perfectly acceptable.
This reverts commit 47a144a.
4afed44
to
c3b681c
Compare
This is no longer blocked by #6603. |
I'm closing this in favor of #6864 -- this all seems a bit unnecessary when you could just wrap your |
For what it's worth, I do think |
could we create our own wrapper that would only trigger change detection on mutable access to the inner value? |
.We'd need to be aware that the type is |
# Objective It's not clear to users how to handle `!Sync` types as components and resources in the absence of engine level support for them. ## Solution Added a section to `Component`'s and `Resource`'s type level docs on available options for making a type `Sync` when it holds `!Sync` fields, linking `bevy_utils::synccell::SyncCell` and the currently unstable `std::sync::Exclusive`. Also added a compile_fail doctest that illustrates how to apply `SyncCell`. These will break when/if #6572 gets merged, at which point these docs should be updated.
…ine#6864) # Objective It's not clear to users how to handle `!Sync` types as components and resources in the absence of engine level support for them. ## Solution Added a section to `Component`'s and `Resource`'s type level docs on available options for making a type `Sync` when it holds `!Sync` fields, linking `bevy_utils::synccell::SyncCell` and the currently unstable `std::sync::Exclusive`. Also added a compile_fail doctest that illustrates how to apply `SyncCell`. These will break when/if bevyengine#6572 gets merged, at which point these docs should be updated.
…ine#6864) # Objective It's not clear to users how to handle `!Sync` types as components and resources in the absence of engine level support for them. ## Solution Added a section to `Component`'s and `Resource`'s type level docs on available options for making a type `Sync` when it holds `!Sync` fields, linking `bevy_utils::synccell::SyncCell` and the currently unstable `std::sync::Exclusive`. Also added a compile_fail doctest that illustrates how to apply `SyncCell`. These will break when/if bevyengine#6572 gets merged, at which point these docs should be updated.
Objective
Allow non-
Sync
types to be used as resources, without having to resort toNonSend<>
.Currently, non-
Sync
types are conflated with non-Send
types, which results in overly restrictive scheduling. Any system that uses non-Send
types must be run on the main thread, which increases thread contention and prevents parallelization. This is unnecessary, asSend + !Sync
resources could be accessed from any thread, as long as it's only one at a time.Solution
Resource: Sync
.Resource
trait tracks whether each type isSync
. The scheduler ensures that shared access to non-Sync
types does not occur.Example
Future Work
For the time being, systems with read-only access to a non-
Sync
resource are run on the main thread. This is overly restrictive: we will need to update ourAccess
model to properly support non-Sync
reads.Changelog
Resource
now supports types that do not implementstd::marker::Sync
.Migration Guide
The
Resource
trait is nowunsafe
to implement manually. Use of the derive macro is recommended.Any generic code that relied on
Resource: Sync
will now need to specify theSync
bound.