bevyengine / bevyengine/bevy

Upstream `bevy_command_non_send`

Open
#12,795 3 comments 0 reactions 0 assignees View on GitHub
A-ECS C-Feature C-Usability S-Blocked
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 22h
Merged PRs (30d)
161

Description

## 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.

[`Commands::insert_resource`]: https://docs.rs/bevy/0.13.1/bevy/ecs/system/struct.Commands.html#method.insert_resource

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

[`Command`]: https://docs.rs/bevy/0.13.1/bevy/ecs/system/trait.Command.html

## 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:

[`bevy_command_non_send`]: https://github.com/BD103/bevy_command_non_send

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

The exact implementation can be viewed [here](https://github.com/BD103/bevy_command_non_send/blob/main/src/lib.rs). `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:

```rust
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](https://bd103.github.io/bevy_command_non_send/bevy_command_non_send/). It comes complete with rigorous documentation and a test suite, which you can view by browsing [the source](https://bd103.github.io/bevy_command_non_send/src/bevy_command_non_send/lib.rs.html).

Though I say "upstream" in this feature request, I really mean taking heavy inspiration. I don't intend on the [functions](https://bd103.github.io/bevy_command_non_send/bevy_command_non_send/index.html#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`](https://bd103.github.io/bevy_command_non_send/bevy_command_non_send/trait.CommandsExt.html) 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`](https://docs.rs/cpal/latest/cpal/platform/struct.Stream.html) 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. :)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.