-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Objective - Contributes to #16877 ## Solution - Expanded `bevy_platform_support::sync` module to provide API-compatible replacements for `std` items such as `RwLock`, `Mutex`, and `OnceLock`. - Removed `spin` from all crates except `bevy_platform_support`. ## Testing - CI --- ## Notes - The sync primitives, while verbose, entirely rely on `spin` for their implementation requiring no `unsafe` and not changing the status-quo on _how_ locks actually work within Bevy. This is just a refactoring to consolidate the "hacks" and workarounds required to get a consistent experience when either using `std::sync` or `spin`. - I have opted to rely on `std::sync` for `std` compatible locks, maintaining the status quo. However, now that we have these locks factored out into the own module, it would be trivial to investigate alternate locking backends, such as `parking_lot`. - API for these locking types is entirely based on `std`. I have implemented methods and types which aren't currently in use within Bevy (e.g., `LazyLock` and `Once`) for the sake of completeness. As the standard library is highly stable, I don't expect the Bevy and `std` implementations to drift apart much if at all. --------- Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com> Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
- Loading branch information
1 parent
dd2d84b
commit 04990fc
Showing
22 changed files
with
753 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
//! Provides various atomic alternatives to language primitives. | ||
//! | ||
//! Certain platforms lack complete atomic support, requiring the use of a fallback | ||
//! such as `portable-atomic`. | ||
//! Using these types will ensure the correct atomic provider is used without the need for | ||
//! feature gates in your own code. | ||
pub use atomic::{ | ||
AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, | ||
AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering, | ||
}; | ||
|
||
#[cfg(not(feature = "portable-atomic"))] | ||
use core::sync::atomic; | ||
|
||
#[cfg(feature = "portable-atomic")] | ||
use portable_atomic as atomic; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
//! Provides `Barrier` and `BarrierWaitResult` | ||
pub use barrier::{Barrier, BarrierWaitResult}; | ||
|
||
#[cfg(feature = "std")] | ||
use std::sync as barrier; | ||
|
||
#[cfg(not(feature = "std"))] | ||
mod barrier { | ||
use core::fmt; | ||
|
||
/// Fallback implementation of `Barrier` from the standard library. | ||
pub struct Barrier { | ||
inner: spin::Barrier, | ||
} | ||
|
||
impl Barrier { | ||
/// Creates a new barrier that can block a given number of threads. | ||
/// | ||
/// See the standard library for further details. | ||
#[must_use] | ||
pub const fn new(n: usize) -> Self { | ||
Self { | ||
inner: spin::Barrier::new(n), | ||
} | ||
} | ||
|
||
/// Blocks the current thread until all threads have rendezvoused here. | ||
/// | ||
/// See the standard library for further details. | ||
pub fn wait(&self) -> BarrierWaitResult { | ||
BarrierWaitResult { | ||
inner: self.inner.wait(), | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Debug for Barrier { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.debug_struct("Barrier").finish_non_exhaustive() | ||
} | ||
} | ||
|
||
/// Fallback implementation of `BarrierWaitResult` from the standard library. | ||
pub struct BarrierWaitResult { | ||
inner: spin::barrier::BarrierWaitResult, | ||
} | ||
|
||
impl BarrierWaitResult { | ||
/// Returns `true` if this thread is the "leader thread" for the call to [`Barrier::wait()`]. | ||
/// | ||
/// See the standard library for further details. | ||
#[must_use] | ||
pub fn is_leader(&self) -> bool { | ||
self.inner.is_leader() | ||
} | ||
} | ||
|
||
impl fmt::Debug for BarrierWaitResult { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.debug_struct("BarrierWaitResult") | ||
.field("is_leader", &self.is_leader()) | ||
.finish() | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
//! Provides `LazyLock` | ||
pub use lazy_lock::LazyLock; | ||
|
||
#[cfg(feature = "std")] | ||
use std::sync as lazy_lock; | ||
|
||
#[cfg(not(feature = "std"))] | ||
mod lazy_lock { | ||
pub use spin::Lazy as LazyLock; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//! Provides various synchronization alternatives to language primitives. | ||
//! | ||
//! Currently missing from this module are the following items: | ||
//! * `Condvar` | ||
//! * `WaitTimeoutResult` | ||
//! * `mpsc` | ||
//! | ||
//! Otherwise, this is a drop-in replacement for `std::sync`. | ||
pub use barrier::{Barrier, BarrierWaitResult}; | ||
pub use lazy_lock::LazyLock; | ||
pub use mutex::{Mutex, MutexGuard}; | ||
pub use once::{Once, OnceLock, OnceState}; | ||
pub use poison::{LockResult, PoisonError, TryLockError, TryLockResult}; | ||
pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; | ||
|
||
#[cfg(feature = "alloc")] | ||
pub use arc::{Arc, Weak}; | ||
|
||
pub mod atomic; | ||
|
||
mod barrier; | ||
mod lazy_lock; | ||
mod mutex; | ||
mod once; | ||
mod poison; | ||
mod rwlock; | ||
|
||
#[cfg(all(feature = "alloc", feature = "portable-atomic"))] | ||
use portable_atomic_util as arc; | ||
|
||
#[cfg(all(feature = "alloc", not(feature = "portable-atomic")))] | ||
use alloc::sync as arc; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
//! Provides `Mutex` and `MutexGuard` | ||
pub use mutex::{Mutex, MutexGuard}; | ||
|
||
#[cfg(feature = "std")] | ||
use std::sync as mutex; | ||
|
||
#[cfg(not(feature = "std"))] | ||
mod mutex { | ||
use crate::sync::{LockResult, TryLockError, TryLockResult}; | ||
use core::fmt; | ||
|
||
pub use spin::MutexGuard; | ||
|
||
/// Fallback implementation of `Mutex` from the standard library. | ||
pub struct Mutex<T: ?Sized> { | ||
inner: spin::Mutex<T>, | ||
} | ||
|
||
impl<T> Mutex<T> { | ||
/// Creates a new mutex in an unlocked state ready for use. | ||
/// | ||
/// See the standard library for further details. | ||
pub const fn new(t: T) -> Self { | ||
Self { | ||
inner: spin::Mutex::new(t), | ||
} | ||
} | ||
} | ||
|
||
impl<T: ?Sized> Mutex<T> { | ||
/// Acquires a mutex, blocking the current thread until it is able to do so. | ||
/// | ||
/// See the standard library for further details. | ||
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> { | ||
Ok(self.inner.lock()) | ||
} | ||
|
||
/// Attempts to acquire this lock. | ||
/// | ||
/// See the standard library for further details. | ||
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> { | ||
self.inner.try_lock().ok_or(TryLockError::WouldBlock) | ||
} | ||
|
||
/// Determines whether the mutex is poisoned. | ||
/// | ||
/// See the standard library for further details. | ||
pub fn is_poisoned(&self) -> bool { | ||
false | ||
} | ||
|
||
/// Clear the poisoned state from a mutex. | ||
/// | ||
/// See the standard library for further details. | ||
pub fn clear_poison(&self) { | ||
// no-op | ||
} | ||
|
||
/// Consumes this mutex, returning the underlying data. | ||
/// | ||
/// See the standard library for further details. | ||
pub fn into_inner(self) -> LockResult<T> | ||
where | ||
T: Sized, | ||
{ | ||
Ok(self.inner.into_inner()) | ||
} | ||
|
||
/// Returns a mutable reference to the underlying data. | ||
/// | ||
/// See the standard library for further details. | ||
pub fn get_mut(&mut self) -> LockResult<&mut T> { | ||
Ok(self.inner.get_mut()) | ||
} | ||
} | ||
|
||
impl<T> From<T> for Mutex<T> { | ||
fn from(t: T) -> Self { | ||
Mutex::new(t) | ||
} | ||
} | ||
|
||
impl<T: ?Sized + Default> Default for Mutex<T> { | ||
fn default() -> Mutex<T> { | ||
Mutex::new(Default::default()) | ||
} | ||
} | ||
|
||
impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
let mut d = f.debug_struct("Mutex"); | ||
match self.try_lock() { | ||
Ok(guard) => { | ||
d.field("data", &&*guard); | ||
} | ||
Err(TryLockError::Poisoned(err)) => { | ||
d.field("data", &&**err.get_ref()); | ||
} | ||
Err(TryLockError::WouldBlock) => { | ||
d.field("data", &format_args!("<locked>")); | ||
} | ||
} | ||
d.field("poisoned", &false); | ||
d.finish_non_exhaustive() | ||
} | ||
} | ||
} |
Oops, something went wrong.