Skip to content

Upstream bevy_command_non_send #12795

@BD103

Description

@BD103

What problem does this solve or what need does it fill?

The only current way to insert or remove a non-send resource while the App is running is to create a system that takes &mut World. &mut World systems have exclusive access to the world, which reduces the amount of parallelism that can be achieved.

For comparison, Resource has Commands::insert_resource, which allows deferring all resource operations to occur at the same time.

I believe the historical reason non-send operations were excluded from Commands is because Command has a Send bound.

What solution would you like?

I recently created a library named bevy_command_non_send, which adds methods for interacting with non-send resources using commands. I believe it would be useful to others and worth upstreaming into the main Commands implementation. This would involve introducing 3 new methods:

impl Commands<'_, '_> {
    fn init_non_send_resource<R: FromWorld + 'static>(&mut self);
    fn insert_non_send_resource<F, R>(&mut self, func: F)
       where F: FnOnce() -> R + Send + 'static,
             R: 'static;
    fn remove_non_send_resource<R: 'static>(&mut self);
}

The exact implementation can be viewed here. init_non_send_resource and remove_non_send_resource are simple enough because the system never gains access to the non-send resource, so there are no issues with the data crossing between threads. insert_non_send_resource is the only real issue, since it allows the system to construct the non-send resource before inserting it into the World.

My solution to this was to defer creating the resource until it can be made on the main thread. insert_non_send_resource will take a closure that is Send, but can return a non-send resource. Calling it would look like this:

struct MyNonSend(*const u8);

fn create_my_non_send(mut commands: Commands) {
    commands.insert_non_send_resource(|| {
        MyNonSend(std::ptr::null())
    });
}

What alternative(s) have you considered?

I believe at least init_non_send_resource and remove_non_send_resource should be implemented. I can understand if insert_non_send_resource requires further consideration, since it is the most complicated of proposed API.

I think non-send resources are a bit of a sore spot right now, but supporting them with Commands will make them more on-pace with the universal Resource. I cannot think of any clear alternatives, but feel free to add your thoughts in a comment if I missed anything.

Additional context

For your convenience, I have published the documentation for bevy_command_non_send here. It comes complete with rigorous documentation and a test suite, which you can view by browsing the source.

Though I say "upstream" in this feature request, I really mean taking heavy inspiration. I don't intend on the functions to be made public in bevy_ecs, since other commands that are built-in are not as well. Instead, I want the functionality of CommandsExt to be directly implemented for Commands.

I originally came up with this idea while working with integrating cpal with Bevy. I was trying to get a microphone working, which required keeping a non-send Stream type alive for the course of the app. This would happen during Startup, but I did not want to claim the entire &mut World for myself. Commands is a great solution, but lacking in this particular area. :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ECSEntities, components, systems, and eventsC-FeatureA new feature, making something new possibleC-UsabilityA targeted quality-of-life change that makes Bevy easier to useS-BlockedThis cannot move forward until something else changes

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions