wgpu_sync/
mutex.rs

1cfg_if::cfg_if! {
2    if #[cfg(feature = "std")] {
3        type RawMutexInner = parking_lot::RawMutex;
4    } else {
5        type RawMutexInner = core::cell::Cell<bool>;
6
7        /// When a `no_std` locking primitive is under contention, the "correct" way to
8        /// handle it would be to spin until the lock is available. This is because
9        /// without `std` there is no standard way to yield/block the current thread.
10        /// However, since we only support `no_std` locks that aren't `Sync`, we know
11        /// that only one thread can access the lock at a time. Therefore, we know this
12        /// is actually a deadlock and will never resolve. We choose to panic in these
13        /// cases to highlight what is almost certainly an internal bug.
14        fn deadlock() -> ! {
15            panic!("a locking primitive in wgpu is currently deadlocked");
16        }
17    }
18}
19
20/// Raw implementation for a [`lock_api::Mutex`].
21///
22/// This will delegate to [`parking_lot`] if the `std` feature is enabled (which
23/// it is by default). Otherwise, it will provide a `!Sync` implementation
24/// similar to [`RefCell`].
25///
26/// [`parking_lot`]: https://docs.rs/parking_lot/
27/// [`RefCell`]: core::cell::RefCell
28pub struct RawMutex(RawMutexInner);
29
30impl RawMutex {
31    /// Constructs a new [`RawMutex`].
32    pub const fn new() -> Self {
33        Self({
34            cfg_if::cfg_if! {
35                if #[cfg(feature = "std")] {
36                    lock_api::RawMutex::INIT
37                } else {
38                    RawMutexInner::new(false)
39                }
40            }
41        })
42    }
43}
44
45impl Default for RawMutex {
46    fn default() -> Self {
47        Self::new()
48    }
49}
50
51impl core::fmt::Debug for RawMutex {
52    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53        f.debug_tuple("RawMutex").finish_non_exhaustive()
54    }
55}
56
57// SAFETY:
58//
59// # With `std`
60//
61// This implementation directly delegates to an existing implementation of
62// `RawMutex`, and is therefore safe.
63//
64// # Without `std`
65//
66// This implementation tracks the state of the mutex in a boolean, where `false`
67// indicates it is unlocked, and `true` indicates it is locked. `is_locked`
68// directly returns this state, and only `try_lock` and `unlock` are able to
69// modify it. Both methods ensure the state of the lock is sound.
70unsafe impl lock_api::RawMutex for RawMutex {
71    type GuardMarker = lock_api::GuardNoSend;
72
73    const INIT: RawMutex = RawMutex::new();
74
75    #[inline]
76    fn lock(&self) {
77        cfg_if::cfg_if! {
78            if #[cfg(feature = "std")] {
79                lock_api::RawMutex::lock(&self.0)
80            } else {
81                if !self.try_lock() {
82                    // Since this "mutex" is `!Sync`, any attempt to lock it twice
83                    // must be from the same thread, which means a deadlock.
84                    deadlock()
85                }
86            }
87        }
88    }
89
90    #[inline]
91    fn try_lock(&self) -> bool {
92        cfg_if::cfg_if! {
93            if #[cfg(feature = "std")] {
94                lock_api::RawMutex::try_lock(&self.0)
95            } else {
96                !self.0.replace(true)
97            }
98        }
99    }
100
101    #[inline]
102    unsafe fn unlock(&self) {
103        cfg_if::cfg_if! {
104            if #[cfg(feature = "std")] {
105                // SAFETY: directly delegating to an accepted implementation
106                unsafe { lock_api::RawMutex::unlock(&self.0) }
107            } else {
108                self.0.set(false);
109            }
110        }
111    }
112
113    #[inline]
114    fn is_locked(&self) -> bool {
115        cfg_if::cfg_if! {
116            if #[cfg(feature = "std")] {
117                lock_api::RawMutex::is_locked(&self.0)
118            } else {
119                self.0.get()
120            }
121        }
122    }
123}
124
125// SAFETY:
126//
127// # With `std`
128//
129// This implementation directly delegates to an existing implementation of
130// `RawMutexTimed`, and is therefore safe.
131#[cfg(feature = "std")]
132unsafe impl lock_api::RawMutexTimed for RawMutex {
133    type Duration = core::time::Duration;
134    type Instant = <RawMutexInner as lock_api::RawMutexTimed>::Instant;
135
136    fn try_lock_for(&self, timeout: Self::Duration) -> bool {
137        lock_api::RawMutexTimed::try_lock_for(&self.0, timeout)
138    }
139
140    fn try_lock_until(&self, timeout: Self::Instant) -> bool {
141        lock_api::RawMutexTimed::try_lock_until(&self.0, timeout)
142    }
143}