diff options
author | Joel Galenson <jgalenson@google.com> | 2021-09-23 15:02:33 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-09-23 15:02:33 +0000 |
commit | 25109b81160d6c6cfdad41c121e99e35955227c4 (patch) | |
tree | 4f80424a9f04b06b382379c2a2c9af0bb7cb4174 | |
parent | 674b7a2427b5336a8f10b4eaef1bbd238e4f0698 (diff) | |
parent | 1fd07417ced44c41675a159f74db7a6afac38544 (diff) | |
download | lock_api-25109b81160d6c6cfdad41c121e99e35955227c4.tar.gz |
Upgrade rust/crates/lock_api to 0.4.5 am: 3272971475 am: 42502d8df4 am: 1fd07417ce
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/lock_api/+/1833321
Change-Id: I3e91ecccbd6ee580d63e19d0ffb91a11edf97581
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | Android.bp | 15 | ||||
-rw-r--r-- | Cargo.toml | 14 | ||||
-rw-r--r-- | Cargo.toml.orig | 5 | ||||
-rw-r--r-- | METADATA | 8 | ||||
-rw-r--r-- | src/lib.rs | 7 | ||||
-rw-r--r-- | src/mutex.rs | 189 | ||||
-rw-r--r-- | src/remutex.rs | 185 | ||||
-rw-r--r-- | src/rwlock.rs | 797 |
9 files changed, 1200 insertions, 22 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 6ffdcd3..58935f2 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "0dcf7ce54a4f8ca76367c1bd2eeb48644b416d84" + "sha1": "18001b819c1539c06c176c671bbe54e70b5c3d69" } } @@ -1,4 +1,4 @@ -// This file is generated by cargo2android.py --run --device --dependencies --tests. +// This file is generated by cargo2android.py --run --device --tests. // Do not modify this file as changes will be overridden on upgrade. package { @@ -41,6 +41,8 @@ rust_library { name: "liblock_api", host_supported: true, crate_name: "lock_api", + cargo_env_compat: true, + cargo_pkg_version: "0.4.5", srcs: ["src/lib.rs"], edition: "2018", rustlibs: [ @@ -49,9 +51,11 @@ rust_library { } rust_defaults { - name: "lock_api_defaults", + name: "lock_api_test_defaults", crate_name: "lock_api", srcs: ["src/lib.rs"], + cargo_env_compat: true, + cargo_pkg_version: "0.4.5", test_suites: ["general-tests"], auto_gen_config: true, edition: "2018", @@ -62,7 +66,7 @@ rust_defaults { rust_test_host { name: "lock_api_host_test_src_lib", - defaults: ["lock_api_defaults"], + defaults: ["lock_api_test_defaults"], test_options: { unit_test: true, }, @@ -70,8 +74,5 @@ rust_test_host { rust_test { name: "lock_api_device_test_src_lib", - defaults: ["lock_api_defaults"], + defaults: ["lock_api_test_defaults"], } - -// dependent_library ["feature_list"] -// scopeguard-1.1.0 @@ -3,17 +3,16 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "lock_api" -version = "0.4.4" +version = "0.4.5" authors = ["Amanieu d'Antras <amanieu@gmail.com>"] description = "Wrappers to create fully-featured Mutex and RwLock types. Compatible with no_std." keywords = ["mutex", "rwlock", "lock", "no_std"] @@ -29,9 +28,10 @@ version = "1.1.0" default-features = false [dependencies.serde] -version = "1.0.114" +version = "1.0.126" optional = true default-features = false [features] +arc_lock = [] nightly = [] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 55b5c35..96ffdfc 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "lock_api" -version = "0.4.4" +version = "0.4.5" authors = ["Amanieu d'Antras <amanieu@gmail.com>"] description = "Wrappers to create fully-featured Mutex and RwLock types. Compatible with no_std." license = "Apache-2.0/MIT" @@ -16,7 +16,8 @@ owning_ref = { version = "0.4.1", optional = true } # Optional dependency for supporting serde. Optional crates automatically # create a feature with the same name as the crate, so if you need serde # support, just pass "--features serde" when building this crate. -serde = { version = "1.0.114", default-features = false, optional = true } +serde = { version = "1.0.126", default-features = false, optional = true } [features] nightly = [] +arc_lock = [] @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/lock_api/lock_api-0.4.4.crate" + value: "https://static.crates.io/crates/lock_api/lock_api-0.4.5.crate" } - version: "0.4.4" + version: "0.4.5" license_type: NOTICE last_upgrade_date { year: 2021 - month: 5 - day: 19 + month: 9 + day: 22 } } @@ -79,9 +79,11 @@ //! //! # Cargo features //! -//! This crate supports two cargo features: +//! This crate supports three cargo features: //! //! - `owning_ref`: Allows your lock types to be used with the `owning_ref` crate. +//! - `arc_lock`: Enables locking from an `Arc`. This enables types such as `ArcMutexGuard`. Note that this +//! requires the `alloc` crate to be present. //! - `nightly`: Enables nightly-only features. At the moment the only such //! feature is `const fn` constructors for lock types. @@ -93,6 +95,9 @@ #[macro_use] extern crate scopeguard; +#[cfg(feature = "arc_lock")] +extern crate alloc; + /// Marker type which indicates that the Guard type for a lock is `Send`. pub struct GuardSend(()); diff --git a/src/mutex.rs b/src/mutex.rs index aded96d..f64fc13 100644 --- a/src/mutex.rs +++ b/src/mutex.rs @@ -11,6 +11,13 @@ use core::marker::PhantomData; use core::mem; use core::ops::{Deref, DerefMut}; +#[cfg(feature = "arc_lock")] +use alloc::sync::Arc; +#[cfg(feature = "arc_lock")] +use core::mem::ManuallyDrop; +#[cfg(feature = "arc_lock")] +use core::ptr; + #[cfg(feature = "owning_ref")] use owning_ref::StableAddress; @@ -286,6 +293,45 @@ impl<R: RawMutex, T: ?Sized> Mutex<R, T> { pub fn data_ptr(&self) -> *mut T { self.data.get() } + + /// # Safety + /// + /// The lock needs to be held for the behavior of this function to be defined. + #[cfg(feature = "arc_lock")] + #[inline] + unsafe fn guard_arc(self: &Arc<Self>) -> ArcMutexGuard<R, T> { + ArcMutexGuard { + mutex: self.clone(), + marker: PhantomData, + } + } + + /// Acquires a lock through an `Arc`. + /// + /// This method is similar to the `lock` method; however, it requires the `Mutex` to be inside of an `Arc` + /// and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn lock_arc(self: &Arc<Self>) -> ArcMutexGuard<R, T> { + self.raw.lock(); + // SAFETY: the locking guarantee is upheld + unsafe { self.guard_arc() } + } + + /// Attempts to acquire a lock through an `Arc`. + /// + /// This method is similar to the `try_lock` method; however, it requires the `Mutex` to be inside of an + /// `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + 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() }) + } else { + None + } + } } impl<R: RawMutexFair, T: ?Sized> Mutex<R, T> { @@ -336,6 +382,39 @@ impl<R: RawMutexTimed, T: ?Sized> Mutex<R, T> { None } } + + /// Attempts to acquire this lock through an `Arc` until a timeout is reached. + /// + /// This method is similar to the `try_lock_for` method; however, it requires the `Mutex` to be inside of an + /// `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + 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() }) + } else { + None + } + } + + /// Attempts to acquire this lock through an `Arc` until a timeout is reached. + /// + /// This method is similar to the `try_lock_until` method; however, it requires the `Mutex` to be inside of + /// an `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_lock_arc_until( + self: &Arc<Self>, + timeout: R::Instant, + ) -> Option<ArcMutexGuard<R, T>> { + if self.raw.try_lock_until(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.guard_arc() }) + } else { + None + } + } } impl<R: RawMutex, T: ?Sized + Default> Default for Mutex<R, T> { @@ -583,6 +662,116 @@ impl<'a, R: RawMutex + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display for Mutex #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> StableAddress for MutexGuard<'a, R, T> {} +/// An RAII mutex guard returned by the `Arc` locking operations on `Mutex`. +/// +/// 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")] +#[must_use = "if unused the Mutex will immediately unlock"] +pub struct ArcMutexGuard<R: RawMutex, T: ?Sized> { + mutex: Arc<Mutex<R, T>>, + marker: PhantomData<R::GuardMarker>, +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutex, T: ?Sized> ArcMutexGuard<R, T> { + /// Returns a reference to the `Mutex` this is guarding, contained in its `Arc`. + #[inline] + pub fn mutex(&self) -> &Arc<Mutex<R, T>> { + &self.mutex + } + + /// Temporarily unlocks the mutex to execute the given function. + /// + /// This is safe because `&mut` guarantees that there exist no other + /// references to the data protected by the mutex. + #[inline] + pub fn unlocked<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: A MutexGuard always holds the lock. + unsafe { + s.mutex.raw.unlock(); + } + defer!(s.mutex.raw.lock()); + f() + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutexFair, T: ?Sized> ArcMutexGuard<R, T> { + /// Unlocks the mutex using a fair unlock protocol. + /// + /// This is functionally identical to the `unlock_fair` method on [`MutexGuard`]. + #[inline] + pub fn unlock_fair(s: Self) { + // Safety: A MutexGuard always holds the lock. + unsafe { + s.mutex.raw.unlock_fair(); + } + + // SAFETY: make sure the Arc gets it reference decremented + let mut s = ManuallyDrop::new(s); + unsafe { ptr::drop_in_place(&mut s.mutex) }; + } + + /// Temporarily unlocks the mutex to execute the given function. + /// + /// This is functionally identical to the `unlocked_fair` method on [`MutexGuard`]. + #[inline] + pub fn unlocked_fair<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: A MutexGuard always holds the lock. + unsafe { + s.mutex.raw.unlock_fair(); + } + defer!(s.mutex.raw.lock()); + f() + } + + /// Temporarily yields the mutex to a waiting thread if there is one. + /// + /// This is functionally identical to the `bump` method on [`MutexGuard`]. + #[inline] + pub fn bump(s: &mut Self) { + // Safety: A MutexGuard always holds the lock. + unsafe { + s.mutex.raw.bump(); + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutex, T: ?Sized> Deref for ArcMutexGuard<R, T> { + type Target = T; + #[inline] + fn deref(&self) -> &T { + unsafe { &*self.mutex.data.get() } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutex, T: ?Sized> DerefMut for ArcMutexGuard<R, T> { + #[inline] + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.mutex.data.get() } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutex, T: ?Sized> Drop for ArcMutexGuard<R, T> { + #[inline] + fn drop(&mut self) { + // Safety: A MutexGuard always holds the lock. + unsafe { + self.mutex.raw.unlock(); + } + } +} + /// An RAII mutex guard returned by `MutexGuard::map`, which can point to a /// subfield of the protected data. /// diff --git a/src/remutex.rs b/src/remutex.rs index b49ec6c..8493a24 100644 --- a/src/remutex.rs +++ b/src/remutex.rs @@ -19,6 +19,13 @@ use core::{ sync::atomic::{AtomicUsize, Ordering}, }; +#[cfg(feature = "arc_lock")] +use alloc::sync::Arc; +#[cfg(feature = "arc_lock")] +use core::mem::ManuallyDrop; +#[cfg(feature = "arc_lock")] +use core::ptr; + #[cfg(feature = "owning_ref")] use owning_ref::StableAddress; @@ -392,6 +399,45 @@ impl<R: RawMutex, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { pub fn data_ptr(&self) -> *mut T { self.data.get() } + + /// # Safety + /// + /// The lock must be held before calling this method. + #[cfg(feature = "arc_lock")] + #[inline] + unsafe fn guard_arc(self: &Arc<Self>) -> ArcReentrantMutexGuard<R, G, T> { + ArcReentrantMutexGuard { + remutex: self.clone(), + marker: PhantomData, + } + } + + /// Acquires a reentrant mutex through an `Arc`. + /// + /// This method is similar to the `lock` method; however, it requires the `ReentrantMutex` to be inside of an + /// `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn lock_arc(self: &Arc<Self>) -> ArcReentrantMutexGuard<R, G, T> { + self.raw.lock(); + // SAFETY: locking guarantee is upheld + unsafe { self.guard_arc() } + } + + /// Attempts to acquire a reentrant mutex through an `Arc`. + /// + /// This method is similar to the `try_lock` method; however, it requires the `ReentrantMutex` to be inside + /// of an `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + 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() }) + } else { + None + } + } } impl<R: RawMutexFair, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { @@ -442,6 +488,36 @@ impl<R: RawMutexTimed, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { None } } + + /// Attempts to acquire this lock until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_lock_for` method; however, it requires the `ReentrantMutex` to be + /// inside of an `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_lock_arc_for(self: &Arc<Self>, timeout: R::Duration) -> Option<ArcReentrantMutexGuard<R, G, T>> { + if self.raw.try_lock_for(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.guard_arc() }) + } else { + None + } + } + + /// Attempts to acquire this lock until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_lock_until` method; however, it requires the `ReentrantMutex` to be + /// inside of an `Arc` and the resulting mutex guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_lock_arc_until(self: &Arc<Self>, timeout: R::Instant) -> Option<ArcReentrantMutexGuard<R, G, T>> { + if self.raw.try_lock_until(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.guard_arc() }) + } else { + None + } + } } impl<R: RawMutex, G: GetThreadId, T: ?Sized + Default> Default for ReentrantMutex<R, G, T> { @@ -660,6 +736,7 @@ impl<'a, R: RawMutexFair + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> s.remutex.raw.bump(); } } + } impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> Deref @@ -706,6 +783,114 @@ unsafe impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> StableAdd { } +/// An RAII mutex guard returned by the `Arc` locking operations on `ReentrantMutex`. +/// +/// This is similar to the `ReentrantMutexGuard` struct, except instead of using a reference to unlock the +/// `Mutex` it uses an `Arc<ReentrantMutex>`. This has several advantages, most notably that it has an `'static` +/// lifetime. +#[cfg(feature = "arc_lock")] +#[must_use = "if unused the ReentrantMutex will immediately unlock"] +pub struct ArcReentrantMutexGuard<R: RawMutex, G: GetThreadId, T: ?Sized> { + remutex: Arc<ReentrantMutex<R, G, T>>, + marker: PhantomData<GuardNoSend>, +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutex, G: GetThreadId, T: ?Sized> ArcReentrantMutexGuard<R, G, T> { + /// Returns a reference to the `ReentrantMutex` this object is guarding, contained in its `Arc`. + pub fn remutex(s: &Self) -> &Arc<ReentrantMutex<R, G, T>> { + &s.remutex + } + + /// Temporarily unlocks the mutex to execute the given function. + /// + /// This is safe because `&mut` guarantees that there exist no other + /// references to the data protected by the mutex. + #[inline] + pub fn unlocked<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: A ReentrantMutexGuard always holds the lock. + unsafe { + s.remutex.raw.unlock(); + } + defer!(s.remutex.raw.lock()); + f() + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutexFair, G: GetThreadId, T: ?Sized> + ArcReentrantMutexGuard<R, G, T> +{ + /// Unlocks the mutex using a fair unlock protocol. + /// + /// This is functionally identical to the `unlock_fair` method on [`ReentrantMutexGuard`]. + #[inline] + pub fn unlock_fair(s: Self) { + // Safety: A ReentrantMutexGuard always holds the lock + unsafe { + s.remutex.raw.unlock_fair(); + } + + // SAFETY: ensure that the Arc's refcount is decremented + let mut s = ManuallyDrop::new(s); + unsafe { ptr::drop_in_place(&mut s.remutex) }; + } + + /// Temporarily unlocks the mutex to execute the given function. + /// + /// This is functionally identical to the `unlocked_fair` method on [`ReentrantMutexGuard`]. + #[inline] + pub fn unlocked_fair<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: A ReentrantMutexGuard always holds the lock + unsafe { + s.remutex.raw.unlock_fair(); + } + defer!(s.remutex.raw.lock()); + f() + } + + /// Temporarily yields the mutex to a waiting thread if there is one. + /// + /// This is functionally equivalent to the `bump` method on [`ReentrantMutexGuard`]. + #[inline] + pub fn bump(s: &mut Self) { + // Safety: A ReentrantMutexGuard always holds the lock + unsafe { + s.remutex.raw.bump(); + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutex, G: GetThreadId, T: ?Sized> Deref + for ArcReentrantMutexGuard<R, G, T> +{ + type Target = T; + #[inline] + fn deref(&self) -> &T { + unsafe { &*self.remutex.data.get() } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawMutex, G: GetThreadId, T: ?Sized> Drop + for ArcReentrantMutexGuard<R, G, T> +{ + #[inline] + fn drop(&mut self) { + // Safety: A ReentrantMutexGuard always holds the lock. + unsafe { + self.remutex.raw.unlock(); + } + } +} + /// An RAII mutex guard returned by `ReentrantMutexGuard::map`, which can point to a /// subfield of the protected data. /// diff --git a/src/rwlock.rs b/src/rwlock.rs index e97de98..c404934 100644 --- a/src/rwlock.rs +++ b/src/rwlock.rs @@ -11,6 +11,13 @@ use core::marker::PhantomData; use core::mem; use core::ops::{Deref, DerefMut}; +#[cfg(feature = "arc_lock")] +use alloc::sync::Arc; +#[cfg(feature = "arc_lock")] +use core::mem::ManuallyDrop; +#[cfg(feature = "arc_lock")] +use core::ptr; + #[cfg(feature = "owning_ref")] use owning_ref::StableAddress; @@ -557,6 +564,84 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { pub fn data_ptr(&self) -> *mut T { self.data.get() } + + /// # Safety + /// + /// The lock must be held when calling this method. + #[cfg(feature = "arc_lock")] + #[inline] + unsafe fn read_guard_arc(self: &Arc<Self>) -> ArcRwLockReadGuard<R, T> { + ArcRwLockReadGuard { + rwlock: self.clone(), + marker: PhantomData, + } + } + + /// # Safety + /// + /// The lock must be held when calling this method. + #[cfg(feature = "arc_lock")] + #[inline] + unsafe fn write_guard_arc(self: &Arc<Self>) -> ArcRwLockWriteGuard<R, T> { + ArcRwLockWriteGuard { + rwlock: self.clone(), + marker: PhantomData, + } + } + + /// Locks this `RwLock` with read access, through an `Arc`. + /// + /// This method is similar to the `read` method; however, it requires the `RwLock` to be inside of an `Arc` + /// and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn read_arc(self: &Arc<Self>) -> ArcRwLockReadGuard<R, T> { + self.raw.lock_shared(); + // SAFETY: locking guarantee is upheld + unsafe { self.read_guard_arc() } + } + + /// Attempts to lock this `RwLock` with read access, through an `Arc`. + /// + /// This method is similar to the `try_read` method; however, it requires the `RwLock` to be inside of an + /// `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + 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() }) + } else { + None + } + } + + /// Locks this `RwLock` with write access, through an `Arc`. + /// + /// This method is similar to the `write` method; however, it requires the `RwLock` to be inside of an `Arc` + /// and the resulting write guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn write_arc(self: &Arc<Self>) -> ArcRwLockWriteGuard<R, T> { + self.raw.lock_exclusive(); + // SAFETY: locking guarantee is upheld + unsafe { self.write_guard_arc() } + } + + /// Attempts to lock this `RwLock` with writ access, through an `Arc`. + /// + /// This method is similar to the `try_write` method; however, it requires the `RwLock` to be inside of an + /// `Arc` and the resulting write guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + 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() }) + } else { + None + } + } } impl<R: RawRwLockFair, T: ?Sized> RwLock<R, T> { @@ -657,6 +742,66 @@ impl<R: RawRwLockTimed, T: ?Sized> RwLock<R, T> { None } } + + /// Attempts to acquire this `RwLock` with read access until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_read_for` method; however, it requires the `RwLock` to be inside of an + /// `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_read_arc_for(self: &Arc<Self>, timeout: R::Duration) -> Option<ArcRwLockReadGuard<R, T>> { + if self.raw.try_lock_shared_for(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.read_guard_arc() }) + } else { + None + } + } + + /// Attempts to acquire this `RwLock` with read access until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_read_until` method; however, it requires the `RwLock` to be inside of + /// an `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_read_arc_until(self: &Arc<Self>, timeout: R::Instant) -> Option<ArcRwLockReadGuard<R, T>> { + if self.raw.try_lock_shared_until(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.read_guard_arc() }) + } else { + None + } + } + + /// Attempts to acquire this `RwLock` with write access until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_write_for` method; however, it requires the `RwLock` to be inside of + /// an `Arc` and the resulting write guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_write_arc_for(self: &Arc<Self>, timeout: R::Duration) -> Option<ArcRwLockWriteGuard<R, T>> { + if self.raw.try_lock_exclusive_for(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.write_guard_arc() }) + } else { + None + } + } + + /// Attempts to acquire this `RwLock` with read access until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_write_until` method; however, it requires the `RwLock` to be inside of + /// an `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_write_arc_until(self: &Arc<Self>, timeout: R::Instant) -> Option<ArcRwLockWriteGuard<R, T>> { + if self.raw.try_lock_exclusive_until(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.write_guard_arc() }) + } else { + None + } + } } impl<R: RawRwLockRecursive, T: ?Sized> RwLock<R, T> { @@ -701,6 +846,33 @@ impl<R: RawRwLockRecursive, T: ?Sized> RwLock<R, T> { None } } + + /// Locks this `RwLock` with shared read access, through an `Arc`. + /// + /// This method is similar to the `read_recursive` method; however, it requires the `RwLock` to be inside of + /// an `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + 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() } + } + + /// Attempts to lock this `RwLock` with shared read access, through an `Arc`. + /// + /// This method is similar to the `try_read_recursive` method; however, it requires the `RwLock` to be inside + /// of an `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + 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() }) + } else { + None + } + } } impl<R: RawRwLockRecursiveTimed, T: ?Sized> RwLock<R, T> { @@ -745,6 +917,36 @@ impl<R: RawRwLockRecursiveTimed, T: ?Sized> RwLock<R, T> { None } } + + /// Attempts to lock this `RwLock` with read access until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_read_recursive_for` method; however, it requires the `RwLock` to be + /// inside of an `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_read_arc_recursive_for(self: &Arc<Self>, timeout: R::Duration) -> Option<ArcRwLockReadGuard<R, T>> { + if self.raw.try_lock_shared_recursive_for(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.read_guard_arc() }) + } else { + None + } + } + + /// Attempts to lock this `RwLock` with read access until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_read_recursive_until` method; however, it requires the `RwLock` to be + /// inside of an `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_read_arc_recursive_until(self: &Arc<Self>, timeout: R::Instant) -> Option<ArcRwLockReadGuard<R, T>> { + if self.raw.try_lock_shared_recursive_until(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.read_guard_arc() }) + } else { + None + } + } } impl<R: RawRwLockUpgrade, T: ?Sized> RwLock<R, T> { @@ -791,6 +993,45 @@ impl<R: RawRwLockUpgrade, T: ?Sized> RwLock<R, T> { None } } + + /// # Safety + /// + /// The lock must be held when calling this method. + #[cfg(feature = "arc_lock")] + #[inline] + unsafe fn upgradable_guard_arc(self: &Arc<Self>) -> ArcRwLockUpgradableReadGuard<R, T> { + ArcRwLockUpgradableReadGuard { + rwlock: self.clone(), + marker: PhantomData + } + } + + /// Locks this `RwLock` with upgradable read access, through an `Arc`. + /// + /// This method is similar to the `upgradable_read` method; however, it requires the `RwLock` to be + /// inside of an `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + 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() } + } + + /// Attempts to lock this `RwLock` with upgradable read access, through an `Arc`. + /// + /// This method is similar to the `try_upgradable_read` method; however, it requires the `RwLock` to be + /// inside of an `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + 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() }) + } else { + None + } + } } impl<R: RawRwLockUpgradeTimed, T: ?Sized> RwLock<R, T> { @@ -831,6 +1072,42 @@ impl<R: RawRwLockUpgradeTimed, T: ?Sized> RwLock<R, T> { None } } + + /// Attempts to lock this `RwLock` with upgradable access until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_upgradable_read_for` method; however, it requires the `RwLock` to be + /// inside of an `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_upgradable_read_arc_for( + self: &Arc<Self>, + timeout: R::Duration, + ) -> Option<ArcRwLockUpgradableReadGuard<R, T>> { + if self.raw.try_lock_upgradable_for(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.upgradable_guard_arc() }) + } else { + None + } + } + + /// Attempts to lock this `RwLock` with upgradable access until a timeout is reached, through an `Arc`. + /// + /// This method is similar to the `try_upgradable_read_until` method; however, it requires the `RwLock` to be + /// inside of an `Arc` and the resulting read guard has no lifetime requirements. + #[cfg(feature = "arc_lock")] + #[inline] + pub fn try_upgradable_read_arc_until( + self: &Arc<Self>, + timeout: R::Instant, + ) -> Option<ArcRwLockUpgradableReadGuard<R, T>> { + if self.raw.try_lock_upgradable_until(timeout) { + // SAFETY: locking guarantee is upheld + Some(unsafe { self.upgradable_guard_arc() }) + } else { + None + } + } } impl<R: RawRwLock, T: ?Sized + Default> Default for RwLock<R, T> { @@ -1041,6 +1318,122 @@ impl<'a, R: RawRwLock + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress for RwLockReadGuard<'a, R, T> {} +/// An RAII rwlock guard returned by the `Arc` locking operations on `RwLock`. +/// +/// 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")] +#[must_use = "if unused the RwLock will immediately unlock"] +pub struct ArcRwLockReadGuard<R: RawRwLock, T: ?Sized> { + rwlock: Arc<RwLock<R, T>>, + marker: PhantomData<R::GuardMarker>, +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: ?Sized> ArcRwLockReadGuard<R, T> { + /// Returns a reference to the rwlock, contained in its `Arc`. + pub fn rwlock(s: &Self) -> &Arc<RwLock<R, T>> { + &s.rwlock + } + + /// Temporarily unlocks the `RwLock` to execute the given function. + /// + /// This is functionally identical to the `unlocked` method on [`RwLockReadGuard`]. + #[inline] + pub fn unlocked<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: An RwLockReadGuard always holds a shared lock. + unsafe { + s.rwlock.raw.unlock_shared(); + } + defer!(s.rwlock.raw.lock_shared()); + f() + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockFair, T: ?Sized> ArcRwLockReadGuard<R, T> { + /// Unlocks the `RwLock` using a fair unlock protocol. + /// + /// This is functionally identical to the `unlock_fair` method on [`RwLockReadGuard`]. + #[inline] + pub fn unlock_fair(s: Self) { + // Safety: An RwLockReadGuard always holds a shared lock. + unsafe { + s.rwlock.raw.unlock_shared_fair(); + } + + // SAFETY: ensure the Arc has its refcount decremented + let mut s = ManuallyDrop::new(s); + unsafe { ptr::drop_in_place(&mut s.rwlock) }; + } + + /// Temporarily unlocks the `RwLock` to execute the given function. + /// + /// This is functionally identical to the `unlocked_fair` method on [`RwLockReadGuard`]. + #[inline] + pub fn unlocked_fair<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: An RwLockReadGuard always holds a shared lock. + unsafe { + s.rwlock.raw.unlock_shared_fair(); + } + defer!(s.rwlock.raw.lock_shared()); + f() + } + + /// Temporarily yields the `RwLock` to a waiting thread if there is one. + /// + /// This is functionally identical to the `bump` method on [`RwLockReadGuard`]. + #[inline] + pub fn bump(s: &mut Self) { + // Safety: An RwLockReadGuard always holds a shared lock. + unsafe { + s.rwlock.raw.bump_shared(); + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: ?Sized> Deref for ArcRwLockReadGuard<R, T> { + type Target = T; + #[inline] + fn deref(&self) -> &T { + unsafe { &*self.rwlock.data.get() } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: ?Sized> Drop for ArcRwLockReadGuard<R, T> { + #[inline] + fn drop(&mut self) { + // Safety: An RwLockReadGuard always holds a shared lock. + unsafe { + self.rwlock.raw.unlock_shared(); + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: fmt::Debug + ?Sized> fmt::Debug for ArcRwLockReadGuard<R, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: fmt::Display + ?Sized> fmt::Display + for ArcRwLockReadGuard<R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + /// RAII structure used to release the exclusive write access of a lock when /// dropped. #[must_use = "if unused the RwLock will immediately unlock"] @@ -1262,6 +1655,175 @@ impl<'a, R: RawRwLock + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress for RwLockWriteGuard<'a, R, T> {} +/// An RAII rwlock guard returned by the `Arc` locking operations on `RwLock`. +/// 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")] +#[must_use = "if unused the RwLock will immediately unlock"] +pub struct ArcRwLockWriteGuard<R: RawRwLock, T: ?Sized> { + rwlock: Arc<RwLock<R, T>>, + marker: PhantomData<R::GuardMarker>, +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: ?Sized> ArcRwLockWriteGuard<R, T> { + /// Returns a reference to the rwlock, contained in its `Arc`. + pub fn rwlock(s: &Self) -> &Arc<RwLock<R, T>> { + &s.rwlock + } + + /// Temporarily unlocks the `RwLock` to execute the given function. + /// + /// This is functionally equivalent to the `unlocked` method on [`RwLockWriteGuard`]. + #[inline] + pub fn unlocked<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: An RwLockWriteGuard always holds a shared lock. + unsafe { + s.rwlock.raw.unlock_exclusive(); + } + defer!(s.rwlock.raw.lock_exclusive()); + f() + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockDowngrade, T: ?Sized> ArcRwLockWriteGuard<R, T> { + /// Atomically downgrades a write lock into a read lock without allowing any + /// writers to take exclusive access of the lock in the meantime. + /// + /// This is functionally equivalent to the `downgrade` method on [`RwLockWriteGuard`]. + pub fn downgrade(s: Self) -> ArcRwLockReadGuard<R, T> { + // Safety: An RwLockWriteGuard always holds an exclusive lock. + unsafe { + s.rwlock.raw.downgrade(); + } + + // SAFETY: prevent the arc's refcount from changing using ManuallyDrop and ptr::read + let s = ManuallyDrop::new(s); + let rwlock = unsafe { ptr::read(&s.rwlock) }; + + ArcRwLockReadGuard { + rwlock, + marker: PhantomData, + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockUpgradeDowngrade, T: ?Sized> ArcRwLockWriteGuard<R, T> { + /// Atomically downgrades a write lock into an upgradable read lock without allowing any + /// writers to take exclusive access of the lock in the meantime. + /// + /// This is functionally identical to the `downgrade_to_upgradable` method on [`RwLockWriteGuard`]. + pub fn downgrade_to_upgradable(s: Self) -> ArcRwLockUpgradableReadGuard<R, T> { + // Safety: An RwLockWriteGuard always holds an exclusive lock. + unsafe { + s.rwlock.raw.downgrade_to_upgradable(); + } + + // SAFETY: same as above + let s = ManuallyDrop::new(s); + let rwlock = unsafe { ptr::read(&s.rwlock) }; + + ArcRwLockUpgradableReadGuard { + rwlock, + marker: PhantomData, + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockFair, T: ?Sized> ArcRwLockWriteGuard<R, T> { + /// Unlocks the `RwLock` using a fair unlock protocol. + /// + /// This is functionally equivalent to the `unlock_fair` method on [`RwLockWriteGuard`]. + #[inline] + pub fn unlock_fair(s: Self) { + // Safety: An RwLockWriteGuard always holds an exclusive lock. + unsafe { + s.rwlock.raw.unlock_exclusive_fair(); + } + + // SAFETY: prevent the Arc from leaking memory + let mut s = ManuallyDrop::new(s); + unsafe { ptr::drop_in_place(&mut s.rwlock) }; + } + + /// Temporarily unlocks the `RwLock` to execute the given function. + /// + /// This is functionally equivalent to the `unlocked_fair` method on [`RwLockWriteGuard`]. + #[inline] + pub fn unlocked_fair<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: An RwLockWriteGuard always holds an exclusive lock. + unsafe { + s.rwlock.raw.unlock_exclusive_fair(); + } + defer!(s.rwlock.raw.lock_exclusive()); + f() + } + + /// Temporarily yields the `RwLock` to a waiting thread if there is one. + /// + /// This method is functionally equivalent to the `bump` method on [`RwLockWriteGuard`]. + #[inline] + pub fn bump(s: &mut Self) { + // Safety: An RwLockWriteGuard always holds an exclusive lock. + unsafe { + s.rwlock.raw.bump_exclusive(); + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: ?Sized> Deref for ArcRwLockWriteGuard<R, T> { + type Target = T; + #[inline] + fn deref(&self) -> &T { + unsafe { &*self.rwlock.data.get() } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: ?Sized> DerefMut for ArcRwLockWriteGuard<R, T> { + #[inline] + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.rwlock.data.get() } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: ?Sized> Drop for ArcRwLockWriteGuard<R, T> { + #[inline] + fn drop(&mut self) { + // Safety: An RwLockWriteGuard always holds an exclusive lock. + unsafe { + self.rwlock.raw.unlock_exclusive(); + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: fmt::Debug + ?Sized> fmt::Debug for ArcRwLockWriteGuard<R, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLock, T: fmt::Display + ?Sized> fmt::Display + for ArcRwLockWriteGuard<R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + /// RAII structure used to release the upgradable read access of a lock when /// dropped. #[must_use = "if unused the RwLock will immediately unlock"] @@ -1495,6 +2057,241 @@ unsafe impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> StableAddress { } +/// An RAII rwlock guard returned by the `Arc` locking operations on `RwLock`. +/// This is similar to the `RwLockUpgradableReadGuard` 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")] +#[must_use = "if unused the RwLock will immediately unlock"] +pub struct ArcRwLockUpgradableReadGuard<R: RawRwLockUpgrade, T: ?Sized> { + rwlock: Arc<RwLock<R, T>>, + marker: PhantomData<R::GuardMarker>, +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockUpgrade , T: ?Sized> ArcRwLockUpgradableReadGuard<R, T> { + /// Returns a reference to the rwlock, contained in its original `Arc`. + pub fn rwlock(s: &Self) -> &Arc<RwLock<R, T>> { + &s.rwlock + } + + /// Temporarily unlocks the `RwLock` to execute the given function. + /// + /// This is functionally identical to the `unlocked` method on [`RwLockUpgradableReadGuard`]. + #[inline] + pub fn unlocked<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. + unsafe { + s.rwlock.raw.unlock_upgradable(); + } + defer!(s.rwlock.raw.lock_upgradable()); + f() + } + + /// Atomically upgrades an upgradable read lock lock into a 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. + unsafe { + s.rwlock.raw.upgrade(); + } + + // SAFETY: avoid incrementing or decrementing the refcount using ManuallyDrop and reading the Arc out + // of the struct + let s = ManuallyDrop::new(s); + let rwlock = unsafe { ptr::read(&s.rwlock) }; + + ArcRwLockWriteGuard { + rwlock, + marker: PhantomData, + } + } + + /// Tries to atomically upgrade an upgradable read lock into a 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> { + // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. + if unsafe { s.rwlock.raw.try_upgrade() } { + // SAFETY: same as above + let s = ManuallyDrop::new(s); + let rwlock = unsafe { ptr::read(&s.rwlock) }; + + Ok(ArcRwLockWriteGuard { + rwlock, + marker: PhantomData, + }) + } else { + Err(s) + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockUpgradeFair, T: ?Sized> ArcRwLockUpgradableReadGuard<R, T> { + /// Unlocks the `RwLock` using a fair unlock protocol. + /// + /// This is functionally identical to the `unlock_fair` method on [`RwLockUpgradableReadGuard`]. + #[inline] + pub fn unlock_fair(s: Self) { + // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. + unsafe { + s.rwlock.raw.unlock_upgradable_fair(); + } + + // SAFETY: make sure we decrement the refcount properly + let mut s = ManuallyDrop::new(s); + unsafe { ptr::drop_in_place(&mut s.rwlock) }; + } + + /// Temporarily unlocks the `RwLock` to execute the given function. + /// + /// This is functionally equivalent to the `unlocked_fair` method on [`RwLockUpgradableReadGuard`]. + #[inline] + pub fn unlocked_fair<F, U>(s: &mut Self, f: F) -> U + where + F: FnOnce() -> U, + { + // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. + unsafe { + s.rwlock.raw.unlock_upgradable_fair(); + } + defer!(s.rwlock.raw.lock_upgradable()); + f() + } + + /// Temporarily yields the `RwLock` to a waiting thread if there is one. + /// + /// This method is functionally equivalent to calling `bump` on [`RwLockUpgradableReadGuard`]. + #[inline] + pub fn bump(s: &mut Self) { + // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. + unsafe { + s.rwlock.raw.bump_upgradable(); + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockUpgradeDowngrade, T: ?Sized> ArcRwLockUpgradableReadGuard<R, T> { + /// Atomically downgrades an upgradable read lock lock into a shared read lock + /// without allowing any writers to take exclusive access of the lock in the + /// meantime. + /// + /// Note that if there are any writers currently waiting to take the lock + /// then other readers may not be able to acquire the lock even if it was + /// downgraded. + pub fn downgrade(s: Self) -> ArcRwLockReadGuard<R, T> { + // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. + unsafe { + s.rwlock.raw.downgrade_upgradable(); + } + + // SAFETY: use ManuallyDrop and ptr::read to ensure the refcount is not changed + let s = ManuallyDrop::new(s); + let rwlock = unsafe { ptr::read(&s.rwlock) }; + + ArcRwLockReadGuard { + rwlock, + marker: PhantomData, + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockUpgradeTimed, T: ?Sized> ArcRwLockUpgradableReadGuard<R, T> { + /// Tries to atomically upgrade an upgradable read lock into a exclusive + /// write lock, until a timeout is reached. + /// + /// If the access could not be granted before the timeout expires, then + /// the current guard is returned. + pub fn try_upgrade_for( + s: Self, + timeout: R::Duration, + ) -> Result<ArcRwLockWriteGuard<R, T>, Self> { + // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. + if unsafe { s.rwlock.raw.try_upgrade_for(timeout) } { + // SAFETY: same as above + let s = ManuallyDrop::new(s); + let rwlock = unsafe { ptr::read(&s.rwlock) }; + + Ok(ArcRwLockWriteGuard { + rwlock, + marker: PhantomData, + }) + } else { + Err(s) + } + } + + /// Tries to atomically upgrade an upgradable read lock into a exclusive + /// write lock, until a timeout is reached. + /// + /// If the access could not be granted before the timeout expires, then + /// the current guard is returned. + #[inline] + pub fn try_upgrade_until( + s: Self, + timeout: R::Instant, + ) -> Result<ArcRwLockWriteGuard<R, T>, Self> { + // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. + if unsafe { s.rwlock.raw.try_upgrade_until(timeout) } { + // SAFETY: same as above + let s = ManuallyDrop::new(s); + let rwlock = unsafe { ptr::read(&s.rwlock) }; + + Ok(ArcRwLockWriteGuard { + rwlock, + marker: PhantomData, + }) + } else { + Err(s) + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockUpgrade, T: ?Sized> Deref for ArcRwLockUpgradableReadGuard<R, T> { + type Target = T; + #[inline] + fn deref(&self) -> &T { + unsafe { &*self.rwlock.data.get() } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockUpgrade, T: ?Sized> Drop for ArcRwLockUpgradableReadGuard<R, T> { + #[inline] + fn drop(&mut self) { + // Safety: An RwLockUpgradableReadGuard always holds an upgradable lock. + unsafe { + self.rwlock.raw.unlock_upgradable(); + } + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockUpgrade, T: fmt::Debug + ?Sized> fmt::Debug + for ArcRwLockUpgradableReadGuard<R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[cfg(feature = "arc_lock")] +impl<R: RawRwLockUpgrade, T: fmt::Display + ?Sized> fmt::Display + for ArcRwLockUpgradableReadGuard<R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + + /// An RAII read lock guard returned by `RwLockReadGuard::map`, which can point to a /// subfield of the protected data. /// |