-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rollup merge of #77147 - fusion-engineering-forks:static-mutex, r=dto…
…lnay Split sys_common::Mutex in StaticMutex and MovableMutex. The (unsafe) `Mutex` from `sys_common` had a rather complicated interface. You were supposed to call `init()` manually, unless you could guarantee it was neither moved nor used reentrantly. Calling `destroy()` was also optional, although it was unclear if 1) resources might be leaked or not, and 2) if `destroy()` should only be called when `init()` was called. This allowed for a number of interesting (confusing?) different ways to use this `Mutex`, all captured in a single type. In practice, this type was only ever used in two ways: 1. As a static variable. In this case, neither `init()` nor `destroy()` are called. The variable is never moved, and it is never used reentrantly. It is only ever locked using the `LockGuard`, never with `raw_lock`. 2. As a `Box`ed variable. In this case, both `init()` and `destroy()` are called, it will be moved and possibly used reentrantly. No other combinations are used anywhere in `std`. This change simplifies things by splitting this `Mutex` type into two types matching the two use cases: `StaticMutex` and `MovableMutex`. The interface of both new types is now both safer and simpler. The first one does not call nor expose `init`/`destroy`, and the second one calls those automatically in its `new()` and `Drop` functions. Also, the locking functions of `MovableMutex` are no longer unsafe. --- This will also make it easier to conditionally box mutexes later, by moving that decision into sys/sys_common. Some of the mutex implementations (at least those of Wasm and 'sys/unsupported') are safe to move, so wouldn't need a box. ~~(But that's blocked on #76932 for now.)~~ (See #77380.)
- Loading branch information
Showing
15 changed files
with
104 additions
and
133 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,101 +1,106 @@ | ||
use crate::sys::mutex as imp; | ||
|
||
/// An OS-based mutual exclusion lock. | ||
/// An OS-based mutual exclusion lock, meant for use in static variables. | ||
/// | ||
/// This mutex has a const constructor ([`StaticMutex::new`]), does not | ||
/// implement `Drop` to cleanup resources, and causes UB when moved or used | ||
/// reentrantly. | ||
/// | ||
/// This mutex does not implement poisoning. | ||
/// | ||
/// This is the thinnest cross-platform wrapper around OS mutexes. All usage of | ||
/// this mutex is unsafe and it is recommended to instead use the safe wrapper | ||
/// at the top level of the crate instead of this type. | ||
pub struct Mutex(imp::Mutex); | ||
/// This is a wrapper around `imp::Mutex` that does *not* call `init()` and | ||
/// `destroy()`. | ||
pub struct StaticMutex(imp::Mutex); | ||
|
||
unsafe impl Sync for Mutex {} | ||
unsafe impl Sync for StaticMutex {} | ||
|
||
impl Mutex { | ||
impl StaticMutex { | ||
/// Creates a new mutex for use. | ||
/// | ||
/// Behavior is undefined if the mutex is moved after it is | ||
/// first used with any of the functions below. | ||
/// Also, until `init` is called, behavior is undefined if this | ||
/// mutex is ever used reentrantly, i.e., `raw_lock` or `try_lock` | ||
/// are called by the thread currently holding the lock. | ||
/// Also, the behavior is undefined if this mutex is ever used reentrantly, | ||
/// i.e., `lock` is called by the thread currently holding the lock. | ||
#[rustc_const_stable(feature = "const_sys_mutex_new", since = "1.0.0")] | ||
pub const fn new() -> Mutex { | ||
Mutex(imp::Mutex::new()) | ||
pub const fn new() -> Self { | ||
Self(imp::Mutex::new()) | ||
} | ||
|
||
/// Prepare the mutex for use. | ||
/// Calls raw_lock() and then returns an RAII guard to guarantee the mutex | ||
/// will be unlocked. | ||
/// | ||
/// This should be called once the mutex is at a stable memory address. | ||
/// If called, this must be the very first thing that happens to the mutex. | ||
/// Calling it in parallel with or after any operation (including another | ||
/// `init()`) is undefined behavior. | ||
/// It is undefined behaviour to call this function while locked, or if the | ||
/// mutex has been moved since the last time this was called. | ||
#[inline] | ||
pub unsafe fn init(&mut self) { | ||
self.0.init() | ||
pub unsafe fn lock(&self) -> StaticMutexGuard<'_> { | ||
self.0.lock(); | ||
StaticMutexGuard(&self.0) | ||
} | ||
} | ||
|
||
/// Locks the mutex blocking the current thread until it is available. | ||
/// | ||
/// Behavior is undefined if the mutex has been moved between this and any | ||
/// previous function call. | ||
#[must_use] | ||
pub struct StaticMutexGuard<'a>(&'a imp::Mutex); | ||
|
||
impl Drop for StaticMutexGuard<'_> { | ||
#[inline] | ||
pub unsafe fn raw_lock(&self) { | ||
self.0.lock() | ||
fn drop(&mut self) { | ||
unsafe { | ||
self.0.unlock(); | ||
} | ||
} | ||
} | ||
|
||
/// Calls raw_lock() and then returns an RAII guard to guarantee the mutex | ||
/// will be unlocked. | ||
/// An OS-based mutual exclusion lock. | ||
/// | ||
/// This mutex does *not* have a const constructor, cleans up its resources in | ||
/// its `Drop` implementation, may safely be moved (when not borrowed), and | ||
/// does not cause UB when used reentrantly. | ||
/// | ||
/// This mutex does not implement poisoning. | ||
/// | ||
/// This is a wrapper around `Box<imp::Mutex>`, to allow the object to be moved | ||
/// without moving the raw mutex. | ||
pub struct MovableMutex(Box<imp::Mutex>); | ||
|
||
unsafe impl Sync for MovableMutex {} | ||
|
||
impl MovableMutex { | ||
/// Creates a new mutex. | ||
pub fn new() -> Self { | ||
let mut mutex = box imp::Mutex::new(); | ||
unsafe { mutex.init() }; | ||
Self(mutex) | ||
} | ||
|
||
pub(crate) fn raw(&self) -> &imp::Mutex { | ||
&self.0 | ||
} | ||
|
||
/// Locks the mutex blocking the current thread until it is available. | ||
#[inline] | ||
pub unsafe fn lock(&self) -> MutexGuard<'_> { | ||
self.raw_lock(); | ||
MutexGuard(&self.0) | ||
pub fn raw_lock(&self) { | ||
unsafe { self.0.lock() } | ||
} | ||
|
||
/// Attempts to lock the mutex without blocking, returning whether it was | ||
/// successfully acquired or not. | ||
/// | ||
/// Behavior is undefined if the mutex has been moved between this and any | ||
/// previous function call. | ||
#[inline] | ||
pub unsafe fn try_lock(&self) -> bool { | ||
self.0.try_lock() | ||
pub fn try_lock(&self) -> bool { | ||
unsafe { self.0.try_lock() } | ||
} | ||
|
||
/// Unlocks the mutex. | ||
/// | ||
/// Behavior is undefined if the current thread does not actually hold the | ||
/// mutex. | ||
/// | ||
/// Consider switching from the pair of raw_lock() and raw_unlock() to | ||
/// lock() whenever possible. | ||
#[inline] | ||
pub unsafe fn raw_unlock(&self) { | ||
self.0.unlock() | ||
} | ||
|
||
/// Deallocates all resources associated with this mutex. | ||
/// | ||
/// Behavior is undefined if there are current or will be future users of | ||
/// this mutex. | ||
#[inline] | ||
pub unsafe fn destroy(&self) { | ||
self.0.destroy() | ||
} | ||
} | ||
|
||
// not meant to be exported to the outside world, just the containing module | ||
pub fn raw(mutex: &Mutex) -> &imp::Mutex { | ||
&mutex.0 | ||
} | ||
|
||
#[must_use] | ||
/// A simple RAII utility for the above Mutex without the poisoning semantics. | ||
pub struct MutexGuard<'a>(&'a imp::Mutex); | ||
|
||
impl Drop for MutexGuard<'_> { | ||
#[inline] | ||
impl Drop for MovableMutex { | ||
fn drop(&mut self) { | ||
unsafe { | ||
self.0.unlock(); | ||
} | ||
unsafe { self.0.destroy() }; | ||
} | ||
} |
Oops, something went wrong.