pax_global_header00006660000000000000000000000064146266310330014516gustar00rootroot0000000000000052 comment=cc249b2d31590a6e97616a351032747f3e19bc87 async-lock-3.4.0/000077500000000000000000000000001462663103300135655ustar00rootroot00000000000000async-lock-3.4.0/.github/000077500000000000000000000000001462663103300151255ustar00rootroot00000000000000async-lock-3.4.0/.github/dependabot.yml000066400000000000000000000002331462663103300177530ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: cargo directory: / schedule: interval: weekly commit-message: prefix: '' labels: [] async-lock-3.4.0/.github/workflows/000077500000000000000000000000001462663103300171625ustar00rootroot00000000000000async-lock-3.4.0/.github/workflows/ci.yml000066400000000000000000000072431462663103300203060ustar00rootroot00000000000000name: CI permissions: contents: read on: pull_request: push: branches: - master schedule: - cron: '0 2 * * 0' env: CARGO_INCREMENTAL: 0 CARGO_NET_GIT_FETCH_WITH_CLI: true CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 RUSTFLAGS: -D warnings RUSTDOCFLAGS: -D warnings RUSTUP_MAX_RETRIES: 10 defaults: run: shell: bash jobs: test: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] rust: [nightly, beta, stable] steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - run: rustup target add wasm32-unknown-unknown - name: Install WASM Test Tools and Cargo Hack uses: taiki-e/install-action@v2 with: tool: cargo-hack,wasm-pack - name: Run cargo check run: cargo check --all --all-features --all-targets - run: cargo check --all --no-default-features - name: Run cargo check (without dev-dependencies to catch missing feature flags) if: startsWith(matrix.rust, 'nightly') run: cargo check -Z features=dev_dep - run: rustup target add thumbv7m-none-eabi - run: cargo hack build --all --target thumbv7m-none-eabi --no-default-features --no-dev-deps - name: Run cargo check for WASM run: cargo check --all --all-features --all-targets --target wasm32-unknown-unknown - name: Test WASM run: wasm-pack test --headless --chrome - run: cargo test --all msrv: runs-on: ubuntu-latest strategy: matrix: # When updating this, the reminder to update the minimum supported # Rust version in Cargo.toml. rust: ['1.61'] steps: - uses: actions/checkout@v4 - name: Install cargo-hack uses: taiki-e/install-action@cargo-hack - run: cargo hack build --rust-version - run: cargo hack build --no-default-features --rust-version clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup update stable - run: cargo clippy --all-features --all-targets loom: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup update stable - name: Loom tests run: cargo test --release --test loom --features loom env: RUSTFLAGS: "--cfg=loom" LOOM_MAX_PREEMPTIONS: 4 fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup update stable - run: cargo fmt --all --check miri: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup toolchain install nightly --component miri && rustup default nightly - run: cargo miri test env: # -Zmiri-ignore-leaks is needed because we use detached threads in doctests MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout doc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup update stable - run: cargo doc --all --all-features security_audit: permissions: checks: write contents: read issues: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # https://github.com/rustsec/audit-check/issues/2 - uses: rustsec/audit-check@master with: token: ${{ secrets.GITHUB_TOKEN }} async-lock-3.4.0/.github/workflows/release.yml000066400000000000000000000006411462663103300213260ustar00rootroot00000000000000name: Release permissions: contents: write on: push: tags: - v[0-9]+.* jobs: create-release: if: github.repository_owner == 'smol-rs' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: taiki-e/create-gh-release-action@v1 with: changelog: CHANGELOG.md branch: master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} async-lock-3.4.0/.gitignore000066400000000000000000000000231462663103300155500ustar00rootroot00000000000000target/ Cargo.lock async-lock-3.4.0/CHANGELOG.md000066400000000000000000000056061462663103300154050ustar00rootroot00000000000000# Version 3.4.0 - Port to `event-listener` v5.0.0. (#74) # Version 3.3.0 - Add a `forget()` method for semaphore guards. (#73) - Increase MSRV to v1.60. (#75) # Version 3.2.0 - Add missing methods for blocking on locking with types wrapped in `Arc` (#71). # Version 3.1.2 - Bump `event-listener` to version v4.0.0. (#69) # Version 3.1.1 - Add a note to the documentation comparing this crate against `libstd`'s locks. (#58) # Version 3.1.0 - Add a `Default` implementation for `OnceCell` (#63). # Version 3.0.0 - **Breaking:** Add an enabled-by-default `std` feature that allows using this crate without the standard library. (#43) - Support blocking and non-blocking operations on the same locks. (#56) - Switch to a more efficient event notification mechanism. (#43) # Version 2.8.0 - Fix a bug where the `SemaphoreGuard::acquire_arc` future would busy wait under certain conditions (#42). - Add a `Semaphore::add_permits()` function to increase the number of available permits on the semaphore (#44). - Make `RwLockReadGuard` covariant over its lifetime (#45) - Add `RwLockReadGuardArc`, `RwLockWriteGuardArc`, and other reference counted guards for the `RwLock` type (#47). - Loosen the `Send`/`Sync` bounds on certain future types (#48). - Fix UB caused by the `MutexGuardArc::source` function allowing the user to drop an object in a different thread than the one it was acquired in (#50). This is a breaking change, but in the name of soundness. Therefore it doesn't break any valid behavior. - Fix a bug where this crate would not compile properly on `wasm64` (#51). # Version 2.7.0 - Replace some `async` blocks with manual futures (#34) - Remove our dependency on `futures-lite` (#36) - Mark guard types with `#[clippy::has_significant_drop]` (#37) # Version 2.6.0 - Add `OnceCell`. (#27) - Support wasm64. # Version 2.5.0 - Fix an issue where the future returned by `Mutex::lock_arc`/`Semaphore::acquire_arc` holds a reference to `self`. (#20, #21) # Version 2.4.0 - Add WASM support. (#14) # Version 2.3.0 - Merge all subcrates. # Version 2.2.0 - Add functions to upgrade and downgrade `RwLock` guards. - Make all constructors `const fn`. # Version 2.1.3 - Add `#![forbid(unsafe_code)]`. # Version 2.1.2 - Update dependencies. # Version 2.1.1 - Update crate description. # Version 2.1.0 - Add `Barrier` and `Semaphore`. # Version 2.0.1 - Update crate description. # Version 2.0.0 - Only re-export `async-mutex` and `async-rwlock`. # Version 1.1.5 - Replace the implementation with `async-mutex`. # Version 1.1.4 - Replace `usize::MAX` with `std::usize::MAX`. # Version 1.1.3 - Update dependencies. # Version 1.1.2 - Fix a deadlock issue. # Version 1.1.1 - Fix some typos. # Version 1.1.0 - Make locking fair. - Add `LockGuard::source()`. # Version 1.0.2 - Bump the `event-listener` version. - Add tests. # Version 1.0.1 - Update Cargo categories. # Version 1.0.0 - Initial version async-lock-3.4.0/Cargo.toml000066400000000000000000000021571462663103300155220ustar00rootroot00000000000000[package] name = "async-lock" # When publishing a new version: # - Update CHANGELOG.md # - Create "v3.x.y" git tag version = "3.4.0" authors = ["Stjepan Glavina "] edition = "2021" rust-version = "1.60" description = "Async synchronization primitives" license = "Apache-2.0 OR MIT" repository = "https://github.com/smol-rs/async-lock" keywords = ["lock", "mutex", "rwlock", "semaphore", "barrier"] categories = ["asynchronous", "concurrency"] exclude = ["/.*"] [dependencies] event-listener = { version = "5.0.0", default-features = false } event-listener-strategy = { version = "0.5.0", default-features = false } pin-project-lite = "0.2.11" [target.'cfg(loom)'.dependencies] loom = { version = "0.7", optional = true } [features] default = ["std"] std = ["event-listener/std", "event-listener-strategy/std"] loom = ["event-listener/loom", "dep:loom"] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(loom)'] } [dev-dependencies] fastrand = "2.0.0" flume = "0.11.0" futures-lite = "2.0.0" waker-fn = "1.1.0" [target.'cfg(target_family = "wasm")'.dev-dependencies] wasm-bindgen-test = "0.3" async-lock-3.4.0/LICENSE-APACHE000066400000000000000000000251371462663103300155210ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. async-lock-3.4.0/LICENSE-MIT000066400000000000000000000017771462663103300152350ustar00rootroot00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. async-lock-3.4.0/README.md000066400000000000000000000024141462663103300150450ustar00rootroot00000000000000# async-lock [![Build](https://github.com/smol-rs/async-lock/workflows/Build%20and%20test/badge.svg)]( https://github.com/smol-rs/async-lock/actions) [![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( https://github.com/smol-rs/async-lock) [![Cargo](https://img.shields.io/crates/v/async-lock.svg)]( https://crates.io/crates/async-lock) [![Documentation](https://docs.rs/async-lock/badge.svg)]( https://docs.rs/async-lock) Async synchronization primitives. This crate provides the following primitives: * `Barrier` - enables tasks to synchronize all together at the same time. * `Mutex` - a mutual exclusion lock. * `RwLock` - a reader-writer lock, allowing any number of readers or a single writer. * `Semaphore` - limits the number of concurrent operations. ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. #### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. async-lock-3.4.0/src/000077500000000000000000000000001462663103300143545ustar00rootroot00000000000000async-lock-3.4.0/src/barrier.rs000066400000000000000000000216621462663103300163570ustar00rootroot00000000000000use event_listener::{Event, EventListener}; use event_listener_strategy::{easy_wrapper, EventListenerFuture, Strategy}; use core::fmt; use core::pin::Pin; use core::task::Poll; use crate::futures::Lock; use crate::Mutex; /// A counter to synchronize multiple tasks at the same time. #[derive(Debug)] pub struct Barrier { n: usize, state: Mutex, event: Event, } #[derive(Debug)] struct State { count: usize, generation_id: u64, } impl Barrier { const_fn! { const_if: #[cfg(not(loom))]; /// Creates a barrier that can block the given number of tasks. /// /// A barrier will block `n`-1 tasks which call [`wait()`] and then wake up all tasks /// at once when the `n`th task calls [`wait()`]. /// /// [`wait()`]: `Barrier::wait()` /// /// # Examples /// /// ``` /// use async_lock::Barrier; /// /// let barrier = Barrier::new(5); /// ``` pub const fn new(n: usize) -> Barrier { Barrier { n, state: Mutex::new(State { count: 0, generation_id: 0, }), event: Event::new(), } } } /// Blocks the current task until all tasks reach this point. /// /// Barriers are reusable after all tasks have synchronized, and can be used continuously. /// /// Returns a [`BarrierWaitResult`] indicating whether this task is the "leader", meaning the /// last task to call this method. /// /// # Examples /// /// ``` /// use async_lock::Barrier; /// use futures_lite::future; /// use std::sync::Arc; /// use std::thread; /// /// let barrier = Arc::new(Barrier::new(5)); /// /// for _ in 0..5 { /// let b = barrier.clone(); /// thread::spawn(move || { /// future::block_on(async { /// // The same messages will be printed together. /// // There will NOT be interleaving of "before" and "after". /// println!("before wait"); /// b.wait().await; /// println!("after wait"); /// }); /// }); /// } /// ``` pub fn wait(&self) -> BarrierWait<'_> { BarrierWait::_new(BarrierWaitInner { barrier: self, lock: Some(self.state.lock()), evl: None, state: WaitState::Initial, }) } /// Blocks the current thread until all tasks reach this point. /// /// Barriers are reusable after all tasks have synchronized, and can be used continuously. /// /// Returns a [`BarrierWaitResult`] indicating whether this task is the "leader", meaning the /// last task to call this method. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`wait`][`Barrier::wait`] method, /// this method will block the current thread until the wait is complete. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a barrier can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use async_lock::Barrier; /// use futures_lite::future; /// use std::sync::Arc; /// use std::thread; /// /// let barrier = Arc::new(Barrier::new(5)); /// /// for _ in 0..5 { /// let b = barrier.clone(); /// thread::spawn(move || { /// // The same messages will be printed together. /// // There will NOT be interleaving of "before" and "after". /// println!("before wait"); /// b.wait_blocking(); /// println!("after wait"); /// }); /// } /// # // Wait for threads to stop. /// # std::thread::sleep(std::time::Duration::from_secs(1)); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] pub fn wait_blocking(&self) -> BarrierWaitResult { self.wait().wait() } } easy_wrapper! { /// The future returned by [`Barrier::wait()`]. pub struct BarrierWait<'a>(BarrierWaitInner<'a> => BarrierWaitResult); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { /// The future returned by [`Barrier::wait()`]. struct BarrierWaitInner<'a> { // The barrier to wait on. barrier: &'a Barrier, // The ongoing mutex lock operation we are blocking on. #[pin] lock: Option>, // An event listener for the `barrier.event` event. evl: Option, // The current state of the future. state: WaitState, } } impl fmt::Debug for BarrierWait<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("BarrierWait { .. }") } } enum WaitState { /// We are getting the original values of the state. Initial, /// We are waiting for the listener to complete. Waiting { local_gen: u64 }, /// Waiting to re-acquire the lock to check the state again. Reacquiring { local_gen: u64 }, } impl EventListenerFuture for BarrierWaitInner<'_> { type Output = BarrierWaitResult; fn poll_with_strategy<'a, S: Strategy<'a>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let mut this = self.project(); loop { match this.state { WaitState::Initial => { // See if the lock is ready yet. let mut state = ready!(this .lock .as_mut() .as_pin_mut() .unwrap() .poll_with_strategy(strategy, cx)); this.lock.as_mut().set(None); let local_gen = state.generation_id; state.count += 1; if state.count < this.barrier.n { // We need to wait for the event. *this.evl = Some(this.barrier.event.listen()); *this.state = WaitState::Waiting { local_gen }; } else { // We are the last one. state.count = 0; state.generation_id = state.generation_id.wrapping_add(1); this.barrier.event.notify(core::usize::MAX); return Poll::Ready(BarrierWaitResult { is_leader: true }); } } WaitState::Waiting { local_gen } => { ready!(strategy.poll(this.evl, cx)); // We are now re-acquiring the mutex. this.lock.as_mut().set(Some(this.barrier.state.lock())); *this.state = WaitState::Reacquiring { local_gen: *local_gen, }; } WaitState::Reacquiring { local_gen } => { // Acquire the local state again. let state = ready!(this .lock .as_mut() .as_pin_mut() .unwrap() .poll_with_strategy(strategy, cx)); this.lock.set(None); if *local_gen == state.generation_id && state.count < this.barrier.n { // We need to wait for the event again. *this.evl = Some(this.barrier.event.listen()); *this.state = WaitState::Waiting { local_gen: *local_gen, }; } else { // We are ready, but not the leader. return Poll::Ready(BarrierWaitResult { is_leader: false }); } } } } } } /// Returned by [`Barrier::wait()`] when all tasks have called it. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::Barrier; /// /// let barrier = Barrier::new(1); /// let barrier_wait_result = barrier.wait().await; /// # }); /// ``` #[derive(Debug, Clone)] pub struct BarrierWaitResult { is_leader: bool, } impl BarrierWaitResult { /// Returns `true` if this task was the last to call to [`Barrier::wait()`]. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::Barrier; /// use futures_lite::future; /// /// let barrier = Barrier::new(2); /// let (a, b) = future::zip(barrier.wait(), barrier.wait()).await; /// assert_eq!(a.is_leader(), false); /// assert_eq!(b.is_leader(), true); /// # }); /// ``` pub fn is_leader(&self) -> bool { self.is_leader } } async-lock-3.4.0/src/lib.rs000066400000000000000000000121331462663103300154700ustar00rootroot00000000000000//! Async synchronization primitives. //! //! This crate provides the following primitives: //! //! * [`Barrier`] - enables tasks to synchronize all together at the same time. //! * [`Mutex`] - a mutual exclusion lock. //! * [`RwLock`] - a reader-writer lock, allowing any number of readers or a single writer. //! * [`Semaphore`] - limits the number of concurrent operations. //! //! ## Relationship with `std::sync` //! //! In general, you should consider using [`std::sync`] types over types from this crate. //! //! There are two primary use cases for types from this crate: //! //! - You need to use a synchronization primitive in a `no_std` environment. //! - You need to hold a lock across an `.await` point. //! (Holding an [`std::sync`] lock guard across an `.await` will make your future non-`Send`, //! and is also highly likely to cause deadlocks.) //! //! If you already use `libstd` and you aren't holding locks across await points (there is a //! Clippy lint called [`await_holding_lock`] that emits warnings for this scenario), you should //! consider [`std::sync`] instead of this crate. Those types are optimized for the currently //! running operating system, are less complex and are generally much faster. //! //! In contrast, `async-lock`'s notification system uses `std::sync::Mutex` under the hood if //! the `std` feature is enabled, and will fall back to a significantly slower strategy if it is //! not. So, there are few cases where `async-lock` is a win for performance over [`std::sync`]. //! //! [`std::sync`]: https://doc.rust-lang.org/std/sync/index.html //! [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/stable/index.html#/await_holding_lock #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![doc( html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" )] #![doc( html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" )] extern crate alloc; /// Simple macro to extract the value of `Poll` or return `Pending`. /// /// TODO: Drop in favor of `core::task::ready`, once MSRV is bumped to 1.64. macro_rules! ready { ($e:expr) => {{ use ::core::task::Poll; match $e { Poll::Ready(v) => v, Poll::Pending => return Poll::Pending, } }}; } /// Pins a variable on the stack. /// /// TODO: Drop in favor of `core::pin::pin`, once MSRV is bumped to 1.68. #[cfg(all(feature = "std", not(target_family = "wasm")))] macro_rules! pin { ($($x:ident),* $(,)?) => { $( let mut $x = $x; #[allow(unused_mut)] let mut $x = unsafe { core::pin::Pin::new_unchecked(&mut $x) }; )* } } /// Make the given function const if the given condition is true. macro_rules! const_fn { ( const_if: #[cfg($($cfg:tt)+)]; $(#[$($attr:tt)*])* $vis:vis const fn $($rest:tt)* ) => { #[cfg($($cfg)+)] $(#[$($attr)*])* $vis const fn $($rest)* #[cfg(not($($cfg)+))] $(#[$($attr)*])* $vis fn $($rest)* }; } mod barrier; mod mutex; mod once_cell; mod rwlock; mod semaphore; pub use barrier::{Barrier, BarrierWaitResult}; pub use mutex::{Mutex, MutexGuard, MutexGuardArc}; pub use once_cell::OnceCell; pub use rwlock::{ RwLock, RwLockReadGuard, RwLockReadGuardArc, RwLockUpgradableReadGuard, RwLockUpgradableReadGuardArc, RwLockWriteGuard, RwLockWriteGuardArc, }; pub use semaphore::{Semaphore, SemaphoreGuard, SemaphoreGuardArc}; pub mod futures { //! Named futures for use with `async_lock` primitives. pub use crate::barrier::BarrierWait; pub use crate::mutex::{Lock, LockArc}; pub use crate::rwlock::futures::{ Read, ReadArc, UpgradableRead, UpgradableReadArc, Upgrade, UpgradeArc, Write, WriteArc, }; pub use crate::semaphore::{Acquire, AcquireArc}; } #[cfg(not(loom))] /// Synchronization primitive implementation. mod sync { pub(super) use core::sync::atomic; pub(super) trait WithMut { type Output; fn with_mut(&mut self, f: F) -> R where F: FnOnce(&mut Self::Output) -> R; } impl WithMut for atomic::AtomicUsize { type Output = usize; #[inline] fn with_mut(&mut self, f: F) -> R where F: FnOnce(&mut Self::Output) -> R, { f(self.get_mut()) } } } #[cfg(loom)] /// Synchronization primitive implementation. mod sync { pub(super) use loom::sync::atomic; } #[cold] fn abort() -> ! { // For no_std targets, panicking while panicking is defined as an abort #[cfg(not(feature = "std"))] { struct Bomb; impl Drop for Bomb { fn drop(&mut self) { panic!("Panicking while panicking to abort") } } let _bomb = Bomb; panic!("Panicking while panicking to abort") } // For libstd targets, abort using std::process::abort #[cfg(feature = "std")] std::process::abort() } async-lock-3.4.0/src/mutex.rs000066400000000000000000000547201462663103300160740ustar00rootroot00000000000000use core::borrow::Borrow; use core::cell::UnsafeCell; use core::fmt; use core::marker::{PhantomData, PhantomPinned}; use core::ops::{Deref, DerefMut}; use core::pin::Pin; use core::task::Poll; use core::usize; use alloc::sync::Arc; // We don't use loom::UnsafeCell as that doesn't work with the Mutex API. use crate::sync::atomic::{AtomicUsize, Ordering}; #[cfg(all(feature = "std", not(target_family = "wasm")))] use std::time::{Duration, Instant}; use event_listener::{Event, EventListener}; use event_listener_strategy::{easy_wrapper, EventListenerFuture}; /// An async mutex. /// /// The locking mechanism uses eventual fairness to ensure locking will be fair on average without /// sacrificing performance. This is done by forcing a fair lock whenever a lock operation is /// starved for longer than 0.5 milliseconds. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::Mutex; /// /// let m = Mutex::new(1); /// /// let mut guard = m.lock().await; /// *guard = 2; /// /// assert!(m.try_lock().is_none()); /// drop(guard); /// assert_eq!(*m.try_lock().unwrap(), 2); /// # }) /// ``` pub struct Mutex { /// Current state of the mutex. /// /// The least significant bit is set to 1 if the mutex is locked. /// The other bits hold the number of starved lock operations. state: AtomicUsize, /// Lock operations waiting for the mutex to be released. lock_ops: Event, /// The value inside the mutex. data: UnsafeCell, } unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} impl Mutex { const_fn! { const_if: #[cfg(not(loom))]; /// Creates a new async mutex. /// /// # Examples /// /// ``` /// use async_lock::Mutex; /// /// let mutex = Mutex::new(0); /// ``` pub const fn new(data: T) -> Mutex { Mutex { state: AtomicUsize::new(0), lock_ops: Event::new(), data: UnsafeCell::new(data), } } } /// Consumes the mutex, returning the underlying data. /// /// # Examples /// /// ``` /// use async_lock::Mutex; /// /// let mutex = Mutex::new(10); /// assert_eq!(mutex.into_inner(), 10); /// ``` pub fn into_inner(self) -> T { self.data.into_inner() } } impl Mutex { /// Acquires the mutex. /// /// Returns a guard that releases the mutex when dropped. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::Mutex; /// /// let mutex = Mutex::new(10); /// let guard = mutex.lock().await; /// assert_eq!(*guard, 10); /// # }) /// ``` #[inline] pub fn lock(&self) -> Lock<'_, T> { Lock::_new(LockInner { mutex: self, acquire_slow: None, }) } /// Acquires the mutex using the blocking strategy. /// /// Returns a guard that releases the mutex when dropped. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`lock`][Mutex::lock] method, /// this method will block the current thread until the lock is acquired. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a mutex can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use async_lock::Mutex; /// /// let mutex = Mutex::new(10); /// let guard = mutex.lock_blocking(); /// assert_eq!(*guard, 10); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn lock_blocking(&self) -> MutexGuard<'_, T> { self.lock().wait() } /// Attempts to acquire the mutex. /// /// If the mutex could not be acquired at this time, then [`None`] is returned. Otherwise, a /// guard is returned that releases the mutex when dropped. /// /// # Examples /// /// ``` /// use async_lock::Mutex; /// /// let mutex = Mutex::new(10); /// if let Some(guard) = mutex.try_lock() { /// assert_eq!(*guard, 10); /// } /// # ; /// ``` #[inline] pub fn try_lock(&self) -> Option> { if self .state .compare_exchange(0, 1, Ordering::Acquire, Ordering::Acquire) .is_ok() { Some(MutexGuard(self)) } else { None } } /// Returns a mutable reference to the underlying data. /// /// Since this call borrows the mutex mutably, no actual locking takes place -- the mutable /// borrow statically guarantees the mutex is not already acquired. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::Mutex; /// /// let mut mutex = Mutex::new(0); /// *mutex.get_mut() = 10; /// assert_eq!(*mutex.lock().await, 10); /// # }) /// ``` pub fn get_mut(&mut self) -> &mut T { self.data.get_mut() } /// Unlocks the mutex directly. /// /// # Safety /// /// This function is intended to be used only in the case where the mutex is locked, /// and the guard is subsequently forgotten. Calling this while you don't hold a lock /// on the mutex will likely lead to UB. pub(crate) unsafe fn unlock_unchecked(&self) { // Remove the last bit and notify a waiting lock operation. self.state.fetch_sub(1, Ordering::Release); self.lock_ops.notify(1); } } impl Mutex { /// Acquires the mutex and clones a reference to it. /// /// Returns an owned guard that releases the mutex when dropped. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::Mutex; /// use std::sync::Arc; /// /// let mutex = Arc::new(Mutex::new(10)); /// let guard = mutex.lock_arc().await; /// assert_eq!(*guard, 10); /// # }) /// ``` #[inline] pub fn lock_arc(self: &Arc) -> LockArc { LockArc::_new(LockArcInnards::Unpolled { mutex: Some(self.clone()), }) } /// Acquires the mutex and clones a reference to it using the blocking strategy. /// /// Returns an owned guard that releases the mutex when dropped. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`lock_arc`][Mutex::lock_arc] method, /// this method will block the current thread until the lock is acquired. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a mutex can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use async_lock::Mutex; /// use std::sync::Arc; /// /// let mutex = Arc::new(Mutex::new(10)); /// let guard = mutex.lock_arc_blocking(); /// assert_eq!(*guard, 10); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn lock_arc_blocking(self: &Arc) -> MutexGuardArc { self.lock_arc().wait() } /// Attempts to acquire the mutex and clone a reference to it. /// /// If the mutex could not be acquired at this time, then [`None`] is returned. Otherwise, an /// owned guard is returned that releases the mutex when dropped. /// /// # Examples /// /// ``` /// use async_lock::Mutex; /// use std::sync::Arc; /// /// let mutex = Arc::new(Mutex::new(10)); /// if let Some(guard) = mutex.try_lock() { /// assert_eq!(*guard, 10); /// } /// # ; /// ``` #[inline] pub fn try_lock_arc(self: &Arc) -> Option> { if self .state .compare_exchange(0, 1, Ordering::Acquire, Ordering::Acquire) .is_ok() { Some(MutexGuardArc(self.clone())) } else { None } } } impl fmt::Debug for Mutex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct Locked; impl fmt::Debug for Locked { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("") } } match self.try_lock() { None => f.debug_struct("Mutex").field("data", &Locked).finish(), Some(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(), } } } impl From for Mutex { fn from(val: T) -> Mutex { Mutex::new(val) } } impl Default for Mutex { fn default() -> Mutex { Mutex::new(Default::default()) } } easy_wrapper! { /// The future returned by [`Mutex::lock`]. pub struct Lock<'a, T: ?Sized>(LockInner<'a, T> => MutexGuard<'a, T>); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { /// Inner future for acquiring the mutex. struct LockInner<'a, T: ?Sized> { // Reference to the mutex. mutex: &'a Mutex, // The future that waits for the mutex to become available. #[pin] acquire_slow: Option, T>>, } } unsafe impl Send for Lock<'_, T> {} unsafe impl Sync for Lock<'_, T> {} impl fmt::Debug for Lock<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Lock { .. }") } } impl<'a, T: ?Sized> EventListenerFuture for LockInner<'a, T> { type Output = MutexGuard<'a, T>; #[inline] fn poll_with_strategy<'x, S: event_listener_strategy::Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, context: &mut S::Context, ) -> Poll { let mut this = self.project(); // This may seem weird, but the borrow checker complains otherwise. if this.acquire_slow.is_none() { match this.mutex.try_lock() { Some(guard) => return Poll::Ready(guard), None => { this.acquire_slow.set(Some(AcquireSlow::new(this.mutex))); } } } ready!(this .acquire_slow .as_pin_mut() .unwrap() .poll_with_strategy(strategy, context)); Poll::Ready(MutexGuard(this.mutex)) } } easy_wrapper! { /// The future returned by [`Mutex::lock_arc`]. pub struct LockArc(LockArcInnards => MutexGuardArc); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { #[project = LockArcInnardsProj] enum LockArcInnards { /// We have not tried to poll the fast path yet. Unpolled { mutex: Option>> }, /// We are acquiring the mutex through the slow path. AcquireSlow { #[pin] inner: AcquireSlow>, T> }, } } unsafe impl Send for LockArc {} unsafe impl Sync for LockArc {} impl fmt::Debug for LockArcInnards { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("LockArc { .. }") } } impl EventListenerFuture for LockArcInnards { type Output = MutexGuardArc; fn poll_with_strategy<'a, S: event_listener_strategy::Strategy<'a>>( mut self: Pin<&mut Self>, strategy: &mut S, context: &mut S::Context, ) -> Poll { // Set the inner future if needed. if let LockArcInnardsProj::Unpolled { mutex } = self.as_mut().project() { let mutex = mutex.take().expect("mutex taken more than once"); // Try the fast path before trying to register slowly. if let Some(guard) = mutex.try_lock_arc() { return Poll::Ready(guard); } // Set the inner future to the slow acquire path. self.as_mut().set(LockArcInnards::AcquireSlow { inner: AcquireSlow::new(mutex), }); } // Poll the inner future. let value = match self.project() { LockArcInnardsProj::AcquireSlow { inner } => { ready!(inner.poll_with_strategy(strategy, context)) } _ => unreachable!(), }; Poll::Ready(MutexGuardArc(value)) } } pin_project_lite::pin_project! { /// Future for acquiring the mutex slowly. struct AcquireSlow>, T: ?Sized> { // Reference to the mutex. mutex: Option, // The event listener waiting on the mutex. listener: Option, // The point at which the mutex lock was started. start: Start, // This lock operation is starving. starved: bool, // Capture the `T` lifetime. #[pin] _marker: PhantomData, // Keeping this type `!Unpin` enables future optimizations. #[pin] _pin: PhantomPinned } impl>> PinnedDrop for AcquireSlow { fn drop(this: Pin<&mut Self>) { // Make sure the starvation counter is decremented. this.take_mutex(); } } } /// `pin_project_lite` doesn't support `#[cfg]` yet, so we have to do this manually. struct Start { #[cfg(all(feature = "std", not(target_family = "wasm")))] start: Option, } impl>> AcquireSlow { /// Create a new `AcquireSlow` future. #[cold] fn new(mutex: B) -> Self { AcquireSlow { mutex: Some(mutex), listener: None, start: Start { #[cfg(all(feature = "std", not(target_family = "wasm")))] start: None, }, starved: false, _marker: PhantomData, _pin: PhantomPinned, } } /// Take the mutex reference out, decrementing the counter if necessary. fn take_mutex(self: Pin<&mut Self>) -> Option { let this = self.project(); let mutex = this.mutex.take(); if *this.starved { if let Some(mutex) = mutex.as_ref() { // Decrement this counter before we exit. mutex.borrow().state.fetch_sub(2, Ordering::Release); } } mutex } } impl>> EventListenerFuture for AcquireSlow { type Output = B; #[cold] fn poll_with_strategy<'a, S: event_listener_strategy::Strategy<'a>>( mut self: Pin<&mut Self>, strategy: &mut S, context: &mut S::Context, ) -> Poll { let this = self.as_mut().project(); #[cfg(all(feature = "std", not(target_family = "wasm")))] let start = *this.start.start.get_or_insert_with(Instant::now); let mutex = Borrow::>::borrow( this.mutex.as_ref().expect("future polled after completion"), ); // Only use this hot loop if we aren't currently starved. if !*this.starved { loop { // Start listening for events. if this.listener.is_none() { *this.listener = Some(mutex.lock_ops.listen()); // Try locking if nobody is being starved. match mutex .state .compare_exchange(0, 1, Ordering::Acquire, Ordering::Acquire) .unwrap_or_else(|x| x) { // Lock acquired! 0 => return Poll::Ready(self.take_mutex().unwrap()), // Lock is held and nobody is starved. 1 => {} // Somebody is starved. _ => break, } } else { ready!(strategy.poll(this.listener, context)); // Try locking if nobody is being starved. match mutex .state .compare_exchange(0, 1, Ordering::Acquire, Ordering::Acquire) .unwrap_or_else(|x| x) { // Lock acquired! 0 => return Poll::Ready(self.take_mutex().unwrap()), // Lock is held and nobody is starved. 1 => {} // Somebody is starved. _ => { // Notify the first listener in line because we probably received a // notification that was meant for a starved task. mutex.lock_ops.notify(1); break; } } // If waiting for too long, fall back to a fairer locking strategy that will prevent // newer lock operations from starving us forever. #[cfg(all(feature = "std", not(target_family = "wasm")))] if start.elapsed() > Duration::from_micros(500) { break; } } } // Increment the number of starved lock operations. if mutex.state.fetch_add(2, Ordering::Release) > usize::MAX / 2 { // In case of potential overflow, abort. crate::abort(); } // Indicate that we are now starving and will use a fairer locking strategy. *this.starved = true; } // Fairer locking loop. loop { if this.listener.is_none() { // Start listening for events. *this.listener = Some(mutex.lock_ops.listen()); // Try locking if nobody else is being starved. match mutex .state .compare_exchange(2, 2 | 1, Ordering::Acquire, Ordering::Acquire) .unwrap_or_else(|x| x) { // Lock acquired! 2 => return Poll::Ready(self.take_mutex().unwrap()), // Lock is held by someone. s if s % 2 == 1 => {} // Lock is available. _ => { // Be fair: notify the first listener and then go wait in line. mutex.lock_ops.notify(1); } } } else { // Wait for a notification. ready!(strategy.poll(this.listener, context)); // Try acquiring the lock without waiting for others. if mutex.state.fetch_or(1, Ordering::Acquire) % 2 == 0 { return Poll::Ready(self.take_mutex().unwrap()); } } } } } /// A guard that releases the mutex when dropped. #[clippy::has_significant_drop] pub struct MutexGuard<'a, T: ?Sized>(&'a Mutex); unsafe impl Send for MutexGuard<'_, T> {} unsafe impl Sync for MutexGuard<'_, T> {} impl<'a, T: ?Sized> MutexGuard<'a, T> { /// Returns a reference to the mutex a guard came from. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::{Mutex, MutexGuard}; /// /// let mutex = Mutex::new(10i32); /// let guard = mutex.lock().await; /// dbg!(MutexGuard::source(&guard)); /// # }) /// ``` pub fn source(guard: &MutexGuard<'a, T>) -> &'a Mutex { guard.0 } } impl Drop for MutexGuard<'_, T> { #[inline] fn drop(&mut self) { // SAFETY: we are dropping the mutex guard, therefore unlocking the mutex. unsafe { self.0.unlock_unchecked(); } } } impl fmt::Debug for MutexGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl fmt::Display for MutexGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } impl Deref for MutexGuard<'_, T> { type Target = T; fn deref(&self) -> &T { unsafe { &*self.0.data.get() } } } impl DerefMut for MutexGuard<'_, T> { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.0.data.get() } } } /// An owned guard that releases the mutex when dropped. #[clippy::has_significant_drop] pub struct MutexGuardArc(Arc>); unsafe impl Send for MutexGuardArc {} unsafe impl Sync for MutexGuardArc {} impl MutexGuardArc { /// Returns a reference to the mutex a guard came from. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::{Mutex, MutexGuardArc}; /// use std::sync::Arc; /// /// let mutex = Arc::new(Mutex::new(10i32)); /// let guard = mutex.lock_arc().await; /// dbg!(MutexGuardArc::source(&guard)); /// # }) /// ``` pub fn source(guard: &Self) -> &Arc> where // Required because `MutexGuardArc` implements `Sync` regardless of whether `T` is `Send`, // but this method allows dropping `T` from a different thead than it was created in. T: Send, { &guard.0 } } impl Drop for MutexGuardArc { #[inline] fn drop(&mut self) { // SAFETY: we are dropping the mutex guard, therefore unlocking the mutex. unsafe { self.0.unlock_unchecked(); } } } impl fmt::Debug for MutexGuardArc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl fmt::Display for MutexGuardArc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } impl Deref for MutexGuardArc { type Target = T; fn deref(&self) -> &T { unsafe { &*self.0.data.get() } } } impl DerefMut for MutexGuardArc { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.0.data.get() } } } async-lock-3.4.0/src/once_cell.rs000066400000000000000000000622571462663103300166610ustar00rootroot00000000000000use core::cell::UnsafeCell; use core::convert::Infallible; use core::fmt; use core::future::Future; use core::mem::{forget, MaybeUninit}; use core::ptr; use crate::sync::atomic::{AtomicUsize, Ordering}; #[cfg(not(loom))] use crate::sync::WithMut; #[cfg(all(feature = "std", not(target_family = "wasm")))] use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; use event_listener::Event; use event_listener_strategy::{NonBlocking, Strategy}; #[cfg(all(feature = "std", not(target_family = "wasm")))] use event_listener::Listener; /// The current state of the `OnceCell`. #[derive(Copy, Clone, PartialEq, Eq)] #[repr(usize)] enum State { /// The `OnceCell` is uninitialized. Uninitialized = 0, /// The `OnceCell` is being initialized. Initializing = 1, /// The `OnceCell` is initialized. Initialized = 2, } impl From for State { fn from(val: usize) -> Self { match val { 0 => State::Uninitialized, 1 => State::Initializing, 2 => State::Initialized, _ => unreachable!("Invalid state"), } } } impl From for usize { fn from(val: State) -> Self { val as usize } } /// A memory location that can be written to at most once. /// /// A `OnceCell` can be used to store a single value, and only once. However, /// once the value is stored, it can be accessed directly through a reference /// instead of needing an RAII guard like `Mutex` or `RwLock`. /// /// # Examples /// /// This structure is useful for a variety of patterns, most notably for one-time /// initialization. /// /// ```rust /// use async_lock::OnceCell; /// /// # struct Foobar; /// /// async fn very_expensive_initialization() -> Foobar { /// // Imagine this is very expensive to initialize, /// // for instance, it requires a network request or /// // a database call. /// # Foobar /// } /// /// struct LazyFoobar { /// inner: OnceCell, /// } /// /// impl LazyFoobar { /// fn new() -> Self { /// Self { /// inner: OnceCell::new(), /// } /// } /// /// async fn load(&self) -> &Foobar { /// self.inner.get_or_init(|| async { /// very_expensive_initialization().await /// }).await /// } /// } /// ``` pub struct OnceCell { /// Listeners waiting for a chance to initialize the cell. /// /// These are the users of get_or_init() and similar functions. active_initializers: Event, /// Listeners waiting for the cell to be initialized. /// /// These are the users of wait(). passive_waiters: Event, /// State associated with the cell. state: AtomicUsize, /// The value of the cell. value: UnsafeCell>, } unsafe impl Send for OnceCell {} unsafe impl Sync for OnceCell {} impl OnceCell { const_fn! { const_if: #[cfg(not(loom))]; /// Create a new, uninitialized `OnceCell`. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// let cell = OnceCell::new(); /// # cell.set_blocking(1); /// ``` pub const fn new() -> Self { Self { active_initializers: Event::new(), passive_waiters: Event::new(), state: AtomicUsize::new(State::Uninitialized as _), value: UnsafeCell::new(MaybeUninit::uninit()), } } } /// Tell whether or not the cell is initialized. /// /// This may not always be accurate. For instance, it is possible for /// another thread to initialize the cell between the time when this /// function is called and the time when the result is actually used. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// # futures_lite::future::block_on(async { /// let cell = OnceCell::new(); /// assert!(!cell.is_initialized()); /// cell.set(1).await; /// assert!(cell.is_initialized()); /// # }); /// ``` pub fn is_initialized(&self) -> bool { State::from(self.state.load(Ordering::Acquire)) == State::Initialized } /// Get a reference to the inner value, or `None` if the value /// is not yet initialized. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// # futures_lite::future::block_on(async { /// let cell = OnceCell::new(); /// assert!(cell.get().is_none()); /// cell.set(1).await; /// assert_eq!(cell.get(), Some(&1)); /// # }); /// ``` pub fn get(&self) -> Option<&T> { if self.is_initialized() { // SAFETY: We know that the value is initialized, so it is safe to // read it. Some(unsafe { self.get_unchecked() }) } else { None } } /// Get a mutable reference to the inner value, or `None` if the value /// is not yet initialized. /// /// This function is useful for initializing the value inside the cell /// when we still have a mutable reference to the cell. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// # futures_lite::future::block_on(async { /// let mut cell = OnceCell::new(); /// assert!(cell.get_mut().is_none()); /// cell.set(1).await; /// assert_eq!(cell.get_mut(), Some(&mut 1)); /// *cell.get_mut().unwrap() = 2; /// assert_eq!(cell.get(), Some(&2)); /// # }); /// ``` pub fn get_mut(&mut self) -> Option<&mut T> { self.state.with_mut(|state| { if State::from(*state) == State::Initialized { // SAFETY: We know that the value is initialized, so it is safe to // read it. Some(unsafe { &mut *self.value.get().cast() }) } else { None } }) } /// Take the value out of this `OnceCell`, moving it back to the uninitialized /// state. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// # futures_lite::future::block_on(async { /// let mut cell = OnceCell::new(); /// cell.set(1).await; /// assert_eq!(cell.take(), Some(1)); /// assert!(!cell.is_initialized()); /// # }); /// ``` pub fn take(&mut self) -> Option { self.state.with_mut(|state| { if State::from(*state) == State::Initialized { // SAFETY: We know that the value is initialized, so it is safe to // read it. let value = unsafe { ptr::read(self.value.get().cast()) }; *state = State::Uninitialized.into(); Some(value) } else { None } }) } /// Convert this `OnceCell` into the inner value, if it is initialized. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// # futures_lite::future::block_on(async { /// let cell = OnceCell::new(); /// cell.set(1).await; /// assert_eq!(cell.into_inner(), Some(1)); /// # }); /// ``` pub fn into_inner(mut self) -> Option { self.take() } /// Wait for the cell to be initialized, and then return a reference to the /// inner value. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// use std::sync::Arc; /// use std::time::Duration; /// use std::thread::{sleep, spawn}; /// /// let cell = Arc::new(OnceCell::new()); /// let cell2 = cell.clone(); /// /// spawn(move || { /// sleep(Duration::from_millis(5)); /// cell2.set_blocking(1); /// }); /// /// # futures_lite::future::block_on(async { /// assert_eq!(cell.wait().await, &1); /// # }); /// ``` pub async fn wait(&self) -> &T { // Fast path: see if the value is already initialized. if let Some(value) = self.get() { return value; } // Slow path: wait for the value to be initialized. event_listener::listener!(self.passive_waiters => listener); // Try again. if let Some(value) = self.get() { return value; } listener.await; debug_assert!(self.is_initialized()); // SAFETY: We know that the value is initialized, so it is safe to // read it. unsafe { self.get_unchecked() } } /// Wait for the cell to be initialized, and then return a reference to the /// inner value. /// /// # Blocking /// /// In contrast to the `wait` method, this method blocks the current thread of /// execution instead of awaiting. /// /// This method should not be used in an asynchronous context. It is intended /// to be used such that a `OnceCell` can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in deadlocks. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// use std::sync::Arc; /// use std::time::Duration; /// use std::thread::{sleep, spawn}; /// /// let cell = Arc::new(OnceCell::new()); /// let cell2 = cell.clone(); /// /// spawn(move || { /// sleep(Duration::from_millis(5)); /// cell2.set_blocking(1); /// }); /// /// assert_eq!(cell.wait_blocking(), &1); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] pub fn wait_blocking(&self) -> &T { // Fast path: see if the value is already initialized. if let Some(value) = self.get() { return value; } // Slow path: wait for the value to be initialized. event_listener::listener!(self.passive_waiters => listener); // Try again. if let Some(value) = self.get() { return value; } listener.wait(); debug_assert!(self.is_initialized()); // SAFETY: We know that the value is initialized, so it is safe to // read it. unsafe { self.get_unchecked() } } /// Either get the value or initialize it with the given closure. /// /// The cell will not be initialized if the closure returns an error. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// # /// # // Prevent explicit value errors. /// # fn _explicit(_: &Result<&i32, ()>) {} /// /// # futures_lite::future::block_on(async { /// let cell = OnceCell::new(); /// /// let result = cell.get_or_try_init(|| async { Err(()) }).await; /// assert!(result.is_err()); /// /// let result = cell.get_or_try_init(|| async { Ok(1) }).await; /// # _explicit(&result); /// assert_eq!(result.unwrap(), &1); /// /// let result = cell.get_or_try_init(|| async { Err(()) }).await; /// /// assert_eq!(result.unwrap(), &1); /// # }); /// ``` pub async fn get_or_try_init>>( &self, closure: impl FnOnce() -> Fut, ) -> Result<&T, E> { // Fast path: see if the value is already initialized. if let Some(value) = self.get() { return Ok(value); } // Slow path: initialize the value. self.initialize_or_wait(closure, &mut NonBlocking::default()) .await?; debug_assert!(self.is_initialized()); // SAFETY: We know that the value is initialized, so it is safe to // read it. Ok(unsafe { self.get_unchecked() }) } /// Either get the value or initialize it with the given closure. /// /// The cell will not be initialized if the closure returns an error. /// /// # Blocking /// /// In contrast to the `get_or_try_init` method, this method blocks the current thread of /// execution instead of awaiting. /// /// This method should not be used in an asynchronous context. It is intended /// to be used such that a `OnceCell` can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in deadlocks. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// # /// # // Prevent explicit type errors. /// # fn _explicit(_: &Result<&i32, ()>) {} /// /// let cell = OnceCell::new(); /// /// let result = cell.get_or_try_init_blocking(|| Err(())); /// assert!(result.is_err()); /// /// let result = cell.get_or_try_init_blocking(|| Ok(1)); /// # _explicit(&result); /// assert_eq!(result.unwrap(), &1); /// /// let result = cell.get_or_try_init_blocking(|| Err(())); /// /// assert_eq!(result.unwrap(), &1); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] pub fn get_or_try_init_blocking( &self, closure: impl FnOnce() -> Result, ) -> Result<&T, E> { // Fast path: see if the value is already initialized. if let Some(value) = self.get() { return Ok(value); } // Slow path: initialize the value. // The futures provided should never block, so we can use `now_or_never`. now_or_never(self.initialize_or_wait( move || core::future::ready(closure()), &mut event_listener_strategy::Blocking::default(), ))?; debug_assert!(self.is_initialized()); // SAFETY: We know that the value is initialized, so it is safe to // read it. Ok(unsafe { self.get_unchecked() }) } /// Either get the value or initialize it with the given closure. /// /// Many tasks may call this function, but the value will only be set once /// and only one closure will be invoked. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// # futures_lite::future::block_on(async { /// let cell = OnceCell::new(); /// assert_eq!(cell.get_or_init(|| async { 1 }).await, &1); /// assert_eq!(cell.get_or_init(|| async { 2 }).await, &1); /// # }); /// ``` pub async fn get_or_init>(&self, closure: impl FnOnce() -> Fut) -> &T { match self .get_or_try_init(move || async move { let result: Result = Ok(closure().await); result }) .await { Ok(value) => value, Err(infallible) => match infallible {}, } } /// Either get the value or initialize it with the given closure. /// /// Many tasks may call this function, but the value will only be set once /// and only one closure will be invoked. /// /// # Blocking /// /// In contrast to the `get_or_init` method, this method blocks the current thread of /// execution instead of awaiting. /// /// This method should not be used in an asynchronous context. It is intended /// to be used such that a `OnceCell` can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in deadlocks. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// let cell = OnceCell::new(); /// assert_eq!(cell.get_or_init_blocking(|| 1), &1); /// assert_eq!(cell.get_or_init_blocking(|| 2), &1); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] pub fn get_or_init_blocking(&self, closure: impl FnOnce() -> T + Unpin) -> &T { let result = self.get_or_try_init_blocking(move || { let result: Result = Ok(closure()); result }); match result { Ok(value) => value, Err(infallible) => match infallible {}, } } /// Try to set the value of the cell. /// /// If the cell is already initialized, this method returns the original /// value back. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// # futures_lite::future::block_on(async { /// let cell = OnceCell::new(); /// /// assert_eq!(cell.set(1).await, Ok(&1)); /// assert_eq!(cell.get(), Some(&1)); /// assert_eq!(cell.set(2).await, Err(2)); /// # }); /// ``` pub async fn set(&self, value: T) -> Result<&T, T> { let mut value = Some(value); self.get_or_init(|| async { value.take().unwrap() }).await; match value { Some(value) => Err(value), None => { // SAFETY: value was taken, so we are initialized Ok(unsafe { self.get_unchecked() }) } } } /// Try to set the value of the cell. /// /// If the cell is already initialized, this method returns the original /// value back. /// /// # Blocking /// /// In contrast to the `set` method, this method blocks the current thread of /// execution instead of awaiting. /// /// This method should not be used in an asynchronous context. It is intended /// to be used such that a `OnceCell` can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in deadlocks. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// let cell = OnceCell::new(); /// /// assert_eq!(cell.set_blocking(1), Ok(&1)); /// assert_eq!(cell.get(), Some(&1)); /// assert_eq!(cell.set_blocking(2), Err(2)); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] pub fn set_blocking(&self, value: T) -> Result<&T, T> { let mut value = Some(value); self.get_or_init_blocking(|| value.take().unwrap()); match value { Some(value) => Err(value), None => { // SAFETY: value was taken, so we are initialized Ok(unsafe { self.get_unchecked() }) } } } /// Wait for the cell to be initialized, optionally using a closure /// to initialize the cell if it is not initialized yet. #[cold] async fn initialize_or_wait>, F: FnOnce() -> Fut>( &self, closure: F, strategy: &mut impl for<'a> Strategy<'a>, ) -> Result<(), E> { // The event listener we're currently waiting on. let mut event_listener = None; let mut closure = Some(closure); loop { // Check the current state of the cell. let state = self.state.load(Ordering::Acquire); // Determine what we should do based on our state. match state.into() { State::Initialized => { // The cell is initialized now, so we can return. return Ok(()); } State::Initializing => { // The cell is currently initializing, or the cell is uninitialized // but we do not have the ability to initialize it. // // We need to wait the initialization to complete. if let Some(listener) = event_listener.take() { strategy.wait(listener).await; } else { event_listener = Some(self.active_initializers.listen()); } } State::Uninitialized => { // Try to move the cell into the initializing state. if self .state .compare_exchange( State::Uninitialized.into(), State::Initializing.into(), Ordering::AcqRel, Ordering::Acquire, ) .is_err() { // The cell was initialized while we were trying to // initialize it. continue; } // Now that we have an exclusive lock on the cell's value, // we can try to initialize it. let _guard = Guard(self); let initializer = closure.take().unwrap(); match (initializer)().await { Ok(value) => { // Write the value into the cell and update the state. unsafe { ptr::write(self.value.get().cast(), value); } forget(_guard); self.state .store(State::Initialized.into(), Ordering::Release); // Notify the listeners that the value is initialized. self.active_initializers.notify_additional(core::usize::MAX); self.passive_waiters.notify_additional(core::usize::MAX); return Ok(()); } Err(err) => { // Update the state to indicate that the value is // uninitialized. drop(_guard); return Err(err); } } } } } /// Set the cell's state back to `UNINITIALIZED on drop. /// /// If the closure panics, this ensures that the cell's state is set back to /// `UNINITIALIZED` and that the next listener is notified. struct Guard<'a, T>(&'a OnceCell); impl<'a, T> Drop for Guard<'a, T> { fn drop(&mut self) { self.0 .state .store(State::Uninitialized.into(), Ordering::Release); // Notify the next initializer that it's their turn. self.0.active_initializers.notify(1); } } } /// Get a reference to the inner value. /// /// # Safety /// /// The caller must ensure that the cell is initialized. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// # futures_lite::future::block_on(async { /// let cell = OnceCell::new(); /// cell.set(1).await; /// /// // SAFETY: We know that the value is initialized, so it is safe to /// // read it. /// assert_eq!(unsafe { cell.get_unchecked() }, &1); /// # }); /// ``` pub unsafe fn get_unchecked(&self) -> &T { // SAFETY: The caller asserts that the value is initialized &*self.value.get().cast() } } impl From for OnceCell { /// Create a new, initialized `OnceCell` from an existing value. /// /// # Example /// /// ```rust /// use async_lock::OnceCell; /// /// let cell = OnceCell::from(42); /// assert_eq!(cell.get(), Some(&42)); /// ``` fn from(value: T) -> Self { Self { active_initializers: Event::new(), passive_waiters: Event::new(), state: AtomicUsize::new(State::Initialized.into()), value: UnsafeCell::new(MaybeUninit::new(value)), } } } impl fmt::Debug for OnceCell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct Inner<'a, T>(&'a OnceCell); impl fmt::Debug for Inner<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0.state.load(Ordering::Acquire).into() { State::Uninitialized => f.write_str(""), State::Initializing => f.write_str(""), State::Initialized => { // SAFETY: "value" is initialized. let value = unsafe { self.0.get_unchecked() }; fmt::Debug::fmt(value, f) } } } } f.debug_tuple("OnceCell").field(&Inner(self)).finish() } } impl Drop for OnceCell { fn drop(&mut self) { self.state.with_mut(|state| { if State::from(*state) == State::Initialized { // SAFETY: We know that the value is initialized, so it is safe to // drop it. unsafe { self.value.get().cast::().drop_in_place() } } }); } } impl Default for OnceCell { // Calls `OnceCell::new`. #[inline] fn default() -> Self { Self::new() } } /// Either return the result of a future now, or panic. #[cfg(all(feature = "std", not(target_family = "wasm")))] fn now_or_never(f: impl Future) -> T { const NOOP_WAKER: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); unsafe fn wake(_: *const ()) {} unsafe fn wake_by_ref(_: *const ()) {} unsafe fn clone(_: *const ()) -> RawWaker { RawWaker::new(ptr::null(), &NOOP_WAKER) } unsafe fn drop(_: *const ()) {} pin!(f); let waker = unsafe { Waker::from_raw(RawWaker::new(ptr::null(), &NOOP_WAKER)) }; // Poll the future exactly once. let mut cx = Context::from_waker(&waker); match f.poll(&mut cx) { Poll::Ready(value) => value, Poll::Pending => unreachable!("future not ready"), } } async-lock-3.4.0/src/rwlock.rs000066400000000000000000001257651462663103300162430ustar00rootroot00000000000000use core::cell::UnsafeCell; use core::fmt; use core::mem::{self, ManuallyDrop}; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull}; use alloc::sync::Arc; pub(crate) mod futures; mod raw; use self::futures::{ Read, ReadArc, UpgradableRead, UpgradableReadArc, Upgrade, UpgradeArc, Write, WriteArc, }; use self::raw::{RawRwLock, RawUpgrade}; /// An async reader-writer lock. /// /// This type of lock allows multiple readers or one writer at any point in time. /// /// The locking strategy is write-preferring, which means writers are never starved. /// Releasing a write lock wakes the next blocked reader and the next blocked writer. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::RwLock; /// /// let lock = RwLock::new(5); /// /// // Multiple read locks can be held at a time. /// let r1 = lock.read().await; /// let r2 = lock.read().await; /// assert_eq!(*r1, 5); /// assert_eq!(*r2, 5); /// drop((r1, r2)); /// /// // Only one write lock can be held at a time. /// let mut w = lock.write().await; /// *w += 1; /// assert_eq!(*w, 6); /// # }) /// ``` pub struct RwLock { /// The underlying locking implementation. /// Doesn't depend on `T`. raw: RawRwLock, /// The inner value. value: UnsafeCell, } unsafe impl Send for RwLock {} unsafe impl Sync for RwLock {} impl RwLock { const_fn! { const_if: #[cfg(not(loom))]; /// Creates a new reader-writer lock. /// /// # Examples /// /// ``` /// use async_lock::RwLock; /// /// let lock = RwLock::new(0); /// ``` #[must_use] #[inline] pub const fn new(t: T) -> RwLock { RwLock { raw: RawRwLock::new(), value: UnsafeCell::new(t), } } } /// Unwraps the lock and returns the inner value. /// /// # Examples /// /// ``` /// use async_lock::RwLock; /// /// let lock = RwLock::new(5); /// assert_eq!(lock.into_inner(), 5); /// ``` #[must_use] #[inline] pub fn into_inner(self) -> T { self.value.into_inner() } /// Attempts to acquire an an owned, reference-counted read lock. /// /// If a read lock could not be acquired at this time, then [`None`] is returned. Otherwise, a /// guard is returned that releases the lock when dropped. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::RwLock; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let reader = lock.read_arc().await; /// assert_eq!(*reader, 1); /// /// assert!(lock.try_read_arc().is_some()); /// # }) /// ``` #[inline] pub fn try_read_arc(self: &Arc) -> Option> { if self.raw.try_read() { let arc = self.clone(); // SAFETY: we previously acquired a read lock. Some(unsafe { RwLockReadGuardArc::from_arc(arc) }) } else { None } } /// Acquires an owned, reference-counted read lock. /// /// Returns a guard that releases the lock when dropped. /// /// Note that attempts to acquire a read lock will block if there are also concurrent attempts /// to acquire a write lock. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::RwLock; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let reader = lock.read_arc().await; /// assert_eq!(*reader, 1); /// /// assert!(lock.try_read_arc().is_some()); /// # }) /// ``` #[inline] pub fn read_arc<'a>(self: &'a Arc) -> ReadArc<'a, T> { ReadArc::new(self.raw.read(), self) } /// Acquires an owned, reference-counted read lock. /// /// Returns a guard that releases the lock when dropped. /// /// Note that attempts to acquire a read lock will block if there are also concurrent attempts /// to acquire a write lock. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`read_arc`][`RwLock::read_arc`] method, /// this method will block the current thread until the read lock is acquired. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a lock can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use std::sync::Arc; /// use async_lock::RwLock; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let reader = lock.read_arc_blocking(); /// assert_eq!(*reader, 1); /// /// assert!(lock.try_read().is_some()); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn read_arc_blocking(self: &Arc) -> RwLockReadGuardArc { self.read_arc().wait() } } impl RwLock { /// Attempts to acquire a read lock. /// /// If a read lock could not be acquired at this time, then [`None`] is returned. Otherwise, a /// guard is returned that releases the lock when dropped. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::RwLock; /// /// let lock = RwLock::new(1); /// /// let reader = lock.read().await; /// assert_eq!(*reader, 1); /// /// assert!(lock.try_read().is_some()); /// # }) /// ``` #[inline] pub fn try_read(&self) -> Option> { if self.raw.try_read() { Some(RwLockReadGuard { lock: &self.raw, value: self.value.get(), }) } else { None } } /// Acquires a read lock. /// /// Returns a guard that releases the lock when dropped. /// /// Note that attempts to acquire a read lock will block if there are also concurrent attempts /// to acquire a write lock. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::RwLock; /// /// let lock = RwLock::new(1); /// /// let reader = lock.read().await; /// assert_eq!(*reader, 1); /// /// assert!(lock.try_read().is_some()); /// # }) /// ``` #[inline] pub fn read(&self) -> Read<'_, T> { Read::new(self.raw.read(), self.value.get()) } /// Acquires a read lock. /// /// Returns a guard that releases the lock when dropped. /// /// Note that attempts to acquire a read lock will block if there are also concurrent attempts /// to acquire a write lock. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`read`][`RwLock::read`] method, /// this method will block the current thread until the read lock is acquired. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a lock can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use async_lock::RwLock; /// /// let lock = RwLock::new(1); /// /// let reader = lock.read_blocking(); /// assert_eq!(*reader, 1); /// /// assert!(lock.try_read().is_some()); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn read_blocking(&self) -> RwLockReadGuard<'_, T> { self.read().wait() } /// Attempts to acquire a read lock with the possiblity to upgrade to a write lock. /// /// If a read lock could not be acquired at this time, then [`None`] is returned. Otherwise, a /// guard is returned that releases the lock when dropped. /// /// Upgradable read lock reserves the right to be upgraded to a write lock, which means there /// can be at most one upgradable read lock at a time. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::{RwLock, RwLockUpgradableReadGuard}; /// /// let lock = RwLock::new(1); /// /// let reader = lock.upgradable_read().await; /// assert_eq!(*reader, 1); /// assert_eq!(*lock.try_read().unwrap(), 1); /// /// let mut writer = RwLockUpgradableReadGuard::upgrade(reader).await; /// *writer = 2; /// # }) /// ``` #[inline] pub fn try_upgradable_read(&self) -> Option> { if self.raw.try_upgradable_read() { Some(RwLockUpgradableReadGuard { lock: &self.raw, value: self.value.get(), }) } else { None } } /// Acquires a read lock with the possiblity to upgrade to a write lock. /// /// Returns a guard that releases the lock when dropped. /// /// Upgradable read lock reserves the right to be upgraded to a write lock, which means there /// can be at most one upgradable read lock at a time. /// /// Note that attempts to acquire an upgradable read lock will block if there are concurrent /// attempts to acquire another upgradable read lock or a write lock. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::{RwLock, RwLockUpgradableReadGuard}; /// /// let lock = RwLock::new(1); /// /// let reader = lock.upgradable_read().await; /// assert_eq!(*reader, 1); /// assert_eq!(*lock.try_read().unwrap(), 1); /// /// let mut writer = RwLockUpgradableReadGuard::upgrade(reader).await; /// *writer = 2; /// # }) /// ``` #[inline] pub fn upgradable_read(&self) -> UpgradableRead<'_, T> { UpgradableRead::new(self.raw.upgradable_read(), self.value.get()) } /// Attempts to acquire a read lock with the possiblity to upgrade to a write lock. /// /// Returns a guard that releases the lock when dropped. /// /// Upgradable read lock reserves the right to be upgraded to a write lock, which means there /// can be at most one upgradable read lock at a time. /// /// Note that attempts to acquire an upgradable read lock will block if there are concurrent /// attempts to acquire another upgradable read lock or a write lock. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`upgradable_read`][`RwLock::upgradable_read`] /// method, this method will block the current thread until the read lock is acquired. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a lock can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use async_lock::{RwLock, RwLockUpgradableReadGuard}; /// /// let lock = RwLock::new(1); /// /// let reader = lock.upgradable_read_blocking(); /// assert_eq!(*reader, 1); /// assert_eq!(*lock.try_read().unwrap(), 1); /// /// let mut writer = RwLockUpgradableReadGuard::upgrade_blocking(reader); /// *writer = 2; /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn upgradable_read_blocking(&self) -> RwLockUpgradableReadGuard<'_, T> { self.upgradable_read().wait() } /// Attempts to acquire an owned, reference-counted read lock /// with the possiblity to upgrade to a write lock. /// /// Returns a guard that releases the lock when dropped. /// /// Upgradable read lock reserves the right to be upgraded to a write lock, which means there /// can be at most one upgradable read lock at a time. /// /// Note that attempts to acquire an upgradable read lock will block if there are concurrent /// attempts to acquire another upgradable read lock or a write lock. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`upgradable_read_arc`][`RwLock::upgradable_read_arc`] /// method, this method will block the current thread until the read lock is acquired. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a lock can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use std::sync::Arc; /// use async_lock::{RwLock, RwLockUpgradableReadGuardArc}; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let reader = lock.upgradable_read_arc_blocking(); /// assert_eq!(*reader, 1); /// assert_eq!(*lock.try_read().unwrap(), 1); /// /// let mut writer = RwLockUpgradableReadGuardArc::upgrade_blocking(reader); /// *writer = 2; /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn upgradable_read_arc_blocking(self: &Arc) -> RwLockUpgradableReadGuardArc { self.upgradable_read_arc().wait() } /// Attempts to acquire an owned, reference-counted read lock with the possiblity to /// upgrade to a write lock. /// /// If a read lock could not be acquired at this time, then [`None`] is returned. Otherwise, a /// guard is returned that releases the lock when dropped. /// /// Upgradable read lock reserves the right to be upgraded to a write lock, which means there /// can be at most one upgradable read lock at a time. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::{RwLock, RwLockUpgradableReadGuardArc}; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let reader = lock.upgradable_read_arc().await; /// assert_eq!(*reader, 1); /// assert_eq!(*lock.try_read_arc().unwrap(), 1); /// /// let mut writer = RwLockUpgradableReadGuardArc::upgrade(reader).await; /// *writer = 2; /// # }) /// ``` #[inline] pub fn try_upgradable_read_arc(self: &Arc) -> Option> { if self.raw.try_upgradable_read() { Some(RwLockUpgradableReadGuardArc { lock: self.clone() }) } else { None } } /// Acquires an owned, reference-counted read lock with the possiblity /// to upgrade to a write lock. /// /// Returns a guard that releases the lock when dropped. /// /// Upgradable read lock reserves the right to be upgraded to a write lock, which means there /// can be at most one upgradable read lock at a time. /// /// Note that attempts to acquire an upgradable read lock will block if there are concurrent /// attempts to acquire another upgradable read lock or a write lock. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::{RwLock, RwLockUpgradableReadGuardArc}; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let reader = lock.upgradable_read_arc().await; /// assert_eq!(*reader, 1); /// assert_eq!(*lock.try_read_arc().unwrap(), 1); /// /// let mut writer = RwLockUpgradableReadGuardArc::upgrade(reader).await; /// *writer = 2; /// # }) /// ``` #[inline] pub fn upgradable_read_arc<'a>(self: &'a Arc) -> UpgradableReadArc<'a, T> { UpgradableReadArc::new(self.raw.upgradable_read(), self) } /// Attempts to acquire a write lock. /// /// If a write lock could not be acquired at this time, then [`None`] is returned. Otherwise, a /// guard is returned that releases the lock when dropped. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::RwLock; /// /// let lock = RwLock::new(1); /// /// assert!(lock.try_write().is_some()); /// let reader = lock.read().await; /// assert!(lock.try_write().is_none()); /// # }) /// ``` #[inline] pub fn try_write(&self) -> Option> { if self.raw.try_write() { Some(RwLockWriteGuard { lock: &self.raw, value: self.value.get(), }) } else { None } } /// Acquires a write lock. /// /// Returns a guard that releases the lock when dropped. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::RwLock; /// /// let lock = RwLock::new(1); /// /// let writer = lock.write().await; /// assert!(lock.try_read().is_none()); /// # }) /// ``` #[inline] pub fn write(&self) -> Write<'_, T> { Write::new(self.raw.write(), self.value.get()) } /// Acquires a write lock. /// /// Returns a guard that releases the lock when dropped. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`write`] method, this method will /// block the current thread until the write lock is acquired. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a lock can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use async_lock::RwLock; /// /// let lock = RwLock::new(1); /// /// let writer = lock.write_blocking(); /// assert!(lock.try_read().is_none()); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn write_blocking(&self) -> RwLockWriteGuard<'_, T> { self.write().wait() } /// Attempts to acquire an owned, reference-counted write lock. /// /// If a write lock could not be acquired at this time, then [`None`] is returned. Otherwise, a /// guard is returned that releases the lock when dropped. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::RwLock; /// /// let lock = Arc::new(RwLock::new(1)); /// /// assert!(lock.try_write_arc().is_some()); /// let reader = lock.read_arc().await; /// assert!(lock.try_write_arc().is_none()); /// # }) /// ``` #[inline] pub fn try_write_arc(self: &Arc) -> Option> { if self.raw.try_write() { Some(RwLockWriteGuardArc { lock: self.clone() }) } else { None } } /// Acquires an owned, reference-counted write lock. /// /// Returns a guard that releases the lock when dropped. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::RwLock; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let writer = lock.write_arc().await; /// assert!(lock.try_read_arc().is_none()); /// # }) /// ``` #[inline] pub fn write_arc<'a>(self: &'a Arc) -> WriteArc<'a, T> { WriteArc::new(self.raw.write(), self) } /// Acquires an owned, reference-counted write lock. /// /// Returns a guard that releases the lock when dropped. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`write_arc`][RwLock::write_arc] method, this method will /// block the current thread until the write lock is acquired. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a lock can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use std::sync::Arc; /// use async_lock::RwLock; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let writer = lock.write_arc_blocking(); /// assert!(lock.try_read().is_none()); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn write_arc_blocking(self: &Arc) -> RwLockWriteGuardArc { self.write_arc().wait() } /// Returns a mutable reference to the inner value. /// /// Since this call borrows the lock mutably, no actual locking takes place. The mutable borrow /// statically guarantees no locks exist. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::RwLock; /// /// let mut lock = RwLock::new(1); /// /// *lock.get_mut() = 2; /// assert_eq!(*lock.read().await, 2); /// # }) /// ``` #[must_use] #[inline] pub fn get_mut(&mut self) -> &mut T { unsafe { &mut *self.value.get() } } } impl fmt::Debug for RwLock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct Locked; impl fmt::Debug for Locked { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("") } } match self.try_read() { None => f.debug_struct("RwLock").field("value", &Locked).finish(), Some(guard) => f.debug_struct("RwLock").field("value", &&*guard).finish(), } } } impl From for RwLock { #[inline] fn from(val: T) -> RwLock { RwLock::new(val) } } impl Default for RwLock { #[inline] fn default() -> RwLock { RwLock::new(Default::default()) } } /// A guard that releases the read lock when dropped. #[clippy::has_significant_drop] pub struct RwLockReadGuard<'a, T: ?Sized> { /// Reference to underlying locking implementation. /// Doesn't depend on `T`. lock: &'a RawRwLock, /// Pointer to the value protected by the lock. Covariant in `T`. value: *const T, } unsafe impl Send for RwLockReadGuard<'_, T> {} unsafe impl Sync for RwLockReadGuard<'_, T> {} impl Drop for RwLockReadGuard<'_, T> { #[inline] fn drop(&mut self) { // SAFETY: we are dropping a read guard. unsafe { self.lock.read_unlock(); } } } impl fmt::Debug for RwLockReadGuard<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl fmt::Display for RwLockReadGuard<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } impl Deref for RwLockReadGuard<'_, T> { type Target = T; #[inline] fn deref(&self) -> &T { unsafe { &*self.value } } } /// An owned, reference-counting guard that releases the read lock when dropped. #[clippy::has_significant_drop] pub struct RwLockReadGuardArc { /// **WARNING**: This doesn't actually point to a `T`! /// It points to a `RwLock`, via a pointer obtained with `Arc::into_raw`. /// We lie for covariance. lock: NonNull, } unsafe impl Send for RwLockReadGuardArc {} unsafe impl Sync for RwLockReadGuardArc {} impl RwLockReadGuardArc { /// Constructs the underlying `Arc` back from the underlying `RwLock`. /// /// # Safety /// /// Both the returned `Arc` and the guard will decrement their reference /// counts on drop! So one of the two must be forgotten. #[inline] unsafe fn inner_arc(guard: &Self) -> ManuallyDrop>> { ManuallyDrop::new(Arc::from_raw(guard.lock.as_ptr().cast())) } /// Constructs a guard from the underlying `Arc`. /// /// # Safety /// /// A read lock must be acquired before calling this. #[inline] unsafe fn from_arc(arc: Arc>) -> Self { let ptr = Arc::into_raw(arc); Self { lock: NonNull::new(ptr as *mut RwLock as *mut T).unwrap(), } } } impl Drop for RwLockReadGuardArc { #[inline] fn drop(&mut self) { // SAFETY: we are in `drop`, decrementing the reference count // on purpose. // We hold a read lock on the `RwLock`. unsafe { let arc = ManuallyDrop::into_inner(Self::inner_arc(self)); arc.raw.read_unlock(); } } } impl fmt::Debug for RwLockReadGuardArc { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl fmt::Display for RwLockReadGuardArc { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } impl Deref for RwLockReadGuardArc { type Target = T; #[inline] fn deref(&self) -> &T { // SAFETY: we use `ManuallyDrop` to avoid double-drop. // We hold a read lock on the `RwLock`. unsafe { let arc = Self::inner_arc(self); &*arc.value.get() } } } /// A guard that releases the upgradable read lock when dropped. #[clippy::has_significant_drop] pub struct RwLockUpgradableReadGuard<'a, T: ?Sized> { /// Reference to underlying locking implementation. /// Doesn't depend on `T`. /// This guard holds a lock on the witer mutex! lock: &'a RawRwLock, /// Pointer to the value protected by the lock. Invariant in `T` /// as the upgradable lock could provide write access. value: *mut T, } impl<'a, T: ?Sized> Drop for RwLockUpgradableReadGuard<'a, T> { #[inline] fn drop(&mut self) { // SAFETY: we are dropping an upgradable read guard. unsafe { self.lock.upgradable_read_unlock(); } } } unsafe impl Send for RwLockUpgradableReadGuard<'_, T> {} unsafe impl Sync for RwLockUpgradableReadGuard<'_, T> {} impl<'a, T: ?Sized> RwLockUpgradableReadGuard<'a, T> { /// Downgrades into a regular reader guard. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::{RwLock, RwLockUpgradableReadGuard}; /// /// let lock = RwLock::new(1); /// /// let reader = lock.upgradable_read().await; /// assert_eq!(*reader, 1); /// /// assert!(lock.try_upgradable_read().is_none()); /// /// let reader = RwLockUpgradableReadGuard::downgrade(reader); /// /// assert!(lock.try_upgradable_read().is_some()); /// # }) /// ``` #[inline] pub fn downgrade(guard: Self) -> RwLockReadGuard<'a, T> { let upgradable = ManuallyDrop::new(guard); // SAFETY: `guard` is an upgradable read lock. unsafe { upgradable.lock.downgrade_upgradable_read(); }; RwLockReadGuard { lock: upgradable.lock, value: upgradable.value, } } /// Attempts to upgrade into a write lock. /// /// If a write lock could not be acquired at this time, then [`None`] is returned. Otherwise, /// an upgraded guard is returned that releases the write lock when dropped. /// /// This function can only fail if there are other active read locks. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::{RwLock, RwLockUpgradableReadGuard}; /// /// let lock = RwLock::new(1); /// /// let reader = lock.upgradable_read().await; /// assert_eq!(*reader, 1); /// /// let reader2 = lock.read().await; /// let reader = RwLockUpgradableReadGuard::try_upgrade(reader).unwrap_err(); /// /// drop(reader2); /// let writer = RwLockUpgradableReadGuard::try_upgrade(reader).unwrap(); /// # }) /// ``` #[inline] pub fn try_upgrade(guard: Self) -> Result, Self> { // If there are no readers, grab the write lock. // SAFETY: `guard` is an upgradable read guard if unsafe { guard.lock.try_upgrade() } { let reader = ManuallyDrop::new(guard); Ok(RwLockWriteGuard { lock: reader.lock, value: reader.value, }) } else { Err(guard) } } /// Upgrades into a write lock. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::{RwLock, RwLockUpgradableReadGuard}; /// /// let lock = RwLock::new(1); /// /// let reader = lock.upgradable_read().await; /// assert_eq!(*reader, 1); /// /// let mut writer = RwLockUpgradableReadGuard::upgrade(reader).await; /// *writer = 2; /// # }) /// ``` #[inline] pub fn upgrade(guard: Self) -> Upgrade<'a, T> { let reader = ManuallyDrop::new(guard); Upgrade::new( // SAFETY: `reader` is an upgradable read guard unsafe { reader.lock.upgrade() }, reader.value, ) } /// Upgrades into a write lock. /// /// # Blocking /// /// This function will block the current thread until it is able to acquire the write lock. /// /// # Examples /// /// ``` /// use async_lock::{RwLock, RwLockUpgradableReadGuard}; /// /// let lock = RwLock::new(1); /// /// let reader = lock.upgradable_read_blocking(); /// assert_eq!(*reader, 1); /// /// let mut writer = RwLockUpgradableReadGuard::upgrade_blocking(reader); /// *writer = 2; /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn upgrade_blocking(guard: Self) -> RwLockWriteGuard<'a, T> { RwLockUpgradableReadGuard::upgrade(guard).wait() } } impl fmt::Debug for RwLockUpgradableReadGuard<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl fmt::Display for RwLockUpgradableReadGuard<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } impl Deref for RwLockUpgradableReadGuard<'_, T> { type Target = T; #[inline] fn deref(&self) -> &T { unsafe { &*self.value } } } /// An owned, reference-counting guard that releases the upgradable read lock when dropped. #[clippy::has_significant_drop] pub struct RwLockUpgradableReadGuardArc { /// We want invariance, so no need for pointer tricks. lock: Arc>, } impl Drop for RwLockUpgradableReadGuardArc { #[inline] fn drop(&mut self) { // SAFETY: we are dropping an upgradable read guard. unsafe { self.lock.raw.upgradable_read_unlock(); } } } unsafe impl Send for RwLockUpgradableReadGuardArc {} unsafe impl Sync for RwLockUpgradableReadGuardArc {} impl fmt::Debug for RwLockUpgradableReadGuardArc { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl fmt::Display for RwLockUpgradableReadGuardArc { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } impl Deref for RwLockUpgradableReadGuardArc { type Target = T; #[inline] fn deref(&self) -> &T { unsafe { &*self.lock.value.get() } } } impl RwLockUpgradableReadGuardArc { /// Downgrades into a regular reader guard. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::{RwLock, RwLockUpgradableReadGuardArc}; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let reader = lock.upgradable_read_arc().await; /// assert_eq!(*reader, 1); /// /// assert!(lock.try_upgradable_read_arc().is_none()); /// /// let reader = RwLockUpgradableReadGuardArc::downgrade(reader); /// /// assert!(lock.try_upgradable_read_arc().is_some()); /// # }) /// ``` #[inline] pub fn downgrade(guard: Self) -> RwLockReadGuardArc { // SAFETY: we hold an upgradable read lock, which we are downgrading. unsafe { guard.lock.raw.downgrade_upgradable_read(); } // SAFETY: we just downgraded to a read lock. unsafe { RwLockReadGuardArc::from_arc(Self::into_arc(guard)) } } } impl RwLockUpgradableReadGuardArc { /// Consumes the lock (without dropping) and returns the underlying `Arc`. #[inline] fn into_arc(guard: Self) -> Arc> { let guard = ManuallyDrop::new(guard); // SAFETY: `guard` is not used after this unsafe { ptr::read(&guard.lock) } } /// Attempts to upgrade into a write lock. /// /// If a write lock could not be acquired at this time, then [`None`] is returned. Otherwise, /// an upgraded guard is returned that releases the write lock when dropped. /// /// This function can only fail if there are other active read locks. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::{RwLock, RwLockUpgradableReadGuardArc}; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let reader = lock.upgradable_read_arc().await; /// assert_eq!(*reader, 1); /// /// let reader2 = lock.read_arc().await; /// let reader = RwLockUpgradableReadGuardArc::try_upgrade(reader).unwrap_err(); /// /// drop(reader2); /// let writer = RwLockUpgradableReadGuardArc::try_upgrade(reader).unwrap(); /// # }) /// ``` #[inline] pub fn try_upgrade(guard: Self) -> Result, Self> { // SAFETY: We hold an upgradable read guard. if unsafe { guard.lock.raw.try_upgrade() } { Ok(RwLockWriteGuardArc { lock: Self::into_arc(guard), }) } else { Err(guard) } } /// Upgrades into a write lock. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::{RwLock, RwLockUpgradableReadGuardArc}; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let reader = lock.upgradable_read_arc().await; /// assert_eq!(*reader, 1); /// /// let mut writer = RwLockUpgradableReadGuardArc::upgrade(reader).await; /// *writer = 2; /// # }) /// ``` #[inline] pub fn upgrade(guard: Self) -> UpgradeArc { // We need to do some ugly lying about lifetimes; // See the comment on the `raw` field of `ArcUpgrade` // for an explanation. // SAFETY: we hold an upgradable read guard. let raw: RawUpgrade<'_> = unsafe { guard.lock.raw.upgrade() }; // SAFETY: see above explanation. let raw: RawUpgrade<'static> = unsafe { mem::transmute(raw) }; unsafe { UpgradeArc::new( ManuallyDrop::new(raw), ManuallyDrop::new(Self::into_arc(guard)), ) } } /// Upgrades into a write lock. /// /// # Blocking /// /// This function will block the current thread until it is able to acquire the write lock. /// /// # Examples /// /// ``` /// use std::sync::Arc; /// use async_lock::{RwLock, RwLockUpgradableReadGuardArc}; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let reader = lock.upgradable_read_arc_blocking(); /// assert_eq!(*reader, 1); /// /// let mut writer = RwLockUpgradableReadGuardArc::upgrade_blocking(reader); /// *writer = 2; /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn upgrade_blocking(guard: Self) -> RwLockWriteGuardArc { RwLockUpgradableReadGuardArc::upgrade(guard).wait() } } /// A guard that releases the write lock when dropped. #[clippy::has_significant_drop] pub struct RwLockWriteGuard<'a, T: ?Sized> { /// Reference to underlying locking implementation. /// Doesn't depend on `T`. /// This guard holds a lock on the witer mutex! lock: &'a RawRwLock, /// Pointer to the value protected by the lock. Invariant in `T`. value: *mut T, } unsafe impl Send for RwLockWriteGuard<'_, T> {} unsafe impl Sync for RwLockWriteGuard<'_, T> {} impl<'a, T: ?Sized> Drop for RwLockWriteGuard<'a, T> { #[inline] fn drop(&mut self) { // SAFETY: we are dropping a write lock unsafe { self.lock.write_unlock(); } } } impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { /// Downgrades into a regular reader guard. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::{RwLock, RwLockWriteGuard}; /// /// let lock = RwLock::new(1); /// /// let mut writer = lock.write().await; /// *writer += 1; /// /// assert!(lock.try_read().is_none()); /// /// let reader = RwLockWriteGuard::downgrade(writer); /// assert_eq!(*reader, 2); /// /// assert!(lock.try_read().is_some()); /// # }) /// ``` #[inline] pub fn downgrade(guard: Self) -> RwLockReadGuard<'a, T> { let write = ManuallyDrop::new(guard); // SAFETY: `write` is a write guard unsafe { write.lock.downgrade_write(); } RwLockReadGuard { lock: write.lock, value: write.value, } } /// Downgrades into an upgradable reader guard. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard}; /// /// let lock = RwLock::new(1); /// /// let mut writer = lock.write().await; /// *writer += 1; /// /// assert!(lock.try_read().is_none()); /// /// let reader = RwLockWriteGuard::downgrade_to_upgradable(writer); /// assert_eq!(*reader, 2); /// /// assert!(lock.try_write().is_none()); /// assert!(lock.try_read().is_some()); /// /// assert!(RwLockUpgradableReadGuard::try_upgrade(reader).is_ok()) /// # }) /// ``` #[inline] pub fn downgrade_to_upgradable(guard: Self) -> RwLockUpgradableReadGuard<'a, T> { let write = ManuallyDrop::new(guard); // SAFETY: `write` is a write guard unsafe { write.lock.downgrade_to_upgradable(); } RwLockUpgradableReadGuard { lock: write.lock, value: write.value, } } } impl fmt::Debug for RwLockWriteGuard<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl fmt::Display for RwLockWriteGuard<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } impl Deref for RwLockWriteGuard<'_, T> { type Target = T; #[inline] fn deref(&self) -> &T { unsafe { &*self.value } } } impl DerefMut for RwLockWriteGuard<'_, T> { #[inline] fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.value } } } /// An owned, reference-counted guard that releases the write lock when dropped. #[clippy::has_significant_drop] pub struct RwLockWriteGuardArc { lock: Arc>, } unsafe impl Send for RwLockWriteGuardArc {} unsafe impl Sync for RwLockWriteGuardArc {} impl Drop for RwLockWriteGuardArc { #[inline] fn drop(&mut self) { // SAFETY: we are dropping a write lock. unsafe { self.lock.raw.write_unlock(); } } } impl RwLockWriteGuardArc { /// Downgrades into a regular reader guard. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::{RwLock, RwLockWriteGuardArc}; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let mut writer = lock.write_arc().await; /// *writer += 1; /// /// assert!(lock.try_read_arc().is_none()); /// /// let reader = RwLockWriteGuardArc::downgrade(writer); /// assert_eq!(*reader, 2); /// /// assert!(lock.try_read_arc().is_some()); /// # }) /// ``` #[inline] pub fn downgrade(guard: Self) -> RwLockReadGuardArc { // SAFETY: `write` is a write guard unsafe { guard.lock.raw.downgrade_write(); } // SAFETY: we just downgraded to a read lock unsafe { RwLockReadGuardArc::from_arc(Self::into_arc(guard)) } } } impl RwLockWriteGuardArc { /// Consumes the lock (without dropping) and returns the underlying `Arc`. #[inline] fn into_arc(guard: Self) -> Arc> { let guard = ManuallyDrop::new(guard); // SAFETY: `guard` is not used after this unsafe { ptr::read(&guard.lock) } } /// Downgrades into an upgradable reader guard. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use std::sync::Arc; /// use async_lock::{RwLock, RwLockUpgradableReadGuardArc, RwLockWriteGuardArc}; /// /// let lock = Arc::new(RwLock::new(1)); /// /// let mut writer = lock.write_arc().await; /// *writer += 1; /// /// assert!(lock.try_read_arc().is_none()); /// /// let reader = RwLockWriteGuardArc::downgrade_to_upgradable(writer); /// assert_eq!(*reader, 2); /// /// assert!(lock.try_write_arc().is_none()); /// assert!(lock.try_read_arc().is_some()); /// /// assert!(RwLockUpgradableReadGuardArc::try_upgrade(reader).is_ok()) /// # }) /// ``` #[inline] pub fn downgrade_to_upgradable(guard: Self) -> RwLockUpgradableReadGuardArc { // SAFETY: `guard` is a write guard unsafe { guard.lock.raw.downgrade_to_upgradable(); } RwLockUpgradableReadGuardArc { lock: Self::into_arc(guard), } } } impl fmt::Debug for RwLockWriteGuardArc { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl fmt::Display for RwLockWriteGuardArc { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } impl Deref for RwLockWriteGuardArc { type Target = T; #[inline] fn deref(&self) -> &T { unsafe { &*self.lock.value.get() } } } impl DerefMut for RwLockWriteGuardArc { #[inline] fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.lock.value.get() } } } async-lock-3.4.0/src/rwlock/000077500000000000000000000000001462663103300156555ustar00rootroot00000000000000async-lock-3.4.0/src/rwlock/futures.rs000066400000000000000000000350421462663103300177240ustar00rootroot00000000000000use core::fmt; use core::mem::ManuallyDrop; use core::pin::Pin; use core::task::Poll; use alloc::sync::Arc; use super::raw::{RawRead, RawUpgradableRead, RawUpgrade, RawWrite}; use super::{ RwLock, RwLockReadGuard, RwLockReadGuardArc, RwLockUpgradableReadGuard, RwLockUpgradableReadGuardArc, RwLockWriteGuard, RwLockWriteGuardArc, }; use event_listener_strategy::{easy_wrapper, EventListenerFuture, Strategy}; easy_wrapper! { /// The future returned by [`RwLock::read`]. pub struct Read<'a, T: ?Sized>(ReadInner<'a, T> => RwLockReadGuard<'a, T>); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { /// The future returned by [`RwLock::read`]. struct ReadInner<'a, T: ?Sized> { // Raw read lock acquisition future, doesn't depend on `T`. #[pin] pub(super) raw: RawRead<'a>, // Pointer to the value protected by the lock. Covariant in `T`. pub(super) value: *const T, } } unsafe impl Send for ReadInner<'_, T> {} unsafe impl Sync for ReadInner<'_, T> {} impl<'x, T: ?Sized> Read<'x, T> { #[inline] pub(super) fn new(raw: RawRead<'x>, value: *const T) -> Self { Self::_new(ReadInner { raw, value }) } } impl fmt::Debug for Read<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Read { .. }") } } impl<'a, T: ?Sized> EventListenerFuture for ReadInner<'a, T> { type Output = RwLockReadGuard<'a, T>; #[inline] fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let mut this = self.project(); ready!(this.raw.as_mut().poll_with_strategy(strategy, cx)); Poll::Ready(RwLockReadGuard { lock: this.raw.lock, value: *this.value, }) } } easy_wrapper! { /// The future returned by [`RwLock::read_arc`]. pub struct ReadArc<'a, T>(ReadArcInner<'a, T> => RwLockReadGuardArc); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { /// The future returned by [`RwLock::read_arc`]. struct ReadArcInner<'a, T> { // Raw read lock acquisition future, doesn't depend on `T`. #[pin] pub(super) raw: RawRead<'a>, // FIXME: Could be covariant in T pub(super) lock: &'a Arc>, } } unsafe impl Send for ReadArcInner<'_, T> {} unsafe impl Sync for ReadArcInner<'_, T> {} impl<'x, T> ReadArc<'x, T> { #[inline] pub(super) fn new(raw: RawRead<'x>, lock: &'x Arc>) -> Self { Self::_new(ReadArcInner { raw, lock }) } } impl fmt::Debug for ReadArc<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("ReadArc { .. }") } } impl<'a, T> EventListenerFuture for ReadArcInner<'a, T> { type Output = RwLockReadGuardArc; #[inline] fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let mut this = self.project(); ready!(this.raw.as_mut().poll_with_strategy(strategy, cx)); // SAFETY: we just acquired a read lock Poll::Ready(unsafe { RwLockReadGuardArc::from_arc(this.lock.clone()) }) } } easy_wrapper! { /// The future returned by [`RwLock::upgradable_read`]. pub struct UpgradableRead<'a, T: ?Sized>( UpgradableReadInner<'a, T> => RwLockUpgradableReadGuard<'a, T> ); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { /// The future returned by [`RwLock::upgradable_read`]. struct UpgradableReadInner<'a, T: ?Sized> { // Raw upgradable read lock acquisition future, doesn't depend on `T`. #[pin] pub(super) raw: RawUpgradableRead<'a>, // Pointer to the value protected by the lock. Invariant in `T` // as the upgradable lock could provide write access. pub(super) value: *mut T, } } unsafe impl Send for UpgradableReadInner<'_, T> {} unsafe impl Sync for UpgradableReadInner<'_, T> {} impl<'x, T: ?Sized> UpgradableRead<'x, T> { #[inline] pub(super) fn new(raw: RawUpgradableRead<'x>, value: *mut T) -> Self { Self::_new(UpgradableReadInner { raw, value }) } } impl fmt::Debug for UpgradableRead<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("UpgradableRead { .. }") } } impl<'a, T: ?Sized> EventListenerFuture for UpgradableReadInner<'a, T> { type Output = RwLockUpgradableReadGuard<'a, T>; #[inline] fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let mut this = self.project(); ready!(this.raw.as_mut().poll_with_strategy(strategy, cx)); Poll::Ready(RwLockUpgradableReadGuard { lock: this.raw.lock, value: *this.value, }) } } easy_wrapper! { /// The future returned by [`RwLock::upgradable_read_arc`]. pub struct UpgradableReadArc<'a, T: ?Sized>( UpgradableReadArcInner<'a, T> => RwLockUpgradableReadGuardArc ); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { /// The future returned by [`RwLock::upgradable_read_arc`]. struct UpgradableReadArcInner<'a, T: ?Sized> { // Raw upgradable read lock acquisition future, doesn't depend on `T`. #[pin] pub(super) raw: RawUpgradableRead<'a>, pub(super) lock: &'a Arc>, } } unsafe impl Send for UpgradableReadArcInner<'_, T> {} unsafe impl Sync for UpgradableReadArcInner<'_, T> {} impl<'x, T: ?Sized> UpgradableReadArc<'x, T> { #[inline] pub(super) fn new(raw: RawUpgradableRead<'x>, lock: &'x Arc>) -> Self { Self::_new(UpgradableReadArcInner { raw, lock }) } } impl fmt::Debug for UpgradableReadArc<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("UpgradableReadArc { .. }") } } impl<'a, T: ?Sized> EventListenerFuture for UpgradableReadArcInner<'a, T> { type Output = RwLockUpgradableReadGuardArc; #[inline] fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let mut this = self.project(); ready!(this.raw.as_mut().poll_with_strategy(strategy, cx)); Poll::Ready(RwLockUpgradableReadGuardArc { lock: this.lock.clone(), }) } } easy_wrapper! { /// The future returned by [`RwLock::write`]. pub struct Write<'a, T: ?Sized>(WriteInner<'a, T> => RwLockWriteGuard<'a, T>); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { /// The future returned by [`RwLock::write`]. struct WriteInner<'a, T: ?Sized> { // Raw write lock acquisition future, doesn't depend on `T`. #[pin] pub(super) raw: RawWrite<'a>, // Pointer to the value protected by the lock. Invariant in `T`. pub(super) value: *mut T, } } unsafe impl Send for WriteInner<'_, T> {} unsafe impl Sync for WriteInner<'_, T> {} impl<'x, T: ?Sized> Write<'x, T> { #[inline] pub(super) fn new(raw: RawWrite<'x>, value: *mut T) -> Self { Self::_new(WriteInner { raw, value }) } } impl fmt::Debug for Write<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Write { .. }") } } impl<'a, T: ?Sized> EventListenerFuture for WriteInner<'a, T> { type Output = RwLockWriteGuard<'a, T>; #[inline] fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let mut this = self.project(); ready!(this.raw.as_mut().poll_with_strategy(strategy, cx)); Poll::Ready(RwLockWriteGuard { lock: this.raw.lock, value: *this.value, }) } } easy_wrapper! { /// The future returned by [`RwLock::write_arc`]. pub struct WriteArc<'a, T: ?Sized>(WriteArcInner<'a, T> => RwLockWriteGuardArc); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { /// The future returned by [`RwLock::write_arc`]. struct WriteArcInner<'a, T: ?Sized> { // Raw write lock acquisition future, doesn't depend on `T`. #[pin] pub(super) raw: RawWrite<'a>, pub(super) lock: &'a Arc>, } } unsafe impl Send for WriteArcInner<'_, T> {} unsafe impl Sync for WriteArcInner<'_, T> {} impl<'x, T: ?Sized> WriteArc<'x, T> { #[inline] pub(super) fn new(raw: RawWrite<'x>, lock: &'x Arc>) -> Self { Self::_new(WriteArcInner { raw, lock }) } } impl fmt::Debug for WriteArc<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("WriteArc { .. }") } } impl<'a, T: ?Sized> EventListenerFuture for WriteArcInner<'a, T> { type Output = RwLockWriteGuardArc; #[inline] fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let mut this = self.project(); ready!(this.raw.as_mut().poll_with_strategy(strategy, cx)); Poll::Ready(RwLockWriteGuardArc { lock: this.lock.clone(), }) } } easy_wrapper! { /// The future returned by [`RwLockUpgradableReadGuard::upgrade`]. pub struct Upgrade<'a, T: ?Sized>(UpgradeInner<'a, T> => RwLockWriteGuard<'a, T>); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { /// The future returned by [`RwLockUpgradableReadGuard::upgrade`]. struct UpgradeInner<'a, T: ?Sized> { // Raw read lock upgrade future, doesn't depend on `T`. #[pin] pub(super) raw: RawUpgrade<'a>, // Pointer to the value protected by the lock. Invariant in `T`. pub(super) value: *mut T, } } unsafe impl Send for UpgradeInner<'_, T> {} unsafe impl Sync for UpgradeInner<'_, T> {} impl<'x, T: ?Sized> Upgrade<'x, T> { #[inline] pub(super) fn new(raw: RawUpgrade<'x>, value: *mut T) -> Self { Self::_new(UpgradeInner { raw, value }) } } impl fmt::Debug for Upgrade<'_, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Upgrade").finish() } } impl<'a, T: ?Sized> EventListenerFuture for UpgradeInner<'a, T> { type Output = RwLockWriteGuard<'a, T>; #[inline] fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let mut this = self.project(); let lock = ready!(this.raw.as_mut().poll_with_strategy(strategy, cx)); Poll::Ready(RwLockWriteGuard { lock, value: *this.value, }) } } easy_wrapper! { /// The future returned by [`RwLockUpgradableReadGuardArc::upgrade`]. pub struct UpgradeArc(UpgradeArcInner => RwLockWriteGuardArc); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { /// The future returned by [`RwLockUpgradableReadGuardArc::upgrade`]. struct UpgradeArcInner { // Raw read lock upgrade future, doesn't depend on `T`. // `'static` is a lie, this field is actually referencing the // `Arc` data. But since this struct also stores said `Arc`, we know // this value will be alive as long as the struct is. // // Yes, one field of the `ArcUpgrade` struct is referencing another. // Such self-references are usually not sound without pinning. // However, in this case, there is an indirection via the heap; // moving the `ArcUpgrade` won't move the heap allocation of the `Arc`, // so the reference inside `RawUpgrade` isn't invalidated. #[pin] pub(super) raw: ManuallyDrop>, // Pointer to the value protected by the lock. Invariant in `T`. pub(super) lock: ManuallyDrop>>, } impl PinnedDrop for UpgradeArcInner { fn drop(this: Pin<&mut Self>) { let this = this.project(); let is_ready = this.raw.is_ready(); // SAFETY: The drop impl for raw assumes that it is pinned. unsafe { ManuallyDrop::drop(this.raw.get_unchecked_mut()); } if !is_ready { // SAFETY: we drop the `Arc` (decrementing the reference count) // only if this future was cancelled before returning an // upgraded lock. unsafe { ManuallyDrop::drop(this.lock); }; } } } } impl UpgradeArc { #[inline] pub(super) unsafe fn new( raw: ManuallyDrop>, lock: ManuallyDrop>>, ) -> Self { Self::_new(UpgradeArcInner { raw, lock }) } } impl fmt::Debug for UpgradeArc { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ArcUpgrade").finish() } } impl EventListenerFuture for UpgradeArcInner { type Output = RwLockWriteGuardArc; #[inline] fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let this = self.project(); unsafe { // SAFETY: Practically, this is a pin projection. ready!(Pin::new_unchecked(&mut **this.raw.get_unchecked_mut()) .poll_with_strategy(strategy, cx)); } Poll::Ready(RwLockWriteGuardArc { lock: unsafe { ManuallyDrop::take(this.lock) }, }) } } async-lock-3.4.0/src/rwlock/raw.rs000066400000000000000000000424271462663103300170250ustar00rootroot00000000000000//! Raw, unsafe reader-writer locking implementation, //! doesn't depend on the data protected by the lock. //! [`RwLock`](super::RwLock) is implemented in terms of this. //! //! Splitting the implementation this way allows instantiating //! the locking code only once, and also lets us make //! [`RwLockReadGuard`](super::RwLockReadGuard) covariant in `T`. use core::marker::PhantomPinned; use core::mem::forget; use core::pin::Pin; use core::task::Poll; use crate::sync::atomic::{AtomicUsize, Ordering}; use event_listener::{Event, EventListener}; use event_listener_strategy::{EventListenerFuture, Strategy}; use crate::futures::Lock; use crate::Mutex; const WRITER_BIT: usize = 1; const ONE_READER: usize = 2; /// A "raw" RwLock that doesn't hold any data. pub(super) struct RawRwLock { /// Acquired by the writer. mutex: Mutex<()>, /// Event triggered when the last reader is dropped. no_readers: Event, /// Event triggered when the writer is dropped. no_writer: Event, /// Current state of the lock. /// /// The least significant bit (`WRITER_BIT`) is set to 1 when a writer is holding the lock or /// trying to acquire it. /// /// The upper bits contain the number of currently active readers. Each active reader /// increments the state by `ONE_READER`. state: AtomicUsize, } impl RawRwLock { const_fn! { const_if: #[cfg(not(loom))]; #[inline] pub(super) const fn new() -> Self { RawRwLock { mutex: Mutex::new(()), no_readers: Event::new(), no_writer: Event::new(), state: AtomicUsize::new(0), } } } /// Returns `true` iff a read lock was successfully acquired. pub(super) fn try_read(&self) -> bool { let mut state = self.state.load(Ordering::Acquire); loop { // If there's a writer holding the lock or attempting to acquire it, we cannot acquire // a read lock here. if state & WRITER_BIT != 0 { return false; } // Make sure the number of readers doesn't overflow. if state > core::isize::MAX as usize { crate::abort(); } // Increment the number of readers. match self.state.compare_exchange( state, state + ONE_READER, Ordering::AcqRel, Ordering::Acquire, ) { Ok(_) => return true, Err(s) => state = s, } } } #[inline] pub(super) fn read(&self) -> RawRead<'_> { RawRead { lock: self, state: self.state.load(Ordering::Acquire), listener: None, _pin: PhantomPinned, } } /// Returns `true` iff an upgradable read lock was successfully acquired. pub(super) fn try_upgradable_read(&self) -> bool { // First try grabbing the mutex. let lock = if let Some(lock) = self.mutex.try_lock() { lock } else { return false; }; forget(lock); let mut state = self.state.load(Ordering::Acquire); // Make sure the number of readers doesn't overflow. if state > core::isize::MAX as usize { crate::abort(); } // Increment the number of readers. loop { match self.state.compare_exchange( state, state + ONE_READER, Ordering::AcqRel, Ordering::Acquire, ) { Ok(_) => return true, Err(s) => state = s, } } } #[inline] pub(super) fn upgradable_read(&self) -> RawUpgradableRead<'_> { RawUpgradableRead { lock: self, acquire: self.mutex.lock(), } } /// Returs `true` iff a write lock was successfully acquired. pub(super) fn try_write(&self) -> bool { // First try grabbing the mutex. let lock = if let Some(lock) = self.mutex.try_lock() { lock } else { return false; }; // If there are no readers, grab the write lock. if self .state .compare_exchange(0, WRITER_BIT, Ordering::AcqRel, Ordering::Acquire) .is_ok() { forget(lock); true } else { drop(lock); false } } #[inline] pub(super) fn write(&self) -> RawWrite<'_> { RawWrite { lock: self, no_readers: None, state: WriteState::Acquiring { lock: self.mutex.lock(), }, } } /// Returns `true` iff a the upgradable read lock was successfully upgraded to a write lock. /// /// # Safety /// /// Caller must hold an upgradable read lock. /// This will attempt to upgrade it to a write lock. pub(super) unsafe fn try_upgrade(&self) -> bool { self.state .compare_exchange(ONE_READER, WRITER_BIT, Ordering::AcqRel, Ordering::Acquire) .is_ok() } /// # Safety /// /// Caller must hold an upgradable read lock. /// This will upgrade it to a write lock. pub(super) unsafe fn upgrade(&self) -> RawUpgrade<'_> { // Set `WRITER_BIT` and decrement the number of readers at the same time. self.state .fetch_sub(ONE_READER - WRITER_BIT, Ordering::SeqCst); RawUpgrade { lock: Some(self), listener: None, _pin: PhantomPinned, } } /// # Safety /// /// Caller must hold an upgradable read lock. /// This will downgrade it to a stadard read lock. #[inline] pub(super) unsafe fn downgrade_upgradable_read(&self) { self.mutex.unlock_unchecked(); } /// # Safety /// /// Caller must hold a write lock. /// This will downgrade it to a read lock. pub(super) unsafe fn downgrade_write(&self) { // Atomically downgrade state. self.state .fetch_add(ONE_READER - WRITER_BIT, Ordering::SeqCst); // Release the writer mutex. self.mutex.unlock_unchecked(); // Trigger the "no writer" event. self.no_writer.notify(1); } /// # Safety /// /// Caller must hold a write lock. /// This will downgrade it to an upgradable read lock. pub(super) unsafe fn downgrade_to_upgradable(&self) { // Atomically downgrade state. self.state .fetch_add(ONE_READER - WRITER_BIT, Ordering::SeqCst); } /// # Safety /// /// Caller must hold a read lock . /// This will unlock that lock. pub(super) unsafe fn read_unlock(&self) { // Decrement the number of readers. if self.state.fetch_sub(ONE_READER, Ordering::SeqCst) & !WRITER_BIT == ONE_READER { // If this was the last reader, trigger the "no readers" event. self.no_readers.notify(1); } } /// # Safety /// /// Caller must hold an upgradable read lock. /// This will unlock that lock. pub(super) unsafe fn upgradable_read_unlock(&self) { // Decrement the number of readers. if self.state.fetch_sub(ONE_READER, Ordering::SeqCst) & !WRITER_BIT == ONE_READER { // If this was the last reader, trigger the "no readers" event. self.no_readers.notify(1); } // SAFETY: upgradable read guards acquire the writer mutex upon creation. self.mutex.unlock_unchecked(); } /// # Safety /// /// Caller must hold a write lock. /// This will unlock that lock. pub(super) unsafe fn write_unlock(&self) { // Unset `WRITER_BIT`. self.state.fetch_and(!WRITER_BIT, Ordering::SeqCst); // Trigger the "no writer" event. self.no_writer.notify(1); // Release the writer lock. // SAFETY: `RwLockWriteGuard` always holds a lock on writer mutex. self.mutex.unlock_unchecked(); } } pin_project_lite::pin_project! { /// The future returned by [`RawRwLock::read`]. pub(super) struct RawRead<'a> { // The lock that is being acquired. pub(super) lock: &'a RawRwLock, // The last-observed state of the lock. state: usize, // The listener for the "no writers" event. listener: Option, // Making this type `!Unpin` enables future optimizations. #[pin] _pin: PhantomPinned } } impl<'a> EventListenerFuture for RawRead<'a> { type Output = (); fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll<()> { let this = self.project(); loop { if *this.state & WRITER_BIT == 0 { // Make sure the number of readers doesn't overflow. if *this.state > core::isize::MAX as usize { crate::abort(); } // If nobody is holding a write lock or attempting to acquire it, increment the // number of readers. match this.lock.state.compare_exchange( *this.state, *this.state + ONE_READER, Ordering::AcqRel, Ordering::Acquire, ) { Ok(_) => return Poll::Ready(()), Err(s) => *this.state = s, } } else { // Start listening for "no writer" events. let load_ordering = if this.listener.is_none() { *this.listener = Some(this.lock.no_writer.listen()); // Make sure there really is no writer. Ordering::SeqCst } else { // Wait for the writer to finish. ready!(strategy.poll(this.listener, cx)); // Notify the next reader waiting in list. this.lock.no_writer.notify(1); // Check the state again. Ordering::Acquire }; // Reload the state. *this.state = this.lock.state.load(load_ordering); } } } } pin_project_lite::pin_project! { /// The future returned by [`RawRwLock::upgradable_read`]. pub(super) struct RawUpgradableRead<'a> { // The lock that is being acquired. pub(super) lock: &'a RawRwLock, // The mutex we are trying to acquire. #[pin] acquire: Lock<'a, ()>, } } impl<'a> EventListenerFuture for RawUpgradableRead<'a> { type Output = (); fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll<()> { let this = self.project(); // Acquire the mutex. let mutex_guard = ready!(this.acquire.poll_with_strategy(strategy, cx)); forget(mutex_guard); // Load the current state. let mut state = this.lock.state.load(Ordering::Acquire); // Make sure the number of readers doesn't overflow. if state > core::isize::MAX as usize { crate::abort(); } // Increment the number of readers. loop { match this.lock.state.compare_exchange( state, state + ONE_READER, Ordering::AcqRel, Ordering::Acquire, ) { Ok(_) => { return Poll::Ready(()); } Err(s) => state = s, } } } } pin_project_lite::pin_project! { /// The future returned by [`RawRwLock::write`]. pub(super) struct RawWrite<'a> { // The lock that is being acquired. pub(super) lock: &'a RawRwLock, // Our listener for the "no readers" event. no_readers: Option, // Current state fof this future. #[pin] state: WriteState<'a>, } impl PinnedDrop for RawWrite<'_> { fn drop(this: Pin<&mut Self>) { let this = this.project(); if matches!(this.state.project(), WriteStateProj::WaitingReaders) { // Safety: we hold a write lock, more or less. unsafe { this.lock.write_unlock(); } } } } } pin_project_lite::pin_project! { #[project = WriteStateProj] #[project_replace = WriteStateProjReplace] enum WriteState<'a> { // We are currently acquiring the inner mutex. Acquiring { #[pin] lock: Lock<'a, ()> }, // We are currently waiting for readers to finish. WaitingReaders, // The future has completed. Acquired, } } impl<'a> EventListenerFuture for RawWrite<'a> { type Output = (); fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll<()> { let mut this = self.project(); loop { match this.state.as_mut().project() { WriteStateProj::Acquiring { lock } => { // First grab the mutex. let mutex_guard = ready!(lock.poll_with_strategy(strategy, cx)); forget(mutex_guard); // Set `WRITER_BIT` and create a guard that unsets it in case this future is canceled. let new_state = this.lock.state.fetch_or(WRITER_BIT, Ordering::SeqCst); // If we just acquired the lock, return. if new_state == WRITER_BIT { this.state.as_mut().set(WriteState::Acquired); return Poll::Ready(()); } // Start waiting for the readers to finish. *this.no_readers = Some(this.lock.no_readers.listen()); this.state.as_mut().set(WriteState::WaitingReaders); } WriteStateProj::WaitingReaders => { let load_ordering = if this.no_readers.is_some() { Ordering::Acquire } else { Ordering::SeqCst }; // Check the state again. if this.lock.state.load(load_ordering) == WRITER_BIT { // We are the only ones holding the lock, return `Ready`. this.state.as_mut().set(WriteState::Acquired); return Poll::Ready(()); } // Wait for the readers to finish. if this.no_readers.is_none() { // Register a listener. *this.no_readers = Some(this.lock.no_readers.listen()); } else { // Wait for the readers to finish. ready!(strategy.poll(this.no_readers, cx)); }; } WriteStateProj::Acquired => panic!("Write lock already acquired"), } } } } pin_project_lite::pin_project! { /// The future returned by [`RawRwLock::upgrade`]. pub(super) struct RawUpgrade<'a> { lock: Option<&'a RawRwLock>, // The event listener we are waiting on. listener: Option, // Keeping this future `!Unpin` enables future optimizations. #[pin] _pin: PhantomPinned } impl PinnedDrop for RawUpgrade<'_> { fn drop(this: Pin<&mut Self>) { let this = this.project(); if let Some(lock) = this.lock { // SAFETY: we are dropping the future that would give us a write lock, // so we don't need said lock anymore. unsafe { lock.write_unlock(); } } } } } impl<'a> EventListenerFuture for RawUpgrade<'a> { type Output = &'a RawRwLock; fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll<&'a RawRwLock> { let this = self.project(); let lock = this.lock.expect("cannot poll future after completion"); // If there are readers, we need to wait for them to finish. loop { let load_ordering = if this.listener.is_some() { Ordering::Acquire } else { Ordering::SeqCst }; // See if the number of readers is zero. let state = lock.state.load(load_ordering); if state == WRITER_BIT { break; } // If there are readers, wait for them to finish. if this.listener.is_none() { // Start listening for "no readers" events. *this.listener = Some(lock.no_readers.listen()); } else { // Wait for the readers to finish. ready!(strategy.poll(this.listener, cx)); }; } // We are done. Poll::Ready(this.lock.take().unwrap()) } } impl<'a> RawUpgrade<'a> { /// Whether the future returned `Poll::Ready(..)` at some point. #[inline] pub(super) fn is_ready(&self) -> bool { self.lock.is_none() } } async-lock-3.4.0/src/semaphore.rs000066400000000000000000000260131462663103300167070ustar00rootroot00000000000000use core::fmt; use core::marker::PhantomPinned; use core::mem; use core::pin::Pin; use core::task::Poll; use crate::sync::atomic::{AtomicUsize, Ordering}; use alloc::sync::Arc; use event_listener::{Event, EventListener}; use event_listener_strategy::{easy_wrapper, EventListenerFuture, Strategy}; /// A counter for limiting the number of concurrent operations. #[derive(Debug)] pub struct Semaphore { count: AtomicUsize, event: Event, } impl Semaphore { const_fn! { const_if: #[cfg(not(loom))]; /// Creates a new semaphore with a limit of `n` concurrent operations. /// /// # Examples /// /// ``` /// use async_lock::Semaphore; /// /// let s = Semaphore::new(5); /// ``` pub const fn new(n: usize) -> Semaphore { Semaphore { count: AtomicUsize::new(n), event: Event::new(), } } } /// Attempts to get a permit for a concurrent operation. /// /// If the permit could not be acquired at this time, then [`None`] is returned. Otherwise, a /// guard is returned that releases the mutex when dropped. /// /// # Examples /// /// ``` /// use async_lock::Semaphore; /// /// let s = Semaphore::new(2); /// /// let g1 = s.try_acquire().unwrap(); /// let g2 = s.try_acquire().unwrap(); /// /// assert!(s.try_acquire().is_none()); /// drop(g2); /// assert!(s.try_acquire().is_some()); /// ``` pub fn try_acquire(&self) -> Option> { let mut count = self.count.load(Ordering::Acquire); loop { if count == 0 { return None; } match self.count.compare_exchange_weak( count, count - 1, Ordering::AcqRel, Ordering::Acquire, ) { Ok(_) => return Some(SemaphoreGuard(self)), Err(c) => count = c, } } } /// Waits for a permit for a concurrent operation. /// /// Returns a guard that releases the permit when dropped. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::Semaphore; /// /// let s = Semaphore::new(2); /// let guard = s.acquire().await; /// # }); /// ``` pub fn acquire(&self) -> Acquire<'_> { Acquire::_new(AcquireInner { semaphore: self, listener: None, _pin: PhantomPinned, }) } /// Waits for a permit for a concurrent operation. /// /// Returns a guard that releases the permit when dropped. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`acquire`][Semaphore::acquire] method, /// this method will block the current thread until the permit is acquired. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a semaphore can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use async_lock::Semaphore; /// /// let s = Semaphore::new(2); /// let guard = s.acquire_blocking(); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn acquire_blocking(&self) -> SemaphoreGuard<'_> { self.acquire().wait() } /// Attempts to get an owned permit for a concurrent operation. /// /// If the permit could not be acquired at this time, then [`None`] is returned. Otherwise, an /// owned guard is returned that releases the mutex when dropped. /// /// # Examples /// /// ``` /// use async_lock::Semaphore; /// use std::sync::Arc; /// /// let s = Arc::new(Semaphore::new(2)); /// /// let g1 = s.try_acquire_arc().unwrap(); /// let g2 = s.try_acquire_arc().unwrap(); /// /// assert!(s.try_acquire_arc().is_none()); /// drop(g2); /// assert!(s.try_acquire_arc().is_some()); /// ``` pub fn try_acquire_arc(self: &Arc) -> Option { let mut count = self.count.load(Ordering::Acquire); loop { if count == 0 { return None; } match self.count.compare_exchange_weak( count, count - 1, Ordering::AcqRel, Ordering::Acquire, ) { Ok(_) => return Some(SemaphoreGuardArc(Some(self.clone()))), Err(c) => count = c, } } } /// Waits for an owned permit for a concurrent operation. /// /// Returns a guard that releases the permit when dropped. /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_lock::Semaphore; /// use std::sync::Arc; /// /// let s = Arc::new(Semaphore::new(2)); /// let guard = s.acquire_arc().await; /// # }); /// ``` pub fn acquire_arc(self: &Arc) -> AcquireArc { AcquireArc::_new(AcquireArcInner { semaphore: self.clone(), listener: None, _pin: PhantomPinned, }) } /// Waits for an owned permit for a concurrent operation. /// /// Returns a guard that releases the permit when dropped. /// /// # Blocking /// /// Rather than using asynchronous waiting, like the [`acquire_arc`][Semaphore::acquire_arc] method, /// this method will block the current thread until the permit is acquired. /// /// This method should not be used in an asynchronous context. It is intended to be /// used in a way that a semaphore can be used in both asynchronous and synchronous contexts. /// Calling this method in an asynchronous context may result in a deadlock. /// /// # Examples /// /// ``` /// use std::sync::Arc; /// use async_lock::Semaphore; /// /// let s = Arc::new(Semaphore::new(2)); /// let guard = s.acquire_arc_blocking(); /// ``` #[cfg(all(feature = "std", not(target_family = "wasm")))] #[inline] pub fn acquire_arc_blocking(self: &Arc) -> SemaphoreGuardArc { self.acquire_arc().wait() } /// Adds `n` additional permits to the semaphore. /// /// # Examples /// /// ``` /// use async_lock::Semaphore; /// /// # futures_lite::future::block_on(async { /// let s = Semaphore::new(1); /// /// let _guard = s.acquire().await; /// assert!(s.try_acquire().is_none()); /// /// s.add_permits(2); /// /// let _guard = s.acquire().await; /// let _guard = s.acquire().await; /// # }); /// ``` pub fn add_permits(&self, n: usize) { self.count.fetch_add(n, Ordering::AcqRel); self.event.notify(n); } } easy_wrapper! { /// The future returned by [`Semaphore::acquire`]. pub struct Acquire<'a>(AcquireInner<'a> => SemaphoreGuard<'a>); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { struct AcquireInner<'a> { // The semaphore being acquired. semaphore: &'a Semaphore, // The listener waiting on the semaphore. listener: Option, // Keeping this future `!Unpin` enables future optimizations. #[pin] _pin: PhantomPinned } } impl fmt::Debug for Acquire<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Acquire { .. }") } } impl<'a> EventListenerFuture for AcquireInner<'a> { type Output = SemaphoreGuard<'a>; fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let this = self.project(); loop { match this.semaphore.try_acquire() { Some(guard) => return Poll::Ready(guard), None => { // Wait on the listener. if this.listener.is_none() { *this.listener = Some(this.semaphore.event.listen()); } else { ready!(strategy.poll(this.listener, cx)); } } } } } } easy_wrapper! { /// The future returned by [`Semaphore::acquire_arc`]. pub struct AcquireArc(AcquireArcInner => SemaphoreGuardArc); #[cfg(all(feature = "std", not(target_family = "wasm")))] pub(crate) wait(); } pin_project_lite::pin_project! { struct AcquireArcInner { // The semaphore being acquired. semaphore: Arc, // The listener waiting on the semaphore. listener: Option, // Keeping this future `!Unpin` enables future optimizations. #[pin] _pin: PhantomPinned } } impl fmt::Debug for AcquireArc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("AcquireArc { .. }") } } impl EventListenerFuture for AcquireArcInner { type Output = SemaphoreGuardArc; fn poll_with_strategy<'x, S: Strategy<'x>>( self: Pin<&mut Self>, strategy: &mut S, cx: &mut S::Context, ) -> Poll { let this = self.project(); loop { match this.semaphore.try_acquire_arc() { Some(guard) => return Poll::Ready(guard), None => { // Wait on the listener. if this.listener.is_none() { *this.listener = Some(this.semaphore.event.listen()); } else { ready!(strategy.poll(this.listener, cx)); } } } } } } /// A guard that releases the acquired permit. #[clippy::has_significant_drop] #[derive(Debug)] pub struct SemaphoreGuard<'a>(&'a Semaphore); impl SemaphoreGuard<'_> { /// Drops the guard _without_ releasing the acquired permit. #[inline] pub fn forget(self) { mem::forget(self); } } impl Drop for SemaphoreGuard<'_> { fn drop(&mut self) { self.0.count.fetch_add(1, Ordering::AcqRel); self.0.event.notify(1); } } /// An owned guard that releases the acquired permit. #[clippy::has_significant_drop] #[derive(Debug)] pub struct SemaphoreGuardArc(Option>); impl SemaphoreGuardArc { /// Drops the guard _without_ releasing the acquired permit. /// (Will still decrement the `Arc` reference count.) #[inline] pub fn forget(mut self) { // Drop the inner `Arc` in order to decrement the reference count. // FIXME: get rid of the `Option` once RFC 3466 or equivalent becomes available. drop(self.0.take()); mem::forget(self); } } impl Drop for SemaphoreGuardArc { fn drop(&mut self) { let opt = self.0.take().unwrap(); opt.count.fetch_add(1, Ordering::AcqRel); opt.event.notify(1); } } async-lock-3.4.0/tests/000077500000000000000000000000001462663103300147275ustar00rootroot00000000000000async-lock-3.4.0/tests/barrier.rs000066400000000000000000000046751462663103300167370ustar00rootroot00000000000000use std::sync::Arc; use std::thread; use async_lock::Barrier; use futures_lite::future; #[test] #[cfg_attr(miri, ignore)] fn smoke() { future::block_on(async move { const N: usize = 10; let barrier = Arc::new(Barrier::new(N)); for _ in 0..10 { let (tx, rx) = flume::unbounded(); for _ in 0..N - 1 { let c = barrier.clone(); let tx = tx.clone(); thread::spawn(move || { future::block_on(async move { let res = c.wait().await; tx.send_async(res.is_leader()).await.unwrap(); }) }); } // At this point, all spawned threads should be blocked, // so we shouldn't get anything from the cahnnel. let res = rx.try_recv(); assert!(res.is_err()); let mut leader_found = barrier.wait().await.is_leader(); // Now, the barrier is cleared and we should get data. for _ in 0..N - 1 { if rx.recv_async().await.unwrap() { assert!(!leader_found); leader_found = true; } } assert!(leader_found); } }); } #[cfg(all(feature = "std", not(target_family = "wasm")))] #[test] #[cfg_attr(miri, ignore)] fn smoke_blocking() { future::block_on(async move { const N: usize = 10; let barrier = Arc::new(Barrier::new(N)); for _ in 0..10 { let (tx, rx) = flume::unbounded(); for _ in 0..N - 1 { let c = barrier.clone(); let tx = tx.clone(); thread::spawn(move || { let res = c.wait_blocking(); tx.send(res.is_leader()).unwrap(); }); } // At this point, all spawned threads should be blocked, // so we shouldn't get anything from the cahnnel. let res = rx.try_recv(); assert!(res.is_err()); let mut leader_found = barrier.wait_blocking().is_leader(); // Now, the barrier is cleared and we should get data. for _ in 0..N - 1 { if rx.recv_async().await.unwrap() { assert!(!leader_found); leader_found = true; } } assert!(leader_found); } }); } async-lock-3.4.0/tests/common/000077500000000000000000000000001462663103300162175ustar00rootroot00000000000000async-lock-3.4.0/tests/common/mod.rs000066400000000000000000000013111462663103300173400ustar00rootroot00000000000000use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::task::Context; use futures_lite::prelude::*; use waker_fn::waker_fn; pub fn check_yields_when_contended(contending_guard: G, acquire_future: impl Future) { let was_woken = Arc::new(AtomicBool::new(false)); let waker = { let was_woken = Arc::clone(&was_woken); waker_fn(move || was_woken.store(true, Ordering::SeqCst)) }; let mut cx = Context::from_waker(&waker); futures_lite::pin!(acquire_future); assert!(acquire_future.as_mut().poll(&mut cx).is_pending()); drop(contending_guard); assert!(was_woken.load(Ordering::SeqCst)); assert!(acquire_future.poll(&mut cx).is_ready()); } async-lock-3.4.0/tests/loom.rs000066400000000000000000000022511462663103300162430ustar00rootroot00000000000000#![cfg(loom)] use loom::sync::{mpsc, Arc}; use loom::thread; use async_lock::Barrier; #[ignore] #[test] fn barrier_smoke() { loom::model(|| { const N: usize = 10; let barrier = Arc::new(Barrier::new(N)); for _ in 0..10 { let (tx, rx) = mpsc::channel(); for _ in 0..loom::MAX_THREADS - 1 { let c = barrier.clone(); let tx = tx.clone(); thread::spawn(move || { let res = c.wait_blocking(); tx.send(res.is_leader()).unwrap(); }); } // At this point, all spawned threads should be blocked, // so we shouldn't get anything from the cahnnel. let res = rx.try_recv(); assert!(res.is_err()); let mut leader_found = barrier.wait_blocking().is_leader(); // Now, the barrier is cleared and we should get data. for _ in 0..N - 1 { if rx.recv().unwrap() { assert!(!leader_found); leader_found = true; } } assert!(leader_found); } }); } async-lock-3.4.0/tests/mutex.rs000066400000000000000000000045621462663103300164460ustar00rootroot00000000000000mod common; use std::sync::Arc; #[cfg(not(target_family = "wasm"))] use std::thread; use async_lock::Mutex; use futures_lite::future; use common::check_yields_when_contended; #[cfg(target_family = "wasm")] use wasm_bindgen_test::wasm_bindgen_test as test; #[cfg(target_family = "wasm")] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); #[test] fn smoke() { future::block_on(async { let m = Mutex::new(()); drop(m.lock().await); drop(m.lock().await); }) } #[cfg(all(feature = "std", not(target_family = "wasm")))] #[test] fn smoke_blocking() { let m = Mutex::new(()); drop(m.lock_blocking()); drop(m.lock_blocking()); } #[cfg(all(feature = "std", not(target_family = "wasm")))] #[test] fn smoke_arc_blocking() { let m = Arc::new(Mutex::new(())); drop(m.lock_arc_blocking()); drop(m.lock_arc_blocking()); } #[test] fn try_lock() { let m = Mutex::new(()); *m.try_lock().unwrap() = (); } #[test] fn into_inner() { let m = Mutex::new(10i32); assert_eq!(m.into_inner(), 10); } #[test] fn get_mut() { let mut m = Mutex::new(10i32); *m.get_mut() = 20; assert_eq!(m.into_inner(), 20); } #[cfg(not(target_family = "wasm"))] #[test] fn contention() { future::block_on(async { let (tx, rx) = flume::unbounded(); let tx = Arc::new(tx); let mutex = Arc::new(Mutex::new(0i32)); let num_tasks = 100; for _ in 0..num_tasks { let tx = tx.clone(); let mutex = mutex.clone(); thread::spawn(|| { future::block_on(async move { let mut lock = mutex.lock().await; *lock += 1; tx.send_async(()).await.unwrap(); drop(lock); }) }); } for _ in 0..num_tasks { rx.recv_async().await.unwrap(); } let lock = mutex.lock().await; assert_eq!(num_tasks, *lock); }); } #[test] fn lifetime() { // Show that the future keeps the mutex alive. let _fut = { let mutex = Arc::new(Mutex::new(0i32)); mutex.lock_arc() }; } #[test] fn yields_when_contended() { let m = Mutex::new(()); check_yields_when_contended(m.try_lock().unwrap(), m.lock()); let m = Arc::new(m); check_yields_when_contended(m.try_lock_arc().unwrap(), m.lock_arc()); } async-lock-3.4.0/tests/rwlock.rs000066400000000000000000000406361462663103300166070ustar00rootroot00000000000000mod common; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use common::check_yields_when_contended; #[cfg(not(target_family = "wasm"))] use futures_lite::prelude::*; #[cfg(not(target_family = "wasm"))] use std::thread; use futures_lite::future; use async_lock::{ RwLock, RwLockReadGuard, RwLockReadGuardArc, RwLockUpgradableReadGuard, RwLockUpgradableReadGuardArc, }; #[cfg(target_family = "wasm")] use wasm_bindgen_test::wasm_bindgen_test as test; #[cfg(target_family = "wasm")] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); #[cfg(not(target_family = "wasm"))] fn spawn(f: impl Future + Send + 'static) -> future::Boxed { let (s, r) = flume::bounded(1); thread::spawn(move || { future::block_on(async { let _ = s.send_async(f.await).await; }) }); async move { r.recv_async().await.unwrap() }.boxed() } #[test] fn smoke() { future::block_on(async { let lock = RwLock::new(()); drop(lock.read().await); drop(lock.write().await); drop((lock.read().await, lock.read().await)); drop(lock.write().await); }); } #[cfg(all(feature = "std", not(target_family = "wasm")))] #[test] fn smoke_blocking() { let lock = RwLock::new(()); drop(lock.read_blocking()); drop(lock.write_blocking()); drop((lock.read_blocking(), lock.read_blocking())); let read = lock.read_blocking(); let upgradabe = lock.upgradable_read_blocking(); drop(read); drop(RwLockUpgradableReadGuard::upgrade_blocking(upgradabe)); drop(lock.write_blocking()); } #[cfg(all(feature = "std", not(target_family = "wasm")))] #[test] fn smoke_arc_blocking() { let lock = Arc::new(RwLock::new(())); drop(lock.read_arc_blocking()); drop(lock.write_arc_blocking()); drop((lock.read_arc_blocking(), lock.read_arc_blocking())); let read = lock.read_arc_blocking(); let upgradabe = lock.upgradable_read_arc_blocking(); drop(read); drop(RwLockUpgradableReadGuardArc::upgrade_blocking(upgradabe)); drop(lock.write_arc_blocking()); } #[test] fn try_write() { future::block_on(async { let lock = RwLock::new(0isize); let read_guard = lock.read().await; assert!(lock.try_write().is_none()); drop(read_guard); }); } #[test] fn into_inner() { let lock = RwLock::new(10); assert_eq!(lock.into_inner(), 10); } #[test] fn into_inner_and_drop() { struct Counter(Arc); impl Drop for Counter { fn drop(&mut self) { self.0.fetch_add(1, Ordering::SeqCst); } } let cnt = Arc::new(AtomicUsize::new(0)); let lock = RwLock::new(Counter(cnt.clone())); assert_eq!(cnt.load(Ordering::SeqCst), 0); { let _inner = lock.into_inner(); assert_eq!(cnt.load(Ordering::SeqCst), 0); } assert_eq!(cnt.load(Ordering::SeqCst), 1); } #[test] fn get_mut() { let mut lock = RwLock::new(10); *lock.get_mut() = 20; assert_eq!(lock.into_inner(), 20); } // Miri bug; this works when async is replaced with blocking #[cfg(not(target_family = "wasm"))] #[test] #[cfg_attr(miri, ignore)] fn contention() { const N: u32 = 10; const M: usize = if cfg!(miri) { 100 } else { 1000 }; let (tx, rx) = flume::unbounded(); let tx = Arc::new(tx); let rw = Arc::new(RwLock::new(())); // Spawn N tasks that randomly acquire the lock M times. for _ in 0..N { let tx = tx.clone(); let rw = rw.clone(); let _spawned = spawn(async move { for _ in 0..M { if fastrand::u32(..N) == 0 { drop(rw.write().await); } else { drop(rw.read().await); } } tx.send_async(()).await.unwrap(); }); } future::block_on(async move { for _ in 0..N { rx.recv_async().await.unwrap(); } }); } #[cfg(not(target_family = "wasm"))] #[test] #[cfg_attr(miri, ignore)] fn contention_arc() { const N: u32 = 10; const M: usize = if cfg!(miri) { 100 } else { 1000 }; let (tx, rx) = flume::unbounded(); let tx = Arc::new(tx); let rw = Arc::new(RwLock::new(())); // Spawn N tasks that randomly acquire the lock M times. for _ in 0..N { let tx = tx.clone(); let rw = rw.clone(); let _spawned = spawn(async move { for _ in 0..M { if fastrand::u32(..N) == 0 { drop(rw.write_arc().await); } else { drop(rw.read_arc().await); } } tx.send_async(()).await.unwrap(); }); } future::block_on(async move { for _ in 0..N { rx.recv_async().await.unwrap(); } }); } #[cfg(not(target_family = "wasm"))] #[test] fn writer_and_readers() { let lock = Arc::new(RwLock::new(0i32)); let (tx, rx) = flume::unbounded(); // Spawn a writer task. let _spawned = spawn({ let lock = lock.clone(); async move { let mut lock = lock.write().await; for _ in 0..1000 { let tmp = *lock; *lock = -1; future::yield_now().await; *lock = tmp + 1; } tx.send_async(()).await.unwrap(); } }); // Readers try to catch the writer in the act. let mut readers = Vec::new(); for _ in 0..5 { let lock = lock.clone(); readers.push(spawn(async move { for _ in 0..1000 { let lock = lock.read().await; assert!(*lock >= 0); } })); } future::block_on(async move { // Wait for readers to pass their asserts. for r in readers { r.await; } // Wait for writer to finish. rx.recv_async().await.unwrap(); let lock = lock.read().await; assert_eq!(*lock, 1000); }); } #[cfg(not(target_family = "wasm"))] #[test] fn writer_and_readers_arc() { let lock = Arc::new(RwLock::new(0i32)); let (tx, rx) = flume::unbounded(); // Spawn a writer task. let _spawned = spawn({ let lock = lock.clone(); async move { let mut lock = lock.write_arc().await; for _ in 0..1000 { let tmp = *lock; *lock = -1; future::yield_now().await; *lock = tmp + 1; } tx.send_async(()).await.unwrap(); } }); // Readers try to catch the writer in the act. let mut readers = Vec::new(); for _ in 0..5 { let lock = lock.clone(); readers.push(spawn(async move { for _ in 0..1000 { let lock = lock.read_arc().await; assert!(*lock >= 0); } })); } future::block_on(async move { // Wait for readers to pass their asserts. for r in readers { r.await; } // Wait for writer to finish. rx.recv_async().await.unwrap(); let lock = lock.read_arc().await; assert_eq!(*lock, 1000); }); } #[test] fn upgrade() { future::block_on(async { let lock: RwLock = RwLock::new(0); let read_guard = lock.read().await; let read_guard2 = lock.read().await; // Should be able to obtain an upgradable lock. let upgradable_guard = lock.upgradable_read().await; // Should be able to obtain a read lock when an upgradable lock is active. let read_guard3 = lock.read().await; assert_eq!(0, *read_guard3); drop(read_guard); drop(read_guard2); drop(read_guard3); // Writers should not pass. assert!(lock.try_write().is_none()); let mut write_guard = RwLockUpgradableReadGuard::try_upgrade(upgradable_guard).expect( "should be able to upgrade an upgradable lock because there are no more readers", ); *write_guard += 1; drop(write_guard); let read_guard = lock.read().await; assert_eq!(1, *read_guard) }); } #[test] fn upgrade_arc() { future::block_on(async { let lock: Arc> = Arc::new(RwLock::new(0)); let read_guard = lock.read_arc().await; let read_guard2 = lock.read_arc().await; // Should be able to obtain an upgradable lock. let upgradable_guard = lock.upgradable_read_arc().await; // Should be able to obtain a read lock when an upgradable lock is active. let read_guard3 = lock.read_arc().await; assert_eq!(0, *read_guard3); drop(read_guard); drop(read_guard2); drop(read_guard3); // Writers should not pass. assert!(lock.try_write().is_none()); let mut write_guard = RwLockUpgradableReadGuardArc::try_upgrade(upgradable_guard).expect( "should be able to upgrade an upgradable lock because there are no more readers", ); *write_guard += 1; drop(write_guard); let read_guard = lock.read_arc().await; assert_eq!(1, *read_guard) }); } #[test] fn not_upgrade() { future::block_on(async { let mutex: RwLock = RwLock::new(0); let read_guard = mutex.read().await; let read_guard2 = mutex.read().await; // Should be able to obtain an upgradable lock. let upgradable_guard = mutex.upgradable_read().await; // Should be able to obtain a shared lock when an upgradable lock is active. let read_guard3 = mutex.read().await; assert_eq!(0, *read_guard3); drop(read_guard); drop(read_guard2); drop(read_guard3); // Drop the upgradable lock. drop(upgradable_guard); assert_eq!(0, *(mutex.read().await)); // Should be able to acquire a write lock because there are no more readers. let mut write_guard = mutex.write().await; *write_guard += 1; drop(write_guard); let read_guard = mutex.read().await; assert_eq!(1, *read_guard) }); } #[test] fn not_upgrade_arc() { future::block_on(async { let mutex: Arc> = Arc::new(RwLock::new(0)); let read_guard = mutex.read_arc().await; let read_guard2 = mutex.read_arc().await; // Should be able to obtain an upgradable lock. let upgradable_guard = mutex.upgradable_read_arc().await; // Should be able to obtain a shared lock when an upgradable lock is active. let read_guard3 = mutex.read_arc().await; assert_eq!(0, *read_guard3); drop(read_guard); drop(read_guard2); drop(read_guard3); // Drop the upgradable lock. drop(upgradable_guard); assert_eq!(0, *(mutex.read_arc().await)); // Should be able to acquire a write lock because there are no more readers. let mut write_guard = mutex.write_arc().await; *write_guard += 1; drop(write_guard); let read_guard = mutex.read_arc().await; assert_eq!(1, *read_guard) }); } #[test] fn upgradable_with_concurrent_writer() { future::block_on(async { let lock: Arc> = Arc::new(RwLock::new(0)); let lock2 = lock.clone(); let upgradable_guard = lock.upgradable_read().await; future::or( async move { let mut write_guard = lock2.write().await; *write_guard = 1; }, async move { let mut write_guard = RwLockUpgradableReadGuard::upgrade(upgradable_guard).await; assert_eq!(*write_guard, 0); *write_guard = 2; }, ) .await; assert_eq!(2, *(lock.write().await)); let read_guard = lock.read().await; assert_eq!(2, *read_guard); }); } #[test] fn upgradable_with_concurrent_writer_arc() { future::block_on(async { let lock: Arc> = Arc::new(RwLock::new(0)); let lock2 = lock.clone(); let upgradable_guard = lock.upgradable_read_arc().await; future::or( async move { let mut write_guard = lock2.write_arc().await; *write_guard = 1; }, async move { let mut write_guard = RwLockUpgradableReadGuardArc::upgrade(upgradable_guard).await; assert_eq!(*write_guard, 0); *write_guard = 2; }, ) .await; assert_eq!(2, *(lock.write_arc().await)); let read_guard = lock.read_arc().await; assert_eq!(2, *read_guard); }); } #[test] fn yields_when_contended() { let rw = RwLock::new(()); check_yields_when_contended(rw.try_write().unwrap(), rw.read()); check_yields_when_contended(rw.try_write().unwrap(), rw.upgradable_read()); check_yields_when_contended(rw.try_write().unwrap(), rw.write()); check_yields_when_contended(rw.try_read().unwrap(), rw.write()); check_yields_when_contended(rw.try_upgradable_read().unwrap(), rw.write()); check_yields_when_contended(rw.try_upgradable_read().unwrap(), rw.upgradable_read()); let upgradable = rw.try_upgradable_read().unwrap(); check_yields_when_contended( rw.try_read().unwrap(), RwLockUpgradableReadGuard::upgrade(upgradable), ); } #[test] fn yields_when_contended_arc() { let rw = Arc::new(RwLock::new(())); check_yields_when_contended(rw.try_write_arc().unwrap(), rw.read_arc()); check_yields_when_contended(rw.try_write_arc().unwrap(), rw.upgradable_read_arc()); check_yields_when_contended(rw.try_write_arc().unwrap(), rw.write_arc()); check_yields_when_contended(rw.try_read_arc().unwrap(), rw.write_arc()); check_yields_when_contended(rw.try_upgradable_read_arc().unwrap(), rw.write_arc()); check_yields_when_contended( rw.try_upgradable_read_arc().unwrap(), rw.upgradable_read_arc(), ); let upgradable = rw.try_upgradable_read_arc().unwrap(); check_yields_when_contended( rw.try_read_arc().unwrap(), RwLockUpgradableReadGuardArc::upgrade(upgradable), ); } #[test] fn cancellation() { future::block_on(async { let rw = RwLock::new(()); drop(rw.read()); drop(rw.upgradable_read()); drop(rw.write()); let read = rw.read().await; drop(read); let upgradable_read = rw.upgradable_read().await; drop(upgradable_read); let write = rw.write().await; drop(write); let upgradable_read = rw.upgradable_read().await; drop(RwLockUpgradableReadGuard::upgrade(upgradable_read)); let upgradable_read = rw.upgradable_read().await; let write = RwLockUpgradableReadGuard::upgrade(upgradable_read).await; drop(write); }); } #[test] fn arc_rwlock_refcounts() { future::block_on(async { let rw = Arc::new(RwLock::new(())); assert_eq!(Arc::strong_count(&rw), 1); drop(rw.read_arc()); assert_eq!(Arc::strong_count(&rw), 1); drop(rw.upgradable_read_arc()); assert_eq!(Arc::strong_count(&rw), 1); drop(rw.write()); assert_eq!(Arc::strong_count(&rw), 1); let read = rw.read_arc().await; assert_eq!(Arc::strong_count(&rw), 2); drop(read); assert_eq!(Arc::strong_count(&rw), 1); let upgradable_read = rw.upgradable_read_arc().await; assert_eq!(Arc::strong_count(&rw), 2); drop(upgradable_read); assert_eq!(Arc::strong_count(&rw), 1); let write = rw.write_arc().await; assert_eq!(Arc::strong_count(&rw), 2); drop(write); assert_eq!(Arc::strong_count(&rw), 1); let upgradable_read = rw.upgradable_read_arc().await; assert_eq!(Arc::strong_count(&rw), 2); drop(RwLockUpgradableReadGuardArc::upgrade(upgradable_read)); assert_eq!(Arc::strong_count(&rw), 1); let upgradable_read = rw.upgradable_read_arc().await; assert_eq!(Arc::strong_count(&rw), 2); let write = RwLockUpgradableReadGuardArc::upgrade(upgradable_read).await; assert_eq!(Arc::strong_count(&rw), 2); drop(write); assert_eq!(Arc::strong_count(&rw), 1); }); } // We are testing that this compiles. fn _covariance_test<'g>(guard: RwLockReadGuard<'g, &'static ()>) { let _: RwLockReadGuard<'g, &'g ()> = guard; } // We are testing that this compiles. fn _covariance_test_arc( guard: RwLockReadGuardArc<&'static ()>, mut _guard_2: RwLockReadGuardArc<&()>, ) { _guard_2 = guard; } async-lock-3.4.0/tests/semaphore.rs000066400000000000000000000106211462663103300172600ustar00rootroot00000000000000mod common; use std::future::Future; use std::mem::forget; use std::pin::Pin; use std::sync::{ atomic::{AtomicUsize, Ordering}, mpsc, Arc, }; use std::task::Context; use std::task::Poll; use std::thread; use common::check_yields_when_contended; use async_lock::Semaphore; use futures_lite::{future, pin}; #[test] fn try_acquire() { let s = Semaphore::new(2); let g1 = s.try_acquire().unwrap(); let _g2 = s.try_acquire().unwrap(); assert!(s.try_acquire().is_none()); drop(g1); assert!(s.try_acquire().is_some()); } #[test] fn stress() { const COUNT: usize = if cfg!(miri) { 500 } else { 10_000 }; let s = Arc::new(Semaphore::new(5)); let (tx, rx) = mpsc::channel::<()>(); for _ in 0..50 { let s = s.clone(); let tx = tx.clone(); thread::spawn(move || { future::block_on(async { for _ in 0..COUNT { s.acquire().await; } drop(tx); }) }); } drop(tx); let _ = rx.recv(); let _g1 = s.try_acquire().unwrap(); let g2 = s.try_acquire().unwrap(); let _g3 = s.try_acquire().unwrap(); let _g4 = s.try_acquire().unwrap(); let _g5 = s.try_acquire().unwrap(); assert!(s.try_acquire().is_none()); drop(g2); assert!(s.try_acquire().is_some()); } #[test] fn as_mutex() { let s = Arc::new(Semaphore::new(1)); let s2 = s.clone(); let _t = thread::spawn(move || { future::block_on(async { let _g = s2.acquire().await; }); }); future::block_on(async { let _g = s.acquire().await; }); } #[test] fn multi_resource() { let s = Arc::new(Semaphore::new(2)); let s2 = s.clone(); let (tx1, rx1) = mpsc::channel(); let (tx2, rx2) = mpsc::channel(); let _t = thread::spawn(move || { future::block_on(async { let _g = s2.acquire().await; let _ = rx2.recv(); tx1.send(()).unwrap(); }); }); future::block_on(async { let _g = s.acquire().await; tx2.send(()).unwrap(); rx1.recv().unwrap(); }); } #[test] fn lifetime() { // Show that the future keeps the semaphore alive. let _fut = { let mutex = Arc::new(Semaphore::new(2)); mutex.acquire_arc() }; } #[test] fn yields_when_contended() { let s = Semaphore::new(1); check_yields_when_contended(s.try_acquire().unwrap(), s.acquire()); let s = Arc::new(s); check_yields_when_contended(s.try_acquire_arc().unwrap(), s.acquire_arc()); } #[cfg(all(feature = "std", not(target_family = "wasm")))] #[test] fn smoke_blocking() { let s = Semaphore::new(2); let g1 = s.acquire_blocking(); let _g2 = s.acquire_blocking(); assert!(s.try_acquire().is_none()); drop(g1); assert!(s.try_acquire().is_some()); } #[cfg(all(feature = "std", not(target_family = "wasm")))] #[test] fn smoke_arc_blocking() { let s = Arc::new(Semaphore::new(2)); let g1 = s.acquire_arc_blocking(); let _g2 = s.acquire_arc_blocking(); assert!(s.try_acquire().is_none()); drop(g1); assert!(s.try_acquire().is_some()); } #[test] fn add_permits() { static COUNTER: AtomicUsize = AtomicUsize::new(0); let s = Arc::new(Semaphore::new(0)); let (tx, rx) = mpsc::channel::<()>(); for _ in 0..50 { let s = s.clone(); let tx = tx.clone(); thread::spawn(move || { future::block_on(async { let perm = s.acquire().await; forget(perm); COUNTER.fetch_add(1, Ordering::Relaxed); drop(tx); }) }); } assert_eq!(COUNTER.load(Ordering::Relaxed), 0); s.add_permits(50); drop(tx); let _ = rx.recv(); assert_eq!(COUNTER.load(Ordering::Relaxed), 50); } #[test] fn add_permits_2() { future::block_on(AddPermitsTest); } struct AddPermitsTest; impl Future for AddPermitsTest { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { let s = Semaphore::new(0); let acq = s.acquire(); pin!(acq); let acq_2 = s.acquire(); pin!(acq_2); assert!(acq.as_mut().poll(cx).is_pending()); assert!(acq_2.as_mut().poll(cx).is_pending()); s.add_permits(1); let g = acq.poll(cx); assert!(g.is_ready()); assert!(acq_2.poll(cx).is_pending()); Poll::Ready(()) } }