diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:06:54 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:06:54 +0000 |
commit | a81c71f13ebf249161902a2fb911c34d2980dd4c (patch) | |
tree | 78a003706c177f2ed754dc2bc85e7905450ba91d | |
parent | f7b39d462728d7e5dfb7c8cb27007cde42ebbc7b (diff) | |
parent | 8f1564262a5d3b1638d877319ad5aa3e88e02724 (diff) | |
download | lock_api-android13-mainline-conscrypt-release.tar.gz |
Snap for 8564071 from 8f1564262a5d3b1638d877319ad5aa3e88e02724 to mainline-conscrypt-releaseaml_con_331413000aml_con_331411000aml_con_331312000aml_con_331115000aml_con_331011010android13-mainline-conscrypt-release
Change-Id: Iaef2e96fa8df2496e2211322812d87da0915f8be
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | Android.bp | 34 | ||||
-rw-r--r-- | Cargo.toml | 14 | ||||
-rw-r--r-- | Cargo.toml.orig | 5 | ||||
-rw-r--r-- | METADATA | 10 | ||||
-rw-r--r-- | TEST_MAPPING | 6 | ||||
-rw-r--r-- | cargo2android.json | 11 | ||||
-rw-r--r-- | src/lib.rs | 9 | ||||
-rw-r--r-- | src/mutex.rs | 189 | ||||
-rw-r--r-- | src/remutex.rs | 197 | ||||
-rw-r--r-- | src/rwlock.rs | 828 |
11 files changed, 1263 insertions, 47 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index e1a0b04..9ed45c2 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "86969fd7baf94312520e0b5a5f3b0861a0fd411b" - } -} + "sha1": "a75875b0bf904287a9749e8eabea919b5e9dd8a9" + }, + "path_in_vcs": "lock_api" +}
\ No newline at end of file @@ -1,4 +1,5 @@ -// This file is generated by cargo2android.py --run --device --dependencies --tests. +// This file is generated by cargo2android.py --config cargo2android.json. +// Do not modify this file as changes will be overridden on upgrade. package { default_applicable_licenses: ["external_rust_crates_lock_api_license"], @@ -40,34 +41,17 @@ rust_library { name: "liblock_api", host_supported: true, crate_name: "lock_api", + cargo_env_compat: true, + cargo_pkg_version: "0.4.6", srcs: ["src/lib.rs"], edition: "2018", rustlibs: [ "libscopeguard", ], -} - -rust_defaults { - name: "lock_api_defaults", - crate_name: "lock_api", - srcs: ["src/lib.rs"], - test_suites: ["general-tests"], - auto_gen_config: true, - edition: "2018", - rustlibs: [ - "libscopeguard", + apex_available: [ + "//apex_available:platform", + "com.android.bluetooth", + "com.android.virt", ], + min_sdk_version: "29", } - -rust_test_host { - name: "lock_api_host_test_src_lib", - defaults: ["lock_api_defaults"], -} - -rust_test { - name: "lock_api_device_test_src_lib", - defaults: ["lock_api_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.2" +version = "0.4.6" 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 9e884a4..e6a805f 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "lock_api" -version = "0.4.2" +version = "0.4.6" 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.2.crate" + value: "https://static.crates.io/crates/lock_api/lock_api-0.4.6.crate" } - version: "0.4.2" + version: "0.4.6" license_type: NOTICE last_upgrade_date { - year: 2020 - month: 11 - day: 17 + year: 2022 + month: 3 + day: 1 } } diff --git a/TEST_MAPPING b/TEST_MAPPING index 738f5ec..f439cf8 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,8 +1,8 @@ -// Generated by cargo2android.py for tests in Android.bp +// Generated by update_crate_tests.py for tests that depend on this crate. { - "presubmit": [ + "imports": [ { - "name": "lock_api_device_test_src_lib" + "path": "external/rust/crates/vulkano" } ] } diff --git a/cargo2android.json b/cargo2android.json new file mode 100644 index 0000000..22531ba --- /dev/null +++ b/cargo2android.json @@ -0,0 +1,11 @@ +{ + "apex-available": [ + "//apex_available:platform", + "com.android.bluetooth", + "com.android.virt" + ], + "device": true, + "min-sdk-version": "29", + "run": true, + "tests": true +} @@ -79,20 +79,25 @@ //! //! # 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. #![no_std] #![warn(missing_docs)] #![warn(rust_2018_idioms)] -#![cfg_attr(feature = "nightly", feature(const_fn))] +#![cfg_attr(feature = "nightly", feature(const_fn_trait_bound))] #[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..81c25fb 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 09833b0..dd992b4 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; @@ -135,6 +142,13 @@ impl<R: RawMutex, G: GetThreadId> RawReentrantMutex<R, G> { pub fn is_locked(&self) -> bool { self.mutex.is_locked() } + + /// Checks whether the mutex is currently held by the current thread. + #[inline] + pub fn is_owned_by_current_thread(&self) -> bool { + let id = self.get_thread_id.nonzero_thread_id().get(); + self.owner.load(Ordering::Relaxed) == id + } } impl<R: RawMutexFair, G: GetThreadId> RawReentrantMutex<R, G> { @@ -333,6 +347,12 @@ impl<R: RawMutex, G: GetThreadId, T: ?Sized> ReentrantMutex<R, G, T> { self.raw.is_locked() } + /// Checks whether the mutex is currently held by the current thread. + #[inline] + pub fn is_owned_by_current_thread(&self) -> bool { + self.raw.is_owned_by_current_thread() + } + /// Forcibly unlocks the mutex. /// /// This is useful when combined with `mem::forget` to hold a lock without @@ -379,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> { @@ -429,6 +488,42 @@ 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> { @@ -693,6 +788,108 @@ 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..9bfa1da 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; @@ -77,6 +84,18 @@ pub unsafe trait RawRwLock { } !acquired_lock } + + /// Check if this `RwLock` is currently exclusively locked. + fn is_locked_exclusive(&self) -> bool { + let acquired_lock = self.try_lock_shared(); + if acquired_lock { + // Safety: A shared lock was successfully acquired above. + unsafe { + self.unlock_shared(); + } + } + !acquired_lock + } } /// Additional methods for RwLocks which support fair unlocking. @@ -495,6 +514,12 @@ impl<R: RawRwLock, T: ?Sized> RwLock<R, T> { self.raw.is_locked() } + /// Check if this `RwLock` is currently exclusively locked. + #[inline] + pub fn is_locked_exclusive(&self) -> bool { + self.raw.is_locked_exclusive() + } + /// Forcibly unlocks a read lock. /// /// This is useful when combined with `mem::forget` to hold a lock without @@ -557,6 +582,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 +760,78 @@ 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 +876,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 +947,42 @@ 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 +1029,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 +1108,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 +1354,120 @@ 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 +1689,173 @@ 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 +2089,240 @@ 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. /// |