-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Description
What problem does this solve or what need does it fill?
This issue is a replacement and extension of #17517, which attempts to solve the same problem by creating an optionally !Send World using generics. The problems with !Send resources listed in that issue are the same issues we are trying to solve here:
NonSendresources are confusing (they don't implResource!), badly named (#13481), require API duplication and hamper us when implementing valuable new ECS features like #17485.That said, not everything we might want to stick in and access from the ECS is threadsafe! Audio, window handles, scripting language integrations and more are all inherently !Send. Simply removing them is not an option. Ultimately, the problem is that our model here is muddy, hampering our ability to teach, reason about and safely improve these areas of code.
Simultaneously, users have requested support for
!Sendcomponents, particularly for use with web or scripting languages.
Through discussion on Discord and experimentation, we have determined that making World optionally !Send using generics is a bit messy and hacky, and we want a more long-term solution rather than a work-around.
In summary, we need to solve these problems:
- Eliminate
!Sendresources so the resources-as-components effort can be unblocked, and... - Find a place to store the data from those
!Sendresources while still supporting their!Sendstatus
What solution would you like?
Short term
Short term, we can replace !Send resources with thread_local!. This will unblock the resources-as-components effort. However, thread_local! requires std, which conflicts with Bevy's support for no_std, so this is not a good long-term solution.
Long term, @bushrat011899 brought up supporting both Send and !Send Worlds in the same binary. This would allow us to store the data that is currently in !Send resources in it's own !Send World as !Send components (since resources-as-components aims to remove the concept of resources altogether). If data needs to be shared from the Send World to the !Send World, @maniwani has suggested an API feature for sending functions to be run on other worlds. Usage might look something like this:
fn send_something_to_foo_world(
mut worlds: Worlds,
foo_world: Res<FooWorld>,
) {
let id: WorldId = *foo_world.0;
worlds.get_mut(id).block_on(move |mut world| {
// Something to run on the world with non-send Foo stuff
});
}Long term
The long-term plan looks like this:
- We make non-send resources
thread_local!so that resources-as-components is unblocked 🐢 Make!Sendresourcesthread_local!#17682 - Create inter-thread executor that allows a system to execute arbitrary code on the main thread. This is needed because currently the only way to ensure a System runs on the main thread is to use
!Sendsystem params, which is getting removed. We need to ensure anythread_local!s are running on the same thread they're created in. 🐢 Create inter-thread executor API #18172 - Create official support for
Sendand!SendWorldin a single binary a. More details to follow - Move
thread_local!global vars into a new!SendWorldas!SendcomponentsMore issues will be created to break this work out further## What alternative(s) have you considered? - Move main
Worldto a thread that is not main thread
Some ideas we have discussed:
- Only officially supporting
SendWorld(not supporting!SendWorld) and requiring that users store their own!Senddata. However, that would come at the cost of worse UX - Only supporting either a
SendWorldor a!SendWorldin the same binary, but not both simultaneously and using a separate data store for!Sendglobal data to replace our!Sendresource usage. However, this would require the creation of a new!Senddata store, which can be solved automatically by just creating support forSendand!SendWorlds to co-exist. Imo, it is cleaner to use a!SendWorldas a!Senddata store and doesn't require us to re-engineer something we already have implemented. Plus having aSendand!SendWorldco-exist in the same binary is more dynamically useful (it's more general and has potential to be more generally useful to end users and/or other bevy teams to solve a variety of other problems).
Why 🐢 turtle emoji?
Because that's the name of the Discord work group that discusses this issue. And turtles are cute.