Skip to content

[Merged by Bors] - Allow returning a value from EntityMut::world_scope - #7385

Closed
joseph-gio wants to merge 4 commits into
bevyengine:mainfrom
joseph-gio:world-scope-return
Closed

joseph-gio wants to merge 4 commits into
bevyengine:mainfrom
joseph-gio:world-scope-return

Conversation

@joseph-gio

Copy link
Copy Markdown
Member

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.

@joseph-gio joseph-gio added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events labels Jan 27, 2023

@james7132 james7132 left a comment

Copy link
Copy Markdown
Member

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
Copy Markdown
Member

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

@joseph-gio

joseph-gio commented Jan 27, 2023 •

Copy link
Copy Markdown
Member Author

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

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

Copy link
Copy Markdown
Member

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
@joseph-gio
joseph-gio 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