From 9d903a3a57d129dce241585549166deb12ab3488 Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 5 Sep 2025 23:59:29 -0700 Subject: [PATCH 01/13] Use non-poisoning locks --- crates/bevy_ecs/src/component/info.rs | 15 +- crates/bevy_ecs/src/component/register.rs | 52 +----- crates/bevy_ecs/src/intern.rs | 10 +- crates/bevy_ecs/src/reflect/mod.rs | 22 +-- crates/bevy_platform/src/sync/mod.rs | 2 +- crates/bevy_platform/src/sync/mutex.rs | 129 ++++++++++--- crates/bevy_platform/src/sync/poison.rs | 117 +++--------- crates/bevy_platform/src/sync/rwlock.rs | 170 +++++++++++++++--- crates/bevy_reflect/src/func/registry.rs | 26 +-- crates/bevy_reflect/src/type_registry.rs | 37 +--- crates/bevy_reflect/src/utility.rs | 10 +- crates/bevy_render/src/render_phase/draw.rs | 8 +- .../src/render_resource/pipeline_cache.rs | 32 ++-- crates/bevy_render/src/renderer/mod.rs | 5 +- crates/bevy_scene/src/scene_loader.rs | 5 +- crates/bevy_winit/src/system.rs | 2 +- 16 files changed, 320 insertions(+), 322 deletions(-) diff --git a/crates/bevy_ecs/src/component/info.rs b/crates/bevy_ecs/src/component/info.rs index 0e222692d7fb1..bf097292c654d 100644 --- a/crates/bevy_ecs/src/component/info.rs +++ b/crates/bevy_ecs/src/component/info.rs @@ -1,5 +1,5 @@ use alloc::{borrow::Cow, vec::Vec}; -use bevy_platform::{hash::FixedHasher, sync::PoisonError}; +use bevy_platform::hash::FixedHasher; use bevy_ptr::OwningPtr; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; @@ -392,7 +392,7 @@ impl Components { /// Returns the number of components registered with this instance. #[inline] pub fn num_queued(&self) -> usize { - let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); + let queued = self.queued.read(); queued.components.len() + queued.dynamic_registrations.len() + queued.resources.len() } @@ -405,10 +405,7 @@ impl Components { /// A faster version of [`Self::num_queued`]. #[inline] pub fn num_queued_mut(&mut self) -> usize { - let queued = self - .queued - .get_mut() - .unwrap_or_else(PoisonError::into_inner); + let queued = self.queued.get_mut(); queued.components.len() + queued.dynamic_registrations.len() + queued.resources.len() } @@ -451,7 +448,7 @@ impl Components { .get(id.0) .and_then(|info| info.as_ref().map(|info| Cow::Borrowed(&info.descriptor))) .or_else(|| { - let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); + let queued = self.queued.read(); // first check components, then resources, then dynamic queued .components @@ -473,7 +470,7 @@ impl Components { .get(id.0) .and_then(|info| info.as_ref().map(|info| info.descriptor.name())) .or_else(|| { - let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); + let queued = self.queued.read(); // first check components, then resources, then dynamic queued .components @@ -622,7 +619,6 @@ impl Components { self.indices.get(&type_id).copied().or_else(|| { self.queued .read() - .unwrap_or_else(PoisonError::into_inner) .components .get(&type_id) .map(|queued| queued.id) @@ -669,7 +665,6 @@ impl Components { self.resource_indices.get(&type_id).copied().or_else(|| { self.queued .read() - .unwrap_or_else(PoisonError::into_inner) .resources .get(&type_id) .map(|queued| queued.id) diff --git a/crates/bevy_ecs/src/component/register.rs b/crates/bevy_ecs/src/component/register.rs index 5d8ac3ee6e98b..80956a9ef2037 100644 --- a/crates/bevy_ecs/src/component/register.rs +++ b/crates/bevy_ecs/src/component/register.rs @@ -1,5 +1,4 @@ use alloc::{boxed::Box, vec::Vec}; -use bevy_platform::sync::PoisonError; use bevy_utils::TypeIdMap; use core::any::Any; use core::{any::TypeId, fmt::Debug, ops::Deref}; @@ -125,11 +124,7 @@ impl<'w> ComponentsRegistrator<'w> { // components while let Some(registrator) = { - let queued = self - .components - .queued - .get_mut() - .unwrap_or_else(PoisonError::into_inner); + let queued = self.components.queued.get_mut(); queued.components.keys().next().copied().map(|type_id| { // SAFETY: the id just came from a valid iterator. unsafe { queued.components.remove(&type_id).debug_checked_unwrap() } @@ -140,11 +135,7 @@ impl<'w> ComponentsRegistrator<'w> { // resources while let Some(registrator) = { - let queued = self - .components - .queued - .get_mut() - .unwrap_or_else(PoisonError::into_inner); + let queued = self.components.queued.get_mut(); queued.resources.keys().next().copied().map(|type_id| { // SAFETY: the id just came from a valid iterator. unsafe { queued.resources.remove(&type_id).debug_checked_unwrap() } @@ -154,11 +145,7 @@ impl<'w> ComponentsRegistrator<'w> { } // dynamic - let queued = &mut self - .components - .queued - .get_mut() - .unwrap_or_else(PoisonError::into_inner); + let queued = &mut self.components.queued.get_mut(); if !queued.dynamic_registrations.is_empty() { for registrator in core::mem::take(&mut queued.dynamic_registrations) { registrator.register(self); @@ -188,14 +175,7 @@ impl<'w> ComponentsRegistrator<'w> { return id; } - if let Some(registrator) = self - .components - .queued - .get_mut() - .unwrap_or_else(PoisonError::into_inner) - .components - .remove(&type_id) - { + if let Some(registrator) = self.components.queued.get_mut().components.remove(&type_id) { // If we are trying to register something that has already been queued, we respect the queue. // Just like if we are trying to register something that already is, we respect the first registration. return registrator.register(self); @@ -325,14 +305,7 @@ impl<'w> ComponentsRegistrator<'w> { return *id; } - if let Some(registrator) = self - .components - .queued - .get_mut() - .unwrap_or_else(PoisonError::into_inner) - .resources - .remove(&type_id) - { + if let Some(registrator) = self.components.queued.get_mut().resources.remove(&type_id) { // If we are trying to register something that has already been queued, we respect the queue. // Just like if we are trying to register something that already is, we respect the first registration. return registrator.register(self); @@ -500,7 +473,6 @@ impl<'w> ComponentsQueuedRegistrator<'w> { self.components .queued .write() - .unwrap_or_else(PoisonError::into_inner) .components .entry(type_id) .or_insert_with(|| { @@ -525,7 +497,6 @@ impl<'w> ComponentsQueuedRegistrator<'w> { self.components .queued .write() - .unwrap_or_else(PoisonError::into_inner) .resources .entry(type_id) .or_insert_with(|| { @@ -542,15 +513,10 @@ impl<'w> ComponentsQueuedRegistrator<'w> { func: impl FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor) + 'static, ) -> ComponentId { let id = self.ids.next(); - self.components - .queued - .write() - .unwrap_or_else(PoisonError::into_inner) - .dynamic_registrations - .push( - // SAFETY: The id was just generated. - unsafe { QueuedRegistration::new(id, descriptor, func) }, - ); + self.components.queued.write().dynamic_registrations.push( + // SAFETY: The id was just generated. + unsafe { QueuedRegistration::new(id, descriptor, func) }, + ); id } diff --git a/crates/bevy_ecs/src/intern.rs b/crates/bevy_ecs/src/intern.rs index b10e6a2ac691b..77e49ad8749c4 100644 --- a/crates/bevy_ecs/src/intern.rs +++ b/crates/bevy_ecs/src/intern.rs @@ -5,11 +5,7 @@ //! and make comparisons for any type as fast as integers. use alloc::{borrow::ToOwned, boxed::Box}; -use bevy_platform::{ - collections::HashSet, - hash::FixedHasher, - sync::{PoisonError, RwLock}, -}; +use bevy_platform::{collections::HashSet, hash::FixedHasher, sync::RwLock}; use core::{fmt::Debug, hash::Hash, ops::Deref}; /// An interned value. Will stay valid until the end of the program and will not drop. @@ -140,7 +136,7 @@ impl Interner { /// will return [`Interned`] using the same static reference. pub fn intern(&self, value: &T) -> Interned { { - let set = self.0.read().unwrap_or_else(PoisonError::into_inner); + let set = self.0.read(); if let Some(value) = set.get(value) { return Interned(*value); @@ -148,7 +144,7 @@ impl Interner { } { - let mut set = self.0.write().unwrap_or_else(PoisonError::into_inner); + let mut set = self.0.write(); if let Some(value) = set.get(value) { Interned(*value) diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index c306723f3a707..e2d453011bdf1 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -1,14 +1,10 @@ //! Types that enable reflection support. -use core::{ - any::TypeId, - ops::{Deref, DerefMut}, -}; +use core::{any::TypeId, ops::Deref}; use crate::{resource::Resource, world::World}; use bevy_reflect::{ - std_traits::ReflectDefault, PartialReflect, Reflect, ReflectFromReflect, TypePath, - TypeRegistry, TypeRegistryArc, + std_traits::ReflectDefault, PartialReflect, Reflect, ReflectFromReflect, TypePath, TypeRegistry, }; mod bundle; @@ -18,6 +14,7 @@ mod from_world; mod map_entities; mod resource; +use bevy_platform::sync::{Arc, RwLock}; use bevy_utils::prelude::DebugName; pub use bundle::{ReflectBundle, ReflectBundleFns}; pub use component::{ReflectComponent, ReflectComponentFns}; @@ -28,11 +25,11 @@ pub use resource::{ReflectResource, ReflectResourceFns}; /// A [`Resource`] storing [`TypeRegistry`] for /// type registrations relevant to a whole app. -#[derive(Resource, Clone, Default)] -pub struct AppTypeRegistry(pub TypeRegistryArc); +#[derive(Resource, Clone, Default, Debug)] +pub struct AppTypeRegistry(Arc>); impl Deref for AppTypeRegistry { - type Target = TypeRegistryArc; + type Target = RwLock; #[inline] fn deref(&self) -> &Self::Target { @@ -40,13 +37,6 @@ impl Deref for AppTypeRegistry { } } -impl DerefMut for AppTypeRegistry { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - impl AppTypeRegistry { /// Creates [`AppTypeRegistry`] and automatically registers all types deriving [`Reflect`]. /// diff --git a/crates/bevy_platform/src/sync/mod.rs b/crates/bevy_platform/src/sync/mod.rs index 79ceff7ee82c2..f311637387cdf 100644 --- a/crates/bevy_platform/src/sync/mod.rs +++ b/crates/bevy_platform/src/sync/mod.rs @@ -11,7 +11,7 @@ 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 poison::{TryLockError, TryLockResult}; pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; crate::cfg::alloc! { diff --git a/crates/bevy_platform/src/sync/mutex.rs b/crates/bevy_platform/src/sync/mutex.rs index 7ff363f5741d7..03d95891f6c0b 100644 --- a/crates/bevy_platform/src/sync/mutex.rs +++ b/crates/bevy_platform/src/sync/mutex.rs @@ -3,18 +3,17 @@ pub use implementation::{Mutex, MutexGuard}; #[cfg(feature = "std")] -use std::sync as implementation; - -#[cfg(not(feature = "std"))] mod implementation { - use crate::sync::{LockResult, TryLockError, TryLockResult}; + use crate::sync::{TryLockError, TryLockResult}; use core::fmt; + use std::sync::PoisonError; - pub use spin::MutexGuard; + pub use std::sync::MutexGuard; /// Fallback implementation of `Mutex` from the standard library. + #[repr(transparent)] pub struct Mutex { - inner: spin::Mutex, + inner: std::sync::Mutex, } impl Mutex { @@ -23,7 +22,7 @@ mod implementation { /// See the standard library for further details. pub const fn new(t: T) -> Self { Self { - inner: spin::Mutex::new(t), + inner: std::sync::Mutex::new(t), } } } @@ -32,46 +31,133 @@ mod implementation { /// 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> { - Ok(self.inner.lock()) + pub fn lock(&self) -> MutexGuard<'_, T> { + match self.inner.lock() { + Ok(guard) => guard, + Err(err) => { + self.inner.clear_poison(); + err.into_inner() + } + } } /// Attempts to acquire this lock. /// /// See the standard library for further details. pub fn try_lock(&self) -> TryLockResult> { - self.inner.try_lock().ok_or(TryLockError::WouldBlock) + match self.inner.try_lock() { + Ok(guard) => Ok(guard), + Err(std::sync::TryLockError::Poisoned(err)) => { + self.inner.clear_poison(); + Ok(err.into_inner()) + } + Err(std::sync::TryLockError::WouldBlock) => Err(TryLockError::WouldBlock), + } } - /// Determines whether the mutex is poisoned. + /// Consumes this mutex, returning the underlying data. /// /// See the standard library for further details. - pub fn is_poisoned(&self) -> bool { - false + pub fn into_inner(self) -> T + where + T: Sized, + { + self.inner + .into_inner() + .unwrap_or_else(PoisonError::into_inner) } - /// Clear the poisoned state from a mutex. + /// Returns a mutable reference to the underlying data. /// /// See the standard library for further details. - pub fn clear_poison(&self) { - // no-op + pub fn get_mut(&mut self) -> &mut T { + self.inner.get_mut().unwrap_or_else(PoisonError::into_inner) + } + } + + impl From for Mutex { + fn from(t: T) -> Self { + Mutex::new(t) + } + } + + impl Default for Mutex { + fn default() -> Mutex { + Mutex::new(Default::default()) + } + } + + impl fmt::Debug for Mutex { + 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::WouldBlock) => { + d.field("data", &format_args!("")); + } + } + d.field("poisoned", &false); + d.finish_non_exhaustive() + } + } +} + +#[cfg(not(feature = "std"))] +mod implementation { + use crate::sync::{LockResult, TryLockError, TryLockResult}; + use core::fmt; + + pub use spin::MutexGuard; + + /// Fallback implementation of `Mutex` from the standard library. + #[repr(transparent)] + pub struct Mutex { + inner: spin::Mutex, + } + + impl Mutex { + /// 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 Mutex { + /// 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) -> MutexGuard<'_, T> { + self.inner.lock() + } + + /// Attempts to acquire this lock. + /// + /// See the standard library for further details. + pub fn try_lock(&self) -> TryLockResult> { + self.inner.try_lock().ok_or(TryLockError::WouldBlock) } /// Consumes this mutex, returning the underlying data. /// /// See the standard library for further details. - pub fn into_inner(self) -> LockResult + pub fn into_inner(self) -> T where T: Sized, { - Ok(self.inner.into_inner()) + 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()) + pub fn get_mut(&mut self) -> &mut T { + self.inner.get_mut() } } @@ -94,9 +180,6 @@ mod implementation { Ok(guard) => { d.field("data", &&*guard); } - Err(TryLockError::Poisoned(err)) => { - d.field("data", &&**err.get_ref()); - } Err(TryLockError::WouldBlock) => { d.field("data", &format_args!("")); } diff --git a/crates/bevy_platform/src/sync/poison.rs b/crates/bevy_platform/src/sync/poison.rs index 79eafc42505dd..12a817d78e961 100644 --- a/crates/bevy_platform/src/sync/poison.rs +++ b/crates/bevy_platform/src/sync/poison.rs @@ -1,107 +1,32 @@ //! Provides `LockResult`, `PoisonError`, `TryLockError`, `TryLockResult` -pub use implementation::{LockResult, PoisonError, TryLockError, TryLockResult}; +use core::{error::Error, fmt}; -#[cfg(feature = "std")] -use std::sync as implementation; - -#[cfg(not(feature = "std"))] -mod implementation { - use core::{error::Error, fmt}; - - /// Fallback implementation of `PoisonError` from the standard library. - pub struct PoisonError { - guard: T, - } - - impl fmt::Debug for PoisonError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PoisonError").finish_non_exhaustive() - } - } - - impl fmt::Display for PoisonError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "poisoned lock: another task failed inside".fmt(f) - } - } - - impl Error for PoisonError {} - - impl PoisonError { - /// Creates a `PoisonError`. - /// - /// See the standard library for further details. - #[cfg(panic = "unwind")] - pub fn new(guard: T) -> PoisonError { - PoisonError { guard } - } - - /// Consumes this error indicating that a lock is poisoned, returning the - /// underlying guard to allow access regardless. - /// - /// See the standard library for further details. - pub fn into_inner(self) -> T { - self.guard - } - - /// Reaches into this error indicating that a lock is poisoned, returning a - /// reference to the underlying guard to allow access regardless. - /// - /// See the standard library for further details. - pub fn get_ref(&self) -> &T { - &self.guard - } - - /// Reaches into this error indicating that a lock is poisoned, returning a - /// mutable reference to the underlying guard to allow access regardless. - /// - /// See the standard library for further details. - pub fn get_mut(&mut self) -> &mut T { - &mut self.guard - } - } - - /// Fallback implementation of `TryLockError` from the standard library. - pub enum TryLockError { - /// The lock could not be acquired because another thread failed while holding - /// the lock. - Poisoned(PoisonError), - /// The lock could not be acquired at this time because the operation would - /// otherwise block. - WouldBlock, - } - - impl From> for TryLockError { - fn from(err: PoisonError) -> TryLockError { - TryLockError::Poisoned(err) - } - } +/// Fallback implementation of `TryLockError` from the standard library. +pub enum TryLockError { + /// The lock could not be acquired at this time because the operation would + /// otherwise block. + WouldBlock, +} - impl fmt::Debug for TryLockError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f), - TryLockError::WouldBlock => "WouldBlock".fmt(f), - } +impl fmt::Debug for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TryLockError::WouldBlock => "WouldBlock".fmt(f), } } +} - impl fmt::Display for TryLockError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TryLockError::Poisoned(..) => "poisoned lock: another task failed inside", - TryLockError::WouldBlock => "try_lock failed because the operation would block", - } - .fmt(f) +impl fmt::Display for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TryLockError::WouldBlock => "try_lock failed because the operation would block", } + .fmt(f) } +} - impl Error for TryLockError {} - - /// Fallback implementation of `LockResult` from the standard library. - pub type LockResult = Result>; +impl Error for TryLockError {} - /// Fallback implementation of `TryLockResult` from the standard library. - pub type TryLockResult = Result>; -} +/// Fallback implementation of `TryLockResult` from the standard library. +pub type TryLockResult = Result; diff --git a/crates/bevy_platform/src/sync/rwlock.rs b/crates/bevy_platform/src/sync/rwlock.rs index f1f529baafe09..e659eb222b9ca 100644 --- a/crates/bevy_platform/src/sync/rwlock.rs +++ b/crates/bevy_platform/src/sync/rwlock.rs @@ -3,18 +3,17 @@ pub use implementation::{RwLock, RwLockReadGuard, RwLockWriteGuard}; #[cfg(feature = "std")] -use std::sync as implementation; - -#[cfg(not(feature = "std"))] mod implementation { - use crate::sync::{LockResult, TryLockError, TryLockResult}; + use crate::sync::{TryLockError, TryLockResult}; use core::fmt; - pub use spin::rwlock::{RwLockReadGuard, RwLockWriteGuard}; + use std::sync::PoisonError; + pub use std::sync::{RwLockReadGuard, RwLockWriteGuard}; /// Fallback implementation of `RwLock` from the standard library. + #[repr(transparent)] pub struct RwLock { - inner: spin::RwLock, + inner: std::sync::RwLock, } impl RwLock { @@ -23,7 +22,7 @@ mod implementation { /// See the standard library for further details. pub const fn new(t: T) -> RwLock { Self { - inner: spin::RwLock::new(t), + inner: std::sync::RwLock::new(t), } } } @@ -33,61 +32,183 @@ mod implementation { /// until it can be acquired. /// /// See the standard library for further details. - pub fn read(&self) -> LockResult> { - Ok(self.inner.read()) + #[inline] + pub fn read(&self) -> RwLockReadGuard<'_, T> { + match self.inner.read() { + Ok(guard) => guard, + Err(err) => { + self.inner.clear_poison(); + err.into_inner() + } + } } /// Attempts to acquire this `RwLock` with shared read access. /// /// See the standard library for further details. + #[inline] pub fn try_read(&self) -> TryLockResult> { - self.inner.try_read().ok_or(TryLockError::WouldBlock) + match self.inner.try_read() { + Ok(guard) => Ok(guard), + Err(std::sync::TryLockError::Poisoned(err)) => { + self.inner.clear_poison(); + Ok(err.into_inner()) + } + Err(std::sync::TryLockError::WouldBlock) => Err(TryLockError::WouldBlock), + } } /// Locks this `RwLock` with exclusive write access, blocking the current /// thread until it can be acquired. /// /// See the standard library for further details. - pub fn write(&self) -> LockResult> { - Ok(self.inner.write()) + #[inline] + pub fn write(&self) -> RwLockWriteGuard<'_, T> { + match self.inner.write() { + Ok(guard) => guard, + Err(err) => { + self.inner.clear_poison(); + err.into_inner() + } + } } /// Attempts to lock this `RwLock` with exclusive write access. /// /// See the standard library for further details. + #[inline] pub fn try_write(&self) -> TryLockResult> { - self.inner.try_write().ok_or(TryLockError::WouldBlock) + match self.inner.try_write() { + Ok(guard) => Ok(guard), + Err(std::sync::TryLockError::Poisoned(err)) => { + self.inner.clear_poison(); + Ok(err.into_inner()) + } + Err(std::sync::TryLockError::WouldBlock) => Err(TryLockError::WouldBlock), + } + } + + /// Consumes this `RwLock`, returning the underlying data. + /// + /// See the standard library for further details. + #[inline] + pub fn into_inner(self) -> T + where + T: Sized, + { + self.inner + .into_inner() + .unwrap_or_else(PoisonError::into_inner) + } + + /// Returns a mutable reference to the underlying data. + /// + /// See the standard library for further details. + #[inline] + pub fn get_mut(&mut self) -> &mut T { + self.inner.get_mut().unwrap_or_else(PoisonError::into_inner) + } + } + + impl fmt::Debug for RwLock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("RwLock"); + match self.try_read() { + Ok(guard) => { + d.field("data", &&*guard); + } + Err(TryLockError::WouldBlock) => { + d.field("data", &format_args!("")); + } + } + d.field("poisoned", &false); + d.finish_non_exhaustive() + } + } + + impl Default for RwLock { + fn default() -> RwLock { + RwLock::new(Default::default()) } + } + + impl From for RwLock { + fn from(t: T) -> Self { + RwLock::new(t) + } + } +} + +#[cfg(not(feature = "std"))] +mod implementation { + use crate::sync::{LockResult, TryLockError, TryLockResult}; + use core::fmt; - /// Determines whether the lock is poisoned. + pub use spin::rwlock::{RwLockReadGuard, RwLockWriteGuard}; + + /// Fallback implementation of `RwLock` from the standard library. + #[repr(transparent)] + pub struct RwLock { + inner: spin::RwLock, + } + + impl RwLock { + /// Creates a new instance of an `RwLock` which is unlocked. /// /// See the standard library for further details. - pub fn is_poisoned(&self) -> bool { - false + pub const fn new(t: T) -> RwLock { + Self { + inner: spin::RwLock::new(t), + } } + } - /// Clear the poisoned state from a lock. + impl RwLock { + /// Locks this `RwLock` with shared read access, blocking the current thread + /// until it can be acquired. /// /// See the standard library for further details. - pub fn clear_poison(&self) { - // no-op + pub fn read(&self) -> RwLockReadGuard<'_, T> { + self.inner.read() + } + + /// Attempts to acquire this `RwLock` with shared read access. + /// + /// See the standard library for further details. + pub fn try_read(&self) -> TryLockResult> { + self.inner.try_read().ok_or(TryLockError::WouldBlock) + } + + /// Locks this `RwLock` with exclusive write access, blocking the current + /// thread until it can be acquired. + /// + /// See the standard library for further details. + pub fn write(&self) -> RwLockWriteGuard<'_, T> { + self.inner.write() + } + + /// Attempts to lock this `RwLock` with exclusive write access. + /// + /// See the standard library for further details. + pub fn try_write(&self) -> TryLockResult> { + self.inner.try_write().ok_or(TryLockError::WouldBlock) } /// Consumes this `RwLock`, returning the underlying data. /// /// See the standard library for further details. - pub fn into_inner(self) -> LockResult + pub fn into_inner(self) -> T where T: Sized, { - Ok(self.inner.into_inner()) + 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()) + pub fn get_mut(&mut self) -> &mut T { + self.inner.get_mut() } } @@ -98,9 +219,6 @@ mod implementation { Ok(guard) => { d.field("data", &&*guard); } - Err(TryLockError::Poisoned(err)) => { - d.field("data", &&**err.get_ref()); - } Err(TryLockError::WouldBlock) => { d.field("data", &format_args!("")); } diff --git a/crates/bevy_reflect/src/func/registry.rs b/crates/bevy_reflect/src/func/registry.rs index 08ed7bd7f1057..3ec432f8149c7 100644 --- a/crates/bevy_reflect/src/func/registry.rs +++ b/crates/bevy_reflect/src/func/registry.rs @@ -1,8 +1,5 @@ use alloc::borrow::Cow; -use bevy_platform::{ - collections::HashMap, - sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, -}; +use bevy_platform::collections::HashMap; use core::fmt::Debug; use crate::func::{ @@ -333,27 +330,6 @@ impl Debug for FunctionRegistry { } } -/// A synchronized wrapper around a [`FunctionRegistry`]. -#[derive(Clone, Default, Debug)] -pub struct FunctionRegistryArc { - /// The wrapped [`FunctionRegistry`]. - pub internal: Arc>, -} - -impl FunctionRegistryArc { - /// Takes a read lock on the underlying [`FunctionRegistry`]. - pub fn read(&self) -> RwLockReadGuard<'_, FunctionRegistry> { - self.internal.read().unwrap_or_else(PoisonError::into_inner) - } - - /// Takes a write lock on the underlying [`FunctionRegistry`]. - pub fn write(&self) -> RwLockWriteGuard<'_, FunctionRegistry> { - self.internal - .write() - .unwrap_or_else(PoisonError::into_inner) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index d534cbff889b5..429b6906c0fb4 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -1,9 +1,6 @@ use crate::{serde::Serializable, FromReflect, Reflect, TypeInfo, TypePath, Typed}; use alloc::{boxed::Box, string::String}; -use bevy_platform::{ - collections::{HashMap, HashSet}, - sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, -}; +use bevy_platform::collections::{HashMap, HashSet}; use bevy_ptr::{Ptr, PtrMut}; use bevy_utils::TypeIdMap; use core::{ @@ -33,23 +30,9 @@ pub struct TypeRegistry { ambiguous_names: HashSet<&'static str>, } -// TODO: remove this wrapper once we migrate to Atelier Assets and the Scene AssetLoader doesn't -// need a TypeRegistry ref -/// A synchronized wrapper around a [`TypeRegistry`]. -#[derive(Clone, Default)] -pub struct TypeRegistryArc { - /// The wrapped [`TypeRegistry`]. - pub internal: Arc>, -} - -impl Debug for TypeRegistryArc { +impl Debug for TypeRegistry { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - self.internal - .read() - .unwrap_or_else(PoisonError::into_inner) - .type_path_to_id - .keys() - .fmt(f) + self.type_path_to_id.keys().fmt(f) } } @@ -506,20 +489,6 @@ impl TypeRegistry { } } -impl TypeRegistryArc { - /// Takes a read lock on the underlying [`TypeRegistry`]. - pub fn read(&self) -> RwLockReadGuard<'_, TypeRegistry> { - self.internal.read().unwrap_or_else(PoisonError::into_inner) - } - - /// Takes a write lock on the underlying [`TypeRegistry`]. - pub fn write(&self) -> RwLockWriteGuard<'_, TypeRegistry> { - self.internal - .write() - .unwrap_or_else(PoisonError::into_inner) - } -} - /// Runtime storage for type metadata, registered into the [`TypeRegistry`]. /// /// An instance of `TypeRegistration` can be created using the [`TypeRegistration::of`] method, diff --git a/crates/bevy_reflect/src/utility.rs b/crates/bevy_reflect/src/utility.rs index 42d8d8be79fb7..d9bc0182a28ed 100644 --- a/crates/bevy_reflect/src/utility.rs +++ b/crates/bevy_reflect/src/utility.rs @@ -4,7 +4,7 @@ use crate::TypeInfo; use alloc::boxed::Box; use bevy_platform::{ hash::{DefaultHasher, FixedHasher, NoOpHash}, - sync::{OnceLock, PoisonError, RwLock}, + sync::{OnceLock, RwLock}, }; use bevy_utils::TypeIdMap; use core::{ @@ -252,11 +252,7 @@ impl GenericTypeCell { /// /// This method will then return the correct [`TypedProperty`] reference for the given type `T`. fn get_by_type_id(&self, type_id: TypeId) -> Option<&T::Stored> { - self.0 - .read() - .unwrap_or_else(PoisonError::into_inner) - .get(&type_id) - .copied() + self.0.read().get(&type_id).copied() } /// Returns a reference to the [`TypedProperty`] stored in the cell. @@ -274,7 +270,7 @@ impl GenericTypeCell { } fn insert_by_type_id(&self, type_id: TypeId, value: T::Stored) -> &T::Stored { - let mut write_lock = self.0.write().unwrap_or_else(PoisonError::into_inner); + let mut write_lock = self.0.write(); write_lock .entry(type_id) diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 39c6a074e6f57..20f796dec8b94 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -7,9 +7,9 @@ use bevy_ecs::{ system::{ReadOnlySystemParam, SystemParam, SystemParamItem, SystemState}, world::World, }; +use bevy_platform::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use bevy_utils::TypeIdMap; use core::{any::TypeId, fmt::Debug, hash::Hash}; -use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}; use thiserror::Error; use variadics_please::all_tuples; @@ -132,14 +132,12 @@ impl Default for DrawFunctions

{ impl DrawFunctions

{ /// Accesses the draw functions in read mode. pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal

> { - self.internal.read().unwrap_or_else(PoisonError::into_inner) + self.internal.read() } /// Accesses the draw functions in write mode. pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal

> { - self.internal - .write() - .unwrap_or_else(PoisonError::into_inner) + self.internal.write() } } diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 1224e1998fb0d..af342ba9f8b5d 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -12,6 +12,7 @@ use bevy_ecs::{ system::{Res, ResMut}, }; use bevy_platform::collections::{HashMap, HashSet}; +use bevy_platform::sync::Mutex; use bevy_shader::{ CachedPipelineId, PipelineCacheError, Shader, ShaderCache, ShaderCacheSource, ShaderDefVal, ValidateShader, @@ -19,7 +20,6 @@ use bevy_shader::{ use bevy_tasks::Task; use bevy_utils::default; use core::{future::Future, hash::Hash, mem}; -use std::sync::{Mutex, PoisonError}; use tracing::error; use wgpu::{PipelineCompilationOptions, VertexBufferLayout as RawVertexBufferLayout}; @@ -409,10 +409,7 @@ impl PipelineCache { &self, descriptor: RenderPipelineDescriptor, ) -> CachedRenderPipelineId { - let mut new_pipelines = self - .new_pipelines - .lock() - .unwrap_or_else(PoisonError::into_inner); + let mut new_pipelines = self.new_pipelines.lock(); let id = CachedRenderPipelineId(self.pipelines.len() + new_pipelines.len()); new_pipelines.push(CachedPipeline { descriptor: PipelineDescriptor::RenderPipelineDescriptor(Box::new(descriptor)), @@ -438,10 +435,7 @@ impl PipelineCache { &self, descriptor: ComputePipelineDescriptor, ) -> CachedComputePipelineId { - let mut new_pipelines = self - .new_pipelines - .lock() - .unwrap_or_else(PoisonError::into_inner); + let mut new_pipelines = self.new_pipelines.lock(); let id = CachedComputePipelineId(self.pipelines.len() + new_pipelines.len()); new_pipelines.push(CachedPipeline { descriptor: PipelineDescriptor::ComputePipelineDescriptor(Box::new(descriptor)), @@ -451,7 +445,7 @@ impl PipelineCache { } fn set_shader(&mut self, id: AssetId, shader: Shader) { - let mut shader_cache = self.shader_cache.lock().unwrap(); + let mut shader_cache = self.shader_cache.lock(); let pipelines_to_queue = shader_cache.set_shader(id, shader); for cached_pipeline in pipelines_to_queue { self.pipelines[cached_pipeline].state = CachedPipelineState::Queued; @@ -460,7 +454,7 @@ impl PipelineCache { } fn remove_shader(&mut self, shader: AssetId) { - let mut shader_cache = self.shader_cache.lock().unwrap(); + let mut shader_cache = self.shader_cache.lock(); let pipelines_to_queue = shader_cache.remove(shader); for cached_pipeline in pipelines_to_queue { self.pipelines[cached_pipeline].state = CachedPipelineState::Queued; @@ -479,8 +473,8 @@ impl PipelineCache { create_pipeline_task( async move { - let mut shader_cache = shader_cache.lock().unwrap(); - let mut layout_cache = layout_cache.lock().unwrap(); + let mut shader_cache = shader_cache.lock(); + let mut layout_cache = layout_cache.lock(); let vertex_module = match shader_cache.get( &device, @@ -590,8 +584,8 @@ impl PipelineCache { create_pipeline_task( async move { - let mut shader_cache = shader_cache.lock().unwrap(); - let mut layout_cache = layout_cache.lock().unwrap(); + let mut shader_cache = shader_cache.lock(); + let mut layout_cache = layout_cache.lock(); let compute_module = match shader_cache.get( &device, @@ -649,10 +643,7 @@ impl PipelineCache { let mut pipelines = mem::take(&mut self.pipelines); { - let mut new_pipelines = self - .new_pipelines - .lock() - .unwrap_or_else(PoisonError::into_inner); + let mut new_pipelines = self.new_pipelines.lock(); for new_pipeline in new_pipelines.drain(..) { let id = pipelines.len(); pipelines.push(new_pipeline); @@ -698,8 +689,7 @@ impl PipelineCache { // Shader could not be processed ... retrying won't help PipelineCacheError::ProcessShaderError(err) => { - let error_detail = - err.emit_to_string(&self.shader_cache.lock().unwrap().composer); + let error_detail = err.emit_to_string(&self.shader_cache.lock().composer); if std::env::var("VERBOSE_SHADER_ERROR") .is_ok_and(|v| !(v.is_empty() || v == "0" || v == "false")) { diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 516f48e843849..e3aabef484d7d 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -206,10 +206,7 @@ pub async fn initialize_renderer( ); let surface = primary_window.and_then(|wrapper| { - let maybe_handle = wrapper - .0 - .lock() - .expect("Couldn't get the window handle in time for renderer initialization"); + let maybe_handle = wrapper.0.lock(); if let Some(wrapper) = maybe_handle.as_ref() { // SAFETY: Plugins should be set up on the main thread. let handle = unsafe { wrapper.get_handle() }; diff --git a/crates/bevy_scene/src/scene_loader.rs b/crates/bevy_scene/src/scene_loader.rs index d74dff84f5a6d..c2978243f8d25 100644 --- a/crates/bevy_scene/src/scene_loader.rs +++ b/crates/bevy_scene/src/scene_loader.rs @@ -3,7 +3,6 @@ use bevy_ecs::{ reflect::AppTypeRegistry, world::{FromWorld, World}, }; -use bevy_reflect::TypeRegistryArc; use thiserror::Error; #[cfg(feature = "serialize")] @@ -22,14 +21,14 @@ pub struct SceneLoader { not(feature = "serialize"), expect(dead_code, reason = "only used with `serialize` feature") )] - type_registry: TypeRegistryArc, + type_registry: AppTypeRegistry, } impl FromWorld for SceneLoader { fn from_world(world: &mut World) -> Self { let type_registry = world.resource::(); SceneLoader { - type_registry: type_registry.0.clone(), + type_registry: (*type_registry).clone(), } } } diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index 30c12acfec804..ea43e84952cbc 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -96,7 +96,7 @@ pub fn create_windows( if let Ok(handle_wrapper) = RawHandleWrapper::new(winit_window) { commands.entity(entity).insert(handle_wrapper.clone()); if let Some(handle_holder) = handle_holder { - *handle_holder.0.lock().unwrap() = Some(handle_wrapper); + *handle_holder.0.lock() = Some(handle_wrapper); } } From 90e2669f510b5cc5a6642ed86949cd0485b23f22 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 00:51:43 -0700 Subject: [PATCH 02/13] More cleanup --- crates/bevy_anti_aliasing/Cargo.toml | 1 + .../src/contrast_adaptive_sharpening/node.rs | 5 ++--- crates/bevy_anti_aliasing/src/dlss/node.rs | 4 ++-- crates/bevy_anti_aliasing/src/dlss/prepare.rs | 7 +++---- crates/bevy_anti_aliasing/src/fxaa/node.rs | 5 ++--- crates/bevy_asset/src/lib.rs | 2 +- .../bevy_core_pipeline/src/tonemapping/node.rs | 7 +++---- .../bevy_core_pipeline/src/upscaling/node.rs | 4 ++-- .../system_information_diagnostics_plugin.rs | 5 ++--- crates/bevy_ecs/src/lib.rs | 15 +++++++-------- .../src/schedule/executor/multi_threaded.rs | 17 +++++++---------- crates/bevy_ecs/src/world/mod.rs | 13 ++++++++----- crates/bevy_gltf/src/lib.rs | 7 +++---- crates/bevy_gltf/src/loader/mod.rs | 8 +++++--- crates/bevy_reflect/src/lib.rs | 4 ++-- crates/bevy_render/src/lib.rs | 6 +++--- .../bevy_render/src/view/window/screenshot.rs | 18 +++++++----------- crates/bevy_tasks/src/edge_executor.rs | 6 +++--- crates/bevy_winit/src/accessibility.rs | 10 ++++------ examples/3d/occlusion_culling.rs | 5 ++--- 20 files changed, 69 insertions(+), 80 deletions(-) diff --git a/crates/bevy_anti_aliasing/Cargo.toml b/crates/bevy_anti_aliasing/Cargo.toml index 696aa026ee24a..1431d69f65e06 100644 --- a/crates/bevy_anti_aliasing/Cargo.toml +++ b/crates/bevy_anti_aliasing/Cargo.toml @@ -28,6 +28,7 @@ bevy_app = { path = "../bevy_app", version = "0.17.0-dev" } bevy_image = { path = "../bevy_image", version = "0.17.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" } bevy_shader = { path = "../bevy_shader", version = "0.17.0-dev" } +bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.17.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" } diff --git a/crates/bevy_anti_aliasing/src/contrast_adaptive_sharpening/node.rs b/crates/bevy_anti_aliasing/src/contrast_adaptive_sharpening/node.rs index c1e6ccdda02c3..cd5d81e41b60f 100644 --- a/crates/bevy_anti_aliasing/src/contrast_adaptive_sharpening/node.rs +++ b/crates/bevy_anti_aliasing/src/contrast_adaptive_sharpening/node.rs @@ -1,7 +1,6 @@ -use std::sync::Mutex; - use crate::contrast_adaptive_sharpening::ViewCasPipeline; use bevy_ecs::prelude::*; +use bevy_platform::sync::Mutex; use bevy_render::{ diagnostic::RecordDiagnostics, extract_component::{ComponentUniforms, DynamicUniformIndex}, @@ -73,7 +72,7 @@ impl Node for CasNode { let source = view_target.source; let destination = view_target.destination; - let mut cached_bind_group = self.cached_bind_group.lock().unwrap(); + let mut cached_bind_group = self.cached_bind_group.lock(); let bind_group = match &mut *cached_bind_group { Some((buffer_id, texture_id, bind_group)) if source.id() == *texture_id && uniforms_id == *buffer_id => diff --git a/crates/bevy_anti_aliasing/src/dlss/node.rs b/crates/bevy_anti_aliasing/src/dlss/node.rs index aae59fa95b821..be8e294936684 100644 --- a/crates/bevy_anti_aliasing/src/dlss/node.rs +++ b/crates/bevy_anti_aliasing/src/dlss/node.rs @@ -72,7 +72,7 @@ impl ViewNode for DlssNode { let diagnostics = render_context.diagnostic_recorder(); let command_encoder = render_context.command_encoder(); - let mut dlss_context = dlss_context.context.lock().unwrap(); + let mut dlss_context = dlss_context.context.lock(); command_encoder.push_debug_group("dlss_super_resolution"); let time_span = diagnostics.time_span(command_encoder, "dlss_super_resolution"); @@ -148,7 +148,7 @@ impl ViewNode for DlssNode { let diagnostics = render_context.diagnostic_recorder(); let command_encoder = render_context.command_encoder(); - let mut dlss_context = dlss_context.context.lock().unwrap(); + let mut dlss_context = dlss_context.context.lock(); command_encoder.push_debug_group("dlss_ray_reconstruction"); let time_span = diagnostics.time_span(command_encoder, "dlss_ray_reconstruction"); diff --git a/crates/bevy_anti_aliasing/src/dlss/prepare.rs b/crates/bevy_anti_aliasing/src/dlss/prepare.rs index a8e88f57d012e..a85c794d0fe09 100644 --- a/crates/bevy_anti_aliasing/src/dlss/prepare.rs +++ b/crates/bevy_anti_aliasing/src/dlss/prepare.rs @@ -9,6 +9,7 @@ use bevy_ecs::{ system::{Commands, Query, Res}, }; use bevy_math::Vec4Swizzles; +use bevy_platform::sync::{Arc, Mutex}; use bevy_render::{ camera::{MipBias, TemporalJitter}, render_resource::TextureUsages, @@ -16,7 +17,6 @@ use bevy_render::{ view::ExtractedView, }; use dlss_wgpu::{DlssFeatureFlags, DlssPerfQualityMode}; -use std::sync::{Arc, Mutex}; #[derive(Component)] pub struct DlssRenderContext { @@ -76,12 +76,11 @@ pub fn prepare_dlss( match dlss_context.as_deref_mut() { Some(dlss_context) - if upscaled_resolution - == F::upscaled_resolution(&dlss_context.context.lock().unwrap()) + if upscaled_resolution == F::upscaled_resolution(&dlss_context.context.lock()) && dlss.perf_quality_mode == dlss_context.perf_quality_mode && dlss_feature_flags == dlss_context.feature_flags => { - let dlss_context = dlss_context.context.lock().unwrap(); + let dlss_context = dlss_context.context.lock(); let render_resolution = F::render_resolution(&dlss_context); temporal_jitter.offset = F::suggested_jitter(&dlss_context, frame_count.0, render_resolution); diff --git a/crates/bevy_anti_aliasing/src/fxaa/node.rs b/crates/bevy_anti_aliasing/src/fxaa/node.rs index 54d2afd33e736..2b2e724914b8b 100644 --- a/crates/bevy_anti_aliasing/src/fxaa/node.rs +++ b/crates/bevy_anti_aliasing/src/fxaa/node.rs @@ -1,7 +1,6 @@ -use std::sync::Mutex; - use crate::fxaa::{CameraFxaaPipeline, Fxaa, FxaaPipeline}; use bevy_ecs::{prelude::*, query::QueryItem}; +use bevy_platform::sync::Mutex; use bevy_render::{ diagnostic::RecordDiagnostics, render_graph::{NodeRunError, RenderGraphContext, ViewNode}, @@ -48,7 +47,7 @@ impl ViewNode for FxaaNode { let post_process = target.post_process_write(); let source = post_process.source; let destination = post_process.destination; - let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap(); + let mut cached_bind_group = self.cached_texture_bind_group.lock(); let bind_group = match &mut *cached_bind_group { Some((id, bind_group)) if source.id() == *id => bind_group, cached_bind_group => { diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index a4409ac947250..d58a244dc6fb6 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -856,7 +856,7 @@ mod tests { } async fn read<'a>(&'a self, path: &'a Path) -> Result { let attempt_number = { - let mut attempt_counters = self.attempt_counters.lock().unwrap(); + let mut attempt_counters = self.attempt_counters.lock(); if let Some(existing) = attempt_counters.get_mut(path) { *existing += 1; *existing diff --git a/crates/bevy_core_pipeline/src/tonemapping/node.rs b/crates/bevy_core_pipeline/src/tonemapping/node.rs index d14f1251fc4b6..5f34d06564906 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/node.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/node.rs @@ -1,8 +1,7 @@ -use std::sync::Mutex; - use crate::tonemapping::{TonemappingLuts, TonemappingPipeline, ViewTonemappingPipeline}; use bevy_ecs::{prelude::*, query::QueryItem}; +use bevy_platform::sync::Mutex; use bevy_render::{ diagnostic::RecordDiagnostics, render_asset::RenderAssets, @@ -67,7 +66,7 @@ impl ViewNode for TonemappingNode { let source = post_process.source; let destination = post_process.destination; - let mut last_tonemapping = self.last_tonemapping.lock().unwrap(); + let mut last_tonemapping = self.last_tonemapping.lock(); let tonemapping_changed = if let Some(last_tonemapping) = &*last_tonemapping { tonemapping != last_tonemapping @@ -78,7 +77,7 @@ impl ViewNode for TonemappingNode { *last_tonemapping = Some(*tonemapping); } - let mut cached_bind_group = self.cached_bind_group.lock().unwrap(); + let mut cached_bind_group = self.cached_bind_group.lock(); let bind_group = match &mut *cached_bind_group { Some((buffer_id, texture_id, lut_id, bind_group)) if view_uniforms_id == *buffer_id diff --git a/crates/bevy_core_pipeline/src/upscaling/node.rs b/crates/bevy_core_pipeline/src/upscaling/node.rs index e00924460c271..df097dc627ddb 100644 --- a/crates/bevy_core_pipeline/src/upscaling/node.rs +++ b/crates/bevy_core_pipeline/src/upscaling/node.rs @@ -1,6 +1,7 @@ use crate::{blit::BlitPipeline, upscaling::ViewUpscalingPipeline}; use bevy_camera::{CameraOutputMode, ClearColor, ClearColorConfig}; use bevy_ecs::{prelude::*, query::QueryItem}; +use bevy_platform::sync::Mutex; use bevy_render::{ camera::ExtractedCamera, diagnostic::RecordDiagnostics, @@ -9,7 +10,6 @@ use bevy_render::{ renderer::RenderContext, view::ViewTarget, }; -use std::sync::Mutex; #[derive(Default)] pub struct UpscalingNode { @@ -53,7 +53,7 @@ impl ViewNode for UpscalingNode { // texture to be upscaled to the output texture let main_texture_view = target.main_texture_view(); - let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap(); + let mut cached_bind_group = self.cached_texture_bind_group.lock(); let bind_group = match &mut *cached_bind_group { Some((id, bind_group)) if main_texture_view.id() == *id => bind_group, cached_bind_group => { diff --git a/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs index 83d3663895ca5..39c0c4d23c0e2 100644 --- a/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs @@ -79,10 +79,9 @@ pub mod internal { use bevy_app::{App, First, Startup, Update}; use bevy_ecs::resource::Resource; use bevy_ecs::{prelude::ResMut, system::Local}; - use bevy_platform::time::Instant; + use bevy_platform::{sync::Mutex, time::Instant}; use bevy_tasks::{available_parallelism, block_on, poll_once, AsyncComputeTaskPool, Task}; use log::info; - use std::sync::Mutex; use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System}; use crate::{Diagnostic, Diagnostics, DiagnosticsStore}; @@ -154,7 +153,7 @@ pub mod internal { { let sys = Arc::clone(sysinfo); let task = thread_pool.spawn(async move { - let mut sys = sys.lock().unwrap(); + let mut sys = sys.lock(); let pid = sysinfo::get_current_pid().expect("Failed to get current process ID"); sys.refresh_processes(sysinfo::ProcessesToUpdate::Some(&[pid]), true); diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index cb0c40913c042..e9477b985c928 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -169,14 +169,13 @@ mod tests { world::{EntityMut, EntityRef, Mut, World}, }; use alloc::{string::String, sync::Arc, vec, vec::Vec}; - use bevy_platform::collections::HashSet; + use bevy_platform::{collections::HashSet, sync::Mutex}; use bevy_tasks::{ComputeTaskPool, TaskPool}; use core::{ any::TypeId, marker::PhantomData, sync::atomic::{AtomicUsize, Ordering}, }; - use std::sync::Mutex; #[derive(Component, Resource, Debug, PartialEq, Eq, Hash, Clone, Copy)] struct A(usize); @@ -507,12 +506,12 @@ mod tests { .query::<(Entity, &A)>() .par_iter(&world) .for_each(|(e, &A(i))| { - results.lock().unwrap().push((e, i)); + results.lock().push((e, i)); }); - results.lock().unwrap().sort(); + results.lock().sort(); let mut expected = [(e1, 1), (e2, 2), (e3, 3), (e4, 4), (e5, 5)]; expected.sort(); - assert_eq!(&*results.lock().unwrap(), &expected); + assert_eq!(&*results.lock(), &expected); } #[test] @@ -528,11 +527,11 @@ mod tests { world .query::<(Entity, &SparseStored)>() .par_iter(&world) - .for_each(|(e, &SparseStored(i))| results.lock().unwrap().push((e, i))); - results.lock().unwrap().sort(); + .for_each(|(e, &SparseStored(i))| results.lock().push((e, i))); + results.lock().sort(); let mut expected = [(e1, 1), (e2, 2), (e3, 3), (e4, 4), (e5, 5)]; expected.sort(); - assert_eq!(&*results.lock().unwrap(), &expected); + assert_eq!(&*results.lock(), &expected); } #[test] diff --git a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs index bd99344f498fa..963ad59ab6ac0 100644 --- a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs @@ -1,13 +1,12 @@ use alloc::{boxed::Box, vec::Vec}; use bevy_platform::cell::SyncUnsafeCell; -use bevy_platform::sync::Arc; +use bevy_platform::sync::{Arc, Mutex, MutexGuard}; use bevy_tasks::{ComputeTaskPool, Scope, TaskPool, ThreadExecutor}; use concurrent_queue::ConcurrentQueue; use core::{any::Any, panic::AssertUnwindSafe}; use fixedbitset::FixedBitSet; #[cfg(feature = "std")] use std::eprintln; -use std::sync::{Mutex, MutexGuard}; #[cfg(feature = "trace")] use tracing::{info_span, Span}; @@ -154,7 +153,7 @@ impl SystemExecutor for MultiThreadedExecutor { } fn init(&mut self, schedule: &SystemSchedule) { - let state = self.state.get_mut().unwrap(); + let state = self.state.get_mut(); // pre-allocate space let sys_count = schedule.system_ids.len(); let set_count = schedule.set_ids.len(); @@ -242,7 +241,7 @@ impl SystemExecutor for MultiThreadedExecutor { _skip_systems: Option<&FixedBitSet>, error_handler: ErrorHandler, ) { - let state = self.state.get_mut().unwrap(); + let state = self.state.get_mut(); // reset counts if schedule.systems.is_empty() { return; @@ -295,21 +294,19 @@ impl SystemExecutor for MultiThreadedExecutor { // End the borrows of self and world in environment by copying out the reference to systems. let systems = environment.systems; - let state = self.state.get_mut().unwrap(); + let state = self.state.get_mut(); if self.apply_final_deferred { // Do one final apply buffers after all systems have completed // Commands should be applied while on the scope's thread, not the executor's thread let res = apply_deferred(&state.unapplied_systems, systems, world); if let Err(payload) = res { - let panic_payload = self.panic_payload.get_mut().unwrap(); - *panic_payload = Some(payload); + *self.panic_payload.get_mut() = Some(payload); } state.unapplied_systems.clear(); } // check to see if there was a panic - let payload = self.panic_payload.get_mut().unwrap(); - if let Some(payload) = payload.take() { + if let Some(payload) = self.panic_payload.get_mut().take() { std::panic::resume_unwind(payload); } @@ -346,7 +343,7 @@ impl<'scope, 'env: 'scope, 'sys> Context<'scope, 'env, 'sys> { } // set the payload to propagate the error { - let mut panic_payload = self.environment.executor.panic_payload.lock().unwrap(); + let mut panic_payload = self.environment.executor.panic_payload.lock(); *panic_payload = Some(payload); } } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 7632e30159f7a..20625f539fab3 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -3691,14 +3691,17 @@ mod tests { vec::Vec, }; use bevy_ecs_macros::Component; - use bevy_platform::collections::{HashMap, HashSet}; + use bevy_platform::{ + collections::{HashMap, HashSet}, + sync::Mutex, + }; use bevy_utils::prelude::DebugName; use core::{ any::TypeId, panic, sync::atomic::{AtomicBool, AtomicU32, Ordering}, }; - use std::{println, sync::Mutex}; + use std::println; type ID = u8; @@ -3724,7 +3727,7 @@ mod tests { id: u8, ) -> Self { println!("creating component with id {id}"); - drop_log.lock().unwrap().push(DropLogItem::Create(id)); + drop_log.lock().push(DropLogItem::Create(id)); Self { drop_log: Arc::clone(drop_log), @@ -3740,7 +3743,7 @@ mod tests { println!("dropping component with id {}", self.id); { - let mut drop_log = self.drop_log.lock().unwrap(); + let mut drop_log = self.drop_log.lock(); drop_log.push(DropLogItem::Drop(self.id)); // Don't keep the mutex while panicking, or we'll poison it. drop(drop_log); @@ -3773,7 +3776,7 @@ mod tests { } pub fn finish(self, panic_res: std::thread::Result<()>) -> Vec { - let drop_log = self.drop_log.lock().unwrap(); + let drop_log = self.drop_log.lock(); let expected_panic_flag = self.expected_panic_flag.load(Ordering::SeqCst); if !expected_panic_flag { diff --git a/crates/bevy_gltf/src/lib.rs b/crates/bevy_gltf/src/lib.rs index 96b9d7f1d917c..89d798443c327 100644 --- a/crates/bevy_gltf/src/lib.rs +++ b/crates/bevy_gltf/src/lib.rs @@ -99,10 +99,9 @@ mod vertex_attributes; extern crate alloc; use alloc::sync::Arc; -use std::sync::Mutex; use tracing::warn; -use bevy_platform::collections::HashMap; +use bevy_platform::{collections::HashMap, sync::Mutex}; use bevy_app::prelude::*; use bevy_asset::AssetApp; @@ -133,7 +132,7 @@ impl DefaultGltfImageSampler { /// Returns the current default [`ImageSamplerDescriptor`]. pub fn get(&self) -> ImageSamplerDescriptor { - self.0.lock().unwrap().clone() + self.0.lock().clone() } /// Makes a clone of internal [`Arc`] pointer. @@ -148,7 +147,7 @@ impl DefaultGltfImageSampler { /// Doesn't apply to samplers already built on top of it, i.e. `GltfLoader`'s output. /// Assets need to manually be reloaded. pub fn set(&self, descriptor: &ImageSamplerDescriptor) { - *self.0.lock().unwrap() = descriptor.clone(); + *self.0.lock() = descriptor.clone(); } } diff --git a/crates/bevy_gltf/src/loader/mod.rs b/crates/bevy_gltf/src/loader/mod.rs index 8aa7db8c34e6f..75be731b7ebc3 100644 --- a/crates/bevy_gltf/src/loader/mod.rs +++ b/crates/bevy_gltf/src/loader/mod.rs @@ -5,7 +5,6 @@ use alloc::sync::Arc; use std::{ io::Error, path::{Path, PathBuf}, - sync::Mutex, }; #[cfg(feature = "bevy_animation")] @@ -39,7 +38,10 @@ use bevy_mesh::{ #[cfg(feature = "pbr_transmission_textures")] use bevy_pbr::UvChannel; use bevy_pbr::{MeshMaterial3d, StandardMaterial, MAX_JOINTS}; -use bevy_platform::collections::{HashMap, HashSet}; +use bevy_platform::{ + collections::{HashMap, HashSet}, + sync::Mutex, +}; use bevy_render::render_resource::Face; use bevy_scene::Scene; #[cfg(not(target_arch = "wasm32"))] @@ -575,7 +577,7 @@ impl GltfLoader { let default_sampler = match settings.default_sampler.as_ref() { Some(sampler) => sampler, - None => &loader.default_sampler.lock().unwrap().clone(), + None => &loader.default_sampler.lock().clone(), }; // We collect handles to ensure loaded images from paths are not unloaded before they are used elsewhere // in the loader. This prevents "reloads", but it also prevents dropping the is_srgb context on reload. diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 477c222a345ce..72c83a157c0f3 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -814,12 +814,12 @@ pub mod __macro_exports { /// Adds adds a new registration function for [`TypeRegistry`] pub fn push_registration_fn(registration_fn: fn(&mut TypeRegistry)) { - REGISTRATION_FNS.lock().unwrap().push(registration_fn); + REGISTRATION_FNS.lock().push(registration_fn); } /// Registers all collected types. pub fn register_types(registry: &mut TypeRegistry) { - for func in REGISTRATION_FNS.lock().unwrap().iter() { + for func in REGISTRATION_FNS.lock().iter() { (func)(registry); } } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 8c54629fbe0b0..48b93c9f1852a 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -98,6 +98,7 @@ use bevy_ecs::{ schedule::{ScheduleBuildSettings, ScheduleLabel}, }; use bevy_image::{CompressedImageFormatSupport, CompressedImageFormats}; +use bevy_platform::sync::Mutex; use bevy_shader::{load_shader_library, Shader, ShaderLoader}; use bevy_utils::prelude::default; use bevy_window::{PrimaryWindow, RawHandleWrapperHolder}; @@ -110,7 +111,6 @@ use render_asset::{ RenderAssetBytesPerFrame, RenderAssetBytesPerFrameLimiter, }; use settings::RenderResources; -use std::sync::Mutex; use sync_world::{despawn_temporary_render_entities, entity_sync_system, SyncWorldPlugin}; pub use wgpu_wrapper::WgpuWrapper; @@ -346,7 +346,7 @@ impl Plugin for RenderPlugin { ) .await; - *future_render_resources_wrapper.lock().unwrap() = Some(render_resources); + *future_render_resources_wrapper.lock() = Some(render_resources); }; // In wasm, spawn a task and detach it for execution @@ -411,7 +411,7 @@ impl Plugin for RenderPlugin { if let Some(future_render_resources) = app.world_mut().remove_resource::() { - let render_resources = future_render_resources.0.lock().unwrap().take().unwrap(); + let render_resources = future_render_resources.0.lock().take().unwrap(); let RenderResources(device, queue, adapter_info, render_adapter, instance, ..) = render_resources; diff --git a/crates/bevy_render/src/view/window/screenshot.rs b/crates/bevy_render/src/view/window/screenshot.rs index 19311f796d671..646fef27844f9 100644 --- a/crates/bevy_render/src/view/window/screenshot.rs +++ b/crates/bevy_render/src/view/window/screenshot.rs @@ -13,7 +13,7 @@ use crate::{ view::{prepare_view_attachments, prepare_view_targets, ViewTargetAttachments, WindowSurfaces}, ExtractSchedule, MainWorld, Render, RenderApp, RenderStartup, RenderSystems, }; -use alloc::{borrow::Cow, sync::Arc}; +use alloc::borrow::Cow; use bevy_app::{First, Plugin, Update}; use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle, RenderAssetUsages}; use bevy_camera::{ManualTextureViewHandle, NormalizedRenderTarget, RenderTarget}; @@ -22,7 +22,7 @@ use bevy_ecs::{ entity::EntityHashMap, event::event_update_system, prelude::*, system::SystemState, }; use bevy_image::{Image, TextureFormatPixelInfo, ToExtents}; -use bevy_platform::collections::HashSet; +use bevy_platform::{cell::SyncCell, collections::HashSet}; use bevy_reflect::Reflect; use bevy_shader::Shader; use bevy_tasks::AsyncComputeTaskPool; @@ -31,10 +31,7 @@ use bevy_window::{PrimaryWindow, WindowRef}; use core::ops::Deref; use std::{ path::Path, - sync::{ - mpsc::{Receiver, Sender}, - Mutex, - }, + sync::mpsc::{Receiver, Sender}, }; use tracing::{error, info, warn}; use wgpu::{CommandEncoder, Extent3d, TextureFormat}; @@ -110,7 +107,7 @@ struct ScreenshotPreparedState { } #[derive(Resource, Deref, DerefMut)] -pub struct CapturedScreenshots(pub Arc>>); +pub struct CapturedScreenshots(pub SyncCell>); #[derive(Resource, Deref, DerefMut, Default)] struct RenderScreenshotTargets(EntityHashMap); @@ -191,10 +188,9 @@ fn clear_screenshots(mut commands: Commands, screenshots: Query, + mut captured_screenshots: ResMut, ) { - let captured_screenshots = captured_screenshots.lock().unwrap(); - while let Ok((entity, image)) = captured_screenshots.try_recv() { + while let Ok((entity, image)) = captured_screenshots.get().try_recv() { commands.entity(entity).insert(Captured); commands.trigger_targets(ScreenshotCaptured(image), entity); } @@ -396,7 +392,7 @@ impl Plugin for ScreenshotPlugin { embedded_asset!(app, "screenshot.wgsl"); let (tx, rx) = std::sync::mpsc::channel(); - app.insert_resource(CapturedScreenshots(Arc::new(Mutex::new(rx)))) + app.insert_resource(CapturedScreenshots(SyncCell::new(rx))) .add_systems( First, clear_screenshots diff --git a/crates/bevy_tasks/src/edge_executor.rs b/crates/bevy_tasks/src/edge_executor.rs index a8c80725cafe9..a9c2a7c2e7429 100644 --- a/crates/bevy_tasks/src/edge_executor.rs +++ b/crates/bevy_tasks/src/edge_executor.rs @@ -523,7 +523,7 @@ mod drop_tests { use core::mem; use core::sync::atomic::{AtomicUsize, Ordering}; use core::task::{Poll, Waker}; - use std::sync::Mutex; + use bevy_platform::sync::Mutex; use bevy_platform::sync::LazyLock; use futures_lite::future; @@ -543,14 +543,14 @@ mod drop_tests { }); future::poll_fn(|cx| { - *WAKER.lock().unwrap() = Some(cx.waker().clone()); + *WAKER.lock() = Some(cx.waker().clone()); Poll::Pending::<()> }) .await; }); future::block_on(ex.tick()); - assert!(WAKER.lock().unwrap().is_some()); + assert!(WAKER.lock().is_some()); assert_eq!(DROP.load(Ordering::SeqCst), 0); mem::forget(ex); diff --git a/crates/bevy_winit/src/accessibility.rs b/crates/bevy_winit/src/accessibility.rs index 634e6eac90436..fed6e00d22a54 100644 --- a/crates/bevy_winit/src/accessibility.rs +++ b/crates/bevy_winit/src/accessibility.rs @@ -2,8 +2,8 @@ use alloc::{collections::VecDeque, sync::Arc}; use bevy_input_focus::InputFocus; +use bevy_platform::sync::Mutex; use core::cell::RefCell; -use std::sync::Mutex; use winit::event_loop::ActiveEventLoop; use accesskit::{ @@ -96,7 +96,7 @@ struct WinitActivationHandler(Arc>); impl ActivationHandler for WinitActivationHandler { fn request_initial_tree(&mut self) -> Option { - Some(self.0.lock().unwrap().build_initial_tree()) + Some(self.0.lock().build_initial_tree()) } } @@ -111,8 +111,7 @@ struct WinitActionHandler(Arc>); impl ActionHandler for WinitActionHandler { fn do_action(&mut self, request: ActionRequest) { - let mut requests = self.0.lock().unwrap(); - requests.push_back(request); + self.0.lock().push_back(request); } } @@ -175,8 +174,7 @@ fn poll_receivers( mut actions: EventWriter, ) { for (_id, handler) in handlers.iter() { - let mut handler = handler.lock().unwrap(); - while let Some(event) = handler.pop_front() { + while let Some(event) = handler.lock().pop_front() { actions.write(ActionRequestWrapper(event)); } } diff --git a/examples/3d/occlusion_culling.rs b/examples/3d/occlusion_culling.rs index d61356b9bca8c..6055fb3dbd67c 100644 --- a/examples/3d/occlusion_culling.rs +++ b/examples/3d/occlusion_culling.rs @@ -148,8 +148,7 @@ fn init_saved_indirect_parameters( gpu_preprocessing_support: Res, saved_indirect_parameters: Res, ) { - let mut saved_indirect_parameters = saved_indirect_parameters.0.lock().unwrap(); - *saved_indirect_parameters = Some(SavedIndirectParametersData { + *saved_indirect_parameters.0.lock() = Some(SavedIndirectParametersData { data: vec![], count: 0, occlusion_culling_supported: gpu_preprocessing_support.is_culling_supported(), @@ -545,7 +544,7 @@ fn update_status_text( occlusion_culling_supported, occlusion_culling_introspection_supported, ): (u32, bool, bool) = { - let saved_indirect_parameters = saved_indirect_parameters.lock().unwrap(); + let saved_indirect_parameters = saved_indirect_parameters.lock(); let Some(saved_indirect_parameters) = saved_indirect_parameters.as_ref() else { // Bail out early if the resource isn't initialized yet. return; From dbc488617b6afebeaba19891a3910c1101448933 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 01:03:54 -0700 Subject: [PATCH 03/13] Do not lock in a loop --- crates/bevy_winit/src/accessibility.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/bevy_winit/src/accessibility.rs b/crates/bevy_winit/src/accessibility.rs index fed6e00d22a54..2ed47e524e8cb 100644 --- a/crates/bevy_winit/src/accessibility.rs +++ b/crates/bevy_winit/src/accessibility.rs @@ -174,7 +174,8 @@ fn poll_receivers( mut actions: EventWriter, ) { for (_id, handler) in handlers.iter() { - while let Some(event) = handler.lock().pop_front() { + let mut handler = handler.lock(); + while let Some(event) = handler.pop_front() { actions.write(ActionRequestWrapper(event)); } } From e4594fa026c41568da5c4ccde62885ad6254c4bc Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 01:26:42 -0700 Subject: [PATCH 04/13] More cleanup --- crates/bevy_remote/src/lib.rs | 17 +++++++++-------- examples/3d/occlusion_culling.rs | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/bevy_remote/src/lib.rs b/crates/bevy_remote/src/lib.rs index b8e5813177067..7e208b7ec0256 100644 --- a/crates/bevy_remote/src/lib.rs +++ b/crates/bevy_remote/src/lib.rs @@ -500,6 +500,8 @@ extern crate alloc; +use core::mem; + use async_channel::{Receiver, Sender}; use bevy_app::{prelude::*, MainScheduleOrder}; use bevy_derive::{Deref, DerefMut}; @@ -510,11 +512,10 @@ use bevy_ecs::{ system::{Commands, In, IntoSystem, ResMut, System, SystemId}, world::World, }; -use bevy_platform::collections::HashMap; +use bevy_platform::{collections::HashMap, sync::Mutex}; use bevy_utils::prelude::default; use serde::{Deserialize, Serialize}; use serde_json::Value; -use std::sync::RwLock; pub mod builtin_methods; #[cfg(feature = "http")] @@ -531,7 +532,7 @@ const CHANNEL_SIZE: usize = 16; /// [crate-level documentation]: crate pub struct RemotePlugin { /// The verbs that the server will recognize and respond to. - methods: RwLock>, + methods: Mutex>, } impl RemotePlugin { @@ -539,7 +540,7 @@ impl RemotePlugin { /// any associated methods. fn empty() -> Self { Self { - methods: RwLock::new(vec![]), + methods: Mutex::new(Vec::new()), } } @@ -550,7 +551,7 @@ impl RemotePlugin { name: impl Into, handler: impl IntoSystem>, BrpResult, M>, ) -> Self { - self.methods.get_mut().unwrap().push(( + self.methods.get_mut().push(( name.into(), RemoteMethodHandler::Instant(Box::new(IntoSystem::into_system(handler))), )); @@ -564,7 +565,7 @@ impl RemotePlugin { name: impl Into, handler: impl IntoSystem>, BrpResult>, M>, ) -> Self { - self.methods.get_mut().unwrap().push(( + self.methods.get_mut().push(( name.into(), RemoteMethodHandler::Watching(Box::new(IntoSystem::into_system(handler))), )); @@ -654,8 +655,8 @@ impl Plugin for RemotePlugin { fn build(&self, app: &mut App) { let mut remote_methods = RemoteMethods::new(); - let plugin_methods = &mut *self.methods.write().unwrap(); - for (name, handler) in plugin_methods.drain(..) { + // Take the Vec to ensure it's properly deallocated after this function ends. + for (name, handler) in mem::take(&mut *self.methods.lock()) { remote_methods.insert( name, match handler { diff --git a/examples/3d/occlusion_culling.rs b/examples/3d/occlusion_culling.rs index 6055fb3dbd67c..229b708f086c6 100644 --- a/examples/3d/occlusion_culling.rs +++ b/examples/3d/occlusion_culling.rs @@ -10,7 +10,6 @@ use std::{ f32::consts::PI, fmt::Write as _, result::Result, - sync::{Arc, Mutex}, }; use bevy::{ @@ -24,6 +23,7 @@ use bevy::{ }, pbr::PbrPlugin, prelude::*, + platform::{Arc, Mutex}, render::{ batching::gpu_preprocessing::{ GpuPreprocessingSupport, IndirectParametersBuffers, IndirectParametersIndexed, From b72c095fb2d51836bd98dd8ceaa87fbe52e64d19 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 01:44:26 -0700 Subject: [PATCH 05/13] More wrapper cleanup --- crates/bevy_render/src/render_phase/draw.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 20f796dec8b94..36eb82678fafb 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -7,7 +7,8 @@ use bevy_ecs::{ system::{ReadOnlySystemParam, SystemParam, SystemParamItem, SystemState}, world::World, }; -use bevy_platform::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use bevy_platform::sync::RwLock; +use derive_more::Deref; use bevy_utils::TypeIdMap; use core::{any::TypeId, fmt::Debug, hash::Hash}; use thiserror::Error; @@ -113,7 +114,7 @@ impl DrawFunctionsInternal

{ /// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock. /// /// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used. -#[derive(Resource)] +#[derive(Resource, Deref)] pub struct DrawFunctions { internal: RwLock>, } @@ -129,18 +130,6 @@ impl Default for DrawFunctions

{ } } -impl DrawFunctions

{ - /// Accesses the draw functions in read mode. - pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal

> { - self.internal.read() - } - - /// Accesses the draw functions in write mode. - pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal

> { - self.internal.write() - } -} - /// [`RenderCommand`]s are modular standardized pieces of render logic that can be composed into /// [`Draw`] functions. /// From 2ba952537542480c695ed953d23ae7f37ff40c5a Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 02:09:42 -0700 Subject: [PATCH 06/13] Fix states test --- crates/bevy_state/src/reflect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_state/src/reflect.rs b/crates/bevy_state/src/reflect.rs index 0ff09c3759d21..c7ea6bd136f58 100644 --- a/crates/bevy_state/src/reflect.rs +++ b/crates/bevy_state/src/reflect.rs @@ -122,7 +122,7 @@ mod tests { .insert_state(StateTest::A) .register_type_mutable_state::(); - let type_registry = app.world_mut().resource::().0.clone(); + let type_registry = app.world().resource::().clone(); let type_registry = type_registry.read(); let (reflect_state, reflect_mutable_state) = ( From 11ec5140300db61e8f7aad3c87622b3ae64f5a92 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 02:13:23 -0700 Subject: [PATCH 07/13] Formatting --- crates/bevy_render/src/render_phase/draw.rs | 2 +- examples/3d/occlusion_culling.rs | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 36eb82678fafb..8265ebb2f4bd2 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -8,9 +8,9 @@ use bevy_ecs::{ world::World, }; use bevy_platform::sync::RwLock; -use derive_more::Deref; use bevy_utils::TypeIdMap; use core::{any::TypeId, fmt::Debug, hash::Hash}; +use derive_more::Deref; use thiserror::Error; use variadics_please::all_tuples; diff --git a/examples/3d/occlusion_culling.rs b/examples/3d/occlusion_culling.rs index 229b708f086c6..a66e66097b193 100644 --- a/examples/3d/occlusion_culling.rs +++ b/examples/3d/occlusion_culling.rs @@ -5,12 +5,7 @@ //! cubes. The demo displays the number of cubes that were actually rendered, so //! the effects of occlusion culling can be seen. -use std::{ - any::TypeId, - f32::consts::PI, - fmt::Write as _, - result::Result, -}; +use std::{any::TypeId, f32::consts::PI, fmt::Write as _, result::Result}; use bevy::{ color::palettes::css::{SILVER, WHITE}, @@ -22,8 +17,8 @@ use bevy::{ prepass::DepthPrepass, }, pbr::PbrPlugin, - prelude::*, platform::{Arc, Mutex}, + prelude::*, render::{ batching::gpu_preprocessing::{ GpuPreprocessingSupport, IndirectParametersBuffers, IndirectParametersIndexed, From a97b7502860b14a513c254e88ae2e009998edcec Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 02:22:51 -0700 Subject: [PATCH 08/13] Fix FunctionRegistryArc references --- crates/bevy_ecs/src/reflect/mod.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index e2d453011bdf1..de07e8f814baf 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -6,6 +6,8 @@ use crate::{resource::Resource, world::World}; use bevy_reflect::{ std_traits::ReflectDefault, PartialReflect, Reflect, ReflectFromReflect, TypePath, TypeRegistry, }; +#[cfg(feature = "reflect_functions")] +use bevy_reflect::func::FunctionRegistry; mod bundle; mod component; @@ -55,11 +57,11 @@ impl AppTypeRegistry { /// [`FunctionRegistry`]: bevy_reflect::func::FunctionRegistry #[cfg(feature = "reflect_functions")] #[derive(Resource, Clone, Default)] -pub struct AppFunctionRegistry(pub bevy_reflect::func::FunctionRegistryArc); +pub struct AppFunctionRegistry(Arc>); #[cfg(feature = "reflect_functions")] impl Deref for AppFunctionRegistry { - type Target = bevy_reflect::func::FunctionRegistryArc; + type Target = RwLock; #[inline] fn deref(&self) -> &Self::Target { @@ -67,14 +69,6 @@ impl Deref for AppFunctionRegistry { } } -#[cfg(feature = "reflect_functions")] -impl DerefMut for AppFunctionRegistry { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - /// Creates a `T` from a `&dyn PartialReflect`. /// /// This will try the following strategies, in this order: From 0dad7893506a4065964c781622c34a46c9b10661 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 02:25:48 -0700 Subject: [PATCH 09/13] Formatting --- crates/bevy_ecs/src/reflect/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index de07e8f814baf..614efede8d51e 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -3,11 +3,11 @@ use core::{any::TypeId, ops::Deref}; use crate::{resource::Resource, world::World}; +#[cfg(feature = "reflect_functions")] +use bevy_reflect::func::FunctionRegistry; use bevy_reflect::{ std_traits::ReflectDefault, PartialReflect, Reflect, ReflectFromReflect, TypePath, TypeRegistry, }; -#[cfg(feature = "reflect_functions")] -use bevy_reflect::func::FunctionRegistry; mod bundle; mod component; From 811be03645ea9d58d03da8375526a26444c1497b Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 14:23:02 -0700 Subject: [PATCH 10/13] CI fixes --- crates/bevy_asset/src/lib.rs | 7 +++++-- crates/bevy_render/src/render_phase/draw.rs | 7 +++++-- examples/3d/occlusion_culling.rs | 5 +---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index ff19860a51577..ea7c5587f817f 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -727,7 +727,10 @@ mod tests { prelude::*, schedule::{LogLevel, ScheduleBuildSettings}, }; - use bevy_platform::collections::{HashMap, HashSet}; + use bevy_platform::{ + collections::{HashMap, HashSet}, + sync::Mutex, + }; use bevy_reflect::TypePath; use core::time::Duration; use serde::{Deserialize, Serialize}; @@ -823,7 +826,7 @@ mod tests { /// A dummy [`CoolText`] asset reader that only succeeds after `failure_count` times it's read from for each asset. #[derive(Default, Clone)] pub struct UnstableMemoryAssetReader { - pub attempt_counters: Arc, usize>>>, + pub attempt_counters: Arc, usize>>>, pub load_delay: Duration, memory_reader: MemoryAssetReader, failure_count: usize, diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 8265ebb2f4bd2..a2c84dbe9eb4a 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -111,9 +111,12 @@ impl DrawFunctionsInternal

{ } } -/// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock. +/// Stores all draw functions for the [`PhaseItem`] type behind a reader-writer lock. /// -/// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used. +/// This type dereferences to a [`RwLock`]. To access the draw functions, the `read` and +/// `write` methods are used to obtain a lock on the internal state. +/// +/// [`RwLock`]: bevy_platform::sync::RwLock #[derive(Resource, Deref)] pub struct DrawFunctions { internal: RwLock>, diff --git a/examples/3d/occlusion_culling.rs b/examples/3d/occlusion_culling.rs index a66e66097b193..ecc265478011c 100644 --- a/examples/3d/occlusion_culling.rs +++ b/examples/3d/occlusion_culling.rs @@ -17,7 +17,7 @@ use bevy::{ prepass::DepthPrepass, }, pbr::PbrPlugin, - platform::{Arc, Mutex}, + platform::sync::{Arc, Mutex}, prelude::*, render::{ batching::gpu_preprocessing::{ @@ -595,7 +595,6 @@ fn readback_indirect_parameters( // If culling isn't supported on this platform, bail. if !saved_indirect_parameters .lock() - .unwrap() .as_ref() .unwrap() .occlusion_culling_supported @@ -617,7 +616,6 @@ fn readback_indirect_parameters( readback_buffer::(data_buffer, move |indirect_parameters| { saved_indirect_parameters_0 .lock() - .unwrap() .as_mut() .unwrap() .data = indirect_parameters.to_vec(); @@ -625,7 +623,6 @@ fn readback_indirect_parameters( readback_buffer::(batch_sets_buffer, move |indirect_parameters_count| { saved_indirect_parameters_1 .lock() - .unwrap() .as_mut() .unwrap() .count = indirect_parameters_count[0]; From 64ae35898d046d747115503aafaf367e05001693 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 19:39:21 -0700 Subject: [PATCH 11/13] Formatting --- examples/3d/occlusion_culling.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/examples/3d/occlusion_culling.rs b/examples/3d/occlusion_culling.rs index ecc265478011c..50073a41851b4 100644 --- a/examples/3d/occlusion_culling.rs +++ b/examples/3d/occlusion_culling.rs @@ -614,18 +614,10 @@ fn readback_indirect_parameters( let saved_indirect_parameters_0 = (**saved_indirect_parameters).clone(); let saved_indirect_parameters_1 = (**saved_indirect_parameters).clone(); readback_buffer::(data_buffer, move |indirect_parameters| { - saved_indirect_parameters_0 - .lock() - .as_mut() - .unwrap() - .data = indirect_parameters.to_vec(); + saved_indirect_parameters_0.lock().as_mut().unwrap().data = indirect_parameters.to_vec(); }); readback_buffer::(batch_sets_buffer, move |indirect_parameters_count| { - saved_indirect_parameters_1 - .lock() - .as_mut() - .unwrap() - .count = indirect_parameters_count[0]; + saved_indirect_parameters_1.lock().as_mut().unwrap().count = indirect_parameters_count[0]; }); } From 5e61c4f80298e577a2bb2c2edb1f5b32a8fdf9f7 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 6 Sep 2025 19:47:40 -0700 Subject: [PATCH 12/13] Fix no_std builds --- crates/bevy_platform/src/sync/mutex.rs | 2 +- crates/bevy_platform/src/sync/poison.rs | 2 +- crates/bevy_platform/src/sync/rwlock.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_platform/src/sync/mutex.rs b/crates/bevy_platform/src/sync/mutex.rs index 03d95891f6c0b..aacd98b8fdbbc 100644 --- a/crates/bevy_platform/src/sync/mutex.rs +++ b/crates/bevy_platform/src/sync/mutex.rs @@ -106,7 +106,7 @@ mod implementation { #[cfg(not(feature = "std"))] mod implementation { - use crate::sync::{LockResult, TryLockError, TryLockResult}; + use crate::sync::{TryLockError, TryLockResult}; use core::fmt; pub use spin::MutexGuard; diff --git a/crates/bevy_platform/src/sync/poison.rs b/crates/bevy_platform/src/sync/poison.rs index 12a817d78e961..a974004dfa6f1 100644 --- a/crates/bevy_platform/src/sync/poison.rs +++ b/crates/bevy_platform/src/sync/poison.rs @@ -1,4 +1,4 @@ -//! Provides `LockResult`, `PoisonError`, `TryLockError`, `TryLockResult` +//! Provides `TryLockError`, `TryLockResult` use core::{error::Error, fmt}; diff --git a/crates/bevy_platform/src/sync/rwlock.rs b/crates/bevy_platform/src/sync/rwlock.rs index e659eb222b9ca..dedc7be0c5945 100644 --- a/crates/bevy_platform/src/sync/rwlock.rs +++ b/crates/bevy_platform/src/sync/rwlock.rs @@ -141,7 +141,7 @@ mod implementation { #[cfg(not(feature = "std"))] mod implementation { - use crate::sync::{LockResult, TryLockError, TryLockResult}; + use crate::sync::{TryLockError, TryLockResult}; use core::fmt; pub use spin::rwlock::{RwLockReadGuard, RwLockWriteGuard}; From c1b434a54da36933a229ea853f416b76f2bb4a23 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 8 Sep 2025 02:06:11 -0700 Subject: [PATCH 13/13] Actual merge fixes --- .../system_information_diagnostics_plugin.rs | 52 ------------------- 1 file changed, 52 deletions(-) diff --git a/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs index 15cb55a13290e..0f61d88720160 100644 --- a/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs @@ -86,15 +86,9 @@ mod internal { use atomic_waker::AtomicWaker; use bevy_app::{App, First, Startup, Update}; use bevy_ecs::resource::Resource; -<<<<<<< HEAD - use bevy_ecs::{prelude::ResMut, system::Local}; - use bevy_platform::{sync::Mutex, time::Instant}; - use bevy_tasks::{available_parallelism, block_on, poll_once, AsyncComputeTaskPool, Task}; -======= use bevy_ecs::{prelude::ResMut, system::Commands}; use bevy_platform::{cell::SyncCell, time::Instant}; use bevy_tasks::{AsyncComputeTaskPool, Task}; ->>>>>>> main use log::info; use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System}; @@ -189,51 +183,6 @@ mod internal { waker: Arc, } -<<<<<<< HEAD - let last_refresh = last_refresh.get_or_insert_with(Instant::now); - - let thread_pool = AsyncComputeTaskPool::get(); - - // Only queue a new system refresh task when necessary - // Queuing earlier than that will not give new data - if last_refresh.elapsed() > sysinfo::MINIMUM_CPU_UPDATE_INTERVAL - // These tasks don't yield and will take up all of the task pool's - // threads if we don't limit their amount. - && tasks.tasks.len() * 2 < available_parallelism() - { - let sys = Arc::clone(sysinfo); - let task = thread_pool.spawn(async move { - let mut sys = sys.lock(); - let pid = sysinfo::get_current_pid().expect("Failed to get current process ID"); - sys.refresh_processes(sysinfo::ProcessesToUpdate::Some(&[pid]), true); - - sys.refresh_cpu_specifics(CpuRefreshKind::nothing().with_cpu_usage()); - sys.refresh_memory(); - let system_cpu_usage = sys.global_cpu_usage().into(); - let total_mem = sys.total_memory() as f64; - let used_mem = sys.used_memory() as f64; - let system_mem_usage = used_mem / total_mem * 100.0; - - let process_mem_usage = sys - .process(pid) - .map(|p| p.memory() as f64 * BYTES_TO_GIB) - .unwrap_or(0.0); - - let process_cpu_usage = sys - .process(pid) - .map(|p| p.cpu_usage() as f64 / sys.cpus().len() as f64) - .unwrap_or(0.0); - - SysinfoRefreshData { - system_cpu_usage, - system_mem_usage, - process_cpu_usage, - process_mem_usage, - } - }); - tasks.tasks.push(task); - *last_refresh = Instant::now(); -======= struct DiagnosticTask { system: System, last_refresh: Instant, @@ -254,7 +203,6 @@ mod internal { sender, waker: Arc::default(), } ->>>>>>> main } }