Create new NonSendMarker (#18301)

# Objective

Create new `NonSendMarker` that does not depend on `NonSend`.

Required, in order to accomplish #17682. In that issue, we are trying to
replace `!Send` resources with `thread_local!` in order to unblock the
resources-as-components effort. However, when we remove all the `!Send`
resources from a system, that allows the system to run on a thread other
than the main thread, which is against the design of the system. So this
marker gives us the control to require a system to run on the main
thread without depending on `!Send` resources.

## Solution

Create a new `NonSendMarker` to replace the existing one that does not
depend on `NonSend`.

## Testing

Other than running tests, I ran a few examples:
- `window_resizing`
- `wireframe`
- `volumetric_fog` (looks so cool)
- `rotation`
- `button`

There is a Mac/iOS-specific change and I do not have a Mac or iOS device
to test it. I am doubtful that it would cause any problems for 2
reasons:
1. The change is the same as the non-wasm change which I did test
2. The Pixel Eagle tests run Mac tests

But it wouldn't hurt if someone wanted to spin up an example that
utilizes the `bevy_render` crate, which is where the Mac/iSO change was.

## Migration Guide

If `NonSendMarker` is being used from `bevy_app::prelude::*`, replace it
with `bevy_ecs::system::NonSendMarker` or use it from
`bevy_ecs::prelude::*`. In addition to that, `NonSendMarker` does not
need to be wrapped like so:
```rust
fn my_system(_non_send_marker: Option<NonSend<NonSendMarker>>) {
    ...
}
```

Instead, it can be used without any wrappers:
```rust
fn my_system(_non_send_marker: NonSendMarker) {
    ...
}
```

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
This commit is contained in:
Joshua Holmes 2025-03-23 14:37:40 -07:00 committed by GitHub
parent 694db96ab8
commit 8d9f948684
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 35 additions and 11 deletions

View File

@ -58,6 +58,6 @@ pub mod prelude {
RunFixedMainLoopSystem, SpawnScene, Startup, Update,
},
sub_app::SubApp,
NonSendMarker, Plugin, PluginGroup, TaskPoolOptions, TaskPoolPlugin,
Plugin, PluginGroup, TaskPoolOptions, TaskPoolPlugin,
};
}

View File

@ -3,18 +3,19 @@ use crate::{App, Plugin};
use alloc::string::ToString;
use bevy_platform_support::sync::Arc;
use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder};
use core::{fmt::Debug, marker::PhantomData};
use core::fmt::Debug;
use log::trace;
cfg_if::cfg_if! {
if #[cfg(not(all(target_arch = "wasm32", feature = "web")))] {
use {crate::Last, bevy_ecs::prelude::NonSend, bevy_tasks::tick_global_task_pools_on_main_thread};
use {crate::Last, bevy_tasks::tick_global_task_pools_on_main_thread};
use bevy_ecs::system::NonSendMarker;
/// A system used to check and advanced our task pools.
///
/// Calls [`tick_global_task_pools_on_main_thread`],
/// and uses [`NonSendMarker`] to ensure that this system runs on the main thread
fn tick_global_task_pools(_main_thread_marker: Option<NonSend<NonSendMarker>>) {
fn tick_global_task_pools(_main_thread_marker: NonSendMarker) {
tick_global_task_pools_on_main_thread();
}
}
@ -36,8 +37,6 @@ impl Plugin for TaskPoolPlugin {
_app.add_systems(Last, tick_global_task_pools);
}
}
/// A dummy type that is [`!Send`](Send), to force systems to run on the main thread.
pub struct NonSendMarker(PhantomData<*mut ()>);
/// Defines a simple way to determine how many threads to use given the number of remaining cores
/// and number of total cores

View File

@ -91,8 +91,8 @@ pub mod prelude {
spawn::{Spawn, SpawnRelated},
system::{
Command, Commands, Deferred, EntityCommand, EntityCommands, In, InMut, InRef,
IntoSystem, Local, NonSend, NonSendMut, ParamSet, Populated, Query, ReadOnlySystem,
Res, ResMut, Single, System, SystemIn, SystemInput, SystemParamBuilder,
IntoSystem, Local, NonSend, NonSendMarker, NonSendMut, ParamSet, Populated, Query,
ReadOnlySystem, Res, ResMut, Single, System, SystemIn, SystemInput, SystemParamBuilder,
SystemParamFunction, WithParamWarnPolicy,
},
world::{

View File

@ -1420,6 +1420,33 @@ unsafe impl<T: SystemBuffer> SystemParam for Deferred<'_, T> {
}
}
/// A dummy type that is [`!Send`](Send), to force systems to run on the main thread.
pub struct NonSendMarker;
// SAFETY: No world access.
unsafe impl SystemParam for NonSendMarker {
type State = ();
type Item<'w, 's> = Self;
#[inline]
fn init_state(_world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
system_meta.set_non_send();
}
#[inline]
unsafe fn get_param<'world, 'state>(
_state: &'state mut Self::State,
_system_meta: &SystemMeta,
_world: UnsafeWorldCell<'world>,
_change_tick: Tick,
) -> Self::Item<'world, 'state> {
Self
}
}
// SAFETY: Does not read any world state
unsafe impl ReadOnlySystemParam for NonSendMarker {}
/// Shared borrow of a non-[`Send`] resource.
///
/// Only `Send` resources may be accessed with the [`Res`] [`SystemParam`]. In case that the

View File

@ -304,9 +304,7 @@ const DEFAULT_DESIRED_MAXIMUM_FRAME_LATENCY: u32 = 2;
pub fn create_surfaces(
// By accessing a NonSend resource, we tell the scheduler to put this system on the main thread,
// which is necessary for some OS's
#[cfg(any(target_os = "macos", target_os = "ios"))] _marker: Option<
NonSend<bevy_app::NonSendMarker>,
>,
#[cfg(any(target_os = "macos", target_os = "ios"))] _marker: NonSendMarker,
windows: Res<ExtractedWindows>,
mut window_surfaces: ResMut<WindowSurfaces>,
render_instance: Res<RenderInstance>,