From 503b861e3a76fc3bdc00570f7e0bb8dc1a825679 Mon Sep 17 00:00:00 2001 From: Pixelstorm Date: Mon, 25 Sep 2023 20:59:50 +0100 Subject: [PATCH] Allow using async_io::block_on in bevy_tasks (#9626) # Objective Fixes #9625 ## Solution Adds `async-io` as an optional dependency of `bevy_tasks`. When enabled, this causes calls to `futures_lite::future::block_on` to be replaced with calls to `async_io::block_on`. --- ## Changelog - Added a new `async-io` feature to `bevy_tasks`. When enabled, this causes `bevy_tasks` to use `async-io`'s implemention of `block_on` instead of `futures-lite`'s implementation. You should enable this if you use `async-io` in your application. --- Cargo.toml | 3 +++ crates/bevy_asset/src/processor/mod.rs | 4 ++-- crates/bevy_internal/Cargo.toml | 1 + crates/bevy_tasks/Cargo.toml | 1 + crates/bevy_tasks/src/lib.rs | 6 ++++++ crates/bevy_tasks/src/task_pool.rs | 9 +++++---- docs/cargo_features.md | 1 + examples/async_tasks/async_compute.rs | 4 ++-- 8 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d06f251e49..4c417d9eb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -199,6 +199,9 @@ serialize = ["bevy_internal/serialize"] # Enables multithreaded parallelism in the engine. Disabling it forces all engine tasks to run on a single thread. multi-threaded = ["bevy_internal/multi-threaded"] +# Use async-io's implementation of block_on instead of futures-lite's implementation. This is preferred if your application uses async-io. +async-io = ["bevy_internal/async-io"] + # Wayland display server support wayland = ["bevy_internal/wayland"] diff --git a/crates/bevy_asset/src/processor/mod.rs b/crates/bevy_asset/src/processor/mod.rs index 2e67be23de..1228740bea 100644 --- a/crates/bevy_asset/src/processor/mod.rs +++ b/crates/bevy_asset/src/processor/mod.rs @@ -166,7 +166,7 @@ impl AssetProcessor { let processor = _processor.clone(); std::thread::spawn(move || { processor.process_assets(); - futures_lite::future::block_on(processor.listen_for_source_change_events()); + bevy_tasks::block_on(processor.listen_for_source_change_events()); }); } } @@ -190,7 +190,7 @@ impl AssetProcessor { }); // This must happen _after_ the scope resolves or it will happen "too early" // Don't move this into the async scope above! process_assets is a blocking/sync function this is fine - futures_lite::future::block_on(self.finish_processing_assets()); + bevy_tasks::block_on(self.finish_processing_assets()); let end_time = std::time::Instant::now(); debug!("Processing finished in {:?}", end_time - start_time); } diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 1a71d43819..41b06c2311 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -63,6 +63,7 @@ shader_format_spirv = ["bevy_render/shader_format_spirv"] serialize = ["bevy_core/serialize", "bevy_input/serialize", "bevy_time/serialize", "bevy_window/serialize", "bevy_transform/serialize", "bevy_math/serialize", "bevy_scene?/serialize"] multi-threaded = ["bevy_asset/multi-threaded", "bevy_ecs/multi-threaded", "bevy_tasks/multi-threaded"] +async-io = ["bevy_tasks/async-io"] # Display server protocol support (X11 is enabled by default) wayland = ["bevy_winit/wayland"] diff --git a/crates/bevy_tasks/Cargo.toml b/crates/bevy_tasks/Cargo.toml index d7d2f8fb6c..c4607fbcf8 100644 --- a/crates/bevy_tasks/Cargo.toml +++ b/crates/bevy_tasks/Cargo.toml @@ -15,6 +15,7 @@ multi-threaded = [] futures-lite = "1.4.0" async-executor = "1.3.0" async-channel = "1.4.2" +async-io = { version = "1.13.0", optional = true } async-task = "4.2.0" concurrent-queue = "2.0.0" diff --git a/crates/bevy_tasks/src/lib.rs b/crates/bevy_tasks/src/lib.rs index b97d2e9df4..d4b68e2096 100644 --- a/crates/bevy_tasks/src/lib.rs +++ b/crates/bevy_tasks/src/lib.rs @@ -28,6 +28,11 @@ mod thread_executor; #[cfg(all(not(target_arch = "wasm32"), feature = "multi-threaded"))] pub use thread_executor::{ThreadExecutor, ThreadExecutorTicker}; +#[cfg(feature = "async-io")] +pub use async_io::block_on; +#[cfg(not(feature = "async-io"))] +pub use futures_lite::future::block_on; + mod iter; pub use iter::ParallelIterator; @@ -35,6 +40,7 @@ pub use iter::ParallelIterator; pub mod prelude { #[doc(hidden)] pub use crate::{ + block_on, iter::ParallelIterator, slice::{ParallelSlice, ParallelSliceMut}, usages::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}, diff --git a/crates/bevy_tasks/src/task_pool.rs b/crates/bevy_tasks/src/task_pool.rs index 90afe4b4bc..5562a5abc0 100644 --- a/crates/bevy_tasks/src/task_pool.rs +++ b/crates/bevy_tasks/src/task_pool.rs @@ -9,9 +9,10 @@ use std::{ use async_task::FallibleTask; use concurrent_queue::ConcurrentQueue; -use futures_lite::{future, FutureExt}; +use futures_lite::FutureExt; use crate::{ + block_on, thread_executor::{ThreadExecutor, ThreadExecutorTicker}, Task, }; @@ -176,7 +177,7 @@ impl TaskPool { local_executor.tick().await; } }; - future::block_on(ex.run(tick_forever.or(shutdown_rx.recv()))) + block_on(ex.run(tick_forever.or(shutdown_rx.recv()))) }); if let Ok(value) = res { // Use unwrap_err because we expect a Closed error @@ -379,7 +380,7 @@ impl TaskPool { if spawned.is_empty() { Vec::new() } else { - future::block_on(async move { + block_on(async move { let get_results = async { let mut results = Vec::with_capacity(spawned.len()); while let Ok(task) = spawned.pop() { @@ -661,7 +662,7 @@ where T: 'scope, { fn drop(&mut self) { - future::block_on(async { + block_on(async { while let Ok(task) = self.spawned.pop() { task.cancel().await; } diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 9c7ee1701f..f3d618fb0d 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -43,6 +43,7 @@ The default feature set enables most of the expected features of a game engine, |feature name|description| |-|-| |accesskit_unix|Enable AccessKit on Unix backends (currently only works with experimental screen readers and forks.)| +|async-io|Use async-io's implementation of block_on instead of futures-lite's implementation. This is preferred if your application uses async-io.| |basis-universal|Basis Universal compressed texture support| |bevy_ci_testing|Enable systems that allow for automated testing on CI| |bevy_dynamic_plugin|Plugin for dynamic loading (using [libloading](https://crates.io/crates/libloading))| diff --git a/examples/async_tasks/async_compute.rs b/examples/async_tasks/async_compute.rs index 7ab7938097..9fad4d30a9 100644 --- a/examples/async_tasks/async_compute.rs +++ b/examples/async_tasks/async_compute.rs @@ -3,7 +3,7 @@ use bevy::{ prelude::*, - tasks::{AsyncComputeTaskPool, Task}, + tasks::{block_on, AsyncComputeTaskPool, Task}, }; use futures_lite::future; use rand::Rng; @@ -88,7 +88,7 @@ fn handle_tasks( box_material_handle: Res, ) { for (entity, mut task) in &mut transform_tasks { - if let Some(transform) = future::block_on(future::poll_once(&mut task.0)) { + if let Some(transform) = block_on(future::poll_once(&mut task.0)) { // Add our new PbrBundle of components to our tagged entity commands.entity(entity).insert(PbrBundle { mesh: box_mesh_handle.clone(),