diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-02-08 01:20:56 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-02-08 01:20:56 +0000 |
commit | 9d273378b93f938af3f3c6a75e76d206ac62abc4 (patch) | |
tree | a2a322209580b9cd981a2e8cad86c37fa0ecdea0 | |
parent | 79f3ae59497b278d20c5d316c7865a20de71951f (diff) | |
parent | e735ee64f5249ff65d2d3b1f58069864a9ad0df5 (diff) | |
download | lock_api-simpleperf-release.tar.gz |
Snap for 11421525 from e735ee64f5249ff65d2d3b1f58069864a9ad0df5 to simpleperf-releasesimpleperf-release
Change-Id: Ice4439abb6fba93091400fac046a536d259f7ea8
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | Android.bp | 6 | ||||
-rw-r--r-- | Cargo.toml | 13 | ||||
-rw-r--r-- | Cargo.toml.orig | 9 | ||||
-rw-r--r-- | METADATA | 25 | ||||
-rw-r--r-- | src/lib.rs | 3 | ||||
-rw-r--r-- | src/mutex.rs | 41 | ||||
-rw-r--r-- | src/remutex.rs | 39 | ||||
-rw-r--r-- | src/rwlock.rs | 387 |
9 files changed, 427 insertions, 98 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 6b5d531..73ab3ff 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "9e956adc2c6ecde7c15ff7611396d24be711c8a9" + "sha1": "8d92826bdcc8f7a507a2803ecbe5d98747f1df34" }, "path_in_vcs": "lock_api" }
\ No newline at end of file @@ -42,9 +42,13 @@ rust_library { host_supported: true, crate_name: "lock_api", cargo_env_compat: true, - cargo_pkg_version: "0.4.9", + cargo_pkg_version: "0.4.11", srcs: ["src/lib.rs"], edition: "2018", + features: [ + "atomic_usize", + "default", + ], cfgs: ["has_const_fn_trait_bound"], rustlibs: ["libscopeguard"], apex_available: [ @@ -11,8 +11,9 @@ [package] edition = "2018" +rust-version = "1.49.0" name = "lock_api" -version = "0.4.9" +version = "0.4.11" authors = ["Amanieu d'Antras <amanieu@gmail.com>"] description = "Wrappers to create fully-featured Mutex and RwLock types. Compatible with no_std." keywords = [ @@ -28,6 +29,14 @@ categories = [ license = "MIT OR Apache-2.0" repository = "https://github.com/Amanieu/parking_lot" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", + "--generate-link-to-definition", +] + [dependencies.owning_ref] version = "0.4.1" optional = true @@ -46,4 +55,6 @@ version = "1.1.0" [features] arc_lock = [] +atomic_usize = [] +default = ["atomic_usize"] nightly = [] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index c21bd8a..683e69f 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "lock_api" -version = "0.4.9" +version = "0.4.11" authors = ["Amanieu d'Antras <amanieu@gmail.com>"] description = "Wrappers to create fully-featured Mutex and RwLock types. Compatible with no_std." license = "MIT OR Apache-2.0" @@ -8,6 +8,11 @@ repository = "https://github.com/Amanieu/parking_lot" keywords = ["mutex", "rwlock", "lock", "no_std"] categories = ["concurrency", "no-std"] edition = "2018" +rust-version = "1.49.0" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] [dependencies] scopeguard = { version = "1.1.0", default-features = false } @@ -22,5 +27,7 @@ serde = { version = "1.0.126", default-features = false, optional = true } autocfg = "1.1.0" [features] +default = ["atomic_usize"] nightly = [] arc_lock = [] +atomic_usize = [] @@ -1,23 +1,20 @@ # This project was upgraded with external_updater. -# Usage: tools/external_updater/updater.sh update rust/crates/lock_api -# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md +# Usage: tools/external_updater/updater.sh update external/rust/crates/lock_api +# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md name: "lock_api" description: "Wrappers to create fully-featured Mutex and RwLock types. Compatible with no_std." third_party { - url { - type: HOMEPAGE - value: "https://crates.io/crates/lock_api" - } - url { - type: ARCHIVE - value: "https://static.crates.io/crates/lock_api/lock_api-0.4.9.crate" - } - version: "0.4.9" license_type: NOTICE last_upgrade_date { - year: 2022 - month: 12 - day: 12 + year: 2024 + month: 2 + day: 2 + } + homepage: "https://crates.io/crates/lock_api" + identifier { + type: "Archive" + value: "https://static.crates.io/crates/lock_api/lock_api-0.4.11.crate" + version: "0.4.11" } } @@ -86,6 +86,7 @@ //! requires the `alloc` crate to be present. #![no_std] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![warn(missing_docs)] #![warn(rust_2018_idioms)] @@ -106,7 +107,9 @@ unsafe impl Sync for GuardNoSend {} mod mutex; pub use crate::mutex::*; +#[cfg(feature = "atomic_usize")] mod remutex; +#[cfg(feature = "atomic_usize")] pub use crate::remutex::*; mod rwlock; diff --git a/src/mutex.rs b/src/mutex.rs index c97e543..80eadfa 100644 --- a/src/mutex.rs +++ b/src/mutex.rs @@ -189,11 +189,16 @@ impl<R, T> Mutex<R, T> { } impl<R: RawMutex, T: ?Sized> Mutex<R, T> { + /// Creates a new `MutexGuard` without checking if the mutex is locked. + /// /// # Safety /// - /// The lock must be held when calling this method. + /// This method must only be called if the thread logically holds the lock. + /// + /// Calling this function when a guard has already been produced is undefined behaviour unless + /// the guard was forgotten with `mem::forget`. #[inline] - unsafe fn guard(&self) -> MutexGuard<'_, R, T> { + pub unsafe fn make_guard_unchecked(&self) -> MutexGuard<'_, R, T> { MutexGuard { mutex: self, marker: PhantomData, @@ -213,7 +218,7 @@ impl<R: RawMutex, T: ?Sized> Mutex<R, T> { pub fn lock(&self) -> MutexGuard<'_, R, T> { self.raw.lock(); // SAFETY: The lock is held, as required. - unsafe { self.guard() } + unsafe { self.make_guard_unchecked() } } /// Attempts to acquire this lock. @@ -227,7 +232,7 @@ impl<R: RawMutex, T: ?Sized> Mutex<R, T> { pub fn try_lock(&self) -> Option<MutexGuard<'_, R, T>> { if self.raw.try_lock() { // SAFETY: The lock is held, as required. - Some(unsafe { self.guard() }) + Some(unsafe { self.make_guard_unchecked() }) } else { None } @@ -257,7 +262,7 @@ impl<R: RawMutex, T: ?Sized> Mutex<R, T> { /// # Safety /// /// This method must only be called if the current thread logically owns a - /// `MutexGuard` but that guard has be discarded using `mem::forget`. + /// `MutexGuard` but that guard has been discarded using `mem::forget`. /// Behavior is undefined if a mutex is unlocked when not locked. #[inline] pub unsafe fn force_unlock(&self) { @@ -294,12 +299,17 @@ impl<R: RawMutex, T: ?Sized> Mutex<R, T> { self.data.get() } + /// Creates a new `ArcMutexGuard` without checking if the mutex is locked. + /// /// # Safety /// - /// The lock needs to be held for the behavior of this function to be defined. + /// This method must only be called if the thread logically holds the lock. + /// + /// Calling this function when a guard has already been produced is undefined behaviour unless + /// the guard was forgotten with `mem::forget`. #[cfg(feature = "arc_lock")] #[inline] - unsafe fn guard_arc(self: &Arc<Self>) -> ArcMutexGuard<R, T> { + unsafe fn make_arc_guard_unchecked(self: &Arc<Self>) -> ArcMutexGuard<R, T> { ArcMutexGuard { mutex: self.clone(), marker: PhantomData, @@ -315,7 +325,7 @@ impl<R: RawMutex, T: ?Sized> Mutex<R, T> { pub fn lock_arc(self: &Arc<Self>) -> ArcMutexGuard<R, T> { self.raw.lock(); // SAFETY: the locking guarantee is upheld - unsafe { self.guard_arc() } + unsafe { self.make_arc_guard_unchecked() } } /// Attempts to acquire a lock through an `Arc`. @@ -327,7 +337,7 @@ impl<R: RawMutex, T: ?Sized> Mutex<R, T> { pub fn try_lock_arc(self: &Arc<Self>) -> Option<ArcMutexGuard<R, T>> { if self.raw.try_lock() { // SAFETY: locking guarantee is upheld - Some(unsafe { self.guard_arc() }) + Some(unsafe { self.make_arc_guard_unchecked() }) } else { None } @@ -344,7 +354,7 @@ impl<R: RawMutexFair, T: ?Sized> Mutex<R, T> { /// # Safety /// /// This method must only be called if the current thread logically owns a - /// `MutexGuard` but that guard has be discarded using `mem::forget`. + /// `MutexGuard` but that guard has been discarded using `mem::forget`. /// Behavior is undefined if a mutex is unlocked when not locked. #[inline] pub unsafe fn force_unlock_fair(&self) { @@ -362,7 +372,7 @@ impl<R: RawMutexTimed, T: ?Sized> Mutex<R, T> { pub fn try_lock_for(&self, timeout: R::Duration) -> Option<MutexGuard<'_, R, T>> { if self.raw.try_lock_for(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.guard() }) + Some(unsafe { self.make_guard_unchecked() }) } else { None } @@ -377,7 +387,7 @@ impl<R: RawMutexTimed, T: ?Sized> Mutex<R, T> { pub fn try_lock_until(&self, timeout: R::Instant) -> Option<MutexGuard<'_, R, T>> { if self.raw.try_lock_until(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.guard() }) + Some(unsafe { self.make_guard_unchecked() }) } else { None } @@ -392,7 +402,7 @@ impl<R: RawMutexTimed, T: ?Sized> Mutex<R, T> { pub fn try_lock_arc_for(self: &Arc<Self>, timeout: R::Duration) -> Option<ArcMutexGuard<R, T>> { if self.raw.try_lock_for(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.guard_arc() }) + Some(unsafe { self.make_arc_guard_unchecked() }) } else { None } @@ -410,7 +420,7 @@ impl<R: RawMutexTimed, T: ?Sized> Mutex<R, T> { ) -> Option<ArcMutexGuard<R, T>> { if self.raw.try_lock_until(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.guard_arc() }) + Some(unsafe { self.make_arc_guard_unchecked() }) } else { None } @@ -485,6 +495,7 @@ where /// /// The data protected by the mutex can be accessed through this guard via its /// `Deref` and `DerefMut` implementations. +#[clippy::has_significant_drop] #[must_use = "if unused the Mutex will immediately unlock"] pub struct MutexGuard<'a, R: RawMutex, T: ?Sized> { mutex: &'a Mutex<R, T>, @@ -678,6 +689,7 @@ unsafe impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> StableAddress for MutexGuard<' /// This is similar to the `MutexGuard` struct, except instead of using a reference to unlock the `Mutex` it /// uses an `Arc<Mutex>`. This has several advantages, most notably that it has an `'static` lifetime. #[cfg(feature = "arc_lock")] +#[clippy::has_significant_drop] #[must_use = "if unused the Mutex will immediately unlock"] pub struct ArcMutexGuard<R: RawMutex, T: ?Sized> { mutex: Arc<Mutex<R, T>>, @@ -813,6 +825,7 @@ impl<R: RawMutex, T: ?Sized> Drop for ArcMutexGuard<R, T> { /// former doesn't support temporarily unlocking and re-locking, since that /// could introduce soundness issues if the locked object is modified by another /// thread. +#[clippy::has_significant_drop] #[must_use = "if unused the Mutex will immediately unlock"] pub struct MappedMutexGuard<'a, R: RawMutex, T: ?Sized> { raw: &'a R, diff --git a/src/remutex.rs b/src/remutex.rs index 3e2010f..74f2da3 100644 --- a/src/remutex.rs +++ b/src/remutex.rs @@ -183,8 +183,10 @@ impl<R: RawMutexFair, G: GetThreadId> RawReentrantMutex<R, G> { if self.lock_count.get() == 1 { let id = self.owner.load(Ordering::Relaxed); self.owner.store(0, Ordering::Relaxed); + self.lock_count.set(0); self.mutex.bump(); self.owner.store(id, Ordering::Relaxed); + self.lock_count.set(1); } } } @@ -287,11 +289,16 @@ impl<R, G, T> ReentrantMutex<R, G, T> { } impl<R: RawMutex, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { + /// Creates a new `ReentrantMutexGuard` without checking if the lock is held. + /// /// # Safety /// - /// The lock must be held when calling this method. + /// This method must only be called if the thread logically holds the lock. + /// + /// Calling this function when a guard has already been produced is undefined behaviour unless + /// the guard was forgotten with `mem::forget`. #[inline] - unsafe fn guard(&self) -> ReentrantMutexGuard<'_, R, G, T> { + pub unsafe fn make_guard_unchecked(&self) -> ReentrantMutexGuard<'_, R, G, T> { ReentrantMutexGuard { remutex: &self, marker: PhantomData, @@ -312,7 +319,7 @@ impl<R: RawMutex, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { pub fn lock(&self) -> ReentrantMutexGuard<'_, R, G, T> { self.raw.lock(); // SAFETY: The lock is held, as required. - unsafe { self.guard() } + unsafe { self.make_guard_unchecked() } } /// Attempts to acquire this lock. @@ -326,7 +333,7 @@ impl<R: RawMutex, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { pub fn try_lock(&self) -> Option<ReentrantMutexGuard<'_, R, G, T>> { if self.raw.try_lock() { // SAFETY: The lock is held, as required. - Some(unsafe { self.guard() }) + Some(unsafe { self.make_guard_unchecked() }) } else { None } @@ -400,12 +407,17 @@ impl<R: RawMutex, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { self.data.get() } + /// Creates a new `ArcReentrantMutexGuard` without checking if the lock is held. + /// /// # Safety /// - /// The lock must be held before calling this method. + /// This method must only be called if the thread logically holds the lock. + /// + /// Calling this function when a guard has already been produced is undefined behaviour unless + /// the guard was forgotten with `mem::forget`. #[cfg(feature = "arc_lock")] #[inline] - unsafe fn guard_arc(self: &Arc<Self>) -> ArcReentrantMutexGuard<R, G, T> { + pub unsafe fn make_arc_guard_unchecked(self: &Arc<Self>) -> ArcReentrantMutexGuard<R, G, T> { ArcReentrantMutexGuard { remutex: self.clone(), marker: PhantomData, @@ -421,7 +433,7 @@ impl<R: RawMutex, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { pub fn lock_arc(self: &Arc<Self>) -> ArcReentrantMutexGuard<R, G, T> { self.raw.lock(); // SAFETY: locking guarantee is upheld - unsafe { self.guard_arc() } + unsafe { self.make_arc_guard_unchecked() } } /// Attempts to acquire a reentrant mutex through an `Arc`. @@ -433,7 +445,7 @@ impl<R: RawMutex, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { pub fn try_lock_arc(self: &Arc<Self>) -> Option<ArcReentrantMutexGuard<R, G, T>> { if self.raw.try_lock() { // SAFETY: locking guarantee is upheld - Some(unsafe { self.guard_arc() }) + Some(unsafe { self.make_arc_guard_unchecked() }) } else { None } @@ -468,7 +480,7 @@ impl<R: RawMutexTimed, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { pub fn try_lock_for(&self, timeout: R::Duration) -> Option<ReentrantMutexGuard<'_, R, G, T>> { if self.raw.try_lock_for(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.guard() }) + Some(unsafe { self.make_guard_unchecked() }) } else { None } @@ -483,7 +495,7 @@ impl<R: RawMutexTimed, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { pub fn try_lock_until(&self, timeout: R::Instant) -> Option<ReentrantMutexGuard<'_, R, G, T>> { if self.raw.try_lock_until(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.guard() }) + Some(unsafe { self.make_guard_unchecked() }) } else { None } @@ -501,7 +513,7 @@ impl<R: RawMutexTimed, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { ) -> Option<ArcReentrantMutexGuard<R, G, T>> { if self.raw.try_lock_for(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.guard_arc() }) + Some(unsafe { self.make_arc_guard_unchecked() }) } else { None } @@ -519,7 +531,7 @@ impl<R: RawMutexTimed, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { ) -> Option<ArcReentrantMutexGuard<R, G, T>> { if self.raw.try_lock_until(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.guard_arc() }) + Some(unsafe { self.make_arc_guard_unchecked() }) } else { None } @@ -599,6 +611,7 @@ where /// /// The data protected by the mutex can be accessed through this guard via its /// `Deref` implementation. +#[clippy::has_significant_drop] #[must_use = "if unused the ReentrantMutex will immediately unlock"] pub struct ReentrantMutexGuard<'a, R: RawMutex, G: GetThreadId, T: ?Sized> { remutex: &'a ReentrantMutex<R, G, T>, @@ -794,6 +807,7 @@ unsafe impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> StableAdd /// `Mutex` it uses an `Arc<ReentrantMutex>`. This has several advantages, most notably that it has an `'static` /// lifetime. #[cfg(feature = "arc_lock")] +#[clippy::has_significant_drop] #[must_use = "if unused the ReentrantMutex will immediately unlock"] pub struct ArcReentrantMutexGuard<R: RawMutex, G: GetThreadId, T: ?Sized> { remutex: Arc<ReentrantMutex<R, G, T>>, @@ -897,6 +911,7 @@ impl<R: RawMutex, G: GetThreadId, T: ?Sized> Drop for ArcReentrantMutexGuard<R, /// former doesn't support temporarily unlocking and re-locking, since that /// could introduce soundness issues if the locked object is modified by another /// thread. +#[clippy::has_significant_drop] #[must_use = "if unused the ReentrantMutex will immediately unlock"] pub struct MappedReentrantMutexGuard<'a, R: RawMutex, G: GetThreadId, T: ?Sized> { raw: &'a RawReentrantMutex<R, G>, diff --git a/src/rwlock.rs b/src/rwlock.rs index c972fb6..cf9e8aa 100644 --- a/src/rwlock.rs +++ b/src/rwlock.rs @@ -409,22 +409,33 @@ impl<R, T> RwLock<R, T> { } impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { + /// Creates a new `RwLockReadGuard` without checking if the lock is held. + /// /// # Safety /// - /// The lock must be held when calling this method. + /// This method must only be called if the thread logically holds a read lock. + /// + /// This function does not increment the read count of the lock. Calling this function when a + /// guard has already been produced is undefined behaviour unless the guard was forgotten + /// with `mem::forget`.` #[inline] - unsafe fn read_guard(&self) -> RwLockReadGuard<'_, R, T> { + pub unsafe fn make_read_guard_unchecked(&self) -> RwLockReadGuard<'_, R, T> { RwLockReadGuard { rwlock: self, marker: PhantomData, } } + /// Creates a new `RwLockReadGuard` without checking if the lock is held. + /// /// # Safety /// - /// The lock must be held when calling this method. + /// This method must only be called if the thread logically holds a write lock. + /// + /// Calling this function when a guard has already been produced is undefined behaviour unless + /// the guard was forgotten with `mem::forget`. #[inline] - unsafe fn write_guard(&self) -> RwLockWriteGuard<'_, R, T> { + pub unsafe fn make_write_guard_unchecked(&self) -> RwLockWriteGuard<'_, R, T> { RwLockWriteGuard { rwlock: self, marker: PhantomData, @@ -447,7 +458,7 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { pub fn read(&self) -> RwLockReadGuard<'_, R, T> { self.raw.lock_shared(); // SAFETY: The lock is held, as required. - unsafe { self.read_guard() } + unsafe { self.make_read_guard_unchecked() } } /// Attempts to acquire this `RwLock` with shared read access. @@ -461,7 +472,7 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { pub fn try_read(&self) -> Option<RwLockReadGuard<'_, R, T>> { if self.raw.try_lock_shared() { // SAFETY: The lock is held, as required. - Some(unsafe { self.read_guard() }) + Some(unsafe { self.make_read_guard_unchecked() }) } else { None } @@ -479,7 +490,7 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { pub fn write(&self) -> RwLockWriteGuard<'_, R, T> { self.raw.lock_exclusive(); // SAFETY: The lock is held, as required. - unsafe { self.write_guard() } + unsafe { self.make_write_guard_unchecked() } } /// Attempts to lock this `RwLock` with exclusive write access. @@ -493,7 +504,7 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { pub fn try_write(&self) -> Option<RwLockWriteGuard<'_, R, T>> { if self.raw.try_lock_exclusive() { // SAFETY: The lock is held, as required. - Some(unsafe { self.write_guard() }) + Some(unsafe { self.make_write_guard_unchecked() }) } else { None } @@ -583,24 +594,35 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { self.data.get() } + /// Creates a new `RwLockReadGuard` without checking if the lock is held. + /// /// # Safety /// - /// The lock must be held when calling this method. + /// This method must only be called if the thread logically holds a read lock. + /// + /// This function does not increment the read count of the lock. Calling this function when a + /// guard has already been produced is undefined behaviour unless the guard was forgotten + /// with `mem::forget`.` #[cfg(feature = "arc_lock")] #[inline] - unsafe fn read_guard_arc(self: &Arc<Self>) -> ArcRwLockReadGuard<R, T> { + pub unsafe fn make_arc_read_guard_unchecked(self: &Arc<Self>) -> ArcRwLockReadGuard<R, T> { ArcRwLockReadGuard { rwlock: self.clone(), marker: PhantomData, } } + /// Creates a new `RwLockWriteGuard` without checking if the lock is held. + /// /// # Safety /// - /// The lock must be held when calling this method. + /// This method must only be called if the thread logically holds a write lock. + /// + /// Calling this function when a guard has already been produced is undefined behaviour unless + /// the guard was forgotten with `mem::forget`. #[cfg(feature = "arc_lock")] #[inline] - unsafe fn write_guard_arc(self: &Arc<Self>) -> ArcRwLockWriteGuard<R, T> { + pub unsafe fn make_arc_write_guard_unchecked(self: &Arc<Self>) -> ArcRwLockWriteGuard<R, T> { ArcRwLockWriteGuard { rwlock: self.clone(), marker: PhantomData, @@ -616,7 +638,7 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { pub fn read_arc(self: &Arc<Self>) -> ArcRwLockReadGuard<R, T> { self.raw.lock_shared(); // SAFETY: locking guarantee is upheld - unsafe { self.read_guard_arc() } + unsafe { self.make_arc_read_guard_unchecked() } } /// Attempts to lock this `RwLock` with read access, through an `Arc`. @@ -628,7 +650,7 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { pub fn try_read_arc(self: &Arc<Self>) -> Option<ArcRwLockReadGuard<R, T>> { if self.raw.try_lock_shared() { // SAFETY: locking guarantee is upheld - Some(unsafe { self.read_guard_arc() }) + Some(unsafe { self.make_arc_read_guard_unchecked() }) } else { None } @@ -643,7 +665,7 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { pub fn write_arc(self: &Arc<Self>) -> ArcRwLockWriteGuard<R, T> { self.raw.lock_exclusive(); // SAFETY: locking guarantee is upheld - unsafe { self.write_guard_arc() } + unsafe { self.make_arc_write_guard_unchecked() } } /// Attempts to lock this `RwLock` with writ access, through an `Arc`. @@ -655,7 +677,7 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { pub fn try_write_arc(self: &Arc<Self>) -> Option<ArcRwLockWriteGuard<R, T>> { if self.raw.try_lock_exclusive() { // SAFETY: locking guarantee is upheld - Some(unsafe { self.write_guard_arc() }) + Some(unsafe { self.make_arc_write_guard_unchecked() }) } else { None } @@ -707,7 +729,7 @@ impl<R: RawRwLockTimed, T: ?Sized> RwLock<R, T> { pub fn try_read_for(&self, timeout: R::Duration) -> Option<RwLockReadGuard<'_, R, T>> { if self.raw.try_lock_shared_for(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.read_guard() }) + Some(unsafe { self.make_read_guard_unchecked() }) } else { None } @@ -723,7 +745,7 @@ impl<R: RawRwLockTimed, T: ?Sized> RwLock<R, T> { pub fn try_read_until(&self, timeout: R::Instant) -> Option<RwLockReadGuard<'_, R, T>> { if self.raw.try_lock_shared_until(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.read_guard() }) + Some(unsafe { self.make_read_guard_unchecked() }) } else { None } @@ -739,7 +761,7 @@ impl<R: RawRwLockTimed, T: ?Sized> RwLock<R, T> { pub fn try_write_for(&self, timeout: R::Duration) -> Option<RwLockWriteGuard<'_, R, T>> { if self.raw.try_lock_exclusive_for(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.write_guard() }) + Some(unsafe { self.make_write_guard_unchecked() }) } else { None } @@ -755,7 +777,7 @@ impl<R: RawRwLockTimed, T: ?Sized> RwLock<R, T> { pub fn try_write_until(&self, timeout: R::Instant) -> Option<RwLockWriteGuard<'_, R, T>> { if self.raw.try_lock_exclusive_until(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.write_guard() }) + Some(unsafe { self.make_write_guard_unchecked() }) } else { None } @@ -773,7 +795,7 @@ impl<R: RawRwLockTimed, T: ?Sized> RwLock<R, T> { ) -> Option<ArcRwLockReadGuard<R, T>> { if self.raw.try_lock_shared_for(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.read_guard_arc() }) + Some(unsafe { self.make_arc_read_guard_unchecked() }) } else { None } @@ -791,7 +813,7 @@ impl<R: RawRwLockTimed, T: ?Sized> RwLock<R, T> { ) -> Option<ArcRwLockReadGuard<R, T>> { if self.raw.try_lock_shared_until(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.read_guard_arc() }) + Some(unsafe { self.make_arc_read_guard_unchecked() }) } else { None } @@ -809,7 +831,7 @@ impl<R: RawRwLockTimed, T: ?Sized> RwLock<R, T> { ) -> Option<ArcRwLockWriteGuard<R, T>> { if self.raw.try_lock_exclusive_for(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.write_guard_arc() }) + Some(unsafe { self.make_arc_write_guard_unchecked() }) } else { None } @@ -827,7 +849,7 @@ impl<R: RawRwLockTimed, T: ?Sized> RwLock<R, T> { ) -> Option<ArcRwLockWriteGuard<R, T>> { if self.raw.try_lock_exclusive_until(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.write_guard_arc() }) + Some(unsafe { self.make_arc_write_guard_unchecked() }) } else { None } @@ -854,7 +876,7 @@ impl<R: RawRwLockRecursive, T: ?Sized> RwLock<R, T> { pub fn read_recursive(&self) -> RwLockReadGuard<'_, R, T> { self.raw.lock_shared_recursive(); // SAFETY: The lock is held, as required. - unsafe { self.read_guard() } + unsafe { self.make_read_guard_unchecked() } } /// Attempts to acquire this `RwLock` with shared read access. @@ -871,7 +893,7 @@ impl<R: RawRwLockRecursive, T: ?Sized> RwLock<R, T> { pub fn try_read_recursive(&self) -> Option<RwLockReadGuard<'_, R, T>> { if self.raw.try_lock_shared_recursive() { // SAFETY: The lock is held, as required. - Some(unsafe { self.read_guard() }) + Some(unsafe { self.make_read_guard_unchecked() }) } else { None } @@ -886,7 +908,7 @@ impl<R: RawRwLockRecursive, T: ?Sized> RwLock<R, T> { pub fn read_arc_recursive(self: &Arc<Self>) -> ArcRwLockReadGuard<R, T> { self.raw.lock_shared_recursive(); // SAFETY: locking guarantee is upheld - unsafe { self.read_guard_arc() } + unsafe { self.make_arc_read_guard_unchecked() } } /// Attempts to lock this `RwLock` with shared read access, through an `Arc`. @@ -898,7 +920,7 @@ impl<R: RawRwLockRecursive, T: ?Sized> RwLock<R, T> { pub fn try_read_recursive_arc(self: &Arc<Self>) -> Option<ArcRwLockReadGuard<R, T>> { if self.raw.try_lock_shared_recursive() { // SAFETY: locking guarantee is upheld - Some(unsafe { self.read_guard_arc() }) + Some(unsafe { self.make_arc_read_guard_unchecked() }) } else { None } @@ -923,7 +945,7 @@ impl<R: RawRwLockRecursiveTimed, T: ?Sized> RwLock<R, T> { ) -> Option<RwLockReadGuard<'_, R, T>> { if self.raw.try_lock_shared_recursive_for(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.read_guard() }) + Some(unsafe { self.make_read_guard_unchecked() }) } else { None } @@ -942,7 +964,7 @@ impl<R: RawRwLockRecursiveTimed, T: ?Sized> RwLock<R, T> { ) -> Option<RwLockReadGuard<'_, R, T>> { if self.raw.try_lock_shared_recursive_until(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.read_guard() }) + Some(unsafe { self.make_read_guard_unchecked() }) } else { None } @@ -960,7 +982,7 @@ impl<R: RawRwLockRecursiveTimed, T: ?Sized> RwLock<R, T> { ) -> Option<ArcRwLockReadGuard<R, T>> { if self.raw.try_lock_shared_recursive_for(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.read_guard_arc() }) + Some(unsafe { self.make_arc_read_guard_unchecked() }) } else { None } @@ -978,7 +1000,7 @@ impl<R: RawRwLockRecursiveTimed, T: ?Sized> RwLock<R, T> { ) -> Option<ArcRwLockReadGuard<R, T>> { if self.raw.try_lock_shared_recursive_until(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.read_guard_arc() }) + Some(unsafe { self.make_arc_read_guard_unchecked() }) } else { None } @@ -986,11 +1008,17 @@ impl<R: RawRwLockRecursiveTimed, T: ?Sized> RwLock<R, T> { } impl<R: RawRwLockUpgrade, T: ?Sized> RwLock<R, T> { + /// Creates a new `RwLockUpgradableReadGuard` without checking if the lock is held. + /// /// # Safety /// - /// The lock must be held when calling this method. + /// This method must only be called if the thread logically holds an upgradable read lock. + /// + /// This function does not increment the read count of the lock. Calling this function when a + /// guard has already been produced is undefined behaviour unless the guard was forgotten + /// with `mem::forget`.` #[inline] - unsafe fn upgradable_guard(&self) -> RwLockUpgradableReadGuard<'_, R, T> { + pub unsafe fn make_upgradable_guard_unchecked(&self) -> RwLockUpgradableReadGuard<'_, R, T> { RwLockUpgradableReadGuard { rwlock: self, marker: PhantomData, @@ -1010,7 +1038,7 @@ impl<R: RawRwLockUpgrade, T: ?Sized> RwLock<R, T> { pub fn upgradable_read(&self) -> RwLockUpgradableReadGuard<'_, R, T> { self.raw.lock_upgradable(); // SAFETY: The lock is held, as required. - unsafe { self.upgradable_guard() } + unsafe { self.make_upgradable_guard_unchecked() } } /// Attempts to acquire this `RwLock` with upgradable read access. @@ -1024,18 +1052,26 @@ impl<R: RawRwLockUpgrade, T: ?Sized> RwLock<R, T> { pub fn try_upgradable_read(&self) -> Option<RwLockUpgradableReadGuard<'_, R, T>> { if self.raw.try_lock_upgradable() { // SAFETY: The lock is held, as required. - Some(unsafe { self.upgradable_guard() }) + Some(unsafe { self.make_upgradable_guard_unchecked() }) } else { None } } + /// Creates a new `ArcRwLockUpgradableReadGuard` without checking if the lock is held. + /// /// # Safety /// - /// The lock must be held when calling this method. + /// This method must only be called if the thread logically holds an upgradable read lock. + /// + /// This function does not increment the read count of the lock. Calling this function when a + /// guard has already been produced is undefined behaviour unless the guard was forgotten + /// with `mem::forget`.` #[cfg(feature = "arc_lock")] #[inline] - unsafe fn upgradable_guard_arc(self: &Arc<Self>) -> ArcRwLockUpgradableReadGuard<R, T> { + pub unsafe fn make_upgradable_arc_guard_unchecked( + self: &Arc<Self>, + ) -> ArcRwLockUpgradableReadGuard<R, T> { ArcRwLockUpgradableReadGuard { rwlock: self.clone(), marker: PhantomData, @@ -1051,7 +1087,7 @@ impl<R: RawRwLockUpgrade, T: ?Sized> RwLock<R, T> { pub fn upgradable_read_arc(self: &Arc<Self>) -> ArcRwLockUpgradableReadGuard<R, T> { self.raw.lock_upgradable(); // SAFETY: locking guarantee is upheld - unsafe { self.upgradable_guard_arc() } + unsafe { self.make_upgradable_arc_guard_unchecked() } } /// Attempts to lock this `RwLock` with upgradable read access, through an `Arc`. @@ -1063,7 +1099,7 @@ impl<R: RawRwLockUpgrade, T: ?Sized> RwLock<R, T> { pub fn try_upgradable_read_arc(self: &Arc<Self>) -> Option<ArcRwLockUpgradableReadGuard<R, T>> { if self.raw.try_lock_upgradable() { // SAFETY: locking guarantee is upheld - Some(unsafe { self.upgradable_guard_arc() }) + Some(unsafe { self.make_upgradable_arc_guard_unchecked() }) } else { None } @@ -1084,7 +1120,7 @@ impl<R: RawRwLockUpgradeTimed, T: ?Sized> RwLock<R, T> { ) -> Option<RwLockUpgradableReadGuard<'_, R, T>> { if self.raw.try_lock_upgradable_for(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.upgradable_guard() }) + Some(unsafe { self.make_upgradable_guard_unchecked() }) } else { None } @@ -1103,7 +1139,7 @@ impl<R: RawRwLockUpgradeTimed, T: ?Sized> RwLock<R, T> { ) -> Option<RwLockUpgradableReadGuard<'_, R, T>> { if self.raw.try_lock_upgradable_until(timeout) { // SAFETY: The lock is held, as required. - Some(unsafe { self.upgradable_guard() }) + Some(unsafe { self.make_upgradable_guard_unchecked() }) } else { None } @@ -1121,7 +1157,7 @@ impl<R: RawRwLockUpgradeTimed, T: ?Sized> RwLock<R, T> { ) -> Option<ArcRwLockUpgradableReadGuard<R, T>> { if self.raw.try_lock_upgradable_for(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.upgradable_guard_arc() }) + Some(unsafe { self.make_upgradable_arc_guard_unchecked() }) } else { None } @@ -1139,7 +1175,7 @@ impl<R: RawRwLockUpgradeTimed, T: ?Sized> RwLock<R, T> { ) -> Option<ArcRwLockUpgradableReadGuard<R, T>> { if self.raw.try_lock_upgradable_until(timeout) { // SAFETY: locking guarantee is upheld - Some(unsafe { self.upgradable_guard_arc() }) + Some(unsafe { self.make_upgradable_arc_guard_unchecked() }) } else { None } @@ -1182,12 +1218,15 @@ impl<R: RawRwLock, T: ?Sized + fmt::Debug> fmt::Debug for RwLock<R, T> { /// RAII structure used to release the shared read access of a lock when /// dropped. +#[clippy::has_significant_drop] #[must_use = "if unused the RwLock will immediately unlock"] pub struct RwLockReadGuard<'a, R: RawRwLock, T: ?Sized> { rwlock: &'a RwLock<R, T>, marker: PhantomData<(&'a T, R::GuardMarker)>, } +unsafe impl<R: RawRwLock + Sync, T: Sync + ?Sized> Sync for RwLockReadGuard<'_, R, T> {} + impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> RwLockReadGuard<'a, R, T> { /// Returns a reference to the original reader-writer lock object. pub fn rwlock(s: &Self) -> &'a RwLock<R, T> { @@ -1246,8 +1285,6 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> RwLockReadGuard<'a, R, T> { /// Temporarily unlocks the `RwLock` to execute the given function. /// - /// The `RwLock` is unlocked a fair unlock protocol. - /// /// This is safe because `&mut` guarantees that there exist no other /// references to the data protected by the `RwLock`. #[inline] @@ -1359,6 +1396,7 @@ unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress for RwLockReadG /// This is similar to the `RwLockReadGuard` struct, except instead of using a reference to unlock the `RwLock` /// it uses an `Arc<RwLock>`. This has several advantages, most notably that it has an `'static` lifetime. #[cfg(feature = "arc_lock")] +#[clippy::has_significant_drop] #[must_use = "if unused the RwLock will immediately unlock"] pub struct ArcRwLockReadGuard<R: RawRwLock, T: ?Sized> { rwlock: Arc<RwLock<R, T>>, @@ -1470,12 +1508,15 @@ impl<R: RawRwLock, T: fmt::Display + ?Sized> fmt::Display for ArcRwLockReadGuard /// RAII structure used to release the exclusive write access of a lock when /// dropped. +#[clippy::has_significant_drop] #[must_use = "if unused the RwLock will immediately unlock"] pub struct RwLockWriteGuard<'a, R: RawRwLock, T: ?Sized> { rwlock: &'a RwLock<R, T>, marker: PhantomData<(&'a mut T, R::GuardMarker)>, } +unsafe impl<R: RawRwLock + Sync, T: Sync + ?Sized> Sync for RwLockWriteGuard<'_, R, T> {} + impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> RwLockWriteGuard<'a, R, T> { /// Returns a reference to the original reader-writer lock object. pub fn rwlock(s: &Self) -> &'a RwLock<R, T> { @@ -1693,6 +1734,7 @@ unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress for RwLockWrite /// This is similar to the `RwLockWriteGuard` struct, except instead of using a reference to unlock the `RwLock` /// it uses an `Arc<RwLock>`. This has several advantages, most notably that it has an `'static` lifetime. #[cfg(feature = "arc_lock")] +#[clippy::has_significant_drop] #[must_use = "if unused the RwLock will immediately unlock"] pub struct ArcRwLockWriteGuard<R: RawRwLock, T: ?Sized> { rwlock: Arc<RwLock<R, T>>, @@ -1858,6 +1900,7 @@ impl<R: RawRwLock, T: fmt::Display + ?Sized> fmt::Display for ArcRwLockWriteGuar /// RAII structure used to release the upgradable read access of a lock when /// dropped. +#[clippy::has_significant_drop] #[must_use = "if unused the RwLock will immediately unlock"] pub struct RwLockUpgradableReadGuard<'a, R: RawRwLockUpgrade, T: ?Sized> { rwlock: &'a RwLock<R, T>, @@ -1892,7 +1935,7 @@ impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> RwLockUpgradableReadGuard<'a, f() } - /// Atomically upgrades an upgradable read lock lock into a exclusive write lock, + /// Atomically upgrades an upgradable read lock lock into an exclusive write lock, /// blocking the current thread until it can be acquired. pub fn upgrade(s: Self) -> RwLockWriteGuard<'a, R, T> { // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. @@ -1907,7 +1950,7 @@ impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> RwLockUpgradableReadGuard<'a, } } - /// Tries to atomically upgrade an upgradable read lock into a exclusive write lock. + /// Tries to atomically upgrade an upgradable read lock into an exclusive write lock. /// /// If the access could not be granted at this time, then the current guard is returned. pub fn try_upgrade(s: Self) -> Result<RwLockWriteGuard<'a, R, T>, Self> { @@ -2000,10 +2043,60 @@ impl<'a, R: RawRwLockUpgradeDowngrade + 'a, T: ?Sized + 'a> RwLockUpgradableRead marker: PhantomData, } } + + /// First, atomically upgrades an upgradable read lock lock into an exclusive write lock, + /// blocking the current thread until it can be acquired. + /// + /// Then, calls the provided closure with an exclusive reference to the lock's data. + /// + /// Finally, atomically downgrades the lock back to an upgradable read lock. + /// The closure's return value is wrapped in `Some` and returned. + /// + /// This function only requires a mutable reference to the guard, unlike + /// `upgrade` which takes the guard by value. + pub fn with_upgraded<Ret, F: FnOnce(&mut T) -> Ret>(&mut self, f: F) -> Ret { + unsafe { + self.rwlock.raw.upgrade(); + } + + // Safety: We just upgraded the lock, so we have mutable access to the data. + // This will restore the state the lock was in at the start of the function. + defer!(unsafe { self.rwlock.raw.downgrade_to_upgradable() }); + + // Safety: We upgraded the lock, so we have mutable access to the data. + // When this function returns, whether by drop or panic, + // the drop guard will downgrade it back to an upgradeable lock. + f(unsafe { &mut *self.rwlock.data.get() }) + } + + /// First, tries to atomically upgrade an upgradable read lock into an exclusive write lock. + /// + /// If the access could not be granted at this time, then `None` is returned. + /// + /// Otherwise, calls the provided closure with an exclusive reference to the lock's data, + /// and finally downgrades the lock back to an upgradable read lock. + /// The closure's return value is wrapped in `Some` and returned. + /// + /// This function only requires a mutable reference to the guard, unlike + /// `try_upgrade` which takes the guard by value. + pub fn try_with_upgraded<Ret, F: FnOnce(&mut T) -> Ret>(&mut self, f: F) -> Option<Ret> { + if unsafe { self.rwlock.raw.try_upgrade() } { + // Safety: We just upgraded the lock, so we have mutable access to the data. + // This will restore the state the lock was in at the start of the function. + defer!(unsafe { self.rwlock.raw.downgrade_to_upgradable() }); + + // Safety: We upgraded the lock, so we have mutable access to the data. + // When this function returns, whether by drop or panic, + // the drop guard will downgrade it back to an upgradeable lock. + Some(f(unsafe { &mut *self.rwlock.data.get() })) + } else { + None + } + } } impl<'a, R: RawRwLockUpgradeTimed + 'a, T: ?Sized + 'a> RwLockUpgradableReadGuard<'a, R, T> { - /// Tries to atomically upgrade an upgradable read lock into a exclusive + /// Tries to atomically upgrade an upgradable read lock into an exclusive /// write lock, until a timeout is reached. /// /// If the access could not be granted before the timeout expires, then @@ -2025,7 +2118,7 @@ impl<'a, R: RawRwLockUpgradeTimed + 'a, T: ?Sized + 'a> RwLockUpgradableReadGuar } } - /// Tries to atomically upgrade an upgradable read lock into a exclusive + /// Tries to atomically upgrade an upgradable read lock into an exclusive /// write lock, until a timeout is reached. /// /// If the access could not be granted before the timeout expires, then @@ -2049,6 +2142,72 @@ impl<'a, R: RawRwLockUpgradeTimed + 'a, T: ?Sized + 'a> RwLockUpgradableReadGuar } } +impl<'a, R: RawRwLockUpgradeTimed + RawRwLockUpgradeDowngrade + 'a, T: ?Sized + 'a> + RwLockUpgradableReadGuard<'a, R, T> +{ + /// Tries to atomically upgrade an upgradable read lock into an exclusive + /// write lock, until a timeout is reached. + /// + /// If the access could not be granted before the timeout expires, then + /// `None` is returned. + /// + /// Otherwise, calls the provided closure with an exclusive reference to the lock's data, + /// and finally downgrades the lock back to an upgradable read lock. + /// The closure's return value is wrapped in `Some` and returned. + /// + /// This function only requires a mutable reference to the guard, unlike + /// `try_upgrade_for` which takes the guard by value. + pub fn try_with_upgraded_for<Ret, F: FnOnce(&mut T) -> Ret>( + &mut self, + timeout: R::Duration, + f: F, + ) -> Option<Ret> { + if unsafe { self.rwlock.raw.try_upgrade_for(timeout) } { + // Safety: We just upgraded the lock, so we have mutable access to the data. + // This will restore the state the lock was in at the start of the function. + defer!(unsafe { self.rwlock.raw.downgrade_upgradable() }); + + // Safety: We upgraded the lock, so we have mutable access to the data. + // When this function returns, whether by drop or panic, + // the drop guard will downgrade it back to an upgradeable lock. + Some(f(unsafe { &mut *self.rwlock.data.get() })) + } else { + None + } + } + + /// Tries to atomically upgrade an upgradable read lock into an exclusive + /// write lock, until a timeout is reached. + /// + /// If the access could not be granted before the timeout expires, then + /// `None` is returned. + /// + /// Otherwise, calls the provided closure with an exclusive reference to the lock's data, + /// and finally downgrades the lock back to an upgradable read lock. + /// The closure's return value is wrapped in `Some` and returned. + /// + /// This function only requires a mutable reference to the guard, unlike + /// `try_upgrade_until` which takes the guard by value. + pub fn try_with_upgraded_until<Ret, F: FnOnce(&mut T) -> Ret>( + &mut self, + timeout: R::Instant, + f: F, + ) -> Option<Ret> { + if unsafe { self.rwlock.raw.try_upgrade_until(timeout) } { + // Safety: We just upgraded the lock, so we have mutable access to the data. + // This will restore the state the lock was in at the start of the function. + defer!(unsafe { self.rwlock.raw.downgrade_upgradable() }); + + // Safety: We upgraded the lock, so we have mutable access to the data. + // When this function returns, whether by drop or panic, + // the drop guard will downgrade it back to an upgradeable lock. + Some(f(unsafe { &mut *self.rwlock.data.get() })) + } else { + None + } + } +} + impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> Deref for RwLockUpgradableReadGuard<'a, R, T> { type Target = T; #[inline] @@ -2094,6 +2253,7 @@ unsafe impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> StableAddress /// `RwLock` it uses an `Arc<RwLock>`. This has several advantages, most notably that it has an `'static` /// lifetime. #[cfg(feature = "arc_lock")] +#[clippy::has_significant_drop] #[must_use = "if unused the RwLock will immediately unlock"] pub struct ArcRwLockUpgradableReadGuard<R: RawRwLockUpgrade, T: ?Sized> { rwlock: Arc<RwLock<R, T>>, @@ -2123,7 +2283,7 @@ impl<R: RawRwLockUpgrade, T: ?Sized> ArcRwLockUpgradableReadGuard<R, T> { f() } - /// Atomically upgrades an upgradable read lock lock into a exclusive write lock, + /// Atomically upgrades an upgradable read lock lock into an exclusive write lock, /// blocking the current thread until it can be acquired. pub fn upgrade(s: Self) -> ArcRwLockWriteGuard<R, T> { // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. @@ -2142,7 +2302,7 @@ impl<R: RawRwLockUpgrade, T: ?Sized> ArcRwLockUpgradableReadGuard<R, T> { } } - /// Tries to atomically upgrade an upgradable read lock into a exclusive write lock. + /// Tries to atomically upgrade an upgradable read lock into an exclusive write lock. /// /// If the access could not be granted at this time, then the current guard is returned. pub fn try_upgrade(s: Self) -> Result<ArcRwLockWriteGuard<R, T>, Self> { @@ -2231,11 +2391,61 @@ impl<R: RawRwLockUpgradeDowngrade, T: ?Sized> ArcRwLockUpgradableReadGuard<R, T> marker: PhantomData, } } + + /// First, atomically upgrades an upgradable read lock lock into an exclusive write lock, + /// blocking the current thread until it can be acquired. + /// + /// Then, calls the provided closure with an exclusive reference to the lock's data. + /// + /// Finally, atomically downgrades the lock back to an upgradable read lock. + /// The closure's return value is returned. + /// + /// This function only requires a mutable reference to the guard, unlike + /// `upgrade` which takes the guard by value. + pub fn with_upgraded<Ret, F: FnOnce(&mut T) -> Ret>(&mut self, f: F) -> Ret { + unsafe { + self.rwlock.raw.upgrade(); + } + + // Safety: We just upgraded the lock, so we have mutable access to the data. + // This will restore the state the lock was in at the start of the function. + defer!(unsafe { self.rwlock.raw.downgrade_upgradable() }); + + // Safety: We upgraded the lock, so we have mutable access to the data. + // When this function returns, whether by drop or panic, + // the drop guard will downgrade it back to an upgradeable lock. + f(unsafe { &mut *self.rwlock.data.get() }) + } + + /// First, tries to atomically upgrade an upgradable read lock into an exclusive write lock. + /// + /// If the access could not be granted at this time, then `None` is returned. + /// + /// Otherwise, calls the provided closure with an exclusive reference to the lock's data, + /// and finally downgrades the lock back to an upgradable read lock. + /// The closure's return value is wrapped in `Some` and returned. + /// + /// This function only requires a mutable reference to the guard, unlike + /// `try_upgrade` which takes the guard by value. + pub fn try_with_upgraded<Ret, F: FnOnce(&mut T) -> Ret>(&mut self, f: F) -> Option<Ret> { + if unsafe { self.rwlock.raw.try_upgrade() } { + // Safety: We just upgraded the lock, so we have mutable access to the data. + // This will restore the state the lock was in at the start of the function. + defer!(unsafe { self.rwlock.raw.downgrade_upgradable() }); + + // Safety: We upgraded the lock, so we have mutable access to the data. + // When this function returns, whether by drop or panic, + // the drop guard will downgrade it back to an upgradeable lock. + Some(f(unsafe { &mut *self.rwlock.data.get() })) + } else { + None + } + } } #[cfg(feature = "arc_lock")] impl<R: RawRwLockUpgradeTimed, T: ?Sized> ArcRwLockUpgradableReadGuard<R, T> { - /// Tries to atomically upgrade an upgradable read lock into a exclusive + /// Tries to atomically upgrade an upgradable read lock into an exclusive /// write lock, until a timeout is reached. /// /// If the access could not be granted before the timeout expires, then @@ -2259,7 +2469,7 @@ impl<R: RawRwLockUpgradeTimed, T: ?Sized> ArcRwLockUpgradableReadGuard<R, T> { } } - /// Tries to atomically upgrade an upgradable read lock into a exclusive + /// Tries to atomically upgrade an upgradable read lock into an exclusive /// write lock, until a timeout is reached. /// /// If the access could not be granted before the timeout expires, then @@ -2286,6 +2496,73 @@ impl<R: RawRwLockUpgradeTimed, T: ?Sized> ArcRwLockUpgradableReadGuard<R, T> { } #[cfg(feature = "arc_lock")] +impl<R: RawRwLockUpgradeTimed + RawRwLockUpgradeDowngrade, T: ?Sized> + ArcRwLockUpgradableReadGuard<R, T> +{ + /// Tries to atomically upgrade an upgradable read lock into an exclusive + /// write lock, until a timeout is reached. + /// + /// If the access could not be granted before the timeout expires, then + /// `None` is returned. + /// + /// Otherwise, calls the provided closure with an exclusive reference to the lock's data, + /// and finally downgrades the lock back to an upgradable read lock. + /// The closure's return value is wrapped in `Some` and returned. + /// + /// This function only requires a mutable reference to the guard, unlike + /// `try_upgrade_for` which takes the guard by value. + pub fn try_with_upgraded_for<Ret, F: FnOnce(&mut T) -> Ret>( + &mut self, + timeout: R::Duration, + f: F, + ) -> Option<Ret> { + if unsafe { self.rwlock.raw.try_upgrade_for(timeout) } { + // Safety: We just upgraded the lock, so we have mutable access to the data. + // This will restore the state the lock was in at the start of the function. + defer!(unsafe { self.rwlock.raw.downgrade_upgradable() }); + + // Safety: We upgraded the lock, so we have mutable access to the data. + // When this function returns, whether by drop or panic, + // the drop guard will downgrade it back to an upgradeable lock. + Some(f(unsafe { &mut *self.rwlock.data.get() })) + } else { + None + } + } + + /// Tries to atomically upgrade an upgradable read lock into an exclusive + /// write lock, until a timeout is reached. + /// + /// If the access could not be granted before the timeout expires, then + /// `None` is returned. + /// + /// Otherwise, calls the provided closure with an exclusive reference to the lock's data, + /// and finally downgrades the lock back to an upgradable read lock. + /// The closure's return value is wrapped in `Some` and returned. + /// + /// This function only requires a mutable reference to the guard, unlike + /// `try_upgrade_until` which takes the guard by value. + pub fn try_with_upgraded_until<Ret, F: FnOnce(&mut T) -> Ret>( + &mut self, + timeout: R::Instant, + f: F, + ) -> Option<Ret> { + if unsafe { self.rwlock.raw.try_upgrade_until(timeout) } { + // Safety: We just upgraded the lock, so we have mutable access to the data. + // This will restore the state the lock was in at the start of the function. + defer!(unsafe { self.rwlock.raw.downgrade_upgradable() }); + + // Safety: We upgraded the lock, so we have mutable access to the data. + // When this function returns, whether by drop or panic, + // the drop guard will downgrade it back to an upgradeable lock. + Some(f(unsafe { &mut *self.rwlock.data.get() })) + } else { + None + } + } +} + +#[cfg(feature = "arc_lock")] impl<R: RawRwLockUpgrade, T: ?Sized> Deref for ArcRwLockUpgradableReadGuard<R, T> { type Target = T; #[inline] @@ -2330,6 +2607,7 @@ impl<R: RawRwLockUpgrade, T: fmt::Display + ?Sized> fmt::Display /// former doesn't support temporarily unlocking and re-locking, since that /// could introduce soundness issues if the locked object is modified by another /// thread. +#[clippy::has_significant_drop] #[must_use = "if unused the RwLock will immediately unlock"] pub struct MappedRwLockReadGuard<'a, R: RawRwLock, T: ?Sized> { raw: &'a R, @@ -2465,6 +2743,7 @@ unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress /// former doesn't support temporarily unlocking and re-locking, since that /// could introduce soundness issues if the locked object is modified by another /// thread. +#[clippy::has_significant_drop] #[must_use = "if unused the RwLock will immediately unlock"] pub struct MappedRwLockWriteGuard<'a, R: RawRwLock, T: ?Sized> { raw: &'a R, |