Skip to content
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

[Merged by Bors] - System Param Lifetime Split #2605

Closed
wants to merge 7 commits into from

Conversation

cart
Copy link
Member

@cart cart commented Aug 5, 2021

Objective

Enable using exact World lifetimes during read-only access . This is motivated by the new renderer's need to allow read-only world-only queries to outlive the query itself (but still be constrained by the world lifetime).

For example:

&mesh_view_bind_groups.view,

Solution

Split out SystemParam state and world lifetimes and pipe those lifetimes up to read-only Query ops (and add into_inner for Res). According to every safety test I've run so far (except one), this is safe (see the temporary safety test commit). Note that changing the mutable variants to the new lifetimes would allow aliased mutable pointers (try doing that to see how it affects the temporary safety tests).

The new state lifetime on SystemParam does make #[derive(SystemParam)] more cumbersome (the current impl requires PhantomData if you don't use both lifetimes). We can make this better by detecting whether or not a lifetime is used in the derive and adjusting accordingly, but that should probably be done in its own pr.

Why is this a draft?

The new lifetimes break QuerySet safety in one very specific case (see the query_set system in system_safety_test). We need to solve this before we can use the lifetimes given.

This is due to the fact that QuerySet is just a wrapper over Query, which now relies on world lifetimes instead of &self lifetimes to prevent aliasing (but in systems, each Query has its own implied lifetime, not a centralized world lifetime). I believe the fix is to rewrite QuerySet to have its own World lifetime (and own the internal reference). This will complicate the impl a bit, but I think it is doable. I'm curious if anyone else has better ideas.

Personally, I think these new lifetimes need to happen. We've gotta have a way to directly tie read-only World queries to the World lifetime. The new renderer is the first place this has come up, but I doubt it will be the last. Worst case scenario we can come up with a second WorldLifetimeQuery<Q, F = ()> parameter to enable these read-only scenarios, but I'd rather not add another type to the type zoo.

@cart cart added the A-ECS Entities, components, systems, and events label Aug 5, 2021
@cart cart added this to the Bevy 0.6 milestone Aug 5, 2021
@cart cart marked this pull request as draft August 5, 2021 22:46
@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Aug 5, 2021
@alice-i-cecile alice-i-cecile requested a review from BoxyUwU August 5, 2021 22:48
@alice-i-cecile alice-i-cecile added C-Code-Quality A section of code that is hard to understand or change and removed S-Needs-Triage This issue needs to be labelled labels Aug 5, 2021
@alice-i-cecile
Copy link
Member

@BoxyUwU you made this same change in your relations PR. What motivated that?

@cart cart force-pushed the system-param-lifetimes branch from 06a7783 to 42fedfc Compare August 6, 2021 01:46
@alice-i-cecile alice-i-cecile added M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Needs-Migration-Guide labels Aug 8, 2021
@cart cart marked this pull request as ready for review August 8, 2021 19:29
@cart cart force-pushed the system-param-lifetimes branch from 4c7f66a to 2ee8462 Compare August 12, 2021 00:07
@cart
Copy link
Member Author

cart commented Aug 15, 2021

bors r+

bors bot pushed a commit that referenced this pull request Aug 15, 2021
# Objective

Enable using exact World lifetimes during read-only access . This is motivated by the new renderer's need to allow read-only world-only queries to outlive the query itself (but still be constrained by the world lifetime).

For example:
https://github.com/bevyengine/bevy/blob/115b170d1f11a91146bb6d6e9684dceb8b21f786/pipelined/bevy_pbr2/src/render/mod.rs#L774

## Solution

Split out SystemParam state and world lifetimes and pipe those lifetimes up to read-only Query ops (and add into_inner for Res). According to every safety test I've run so far (except one), this is safe (see the temporary safety test commit). Note that changing the mutable variants to the new lifetimes would allow aliased mutable pointers (try doing that to see how it affects the temporary safety tests).

The new state lifetime on SystemParam does make `#[derive(SystemParam)]` more cumbersome (the current impl requires PhantomData if you don't use both lifetimes). We can make this better by detecting whether or not a lifetime is used in the derive and adjusting accordingly, but that should probably be done in its own pr.  

## Why is this a draft?

The new lifetimes break QuerySet safety in one very specific case (see the query_set system in system_safety_test). We need to solve this before we can use the lifetimes given.

This is due to the fact that QuerySet is just a wrapper over Query, which now relies on world lifetimes instead of `&self` lifetimes to prevent aliasing (but in systems, each Query has its own implied lifetime, not a centralized world lifetime).  I believe the fix is to rewrite QuerySet to have its own World lifetime (and own the internal reference). This will complicate the impl a bit, but I think it is doable. I'm curious if anyone else has better ideas.

Personally, I think these new lifetimes need to happen. We've gotta have a way to directly tie read-only World queries to the World lifetime. The new renderer is the first place this has come up, but I doubt it will be the last. Worst case scenario we can come up with a second `WorldLifetimeQuery<Q, F = ()>` parameter to enable these read-only scenarios, but I'd rather not add another type to the type zoo.
@bors bors bot changed the title System Param Lifetime Split [Merged by Bors] - System Param Lifetime Split Aug 15, 2021
@bors bors bot closed this Aug 15, 2021
geom3trik added a commit to geom3trik/bevy that referenced this pull request Sep 30, 2021
bors bot pushed a commit that referenced this pull request Oct 15, 2021
#2605 changed the lifetime annotations on `get_component` introducing unsoundness as you could keep the returned borrow even after using the query.

Example unsoundness:
```rust
use bevy::prelude::*;

fn main() {
    App::new()
        .add_startup_system(startup)
        .add_system(unsound)
        .run();
}

#[derive(Debug, Component, PartialEq, Eq)]
struct Foo(Vec<u32>);

fn startup(mut c: Commands) {
    let e = c.spawn().insert(Foo(vec![10])).id();
    c.insert_resource(e);
}

fn unsound(mut q: Query<&mut Foo>, res: Res<Entity>) {
    let foo = q.get_component::<Foo>(*res).unwrap();
    let mut foo2 = q.iter_mut().next().unwrap();

    let first_elem = &foo.0[0];
    for _ in 0..16 {
        foo2.0.push(12);
    }
    dbg!(*first_elem);
}
```
output:
`[src/main.rs:26] *first_elem = 0`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Code-Quality A section of code that is hard to understand or change M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants