├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches └── bench.rs ├── examples └── mutex.rs ├── src ├── intrusive.rs ├── lib.rs ├── notify.rs ├── slab.rs └── slab │ └── node.rs └── tests ├── loom.rs └── notify.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | commit-message: 8 | prefix: '' 9 | labels: [] 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | push: 9 | branches: 10 | - master 11 | schedule: 12 | - cron: '0 2 * * 0' 13 | 14 | env: 15 | CARGO_INCREMENTAL: 0 16 | CARGO_NET_GIT_FETCH_WITH_CLI: true 17 | CARGO_NET_RETRY: 10 18 | CARGO_TERM_COLOR: always 19 | RUST_BACKTRACE: 1 20 | RUSTFLAGS: -D warnings 21 | RUSTDOCFLAGS: -D warnings 22 | RUSTUP_MAX_RETRIES: 10 23 | 24 | defaults: 25 | run: 26 | shell: bash 27 | 28 | jobs: 29 | fmt: 30 | uses: smol-rs/.github/.github/workflows/fmt.yml@main 31 | security_audit: 32 | uses: smol-rs/.github/.github/workflows/security_audit.yml@main 33 | permissions: 34 | checks: write 35 | contents: read 36 | issues: write 37 | secrets: inherit 38 | 39 | test: 40 | runs-on: ubuntu-latest 41 | strategy: 42 | fail-fast: false 43 | matrix: 44 | include: 45 | - rust: stable 46 | - rust: beta 47 | - rust: nightly 48 | - rust: nightly 49 | target: i686-unknown-linux-gnu 50 | steps: 51 | - uses: actions/checkout@v4 52 | - name: Install Rust 53 | run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} 54 | - name: Install cross-compilation tools 55 | uses: taiki-e/setup-cross-toolchain-action@v1 56 | with: 57 | target: ${{ matrix.target }} 58 | if: matrix.target != '' 59 | - run: cargo build --all --all-features --all-targets 60 | - run: cargo test --all 61 | - run: cargo test --all --release 62 | - run: cargo test --no-default-features --tests 63 | - run: cargo test --no-default-features --tests --release 64 | - name: Install cargo-hack 65 | uses: taiki-e/install-action@cargo-hack 66 | - run: rustup target add thumbv7m-none-eabi 67 | - name: Run cargo check (without dev-dependencies to catch missing feature flags) 68 | run: cargo hack build --all --no-dev-deps 69 | - run: cargo hack build --all --target thumbv7m-none-eabi --no-default-features --no-dev-deps 70 | - run: cargo hack build --target thumbv7m-none-eabi --no-default-features --no-dev-deps --features portable-atomic 71 | - run: cargo hack build --target thumbv7m-none-eabi --no-default-features --no-dev-deps --features critical-section 72 | #- name: Install wasm-pack 73 | # uses: taiki-e/install-action@wasm-pack 74 | #- run: wasm-pack test --node 75 | #- run: wasm-pack test --node --no-default-features 76 | #- run: wasm-pack test --node --no-default-features --features portable-atomic 77 | - name: Clone some dependent crates 78 | run: | 79 | git clone https://github.com/smol-rs/event-listener-strategy.git 80 | git clone https://github.com/smol-rs/async-channel.git 81 | git clone https://github.com/smol-rs/async-lock.git 82 | - name: Patch dependent crates 83 | run: | 84 | echo '[patch.crates-io]' >> event-listener-strategy/Cargo.toml 85 | echo 'event-listener = { path = ".." }' >> event-listener-strategy/Cargo.toml 86 | echo '[patch.crates-io]' >> async-channel/Cargo.toml 87 | echo 'event-listener = { path = ".." }' >> async-channel/Cargo.toml 88 | echo 'event-listener-strategy = { path = "../event-listener-strategy" }' >> async-channel/Cargo.toml 89 | echo '[patch.crates-io]' >> async-lock/Cargo.toml 90 | echo 'event-listener = { path = ".." }' >> async-lock/Cargo.toml 91 | echo 'event-listener-strategy = { path = "../event-listener-strategy" }' >> async-lock/Cargo.toml 92 | echo 'async-channel = { path = "../async-channel" }' >> async-lock/Cargo.toml 93 | - name: Test dependent crates 94 | run: | 95 | cargo test --manifest-path=event-listener-strategy/Cargo.toml 96 | cargo test --manifest-path=async-channel/Cargo.toml 97 | cargo test --manifest-path=async-lock/Cargo.toml 98 | 99 | msrv: 100 | runs-on: ubuntu-latest 101 | steps: 102 | - uses: actions/checkout@v4 103 | - name: Install cargo-hack 104 | uses: taiki-e/install-action@cargo-hack 105 | - run: cargo hack build --all --rust-version 106 | - run: cargo hack build --all --no-default-features --rust-version 107 | - run: cargo hack build --all --no-default-features --rust-version --features critical-section 108 | 109 | clippy: 110 | runs-on: ubuntu-latest 111 | steps: 112 | - uses: actions/checkout@v4 113 | - name: Install Rust 114 | run: rustup update stable 115 | - run: cargo clippy --all --all-features --all-targets 116 | 117 | miri: 118 | runs-on: ubuntu-latest 119 | steps: 120 | - uses: actions/checkout@v4 121 | - name: Install Rust 122 | run: rustup toolchain install nightly --component miri && rustup default nightly 123 | - run: | 124 | echo "MIRIFLAGS=-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation" >>"${GITHUB_ENV}" 125 | echo "RUSTFLAGS=${RUSTFLAGS} -Z randomize-layout" >>"${GITHUB_ENV}" 126 | - run: cargo miri test --all 127 | - run: cargo miri test --no-default-features --tests 128 | - run: cargo miri test --no-default-features --features portable-atomic --tests 129 | - name: Clone some dependent crates 130 | run: | 131 | git clone https://github.com/smol-rs/event-listener-strategy.git 132 | git clone https://github.com/smol-rs/async-channel.git 133 | git clone https://github.com/smol-rs/async-lock.git 134 | - name: Patch dependent crates 135 | run: | 136 | echo '[patch.crates-io]' >> event-listener-strategy/Cargo.toml 137 | echo 'event-listener = { path = ".." }' >> event-listener-strategy/Cargo.toml 138 | echo '[patch.crates-io]' >> async-channel/Cargo.toml 139 | echo 'event-listener = { path = ".." }' >> async-channel/Cargo.toml 140 | echo 'event-listener-strategy = { path = "../event-listener-strategy" }' >> async-channel/Cargo.toml 141 | echo '[patch.crates-io]' >> async-lock/Cargo.toml 142 | echo 'event-listener = { path = ".." }' >> async-lock/Cargo.toml 143 | echo 'event-listener-strategy = { path = "../event-listener-strategy" }' >> async-lock/Cargo.toml 144 | echo 'async-channel = { path = "../async-channel" }' >> async-lock/Cargo.toml 145 | - name: Test dependent crates 146 | # async-channel isn't included here as it appears to be broken on MIRI. 147 | # See https://github.com/smol-rs/async-channel/issues/85 148 | run: | 149 | cargo miri test --manifest-path=event-listener-strategy/Cargo.toml 150 | cargo miri test --manifest-path=async-lock/Cargo.toml 151 | 152 | loom: 153 | runs-on: ubuntu-latest 154 | steps: 155 | - uses: actions/checkout@v4 156 | - name: Install Rust 157 | run: rustup update stable 158 | - name: Loom tests 159 | run: RUSTFLAGS="--cfg=loom" cargo test --release --test loom --features loom 160 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | push: 8 | tags: 9 | - v[0-9]+.* 10 | 11 | jobs: 12 | create-release: 13 | if: github.repository_owner == 'smol-rs' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: taiki-e/create-gh-release-action@v1 18 | with: 19 | changelog: CHANGELOG.md 20 | branch: master 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 5.4.0 2 | 3 | - Add a `no_std` implementation based on the `critical-section` crate, enabled 4 | via the feature of the same name. (#148) 5 | 6 | # Version 5.3.1 7 | 8 | - Disable some optimizations that, in rare conditions, can cause race conditions 9 | causing notifications to be dropped. (#139) 10 | - Ensure the portable-atomic feature is set properly. (#134) 11 | - Update `portable-atomic-util` to v0.2.0. (#132) 12 | - Document the std feature. (#134) 13 | 14 | # Version 5.3.0 15 | 16 | - Add a `loom` implementation. This feature is unstable and is not semver-supported. (#126) 17 | - Make the panic message for polling the `EventListener` after it has completed more clear. (#125) 18 | 19 | # Version 5.2.0 20 | 21 | - Make `StackSlot` `Sync`. (#121) 22 | 23 | # Version 5.1.0 24 | 25 | - Make `StackSlot` `Send`. (#119) 26 | 27 | # Version 5.0.0 28 | 29 | - **Breaking:** Rework the API to afford better usage. (#105) 30 | - The heap-based API of the v2.x line is back. 31 | - However, there is a stack-based API as an alternative. 32 | - Add a way to get the total number of listeners. (#114) 33 | 34 | # Version 4.0.3 35 | 36 | - Relax MSRV to 1.60. (#110) 37 | 38 | # Version 4.0.2 39 | 40 | - Avoid spinning in `wait_deadline`. (#107) 41 | 42 | # Version 4.0.1 43 | 44 | - Fix a use-after-move error after an `EventListener` is assigned to listen to 45 | another `Event`. (#101) 46 | 47 | # Version 4.0.0 48 | 49 | - **Breaking:** Fix a footgun in the `EventListener` type. `EventListener::new()` 50 | now no longer takes an `&Event` as an argument, and `EventListener::listen()` 51 | takes the `&Event` as an argument. Hopefully this should prevent `.await`ing 52 | on a listener without making sure it's listening first. (#94) 53 | 54 | # Version 3.1.0 55 | 56 | - Implement `UnwindSafe` and `RefUnwindSafe` for `EventListener`. This was unintentionally removed in version 3 (#96). 57 | 58 | # Version 3.0.1 59 | 60 | - Emphasize that `listen()` must be called on `EventListener` in documentation. (#90) 61 | - Write useful output in `fmt::Debug` implementations. (#86) 62 | 63 | # Version 3.0.0 64 | 65 | - Use the `parking` crate instead of threading APIs (#27) 66 | - Bump MSRV to 1.59 (#71) 67 | - **Breaking:** Make this crate `no_std`-compatible on `default-features = false`. (#34) 68 | - Create a new `event-listener-strategy` crate for abstracting over blocking/non-blocking operations. (#49) 69 | - **Breaking:** Change the `EventListener` API to be `!Unpin`. (#51) 70 | - Enable a feature for the `portable-atomic` crate. (#53) 71 | - **Breaking:** Add a `Notification` trait which is used to enable tagged events. (#52) 72 | - Add an `is_notified()` method to `Event`. (#48) 73 | - **Breaking:** Make it so `notify()` returns the number of listeners notified. (#57) 74 | 75 | # Version 2.5.3 76 | 77 | - Fix fence on x86 and miri. 78 | 79 | # Version 2.5.2 80 | 81 | - Fix stacked borrows violation when `-Zmiri-tag-raw-pointers` is enabled. (#24) 82 | 83 | # Version 2.5.1 84 | 85 | - Replace spinlock with a mutex. 86 | 87 | # Version 2.5.0 88 | 89 | - Add `EventListener::discard()`. 90 | 91 | # Version 2.4.0 92 | 93 | - `Event::new()` is now a const fn. 94 | 95 | # Version 2.3.3 96 | 97 | - Fix a bug in `List::insert()` that was causing deadlocks. 98 | 99 | # Version 2.3.2 100 | 101 | - Optimization: use a simple spinlock and cache an `Entry` for less allocation. 102 | 103 | # Version 2.3.1 104 | 105 | - Optimization: don't initialize `Inner` when notifying `Event`. 106 | 107 | # Version 2.3.0 108 | 109 | - Implement `UnwindSafe`/`RefUnwindSafe` for `Event`/`EventListener`. 110 | 111 | # Version 2.2.1 112 | 113 | - Always keep the last waker in `EventListener::poll()`. 114 | 115 | # Version 2.2.0 116 | 117 | - Add `EventListener::same_event()`. 118 | 119 | # Version 2.1.0 120 | 121 | - Add `EventListener::listens_to()`. 122 | 123 | # Version 2.0.1 124 | 125 | - Replace `usize::MAX` with `std::usize::MAX`. 126 | 127 | # Version 2.0.0 128 | 129 | - Remove `Event::notify_one()` and `Event::notify_all()`. 130 | - Add `Event::notify_relaxed()` and `Event::notify_additional_relaxed()`. 131 | - Dropped notified `EventListener` now notifies one *or* one additional listener. 132 | 133 | # Version 1.2.0 134 | 135 | - Add `Event::notify_additional()`. 136 | 137 | # Version 1.1.2 138 | 139 | - Change a `Relaxed` load to `Acquire` load. 140 | 141 | # Version 1.1.1 142 | 143 | - Fix a bug in `EventListener::wait_timeout()`. 144 | 145 | # Version 1.1.0 146 | 147 | - Add `EventListener::notify()`. 148 | 149 | # Version 1.0.1 150 | 151 | - Reduce the complexity of `notify_all()` from O(n) to amortized O(1). 152 | - Fix a bug where entries were notified in wrong order. 153 | - Add tests. 154 | 155 | # Version 1.0.0 156 | 157 | - Initial version. 158 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "event-listener" 3 | # When publishing a new version: 4 | # - Update CHANGELOG.md 5 | # - Create "v5.x.y" git tag 6 | version = "5.4.0" 7 | authors = ["Stjepan Glavina ", "John Nunley "] 8 | edition = "2021" 9 | rust-version = "1.60" 10 | description = "Notify async tasks or threads" 11 | license = "Apache-2.0 OR MIT" 12 | repository = "https://github.com/smol-rs/event-listener" 13 | keywords = ["condvar", "eventcount", "wake", "blocking", "park"] 14 | categories = ["asynchronous", "concurrency"] 15 | exclude = ["/.*"] 16 | 17 | [features] 18 | default = ["std"] 19 | std = ["concurrent-queue/std", "parking"] 20 | portable-atomic = [ 21 | "portable-atomic-util", 22 | "portable_atomic_crate", 23 | "concurrent-queue/portable-atomic", 24 | ] 25 | loom = ["concurrent-queue/loom", "parking?/loom", "dep:loom"] 26 | 27 | [lints.rust] 28 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(loom)'] } 29 | 30 | [dependencies] 31 | concurrent-queue = { version = "2.4.0", default-features = false } 32 | critical-section = { version = "1.2.0", default-features = false, optional = true } 33 | pin-project-lite = "0.2.12" 34 | portable-atomic-util = { version = "0.2.0", default-features = false, optional = true, features = ["alloc"] } 35 | 36 | [target.'cfg(not(target_family = "wasm"))'.dependencies] 37 | parking = { version = "2.0.0", optional = true } 38 | 39 | [target.'cfg(loom)'.dependencies] 40 | loom = { version = "0.7", optional = true } 41 | 42 | [dependencies.portable_atomic_crate] 43 | package = "portable-atomic" 44 | version = "1.2.0" 45 | default-features = false 46 | optional = true 47 | 48 | [dev-dependencies] 49 | critical-section = { version = "1.2.0", features = ["std"] } 50 | futures-lite = "2.0.0" 51 | try-lock = "0.2.5" 52 | waker-fn = "1" 53 | 54 | [dev-dependencies.criterion] 55 | version = "0.5" 56 | default-features = false 57 | features = ["cargo_bench_support"] 58 | 59 | [target.'cfg(target_family = "wasm")'.dev-dependencies] 60 | wasm-bindgen-test = "0.3" 61 | 62 | [[bench]] 63 | name = "bench" 64 | harness = false 65 | 66 | [lib] 67 | bench = false 68 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # event-listener 2 | 3 | [![Build](https://github.com/smol-rs/event-listener/workflows/CI/badge.svg)]( 4 | https://github.com/smol-rs/event-listener/actions) 5 | [![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( 6 | https://github.com/smol-rs/event-listener) 7 | [![Cargo](https://img.shields.io/crates/v/event-listener.svg)]( 8 | https://crates.io/crates/event-listener) 9 | [![Documentation](https://docs.rs/event-listener/badge.svg)]( 10 | https://docs.rs/event-listener) 11 | 12 | Notify async tasks or threads. 13 | 14 | This is a synchronization primitive similar to [eventcounts] invented by Dmitry Vyukov. 15 | 16 | You can use this crate to turn non-blocking data structures into async or blocking data 17 | structures. See a [simple mutex] implementation that exposes an async and a blocking interface 18 | for acquiring locks. 19 | 20 | [eventcounts]: https://www.1024cores.net/home/lock-free-algorithms/eventcounts 21 | [simple mutex]: ./examples/mutex.rs 22 | 23 | ## Examples 24 | 25 | Wait until another thread sets a boolean flag: 26 | 27 | ```rust 28 | use std::sync::atomic::{AtomicBool, Ordering}; 29 | use std::sync::Arc; 30 | use std::thread; 31 | use std::time::Duration; 32 | use event_listener::Event; 33 | 34 | let flag = Arc::new(AtomicBool::new(false)); 35 | let event = Arc::new(Event::new()); 36 | 37 | // Spawn a thread that will set the flag after 1 second. 38 | thread::spawn({ 39 | let flag = flag.clone(); 40 | let event = event.clone(); 41 | move || { 42 | // Wait for a second. 43 | thread::sleep(Duration::from_secs(1)); 44 | 45 | // Set the flag. 46 | flag.store(true, Ordering::SeqCst); 47 | 48 | // Notify all listeners that the flag has been set. 49 | event.notify(usize::MAX); 50 | } 51 | }); 52 | 53 | // Wait until the flag is set. 54 | loop { 55 | // Check the flag. 56 | if flag.load(Ordering::SeqCst) { 57 | break; 58 | } 59 | 60 | // Start listening for events. 61 | let listener = event.listen(); 62 | 63 | // Check the flag again after creating the listener. 64 | if flag.load(Ordering::SeqCst) { 65 | break; 66 | } 67 | 68 | // Wait for a notification and continue the loop. 69 | listener.wait(); 70 | } 71 | ``` 72 | 73 | ## License 74 | 75 | Licensed under either of 76 | 77 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) 78 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) 79 | 80 | at your option. 81 | 82 | #### Contribution 83 | 84 | Unless you explicitly state otherwise, any contribution intentionally submitted 85 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 86 | dual licensed as above, without any additional terms or conditions. 87 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | use std::iter; 2 | 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | use event_listener::{Event, Listener}; 5 | 6 | const COUNT: usize = 8000; 7 | 8 | fn bench_events(c: &mut Criterion) { 9 | c.bench_function("notify_and_wait", |b| { 10 | let ev = Event::new(); 11 | let mut handles = Vec::with_capacity(COUNT); 12 | 13 | b.iter(|| { 14 | handles.extend(iter::repeat_with(|| ev.listen()).take(COUNT)); 15 | 16 | ev.notify(COUNT); 17 | 18 | for handle in handles.drain(..) { 19 | handle.wait(); 20 | } 21 | }); 22 | }); 23 | } 24 | 25 | criterion_group!(benches, bench_events); 26 | criterion_main!(benches); 27 | -------------------------------------------------------------------------------- /examples/mutex.rs: -------------------------------------------------------------------------------- 1 | //! A simple mutex implementation. 2 | //! 3 | //! This mutex exposes both blocking and async methods for acquiring a lock. 4 | 5 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 6 | mod example { 7 | #![allow(dead_code)] 8 | 9 | use std::ops::{Deref, DerefMut}; 10 | use std::sync::{mpsc, Arc}; 11 | use std::thread; 12 | use std::time::{Duration, Instant}; 13 | 14 | use event_listener::{listener, Event, Listener}; 15 | use try_lock::{Locked, TryLock}; 16 | 17 | /// A simple mutex. 18 | struct Mutex { 19 | /// Blocked lock operations. 20 | lock_ops: Event, 21 | 22 | /// The inner non-blocking mutex. 23 | data: TryLock, 24 | } 25 | 26 | unsafe impl Send for Mutex {} 27 | unsafe impl Sync for Mutex {} 28 | 29 | impl Mutex { 30 | /// Creates a mutex. 31 | fn new(t: T) -> Mutex { 32 | Mutex { 33 | lock_ops: Event::new(), 34 | data: TryLock::new(t), 35 | } 36 | } 37 | 38 | /// Attempts to acquire a lock. 39 | fn try_lock(&self) -> Option> { 40 | self.data.try_lock().map(|l| MutexGuard { 41 | lock_ops: &self.lock_ops, 42 | locked: Some(l), 43 | }) 44 | } 45 | 46 | /// Blocks until a lock is acquired. 47 | fn lock(&self) -> MutexGuard<'_, T> { 48 | loop { 49 | // Attempt grabbing a lock. 50 | if let Some(guard) = self.try_lock() { 51 | return guard; 52 | } 53 | 54 | // Set up an event listener. 55 | listener!(self.lock_ops => listener); 56 | 57 | // Try again. 58 | if let Some(guard) = self.try_lock() { 59 | return guard; 60 | } 61 | 62 | // Wait for a notification. 63 | listener.wait(); 64 | } 65 | } 66 | 67 | /// Blocks until a lock is acquired or the timeout is reached. 68 | fn lock_timeout(&self, timeout: Duration) -> Option> { 69 | let deadline = Instant::now() + timeout; 70 | 71 | loop { 72 | // Attempt grabbing a lock. 73 | if let Some(guard) = self.try_lock() { 74 | return Some(guard); 75 | } 76 | 77 | // Set up an event listener. 78 | listener!(self.lock_ops => listener); 79 | 80 | // Try again. 81 | if let Some(guard) = self.try_lock() { 82 | return Some(guard); 83 | } 84 | 85 | // Wait until a notification is received. 86 | listener.wait_deadline(deadline)?; 87 | } 88 | } 89 | 90 | /// Acquires a lock asynchronously. 91 | async fn lock_async(&self) -> MutexGuard<'_, T> { 92 | loop { 93 | // Attempt grabbing a lock. 94 | if let Some(guard) = self.try_lock() { 95 | return guard; 96 | } 97 | 98 | // Set up an event listener. 99 | listener!(self.lock_ops => listener); 100 | 101 | // Try again. 102 | if let Some(guard) = self.try_lock() { 103 | return guard; 104 | } 105 | 106 | // Wait until a notification is received. 107 | listener.await; 108 | } 109 | } 110 | } 111 | 112 | /// A guard holding a lock. 113 | struct MutexGuard<'a, T> { 114 | lock_ops: &'a Event, 115 | locked: Option>, 116 | } 117 | 118 | impl Deref for MutexGuard<'_, T> { 119 | type Target = T; 120 | 121 | fn deref(&self) -> &T { 122 | self.locked.as_deref().unwrap() 123 | } 124 | } 125 | 126 | impl DerefMut for MutexGuard<'_, T> { 127 | fn deref_mut(&mut self) -> &mut T { 128 | self.locked.as_deref_mut().unwrap() 129 | } 130 | } 131 | 132 | impl Drop for MutexGuard<'_, T> { 133 | fn drop(&mut self) { 134 | self.locked = None; 135 | self.lock_ops.notify(1); 136 | } 137 | } 138 | 139 | pub(super) fn entry() { 140 | const N: usize = 10; 141 | 142 | // A shared counter. 143 | let counter = Arc::new(Mutex::new(0)); 144 | 145 | // A channel that signals when all threads are done. 146 | let (tx, rx) = mpsc::channel(); 147 | 148 | // Spawn a bunch of threads incrementing the counter. 149 | for _ in 0..N { 150 | let counter = counter.clone(); 151 | let tx = tx.clone(); 152 | 153 | thread::spawn(move || { 154 | let mut counter = counter.lock(); 155 | *counter += 1; 156 | 157 | // If this is the last increment, signal that we're done. 158 | if *counter == N { 159 | tx.send(()).unwrap(); 160 | } 161 | }); 162 | } 163 | 164 | // Wait until the last thread increments the counter. 165 | rx.recv().unwrap(); 166 | 167 | // The counter must equal the number of threads. 168 | assert_eq!(*counter.lock(), N); 169 | 170 | println!("Done!"); 171 | } 172 | } 173 | 174 | #[cfg(any(target_family = "wasm", not(feature = "std")))] 175 | mod example { 176 | pub(super) fn entry() { 177 | println!("This example is not supported on wasm yet."); 178 | } 179 | } 180 | 181 | fn main() { 182 | example::entry(); 183 | } 184 | -------------------------------------------------------------------------------- /src/intrusive.rs: -------------------------------------------------------------------------------- 1 | //! Intrusive linked list-based implementation of `event-listener`. 2 | //! 3 | //! This implementation crates an intrusive linked list of listeners. This list 4 | //! is secured using either a libstd mutex or a critical section. 5 | 6 | use crate::notify::{GenericNotify, Internal, Notification}; 7 | use crate::sync::atomic::Ordering; 8 | use crate::sync::cell::{Cell, UnsafeCell}; 9 | use crate::{RegisterResult, State, TaskRef}; 10 | 11 | #[cfg(feature = "critical-section")] 12 | use core::cell::RefCell; 13 | #[cfg(all(feature = "std", not(feature = "critical-section")))] 14 | use core::ops::{Deref, DerefMut}; 15 | 16 | use core::marker::PhantomPinned; 17 | use core::mem; 18 | use core::pin::Pin; 19 | use core::ptr::NonNull; 20 | 21 | pub(super) struct List( 22 | /// libstd-based implementation uses a normal Muetx to secure the data. 23 | #[cfg(all(feature = "std", not(feature = "critical-section")))] 24 | crate::sync::Mutex>, 25 | /// Critical-section-based implementation uses a CS cell that wraps a RefCell. 26 | #[cfg(feature = "critical-section")] 27 | critical_section::Mutex>>, 28 | ); 29 | 30 | struct Inner { 31 | /// The head of the linked list. 32 | head: Option>>, 33 | 34 | /// The tail of the linked list. 35 | tail: Option>>, 36 | 37 | /// The first unnotified listener. 38 | next: Option>>, 39 | 40 | /// Total number of listeners. 41 | len: usize, 42 | 43 | /// The number of notified listeners. 44 | notified: usize, 45 | } 46 | 47 | impl List { 48 | /// Create a new, empty event listener list. 49 | pub(super) fn new() -> Self { 50 | let inner = Inner { 51 | head: None, 52 | tail: None, 53 | next: None, 54 | len: 0, 55 | notified: 0, 56 | }; 57 | 58 | #[cfg(feature = "critical-section")] 59 | { 60 | Self(critical_section::Mutex::new(RefCell::new(inner))) 61 | } 62 | 63 | #[cfg(not(feature = "critical-section"))] 64 | Self(crate::sync::Mutex::new(inner)) 65 | } 66 | 67 | /// Get the total number of listeners without blocking. 68 | #[cfg(all(feature = "std", not(feature = "critical-section")))] 69 | pub(crate) fn try_total_listeners(&self) -> Option { 70 | self.0.try_lock().ok().map(|list| list.len) 71 | } 72 | 73 | /// Get the total number of listeners without blocking. 74 | #[cfg(feature = "critical-section")] 75 | pub(crate) fn try_total_listeners(&self) -> Option { 76 | Some(self.total_listeners()) 77 | } 78 | 79 | /// Get the total number of listeners with blocking. 80 | #[cfg(all(feature = "std", not(feature = "critical-section")))] 81 | pub(crate) fn total_listeners(&self) -> usize { 82 | self.0.lock().unwrap_or_else(|e| e.into_inner()).len 83 | } 84 | 85 | /// Get the total number of listeners with blocking. 86 | #[cfg(feature = "critical-section")] 87 | #[allow(unused)] 88 | pub(crate) fn total_listeners(&self) -> usize { 89 | critical_section::with(|cs| self.0.borrow(cs).borrow().len) 90 | } 91 | } 92 | 93 | impl crate::Inner { 94 | #[cfg(all(feature = "std", not(feature = "critical-section")))] 95 | fn with_inner(&self, f: impl FnOnce(&mut Inner) -> R) -> R { 96 | struct ListLock<'a, 'b, T> { 97 | lock: crate::sync::MutexGuard<'a, Inner>, 98 | inner: &'b crate::Inner, 99 | } 100 | 101 | impl Deref for ListLock<'_, '_, T> { 102 | type Target = Inner; 103 | 104 | fn deref(&self) -> &Self::Target { 105 | &self.lock 106 | } 107 | } 108 | 109 | impl DerefMut for ListLock<'_, '_, T> { 110 | fn deref_mut(&mut self) -> &mut Self::Target { 111 | &mut self.lock 112 | } 113 | } 114 | 115 | impl Drop for ListLock<'_, '_, T> { 116 | fn drop(&mut self) { 117 | update_notified(&self.inner.notified, &self.lock); 118 | } 119 | } 120 | 121 | let mut list = ListLock { 122 | inner: self, 123 | lock: self.list.0.lock().unwrap_or_else(|e| e.into_inner()), 124 | }; 125 | f(&mut list) 126 | } 127 | 128 | #[cfg(feature = "critical-section")] 129 | fn with_inner(&self, f: impl FnOnce(&mut Inner) -> R) -> R { 130 | struct ListWrapper<'a, T> { 131 | inner: &'a crate::Inner, 132 | list: &'a mut Inner, 133 | } 134 | 135 | impl Drop for ListWrapper<'_, T> { 136 | fn drop(&mut self) { 137 | update_notified(&self.inner.notified, self.list); 138 | } 139 | } 140 | 141 | critical_section::with(move |cs| { 142 | let mut list = self.list.0.borrow_ref_mut(cs); 143 | let wrapper = ListWrapper { 144 | inner: self, 145 | list: &mut *list, 146 | }; 147 | 148 | f(wrapper.list) 149 | }) 150 | } 151 | 152 | /// Add a new listener to the list. 153 | pub(crate) fn insert(&self, mut listener: Pin<&mut Option>>) { 154 | self.with_inner(|inner| { 155 | listener.as_mut().set(Some(Listener { 156 | link: UnsafeCell::new(Link { 157 | state: Cell::new(State::Created), 158 | prev: Cell::new(inner.tail), 159 | next: Cell::new(None), 160 | }), 161 | _pin: PhantomPinned, 162 | })); 163 | let listener = listener.as_pin_mut().unwrap(); 164 | 165 | { 166 | let entry_guard = listener.link.get(); 167 | // SAFETY: We are locked, so we can access the inner `link`. 168 | let entry = unsafe { entry_guard.deref() }; 169 | 170 | // Replace the tail with the new entry. 171 | match mem::replace(&mut inner.tail, Some(entry.into())) { 172 | None => inner.head = Some(entry.into()), 173 | Some(t) => unsafe { t.as_ref().next.set(Some(entry.into())) }, 174 | }; 175 | } 176 | 177 | // If there are no unnotified entries, this is the first one. 178 | if inner.next.is_none() { 179 | inner.next = inner.tail; 180 | } 181 | 182 | // Bump the entry count. 183 | inner.len += 1; 184 | }); 185 | } 186 | 187 | /// Remove a listener from the list. 188 | pub(crate) fn remove( 189 | &self, 190 | listener: Pin<&mut Option>>, 191 | propagate: bool, 192 | ) -> Option> { 193 | self.with_inner(|inner| inner.remove(listener, propagate)) 194 | } 195 | 196 | /// Notifies a number of entries. 197 | #[cold] 198 | pub(crate) fn notify(&self, notify: impl Notification) -> usize { 199 | self.with_inner(|inner| inner.notify(notify)) 200 | } 201 | 202 | /// Register a task to be notified when the event is triggered. 203 | /// 204 | /// Returns `true` if the listener was already notified, and `false` otherwise. If the listener 205 | /// isn't inserted, returns `None`. 206 | pub(crate) fn register( 207 | &self, 208 | mut listener: Pin<&mut Option>>, 209 | task: TaskRef<'_>, 210 | ) -> RegisterResult { 211 | self.with_inner(|inner| { 212 | let entry_guard = match listener.as_mut().as_pin_mut() { 213 | Some(listener) => listener.link.get(), 214 | None => return RegisterResult::NeverInserted, 215 | }; 216 | // SAFETY: We are locked, so we can access the inner `link`. 217 | let entry = unsafe { entry_guard.deref() }; 218 | 219 | // Take out the state and check it. 220 | match entry.state.replace(State::NotifiedTaken) { 221 | State::Notified { tag, .. } => { 222 | // We have been notified, remove the listener. 223 | inner.remove(listener, false); 224 | RegisterResult::Notified(tag) 225 | } 226 | 227 | State::Task(other_task) => { 228 | // Only replace the task if it's different. 229 | entry.state.set(State::Task({ 230 | if !task.will_wake(other_task.as_task_ref()) { 231 | task.into_task() 232 | } else { 233 | other_task 234 | } 235 | })); 236 | 237 | RegisterResult::Registered 238 | } 239 | 240 | _ => { 241 | // We have not been notified, register the task. 242 | entry.state.set(State::Task(task.into_task())); 243 | RegisterResult::Registered 244 | } 245 | } 246 | }) 247 | } 248 | } 249 | 250 | impl Inner { 251 | fn remove( 252 | &mut self, 253 | mut listener: Pin<&mut Option>>, 254 | propagate: bool, 255 | ) -> Option> { 256 | let entry_guard = listener.as_mut().as_pin_mut()?.link.get(); 257 | let entry = unsafe { entry_guard.deref() }; 258 | 259 | let prev = entry.prev.get(); 260 | let next = entry.next.get(); 261 | 262 | // Unlink from the previous entry. 263 | match prev { 264 | None => self.head = next, 265 | Some(p) => unsafe { 266 | p.as_ref().next.set(next); 267 | }, 268 | } 269 | 270 | // Unlink from the next entry. 271 | match next { 272 | None => self.tail = prev, 273 | Some(n) => unsafe { 274 | n.as_ref().prev.set(prev); 275 | }, 276 | } 277 | 278 | // If this was the first unnotified entry, update the next pointer. 279 | if self.next == Some(entry.into()) { 280 | self.next = next; 281 | } 282 | 283 | // The entry is now fully unlinked, so we can now take it out safely. 284 | let entry = unsafe { 285 | listener 286 | .get_unchecked_mut() 287 | .take() 288 | .unwrap() 289 | .link 290 | .into_inner() 291 | }; 292 | 293 | // This State::Created is immediately dropped and exists as a workaround for the absence of 294 | // loom::cell::Cell::into_inner. The intent is `let mut state = entry.state.into_inner();` 295 | // 296 | // refs: https://github.com/tokio-rs/loom/pull/341 297 | let mut state = entry.state.replace(State::Created); 298 | 299 | // Update the notified count. 300 | if state.is_notified() { 301 | self.notified -= 1; 302 | 303 | if propagate { 304 | let state = mem::replace(&mut state, State::NotifiedTaken); 305 | if let State::Notified { additional, tag } = state { 306 | let tags = { 307 | let mut tag = Some(tag); 308 | move || tag.take().expect("tag already taken") 309 | }; 310 | self.notify(GenericNotify::new(1, additional, tags)); 311 | } 312 | } 313 | } 314 | self.len -= 1; 315 | 316 | Some(state) 317 | } 318 | 319 | #[cold] 320 | fn notify(&mut self, mut notify: impl Notification) -> usize { 321 | let mut n = notify.count(Internal::new()); 322 | let is_additional = notify.is_additional(Internal::new()); 323 | 324 | if !is_additional { 325 | if n < self.notified { 326 | return 0; 327 | } 328 | n -= self.notified; 329 | } 330 | 331 | let original_count = n; 332 | while n > 0 { 333 | n -= 1; 334 | 335 | // Notify the next entry. 336 | match self.next { 337 | None => return original_count - n - 1, 338 | 339 | Some(e) => { 340 | // Get the entry and move the pointer forwards. 341 | let entry = unsafe { e.as_ref() }; 342 | self.next = entry.next.get(); 343 | 344 | // Set the state to `Notified` and notify. 345 | let tag = notify.next_tag(Internal::new()); 346 | if let State::Task(task) = entry.state.replace(State::Notified { 347 | additional: is_additional, 348 | tag, 349 | }) { 350 | task.wake(); 351 | } 352 | 353 | // Bump the notified count. 354 | self.notified += 1; 355 | } 356 | } 357 | } 358 | 359 | original_count - n 360 | } 361 | } 362 | 363 | fn update_notified(slot: &crate::sync::atomic::AtomicUsize, list: &Inner) { 364 | // Update the notified count. 365 | let notified = if list.notified < list.len { 366 | list.notified 367 | } else { 368 | usize::MAX 369 | }; 370 | 371 | slot.store(notified, Ordering::Release); 372 | } 373 | 374 | pub(crate) struct Listener { 375 | /// The inner link in the linked list. 376 | /// 377 | /// # Safety 378 | /// 379 | /// This can only be accessed while the central mutex is locked. 380 | link: UnsafeCell>, 381 | 382 | /// This listener cannot be moved after being pinned. 383 | _pin: PhantomPinned, 384 | } 385 | 386 | struct Link { 387 | /// The current state of the listener. 388 | state: Cell>, 389 | 390 | /// The previous link in the linked list. 391 | prev: Cell>>>, 392 | 393 | /// The next link in the linked list. 394 | next: Cell>>>, 395 | } 396 | 397 | #[cfg(test)] 398 | mod tests { 399 | use super::*; 400 | use futures_lite::pin; 401 | 402 | #[cfg(target_family = "wasm")] 403 | use wasm_bindgen_test::wasm_bindgen_test as test; 404 | 405 | macro_rules! make_listeners { 406 | ($($id:ident),*) => { 407 | $( 408 | let $id = Option::>::None; 409 | pin!($id); 410 | )* 411 | }; 412 | } 413 | 414 | #[test] 415 | fn insert() { 416 | let inner = crate::Inner::new(); 417 | make_listeners!(listen1, listen2, listen3); 418 | 419 | // Register the listeners. 420 | inner.insert(listen1.as_mut()); 421 | inner.insert(listen2.as_mut()); 422 | inner.insert(listen3.as_mut()); 423 | 424 | assert_eq!(inner.list.try_total_listeners(), Some(3)); 425 | 426 | // Remove one. 427 | assert_eq!(inner.remove(listen2, false), Some(State::Created)); 428 | assert_eq!(inner.list.try_total_listeners(), Some(2)); 429 | 430 | // Remove another. 431 | assert_eq!(inner.remove(listen1, false), Some(State::Created)); 432 | assert_eq!(inner.list.try_total_listeners(), Some(1)); 433 | } 434 | 435 | #[test] 436 | fn drop_non_notified() { 437 | let inner = crate::Inner::new(); 438 | make_listeners!(listen1, listen2, listen3); 439 | 440 | // Register the listeners. 441 | inner.insert(listen1.as_mut()); 442 | inner.insert(listen2.as_mut()); 443 | inner.insert(listen3.as_mut()); 444 | 445 | // Notify one. 446 | inner.notify(GenericNotify::new(1, false, || ())); 447 | 448 | // Remove one. 449 | inner.remove(listen3, true); 450 | 451 | // Remove the rest. 452 | inner.remove(listen1, true); 453 | inner.remove(listen2, true); 454 | } 455 | } 456 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Notify async tasks or threads. 2 | //! 3 | //! This is a synchronization primitive similar to [eventcounts] invented by Dmitry Vyukov. 4 | //! 5 | //! You can use this crate to turn non-blocking data structures into async or blocking data 6 | //! structures. See a [simple mutex] implementation that exposes an async and a blocking interface 7 | //! for acquiring locks. 8 | //! 9 | //! [eventcounts]: https://www.1024cores.net/home/lock-free-algorithms/eventcounts 10 | //! [simple mutex]: https://github.com/smol-rs/event-listener/blob/master/examples/mutex.rs 11 | //! 12 | //! # Examples 13 | //! 14 | //! Wait until another thread sets a boolean flag: 15 | //! 16 | //! ``` 17 | //! use std::sync::atomic::{AtomicBool, Ordering}; 18 | //! use std::sync::Arc; 19 | //! use std::thread; 20 | //! use std::time::Duration; 21 | //! use std::usize; 22 | //! use event_listener::{Event, Listener}; 23 | //! 24 | //! let flag = Arc::new(AtomicBool::new(false)); 25 | //! let event = Arc::new(Event::new()); 26 | //! 27 | //! // Spawn a thread that will set the flag after 1 second. 28 | //! thread::spawn({ 29 | //! let flag = flag.clone(); 30 | //! let event = event.clone(); 31 | //! move || { 32 | //! // Wait for a second. 33 | //! thread::sleep(Duration::from_secs(1)); 34 | //! 35 | //! // Set the flag. 36 | //! flag.store(true, Ordering::SeqCst); 37 | //! 38 | //! // Notify all listeners that the flag has been set. 39 | //! event.notify(usize::MAX); 40 | //! } 41 | //! }); 42 | //! 43 | //! // Wait until the flag is set. 44 | //! loop { 45 | //! // Check the flag. 46 | //! if flag.load(Ordering::SeqCst) { 47 | //! break; 48 | //! } 49 | //! 50 | //! // Start listening for events. 51 | //! let mut listener = event.listen(); 52 | //! 53 | //! // Check the flag again after creating the listener. 54 | //! if flag.load(Ordering::SeqCst) { 55 | //! break; 56 | //! } 57 | //! 58 | //! // Wait for a notification and continue the loop. 59 | //! listener.wait(); 60 | //! } 61 | //! ``` 62 | //! 63 | //! # Features 64 | //! 65 | //! - The `std` feature (enabled by default) enables the use of the Rust standard library. Disable it for `no_std` 66 | //! support. 67 | //! 68 | //! - The `critical-section` feature enables usage of the [`critical-section`] crate to enable a 69 | //! more efficient implementation of `event-listener` for `no_std` platforms. 70 | //! 71 | //! - The `portable-atomic` feature enables the use of the [`portable-atomic`] crate to provide 72 | //! atomic operations on platforms that don't support them. 73 | //! 74 | //! [`critical-section`]: https://crates.io/crates/critical-section 75 | //! [`portable-atomic`]: https://crates.io/crates/portable-atomic 76 | 77 | #![cfg_attr(not(feature = "std"), no_std)] 78 | #![allow(clippy::multiple_bound_locations)] // This is a WONTFIX issue with pin-project-lite 79 | #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] 80 | #![doc( 81 | html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" 82 | )] 83 | #![doc( 84 | html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" 85 | )] 86 | 87 | #[cfg(not(feature = "std"))] 88 | extern crate alloc; 89 | #[cfg(feature = "std")] 90 | extern crate std as alloc; 91 | 92 | #[cfg_attr( 93 | any(feature = "std", feature = "critical-section"), 94 | path = "intrusive.rs" 95 | )] 96 | #[cfg_attr( 97 | not(any(feature = "std", feature = "critical-section")), 98 | path = "slab.rs" 99 | )] 100 | mod sys; 101 | 102 | mod notify; 103 | 104 | #[cfg(not(feature = "std"))] 105 | use alloc::boxed::Box; 106 | 107 | use core::borrow::Borrow; 108 | use core::fmt; 109 | use core::future::Future; 110 | use core::mem::ManuallyDrop; 111 | use core::pin::Pin; 112 | use core::ptr; 113 | use core::task::{Context, Poll, Waker}; 114 | 115 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 116 | use { 117 | parking::{Parker, Unparker}, 118 | std::time::{Duration, Instant}, 119 | }; 120 | 121 | use sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; 122 | use sync::Arc; 123 | 124 | #[cfg(not(loom))] 125 | use sync::WithMut; 126 | 127 | use notify::NotificationPrivate; 128 | pub use notify::{IntoNotification, Notification}; 129 | 130 | /// Inner state of [`Event`]. 131 | struct Inner { 132 | /// The number of notified entries, or `usize::MAX` if all of them have been notified. 133 | /// 134 | /// If there are no entries, this value is set to `usize::MAX`. 135 | notified: AtomicUsize, 136 | 137 | /// Inner queue of event listeners. 138 | /// 139 | /// On `std` platforms, this is an intrusive linked list. On `no_std` platforms, this is a 140 | /// more traditional `Vec` of listeners, with an atomic queue used as a backup for high 141 | /// contention. 142 | list: sys::List, 143 | } 144 | 145 | impl Inner { 146 | fn new() -> Self { 147 | Self { 148 | notified: AtomicUsize::new(usize::MAX), 149 | list: sys::List::new(), 150 | } 151 | } 152 | } 153 | 154 | /// A synchronization primitive for notifying async tasks and threads. 155 | /// 156 | /// Listeners can be registered using [`Event::listen()`]. There are two ways to notify listeners: 157 | /// 158 | /// 1. [`Event::notify()`] notifies a number of listeners. 159 | /// 2. [`Event::notify_additional()`] notifies a number of previously unnotified listeners. 160 | /// 161 | /// If there are no active listeners at the time a notification is sent, it simply gets lost. 162 | /// 163 | /// There are two ways for a listener to wait for a notification: 164 | /// 165 | /// 1. In an asynchronous manner using `.await`. 166 | /// 2. In a blocking manner by calling [`EventListener::wait()`] on it. 167 | /// 168 | /// If a notified listener is dropped without receiving a notification, dropping will notify 169 | /// another active listener. Whether one *additional* listener will be notified depends on what 170 | /// kind of notification was delivered. 171 | /// 172 | /// Listeners are registered and notified in the first-in first-out fashion, ensuring fairness. 173 | pub struct Event { 174 | /// A pointer to heap-allocated inner state. 175 | /// 176 | /// This pointer is initially null and gets lazily initialized on first use. Semantically, it 177 | /// is an `Arc` so it's important to keep in mind that it contributes to the [`Arc`]'s 178 | /// reference count. 179 | inner: AtomicPtr>, 180 | } 181 | 182 | unsafe impl Send for Event {} 183 | unsafe impl Sync for Event {} 184 | 185 | impl core::panic::UnwindSafe for Event {} 186 | impl core::panic::RefUnwindSafe for Event {} 187 | 188 | impl fmt::Debug for Event { 189 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 190 | match self.try_inner() { 191 | Some(inner) => { 192 | let notified_count = inner.notified.load(Ordering::Relaxed); 193 | let total_count = match inner.list.try_total_listeners() { 194 | Some(total_count) => total_count, 195 | None => { 196 | return f 197 | .debug_tuple("Event") 198 | .field(&format_args!("")) 199 | .finish() 200 | } 201 | }; 202 | 203 | f.debug_struct("Event") 204 | .field("listeners_notified", ¬ified_count) 205 | .field("listeners_total", &total_count) 206 | .finish() 207 | } 208 | None => f 209 | .debug_tuple("Event") 210 | .field(&format_args!("")) 211 | .finish(), 212 | } 213 | } 214 | } 215 | 216 | impl Default for Event { 217 | #[inline] 218 | fn default() -> Self { 219 | Self::new() 220 | } 221 | } 222 | 223 | impl Event { 224 | /// Creates a new `Event` with a tag type. 225 | /// 226 | /// Tagging cannot be implemented efficiently on `no_std`, so this is only available when the 227 | /// `std` feature is enabled. 228 | /// 229 | /// # Examples 230 | /// 231 | /// ``` 232 | /// use event_listener::Event; 233 | /// 234 | /// let event = Event::::with_tag(); 235 | /// ``` 236 | #[cfg(all(feature = "std", not(loom)))] 237 | #[inline] 238 | pub const fn with_tag() -> Self { 239 | Self { 240 | inner: AtomicPtr::new(ptr::null_mut()), 241 | } 242 | } 243 | #[cfg(all(feature = "std", loom))] 244 | #[inline] 245 | pub fn with_tag() -> Self { 246 | Self { 247 | inner: AtomicPtr::new(ptr::null_mut()), 248 | } 249 | } 250 | 251 | /// Tell whether any listeners are currently notified. 252 | /// 253 | /// # Examples 254 | /// 255 | /// ``` 256 | /// use event_listener::{Event, Listener}; 257 | /// 258 | /// let event = Event::new(); 259 | /// let listener = event.listen(); 260 | /// assert!(!event.is_notified()); 261 | /// 262 | /// event.notify(1); 263 | /// assert!(event.is_notified()); 264 | /// ``` 265 | #[inline] 266 | pub fn is_notified(&self) -> bool { 267 | self.try_inner() 268 | .map_or(false, |inner| inner.notified.load(Ordering::Acquire) > 0) 269 | } 270 | 271 | /// Returns a guard listening for a notification. 272 | /// 273 | /// This method emits a `SeqCst` fence after registering a listener. For now, this method 274 | /// is an alias for calling [`EventListener::new()`], pinning it to the heap, and then 275 | /// inserting it into a list. 276 | /// 277 | /// # Examples 278 | /// 279 | /// ``` 280 | /// use event_listener::Event; 281 | /// 282 | /// let event = Event::new(); 283 | /// let listener = event.listen(); 284 | /// ``` 285 | /// 286 | /// # Caveats 287 | /// 288 | /// The above example is equivalent to this code: 289 | /// 290 | /// ```no_compile 291 | /// use event_listener::{Event, EventListener}; 292 | /// 293 | /// let event = Event::new(); 294 | /// let mut listener = Box::pin(EventListener::new()); 295 | /// listener.listen(&event); 296 | /// ``` 297 | /// 298 | /// It creates a new listener, pins it to the heap, and inserts it into the linked list 299 | /// of listeners. While this type of usage is simple, it may be desired to eliminate this 300 | /// heap allocation. In this case, consider using the [`EventListener::new`] constructor 301 | /// directly, which allows for greater control over where the [`EventListener`] is 302 | /// allocated. However, users of this `new` method must be careful to ensure that the 303 | /// [`EventListener`] is `listen`ing before waiting on it; panics may occur otherwise. 304 | #[cold] 305 | pub fn listen(&self) -> EventListener { 306 | let inner = ManuallyDrop::new(unsafe { Arc::from_raw(self.inner()) }); 307 | 308 | // Allocate the listener on the heap and insert it. 309 | let mut listener = Box::pin(InnerListener { 310 | event: Arc::clone(&inner), 311 | listener: None, 312 | }); 313 | listener.as_mut().listen(); 314 | 315 | // Return the listener. 316 | EventListener { listener } 317 | } 318 | 319 | /// Notifies a number of active listeners. 320 | /// 321 | /// The number is allowed to be zero or exceed the current number of listeners. 322 | /// 323 | /// The [`Notification`] trait is used to define what kind of notification is delivered. 324 | /// The default implementation (implemented on `usize`) is a notification that only notifies 325 | /// *at least* the specified number of listeners. 326 | /// 327 | /// In certain cases, this function emits a `SeqCst` fence before notifying listeners. 328 | /// 329 | /// This function returns the number of [`EventListener`]s that were notified by this call. 330 | /// 331 | /// # Caveats 332 | /// 333 | /// If the `std` feature is disabled, the notification will be delayed under high contention, 334 | /// such as when another thread is taking a while to `notify` the event. In this circumstance, 335 | /// this function will return `0` instead of the number of listeners actually notified. Therefore 336 | /// if the `std` feature is disabled the return value of this function should not be relied upon 337 | /// for soundness and should be used only as a hint. 338 | /// 339 | /// If the `std` feature is enabled, no spurious returns are possible, since the `std` 340 | /// implementation uses system locking primitives to ensure there is no unavoidable 341 | /// contention. 342 | /// 343 | /// # Examples 344 | /// 345 | /// Use the default notification strategy: 346 | /// 347 | /// ``` 348 | /// use event_listener::Event; 349 | /// 350 | /// let event = Event::new(); 351 | /// 352 | /// // This notification gets lost because there are no listeners. 353 | /// event.notify(1); 354 | /// 355 | /// let listener1 = event.listen(); 356 | /// let listener2 = event.listen(); 357 | /// let listener3 = event.listen(); 358 | /// 359 | /// // Notifies two listeners. 360 | /// // 361 | /// // Listener queueing is fair, which means `listener1` and `listener2` 362 | /// // get notified here since they start listening before `listener3`. 363 | /// event.notify(2); 364 | /// ``` 365 | /// 366 | /// Notify without emitting a `SeqCst` fence. This uses the [`relaxed`] notification strategy. 367 | /// This is equivalent to calling [`Event::notify_relaxed()`]. 368 | /// 369 | /// [`relaxed`]: IntoNotification::relaxed 370 | /// 371 | /// ``` 372 | /// use event_listener::{IntoNotification, Event}; 373 | /// use std::sync::atomic::{self, Ordering}; 374 | /// 375 | /// let event = Event::new(); 376 | /// 377 | /// // This notification gets lost because there are no listeners. 378 | /// event.notify(1.relaxed()); 379 | /// 380 | /// let listener1 = event.listen(); 381 | /// let listener2 = event.listen(); 382 | /// let listener3 = event.listen(); 383 | /// 384 | /// // We should emit a fence manually when using relaxed notifications. 385 | /// atomic::fence(Ordering::SeqCst); 386 | /// 387 | /// // Notifies two listeners. 388 | /// // 389 | /// // Listener queueing is fair, which means `listener1` and `listener2` 390 | /// // get notified here since they start listening before `listener3`. 391 | /// event.notify(2.relaxed()); 392 | /// ``` 393 | /// 394 | /// Notify additional listeners. In contrast to [`Event::notify()`], this method will notify `n` 395 | /// *additional* listeners that were previously unnotified. This uses the [`additional`] 396 | /// notification strategy. This is equivalent to calling [`Event::notify_additional()`]. 397 | /// 398 | /// [`additional`]: IntoNotification::additional 399 | /// 400 | /// ``` 401 | /// use event_listener::{IntoNotification, Event}; 402 | /// 403 | /// let event = Event::new(); 404 | /// 405 | /// // This notification gets lost because there are no listeners. 406 | /// event.notify(1.additional()); 407 | /// 408 | /// let listener1 = event.listen(); 409 | /// let listener2 = event.listen(); 410 | /// let listener3 = event.listen(); 411 | /// 412 | /// // Notifies two listeners. 413 | /// // 414 | /// // Listener queueing is fair, which means `listener1` and `listener2` 415 | /// // get notified here since they start listening before `listener3`. 416 | /// event.notify(1.additional()); 417 | /// event.notify(1.additional()); 418 | /// ``` 419 | /// 420 | /// Notifies with the [`additional`] and [`relaxed`] strategies at the same time. This is 421 | /// equivalent to calling [`Event::notify_additional_relaxed()`]. 422 | /// 423 | /// ``` 424 | /// use event_listener::{IntoNotification, Event}; 425 | /// use std::sync::atomic::{self, Ordering}; 426 | /// 427 | /// let event = Event::new(); 428 | /// 429 | /// // This notification gets lost because there are no listeners. 430 | /// event.notify(1.additional().relaxed()); 431 | /// 432 | /// let listener1 = event.listen(); 433 | /// let listener2 = event.listen(); 434 | /// let listener3 = event.listen(); 435 | /// 436 | /// // We should emit a fence manually when using relaxed notifications. 437 | /// atomic::fence(Ordering::SeqCst); 438 | /// 439 | /// // Notifies two listeners. 440 | /// // 441 | /// // Listener queueing is fair, which means `listener1` and `listener2` 442 | /// // get notified here since they start listening before `listener3`. 443 | /// event.notify(1.additional().relaxed()); 444 | /// event.notify(1.additional().relaxed()); 445 | /// ``` 446 | #[inline] 447 | pub fn notify(&self, notify: impl IntoNotification) -> usize { 448 | let notify = notify.into_notification(); 449 | 450 | // Make sure the notification comes after whatever triggered it. 451 | notify.fence(notify::Internal::new()); 452 | 453 | let inner = unsafe { &*self.inner() }; 454 | inner.notify(notify) 455 | } 456 | 457 | /// Return a reference to the inner state if it has been initialized. 458 | #[inline] 459 | fn try_inner(&self) -> Option<&Inner> { 460 | let inner = self.inner.load(Ordering::Acquire); 461 | unsafe { inner.as_ref() } 462 | } 463 | 464 | /// Returns a raw, initialized pointer to the inner state. 465 | /// 466 | /// This returns a raw pointer instead of reference because `from_raw` 467 | /// requires raw/mut provenance: . 468 | fn inner(&self) -> *const Inner { 469 | let mut inner = self.inner.load(Ordering::Acquire); 470 | 471 | // If this is the first use, initialize the state. 472 | if inner.is_null() { 473 | // Allocate the state on the heap. 474 | let new = Arc::new(Inner::::new()); 475 | 476 | // Convert the state to a raw pointer. 477 | let new = Arc::into_raw(new) as *mut Inner; 478 | 479 | // Replace the null pointer with the new state pointer. 480 | inner = self 481 | .inner 482 | .compare_exchange(inner, new, Ordering::AcqRel, Ordering::Acquire) 483 | .unwrap_or_else(|x| x); 484 | 485 | // Check if the old pointer value was indeed null. 486 | if inner.is_null() { 487 | // If yes, then use the new state pointer. 488 | inner = new; 489 | } else { 490 | // If not, that means a concurrent operation has initialized the state. 491 | // In that case, use the old pointer and deallocate the new one. 492 | unsafe { 493 | drop(Arc::from_raw(new)); 494 | } 495 | } 496 | } 497 | 498 | inner 499 | } 500 | 501 | /// Get the number of listeners currently listening to this [`Event`]. 502 | /// 503 | /// This call returns the number of [`EventListener`]s that are currently listening to 504 | /// this event. It does this by acquiring the internal event lock and reading the listener 505 | /// count. Therefore it is only available for `std`-enabled platforms. 506 | /// 507 | /// # Caveats 508 | /// 509 | /// This function returns just a snapshot of the number of listeners at this point in time. 510 | /// Due to the nature of multi-threaded CPUs, it is possible that this number will be 511 | /// inaccurate by the time that this function returns. 512 | /// 513 | /// It is possible for the actual number to change at any point. Therefore, the number should 514 | /// only ever be used as a hint. 515 | /// 516 | /// # Examples 517 | /// 518 | /// ``` 519 | /// use event_listener::Event; 520 | /// 521 | /// let event = Event::new(); 522 | /// 523 | /// assert_eq!(event.total_listeners(), 0); 524 | /// 525 | /// let listener1 = event.listen(); 526 | /// assert_eq!(event.total_listeners(), 1); 527 | /// 528 | /// let listener2 = event.listen(); 529 | /// assert_eq!(event.total_listeners(), 2); 530 | /// 531 | /// drop(listener1); 532 | /// drop(listener2); 533 | /// assert_eq!(event.total_listeners(), 0); 534 | /// ``` 535 | #[cfg(feature = "std")] 536 | #[inline] 537 | pub fn total_listeners(&self) -> usize { 538 | if let Some(inner) = self.try_inner() { 539 | inner.list.total_listeners() 540 | } else { 541 | 0 542 | } 543 | } 544 | } 545 | 546 | impl Event<()> { 547 | /// Creates a new [`Event`]. 548 | /// 549 | /// # Examples 550 | /// 551 | /// ``` 552 | /// use event_listener::Event; 553 | /// 554 | /// let event = Event::new(); 555 | /// ``` 556 | #[inline] 557 | #[cfg(not(loom))] 558 | pub const fn new() -> Self { 559 | Self { 560 | inner: AtomicPtr::new(ptr::null_mut()), 561 | } 562 | } 563 | 564 | #[inline] 565 | #[cfg(loom)] 566 | pub fn new() -> Self { 567 | Self { 568 | inner: AtomicPtr::new(ptr::null_mut()), 569 | } 570 | } 571 | 572 | /// Notifies a number of active listeners without emitting a `SeqCst` fence. 573 | /// 574 | /// The number is allowed to be zero or exceed the current number of listeners. 575 | /// 576 | /// In contrast to [`Event::notify_additional()`], this method only makes sure *at least* `n` 577 | /// listeners among the active ones are notified. 578 | /// 579 | /// Unlike [`Event::notify()`], this method does not emit a `SeqCst` fence. 580 | /// 581 | /// This method only works for untagged events. In other cases, it is recommended to instead 582 | /// use [`Event::notify()`] like so: 583 | /// 584 | /// ``` 585 | /// use event_listener::{IntoNotification, Event}; 586 | /// let event = Event::new(); 587 | /// 588 | /// // Old way: 589 | /// event.notify_relaxed(1); 590 | /// 591 | /// // New way: 592 | /// event.notify(1.relaxed()); 593 | /// ``` 594 | /// 595 | /// # Examples 596 | /// 597 | /// ``` 598 | /// use event_listener::{Event, IntoNotification}; 599 | /// use std::sync::atomic::{self, Ordering}; 600 | /// 601 | /// let event = Event::new(); 602 | /// 603 | /// // This notification gets lost because there are no listeners. 604 | /// event.notify_relaxed(1); 605 | /// 606 | /// let listener1 = event.listen(); 607 | /// let listener2 = event.listen(); 608 | /// let listener3 = event.listen(); 609 | /// 610 | /// // We should emit a fence manually when using relaxed notifications. 611 | /// atomic::fence(Ordering::SeqCst); 612 | /// 613 | /// // Notifies two listeners. 614 | /// // 615 | /// // Listener queueing is fair, which means `listener1` and `listener2` 616 | /// // get notified here since they start listening before `listener3`. 617 | /// event.notify_relaxed(2); 618 | /// ``` 619 | #[inline] 620 | pub fn notify_relaxed(&self, n: usize) -> usize { 621 | self.notify(n.relaxed()) 622 | } 623 | 624 | /// Notifies a number of active and still unnotified listeners. 625 | /// 626 | /// The number is allowed to be zero or exceed the current number of listeners. 627 | /// 628 | /// In contrast to [`Event::notify()`], this method will notify `n` *additional* listeners that 629 | /// were previously unnotified. 630 | /// 631 | /// This method emits a `SeqCst` fence before notifying listeners. 632 | /// 633 | /// This method only works for untagged events. In other cases, it is recommended to instead 634 | /// use [`Event::notify()`] like so: 635 | /// 636 | /// ``` 637 | /// use event_listener::{IntoNotification, Event}; 638 | /// let event = Event::new(); 639 | /// 640 | /// // Old way: 641 | /// event.notify_additional(1); 642 | /// 643 | /// // New way: 644 | /// event.notify(1.additional()); 645 | /// ``` 646 | /// 647 | /// # Examples 648 | /// 649 | /// ``` 650 | /// use event_listener::Event; 651 | /// 652 | /// let event = Event::new(); 653 | /// 654 | /// // This notification gets lost because there are no listeners. 655 | /// event.notify_additional(1); 656 | /// 657 | /// let listener1 = event.listen(); 658 | /// let listener2 = event.listen(); 659 | /// let listener3 = event.listen(); 660 | /// 661 | /// // Notifies two listeners. 662 | /// // 663 | /// // Listener queueing is fair, which means `listener1` and `listener2` 664 | /// // get notified here since they start listening before `listener3`. 665 | /// event.notify_additional(1); 666 | /// event.notify_additional(1); 667 | /// ``` 668 | #[inline] 669 | pub fn notify_additional(&self, n: usize) -> usize { 670 | self.notify(n.additional()) 671 | } 672 | 673 | /// Notifies a number of active and still unnotified listeners without emitting a `SeqCst` 674 | /// fence. 675 | /// 676 | /// The number is allowed to be zero or exceed the current number of listeners. 677 | /// 678 | /// In contrast to [`Event::notify()`], this method will notify `n` *additional* listeners that 679 | /// were previously unnotified. 680 | /// 681 | /// Unlike [`Event::notify_additional()`], this method does not emit a `SeqCst` fence. 682 | /// 683 | /// This method only works for untagged events. In other cases, it is recommended to instead 684 | /// use [`Event::notify()`] like so: 685 | /// 686 | /// ``` 687 | /// use event_listener::{IntoNotification, Event}; 688 | /// let event = Event::new(); 689 | /// 690 | /// // Old way: 691 | /// event.notify_additional_relaxed(1); 692 | /// 693 | /// // New way: 694 | /// event.notify(1.additional().relaxed()); 695 | /// ``` 696 | /// 697 | /// # Examples 698 | /// 699 | /// ``` 700 | /// use event_listener::Event; 701 | /// use std::sync::atomic::{self, Ordering}; 702 | /// 703 | /// let event = Event::new(); 704 | /// 705 | /// // This notification gets lost because there are no listeners. 706 | /// event.notify(1); 707 | /// 708 | /// let listener1 = event.listen(); 709 | /// let listener2 = event.listen(); 710 | /// let listener3 = event.listen(); 711 | /// 712 | /// // We should emit a fence manually when using relaxed notifications. 713 | /// atomic::fence(Ordering::SeqCst); 714 | /// 715 | /// // Notifies two listeners. 716 | /// // 717 | /// // Listener queueing is fair, which means `listener1` and `listener2` 718 | /// // get notified here since they start listening before `listener3`. 719 | /// event.notify_additional_relaxed(1); 720 | /// event.notify_additional_relaxed(1); 721 | /// ``` 722 | #[inline] 723 | pub fn notify_additional_relaxed(&self, n: usize) -> usize { 724 | self.notify(n.additional().relaxed()) 725 | } 726 | } 727 | 728 | impl Drop for Event { 729 | #[inline] 730 | fn drop(&mut self) { 731 | self.inner.with_mut(|&mut inner| { 732 | // If the state pointer has been initialized, drop it. 733 | if !inner.is_null() { 734 | unsafe { 735 | drop(Arc::from_raw(inner)); 736 | } 737 | } 738 | }) 739 | } 740 | } 741 | 742 | /// A handle that is listening to an [`Event`]. 743 | /// 744 | /// This trait represents a type waiting for a notification from an [`Event`]. See the 745 | /// [`EventListener`] type for more documentation on this trait's usage. 746 | pub trait Listener: Future + __sealed::Sealed { 747 | /// Blocks until a notification is received. 748 | /// 749 | /// # Examples 750 | /// 751 | /// ``` 752 | /// use event_listener::{Event, Listener}; 753 | /// 754 | /// let event = Event::new(); 755 | /// let mut listener = event.listen(); 756 | /// 757 | /// // Notify `listener`. 758 | /// event.notify(1); 759 | /// 760 | /// // Receive the notification. 761 | /// listener.wait(); 762 | /// ``` 763 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 764 | fn wait(self) -> T; 765 | 766 | /// Blocks until a notification is received or a timeout is reached. 767 | /// 768 | /// Returns `Some` if a notification was received. 769 | /// 770 | /// # Examples 771 | /// 772 | /// ``` 773 | /// use std::time::Duration; 774 | /// use event_listener::{Event, Listener}; 775 | /// 776 | /// let event = Event::new(); 777 | /// let mut listener = event.listen(); 778 | /// 779 | /// // There are no notification so this times out. 780 | /// assert!(listener.wait_timeout(Duration::from_secs(1)).is_none()); 781 | /// ``` 782 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 783 | fn wait_timeout(self, timeout: Duration) -> Option; 784 | 785 | /// Blocks until a notification is received or a deadline is reached. 786 | /// 787 | /// Returns `true` if a notification was received. 788 | /// 789 | /// # Examples 790 | /// 791 | /// ``` 792 | /// use std::time::{Duration, Instant}; 793 | /// use event_listener::{Event, Listener}; 794 | /// 795 | /// let event = Event::new(); 796 | /// let mut listener = event.listen(); 797 | /// 798 | /// // There are no notification so this times out. 799 | /// assert!(listener.wait_deadline(Instant::now() + Duration::from_secs(1)).is_none()); 800 | /// ``` 801 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 802 | fn wait_deadline(self, deadline: Instant) -> Option; 803 | 804 | /// Drops this listener and discards its notification (if any) without notifying another 805 | /// active listener. 806 | /// 807 | /// Returns `true` if a notification was discarded. 808 | /// 809 | /// # Examples 810 | /// 811 | /// ``` 812 | /// use event_listener::{Event, Listener}; 813 | /// 814 | /// let event = Event::new(); 815 | /// let mut listener1 = event.listen(); 816 | /// let mut listener2 = event.listen(); 817 | /// 818 | /// event.notify(1); 819 | /// 820 | /// assert!(listener1.discard()); 821 | /// assert!(!listener2.discard()); 822 | /// ``` 823 | fn discard(self) -> bool; 824 | 825 | /// Returns `true` if this listener listens to the given `Event`. 826 | /// 827 | /// # Examples 828 | /// 829 | /// ``` 830 | /// use event_listener::{Event, Listener}; 831 | /// 832 | /// let event = Event::new(); 833 | /// let listener = event.listen(); 834 | /// 835 | /// assert!(listener.listens_to(&event)); 836 | /// ``` 837 | fn listens_to(&self, event: &Event) -> bool; 838 | 839 | /// Returns `true` if both listeners listen to the same `Event`. 840 | /// 841 | /// # Examples 842 | /// 843 | /// ``` 844 | /// use event_listener::{Event, Listener}; 845 | /// 846 | /// let event = Event::new(); 847 | /// let listener1 = event.listen(); 848 | /// let listener2 = event.listen(); 849 | /// 850 | /// assert!(listener1.same_event(&listener2)); 851 | /// ``` 852 | fn same_event(&self, other: &Self) -> bool; 853 | } 854 | 855 | /// Implement the `Listener` trait using the underlying `InnerListener`. 856 | macro_rules! forward_impl_to_listener { 857 | ($gen:ident => $ty:ty) => { 858 | impl<$gen> crate::Listener<$gen> for $ty { 859 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 860 | fn wait(mut self) -> $gen { 861 | self.listener_mut().wait_internal(None).unwrap() 862 | } 863 | 864 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 865 | fn wait_timeout(mut self, timeout: std::time::Duration) -> Option<$gen> { 866 | self.listener_mut() 867 | .wait_internal(std::time::Instant::now().checked_add(timeout)) 868 | } 869 | 870 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 871 | fn wait_deadline(mut self, deadline: std::time::Instant) -> Option<$gen> { 872 | self.listener_mut().wait_internal(Some(deadline)) 873 | } 874 | 875 | fn discard(mut self) -> bool { 876 | self.listener_mut().discard() 877 | } 878 | 879 | #[inline] 880 | fn listens_to(&self, event: &Event<$gen>) -> bool { 881 | core::ptr::eq::>( 882 | &*self.listener().event, 883 | event.inner.load(core::sync::atomic::Ordering::Acquire), 884 | ) 885 | } 886 | 887 | #[inline] 888 | fn same_event(&self, other: &$ty) -> bool { 889 | core::ptr::eq::>(&*self.listener().event, &*other.listener().event) 890 | } 891 | } 892 | 893 | impl<$gen> Future for $ty { 894 | type Output = $gen; 895 | 896 | #[inline] 897 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<$gen> { 898 | self.listener_mut().poll_internal(cx) 899 | } 900 | } 901 | }; 902 | } 903 | 904 | /// A guard waiting for a notification from an [`Event`]. 905 | /// 906 | /// There are two ways for a listener to wait for a notification: 907 | /// 908 | /// 1. In an asynchronous manner using `.await`. 909 | /// 2. In a blocking manner by calling [`EventListener::wait()`] on it. 910 | /// 911 | /// If a notified listener is dropped without receiving a notification, dropping will notify 912 | /// another active listener. Whether one *additional* listener will be notified depends on what 913 | /// kind of notification was delivered. 914 | /// 915 | /// See the [`Listener`] trait for the functionality exposed by this type. 916 | /// 917 | /// This structure allocates the listener on the heap. 918 | pub struct EventListener { 919 | listener: Pin>>>>, 920 | } 921 | 922 | unsafe impl Send for EventListener {} 923 | unsafe impl Sync for EventListener {} 924 | 925 | impl core::panic::UnwindSafe for EventListener {} 926 | impl core::panic::RefUnwindSafe for EventListener {} 927 | impl Unpin for EventListener {} 928 | 929 | impl fmt::Debug for EventListener { 930 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 931 | f.debug_struct("EventListener").finish_non_exhaustive() 932 | } 933 | } 934 | 935 | impl EventListener { 936 | #[inline] 937 | fn listener(&self) -> &InnerListener>> { 938 | &self.listener 939 | } 940 | 941 | #[inline] 942 | fn listener_mut(&mut self) -> Pin<&mut InnerListener>>> { 943 | self.listener.as_mut() 944 | } 945 | } 946 | 947 | forward_impl_to_listener! { T => EventListener } 948 | 949 | /// Create a stack-based event listener for an [`Event`]. 950 | /// 951 | /// [`EventListener`] allocates the listener on the heap. While this works for most use cases, in 952 | /// practice this heap allocation can be expensive for repeated uses. This method allows for 953 | /// allocating the listener on the stack instead. 954 | /// 955 | /// There are limitations to using this macro instead of the [`EventListener`] type, however. 956 | /// Firstly, it is significantly less flexible. The listener is locked to the current stack 957 | /// frame, meaning that it can't be returned or put into a place where it would go out of 958 | /// scope. For instance, this will not work: 959 | /// 960 | /// ```compile_fail 961 | /// use event_listener::{Event, Listener, listener}; 962 | /// 963 | /// fn get_listener(event: &Event) -> impl Listener { 964 | /// listener!(event => cant_return_this); 965 | /// cant_return_this 966 | /// } 967 | /// ``` 968 | /// 969 | /// In addition, the types involved in creating this listener are not able to be named. Therefore 970 | /// it cannot be used in hand-rolled futures or similar structures. 971 | /// 972 | /// The type created by this macro implements [`Listener`], allowing it to be used in cases where 973 | /// [`EventListener`] would normally be used. 974 | /// 975 | /// ## Example 976 | /// 977 | /// To use this macro, replace cases where you would normally use this... 978 | /// 979 | /// ```no_compile 980 | /// let listener = event.listen(); 981 | /// ``` 982 | /// 983 | /// ...with this: 984 | /// 985 | /// ```no_compile 986 | /// listener!(event => listener); 987 | /// ``` 988 | /// 989 | /// Here is the top level example from this crate's documentation, but using [`listener`] instead 990 | /// of [`EventListener`]. 991 | /// 992 | /// ``` 993 | /// use std::sync::atomic::{AtomicBool, Ordering}; 994 | /// use std::sync::Arc; 995 | /// use std::thread; 996 | /// use std::time::Duration; 997 | /// use std::usize; 998 | /// use event_listener::{Event, listener, IntoNotification, Listener}; 999 | /// 1000 | /// let flag = Arc::new(AtomicBool::new(false)); 1001 | /// let event = Arc::new(Event::new()); 1002 | /// 1003 | /// // Spawn a thread that will set the flag after 1 second. 1004 | /// thread::spawn({ 1005 | /// let flag = flag.clone(); 1006 | /// let event = event.clone(); 1007 | /// move || { 1008 | /// // Wait for a second. 1009 | /// thread::sleep(Duration::from_secs(1)); 1010 | /// 1011 | /// // Set the flag. 1012 | /// flag.store(true, Ordering::SeqCst); 1013 | /// 1014 | /// // Notify all listeners that the flag has been set. 1015 | /// event.notify(usize::MAX); 1016 | /// } 1017 | /// }); 1018 | /// 1019 | /// // Wait until the flag is set. 1020 | /// loop { 1021 | /// // Check the flag. 1022 | /// if flag.load(Ordering::SeqCst) { 1023 | /// break; 1024 | /// } 1025 | /// 1026 | /// // Start listening for events. 1027 | /// // NEW: Changed to a stack-based listener. 1028 | /// listener!(event => listener); 1029 | /// 1030 | /// // Check the flag again after creating the listener. 1031 | /// if flag.load(Ordering::SeqCst) { 1032 | /// break; 1033 | /// } 1034 | /// 1035 | /// // Wait for a notification and continue the loop. 1036 | /// listener.wait(); 1037 | /// } 1038 | /// ``` 1039 | #[macro_export] 1040 | macro_rules! listener { 1041 | ($event:expr => $listener:ident) => { 1042 | let mut $listener = $crate::__private::StackSlot::new(&$event); 1043 | // SAFETY: We shadow $listener so it can't be moved after. 1044 | let mut $listener = unsafe { $crate::__private::Pin::new_unchecked(&mut $listener) }; 1045 | #[allow(unused_mut)] 1046 | let mut $listener = $listener.listen(); 1047 | }; 1048 | } 1049 | 1050 | pin_project_lite::pin_project! { 1051 | #[project(!Unpin)] 1052 | #[project = ListenerProject] 1053 | struct InnerListener>> 1054 | where 1055 | B: Unpin, 1056 | { 1057 | // The reference to the original event. 1058 | event: B, 1059 | 1060 | // The inner state of the listener. 1061 | // 1062 | // This is only ever `None` during initialization. After `listen()` has completed, this 1063 | // should be `Some`. 1064 | #[pin] 1065 | listener: Option>, 1066 | } 1067 | 1068 | impl>> PinnedDrop for InnerListener 1069 | where 1070 | B: Unpin, 1071 | { 1072 | fn drop(mut this: Pin<&mut Self>) { 1073 | // If we're being dropped, we need to remove ourself from the list. 1074 | let this = this.project(); 1075 | (*this.event).borrow().remove(this.listener, true); 1076 | } 1077 | } 1078 | } 1079 | 1080 | unsafe impl> + Unpin + Send> Send for InnerListener {} 1081 | unsafe impl> + Unpin + Sync> Sync for InnerListener {} 1082 | 1083 | impl> + Unpin> InnerListener { 1084 | /// Insert this listener into the linked list. 1085 | #[inline] 1086 | fn listen(self: Pin<&mut Self>) { 1087 | let this = self.project(); 1088 | (*this.event).borrow().insert(this.listener); 1089 | } 1090 | 1091 | /// Wait until the provided deadline. 1092 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 1093 | fn wait_internal(mut self: Pin<&mut Self>, deadline: Option) -> Option { 1094 | fn parker_and_task() -> (Parker, Task) { 1095 | let parker = Parker::new(); 1096 | let unparker = parker.unparker(); 1097 | (parker, Task::Unparker(unparker)) 1098 | } 1099 | 1100 | crate::sync::thread_local! { 1101 | /// Cached thread-local parker/unparker pair. 1102 | static PARKER: (Parker, Task) = parker_and_task(); 1103 | } 1104 | 1105 | // Try to borrow the thread-local parker/unparker pair. 1106 | PARKER 1107 | .try_with({ 1108 | let this = self.as_mut(); 1109 | |(parker, unparker)| this.wait_with_parker(deadline, parker, unparker.as_task_ref()) 1110 | }) 1111 | .unwrap_or_else(|_| { 1112 | // If the pair isn't accessible, we may be being called in a destructor. 1113 | // Just create a new pair. 1114 | let (parker, unparker) = parking::pair(); 1115 | self.as_mut() 1116 | .wait_with_parker(deadline, &parker, TaskRef::Unparker(&unparker)) 1117 | }) 1118 | } 1119 | 1120 | /// Wait until the provided deadline using the specified parker/unparker pair. 1121 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 1122 | fn wait_with_parker( 1123 | self: Pin<&mut Self>, 1124 | deadline: Option, 1125 | parker: &Parker, 1126 | unparker: TaskRef<'_>, 1127 | ) -> Option { 1128 | let mut this = self.project(); 1129 | let inner = (*this.event).borrow(); 1130 | 1131 | // Set the listener's state to `Task`. 1132 | if let Some(tag) = inner.register(this.listener.as_mut(), unparker).notified() { 1133 | // We were already notified, so we don't need to park. 1134 | return Some(tag); 1135 | } 1136 | 1137 | // Wait until a notification is received or the timeout is reached. 1138 | loop { 1139 | match deadline { 1140 | None => parker.park(), 1141 | 1142 | #[cfg(loom)] 1143 | Some(_deadline) => { 1144 | panic!("parking does not support timeouts under loom"); 1145 | } 1146 | 1147 | #[cfg(not(loom))] 1148 | Some(deadline) => { 1149 | // Make sure we're not timed out already. 1150 | let now = Instant::now(); 1151 | if now >= deadline { 1152 | // Remove our entry and check if we were notified. 1153 | return inner 1154 | .remove(this.listener.as_mut(), false) 1155 | .expect("We never removed ourself from the list") 1156 | .notified(); 1157 | } 1158 | parker.park_deadline(deadline); 1159 | } 1160 | } 1161 | 1162 | // See if we were notified. 1163 | if let Some(tag) = inner.register(this.listener.as_mut(), unparker).notified() { 1164 | return Some(tag); 1165 | } 1166 | } 1167 | } 1168 | 1169 | /// Drops this listener and discards its notification (if any) without notifying another 1170 | /// active listener. 1171 | fn discard(self: Pin<&mut Self>) -> bool { 1172 | let this = self.project(); 1173 | (*this.event) 1174 | .borrow() 1175 | .remove(this.listener, false) 1176 | .map_or(false, |state| state.is_notified()) 1177 | } 1178 | 1179 | /// Poll this listener for a notification. 1180 | fn poll_internal(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 1181 | let this = self.project(); 1182 | let inner = (*this.event).borrow(); 1183 | 1184 | // Try to register the listener. 1185 | match inner 1186 | .register(this.listener, TaskRef::Waker(cx.waker())) 1187 | .notified() 1188 | { 1189 | Some(tag) => { 1190 | // We were already notified, so we don't need to park. 1191 | Poll::Ready(tag) 1192 | } 1193 | 1194 | None => { 1195 | // We're now waiting for a notification. 1196 | Poll::Pending 1197 | } 1198 | } 1199 | } 1200 | } 1201 | 1202 | /// The state of a listener. 1203 | #[derive(PartialEq)] 1204 | enum State { 1205 | /// The listener was just created. 1206 | Created, 1207 | 1208 | /// The listener has received a notification. 1209 | /// 1210 | /// The `bool` is `true` if this was an "additional" notification. 1211 | Notified { 1212 | /// Whether or not this is an "additional" notification. 1213 | additional: bool, 1214 | 1215 | /// The tag associated with the notification. 1216 | tag: T, 1217 | }, 1218 | 1219 | /// A task is waiting for a notification. 1220 | Task(Task), 1221 | 1222 | /// Empty hole used to replace a notified listener. 1223 | NotifiedTaken, 1224 | } 1225 | 1226 | impl fmt::Debug for State { 1227 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1228 | match self { 1229 | Self::Created => f.write_str("Created"), 1230 | Self::Notified { additional, .. } => f 1231 | .debug_struct("Notified") 1232 | .field("additional", additional) 1233 | .finish(), 1234 | Self::Task(_) => f.write_str("Task(_)"), 1235 | Self::NotifiedTaken => f.write_str("NotifiedTaken"), 1236 | } 1237 | } 1238 | } 1239 | 1240 | impl State { 1241 | fn is_notified(&self) -> bool { 1242 | matches!(self, Self::Notified { .. } | Self::NotifiedTaken) 1243 | } 1244 | 1245 | /// If this state was notified, return the tag associated with the notification. 1246 | #[allow(unused)] 1247 | fn notified(self) -> Option { 1248 | match self { 1249 | Self::Notified { tag, .. } => Some(tag), 1250 | Self::NotifiedTaken => panic!("listener was already notified but taken"), 1251 | _ => None, 1252 | } 1253 | } 1254 | } 1255 | 1256 | /// The result of registering a listener. 1257 | #[derive(Debug, PartialEq)] 1258 | enum RegisterResult { 1259 | /// The listener was already notified. 1260 | Notified(T), 1261 | 1262 | /// The listener has been registered. 1263 | Registered, 1264 | 1265 | /// The listener was never inserted into the list. 1266 | NeverInserted, 1267 | } 1268 | 1269 | impl RegisterResult { 1270 | /// Whether or not the listener was notified. 1271 | /// 1272 | /// Panics if the listener was never inserted into the list. 1273 | fn notified(self) -> Option { 1274 | match self { 1275 | Self::Notified(tag) => Some(tag), 1276 | Self::Registered => None, 1277 | Self::NeverInserted => panic!("{}", NEVER_INSERTED_PANIC), 1278 | } 1279 | } 1280 | } 1281 | 1282 | /// A task that can be woken up. 1283 | #[derive(Debug, Clone)] 1284 | enum Task { 1285 | /// A waker that wakes up a future. 1286 | Waker(Waker), 1287 | 1288 | /// An unparker that wakes up a thread. 1289 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 1290 | Unparker(Unparker), 1291 | } 1292 | 1293 | impl Task { 1294 | fn as_task_ref(&self) -> TaskRef<'_> { 1295 | match self { 1296 | Self::Waker(waker) => TaskRef::Waker(waker), 1297 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 1298 | Self::Unparker(unparker) => TaskRef::Unparker(unparker), 1299 | } 1300 | } 1301 | 1302 | fn wake(self) { 1303 | match self { 1304 | Self::Waker(waker) => waker.wake(), 1305 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 1306 | Self::Unparker(unparker) => { 1307 | unparker.unpark(); 1308 | } 1309 | } 1310 | } 1311 | } 1312 | 1313 | impl PartialEq for Task { 1314 | fn eq(&self, other: &Self) -> bool { 1315 | self.as_task_ref().will_wake(other.as_task_ref()) 1316 | } 1317 | } 1318 | 1319 | /// A reference to a task. 1320 | #[derive(Clone, Copy)] 1321 | enum TaskRef<'a> { 1322 | /// A waker that wakes up a future. 1323 | Waker(&'a Waker), 1324 | 1325 | /// An unparker that wakes up a thread. 1326 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 1327 | Unparker(&'a Unparker), 1328 | } 1329 | 1330 | impl TaskRef<'_> { 1331 | /// Tells if this task will wake up the other task. 1332 | #[allow(unreachable_patterns)] 1333 | fn will_wake(self, other: Self) -> bool { 1334 | match (self, other) { 1335 | (Self::Waker(a), Self::Waker(b)) => a.will_wake(b), 1336 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 1337 | (Self::Unparker(_), Self::Unparker(_)) => { 1338 | // TODO: Use unreleased will_unpark API. 1339 | false 1340 | } 1341 | _ => false, 1342 | } 1343 | } 1344 | 1345 | /// Converts this task reference to a task by cloning. 1346 | fn into_task(self) -> Task { 1347 | match self { 1348 | Self::Waker(waker) => Task::Waker(waker.clone()), 1349 | #[cfg(all(feature = "std", not(target_family = "wasm")))] 1350 | Self::Unparker(unparker) => Task::Unparker(unparker.clone()), 1351 | } 1352 | } 1353 | } 1354 | 1355 | const NEVER_INSERTED_PANIC: &str = "\ 1356 | EventListener was not inserted into the linked list, make sure you're not polling \ 1357 | EventListener/listener! after it has finished"; 1358 | 1359 | #[cfg(not(loom))] 1360 | /// Synchronization primitive implementation. 1361 | mod sync { 1362 | #[cfg(not(feature = "portable-atomic"))] 1363 | pub(super) use alloc::sync::Arc; 1364 | #[cfg(not(feature = "portable-atomic"))] 1365 | pub(super) use core::sync::atomic; 1366 | 1367 | #[cfg(feature = "portable-atomic")] 1368 | pub(super) use portable_atomic_crate as atomic; 1369 | #[cfg(feature = "portable-atomic")] 1370 | pub(super) use portable_atomic_util::Arc; 1371 | 1372 | #[allow(unused)] 1373 | #[cfg(all(feature = "std", not(feature = "critical-section"), not(loom)))] 1374 | pub(super) use std::sync::{Mutex, MutexGuard}; 1375 | #[cfg(all(feature = "std", not(target_family = "wasm"), not(loom)))] 1376 | pub(super) use std::thread_local; 1377 | 1378 | pub(super) trait WithMut { 1379 | type Output; 1380 | 1381 | fn with_mut(&mut self, f: F) -> R 1382 | where 1383 | F: FnOnce(&mut Self::Output) -> R; 1384 | } 1385 | 1386 | impl WithMut for atomic::AtomicPtr { 1387 | type Output = *mut T; 1388 | 1389 | #[inline] 1390 | fn with_mut(&mut self, f: F) -> R 1391 | where 1392 | F: FnOnce(&mut Self::Output) -> R, 1393 | { 1394 | f(self.get_mut()) 1395 | } 1396 | } 1397 | 1398 | pub(crate) mod cell { 1399 | pub(crate) use core::cell::Cell; 1400 | 1401 | /// This newtype around *mut T exists for interoperability with loom::cell::ConstPtr, 1402 | /// which works as a guard and performs additional logic to track access scope. 1403 | pub(crate) struct ConstPtr(*mut T); 1404 | impl ConstPtr { 1405 | pub(crate) unsafe fn deref(&self) -> &T { 1406 | &*self.0 1407 | } 1408 | 1409 | #[allow(unused)] // std code does not need this 1410 | pub(crate) unsafe fn deref_mut(&mut self) -> &mut T { 1411 | &mut *self.0 1412 | } 1413 | } 1414 | 1415 | /// This UnsafeCell wrapper exists for interoperability with loom::cell::UnsafeCell, and 1416 | /// only contains the interface that is needed for this crate. 1417 | #[derive(Debug, Default)] 1418 | pub(crate) struct UnsafeCell(core::cell::UnsafeCell); 1419 | 1420 | impl UnsafeCell { 1421 | pub(crate) fn new(data: T) -> UnsafeCell { 1422 | UnsafeCell(core::cell::UnsafeCell::new(data)) 1423 | } 1424 | 1425 | pub(crate) fn get(&self) -> ConstPtr { 1426 | ConstPtr(self.0.get()) 1427 | } 1428 | 1429 | #[allow(dead_code)] // no_std does not need this 1430 | pub(crate) fn into_inner(self) -> T { 1431 | self.0.into_inner() 1432 | } 1433 | } 1434 | } 1435 | } 1436 | 1437 | #[cfg(loom)] 1438 | /// Synchronization primitive implementation. 1439 | mod sync { 1440 | pub(super) use loom::sync::{atomic, Arc, Mutex, MutexGuard}; 1441 | pub(super) use loom::{cell, thread_local}; 1442 | } 1443 | 1444 | fn __test_send_and_sync() { 1445 | fn _assert_send() {} 1446 | fn _assert_sync() {} 1447 | 1448 | _assert_send::>(); 1449 | _assert_sync::>(); 1450 | _assert_send::>(); 1451 | _assert_sync::>(); 1452 | _assert_send::>(); 1453 | _assert_sync::>(); 1454 | _assert_send::>(); 1455 | _assert_sync::>(); 1456 | } 1457 | 1458 | #[doc(hidden)] 1459 | mod __sealed { 1460 | use super::{EventListener, __private::StackListener}; 1461 | 1462 | pub trait Sealed {} 1463 | impl Sealed for EventListener {} 1464 | impl Sealed for StackListener<'_, '_, T> {} 1465 | } 1466 | 1467 | /// Semver exempt module. 1468 | #[doc(hidden)] 1469 | pub mod __private { 1470 | pub use core::pin::Pin; 1471 | 1472 | use super::{Event, Inner, InnerListener}; 1473 | use core::fmt; 1474 | use core::future::Future; 1475 | use core::task::{Context, Poll}; 1476 | 1477 | pin_project_lite::pin_project! { 1478 | /// Space on the stack where a stack-based listener can be allocated. 1479 | #[doc(hidden)] 1480 | #[project(!Unpin)] 1481 | pub struct StackSlot<'ev, T> { 1482 | #[pin] 1483 | listener: InnerListener> 1484 | } 1485 | } 1486 | 1487 | impl fmt::Debug for StackSlot<'_, T> { 1488 | #[inline] 1489 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1490 | f.debug_struct("StackSlot").finish_non_exhaustive() 1491 | } 1492 | } 1493 | 1494 | impl core::panic::UnwindSafe for StackSlot<'_, T> {} 1495 | impl core::panic::RefUnwindSafe for StackSlot<'_, T> {} 1496 | unsafe impl Send for StackSlot<'_, T> {} 1497 | unsafe impl Sync for StackSlot<'_, T> {} 1498 | 1499 | impl<'ev, T> StackSlot<'ev, T> { 1500 | /// Create a new `StackSlot` on the stack. 1501 | #[inline] 1502 | #[doc(hidden)] 1503 | pub fn new(event: &'ev Event) -> Self { 1504 | let inner = unsafe { &*event.inner() }; 1505 | Self { 1506 | listener: InnerListener { 1507 | event: inner, 1508 | listener: None, 1509 | }, 1510 | } 1511 | } 1512 | 1513 | /// Start listening on this `StackSlot`. 1514 | #[inline] 1515 | #[doc(hidden)] 1516 | pub fn listen(mut self: Pin<&mut Self>) -> StackListener<'ev, '_, T> { 1517 | // Insert ourselves into the list. 1518 | self.as_mut().project().listener.listen(); 1519 | 1520 | // We are now listening. 1521 | StackListener { slot: self } 1522 | } 1523 | } 1524 | 1525 | /// A stack-based `EventListener`. 1526 | #[doc(hidden)] 1527 | pub struct StackListener<'ev, 'stack, T> { 1528 | slot: Pin<&'stack mut StackSlot<'ev, T>>, 1529 | } 1530 | 1531 | impl core::panic::UnwindSafe for StackListener<'_, '_, T> {} 1532 | impl core::panic::RefUnwindSafe for StackListener<'_, '_, T> {} 1533 | impl Unpin for StackListener<'_, '_, T> {} 1534 | 1535 | impl fmt::Debug for StackListener<'_, '_, T> { 1536 | #[inline] 1537 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1538 | f.debug_struct("StackListener").finish_non_exhaustive() 1539 | } 1540 | } 1541 | 1542 | impl<'ev, T> StackListener<'ev, '_, T> { 1543 | #[inline] 1544 | fn listener(&self) -> &InnerListener> { 1545 | &self.slot.listener 1546 | } 1547 | 1548 | #[inline] 1549 | fn listener_mut(&mut self) -> Pin<&mut InnerListener>> { 1550 | self.slot.as_mut().project().listener 1551 | } 1552 | } 1553 | 1554 | forward_impl_to_listener! { T => StackListener<'_, '_, T> } 1555 | } 1556 | -------------------------------------------------------------------------------- /src/notify.rs: -------------------------------------------------------------------------------- 1 | //! The `Notification` trait for specifying notification. 2 | 3 | use crate::sync::atomic::{self, Ordering}; 4 | #[cfg(feature = "std")] 5 | use core::fmt; 6 | 7 | pub(crate) use __private::Internal; 8 | 9 | /// The type of notification to use with an [`Event`]. 10 | /// 11 | /// This is hidden and sealed to prevent changes to this trait from being breaking. 12 | /// 13 | /// [`Event`]: crate::Event 14 | #[doc(hidden)] 15 | pub trait NotificationPrivate { 16 | /// The tag data associated with a notification. 17 | type Tag; 18 | 19 | /// Emit a fence to ensure that the notification is visible to the listeners. 20 | fn fence(&self, internal: Internal); 21 | 22 | /// Whether or not the number of currently waiting listeners should be subtracted from `count()`. 23 | fn is_additional(&self, internal: Internal) -> bool; 24 | 25 | /// Get the number of listeners to wake. 26 | fn count(&self, internal: Internal) -> usize; 27 | 28 | /// Get a tag to be associated with a notification. 29 | /// 30 | /// This method is expected to be called `count()` times. 31 | fn next_tag(&mut self, internal: Internal) -> Self::Tag; 32 | } 33 | 34 | /// A notification that can be used to notify an [`Event`]. 35 | /// 36 | /// This type is used by the [`Event::notify()`] function to determine how many listeners to wake up, whether 37 | /// or not to subtract additional listeners, and other properties. The actual internal data is hidden in a 38 | /// private trait and is intentionally not exposed. This means that users cannot manually implement the 39 | /// [`Notification`] trait. However, it also means that changing the underlying trait is not a semver breaking 40 | /// change. 41 | /// 42 | /// Users can create types that implement notifications using the combinators on the [`IntoNotification`] type. 43 | /// Typical construction of a [`Notification`] starts with a numeric literal (like `3usize`) and then optionally 44 | /// adding combinators. 45 | /// 46 | /// # Example 47 | /// 48 | /// ``` 49 | /// use event_listener::{Event, IntoNotification, Notification}; 50 | /// 51 | /// fn notify(ev: &Event, notify: impl Notification) { 52 | /// ev.notify(notify); 53 | /// } 54 | /// 55 | /// notify(&Event::new(), 1.additional()); 56 | /// ``` 57 | /// 58 | /// [`Event`]: crate::Event 59 | pub trait Notification: NotificationPrivate {} 60 | impl Notification for N {} 61 | 62 | /// Notify a given number of unnotifed listeners. 63 | #[derive(Debug, Clone)] 64 | #[doc(hidden)] 65 | pub struct Notify(usize); 66 | 67 | impl Notify { 68 | /// Create a new `Notify` with the given number of listeners to notify. 69 | fn new(count: usize) -> Self { 70 | Self(count) 71 | } 72 | } 73 | 74 | impl NotificationPrivate for Notify { 75 | type Tag = (); 76 | 77 | fn is_additional(&self, _: Internal) -> bool { 78 | false 79 | } 80 | 81 | fn fence(&self, _: Internal) { 82 | full_fence(); 83 | } 84 | 85 | fn count(&self, _: Internal) -> usize { 86 | self.0 87 | } 88 | 89 | fn next_tag(&mut self, _: Internal) -> Self::Tag {} 90 | } 91 | 92 | /// Make the underlying notification additional. 93 | #[derive(Debug, Clone)] 94 | #[doc(hidden)] 95 | pub struct Additional(N); 96 | 97 | impl Additional { 98 | /// Create a new `Additional` with the given notification. 99 | fn new(inner: N) -> Self { 100 | Self(inner) 101 | } 102 | } 103 | 104 | impl NotificationPrivate for Additional 105 | where 106 | N: Notification + ?Sized, 107 | { 108 | type Tag = N::Tag; 109 | 110 | fn is_additional(&self, _: Internal) -> bool { 111 | true 112 | } 113 | 114 | fn fence(&self, i: Internal) { 115 | self.0.fence(i); 116 | } 117 | 118 | fn count(&self, i: Internal) -> usize { 119 | self.0.count(i) 120 | } 121 | 122 | fn next_tag(&mut self, i: Internal) -> Self::Tag { 123 | self.0.next_tag(i) 124 | } 125 | } 126 | 127 | /// Don't emit a fence for this notification. 128 | #[derive(Debug, Clone)] 129 | #[doc(hidden)] 130 | pub struct Relaxed(N); 131 | 132 | impl Relaxed { 133 | /// Create a new `Relaxed` with the given notification. 134 | fn new(inner: N) -> Self { 135 | Self(inner) 136 | } 137 | } 138 | 139 | impl NotificationPrivate for Relaxed 140 | where 141 | N: Notification + ?Sized, 142 | { 143 | type Tag = N::Tag; 144 | 145 | fn is_additional(&self, i: Internal) -> bool { 146 | self.0.is_additional(i) 147 | } 148 | 149 | fn fence(&self, _: Internal) { 150 | // Don't emit a fence. 151 | } 152 | 153 | fn count(&self, i: Internal) -> usize { 154 | self.0.count(i) 155 | } 156 | 157 | fn next_tag(&mut self, i: Internal) -> Self::Tag { 158 | self.0.next_tag(i) 159 | } 160 | } 161 | 162 | /// Use a tag to notify listeners. 163 | #[cfg(feature = "std")] 164 | #[derive(Debug, Clone)] 165 | #[doc(hidden)] 166 | pub struct Tag { 167 | tag: T, 168 | inner: N, 169 | } 170 | 171 | #[cfg(feature = "std")] 172 | impl Tag { 173 | /// Create a new `Tag` with the given tag and notification. 174 | fn new(tag: T, inner: N) -> Self 175 | where 176 | N: Sized, 177 | { 178 | Self { tag, inner } 179 | } 180 | } 181 | 182 | #[cfg(feature = "std")] 183 | impl NotificationPrivate for Tag 184 | where 185 | N: Notification + ?Sized, 186 | T: Clone, 187 | { 188 | type Tag = T; 189 | 190 | fn is_additional(&self, i: Internal) -> bool { 191 | self.inner.is_additional(i) 192 | } 193 | 194 | fn fence(&self, i: Internal) { 195 | self.inner.fence(i); 196 | } 197 | 198 | fn count(&self, i: Internal) -> usize { 199 | self.inner.count(i) 200 | } 201 | 202 | fn next_tag(&mut self, _: Internal) -> Self::Tag { 203 | self.tag.clone() 204 | } 205 | } 206 | 207 | /// Use a function to generate a tag to notify listeners. 208 | #[cfg(feature = "std")] 209 | #[doc(hidden)] 210 | pub struct TagWith { 211 | tag: F, 212 | inner: N, 213 | } 214 | 215 | #[cfg(feature = "std")] 216 | impl fmt::Debug for TagWith { 217 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 218 | struct Ellipses; 219 | 220 | impl fmt::Debug for Ellipses { 221 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 222 | f.write_str("..") 223 | } 224 | } 225 | 226 | f.debug_struct("TagWith") 227 | .field("tag", &Ellipses) 228 | .field("inner", &self.inner) 229 | .finish() 230 | } 231 | } 232 | 233 | #[cfg(feature = "std")] 234 | impl TagWith { 235 | /// Create a new `TagFn` with the given tag function and notification. 236 | fn new(tag: F, inner: N) -> Self { 237 | Self { tag, inner } 238 | } 239 | } 240 | 241 | #[cfg(feature = "std")] 242 | impl NotificationPrivate for TagWith 243 | where 244 | N: Notification + ?Sized, 245 | F: FnMut() -> T, 246 | { 247 | type Tag = T; 248 | 249 | fn is_additional(&self, i: Internal) -> bool { 250 | self.inner.is_additional(i) 251 | } 252 | 253 | fn fence(&self, i: Internal) { 254 | self.inner.fence(i); 255 | } 256 | 257 | fn count(&self, i: Internal) -> usize { 258 | self.inner.count(i) 259 | } 260 | 261 | fn next_tag(&mut self, _: Internal) -> Self::Tag { 262 | (self.tag)() 263 | } 264 | } 265 | 266 | /// A generic notification. 267 | #[derive(Debug)] 268 | pub(crate) struct GenericNotify { 269 | /// Number of listeners to notify. 270 | count: usize, 271 | 272 | /// Whether this notification is additional. 273 | additional: bool, 274 | 275 | /// Generate tags. 276 | tags: F, 277 | } 278 | 279 | impl> GenericNotify { 280 | pub(crate) fn new(count: usize, additional: bool, tags: F) -> Self { 281 | Self { 282 | count, 283 | additional, 284 | tags, 285 | } 286 | } 287 | } 288 | 289 | impl> NotificationPrivate for GenericNotify { 290 | type Tag = T; 291 | 292 | fn is_additional(&self, _: Internal) -> bool { 293 | self.additional 294 | } 295 | 296 | fn fence(&self, _: Internal) { 297 | // Don't emit a fence. 298 | } 299 | 300 | fn count(&self, _: Internal) -> usize { 301 | self.count 302 | } 303 | 304 | fn next_tag(&mut self, _: Internal) -> Self::Tag { 305 | self.tags.next_tag() 306 | } 307 | } 308 | 309 | /// The producer for a generic notification. 310 | pub(crate) trait TagProducer { 311 | type Tag; 312 | 313 | /// Get the next tag. 314 | fn next_tag(&mut self) -> Self::Tag; 315 | } 316 | 317 | impl T> TagProducer for F { 318 | type Tag = T; 319 | 320 | fn next_tag(&mut self) -> T { 321 | (self)() 322 | } 323 | } 324 | 325 | /// A value that can be converted into a [`Notification`]. 326 | /// 327 | /// This trait adds onto the [`Notification`] trait by providing combinators that can be applied to all 328 | /// notification types as well as numeric literals. This transforms what would normally be: 329 | /// 330 | /// ``` 331 | /// use event_listener::Event; 332 | /// 333 | /// let event = Event::new(); 334 | /// 335 | /// // Note that each use case needs its own function, leading to bloat. 336 | /// event.notify(1); 337 | /// event.notify_additional(3); 338 | /// event.notify_relaxed(5); 339 | /// event.notify_additional_relaxed(2); 340 | /// ``` 341 | /// 342 | /// into this: 343 | /// 344 | /// ``` 345 | /// use event_listener::{Event, IntoNotification, Listener}; 346 | /// 347 | /// let event = Event::new(); 348 | /// 349 | /// event.notify(1); 350 | /// event.notify(3.additional()); 351 | /// event.notify(5.relaxed()); 352 | /// event.notify(2.additional().relaxed()); 353 | /// ``` 354 | /// 355 | /// This trait is implemented for all types that implement [`Notification`], as well as for non-floating-point 356 | /// numeric literals (`usize`, `i32`, etc). 357 | /// 358 | /// This function can be thought of as being analogous to [`std::iter::IntoIterator`], but for [`Notification`]. 359 | pub trait IntoNotification: __private::Sealed { 360 | /// The tag data associated with a notification. 361 | /// 362 | /// By default, most [`Event`]s will use the unit type, `()`. However, this can be used to pass data along to 363 | /// the listener. 364 | type Tag; 365 | 366 | /// The notification type. 367 | /// 368 | /// Tells what kind of underlying type that the [`Notification`] is. You probably don't need to worry about 369 | /// this. 370 | type Notify: Notification; 371 | 372 | /// Convert this value into a notification. 373 | /// 374 | /// This allows the user to convert an [`IntoNotification`] into a [`Notification`]. 375 | /// 376 | /// # Panics 377 | /// 378 | /// This function panics if the value represents a negative number of notifications. 379 | /// 380 | /// # Examples 381 | /// 382 | /// ``` 383 | /// use event_listener::IntoNotification; 384 | /// 385 | /// let _ = 3.into_notification(); 386 | /// ``` 387 | fn into_notification(self) -> Self::Notify; 388 | 389 | /// Convert this value into an additional notification. 390 | /// 391 | /// By default, notifications ignore listeners that are already notified. Generally, this happens when there 392 | /// is an [`EventListener`] that has been woken up, but hasn't been polled to completion or waited on yet. 393 | /// For instance, if you have three notified listeners and you call `event.notify(5)`, only two listeners 394 | /// will be woken up. 395 | /// 396 | /// This default behavior is generally desired. For instance, if you are writing a `Mutex` implementation 397 | /// powered by an [`Event`], you usually only want one consumer to be notified at a time. If you notified 398 | /// a listener when another listener is already notified, you would have unnecessary contention for your 399 | /// lock, as both listeners fight over the lock. Therefore, you would call `event.notify(1)` to make sure 400 | /// *at least* one listener is awake. 401 | /// 402 | /// Sometimes, this behavior is not desired. For instance, if you are writing an MPMC channel, it is desirable 403 | /// for multiple listeners to be reading from the underlying queue at once. In this case, you would instead 404 | /// call `event.notify(1.additional())`. 405 | /// 406 | /// # Examples 407 | /// 408 | /// ``` 409 | /// use event_listener::{Event, IntoNotification, Listener}; 410 | /// 411 | /// let event = Event::new(); 412 | /// 413 | /// let mut l1 = event.listen(); 414 | /// let mut l2 = event.listen(); 415 | /// 416 | /// // This will only wake up the first listener, as the second call observes that there is already a 417 | /// // notified listener. 418 | /// event.notify(1); 419 | /// event.notify(1); 420 | /// 421 | /// // This call wakes up the other listener. 422 | /// event.notify(1.additional()); 423 | /// ``` 424 | fn additional(self) -> Additional 425 | where 426 | Self: Sized, 427 | { 428 | Additional::new(self.into_notification()) 429 | } 430 | 431 | /// Don't emit a fence for this notification. 432 | /// 433 | /// Usually, notifications emit a `SeqCst` atomic fence before any listeners are woken up. This ensures 434 | /// that notification state isn't inconsistent before any wakers are woken up. However, it may be 435 | /// desirable to omit this fence in certain cases. 436 | /// 437 | /// - You are running the [`Event`] on a single thread, where no synchronization needs to occur. 438 | /// - You are emitting the `SeqCst` fence yourself. 439 | /// 440 | /// In these cases, `relaxed()` can be used to avoid emitting the `SeqCst` fence. 441 | /// 442 | /// # Examples 443 | /// 444 | /// ``` 445 | /// use event_listener::{Event, IntoNotification, Listener}; 446 | /// use std::sync::atomic::{self, Ordering}; 447 | /// 448 | /// let event = Event::new(); 449 | /// 450 | /// let listener1 = event.listen(); 451 | /// let listener2 = event.listen(); 452 | /// let listener3 = event.listen(); 453 | /// 454 | /// // We should emit a fence manually when using relaxed notifications. 455 | /// atomic::fence(Ordering::SeqCst); 456 | /// 457 | /// // Notifies two listeners. 458 | /// // 459 | /// // Listener queueing is fair, which means `listener1` and `listener2` 460 | /// // get notified here since they start listening before `listener3`. 461 | /// event.notify(1.relaxed()); 462 | /// event.notify(1.relaxed()); 463 | /// ``` 464 | fn relaxed(self) -> Relaxed 465 | where 466 | Self: Sized, 467 | { 468 | Relaxed::new(self.into_notification()) 469 | } 470 | 471 | /// Use a tag with this notification. 472 | /// 473 | /// In many cases, it is desired to send additional information to the listener of the [`Event`]. For instance, 474 | /// it is possible to optimize a `Mutex` implementation by locking directly on the next listener, without 475 | /// needing to ever unlock the mutex at all. 476 | /// 477 | /// The tag provided is cloned to provide the tag for all listeners. In cases where this is not flexible 478 | /// enough, use [`IntoNotification::with_tag()`] instead. 479 | /// 480 | /// Tagging functions cannot be implemented efficiently for `no_std`, so this is only available 481 | /// when the `std` feature is enabled. 482 | /// 483 | /// # Examples 484 | /// 485 | /// ``` 486 | /// use event_listener::{IntoNotification, Listener, Event}; 487 | /// 488 | /// let event = Event::::with_tag(); 489 | /// 490 | /// let mut listener1 = event.listen(); 491 | /// let mut listener2 = event.listen(); 492 | /// 493 | /// // Notify with `true` then `false`. 494 | /// event.notify(1.additional().tag(true)); 495 | /// event.notify(1.additional().tag(false)); 496 | /// 497 | /// assert_eq!(listener1.wait(), true); 498 | /// assert_eq!(listener2.wait(), false); 499 | /// ``` 500 | #[cfg(feature = "std")] 501 | fn tag(self, tag: T) -> Tag 502 | where 503 | Self: Sized + IntoNotification, 504 | { 505 | Tag::new(tag, self.into_notification()) 506 | } 507 | 508 | /// Use a function to generate a tag with this notification. 509 | /// 510 | /// In many cases, it is desired to send additional information to the listener of the [`Event`]. For instance, 511 | /// it is possible to optimize a `Mutex` implementation by locking directly on the next listener, without 512 | /// needing to ever unlock the mutex at all. 513 | /// 514 | /// Tagging functions cannot be implemented efficiently for `no_std`, so this is only available 515 | /// when the `std` feature is enabled. 516 | /// 517 | /// # Examples 518 | /// 519 | /// ``` 520 | /// use event_listener::{IntoNotification, Listener, Event}; 521 | /// 522 | /// let event = Event::::with_tag(); 523 | /// 524 | /// let mut listener1 = event.listen(); 525 | /// let mut listener2 = event.listen(); 526 | /// 527 | /// // Notify with `true` then `false`. 528 | /// event.notify(1.additional().tag_with(|| true)); 529 | /// event.notify(1.additional().tag_with(|| false)); 530 | /// 531 | /// assert_eq!(listener1.wait(), true); 532 | /// assert_eq!(listener2.wait(), false); 533 | /// ``` 534 | #[cfg(feature = "std")] 535 | fn tag_with(self, tag: F) -> TagWith 536 | where 537 | Self: Sized + IntoNotification, 538 | F: FnMut() -> T, 539 | { 540 | TagWith::new(tag, self.into_notification()) 541 | } 542 | } 543 | 544 | impl IntoNotification for N { 545 | type Tag = N::Tag; 546 | type Notify = N; 547 | 548 | fn into_notification(self) -> Self::Notify { 549 | self 550 | } 551 | } 552 | 553 | macro_rules! impl_for_numeric_types { 554 | ($($ty:ty)*) => {$( 555 | impl IntoNotification for $ty { 556 | type Tag = (); 557 | type Notify = Notify; 558 | 559 | #[allow(unused_comparisons)] 560 | fn into_notification(self) -> Self::Notify { 561 | if self < 0 { 562 | panic!("negative notification count"); 563 | } 564 | 565 | Notify::new(self.try_into().expect("overflow")) 566 | } 567 | } 568 | 569 | impl __private::Sealed for $ty {} 570 | )*}; 571 | } 572 | 573 | impl_for_numeric_types! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } 574 | 575 | /// Equivalent to `atomic::fence(Ordering::SeqCst)`, but in some cases faster. 576 | #[inline] 577 | pub(super) fn full_fence() { 578 | #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), not(miri), not(loom)))] 579 | { 580 | use core::{arch::asm, cell::UnsafeCell}; 581 | // HACK(stjepang): On x86 architectures there are two different ways of executing 582 | // a `SeqCst` fence. 583 | // 584 | // 1. `atomic::fence(SeqCst)`, which compiles into a `mfence` instruction. 585 | // 2. A `lock ` instruction. 586 | // 587 | // Both instructions have the effect of a full barrier, but empirical benchmarks have shown 588 | // that the second one is sometimes a bit faster. 589 | let a = UnsafeCell::new(0_usize); 590 | // It is common to use `lock or` here, but when using a local variable, `lock not`, which 591 | // does not change the flag, should be slightly more efficient. 592 | // Refs: https://www.felixcloutier.com/x86/not 593 | unsafe { 594 | #[cfg(target_pointer_width = "64")] 595 | asm!("lock not qword ptr [{0}]", in(reg) a.get(), options(nostack, preserves_flags)); 596 | #[cfg(target_pointer_width = "32")] 597 | asm!("lock not dword ptr [{0:e}]", in(reg) a.get(), options(nostack, preserves_flags)); 598 | } 599 | return; 600 | } 601 | #[allow(unreachable_code)] 602 | { 603 | atomic::fence(Ordering::SeqCst); 604 | } 605 | } 606 | 607 | mod __private { 608 | /// Make sure the NotificationPrivate trait can't be implemented outside of this crate. 609 | #[doc(hidden)] 610 | #[derive(Debug)] 611 | pub struct Internal(()); 612 | 613 | impl Internal { 614 | pub(crate) fn new() -> Self { 615 | Self(()) 616 | } 617 | } 618 | 619 | #[doc(hidden)] 620 | pub trait Sealed {} 621 | impl Sealed for N {} 622 | } 623 | -------------------------------------------------------------------------------- /src/slab.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of `event-listener` built exclusively on atomics. 2 | //! 3 | //! On `no_std`, we don't have access to `Mutex`, so we can't use intrusive linked lists like the `std` 4 | //! implementation. Normally, we would use a concurrent atomic queue to store listeners, but benchmarks 5 | //! show that using queues in this way is very slow, especially for the single threaded use-case. 6 | //! 7 | //! We've found that it's easier to assume that the `Event` won't be under high contention in most use 8 | //! cases. Therefore, we use a spinlock that protects a linked list of listeners, and fall back to an 9 | //! atomic queue if the lock is contended. Benchmarks show that this is about 20% slower than the std 10 | //! implementation, but still much faster than using a queue. 11 | 12 | #[path = "slab/node.rs"] 13 | mod node; 14 | 15 | use node::{Node, NothingProducer, TaskWaiting}; 16 | 17 | use crate::notify::{GenericNotify, Internal, Notification}; 18 | use crate::sync::atomic::{AtomicBool, Ordering}; 19 | use crate::sync::cell::{Cell, ConstPtr, UnsafeCell}; 20 | use crate::sync::Arc; 21 | use crate::{RegisterResult, State, Task, TaskRef}; 22 | 23 | use core::fmt; 24 | use core::marker::PhantomData; 25 | use core::mem; 26 | use core::num::NonZeroUsize; 27 | use core::ops; 28 | use core::pin::Pin; 29 | 30 | use alloc::vec::Vec; 31 | 32 | impl crate::Inner { 33 | /// Locks the list. 34 | fn try_lock(&self) -> Option> { 35 | self.list.inner.try_lock().map(|guard| ListGuard { 36 | inner: self, 37 | guard: Some(guard), 38 | tasks: alloc::vec![], 39 | }) 40 | } 41 | 42 | /// Force a queue update. 43 | fn queue_update(&self) { 44 | // Locking and unlocking the mutex will drain the queue if there is no contention. 45 | drop(self.try_lock()); 46 | } 47 | 48 | /// Add a new listener to the list. 49 | /// 50 | /// Does nothing if the list is already registered. 51 | pub(crate) fn insert(&self, mut listener: Pin<&mut Option>>) { 52 | if listener.as_ref().as_pin_ref().is_some() { 53 | // Already inserted. 54 | return; 55 | } 56 | 57 | match self.try_lock() { 58 | Some(mut lock) => { 59 | let key = lock.insert(State::Created); 60 | *listener = Some(Listener::HasNode(key)); 61 | } 62 | 63 | None => { 64 | // Push it to the queue. 65 | let (node, task_waiting) = Node::listener(); 66 | self.list.queue.push(node).unwrap(); 67 | *listener = Some(Listener::Queued(task_waiting)); 68 | 69 | // Force a queue update. 70 | self.queue_update(); 71 | } 72 | } 73 | } 74 | 75 | /// Remove a listener from the list. 76 | pub(crate) fn remove( 77 | &self, 78 | mut listener: Pin<&mut Option>>, 79 | propagate: bool, 80 | ) -> Option> { 81 | loop { 82 | let state = match listener.as_mut().take() { 83 | Some(Listener::HasNode(key)) => { 84 | match self.try_lock() { 85 | Some(mut list) => { 86 | // Fast path removal. 87 | list.remove(key, propagate) 88 | } 89 | 90 | None => { 91 | // Slow path removal. 92 | // This is why intrusive lists don't work on no_std. 93 | let node = Node::RemoveListener { 94 | listener: key, 95 | propagate, 96 | }; 97 | 98 | self.list.queue.push(node).unwrap(); 99 | 100 | // Force a queue update. 101 | self.queue_update(); 102 | 103 | None 104 | } 105 | } 106 | } 107 | 108 | Some(Listener::Queued(tw)) => { 109 | // Make sure it's not added after the queue is drained. 110 | if let Some(key) = tw.cancel() { 111 | // If it was already added, set up our listener and try again. 112 | *listener = Some(Listener::HasNode(key)); 113 | continue; 114 | } 115 | 116 | None 117 | } 118 | 119 | None => None, 120 | 121 | _ => unreachable!(), 122 | }; 123 | 124 | return state; 125 | } 126 | } 127 | 128 | /// Notifies a number of entries. 129 | #[cold] 130 | pub(crate) fn notify(&self, notify: impl Notification) -> usize { 131 | match self.try_lock() { 132 | Some(mut guard) => { 133 | // Notify the listeners. 134 | guard.notify(notify) 135 | } 136 | 137 | None => { 138 | // Push it to the queue. 139 | let node = Node::Notify(GenericNotify::new( 140 | notify.count(Internal::new()), 141 | notify.is_additional(Internal::new()), 142 | NothingProducer::default(), 143 | )); 144 | 145 | self.list.queue.push(node).unwrap(); 146 | 147 | // Force a queue update. 148 | self.queue_update(); 149 | 150 | // We haven't notified anyone yet. 151 | 0 152 | } 153 | } 154 | } 155 | 156 | /// Register a task to be notified when the event is triggered. 157 | /// 158 | /// Returns `true` if the listener was already notified, and `false` otherwise. If the listener 159 | /// isn't inserted, returns `None`. 160 | pub(crate) fn register( 161 | &self, 162 | mut listener: Pin<&mut Option>>, 163 | task: TaskRef<'_>, 164 | ) -> RegisterResult { 165 | loop { 166 | match listener.as_mut().take() { 167 | Some(Listener::HasNode(key)) => { 168 | *listener = Some(Listener::HasNode(key)); 169 | match self.try_lock() { 170 | Some(mut guard) => { 171 | // Fast path registration. 172 | return guard.register(listener, task); 173 | } 174 | 175 | None => { 176 | // Wait for the lock. 177 | let node = Node::Waiting(task.into_task()); 178 | self.list.queue.push(node).unwrap(); 179 | 180 | // Force a queue update. 181 | self.queue_update(); 182 | 183 | return RegisterResult::Registered; 184 | } 185 | } 186 | } 187 | 188 | Some(Listener::Queued(task_waiting)) => { 189 | // Force a queue update. 190 | self.queue_update(); 191 | 192 | // Are we done yet? 193 | match task_waiting.status() { 194 | Some(key) => { 195 | assert!(key.get() != usize::MAX); 196 | 197 | // We're inserted now, adjust state. 198 | *listener = Some(Listener::HasNode(key)); 199 | } 200 | 201 | None => { 202 | // We're still queued, so register the task. 203 | task_waiting.register(task.into_task()); 204 | *listener = Some(Listener::Queued(task_waiting)); 205 | 206 | // Force a queue update. 207 | self.queue_update(); 208 | 209 | return RegisterResult::Registered; 210 | } 211 | } 212 | } 213 | 214 | None => return RegisterResult::NeverInserted, 215 | 216 | _ => unreachable!(), 217 | } 218 | } 219 | } 220 | } 221 | 222 | #[derive(Debug)] 223 | pub(crate) struct List { 224 | /// The inner list. 225 | inner: Mutex>, 226 | 227 | /// The queue of pending operations. 228 | queue: concurrent_queue::ConcurrentQueue>, 229 | } 230 | 231 | impl List { 232 | pub(super) fn new() -> List { 233 | List { 234 | inner: Mutex::new(ListenerSlab::new()), 235 | queue: concurrent_queue::ConcurrentQueue::unbounded(), 236 | } 237 | } 238 | 239 | /// Try to get the total number of listeners without blocking. 240 | pub(super) fn try_total_listeners(&self) -> Option { 241 | self.inner.try_lock().map(|lock| lock.listeners.len()) 242 | } 243 | } 244 | 245 | /// The guard returned by [`Inner::lock`]. 246 | pub(crate) struct ListGuard<'a, T> { 247 | /// Reference to the inner state. 248 | pub(crate) inner: &'a crate::Inner, 249 | 250 | /// The locked list. 251 | pub(crate) guard: Option>>, 252 | 253 | /// Tasks to wake up once this guard is dropped. 254 | tasks: Vec, 255 | } 256 | 257 | impl ListGuard<'_, T> { 258 | #[cold] 259 | fn process_nodes_slow(&mut self, start_node: Node) { 260 | let guard = self.guard.as_mut().unwrap(); 261 | 262 | // Process the start node. 263 | self.tasks.extend(start_node.apply(guard)); 264 | 265 | // Process all remaining nodes. 266 | while let Ok(node) = self.inner.list.queue.pop() { 267 | self.tasks.extend(node.apply(guard)); 268 | } 269 | } 270 | 271 | #[inline] 272 | fn process_nodes(&mut self) { 273 | // Process every node left in the queue. 274 | if let Ok(start_node) = self.inner.list.queue.pop() { 275 | self.process_nodes_slow(start_node); 276 | } 277 | } 278 | } 279 | 280 | impl ops::Deref for ListGuard<'_, T> { 281 | type Target = ListenerSlab; 282 | 283 | fn deref(&self) -> &Self::Target { 284 | self.guard.as_ref().unwrap() 285 | } 286 | } 287 | 288 | impl ops::DerefMut for ListGuard<'_, T> { 289 | fn deref_mut(&mut self) -> &mut Self::Target { 290 | self.guard.as_mut().unwrap() 291 | } 292 | } 293 | 294 | impl Drop for ListGuard<'_, T> { 295 | fn drop(&mut self) { 296 | while self.guard.is_some() { 297 | // Process every node left in the queue. 298 | self.process_nodes(); 299 | 300 | // Update the atomic `notified` counter. 301 | let list = self.guard.take().unwrap(); 302 | let notified = if list.notified < list.len { 303 | list.notified 304 | } else { 305 | usize::MAX 306 | }; 307 | 308 | self.inner.notified.store(notified, Ordering::Release); 309 | 310 | // Drop the actual lock. 311 | drop(list); 312 | 313 | // Wakeup all tasks. 314 | for task in self.tasks.drain(..) { 315 | task.wake(); 316 | } 317 | 318 | // There is a deadlock where a node is pushed to the end of the queue after we've finished 319 | // process_nodes() but before we've finished dropping the lock. This can lead to some 320 | // notifications not being properly delivered, or listeners not being added to the list. 321 | // Therefore check before we finish dropping if there is anything left in the queue, and 322 | // if so, lock it again and force a queue update. 323 | if !self.inner.list.queue.is_empty() { 324 | self.guard = self.inner.list.inner.try_lock(); 325 | } 326 | } 327 | } 328 | } 329 | 330 | /// An entry representing a registered listener. 331 | enum Entry { 332 | /// Contains the listener state. 333 | Listener { 334 | /// The state of the listener. 335 | state: Cell>, 336 | 337 | /// The previous listener in the list. 338 | prev: Cell>, 339 | 340 | /// The next listener in the list. 341 | next: Cell>, 342 | }, 343 | 344 | /// An empty slot that contains the index of the next empty slot. 345 | Empty(NonZeroUsize), 346 | 347 | /// Sentinel value. 348 | Sentinel, 349 | } 350 | 351 | struct TakenState<'a, T> { 352 | slot: &'a Cell>, 353 | state: State, 354 | } 355 | 356 | impl Drop for TakenState<'_, T> { 357 | fn drop(&mut self) { 358 | self.slot 359 | .set(mem::replace(&mut self.state, State::NotifiedTaken)); 360 | } 361 | } 362 | 363 | impl fmt::Debug for TakenState<'_, T> { 364 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 365 | fmt::Debug::fmt(&self.state, f) 366 | } 367 | } 368 | 369 | impl PartialEq for TakenState<'_, T> { 370 | fn eq(&self, other: &Self) -> bool { 371 | self.state == other.state 372 | } 373 | } 374 | 375 | impl<'a, T> TakenState<'a, T> { 376 | fn new(slot: &'a Cell>) -> Self { 377 | let state = slot.replace(State::NotifiedTaken); 378 | Self { slot, state } 379 | } 380 | } 381 | 382 | impl fmt::Debug for Entry { 383 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 384 | match self { 385 | Entry::Listener { state, next, prev } => f 386 | .debug_struct("Listener") 387 | .field("state", &TakenState::new(state)) 388 | .field("prev", prev) 389 | .field("next", next) 390 | .finish(), 391 | Entry::Empty(next) => f.debug_tuple("Empty").field(next).finish(), 392 | Entry::Sentinel => f.debug_tuple("Sentinel").finish(), 393 | } 394 | } 395 | } 396 | 397 | impl PartialEq for Entry { 398 | fn eq(&self, other: &Entry) -> bool { 399 | match (self, other) { 400 | ( 401 | Self::Listener { 402 | state: state1, 403 | prev: prev1, 404 | next: next1, 405 | }, 406 | Self::Listener { 407 | state: state2, 408 | prev: prev2, 409 | next: next2, 410 | }, 411 | ) => { 412 | if TakenState::new(state1) != TakenState::new(state2) { 413 | return false; 414 | } 415 | 416 | prev1.get() == prev2.get() && next1.get() == next2.get() 417 | } 418 | (Self::Empty(next1), Self::Empty(next2)) => next1 == next2, 419 | (Self::Sentinel, Self::Sentinel) => true, 420 | _ => false, 421 | } 422 | } 423 | } 424 | 425 | impl Entry { 426 | fn state(&self) -> &Cell> { 427 | match self { 428 | Entry::Listener { state, .. } => state, 429 | _ => unreachable!(), 430 | } 431 | } 432 | 433 | fn prev(&self) -> &Cell> { 434 | match self { 435 | Entry::Listener { prev, .. } => prev, 436 | _ => unreachable!(), 437 | } 438 | } 439 | 440 | fn next(&self) -> &Cell> { 441 | match self { 442 | Entry::Listener { next, .. } => next, 443 | _ => unreachable!(), 444 | } 445 | } 446 | } 447 | 448 | /// A linked list of entries. 449 | pub(crate) struct ListenerSlab { 450 | /// The raw list of entries. 451 | listeners: Vec>, 452 | 453 | /// First entry in the list. 454 | head: Option, 455 | 456 | /// Last entry in the list. 457 | tail: Option, 458 | 459 | /// The first unnotified entry in the list. 460 | start: Option, 461 | 462 | /// The number of notified entries in the list. 463 | notified: usize, 464 | 465 | /// The total number of listeners. 466 | len: usize, 467 | 468 | /// The index of the first `Empty` entry, or the length of the list plus one if there 469 | /// are no empty entries. 470 | first_empty: NonZeroUsize, 471 | } 472 | 473 | impl fmt::Debug for ListenerSlab { 474 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 475 | f.debug_struct("ListenerSlab") 476 | .field("listeners", &self.listeners) 477 | .field("head", &self.head) 478 | .field("tail", &self.tail) 479 | .field("start", &self.start) 480 | .field("notified", &self.notified) 481 | .field("len", &self.len) 482 | .field("first_empty", &self.first_empty) 483 | .finish() 484 | } 485 | } 486 | 487 | impl ListenerSlab { 488 | /// Create a new, empty list. 489 | pub(crate) fn new() -> Self { 490 | Self { 491 | listeners: alloc::vec![Entry::Sentinel], 492 | head: None, 493 | tail: None, 494 | start: None, 495 | notified: 0, 496 | len: 0, 497 | first_empty: unsafe { NonZeroUsize::new_unchecked(1) }, 498 | } 499 | } 500 | 501 | /// Inserts a new entry into the list. 502 | pub(crate) fn insert(&mut self, state: State) -> NonZeroUsize { 503 | // Add the new entry into the list. 504 | let key = { 505 | let entry = Entry::Listener { 506 | state: Cell::new(state), 507 | prev: Cell::new(self.tail), 508 | next: Cell::new(None), 509 | }; 510 | 511 | let key = self.first_empty; 512 | if self.first_empty.get() == self.listeners.len() { 513 | // No empty entries, so add a new entry. 514 | self.listeners.push(entry); 515 | 516 | // SAFETY: Guaranteed to not overflow, since the Vec would have panicked already. 517 | self.first_empty = unsafe { NonZeroUsize::new_unchecked(self.listeners.len()) }; 518 | } else { 519 | // There is an empty entry, so replace it. 520 | let slot = &mut self.listeners[key.get()]; 521 | let next = match mem::replace(slot, entry) { 522 | Entry::Empty(next) => next, 523 | _ => unreachable!(), 524 | }; 525 | 526 | self.first_empty = next; 527 | } 528 | 529 | key 530 | }; 531 | 532 | // Replace the tail with the new entry. 533 | match mem::replace(&mut self.tail, Some(key)) { 534 | None => self.head = Some(key), 535 | Some(tail) => { 536 | let tail = &self.listeners[tail.get()]; 537 | tail.next().set(Some(key)); 538 | } 539 | } 540 | 541 | // If there are no listeners that have been notified, then the new listener is the next 542 | // listener to be notified. 543 | if self.start.is_none() { 544 | self.start = Some(key); 545 | } 546 | 547 | // Increment the length. 548 | self.len += 1; 549 | 550 | key 551 | } 552 | 553 | /// Removes an entry from the list and returns its state. 554 | pub(crate) fn remove(&mut self, key: NonZeroUsize, propagate: bool) -> Option> { 555 | let entry = &self.listeners[key.get()]; 556 | let prev = entry.prev().get(); 557 | let next = entry.next().get(); 558 | 559 | // Unlink from the previous entry. 560 | match prev { 561 | None => self.head = next, 562 | Some(p) => self.listeners[p.get()].next().set(next), 563 | } 564 | 565 | // Unlink from the next entry. 566 | match next { 567 | None => self.tail = prev, 568 | Some(n) => self.listeners[n.get()].prev().set(prev), 569 | } 570 | 571 | // If this was the first unnotified entry, move the pointer to the next one. 572 | if self.start == Some(key) { 573 | self.start = next; 574 | } 575 | 576 | // Extract the state. 577 | let entry = mem::replace( 578 | &mut self.listeners[key.get()], 579 | Entry::Empty(self.first_empty), 580 | ); 581 | self.first_empty = key; 582 | 583 | let mut state = match entry { 584 | Entry::Listener { state, .. } => state.into_inner(), 585 | _ => unreachable!(), 586 | }; 587 | 588 | // Update the counters. 589 | if state.is_notified() { 590 | self.notified = self.notified.saturating_sub(1); 591 | 592 | if propagate { 593 | // Propagate the notification to the next entry. 594 | let state = mem::replace(&mut state, State::NotifiedTaken); 595 | if let State::Notified { tag, additional } = state { 596 | let tags = { 597 | let mut tag = Some(tag); 598 | move || tag.take().expect("called more than once") 599 | }; 600 | 601 | self.notify(GenericNotify::new(1, additional, tags)); 602 | } 603 | } 604 | } 605 | self.len -= 1; 606 | 607 | Some(state) 608 | } 609 | 610 | /// Notifies a number of listeners. 611 | #[cold] 612 | pub(crate) fn notify(&mut self, mut notify: impl Notification) -> usize { 613 | let mut n = notify.count(Internal::new()); 614 | let is_additional = notify.is_additional(Internal::new()); 615 | if !is_additional { 616 | // Make sure we're not notifying more than we have. 617 | if n <= self.notified { 618 | return 0; 619 | } 620 | n -= self.notified; 621 | } 622 | 623 | let original_count = n; 624 | while n > 0 { 625 | n -= 1; 626 | 627 | // Notify the next entry. 628 | match self.start { 629 | None => return original_count - n - 1, 630 | 631 | Some(e) => { 632 | // Get the entry and move the pointer forwards. 633 | let entry = &self.listeners[e.get()]; 634 | self.start = entry.next().get(); 635 | 636 | // Set the state to `Notified` and notify. 637 | let tag = notify.next_tag(Internal::new()); 638 | if let State::Task(task) = entry.state().replace(State::Notified { 639 | tag, 640 | additional: is_additional, 641 | }) { 642 | task.wake(); 643 | } 644 | 645 | // Bump the notified count. 646 | self.notified += 1; 647 | } 648 | } 649 | } 650 | 651 | original_count - n 652 | } 653 | 654 | /// Register a task to be notified when the event is triggered. 655 | /// 656 | /// Returns `true` if the listener was already notified, and `false` otherwise. If the listener 657 | /// isn't inserted, returns `None`. 658 | pub(crate) fn register( 659 | &mut self, 660 | mut listener: Pin<&mut Option>>, 661 | task: TaskRef<'_>, 662 | ) -> RegisterResult { 663 | let key = match *listener { 664 | Some(Listener::HasNode(key)) => key, 665 | _ => return RegisterResult::NeverInserted, 666 | }; 667 | 668 | let entry = &self.listeners[key.get()]; 669 | 670 | // Take the state out and check it. 671 | match entry.state().replace(State::NotifiedTaken) { 672 | State::Notified { tag, .. } => { 673 | // The listener was already notified, so we don't need to do anything. 674 | self.remove(key, false); 675 | *listener = None; 676 | RegisterResult::Notified(tag) 677 | } 678 | 679 | State::Task(other_task) => { 680 | // Only replace the task if it's not the same as the one we're registering. 681 | if task.will_wake(other_task.as_task_ref()) { 682 | entry.state().set(State::Task(other_task)); 683 | } else { 684 | entry.state().set(State::Task(task.into_task())); 685 | } 686 | 687 | RegisterResult::Registered 688 | } 689 | 690 | _ => { 691 | // Register the task. 692 | entry.state().set(State::Task(task.into_task())); 693 | RegisterResult::Registered 694 | } 695 | } 696 | } 697 | } 698 | 699 | pub(crate) enum Listener { 700 | /// The listener has a node inside of the linked list. 701 | HasNode(NonZeroUsize), 702 | 703 | /// The listener has an entry in the queue that may or may not have a task waiting. 704 | Queued(Arc), 705 | 706 | /// Eat the generic type for consistency. 707 | _EatGenericType(PhantomData), 708 | } 709 | 710 | impl fmt::Debug for Listener { 711 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 712 | match self { 713 | Self::HasNode(key) => f.debug_tuple("HasNode").field(key).finish(), 714 | Self::Queued(tw) => f.debug_tuple("Queued").field(tw).finish(), 715 | Self::_EatGenericType(_) => unreachable!(), 716 | } 717 | } 718 | } 719 | 720 | impl Unpin for Listener {} 721 | 722 | impl PartialEq for Listener { 723 | fn eq(&self, other: &Self) -> bool { 724 | match (self, other) { 725 | (Self::HasNode(a), Self::HasNode(b)) => a == b, 726 | (Self::Queued(a), Self::Queued(b)) => Arc::ptr_eq(a, b), 727 | _ => false, 728 | } 729 | } 730 | } 731 | 732 | /// A simple mutex type that optimistically assumes that the lock is uncontended. 733 | pub(crate) struct Mutex { 734 | /// The inner value. 735 | value: UnsafeCell, 736 | 737 | /// Whether the mutex is locked. 738 | locked: AtomicBool, 739 | } 740 | 741 | impl fmt::Debug for Mutex { 742 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 743 | if let Some(lock) = self.try_lock() { 744 | f.debug_tuple("Mutex").field(&*lock).finish() 745 | } else { 746 | f.write_str("Mutex { }") 747 | } 748 | } 749 | } 750 | 751 | impl Mutex { 752 | /// Create a new mutex. 753 | pub(crate) fn new(value: T) -> Self { 754 | Self { 755 | value: UnsafeCell::new(value), 756 | locked: AtomicBool::new(false), 757 | } 758 | } 759 | 760 | /// Lock the mutex. 761 | pub(crate) fn try_lock(&self) -> Option> { 762 | // Try to lock the mutex. 763 | if self 764 | .locked 765 | .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) 766 | .is_ok() 767 | { 768 | // We have successfully locked the mutex. 769 | Some(MutexGuard { 770 | mutex: self, 771 | guard: self.value.get(), 772 | }) 773 | } else { 774 | self.try_lock_slow() 775 | } 776 | } 777 | 778 | #[cold] 779 | fn try_lock_slow(&self) -> Option> { 780 | // Assume that the contention is short-term. 781 | // Spin for a while to see if the mutex becomes unlocked. 782 | let mut spins = 100u32; 783 | 784 | loop { 785 | if self 786 | .locked 787 | .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed) 788 | .is_ok() 789 | { 790 | // We have successfully locked the mutex. 791 | return Some(MutexGuard { 792 | mutex: self, 793 | guard: self.value.get(), 794 | }); 795 | } 796 | 797 | // Use atomic loads instead of compare-exchange. 798 | while self.locked.load(Ordering::Relaxed) { 799 | // Return None once we've exhausted the number of spins. 800 | spins = spins.checked_sub(1)?; 801 | } 802 | } 803 | } 804 | } 805 | 806 | pub(crate) struct MutexGuard<'a, T> { 807 | mutex: &'a Mutex, 808 | guard: ConstPtr, 809 | } 810 | 811 | impl<'a, T> Drop for MutexGuard<'a, T> { 812 | fn drop(&mut self) { 813 | self.mutex.locked.store(false, Ordering::Release); 814 | } 815 | } 816 | 817 | impl<'a, T> ops::Deref for MutexGuard<'a, T> { 818 | type Target = T; 819 | 820 | fn deref(&self) -> &T { 821 | unsafe { self.guard.deref() } 822 | } 823 | } 824 | 825 | impl<'a, T> ops::DerefMut for MutexGuard<'a, T> { 826 | fn deref_mut(&mut self) -> &mut T { 827 | unsafe { self.guard.deref_mut() } 828 | } 829 | } 830 | 831 | unsafe impl Send for Mutex {} 832 | unsafe impl Sync for Mutex {} 833 | 834 | #[cfg(test)] 835 | mod tests { 836 | use super::*; 837 | 838 | #[cfg(target_family = "wasm")] 839 | use wasm_bindgen_test::wasm_bindgen_test as test; 840 | 841 | #[test] 842 | fn smoke_mutex() { 843 | let mutex = Mutex::new(0); 844 | 845 | { 846 | let mut guard = mutex.try_lock().unwrap(); 847 | *guard += 1; 848 | } 849 | 850 | { 851 | let mut guard = mutex.try_lock().unwrap(); 852 | *guard += 1; 853 | } 854 | 855 | let guard = mutex.try_lock().unwrap(); 856 | assert_eq!(*guard, 2); 857 | } 858 | 859 | #[test] 860 | fn smoke_listener_slab() { 861 | let mut listeners = ListenerSlab::<()>::new(); 862 | 863 | // Insert a few listeners. 864 | let key1 = listeners.insert(State::Created); 865 | let key2 = listeners.insert(State::Created); 866 | let key3 = listeners.insert(State::Created); 867 | 868 | assert_eq!(listeners.len, 3); 869 | assert_eq!(listeners.notified, 0); 870 | assert_eq!(listeners.tail, Some(key3)); 871 | assert_eq!(listeners.head, Some(key1)); 872 | assert_eq!(listeners.start, Some(key1)); 873 | assert_eq!(listeners.first_empty, NonZeroUsize::new(4).unwrap()); 874 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 875 | assert_eq!( 876 | listeners.listeners[1], 877 | Entry::Listener { 878 | state: Cell::new(State::Created), 879 | prev: Cell::new(None), 880 | next: Cell::new(Some(key2)), 881 | } 882 | ); 883 | assert_eq!( 884 | listeners.listeners[2], 885 | Entry::Listener { 886 | state: Cell::new(State::Created), 887 | prev: Cell::new(Some(key1)), 888 | next: Cell::new(Some(key3)), 889 | } 890 | ); 891 | assert_eq!( 892 | listeners.listeners[3], 893 | Entry::Listener { 894 | state: Cell::new(State::Created), 895 | prev: Cell::new(Some(key2)), 896 | next: Cell::new(None), 897 | } 898 | ); 899 | 900 | // Remove one. 901 | assert_eq!(listeners.remove(key2, false), Some(State::Created)); 902 | 903 | assert_eq!(listeners.len, 2); 904 | assert_eq!(listeners.notified, 0); 905 | assert_eq!(listeners.tail, Some(key3)); 906 | assert_eq!(listeners.head, Some(key1)); 907 | assert_eq!(listeners.start, Some(key1)); 908 | assert_eq!(listeners.first_empty, NonZeroUsize::new(2).unwrap()); 909 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 910 | assert_eq!( 911 | listeners.listeners[1], 912 | Entry::Listener { 913 | state: Cell::new(State::Created), 914 | prev: Cell::new(None), 915 | next: Cell::new(Some(key3)), 916 | } 917 | ); 918 | assert_eq!( 919 | listeners.listeners[2], 920 | Entry::Empty(NonZeroUsize::new(4).unwrap()) 921 | ); 922 | assert_eq!( 923 | listeners.listeners[3], 924 | Entry::Listener { 925 | state: Cell::new(State::Created), 926 | prev: Cell::new(Some(key1)), 927 | next: Cell::new(None), 928 | } 929 | ); 930 | } 931 | 932 | #[test] 933 | fn listener_slab_notify() { 934 | let mut listeners = ListenerSlab::new(); 935 | 936 | // Insert a few listeners. 937 | let key1 = listeners.insert(State::Created); 938 | let key2 = listeners.insert(State::Created); 939 | let key3 = listeners.insert(State::Created); 940 | 941 | // Notify one. 942 | listeners.notify(GenericNotify::new(1, true, || ())); 943 | 944 | assert_eq!(listeners.len, 3); 945 | assert_eq!(listeners.notified, 1); 946 | assert_eq!(listeners.tail, Some(key3)); 947 | assert_eq!(listeners.head, Some(key1)); 948 | assert_eq!(listeners.start, Some(key2)); 949 | assert_eq!(listeners.first_empty, NonZeroUsize::new(4).unwrap()); 950 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 951 | assert_eq!( 952 | listeners.listeners[1], 953 | Entry::Listener { 954 | state: Cell::new(State::Notified { 955 | additional: true, 956 | tag: () 957 | }), 958 | prev: Cell::new(None), 959 | next: Cell::new(Some(key2)), 960 | } 961 | ); 962 | assert_eq!( 963 | listeners.listeners[2], 964 | Entry::Listener { 965 | state: Cell::new(State::Created), 966 | prev: Cell::new(Some(key1)), 967 | next: Cell::new(Some(key3)), 968 | } 969 | ); 970 | assert_eq!( 971 | listeners.listeners[3], 972 | Entry::Listener { 973 | state: Cell::new(State::Created), 974 | prev: Cell::new(Some(key2)), 975 | next: Cell::new(None), 976 | } 977 | ); 978 | 979 | // Remove the notified listener. 980 | assert_eq!( 981 | listeners.remove(key1, false), 982 | Some(State::Notified { 983 | additional: true, 984 | tag: () 985 | }) 986 | ); 987 | 988 | assert_eq!(listeners.len, 2); 989 | assert_eq!(listeners.notified, 0); 990 | assert_eq!(listeners.tail, Some(key3)); 991 | assert_eq!(listeners.head, Some(key2)); 992 | assert_eq!(listeners.start, Some(key2)); 993 | assert_eq!(listeners.first_empty, NonZeroUsize::new(1).unwrap()); 994 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 995 | assert_eq!( 996 | listeners.listeners[1], 997 | Entry::Empty(NonZeroUsize::new(4).unwrap()) 998 | ); 999 | assert_eq!( 1000 | listeners.listeners[2], 1001 | Entry::Listener { 1002 | state: Cell::new(State::Created), 1003 | prev: Cell::new(None), 1004 | next: Cell::new(Some(key3)), 1005 | } 1006 | ); 1007 | assert_eq!( 1008 | listeners.listeners[3], 1009 | Entry::Listener { 1010 | state: Cell::new(State::Created), 1011 | prev: Cell::new(Some(key2)), 1012 | next: Cell::new(None), 1013 | } 1014 | ); 1015 | } 1016 | 1017 | #[test] 1018 | fn listener_slab_register() { 1019 | let woken = Arc::new(AtomicBool::new(false)); 1020 | let waker = waker_fn::waker_fn({ 1021 | let woken = woken.clone(); 1022 | move || woken.store(true, Ordering::SeqCst) 1023 | }); 1024 | 1025 | let mut listeners = ListenerSlab::new(); 1026 | 1027 | // Insert a few listeners. 1028 | let key1 = listeners.insert(State::Created); 1029 | let key2 = listeners.insert(State::Created); 1030 | let key3 = listeners.insert(State::Created); 1031 | 1032 | // Register one. 1033 | assert_eq!( 1034 | listeners.register( 1035 | Pin::new(&mut Some(Listener::HasNode(key2))), 1036 | TaskRef::Waker(&waker) 1037 | ), 1038 | RegisterResult::Registered 1039 | ); 1040 | 1041 | assert_eq!(listeners.len, 3); 1042 | assert_eq!(listeners.notified, 0); 1043 | assert_eq!(listeners.tail, Some(key3)); 1044 | assert_eq!(listeners.head, Some(key1)); 1045 | assert_eq!(listeners.start, Some(key1)); 1046 | assert_eq!(listeners.first_empty, NonZeroUsize::new(4).unwrap()); 1047 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 1048 | assert_eq!( 1049 | listeners.listeners[1], 1050 | Entry::Listener { 1051 | state: Cell::new(State::Created), 1052 | prev: Cell::new(None), 1053 | next: Cell::new(Some(key2)), 1054 | } 1055 | ); 1056 | assert_eq!( 1057 | listeners.listeners[2], 1058 | Entry::Listener { 1059 | state: Cell::new(State::Task(Task::Waker(waker.clone()))), 1060 | prev: Cell::new(Some(key1)), 1061 | next: Cell::new(Some(key3)), 1062 | } 1063 | ); 1064 | assert_eq!( 1065 | listeners.listeners[3], 1066 | Entry::Listener { 1067 | state: Cell::new(State::Created), 1068 | prev: Cell::new(Some(key2)), 1069 | next: Cell::new(None), 1070 | } 1071 | ); 1072 | 1073 | // Notify the listener. 1074 | listeners.notify(GenericNotify::new(2, false, || ())); 1075 | 1076 | assert_eq!(listeners.len, 3); 1077 | assert_eq!(listeners.notified, 2); 1078 | assert_eq!(listeners.tail, Some(key3)); 1079 | assert_eq!(listeners.head, Some(key1)); 1080 | assert_eq!(listeners.start, Some(key3)); 1081 | assert_eq!(listeners.first_empty, NonZeroUsize::new(4).unwrap()); 1082 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 1083 | assert_eq!( 1084 | listeners.listeners[1], 1085 | Entry::Listener { 1086 | state: Cell::new(State::Notified { 1087 | additional: false, 1088 | tag: (), 1089 | }), 1090 | prev: Cell::new(None), 1091 | next: Cell::new(Some(key2)), 1092 | } 1093 | ); 1094 | assert_eq!( 1095 | listeners.listeners[2], 1096 | Entry::Listener { 1097 | state: Cell::new(State::Notified { 1098 | additional: false, 1099 | tag: (), 1100 | }), 1101 | prev: Cell::new(Some(key1)), 1102 | next: Cell::new(Some(key3)), 1103 | } 1104 | ); 1105 | assert_eq!( 1106 | listeners.listeners[3], 1107 | Entry::Listener { 1108 | state: Cell::new(State::Created), 1109 | prev: Cell::new(Some(key2)), 1110 | next: Cell::new(None), 1111 | } 1112 | ); 1113 | 1114 | assert!(woken.load(Ordering::SeqCst)); 1115 | assert_eq!( 1116 | listeners.register( 1117 | Pin::new(&mut Some(Listener::HasNode(key2))), 1118 | TaskRef::Waker(&waker) 1119 | ), 1120 | RegisterResult::Notified(()) 1121 | ); 1122 | } 1123 | 1124 | #[test] 1125 | fn listener_slab_notify_prop() { 1126 | let woken = Arc::new(AtomicBool::new(false)); 1127 | let waker = waker_fn::waker_fn({ 1128 | let woken = woken.clone(); 1129 | move || woken.store(true, Ordering::SeqCst) 1130 | }); 1131 | 1132 | let mut listeners = ListenerSlab::new(); 1133 | 1134 | // Insert a few listeners. 1135 | let key1 = listeners.insert(State::Created); 1136 | let key2 = listeners.insert(State::Created); 1137 | let key3 = listeners.insert(State::Created); 1138 | 1139 | // Register one. 1140 | assert_eq!( 1141 | listeners.register( 1142 | Pin::new(&mut Some(Listener::HasNode(key2))), 1143 | TaskRef::Waker(&waker) 1144 | ), 1145 | RegisterResult::Registered 1146 | ); 1147 | 1148 | assert_eq!(listeners.len, 3); 1149 | assert_eq!(listeners.notified, 0); 1150 | assert_eq!(listeners.tail, Some(key3)); 1151 | assert_eq!(listeners.head, Some(key1)); 1152 | assert_eq!(listeners.start, Some(key1)); 1153 | assert_eq!(listeners.first_empty, NonZeroUsize::new(4).unwrap()); 1154 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 1155 | assert_eq!( 1156 | listeners.listeners[1], 1157 | Entry::Listener { 1158 | state: Cell::new(State::Created), 1159 | prev: Cell::new(None), 1160 | next: Cell::new(Some(key2)), 1161 | } 1162 | ); 1163 | assert_eq!( 1164 | listeners.listeners[2], 1165 | Entry::Listener { 1166 | state: Cell::new(State::Task(Task::Waker(waker.clone()))), 1167 | prev: Cell::new(Some(key1)), 1168 | next: Cell::new(Some(key3)), 1169 | } 1170 | ); 1171 | assert_eq!( 1172 | listeners.listeners[3], 1173 | Entry::Listener { 1174 | state: Cell::new(State::Created), 1175 | prev: Cell::new(Some(key2)), 1176 | next: Cell::new(None), 1177 | } 1178 | ); 1179 | 1180 | // Notify the first listener. 1181 | listeners.notify(GenericNotify::new(1, false, || ())); 1182 | 1183 | assert_eq!(listeners.len, 3); 1184 | assert_eq!(listeners.notified, 1); 1185 | assert_eq!(listeners.tail, Some(key3)); 1186 | assert_eq!(listeners.head, Some(key1)); 1187 | assert_eq!(listeners.start, Some(key2)); 1188 | assert_eq!(listeners.first_empty, NonZeroUsize::new(4).unwrap()); 1189 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 1190 | assert_eq!( 1191 | listeners.listeners[1], 1192 | Entry::Listener { 1193 | state: Cell::new(State::Notified { 1194 | additional: false, 1195 | tag: (), 1196 | }), 1197 | prev: Cell::new(None), 1198 | next: Cell::new(Some(key2)), 1199 | } 1200 | ); 1201 | assert_eq!( 1202 | listeners.listeners[2], 1203 | Entry::Listener { 1204 | state: Cell::new(State::Task(Task::Waker(waker.clone()))), 1205 | prev: Cell::new(Some(key1)), 1206 | next: Cell::new(Some(key3)), 1207 | } 1208 | ); 1209 | assert_eq!( 1210 | listeners.listeners[3], 1211 | Entry::Listener { 1212 | state: Cell::new(State::Created), 1213 | prev: Cell::new(Some(key2)), 1214 | next: Cell::new(None), 1215 | } 1216 | ); 1217 | 1218 | // Calling notify again should not change anything. 1219 | listeners.notify(GenericNotify::new(1, false, || ())); 1220 | 1221 | assert_eq!(listeners.len, 3); 1222 | assert_eq!(listeners.notified, 1); 1223 | assert_eq!(listeners.tail, Some(key3)); 1224 | assert_eq!(listeners.head, Some(key1)); 1225 | assert_eq!(listeners.start, Some(key2)); 1226 | assert_eq!(listeners.first_empty, NonZeroUsize::new(4).unwrap()); 1227 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 1228 | assert_eq!( 1229 | listeners.listeners[1], 1230 | Entry::Listener { 1231 | state: Cell::new(State::Notified { 1232 | additional: false, 1233 | tag: (), 1234 | }), 1235 | prev: Cell::new(None), 1236 | next: Cell::new(Some(key2)), 1237 | } 1238 | ); 1239 | assert_eq!( 1240 | listeners.listeners[2], 1241 | Entry::Listener { 1242 | state: Cell::new(State::Task(Task::Waker(waker.clone()))), 1243 | prev: Cell::new(Some(key1)), 1244 | next: Cell::new(Some(key3)), 1245 | } 1246 | ); 1247 | assert_eq!( 1248 | listeners.listeners[3], 1249 | Entry::Listener { 1250 | state: Cell::new(State::Created), 1251 | prev: Cell::new(Some(key2)), 1252 | next: Cell::new(None), 1253 | } 1254 | ); 1255 | 1256 | // Remove the first listener. 1257 | assert_eq!( 1258 | listeners.remove(key1, false), 1259 | Some(State::Notified { 1260 | additional: false, 1261 | tag: () 1262 | }) 1263 | ); 1264 | 1265 | assert_eq!(listeners.len, 2); 1266 | assert_eq!(listeners.notified, 0); 1267 | assert_eq!(listeners.tail, Some(key3)); 1268 | assert_eq!(listeners.head, Some(key2)); 1269 | assert_eq!(listeners.start, Some(key2)); 1270 | assert_eq!(listeners.first_empty, NonZeroUsize::new(1).unwrap()); 1271 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 1272 | assert_eq!( 1273 | listeners.listeners[1], 1274 | Entry::Empty(NonZeroUsize::new(4).unwrap()) 1275 | ); 1276 | assert_eq!(*listeners.listeners[2].prev(), Cell::new(None)); 1277 | assert_eq!(*listeners.listeners[2].next(), Cell::new(Some(key3))); 1278 | assert_eq!( 1279 | listeners.listeners[3], 1280 | Entry::Listener { 1281 | state: Cell::new(State::Created), 1282 | prev: Cell::new(Some(key2)), 1283 | next: Cell::new(None), 1284 | } 1285 | ); 1286 | 1287 | // Notify the second listener. 1288 | listeners.notify(GenericNotify::new(1, false, || ())); 1289 | assert!(woken.load(Ordering::SeqCst)); 1290 | 1291 | assert_eq!(listeners.len, 2); 1292 | assert_eq!(listeners.notified, 1); 1293 | assert_eq!(listeners.tail, Some(key3)); 1294 | assert_eq!(listeners.head, Some(key2)); 1295 | assert_eq!(listeners.start, Some(key3)); 1296 | assert_eq!(listeners.first_empty, NonZeroUsize::new(1).unwrap()); 1297 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 1298 | assert_eq!( 1299 | listeners.listeners[1], 1300 | Entry::Empty(NonZeroUsize::new(4).unwrap()) 1301 | ); 1302 | assert_eq!( 1303 | listeners.listeners[2], 1304 | Entry::Listener { 1305 | state: Cell::new(State::Notified { 1306 | additional: false, 1307 | tag: (), 1308 | }), 1309 | prev: Cell::new(None), 1310 | next: Cell::new(Some(key3)), 1311 | } 1312 | ); 1313 | assert_eq!( 1314 | listeners.listeners[3], 1315 | Entry::Listener { 1316 | state: Cell::new(State::Created), 1317 | prev: Cell::new(Some(key2)), 1318 | next: Cell::new(None), 1319 | } 1320 | ); 1321 | 1322 | // Remove and propagate the second listener. 1323 | assert_eq!(listeners.remove(key2, true), Some(State::NotifiedTaken)); 1324 | 1325 | // The third listener should be notified. 1326 | assert_eq!(listeners.len, 1); 1327 | assert_eq!(listeners.notified, 1); 1328 | assert_eq!(listeners.tail, Some(key3)); 1329 | assert_eq!(listeners.head, Some(key3)); 1330 | assert_eq!(listeners.start, None); 1331 | assert_eq!(listeners.first_empty, NonZeroUsize::new(2).unwrap()); 1332 | assert_eq!(listeners.listeners[0], Entry::Sentinel); 1333 | assert_eq!( 1334 | listeners.listeners[1], 1335 | Entry::Empty(NonZeroUsize::new(4).unwrap()) 1336 | ); 1337 | assert_eq!( 1338 | listeners.listeners[2], 1339 | Entry::Empty(NonZeroUsize::new(1).unwrap()) 1340 | ); 1341 | assert_eq!( 1342 | listeners.listeners[3], 1343 | Entry::Listener { 1344 | state: Cell::new(State::Notified { 1345 | additional: false, 1346 | tag: (), 1347 | }), 1348 | prev: Cell::new(None), 1349 | next: Cell::new(None), 1350 | } 1351 | ); 1352 | 1353 | // Remove the third listener. 1354 | assert_eq!( 1355 | listeners.remove(key3, false), 1356 | Some(State::Notified { 1357 | additional: false, 1358 | tag: () 1359 | }) 1360 | ); 1361 | } 1362 | 1363 | #[test] 1364 | fn uncontended_inner() { 1365 | let inner = crate::Inner::new(); 1366 | 1367 | // Register two listeners. 1368 | let (mut listener1, mut listener2, mut listener3) = (None, None, None); 1369 | inner.insert(Pin::new(&mut listener1)); 1370 | inner.insert(Pin::new(&mut listener2)); 1371 | inner.insert(Pin::new(&mut listener3)); 1372 | 1373 | assert_eq!( 1374 | listener1, 1375 | Some(Listener::HasNode(NonZeroUsize::new(1).unwrap())) 1376 | ); 1377 | assert_eq!( 1378 | listener2, 1379 | Some(Listener::HasNode(NonZeroUsize::new(2).unwrap())) 1380 | ); 1381 | 1382 | // Register a waker in the second listener. 1383 | let woken = Arc::new(AtomicBool::new(false)); 1384 | let waker = waker_fn::waker_fn({ 1385 | let woken = woken.clone(); 1386 | move || woken.store(true, Ordering::SeqCst) 1387 | }); 1388 | assert_eq!( 1389 | inner.register(Pin::new(&mut listener2), TaskRef::Waker(&waker)), 1390 | RegisterResult::Registered 1391 | ); 1392 | 1393 | // Notify the first listener. 1394 | inner.notify(GenericNotify::new(1, false, || ())); 1395 | assert!(!woken.load(Ordering::SeqCst)); 1396 | 1397 | // Another notify should do nothing. 1398 | inner.notify(GenericNotify::new(1, false, || ())); 1399 | assert!(!woken.load(Ordering::SeqCst)); 1400 | 1401 | // Receive the notification. 1402 | assert_eq!( 1403 | inner.register(Pin::new(&mut listener1), TaskRef::Waker(&waker)), 1404 | RegisterResult::Notified(()) 1405 | ); 1406 | 1407 | // First listener is already removed. 1408 | assert!(listener1.is_none()); 1409 | 1410 | // Notify the second listener. 1411 | inner.notify(GenericNotify::new(1, false, || ())); 1412 | assert!(woken.load(Ordering::SeqCst)); 1413 | 1414 | // Remove the second listener and propagate the notification. 1415 | assert_eq!( 1416 | inner.remove(Pin::new(&mut listener2), true), 1417 | Some(State::NotifiedTaken) 1418 | ); 1419 | 1420 | // Second listener is already removed. 1421 | assert!(listener2.is_none()); 1422 | 1423 | // Third listener should be notified. 1424 | assert_eq!( 1425 | inner.register(Pin::new(&mut listener3), TaskRef::Waker(&waker)), 1426 | RegisterResult::Notified(()) 1427 | ); 1428 | } 1429 | } 1430 | -------------------------------------------------------------------------------- /src/slab/node.rs: -------------------------------------------------------------------------------- 1 | //! An operation that can be delayed. 2 | 3 | //! The node that makes up queues. 4 | 5 | use crate::notify::{GenericNotify, Internal, NotificationPrivate, TagProducer}; 6 | use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; 7 | use crate::sync::Arc; 8 | use crate::sys::ListenerSlab; 9 | use crate::{State, Task}; 10 | 11 | use alloc::boxed::Box; 12 | 13 | use core::fmt; 14 | use core::marker::PhantomData; 15 | use core::mem; 16 | use core::num::NonZeroUsize; 17 | use core::ptr; 18 | 19 | pub(crate) struct NothingProducer(PhantomData); 20 | 21 | impl Default for NothingProducer { 22 | fn default() -> Self { 23 | Self(PhantomData) 24 | } 25 | } 26 | 27 | impl fmt::Debug for NothingProducer { 28 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 29 | f.debug_struct("NothingProducer").finish() 30 | } 31 | } 32 | 33 | impl TagProducer for NothingProducer { 34 | type Tag = T; 35 | 36 | fn next_tag(&mut self) -> Self::Tag { 37 | // This has to be a zero-sized type with no drop handler. 38 | assert_eq!(mem::size_of::(), 0); 39 | assert!(!mem::needs_drop::()); 40 | 41 | // SAFETY: As this is a ZST without a drop handler, zero is valid. 42 | unsafe { mem::zeroed() } 43 | } 44 | } 45 | 46 | /// A node in the backup queue. 47 | pub(crate) enum Node { 48 | /// This node is requesting to add a listener. 49 | // For some reason, the MSRV build says this variant is never constructed. 50 | #[allow(dead_code)] 51 | AddListener { 52 | /// The state of the listener that wants to be added. 53 | task_waiting: Arc, 54 | }, 55 | 56 | /// This node is notifying a listener. 57 | Notify(GenericNotify>), 58 | 59 | /// This node is removing a listener. 60 | RemoveListener { 61 | /// The ID of the listener to remove. 62 | listener: NonZeroUsize, 63 | 64 | /// Whether to propagate notifications to the next listener. 65 | propagate: bool, 66 | }, 67 | 68 | /// We are waiting for the mutex to lock, so they can manipulate it. 69 | Waiting(Task), 70 | } 71 | 72 | impl fmt::Debug for Node { 73 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 74 | match self { 75 | Self::AddListener { .. } => f.write_str("AddListener"), 76 | Self::Notify(notify) => f 77 | .debug_struct("Notify") 78 | .field("count", ¬ify.count(Internal::new())) 79 | .field("is_additional", ¬ify.is_additional(Internal::new())) 80 | .finish(), 81 | Self::RemoveListener { 82 | listener, 83 | propagate, 84 | } => f 85 | .debug_struct("RemoveListener") 86 | .field("listener", listener) 87 | .field("propagate", propagate) 88 | .finish(), 89 | Self::Waiting(_) => f.write_str("Waiting"), 90 | } 91 | } 92 | } 93 | 94 | #[derive(Debug)] 95 | pub(crate) struct TaskWaiting { 96 | /// The task that is being waited on. 97 | task: AtomicCell, 98 | 99 | /// The ID of the new entry. 100 | /// 101 | /// This is set to zero when the task is still queued, or usize::MAX when the node should not 102 | /// be added at all. 103 | entry_id: AtomicUsize, 104 | } 105 | 106 | impl Node { 107 | pub(crate) fn listener() -> (Self, Arc) { 108 | // Create a new `TaskWaiting` structure. 109 | let task_waiting = Arc::new(TaskWaiting { 110 | task: AtomicCell::new(), 111 | entry_id: AtomicUsize::new(0), 112 | }); 113 | 114 | ( 115 | Self::AddListener { 116 | task_waiting: task_waiting.clone(), 117 | }, 118 | task_waiting, 119 | ) 120 | } 121 | 122 | /// Apply the node to the list. 123 | pub(super) fn apply(self, list: &mut ListenerSlab) -> Option { 124 | match self { 125 | Node::AddListener { task_waiting } => { 126 | // If we're cancelled, do nothing. 127 | if task_waiting.entry_id.load(Ordering::Relaxed) == usize::MAX { 128 | return task_waiting.task.take().map(|t| *t); 129 | } 130 | 131 | // Add a new entry to the list. 132 | let key = list.insert(State::Created); 133 | assert!(key.get() != usize::MAX); 134 | 135 | // Send the new key to the listener and wake it if necessary. 136 | let old_value = task_waiting.entry_id.swap(key.get(), Ordering::Release); 137 | 138 | // If we're cancelled, remove ourselves from the list. 139 | if old_value == usize::MAX { 140 | list.remove(key, false); 141 | } 142 | 143 | return task_waiting.task.take().map(|t| *t); 144 | } 145 | Node::Notify(notify) => { 146 | // Notify the next `count` listeners. 147 | list.notify(notify); 148 | } 149 | Node::RemoveListener { 150 | listener, 151 | propagate, 152 | } => { 153 | // Remove the listener from the list. 154 | list.remove(listener, propagate); 155 | } 156 | Node::Waiting(task) => { 157 | return Some(task); 158 | } 159 | } 160 | 161 | None 162 | } 163 | } 164 | 165 | impl TaskWaiting { 166 | /// Determine if we are still queued. 167 | /// 168 | /// Returns `Some` with the entry ID if we are no longer queued. 169 | pub(crate) fn status(&self) -> Option { 170 | NonZeroUsize::new(self.entry_id.load(Ordering::Acquire)) 171 | } 172 | 173 | /// Register a listener. 174 | pub(crate) fn register(&self, task: Task) { 175 | // Set the task. 176 | if let Some(task) = self.task.replace(Some(Box::new(task))) { 177 | task.wake(); 178 | } 179 | 180 | // If the entry ID is non-zero, then we are no longer queued. 181 | if self.status().is_some() { 182 | // Wake the task. 183 | if let Some(task) = self.task.take() { 184 | task.wake(); 185 | } 186 | } 187 | } 188 | 189 | /// Mark this listener as cancelled, indicating that it should not be inserted into the list. 190 | /// 191 | /// If this listener was already inserted into the list, returns the entry ID. Otherwise returns 192 | /// `None`. 193 | pub(crate) fn cancel(&self) -> Option { 194 | // Set the entry ID to usize::MAX. 195 | let id = self.entry_id.swap(usize::MAX, Ordering::Release); 196 | 197 | // Wake the task. 198 | if let Some(task) = self.task.take() { 199 | task.wake(); 200 | } 201 | 202 | // Return the entry ID if we were queued. 203 | NonZeroUsize::new(id) 204 | } 205 | } 206 | 207 | /// A shared pointer to a value. 208 | /// 209 | /// The inner value is a `Box`. 210 | #[derive(Debug)] 211 | struct AtomicCell(AtomicPtr); 212 | 213 | impl AtomicCell { 214 | /// Create a new `AtomicCell`. 215 | fn new() -> Self { 216 | Self(AtomicPtr::new(ptr::null_mut())) 217 | } 218 | 219 | /// Swap the value out. 220 | fn replace(&self, value: Option>) -> Option> { 221 | let old_value = match value { 222 | Some(value) => self.0.swap(Box::into_raw(value), Ordering::AcqRel), 223 | // Acquire is needed to synchronize with the store of a non-null ptr, but since a null ptr 224 | // will never be dereferenced, there is no need to synchronize the store of a null ptr. 225 | None => self.0.swap(ptr::null_mut(), Ordering::Acquire), 226 | }; 227 | 228 | if old_value.is_null() { 229 | None 230 | } else { 231 | // SAFETY: 232 | // - AcqRel/Acquire ensures that it does not read a pointer to potentially invalid memory. 233 | // - We've checked that old_value is not null. 234 | // - We do not store invalid pointers other than null in self.0. 235 | Some(unsafe { Box::from_raw(old_value) }) 236 | } 237 | } 238 | 239 | /// Take the value out. 240 | fn take(&self) -> Option> { 241 | self.replace(None) 242 | } 243 | } 244 | 245 | impl Drop for AtomicCell { 246 | fn drop(&mut self) { 247 | self.take(); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /tests/loom.rs: -------------------------------------------------------------------------------- 1 | #![cfg(loom)] 2 | use std::future::Future; 3 | use std::pin::Pin; 4 | use std::sync::{Arc, Mutex}; 5 | use std::task::Context; 6 | 7 | use event_listener::{Event, EventListener}; 8 | use waker_fn::waker_fn; 9 | 10 | #[cfg(target_family = "wasm")] 11 | use wasm_bindgen_test::wasm_bindgen_test as test; 12 | 13 | fn is_notified(listener: &mut EventListener) -> bool { 14 | let waker = waker_fn(|| ()); 15 | Pin::new(listener) 16 | .poll(&mut Context::from_waker(&waker)) 17 | .is_ready() 18 | } 19 | 20 | #[test] 21 | fn notify() { 22 | loom::model(|| { 23 | let event = Event::new(); 24 | 25 | let mut l1 = event.listen(); 26 | let mut l2 = event.listen(); 27 | let mut l3 = event.listen(); 28 | 29 | assert!(!is_notified(&mut l1)); 30 | assert!(!is_notified(&mut l2)); 31 | assert!(!is_notified(&mut l3)); 32 | 33 | assert_eq!(event.notify(2), 2); 34 | assert_eq!(event.notify(1), 0); 35 | 36 | assert!(is_notified(&mut l1)); 37 | assert!(is_notified(&mut l2)); 38 | assert!(!is_notified(&mut l3)); 39 | }); 40 | } 41 | 42 | #[test] 43 | fn notify_additional() { 44 | loom::model(|| { 45 | let event = Event::new(); 46 | 47 | let mut l1 = event.listen(); 48 | let mut l2 = event.listen(); 49 | let mut l3 = event.listen(); 50 | 51 | assert_eq!(event.notify_additional(1), 1); 52 | assert_eq!(event.notify(1), 0); 53 | assert_eq!(event.notify_additional(1), 1); 54 | 55 | assert!(is_notified(&mut l1)); 56 | assert!(is_notified(&mut l2)); 57 | assert!(!is_notified(&mut l3)); 58 | }) 59 | } 60 | 61 | #[test] 62 | fn notify_one() { 63 | loom::model(|| { 64 | let event = Event::new(); 65 | 66 | let mut l1 = event.listen(); 67 | let mut l2 = event.listen(); 68 | 69 | assert!(!is_notified(&mut l1)); 70 | assert!(!is_notified(&mut l2)); 71 | 72 | assert_eq!(event.notify(1), 1); 73 | assert!(is_notified(&mut l1)); 74 | assert!(!is_notified(&mut l2)); 75 | 76 | assert_eq!(event.notify(1), 1); 77 | assert!(is_notified(&mut l2)); 78 | }); 79 | } 80 | 81 | #[test] 82 | fn notify_all() { 83 | loom::model(|| { 84 | let event = Event::new(); 85 | 86 | let mut l1 = event.listen(); 87 | let mut l2 = event.listen(); 88 | 89 | assert!(!is_notified(&mut l1)); 90 | assert!(!is_notified(&mut l2)); 91 | 92 | assert_eq!(event.notify(usize::MAX), 2); 93 | assert!(is_notified(&mut l1)); 94 | assert!(is_notified(&mut l2)); 95 | }); 96 | } 97 | 98 | #[test] 99 | fn drop_notified() { 100 | loom::model(|| { 101 | let event = Event::new(); 102 | 103 | let l1 = event.listen(); 104 | let mut l2 = event.listen(); 105 | let mut l3 = event.listen(); 106 | 107 | assert_eq!(event.notify(1), 1); 108 | drop(l1); 109 | assert!(is_notified(&mut l2)); 110 | assert!(!is_notified(&mut l3)); 111 | }); 112 | } 113 | 114 | #[test] 115 | fn drop_notified2() { 116 | loom::model(|| { 117 | let event = Event::new(); 118 | 119 | let l1 = event.listen(); 120 | let mut l2 = event.listen(); 121 | let mut l3 = event.listen(); 122 | 123 | assert_eq!(event.notify(2), 2); 124 | drop(l1); 125 | assert!(is_notified(&mut l2)); 126 | assert!(!is_notified(&mut l3)); 127 | }); 128 | } 129 | 130 | #[test] 131 | fn drop_notified_additional() { 132 | loom::model(|| { 133 | let event = Event::new(); 134 | 135 | let l1 = event.listen(); 136 | let mut l2 = event.listen(); 137 | let mut l3 = event.listen(); 138 | let mut l4 = event.listen(); 139 | 140 | assert_eq!(event.notify_additional(1), 1); 141 | assert_eq!(event.notify(2), 1); 142 | drop(l1); 143 | assert!(is_notified(&mut l2)); 144 | assert!(is_notified(&mut l3)); 145 | assert!(!is_notified(&mut l4)); 146 | }); 147 | } 148 | 149 | #[test] 150 | fn drop_non_notified() { 151 | loom::model(|| { 152 | let event = Event::new(); 153 | 154 | let mut l1 = event.listen(); 155 | let mut l2 = event.listen(); 156 | let l3 = event.listen(); 157 | 158 | assert_eq!(event.notify(1), 1); 159 | drop(l3); 160 | assert!(is_notified(&mut l1)); 161 | assert!(!is_notified(&mut l2)); 162 | }) 163 | } 164 | 165 | #[test] 166 | fn notify_all_fair() { 167 | loom::model(|| { 168 | let event = Event::new(); 169 | let v = Arc::new(Mutex::new(vec![])); 170 | 171 | let mut l1 = event.listen(); 172 | let mut l2 = event.listen(); 173 | let mut l3 = event.listen(); 174 | 175 | let waker1 = { 176 | let v = v.clone(); 177 | waker_fn(move || v.lock().unwrap().push(1)) 178 | }; 179 | let waker2 = { 180 | let v = v.clone(); 181 | waker_fn(move || v.lock().unwrap().push(2)) 182 | }; 183 | let waker3 = { 184 | let v = v.clone(); 185 | waker_fn(move || v.lock().unwrap().push(3)) 186 | }; 187 | 188 | assert!(Pin::new(&mut l1) 189 | .poll(&mut Context::from_waker(&waker1)) 190 | .is_pending()); 191 | assert!(Pin::new(&mut l2) 192 | .poll(&mut Context::from_waker(&waker2)) 193 | .is_pending()); 194 | assert!(Pin::new(&mut l3) 195 | .poll(&mut Context::from_waker(&waker3)) 196 | .is_pending()); 197 | 198 | assert_eq!(event.notify(usize::MAX), 3); 199 | assert_eq!(&*v.lock().unwrap(), &[1, 2, 3]); 200 | 201 | assert!(Pin::new(&mut l1) 202 | .poll(&mut Context::from_waker(&waker1)) 203 | .is_ready()); 204 | assert!(Pin::new(&mut l2) 205 | .poll(&mut Context::from_waker(&waker2)) 206 | .is_ready()); 207 | assert!(Pin::new(&mut l3) 208 | .poll(&mut Context::from_waker(&waker3)) 209 | .is_ready()); 210 | }) 211 | } 212 | -------------------------------------------------------------------------------- /tests/notify.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::sync::{Arc, Mutex}; 4 | use std::task::Context; 5 | 6 | use event_listener::{Event, EventListener}; 7 | use waker_fn::waker_fn; 8 | 9 | #[cfg(target_family = "wasm")] 10 | use wasm_bindgen_test::wasm_bindgen_test as test; 11 | 12 | fn is_notified(listener: &mut EventListener) -> bool { 13 | let waker = waker_fn(|| ()); 14 | Pin::new(listener) 15 | .poll(&mut Context::from_waker(&waker)) 16 | .is_ready() 17 | } 18 | 19 | #[test] 20 | fn notify() { 21 | let event = Event::new(); 22 | 23 | let mut l1 = event.listen(); 24 | let mut l2 = event.listen(); 25 | let mut l3 = event.listen(); 26 | 27 | assert!(!is_notified(&mut l1)); 28 | assert!(!is_notified(&mut l2)); 29 | assert!(!is_notified(&mut l3)); 30 | 31 | assert_eq!(event.notify(2), 2); 32 | assert_eq!(event.notify(1), 0); 33 | 34 | assert!(is_notified(&mut l1)); 35 | assert!(is_notified(&mut l2)); 36 | assert!(!is_notified(&mut l3)); 37 | } 38 | 39 | #[test] 40 | fn notify_additional() { 41 | let event = Event::new(); 42 | 43 | let mut l1 = event.listen(); 44 | let mut l2 = event.listen(); 45 | let mut l3 = event.listen(); 46 | 47 | assert_eq!(event.notify_additional(1), 1); 48 | assert_eq!(event.notify(1), 0); 49 | assert_eq!(event.notify_additional(1), 1); 50 | 51 | assert!(is_notified(&mut l1)); 52 | assert!(is_notified(&mut l2)); 53 | assert!(!is_notified(&mut l3)); 54 | } 55 | 56 | #[test] 57 | fn notify_one() { 58 | let event = Event::new(); 59 | 60 | let mut l1 = event.listen(); 61 | let mut l2 = event.listen(); 62 | 63 | assert!(!is_notified(&mut l1)); 64 | assert!(!is_notified(&mut l2)); 65 | 66 | assert_eq!(event.notify(1), 1); 67 | assert!(is_notified(&mut l1)); 68 | assert!(!is_notified(&mut l2)); 69 | 70 | assert_eq!(event.notify(1), 1); 71 | assert!(is_notified(&mut l2)); 72 | } 73 | 74 | #[test] 75 | fn notify_all() { 76 | let event = Event::new(); 77 | 78 | let mut l1 = event.listen(); 79 | let mut l2 = event.listen(); 80 | 81 | assert!(!is_notified(&mut l1)); 82 | assert!(!is_notified(&mut l2)); 83 | 84 | assert_eq!(event.notify(usize::MAX), 2); 85 | assert!(is_notified(&mut l1)); 86 | assert!(is_notified(&mut l2)); 87 | } 88 | 89 | #[test] 90 | fn drop_notified() { 91 | let event = Event::new(); 92 | 93 | let l1 = event.listen(); 94 | let mut l2 = event.listen(); 95 | let mut l3 = event.listen(); 96 | 97 | assert_eq!(event.notify(1), 1); 98 | drop(l1); 99 | assert!(is_notified(&mut l2)); 100 | assert!(!is_notified(&mut l3)); 101 | } 102 | 103 | #[test] 104 | fn drop_notified2() { 105 | let event = Event::new(); 106 | 107 | let l1 = event.listen(); 108 | let mut l2 = event.listen(); 109 | let mut l3 = event.listen(); 110 | 111 | assert_eq!(event.notify(2), 2); 112 | drop(l1); 113 | assert!(is_notified(&mut l2)); 114 | assert!(!is_notified(&mut l3)); 115 | } 116 | 117 | #[test] 118 | fn drop_notified_additional() { 119 | let event = Event::new(); 120 | 121 | let l1 = event.listen(); 122 | let mut l2 = event.listen(); 123 | let mut l3 = event.listen(); 124 | let mut l4 = event.listen(); 125 | 126 | assert_eq!(event.notify_additional(1), 1); 127 | assert_eq!(event.notify(2), 1); 128 | drop(l1); 129 | assert!(is_notified(&mut l2)); 130 | assert!(is_notified(&mut l3)); 131 | assert!(!is_notified(&mut l4)); 132 | } 133 | 134 | #[test] 135 | fn drop_non_notified() { 136 | let event = Event::new(); 137 | 138 | let mut l1 = event.listen(); 139 | let mut l2 = event.listen(); 140 | let l3 = event.listen(); 141 | 142 | assert_eq!(event.notify(1), 1); 143 | drop(l3); 144 | assert!(is_notified(&mut l1)); 145 | assert!(!is_notified(&mut l2)); 146 | } 147 | 148 | #[test] 149 | fn notify_all_fair() { 150 | let event = Event::new(); 151 | let v = Arc::new(Mutex::new(vec![])); 152 | 153 | let mut l1 = event.listen(); 154 | let mut l2 = event.listen(); 155 | let mut l3 = event.listen(); 156 | 157 | let waker1 = { 158 | let v = v.clone(); 159 | waker_fn(move || v.lock().unwrap().push(1)) 160 | }; 161 | let waker2 = { 162 | let v = v.clone(); 163 | waker_fn(move || v.lock().unwrap().push(2)) 164 | }; 165 | let waker3 = { 166 | let v = v.clone(); 167 | waker_fn(move || v.lock().unwrap().push(3)) 168 | }; 169 | 170 | assert!(Pin::new(&mut l1) 171 | .poll(&mut Context::from_waker(&waker1)) 172 | .is_pending()); 173 | assert!(Pin::new(&mut l2) 174 | .poll(&mut Context::from_waker(&waker2)) 175 | .is_pending()); 176 | assert!(Pin::new(&mut l3) 177 | .poll(&mut Context::from_waker(&waker3)) 178 | .is_pending()); 179 | 180 | assert_eq!(event.notify(usize::MAX), 3); 181 | assert_eq!(&*v.lock().unwrap(), &[1, 2, 3]); 182 | 183 | assert!(Pin::new(&mut l1) 184 | .poll(&mut Context::from_waker(&waker1)) 185 | .is_ready()); 186 | assert!(Pin::new(&mut l2) 187 | .poll(&mut Context::from_waker(&waker2)) 188 | .is_ready()); 189 | assert!(Pin::new(&mut l3) 190 | .poll(&mut Context::from_waker(&waker3)) 191 | .is_ready()); 192 | } 193 | --------------------------------------------------------------------------------