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] - Allow returning a value from EntityMut::world_scope #7385

Closed
wants to merge 4 commits into from

Conversation

JoJoJet
Copy link
Member

@JoJoJet JoJoJet commented Jan 27, 2023

Objective

The function EntityMut::world_scope is a safe abstraction that allows you to temporarily get mutable access to the underlying World of an EntityMut. This function is purely stateful, meaning it is not easily possible to return a value from it.

Solution

Allow returning a computed value from the closure. This is similar to how World::resource_scope works.


Changelog

  • The function EntityMut::world_scope now allows returning a value from the immediately-computed closure.

@JoJoJet JoJoJet added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events labels Jan 27, 2023
Copy link
Member

@james7132 james7132 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding the doctest example!

@alice-i-cecile
Copy link
Member

Definitely better. Can we remove world_mut in this PR, or are there still use cases?

@JoJoJet
Copy link
Member Author

JoJoJet commented Jan 27, 2023

Definitely better. Can we remove world_mut in this PR, or are there still use cases?

world_scope() encapsulates the pattern of modifying the world and then updating the EntityMut's cached EntityLocation. However, the EntityLocation only sometimes needs to be updated, so world_scope() is slower than world_mut in some cases. I'm not sure if the difference is enough to matter, though.

@alice-i-cecile alice-i-cecile added C-Usability A targeted quality-of-life change that makes Bevy easier to use and removed C-Feature A new feature, making something new possible labels Jan 27, 2023
Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bors r+

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Jan 27, 2023
bors bot pushed a commit that referenced this pull request Jan 27, 2023
# Objective

The function `EntityMut::world_scope` is a safe abstraction that allows you to temporarily get mutable access to the underlying `World` of an `EntityMut`. This function is purely stateful, meaning it is not easily possible to return a value from it.

## Solution

Allow returning a computed value from the closure. This is similar to how `World::resource_scope` works.

---

## Changelog

- The function `EntityMut::world_scope` now allows returning a value from the immediately-computed closure.
@bors bors bot changed the title Allow returning a value from EntityMut::world_scope [Merged by Bors] - Allow returning a value from EntityMut::world_scope Jan 27, 2023
@bors bors bot closed this Jan 27, 2023
@JoJoJet JoJoJet deleted the world-scope-return branch January 27, 2023 20:00
bors bot pushed a commit that referenced this pull request Jan 29, 2023
# Objective

Found while working on #7385.

The struct `EntityMut` has the safety invariant that it's cached `EntityLocation` must always accurately specify where the entity is stored. Thus, any time its location might be invalidated (such as by calling `EntityMut::world_mut` and moving archetypes), the cached location *must* be updated by calling `EntityMut::update_location`.

The method `world_scope` encapsulates this pattern in safe API by requiring world mutations to be done in a closure, after which `update_location` will automatically be called. However, this method has a soundness hole: if a panic occurs within the closure, then `update_location` will never get called. If the panic is caught in an outer scope, then the `EntityMut` will be left with an outdated location, which is undefined behavior.

An example of this can be seen in the unit test `entity_mut_world_scope_panic`, which has been added to this PR as a regression test. Without the other changes in this PR, that test will invoke undefined behavior in safe code.

## Solution

Call `EntityMut::update_location()` from within a `Drop` impl, which ensures that it will get executed even if `EntityMut::world_scope` unwinds.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

The function `EntityMut::world_scope` is a safe abstraction that allows you to temporarily get mutable access to the underlying `World` of an `EntityMut`. This function is purely stateful, meaning it is not easily possible to return a value from it.

## Solution

Allow returning a computed value from the closure. This is similar to how `World::resource_scope` works.

---

## Changelog

- The function `EntityMut::world_scope` now allows returning a value from the immediately-computed closure.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

Found while working on bevyengine#7385.

The struct `EntityMut` has the safety invariant that it's cached `EntityLocation` must always accurately specify where the entity is stored. Thus, any time its location might be invalidated (such as by calling `EntityMut::world_mut` and moving archetypes), the cached location *must* be updated by calling `EntityMut::update_location`.

The method `world_scope` encapsulates this pattern in safe API by requiring world mutations to be done in a closure, after which `update_location` will automatically be called. However, this method has a soundness hole: if a panic occurs within the closure, then `update_location` will never get called. If the panic is caught in an outer scope, then the `EntityMut` will be left with an outdated location, which is undefined behavior.

An example of this can be seen in the unit test `entity_mut_world_scope_panic`, which has been added to this PR as a regression test. Without the other changes in this PR, that test will invoke undefined behavior in safe code.

## Solution

Call `EntityMut::update_location()` from within a `Drop` impl, which ensures that it will get executed even if `EntityMut::world_scope` unwinds.
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-Usability A targeted quality-of-life change that makes Bevy easier to use S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants