diff --git a/crates/bevy_tasks/Cargo.toml b/crates/bevy_tasks/Cargo.toml index e915cd941f57f..00ec38e4005c2 100644 --- a/crates/bevy_tasks/Cargo.toml +++ b/crates/bevy_tasks/Cargo.toml @@ -9,14 +9,57 @@ license = "MIT OR Apache-2.0" keywords = ["bevy"] [features] -multi_threaded = ["dep:async-channel", "dep:concurrent-queue"] +default = ["std", "async_executor"] +std = [ + "futures-lite/std", + "async-task/std", + "spin/std", + "edge-executor?/std", + "portable-atomic-util?/std", +] +multi_threaded = ["std", "dep:async-channel", "dep:concurrent-queue"] +async_executor = ["std", "dep:async-executor"] +edge_executor = ["dep:edge-executor"] +critical-section = [ + "dep:critical-section", + "edge-executor?/critical-section", + "portable-atomic?/critical-section", +] +portable-atomic = [ + "dep:portable-atomic", + "dep:portable-atomic-util", + "edge-executor?/portable-atomic", + "async-task/portable-atomic", + "spin/portable_atomic", +] [dependencies] -futures-lite = "2.0.1" -async-executor = "1.11" +futures-lite = { version = "2.0.1", default-features = false, features = [ + "alloc", +] } +async-task = { version = "4.4.0", default-features = false } +spin = { version = "0.9.8", default-features = false, features = [ + "spin_mutex", + "rwlock", + "once", +] } +derive_more = { version = "1", default-features = false, features = [ + "deref", + "deref_mut", +] } + +async-executor = { version = "1.11", optional = true } +edge-executor = { version = "0.4.1", default-features = false, optional = true } async-channel = { version = "2.3.0", optional = true } async-io = { version = "2.0.0", optional = true } concurrent-queue = { version = "2.0.0", optional = true } +critical-section = { version = "1.2.0", optional = true } +portable-atomic = { version = "1", default-features = false, features = [ + "fallback", +], optional = true } +portable-atomic-util = { version = "0.2.4", features = [ + "alloc", +], optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen-futures = "0.4" diff --git a/crates/bevy_tasks/README.md b/crates/bevy_tasks/README.md index 91ac95dce8a27..2af6a606f65fa 100644 --- a/crates/bevy_tasks/README.md +++ b/crates/bevy_tasks/README.md @@ -34,6 +34,10 @@ The determining factor for what kind of work should go in each pool is latency r await receiving data from somewhere (i.e. disk) and signal other systems when the data is ready for consumption. (likely via channels) +## `no_std` Support + +To enable `no_std` support in this crate, you will need to disable default features, and enable the `edge_executor` and `critical-section` features. For platforms without full support for Rust atomics, you may also need to enable the `portable-atomic` feature. + [bevy]: https://bevyengine.org [rayon]: https://github.com/rayon-rs/rayon [async-executor]: https://github.com/stjepang/async-executor diff --git a/crates/bevy_tasks/src/executor.rs b/crates/bevy_tasks/src/executor.rs new file mode 100644 index 0000000000000..04667c1b16d59 --- /dev/null +++ b/crates/bevy_tasks/src/executor.rs @@ -0,0 +1,84 @@ +//! Provides a fundamental executor primitive appropriate for the target platform +//! and feature set selected. +//! By default, the `async_executor` feature will be enabled, which will rely on +//! [`async-executor`] for the underlying implementation. This requires `std`, +//! so is not suitable for `no_std` contexts. Instead, you must use `edge_executor`, +//! which relies on the alternate [`edge-executor`] backend. +//! +//! [`async-executor`]: https://crates.io/crates/async-executor +//! [`edge-executor`]: https://crates.io/crates/edge-executor + +pub use async_task::Task; +use core::{ + fmt, + panic::{RefUnwindSafe, UnwindSafe}, +}; +use derive_more::{Deref, DerefMut}; + +#[cfg(feature = "multi_threaded")] +pub use async_task::FallibleTask; + +#[cfg(feature = "async_executor")] +type ExecutorInner<'a> = async_executor::Executor<'a>; + +#[cfg(feature = "async_executor")] +type LocalExecutorInner<'a> = async_executor::LocalExecutor<'a>; + +#[cfg(all(not(feature = "async_executor"), feature = "edge_executor"))] +type ExecutorInner<'a> = edge_executor::Executor<'a, 64>; + +#[cfg(all(not(feature = "async_executor"), feature = "edge_executor"))] +type LocalExecutorInner<'a> = edge_executor::LocalExecutor<'a, 64>; + +/// Wrapper around a multi-threading-aware async executor. +/// Spawning will generally require tasks to be `Send` and `Sync` to allow multiple +/// threads to send/receive/advance tasks. +/// +/// If you require an executor _without_ the `Send` and `Sync` requirements, consider +/// using [`LocalExecutor`] instead. +#[derive(Deref, DerefMut, Default)] +pub struct Executor<'a>(ExecutorInner<'a>); + +/// Wrapper around a single-threaded async executor. +/// Spawning wont generally require tasks to be `Send` and `Sync`, at the cost of +/// this executor itself not being `Send` or `Sync`. This makes it unsuitable for +/// global statics. +/// +/// If need to store an executor in a global static, or send across threads, +/// consider using [`Executor`] instead. +#[derive(Deref, DerefMut, Default)] +pub struct LocalExecutor<'a>(LocalExecutorInner<'a>); + +impl Executor<'_> { + /// Construct a new [`Executor`] + #[allow(dead_code, reason = "not all feature flags require this function")] + pub const fn new() -> Self { + Self(ExecutorInner::new()) + } +} + +impl LocalExecutor<'_> { + /// Construct a new [`LocalExecutor`] + #[allow(dead_code, reason = "not all feature flags require this function")] + pub const fn new() -> Self { + Self(LocalExecutorInner::new()) + } +} + +impl UnwindSafe for Executor<'_> {} +impl RefUnwindSafe for Executor<'_> {} + +impl UnwindSafe for LocalExecutor<'_> {} +impl RefUnwindSafe for LocalExecutor<'_> {} + +impl fmt::Debug for Executor<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Executor").finish() + } +} + +impl fmt::Debug for LocalExecutor<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("LocalExecutor").finish() + } +} diff --git a/crates/bevy_tasks/src/iter/adapters.rs b/crates/bevy_tasks/src/iter/adapters.rs index 617f5bdf868ca..a3166120790c9 100644 --- a/crates/bevy_tasks/src/iter/adapters.rs +++ b/crates/bevy_tasks/src/iter/adapters.rs @@ -1,5 +1,7 @@ use crate::iter::ParallelIterator; +/// Chains two [`ParallelIterator`]s `T` and `U`, first returning +/// batches from `T`, and then from `U`. #[derive(Debug)] pub struct Chain { pub(crate) left: T, @@ -24,6 +26,7 @@ where } } +/// Maps a [`ParallelIterator`] `P` using the provided function `F`. #[derive(Debug)] pub struct Map { pub(crate) iter: P, @@ -41,6 +44,7 @@ where } } +/// Filters a [`ParallelIterator`] `P` using the provided predicate `F`. #[derive(Debug)] pub struct Filter { pub(crate) iter: P, @@ -60,6 +64,7 @@ where } } +/// Filter-maps a [`ParallelIterator`] `P` using the provided function `F`. #[derive(Debug)] pub struct FilterMap { pub(crate) iter: P, @@ -77,6 +82,7 @@ where } } +/// Flat-maps a [`ParallelIterator`] `P` using the provided function `F`. #[derive(Debug)] pub struct FlatMap { pub(crate) iter: P, @@ -98,6 +104,7 @@ where } } +/// Flattens a [`ParallelIterator`] `P`. #[derive(Debug)] pub struct Flatten

{ pub(crate) iter: P, @@ -117,6 +124,8 @@ where } } +/// Fuses a [`ParallelIterator`] `P`, ensuring once it returns [`None`] once, it always +/// returns [`None`]. #[derive(Debug)] pub struct Fuse

{ pub(crate) iter: Option

, @@ -138,6 +147,7 @@ where } } +/// Inspects a [`ParallelIterator`] `P` using the provided function `F`. #[derive(Debug)] pub struct Inspect { pub(crate) iter: P, @@ -155,6 +165,7 @@ where } } +/// Copies a [`ParallelIterator`] `P`'s returned values. #[derive(Debug)] pub struct Copied

{ pub(crate) iter: P, @@ -171,6 +182,7 @@ where } } +/// Clones a [`ParallelIterator`] `P`'s returned values. #[derive(Debug)] pub struct Cloned

{ pub(crate) iter: P, @@ -187,6 +199,7 @@ where } } +/// Cycles a [`ParallelIterator`] `P` indefinitely. #[derive(Debug)] pub struct Cycle

{ pub(crate) iter: P, diff --git a/crates/bevy_tasks/src/iter/mod.rs b/crates/bevy_tasks/src/iter/mod.rs index 3910166904856..4462fa95abd22 100644 --- a/crates/bevy_tasks/src/iter/mod.rs +++ b/crates/bevy_tasks/src/iter/mod.rs @@ -1,4 +1,5 @@ use crate::TaskPool; +use alloc::vec::Vec; mod adapters; pub use adapters::*; diff --git a/crates/bevy_tasks/src/lib.rs b/crates/bevy_tasks/src/lib.rs index 1d6d35664ed0e..3f3db301bbb00 100644 --- a/crates/bevy_tasks/src/lib.rs +++ b/crates/bevy_tasks/src/lib.rs @@ -4,9 +4,12 @@ html_logo_url = "https://bevyengine.org/assets/icon.png", html_favicon_url = "https://bevyengine.org/assets/icon.png" )] +#![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; +mod executor; + mod slice; pub use slice::{ParallelSlice, ParallelSliceMut}; @@ -37,9 +40,9 @@ mod thread_executor; #[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] pub use thread_executor::{ThreadExecutor, ThreadExecutorTicker}; -#[cfg(feature = "async-io")] +#[cfg(all(feature = "async-io", feature = "std"))] pub use async_io::block_on; -#[cfg(not(feature = "async-io"))] +#[cfg(all(not(feature = "async-io"), feature = "std"))] pub use futures_lite::future::block_on; pub use futures_lite::future::poll_once; @@ -54,13 +57,17 @@ pub use futures_lite; pub mod prelude { #[doc(hidden)] pub use crate::{ - block_on, iter::ParallelIterator, slice::{ParallelSlice, ParallelSliceMut}, usages::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}, }; + + #[cfg(feature = "std")] + #[doc(hidden)] + pub use crate::block_on; } +#[cfg(feature = "std")] use core::num::NonZero; /// Gets the logical CPU core count available to the current process. @@ -69,8 +76,18 @@ use core::num::NonZero; /// it will return a default value of 1 if it internally errors out. /// /// This will always return at least 1. +#[cfg(feature = "std")] pub fn available_parallelism() -> usize { std::thread::available_parallelism() .map(NonZero::::get) .unwrap_or(1) } + +/// Gets the logical CPU core count available to the current process. +/// +/// This will always return at least 1. +#[cfg(not(feature = "std"))] +pub fn available_parallelism() -> usize { + // Without access to std, assume a single thread is available + 1 +} diff --git a/crates/bevy_tasks/src/single_threaded_task_pool.rs b/crates/bevy_tasks/src/single_threaded_task_pool.rs index 054d22260eac4..51adc739c1f8c 100644 --- a/crates/bevy_tasks/src/single_threaded_task_pool.rs +++ b/crates/bevy_tasks/src/single_threaded_task_pool.rs @@ -1,12 +1,34 @@ -use alloc::{rc::Rc, sync::Arc}; +use alloc::{string::String, vec::Vec}; use core::{cell::RefCell, future::Future, marker::PhantomData, mem}; use crate::Task; +#[cfg(feature = "portable-atomic")] +use portable_atomic_util::Arc; + +#[cfg(not(feature = "portable-atomic"))] +use alloc::sync::Arc; + +#[cfg(feature = "std")] +use crate::executor::LocalExecutor; + +#[cfg(not(feature = "std"))] +use crate::executor::Executor as LocalExecutor; + +#[cfg(feature = "std")] thread_local! { - static LOCAL_EXECUTOR: async_executor::LocalExecutor<'static> = const { async_executor::LocalExecutor::new() }; + static LOCAL_EXECUTOR: LocalExecutor<'static> = const { LocalExecutor::new() }; } +#[cfg(not(feature = "std"))] +static LOCAL_EXECUTOR: LocalExecutor<'static> = const { LocalExecutor::new() }; + +#[cfg(feature = "std")] +type ScopeResult = alloc::rc::Rc>>; + +#[cfg(not(feature = "std"))] +type ScopeResult = Arc>>; + /// Used to create a [`TaskPool`]. #[derive(Debug, Default, Clone)] pub struct TaskPoolBuilder {} @@ -124,15 +146,13 @@ impl TaskPool { // Any usages of the references passed into `Scope` must be accessed through // the transmuted reference for the rest of this function. - let executor = &async_executor::LocalExecutor::new(); + let executor = &LocalExecutor::new(); // SAFETY: As above, all futures must complete in this function so we can change the lifetime - let executor: &'env async_executor::LocalExecutor<'env> = - unsafe { mem::transmute(executor) }; + let executor: &'env LocalExecutor<'env> = unsafe { mem::transmute(executor) }; - let results: RefCell>>>> = RefCell::new(Vec::new()); + let results: RefCell>> = RefCell::new(Vec::new()); // SAFETY: As above, all futures must complete in this function so we can change the lifetime - let results: &'env RefCell>>>> = - unsafe { mem::transmute(&results) }; + let results: &'env RefCell>> = unsafe { mem::transmute(&results) }; let mut scope = Scope { executor, @@ -152,7 +172,16 @@ impl TaskPool { let results = scope.results.borrow(); results .iter() - .map(|result| result.borrow_mut().take().unwrap()) + .map(|result| { + #[cfg(feature = "std")] + return result.borrow_mut().take().unwrap(); + + #[cfg(not(feature = "std"))] + { + let mut lock = result.lock(); + lock.take().unwrap() + } + }) .collect() } @@ -162,29 +191,42 @@ impl TaskPool { /// end-user. /// /// If the provided future is non-`Send`, [`TaskPool::spawn_local`] should be used instead. - pub fn spawn(&self, future: impl Future + 'static) -> Task + pub fn spawn( + &self, + future: impl Future + 'static + MaybeSend + MaybeSync, + ) -> Task where - T: 'static, + T: 'static + MaybeSend + MaybeSync, { - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", feature = "std"))] return Task::wrap_future(future); - #[cfg(not(target_arch = "wasm32"))] - { - LOCAL_EXECUTOR.with(|executor| { - let task = executor.spawn(future); - // Loop until all tasks are done - while executor.try_tick() {} + #[cfg(all(not(target_arch = "wasm32"), feature = "std"))] + return LOCAL_EXECUTOR.with(|executor| { + let task = executor.spawn(future); + // Loop until all tasks are done + while executor.try_tick() {} - Task::new(task) - }) - } + Task::new(task) + }); + + #[cfg(not(feature = "std"))] + return { + let task = LOCAL_EXECUTOR.spawn(future); + // Loop until all tasks are done + while LOCAL_EXECUTOR.try_tick() {} + + Task::new(task) + }; } /// Spawns a static future on the JS event loop. This is exactly the same as [`TaskPool::spawn`]. - pub fn spawn_local(&self, future: impl Future + 'static) -> Task + pub fn spawn_local( + &self, + future: impl Future + 'static + MaybeSend + MaybeSync, + ) -> Task where - T: 'static, + T: 'static + MaybeSend + MaybeSync, { self.spawn(future) } @@ -202,9 +244,13 @@ impl TaskPool { /// ``` pub fn with_local_executor(&self, f: F) -> R where - F: FnOnce(&async_executor::LocalExecutor) -> R, + F: FnOnce(&LocalExecutor) -> R, { - LOCAL_EXECUTOR.with(f) + #[cfg(feature = "std")] + return LOCAL_EXECUTOR.with(f); + + #[cfg(not(feature = "std"))] + return f(&LOCAL_EXECUTOR); } } @@ -213,9 +259,9 @@ impl TaskPool { /// For more information, see [`TaskPool::scope`]. #[derive(Debug)] pub struct Scope<'scope, 'env: 'scope, T> { - executor: &'scope async_executor::LocalExecutor<'scope>, + executor: &'scope LocalExecutor<'scope>, // Vector to gather results of all futures spawned during scope run - results: &'env RefCell>>>>, + results: &'env RefCell>>, // make `Scope` invariant over 'scope and 'env scope: PhantomData<&'scope mut &'scope ()>, @@ -230,7 +276,7 @@ impl<'scope, 'env, T: Send + 'env> Scope<'scope, 'env, T> { /// On the single threaded task pool, it just calls [`Scope::spawn_on_scope`]. /// /// For more information, see [`TaskPool::scope`]. - pub fn spawn + 'scope>(&self, f: Fut) { + pub fn spawn + 'scope + MaybeSend>(&self, f: Fut) { self.spawn_on_scope(f); } @@ -241,7 +287,7 @@ impl<'scope, 'env, T: Send + 'env> Scope<'scope, 'env, T> { /// On the single threaded task pool, it just calls [`Scope::spawn_on_scope`]. /// /// For more information, see [`TaskPool::scope`]. - pub fn spawn_on_external + 'scope>(&self, f: Fut) { + pub fn spawn_on_external + 'scope + MaybeSend>(&self, f: Fut) { self.spawn_on_scope(f); } @@ -250,13 +296,41 @@ impl<'scope, 'env, T: Send + 'env> Scope<'scope, 'env, T> { /// returned as a part of [`TaskPool::scope`]'s return value. /// /// For more information, see [`TaskPool::scope`]. - pub fn spawn_on_scope + 'scope>(&self, f: Fut) { - let result = Rc::new(RefCell::new(None)); + pub fn spawn_on_scope + 'scope + MaybeSend>(&self, f: Fut) { + let result = ScopeResult::::default(); self.results.borrow_mut().push(result.clone()); let f = async move { let temp_result = f.await; + + #[cfg(feature = "std")] result.borrow_mut().replace(temp_result); + + #[cfg(not(feature = "std"))] + { + let mut lock = result.lock(); + *lock = Some(temp_result); + } }; self.executor.spawn(f).detach(); } } + +#[cfg(feature = "std")] +mod send_sync_bounds { + pub trait MaybeSend {} + impl MaybeSend for T {} + + pub trait MaybeSync {} + impl MaybeSync for T {} +} + +#[cfg(not(feature = "std"))] +mod send_sync_bounds { + pub trait MaybeSend: Send {} + impl MaybeSend for T {} + + pub trait MaybeSync: Sync {} + impl MaybeSync for T {} +} + +use send_sync_bounds::{MaybeSend, MaybeSync}; diff --git a/crates/bevy_tasks/src/slice.rs b/crates/bevy_tasks/src/slice.rs index a8a87c9ce80a0..5f964a4561778 100644 --- a/crates/bevy_tasks/src/slice.rs +++ b/crates/bevy_tasks/src/slice.rs @@ -1,4 +1,5 @@ use super::TaskPool; +use alloc::vec::Vec; /// Provides functions for mapping read-only slices across a provided [`TaskPool`]. pub trait ParallelSlice: AsRef<[T]> { diff --git a/crates/bevy_tasks/src/task.rs b/crates/bevy_tasks/src/task.rs index 53292c7574f44..cf5095408b0f3 100644 --- a/crates/bevy_tasks/src/task.rs +++ b/crates/bevy_tasks/src/task.rs @@ -14,11 +14,11 @@ use core::{ /// Tasks that panic get immediately canceled. Awaiting a canceled task also causes a panic. #[derive(Debug)] #[must_use = "Tasks are canceled when dropped, use `.detach()` to run them in the background."] -pub struct Task(async_executor::Task); +pub struct Task(crate::executor::Task); impl Task { /// Creates a new task from a given `async_executor::Task` - pub fn new(task: async_executor::Task) -> Self { + pub fn new(task: crate::executor::Task) -> Self { Self(task) } diff --git a/crates/bevy_tasks/src/task_pool.rs b/crates/bevy_tasks/src/task_pool.rs index 9fab3fbbe8317..6a4e000864232 100644 --- a/crates/bevy_tasks/src/task_pool.rs +++ b/crates/bevy_tasks/src/task_pool.rs @@ -2,7 +2,7 @@ use alloc::sync::Arc; use core::{future::Future, marker::PhantomData, mem, panic::AssertUnwindSafe}; use std::thread::{self, JoinHandle}; -use async_executor::FallibleTask; +use crate::executor::FallibleTask; use concurrent_queue::ConcurrentQueue; use futures_lite::FutureExt; @@ -102,7 +102,7 @@ impl TaskPoolBuilder { #[derive(Debug)] pub struct TaskPool { /// The executor for the pool. - executor: Arc>, + executor: Arc>, // The inner state of the pool. threads: Vec>, @@ -111,7 +111,7 @@ pub struct TaskPool { impl TaskPool { thread_local! { - static LOCAL_EXECUTOR: async_executor::LocalExecutor<'static> = const { async_executor::LocalExecutor::new() }; + static LOCAL_EXECUTOR: crate::executor::LocalExecutor<'static> = const { crate::executor::LocalExecutor::new() }; static THREAD_EXECUTOR: Arc> = Arc::new(ThreadExecutor::new()); } @@ -128,7 +128,7 @@ impl TaskPool { fn new_internal(builder: TaskPoolBuilder) -> Self { let (shutdown_tx, shutdown_rx) = async_channel::unbounded::<()>(); - let executor = Arc::new(async_executor::Executor::new()); + let executor = Arc::new(crate::executor::Executor::new()); let num_threads = builder .num_threads @@ -344,9 +344,9 @@ impl TaskPool { // transmute the lifetimes to 'env here to appease the compiler as it is unable to validate safety. // Any usages of the references passed into `Scope` must be accessed through // the transmuted reference for the rest of this function. - let executor: &async_executor::Executor = &self.executor; + let executor: &crate::executor::Executor = &self.executor; // SAFETY: As above, all futures must complete in this function so we can change the lifetime - let executor: &'env async_executor::Executor = unsafe { mem::transmute(executor) }; + let executor: &'env crate::executor::Executor = unsafe { mem::transmute(executor) }; // SAFETY: As above, all futures must complete in this function so we can change the lifetime let external_executor: &'env ThreadExecutor<'env> = unsafe { mem::transmute(external_executor) }; @@ -432,7 +432,7 @@ impl TaskPool { #[inline] async fn execute_global_external_scope<'scope, 'ticker, T>( - executor: &'scope async_executor::Executor<'scope>, + executor: &'scope crate::executor::Executor<'scope>, external_ticker: ThreadExecutorTicker<'scope, 'ticker>, scope_ticker: ThreadExecutorTicker<'scope, 'ticker>, get_results: impl Future>, @@ -478,7 +478,7 @@ impl TaskPool { #[inline] async fn execute_global_scope<'scope, 'ticker, T>( - executor: &'scope async_executor::Executor<'scope>, + executor: &'scope crate::executor::Executor<'scope>, scope_ticker: ThreadExecutorTicker<'scope, 'ticker>, get_results: impl Future>, ) -> Vec { @@ -562,7 +562,7 @@ impl TaskPool { /// ``` pub fn with_local_executor(&self, f: F) -> R where - F: FnOnce(&async_executor::LocalExecutor) -> R, + F: FnOnce(&crate::executor::LocalExecutor) -> R, { Self::LOCAL_EXECUTOR.with(f) } @@ -593,7 +593,7 @@ impl Drop for TaskPool { /// For more information, see [`TaskPool::scope`]. #[derive(Debug)] pub struct Scope<'scope, 'env: 'scope, T> { - executor: &'scope async_executor::Executor<'scope>, + executor: &'scope crate::executor::Executor<'scope>, external_executor: &'scope ThreadExecutor<'scope>, scope_executor: &'scope ThreadExecutor<'scope>, spawned: &'scope ConcurrentQueue>>>, diff --git a/crates/bevy_tasks/src/thread_executor.rs b/crates/bevy_tasks/src/thread_executor.rs index b25811b559341..0f8a9c3be9038 100644 --- a/crates/bevy_tasks/src/thread_executor.rs +++ b/crates/bevy_tasks/src/thread_executor.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use std::thread::{self, ThreadId}; -use async_executor::{Executor, Task}; +use crate::executor::{Executor, Task}; use futures_lite::Future; /// An executor that can only be ticked on the thread it was instantiated on. But diff --git a/crates/bevy_tasks/src/usages.rs b/crates/bevy_tasks/src/usages.rs index b260274a0fb11..5be44f3b9e690 100644 --- a/crates/bevy_tasks/src/usages.rs +++ b/crates/bevy_tasks/src/usages.rs @@ -1,11 +1,20 @@ use super::TaskPool; use core::ops::Deref; + +#[cfg(feature = "std")] use std::sync::OnceLock; +#[cfg(not(feature = "std"))] +use spin::Once; + macro_rules! taskpool { ($(#[$attr:meta])* ($static:ident, $type:ident)) => { + #[cfg(feature = "std")] static $static: OnceLock<$type> = OnceLock::new(); + #[cfg(not(feature = "std"))] + static $static: Once<$type> = Once::new(); + $(#[$attr])* #[derive(Debug)] pub struct $type(TaskPool); @@ -13,7 +22,15 @@ macro_rules! taskpool { impl $type { #[doc = concat!(" Gets the global [`", stringify!($type), "`] instance, or initializes it with `f`.")] pub fn get_or_init(f: impl FnOnce() -> TaskPool) -> &'static Self { - $static.get_or_init(|| Self(f())) + #[cfg(feature = "std")] + { + $static.get_or_init(|| Self(f())) + } + + #[cfg(not(feature = "std"))] + { + $static.call_once(|| Self(f())) + } } #[doc = concat!(" Attempts to get the global [`", stringify!($type), "`] instance, \ diff --git a/tools/ci/src/commands/compile_check_no_std.rs b/tools/ci/src/commands/compile_check_no_std.rs index d52169a2651fe..6e057ba4d2050 100644 --- a/tools/ci/src/commands/compile_check_no_std.rs +++ b/tools/ci/src/commands/compile_check_no_std.rs @@ -62,14 +62,6 @@ impl Prepare for CompileCheckNoStdCommand { "Please fix compiler errors in output above for bevy_mikktspace no_std compatibility.", )); - commands.push(PreparedCommand::new::( - cmd!( - sh, - "cargo check -p bevy_mikktspace --no-default-features --features libm --target {target}" - ), - "Please fix compiler errors in output above for bevy_mikktspace no_std compatibility.", - )); - commands.push(PreparedCommand::new::( cmd!( sh, @@ -94,6 +86,14 @@ impl Prepare for CompileCheckNoStdCommand { "Please fix compiler errors in output above for bevy_color no_std compatibility.", )); + commands.push(PreparedCommand::new::( + cmd!( + sh, + "cargo check -p bevy_tasks --no-default-features --features edge_executor,critical-section --target {target}" + ), + "Please fix compiler errors in output above for bevy_tasks no_std compatibility.", + )); + commands } }