Skip to content

Commit 981b703

Browse files
committed
feat: non-send commands
1 parent e9e20c8 commit 981b703

File tree

1 file changed

+120
-0
lines changed
  • crates/bevy_ecs/src/system/commands

1 file changed

+120
-0
lines changed

crates/bevy_ecs/src/system/commands/mod.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,102 @@ impl<'w, 's> Commands<'w, 's> {
493493
self.queue.push(remove_resource::<R>);
494494
}
495495

496+
/// Pushes a [`Command`] to the queue for inserting a non-[`Send`] resource in the [`World`] with an inferred value.
497+
///
498+
/// See [`World::init_non_send_resource`] for more details.
499+
///
500+
/// # Example
501+
///
502+
/// ```
503+
/// # use bevy::prelude::*;
504+
/// #
505+
/// struct MyNonSend(*const u8);
506+
///
507+
/// impl Default for MyNonSend {
508+
/// fn default() -> Self {
509+
/// MyNonSend(std::ptr::null())
510+
/// }
511+
/// }
512+
///
513+
/// fn create_my_non_send(mut commands: Commands) {
514+
/// commands.init_non_send_resource::<MyNonSend>();
515+
/// }
516+
/// #
517+
/// # App::new()
518+
/// # .add_systems(Startup, (create_my_non_send, check).chain())
519+
/// # .run();
520+
/// #
521+
/// # fn check(my_non_send: NonSend<MyNonSend>) {
522+
/// # assert!(my_non_send.0.is_null());
523+
/// # }
524+
/// ```
525+
pub fn init_non_send_resource<R: FromWorld + 'static>(&mut self) {
526+
self.queue.push(init_non_send_resource::<R>);
527+
}
528+
529+
/// Pushes a [`Command`] to the queue for inserting a non-[`Send`] resource in the [`World`] with an specific value.
530+
///
531+
/// Note that this command takes a closure, not a value. This closure is executed on the main thread and should return
532+
/// the value of the non-[`Send`] resource. The closure itself must be [`Send`], but its returned value does not need to be.
533+
///
534+
/// See [`World::insert_non_send_resource`] for more details.
535+
///
536+
/// # Example
537+
///
538+
/// ```
539+
/// # use bevy::prelude::*;
540+
/// #
541+
/// struct MyNonSend(*const u8);
542+
///
543+
/// fn create_my_non_send(mut commands: Commands) {
544+
/// // Note that this is a closure:
545+
/// commands.insert_non_send_resource(|| {
546+
/// MyNonSend(std::ptr::null())
547+
/// });
548+
/// }
549+
/// #
550+
/// # App::new()
551+
/// # .add_systems(Startup, (create_my_non_send, check).chain())
552+
/// # .run();
553+
/// #
554+
/// # fn check(my_non_send: NonSend<MyNonSend>) {
555+
/// # assert!(my_non_send.0.is_null());
556+
/// # }
557+
/// ```
558+
pub fn insert_non_send_resource<F, R>(&mut self, func: F)
559+
where
560+
F: FnOnce() -> R + Send + 'static,
561+
R: 'static,
562+
{
563+
self.queue.push(insert_non_send_resource(func));
564+
}
565+
566+
/// Pushes a [`Command`] to the queue for removing a non-[`Send`] resource from the [`World`].
567+
///
568+
/// See [`World::remove_non_send_resource`] for more details.
569+
///
570+
/// ```
571+
/// # use bevy::prelude::*;
572+
/// #
573+
/// struct MyNonSend(*const u8);
574+
///
575+
/// fn remove_my_non_send(mut commands: Commands) {
576+
/// commands.remove_non_send_resource::<MyNonSend>();
577+
/// }
578+
/// #
579+
/// # App::new()
580+
/// # .insert_non_send_resource(MyNonSend(std::ptr::null()))
581+
/// # .add_systems(Startup, (remove_my_non_send, check).chain())
582+
/// # .run();
583+
/// #
584+
/// # fn check(my_non_send: Option<NonSend<MyNonSend>>) {
585+
/// # assert!(my_non_send.is_none());
586+
/// # }
587+
/// ```
588+
pub fn remove_non_send_resource<R: 'static>(&mut self) {
589+
self.queue.push(remove_non_send_resource::<R>);
590+
}
591+
496592
/// Runs the system corresponding to the given [`SystemId`].
497593
/// Systems are ran in an exclusive and single threaded way.
498594
/// Running slow systems can become a bottleneck.
@@ -1134,6 +1230,30 @@ fn remove_resource<R: Resource>(world: &mut World) {
11341230
world.remove_resource::<R>();
11351231
}
11361232

1233+
/// A [`Command`] that inserts a non-[`Send`] resource `R` into the world using
1234+
/// a value created with the [`FromWorld`] trait.
1235+
fn init_non_send_resource<R: FromWorld + 'static>(world: &mut World) {
1236+
world.init_non_send_resource::<R>();
1237+
}
1238+
1239+
/// A [`Command`] that removes a non-[`Send`] resource `R` from the world.
1240+
fn remove_non_send_resource<R: 'static>(world: &mut World) {
1241+
world.remove_non_send_resource::<R>();
1242+
}
1243+
1244+
/// A [`Command`] that inserts a non-[`Send`] resource into the world by calling
1245+
/// `func` on the main thread and inserting its returned value.
1246+
fn insert_non_send_resource<F, R>(func: F) -> impl Command
1247+
where
1248+
// `R` is not `Send`, but the function is!
1249+
F: FnOnce() -> R + Send + 'static,
1250+
R: 'static,
1251+
{
1252+
move |world: &mut World| {
1253+
world.insert_non_send_resource((func)());
1254+
}
1255+
}
1256+
11371257
/// [`EntityCommand`] to log the components of a given entity. See [`EntityCommands::log_components`].
11381258
fn log_components(entity: Entity, world: &mut World) {
11391259
let debug_infos: Vec<_> = world

0 commit comments

Comments
 (0)