Add sleep based on spin to bevy_platform_support (#18633)

# Objective

- Fixes #18617

## Solution

- Added `thread::sleep` to `bevy_platform_support` using a spin-based
fallback.
- Fixed bug in `bevy_platform_support::time::Instant::elapsed`
(comparison was backwards)
- Switched `ScheduleRunnerPlugin` to use
`bevy_platform_support:🧵:sleep` on `std` and `no_std` platforms
(WASM + Browser excluded)

## Testing

- Ran reproduction code from @mockersf in linked issue and confirmed a
consistent 60 counts per `println!`.

---

## Notes

- I chose to add `bevy_platform_support:🧵:sleep` instead of
putting the fix in-line within `ScheduleRunnerPlugin` to keep the
separation of concerns clean. `sleep` is only used in one other location
in Bevy, `bevy_asset`, but I have decided to leave that as-is since
`bevy_asset` isn't `no_std` compatible anyway.
- The bug in `bevy_platform_support::time::Instant::elapsed` wasn't the
cause of this issue, but it did prevent this fix from working so I have
included the it in this PR.
This commit is contained in:
Zachary Harrold 2025-04-01 10:21:49 +11:00 committed by GitHub
parent 951c4dac7e
commit 99289ad988
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 33 additions and 4 deletions

View File

@ -159,9 +159,8 @@ impl Plugin for ScheduleRunnerPlugin {
} else {
loop {
match tick(&mut app, wait) {
Ok(Some(_delay)) => {
#[cfg(feature = "std")]
std::thread::sleep(_delay);
Ok(Some(delay)) => {
bevy_platform_support::thread::sleep(delay);
}
Ok(None) => continue,
Err(exit) => return exit,

View File

@ -17,6 +17,7 @@ extern crate alloc;
pub mod hash;
pub mod sync;
pub mod thread;
pub mod time;
#[cfg(feature = "alloc")]

View File

@ -0,0 +1,29 @@
//! Provides `sleep` for all platforms.
pub use thread::sleep;
cfg_if::cfg_if! {
// TODO: use browser timeouts based on ScheduleRunnerPlugin::build
if #[cfg(feature = "std")] {
use std::thread;
} else {
mod fallback {
use core::{hint::spin_loop, time::Duration};
use crate::time::Instant;
/// Puts the current thread to sleep for at least the specified amount of time.
///
/// As this is a `no_std` fallback implementation, this will spin the current thread.
pub fn sleep(dur: Duration) {
let start = Instant::now();
while start.elapsed() < dur {
spin_loop()
}
}
}
use fallback as thread;
}
}

View File

@ -80,7 +80,7 @@ impl Instant {
/// Returns the amount of time elapsed since this instant.
#[must_use]
pub fn elapsed(&self) -> Duration {
self.saturating_duration_since(Instant::now())
Instant::now().saturating_duration_since(*self)
}
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as