diff --git a/Cargo.lock b/Cargo.lock index dca786b3772..fc6f80378a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4633,7 +4633,6 @@ dependencies = [ "js-sys", "log", "naga", - "parking_lot", "portable-atomic", "profiling", "raw-window-handle", @@ -4706,7 +4705,6 @@ dependencies = [ "naga", "naga-types", "once_cell", - "parking_lot", "portable-atomic", "profiling", "raw-window-handle", @@ -4866,7 +4864,6 @@ dependencies = [ "objc2-quartz-core 0.3.2", "once_cell", "ordered-float", - "parking_lot", "portable-atomic", "portable-atomic-util", "profiling", @@ -4972,14 +4969,18 @@ version = "29.0.0" dependencies = [ "bitflags 2.13.0", "bytemuck", + "cfg-if", "exhaust", "hashbrown 0.17.1", "js-sys", + "lock_api", "log", "naga-types", + "parking_lot", "raw-window-handle", "serde", "serde_json", + "spin", "static_assertions", "web-sys", ] diff --git a/Cargo.toml b/Cargo.toml index 2a9dcad1b12..cc5d2bc70af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,6 +154,8 @@ libfuzzer-sys = ">0.4.0,<=0.4.7" libloading = "0.8" libm = { version = "0.2.6", default-features = false } libtest-mimic = "0.8" +# Minimum version supported by parking_lot 0.12.3 +lock_api = { version = "0.4.6", default-features = false } log = "0.4.29" macro_rules_attribute = "0.2" nanoserde = "0.2" @@ -196,6 +198,8 @@ serde_json = "1.0.143" serde = { version = "1.0.225", default-features = false } shell-words = "1" smallvec = "1.14" +# NOTE: `crossbeam-deque` currently relies on this version of spin +spin = { version = "0.9.8", default-features = false } spirv = "0.4" static_assertions = "1.1" strum = { version = "0.28", default-features = false, features = ["derive"] } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 90f88dc0a61..32ef905737c 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -67,6 +67,7 @@ exhaust.workspace = true futures-lite.workspace = true libtest-mimic.workspace = true log.workspace = true +parking_lot = { workspace = true, features = ["deadlock_detection"] } png.workspace = true pollster.workspace = true profiling.workspace = true @@ -81,7 +82,6 @@ half = { workspace = true, features = ["bytemuck", "std"] } itertools.workspace = true image.workspace = true nanorand.workspace = true -parking_lot.workspace = true strum = { workspace = true, features = ["derive"] } trybuild.workspace = true @@ -90,7 +90,6 @@ trybuild.workspace = true # Cargo-metadata doesn't compile on wasm due to old cargo-util-schemas dependency. cargo_metadata.workspace = true env_logger.workspace = true -parking_lot = { workspace = true, features = ["deadlock_detection"] } ureq.workspace = true [target.'cfg(not(any(target_arch = "wasm32", miri)))'.dependencies] @@ -102,7 +101,6 @@ console_log.workspace = true wasm-bindgen.workspace = true wasm-bindgen-futures.workspace = true web-sys = { workspace = true } -parking_lot = { workspace = true, features = ["deadlock_detection"] } # Webassembly Dev Dependencies [target.'cfg(target_arch = "wasm32")'.dev-dependencies] diff --git a/tests/src/native.rs b/tests/src/native.rs index 6e31e692e36..1f117a518f5 100644 --- a/tests/src/native.rs +++ b/tests/src/native.rs @@ -5,7 +5,7 @@ use std::{future::Future, pin::Pin}; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; use crate::{ config::GpuTestConfiguration, params::TestInfo, report::AdapterReport, run::execute_test, @@ -87,7 +87,8 @@ impl NativeTest { } #[doc(hidden)] -pub static TEST_LIST: Mutex> = Mutex::new(Vec::new()); +pub static TEST_LIST: Mutex> = + Mutex::const_new(wgpu_types::sync::RawMutex::new(), Vec::new()); /// Return value for the main function. pub type MainResult = anyhow::Result<()>; diff --git a/tests/tests/wgpu-gpu/regression/issue_4024.rs b/tests/tests/wgpu-gpu/regression/issue_4024.rs index 9b7d34b202b..74909b94c0c 100644 --- a/tests/tests/wgpu-gpu/regression/issue_4024.rs +++ b/tests/tests/wgpu-gpu/regression/issue_4024.rs @@ -1,7 +1,7 @@ use std::sync::Arc; -use parking_lot::Mutex; use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; +use wgpu_types::sync::Mutex; use wgpu::*; diff --git a/tests/tests/wgpu-validation/api/error_scopes.rs b/tests/tests/wgpu-validation/api/error_scopes.rs index 85b4837f71e..de2e48d9c49 100644 --- a/tests/tests/wgpu-validation/api/error_scopes.rs +++ b/tests/tests/wgpu-validation/api/error_scopes.rs @@ -4,7 +4,7 @@ use std::{ sync::Arc, }; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; const ERR: &str = "Buffer size 9223372036854775808 is greater than the maximum buffer size"; fn raise_validation_error(device: &wgpu::Device) { diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 839f941b6c2..e70b85df8f0 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -190,7 +190,6 @@ indexmap.workspace = true log.workspace = true macro_rules_attribute = { workspace = true, optional = true } once_cell = { workspace = true, features = ["std"] } -parking_lot.workspace = true profiling = { workspace = true, default-features = false } raw-window-handle.workspace = true ron = { workspace = true, optional = true } diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index e0dbff7f679..2f65bae8c20 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -1,5 +1,5 @@ -use parking_lot::Mutex; use thiserror::Error; +use wgpu_types::sync::Mutex; use wgt::{ error::{ErrorType, WebGpuError}, BufferAddress, DynamicOffset, diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 58f0ee58540..36b715a1a22 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -1,7 +1,7 @@ use alloc::{borrow::Cow, sync::Arc, vec::Vec}; use core::{convert::Infallible, fmt, num::NonZeroU32, ops::Range, str}; -use parking_lot::Mutex; use smallvec::SmallVec; +use wgpu_types::sync::Mutex; use arrayvec::ArrayVec; use thiserror::Error; diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 844c4ddc095..f8663785989 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -1181,7 +1181,7 @@ impl Global { // no lock rank here because only one thread should be using compute pass // and it's only used by id variants of compute pass methods on global // so no deadlock (or concurrent lock) should happen in practise - let id = fid.assign(Arc::new(parking_lot::Mutex::new(*render_bundle_encoder))); + let id = fid.assign(Arc::new(wgt::sync::Mutex::new(*render_bundle_encoder))); (id, error) } diff --git a/wgpu-core/src/hub.rs b/wgpu-core/src/hub.rs index 3b22e4a1453..a490e25fe4f 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -135,7 +135,7 @@ use crate::{ }, }; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; #[derive(Debug, PartialEq, Eq)] pub struct HubReport { diff --git a/wgpu-core/src/lock/mod.rs b/wgpu-core/src/lock/mod.rs index 2e83f5a3586..7e212dc30f6 100644 --- a/wgpu-core/src/lock/mod.rs +++ b/wgpu-core/src/lock/mod.rs @@ -27,8 +27,8 @@ //! //! Otherwise, `wgpu-core` uses the [`vanilla`] module's locks. //! -//! [`Mutex`]: parking_lot::Mutex -//! [`RwLock`]: parking_lot::RwLock +//! [`Mutex`]: wgpu_types::sync::Mutex +//! [`RwLock`]: wgpu_types::sync::RwLock //! [`SnatchLock`]: crate::snatch::SnatchLock pub mod rank; diff --git a/wgpu-core/src/lock/observing.rs b/wgpu-core/src/lock/observing.rs index 25c25185df4..8da667e2ff6 100644 --- a/wgpu-core/src/lock/observing.rs +++ b/wgpu-core/src/lock/observing.rs @@ -42,30 +42,30 @@ pub type RankData = Option; /// A `Mutex` instrumented for lock acquisition order observation. /// -/// This is just a wrapper around a [`parking_lot::Mutex`], along with +/// This is just a wrapper around a [`wgpu_types::sync::Mutex`], along with /// its rank in the `wgpu_core` lock ordering. /// /// For details, see [the module documentation][self]. pub struct Mutex { - inner: parking_lot::Mutex, + inner: wgpu_types::sync::Mutex, rank: LockRank, } /// A guard produced by locking [`Mutex`]. /// -/// This is just a wrapper around a [`parking_lot::MutexGuard`], along +/// This is just a wrapper around a [`wgpu_types::sync::MutexGuard`], along /// with the state needed to track lock acquisition. /// /// For details, see [the module documentation][self]. pub struct MutexGuard<'a, T> { - inner: parking_lot::MutexGuard<'a, T>, + inner: wgpu_types::sync::MutexGuard<'a, T>, _state: LockStateGuard, } impl Mutex { pub fn new(rank: LockRank, value: T) -> Mutex { Mutex { - inner: parking_lot::Mutex::new(value), + inner: wgpu_types::sync::Mutex::new(value), rank, } } @@ -106,41 +106,41 @@ impl core::fmt::Debug for Mutex { /// An `RwLock` instrumented for lock acquisition order observation. /// -/// This is just a wrapper around a [`parking_lot::RwLock`], along with +/// This is just a wrapper around a [`wgpu_types::sync::RwLock`], along with /// its rank in the `wgpu_core` lock ordering. /// /// For details, see [the module documentation][self]. pub struct RwLock { - inner: parking_lot::RwLock, + inner: wgpu_types::sync::RwLock, rank: LockRank, } /// A read guard produced by locking [`RwLock`] for reading. /// -/// This is just a wrapper around a [`parking_lot::RwLockReadGuard`], along with +/// This is just a wrapper around a [`wgpu_types::sync::RwLockReadGuard`], along with /// the state needed to track lock acquisition. /// /// For details, see [the module documentation][self]. pub struct RwLockReadGuard<'a, T> { - inner: parking_lot::RwLockReadGuard<'a, T>, + inner: wgpu_types::sync::RwLockReadGuard<'a, T>, _state: LockStateGuard, } /// A write guard produced by locking [`RwLock`] for writing. /// -/// This is just a wrapper around a [`parking_lot::RwLockWriteGuard`], along +/// This is just a wrapper around a [`wgpu_types::sync::RwLockWriteGuard`], along /// with the state needed to track lock acquisition. /// /// For details, see [the module documentation][self]. pub struct RwLockWriteGuard<'a, T> { - inner: parking_lot::RwLockWriteGuard<'a, T>, + inner: wgpu_types::sync::RwLockWriteGuard<'a, T>, _state: LockStateGuard, } impl RwLock { pub fn new(rank: LockRank, value: T) -> RwLock { RwLock { - inner: parking_lot::RwLock::new(value), + inner: wgpu_types::sync::RwLock::new(value), rank, } } diff --git a/wgpu-core/src/lock/rank.rs b/wgpu-core/src/lock/rank.rs index f177dc6c914..5dd476adc63 100644 --- a/wgpu-core/src/lock/rank.rs +++ b/wgpu-core/src/lock/rank.rs @@ -26,8 +26,8 @@ /// TODO(): Resolve invalid /// acquisitions of DEVICE_COMMAND_INDICES followed by COMMAND_BUFFER_DATA. /// -/// [`Mutex`]: parking_lot::Mutex -/// [`RwLock`]: parking_lot::RwLock +/// [`Mutex`]: wgpu_types::sync::Mutex +/// [`RwLock`]: wgpu_types::sync::RwLock /// [`SnatchLock`]: crate::snatch::SnatchLock /// [`CommandBuffer::data`]: crate::command::CommandBuffer::data #[derive(Debug, Copy, Clone)] diff --git a/wgpu-core/src/lock/ranked.rs b/wgpu-core/src/lock/ranked.rs index fff16a96e52..682d55f745f 100644 --- a/wgpu-core/src/lock/ranked.rs +++ b/wgpu-core/src/lock/ranked.rs @@ -63,23 +63,23 @@ pub use LockState as RankData; /// A `Mutex` instrumented for deadlock prevention. /// -/// This is just a wrapper around a [`parking_lot::Mutex`], along with +/// This is just a wrapper around a [`wgpu_types::sync::Mutex`], along with /// its rank in the `wgpu_core` lock ordering. /// /// For details, see [the module documentation][self]. pub struct Mutex { - inner: parking_lot::Mutex, + inner: wgpu_types::sync::Mutex, rank: LockRank, } /// A guard produced by locking [`Mutex`]. /// -/// This is just a wrapper around a [`parking_lot::MutexGuard`], along +/// This is just a wrapper around a [`wgpu_types::sync::MutexGuard`], along /// with the state needed to track lock acquisition. /// /// For details, see [the module documentation][self]. pub struct MutexGuard<'a, T> { - inner: parking_lot::MutexGuard<'a, T>, + inner: wgpu_types::sync::MutexGuard<'a, T>, #[cfg_attr(not(miri), expect(unused))] // but `Drop` has important side effects saved: LockStateGuard, } @@ -216,7 +216,7 @@ fn release(saved: LockState) { impl Mutex { pub fn new(rank: LockRank, value: T) -> Mutex { Mutex { - inner: parking_lot::Mutex::new(value), + inner: wgpu_types::sync::Mutex::new(value), rank, } } @@ -257,41 +257,41 @@ impl fmt::Debug for Mutex { /// An `RwLock` instrumented for deadlock prevention. /// -/// This is just a wrapper around a [`parking_lot::RwLock`], along with +/// This is just a wrapper around a [`wgpu_types::sync::RwLock`], along with /// its rank in the `wgpu_core` lock ordering. /// /// For details, see [the module documentation][self]. pub struct RwLock { - inner: parking_lot::RwLock, + inner: wgpu_types::sync::RwLock, rank: LockRank, } /// A read guard produced by locking [`RwLock`] for reading. /// -/// This is just a wrapper around a [`parking_lot::RwLockReadGuard`], along with +/// This is just a wrapper around a [`wgpu_types::sync::RwLockReadGuard`], along with /// the state needed to track lock acquisition. /// /// For details, see [the module documentation][self]. pub struct RwLockReadGuard<'a, T> { - inner: parking_lot::RwLockReadGuard<'a, T>, + inner: wgpu_types::sync::RwLockReadGuard<'a, T>, saved: LockStateGuard, } /// A write guard produced by locking [`RwLock`] for writing. /// -/// This is just a wrapper around a [`parking_lot::RwLockWriteGuard`], along +/// This is just a wrapper around a [`wgpu_types::sync::RwLockWriteGuard`], along /// with the state needed to track lock acquisition. /// /// For details, see [the module documentation][self]. pub struct RwLockWriteGuard<'a, T> { - inner: parking_lot::RwLockWriteGuard<'a, T>, + inner: wgpu_types::sync::RwLockWriteGuard<'a, T>, saved: LockStateGuard, } impl RwLock { pub fn new(rank: LockRank, value: T) -> RwLock { RwLock { - inner: parking_lot::RwLock::new(value), + inner: wgpu_types::sync::RwLock::new(value), rank, } } diff --git a/wgpu-core/src/lock/vanilla.rs b/wgpu-core/src/lock/vanilla.rs index 96999696282..bfecfb9c2b9 100644 --- a/wgpu-core/src/lock/vanilla.rs +++ b/wgpu-core/src/lock/vanilla.rs @@ -1,4 +1,4 @@ -//! Plain, uninstrumented wrappers around [`parking_lot`] lock types. +//! Plain, uninstrumented wrappers around [`wgpu_types::sync`] lock types. //! //! These definitions are used when no particular lock instrumentation //! Cargo feature is selected. @@ -9,9 +9,9 @@ use crate::lock::rank::LockRank; pub struct RankData; -/// A plain wrapper around [`parking_lot::Mutex`]. +/// A plain wrapper around [`wgpu_types::sync::Mutex`]. /// -/// This is just like [`parking_lot::Mutex`], except that our [`new`] +/// This is just like [`wgpu_types::sync::Mutex`], except that our [`new`] /// method takes a rank, indicating where the new mutex should sit in /// `wgpu-core`'s lock ordering. The rank is ignored. /// @@ -19,16 +19,16 @@ pub struct RankData; /// /// [`new`]: Mutex::new /// [`lock`]: crate::lock -pub struct Mutex(parking_lot::Mutex); +pub struct Mutex(wgpu_types::sync::Mutex); /// A guard produced by locking [`Mutex`]. /// -/// This is just a wrapper around a [`parking_lot::MutexGuard`]. -pub struct MutexGuard<'a, T>(parking_lot::MutexGuard<'a, T>); +/// This is just a wrapper around a [`wgpu_types::sync::MutexGuard`]. +pub struct MutexGuard<'a, T>(wgpu_types::sync::MutexGuard<'a, T>); impl Mutex { pub fn new(_rank: LockRank, value: T) -> Mutex { - Mutex(parking_lot::Mutex::new(value)) + Mutex(wgpu_types::sync::Mutex::new(value)) } pub fn lock(&self) -> MutexGuard<'_, T> { @@ -60,9 +60,9 @@ impl fmt::Debug for Mutex { } } -/// A plain wrapper around [`parking_lot::RwLock`]. +/// A plain wrapper around [`wgpu_types::sync::RwLock`]. /// -/// This is just like [`parking_lot::RwLock`], except that our [`new`] +/// This is just like [`wgpu_types::sync::RwLock`], except that our [`new`] /// method takes a rank, indicating where the new mutex should sit in /// `wgpu-core`'s lock ordering. The rank is ignored. /// @@ -70,21 +70,21 @@ impl fmt::Debug for Mutex { /// /// [`new`]: RwLock::new /// [`lock`]: crate::lock -pub struct RwLock(parking_lot::RwLock); +pub struct RwLock(wgpu_types::sync::RwLock); /// A read guard produced by locking [`RwLock`] as a reader. /// -/// This is just a wrapper around a [`parking_lot::RwLockReadGuard`]. -pub struct RwLockReadGuard<'a, T>(parking_lot::RwLockReadGuard<'a, T>); +/// This is just a wrapper around a [`wgpu_types::sync::RwLockReadGuard`]. +pub struct RwLockReadGuard<'a, T>(wgpu_types::sync::RwLockReadGuard<'a, T>); /// A write guard produced by locking [`RwLock`] as a writer. /// -/// This is just a wrapper around a [`parking_lot::RwLockWriteGuard`]. -pub struct RwLockWriteGuard<'a, T>(parking_lot::RwLockWriteGuard<'a, T>); +/// This is just a wrapper around a [`wgpu_types::sync::RwLockWriteGuard`]. +pub struct RwLockWriteGuard<'a, T>(wgpu_types::sync::RwLockWriteGuard<'a, T>); impl RwLock { pub fn new(_rank: LockRank, value: T) -> RwLock { - RwLock(parking_lot::RwLock::new(value)) + RwLock(wgpu_types::sync::RwLock::new(value)) } pub fn read(&self) -> RwLockReadGuard<'_, T> { diff --git a/wgpu-core/src/storage.rs b/wgpu-core/src/storage.rs index dad3808c3ae..1b10161d814 100644 --- a/wgpu-core/src/storage.rs +++ b/wgpu-core/src/storage.rs @@ -4,7 +4,7 @@ use core::mem; use crate::id::{Id, Marker}; use crate::resource::ResourceType; use crate::{Epoch, Index}; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; /// An entry in a `Storage::map` table. #[derive(Debug)] diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index e0c079f9653..102dcd24a3b 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -73,6 +73,7 @@ unexpected_cfgs = { level = "warn", check-cfg = [ metal = [ # Metal is only available on Apple platforms, therefore request MSL output also only if we target an Apple platform. "naga/msl-out", + "wgpu-types/std", "dep:arrayvec", "dep:block2", "dep:bytemuck", @@ -82,7 +83,6 @@ metal = [ "dep:objc2-foundation", "dep:objc2-metal", "dep:objc2-quartz-core", - "dep:parking_lot", "dep:profiling", "dep:smallvec", "dep:raw-window-metal", @@ -96,7 +96,6 @@ vulkan = [ "dep:libc", "dep:libloading", "dep:ordered-float", - "dep:parking_lot", "dep:profiling", "dep:raw-window-metal", "dep:smallvec", @@ -119,7 +118,6 @@ gles = [ "dep:libloading", "dep:ndk-sys", "dep:objc2", - "dep:parking_lot", "dep:profiling", "dep:wasm-bindgen", "dep:wayland-sys", @@ -142,7 +140,6 @@ dx12 = [ "dep:libloading", "dep:once_cell", "dep:ordered-float", - "dep:parking_lot", "dep:profiling", "dep:range-alloc", "dep:windows-core", @@ -190,7 +187,7 @@ device_lost_panic = [] # Only affects the d3d12 and vulkan backends. internal_error_panic = [] # Tracks validation errors in a `VALIDATION_CANARY` static. -validation_canary = ["dep:parking_lot"] +validation_canary = [] [[example]] name = "halmark" @@ -213,7 +210,6 @@ wgpu-types = { workspace = true, default-features = false } bitflags.workspace = true cfg-if.workspace = true log = { workspace = true } -parking_lot = { workspace = true, optional = true } raw-window-handle.workspace = true thiserror.workspace = true diff --git a/wgpu-hal/src/auxil/dxgi/exception.rs b/wgpu-hal/src/auxil/dxgi/exception.rs index ac7d55d8f7d..48f0c87f2b6 100644 --- a/wgpu-hal/src/auxil/dxgi/exception.rs +++ b/wgpu-hal/src/auxil/dxgi/exception.rs @@ -1,6 +1,6 @@ use alloc::{borrow::Cow, string::String}; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; use windows::Win32::{Foundation, System::Diagnostics::Debug}; // This is a mutex as opposed to an atomic as we need to completely diff --git a/wgpu-hal/src/auxil/dxgi/hdr.rs b/wgpu-hal/src/auxil/dxgi/hdr.rs index 3d810ee6396..3e224c1fa5a 100644 --- a/wgpu-hal/src/auxil/dxgi/hdr.rs +++ b/wgpu-hal/src/auxil/dxgi/hdr.rs @@ -4,7 +4,7 @@ //! both query through a [`DxgiHdrSource`], so the same monitor reports identical //! numbers under either backend. -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; use windows::{ core::Interface as _, Win32::{ diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index e1d436ab1bb..2b05638fe6d 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -2,7 +2,7 @@ use alloc::{string::String, sync::Arc, vec::Vec}; use core::{ptr, sync::atomic::AtomicU64}; use std::thread; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; use windows::{ core::Interface as _, Win32::{ diff --git a/wgpu-hal/src/dx12/descriptor.rs b/wgpu-hal/src/dx12/descriptor.rs index f57ba46e1e2..b017d6967ad 100644 --- a/wgpu-hal/src/dx12/descriptor.rs +++ b/wgpu-hal/src/dx12/descriptor.rs @@ -2,8 +2,8 @@ use alloc::vec::Vec; use core::fmt; use bit_set::BitSet; -use parking_lot::Mutex; use range_alloc::RangeAllocator; +use wgpu_types::sync::Mutex; use windows::Win32::Graphics::Direct3D12; use crate::auxil::dxgi::result::HResult as _; diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index 63fb31395b0..6dffbbfe66f 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -10,7 +10,7 @@ use core::{ffi, num::NonZeroU32, ptr, time::Duration}; use std::time::Instant; use bytemuck::TransparentWrapper; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; use windows::{ core::Interface as _, Win32::{ diff --git a/wgpu-hal/src/dx12/instance.rs b/wgpu-hal/src/dx12/instance.rs index a420d639f8b..9d2c8351e40 100644 --- a/wgpu-hal/src/dx12/instance.rs +++ b/wgpu-hal/src/dx12/instance.rs @@ -1,6 +1,6 @@ use alloc::{string::String, sync::Arc, vec::Vec}; -use parking_lot::RwLock; +use wgpu_types::sync::RwLock; use windows::Win32::{Foundation, Graphics::Dxgi}; use super::SurfaceTarget; diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 285d6356639..79ffb36e7b9 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -97,8 +97,8 @@ use core::{ffi, fmt, mem, ops::Deref, sync::atomic::AtomicU64}; use arrayvec::ArrayVec; use hashbrown::HashMap; -use parking_lot::{Mutex, RwLock}; use suballocation::Allocator; +use wgpu_types::sync::{Mutex, RwLock}; use windows::{ core::{Free as _, Interface}, Win32::{ diff --git a/wgpu-hal/src/dx12/sampler.rs b/wgpu-hal/src/dx12/sampler.rs index 4b58a0b4232..2aac0ab40df 100644 --- a/wgpu-hal/src/dx12/sampler.rs +++ b/wgpu-hal/src/dx12/sampler.rs @@ -7,7 +7,7 @@ use alloc::vec::Vec; use hashbrown::{hash_map::Entry, HashMap}; use ordered_float::OrderedFloat; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; use windows::Win32::Graphics::Direct3D12::*; use crate::dx12::HResult; diff --git a/wgpu-hal/src/dx12/suballocation.rs b/wgpu-hal/src/dx12/suballocation.rs index 89c7dc47974..df859d0215c 100644 --- a/wgpu-hal/src/dx12/suballocation.rs +++ b/wgpu-hal/src/dx12/suballocation.rs @@ -1,7 +1,7 @@ use alloc::sync::Arc; use gpu_allocator::{d3d12::AllocationCreateDesc, MemoryLocation}; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; use windows::Win32::Graphics::{Direct3D12, Dxgi}; use crate::{ diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 07241507522..8d51272f573 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -2,7 +2,7 @@ use alloc::{borrow::ToOwned as _, format, string::String, sync::Arc, vec, vec::V use core::sync::atomic::AtomicU8; use glow::HasContext; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; use wgt::AstcChannel; use crate::auxil::db; diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs index eeb73694fed..63e0cfe3dca 100644 --- a/wgpu-hal/src/gles/egl.rs +++ b/wgpu-hal/src/gles/egl.rs @@ -4,7 +4,7 @@ use std::sync::LazyLock; use glow::HasContext; use hashbrown::HashMap; -use parking_lot::{MappedMutexGuard, Mutex, MutexGuard, RwLock}; +use wgpu_types::sync::{MappedMutexGuard, Mutex, MutexGuard, RwLock}; /// The amount of time to wait while trying to obtain a lock to the adapter context const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 6; diff --git a/wgpu-hal/src/gles/fence.rs b/wgpu-hal/src/gles/fence.rs index 0470e5ba151..d38913cf420 100644 --- a/wgpu-hal/src/gles/fence.rs +++ b/wgpu-hal/src/gles/fence.rs @@ -1,6 +1,6 @@ use alloc::{sync::Arc, vec::Vec}; use core::sync::atomic::Ordering; -use parking_lot::RwLock; +use wgpu_types::sync::RwLock; use glow::HasContext; diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index 24e26c67ec8..73dff471692 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -119,7 +119,7 @@ use core::{ ops::Range, sync::atomic::{AtomicU32, AtomicU8}, }; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; use arrayvec::ArrayVec; use glow::HasContext; diff --git a/wgpu-hal/src/gles/web.rs b/wgpu-hal/src/gles/web.rs index 326b4f35eb3..e8d27792888 100644 --- a/wgpu-hal/src/gles/web.rs +++ b/wgpu-hal/src/gles/web.rs @@ -1,8 +1,8 @@ use alloc::{format, string::String, vec::Vec}; use glow::HasContext; -use parking_lot::{Mutex, RwLock}; use wasm_bindgen::{JsCast, JsValue}; +use wgpu_types::sync::{Mutex, RwLock}; use super::TextureFormatDesc; diff --git a/wgpu-hal/src/gles/wgl.rs b/wgpu-hal/src/gles/wgl.rs index 09ee3e95985..eb9f5ead20c 100644 --- a/wgpu-hal/src/gles/wgl.rs +++ b/wgpu-hal/src/gles/wgl.rs @@ -24,8 +24,8 @@ use glutin_wgl_sys::wgl_extra::{ CONTEXT_PROFILE_MASK_ARB, }; use hashbrown::HashSet; -use parking_lot::{Mutex, MutexGuard, RwLock}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; +use wgpu_types::sync::{Mutex, MutexGuard, RwLock}; use wgt::InstanceFlags; use windows::{ core::{Error, PCSTR}, diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index 4332bbf9188..cbb962db8c4 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -11,8 +11,8 @@ use wgt::{AstcBlock, AstcChannel}; use alloc::{string::ToString as _, sync::Arc, vec::Vec}; use core::sync::atomic; -use parking_lot::Mutex; use std::sync::OnceLock; +use wgpu_types::sync::Mutex; use crate::metal::QueueShared; diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs index c567073fc03..d556c1ad850 100644 --- a/wgpu-hal/src/metal/device.rs +++ b/wgpu-hal/src/metal/device.rs @@ -22,7 +22,7 @@ use objc2_metal::{ MTLTexture, MTLTextureDescriptor, MTLTextureType, MTLTriangleFillMode, MTLVertexDescriptor, MTLVertexStepFunction, }; -use parking_lot::{Condvar, Mutex, RwLock}; +use wgpu_types::sync::{Condvar, CondvarMutex, RwLock}; use super::{adapter::VERTEX_BUFFER_SLOT_START, conv, PassthroughShader, ShaderModuleSource}; use crate::{auxil::map_naga_stage, DropCallback, DropGuard, TlasInstance}; @@ -2001,7 +2001,7 @@ impl crate::Device for super::Device { None }; Ok(super::Fence { - sync: Arc::new((Mutex::new(0), Condvar::new())), + sync: Arc::new((CondvarMutex::new(0), Condvar::new())), pending_command_buffers: RwLock::new(Vec::new()), shared_event, }) diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index 4c897c27713..c6dc4407f07 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -56,7 +56,7 @@ use objc2_metal::{ MTLTriangleFillMode, MTLWinding, }; use objc2_quartz_core::CAMetalLayer; -use parking_lot::{Condvar, Mutex, RwLock}; +use wgpu_types::sync::{Condvar, Mutex, RwLock}; #[derive(Clone, Debug)] pub struct Api; @@ -1244,7 +1244,7 @@ unsafe impl Sync for QuerySet {} #[derive(Debug)] pub struct Fence { - sync: Arc<(Mutex, Condvar)>, + sync: Arc<(wgpu_types::sync::CondvarMutex, Condvar)>, /// The pending fence values have to be ascending. pending_command_buffers: RwLock>, shared_event: Option>>, diff --git a/wgpu-hal/src/metal/surface.rs b/wgpu-hal/src/metal/surface.rs index 87eac95f09d..074a2f4726d 100644 --- a/wgpu-hal/src/metal/surface.rs +++ b/wgpu-hal/src/metal/surface.rs @@ -11,7 +11,7 @@ use objc2_core_graphics::CGColorSpace; use objc2_foundation::NSObjectProtocol; use objc2_metal::MTLTextureType; use objc2_quartz_core::{CAMetalDrawable, CAMetalLayer}; -use parking_lot::{Mutex, RwLock}; +use wgpu_types::sync::{Mutex, RwLock}; use super::OsFeatures; diff --git a/wgpu-hal/src/validation_canary.rs b/wgpu-hal/src/validation_canary.rs index bd23db33245..67b582253cb 100644 --- a/wgpu-hal/src/validation_canary.rs +++ b/wgpu-hal/src/validation_canary.rs @@ -1,6 +1,6 @@ use alloc::{string::String, vec::Vec}; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; /// Stores the text of any validation errors that have occurred since /// the last call to `get_and_reset`. @@ -17,7 +17,8 @@ use parking_lot::Mutex; /// This prevents the issue of one validation error terminating the /// entire process. pub static VALIDATION_CANARY: ValidationCanary = ValidationCanary { - inner: Mutex::new(Vec::new()), + // Mutex::new is not const for the minimum version of lock_api used. + inner: Mutex::const_new(wgpu_types::sync::RawMutex::new(), Vec::new()), }; /// Flag for internal testing. diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 0346afe695b..465597530cb 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -2,7 +2,7 @@ use alloc::{borrow::ToOwned as _, boxed::Box, collections::BTreeMap, sync::Arc, use core::{ffi::CStr, marker::PhantomData}; use ash::{ext, google, khr, vk}; -use parking_lot::Mutex; +use wgpu_types::sync::Mutex; use crate::{vulkan::semaphore_list::SemaphoreList, AllocationSizes}; diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 948f77a5828..6d3ed151414 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -10,7 +10,7 @@ use core::{ use arrayvec::ArrayVec; use ash::{ext, vk}; use hashbrown::hash_map::Entry; -use parking_lot::{Mutex, RwLock}; +use wgpu_types::sync::{Mutex, RwLock}; use super::{conv, descriptor::DescriptorCounts, RawTlasInstance}; use crate::TlasInstance; diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs index 177587582a6..3aeb1af6d47 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -9,7 +9,7 @@ use std::thread; use arrayvec::ArrayVec; use ash::{ext, khr, vk}; -use parking_lot::RwLock; +use wgpu_types::sync::RwLock; unsafe extern "system" fn debug_utils_messenger_callback( message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 51e7414b622..e718ce9690a 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -44,7 +44,7 @@ use arrayvec::ArrayVec; use ash::{ext, khr, vk}; use bytemuck::{Pod, Zeroable}; use hashbrown::HashSet; -use parking_lot::{Mutex, RwLock}; +use wgpu_types::sync::{Mutex, RwLock}; use naga::FastHashMap; use wgt::InternalCounter; diff --git a/wgpu-hal/src/vulkan/swapchain/native.rs b/wgpu-hal/src/vulkan/swapchain/native.rs index 35f3b281150..258d5fb5628 100644 --- a/wgpu-hal/src/vulkan/swapchain/native.rs +++ b/wgpu-hal/src/vulkan/swapchain/native.rs @@ -4,7 +4,7 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec}; use core::any::Any; use ash::{khr, vk}; -use parking_lot::{Mutex, MutexGuard}; +use wgpu_types::sync::{Mutex, MutexGuard}; use crate::vulkan::{ conv, map_host_device_oom_and_lost_err, diff --git a/wgpu-types/Cargo.toml b/wgpu-types/Cargo.toml index 62f456c3bf1..35a0d41c680 100644 --- a/wgpu-types/Cargo.toml +++ b/wgpu-types/Cargo.toml @@ -38,7 +38,7 @@ alloc_instead_of_core = "warn" [features] default = ["std"] -std = ["js-sys?/std", "web-sys?/std", "naga-types/std"] +std = ["js-sys?/std", "web-sys?/std", "naga-types/std", "dep:parking_lot"] strict_asserts = [] fragile-send-sync-non-atomic-wasm = [] serde = [ @@ -55,18 +55,34 @@ trace = ["std", "serde/std"] web = ["dep:js-sys", "dep:web-sys"] exhaust = ["dep:exhaust"] +# Enables the `spin` set of locking primitives. +# This is generally only useful for `no_std` targets, and will be unused if +# `parking_lot` is available. +# Will fallback to a `RefCell` based implementation which is `!Sync` when no +# alternative feature is enabled. +spin = ["dep:spin"] + [dependencies] naga-types = { workspace = true, features = [] } bitflags.workspace = true bytemuck = { workspace = true, features = ["derive"] } +cfg-if.workspace = true exhaust = { workspace = true, optional = true } +lock_api = { workspace = true, default-features = false } log.workspace = true +parking_lot = { workspace = true, optional = true } raw-window-handle.workspace = true serde = { workspace = true, default-features = false, features = [ "alloc", "derive", ], optional = true } +spin = { workspace = true, features = [ + "rwlock", + "mutex", + "spin_mutex", + "lock_api", +], optional = true } static_assertions.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index e5ce86c41aa..6878575c094 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -47,6 +47,7 @@ mod render; mod send_sync; mod shader; mod surface; +pub mod sync; mod texture; mod tokens; mod transfers; diff --git a/wgpu-types/src/sync.rs b/wgpu-types/src/sync.rs new file mode 100644 index 00000000000..1ec18a6f607 --- /dev/null +++ b/wgpu-types/src/sync.rs @@ -0,0 +1,471 @@ +//! Provides [`Mutex`] and [`RwLock`] types with an appropriate implementation. + +/// A [`Mutex`](lock_api::Mutex) using [`RawMutex`] for its backing implementation. +pub type Mutex = lock_api::Mutex; + +/// A [`MutexGuard`](lock_api::MutexGuard) using [`RawMutex`] for its backing implementation. +pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawMutex, T>; + +/// A [`MappedMutexGuard`](lock_api::MappedMutexGuard) using [`RawMutex`] for its backing implementation. +pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawMutex, T>; + +/// A [`RwLock`](lock_api::RwLock) using [`RawRwLock`] for its backing implementation. +pub type RwLock = lock_api::RwLock; + +/// A [`RwLockReadGuard`](lock_api::RwLockReadGuard) using [`RawRwLock`] for its backing implementation. +pub type RwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, RawRwLock, T>; + +/// A [`RwLockWriteGuard`](lock_api::RwLockWriteGuard) using [`RawRwLock`] for its backing implementation. +pub type RwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, RawRwLock, T>; + +/// A [`RwLockUpgradableReadGuard`](lock_api::RwLockUpgradableReadGuard) using [`RawRwLock`] for its backing implementation. +pub type RwLockUpgradableReadGuard<'a, T> = lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>; + +// FIXME: +// * `Condvar` is only available through `parking_lot` and not through `lock_api`. +// * `Condvar` only works with the speific `RawMutex` implementation from `parking_lot`. +#[cfg(feature = "std")] +pub use parking_lot::{Condvar, Mutex as CondvarMutex}; + +// Note that both `spin` and `parking_lot` provide types which already implement +// the parts of the `lock_api` we're going to implement below. +// We explicitly wrap those implementations to ensure we have the intersection of +// their available APIs. +// +// For example, `spin` implements `RawMutex` with `GuardSend`, while `parking_lot` +// implements it with `GuardNoSend`. +// Further, `parking_lot` implements `RawRwLockUpgrade`, while `spin` does not. + +cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + type RawMutexInner = parking_lot::RawMutex; + type RawRwLockInner = parking_lot::RawRwLock; + } else if #[cfg(feature = "spin")] { + type RawMutexInner = spin::Mutex<()>; + type RawRwLockInner = spin::RwLock<()>; + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + use core::sync::atomic::Ordering; + + #[repr(usize)] + enum RawRwLockState { + Exclusive = 0, + Unlocked = 1, + SingleShared = 2, + } + + type RawMutexInner = core::sync::atomic::AtomicBool; + type RawRwLockInner = core::sync::atomic::AtomicUsize; + } else { + compile_error!("wgpu-types does not support platforms without atomics"); + } +} + +/// Raw implementation for a [`Mutex`]. +/// +/// This will delegate to (in order): +/// +/// * [`parking_lot`] if the `std` feature is enabled (default) +/// * [`spin`] if the `spin` feature is enabled +/// * [`atomic`] if the target supports atomic operations +/// +/// [`parking_lot`]: https://docs.rs/parking_lot/ +/// [`spin`]: https://docs.rs/spin/ +/// [`atomic`]: core::sync::atomic +pub struct RawMutex(RawMutexInner); + +impl RawMutex { + /// Constructs a new [`RawMutex`]. + pub const fn new() -> Self { + Self({ + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawMutex::INIT + } else { + RawMutexInner::new(false) + } + } + }) + } +} + +impl Default for RawMutex { + fn default() -> Self { + Self::new() + } +} + +impl core::fmt::Debug for RawMutex { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("RawMutex").finish() + } +} + +// SAFETY: +// +// # With `std` or `spin` +// +// This implementation directly delegates to an existing implementation of +// `RawMutex`, and is therefore safe. +// +// # Without `std` or `spin` +// +// From the `lock_api` documentation for `RawMutex`: +// +// > Implementations of this trait must ensure that the mutex is actually +// > exclusive: a lock can’t be acquired while the mutex is already locked. +// +// 1. A lock can only be acquired after `try_lock` returns `true` _or_ `lock` returns. +// 2. `try_lock` can only return `true` _if_ it is able to write a value of `true` +// into its internal state _and_ the internal state is currently false. +// 3. `lock` can only return when `try_lock` returns `true`. +// 4. Therefore, a lock can only be acquired when the internal state is `false` +// 5. Internal state can only be `false` if the lock has been released by a call +// to `unlock`, or it is in its initial state. +// +// Therefore, this implementation of `RawMutex` is safe. +unsafe impl lock_api::RawMutex for RawMutex { + type GuardMarker = lock_api::GuardNoSend; + + const INIT: RawMutex = RawMutex::new(); + + #[inline] + fn lock(&self) { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawMutex::lock(&self.0) + } else { + while !self.try_lock() { + core::hint::spin_loop() + } + } + } + } + + #[inline] + fn try_lock(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawMutex::try_lock(&self.0) + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + self.0 + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_ok() + } else { + match self.0.try_borrow_mut() { + Ok(mut guard) => { + if *guard { + false + } else { + *guard = true; + true + } + } + Err(_) => false, + } + } + } + } + + #[inline] + unsafe fn unlock(&self) { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + // SAFETY: directly delegating to an accepted implementation + unsafe { lock_api::RawMutex::unlock(&self.0) } + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + self.0.store(false, Ordering::Release); + } else { + compile_error!("wgpu-types does not support platforms without atomics"); + } + } + } + + #[inline] + fn is_locked(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawMutex::is_locked(&self.0) + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + self.0.load(Ordering::Acquire) + } else { + compile_error!("wgpu-types does not support platforms without atomics"); + } + } + } +} + +// SAFETY: +// +// # With `std` +// +// This implementation directly delegates to an existing implementation of +// `RawMutexTimed`, and is therefore safe. +// +// # Without `std` +// +// There is no specific safety conditions for this trait. +// However, the implementation without `std` only uses safe APIs and pessimistically +// fails since, without `std`, `wgpu-types` currently has no mechanism for timekeeping. +unsafe impl lock_api::RawMutexTimed for RawMutex { + cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + type Duration = ::Duration; + type Instant = ::Instant; + } else { + type Duration = core::time::Duration; + type Instant = (); + } + } + + fn try_lock_for(&self, _timeout: Self::Duration) -> bool { + cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + lock_api::RawMutexTimed::try_lock_for(&self.0, _timeout) + } else { + lock_api::RawMutex::try_lock(self) + } + } + } + + fn try_lock_until(&self, _timeout: Self::Instant) -> bool { + cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + lock_api::RawMutexTimed::try_lock_until(&self.0, _timeout) + } else { + lock_api::RawMutex::try_lock(self) + } + } + } +} + +/// Raw implementation for a [`RwLock`]. +/// +/// This will delegate to (in order): +/// +/// * [`parking_lot`] if the `std` feature is enabled (default) +/// * [`spin`] if the `spin` feature is enabled +/// * [`atomic`] if the target supports atomic operations +/// +/// [`parking_lot`]: https://docs.rs/parking_lot/ +/// [`spin`]: https://docs.rs/spin/ +/// [`atomic`]: core::sync::atomic +pub struct RawRwLock(RawRwLockInner); + +impl RawRwLock { + /// Constructs a new [`RawRwLock`]. + pub const fn new() -> Self { + Self({ + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawRwLock::INIT + } else { + RawRwLockInner::new(RawRwLockState::Unlocked as _) + } + } + }) + } +} + +impl Default for RawRwLock { + fn default() -> Self { + Self::new() + } +} + +impl core::fmt::Debug for RawRwLock { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("RawRwLock").finish() + } +} + +// SAFETY: +// With `std` or `spin` enabled, this implementation directly delegates to an +// existing implementation of `RawRwLock`, and is therefore safe. +// +// From the `lock_api` documentation for RawRwLock: +// +// > Implementations of this trait must ensure that the RwLock is actually exclusive: +// > an exclusive lock can’t be acquired while an exclusive or shared lock exists, +// > and a shared lock can’t be acquire while an exclusive lock exists. +// +// 1. An exclusive lock can only be acquired when `try_lock_exclusive` returns `true` +// _or_ `lock_exclusive` returns. +// 2. `try_lock_exclusive` can only return `true` when `count` is `0`. +// 3. `lock_exclusive` can only return when `try_lock_exclusive` returns `true`. +// 4. Therefore, an exclusive lock can only be acquired when the `count` is `0`. +// 5. When `try_lock_exclusive` returns `true`, it sets `count` to `1` and +// `is_exclusive` to `true`. +// 6. A shared lock can only be acquired when `try_lock_shared` returns `true, +// _or_ `lock_shared` returns, _or_ `downgrade` returns. +// 7. `try_lock_shared` can only return true when `is_exclusive` is `false` _or_ +// `count` is `0`. +// 8. `lock_shared` can only return when `try_lock_shared` returns `true`. +// 9. `downgrade` can only be called when an exclusive lock is already acquired, +// _and_ the exclusive lock is being exchanged for a shared lock. +// 10. Therefore, a shared lock can only be acquired when `is_exclusive` is `false` +// _or_ `count` is `0`. +// 11. `is_exclusive` is set to `true` whenever an exclusive lock is acquired. +// 12. `is_exclusive` is set to `false` whenever a shared lock is acquired. +// 13. `count` is incremented whenever any lock is acquired. +// 14. `count` is decremented whenever any lock is released. +// 15. Therefore, an exclusive lock can only be acquired when all other locks are released. +// 16. Therefore, a shared lock can only be acquired when any exclusive lock is released. +// +// Therefore, this implementation of `RawRwLock` is safe. +unsafe impl lock_api::RawRwLock for RawRwLock { + type GuardMarker = lock_api::GuardNoSend; + + const INIT: RawRwLock = RawRwLock::new(); + + #[inline] + fn lock_exclusive(&self) { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawRwLock::lock_exclusive(&self.0) + } else { + while !self.try_lock_exclusive() { + core::hint::spin_loop() + } + } + } + } + + #[inline] + fn try_lock_exclusive(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawRwLock::try_lock_exclusive(&self.0) + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + self.0 + .compare_exchange(RawRwLockState::Unlocked as _, RawRwLockState::Exclusive as _, Ordering::Acquire, Ordering::Relaxed) + .is_ok() + } else { + compile_error!("wgpu-types does not support platforms without atomics"); + } + } + } + + #[inline] + unsafe fn unlock_exclusive(&self) { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + // SAFETY: directly delegating to an accepted implementation + unsafe { lock_api::RawRwLock::unlock_exclusive(&self.0) } + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + self.0.store(RawRwLockState::Unlocked as _, Ordering::Release); + } else { + compile_error!("wgpu-types does not support platforms without atomics"); + } + } + } + + #[inline] + fn lock_shared(&self) { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawRwLock::lock_shared(&self.0) + } else { + while !self.try_lock_shared() { + core::hint::spin_loop() + } + } + } + } + + #[inline] + fn try_lock_shared(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawRwLock::try_lock_shared(&self.0) + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + self.0 + .fetch_update(Ordering::Acquire, Ordering::Relaxed, |state| { + if state == RawRwLockState::Exclusive as _ { + None + } else { + Some(state + 1) + } + }) + .is_ok() + } else { + compile_error!("wgpu-types does not support platforms without atomics"); + } + } + } + + #[inline] + unsafe fn unlock_shared(&self) { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + // SAFETY: directly delegating to an accepted implementation + unsafe { lock_api::RawRwLock::unlock_shared(&self.0) } + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + self.0.fetch_sub(1, Ordering::Release); + } else { + compile_error!("wgpu-types does not support platforms without atomics"); + } + } + } + + #[inline] + fn is_locked(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawRwLock::is_locked(&self.0) + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + self.0.load(Ordering::Acquire) != RawRwLockState::Unlocked as _ + } else { + compile_error!("wgpu-types does not support platforms without atomics"); + } + } + } + + #[inline] + fn is_locked_exclusive(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + lock_api::RawRwLock::is_locked_exclusive(&self.0) + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + self.0.load(Ordering::Acquire) == RawRwLockState::Exclusive as _ + } else { + compile_error!("wgpu-types does not support platforms without atomics"); + } + } + } +} + +// SAFETY: +// +// # With `std` or `spin` +// +// This implementation directly delegates to an existing implementation of +// `RawRwLockDowngrade`, and is therefore safe. +// +// # Without `std` or `spin` +// +// There is no specific safety conditions for this trait. +// However, from the `lock_api` documentation for `RawRwLockDowngrade`: +// +// > Additional methods for RwLocks which support atomically downgrading an +// > exclusive lock to a shared lock. +// +// `RawRwLock` supports an atomic downgrade by directly flipping `is_exclusive` +// to `false`. +// This is exactly equivalent to releasing an exclusive lock (decrements `count`), +// and acquiring a shared lock (increments `count` and sets `is_exclusive` to `false`). +// +// Therefore, this implementation of `RawRwLockDowngrade` is safe. +unsafe impl lock_api::RawRwLockDowngrade for RawRwLock { + unsafe fn downgrade(&self) { + cfg_if::cfg_if! { + if #[cfg(any(feature = "std", feature = "spin"))] { + // SAFETY: directly delegating to an accepted implementation + unsafe { lock_api::RawRwLockDowngrade::downgrade(&self.0) } + } else if #[cfg(all(target_has_atomic = "8", target_has_atomic = "ptr"))] { + self.0.store(RawRwLockState::SingleShared as _, Ordering::Release); + } else { + compile_error!("wgpu-types does not support platforms without atomics"); + } + } + } +} diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index cfed2660bb1..bdbf2be6e5f 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -36,7 +36,6 @@ ignored = ["cfg_aliases"] [features] default = [ "std", - "parking_lot", "dx12", "metal", "gles", @@ -194,7 +193,8 @@ std = [ ## required, e.g., for `no_std` support. ## If disabled, either `std::sync::Mutex` or `core::cell::RefCell` will be used, ## based on whether `std` is enabled or not. -parking_lot = ["dep:parking_lot"] +# FIXME: This can probably be removed as a feature and directly controlled by `std` +parking_lot = ["wgpu-types/std"] ## Enables `exhaust` method for `TextureFormat` of `wgpu-types`. ## @@ -218,7 +218,6 @@ cfg-if.workspace = true document-features.workspace = true log.workspace = true hashbrown.workspace = true -parking_lot = { workspace = true, optional = true } profiling.workspace = true raw-window-handle = { workspace = true, features = ["alloc"] } static_assertions.workspace = true diff --git a/wgpu/src/util/mod.rs b/wgpu/src/util/mod.rs index 1b9574d3e9f..0585e43ee4a 100644 --- a/wgpu/src/util/mod.rs +++ b/wgpu/src/util/mod.rs @@ -10,7 +10,6 @@ mod belt; mod device; mod encoder; mod init; -mod mutex; mod panicking; mod spirv; mod texture_blitter; @@ -29,8 +28,8 @@ pub use wgt::{ math::*, DispatchIndirectArgs, DrawIndexedIndirectArgs, DrawIndirectArgs, TextureDataOrder, }; -pub(crate) use mutex::Mutex; pub(crate) use panicking::is_panicking; +pub(crate) use wgt::sync::Mutex; use crate::BufferUsages; diff --git a/wgpu/src/util/mutex.rs b/wgpu/src/util/mutex.rs deleted file mode 100644 index 86dff533d0d..00000000000 --- a/wgpu/src/util/mutex.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! Provides a [`Mutex`] for internal use based on what features are available. - -cfg_if::cfg_if! { - if #[cfg(feature = "parking_lot")] { - use parking_lot::Mutex as MutexInner; - } else if #[cfg(std)] { - use std::sync::Mutex as MutexInner; - } else { - use core::cell::RefCell as MutexInner; - } -} - -pub(crate) struct Mutex { - inner: MutexInner, -} - -impl core::fmt::Debug for Mutex -where - MutexInner: core::fmt::Debug, -{ - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - as core::fmt::Debug>::fmt(&self.inner, f) - } -} - -impl Default for Mutex { - fn default() -> Self { - Self::new(::default()) - } -} - -impl Mutex { - pub const fn new(value: T) -> Self { - Self { - inner: MutexInner::new(value), - } - } -} - -impl Mutex { - pub fn lock(&self) -> impl core::ops::DerefMut + '_ { - cfg_if::cfg_if! { - if #[cfg(feature = "parking_lot")] { - self.inner.lock() - } else if #[cfg(std)] { - self.inner.lock().unwrap_or_else(std::sync::PoisonError::into_inner) - } else { - loop { - let Ok(lock) = self.inner.try_borrow_mut() else { - // Without `std` all we can do is spin until the current lock is released - core::hint::spin_loop(); - continue; - }; - - break lock; - } - } - } - } -}