
# Objective - Fixes #15460 (will open new issues for further `no_std` efforts) - Supersedes #17715 ## Solution - Threaded in new features as required - Made certain crates optional but default enabled - Removed `compile-check-no-std` from internal `ci` tool since GitHub CI can now simply check `bevy` itself now - Added CI task to check `bevy` on `thumbv6m-none-eabi` to ensure `portable-atomic` support is still valid [^1] [^1]: This may be controversial, since it could be interpreted as implying Bevy will maintain support for `thumbv6m-none-eabi` going forward. In reality, just like `x86_64-unknown-none`, this is a [canary](https://en.wiktionary.org/wiki/canary_in_a_coal_mine) target to make it clear when `portable-atomic` no longer works as intended (fixing atomic support on atomically challenged platforms). If a PR comes through and makes supporting this class of platforms impossible, then this CI task can be removed. I however wager this won't be a problem. ## Testing - CI --- ## Release Notes Bevy now has support for `no_std` directly from the `bevy` crate. Users can disable default features and enable a new `default_no_std` feature instead, allowing `bevy` to be used in `no_std` applications and libraries. ```toml # Bevy for `no_std` platforms bevy = { version = "0.16", default-features = false, features = ["default_no_std"] } ``` `default_no_std` enables certain required features, such as `libm` and `critical-section`, and as many optional crates as possible (currently just `bevy_state`). For atomically-challenged platforms such as the Raspberry Pi Pico, `portable-atomic` will be used automatically. For library authors, we recommend depending on `bevy` with `default-features = false` to allow `std` and `no_std` users to both depend on your crate. Here are some recommended features a library crate may want to expose: ```toml [features] # Most users will be on a platform which has `std` and can use the more-powerful `async_executor`. default = ["std", "async_executor"] # Features for typical platforms. std = ["bevy/std"] async_executor = ["bevy/async_executor"] # Features for `no_std` platforms. libm = ["bevy/libm"] critical-section = ["bevy/critical-section"] [dependencies] # We disable default features to ensure we don't accidentally enable `std` on `no_std` targets, for example. bevy = { version = "0.16", default-features = false } ``` While this is verbose, it gives the maximum control to end-users to decide how they wish to use Bevy on their platform. We encourage library authors to experiment with `no_std` support. For libraries relying exclusively on `bevy` and no other dependencies, it may be as simple as adding `#![no_std]` to your `lib.rs` and exposing features as above! Bevy can also provide many `std` types, such as `HashMap`, `Mutex`, and `Instant` on all platforms. See `bevy::platform_support` for details on what's available out of the box! ## Migration Guide - If you were previously relying on `bevy` with default features disabled, you may need to enable the `std` and `async_executor` features. - `bevy_reflect` has had its `bevy` feature removed. If you were relying on this feature, simply enable `smallvec` and `smol_str` instead. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
86 lines
3.0 KiB
Rust
86 lines
3.0 KiB
Rust
//! 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
|
|
|
|
use core::{
|
|
fmt,
|
|
panic::{RefUnwindSafe, UnwindSafe},
|
|
};
|
|
use derive_more::{Deref, DerefMut};
|
|
|
|
#[cfg(all(feature = "multi_threaded", not(target_arch = "wasm32")))]
|
|
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(not(feature = "async_executor"))]
|
|
type ExecutorInner<'a> = crate::edge_executor::Executor<'a, 64>;
|
|
|
|
#[cfg(not(feature = "async_executor"))]
|
|
type LocalExecutorInner<'a> = crate::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`]
|
|
#[expect(clippy::allow_attributes, reason = "This lint may not always trigger.")]
|
|
#[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`]
|
|
#[expect(clippy::allow_attributes, reason = "This lint may not always trigger.")]
|
|
#[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()
|
|
}
|
|
}
|