crossbeam-channel-0.5.14/.cargo_vcs_info.json0000644000000001570000000000100145120ustar { "git": { "sha1": "ccd83ac4108a2a1b41e9c6e79c87267167d18dfa" }, "path_in_vcs": "crossbeam-channel" }crossbeam-channel-0.5.14/CHANGELOG.md000064400000000000000000000155201046102023000151130ustar 00000000000000# Version 0.5.14 - Fix stack overflow when sending large value to unbounded channel. (#1146, #1147) - Add `Select::new_biased` function. (#1150) - Remove inefficient spinning. (#1154) - Suppress buggy `clippy::zero_repeat_side_effects` lint in macro generated code. (#1123) # Version 0.5.13 - Add `select_biased!` macro. (#1040) # Version 0.5.12 - Fix memory leak in unbounded channel. (#1084) # Version 0.5.11 - Remove dependency on `cfg-if`. (#1072) # Version 0.5.10 - Relax the minimum supported Rust version to 1.60. (#1056) - Optimize `Drop` implementation of bounded channel. (#1057) # Version 0.5.9 - Bump the minimum supported Rust version to 1.61. (#1037) # Version 0.5.8 - Fix race condition in unbounded channel. (#972) # Version 0.5.7 **Note:** This release has been yanked due to bug fixed in 0.5.8. - Improve handling of very large timeout. (#953) # Version 0.5.6 **Note:** This release has been yanked due to bug fixed in 0.5.8. - Bump the minimum supported Rust version to 1.38. (#877) # Version 0.5.5 **Note:** This release has been yanked due to bug fixed in 0.5.8. - Replace Spinlock with Mutex. (#835) # Version 0.5.4 **Note:** This release has been yanked due to bug fixed in 0.5.8. - Workaround a bug in upstream related to TLS access on AArch64 Linux. (#802) # Version 0.5.3 **Note:** This release has been yanked. See [#802](https://github.com/crossbeam-rs/crossbeam/issues/802) for details. - Fix panic on very large timeout. (#798) # Version 0.5.2 **Note:** This release has been yanked. See [#802](https://github.com/crossbeam-rs/crossbeam/issues/802) for details. - Fix stacked borrows violations when `-Zmiri-tag-raw-pointers` is enabled. (#763, #764) # Version 0.5.1 **Note:** This release has been yanked due to bug fixed in 0.5.8. - Fix memory leak in unbounded channel. (#669) # Version 0.5.0 - Bump the minimum supported Rust version to 1.36. - Add `at()` function. - Add `Sender::send_deadline()` and `Receiver::recv_deadline()` methods. - Add `Select::select_deadline()` and `Select::ready_deadline()` methods. - Add `std` (enabled by default) feature for forward compatibility. - Allow `select!` macro compile with `forbid(unsafe_code)`. # Version 0.4.4 - Fix bug in release (yanking 0.4.3) - Fix UB and breaking change introduced in 0.4.3 # Version 0.4.3 **Note:** This release has been yanked. See [GHSA-v5m7-53cv-f3hx](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-v5m7-53cv-f3hx) for details. - Change license to "MIT OR Apache-2.0". # Version 0.4.2 - Fix bug in release (yanking 0.4.1) # Version 0.4.1 - Avoid time drift in `channel::tick`. (#456) - Fix unsoundness issues by adopting `MaybeUninit`. (#458) # Version 0.4.0 - Bump the minimum required version to 1.28. - Bump `crossbeam-utils` to `0.7`. # Version 0.3.9 - Fix a bug in reference counting. - Optimize `recv_timeout()`. - Add `Select::remove()`. - Various small improvements, code cleanup, more tests. # Version 0.3.8 - Bump the minimum required version of `crossbeam-utils`. # Version 0.3.7 - Remove `parking_lot` and `rand` dependencies. - Expand documentation. - Implement `Default` for `Select`. - Make `size_of::>()` smaller. - Several minor optimizations. - Add more tests. # Version 0.3.6 - Fix a bug in initialization of unbounded channels. # Version 0.3.5 - New implementation for unbounded channels. - A number of small performance improvements. - Remove `crossbeam-epoch` dependency. # Version 0.3.4 - Bump `crossbeam-epoch` to `0.7`. - Improve documentation. # Version 0.3.3 - Relax the lifetime in `SelectedOperation<'_>`. - Add `Select::try_ready()`, `Select::ready()`, and `Select::ready_timeout()`. - Update licensing notices. - Improve documentation. - Add methods `is_disconnected()`, `is_timeout()`, `is_empty()`, and `is_full()` on error types. # Version 0.3.2 - More elaborate licensing notices. # Version 0.3.1 - Update `crossbeam-utils` to `0.6`. # Version 0.3.0 - Add a special `never` channel type. - Dropping all receivers now closes the channel. - The interface of sending and receiving methods is now very similar to those in v0.1. - The syntax for `send` in `select!` is now `send(sender, msg) -> res => body`. - The syntax for `recv` in `select!` is now `recv(receiver) -> res => body`. - New, more efficient interface for `Select` without callbacks. - Timeouts can be specified in `select!`. # Version 0.2.6 - `Select` struct that can add cases dynamically. - More documentation (in particular, the FAQ section). - Optimize contended sends/receives in unbounded channels. # Version 0.2.5 - Use `LocalKey::try_with` instead of `LocalKey::with`. - Remove helper macros `__crossbeam_channel*`. # Version 0.2.4 - Make `select!` linearizable with other channel operations. - Update `crossbeam-utils` to `0.5.0`. - Update `parking_lot` to `0.6.3`. - Remove Mac OS X tests. # Version 0.2.3 - Add Mac OS X tests. - Lower some memory orderings. - Eliminate calls to `mem::unitialized`, which caused bugs with ZST. # Version 0.2.2 - Add more tests. - Update `crossbeam-epoch` to 0.5.0 - Initialize the RNG seed to a random value. - Replace `libc::abort` with `std::process::abort`. - Ignore clippy warnings in `select!`. - Better interaction of `select!` with the NLL borrow checker. # Version 0.2.1 - Fix compilation errors when using `select!` with `#[deny(unsafe_code)]`. # Version 0.2.0 - Implement `IntoIterator` for `Receiver`. - Add a new `select!` macro. - Add special channels `after` and `tick`. - Dropping receivers doesn't close the channel anymore. - Change the signature of `recv`, `send`, and `try_recv`. - Remove `Sender::is_closed` and `Receiver::is_closed`. - Remove `Sender::close` and `Receiver::close`. - Remove `Sender::send_timeout` and `Receiver::recv_timeout`. - Remove `Sender::try_send`. - Remove `Select` and `select_loop!`. - Remove all error types. - Remove `Iter`, `TryIter`, and `IntoIter`. - Remove the `nightly` feature. - Remove ordering operators for `Sender` and `Receiver`. # Version 0.1.3 - Add `Sender::disconnect` and `Receiver::disconnect`. - Implement comparison operators for `Sender` and `Receiver`. - Allow arbitrary patterns in place of `msg` in `recv(r, msg)`. - Add a few conversion impls between error types. - Add benchmarks for `atomicring` and `mpmc`. - Add benchmarks for different message sizes. - Documentation improvements. - Update `crossbeam-epoch` to 0.4.0 - Update `crossbeam-utils` to 0.3.0 - Update `parking_lot` to 0.5 - Update `rand` to 0.4 # Version 0.1.2 - Allow conditional cases in `select_loop!` macro. - Fix typos in documentation. - Fix deadlock in selection when all channels are disconnected and a timeout is specified. # Version 0.1.1 - Implement `Debug` for `Sender`, `Receiver`, `Iter`, `TryIter`, `IntoIter`, and `Select`. - Implement `Default` for `Select`. # Version 0.1.0 - First implementation of the channels. - Add `select_loop!` macro by @TimNN. crossbeam-channel-0.5.14/Cargo.lock0000644000000111650000000000100124660ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crossbeam-channel" version = "0.5.14" dependencies = [ "crossbeam-utils", "num_cpus", "rand", "signal-hook", ] [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "libc" version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "ppv-lite86" version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "signal-hook" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", ] [[package]] name = "signal-hook-registry" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "syn" version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn", ] crossbeam-channel-0.5.14/Cargo.toml0000644000000051560000000000100125140ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.60" name = "crossbeam-channel" version = "0.5.14" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Multi-producer multi-consumer channels for message passing" homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel" readme = "README.md" keywords = [ "channel", "mpmc", "select", "golang", "message", ] categories = [ "algorithms", "concurrency", "data-structures", ] license = "MIT OR Apache-2.0" repository = "https://github.com/crossbeam-rs/crossbeam" [lib] name = "crossbeam_channel" path = "src/lib.rs" [[example]] name = "fibonacci" path = "examples/fibonacci.rs" [[example]] name = "matching" path = "examples/matching.rs" [[example]] name = "stopwatch" path = "examples/stopwatch.rs" [[test]] name = "after" path = "tests/after.rs" [[test]] name = "array" path = "tests/array.rs" [[test]] name = "golang" path = "tests/golang.rs" [[test]] name = "iter" path = "tests/iter.rs" [[test]] name = "list" path = "tests/list.rs" [[test]] name = "mpsc" path = "tests/mpsc.rs" [[test]] name = "never" path = "tests/never.rs" [[test]] name = "ready" path = "tests/ready.rs" [[test]] name = "same_channel" path = "tests/same_channel.rs" [[test]] name = "select" path = "tests/select.rs" [[test]] name = "select_macro" path = "tests/select_macro.rs" [[test]] name = "thread_locals" path = "tests/thread_locals.rs" [[test]] name = "tick" path = "tests/tick.rs" [[test]] name = "zero" path = "tests/zero.rs" [[bench]] name = "crossbeam" path = "benches/crossbeam.rs" [dependencies.crossbeam-utils] version = "0.8.18" default-features = false [dev-dependencies.num_cpus] version = "1.13.0" [dev-dependencies.rand] version = "0.8" [dev-dependencies.signal-hook] version = "0.3" [features] default = ["std"] std = ["crossbeam-utils/std"] [lints.clippy.declare_interior_mutable_const] level = "allow" priority = 1 [lints.clippy.lint_groups_priority] level = "allow" priority = 1 [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = [ "cfg(crossbeam_loom)", "cfg(crossbeam_sanitize)", ] crossbeam-channel-0.5.14/Cargo.toml.orig000064400000000000000000000020211046102023000161610ustar 00000000000000[package] name = "crossbeam-channel" # When publishing a new version: # - Update CHANGELOG.md # - Update README.md (when increasing major or minor version) # - Run './tools/publish.sh crossbeam-channel ' version = "0.5.14" edition = "2021" rust-version = "1.60" license = "MIT OR Apache-2.0" repository = "https://github.com/crossbeam-rs/crossbeam" homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel" description = "Multi-producer multi-consumer channels for message passing" keywords = ["channel", "mpmc", "select", "golang", "message"] categories = ["algorithms", "concurrency", "data-structures"] [features] default = ["std"] # Enable to use APIs that require `std`. # This is enabled by default. # # NOTE: Disabling `std` feature is not supported yet. std = ["crossbeam-utils/std"] [dependencies] crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-features = false } [dev-dependencies] num_cpus = "1.13.0" rand = "0.8" signal-hook = "0.3" [lints] workspace = true crossbeam-channel-0.5.14/LICENSE-APACHE000064400000000000000000000251371046102023000152330ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. crossbeam-channel-0.5.14/LICENSE-MIT000064400000000000000000000021131046102023000147300ustar 00000000000000The MIT License (MIT) Copyright (c) 2019 The Crossbeam Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. crossbeam-channel-0.5.14/LICENSE-THIRD-PARTY000064400000000000000000001012471046102023000160560ustar 00000000000000=============================================================================== matching.go https://creativecommons.org/licenses/by/3.0/legalcode Creative Commons Legal Code Attribution 3.0 Unported CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. License THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 1. Definitions a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, d. to Distribute and Publicly Perform Adaptations. e. For the avoidance of doubt: i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested. b. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. c. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. 5. Representations, Warranties and Disclaimer UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. Termination a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 8. Miscellaneous a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. Creative Commons Notice Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License. Creative Commons may be contacted at https://creativecommons.org/. =============================================================================== The Go Programming Language https://golang.org/LICENSE Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =============================================================================== The Rust Programming Language https://github.com/rust-lang/rust/blob/master/LICENSE-MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. =============================================================================== The Rust Programming Language https://github.com/rust-lang/rust/blob/master/LICENSE-APACHE Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. crossbeam-channel-0.5.14/README.md000064400000000000000000000073251046102023000145650ustar 00000000000000# Crossbeam Channel [![Build Status](https://github.com/crossbeam-rs/crossbeam/workflows/CI/badge.svg)]( https://github.com/crossbeam-rs/crossbeam/actions) [![License](https://img.shields.io/badge/license-MIT_OR_Apache--2.0-blue.svg)]( https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel#license) [![Cargo](https://img.shields.io/crates/v/crossbeam-channel.svg)]( https://crates.io/crates/crossbeam-channel) [![Documentation](https://docs.rs/crossbeam-channel/badge.svg)]( https://docs.rs/crossbeam-channel) [![Rust 1.60+](https://img.shields.io/badge/rust-1.60+-lightgray.svg)]( https://www.rust-lang.org) [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) This crate provides multi-producer multi-consumer channels for message passing. It is an alternative to [`std::sync::mpsc`] with more features and better performance. Some highlights: * [`Sender`]s and [`Receiver`]s can be cloned and shared among threads. * Two main kinds of channels are [`bounded`] and [`unbounded`]. * Convenient extra channels like [`after`], [`never`], and [`tick`]. * The [`select!`] macro can block on multiple channel operations. * [`Select`] can select over a dynamically built list of channel operations. * Channels use locks very sparingly for maximum [performance](benchmarks). [`std::sync::mpsc`]: https://doc.rust-lang.org/std/sync/mpsc/index.html [`Sender`]: https://docs.rs/crossbeam-channel/*/crossbeam_channel/struct.Sender.html [`Receiver`]: https://docs.rs/crossbeam-channel/*/crossbeam_channel/struct.Receiver.html [`bounded`]: https://docs.rs/crossbeam-channel/*/crossbeam_channel/fn.bounded.html [`unbounded`]: https://docs.rs/crossbeam-channel/*/crossbeam_channel/fn.unbounded.html [`after`]: https://docs.rs/crossbeam-channel/*/crossbeam_channel/fn.after.html [`never`]: https://docs.rs/crossbeam-channel/*/crossbeam_channel/fn.never.html [`tick`]: https://docs.rs/crossbeam-channel/*/crossbeam_channel/fn.tick.html [`select!`]: https://docs.rs/crossbeam-channel/*/crossbeam_channel/macro.select.html [`Select`]: https://docs.rs/crossbeam-channel/*/crossbeam_channel/struct.Select.html ## Usage Add this to your `Cargo.toml`: ```toml [dependencies] crossbeam-channel = "0.5" ``` ## Compatibility Crossbeam Channel supports stable Rust releases going back at least six months, and every time the minimum supported Rust version is increased, a new minor version is released. Currently, the minimum supported Rust version is 1.60. ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. #### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. #### Third party software This product includes copies and modifications of software developed by third parties: * [examples/matching.rs](examples/matching.rs) includes [matching.go](http://www.nada.kth.se/~snilsson/concurrency/src/matching.go) by Stefan Nilsson, licensed under Creative Commons Attribution 3.0 Unported License. * [tests/mpsc.rs](tests/mpsc.rs) includes modifications of code from The Rust Programming Language, licensed under the MIT License and the Apache License, Version 2.0. * [tests/golang.rs](tests/golang.rs) is based on code from The Go Programming Language, licensed under the 3-Clause BSD License. See the source code files for more details. Copies of third party licenses can be found in [LICENSE-THIRD-PARTY](LICENSE-THIRD-PARTY). crossbeam-channel-0.5.14/benches/crossbeam.rs000064400000000000000000000452201046102023000172350ustar 00000000000000#![feature(test)] extern crate test; use crossbeam_channel::{bounded, unbounded}; use crossbeam_utils::thread::scope; use test::Bencher; const TOTAL_STEPS: usize = 40_000; mod unbounded { use super::*; #[bench] fn create(b: &mut Bencher) { b.iter(unbounded::); } #[bench] fn oneshot(b: &mut Bencher) { b.iter(|| { let (s, r) = unbounded::(); s.send(0).unwrap(); r.recv().unwrap(); }); } #[bench] fn inout(b: &mut Bencher) { let (s, r) = unbounded::(); b.iter(|| { s.send(0).unwrap(); r.recv().unwrap(); }); } #[bench] fn par_inout(b: &mut Bencher) { let threads = num_cpus::get(); let steps = TOTAL_STEPS / threads; let (s, r) = unbounded::(); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); r.recv().unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } #[bench] fn spsc(b: &mut Bencher) { let steps = TOTAL_STEPS; let (s, r) = unbounded::(); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); b.iter(|| { s1.send(()).unwrap(); for _ in 0..steps { r.recv().unwrap(); } r2.recv().unwrap(); }); drop(s1); }) .unwrap(); } #[bench] fn spmc(b: &mut Bencher) { let threads = num_cpus::get() - 1; let steps = TOTAL_STEPS / threads; let (s, r) = unbounded::(); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads { scope.spawn(|_| { while r1.recv().is_ok() { for _ in 0..steps { r.recv().unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for i in 0..steps * threads { s.send(i as i32).unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } #[bench] fn mpsc(b: &mut Bencher) { let threads = num_cpus::get() - 1; let steps = TOTAL_STEPS / threads; let (s, r) = unbounded::(); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for _ in 0..steps * threads { r.recv().unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } #[bench] fn mpmc(b: &mut Bencher) { let threads = num_cpus::get(); let steps = TOTAL_STEPS / threads; let (s, r) = unbounded::(); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads / 2 { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); } for _ in 0..threads / 2 { scope.spawn(|_| { while r1.recv().is_ok() { for _ in 0..steps { r.recv().unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } } mod bounded_n { use super::*; #[bench] fn spsc(b: &mut Bencher) { let steps = TOTAL_STEPS; let (s, r) = bounded::(steps); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); b.iter(|| { s1.send(()).unwrap(); for _ in 0..steps { r.recv().unwrap(); } r2.recv().unwrap(); }); drop(s1); }) .unwrap(); } #[bench] fn spmc(b: &mut Bencher) { let threads = num_cpus::get() - 1; let steps = TOTAL_STEPS / threads; let (s, r) = bounded::(steps * threads); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads { scope.spawn(|_| { while r1.recv().is_ok() { for _ in 0..steps { r.recv().unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for i in 0..steps * threads { s.send(i as i32).unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } #[bench] fn mpsc(b: &mut Bencher) { let threads = num_cpus::get() - 1; let steps = TOTAL_STEPS / threads; let (s, r) = bounded::(steps * threads); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for _ in 0..steps * threads { r.recv().unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } #[bench] fn par_inout(b: &mut Bencher) { let threads = num_cpus::get(); let steps = TOTAL_STEPS / threads; let (s, r) = bounded::(threads); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); r.recv().unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } #[bench] fn mpmc(b: &mut Bencher) { let threads = num_cpus::get(); assert_eq!(threads % 2, 0); let steps = TOTAL_STEPS / threads; let (s, r) = bounded::(steps * threads); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads / 2 { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); } for _ in 0..threads / 2 { scope.spawn(|_| { while r1.recv().is_ok() { for _ in 0..steps { r.recv().unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } } mod bounded_1 { use super::*; #[bench] fn create(b: &mut Bencher) { b.iter(|| bounded::(1)); } #[bench] fn oneshot(b: &mut Bencher) { b.iter(|| { let (s, r) = bounded::(1); s.send(0).unwrap(); r.recv().unwrap(); }); } #[bench] fn spsc(b: &mut Bencher) { let steps = TOTAL_STEPS; let (s, r) = bounded::(1); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); b.iter(|| { s1.send(()).unwrap(); for _ in 0..steps { r.recv().unwrap(); } r2.recv().unwrap(); }); drop(s1); }) .unwrap(); } #[bench] fn spmc(b: &mut Bencher) { let threads = num_cpus::get() - 1; let steps = TOTAL_STEPS / threads; let (s, r) = bounded::(1); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads { scope.spawn(|_| { while r1.recv().is_ok() { for _ in 0..steps { r.recv().unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for i in 0..steps * threads { s.send(i as i32).unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } #[bench] fn mpsc(b: &mut Bencher) { let threads = num_cpus::get() - 1; let steps = TOTAL_STEPS / threads; let (s, r) = bounded::(1); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for _ in 0..steps * threads { r.recv().unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } #[bench] fn mpmc(b: &mut Bencher) { let threads = num_cpus::get(); let steps = TOTAL_STEPS / threads; let (s, r) = bounded::(1); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads / 2 { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); } for _ in 0..threads / 2 { scope.spawn(|_| { while r1.recv().is_ok() { for _ in 0..steps { r.recv().unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } } mod bounded_0 { use super::*; #[bench] fn create(b: &mut Bencher) { b.iter(|| bounded::(0)); } #[bench] fn spsc(b: &mut Bencher) { let steps = TOTAL_STEPS; let (s, r) = bounded::(0); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); b.iter(|| { s1.send(()).unwrap(); for _ in 0..steps { r.recv().unwrap(); } r2.recv().unwrap(); }); drop(s1); }) .unwrap(); } #[bench] fn spmc(b: &mut Bencher) { let threads = num_cpus::get() - 1; let steps = TOTAL_STEPS / threads; let (s, r) = bounded::(0); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads { scope.spawn(|_| { while r1.recv().is_ok() { for _ in 0..steps { r.recv().unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for i in 0..steps * threads { s.send(i as i32).unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } #[bench] fn mpsc(b: &mut Bencher) { let threads = num_cpus::get() - 1; let steps = TOTAL_STEPS / threads; let (s, r) = bounded::(0); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for _ in 0..steps * threads { r.recv().unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } #[bench] fn mpmc(b: &mut Bencher) { let threads = num_cpus::get(); let steps = TOTAL_STEPS / threads; let (s, r) = bounded::(0); let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { for _ in 0..threads / 2 { scope.spawn(|_| { while r1.recv().is_ok() { for i in 0..steps { s.send(i as i32).unwrap(); } s2.send(()).unwrap(); } }); } for _ in 0..threads / 2 { scope.spawn(|_| { while r1.recv().is_ok() { for _ in 0..steps { r.recv().unwrap(); } s2.send(()).unwrap(); } }); } b.iter(|| { for _ in 0..threads { s1.send(()).unwrap(); } for _ in 0..threads { r2.recv().unwrap(); } }); drop(s1); }) .unwrap(); } } crossbeam-channel-0.5.14/examples/fibonacci.rs000064400000000000000000000010521046102023000173760ustar 00000000000000//! An asynchronous fibonacci sequence generator. use std::thread; use crossbeam_channel::{bounded, Sender}; // Sends the Fibonacci sequence into the channel until it becomes disconnected. fn fibonacci(sender: Sender) { let (mut x, mut y) = (0, 1); while sender.send(x).is_ok() { let tmp = x; x = y; y += tmp; } } fn main() { let (s, r) = bounded(0); thread::spawn(|| fibonacci(s)); // Print the first 20 Fibonacci numbers. for num in r.iter().take(20) { println!("{}", num); } } crossbeam-channel-0.5.14/examples/matching.rs000064400000000000000000000044661046102023000172670ustar 00000000000000//! Using `select!` to send and receive on the same channel at the same time. //! //! This example is based on the following program in Go. //! //! Source: //! - https://web.archive.org/web/20171209034309/https://www.nada.kth.se/~snilsson/concurrency //! - http://www.nada.kth.se/~snilsson/concurrency/src/matching.go //! //! Copyright & License: //! - Stefan Nilsson //! - Creative Commons Attribution 3.0 Unported License //! - https://creativecommons.org/licenses/by/3.0/ //! //! ```go //! func main() { //! people := []string{"Anna", "Bob", "Cody", "Dave", "Eva"} //! match := make(chan string, 1) // Make room for one unmatched send. //! wg := new(sync.WaitGroup) //! for _, name := range people { //! wg.Add(1) //! go Seek(name, match, wg) //! } //! wg.Wait() //! select { //! case name := <-match: //! fmt.Printf("No one received %s’s message.\n", name) //! default: //! // There was no pending send operation. //! } //! } //! //! // Seek either sends or receives, whichever possible, a name on the match //! // channel and notifies the wait group when done. //! func Seek(name string, match chan string, wg *sync.WaitGroup) { //! select { //! case peer := <-match: //! fmt.Printf("%s received a message from %s.\n", name, peer) //! case match <- name: //! // Wait for someone to receive my message. //! } //! wg.Done() //! } //! ``` use crossbeam_channel::{bounded, select}; use crossbeam_utils::thread; fn main() { let people = vec!["Anna", "Bob", "Cody", "Dave", "Eva"]; let (s, r) = bounded(1); // Make room for one unmatched send. // Either send my name into the channel or receive someone else's, whatever happens first. let seek = |name, s, r| { select! { recv(r) -> peer => println!("{} received a message from {}.", name, peer.unwrap()), send(s, name) -> _ => {}, // Wait for someone to receive my message. } }; thread::scope(|scope| { for name in people { let (s, r) = (s.clone(), r.clone()); scope.spawn(move |_| seek(name, s, r)); } }) .unwrap(); // Check if there is a pending send operation. if let Ok(name) = r.try_recv() { println!("No one received {}’s message.", name); } } crossbeam-channel-0.5.14/examples/stopwatch.rs000064400000000000000000000026721046102023000175060ustar 00000000000000//! Prints the elapsed time every 1 second and quits on Ctrl+C. #[cfg(windows)] // signal_hook::iterator does not work on windows fn main() { println!("This example does not work on Windows"); } #[cfg(not(windows))] fn main() { use std::io; use std::thread; use std::time::{Duration, Instant}; use crossbeam_channel::{bounded, select, tick, Receiver}; use signal_hook::consts::SIGINT; use signal_hook::iterator::Signals; // Creates a channel that gets a message every time `SIGINT` is signalled. fn sigint_notifier() -> io::Result> { let (s, r) = bounded(100); let mut signals = Signals::new(&[SIGINT])?; thread::spawn(move || { for _ in signals.forever() { if s.send(()).is_err() { break; } } }); Ok(r) } // Prints the elapsed time. fn show(dur: Duration) { println!("Elapsed: {}.{:03} sec", dur.as_secs(), dur.subsec_millis()); } let start = Instant::now(); let update = tick(Duration::from_secs(1)); let ctrl_c = sigint_notifier().unwrap(); loop { select! { recv(update) -> _ => { show(start.elapsed()); } recv(ctrl_c) -> _ => { println!(); println!("Goodbye!"); show(start.elapsed()); break; } } } } crossbeam-channel-0.5.14/src/channel.rs000064400000000000000000001363361046102023000160600ustar 00000000000000//! The channel interface. use std::fmt; use std::iter::FusedIterator; use std::mem; use std::panic::{RefUnwindSafe, UnwindSafe}; use std::sync::Arc; use std::time::{Duration, Instant}; use crate::context::Context; use crate::counter; use crate::err::{ RecvError, RecvTimeoutError, SendError, SendTimeoutError, TryRecvError, TrySendError, }; use crate::flavors; use crate::select::{Operation, SelectHandle, Token}; /// Creates a channel of unbounded capacity. /// /// This channel has a growable buffer that can hold any number of messages at a time. /// /// # Examples /// /// ``` /// use std::thread; /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// /// // Computes the n-th Fibonacci number. /// fn fib(n: i32) -> i32 { /// if n <= 1 { /// n /// } else { /// fib(n - 1) + fib(n - 2) /// } /// } /// /// // Spawn an asynchronous computation. /// thread::spawn(move || s.send(fib(20)).unwrap()); /// /// // Print the result of the computation. /// println!("{}", r.recv().unwrap()); /// ``` pub fn unbounded() -> (Sender, Receiver) { let (s, r) = counter::new(flavors::list::Channel::new()); let s = Sender { flavor: SenderFlavor::List(s), }; let r = Receiver { flavor: ReceiverFlavor::List(r), }; (s, r) } /// Creates a channel of bounded capacity. /// /// This channel has a buffer that can hold at most `cap` messages at a time. /// /// A special case is zero-capacity channel, which cannot hold any messages. Instead, send and /// receive operations must appear at the same time in order to pair up and pass the message over. /// /// # Examples /// /// A channel of capacity 1: /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::bounded; /// /// let (s, r) = bounded(1); /// /// // This call returns immediately because there is enough space in the channel. /// s.send(1).unwrap(); /// /// thread::spawn(move || { /// // This call blocks the current thread because the channel is full. /// // It will be able to complete only after the first message is received. /// s.send(2).unwrap(); /// }); /// /// thread::sleep(Duration::from_secs(1)); /// assert_eq!(r.recv(), Ok(1)); /// assert_eq!(r.recv(), Ok(2)); /// ``` /// /// A zero-capacity channel: /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::bounded; /// /// let (s, r) = bounded(0); /// /// thread::spawn(move || { /// // This call blocks the current thread until a receive operation appears /// // on the other side of the channel. /// s.send(1).unwrap(); /// }); /// /// thread::sleep(Duration::from_secs(1)); /// assert_eq!(r.recv(), Ok(1)); /// ``` pub fn bounded(cap: usize) -> (Sender, Receiver) { if cap == 0 { let (s, r) = counter::new(flavors::zero::Channel::new()); let s = Sender { flavor: SenderFlavor::Zero(s), }; let r = Receiver { flavor: ReceiverFlavor::Zero(r), }; (s, r) } else { let (s, r) = counter::new(flavors::array::Channel::with_capacity(cap)); let s = Sender { flavor: SenderFlavor::Array(s), }; let r = Receiver { flavor: ReceiverFlavor::Array(r), }; (s, r) } } /// Creates a receiver that delivers a message after a certain duration of time. /// /// The channel is bounded with capacity of 1 and never gets disconnected. Exactly one message will /// be sent into the channel after `duration` elapses. The message is the instant at which it is /// sent. /// /// # Examples /// /// Using an `after` channel for timeouts: /// /// ``` /// use std::time::Duration; /// use crossbeam_channel::{after, select, unbounded}; /// /// let (s, r) = unbounded::(); /// let timeout = Duration::from_millis(100); /// /// select! { /// recv(r) -> msg => println!("received {:?}", msg), /// recv(after(timeout)) -> _ => println!("timed out"), /// } /// ``` /// /// When the message gets sent: /// /// ``` /// use std::thread; /// use std::time::{Duration, Instant}; /// use crossbeam_channel::after; /// /// // Converts a number of milliseconds into a `Duration`. /// let ms = |ms| Duration::from_millis(ms); /// /// // Returns `true` if `a` and `b` are very close `Instant`s. /// let eq = |a, b| a + ms(60) > b && b + ms(60) > a; /// /// let start = Instant::now(); /// let r = after(ms(100)); /// /// thread::sleep(ms(500)); /// /// // This message was sent 100 ms from the start and received 500 ms from the start. /// assert!(eq(r.recv().unwrap(), start + ms(100))); /// assert!(eq(Instant::now(), start + ms(500))); /// ``` pub fn after(duration: Duration) -> Receiver { match Instant::now().checked_add(duration) { Some(deadline) => Receiver { flavor: ReceiverFlavor::At(Arc::new(flavors::at::Channel::new_deadline(deadline))), }, None => never(), } } /// Creates a receiver that delivers a message at a certain instant in time. /// /// The channel is bounded with capacity of 1 and never gets disconnected. Exactly one message will /// be sent into the channel at the moment in time `when`. The message is the instant at which it /// is sent, which is the same as `when`. If `when` is in the past, the message will be delivered /// instantly to the receiver. /// /// # Examples /// /// Using an `at` channel for timeouts: /// /// ``` /// use std::time::{Instant, Duration}; /// use crossbeam_channel::{at, select, unbounded}; /// /// let (s, r) = unbounded::(); /// let deadline = Instant::now() + Duration::from_millis(500); /// /// select! { /// recv(r) -> msg => println!("received {:?}", msg), /// recv(at(deadline)) -> _ => println!("timed out"), /// } /// ``` /// /// When the message gets sent: /// /// ``` /// use std::time::{Duration, Instant}; /// use crossbeam_channel::at; /// /// // Converts a number of milliseconds into a `Duration`. /// let ms = |ms| Duration::from_millis(ms); /// /// let start = Instant::now(); /// let end = start + ms(100); /// /// let r = at(end); /// /// // This message was sent 100 ms from the start /// assert_eq!(r.recv().unwrap(), end); /// assert!(Instant::now() > start + ms(100)); /// ``` pub fn at(when: Instant) -> Receiver { Receiver { flavor: ReceiverFlavor::At(Arc::new(flavors::at::Channel::new_deadline(when))), } } /// Creates a receiver that never delivers messages. /// /// The channel is bounded with capacity of 0 and never gets disconnected. /// /// # Examples /// /// Using a `never` channel to optionally add a timeout to [`select!`]: /// /// [`select!`]: crate::select! /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{after, select, never, unbounded}; /// /// let (s, r) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s.send(1).unwrap(); /// }); /// /// // Suppose this duration can be a `Some` or a `None`. /// let duration = Some(Duration::from_millis(100)); /// /// // Create a channel that times out after the specified duration. /// let timeout = duration /// .map(|d| after(d)) /// .unwrap_or(never()); /// /// select! { /// recv(r) -> msg => assert_eq!(msg, Ok(1)), /// recv(timeout) -> _ => println!("timed out"), /// } /// ``` pub fn never() -> Receiver { Receiver { flavor: ReceiverFlavor::Never(flavors::never::Channel::new()), } } /// Creates a receiver that delivers messages periodically. /// /// The channel is bounded with capacity of 1 and never gets disconnected. Messages will be /// sent into the channel in intervals of `duration`. Each message is the instant at which it is /// sent. /// /// # Examples /// /// Using a `tick` channel to periodically print elapsed time: /// /// ``` /// use std::time::{Duration, Instant}; /// use crossbeam_channel::tick; /// /// let start = Instant::now(); /// let ticker = tick(Duration::from_millis(100)); /// /// for _ in 0..5 { /// ticker.recv().unwrap(); /// println!("elapsed: {:?}", start.elapsed()); /// } /// ``` /// /// When messages get sent: /// /// ``` /// use std::thread; /// use std::time::{Duration, Instant}; /// use crossbeam_channel::tick; /// /// // Converts a number of milliseconds into a `Duration`. /// let ms = |ms| Duration::from_millis(ms); /// /// // Returns `true` if `a` and `b` are very close `Instant`s. /// let eq = |a, b| a + ms(65) > b && b + ms(65) > a; /// /// let start = Instant::now(); /// let r = tick(ms(100)); /// /// // This message was sent 100 ms from the start and received 100 ms from the start. /// assert!(eq(r.recv().unwrap(), start + ms(100))); /// assert!(eq(Instant::now(), start + ms(100))); /// /// thread::sleep(ms(500)); /// /// // This message was sent 200 ms from the start and received 600 ms from the start. /// assert!(eq(r.recv().unwrap(), start + ms(200))); /// assert!(eq(Instant::now(), start + ms(600))); /// /// // This message was sent 700 ms from the start and received 700 ms from the start. /// assert!(eq(r.recv().unwrap(), start + ms(700))); /// assert!(eq(Instant::now(), start + ms(700))); /// ``` pub fn tick(duration: Duration) -> Receiver { match Instant::now().checked_add(duration) { Some(delivery_time) => Receiver { flavor: ReceiverFlavor::Tick(Arc::new(flavors::tick::Channel::new( delivery_time, duration, ))), }, None => never(), } } /// The sending side of a channel. /// /// # Examples /// /// ``` /// use std::thread; /// use crossbeam_channel::unbounded; /// /// let (s1, r) = unbounded(); /// let s2 = s1.clone(); /// /// thread::spawn(move || s1.send(1).unwrap()); /// thread::spawn(move || s2.send(2).unwrap()); /// /// let msg1 = r.recv().unwrap(); /// let msg2 = r.recv().unwrap(); /// /// assert_eq!(msg1 + msg2, 3); /// ``` pub struct Sender { flavor: SenderFlavor, } /// Sender flavors. enum SenderFlavor { /// Bounded channel based on a preallocated array. Array(counter::Sender>), /// Unbounded channel implemented as a linked list. List(counter::Sender>), /// Zero-capacity channel. Zero(counter::Sender>), } unsafe impl Send for Sender {} unsafe impl Sync for Sender {} impl UnwindSafe for Sender {} impl RefUnwindSafe for Sender {} impl Sender { /// Attempts to send a message into the channel without blocking. /// /// This method will either send a message into the channel immediately or return an error if /// the channel is full or disconnected. The returned error contains the original message. /// /// If called on a zero-capacity channel, this method will send the message only if there /// happens to be a receive operation on the other side of the channel at the same time. /// /// # Examples /// /// ``` /// use crossbeam_channel::{bounded, TrySendError}; /// /// let (s, r) = bounded(1); /// /// assert_eq!(s.try_send(1), Ok(())); /// assert_eq!(s.try_send(2), Err(TrySendError::Full(2))); /// /// drop(r); /// assert_eq!(s.try_send(3), Err(TrySendError::Disconnected(3))); /// ``` pub fn try_send(&self, msg: T) -> Result<(), TrySendError> { match &self.flavor { SenderFlavor::Array(chan) => chan.try_send(msg), SenderFlavor::List(chan) => chan.try_send(msg), SenderFlavor::Zero(chan) => chan.try_send(msg), } } /// Blocks the current thread until a message is sent or the channel is disconnected. /// /// If the channel is full and not disconnected, this call will block until the send operation /// can proceed. If the channel becomes disconnected, this call will wake up and return an /// error. The returned error contains the original message. /// /// If called on a zero-capacity channel, this method will wait for a receive operation to /// appear on the other side of the channel. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{bounded, SendError}; /// /// let (s, r) = bounded(1); /// assert_eq!(s.send(1), Ok(())); /// /// thread::spawn(move || { /// assert_eq!(r.recv(), Ok(1)); /// thread::sleep(Duration::from_secs(1)); /// drop(r); /// }); /// /// assert_eq!(s.send(2), Ok(())); /// assert_eq!(s.send(3), Err(SendError(3))); /// ``` pub fn send(&self, msg: T) -> Result<(), SendError> { match &self.flavor { SenderFlavor::Array(chan) => chan.send(msg, None), SenderFlavor::List(chan) => chan.send(msg, None), SenderFlavor::Zero(chan) => chan.send(msg, None), } .map_err(|err| match err { SendTimeoutError::Disconnected(msg) => SendError(msg), SendTimeoutError::Timeout(_) => unreachable!(), }) } /// Waits for a message to be sent into the channel, but only for a limited time. /// /// If the channel is full and not disconnected, this call will block until the send operation /// can proceed or the operation times out. If the channel becomes disconnected, this call will /// wake up and return an error. The returned error contains the original message. /// /// If called on a zero-capacity channel, this method will wait for a receive operation to /// appear on the other side of the channel. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{bounded, SendTimeoutError}; /// /// let (s, r) = bounded(0); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// assert_eq!(r.recv(), Ok(2)); /// drop(r); /// }); /// /// assert_eq!( /// s.send_timeout(1, Duration::from_millis(500)), /// Err(SendTimeoutError::Timeout(1)), /// ); /// assert_eq!( /// s.send_timeout(2, Duration::from_secs(1)), /// Ok(()), /// ); /// assert_eq!( /// s.send_timeout(3, Duration::from_millis(500)), /// Err(SendTimeoutError::Disconnected(3)), /// ); /// ``` pub fn send_timeout(&self, msg: T, timeout: Duration) -> Result<(), SendTimeoutError> { match Instant::now().checked_add(timeout) { Some(deadline) => self.send_deadline(msg, deadline), None => self.send(msg).map_err(SendTimeoutError::from), } } /// Waits for a message to be sent into the channel, but only until a given deadline. /// /// If the channel is full and not disconnected, this call will block until the send operation /// can proceed or the operation times out. If the channel becomes disconnected, this call will /// wake up and return an error. The returned error contains the original message. /// /// If called on a zero-capacity channel, this method will wait for a receive operation to /// appear on the other side of the channel. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::{Duration, Instant}; /// use crossbeam_channel::{bounded, SendTimeoutError}; /// /// let (s, r) = bounded(0); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// assert_eq!(r.recv(), Ok(2)); /// drop(r); /// }); /// /// let now = Instant::now(); /// /// assert_eq!( /// s.send_deadline(1, now + Duration::from_millis(500)), /// Err(SendTimeoutError::Timeout(1)), /// ); /// assert_eq!( /// s.send_deadline(2, now + Duration::from_millis(1500)), /// Ok(()), /// ); /// assert_eq!( /// s.send_deadline(3, now + Duration::from_millis(2000)), /// Err(SendTimeoutError::Disconnected(3)), /// ); /// ``` pub fn send_deadline(&self, msg: T, deadline: Instant) -> Result<(), SendTimeoutError> { match &self.flavor { SenderFlavor::Array(chan) => chan.send(msg, Some(deadline)), SenderFlavor::List(chan) => chan.send(msg, Some(deadline)), SenderFlavor::Zero(chan) => chan.send(msg, Some(deadline)), } } /// Returns `true` if the channel is empty. /// /// Note: Zero-capacity channels are always empty. /// /// # Examples /// /// ``` /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// assert!(s.is_empty()); /// /// s.send(0).unwrap(); /// assert!(!s.is_empty()); /// ``` pub fn is_empty(&self) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.is_empty(), SenderFlavor::List(chan) => chan.is_empty(), SenderFlavor::Zero(chan) => chan.is_empty(), } } /// Returns `true` if the channel is full. /// /// Note: Zero-capacity channels are always full. /// /// # Examples /// /// ``` /// use crossbeam_channel::bounded; /// /// let (s, r) = bounded(1); /// /// assert!(!s.is_full()); /// s.send(0).unwrap(); /// assert!(s.is_full()); /// ``` pub fn is_full(&self) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.is_full(), SenderFlavor::List(chan) => chan.is_full(), SenderFlavor::Zero(chan) => chan.is_full(), } } /// Returns the number of messages in the channel. /// /// # Examples /// /// ``` /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// assert_eq!(s.len(), 0); /// /// s.send(1).unwrap(); /// s.send(2).unwrap(); /// assert_eq!(s.len(), 2); /// ``` pub fn len(&self) -> usize { match &self.flavor { SenderFlavor::Array(chan) => chan.len(), SenderFlavor::List(chan) => chan.len(), SenderFlavor::Zero(chan) => chan.len(), } } /// If the channel is bounded, returns its capacity. /// /// # Examples /// /// ``` /// use crossbeam_channel::{bounded, unbounded}; /// /// let (s, _) = unbounded::(); /// assert_eq!(s.capacity(), None); /// /// let (s, _) = bounded::(5); /// assert_eq!(s.capacity(), Some(5)); /// /// let (s, _) = bounded::(0); /// assert_eq!(s.capacity(), Some(0)); /// ``` pub fn capacity(&self) -> Option { match &self.flavor { SenderFlavor::Array(chan) => chan.capacity(), SenderFlavor::List(chan) => chan.capacity(), SenderFlavor::Zero(chan) => chan.capacity(), } } /// Returns `true` if senders belong to the same channel. /// /// # Examples /// /// ```rust /// use crossbeam_channel::unbounded; /// /// let (s, _) = unbounded::(); /// /// let s2 = s.clone(); /// assert!(s.same_channel(&s2)); /// /// let (s3, _) = unbounded(); /// assert!(!s.same_channel(&s3)); /// ``` pub fn same_channel(&self, other: &Sender) -> bool { match (&self.flavor, &other.flavor) { (SenderFlavor::Array(ref a), SenderFlavor::Array(ref b)) => a == b, (SenderFlavor::List(ref a), SenderFlavor::List(ref b)) => a == b, (SenderFlavor::Zero(ref a), SenderFlavor::Zero(ref b)) => a == b, _ => false, } } } impl Drop for Sender { fn drop(&mut self) { unsafe { match &self.flavor { SenderFlavor::Array(chan) => chan.release(|c| c.disconnect()), SenderFlavor::List(chan) => chan.release(|c| c.disconnect_senders()), SenderFlavor::Zero(chan) => chan.release(|c| c.disconnect()), } } } } impl Clone for Sender { fn clone(&self) -> Self { let flavor = match &self.flavor { SenderFlavor::Array(chan) => SenderFlavor::Array(chan.acquire()), SenderFlavor::List(chan) => SenderFlavor::List(chan.acquire()), SenderFlavor::Zero(chan) => SenderFlavor::Zero(chan.acquire()), }; Sender { flavor } } } impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Sender { .. }") } } /// The receiving side of a channel. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// /// thread::spawn(move || { /// let _ = s.send(1); /// thread::sleep(Duration::from_secs(1)); /// let _ = s.send(2); /// }); /// /// assert_eq!(r.recv(), Ok(1)); // Received immediately. /// assert_eq!(r.recv(), Ok(2)); // Received after 1 second. /// ``` pub struct Receiver { flavor: ReceiverFlavor, } /// Receiver flavors. enum ReceiverFlavor { /// Bounded channel based on a preallocated array. Array(counter::Receiver>), /// Unbounded channel implemented as a linked list. List(counter::Receiver>), /// Zero-capacity channel. Zero(counter::Receiver>), /// The after flavor. At(Arc), /// The tick flavor. Tick(Arc), /// The never flavor. Never(flavors::never::Channel), } unsafe impl Send for Receiver {} unsafe impl Sync for Receiver {} impl UnwindSafe for Receiver {} impl RefUnwindSafe for Receiver {} impl Receiver { /// Attempts to receive a message from the channel without blocking. /// /// This method will either receive a message from the channel immediately or return an error /// if the channel is empty. /// /// If called on a zero-capacity channel, this method will receive a message only if there /// happens to be a send operation on the other side of the channel at the same time. /// /// # Examples /// /// ``` /// use crossbeam_channel::{unbounded, TryRecvError}; /// /// let (s, r) = unbounded(); /// assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); /// /// s.send(5).unwrap(); /// drop(s); /// /// assert_eq!(r.try_recv(), Ok(5)); /// assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)); /// ``` pub fn try_recv(&self) -> Result { match &self.flavor { ReceiverFlavor::Array(chan) => chan.try_recv(), ReceiverFlavor::List(chan) => chan.try_recv(), ReceiverFlavor::Zero(chan) => chan.try_recv(), ReceiverFlavor::At(chan) => { let msg = chan.try_recv(); unsafe { mem::transmute_copy::, Result>( &msg, ) } } ReceiverFlavor::Tick(chan) => { let msg = chan.try_recv(); unsafe { mem::transmute_copy::, Result>( &msg, ) } } ReceiverFlavor::Never(chan) => chan.try_recv(), } } /// Blocks the current thread until a message is received or the channel is empty and /// disconnected. /// /// If the channel is empty and not disconnected, this call will block until the receive /// operation can proceed. If the channel is empty and becomes disconnected, this call will /// wake up and return an error. /// /// If called on a zero-capacity channel, this method will wait for a send operation to appear /// on the other side of the channel. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{unbounded, RecvError}; /// /// let (s, r) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s.send(5).unwrap(); /// drop(s); /// }); /// /// assert_eq!(r.recv(), Ok(5)); /// assert_eq!(r.recv(), Err(RecvError)); /// ``` pub fn recv(&self) -> Result { match &self.flavor { ReceiverFlavor::Array(chan) => chan.recv(None), ReceiverFlavor::List(chan) => chan.recv(None), ReceiverFlavor::Zero(chan) => chan.recv(None), ReceiverFlavor::At(chan) => { let msg = chan.recv(None); unsafe { mem::transmute_copy::< Result, Result, >(&msg) } } ReceiverFlavor::Tick(chan) => { let msg = chan.recv(None); unsafe { mem::transmute_copy::< Result, Result, >(&msg) } } ReceiverFlavor::Never(chan) => chan.recv(None), } .map_err(|_| RecvError) } /// Waits for a message to be received from the channel, but only for a limited time. /// /// If the channel is empty and not disconnected, this call will block until the receive /// operation can proceed or the operation times out. If the channel is empty and becomes /// disconnected, this call will wake up and return an error. /// /// If called on a zero-capacity channel, this method will wait for a send operation to appear /// on the other side of the channel. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{unbounded, RecvTimeoutError}; /// /// let (s, r) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s.send(5).unwrap(); /// drop(s); /// }); /// /// assert_eq!( /// r.recv_timeout(Duration::from_millis(500)), /// Err(RecvTimeoutError::Timeout), /// ); /// assert_eq!( /// r.recv_timeout(Duration::from_secs(1)), /// Ok(5), /// ); /// assert_eq!( /// r.recv_timeout(Duration::from_secs(1)), /// Err(RecvTimeoutError::Disconnected), /// ); /// ``` pub fn recv_timeout(&self, timeout: Duration) -> Result { match Instant::now().checked_add(timeout) { Some(deadline) => self.recv_deadline(deadline), None => self.recv().map_err(RecvTimeoutError::from), } } /// Waits for a message to be received from the channel, but only before a given deadline. /// /// If the channel is empty and not disconnected, this call will block until the receive /// operation can proceed or the operation times out. If the channel is empty and becomes /// disconnected, this call will wake up and return an error. /// /// If called on a zero-capacity channel, this method will wait for a send operation to appear /// on the other side of the channel. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::{Instant, Duration}; /// use crossbeam_channel::{unbounded, RecvTimeoutError}; /// /// let (s, r) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s.send(5).unwrap(); /// drop(s); /// }); /// /// let now = Instant::now(); /// /// assert_eq!( /// r.recv_deadline(now + Duration::from_millis(500)), /// Err(RecvTimeoutError::Timeout), /// ); /// assert_eq!( /// r.recv_deadline(now + Duration::from_millis(1500)), /// Ok(5), /// ); /// assert_eq!( /// r.recv_deadline(now + Duration::from_secs(5)), /// Err(RecvTimeoutError::Disconnected), /// ); /// ``` pub fn recv_deadline(&self, deadline: Instant) -> Result { match &self.flavor { ReceiverFlavor::Array(chan) => chan.recv(Some(deadline)), ReceiverFlavor::List(chan) => chan.recv(Some(deadline)), ReceiverFlavor::Zero(chan) => chan.recv(Some(deadline)), ReceiverFlavor::At(chan) => { let msg = chan.recv(Some(deadline)); unsafe { mem::transmute_copy::< Result, Result, >(&msg) } } ReceiverFlavor::Tick(chan) => { let msg = chan.recv(Some(deadline)); unsafe { mem::transmute_copy::< Result, Result, >(&msg) } } ReceiverFlavor::Never(chan) => chan.recv(Some(deadline)), } } /// Returns `true` if the channel is empty. /// /// Note: Zero-capacity channels are always empty. /// /// # Examples /// /// ``` /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// /// assert!(r.is_empty()); /// s.send(0).unwrap(); /// assert!(!r.is_empty()); /// ``` pub fn is_empty(&self) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.is_empty(), ReceiverFlavor::List(chan) => chan.is_empty(), ReceiverFlavor::Zero(chan) => chan.is_empty(), ReceiverFlavor::At(chan) => chan.is_empty(), ReceiverFlavor::Tick(chan) => chan.is_empty(), ReceiverFlavor::Never(chan) => chan.is_empty(), } } /// Returns `true` if the channel is full. /// /// Note: Zero-capacity channels are always full. /// /// # Examples /// /// ``` /// use crossbeam_channel::bounded; /// /// let (s, r) = bounded(1); /// /// assert!(!r.is_full()); /// s.send(0).unwrap(); /// assert!(r.is_full()); /// ``` pub fn is_full(&self) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.is_full(), ReceiverFlavor::List(chan) => chan.is_full(), ReceiverFlavor::Zero(chan) => chan.is_full(), ReceiverFlavor::At(chan) => chan.is_full(), ReceiverFlavor::Tick(chan) => chan.is_full(), ReceiverFlavor::Never(chan) => chan.is_full(), } } /// Returns the number of messages in the channel. /// /// # Examples /// /// ``` /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// assert_eq!(r.len(), 0); /// /// s.send(1).unwrap(); /// s.send(2).unwrap(); /// assert_eq!(r.len(), 2); /// ``` pub fn len(&self) -> usize { match &self.flavor { ReceiverFlavor::Array(chan) => chan.len(), ReceiverFlavor::List(chan) => chan.len(), ReceiverFlavor::Zero(chan) => chan.len(), ReceiverFlavor::At(chan) => chan.len(), ReceiverFlavor::Tick(chan) => chan.len(), ReceiverFlavor::Never(chan) => chan.len(), } } /// If the channel is bounded, returns its capacity. /// /// # Examples /// /// ``` /// use crossbeam_channel::{bounded, unbounded}; /// /// let (_, r) = unbounded::(); /// assert_eq!(r.capacity(), None); /// /// let (_, r) = bounded::(5); /// assert_eq!(r.capacity(), Some(5)); /// /// let (_, r) = bounded::(0); /// assert_eq!(r.capacity(), Some(0)); /// ``` pub fn capacity(&self) -> Option { match &self.flavor { ReceiverFlavor::Array(chan) => chan.capacity(), ReceiverFlavor::List(chan) => chan.capacity(), ReceiverFlavor::Zero(chan) => chan.capacity(), ReceiverFlavor::At(chan) => chan.capacity(), ReceiverFlavor::Tick(chan) => chan.capacity(), ReceiverFlavor::Never(chan) => chan.capacity(), } } /// A blocking iterator over messages in the channel. /// /// Each call to [`next`] blocks waiting for the next message and then returns it. However, if /// the channel becomes empty and disconnected, it returns [`None`] without blocking. /// /// [`next`]: Iterator::next /// /// # Examples /// /// ``` /// use std::thread; /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// /// thread::spawn(move || { /// s.send(1).unwrap(); /// s.send(2).unwrap(); /// s.send(3).unwrap(); /// drop(s); // Disconnect the channel. /// }); /// /// // Collect all messages from the channel. /// // Note that the call to `collect` blocks until the sender is dropped. /// let v: Vec<_> = r.iter().collect(); /// /// assert_eq!(v, [1, 2, 3]); /// ``` pub fn iter(&self) -> Iter<'_, T> { Iter { receiver: self } } /// A non-blocking iterator over messages in the channel. /// /// Each call to [`next`] returns a message if there is one ready to be received. The iterator /// never blocks waiting for the next message. /// /// [`next`]: Iterator::next /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded::(); /// /// thread::spawn(move || { /// s.send(1).unwrap(); /// thread::sleep(Duration::from_secs(1)); /// s.send(2).unwrap(); /// thread::sleep(Duration::from_secs(2)); /// s.send(3).unwrap(); /// }); /// /// thread::sleep(Duration::from_secs(2)); /// /// // Collect all messages from the channel without blocking. /// // The third message hasn't been sent yet so we'll collect only the first two. /// let v: Vec<_> = r.try_iter().collect(); /// /// assert_eq!(v, [1, 2]); /// ``` pub fn try_iter(&self) -> TryIter<'_, T> { TryIter { receiver: self } } /// Returns `true` if receivers belong to the same channel. /// /// # Examples /// /// ```rust /// use crossbeam_channel::unbounded; /// /// let (_, r) = unbounded::(); /// /// let r2 = r.clone(); /// assert!(r.same_channel(&r2)); /// /// let (_, r3) = unbounded(); /// assert!(!r.same_channel(&r3)); /// ``` pub fn same_channel(&self, other: &Receiver) -> bool { match (&self.flavor, &other.flavor) { (ReceiverFlavor::Array(a), ReceiverFlavor::Array(b)) => a == b, (ReceiverFlavor::List(a), ReceiverFlavor::List(b)) => a == b, (ReceiverFlavor::Zero(a), ReceiverFlavor::Zero(b)) => a == b, (ReceiverFlavor::At(a), ReceiverFlavor::At(b)) => Arc::ptr_eq(a, b), (ReceiverFlavor::Tick(a), ReceiverFlavor::Tick(b)) => Arc::ptr_eq(a, b), (ReceiverFlavor::Never(_), ReceiverFlavor::Never(_)) => true, _ => false, } } } impl Drop for Receiver { fn drop(&mut self) { unsafe { match &self.flavor { ReceiverFlavor::Array(chan) => chan.release(|c| c.disconnect()), ReceiverFlavor::List(chan) => chan.release(|c| c.disconnect_receivers()), ReceiverFlavor::Zero(chan) => chan.release(|c| c.disconnect()), ReceiverFlavor::At(_) => {} ReceiverFlavor::Tick(_) => {} ReceiverFlavor::Never(_) => {} } } } } impl Clone for Receiver { fn clone(&self) -> Self { let flavor = match &self.flavor { ReceiverFlavor::Array(chan) => ReceiverFlavor::Array(chan.acquire()), ReceiverFlavor::List(chan) => ReceiverFlavor::List(chan.acquire()), ReceiverFlavor::Zero(chan) => ReceiverFlavor::Zero(chan.acquire()), ReceiverFlavor::At(chan) => ReceiverFlavor::At(chan.clone()), ReceiverFlavor::Tick(chan) => ReceiverFlavor::Tick(chan.clone()), ReceiverFlavor::Never(_) => ReceiverFlavor::Never(flavors::never::Channel::new()), }; Receiver { flavor } } } impl fmt::Debug for Receiver { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Receiver { .. }") } } impl<'a, T> IntoIterator for &'a Receiver { type Item = T; type IntoIter = Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl IntoIterator for Receiver { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter { receiver: self } } } /// A blocking iterator over messages in a channel. /// /// Each call to [`next`] blocks waiting for the next message and then returns it. However, if the /// channel becomes empty and disconnected, it returns [`None`] without blocking. /// /// [`next`]: Iterator::next /// /// # Examples /// /// ``` /// use std::thread; /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// /// thread::spawn(move || { /// s.send(1).unwrap(); /// s.send(2).unwrap(); /// s.send(3).unwrap(); /// drop(s); // Disconnect the channel. /// }); /// /// // Collect all messages from the channel. /// // Note that the call to `collect` blocks until the sender is dropped. /// let v: Vec<_> = r.iter().collect(); /// /// assert_eq!(v, [1, 2, 3]); /// ``` pub struct Iter<'a, T> { receiver: &'a Receiver, } impl FusedIterator for Iter<'_, T> {} impl Iterator for Iter<'_, T> { type Item = T; fn next(&mut self) -> Option { self.receiver.recv().ok() } } impl fmt::Debug for Iter<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Iter { .. }") } } /// A non-blocking iterator over messages in a channel. /// /// Each call to [`next`] returns a message if there is one ready to be received. The iterator /// never blocks waiting for the next message. /// /// [`next`]: Iterator::next /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded::(); /// /// thread::spawn(move || { /// s.send(1).unwrap(); /// thread::sleep(Duration::from_secs(1)); /// s.send(2).unwrap(); /// thread::sleep(Duration::from_secs(2)); /// s.send(3).unwrap(); /// }); /// /// thread::sleep(Duration::from_secs(2)); /// /// // Collect all messages from the channel without blocking. /// // The third message hasn't been sent yet so we'll collect only the first two. /// let v: Vec<_> = r.try_iter().collect(); /// /// assert_eq!(v, [1, 2]); /// ``` pub struct TryIter<'a, T> { receiver: &'a Receiver, } impl Iterator for TryIter<'_, T> { type Item = T; fn next(&mut self) -> Option { self.receiver.try_recv().ok() } } impl fmt::Debug for TryIter<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("TryIter { .. }") } } /// A blocking iterator over messages in a channel. /// /// Each call to [`next`] blocks waiting for the next message and then returns it. However, if the /// channel becomes empty and disconnected, it returns [`None`] without blocking. /// /// [`next`]: Iterator::next /// /// # Examples /// /// ``` /// use std::thread; /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// /// thread::spawn(move || { /// s.send(1).unwrap(); /// s.send(2).unwrap(); /// s.send(3).unwrap(); /// drop(s); // Disconnect the channel. /// }); /// /// // Collect all messages from the channel. /// // Note that the call to `collect` blocks until the sender is dropped. /// let v: Vec<_> = r.into_iter().collect(); /// /// assert_eq!(v, [1, 2, 3]); /// ``` pub struct IntoIter { receiver: Receiver, } impl FusedIterator for IntoIter {} impl Iterator for IntoIter { type Item = T; fn next(&mut self) -> Option { self.receiver.recv().ok() } } impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("IntoIter { .. }") } } impl SelectHandle for Sender { fn try_select(&self, token: &mut Token) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.sender().try_select(token), SenderFlavor::List(chan) => chan.sender().try_select(token), SenderFlavor::Zero(chan) => chan.sender().try_select(token), } } fn deadline(&self) -> Option { None } fn register(&self, oper: Operation, cx: &Context) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.sender().register(oper, cx), SenderFlavor::List(chan) => chan.sender().register(oper, cx), SenderFlavor::Zero(chan) => chan.sender().register(oper, cx), } } fn unregister(&self, oper: Operation) { match &self.flavor { SenderFlavor::Array(chan) => chan.sender().unregister(oper), SenderFlavor::List(chan) => chan.sender().unregister(oper), SenderFlavor::Zero(chan) => chan.sender().unregister(oper), } } fn accept(&self, token: &mut Token, cx: &Context) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.sender().accept(token, cx), SenderFlavor::List(chan) => chan.sender().accept(token, cx), SenderFlavor::Zero(chan) => chan.sender().accept(token, cx), } } fn is_ready(&self) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.sender().is_ready(), SenderFlavor::List(chan) => chan.sender().is_ready(), SenderFlavor::Zero(chan) => chan.sender().is_ready(), } } fn watch(&self, oper: Operation, cx: &Context) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.sender().watch(oper, cx), SenderFlavor::List(chan) => chan.sender().watch(oper, cx), SenderFlavor::Zero(chan) => chan.sender().watch(oper, cx), } } fn unwatch(&self, oper: Operation) { match &self.flavor { SenderFlavor::Array(chan) => chan.sender().unwatch(oper), SenderFlavor::List(chan) => chan.sender().unwatch(oper), SenderFlavor::Zero(chan) => chan.sender().unwatch(oper), } } } impl SelectHandle for Receiver { fn try_select(&self, token: &mut Token) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.receiver().try_select(token), ReceiverFlavor::List(chan) => chan.receiver().try_select(token), ReceiverFlavor::Zero(chan) => chan.receiver().try_select(token), ReceiverFlavor::At(chan) => chan.try_select(token), ReceiverFlavor::Tick(chan) => chan.try_select(token), ReceiverFlavor::Never(chan) => chan.try_select(token), } } fn deadline(&self) -> Option { match &self.flavor { ReceiverFlavor::Array(_) => None, ReceiverFlavor::List(_) => None, ReceiverFlavor::Zero(_) => None, ReceiverFlavor::At(chan) => chan.deadline(), ReceiverFlavor::Tick(chan) => chan.deadline(), ReceiverFlavor::Never(chan) => chan.deadline(), } } fn register(&self, oper: Operation, cx: &Context) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.receiver().register(oper, cx), ReceiverFlavor::List(chan) => chan.receiver().register(oper, cx), ReceiverFlavor::Zero(chan) => chan.receiver().register(oper, cx), ReceiverFlavor::At(chan) => chan.register(oper, cx), ReceiverFlavor::Tick(chan) => chan.register(oper, cx), ReceiverFlavor::Never(chan) => chan.register(oper, cx), } } fn unregister(&self, oper: Operation) { match &self.flavor { ReceiverFlavor::Array(chan) => chan.receiver().unregister(oper), ReceiverFlavor::List(chan) => chan.receiver().unregister(oper), ReceiverFlavor::Zero(chan) => chan.receiver().unregister(oper), ReceiverFlavor::At(chan) => chan.unregister(oper), ReceiverFlavor::Tick(chan) => chan.unregister(oper), ReceiverFlavor::Never(chan) => chan.unregister(oper), } } fn accept(&self, token: &mut Token, cx: &Context) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.receiver().accept(token, cx), ReceiverFlavor::List(chan) => chan.receiver().accept(token, cx), ReceiverFlavor::Zero(chan) => chan.receiver().accept(token, cx), ReceiverFlavor::At(chan) => chan.accept(token, cx), ReceiverFlavor::Tick(chan) => chan.accept(token, cx), ReceiverFlavor::Never(chan) => chan.accept(token, cx), } } fn is_ready(&self) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.receiver().is_ready(), ReceiverFlavor::List(chan) => chan.receiver().is_ready(), ReceiverFlavor::Zero(chan) => chan.receiver().is_ready(), ReceiverFlavor::At(chan) => chan.is_ready(), ReceiverFlavor::Tick(chan) => chan.is_ready(), ReceiverFlavor::Never(chan) => chan.is_ready(), } } fn watch(&self, oper: Operation, cx: &Context) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.receiver().watch(oper, cx), ReceiverFlavor::List(chan) => chan.receiver().watch(oper, cx), ReceiverFlavor::Zero(chan) => chan.receiver().watch(oper, cx), ReceiverFlavor::At(chan) => chan.watch(oper, cx), ReceiverFlavor::Tick(chan) => chan.watch(oper, cx), ReceiverFlavor::Never(chan) => chan.watch(oper, cx), } } fn unwatch(&self, oper: Operation) { match &self.flavor { ReceiverFlavor::Array(chan) => chan.receiver().unwatch(oper), ReceiverFlavor::List(chan) => chan.receiver().unwatch(oper), ReceiverFlavor::Zero(chan) => chan.receiver().unwatch(oper), ReceiverFlavor::At(chan) => chan.unwatch(oper), ReceiverFlavor::Tick(chan) => chan.unwatch(oper), ReceiverFlavor::Never(chan) => chan.unwatch(oper), } } } /// Writes a message into the channel. pub(crate) unsafe fn write(s: &Sender, token: &mut Token, msg: T) -> Result<(), T> { match &s.flavor { SenderFlavor::Array(chan) => chan.write(token, msg), SenderFlavor::List(chan) => chan.write(token, msg), SenderFlavor::Zero(chan) => chan.write(token, msg), } } /// Reads a message from the channel. pub(crate) unsafe fn read(r: &Receiver, token: &mut Token) -> Result { match &r.flavor { ReceiverFlavor::Array(chan) => chan.read(token), ReceiverFlavor::List(chan) => chan.read(token), ReceiverFlavor::Zero(chan) => chan.read(token), ReceiverFlavor::At(chan) => { mem::transmute_copy::, Result>(&chan.read(token)) } ReceiverFlavor::Tick(chan) => { mem::transmute_copy::, Result>(&chan.read(token)) } ReceiverFlavor::Never(chan) => chan.read(token), } } crossbeam-channel-0.5.14/src/context.rs000064400000000000000000000116061046102023000161240ustar 00000000000000//! Thread-local context used in select. use std::cell::Cell; use std::ptr; use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; use std::sync::Arc; use std::thread::{self, Thread, ThreadId}; use std::time::Instant; use crossbeam_utils::Backoff; use crate::select::Selected; /// Thread-local context used in select. // This is a private API that is used by the select macro. #[derive(Debug, Clone)] pub struct Context { inner: Arc, } /// Inner representation of `Context`. #[derive(Debug)] struct Inner { /// Selected operation. select: AtomicUsize, /// A slot into which another thread may store a pointer to its `Packet`. packet: AtomicPtr<()>, /// Thread handle. thread: Thread, /// Thread id. thread_id: ThreadId, } impl Context { /// Creates a new context for the duration of the closure. #[inline] pub fn with(f: F) -> R where F: FnOnce(&Context) -> R, { std::thread_local! { /// Cached thread-local context. static CONTEXT: Cell> = Cell::new(Some(Context::new())); } let mut f = Some(f); let mut f = |cx: &Context| -> R { let f = f.take().unwrap(); f(cx) }; CONTEXT .try_with(|cell| match cell.take() { None => f(&Context::new()), Some(cx) => { cx.reset(); let res = f(&cx); cell.set(Some(cx)); res } }) .unwrap_or_else(|_| f(&Context::new())) } /// Creates a new `Context`. #[cold] fn new() -> Context { Context { inner: Arc::new(Inner { select: AtomicUsize::new(Selected::Waiting.into()), packet: AtomicPtr::new(ptr::null_mut()), thread: thread::current(), thread_id: thread::current().id(), }), } } /// Resets `select` and `packet`. #[inline] fn reset(&self) { self.inner .select .store(Selected::Waiting.into(), Ordering::Release); self.inner.packet.store(ptr::null_mut(), Ordering::Release); } /// Attempts to select an operation. /// /// On failure, the previously selected operation is returned. #[inline] pub fn try_select(&self, select: Selected) -> Result<(), Selected> { self.inner .select .compare_exchange( Selected::Waiting.into(), select.into(), Ordering::AcqRel, Ordering::Acquire, ) .map(|_| ()) .map_err(|e| e.into()) } /// Returns the selected operation. #[inline] pub fn selected(&self) -> Selected { Selected::from(self.inner.select.load(Ordering::Acquire)) } /// Stores a packet. /// /// This method must be called after `try_select` succeeds and there is a packet to provide. #[inline] pub fn store_packet(&self, packet: *mut ()) { if !packet.is_null() { self.inner.packet.store(packet, Ordering::Release); } } /// Waits until a packet is provided and returns it. #[inline] pub fn wait_packet(&self) -> *mut () { let backoff = Backoff::new(); loop { let packet = self.inner.packet.load(Ordering::Acquire); if !packet.is_null() { return packet; } backoff.snooze(); } } /// Waits until an operation is selected and returns it. /// /// If the deadline is reached, `Selected::Aborted` will be selected. #[inline] pub fn wait_until(&self, deadline: Option) -> Selected { loop { // Check whether an operation has been selected. let sel = Selected::from(self.inner.select.load(Ordering::Acquire)); if sel != Selected::Waiting { return sel; } // If there's a deadline, park the current thread until the deadline is reached. if let Some(end) = deadline { let now = Instant::now(); if now < end { thread::park_timeout(end - now); } else { // The deadline has been reached. Try aborting select. return match self.try_select(Selected::Aborted) { Ok(()) => Selected::Aborted, Err(s) => s, }; } } else { thread::park(); } } } /// Unparks the thread this context belongs to. #[inline] pub fn unpark(&self) { self.inner.thread.unpark(); } /// Returns the id of the thread this context belongs to. #[inline] pub fn thread_id(&self) -> ThreadId { self.inner.thread_id } } crossbeam-channel-0.5.14/src/counter.rs000064400000000000000000000076751046102023000161320ustar 00000000000000//! Reference counter for channels. use std::boxed::Box; use std::isize; use std::ops; use std::process; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; /// Reference counter internals. struct Counter { /// The number of senders associated with the channel. senders: AtomicUsize, /// The number of receivers associated with the channel. receivers: AtomicUsize, /// Set to `true` if the last sender or the last receiver reference deallocates the channel. destroy: AtomicBool, /// The internal channel. chan: C, } /// Wraps a channel into the reference counter. pub(crate) fn new(chan: C) -> (Sender, Receiver) { let counter = Box::into_raw(Box::new(Counter { senders: AtomicUsize::new(1), receivers: AtomicUsize::new(1), destroy: AtomicBool::new(false), chan, })); let s = Sender { counter }; let r = Receiver { counter }; (s, r) } /// The sending side. pub(crate) struct Sender { counter: *mut Counter, } impl Sender { /// Returns the internal `Counter`. fn counter(&self) -> &Counter { unsafe { &*self.counter } } /// Acquires another sender reference. pub(crate) fn acquire(&self) -> Sender { let count = self.counter().senders.fetch_add(1, Ordering::Relaxed); // Cloning senders and calling `mem::forget` on the clones could potentially overflow the // counter. It's very difficult to recover sensibly from such degenerate scenarios so we // just abort when the count becomes very large. if count > isize::MAX as usize { process::abort(); } Sender { counter: self.counter, } } /// Releases the sender reference. /// /// Function `disconnect` will be called if this is the last sender reference. pub(crate) unsafe fn release bool>(&self, disconnect: F) { if self.counter().senders.fetch_sub(1, Ordering::AcqRel) == 1 { disconnect(&self.counter().chan); if self.counter().destroy.swap(true, Ordering::AcqRel) { drop(Box::from_raw(self.counter)); } } } } impl ops::Deref for Sender { type Target = C; fn deref(&self) -> &C { &self.counter().chan } } impl PartialEq for Sender { fn eq(&self, other: &Sender) -> bool { self.counter == other.counter } } /// The receiving side. pub(crate) struct Receiver { counter: *mut Counter, } impl Receiver { /// Returns the internal `Counter`. fn counter(&self) -> &Counter { unsafe { &*self.counter } } /// Acquires another receiver reference. pub(crate) fn acquire(&self) -> Receiver { let count = self.counter().receivers.fetch_add(1, Ordering::Relaxed); // Cloning receivers and calling `mem::forget` on the clones could potentially overflow the // counter. It's very difficult to recover sensibly from such degenerate scenarios so we // just abort when the count becomes very large. if count > isize::MAX as usize { process::abort(); } Receiver { counter: self.counter, } } /// Releases the receiver reference. /// /// Function `disconnect` will be called if this is the last receiver reference. pub(crate) unsafe fn release bool>(&self, disconnect: F) { if self.counter().receivers.fetch_sub(1, Ordering::AcqRel) == 1 { disconnect(&self.counter().chan); if self.counter().destroy.swap(true, Ordering::AcqRel) { drop(Box::from_raw(self.counter)); } } } } impl ops::Deref for Receiver { type Target = C; fn deref(&self) -> &C { &self.counter().chan } } impl PartialEq for Receiver { fn eq(&self, other: &Receiver) -> bool { self.counter == other.counter } } crossbeam-channel-0.5.14/src/err.rs000064400000000000000000000251531046102023000152320ustar 00000000000000use std::error; use std::fmt; /// An error returned from the [`send`] method. /// /// The message could not be sent because the channel is disconnected. /// /// The error contains the message so it can be recovered. /// /// [`send`]: super::Sender::send #[derive(PartialEq, Eq, Clone, Copy)] pub struct SendError(pub T); /// An error returned from the [`try_send`] method. /// /// The error contains the message being sent so it can be recovered. /// /// [`try_send`]: super::Sender::try_send #[derive(PartialEq, Eq, Clone, Copy)] pub enum TrySendError { /// The message could not be sent because the channel is full. /// /// If this is a zero-capacity channel, then the error indicates that there was no receiver /// available to receive the message at the time. Full(T), /// The message could not be sent because the channel is disconnected. Disconnected(T), } /// An error returned from the [`send_timeout`] method. /// /// The error contains the message being sent so it can be recovered. /// /// [`send_timeout`]: super::Sender::send_timeout #[derive(PartialEq, Eq, Clone, Copy)] pub enum SendTimeoutError { /// The message could not be sent because the channel is full and the operation timed out. /// /// If this is a zero-capacity channel, then the error indicates that there was no receiver /// available to receive the message and the operation timed out. Timeout(T), /// The message could not be sent because the channel is disconnected. Disconnected(T), } /// An error returned from the [`recv`] method. /// /// A message could not be received because the channel is empty and disconnected. /// /// [`recv`]: super::Receiver::recv #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct RecvError; /// An error returned from the [`try_recv`] method. /// /// [`try_recv`]: super::Receiver::try_recv #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum TryRecvError { /// A message could not be received because the channel is empty. /// /// If this is a zero-capacity channel, then the error indicates that there was no sender /// available to send a message at the time. Empty, /// The message could not be received because the channel is empty and disconnected. Disconnected, } /// An error returned from the [`recv_timeout`] method. /// /// [`recv_timeout`]: super::Receiver::recv_timeout #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum RecvTimeoutError { /// A message could not be received because the channel is empty and the operation timed out. /// /// If this is a zero-capacity channel, then the error indicates that there was no sender /// available to send a message and the operation timed out. Timeout, /// The message could not be received because the channel is empty and disconnected. Disconnected, } /// An error returned from the [`try_select`] method. /// /// Failed because none of the channel operations were ready. /// /// [`try_select`]: super::Select::try_select #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct TrySelectError; /// An error returned from the [`select_timeout`] method. /// /// Failed because none of the channel operations became ready before the timeout. /// /// [`select_timeout`]: super::Select::select_timeout #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct SelectTimeoutError; /// An error returned from the [`try_ready`] method. /// /// Failed because none of the channel operations were ready. /// /// [`try_ready`]: super::Select::try_ready #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct TryReadyError; /// An error returned from the [`ready_timeout`] method. /// /// Failed because none of the channel operations became ready before the timeout. /// /// [`ready_timeout`]: super::Select::ready_timeout #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct ReadyTimeoutError; impl fmt::Debug for SendError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "SendError(..)".fmt(f) } } impl fmt::Display for SendError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "sending on a disconnected channel".fmt(f) } } impl error::Error for SendError {} impl SendError { /// Unwraps the message. /// /// # Examples /// /// ``` /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// drop(r); /// /// if let Err(err) = s.send("foo") { /// assert_eq!(err.into_inner(), "foo"); /// } /// ``` pub fn into_inner(self) -> T { self.0 } } impl fmt::Debug for TrySendError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { TrySendError::Full(..) => "Full(..)".fmt(f), TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f), } } } impl fmt::Display for TrySendError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { TrySendError::Full(..) => "sending on a full channel".fmt(f), TrySendError::Disconnected(..) => "sending on a disconnected channel".fmt(f), } } } impl error::Error for TrySendError {} impl From> for TrySendError { fn from(err: SendError) -> TrySendError { match err { SendError(t) => TrySendError::Disconnected(t), } } } impl TrySendError { /// Unwraps the message. /// /// # Examples /// /// ``` /// use crossbeam_channel::bounded; /// /// let (s, r) = bounded(0); /// /// if let Err(err) = s.try_send("foo") { /// assert_eq!(err.into_inner(), "foo"); /// } /// ``` pub fn into_inner(self) -> T { match self { TrySendError::Full(v) => v, TrySendError::Disconnected(v) => v, } } /// Returns `true` if the send operation failed because the channel is full. pub fn is_full(&self) -> bool { match self { TrySendError::Full(_) => true, _ => false, } } /// Returns `true` if the send operation failed because the channel is disconnected. pub fn is_disconnected(&self) -> bool { match self { TrySendError::Disconnected(_) => true, _ => false, } } } impl fmt::Debug for SendTimeoutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "SendTimeoutError(..)".fmt(f) } } impl fmt::Display for SendTimeoutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { SendTimeoutError::Timeout(..) => "timed out waiting on send operation".fmt(f), SendTimeoutError::Disconnected(..) => "sending on a disconnected channel".fmt(f), } } } impl error::Error for SendTimeoutError {} impl From> for SendTimeoutError { fn from(err: SendError) -> SendTimeoutError { match err { SendError(e) => SendTimeoutError::Disconnected(e), } } } impl SendTimeoutError { /// Unwraps the message. /// /// # Examples /// /// ``` /// use std::time::Duration; /// use crossbeam_channel::unbounded; /// /// let (s, r) = unbounded(); /// /// if let Err(err) = s.send_timeout("foo", Duration::from_secs(1)) { /// assert_eq!(err.into_inner(), "foo"); /// } /// ``` pub fn into_inner(self) -> T { match self { SendTimeoutError::Timeout(v) => v, SendTimeoutError::Disconnected(v) => v, } } /// Returns `true` if the send operation timed out. pub fn is_timeout(&self) -> bool { match self { SendTimeoutError::Timeout(_) => true, _ => false, } } /// Returns `true` if the send operation failed because the channel is disconnected. pub fn is_disconnected(&self) -> bool { match self { SendTimeoutError::Disconnected(_) => true, _ => false, } } } impl fmt::Display for RecvError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "receiving on an empty and disconnected channel".fmt(f) } } impl error::Error for RecvError {} impl fmt::Display for TryRecvError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { TryRecvError::Empty => "receiving on an empty channel".fmt(f), TryRecvError::Disconnected => "receiving on an empty and disconnected channel".fmt(f), } } } impl error::Error for TryRecvError {} impl From for TryRecvError { fn from(err: RecvError) -> TryRecvError { match err { RecvError => TryRecvError::Disconnected, } } } impl TryRecvError { /// Returns `true` if the receive operation failed because the channel is empty. pub fn is_empty(&self) -> bool { match self { TryRecvError::Empty => true, _ => false, } } /// Returns `true` if the receive operation failed because the channel is disconnected. pub fn is_disconnected(&self) -> bool { match self { TryRecvError::Disconnected => true, _ => false, } } } impl fmt::Display for RecvTimeoutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { RecvTimeoutError::Timeout => "timed out waiting on receive operation".fmt(f), RecvTimeoutError::Disconnected => "channel is empty and disconnected".fmt(f), } } } impl error::Error for RecvTimeoutError {} impl From for RecvTimeoutError { fn from(err: RecvError) -> RecvTimeoutError { match err { RecvError => RecvTimeoutError::Disconnected, } } } impl RecvTimeoutError { /// Returns `true` if the receive operation timed out. pub fn is_timeout(&self) -> bool { match self { RecvTimeoutError::Timeout => true, _ => false, } } /// Returns `true` if the receive operation failed because the channel is disconnected. pub fn is_disconnected(&self) -> bool { match self { RecvTimeoutError::Disconnected => true, _ => false, } } } impl fmt::Display for TrySelectError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "all operations in select would block".fmt(f) } } impl error::Error for TrySelectError {} impl fmt::Display for SelectTimeoutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "timed out waiting on select".fmt(f) } } impl error::Error for SelectTimeoutError {} crossbeam-channel-0.5.14/src/flavors/array.rs000064400000000000000000000512641046102023000172360ustar 00000000000000//! Bounded channel based on a preallocated array. //! //! This flavor has a fixed, positive capacity. //! //! The implementation is based on Dmitry Vyukov's bounded MPMC queue. //! //! Source: //! - //! - use std::boxed::Box; use std::cell::UnsafeCell; use std::mem::{self, MaybeUninit}; use std::ptr; use std::sync::atomic::{self, AtomicUsize, Ordering}; use std::time::Instant; use crossbeam_utils::{Backoff, CachePadded}; use crate::context::Context; use crate::err::{RecvTimeoutError, SendTimeoutError, TryRecvError, TrySendError}; use crate::select::{Operation, SelectHandle, Selected, Token}; use crate::waker::SyncWaker; /// A slot in a channel. struct Slot { /// The current stamp. stamp: AtomicUsize, /// The message in this slot. msg: UnsafeCell>, } /// The token type for the array flavor. #[derive(Debug)] pub(crate) struct ArrayToken { /// Slot to read from or write to. slot: *const u8, /// Stamp to store into the slot after reading or writing. stamp: usize, } impl Default for ArrayToken { #[inline] fn default() -> Self { ArrayToken { slot: ptr::null(), stamp: 0, } } } /// Bounded channel based on a preallocated array. pub(crate) struct Channel { /// The head of the channel. /// /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but /// packed into a single `usize`. The lower bits represent the index, while the upper bits /// represent the lap. The mark bit in the head is always zero. /// /// Messages are popped from the head of the channel. head: CachePadded, /// The tail of the channel. /// /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but /// packed into a single `usize`. The lower bits represent the index, while the upper bits /// represent the lap. The mark bit indicates that the channel is disconnected. /// /// Messages are pushed into the tail of the channel. tail: CachePadded, /// The buffer holding slots. buffer: Box<[Slot]>, /// The channel capacity. cap: usize, /// A stamp with the value of `{ lap: 1, mark: 0, index: 0 }`. one_lap: usize, /// If this bit is set in the tail, that means the channel is disconnected. mark_bit: usize, /// Senders waiting while the channel is full. senders: SyncWaker, /// Receivers waiting while the channel is empty and not disconnected. receivers: SyncWaker, } impl Channel { /// Creates a bounded channel of capacity `cap`. pub(crate) fn with_capacity(cap: usize) -> Self { assert!(cap > 0, "capacity must be positive"); // Compute constants `mark_bit` and `one_lap`. let mark_bit = (cap + 1).next_power_of_two(); let one_lap = mark_bit * 2; // Head is initialized to `{ lap: 0, mark: 0, index: 0 }`. let head = 0; // Tail is initialized to `{ lap: 0, mark: 0, index: 0 }`. let tail = 0; // Allocate a buffer of `cap` slots initialized // with stamps. let buffer: Box<[Slot]> = (0..cap) .map(|i| { // Set the stamp to `{ lap: 0, mark: 0, index: i }`. Slot { stamp: AtomicUsize::new(i), msg: UnsafeCell::new(MaybeUninit::uninit()), } }) .collect(); Channel { buffer, cap, one_lap, mark_bit, head: CachePadded::new(AtomicUsize::new(head)), tail: CachePadded::new(AtomicUsize::new(tail)), senders: SyncWaker::new(), receivers: SyncWaker::new(), } } /// Returns a receiver handle to the channel. pub(crate) fn receiver(&self) -> Receiver<'_, T> { Receiver(self) } /// Returns a sender handle to the channel. pub(crate) fn sender(&self) -> Sender<'_, T> { Sender(self) } /// Attempts to reserve a slot for sending a message. fn start_send(&self, token: &mut Token) -> bool { let backoff = Backoff::new(); let mut tail = self.tail.load(Ordering::Relaxed); loop { // Check if the channel is disconnected. if tail & self.mark_bit != 0 { token.array.slot = ptr::null(); token.array.stamp = 0; return true; } // Deconstruct the tail. let index = tail & (self.mark_bit - 1); let lap = tail & !(self.one_lap - 1); // Inspect the corresponding slot. debug_assert!(index < self.buffer.len()); let slot = unsafe { self.buffer.get_unchecked(index) }; let stamp = slot.stamp.load(Ordering::Acquire); // If the tail and the stamp match, we may attempt to push. if tail == stamp { let new_tail = if index + 1 < self.cap { // Same lap, incremented index. // Set to `{ lap: lap, mark: 0, index: index + 1 }`. tail + 1 } else { // One lap forward, index wraps around to zero. // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. lap.wrapping_add(self.one_lap) }; // Try moving the tail. match self.tail.compare_exchange_weak( tail, new_tail, Ordering::SeqCst, Ordering::Relaxed, ) { Ok(_) => { // Prepare the token for the follow-up call to `write`. token.array.slot = slot as *const Slot as *const u8; token.array.stamp = tail + 1; return true; } Err(t) => { tail = t; backoff.spin(); } } } else if stamp.wrapping_add(self.one_lap) == tail + 1 { atomic::fence(Ordering::SeqCst); let head = self.head.load(Ordering::Relaxed); // If the head lags one lap behind the tail as well... if head.wrapping_add(self.one_lap) == tail { // ...then the channel is full. return false; } backoff.spin(); tail = self.tail.load(Ordering::Relaxed); } else { // Snooze because we need to wait for the stamp to get updated. backoff.snooze(); tail = self.tail.load(Ordering::Relaxed); } } } /// Writes a message into the channel. pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> { // If there is no slot, the channel is disconnected. if token.array.slot.is_null() { return Err(msg); } let slot: &Slot = &*token.array.slot.cast::>(); // Write the message into the slot and update the stamp. slot.msg.get().write(MaybeUninit::new(msg)); slot.stamp.store(token.array.stamp, Ordering::Release); // Wake a sleeping receiver. self.receivers.notify(); Ok(()) } /// Attempts to reserve a slot for receiving a message. fn start_recv(&self, token: &mut Token) -> bool { let backoff = Backoff::new(); let mut head = self.head.load(Ordering::Relaxed); loop { // Deconstruct the head. let index = head & (self.mark_bit - 1); let lap = head & !(self.one_lap - 1); // Inspect the corresponding slot. debug_assert!(index < self.buffer.len()); let slot = unsafe { self.buffer.get_unchecked(index) }; let stamp = slot.stamp.load(Ordering::Acquire); // If the stamp is ahead of the head by 1, we may attempt to pop. if head + 1 == stamp { let new = if index + 1 < self.cap { // Same lap, incremented index. // Set to `{ lap: lap, mark: 0, index: index + 1 }`. head + 1 } else { // One lap forward, index wraps around to zero. // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. lap.wrapping_add(self.one_lap) }; // Try moving the head. match self.head.compare_exchange_weak( head, new, Ordering::SeqCst, Ordering::Relaxed, ) { Ok(_) => { // Prepare the token for the follow-up call to `read`. token.array.slot = slot as *const Slot as *const u8; token.array.stamp = head.wrapping_add(self.one_lap); return true; } Err(h) => { head = h; backoff.spin(); } } } else if stamp == head { atomic::fence(Ordering::SeqCst); let tail = self.tail.load(Ordering::Relaxed); // If the tail equals the head, that means the channel is empty. if (tail & !self.mark_bit) == head { // If the channel is disconnected... if tail & self.mark_bit != 0 { // ...then receive an error. token.array.slot = ptr::null(); token.array.stamp = 0; return true; } else { // Otherwise, the receive operation is not ready. return false; } } backoff.spin(); head = self.head.load(Ordering::Relaxed); } else { // Snooze because we need to wait for the stamp to get updated. backoff.snooze(); head = self.head.load(Ordering::Relaxed); } } } /// Reads a message from the channel. pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { if token.array.slot.is_null() { // The channel is disconnected. return Err(()); } let slot: &Slot = &*token.array.slot.cast::>(); // Read the message from the slot and update the stamp. let msg = slot.msg.get().read().assume_init(); slot.stamp.store(token.array.stamp, Ordering::Release); // Wake a sleeping sender. self.senders.notify(); Ok(msg) } /// Attempts to send a message into the channel. pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError> { let token = &mut Token::default(); if self.start_send(token) { unsafe { self.write(token, msg).map_err(TrySendError::Disconnected) } } else { Err(TrySendError::Full(msg)) } } /// Sends a message into the channel. pub(crate) fn send( &self, msg: T, deadline: Option, ) -> Result<(), SendTimeoutError> { let token = &mut Token::default(); loop { // Try sending a message several times. let backoff = Backoff::new(); loop { if self.start_send(token) { let res = unsafe { self.write(token, msg) }; return res.map_err(SendTimeoutError::Disconnected); } if backoff.is_completed() { break; } else { backoff.snooze(); } } if let Some(d) = deadline { if Instant::now() >= d { return Err(SendTimeoutError::Timeout(msg)); } } Context::with(|cx| { // Prepare for blocking until a receiver wakes us up. let oper = Operation::hook(token); self.senders.register(oper, cx); // Has the channel become ready just now? if !self.is_full() || self.is_disconnected() { let _ = cx.try_select(Selected::Aborted); } // Block the current thread. let sel = cx.wait_until(deadline); match sel { Selected::Waiting => unreachable!(), Selected::Aborted | Selected::Disconnected => { self.senders.unregister(oper).unwrap(); } Selected::Operation(_) => {} } }); } } /// Attempts to receive a message without blocking. pub(crate) fn try_recv(&self) -> Result { let token = &mut Token::default(); if self.start_recv(token) { unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) } } else { Err(TryRecvError::Empty) } } /// Receives a message from the channel. pub(crate) fn recv(&self, deadline: Option) -> Result { let token = &mut Token::default(); loop { // Try receiving a message several times. let backoff = Backoff::new(); loop { if self.start_recv(token) { let res = unsafe { self.read(token) }; return res.map_err(|_| RecvTimeoutError::Disconnected); } if backoff.is_completed() { break; } else { backoff.snooze(); } } if let Some(d) = deadline { if Instant::now() >= d { return Err(RecvTimeoutError::Timeout); } } Context::with(|cx| { // Prepare for blocking until a sender wakes us up. let oper = Operation::hook(token); self.receivers.register(oper, cx); // Has the channel become ready just now? if !self.is_empty() || self.is_disconnected() { let _ = cx.try_select(Selected::Aborted); } // Block the current thread. let sel = cx.wait_until(deadline); match sel { Selected::Waiting => unreachable!(), Selected::Aborted | Selected::Disconnected => { self.receivers.unregister(oper).unwrap(); // If the channel was disconnected, we still have to check for remaining // messages. } Selected::Operation(_) => {} } }); } } /// Returns the current number of messages inside the channel. pub(crate) fn len(&self) -> usize { loop { // Load the tail, then load the head. let tail = self.tail.load(Ordering::SeqCst); let head = self.head.load(Ordering::SeqCst); // If the tail didn't change, we've got consistent values to work with. if self.tail.load(Ordering::SeqCst) == tail { let hix = head & (self.mark_bit - 1); let tix = tail & (self.mark_bit - 1); return if hix < tix { tix - hix } else if hix > tix { self.cap - hix + tix } else if (tail & !self.mark_bit) == head { 0 } else { self.cap }; } } } /// Returns the capacity of the channel. pub(crate) fn capacity(&self) -> Option { Some(self.cap) } /// Disconnects the channel and wakes up all blocked senders and receivers. /// /// Returns `true` if this call disconnected the channel. pub(crate) fn disconnect(&self) -> bool { let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst); if tail & self.mark_bit == 0 { self.senders.disconnect(); self.receivers.disconnect(); true } else { false } } /// Returns `true` if the channel is disconnected. pub(crate) fn is_disconnected(&self) -> bool { self.tail.load(Ordering::SeqCst) & self.mark_bit != 0 } /// Returns `true` if the channel is empty. pub(crate) fn is_empty(&self) -> bool { let head = self.head.load(Ordering::SeqCst); let tail = self.tail.load(Ordering::SeqCst); // Is the tail equal to the head? // // Note: If the head changes just before we load the tail, that means there was a moment // when the channel was not empty, so it is safe to just return `false`. (tail & !self.mark_bit) == head } /// Returns `true` if the channel is full. pub(crate) fn is_full(&self) -> bool { let tail = self.tail.load(Ordering::SeqCst); let head = self.head.load(Ordering::SeqCst); // Is the head lagging one lap behind tail? // // Note: If the tail changes just before we load the head, that means there was a moment // when the channel was not full, so it is safe to just return `false`. head.wrapping_add(self.one_lap) == tail & !self.mark_bit } } impl Drop for Channel { fn drop(&mut self) { if mem::needs_drop::() { // Get the index of the head. let head = *self.head.get_mut(); let tail = *self.tail.get_mut(); let hix = head & (self.mark_bit - 1); let tix = tail & (self.mark_bit - 1); let len = if hix < tix { tix - hix } else if hix > tix { self.cap - hix + tix } else if (tail & !self.mark_bit) == head { 0 } else { self.cap }; // Loop over all slots that hold a message and drop them. for i in 0..len { // Compute the index of the next slot holding a message. let index = if hix + i < self.cap { hix + i } else { hix + i - self.cap }; unsafe { debug_assert!(index < self.buffer.len()); let slot = self.buffer.get_unchecked_mut(index); (*slot.msg.get()).assume_init_drop(); } } } } } /// Receiver handle to a channel. pub(crate) struct Receiver<'a, T>(&'a Channel); /// Sender handle to a channel. pub(crate) struct Sender<'a, T>(&'a Channel); impl SelectHandle for Receiver<'_, T> { fn try_select(&self, token: &mut Token) -> bool { self.0.start_recv(token) } fn deadline(&self) -> Option { None } fn register(&self, oper: Operation, cx: &Context) -> bool { self.0.receivers.register(oper, cx); self.is_ready() } fn unregister(&self, oper: Operation) { self.0.receivers.unregister(oper); } fn accept(&self, token: &mut Token, _cx: &Context) -> bool { self.try_select(token) } fn is_ready(&self) -> bool { !self.0.is_empty() || self.0.is_disconnected() } fn watch(&self, oper: Operation, cx: &Context) -> bool { self.0.receivers.watch(oper, cx); self.is_ready() } fn unwatch(&self, oper: Operation) { self.0.receivers.unwatch(oper); } } impl SelectHandle for Sender<'_, T> { fn try_select(&self, token: &mut Token) -> bool { self.0.start_send(token) } fn deadline(&self) -> Option { None } fn register(&self, oper: Operation, cx: &Context) -> bool { self.0.senders.register(oper, cx); self.is_ready() } fn unregister(&self, oper: Operation) { self.0.senders.unregister(oper); } fn accept(&self, token: &mut Token, _cx: &Context) -> bool { self.try_select(token) } fn is_ready(&self) -> bool { !self.0.is_full() || self.0.is_disconnected() } fn watch(&self, oper: Operation, cx: &Context) -> bool { self.0.senders.watch(oper, cx); self.is_ready() } fn unwatch(&self, oper: Operation) { self.0.senders.unwatch(oper); } } crossbeam-channel-0.5.14/src/flavors/at.rs000064400000000000000000000134211046102023000165150ustar 00000000000000//! Channel that delivers a message at a certain moment in time. //! //! Messages cannot be sent into this kind of channel; they are materialized on demand. use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use std::time::Instant; use crate::context::Context; use crate::err::{RecvTimeoutError, TryRecvError}; use crate::select::{Operation, SelectHandle, Token}; use crate::utils; /// Result of a receive operation. pub(crate) type AtToken = Option; /// Channel that delivers a message at a certain moment in time pub(crate) struct Channel { /// The instant at which the message will be delivered. delivery_time: Instant, /// `true` if the message has been received. received: AtomicBool, } impl Channel { /// Creates a channel that delivers a message at a certain instant in time. #[inline] pub(crate) fn new_deadline(when: Instant) -> Self { Channel { delivery_time: when, received: AtomicBool::new(false), } } /// Attempts to receive a message without blocking. #[inline] pub(crate) fn try_recv(&self) -> Result { // We use relaxed ordering because this is just an optional optimistic check. if self.received.load(Ordering::Relaxed) { // The message has already been received. return Err(TryRecvError::Empty); } if Instant::now() < self.delivery_time { // The message was not delivered yet. return Err(TryRecvError::Empty); } // Try receiving the message if it is still available. if !self.received.swap(true, Ordering::SeqCst) { // Success! Return delivery time as the message. Ok(self.delivery_time) } else { // The message was already received. Err(TryRecvError::Empty) } } /// Receives a message from the channel. #[inline] pub(crate) fn recv(&self, deadline: Option) -> Result { // We use relaxed ordering because this is just an optional optimistic check. if self.received.load(Ordering::Relaxed) { // The message has already been received. utils::sleep_until(deadline); return Err(RecvTimeoutError::Timeout); } // Wait until the message is received or the deadline is reached. loop { let now = Instant::now(); let deadline = match deadline { // Check if we can receive the next message. _ if now >= self.delivery_time => break, // Check if the timeout deadline has been reached. Some(d) if now >= d => return Err(RecvTimeoutError::Timeout), // Sleep until one of the above happens Some(d) if d < self.delivery_time => d, _ => self.delivery_time, }; thread::sleep(deadline - now); } // Try receiving the message if it is still available. if !self.received.swap(true, Ordering::SeqCst) { // Success! Return the message, which is the instant at which it was delivered. Ok(self.delivery_time) } else { // The message was already received. Block forever. utils::sleep_until(None); unreachable!() } } /// Reads a message from the channel. #[inline] pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { token.at.ok_or(()) } /// Returns `true` if the channel is empty. #[inline] pub(crate) fn is_empty(&self) -> bool { // We use relaxed ordering because this is just an optional optimistic check. if self.received.load(Ordering::Relaxed) { return true; } // If the delivery time hasn't been reached yet, the channel is empty. if Instant::now() < self.delivery_time { return true; } // The delivery time has been reached. The channel is empty only if the message has already // been received. self.received.load(Ordering::SeqCst) } /// Returns `true` if the channel is full. #[inline] pub(crate) fn is_full(&self) -> bool { !self.is_empty() } /// Returns the number of messages in the channel. #[inline] pub(crate) fn len(&self) -> usize { if self.is_empty() { 0 } else { 1 } } /// Returns the capacity of the channel. #[inline] pub(crate) fn capacity(&self) -> Option { Some(1) } } impl SelectHandle for Channel { #[inline] fn try_select(&self, token: &mut Token) -> bool { match self.try_recv() { Ok(msg) => { token.at = Some(msg); true } Err(TryRecvError::Disconnected) => { token.at = None; true } Err(TryRecvError::Empty) => false, } } #[inline] fn deadline(&self) -> Option { // We use relaxed ordering because this is just an optional optimistic check. if self.received.load(Ordering::Relaxed) { None } else { Some(self.delivery_time) } } #[inline] fn register(&self, _oper: Operation, _cx: &Context) -> bool { self.is_ready() } #[inline] fn unregister(&self, _oper: Operation) {} #[inline] fn accept(&self, token: &mut Token, _cx: &Context) -> bool { self.try_select(token) } #[inline] fn is_ready(&self) -> bool { !self.is_empty() } #[inline] fn watch(&self, _oper: Operation, _cx: &Context) -> bool { self.is_ready() } #[inline] fn unwatch(&self, _oper: Operation) {} } crossbeam-channel-0.5.14/src/flavors/list.rs000064400000000000000000000635031046102023000170720ustar 00000000000000//! Unbounded channel implemented as a linked list. use std::alloc::{alloc_zeroed, handle_alloc_error, Layout}; use std::boxed::Box; use std::cell::UnsafeCell; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::ptr; use std::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; use std::time::Instant; use crossbeam_utils::{Backoff, CachePadded}; use crate::context::Context; use crate::err::{RecvTimeoutError, SendTimeoutError, TryRecvError, TrySendError}; use crate::select::{Operation, SelectHandle, Selected, Token}; use crate::waker::SyncWaker; // TODO(stjepang): Once we bump the minimum required Rust version to 1.28 or newer, re-apply the // following changes by @kleimkuhler: // // 1. https://github.com/crossbeam-rs/crossbeam-channel/pull/100 // 2. https://github.com/crossbeam-rs/crossbeam-channel/pull/101 // Bits indicating the state of a slot: // * If a message has been written into the slot, `WRITE` is set. // * If a message has been read from the slot, `READ` is set. // * If the block is being destroyed, `DESTROY` is set. const WRITE: usize = 1; const READ: usize = 2; const DESTROY: usize = 4; // Each block covers one "lap" of indices. const LAP: usize = 32; // The maximum number of messages a block can hold. const BLOCK_CAP: usize = LAP - 1; // How many lower bits are reserved for metadata. const SHIFT: usize = 1; // Has two different purposes: // * If set in head, indicates that the block is not the last one. // * If set in tail, indicates that the channel is disconnected. const MARK_BIT: usize = 1; /// A slot in a block. struct Slot { /// The message. msg: UnsafeCell>, /// The state of the slot. state: AtomicUsize, } impl Slot { /// Waits until a message is written into the slot. fn wait_write(&self) { let backoff = Backoff::new(); while self.state.load(Ordering::Acquire) & WRITE == 0 { backoff.snooze(); } } } /// A block in a linked list. /// /// Each block in the list can hold up to `BLOCK_CAP` messages. struct Block { /// The next block in the linked list. next: AtomicPtr>, /// Slots for messages. slots: [Slot; BLOCK_CAP], } impl Block { const LAYOUT: Layout = { let layout = Layout::new::(); assert!( layout.size() != 0, "Block should never be zero-sized, as it has an AtomicPtr field" ); layout }; /// Creates an empty block. fn new() -> Box { // SAFETY: layout is not zero-sized let ptr = unsafe { alloc_zeroed(Self::LAYOUT) }; // Handle allocation failure if ptr.is_null() { handle_alloc_error(Self::LAYOUT) } // SAFETY: This is safe because: // [1] `Block::next` (AtomicPtr) may be safely zero initialized. // [2] `Block::slots` (Array) may be safely zero initialized because of [3, 4]. // [3] `Slot::msg` (UnsafeCell) may be safely zero initialized because it // holds a MaybeUninit. // [4] `Slot::state` (AtomicUsize) may be safely zero initialized. // TODO: unsafe { Box::new_zeroed().assume_init() } unsafe { Box::from_raw(ptr.cast()) } } /// Waits until the next pointer is set. fn wait_next(&self) -> *mut Block { let backoff = Backoff::new(); loop { let next = self.next.load(Ordering::Acquire); if !next.is_null() { return next; } backoff.snooze(); } } /// Sets the `DESTROY` bit in slots starting from `start` and destroys the block. unsafe fn destroy(this: *mut Block, start: usize) { // It is not necessary to set the `DESTROY` bit in the last slot because that slot has // begun destruction of the block. for i in start..BLOCK_CAP - 1 { let slot = (*this).slots.get_unchecked(i); // Mark the `DESTROY` bit if a thread is still using the slot. if slot.state.load(Ordering::Acquire) & READ == 0 && slot.state.fetch_or(DESTROY, Ordering::AcqRel) & READ == 0 { // If a thread is still using the slot, it will continue destruction of the block. return; } } // No thread is using the block, now it is safe to destroy it. drop(Box::from_raw(this)); } } /// A position in a channel. #[derive(Debug)] struct Position { /// The index in the channel. index: AtomicUsize, /// The block in the linked list. block: AtomicPtr>, } /// The token type for the list flavor. #[derive(Debug)] pub(crate) struct ListToken { /// The block of slots. block: *const u8, /// The offset into the block. offset: usize, } impl Default for ListToken { #[inline] fn default() -> Self { ListToken { block: ptr::null(), offset: 0, } } } /// Unbounded channel implemented as a linked list. /// /// Each message sent into the channel is assigned a sequence number, i.e. an index. Indices are /// represented as numbers of type `usize` and wrap on overflow. /// /// Consecutive messages are grouped into blocks in order to put less pressure on the allocator and /// improve cache efficiency. pub(crate) struct Channel { /// The head of the channel. head: CachePadded>, /// The tail of the channel. tail: CachePadded>, /// Receivers waiting while the channel is empty and not disconnected. receivers: SyncWaker, /// Indicates that dropping a `Channel` may drop messages of type `T`. _marker: PhantomData, } impl Channel { /// Creates a new unbounded channel. pub(crate) fn new() -> Self { Channel { head: CachePadded::new(Position { block: AtomicPtr::new(ptr::null_mut()), index: AtomicUsize::new(0), }), tail: CachePadded::new(Position { block: AtomicPtr::new(ptr::null_mut()), index: AtomicUsize::new(0), }), receivers: SyncWaker::new(), _marker: PhantomData, } } /// Returns a receiver handle to the channel. pub(crate) fn receiver(&self) -> Receiver<'_, T> { Receiver(self) } /// Returns a sender handle to the channel. pub(crate) fn sender(&self) -> Sender<'_, T> { Sender(self) } /// Attempts to reserve a slot for sending a message. fn start_send(&self, token: &mut Token) -> bool { let backoff = Backoff::new(); let mut tail = self.tail.index.load(Ordering::Acquire); let mut block = self.tail.block.load(Ordering::Acquire); let mut next_block = None; loop { // Check if the channel is disconnected. if tail & MARK_BIT != 0 { token.list.block = ptr::null(); return true; } // Calculate the offset of the index into the block. let offset = (tail >> SHIFT) % LAP; // If we reached the end of the block, wait until the next one is installed. if offset == BLOCK_CAP { backoff.snooze(); tail = self.tail.index.load(Ordering::Acquire); block = self.tail.block.load(Ordering::Acquire); continue; } // If we're going to have to install the next block, allocate it in advance in order to // make the wait for other threads as short as possible. if offset + 1 == BLOCK_CAP && next_block.is_none() { next_block = Some(Block::::new()); } // If this is the first message to be sent into the channel, we need to allocate the // first block and install it. if block.is_null() { let new = Box::into_raw(Block::::new()); if self .tail .block .compare_exchange(block, new, Ordering::Release, Ordering::Relaxed) .is_ok() { self.head.block.store(new, Ordering::Release); block = new; } else { next_block = unsafe { Some(Box::from_raw(new)) }; tail = self.tail.index.load(Ordering::Acquire); block = self.tail.block.load(Ordering::Acquire); continue; } } let new_tail = tail + (1 << SHIFT); // Try advancing the tail forward. match self.tail.index.compare_exchange_weak( tail, new_tail, Ordering::SeqCst, Ordering::Acquire, ) { Ok(_) => unsafe { // If we've reached the end of the block, install the next one. if offset + 1 == BLOCK_CAP { let next_block = Box::into_raw(next_block.unwrap()); self.tail.block.store(next_block, Ordering::Release); self.tail.index.fetch_add(1 << SHIFT, Ordering::Release); (*block).next.store(next_block, Ordering::Release); } token.list.block = block as *const u8; token.list.offset = offset; return true; }, Err(t) => { tail = t; block = self.tail.block.load(Ordering::Acquire); backoff.spin(); } } } } /// Writes a message into the channel. pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> { // If there is no slot, the channel is disconnected. if token.list.block.is_null() { return Err(msg); } // Write the message into the slot. let block = token.list.block.cast::>(); let offset = token.list.offset; let slot = (*block).slots.get_unchecked(offset); slot.msg.get().write(MaybeUninit::new(msg)); slot.state.fetch_or(WRITE, Ordering::Release); // Wake a sleeping receiver. self.receivers.notify(); Ok(()) } /// Attempts to reserve a slot for receiving a message. fn start_recv(&self, token: &mut Token) -> bool { let backoff = Backoff::new(); let mut head = self.head.index.load(Ordering::Acquire); let mut block = self.head.block.load(Ordering::Acquire); loop { // Calculate the offset of the index into the block. let offset = (head >> SHIFT) % LAP; // If we reached the end of the block, wait until the next one is installed. if offset == BLOCK_CAP { backoff.snooze(); head = self.head.index.load(Ordering::Acquire); block = self.head.block.load(Ordering::Acquire); continue; } let mut new_head = head + (1 << SHIFT); if new_head & MARK_BIT == 0 { atomic::fence(Ordering::SeqCst); let tail = self.tail.index.load(Ordering::Relaxed); // If the tail equals the head, that means the channel is empty. if head >> SHIFT == tail >> SHIFT { // If the channel is disconnected... if tail & MARK_BIT != 0 { // ...then receive an error. token.list.block = ptr::null(); return true; } else { // Otherwise, the receive operation is not ready. return false; } } // If head and tail are not in the same block, set `MARK_BIT` in head. if (head >> SHIFT) / LAP != (tail >> SHIFT) / LAP { new_head |= MARK_BIT; } } // The block can be null here only if the first message is being sent into the channel. // In that case, just wait until it gets initialized. if block.is_null() { backoff.snooze(); head = self.head.index.load(Ordering::Acquire); block = self.head.block.load(Ordering::Acquire); continue; } // Try moving the head index forward. match self.head.index.compare_exchange_weak( head, new_head, Ordering::SeqCst, Ordering::Acquire, ) { Ok(_) => unsafe { // If we've reached the end of the block, move to the next one. if offset + 1 == BLOCK_CAP { let next = (*block).wait_next(); let mut next_index = (new_head & !MARK_BIT).wrapping_add(1 << SHIFT); if !(*next).next.load(Ordering::Relaxed).is_null() { next_index |= MARK_BIT; } self.head.block.store(next, Ordering::Release); self.head.index.store(next_index, Ordering::Release); } token.list.block = block as *const u8; token.list.offset = offset; return true; }, Err(h) => { head = h; block = self.head.block.load(Ordering::Acquire); backoff.spin(); } } } } /// Reads a message from the channel. pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { if token.list.block.is_null() { // The channel is disconnected. return Err(()); } // Read the message. let block = token.list.block as *mut Block; let offset = token.list.offset; let slot = (*block).slots.get_unchecked(offset); slot.wait_write(); let msg = slot.msg.get().read().assume_init(); // Destroy the block if we've reached the end, or if another thread wanted to destroy but // couldn't because we were busy reading from the slot. if offset + 1 == BLOCK_CAP { Block::destroy(block, 0); } else if slot.state.fetch_or(READ, Ordering::AcqRel) & DESTROY != 0 { Block::destroy(block, offset + 1); } Ok(msg) } /// Attempts to send a message into the channel. pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError> { self.send(msg, None).map_err(|err| match err { SendTimeoutError::Disconnected(msg) => TrySendError::Disconnected(msg), SendTimeoutError::Timeout(_) => unreachable!(), }) } /// Sends a message into the channel. pub(crate) fn send( &self, msg: T, _deadline: Option, ) -> Result<(), SendTimeoutError> { let token = &mut Token::default(); assert!(self.start_send(token)); unsafe { self.write(token, msg) .map_err(SendTimeoutError::Disconnected) } } /// Attempts to receive a message without blocking. pub(crate) fn try_recv(&self) -> Result { let token = &mut Token::default(); if self.start_recv(token) { unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) } } else { Err(TryRecvError::Empty) } } /// Receives a message from the channel. pub(crate) fn recv(&self, deadline: Option) -> Result { let token = &mut Token::default(); loop { // Try receiving a message several times. let backoff = Backoff::new(); loop { if self.start_recv(token) { unsafe { return self.read(token).map_err(|_| RecvTimeoutError::Disconnected); } } if backoff.is_completed() { break; } else { backoff.snooze(); } } if let Some(d) = deadline { if Instant::now() >= d { return Err(RecvTimeoutError::Timeout); } } // Prepare for blocking until a sender wakes us up. Context::with(|cx| { let oper = Operation::hook(token); self.receivers.register(oper, cx); // Has the channel become ready just now? if !self.is_empty() || self.is_disconnected() { let _ = cx.try_select(Selected::Aborted); } // Block the current thread. let sel = cx.wait_until(deadline); match sel { Selected::Waiting => unreachable!(), Selected::Aborted | Selected::Disconnected => { self.receivers.unregister(oper).unwrap(); // If the channel was disconnected, we still have to check for remaining // messages. } Selected::Operation(_) => {} } }); } } /// Returns the current number of messages inside the channel. pub(crate) fn len(&self) -> usize { loop { // Load the tail index, then load the head index. let mut tail = self.tail.index.load(Ordering::SeqCst); let mut head = self.head.index.load(Ordering::SeqCst); // If the tail index didn't change, we've got consistent indices to work with. if self.tail.index.load(Ordering::SeqCst) == tail { // Erase the lower bits. tail &= !((1 << SHIFT) - 1); head &= !((1 << SHIFT) - 1); // Fix up indices if they fall onto block ends. if (tail >> SHIFT) & (LAP - 1) == LAP - 1 { tail = tail.wrapping_add(1 << SHIFT); } if (head >> SHIFT) & (LAP - 1) == LAP - 1 { head = head.wrapping_add(1 << SHIFT); } // Rotate indices so that head falls into the first block. let lap = (head >> SHIFT) / LAP; tail = tail.wrapping_sub((lap * LAP) << SHIFT); head = head.wrapping_sub((lap * LAP) << SHIFT); // Remove the lower bits. tail >>= SHIFT; head >>= SHIFT; // Return the difference minus the number of blocks between tail and head. return tail - head - tail / LAP; } } } /// Returns the capacity of the channel. pub(crate) fn capacity(&self) -> Option { None } /// Disconnects senders and wakes up all blocked receivers. /// /// Returns `true` if this call disconnected the channel. pub(crate) fn disconnect_senders(&self) -> bool { let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst); if tail & MARK_BIT == 0 { self.receivers.disconnect(); true } else { false } } /// Disconnects receivers. /// /// Returns `true` if this call disconnected the channel. pub(crate) fn disconnect_receivers(&self) -> bool { let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst); if tail & MARK_BIT == 0 { // If receivers are dropped first, discard all messages to free // memory eagerly. self.discard_all_messages(); true } else { false } } /// Discards all messages. /// /// This method should only be called when all receivers are dropped. fn discard_all_messages(&self) { let backoff = Backoff::new(); let mut tail = self.tail.index.load(Ordering::Acquire); loop { let offset = (tail >> SHIFT) % LAP; if offset != BLOCK_CAP { break; } // New updates to tail will be rejected by MARK_BIT and aborted unless it's // at boundary. We need to wait for the updates take affect otherwise there // can be memory leaks. backoff.snooze(); tail = self.tail.index.load(Ordering::Acquire); } let mut head = self.head.index.load(Ordering::Acquire); // The channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts // to initialize the first block before noticing that the receivers disconnected. Late allocations // will be deallocated by the sender in Drop let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel); // If we're going to be dropping messages we need to synchronize with initialization if head >> SHIFT != tail >> SHIFT { // The block can be null here only if a sender is in the process of initializing the // channel while another sender managed to send a message by inserting it into the // semi-initialized channel and advanced the tail. // In that case, just wait until it gets initialized. while block.is_null() { backoff.snooze(); block = self.head.block.load(Ordering::Acquire); } } unsafe { // Drop all messages between head and tail and deallocate the heap-allocated blocks. while head >> SHIFT != tail >> SHIFT { let offset = (head >> SHIFT) % LAP; if offset < BLOCK_CAP { // Drop the message in the slot. let slot = (*block).slots.get_unchecked(offset); slot.wait_write(); (*slot.msg.get()).assume_init_drop(); } else { (*block).wait_next(); // Deallocate the block and move to the next one. let next = (*block).next.load(Ordering::Acquire); drop(Box::from_raw(block)); block = next; } head = head.wrapping_add(1 << SHIFT); } // Deallocate the last remaining block. if !block.is_null() { drop(Box::from_raw(block)); } } head &= !MARK_BIT; self.head.index.store(head, Ordering::Release); } /// Returns `true` if the channel is disconnected. pub(crate) fn is_disconnected(&self) -> bool { self.tail.index.load(Ordering::SeqCst) & MARK_BIT != 0 } /// Returns `true` if the channel is empty. pub(crate) fn is_empty(&self) -> bool { let head = self.head.index.load(Ordering::SeqCst); let tail = self.tail.index.load(Ordering::SeqCst); head >> SHIFT == tail >> SHIFT } /// Returns `true` if the channel is full. pub(crate) fn is_full(&self) -> bool { false } } impl Drop for Channel { fn drop(&mut self) { let mut head = *self.head.index.get_mut(); let mut tail = *self.tail.index.get_mut(); let mut block = *self.head.block.get_mut(); // Erase the lower bits. head &= !((1 << SHIFT) - 1); tail &= !((1 << SHIFT) - 1); unsafe { // Drop all messages between head and tail and deallocate the heap-allocated blocks. while head != tail { let offset = (head >> SHIFT) % LAP; if offset < BLOCK_CAP { // Drop the message in the slot. let slot = (*block).slots.get_unchecked(offset); (*slot.msg.get()).assume_init_drop(); } else { // Deallocate the block and move to the next one. let next = *(*block).next.get_mut(); drop(Box::from_raw(block)); block = next; } head = head.wrapping_add(1 << SHIFT); } // Deallocate the last remaining block. if !block.is_null() { drop(Box::from_raw(block)); } } } } /// Receiver handle to a channel. pub(crate) struct Receiver<'a, T>(&'a Channel); /// Sender handle to a channel. pub(crate) struct Sender<'a, T>(&'a Channel); impl SelectHandle for Receiver<'_, T> { fn try_select(&self, token: &mut Token) -> bool { self.0.start_recv(token) } fn deadline(&self) -> Option { None } fn register(&self, oper: Operation, cx: &Context) -> bool { self.0.receivers.register(oper, cx); self.is_ready() } fn unregister(&self, oper: Operation) { self.0.receivers.unregister(oper); } fn accept(&self, token: &mut Token, _cx: &Context) -> bool { self.try_select(token) } fn is_ready(&self) -> bool { !self.0.is_empty() || self.0.is_disconnected() } fn watch(&self, oper: Operation, cx: &Context) -> bool { self.0.receivers.watch(oper, cx); self.is_ready() } fn unwatch(&self, oper: Operation) { self.0.receivers.unwatch(oper); } } impl SelectHandle for Sender<'_, T> { fn try_select(&self, token: &mut Token) -> bool { self.0.start_send(token) } fn deadline(&self) -> Option { None } fn register(&self, _oper: Operation, _cx: &Context) -> bool { self.is_ready() } fn unregister(&self, _oper: Operation) {} fn accept(&self, token: &mut Token, _cx: &Context) -> bool { self.try_select(token) } fn is_ready(&self) -> bool { true } fn watch(&self, _oper: Operation, _cx: &Context) -> bool { self.is_ready() } fn unwatch(&self, _oper: Operation) {} } crossbeam-channel-0.5.14/src/flavors/mod.rs000064400000000000000000000010401046102023000166620ustar 00000000000000//! Channel flavors. //! //! There are six flavors: //! //! 1. `at` - Channel that delivers a message after a certain amount of time. //! 2. `array` - Bounded channel based on a preallocated array. //! 3. `list` - Unbounded channel implemented as a linked list. //! 4. `never` - Channel that never delivers messages. //! 5. `tick` - Channel that delivers messages periodically. //! 6. `zero` - Zero-capacity channel. pub(crate) mod array; pub(crate) mod at; pub(crate) mod list; pub(crate) mod never; pub(crate) mod tick; pub(crate) mod zero; crossbeam-channel-0.5.14/src/flavors/never.rs000064400000000000000000000046631046102023000172400ustar 00000000000000//! Channel that never delivers messages. //! //! Messages cannot be sent into this kind of channel. use std::marker::PhantomData; use std::time::Instant; use crate::context::Context; use crate::err::{RecvTimeoutError, TryRecvError}; use crate::select::{Operation, SelectHandle, Token}; use crate::utils; /// This flavor doesn't need a token. pub(crate) type NeverToken = (); /// Channel that never delivers messages. pub(crate) struct Channel { _marker: PhantomData, } impl Channel { /// Creates a channel that never delivers messages. #[inline] pub(crate) fn new() -> Self { Channel { _marker: PhantomData, } } /// Attempts to receive a message without blocking. #[inline] pub(crate) fn try_recv(&self) -> Result { Err(TryRecvError::Empty) } /// Receives a message from the channel. #[inline] pub(crate) fn recv(&self, deadline: Option) -> Result { utils::sleep_until(deadline); Err(RecvTimeoutError::Timeout) } /// Reads a message from the channel. #[inline] pub(crate) unsafe fn read(&self, _token: &mut Token) -> Result { Err(()) } /// Returns `true` if the channel is empty. #[inline] pub(crate) fn is_empty(&self) -> bool { true } /// Returns `true` if the channel is full. #[inline] pub(crate) fn is_full(&self) -> bool { true } /// Returns the number of messages in the channel. #[inline] pub(crate) fn len(&self) -> usize { 0 } /// Returns the capacity of the channel. #[inline] pub(crate) fn capacity(&self) -> Option { Some(0) } } impl SelectHandle for Channel { #[inline] fn try_select(&self, _token: &mut Token) -> bool { false } #[inline] fn deadline(&self) -> Option { None } #[inline] fn register(&self, _oper: Operation, _cx: &Context) -> bool { self.is_ready() } #[inline] fn unregister(&self, _oper: Operation) {} #[inline] fn accept(&self, token: &mut Token, _cx: &Context) -> bool { self.try_select(token) } #[inline] fn is_ready(&self) -> bool { false } #[inline] fn watch(&self, _oper: Operation, _cx: &Context) -> bool { self.is_ready() } #[inline] fn unwatch(&self, _oper: Operation) {} } crossbeam-channel-0.5.14/src/flavors/tick.rs000064400000000000000000000104151046102023000170430ustar 00000000000000//! Channel that delivers messages periodically. //! //! Messages cannot be sent into this kind of channel; they are materialized on demand. use std::thread; use std::time::{Duration, Instant}; use crossbeam_utils::atomic::AtomicCell; use crate::context::Context; use crate::err::{RecvTimeoutError, TryRecvError}; use crate::select::{Operation, SelectHandle, Token}; /// Result of a receive operation. pub(crate) type TickToken = Option; /// Channel that delivers messages periodically. pub(crate) struct Channel { /// The instant at which the next message will be delivered. delivery_time: AtomicCell, /// The time interval in which messages get delivered. duration: Duration, } impl Channel { /// Creates a channel that delivers messages periodically. #[inline] pub(crate) fn new(delivery_time: Instant, dur: Duration) -> Self { Channel { delivery_time: AtomicCell::new(delivery_time), duration: dur, } } /// Attempts to receive a message without blocking. #[inline] pub(crate) fn try_recv(&self) -> Result { loop { let now = Instant::now(); let delivery_time = self.delivery_time.load(); if now < delivery_time { return Err(TryRecvError::Empty); } if self .delivery_time .compare_exchange(delivery_time, now + self.duration) .is_ok() { return Ok(delivery_time); } } } /// Receives a message from the channel. #[inline] pub(crate) fn recv(&self, deadline: Option) -> Result { loop { let delivery_time = self.delivery_time.load(); let now = Instant::now(); if let Some(d) = deadline { if d < delivery_time { if now < d { thread::sleep(d - now); } return Err(RecvTimeoutError::Timeout); } } if self .delivery_time .compare_exchange(delivery_time, delivery_time.max(now) + self.duration) .is_ok() { if now < delivery_time { thread::sleep(delivery_time - now); } return Ok(delivery_time); } } } /// Reads a message from the channel. #[inline] pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { token.tick.ok_or(()) } /// Returns `true` if the channel is empty. #[inline] pub(crate) fn is_empty(&self) -> bool { Instant::now() < self.delivery_time.load() } /// Returns `true` if the channel is full. #[inline] pub(crate) fn is_full(&self) -> bool { !self.is_empty() } /// Returns the number of messages in the channel. #[inline] pub(crate) fn len(&self) -> usize { if self.is_empty() { 0 } else { 1 } } /// Returns the capacity of the channel. #[inline] pub(crate) fn capacity(&self) -> Option { Some(1) } } impl SelectHandle for Channel { #[inline] fn try_select(&self, token: &mut Token) -> bool { match self.try_recv() { Ok(msg) => { token.tick = Some(msg); true } Err(TryRecvError::Disconnected) => { token.tick = None; true } Err(TryRecvError::Empty) => false, } } #[inline] fn deadline(&self) -> Option { Some(self.delivery_time.load()) } #[inline] fn register(&self, _oper: Operation, _cx: &Context) -> bool { self.is_ready() } #[inline] fn unregister(&self, _oper: Operation) {} #[inline] fn accept(&self, token: &mut Token, _cx: &Context) -> bool { self.try_select(token) } #[inline] fn is_ready(&self) -> bool { !self.is_empty() } #[inline] fn watch(&self, _oper: Operation, _cx: &Context) -> bool { self.is_ready() } #[inline] fn unwatch(&self, _oper: Operation) {} } crossbeam-channel-0.5.14/src/flavors/zero.rs000064400000000000000000000361771046102023000171050ustar 00000000000000//! Zero-capacity channel. //! //! This kind of channel is also known as *rendezvous* channel. use std::boxed::Box; use std::cell::UnsafeCell; use std::marker::PhantomData; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; use std::time::Instant; use std::{fmt, ptr}; use crossbeam_utils::Backoff; use crate::context::Context; use crate::err::{RecvTimeoutError, SendTimeoutError, TryRecvError, TrySendError}; use crate::select::{Operation, SelectHandle, Selected, Token}; use crate::waker::Waker; /// A pointer to a packet. pub(crate) struct ZeroToken(*mut ()); impl Default for ZeroToken { fn default() -> Self { Self(ptr::null_mut()) } } impl fmt::Debug for ZeroToken { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&(self.0 as usize), f) } } /// A slot for passing one message from a sender to a receiver. struct Packet { /// Equals `true` if the packet is allocated on the stack. on_stack: bool, /// Equals `true` once the packet is ready for reading or writing. ready: AtomicBool, /// The message. msg: UnsafeCell>, } impl Packet { /// Creates an empty packet on the stack. fn empty_on_stack() -> Packet { Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(None), } } /// Creates an empty packet on the heap. fn empty_on_heap() -> Box> { Box::new(Packet { on_stack: false, ready: AtomicBool::new(false), msg: UnsafeCell::new(None), }) } /// Creates a packet on the stack, containing a message. fn message_on_stack(msg: T) -> Packet { Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(Some(msg)), } } /// Waits until the packet becomes ready for reading or writing. fn wait_ready(&self) { let backoff = Backoff::new(); while !self.ready.load(Ordering::Acquire) { backoff.snooze(); } } } /// Inner representation of a zero-capacity channel. struct Inner { /// Senders waiting to pair up with a receive operation. senders: Waker, /// Receivers waiting to pair up with a send operation. receivers: Waker, /// Equals `true` when the channel is disconnected. is_disconnected: bool, } /// Zero-capacity channel. pub(crate) struct Channel { /// Inner representation of the channel. inner: Mutex, /// Indicates that dropping a `Channel` may drop values of type `T`. _marker: PhantomData, } impl Channel { /// Constructs a new zero-capacity channel. pub(crate) fn new() -> Self { Channel { inner: Mutex::new(Inner { senders: Waker::new(), receivers: Waker::new(), is_disconnected: false, }), _marker: PhantomData, } } /// Returns a receiver handle to the channel. pub(crate) fn receiver(&self) -> Receiver<'_, T> { Receiver(self) } /// Returns a sender handle to the channel. pub(crate) fn sender(&self) -> Sender<'_, T> { Sender(self) } /// Attempts to reserve a slot for sending a message. fn start_send(&self, token: &mut Token) -> bool { let mut inner = self.inner.lock().unwrap(); // If there's a waiting receiver, pair up with it. if let Some(operation) = inner.receivers.try_select() { token.zero.0 = operation.packet; true } else if inner.is_disconnected { token.zero.0 = ptr::null_mut(); true } else { false } } /// Writes a message into the packet. pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> { // If there is no packet, the channel is disconnected. if token.zero.0.is_null() { return Err(msg); } let packet = &*(token.zero.0 as *const Packet); packet.msg.get().write(Some(msg)); packet.ready.store(true, Ordering::Release); Ok(()) } /// Attempts to pair up with a sender. fn start_recv(&self, token: &mut Token) -> bool { let mut inner = self.inner.lock().unwrap(); // If there's a waiting sender, pair up with it. if let Some(operation) = inner.senders.try_select() { token.zero.0 = operation.packet; true } else if inner.is_disconnected { token.zero.0 = ptr::null_mut(); true } else { false } } /// Reads a message from the packet. pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { // If there is no packet, the channel is disconnected. if token.zero.0.is_null() { return Err(()); } let packet = &*(token.zero.0 as *const Packet); if packet.on_stack { // The message has been in the packet from the beginning, so there is no need to wait // for it. However, after reading the message, we need to set `ready` to `true` in // order to signal that the packet can be destroyed. let msg = packet.msg.get().replace(None).unwrap(); packet.ready.store(true, Ordering::Release); Ok(msg) } else { // Wait until the message becomes available, then read it and destroy the // heap-allocated packet. packet.wait_ready(); let msg = packet.msg.get().replace(None).unwrap(); drop(Box::from_raw(token.zero.0.cast::>())); Ok(msg) } } /// Attempts to send a message into the channel. pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError> { let token = &mut Token::default(); let mut inner = self.inner.lock().unwrap(); // If there's a waiting receiver, pair up with it. if let Some(operation) = inner.receivers.try_select() { token.zero.0 = operation.packet; drop(inner); unsafe { self.write(token, msg).ok().unwrap(); } Ok(()) } else if inner.is_disconnected { Err(TrySendError::Disconnected(msg)) } else { Err(TrySendError::Full(msg)) } } /// Sends a message into the channel. pub(crate) fn send( &self, msg: T, deadline: Option, ) -> Result<(), SendTimeoutError> { let token = &mut Token::default(); let mut inner = self.inner.lock().unwrap(); // If there's a waiting receiver, pair up with it. if let Some(operation) = inner.receivers.try_select() { token.zero.0 = operation.packet; drop(inner); unsafe { self.write(token, msg).ok().unwrap(); } return Ok(()); } if inner.is_disconnected { return Err(SendTimeoutError::Disconnected(msg)); } Context::with(|cx| { // Prepare for blocking until a receiver wakes us up. let oper = Operation::hook(token); let mut packet = Packet::::message_on_stack(msg); inner .senders .register_with_packet(oper, &mut packet as *mut Packet as *mut (), cx); inner.receivers.notify(); drop(inner); // Block the current thread. let sel = cx.wait_until(deadline); match sel { Selected::Waiting => unreachable!(), Selected::Aborted => { self.inner.lock().unwrap().senders.unregister(oper).unwrap(); let msg = unsafe { packet.msg.get().replace(None).unwrap() }; Err(SendTimeoutError::Timeout(msg)) } Selected::Disconnected => { self.inner.lock().unwrap().senders.unregister(oper).unwrap(); let msg = unsafe { packet.msg.get().replace(None).unwrap() }; Err(SendTimeoutError::Disconnected(msg)) } Selected::Operation(_) => { // Wait until the message is read, then drop the packet. packet.wait_ready(); Ok(()) } } }) } /// Attempts to receive a message without blocking. pub(crate) fn try_recv(&self) -> Result { let token = &mut Token::default(); let mut inner = self.inner.lock().unwrap(); // If there's a waiting sender, pair up with it. if let Some(operation) = inner.senders.try_select() { token.zero.0 = operation.packet; drop(inner); unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) } } else if inner.is_disconnected { Err(TryRecvError::Disconnected) } else { Err(TryRecvError::Empty) } } /// Receives a message from the channel. pub(crate) fn recv(&self, deadline: Option) -> Result { let token = &mut Token::default(); let mut inner = self.inner.lock().unwrap(); // If there's a waiting sender, pair up with it. if let Some(operation) = inner.senders.try_select() { token.zero.0 = operation.packet; drop(inner); unsafe { return self.read(token).map_err(|_| RecvTimeoutError::Disconnected); } } if inner.is_disconnected { return Err(RecvTimeoutError::Disconnected); } Context::with(|cx| { // Prepare for blocking until a sender wakes us up. let oper = Operation::hook(token); let mut packet = Packet::::empty_on_stack(); inner.receivers.register_with_packet( oper, &mut packet as *mut Packet as *mut (), cx, ); inner.senders.notify(); drop(inner); // Block the current thread. let sel = cx.wait_until(deadline); match sel { Selected::Waiting => unreachable!(), Selected::Aborted => { self.inner .lock() .unwrap() .receivers .unregister(oper) .unwrap(); Err(RecvTimeoutError::Timeout) } Selected::Disconnected => { self.inner .lock() .unwrap() .receivers .unregister(oper) .unwrap(); Err(RecvTimeoutError::Disconnected) } Selected::Operation(_) => { // Wait until the message is provided, then read it. packet.wait_ready(); unsafe { Ok(packet.msg.get().replace(None).unwrap()) } } } }) } /// Disconnects the channel and wakes up all blocked senders and receivers. /// /// Returns `true` if this call disconnected the channel. pub(crate) fn disconnect(&self) -> bool { let mut inner = self.inner.lock().unwrap(); if !inner.is_disconnected { inner.is_disconnected = true; inner.senders.disconnect(); inner.receivers.disconnect(); true } else { false } } /// Returns the current number of messages inside the channel. pub(crate) fn len(&self) -> usize { 0 } /// Returns the capacity of the channel. pub(crate) fn capacity(&self) -> Option { Some(0) } /// Returns `true` if the channel is empty. pub(crate) fn is_empty(&self) -> bool { true } /// Returns `true` if the channel is full. pub(crate) fn is_full(&self) -> bool { true } } /// Receiver handle to a channel. pub(crate) struct Receiver<'a, T>(&'a Channel); /// Sender handle to a channel. pub(crate) struct Sender<'a, T>(&'a Channel); impl SelectHandle for Receiver<'_, T> { fn try_select(&self, token: &mut Token) -> bool { self.0.start_recv(token) } fn deadline(&self) -> Option { None } fn register(&self, oper: Operation, cx: &Context) -> bool { let packet = Box::into_raw(Packet::::empty_on_heap()); let mut inner = self.0.inner.lock().unwrap(); inner .receivers .register_with_packet(oper, packet.cast::<()>(), cx); inner.senders.notify(); inner.senders.can_select() || inner.is_disconnected } fn unregister(&self, oper: Operation) { if let Some(operation) = self.0.inner.lock().unwrap().receivers.unregister(oper) { unsafe { drop(Box::from_raw(operation.packet.cast::>())); } } } fn accept(&self, token: &mut Token, cx: &Context) -> bool { token.zero.0 = cx.wait_packet(); true } fn is_ready(&self) -> bool { let inner = self.0.inner.lock().unwrap(); inner.senders.can_select() || inner.is_disconnected } fn watch(&self, oper: Operation, cx: &Context) -> bool { let mut inner = self.0.inner.lock().unwrap(); inner.receivers.watch(oper, cx); inner.senders.can_select() || inner.is_disconnected } fn unwatch(&self, oper: Operation) { let mut inner = self.0.inner.lock().unwrap(); inner.receivers.unwatch(oper); } } impl SelectHandle for Sender<'_, T> { fn try_select(&self, token: &mut Token) -> bool { self.0.start_send(token) } fn deadline(&self) -> Option { None } fn register(&self, oper: Operation, cx: &Context) -> bool { let packet = Box::into_raw(Packet::::empty_on_heap()); let mut inner = self.0.inner.lock().unwrap(); inner .senders .register_with_packet(oper, packet.cast::<()>(), cx); inner.receivers.notify(); inner.receivers.can_select() || inner.is_disconnected } fn unregister(&self, oper: Operation) { if let Some(operation) = self.0.inner.lock().unwrap().senders.unregister(oper) { unsafe { drop(Box::from_raw(operation.packet.cast::>())); } } } fn accept(&self, token: &mut Token, cx: &Context) -> bool { token.zero.0 = cx.wait_packet(); true } fn is_ready(&self) -> bool { let inner = self.0.inner.lock().unwrap(); inner.receivers.can_select() || inner.is_disconnected } fn watch(&self, oper: Operation, cx: &Context) -> bool { let mut inner = self.0.inner.lock().unwrap(); inner.senders.watch(oper, cx); inner.receivers.can_select() || inner.is_disconnected } fn unwatch(&self, oper: Operation) { let mut inner = self.0.inner.lock().unwrap(); inner.senders.unwatch(oper); } } crossbeam-channel-0.5.14/src/lib.rs000064400000000000000000000251361046102023000152110ustar 00000000000000//! Multi-producer multi-consumer channels for message passing. //! //! This crate is an alternative to [`std::sync::mpsc`] with more features and better performance. //! //! # Hello, world! //! //! ``` //! use crossbeam_channel::unbounded; //! //! // Create a channel of unbounded capacity. //! let (s, r) = unbounded(); //! //! // Send a message into the channel. //! s.send("Hello, world!").unwrap(); //! //! // Receive the message from the channel. //! assert_eq!(r.recv(), Ok("Hello, world!")); //! ``` //! //! # Channel types //! //! Channels can be created using two functions: //! //! * [`bounded`] creates a channel of bounded capacity, i.e. there is a limit to how many messages //! it can hold at a time. //! //! * [`unbounded`] creates a channel of unbounded capacity, i.e. it can hold any number of //! messages at a time. //! //! Both functions return a [`Sender`] and a [`Receiver`], which represent the two opposite sides //! of a channel. //! //! Creating a bounded channel: //! //! ``` //! use crossbeam_channel::bounded; //! //! // Create a channel that can hold at most 5 messages at a time. //! let (s, r) = bounded(5); //! //! // Can send only 5 messages without blocking. //! for i in 0..5 { //! s.send(i).unwrap(); //! } //! //! // Another call to `send` would block because the channel is full. //! // s.send(5).unwrap(); //! ``` //! //! Creating an unbounded channel: //! //! ``` //! use crossbeam_channel::unbounded; //! //! // Create an unbounded channel. //! let (s, r) = unbounded(); //! //! // Can send any number of messages into the channel without blocking. //! for i in 0..1000 { //! s.send(i).unwrap(); //! } //! ``` //! //! A special case is zero-capacity channel, which cannot hold any messages. Instead, send and //! receive operations must appear at the same time in order to pair up and pass the message over: //! //! ``` //! use std::thread; //! use crossbeam_channel::bounded; //! //! // Create a zero-capacity channel. //! let (s, r) = bounded(0); //! //! // Sending blocks until a receive operation appears on the other side. //! thread::spawn(move || s.send("Hi!").unwrap()); //! //! // Receiving blocks until a send operation appears on the other side. //! assert_eq!(r.recv(), Ok("Hi!")); //! ``` //! //! # Sharing channels //! //! Senders and receivers can be cloned and sent to other threads: //! //! ``` //! use std::thread; //! use crossbeam_channel::bounded; //! //! let (s1, r1) = bounded(0); //! let (s2, r2) = (s1.clone(), r1.clone()); //! //! // Spawn a thread that receives a message and then sends one. //! thread::spawn(move || { //! r2.recv().unwrap(); //! s2.send(2).unwrap(); //! }); //! //! // Send a message and then receive one. //! s1.send(1).unwrap(); //! r1.recv().unwrap(); //! ``` //! //! Note that cloning only creates a new handle to the same sending or receiving side. It does not //! create a separate stream of messages in any way: //! //! ``` //! use crossbeam_channel::unbounded; //! //! let (s1, r1) = unbounded(); //! let (s2, r2) = (s1.clone(), r1.clone()); //! let (s3, r3) = (s2.clone(), r2.clone()); //! //! s1.send(10).unwrap(); //! s2.send(20).unwrap(); //! s3.send(30).unwrap(); //! //! assert_eq!(r3.recv(), Ok(10)); //! assert_eq!(r1.recv(), Ok(20)); //! assert_eq!(r2.recv(), Ok(30)); //! ``` //! //! It's also possible to share senders and receivers by reference: //! //! ``` //! use crossbeam_channel::bounded; //! use crossbeam_utils::thread::scope; //! //! let (s, r) = bounded(0); //! //! scope(|scope| { //! // Spawn a thread that receives a message and then sends one. //! scope.spawn(|_| { //! r.recv().unwrap(); //! s.send(2).unwrap(); //! }); //! //! // Send a message and then receive one. //! s.send(1).unwrap(); //! r.recv().unwrap(); //! }).unwrap(); //! ``` //! //! # Disconnection //! //! When all senders or all receivers associated with a channel get dropped, the channel becomes //! disconnected. No more messages can be sent, but any remaining messages can still be received. //! Send and receive operations on a disconnected channel never block. //! //! ``` //! use crossbeam_channel::{unbounded, RecvError}; //! //! let (s, r) = unbounded(); //! s.send(1).unwrap(); //! s.send(2).unwrap(); //! s.send(3).unwrap(); //! //! // The only sender is dropped, disconnecting the channel. //! drop(s); //! //! // The remaining messages can be received. //! assert_eq!(r.recv(), Ok(1)); //! assert_eq!(r.recv(), Ok(2)); //! assert_eq!(r.recv(), Ok(3)); //! //! // There are no more messages in the channel. //! assert!(r.is_empty()); //! //! // Note that calling `r.recv()` does not block. //! // Instead, `Err(RecvError)` is returned immediately. //! assert_eq!(r.recv(), Err(RecvError)); //! ``` //! //! # Blocking operations //! //! Send and receive operations come in three flavors: //! //! * Non-blocking (returns immediately with success or failure). //! * Blocking (waits until the operation succeeds or the channel becomes disconnected). //! * Blocking with a timeout (blocks only for a certain duration of time). //! //! A simple example showing the difference between non-blocking and blocking operations: //! //! ``` //! use crossbeam_channel::{bounded, RecvError, TryRecvError}; //! //! let (s, r) = bounded(1); //! //! // Send a message into the channel. //! s.send("foo").unwrap(); //! //! // This call would block because the channel is full. //! // s.send("bar").unwrap(); //! //! // Receive the message. //! assert_eq!(r.recv(), Ok("foo")); //! //! // This call would block because the channel is empty. //! // r.recv(); //! //! // Try receiving a message without blocking. //! assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); //! //! // Disconnect the channel. //! drop(s); //! //! // This call doesn't block because the channel is now disconnected. //! assert_eq!(r.recv(), Err(RecvError)); //! ``` //! //! # Iteration //! //! Receivers can be used as iterators. For example, method [`iter`] creates an iterator that //! receives messages until the channel becomes empty and disconnected. Note that iteration may //! block waiting for next message to arrive. //! //! ``` //! use std::thread; //! use crossbeam_channel::unbounded; //! //! let (s, r) = unbounded(); //! //! thread::spawn(move || { //! s.send(1).unwrap(); //! s.send(2).unwrap(); //! s.send(3).unwrap(); //! drop(s); // Disconnect the channel. //! }); //! //! // Collect all messages from the channel. //! // Note that the call to `collect` blocks until the sender is dropped. //! let v: Vec<_> = r.iter().collect(); //! //! assert_eq!(v, [1, 2, 3]); //! ``` //! //! A non-blocking iterator can be created using [`try_iter`], which receives all available //! messages without blocking: //! //! ``` //! use crossbeam_channel::unbounded; //! //! let (s, r) = unbounded(); //! s.send(1).unwrap(); //! s.send(2).unwrap(); //! s.send(3).unwrap(); //! // No need to drop the sender. //! //! // Receive all messages currently in the channel. //! let v: Vec<_> = r.try_iter().collect(); //! //! assert_eq!(v, [1, 2, 3]); //! ``` //! //! # Selection //! //! The [`select!`] macro allows you to define a set of channel operations, wait until any one of //! them becomes ready, and finally execute it. If multiple operations are ready at the same time, //! a random one among them is selected. //! //! It is also possible to define a `default` case that gets executed if none of the operations are //! ready, either right away or for a certain duration of time. //! //! An operation is considered to be ready if it doesn't have to block. Note that it is ready even //! when it will simply return an error because the channel is disconnected. //! //! An example of receiving a message from two channels: //! //! ``` //! use std::thread; //! use std::time::Duration; //! use crossbeam_channel::{select, unbounded}; //! //! let (s1, r1) = unbounded(); //! let (s2, r2) = unbounded(); //! //! thread::spawn(move || s1.send(10).unwrap()); //! thread::spawn(move || s2.send(20).unwrap()); //! //! // At most one of these two receive operations will be executed. //! select! { //! recv(r1) -> msg => assert_eq!(msg, Ok(10)), //! recv(r2) -> msg => assert_eq!(msg, Ok(20)), //! default(Duration::from_secs(1)) => println!("timed out"), //! } //! ``` //! //! If you need to select over a dynamically created list of channel operations, use [`Select`] //! instead. The [`select!`] macro is just a convenience wrapper around [`Select`]. //! //! # Extra channels //! //! Three functions can create special kinds of channels, all of which return just a [`Receiver`] //! handle: //! //! * [`after`] creates a channel that delivers a single message after a certain duration of time. //! * [`tick`] creates a channel that delivers messages periodically. //! * [`never`](never()) creates a channel that never delivers messages. //! //! These channels are very efficient because messages get lazily generated on receive operations. //! //! An example that prints elapsed time every 50 milliseconds for the duration of 1 second: //! //! ``` //! use std::time::{Duration, Instant}; //! use crossbeam_channel::{after, select, tick}; //! //! let start = Instant::now(); //! let ticker = tick(Duration::from_millis(50)); //! let timeout = after(Duration::from_secs(1)); //! //! loop { //! select! { //! recv(ticker) -> _ => println!("elapsed: {:?}", start.elapsed()), //! recv(timeout) -> _ => break, //! } //! } //! ``` //! //! [`send`]: Sender::send //! [`recv`]: Receiver::recv //! [`iter`]: Receiver::iter //! [`try_iter`]: Receiver::try_iter #![no_std] #![doc(test( no_crate_inject, attr( deny(warnings, rust_2018_idioms), allow(dead_code, unused_assignments, unused_variables) ) ))] #![warn( missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub )] #[cfg(feature = "std")] extern crate std; #[cfg(feature = "std")] mod channel; #[cfg(feature = "std")] mod context; #[cfg(feature = "std")] mod counter; #[cfg(feature = "std")] mod err; #[cfg(feature = "std")] mod flavors; #[cfg(feature = "std")] mod select; #[cfg(feature = "std")] mod select_macro; #[cfg(feature = "std")] mod utils; #[cfg(feature = "std")] mod waker; /// Crate internals used by the `select!` macro. #[doc(hidden)] #[cfg(feature = "std")] pub mod internal { pub use crate::select::{select, select_timeout, try_select, SelectHandle}; } #[cfg(feature = "std")] pub use crate::{ channel::{ after, at, bounded, never, tick, unbounded, IntoIter, Iter, Receiver, Sender, TryIter, }, err::{ ReadyTimeoutError, RecvError, RecvTimeoutError, SelectTimeoutError, SendError, SendTimeoutError, TryReadyError, TryRecvError, TrySelectError, TrySendError, }, select::{Select, SelectedOperation}, }; crossbeam-channel-0.5.14/src/select.rs000064400000000000000000001245721046102023000157260ustar 00000000000000//! Interface to the select mechanism. use std::fmt; use std::marker::PhantomData; use std::mem; use std::time::{Duration, Instant}; use std::vec::Vec; use crossbeam_utils::Backoff; use crate::channel::{self, Receiver, Sender}; use crate::context::Context; use crate::err::{ReadyTimeoutError, TryReadyError}; use crate::err::{RecvError, SendError}; use crate::err::{SelectTimeoutError, TrySelectError}; use crate::flavors; use crate::utils; /// Temporary data that gets initialized during select or a blocking operation, and is consumed by /// `read` or `write`. /// /// Each field contains data associated with a specific channel flavor. // This is a private API that is used by the select macro. #[derive(Debug, Default)] pub struct Token { pub(crate) at: flavors::at::AtToken, pub(crate) array: flavors::array::ArrayToken, pub(crate) list: flavors::list::ListToken, #[allow(dead_code)] pub(crate) never: flavors::never::NeverToken, pub(crate) tick: flavors::tick::TickToken, pub(crate) zero: flavors::zero::ZeroToken, } /// Identifier associated with an operation by a specific thread on a specific channel. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Operation(usize); impl Operation { /// Creates an operation identifier from a mutable reference. /// /// This function essentially just turns the address of the reference into a number. The /// reference should point to a variable that is specific to the thread and the operation, /// and is alive for the entire duration of select or blocking operation. #[inline] pub fn hook(r: &mut T) -> Operation { let val = r as *mut T as usize; // Make sure that the pointer address doesn't equal the numerical representation of // `Selected::{Waiting, Aborted, Disconnected}`. assert!(val > 2); Operation(val) } } /// Current state of a select or a blocking operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Selected { /// Still waiting for an operation. Waiting, /// The attempt to block the current thread has been aborted. Aborted, /// An operation became ready because a channel is disconnected. Disconnected, /// An operation became ready because a message can be sent or received. Operation(Operation), } impl From for Selected { #[inline] fn from(val: usize) -> Selected { match val { 0 => Selected::Waiting, 1 => Selected::Aborted, 2 => Selected::Disconnected, oper => Selected::Operation(Operation(oper)), } } } impl Into for Selected { #[inline] fn into(self) -> usize { match self { Selected::Waiting => 0, Selected::Aborted => 1, Selected::Disconnected => 2, Selected::Operation(Operation(val)) => val, } } } /// A receiver or a sender that can participate in select. /// /// This is a handle that assists select in executing an operation, registration, deciding on the /// appropriate deadline for blocking, etc. // This is a private API (exposed inside crossbeam_channel::internal module) that is used by the select macro. pub trait SelectHandle { /// Attempts to select an operation and returns `true` on success. fn try_select(&self, token: &mut Token) -> bool; /// Returns a deadline for an operation, if there is one. fn deadline(&self) -> Option; /// Registers an operation for execution and returns `true` if it is now ready. fn register(&self, oper: Operation, cx: &Context) -> bool; /// Unregisters an operation for execution. fn unregister(&self, oper: Operation); /// Attempts to select an operation the thread got woken up for and returns `true` on success. fn accept(&self, token: &mut Token, cx: &Context) -> bool; /// Returns `true` if an operation can be executed without blocking. fn is_ready(&self) -> bool; /// Registers an operation for readiness notification and returns `true` if it is now ready. fn watch(&self, oper: Operation, cx: &Context) -> bool; /// Unregisters an operation for readiness notification. fn unwatch(&self, oper: Operation); } impl SelectHandle for &T { fn try_select(&self, token: &mut Token) -> bool { (**self).try_select(token) } fn deadline(&self) -> Option { (**self).deadline() } fn register(&self, oper: Operation, cx: &Context) -> bool { (**self).register(oper, cx) } fn unregister(&self, oper: Operation) { (**self).unregister(oper); } fn accept(&self, token: &mut Token, cx: &Context) -> bool { (**self).accept(token, cx) } fn is_ready(&self) -> bool { (**self).is_ready() } fn watch(&self, oper: Operation, cx: &Context) -> bool { (**self).watch(oper, cx) } fn unwatch(&self, oper: Operation) { (**self).unwatch(oper) } } /// Determines when a select operation should time out. #[derive(Clone, Copy, Eq, PartialEq)] enum Timeout { /// No blocking. Now, /// Block forever. Never, /// Time out after the time instant. At(Instant), } /// Runs until one of the operations is selected, potentially blocking the current thread. /// /// Successful receive operations will have to be followed up by `channel::read()` and successful /// send operations by `channel::write()`. fn run_select( handles: &mut [(&dyn SelectHandle, usize, *const u8)], timeout: Timeout, is_biased: bool, ) -> Option<(Token, usize, *const u8)> { if handles.is_empty() { // Wait until the timeout and return. match timeout { Timeout::Now => return None, Timeout::Never => { utils::sleep_until(None); unreachable!(); } Timeout::At(when) => { utils::sleep_until(Some(when)); return None; } } } if !is_biased { // Shuffle the operations for fairness. utils::shuffle(handles); } // Create a token, which serves as a temporary variable that gets initialized in this function // and is later used by a call to `channel::read()` or `channel::write()` that completes the // selected operation. let mut token = Token::default(); // Try selecting one of the operations without blocking. for &(handle, i, ptr) in handles.iter() { if handle.try_select(&mut token) { return Some((token, i, ptr)); } } loop { // Prepare for blocking. let res = Context::with(|cx| { let mut sel = Selected::Waiting; let mut registered_count = 0; let mut index_ready = None; if let Timeout::Now = timeout { cx.try_select(Selected::Aborted).unwrap(); } // Register all operations. for (handle, i, _) in handles.iter_mut() { registered_count += 1; // If registration returns `false`, that means the operation has just become ready. if handle.register(Operation::hook::<&dyn SelectHandle>(handle), cx) { // Try aborting select. sel = match cx.try_select(Selected::Aborted) { Ok(()) => { index_ready = Some(*i); Selected::Aborted } Err(s) => s, }; break; } // If another thread has already selected one of the operations, stop registration. sel = cx.selected(); if sel != Selected::Waiting { break; } } if sel == Selected::Waiting { // Check with each operation for how long we're allowed to block, and compute the // earliest deadline. let mut deadline: Option = match timeout { Timeout::Now => return None, Timeout::Never => None, Timeout::At(when) => Some(when), }; for &(handle, _, _) in handles.iter() { if let Some(x) = handle.deadline() { deadline = deadline.map(|y| x.min(y)).or(Some(x)); } } // Block the current thread. sel = cx.wait_until(deadline); } // Unregister all registered operations. for (handle, _, _) in handles.iter_mut().take(registered_count) { handle.unregister(Operation::hook::<&dyn SelectHandle>(handle)); } match sel { Selected::Waiting => unreachable!(), Selected::Aborted => { // If an operation became ready during registration, try selecting it. if let Some(index_ready) = index_ready { for &(handle, i, ptr) in handles.iter() { if i == index_ready && handle.try_select(&mut token) { return Some((i, ptr)); } } } } Selected::Disconnected => {} Selected::Operation(_) => { // Find the selected operation. for (handle, i, ptr) in handles.iter_mut() { // Is this the selected operation? if sel == Selected::Operation(Operation::hook::<&dyn SelectHandle>(handle)) { // Try selecting this operation. if handle.accept(&mut token, cx) { return Some((*i, *ptr)); } } } } } None }); // Return if an operation was selected. if let Some((i, ptr)) = res { return Some((token, i, ptr)); } // Try selecting one of the operations without blocking. for &(handle, i, ptr) in handles.iter() { if handle.try_select(&mut token) { return Some((token, i, ptr)); } } match timeout { Timeout::Now => return None, Timeout::Never => {} Timeout::At(when) => { if Instant::now() >= when { return None; } } } } } /// Runs until one of the operations becomes ready, potentially blocking the current thread. fn run_ready( handles: &mut [(&dyn SelectHandle, usize, *const u8)], timeout: Timeout, is_biased: bool, ) -> Option { if handles.is_empty() { // Wait until the timeout and return. match timeout { Timeout::Now => return None, Timeout::Never => { utils::sleep_until(None); unreachable!(); } Timeout::At(when) => { utils::sleep_until(Some(when)); return None; } } } if !is_biased { // Shuffle the operations for fairness. utils::shuffle(handles); } loop { let backoff = Backoff::new(); loop { // Check operations for readiness. for &(handle, i, _) in handles.iter() { if handle.is_ready() { return Some(i); } } if backoff.is_completed() { break; } else { backoff.snooze(); } } // Check for timeout. match timeout { Timeout::Now => return None, Timeout::Never => {} Timeout::At(when) => { if Instant::now() >= when { return None; } } } // Prepare for blocking. let res = Context::with(|cx| { let mut sel = Selected::Waiting; let mut registered_count = 0; // Begin watching all operations. for (handle, _, _) in handles.iter_mut() { registered_count += 1; let oper = Operation::hook::<&dyn SelectHandle>(handle); // If registration returns `false`, that means the operation has just become ready. if handle.watch(oper, cx) { sel = match cx.try_select(Selected::Operation(oper)) { Ok(()) => Selected::Operation(oper), Err(s) => s, }; break; } // If another thread has already chosen one of the operations, stop registration. sel = cx.selected(); if sel != Selected::Waiting { break; } } if sel == Selected::Waiting { // Check with each operation for how long we're allowed to block, and compute the // earliest deadline. let mut deadline: Option = match timeout { Timeout::Now => unreachable!(), Timeout::Never => None, Timeout::At(when) => Some(when), }; for &(handle, _, _) in handles.iter() { if let Some(x) = handle.deadline() { deadline = deadline.map(|y| x.min(y)).or(Some(x)); } } // Block the current thread. sel = cx.wait_until(deadline); } // Unwatch all operations. for (handle, _, _) in handles.iter_mut().take(registered_count) { handle.unwatch(Operation::hook::<&dyn SelectHandle>(handle)); } match sel { Selected::Waiting => unreachable!(), Selected::Aborted => {} Selected::Disconnected => {} Selected::Operation(_) => { for (handle, i, _) in handles.iter_mut() { let oper = Operation::hook::<&dyn SelectHandle>(handle); if sel == Selected::Operation(oper) { return Some(*i); } } } } None }); // Return if an operation became ready. if res.is_some() { return res; } } } /// Attempts to select one of the operations without blocking. // This is a private API (exposed inside crossbeam_channel::internal module) that is used by the select macro. #[inline] pub fn try_select<'a>( handles: &mut [(&'a dyn SelectHandle, usize, *const u8)], is_biased: bool, ) -> Result, TrySelectError> { match run_select(handles, Timeout::Now, is_biased) { None => Err(TrySelectError), Some((token, index, ptr)) => Ok(SelectedOperation { token, index, ptr, _marker: PhantomData, }), } } /// Blocks until one of the operations becomes ready and selects it. // This is a private API (exposed inside crossbeam_channel::internal module) that is used by the select macro. #[inline] pub fn select<'a>( handles: &mut [(&'a dyn SelectHandle, usize, *const u8)], is_biased: bool, ) -> SelectedOperation<'a> { if handles.is_empty() { panic!("no operations have been added to `Select`"); } let (token, index, ptr) = run_select(handles, Timeout::Never, is_biased).unwrap(); SelectedOperation { token, index, ptr, _marker: PhantomData, } } /// Blocks for a limited time until one of the operations becomes ready and selects it. // This is a private API (exposed inside crossbeam_channel::internal module) that is used by the select macro. #[inline] pub fn select_timeout<'a>( handles: &mut [(&'a dyn SelectHandle, usize, *const u8)], timeout: Duration, is_biased: bool, ) -> Result, SelectTimeoutError> { match Instant::now().checked_add(timeout) { Some(deadline) => select_deadline(handles, deadline, is_biased), None => Ok(select(handles, is_biased)), } } /// Blocks until a given deadline, or until one of the operations becomes ready and selects it. #[inline] pub(crate) fn select_deadline<'a>( handles: &mut [(&'a dyn SelectHandle, usize, *const u8)], deadline: Instant, is_biased: bool, ) -> Result, SelectTimeoutError> { match run_select(handles, Timeout::At(deadline), is_biased) { None => Err(SelectTimeoutError), Some((token, index, ptr)) => Ok(SelectedOperation { token, index, ptr, _marker: PhantomData, }), } } /// Selects from a set of channel operations. /// /// `Select` allows you to define a set of channel operations, wait until any one of them becomes /// ready, and finally execute it. If multiple operations are ready at the same time, a random one /// among them is selected. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready even /// when it will simply return an error because the channel is disconnected. /// /// The [`select!`] macro is a convenience wrapper around `Select`. However, it cannot select over a /// dynamically created list of channel operations. /// /// [`select!`]: crate::select! /// /// Once a list of operations has been built with `Select`, there are two different ways of /// proceeding: /// /// * Select an operation with [`try_select`], [`select`], or [`select_timeout`]. If successful, /// the returned selected operation has already begun and **must** be completed. If we don't /// complete it, a panic will occur. /// /// * Wait for an operation to become ready with [`try_ready`], [`ready`], or [`ready_timeout`]. If /// successful, we may attempt to execute the operation, but are not obliged to. In fact, it's /// possible for another thread to make the operation not ready just before we try executing it, /// so it's wise to use a retry loop. However, note that these methods might return with success /// spuriously, so it's a good idea to always double check if the operation is really ready. /// /// # Examples /// /// Use [`select`] to receive a message from a list of receivers: /// /// ``` /// use crossbeam_channel::{Receiver, RecvError, Select}; /// /// fn recv_multiple(rs: &[Receiver]) -> Result { /// // Build a list of operations. /// let mut sel = Select::new(); /// for r in rs { /// sel.recv(r); /// } /// /// // Complete the selected operation. /// let oper = sel.select(); /// let index = oper.index(); /// oper.recv(&rs[index]) /// } /// ``` /// /// Use [`ready`] to receive a message from a list of receivers: /// /// ``` /// use crossbeam_channel::{Receiver, RecvError, Select}; /// /// fn recv_multiple(rs: &[Receiver]) -> Result { /// // Build a list of operations. /// let mut sel = Select::new(); /// for r in rs { /// sel.recv(r); /// } /// /// loop { /// // Wait until a receive operation becomes ready and try executing it. /// let index = sel.ready(); /// let res = rs[index].try_recv(); /// /// // If the operation turns out not to be ready, retry. /// if let Err(e) = res { /// if e.is_empty() { /// continue; /// } /// } /// /// // Success! /// return res.map_err(|_| RecvError); /// } /// } /// ``` /// /// [`try_select`]: Select::try_select /// [`select`]: Select::select /// [`select_timeout`]: Select::select_timeout /// [`try_ready`]: Select::try_ready /// [`ready`]: Select::ready /// [`ready_timeout`]: Select::ready_timeout pub struct Select<'a> { /// A list of senders and receivers participating in selection. handles: Vec<(&'a dyn SelectHandle, usize, *const u8)>, /// The next index to assign to an operation. next_index: usize, /// Whether to use the index of handles as bias for selecting ready operations. biased: bool, } unsafe impl Send for Select<'_> {} unsafe impl Sync for Select<'_> {} impl<'a> Select<'a> { /// Creates an empty list of channel operations for selection. /// /// # Examples /// /// ``` /// use crossbeam_channel::Select; /// /// let mut sel = Select::new(); /// /// // The list of operations is empty, which means no operation can be selected. /// assert!(sel.try_select().is_err()); /// ``` pub fn new() -> Select<'a> { Select { handles: Vec::with_capacity(4), next_index: 0, biased: false, } } /// Creates an empty list of channel operations with biased selection. /// /// When multiple handles are ready, this will select the operation with the lowest index. /// /// # Examples /// /// ``` /// use crossbeam_channel::Select; /// /// let mut sel = Select::new_biased(); /// /// // The list of operations is empty, which means no operation can be selected. /// assert!(sel.try_select().is_err()); /// ``` pub fn new_biased() -> Self { Self { biased: true, ..Default::default() } } /// Adds a send operation. /// /// Returns the index of the added operation. /// /// # Examples /// /// ``` /// use crossbeam_channel::{unbounded, Select}; /// /// let (s, r) = unbounded::(); /// /// let mut sel = Select::new(); /// let index = sel.send(&s); /// ``` pub fn send(&mut self, s: &'a Sender) -> usize { let i = self.next_index; let ptr = s as *const Sender<_> as *const u8; self.handles.push((s, i, ptr)); self.next_index += 1; i } /// Adds a receive operation. /// /// Returns the index of the added operation. /// /// # Examples /// /// ``` /// use crossbeam_channel::{unbounded, Select}; /// /// let (s, r) = unbounded::(); /// /// let mut sel = Select::new(); /// let index = sel.recv(&r); /// ``` pub fn recv(&mut self, r: &'a Receiver) -> usize { let i = self.next_index; let ptr = r as *const Receiver<_> as *const u8; self.handles.push((r, i, ptr)); self.next_index += 1; i } /// Removes a previously added operation. /// /// This is useful when an operation is selected because the channel got disconnected and we /// want to try again to select a different operation instead. /// /// If new operations are added after removing some, the indices of removed operations will not /// be reused. /// /// # Panics /// /// An attempt to remove a non-existing or already removed operation will panic. /// /// # Examples /// /// ``` /// use crossbeam_channel::{unbounded, Select}; /// /// let (s1, r1) = unbounded::(); /// let (_, r2) = unbounded::(); /// /// let mut sel = Select::new(); /// let oper1 = sel.recv(&r1); /// let oper2 = sel.recv(&r2); /// /// // Both operations are initially ready, so a random one will be executed. /// let oper = sel.select(); /// assert_eq!(oper.index(), oper2); /// assert!(oper.recv(&r2).is_err()); /// sel.remove(oper2); /// /// s1.send(10).unwrap(); /// /// let oper = sel.select(); /// assert_eq!(oper.index(), oper1); /// assert_eq!(oper.recv(&r1), Ok(10)); /// ``` pub fn remove(&mut self, index: usize) { assert!( index < self.next_index, "index out of bounds; {} >= {}", index, self.next_index, ); let i = self .handles .iter() .enumerate() .find(|(_, (_, i, _))| *i == index) .expect("no operation with this index") .0; self.handles.swap_remove(i); } /// Attempts to select one of the operations without blocking. /// /// If an operation is ready, it is selected and returned. If multiple operations are ready at /// the same time, a random one among them is selected. If none of the operations are ready, an /// error is returned. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready /// even when it will simply return an error because the channel is disconnected. /// /// The selected operation must be completed with [`SelectedOperation::send`] /// or [`SelectedOperation::recv`]. /// /// # Examples /// /// ``` /// use crossbeam_channel::{unbounded, Select}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// s1.send(10).unwrap(); /// s2.send(20).unwrap(); /// /// let mut sel = Select::new(); /// let oper1 = sel.recv(&r1); /// let oper2 = sel.recv(&r2); /// /// // Both operations are initially ready, so a random one will be executed. /// let oper = sel.try_select(); /// match oper { /// Err(_) => panic!("both operations should be ready"), /// Ok(oper) => match oper.index() { /// i if i == oper1 => assert_eq!(oper.recv(&r1), Ok(10)), /// i if i == oper2 => assert_eq!(oper.recv(&r2), Ok(20)), /// _ => unreachable!(), /// } /// } /// ``` pub fn try_select(&mut self) -> Result, TrySelectError> { try_select(&mut self.handles, self.biased) } /// Blocks until one of the operations becomes ready and selects it. /// /// Once an operation becomes ready, it is selected and returned. If multiple operations are /// ready at the same time, a random one among them is selected. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready /// even when it will simply return an error because the channel is disconnected. /// /// The selected operation must be completed with [`SelectedOperation::send`] /// or [`SelectedOperation::recv`]. /// /// # Panics /// /// Panics if no operations have been added to `Select`. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{unbounded, Select}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || s2.send(20).unwrap()); /// /// let mut sel = Select::new(); /// let oper1 = sel.recv(&r1); /// let oper2 = sel.recv(&r2); /// /// // The second operation will be selected because it becomes ready first. /// let oper = sel.select(); /// match oper.index() { /// i if i == oper1 => assert_eq!(oper.recv(&r1), Ok(10)), /// i if i == oper2 => assert_eq!(oper.recv(&r2), Ok(20)), /// _ => unreachable!(), /// } /// ``` pub fn select(&mut self) -> SelectedOperation<'a> { select(&mut self.handles, self.biased) } /// Blocks for a limited time until one of the operations becomes ready and selects it. /// /// If an operation becomes ready, it is selected and returned. If multiple operations are /// ready at the same time, a random one among them is selected. If none of the operations /// become ready for the specified duration, an error is returned. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready /// even when it will simply return an error because the channel is disconnected. /// /// The selected operation must be completed with [`SelectedOperation::send`] /// or [`SelectedOperation::recv`]. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{unbounded, Select}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || s2.send(20).unwrap()); /// /// let mut sel = Select::new(); /// let oper1 = sel.recv(&r1); /// let oper2 = sel.recv(&r2); /// /// // The second operation will be selected because it becomes ready first. /// let oper = sel.select_timeout(Duration::from_millis(500)); /// match oper { /// Err(_) => panic!("should not have timed out"), /// Ok(oper) => match oper.index() { /// i if i == oper1 => assert_eq!(oper.recv(&r1), Ok(10)), /// i if i == oper2 => assert_eq!(oper.recv(&r2), Ok(20)), /// _ => unreachable!(), /// } /// } /// ``` pub fn select_timeout( &mut self, timeout: Duration, ) -> Result, SelectTimeoutError> { select_timeout(&mut self.handles, timeout, self.biased) } /// Blocks until a given deadline, or until one of the operations becomes ready and selects it. /// /// If an operation becomes ready, it is selected and returned. If multiple operations are /// ready at the same time, a random one among them is selected. If none of the operations /// become ready before the given deadline, an error is returned. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready /// even when it will simply return an error because the channel is disconnected. /// /// The selected operation must be completed with [`SelectedOperation::send`] /// or [`SelectedOperation::recv`]. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::{Instant, Duration}; /// use crossbeam_channel::{unbounded, Select}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || s2.send(20).unwrap()); /// /// let mut sel = Select::new(); /// let oper1 = sel.recv(&r1); /// let oper2 = sel.recv(&r2); /// /// let deadline = Instant::now() + Duration::from_millis(500); /// /// // The second operation will be selected because it becomes ready first. /// let oper = sel.select_deadline(deadline); /// match oper { /// Err(_) => panic!("should not have timed out"), /// Ok(oper) => match oper.index() { /// i if i == oper1 => assert_eq!(oper.recv(&r1), Ok(10)), /// i if i == oper2 => assert_eq!(oper.recv(&r2), Ok(20)), /// _ => unreachable!(), /// } /// } /// ``` pub fn select_deadline( &mut self, deadline: Instant, ) -> Result, SelectTimeoutError> { select_deadline(&mut self.handles, deadline, self.biased) } /// Attempts to find a ready operation without blocking. /// /// If an operation is ready, its index is returned. If multiple operations are ready at the /// same time, a random one among them is chosen. If none of the operations are ready, an error /// is returned. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready /// even when it will simply return an error because the channel is disconnected. /// /// Note that this method might return with success spuriously, so it's a good idea to always /// double check if the operation is really ready. /// /// # Examples /// /// ``` /// use crossbeam_channel::{unbounded, Select}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// s1.send(10).unwrap(); /// s2.send(20).unwrap(); /// /// let mut sel = Select::new(); /// let oper1 = sel.recv(&r1); /// let oper2 = sel.recv(&r2); /// /// // Both operations are initially ready, so a random one will be chosen. /// match sel.try_ready() { /// Err(_) => panic!("both operations should be ready"), /// Ok(i) if i == oper1 => assert_eq!(r1.try_recv(), Ok(10)), /// Ok(i) if i == oper2 => assert_eq!(r2.try_recv(), Ok(20)), /// Ok(_) => unreachable!(), /// } /// ``` pub fn try_ready(&mut self) -> Result { match run_ready(&mut self.handles, Timeout::Now, self.biased) { None => Err(TryReadyError), Some(index) => Ok(index), } } /// Blocks until one of the operations becomes ready. /// /// Once an operation becomes ready, its index is returned. If multiple operations are ready at /// the same time, a random one among them is chosen. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready /// even when it will simply return an error because the channel is disconnected. /// /// Note that this method might return with success spuriously, so it's a good idea to always /// double check if the operation is really ready. /// /// # Panics /// /// Panics if no operations have been added to `Select`. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{unbounded, Select}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || s2.send(20).unwrap()); /// /// let mut sel = Select::new(); /// let oper1 = sel.recv(&r1); /// let oper2 = sel.recv(&r2); /// /// // The second operation will be selected because it becomes ready first. /// match sel.ready() { /// i if i == oper1 => assert_eq!(r1.try_recv(), Ok(10)), /// i if i == oper2 => assert_eq!(r2.try_recv(), Ok(20)), /// _ => unreachable!(), /// } /// ``` pub fn ready(&mut self) -> usize { if self.handles.is_empty() { panic!("no operations have been added to `Select`"); } run_ready(&mut self.handles, Timeout::Never, self.biased).unwrap() } /// Blocks for a limited time until one of the operations becomes ready. /// /// If an operation becomes ready, its index is returned. If multiple operations are ready at /// the same time, a random one among them is chosen. If none of the operations become ready /// for the specified duration, an error is returned. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready /// even when it will simply return an error because the channel is disconnected. /// /// Note that this method might return with success spuriously, so it's a good idea to double /// check if the operation is really ready. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{unbounded, Select}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || s2.send(20).unwrap()); /// /// let mut sel = Select::new(); /// let oper1 = sel.recv(&r1); /// let oper2 = sel.recv(&r2); /// /// // The second operation will be selected because it becomes ready first. /// match sel.ready_timeout(Duration::from_millis(500)) { /// Err(_) => panic!("should not have timed out"), /// Ok(i) if i == oper1 => assert_eq!(r1.try_recv(), Ok(10)), /// Ok(i) if i == oper2 => assert_eq!(r2.try_recv(), Ok(20)), /// Ok(_) => unreachable!(), /// } /// ``` pub fn ready_timeout(&mut self, timeout: Duration) -> Result { match Instant::now().checked_add(timeout) { Some(deadline) => self.ready_deadline(deadline), None => Ok(self.ready()), } } /// Blocks until a given deadline, or until one of the operations becomes ready. /// /// If an operation becomes ready, its index is returned. If multiple operations are ready at /// the same time, a random one among them is chosen. If none of the operations become ready /// before the deadline, an error is returned. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready /// even when it will simply return an error because the channel is disconnected. /// /// Note that this method might return with success spuriously, so it's a good idea to double /// check if the operation is really ready. /// /// # Examples /// /// ``` /// use std::thread; /// use std::time::{Duration, Instant}; /// use crossbeam_channel::{unbounded, Select}; /// /// let deadline = Instant::now() + Duration::from_millis(500); /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || s2.send(20).unwrap()); /// /// let mut sel = Select::new(); /// let oper1 = sel.recv(&r1); /// let oper2 = sel.recv(&r2); /// /// // The second operation will be selected because it becomes ready first. /// match sel.ready_deadline(deadline) { /// Err(_) => panic!("should not have timed out"), /// Ok(i) if i == oper1 => assert_eq!(r1.try_recv(), Ok(10)), /// Ok(i) if i == oper2 => assert_eq!(r2.try_recv(), Ok(20)), /// Ok(_) => unreachable!(), /// } /// ``` pub fn ready_deadline(&mut self, deadline: Instant) -> Result { match run_ready(&mut self.handles, Timeout::At(deadline), self.biased) { None => Err(ReadyTimeoutError), Some(index) => Ok(index), } } } impl<'a> Clone for Select<'a> { fn clone(&self) -> Select<'a> { Select { handles: self.handles.clone(), next_index: self.next_index, biased: self.biased, } } } impl<'a> Default for Select<'a> { fn default() -> Select<'a> { Select::new() } } impl fmt::Debug for Select<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Select { .. }") } } /// A selected operation that needs to be completed. /// /// To complete the operation, call [`send`] or [`recv`]. /// /// # Panics /// /// Forgetting to complete the operation is an error and might lead to deadlocks. If a /// `SelectedOperation` is dropped without completion, a panic occurs. /// /// [`send`]: SelectedOperation::send /// [`recv`]: SelectedOperation::recv #[must_use] pub struct SelectedOperation<'a> { /// Token needed to complete the operation. token: Token, /// The index of the selected operation. index: usize, /// The address of the selected `Sender` or `Receiver`. ptr: *const u8, /// Indicates that `Sender`s and `Receiver`s are borrowed. _marker: PhantomData<&'a ()>, } impl SelectedOperation<'_> { /// Returns the index of the selected operation. /// /// # Examples /// /// ``` /// use crossbeam_channel::{bounded, Select}; /// /// let (s1, r1) = bounded::<()>(0); /// let (s2, r2) = bounded::<()>(0); /// let (s3, r3) = bounded::<()>(1); /// /// let mut sel = Select::new(); /// let oper1 = sel.send(&s1); /// let oper2 = sel.recv(&r2); /// let oper3 = sel.send(&s3); /// /// // Only the last operation is ready. /// let oper = sel.select(); /// assert_eq!(oper.index(), 2); /// assert_eq!(oper.index(), oper3); /// /// // Complete the operation. /// oper.send(&s3, ()).unwrap(); /// ``` pub fn index(&self) -> usize { self.index } /// Completes the send operation. /// /// The passed [`Sender`] reference must be the same one that was used in [`Select::send`] /// when the operation was added. /// /// # Panics /// /// Panics if an incorrect [`Sender`] reference is passed. /// /// # Examples /// /// ``` /// use crossbeam_channel::{bounded, Select, SendError}; /// /// let (s, r) = bounded::(0); /// drop(r); /// /// let mut sel = Select::new(); /// let oper1 = sel.send(&s); /// /// let oper = sel.select(); /// assert_eq!(oper.index(), oper1); /// assert_eq!(oper.send(&s, 10), Err(SendError(10))); /// ``` pub fn send(mut self, s: &Sender, msg: T) -> Result<(), SendError> { assert!( s as *const Sender as *const u8 == self.ptr, "passed a sender that wasn't selected", ); let res = unsafe { channel::write(s, &mut self.token, msg) }; mem::forget(self); res.map_err(SendError) } /// Completes the receive operation. /// /// The passed [`Receiver`] reference must be the same one that was used in [`Select::recv`] /// when the operation was added. /// /// # Panics /// /// Panics if an incorrect [`Receiver`] reference is passed. /// /// # Examples /// /// ``` /// use crossbeam_channel::{bounded, Select, RecvError}; /// /// let (s, r) = bounded::(0); /// drop(s); /// /// let mut sel = Select::new(); /// let oper1 = sel.recv(&r); /// /// let oper = sel.select(); /// assert_eq!(oper.index(), oper1); /// assert_eq!(oper.recv(&r), Err(RecvError)); /// ``` pub fn recv(mut self, r: &Receiver) -> Result { assert!( r as *const Receiver as *const u8 == self.ptr, "passed a receiver that wasn't selected", ); let res = unsafe { channel::read(r, &mut self.token) }; mem::forget(self); res.map_err(|_| RecvError) } } impl fmt::Debug for SelectedOperation<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("SelectedOperation { .. }") } } impl Drop for SelectedOperation<'_> { fn drop(&mut self) { panic!("dropped `SelectedOperation` without completing the operation"); } } crossbeam-channel-0.5.14/src/select_macro.rs000064400000000000000000001010341046102023000170730ustar 00000000000000//! The `select!` macro. /// A helper macro for `select!` to hide the long list of macro patterns from the documentation. /// /// The macro consists of two stages: /// 1. Parsing /// 2. Code generation /// /// The parsing stage consists of these subparts: /// 1. `@list`: Turns a list of tokens into a list of cases. /// 2. `@list_errorN`: Diagnoses the syntax error. /// 3. `@case`: Parses a single case and verifies its argument list. /// /// The codegen stage consists of these subparts: /// 1. `@init`: Attempts to optimize `select!` away and initializes the list of handles. /// 1. `@count`: Counts the listed cases. /// 3. `@add`: Adds send/receive operations to the list of handles and starts selection. /// 4. `@complete`: Completes the selected send/receive operation. /// /// If the parsing stage encounters a syntax error or the codegen stage ends up with too many /// cases to process, the macro fails with a compile-time error. #[doc(hidden)] #[macro_export] macro_rules! crossbeam_channel_internal { // The list is empty. Now check the arguments of each processed case. (@list () ($($head:tt)*) ) => { $crate::crossbeam_channel_internal!( @case ($($head)*) () () ) }; // If necessary, insert an empty argument list after `default`. (@list (default => $($tail:tt)*) ($($head:tt)*) ) => { $crate::crossbeam_channel_internal!( @list (default() => $($tail)*) ($($head)*) ) }; // But print an error if `default` is followed by a `->`. (@list (default -> $($tail:tt)*) ($($head:tt)*) ) => { compile_error!( "expected `=>` after `default` case, found `->`" ) }; // Print an error if there's an `->` after the argument list in the default case. (@list (default $args:tt -> $($tail:tt)*) ($($head:tt)*) ) => { compile_error!( "expected `=>` after `default` case, found `->`" ) }; // Print an error if there is a missing result in a recv case. (@list (recv($($args:tt)*) => $($tail:tt)*) ($($head:tt)*) ) => { compile_error!( "expected `->` after `recv` case, found `=>`" ) }; // Print an error if there is a missing result in a send case. (@list (send($($args:tt)*) => $($tail:tt)*) ($($head:tt)*) ) => { compile_error!( "expected `->` after `send` operation, found `=>`" ) }; // Make sure the arrow and the result are not repeated. (@list ($case:ident $args:tt -> $res:tt -> $($tail:tt)*) ($($head:tt)*) ) => { compile_error!("expected `=>`, found `->`") }; // Print an error if there is a semicolon after the block. (@list ($case:ident $args:tt $(-> $res:pat)* => $body:block; $($tail:tt)*) ($($head:tt)*) ) => { compile_error!( "did you mean to put a comma instead of the semicolon after `}`?" ) }; // The first case is separated by a comma. (@list ($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:expr, $($tail:tt)*) ($($head:tt)*) ) => { $crate::crossbeam_channel_internal!( @list ($($tail)*) ($($head)* $case ($($args)*) $(-> $res)* => { $body },) ) }; // Don't require a comma after the case if it has a proper block. (@list ($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:block $($tail:tt)*) ($($head:tt)*) ) => { $crate::crossbeam_channel_internal!( @list ($($tail)*) ($($head)* $case ($($args)*) $(-> $res)* => { $body },) ) }; // Only one case remains. (@list ($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:expr $(,)?) ($($head:tt)*) ) => { $crate::crossbeam_channel_internal!( @list () ($($head)* $case ($($args)*) $(-> $res)* => { $body },) ) }; // Diagnose and print an error. (@list ($($tail:tt)*) ($($head:tt)*) ) => { $crate::crossbeam_channel_internal!(@list_error1 $($tail)*) }; // Stage 1: check the case type. (@list_error1 recv $($tail:tt)*) => { $crate::crossbeam_channel_internal!(@list_error2 recv $($tail)*) }; (@list_error1 send $($tail:tt)*) => { $crate::crossbeam_channel_internal!(@list_error2 send $($tail)*) }; (@list_error1 default $($tail:tt)*) => { $crate::crossbeam_channel_internal!(@list_error2 default $($tail)*) }; (@list_error1 $t:tt $($tail:tt)*) => { compile_error!( concat!( "expected one of `recv`, `send`, or `default`, found `", stringify!($t), "`", ) ) }; (@list_error1 $($tail:tt)*) => { $crate::crossbeam_channel_internal!(@list_error2 $($tail)*); }; // Stage 2: check the argument list. (@list_error2 $case:ident) => { compile_error!( concat!( "missing argument list after `", stringify!($case), "`", ) ) }; (@list_error2 $case:ident => $($tail:tt)*) => { compile_error!( concat!( "missing argument list after `", stringify!($case), "`", ) ) }; (@list_error2 $($tail:tt)*) => { $crate::crossbeam_channel_internal!(@list_error3 $($tail)*) }; // Stage 3: check the `=>` and what comes after it. (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)*) => { compile_error!( concat!( "missing `=>` after `", stringify!($case), "` case", ) ) }; (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* =>) => { compile_error!( "expected expression after `=>`" ) }; (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $body:expr; $($tail:tt)*) => { compile_error!( concat!( "did you mean to put a comma instead of the semicolon after `", stringify!($body), "`?", ) ) }; (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => recv($($a:tt)*) $($tail:tt)*) => { compile_error!( "expected an expression after `=>`" ) }; (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => send($($a:tt)*) $($tail:tt)*) => { compile_error!( "expected an expression after `=>`" ) }; (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => default($($a:tt)*) $($tail:tt)*) => { compile_error!( "expected an expression after `=>`" ) }; (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident($($a:tt)*) $($tail:tt)*) => { compile_error!( concat!( "did you mean to put a comma after `", stringify!($f), "(", stringify!($($a)*), ")`?", ) ) }; (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident!($($a:tt)*) $($tail:tt)*) => { compile_error!( concat!( "did you mean to put a comma after `", stringify!($f), "!(", stringify!($($a)*), ")`?", ) ) }; (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident![$($a:tt)*] $($tail:tt)*) => { compile_error!( concat!( "did you mean to put a comma after `", stringify!($f), "![", stringify!($($a)*), "]`?", ) ) }; (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident!{$($a:tt)*} $($tail:tt)*) => { compile_error!( concat!( "did you mean to put a comma after `", stringify!($f), "!{", stringify!($($a)*), "}`?", ) ) }; (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $body:tt $($tail:tt)*) => { compile_error!( concat!( "did you mean to put a comma after `", stringify!($body), "`?", ) ) }; (@list_error3 $case:ident($($args:tt)*) -> => $($tail:tt)*) => { compile_error!("missing pattern after `->`") }; (@list_error3 $case:ident($($args:tt)*) $t:tt $(-> $r:pat)* => $($tail:tt)*) => { compile_error!( concat!( "expected `->`, found `", stringify!($t), "`", ) ) }; (@list_error3 $case:ident($($args:tt)*) -> $t:tt $($tail:tt)*) => { compile_error!( concat!( "expected a pattern, found `", stringify!($t), "`", ) ) }; (@list_error3 recv($($args:tt)*) $t:tt $($tail:tt)*) => { compile_error!( concat!( "expected `->`, found `", stringify!($t), "`", ) ) }; (@list_error3 send($($args:tt)*) $t:tt $($tail:tt)*) => { compile_error!( concat!( "expected `->`, found `", stringify!($t), "`", ) ) }; (@list_error3 recv $args:tt $($tail:tt)*) => { compile_error!( concat!( "expected an argument list after `recv`, found `", stringify!($args), "`", ) ) }; (@list_error3 send $args:tt $($tail:tt)*) => { compile_error!( concat!( "expected an argument list after `send`, found `", stringify!($args), "`", ) ) }; (@list_error3 default $args:tt $($tail:tt)*) => { compile_error!( concat!( "expected an argument list or `=>` after `default`, found `", stringify!($args), "`", ) ) }; (@list_error3 $($tail:tt)*) => { $crate::crossbeam_channel_internal!(@list_error4 $($tail)*) }; // Stage 4: fail with a generic error message. (@list_error4 $($tail:tt)*) => { compile_error!("invalid syntax") }; // Success! All cases were parsed. (@case () $cases:tt $default:tt ) => { $crate::crossbeam_channel_internal!( @init $cases $default ) }; // Check the format of a recv case. (@case (recv($r:expr $(,)?) -> $res:pat => $body:tt, $($tail:tt)*) ($($cases:tt)*) $default:tt ) => { $crate::crossbeam_channel_internal!( @case ($($tail)*) ($($cases)* recv($r) -> $res => $body,) $default ) }; // Print an error if the argument list is invalid. (@case (recv($($args:tt)*) -> $res:pat => $body:tt, $($tail:tt)*) ($($cases:tt)*) $default:tt ) => { compile_error!( concat!( "invalid argument list in `recv(", stringify!($($args)*), ")`", ) ) }; // Print an error if there is no argument list. (@case (recv $t:tt $($tail:tt)*) ($($cases:tt)*) $default:tt ) => { compile_error!( concat!( "expected an argument list after `recv`, found `", stringify!($t), "`", ) ) }; // Check the format of a send case. (@case (send($s:expr, $m:expr $(,)?) -> $res:pat => $body:tt, $($tail:tt)*) ($($cases:tt)*) $default:tt ) => { $crate::crossbeam_channel_internal!( @case ($($tail)*) ($($cases)* send($s, $m) -> $res => $body,) $default ) }; // Print an error if the argument list is invalid. (@case (send($($args:tt)*) -> $res:pat => $body:tt, $($tail:tt)*) ($($cases:tt)*) $default:tt ) => { compile_error!( concat!( "invalid argument list in `send(", stringify!($($args)*), ")`", ) ) }; // Print an error if there is no argument list. (@case (send $t:tt $($tail:tt)*) ($($cases:tt)*) $default:tt ) => { compile_error!( concat!( "expected an argument list after `send`, found `", stringify!($t), "`", ) ) }; // Check the format of a default case. (@case (default() => $body:tt, $($tail:tt)*) $cases:tt () ) => { $crate::crossbeam_channel_internal!( @case ($($tail)*) $cases (default() => $body,) ) }; // Check the format of a default case with timeout. (@case (default($timeout:expr $(,)?) => $body:tt, $($tail:tt)*) $cases:tt () ) => { $crate::crossbeam_channel_internal!( @case ($($tail)*) $cases (default($timeout) => $body,) ) }; // Check for duplicate default cases... (@case (default $($tail:tt)*) $cases:tt ($($def:tt)+) ) => { compile_error!( "there can be only one `default` case in a `select!` block" ) }; // Print an error if the argument list is invalid. (@case (default($($args:tt)*) => $body:tt, $($tail:tt)*) $cases:tt $default:tt ) => { compile_error!( concat!( "invalid argument list in `default(", stringify!($($args)*), ")`", ) ) }; // Print an error if there is an unexpected token after `default`. (@case (default $t:tt $($tail:tt)*) $cases:tt $default:tt ) => { compile_error!( concat!( "expected an argument list or `=>` after `default`, found `", stringify!($t), "`", ) ) }; // The case was not consumed, therefore it must be invalid. (@case ($case:ident $($tail:tt)*) $cases:tt $default:tt ) => { compile_error!( concat!( "expected one of `recv`, `send`, or `default`, found `", stringify!($case), "`", ) ) }; // Optimize `select!` into `try_recv()`. (@init (recv($r:expr) -> $res:pat => $recv_body:tt,) (default() => $default_body:tt,) ) => {{ match $r { ref _r => { let _r: &$crate::Receiver<_> = _r; match _r.try_recv() { ::std::result::Result::Err($crate::TryRecvError::Empty) => { $default_body } _res => { let _res = _res.map_err(|_| $crate::RecvError); let $res = _res; $recv_body } } } } }}; // Optimize `select!` into `recv()`. (@init (recv($r:expr) -> $res:pat => $body:tt,) () ) => {{ match $r { ref _r => { let _r: &$crate::Receiver<_> = _r; let _res = _r.recv(); let $res = _res; $body } } }}; // Optimize `select!` into `recv_timeout()`. (@init (recv($r:expr) -> $res:pat => $recv_body:tt,) (default($timeout:expr) => $default_body:tt,) ) => {{ match $r { ref _r => { let _r: &$crate::Receiver<_> = _r; match _r.recv_timeout($timeout) { ::std::result::Result::Err($crate::RecvTimeoutError::Timeout) => { $default_body } _res => { let _res = _res.map_err(|_| $crate::RecvError); let $res = _res; $recv_body } } } } }}; // // Optimize the non-blocking case with two receive operations. // (@init // (recv($r1:expr) -> $res1:pat => $recv_body1:tt,) // (recv($r2:expr) -> $res2:pat => $recv_body2:tt,) // (default() => $default_body:tt,) // ) => {{ // match $r1 { // ref _r1 => { // let _r1: &$crate::Receiver<_> = _r1; // // match $r2 { // ref _r2 => { // let _r2: &$crate::Receiver<_> = _r2; // // // TODO(stjepang): Implement this optimization. // } // } // } // } // }}; // // Optimize the blocking case with two receive operations. // (@init // (recv($r1:expr) -> $res1:pat => $body1:tt,) // (recv($r2:expr) -> $res2:pat => $body2:tt,) // () // ) => {{ // match $r1 { // ref _r1 => { // let _r1: &$crate::Receiver<_> = _r1; // // match $r2 { // ref _r2 => { // let _r2: &$crate::Receiver<_> = _r2; // // // TODO(stjepang): Implement this optimization. // } // } // } // } // }}; // // Optimize the case with two receive operations and a timeout. // (@init // (recv($r1:expr) -> $res1:pat => $recv_body1:tt,) // (recv($r2:expr) -> $res2:pat => $recv_body2:tt,) // (default($timeout:expr) => $default_body:tt,) // ) => {{ // match $r1 { // ref _r1 => { // let _r1: &$crate::Receiver<_> = _r1; // // match $r2 { // ref _r2 => { // let _r2: &$crate::Receiver<_> = _r2; // // // TODO(stjepang): Implement this optimization. // } // } // } // } // }}; // // Optimize `select!` into `try_send()`. // (@init // (send($s:expr, $m:expr) -> $res:pat => $send_body:tt,) // (default() => $default_body:tt,) // ) => {{ // match $s { // ref _s => { // let _s: &$crate::Sender<_> = _s; // // TODO(stjepang): Implement this optimization. // } // } // }}; // // Optimize `select!` into `send()`. // (@init // (send($s:expr, $m:expr) -> $res:pat => $body:tt,) // () // ) => {{ // match $s { // ref _s => { // let _s: &$crate::Sender<_> = _s; // // TODO(stjepang): Implement this optimization. // } // } // }}; // // Optimize `select!` into `send_timeout()`. // (@init // (send($s:expr, $m:expr) -> $res:pat => $body:tt,) // (default($timeout:expr) => $body:tt,) // ) => {{ // match $s { // ref _s => { // let _s: &$crate::Sender<_> = _s; // // TODO(stjepang): Implement this optimization. // } // } // }}; // Create the list of handles and add operations to it. (@init ($($cases:tt)*) $default:tt ) => {{ const _LEN: usize = $crate::crossbeam_channel_internal!(@count ($($cases)*)); let _handle: &dyn $crate::internal::SelectHandle = &$crate::never::<()>(); #[allow(unused_mut, clippy::zero_repeat_side_effects)] let mut _sel = [(_handle, 0, ::std::ptr::null()); _LEN]; $crate::crossbeam_channel_internal!( @add _sel ($($cases)*) $default ( (0usize _oper0) (1usize _oper1) (2usize _oper2) (3usize _oper3) (4usize _oper4) (5usize _oper5) (6usize _oper6) (7usize _oper7) (8usize _oper8) (9usize _oper9) (10usize _oper10) (11usize _oper11) (12usize _oper12) (13usize _oper13) (14usize _oper14) (15usize _oper15) (16usize _oper16) (17usize _oper17) (18usize _oper18) (19usize _oper19) (20usize _oper20) (21usize _oper21) (22usize _oper22) (23usize _oper23) (24usize _oper24) (25usize _oper25) (26usize _oper26) (27usize _oper27) (28usize _oper28) (29usize _oper29) (30usize _oper30) (31usize _oper31) ) () ) }}; // Count the listed cases. (@count ()) => { 0 }; (@count ($oper:ident $args:tt -> $res:pat => $body:tt, $($cases:tt)*)) => { 1 + $crate::crossbeam_channel_internal!(@count ($($cases)*)) }; // Run blocking selection. (@add $sel:ident () () $labels:tt $cases:tt ) => {{ let _oper: $crate::SelectedOperation<'_> = { let _oper = $crate::internal::select(&mut $sel, _IS_BIASED); // Erase the lifetime so that `sel` can be dropped early even without NLL. unsafe { ::std::mem::transmute(_oper) } }; $crate::crossbeam_channel_internal! { @complete $sel _oper $cases } }}; // Run non-blocking selection. (@add $sel:ident () (default() => $body:tt,) $labels:tt $cases:tt ) => {{ let _oper: ::std::option::Option<$crate::SelectedOperation<'_>> = { let _oper = $crate::internal::try_select(&mut $sel, _IS_BIASED); // Erase the lifetime so that `sel` can be dropped early even without NLL. unsafe { ::std::mem::transmute(_oper) } }; match _oper { None => { { $sel }; $body } Some(_oper) => { $crate::crossbeam_channel_internal! { @complete $sel _oper $cases } } } }}; // Run selection with a timeout. (@add $sel:ident () (default($timeout:expr) => $body:tt,) $labels:tt $cases:tt ) => {{ let _oper: ::std::option::Option<$crate::SelectedOperation<'_>> = { let _oper = $crate::internal::select_timeout(&mut $sel, $timeout, _IS_BIASED); // Erase the lifetime so that `sel` can be dropped early even without NLL. unsafe { ::std::mem::transmute(_oper) } }; match _oper { ::std::option::Option::None => { { $sel }; $body } ::std::option::Option::Some(_oper) => { $crate::crossbeam_channel_internal! { @complete $sel _oper $cases } } } }}; // Have we used up all labels? (@add $sel:ident $input:tt $default:tt () $cases:tt ) => { compile_error!("too many operations in a `select!` block") }; // Add a receive operation to `sel`. (@add $sel:ident (recv($r:expr) -> $res:pat => $body:tt, $($tail:tt)*) $default:tt (($i:tt $var:ident) $($labels:tt)*) ($($cases:tt)*) ) => {{ match $r { ref _r => { let $var: &$crate::Receiver<_> = unsafe { let _r: &$crate::Receiver<_> = _r; // Erase the lifetime so that `sel` can be dropped early even without NLL. unsafe fn unbind<'a, T>(x: &T) -> &'a T { ::std::mem::transmute(x) } unbind(_r) }; $sel[$i] = ($var, $i, $var as *const $crate::Receiver<_> as *const u8); $crate::crossbeam_channel_internal!( @add $sel ($($tail)*) $default ($($labels)*) ($($cases)* [$i] recv($var) -> $res => $body,) ) } } }}; // Add a send operation to `sel`. (@add $sel:ident (send($s:expr, $m:expr) -> $res:pat => $body:tt, $($tail:tt)*) $default:tt (($i:tt $var:ident) $($labels:tt)*) ($($cases:tt)*) ) => {{ match $s { ref _s => { let $var: &$crate::Sender<_> = unsafe { let _s: &$crate::Sender<_> = _s; // Erase the lifetime so that `sel` can be dropped early even without NLL. unsafe fn unbind<'a, T>(x: &T) -> &'a T { ::std::mem::transmute(x) } unbind(_s) }; $sel[$i] = ($var, $i, $var as *const $crate::Sender<_> as *const u8); $crate::crossbeam_channel_internal!( @add $sel ($($tail)*) $default ($($labels)*) ($($cases)* [$i] send($var, $m) -> $res => $body,) ) } } }}; // Complete a receive operation. (@complete $sel:ident $oper:ident ([$i:tt] recv($r:ident) -> $res:pat => $body:tt, $($tail:tt)*) ) => {{ if $oper.index() == $i { let _res = $oper.recv($r); { $sel }; let $res = _res; $body } else { $crate::crossbeam_channel_internal! { @complete $sel $oper ($($tail)*) } } }}; // Complete a send operation. (@complete $sel:ident $oper:ident ([$i:tt] send($s:ident, $m:expr) -> $res:pat => $body:tt, $($tail:tt)*) ) => {{ if $oper.index() == $i { let _res = $oper.send($s, $m); { $sel }; let $res = _res; $body } else { $crate::crossbeam_channel_internal! { @complete $sel $oper ($($tail)*) } } }}; // Panic if we don't identify the selected case, but this should never happen. (@complete $sel:ident $oper:ident () ) => {{ unreachable!( "internal error in crossbeam-channel: invalid case" ) }}; // Catches a bug within this macro (should not happen). (@$($tokens:tt)*) => { compile_error!( concat!( "internal error in crossbeam-channel: ", stringify!(@$($tokens)*), ) ) }; // The entry points. () => { compile_error!("empty `select!` block") }; ($($case:ident $(($($args:tt)*))* => $body:expr $(,)*)*) => { $crate::crossbeam_channel_internal!( @list ($($case $(($($args)*))* => { $body },)*) () ) }; ($($tokens:tt)*) => { $crate::crossbeam_channel_internal!( @list ($($tokens)*) () ) }; } /// Selects from a set of channel operations. /// /// This macro allows you to define a set of channel operations, wait until any one of them becomes /// ready, and finally execute it. If multiple operations are ready at the same time, a random one /// among them is selected (i.e. the unbiased selection). Use `select_biased!` for the biased /// selection. /// /// It is also possible to define a `default` case that gets executed if none of the operations are /// ready, either right away or for a certain duration of time. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready even /// when it will simply return an error because the channel is disconnected. /// /// The `select!` macro is a convenience wrapper around [`Select`]. However, it cannot select over a /// dynamically created list of channel operations. /// /// [`Select`]: super::Select /// /// # Examples /// /// Block until a send or a receive operation is selected: /// /// ``` /// use crossbeam_channel::{select, unbounded}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// s1.send(10).unwrap(); /// /// // Since both operations are initially ready, a random one will be executed. /// select! { /// recv(r1) -> msg => assert_eq!(msg, Ok(10)), /// send(s2, 20) -> res => { /// assert_eq!(res, Ok(())); /// assert_eq!(r2.recv(), Ok(20)); /// } /// } /// ``` /// /// Select from a set of operations without blocking: /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{select, unbounded}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || { /// thread::sleep(Duration::from_millis(500)); /// s2.send(20).unwrap(); /// }); /// /// // None of the operations are initially ready. /// select! { /// recv(r1) -> msg => panic!(), /// recv(r2) -> msg => panic!(), /// default => println!("not ready"), /// } /// ``` /// /// Select over a set of operations with a timeout: /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{select, unbounded}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || { /// thread::sleep(Duration::from_millis(500)); /// s2.send(20).unwrap(); /// }); /// /// // None of the two operations will become ready within 100 milliseconds. /// select! { /// recv(r1) -> msg => panic!(), /// recv(r2) -> msg => panic!(), /// default(Duration::from_millis(100)) => println!("timed out"), /// } /// ``` /// /// Optionally add a receive operation to `select!` using [`never`]: /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{select, never, unbounded}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || { /// thread::sleep(Duration::from_millis(500)); /// s2.send(20).unwrap(); /// }); /// /// // This receiver can be a `Some` or a `None`. /// let r2 = Some(&r2); /// /// // None of the two operations will become ready within 100 milliseconds. /// select! { /// recv(r1) -> msg => panic!(), /// recv(r2.unwrap_or(&never())) -> msg => assert_eq!(msg, Ok(20)), /// } /// ``` /// /// To optionally add a timeout to `select!`, see the [example] for [`never`]. /// /// [`never`]: super::never /// [example]: super::never#examples #[macro_export] macro_rules! select { ($($tokens:tt)*) => { { const _IS_BIASED: bool = false; $crate::crossbeam_channel_internal!( $($tokens)* ) } }; } /// Selects from a set of channel operations. /// /// This macro allows you to define a list of channel operations, wait until any one of them /// becomes ready, and finally execute it. If multiple operations are ready at the same time, the /// operation nearest to the front of the list is always selected (i.e. the biased selection). Use /// [`select!`] for the unbiased selection. /// /// Otherwise, this macro's functionality is identical to [`select!`]. Refer to it for the syntax. #[macro_export] macro_rules! select_biased { ($($tokens:tt)*) => { { const _IS_BIASED: bool = true; $crate::crossbeam_channel_internal!( $($tokens)* ) } }; } crossbeam-channel-0.5.14/src/utils.rs000064400000000000000000000030151046102023000155730ustar 00000000000000//! Miscellaneous utilities. use std::cell::Cell; use std::num::Wrapping; use std::thread; use std::time::{Duration, Instant}; /// Randomly shuffles a slice. pub(crate) fn shuffle(v: &mut [T]) { let len = v.len(); if len <= 1 { return; } std::thread_local! { static RNG: Cell> = const { Cell::new(Wrapping(1_406_868_647)) }; } let _ = RNG.try_with(|rng| { for i in 1..len { // This is the 32-bit variant of Xorshift. // // Source: https://en.wikipedia.org/wiki/Xorshift let mut x = rng.get(); x ^= x << 13; x ^= x >> 17; x ^= x << 5; rng.set(x); let x = x.0; let n = i + 1; // This is a fast alternative to `let j = x % n`. // // Author: Daniel Lemire // Source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ let j = ((x as u64).wrapping_mul(n as u64) >> 32) as u32 as usize; v.swap(i, j); } }); } /// Sleeps until the deadline, or forever if the deadline isn't specified. pub(crate) fn sleep_until(deadline: Option) { loop { match deadline { None => thread::sleep(Duration::from_secs(1000)), Some(d) => { let now = Instant::now(); if now >= d { break; } thread::sleep(d - now); } } } } crossbeam-channel-0.5.14/src/waker.rs000064400000000000000000000207441046102023000155540ustar 00000000000000//! Waking mechanism for threads blocked on channel operations. use std::ptr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; use std::thread::{self, ThreadId}; use std::vec::Vec; use crate::context::Context; use crate::select::{Operation, Selected}; /// Represents a thread blocked on a specific channel operation. pub(crate) struct Entry { /// The operation. pub(crate) oper: Operation, /// Optional packet. pub(crate) packet: *mut (), /// Context associated with the thread owning this operation. pub(crate) cx: Context, } /// A queue of threads blocked on channel operations. /// /// This data structure is used by threads to register blocking operations and get woken up once /// an operation becomes ready. pub(crate) struct Waker { /// A list of select operations. selectors: Vec, /// A list of operations waiting to be ready. observers: Vec, } impl Waker { /// Creates a new `Waker`. #[inline] pub(crate) fn new() -> Self { Waker { selectors: Vec::new(), observers: Vec::new(), } } /// Registers a select operation. #[inline] pub(crate) fn register(&mut self, oper: Operation, cx: &Context) { self.register_with_packet(oper, ptr::null_mut(), cx); } /// Registers a select operation and a packet. #[inline] pub(crate) fn register_with_packet(&mut self, oper: Operation, packet: *mut (), cx: &Context) { self.selectors.push(Entry { oper, packet, cx: cx.clone(), }); } /// Unregisters a select operation. #[inline] pub(crate) fn unregister(&mut self, oper: Operation) -> Option { if let Some((i, _)) = self .selectors .iter() .enumerate() .find(|&(_, entry)| entry.oper == oper) { let entry = self.selectors.remove(i); Some(entry) } else { None } } /// Attempts to find another thread's entry, select the operation, and wake it up. #[inline] pub(crate) fn try_select(&mut self) -> Option { if self.selectors.is_empty() { None } else { let thread_id = current_thread_id(); self.selectors .iter() .position(|selector| { // Does the entry belong to a different thread? selector.cx.thread_id() != thread_id && selector // Try selecting this operation. .cx .try_select(Selected::Operation(selector.oper)) .is_ok() && { // Provide the packet. selector.cx.store_packet(selector.packet); // Wake the thread up. selector.cx.unpark(); true } }) // Remove the entry from the queue to keep it clean and improve // performance. .map(|pos| self.selectors.remove(pos)) } } /// Returns `true` if there is an entry which can be selected by the current thread. #[inline] pub(crate) fn can_select(&self) -> bool { if self.selectors.is_empty() { false } else { let thread_id = current_thread_id(); self.selectors.iter().any(|entry| { entry.cx.thread_id() != thread_id && entry.cx.selected() == Selected::Waiting }) } } /// Registers an operation waiting to be ready. #[inline] pub(crate) fn watch(&mut self, oper: Operation, cx: &Context) { self.observers.push(Entry { oper, packet: ptr::null_mut(), cx: cx.clone(), }); } /// Unregisters an operation waiting to be ready. #[inline] pub(crate) fn unwatch(&mut self, oper: Operation) { self.observers.retain(|e| e.oper != oper); } /// Notifies all operations waiting to be ready. #[inline] pub(crate) fn notify(&mut self) { for entry in self.observers.drain(..) { if entry.cx.try_select(Selected::Operation(entry.oper)).is_ok() { entry.cx.unpark(); } } } /// Notifies all registered operations that the channel is disconnected. #[inline] pub(crate) fn disconnect(&mut self) { for entry in self.selectors.iter() { if entry.cx.try_select(Selected::Disconnected).is_ok() { // Wake the thread up. // // Here we don't remove the entry from the queue. Registered threads must // unregister from the waker by themselves. They might also want to recover the // packet value and destroy it, if necessary. entry.cx.unpark(); } } self.notify(); } } impl Drop for Waker { #[inline] fn drop(&mut self) { debug_assert_eq!(self.selectors.len(), 0); debug_assert_eq!(self.observers.len(), 0); } } /// A waker that can be shared among threads without locking. /// /// This is a simple wrapper around `Waker` that internally uses a mutex for synchronization. pub(crate) struct SyncWaker { /// The inner `Waker`. inner: Mutex, /// `true` if the waker is empty. is_empty: AtomicBool, } impl SyncWaker { /// Creates a new `SyncWaker`. #[inline] pub(crate) fn new() -> Self { SyncWaker { inner: Mutex::new(Waker::new()), is_empty: AtomicBool::new(true), } } /// Registers the current thread with an operation. #[inline] pub(crate) fn register(&self, oper: Operation, cx: &Context) { let mut inner = self.inner.lock().unwrap(); inner.register(oper, cx); self.is_empty.store( inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst, ); } /// Unregisters an operation previously registered by the current thread. #[inline] pub(crate) fn unregister(&self, oper: Operation) -> Option { let mut inner = self.inner.lock().unwrap(); let entry = inner.unregister(oper); self.is_empty.store( inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst, ); entry } /// Attempts to find one thread (not the current one), select its operation, and wake it up. #[inline] pub(crate) fn notify(&self) { if !self.is_empty.load(Ordering::SeqCst) { let mut inner = self.inner.lock().unwrap(); if !self.is_empty.load(Ordering::SeqCst) { inner.try_select(); inner.notify(); self.is_empty.store( inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst, ); } } } /// Registers an operation waiting to be ready. #[inline] pub(crate) fn watch(&self, oper: Operation, cx: &Context) { let mut inner = self.inner.lock().unwrap(); inner.watch(oper, cx); self.is_empty.store( inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst, ); } /// Unregisters an operation waiting to be ready. #[inline] pub(crate) fn unwatch(&self, oper: Operation) { let mut inner = self.inner.lock().unwrap(); inner.unwatch(oper); self.is_empty.store( inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst, ); } /// Notifies all threads that the channel is disconnected. #[inline] pub(crate) fn disconnect(&self) { let mut inner = self.inner.lock().unwrap(); inner.disconnect(); self.is_empty.store( inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst, ); } } impl Drop for SyncWaker { #[inline] fn drop(&mut self) { debug_assert!(self.is_empty.load(Ordering::SeqCst)); } } /// Returns the id of the current thread. #[inline] fn current_thread_id() -> ThreadId { std::thread_local! { /// Cached thread-local id. static THREAD_ID: ThreadId = thread::current().id(); } THREAD_ID .try_with(|id| *id) .unwrap_or_else(|_| thread::current().id()) } crossbeam-channel-0.5.14/tests/after.rs000064400000000000000000000170771046102023000161240ustar 00000000000000//! Tests for the after channel flavor. #![cfg(not(miri))] // TODO: many assertions failed due to Miri is slow use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use std::thread; use std::time::{Duration, Instant}; use crossbeam_channel::{after, select, Select, TryRecvError}; use crossbeam_utils::thread::scope; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] fn fire() { let start = Instant::now(); let r = after(ms(50)); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); thread::sleep(ms(100)); let fired = r.try_recv().unwrap(); assert!(start < fired); assert!(fired - start >= ms(50)); let now = Instant::now(); assert!(fired < now); assert!(now - fired >= ms(50)); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); select! { recv(r) -> _ => panic!(), default => {} } select! { recv(r) -> _ => panic!(), recv(after(ms(200))) -> _ => {} } } #[test] fn capacity() { const COUNT: usize = 10; for i in 0..COUNT { let r = after(ms(i as u64)); assert_eq!(r.capacity(), Some(1)); } } #[test] fn len_empty_full() { let r = after(ms(50)); assert_eq!(r.len(), 0); assert!(r.is_empty()); assert!(!r.is_full()); thread::sleep(ms(100)); assert_eq!(r.len(), 1); assert!(!r.is_empty()); assert!(r.is_full()); r.try_recv().unwrap(); assert_eq!(r.len(), 0); assert!(r.is_empty()); assert!(!r.is_full()); } #[test] fn try_recv() { let r = after(ms(200)); assert!(r.try_recv().is_err()); thread::sleep(ms(100)); assert!(r.try_recv().is_err()); thread::sleep(ms(200)); assert!(r.try_recv().is_ok()); assert!(r.try_recv().is_err()); thread::sleep(ms(200)); assert!(r.try_recv().is_err()); } #[test] fn recv() { let start = Instant::now(); let r = after(ms(50)); let fired = r.recv().unwrap(); assert!(start < fired); assert!(fired - start >= ms(50)); let now = Instant::now(); assert!(fired < now); assert!(now - fired < fired - start); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[test] fn recv_timeout() { let start = Instant::now(); let r = after(ms(200)); assert!(r.recv_timeout(ms(100)).is_err()); let now = Instant::now(); assert!(now - start >= ms(100)); assert!(now - start <= ms(150)); let fired = r.recv_timeout(ms(200)).unwrap(); assert!(fired - start >= ms(200)); assert!(fired - start <= ms(250)); assert!(r.recv_timeout(ms(200)).is_err()); let now = Instant::now(); assert!(now - start >= ms(400)); assert!(now - start <= ms(450)); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[test] fn recv_two() { let r1 = after(ms(50)); let r2 = after(ms(50)); scope(|scope| { scope.spawn(|_| { select! { recv(r1) -> _ => {} recv(r2) -> _ => {} } }); scope.spawn(|_| { select! { recv(r1) -> _ => {} recv(r2) -> _ => {} } }); }) .unwrap(); } #[test] fn recv_race() { select! { recv(after(ms(50))) -> _ => {} recv(after(ms(100))) -> _ => panic!(), } select! { recv(after(ms(100))) -> _ => panic!(), recv(after(ms(50))) -> _ => {} } } #[test] fn stress_default() { const COUNT: usize = 10; for _ in 0..COUNT { select! { recv(after(ms(0))) -> _ => {} default => panic!(), } } for _ in 0..COUNT { select! { recv(after(ms(100))) -> _ => panic!(), default => {} } } } #[test] fn select() { const THREADS: usize = 4; const COUNT: usize = 1000; const TIMEOUT_MS: u64 = 100; let v = (0..COUNT) .map(|i| after(ms(i as u64 / TIMEOUT_MS / 2))) .collect::>(); let hits = AtomicUsize::new(0); scope(|scope| { for _ in 0..THREADS { scope.spawn(|_| { let v: Vec<&_> = v.iter().collect(); loop { let timeout = after(ms(TIMEOUT_MS)); let mut sel = Select::new(); for r in &v { sel.recv(r); } let oper_timeout = sel.recv(&timeout); let oper = sel.select(); match oper.index() { i if i == oper_timeout => { oper.recv(&timeout).unwrap(); break; } i => { oper.recv(v[i]).unwrap(); hits.fetch_add(1, Ordering::SeqCst); } } } }); } }) .unwrap(); assert_eq!(hits.load(Ordering::SeqCst), COUNT); } #[test] fn ready() { const THREADS: usize = 4; const COUNT: usize = 1000; const TIMEOUT_MS: u64 = 100; let v = (0..COUNT) .map(|i| after(ms(i as u64 / TIMEOUT_MS / 2))) .collect::>(); let hits = AtomicUsize::new(0); scope(|scope| { for _ in 0..THREADS { scope.spawn(|_| { let v: Vec<&_> = v.iter().collect(); loop { let timeout = after(ms(TIMEOUT_MS)); let mut sel = Select::new(); for r in &v { sel.recv(r); } let oper_timeout = sel.recv(&timeout); loop { let i = sel.ready(); if i == oper_timeout { timeout.try_recv().unwrap(); return; } else if v[i].try_recv().is_ok() { hits.fetch_add(1, Ordering::SeqCst); break; } } } }); } }) .unwrap(); assert_eq!(hits.load(Ordering::SeqCst), COUNT); } #[test] fn stress_clone() { const RUNS: usize = 1000; const THREADS: usize = 10; const COUNT: usize = 50; for i in 0..RUNS { let r = after(ms(i as u64)); scope(|scope| { for _ in 0..THREADS { scope.spawn(|_| { let r = r.clone(); let _ = r.try_recv(); for _ in 0..COUNT { drop(r.clone()); thread::yield_now(); } }); } }) .unwrap(); } } #[test] fn fairness() { const COUNT: usize = 1000; for &dur in &[0, 1] { let mut hits = [0usize; 2]; for _ in 0..COUNT { select! { recv(after(ms(dur))) -> _ => hits[0] += 1, recv(after(ms(dur))) -> _ => hits[1] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); } } #[test] fn fairness_duplicates() { const COUNT: usize = 1000; for &dur in &[0, 1] { let mut hits = [0usize; 5]; for _ in 0..COUNT { let r = after(ms(dur)); select! { recv(r) -> _ => hits[0] += 1, recv(r) -> _ => hits[1] += 1, recv(r) -> _ => hits[2] += 1, recv(r) -> _ => hits[3] += 1, recv(r) -> _ => hits[4] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); } } crossbeam-channel-0.5.14/tests/array.rs000064400000000000000000000406451046102023000161360ustar 00000000000000//! Tests for the array channel flavor. use std::any::Any; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use std::thread; use std::time::Duration; use crossbeam_channel::{bounded, select, Receiver}; use crossbeam_channel::{RecvError, RecvTimeoutError, TryRecvError}; use crossbeam_channel::{SendError, SendTimeoutError, TrySendError}; use crossbeam_utils::thread::scope; use rand::{thread_rng, Rng}; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] fn smoke() { let (s, r) = bounded(1); s.send(7).unwrap(); assert_eq!(r.try_recv(), Ok(7)); s.send(8).unwrap(); assert_eq!(r.recv(), Ok(8)); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout)); } #[test] fn capacity() { for i in 1..10 { let (s, r) = bounded::<()>(i); assert_eq!(s.capacity(), Some(i)); assert_eq!(r.capacity(), Some(i)); } } #[test] fn len_empty_full() { let (s, r) = bounded(2); assert_eq!(s.len(), 0); assert!(s.is_empty()); assert!(!s.is_full()); assert_eq!(r.len(), 0); assert!(r.is_empty()); assert!(!r.is_full()); s.send(()).unwrap(); assert_eq!(s.len(), 1); assert!(!s.is_empty()); assert!(!s.is_full()); assert_eq!(r.len(), 1); assert!(!r.is_empty()); assert!(!r.is_full()); s.send(()).unwrap(); assert_eq!(s.len(), 2); assert!(!s.is_empty()); assert!(s.is_full()); assert_eq!(r.len(), 2); assert!(!r.is_empty()); assert!(r.is_full()); r.recv().unwrap(); assert_eq!(s.len(), 1); assert!(!s.is_empty()); assert!(!s.is_full()); assert_eq!(r.len(), 1); assert!(!r.is_empty()); assert!(!r.is_full()); } #[test] fn try_recv() { let (s, r) = bounded(100); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); thread::sleep(ms(1500)); assert_eq!(r.try_recv(), Ok(7)); thread::sleep(ms(500)); assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)); }); scope.spawn(move |_| { thread::sleep(ms(1000)); s.send(7).unwrap(); }); }) .unwrap(); } #[test] fn recv() { let (s, r) = bounded(100); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.recv(), Ok(7)); thread::sleep(ms(1000)); assert_eq!(r.recv(), Ok(8)); thread::sleep(ms(1000)); assert_eq!(r.recv(), Ok(9)); assert_eq!(r.recv(), Err(RecvError)); }); scope.spawn(move |_| { thread::sleep(ms(1500)); s.send(7).unwrap(); s.send(8).unwrap(); s.send(9).unwrap(); }); }) .unwrap(); } #[test] fn recv_timeout() { let (s, r) = bounded::(100); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout)); assert_eq!(r.recv_timeout(ms(1000)), Ok(7)); assert_eq!( r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Disconnected) ); }); scope.spawn(move |_| { thread::sleep(ms(1500)); s.send(7).unwrap(); }); }) .unwrap(); } #[test] fn try_send() { let (s, r) = bounded(1); scope(|scope| { scope.spawn(move |_| { assert_eq!(s.try_send(1), Ok(())); assert_eq!(s.try_send(2), Err(TrySendError::Full(2))); thread::sleep(ms(1500)); assert_eq!(s.try_send(3), Ok(())); thread::sleep(ms(500)); assert_eq!(s.try_send(4), Err(TrySendError::Disconnected(4))); }); scope.spawn(move |_| { thread::sleep(ms(1000)); assert_eq!(r.try_recv(), Ok(1)); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); assert_eq!(r.recv(), Ok(3)); }); }) .unwrap(); } #[test] fn send() { let (s, r) = bounded(1); scope(|scope| { scope.spawn(|_| { s.send(7).unwrap(); thread::sleep(ms(1000)); s.send(8).unwrap(); thread::sleep(ms(1000)); s.send(9).unwrap(); thread::sleep(ms(1000)); s.send(10).unwrap(); }); scope.spawn(|_| { thread::sleep(ms(1500)); assert_eq!(r.recv(), Ok(7)); assert_eq!(r.recv(), Ok(8)); assert_eq!(r.recv(), Ok(9)); }); }) .unwrap(); } #[test] fn send_timeout() { let (s, r) = bounded(2); scope(|scope| { scope.spawn(move |_| { assert_eq!(s.send_timeout(1, ms(1000)), Ok(())); assert_eq!(s.send_timeout(2, ms(1000)), Ok(())); assert_eq!( s.send_timeout(3, ms(500)), Err(SendTimeoutError::Timeout(3)) ); thread::sleep(ms(1000)); assert_eq!(s.send_timeout(4, ms(1000)), Ok(())); thread::sleep(ms(1000)); assert_eq!(s.send(5), Err(SendError(5))); }); scope.spawn(move |_| { thread::sleep(ms(1000)); assert_eq!(r.recv(), Ok(1)); thread::sleep(ms(1000)); assert_eq!(r.recv(), Ok(2)); assert_eq!(r.recv(), Ok(4)); }); }) .unwrap(); } #[test] fn send_after_disconnect() { let (s, r) = bounded(100); s.send(1).unwrap(); s.send(2).unwrap(); s.send(3).unwrap(); drop(r); assert_eq!(s.send(4), Err(SendError(4))); assert_eq!(s.try_send(5), Err(TrySendError::Disconnected(5))); assert_eq!( s.send_timeout(6, ms(500)), Err(SendTimeoutError::Disconnected(6)) ); } #[test] fn recv_after_disconnect() { let (s, r) = bounded(100); s.send(1).unwrap(); s.send(2).unwrap(); s.send(3).unwrap(); drop(s); assert_eq!(r.recv(), Ok(1)); assert_eq!(r.recv(), Ok(2)); assert_eq!(r.recv(), Ok(3)); assert_eq!(r.recv(), Err(RecvError)); } #[test] fn len() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 25_000; #[cfg(miri)] const CAP: usize = 50; #[cfg(not(miri))] const CAP: usize = 1000; let (s, r) = bounded(CAP); assert_eq!(s.len(), 0); assert_eq!(r.len(), 0); for _ in 0..CAP / 10 { for i in 0..50 { s.send(i).unwrap(); assert_eq!(s.len(), i + 1); } for i in 0..50 { r.recv().unwrap(); assert_eq!(r.len(), 50 - i - 1); } } assert_eq!(s.len(), 0); assert_eq!(r.len(), 0); for i in 0..CAP { s.send(i).unwrap(); assert_eq!(s.len(), i + 1); } for _ in 0..CAP { r.recv().unwrap(); } assert_eq!(s.len(), 0); assert_eq!(r.len(), 0); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { assert_eq!(r.recv(), Ok(i)); let len = r.len(); assert!(len <= CAP); } }); scope.spawn(|_| { for i in 0..COUNT { s.send(i).unwrap(); let len = s.len(); assert!(len <= CAP); } }); }) .unwrap(); assert_eq!(s.len(), 0); assert_eq!(r.len(), 0); } #[test] fn disconnect_wakes_sender() { let (s, r) = bounded(1); scope(|scope| { scope.spawn(move |_| { assert_eq!(s.send(()), Ok(())); assert_eq!(s.send(()), Err(SendError(()))); }); scope.spawn(move |_| { thread::sleep(ms(1000)); drop(r); }); }) .unwrap(); } #[test] fn disconnect_wakes_receiver() { let (s, r) = bounded::<()>(1); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.recv(), Err(RecvError)); }); scope.spawn(move |_| { thread::sleep(ms(1000)); drop(s); }); }) .unwrap(); } #[test] fn spsc() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 100_000; let (s, r) = bounded(3); scope(|scope| { scope.spawn(move |_| { for i in 0..COUNT { assert_eq!(r.recv(), Ok(i)); } assert_eq!(r.recv(), Err(RecvError)); }); scope.spawn(move |_| { for i in 0..COUNT { s.send(i).unwrap(); } }); }) .unwrap(); } #[test] fn mpmc() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 25_000; const THREADS: usize = 4; let (s, r) = bounded::(3); let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); scope(|scope| { for _ in 0..THREADS { scope.spawn(|_| { for _ in 0..COUNT { let n = r.recv().unwrap(); v[n].fetch_add(1, Ordering::SeqCst); } }); } for _ in 0..THREADS { scope.spawn(|_| { for i in 0..COUNT { s.send(i).unwrap(); } }); } }) .unwrap(); for c in v { assert_eq!(c.load(Ordering::SeqCst), THREADS); } } #[test] fn stress_oneshot() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; for _ in 0..COUNT { let (s, r) = bounded(1); scope(|scope| { scope.spawn(|_| r.recv().unwrap()); scope.spawn(|_| s.send(0).unwrap()); }) .unwrap(); } } #[test] fn stress_iter() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 100_000; let (request_s, request_r) = bounded(1); let (response_s, response_r) = bounded(1); scope(|scope| { scope.spawn(move |_| { let mut count = 0; loop { for x in response_r.try_iter() { count += x; if count == COUNT { return; } } request_s.send(()).unwrap(); } }); for _ in request_r.iter() { if response_s.send(1).is_err() { break; } } }) .unwrap(); } #[test] fn stress_timeout_two_threads() { const COUNT: usize = 100; let (s, r) = bounded(2); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(50)); } loop { if let Ok(()) = s.send_timeout(i, ms(10)) { break; } } } }); scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(50)); } loop { if let Ok(x) = r.recv_timeout(ms(10)) { assert_eq!(x, i); break; } } } }); }) .unwrap(); } #[test] fn drops() { #[cfg(miri)] const RUNS: usize = 10; #[cfg(not(miri))] const RUNS: usize = 100; #[cfg(miri)] const STEPS: usize = 100; #[cfg(not(miri))] const STEPS: usize = 10_000; static DROPS: AtomicUsize = AtomicUsize::new(0); #[derive(Debug, PartialEq)] struct DropCounter; impl Drop for DropCounter { fn drop(&mut self) { DROPS.fetch_add(1, Ordering::SeqCst); } } let mut rng = thread_rng(); for _ in 0..RUNS { let steps = rng.gen_range(0..STEPS); let additional = rng.gen_range(0..50); DROPS.store(0, Ordering::SeqCst); let (s, r) = bounded::(50); scope(|scope| { scope.spawn(|_| { for _ in 0..steps { r.recv().unwrap(); } }); scope.spawn(|_| { for _ in 0..steps { s.send(DropCounter).unwrap(); } }); }) .unwrap(); for _ in 0..additional { s.send(DropCounter).unwrap(); } assert_eq!(DROPS.load(Ordering::SeqCst), steps); drop(s); drop(r); assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional); } } #[test] fn linearizable() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 25_000; const THREADS: usize = 4; let (s, r) = bounded(THREADS); scope(|scope| { for _ in 0..THREADS { scope.spawn(|_| { for _ in 0..COUNT { s.send(0).unwrap(); r.try_recv().unwrap(); } }); } }) .unwrap(); } #[test] fn fairness() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded::<()>(COUNT); let (s2, r2) = bounded::<()>(COUNT); for _ in 0..COUNT { s1.send(()).unwrap(); s2.send(()).unwrap(); } let mut hits = [0usize; 2]; for _ in 0..COUNT { select! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); } #[test] fn fairness_duplicates() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s, r) = bounded::<()>(COUNT); for _ in 0..COUNT { s.send(()).unwrap(); } let mut hits = [0usize; 5]; for _ in 0..COUNT { select! { recv(r) -> _ => hits[0] += 1, recv(r) -> _ => hits[1] += 1, recv(r) -> _ => hits[2] += 1, recv(r) -> _ => hits[3] += 1, recv(r) -> _ => hits[4] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); } #[test] fn recv_in_send() { let (s, _r) = bounded(1); s.send(()).unwrap(); #[allow(unreachable_code)] { select! { send(s, panic!()) -> _ => panic!(), default => {} } } let (s, r) = bounded(2); s.send(()).unwrap(); select! { send(s, assert_eq!(r.recv(), Ok(()))) -> _ => {} } } #[test] fn channel_through_channel() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 1000; type T = Box; let (s, r) = bounded::(1); scope(|scope| { scope.spawn(move |_| { let mut s = s; for _ in 0..COUNT { let (new_s, new_r) = bounded(1); let new_r: T = Box::new(Some(new_r)); s.send(new_r).unwrap(); s = new_s; } }); scope.spawn(move |_| { let mut r = r; for _ in 0..COUNT { r = r .recv() .unwrap() .downcast_mut::>>() .unwrap() .take() .unwrap() } }); }) .unwrap(); } #[test] fn panic_on_drop() { struct Msg1<'a>(&'a mut bool); impl Drop for Msg1<'_> { fn drop(&mut self) { if *self.0 && !std::thread::panicking() { panic!("double drop"); } else { *self.0 = true; } } } struct Msg2<'a>(&'a mut bool); impl Drop for Msg2<'_> { fn drop(&mut self) { if *self.0 { panic!("double drop"); } else { *self.0 = true; panic!("first drop"); } } } // normal let (s, r) = bounded(2); let (mut a, mut b) = (false, false); s.send(Msg1(&mut a)).unwrap(); s.send(Msg1(&mut b)).unwrap(); drop(s); drop(r); assert!(a); assert!(b); // panic on drop let (s, r) = bounded(2); let (mut a, mut b) = (false, false); s.send(Msg2(&mut a)).unwrap(); s.send(Msg2(&mut b)).unwrap(); drop(s); let res = std::panic::catch_unwind(move || { drop(r); }); assert_eq!( *res.unwrap_err().downcast_ref::<&str>().unwrap(), "first drop" ); assert!(a); // Elements after the panicked element will leak. assert!(!b); } crossbeam-channel-0.5.14/tests/golang.rs000064400000000000000000001542751046102023000162740ustar 00000000000000//! Tests copied from Go and manually rewritten in Rust. //! //! Source: //! - https://github.com/golang/go //! //! Copyright & License: //! - Copyright (c) 2009 The Go Authors //! - https://golang.org/AUTHORS //! - https://golang.org/LICENSE //! - https://golang.org/PATENTS #![allow(clippy::redundant_clone)] use std::alloc::{GlobalAlloc, Layout, System}; use std::any::Any; use std::cell::Cell; use std::collections::HashMap; use std::sync::atomic::{AtomicI32, AtomicUsize, Ordering::SeqCst}; use std::sync::{Arc, Condvar, Mutex}; use std::thread; use std::time::Duration; use crossbeam_channel::{bounded, never, select, tick, unbounded, Receiver, Select, Sender}; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } struct Chan { inner: Arc>>, } struct ChanInner { s: Option>, r: Option>, // Receiver to use when r is None (Go blocks on receiving from nil) nil_r: Receiver, // Sender to use when s is None (Go blocks on sending to nil) nil_s: Sender, // Hold this receiver to prevent nil sender channel from disconnection _nil_sr: Receiver, } impl Clone for Chan { fn clone(&self) -> Chan { Chan { inner: self.inner.clone(), } } } impl Chan { fn send(&self, msg: T) { let s = self .inner .lock() .unwrap() .s .as_ref() .expect("sending into closed channel") .clone(); let _ = s.send(msg); } fn try_recv(&self) -> Option { let r = self.inner.lock().unwrap().r.as_ref().unwrap().clone(); r.try_recv().ok() } fn recv(&self) -> Option { let r = self.inner.lock().unwrap().r.as_ref().unwrap().clone(); r.recv().ok() } fn close_s(&self) { self.inner .lock() .unwrap() .s .take() .expect("channel sender already closed"); } fn close_r(&self) { self.inner .lock() .unwrap() .r .take() .expect("channel receiver already closed"); } fn has_rx(&self) -> bool { self.inner.lock().unwrap().r.is_some() } fn has_tx(&self) -> bool { self.inner.lock().unwrap().s.is_some() } fn rx(&self) -> Receiver { let inner = self.inner.lock().unwrap(); match inner.r.as_ref() { None => inner.nil_r.clone(), Some(r) => r.clone(), } } fn tx(&self) -> Sender { let inner = self.inner.lock().unwrap(); match inner.s.as_ref() { None => inner.nil_s.clone(), Some(s) => s.clone(), } } } impl Iterator for Chan { type Item = T; fn next(&mut self) -> Option { self.recv() } } impl<'a, T> IntoIterator for &'a Chan { type Item = T; type IntoIter = Chan; fn into_iter(self) -> Self::IntoIter { self.clone() } } fn make(cap: usize) -> Chan { let (s, r) = bounded(cap); let (nil_s, _nil_sr) = bounded(0); Chan { inner: Arc::new(Mutex::new(ChanInner { s: Some(s), r: Some(r), nil_r: never(), nil_s, _nil_sr, })), } } fn make_unbounded() -> Chan { let (s, r) = unbounded(); let (nil_s, _nil_sr) = bounded(0); Chan { inner: Arc::new(Mutex::new(ChanInner { s: Some(s), r: Some(r), nil_r: never(), nil_s, _nil_sr, })), } } #[derive(Clone)] struct WaitGroup(Arc); struct WaitGroupInner { cond: Condvar, count: Mutex, } impl WaitGroup { fn new() -> WaitGroup { WaitGroup(Arc::new(WaitGroupInner { cond: Condvar::new(), count: Mutex::new(0), })) } fn add(&self, delta: i32) { let mut count = self.0.count.lock().unwrap(); *count += delta; assert!(*count >= 0); self.0.cond.notify_all(); } fn done(&self) { self.add(-1); } fn wait(&self) { let mut count = self.0.count.lock().unwrap(); while *count > 0 { count = self.0.cond.wait(count).unwrap(); } } } struct Defer { f: Option>, } impl Drop for Defer { fn drop(&mut self) { let f = self.f.take().unwrap(); let mut f = Some(f); let mut f = move || f.take().unwrap()(); f(); } } struct Counter; static ALLOCATED: AtomicUsize = AtomicUsize::new(0); unsafe impl GlobalAlloc for Counter { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { let ret = System.alloc(layout); if !ret.is_null() { ALLOCATED.fetch_add(layout.size(), SeqCst); } ret } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { System.dealloc(ptr, layout); ALLOCATED.fetch_sub(layout.size(), SeqCst); } } #[global_allocator] static A: Counter = Counter; macro_rules! defer { ($body:expr) => { let _defer = Defer { f: Some(Box::new(|| $body)), }; }; } macro_rules! go { (@parse $v:ident, $($tail:tt)*) => {{ let $v = $v.clone(); go!(@parse $($tail)*) }}; (@parse $body:expr) => { ::std::thread::spawn(move || { let res = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| { $body })); if res.is_err() { eprintln!("goroutine panicked: {:?}", res); ::std::process::abort(); } }) }; (@parse $($tail:tt)*) => { compile_error!("invalid `go!` syntax") }; ($($tail:tt)*) => {{ go!(@parse $($tail)*) }}; } // https://github.com/golang/go/blob/master/test/chan/doubleselect.go mod doubleselect { use super::*; #[cfg(miri)] const ITERATIONS: i32 = 100; #[cfg(not(miri))] const ITERATIONS: i32 = 10_000; fn sender(n: i32, c1: Chan, c2: Chan, c3: Chan, c4: Chan) { defer! { c1.close_s() } defer! { c2.close_s() } defer! { c3.close_s() } defer! { c4.close_s() } for i in 0..n { select! { send(c1.tx(), i) -> _ => {} send(c2.tx(), i) -> _ => {} send(c3.tx(), i) -> _ => {} send(c4.tx(), i) -> _ => {} } } } fn mux(out: Chan, inp: Chan, done: Chan) { for v in inp { out.send(v); } done.send(true); } fn recver(inp: Chan) { let mut seen = HashMap::new(); for v in &inp { if seen.contains_key(&v) { panic!("got duplicate value for {}", v); } seen.insert(v, true); } } #[test] fn main() { let c1 = make::(0); let c2 = make::(0); let c3 = make::(0); let c4 = make::(0); let done = make::(0); let cmux = make::(0); go!(c1, c2, c3, c4, sender(ITERATIONS, c1, c2, c3, c4)); go!(cmux, c1, done, mux(cmux, c1, done)); go!(cmux, c2, done, mux(cmux, c2, done)); go!(cmux, c3, done, mux(cmux, c3, done)); go!(cmux, c4, done, mux(cmux, c4, done)); go!(done, cmux, { done.recv(); done.recv(); done.recv(); done.recv(); cmux.close_s(); }); recver(cmux); } } // https://github.com/golang/go/blob/master/test/chan/fifo.go mod fifo { use super::*; const N: i32 = 10; #[test] fn asynch_fifo() { let ch = make::(N as usize); for i in 0..N { ch.send(i); } for i in 0..N { if ch.recv() != Some(i) { panic!("bad receive"); } } } fn chain(ch: Chan, val: i32, inp: Chan, out: Chan) { inp.recv(); if ch.recv() != Some(val) { panic!("{}", val); } out.send(1); } #[test] fn synch_fifo() { let ch = make::(0); let mut inp = make::(0); let start = inp.clone(); for i in 0..N { let out = make::(0); go!(ch, i, inp, out, chain(ch, i, inp, out)); inp = out; } start.send(0); for i in 0..N { ch.send(i); } inp.recv(); } } // https://github.com/golang/go/blob/master/test/chan/goroutines.go mod goroutines { use super::*; fn f(left: Chan, right: Chan) { left.send(right.recv().unwrap()); } #[test] fn main() { let n = 100i32; let leftmost = make::(0); let mut right = leftmost.clone(); let mut left = leftmost.clone(); for _ in 0..n { right = make::(0); go!(left, right, f(left, right)); left = right.clone(); } go!(right, right.send(1)); leftmost.recv().unwrap(); } } // https://github.com/golang/go/blob/master/test/chan/nonblock.go mod nonblock { use super::*; fn i32receiver(c: Chan, strobe: Chan) { if c.recv().unwrap() != 123 { panic!("i32 value"); } strobe.send(true); } fn i32sender(c: Chan, strobe: Chan) { c.send(234); strobe.send(true); } fn i64receiver(c: Chan, strobe: Chan) { if c.recv().unwrap() != 123456 { panic!("i64 value"); } strobe.send(true); } fn i64sender(c: Chan, strobe: Chan) { c.send(234567); strobe.send(true); } fn breceiver(c: Chan, strobe: Chan) { if !c.recv().unwrap() { panic!("b value"); } strobe.send(true); } fn bsender(c: Chan, strobe: Chan) { c.send(true); strobe.send(true); } fn sreceiver(c: Chan, strobe: Chan) { if c.recv().unwrap() != "hello" { panic!("x value"); } strobe.send(true); } fn ssender(c: Chan, strobe: Chan) { c.send("hello again".to_string()); strobe.send(true); } const MAX_TRIES: usize = 10000; // Up to 100ms per test. #[test] fn main() { let ticker = tick(Duration::new(0, 10_000)); // 10 us let sleep = || { ticker.recv().unwrap(); ticker.recv().unwrap(); thread::yield_now(); thread::yield_now(); thread::yield_now(); }; let sync = make::(0); for buffer in 0..2 { let c32 = make::(buffer); let c64 = make::(buffer); let cb = make::(buffer); let cs = make::(buffer); select! { recv(c32.rx()) -> _ => panic!("blocked i32sender"), default => {} } select! { recv(c64.rx()) -> _ => panic!("blocked i64sender"), default => {} } select! { recv(cb.rx()) -> _ => panic!("blocked bsender"), default => {} } select! { recv(cs.rx()) -> _ => panic!("blocked ssender"), default => {} } go!(c32, sync, i32receiver(c32, sync)); let mut r#try = 0; loop { select! { send(c32.tx(), 123) -> _ => break, default => { r#try += 1; if r#try > MAX_TRIES { println!("i32receiver buffer={}", buffer); panic!("fail") } sleep(); } } } sync.recv(); go!(c32, sync, i32sender(c32, sync)); if buffer > 0 { sync.recv(); } let mut r#try = 0; loop { select! { recv(c32.rx()) -> v => { if v != Ok(234) { panic!("i32sender value"); } break; } default => { r#try += 1; if r#try > MAX_TRIES { println!("i32sender buffer={}", buffer); panic!("fail"); } sleep(); } } } if buffer == 0 { sync.recv(); } go!(c64, sync, i64receiver(c64, sync)); let mut r#try = 0; loop { select! { send(c64.tx(), 123456) -> _ => break, default => { r#try += 1; if r#try > MAX_TRIES { println!("i64receiver buffer={}", buffer); panic!("fail") } sleep(); } } } sync.recv(); go!(c64, sync, i64sender(c64, sync)); if buffer > 0 { sync.recv(); } let mut r#try = 0; loop { select! { recv(c64.rx()) -> v => { if v != Ok(234567) { panic!("i64sender value"); } break; } default => { r#try += 1; if r#try > MAX_TRIES { println!("i64sender buffer={}", buffer); panic!("fail"); } sleep(); } } } if buffer == 0 { sync.recv(); } go!(cb, sync, breceiver(cb, sync)); let mut r#try = 0; loop { select! { send(cb.tx(), true) -> _ => break, default => { r#try += 1; if r#try > MAX_TRIES { println!("breceiver buffer={}", buffer); panic!("fail") } sleep(); } } } sync.recv(); go!(cb, sync, bsender(cb, sync)); if buffer > 0 { sync.recv(); } let mut r#try = 0; loop { select! { recv(cb.rx()) -> v => { if v != Ok(true) { panic!("bsender value"); } break; } default => { r#try += 1; if r#try > MAX_TRIES { println!("bsender buffer={}", buffer); panic!("fail"); } sleep(); } } } if buffer == 0 { sync.recv(); } go!(cs, sync, sreceiver(cs, sync)); let mut r#try = 0; loop { select! { send(cs.tx(), "hello".to_string()) -> _ => break, default => { r#try += 1; if r#try > MAX_TRIES { println!("sreceiver buffer={}", buffer); panic!("fail") } sleep(); } } } sync.recv(); go!(cs, sync, ssender(cs, sync)); if buffer > 0 { sync.recv(); } let mut r#try = 0; loop { select! { recv(cs.rx()) -> v => { if v != Ok("hello again".to_string()) { panic!("ssender value"); } break; } default => { r#try += 1; if r#try > MAX_TRIES { println!("ssender buffer={}", buffer); panic!("fail"); } sleep(); } } } if buffer == 0 { sync.recv(); } } } } // https://github.com/golang/go/blob/master/test/chan/select.go mod select { use super::*; #[test] fn main() { let shift = Cell::new(0); let counter = Cell::new(0); let get_value = || { counter.set(counter.get() + 1); 1 << shift.get() }; let send = |mut a: Option<&Chan>, mut b: Option<&Chan>| { let mut i = 0; let never = make::(0); loop { let nil1 = never.tx(); let nil2 = never.tx(); let v1 = get_value(); let v2 = get_value(); select! { send(a.map(|c| c.tx()).unwrap_or(nil1), v1) -> _ => { i += 1; a = None; } send(b.map(|c| c.tx()).unwrap_or(nil2), v2) -> _ => { i += 1; b = None; } default => break, } shift.set(shift.get() + 1); } i }; let a = make::(1); let b = make::(1); assert_eq!(send(Some(&a), Some(&b)), 2); let av = a.recv().unwrap(); let bv = b.recv().unwrap(); assert_eq!(av | bv, 3); assert_eq!(send(Some(&a), None), 1); assert_eq!(counter.get(), 10); } } // https://github.com/golang/go/blob/master/test/chan/select2.go mod select2 { use super::*; #[cfg(miri)] const N: i32 = 200; #[cfg(not(miri))] const N: i32 = 100000; #[test] fn main() { fn sender(c: &Chan, n: i32) { for _ in 0..n { c.send(1); } } fn receiver(c: &Chan, dummy: &Chan, n: i32) { for _ in 0..n { select! { recv(c.rx()) -> _ => {} recv(dummy.rx()) -> _ => { panic!("dummy"); } } } } let c = make_unbounded::(); let dummy = make_unbounded::(); ALLOCATED.store(0, SeqCst); go!(c, sender(&c, N)); receiver(&c, &dummy, N); let alloc = ALLOCATED.load(SeqCst); go!(c, sender(&c, N)); receiver(&c, &dummy, N); assert!( !(ALLOCATED.load(SeqCst) > alloc && (ALLOCATED.load(SeqCst) - alloc) > (N as usize + 10000)) ) } } // https://github.com/golang/go/blob/master/test/chan/select3.go mod select3 { // TODO } // https://github.com/golang/go/blob/master/test/chan/select4.go mod select4 { use super::*; #[test] fn main() { let c = make::(1); let c1 = make::(0); c.send(42); select! { recv(c1.rx()) -> _ => panic!("BUG"), recv(c.rx()) -> v => assert_eq!(v, Ok(42)), } } } // https://github.com/golang/go/blob/master/test/chan/select6.go mod select6 { use super::*; #[test] fn main() { let c1 = make::(0); let c2 = make::(0); let c3 = make::(0); go!(c1, c1.recv()); go!(c1, c2, c3, { select! { recv(c1.rx()) -> _ => panic!("dummy"), recv(c2.rx()) -> _ => c3.send(true), } c1.recv(); }); go!(c2, c2.send(true)); c3.recv(); c1.send(true); c1.send(true); } } // https://github.com/golang/go/blob/master/test/chan/select7.go mod select7 { use super::*; fn recv1(c: Chan) { c.recv().unwrap(); } fn recv2(c: Chan) { select! { recv(c.rx()) -> _ => () } } fn recv3(c: Chan) { let c2 = make::(1); select! { recv(c.rx()) -> _ => (), recv(c2.rx()) -> _ => () } } fn send1(recv: fn(Chan)) { let c = make::(1); go!(c, recv(c)); thread::yield_now(); c.send(1); } fn send2(recv: fn(Chan)) { let c = make::(1); go!(c, recv(c)); thread::yield_now(); select! { send(c.tx(), 1) -> _ => () } } fn send3(recv: fn(Chan)) { let c = make::(1); go!(c, recv(c)); thread::yield_now(); let c2 = make::(1); select! { send(c.tx(), 1) -> _ => (), send(c2.tx(), 1) -> _ => () } } #[test] fn main() { send1(recv1); send2(recv1); send3(recv1); send1(recv2); send2(recv2); send3(recv2); send1(recv3); send2(recv3); send3(recv3); } } // https://github.com/golang/go/blob/master/test/chan/sieve1.go mod sieve1 { use super::*; fn generate(ch: Chan) { let mut i = 2; loop { ch.send(i); i += 1; } } fn filter(in_ch: Chan, out_ch: Chan, prime: i32) { for i in in_ch { if i % prime != 0 { out_ch.send(i); } } } fn sieve(primes: Chan) { let mut ch = make::(1); go!(ch, generate(ch)); loop { let prime = ch.recv().unwrap(); primes.send(prime); let ch1 = make::(1); go!(ch, ch1, prime, filter(ch, ch1, prime)); ch = ch1; } } #[test] fn main() { let primes = make::(1); go!(primes, sieve(primes)); let a = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, ]; #[cfg(miri)] let a = &a[..10]; for item in a.iter() { let x = primes.recv().unwrap(); if x != *item { println!("{} != {}", x, item); panic!("fail"); } } } } // https://github.com/golang/go/blob/master/test/chan/zerosize.go mod zerosize { use super::*; #[test] fn zero_size_struct() { struct ZeroSize; let _ = make::(0); } #[test] fn zero_size_array() { let _ = make::<[u8; 0]>(0); } } // https://github.com/golang/go/blob/master/src/runtime/chan_test.go mod chan_test { use super::*; #[test] fn test_chan() { #[cfg(miri)] const N: i32 = 12; #[cfg(not(miri))] const N: i32 = 200; #[cfg(miri)] const MESSAGES_COUNT: i32 = 20; #[cfg(not(miri))] const MESSAGES_COUNT: i32 = 100; for cap in 0..N { { // Ensure that receive from empty chan blocks. let c = make::(cap as usize); let recv1 = Arc::new(Mutex::new(false)); go!(c, recv1, { c.recv(); *recv1.lock().unwrap() = true; }); let recv2 = Arc::new(Mutex::new(false)); go!(c, recv2, { c.recv(); *recv2.lock().unwrap() = true; }); thread::sleep(ms(1)); if *recv1.lock().unwrap() || *recv2.lock().unwrap() { panic!(); } // Ensure that non-blocking receive does not block. select! { recv(c.rx()) -> _ => panic!(), default => {} } select! { recv(c.rx()) -> _ => panic!(), default => {} } c.send(0); c.send(0); } { // Ensure that send to full chan blocks. let c = make::(cap as usize); for i in 0..cap { c.send(i); } let sent = Arc::new(Mutex::new(0)); go!(sent, c, { c.send(0); *sent.lock().unwrap() = 1; }); thread::sleep(ms(1)); if *sent.lock().unwrap() != 0 { panic!(); } // Ensure that non-blocking send does not block. select! { send(c.tx(), 0) -> _ => panic!(), default => {} } c.recv(); } { // Ensure that we receive 0 from closed chan. let c = make::(cap as usize); for i in 0..cap { c.send(i); } c.close_s(); for i in 0..cap { let v = c.recv(); if v != Some(i) { panic!(); } } if c.recv() != None { panic!(); } if c.try_recv() != None { panic!(); } } { // Ensure that close unblocks receive. let c = make::(cap as usize); let done = make::(0); go!(c, done, { let v = c.try_recv(); done.send(v.is_none()); }); thread::sleep(ms(1)); c.close_s(); if !done.recv().unwrap() { panic!(); } } { // Send many integers, // ensure that we receive them non-corrupted in FIFO order. let c = make::(cap as usize); go!(c, { for i in 0..MESSAGES_COUNT { c.send(i); } }); for i in 0..MESSAGES_COUNT { if c.recv() != Some(i) { panic!(); } } // Same, but using recv2. go!(c, { for i in 0..MESSAGES_COUNT { c.send(i); } }); for i in 0..MESSAGES_COUNT { if c.recv() != Some(i) { panic!(); } } } } } #[test] fn test_nonblock_recv_race() { #[cfg(miri)] const N: usize = 100; #[cfg(not(miri))] const N: usize = 1000; for _ in 0..N { let c = make::(1); c.send(1); let t = go!(c, { select! { recv(c.rx()) -> _ => {} default => panic!("chan is not ready"), } }); c.close_s(); c.recv(); t.join().unwrap(); } } #[test] fn test_nonblock_select_race() { #[cfg(miri)] const N: usize = 100; #[cfg(not(miri))] const N: usize = 1000; let done = make::(1); for _ in 0..N { let c1 = make::(1); let c2 = make::(1); c1.send(1); go!(c1, c2, done, { select! { recv(c1.rx()) -> _ => {} recv(c2.rx()) -> _ => {} default => { done.send(false); return; } } done.send(true); }); c2.send(1); select! { recv(c1.rx()) -> _ => {} default => {} } if !done.recv().unwrap() { panic!("no chan is ready"); } } } #[test] fn test_nonblock_select_race2() { #[cfg(miri)] const N: usize = 100; #[cfg(not(miri))] const N: usize = 1000; let done = make::(1); for _ in 0..N { let c1 = make::(1); let c2 = make::(0); c1.send(1); go!(c1, c2, done, { select! { recv(c1.rx()) -> _ => {} recv(c2.rx()) -> _ => {} default => { done.send(false); return; } } done.send(true); }); c2.close_s(); select! { recv(c1.rx()) -> _ => {} default => {} } if !done.recv().unwrap() { panic!("no chan is ready"); } } } #[test] fn test_self_select() { // Ensure that send/recv on the same chan in select // does not crash nor deadlock. #[cfg(miri)] const N: usize = 100; #[cfg(not(miri))] const N: usize = 1000; for &cap in &[0, 10] { let wg = WaitGroup::new(); wg.add(2); let c = make::(cap); for p in 0..2 { let p = p; go!(wg, p, c, { defer! { wg.done() } for i in 0..N { if p == 0 || i % 2 == 0 { select! { send(c.tx(), p) -> _ => {} recv(c.rx()) -> v => { if cap == 0 && v.ok() == Some(p) { panic!("self receive"); } } } } else { select! { recv(c.rx()) -> v => { if cap == 0 && v.ok() == Some(p) { panic!("self receive"); } } send(c.tx(), p) -> _ => {} } } } }); } wg.wait(); } } #[test] fn test_select_stress() { #[cfg(miri)] const N: usize = 100; #[cfg(not(miri))] const N: usize = 10000; let c = vec![ make::(0), make::(0), make::(2), make::(3), ]; // There are 4 goroutines that send N values on each of the chans, // + 4 goroutines that receive N values on each of the chans, // + 1 goroutine that sends N values on each of the chans in a single select, // + 1 goroutine that receives N values on each of the chans in a single select. // All these sends, receives and selects interact chaotically at runtime, // but we are careful that this whole construct does not deadlock. let wg = WaitGroup::new(); wg.add(10); for k in 0..4 { go!(k, c, wg, { for _ in 0..N { c[k].send(0); } wg.done(); }); go!(k, c, wg, { for _ in 0..N { c[k].recv(); } wg.done(); }); } go!(c, wg, { let mut n = [0; 4]; let mut c1 = c.iter().map(|c| Some(c.rx().clone())).collect::>(); for _ in 0..4 * N { let index = { let mut sel = Select::new(); let mut opers = [!0; 4]; for &i in &[3, 2, 0, 1] { if let Some(c) = &c1[i] { opers[i] = sel.recv(c); } } let oper = sel.select(); let mut index = !0; for i in 0..4 { if opers[i] == oper.index() { index = i; let _ = oper.recv(c1[i].as_ref().unwrap()); break; } } index }; n[index] += 1; if n[index] == N { c1[index] = None; } } wg.done(); }); go!(c, wg, { let mut n = [0; 4]; let mut c1 = c.iter().map(|c| Some(c.tx().clone())).collect::>(); for _ in 0..4 * N { let index = { let mut sel = Select::new(); let mut opers = [!0; 4]; for &i in &[0, 1, 2, 3] { if let Some(c) = &c1[i] { opers[i] = sel.send(c); } } let oper = sel.select(); let mut index = !0; for i in 0..4 { if opers[i] == oper.index() { index = i; let _ = oper.send(c1[i].as_ref().unwrap(), 0); break; } } index }; n[index] += 1; if n[index] == N { c1[index] = None; } } wg.done(); }); wg.wait(); } #[test] fn test_select_fairness() { #[cfg(miri)] const TRIALS: usize = 100; #[cfg(not(miri))] const TRIALS: usize = 10000; let c1 = make::(TRIALS + 1); let c2 = make::(TRIALS + 1); for _ in 0..TRIALS + 1 { c1.send(1); c2.send(2); } let c3 = make::(0); let c4 = make::(0); let out = make::(0); let done = make::(0); let wg = WaitGroup::new(); wg.add(1); go!(wg, c1, c2, c3, c4, out, done, { defer! { wg.done() }; loop { let b; select! { recv(c3.rx()) -> m => b = m.unwrap(), recv(c4.rx()) -> m => b = m.unwrap(), recv(c1.rx()) -> m => b = m.unwrap(), recv(c2.rx()) -> m => b = m.unwrap(), } select! { send(out.tx(), b) -> _ => {} recv(done.rx()) -> _ => return, } } }); let (mut cnt1, mut cnt2) = (0, 0); for _ in 0..TRIALS { match out.recv() { Some(1) => cnt1 += 1, Some(2) => cnt2 += 1, b => panic!("unexpected value {:?} on channel", b), } } // If the select in the goroutine is fair, // cnt1 and cnt2 should be about the same value. // With 10,000 trials, the expected margin of error at // a confidence level of five nines is 4.4172 / (2 * Sqrt(10000)). let r = cnt1 as f64 / TRIALS as f64; let e = (r - 0.5).abs(); if e > 4.4172 / (2.0 * (TRIALS as f64).sqrt()) { panic!( "unfair select: in {} trials, results were {}, {}", TRIALS, cnt1, cnt2, ); } done.close_s(); wg.wait(); } #[test] fn test_chan_send_interface() { struct Mt; let c = make::>(1); c.send(Box::new(Mt)); select! { send(c.tx(), Box::new(Mt)) -> _ => {} default => {} } select! { send(c.tx(), Box::new(Mt)) -> _ => {} send(c.tx(), Box::new(Mt)) -> _ => {} default => {} } } #[test] fn test_pseudo_random_send() { #[cfg(miri)] const N: usize = 20; #[cfg(not(miri))] const N: usize = 100; for cap in 0..N { let c = make::(cap); let l = Arc::new(Mutex::new(vec![0i32; N])); let done = make::(0); go!(c, done, l, { let mut l = l.lock().unwrap(); for i in 0..N { thread::yield_now(); l[i] = c.recv().unwrap(); } done.send(true); }); for _ in 0..N { select! { send(c.tx(), 1) -> _ => {} send(c.tx(), 0) -> _ => {} } } done.recv(); let mut n0 = 0; let mut n1 = 0; for &i in l.lock().unwrap().iter() { n0 += (i + 1) % 2; n1 += i; } if n0 <= N as i32 / 10 || n1 <= N as i32 / 10 { panic!( "Want pseudorandom, got {} zeros and {} ones (chan cap {})", n0, n1, cap, ); } } } #[test] fn test_multi_consumer() { const NWORK: usize = 23; #[cfg(miri)] const NITER: usize = 50; #[cfg(not(miri))] const NITER: usize = 271828; let pn = [2, 3, 7, 11, 13, 17, 19, 23, 27, 31]; let q = make::(NWORK * 3); let r = make::(NWORK * 3); let wg = WaitGroup::new(); for i in 0..NWORK { wg.add(1); let w = i; go!(q, r, wg, pn, { for v in &q { if pn[w % pn.len()] == v { thread::yield_now(); } r.send(v); } wg.done(); }); } let expect = Arc::new(Mutex::new(0)); go!(q, r, expect, wg, pn, { for i in 0..NITER { let v = pn[i % pn.len()]; *expect.lock().unwrap() += v; q.send(v); } q.close_s(); wg.wait(); r.close_s(); }); let mut n = 0; let mut s = 0; for v in &r { n += 1; s += v; } if n != NITER || s != *expect.lock().unwrap() { panic!(); } } #[test] fn test_select_duplicate_channel() { // This test makes sure we can queue a G on // the same channel multiple times. let c = make::(0); let d = make::(0); let e = make::(0); go!(c, d, e, { select! { recv(c.rx()) -> _ => {} recv(d.rx()) -> _ => {} recv(e.rx()) -> _ => {} } e.send(9); }); thread::sleep(ms(1)); go!(c, c.recv()); thread::sleep(ms(1)); d.send(7); e.recv(); c.send(8); } } // https://github.com/golang/go/blob/master/test/closedchan.go mod closedchan { // TODO } // https://github.com/golang/go/blob/master/src/runtime/chanbarrier_test.go mod chanbarrier_test { // TODO } // https://github.com/golang/go/blob/master/src/runtime/race/testdata/chan_test.go mod race_chan_test { // TODO } // https://github.com/golang/go/blob/master/test/ken/chan.go mod chan { use super::*; const MESSAGES_PER_CHANEL: u32 = 76; const MESSAGES_RANGE_LEN: u32 = 100; const END: i32 = 10000; struct ChanWithVals { chan: Chan, /// Next value to send sv: Arc, /// Next value to receive rv: Arc, } struct Totals { /// Total sent messages tots: u32, /// Total received messages totr: u32, } struct Context { nproc: Arc>, cval: Arc>, tot: Arc>, nc: ChanWithVals, randx: Arc>, } impl ChanWithVals { fn with_capacity(capacity: usize) -> Self { ChanWithVals { chan: make(capacity), sv: Arc::new(AtomicI32::new(0)), rv: Arc::new(AtomicI32::new(0)), } } fn closed() -> Self { let ch = ChanWithVals::with_capacity(0); ch.chan.close_r(); ch.chan.close_s(); ch } fn rv(&self) -> i32 { self.rv.load(SeqCst) } fn sv(&self) -> i32 { self.sv.load(SeqCst) } fn send(&mut self, tot: &Mutex) -> bool { { let mut tot = tot.lock().unwrap(); tot.tots += 1 } let esv = expect(self.sv(), self.sv()); self.sv.store(esv, SeqCst); if self.sv() == END { self.chan.close_s(); return true; } false } fn recv(&mut self, v: i32, tot: &Mutex) -> bool { { let mut tot = tot.lock().unwrap(); tot.totr += 1 } let erv = expect(self.rv(), v); self.rv.store(erv, SeqCst); if self.rv() == END { self.chan.close_r(); return true; } false } } impl Clone for ChanWithVals { fn clone(&self) -> Self { ChanWithVals { chan: self.chan.clone(), sv: self.sv.clone(), rv: self.rv.clone(), } } } impl Context { fn nproc(&self) -> &Mutex { self.nproc.as_ref() } fn cval(&self) -> &Mutex { self.cval.as_ref() } fn tot(&self) -> &Mutex { self.tot.as_ref() } fn randx(&self) -> &Mutex { self.randx.as_ref() } } impl Clone for Context { fn clone(&self) -> Self { Context { nproc: self.nproc.clone(), cval: self.cval.clone(), tot: self.tot.clone(), nc: self.nc.clone(), randx: self.randx.clone(), } } } fn nrand(n: i32, randx: &Mutex) -> i32 { let mut randx = randx.lock().unwrap(); *randx += 10007; if *randx >= 1000000 { *randx -= 1000000 } *randx % n } fn change_nproc(adjust: i32, nproc: &Mutex) -> i32 { let mut nproc = nproc.lock().unwrap(); *nproc += adjust; *nproc } fn mkchan(c: usize, n: usize, cval: &Mutex) -> Vec { let mut ca = Vec::::with_capacity(n); let mut cval = cval.lock().unwrap(); for _ in 0..n { *cval += MESSAGES_RANGE_LEN as i32; let chl = ChanWithVals::with_capacity(c); chl.sv.store(*cval, SeqCst); chl.rv.store(*cval, SeqCst); ca.push(chl); } ca } fn expect(v: i32, v0: i32) -> i32 { if v == v0 { return if v % MESSAGES_RANGE_LEN as i32 == MESSAGES_PER_CHANEL as i32 - 1 { END } else { v + 1 }; } panic!("got {}, expected {}", v, v0 + 1); } fn send(mut c: ChanWithVals, ctx: Context) { loop { for _ in 0..=nrand(10, ctx.randx()) { thread::yield_now(); } c.chan.tx().send(c.sv()).unwrap(); if c.send(ctx.tot()) { break; } } change_nproc(-1, ctx.nproc()); } fn recv(mut c: ChanWithVals, ctx: Context) { loop { for _ in (0..nrand(10, ctx.randx())).rev() { thread::yield_now(); } let v = c.chan.rx().recv().unwrap(); if c.recv(v, ctx.tot()) { break; } } change_nproc(-1, ctx.nproc()); } #[allow(clippy::too_many_arguments)] fn sel( mut r0: ChanWithVals, mut r1: ChanWithVals, mut r2: ChanWithVals, mut r3: ChanWithVals, mut s0: ChanWithVals, mut s1: ChanWithVals, mut s2: ChanWithVals, mut s3: ChanWithVals, ctx: Context, ) { let mut a = 0; // local chans running if r0.chan.has_rx() { a += 1; } if r1.chan.has_rx() { a += 1; } if r2.chan.has_rx() { a += 1; } if r3.chan.has_rx() { a += 1; } if s0.chan.has_tx() { a += 1; } if s1.chan.has_tx() { a += 1; } if s2.chan.has_tx() { a += 1; } if s3.chan.has_tx() { a += 1; } loop { for _ in 0..=nrand(5, ctx.randx()) { thread::yield_now(); } select! { recv(r0.chan.rx()) -> v => if r0.recv(v.unwrap(), ctx.tot()) { a -= 1 }, recv(r1.chan.rx()) -> v => if r1.recv(v.unwrap(), ctx.tot()) { a -= 1 }, recv(r2.chan.rx()) -> v => if r2.recv(v.unwrap(), ctx.tot()) { a -= 1 }, recv(r3.chan.rx()) -> v => if r3.recv(v.unwrap(), ctx.tot()) { a -= 1 }, send(s0.chan.tx(), s0.sv()) -> _ => if s0.send(ctx.tot()) { a -= 1 }, send(s1.chan.tx(), s1.sv()) -> _ => if s1.send(ctx.tot()) { a -= 1 }, send(s2.chan.tx(), s2.sv()) -> _ => if s2.send(ctx.tot()) { a -= 1 }, send(s3.chan.tx(), s3.sv()) -> _ => if s3.send(ctx.tot()) { a -= 1 }, } if a == 0 { break; } } change_nproc(-1, ctx.nproc()); } fn get(vec: &[ChanWithVals], idx: usize) -> ChanWithVals { vec.get(idx).unwrap().clone() } /// Direct send to direct recv fn test1(c: ChanWithVals, ctx: &mut Context) { change_nproc(2, ctx.nproc()); go!(c, ctx, send(c, ctx)); go!(c, ctx, recv(c, ctx)); } /// Direct send to select recv fn test2(c: usize, ctx: &mut Context) { let ca = mkchan(c, 4, ctx.cval()); change_nproc(4, ctx.nproc()); go!(ca, ctx, send(get(&ca, 0), ctx)); go!(ca, ctx, send(get(&ca, 1), ctx)); go!(ca, ctx, send(get(&ca, 2), ctx)); go!(ca, ctx, send(get(&ca, 3), ctx)); change_nproc(1, ctx.nproc()); go!( ca, ctx, sel( get(&ca, 0), get(&ca, 1), get(&ca, 2), get(&ca, 3), ctx.nc.clone(), ctx.nc.clone(), ctx.nc.clone(), ctx.nc.clone(), ctx, ) ); } /// Select send to direct recv fn test3(c: usize, ctx: &mut Context) { let ca = mkchan(c, 4, ctx.cval()); change_nproc(4, ctx.nproc()); go!(ca, ctx, recv(get(&ca, 0), ctx)); go!(ca, ctx, recv(get(&ca, 1), ctx)); go!(ca, ctx, recv(get(&ca, 2), ctx)); go!(ca, ctx, recv(get(&ca, 3), ctx)); change_nproc(1, ctx.nproc()); go!( ca, ctx, sel( ctx.nc.clone(), ctx.nc.clone(), ctx.nc.clone(), ctx.nc.clone(), get(&ca, 0), get(&ca, 1), get(&ca, 2), get(&ca, 3), ctx, ) ); } /// Select send to select recv, 4 channels fn test4(c: usize, ctx: &mut Context) { let ca = mkchan(c, 4, ctx.cval()); change_nproc(2, ctx.nproc()); go!( ca, ctx, sel( ctx.nc.clone(), ctx.nc.clone(), ctx.nc.clone(), ctx.nc.clone(), get(&ca, 0), get(&ca, 1), get(&ca, 2), get(&ca, 3), ctx, ) ); go!( ca, ctx, sel( get(&ca, 0), get(&ca, 1), get(&ca, 2), get(&ca, 3), ctx.nc.clone(), ctx.nc.clone(), ctx.nc.clone(), ctx.nc.clone(), ctx, ) ); } /// Select send to select recv, 8 channels fn test5(c: usize, ctx: &mut Context) { let ca = mkchan(c, 8, ctx.cval()); change_nproc(2, ctx.nproc()); go!( ca, ctx, sel( get(&ca, 4), get(&ca, 5), get(&ca, 6), get(&ca, 7), get(&ca, 0), get(&ca, 1), get(&ca, 2), get(&ca, 3), ctx, ) ); go!( ca, ctx, sel( get(&ca, 0), get(&ca, 1), get(&ca, 2), get(&ca, 3), get(&ca, 4), get(&ca, 5), get(&ca, 6), get(&ca, 7), ctx, ) ); } // Direct and select send to direct and select recv fn test6(c: usize, ctx: &mut Context) { let ca = mkchan(c, 12, ctx.cval()); change_nproc(4, ctx.nproc()); go!(ca, ctx, send(get(&ca, 4), ctx)); go!(ca, ctx, send(get(&ca, 5), ctx)); go!(ca, ctx, send(get(&ca, 6), ctx)); go!(ca, ctx, send(get(&ca, 7), ctx)); change_nproc(4, ctx.nproc()); go!(ca, ctx, recv(get(&ca, 8), ctx)); go!(ca, ctx, recv(get(&ca, 9), ctx)); go!(ca, ctx, recv(get(&ca, 10), ctx)); go!(ca, ctx, recv(get(&ca, 11), ctx)); change_nproc(2, ctx.nproc()); go!( ca, ctx, sel( get(&ca, 4), get(&ca, 5), get(&ca, 6), get(&ca, 7), get(&ca, 0), get(&ca, 1), get(&ca, 2), get(&ca, 3), ctx, ) ); go!( ca, ctx, sel( get(&ca, 0), get(&ca, 1), get(&ca, 2), get(&ca, 3), get(&ca, 8), get(&ca, 9), get(&ca, 10), get(&ca, 11), ctx, ) ); } fn wait(ctx: &mut Context) { thread::yield_now(); while change_nproc(0, ctx.nproc()) != 0 { thread::yield_now(); } } fn tests(c: usize, ctx: &mut Context) { let ca = mkchan(c, 4, ctx.cval()); test1(get(&ca, 0), ctx); test1(get(&ca, 1), ctx); test1(get(&ca, 2), ctx); test1(get(&ca, 3), ctx); wait(ctx); test2(c, ctx); wait(ctx); test3(c, ctx); wait(ctx); test4(c, ctx); wait(ctx); test5(c, ctx); wait(ctx); test6(c, ctx); wait(ctx); } #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn main() { let mut ctx = Context { nproc: Arc::new(Mutex::new(0)), cval: Arc::new(Mutex::new(0)), tot: Arc::new(Mutex::new(Totals { tots: 0, totr: 0 })), nc: ChanWithVals::closed(), randx: Arc::new(Mutex::new(0)), }; tests(0, &mut ctx); tests(1, &mut ctx); tests(10, &mut ctx); tests(100, &mut ctx); #[rustfmt::skip] let t = 4 * // buffer sizes (4*4 + // tests 1,2,3,4 channels 8 + // test 5 channels 12) * // test 6 channels MESSAGES_PER_CHANEL; // sends/recvs on a channel let tot = ctx.tot.lock().unwrap(); if tot.tots != t || tot.totr != t { panic!("tots={} totr={} sb={}", tot.tots, tot.totr, t); } } } // https://github.com/golang/go/blob/master/test/ken/chan1.go mod chan1 { use super::*; // sent messages #[cfg(miri)] const N: usize = 20; #[cfg(not(miri))] const N: usize = 1000; // receiving "goroutines" const M: usize = 10; // channel buffering const W: usize = 2; fn r(c: Chan, m: usize, h: Arc>) { loop { select! { recv(c.rx()) -> rr => { let r = rr.unwrap(); let mut data = h.lock().unwrap(); if data[r] != 1 { println!("r\nm={}\nr={}\nh={}\n", m, r, data[r]); panic!("fail") } data[r] = 2; } } } } fn s(c: Chan, h: Arc>) { for n in 0..N { let r = n; let mut data = h.lock().unwrap(); if data[r] != 0 { println!("s"); panic!("fail"); } data[r] = 1; // https://github.com/crossbeam-rs/crossbeam/pull/615#discussion_r550281094 drop(data); c.send(r); } } #[test] fn main() { let h = Arc::new(Mutex::new([0usize; N])); let c = make::(W); for m in 0..M { go!(c, h, { r(c, m, h); }); thread::yield_now(); } thread::yield_now(); thread::yield_now(); s(c, h); } } crossbeam-channel-0.5.14/tests/iter.rs000064400000000000000000000046561046102023000157650ustar 00000000000000//! Tests for iteration over receivers. use crossbeam_channel::unbounded; use crossbeam_utils::thread::scope; #[test] fn nested_recv_iter() { let (s, r) = unbounded::(); let (total_s, total_r) = unbounded::(); scope(|scope| { scope.spawn(move |_| { let mut acc = 0; for x in r.iter() { acc += x; } total_s.send(acc).unwrap(); }); s.send(3).unwrap(); s.send(1).unwrap(); s.send(2).unwrap(); drop(s); assert_eq!(total_r.recv().unwrap(), 6); }) .unwrap(); } #[test] fn recv_iter_break() { let (s, r) = unbounded::(); let (count_s, count_r) = unbounded(); scope(|scope| { scope.spawn(move |_| { let mut count = 0; for x in r.iter() { if count >= 3 { break; } else { count += x; } } count_s.send(count).unwrap(); }); s.send(2).unwrap(); s.send(2).unwrap(); s.send(2).unwrap(); let _ = s.send(2); drop(s); assert_eq!(count_r.recv().unwrap(), 4); }) .unwrap(); } #[test] fn recv_try_iter() { let (request_s, request_r) = unbounded(); let (response_s, response_r) = unbounded(); scope(|scope| { scope.spawn(move |_| { let mut count = 0; loop { for x in response_r.try_iter() { count += x; if count == 6 { return; } } request_s.send(()).unwrap(); } }); for _ in request_r.iter() { if response_s.send(2).is_err() { break; } } }) .unwrap(); } #[test] fn recv_into_iter_owned() { let mut iter = { let (s, r) = unbounded::(); s.send(1).unwrap(); s.send(2).unwrap(); r.into_iter() }; assert_eq!(iter.next().unwrap(), 1); assert_eq!(iter.next().unwrap(), 2); assert!(iter.next().is_none()); } #[test] fn recv_into_iter_borrowed() { let (s, r) = unbounded::(); s.send(1).unwrap(); s.send(2).unwrap(); drop(s); let mut iter = (&r).into_iter(); assert_eq!(iter.next().unwrap(), 1); assert_eq!(iter.next().unwrap(), 2); assert!(iter.next().is_none()); } crossbeam-channel-0.5.14/tests/list.rs000064400000000000000000000316171046102023000157720ustar 00000000000000//! Tests for the list channel flavor. use std::any::Any; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use std::thread; use std::time::Duration; use crossbeam_channel::{select, unbounded, Receiver}; use crossbeam_channel::{RecvError, RecvTimeoutError, TryRecvError}; use crossbeam_channel::{SendError, SendTimeoutError, TrySendError}; use crossbeam_utils::thread::scope; use rand::{thread_rng, Rng}; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] fn smoke() { let (s, r) = unbounded(); s.try_send(7).unwrap(); assert_eq!(r.try_recv(), Ok(7)); s.send(8).unwrap(); assert_eq!(r.recv(), Ok(8)); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout)); } #[test] fn capacity() { let (s, r) = unbounded::<()>(); assert_eq!(s.capacity(), None); assert_eq!(r.capacity(), None); } #[test] fn len_empty_full() { let (s, r) = unbounded(); assert_eq!(s.len(), 0); assert!(s.is_empty()); assert!(!s.is_full()); assert_eq!(r.len(), 0); assert!(r.is_empty()); assert!(!r.is_full()); s.send(()).unwrap(); assert_eq!(s.len(), 1); assert!(!s.is_empty()); assert!(!s.is_full()); assert_eq!(r.len(), 1); assert!(!r.is_empty()); assert!(!r.is_full()); r.recv().unwrap(); assert_eq!(s.len(), 0); assert!(s.is_empty()); assert!(!s.is_full()); assert_eq!(r.len(), 0); assert!(r.is_empty()); assert!(!r.is_full()); } #[test] #[cfg_attr(miri, ignore)] // this test makes timing assumptions, but Miri is so slow it violates them fn try_recv() { let (s, r) = unbounded(); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); thread::sleep(ms(1500)); assert_eq!(r.try_recv(), Ok(7)); thread::sleep(ms(500)); assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)); }); scope.spawn(move |_| { thread::sleep(ms(1000)); s.send(7).unwrap(); }); }) .unwrap(); } #[test] fn recv() { let (s, r) = unbounded(); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.recv(), Ok(7)); thread::sleep(ms(1000)); assert_eq!(r.recv(), Ok(8)); thread::sleep(ms(1000)); assert_eq!(r.recv(), Ok(9)); assert_eq!(r.recv(), Err(RecvError)); }); scope.spawn(move |_| { thread::sleep(ms(1500)); s.send(7).unwrap(); s.send(8).unwrap(); s.send(9).unwrap(); }); }) .unwrap(); } #[test] fn recv_timeout() { let (s, r) = unbounded::(); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout)); assert_eq!(r.recv_timeout(ms(1000)), Ok(7)); assert_eq!( r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Disconnected) ); }); scope.spawn(move |_| { thread::sleep(ms(1500)); s.send(7).unwrap(); }); }) .unwrap(); } #[test] fn try_send() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 1000; let (s, r) = unbounded(); for i in 0..COUNT { assert_eq!(s.try_send(i), Ok(())); } drop(r); assert_eq!(s.try_send(777), Err(TrySendError::Disconnected(777))); } #[test] fn send() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 1000; let (s, r) = unbounded(); for i in 0..COUNT { assert_eq!(s.send(i), Ok(())); } drop(r); assert_eq!(s.send(777), Err(SendError(777))); } #[test] fn send_timeout() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 1000; let (s, r) = unbounded(); for i in 0..COUNT { assert_eq!(s.send_timeout(i, ms(i as u64)), Ok(())); } drop(r); assert_eq!( s.send_timeout(777, ms(0)), Err(SendTimeoutError::Disconnected(777)) ); } #[test] fn send_after_disconnect() { let (s, r) = unbounded(); s.send(1).unwrap(); s.send(2).unwrap(); s.send(3).unwrap(); drop(r); assert_eq!(s.send(4), Err(SendError(4))); assert_eq!(s.try_send(5), Err(TrySendError::Disconnected(5))); assert_eq!( s.send_timeout(6, ms(0)), Err(SendTimeoutError::Disconnected(6)) ); } #[test] fn recv_after_disconnect() { let (s, r) = unbounded(); s.send(1).unwrap(); s.send(2).unwrap(); s.send(3).unwrap(); drop(s); assert_eq!(r.recv(), Ok(1)); assert_eq!(r.recv(), Ok(2)); assert_eq!(r.recv(), Ok(3)); assert_eq!(r.recv(), Err(RecvError)); } #[test] fn len() { let (s, r) = unbounded(); assert_eq!(s.len(), 0); assert_eq!(r.len(), 0); for i in 0..50 { s.send(i).unwrap(); assert_eq!(s.len(), i + 1); } for i in 0..50 { r.recv().unwrap(); assert_eq!(r.len(), 50 - i - 1); } assert_eq!(s.len(), 0); assert_eq!(r.len(), 0); } #[test] fn disconnect_wakes_receiver() { let (s, r) = unbounded::<()>(); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.recv(), Err(RecvError)); }); scope.spawn(move |_| { thread::sleep(ms(1000)); drop(s); }); }) .unwrap(); } #[test] fn spsc() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 100_000; let (s, r) = unbounded(); scope(|scope| { scope.spawn(move |_| { for i in 0..COUNT { assert_eq!(r.recv(), Ok(i)); } assert_eq!(r.recv(), Err(RecvError)); }); scope.spawn(move |_| { for i in 0..COUNT { s.send(i).unwrap(); } }); }) .unwrap(); } #[test] fn mpmc() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 25_000; const THREADS: usize = 4; let (s, r) = unbounded::(); let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); scope(|scope| { for _ in 0..THREADS { scope.spawn(|_| { for _ in 0..COUNT { let n = r.recv().unwrap(); v[n].fetch_add(1, Ordering::SeqCst); } }); } for _ in 0..THREADS { scope.spawn(|_| { for i in 0..COUNT { s.send(i).unwrap(); } }); } }) .unwrap(); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); for c in v { assert_eq!(c.load(Ordering::SeqCst), THREADS); } } #[test] fn stress_oneshot() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; for _ in 0..COUNT { let (s, r) = unbounded(); scope(|scope| { scope.spawn(|_| r.recv().unwrap()); scope.spawn(|_| s.send(0).unwrap()); }) .unwrap(); } } #[test] fn stress_iter() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 100_000; let (request_s, request_r) = unbounded(); let (response_s, response_r) = unbounded(); scope(|scope| { scope.spawn(move |_| { let mut count = 0; loop { for x in response_r.try_iter() { count += x; if count == COUNT { return; } } request_s.send(()).unwrap(); } }); for _ in request_r.iter() { if response_s.send(1).is_err() { break; } } }) .unwrap(); } #[test] fn stress_timeout_two_threads() { const COUNT: usize = 100; let (s, r) = unbounded(); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(50)); } s.send(i).unwrap(); } }); scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(50)); } loop { if let Ok(x) = r.recv_timeout(ms(10)) { assert_eq!(x, i); break; } } } }); }) .unwrap(); } #[test] fn drops() { #[cfg(miri)] const RUNS: usize = 20; #[cfg(not(miri))] const RUNS: usize = 100; #[cfg(miri)] const STEPS: usize = 100; #[cfg(not(miri))] const STEPS: usize = 10_000; static DROPS: AtomicUsize = AtomicUsize::new(0); #[derive(Debug, PartialEq)] struct DropCounter; impl Drop for DropCounter { fn drop(&mut self) { DROPS.fetch_add(1, Ordering::SeqCst); } } let mut rng = thread_rng(); for _ in 0..RUNS { let steps = rng.gen_range(0..STEPS); let additional = rng.gen_range(0..STEPS / 10); DROPS.store(0, Ordering::SeqCst); let (s, r) = unbounded::(); scope(|scope| { scope.spawn(|_| { for _ in 0..steps { r.recv().unwrap(); } }); scope.spawn(|_| { for _ in 0..steps { s.send(DropCounter).unwrap(); } }); }) .unwrap(); for _ in 0..additional { s.try_send(DropCounter).unwrap(); } assert_eq!(DROPS.load(Ordering::SeqCst), steps); drop(s); drop(r); assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional); } } #[test] fn linearizable() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 25_000; const THREADS: usize = 4; let (s, r) = unbounded(); scope(|scope| { for _ in 0..THREADS { scope.spawn(|_| { for _ in 0..COUNT { s.send(0).unwrap(); r.try_recv().unwrap(); } }); } }) .unwrap(); } #[test] fn fairness() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = unbounded::<()>(); let (s2, r2) = unbounded::<()>(); for _ in 0..COUNT { s1.send(()).unwrap(); s2.send(()).unwrap(); } let mut hits = [0usize; 2]; for _ in 0..COUNT { select! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); } #[test] fn fairness_duplicates() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s, r) = unbounded(); for _ in 0..COUNT { s.send(()).unwrap(); } let mut hits = [0usize; 5]; for _ in 0..COUNT { select! { recv(r) -> _ => hits[0] += 1, recv(r) -> _ => hits[1] += 1, recv(r) -> _ => hits[2] += 1, recv(r) -> _ => hits[3] += 1, recv(r) -> _ => hits[4] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); } #[test] fn recv_in_send() { let (s, r) = unbounded(); s.send(()).unwrap(); select! { send(s, assert_eq!(r.recv(), Ok(()))) -> _ => {} } } #[test] fn channel_through_channel() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 1000; type T = Box; let (s, r) = unbounded::(); scope(|scope| { scope.spawn(move |_| { let mut s = s; for _ in 0..COUNT { let (new_s, new_r) = unbounded(); let new_r: T = Box::new(Some(new_r)); s.send(new_r).unwrap(); s = new_s; } }); scope.spawn(move |_| { let mut r = r; for _ in 0..COUNT { r = r .recv() .unwrap() .downcast_mut::>>() .unwrap() .take() .unwrap() } }); }) .unwrap(); } // If `Block` is created on the stack, the array of slots will multiply this `BigStruct` and // probably overflow the thread stack. It's now directly created on the heap to avoid this. #[test] fn stack_overflow() { const N: usize = 32_768; struct BigStruct { _data: [u8; N], } let (sender, receiver) = unbounded::(); sender.send(BigStruct { _data: [0u8; N] }).unwrap(); for _data in receiver.try_iter() {} } crossbeam-channel-0.5.14/tests/mpsc.rs000064400000000000000000001545531046102023000157660ustar 00000000000000//! Tests copied from `std::sync::mpsc`. //! //! This is a copy of tests for the `std::sync::mpsc` channels from the standard library, but //! modified to work with `crossbeam-channel` instead. //! //! Minor tweaks were needed to make the tests compile: //! //! - Replace `box` syntax with `Box::new`. //! - Replace all uses of `Select` with `select!`. //! - Change the imports. //! - Join all spawned threads. //! - Removed assertion from oneshot_multi_thread_send_close_stress tests. //! //! Source: //! - https://github.com/rust-lang/rust/tree/master/src/libstd/sync/mpsc //! //! Copyright & License: //! - Copyright 2013-2014 The Rust Project Developers //! - Apache License, Version 2.0 or MIT license, at your option //! - https://github.com/rust-lang/rust/blob/master/COPYRIGHT //! - https://www.rust-lang.org/en-US/legal.html #![allow(clippy::match_single_binding, clippy::redundant_clone)] use std::sync::mpsc::{RecvError, RecvTimeoutError, TryRecvError}; use std::sync::mpsc::{SendError, TrySendError}; use std::thread::JoinHandle; use std::time::Duration; use crossbeam_channel as cc; pub struct Sender { pub inner: cc::Sender, } impl Sender { pub fn send(&self, t: T) -> Result<(), SendError> { self.inner.send(t).map_err(|cc::SendError(m)| SendError(m)) } } impl Clone for Sender { fn clone(&self) -> Sender { Sender { inner: self.inner.clone(), } } } pub struct SyncSender { pub inner: cc::Sender, } impl SyncSender { pub fn send(&self, t: T) -> Result<(), SendError> { self.inner.send(t).map_err(|cc::SendError(m)| SendError(m)) } pub fn try_send(&self, t: T) -> Result<(), TrySendError> { self.inner.try_send(t).map_err(|err| match err { cc::TrySendError::Full(m) => TrySendError::Full(m), cc::TrySendError::Disconnected(m) => TrySendError::Disconnected(m), }) } } impl Clone for SyncSender { fn clone(&self) -> SyncSender { SyncSender { inner: self.inner.clone(), } } } pub struct Receiver { pub inner: cc::Receiver, } impl Receiver { pub fn try_recv(&self) -> Result { self.inner.try_recv().map_err(|err| match err { cc::TryRecvError::Empty => TryRecvError::Empty, cc::TryRecvError::Disconnected => TryRecvError::Disconnected, }) } pub fn recv(&self) -> Result { self.inner.recv().map_err(|_| RecvError) } pub fn recv_timeout(&self, timeout: Duration) -> Result { self.inner.recv_timeout(timeout).map_err(|err| match err { cc::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout, cc::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected, }) } pub fn iter(&self) -> Iter { Iter { inner: self } } pub fn try_iter(&self) -> TryIter { TryIter { inner: self } } } impl<'a, T> IntoIterator for &'a Receiver { type Item = T; type IntoIter = Iter<'a, T>; fn into_iter(self) -> Iter<'a, T> { self.iter() } } impl IntoIterator for Receiver { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { IntoIter { inner: self } } } pub struct TryIter<'a, T: 'a> { inner: &'a Receiver, } impl<'a, T> Iterator for TryIter<'a, T> { type Item = T; fn next(&mut self) -> Option { self.inner.try_recv().ok() } } pub struct Iter<'a, T: 'a> { inner: &'a Receiver, } impl<'a, T> Iterator for Iter<'a, T> { type Item = T; fn next(&mut self) -> Option { self.inner.recv().ok() } } pub struct IntoIter { inner: Receiver, } impl Iterator for IntoIter { type Item = T; fn next(&mut self) -> Option { self.inner.recv().ok() } } pub fn channel() -> (Sender, Receiver) { let (s, r) = cc::unbounded(); let s = Sender { inner: s }; let r = Receiver { inner: r }; (s, r) } pub fn sync_channel(bound: usize) -> (SyncSender, Receiver) { let (s, r) = cc::bounded(bound); let s = SyncSender { inner: s }; let r = Receiver { inner: r }; (s, r) } macro_rules! select { ( $($name:pat = $rx:ident.$meth:ident() => $code:expr),+ ) => ({ const _IS_BIASED: bool = false; cc::crossbeam_channel_internal! { $( $meth(($rx).inner) -> res => { let $name = res.map_err(|_| ::std::sync::mpsc::RecvError); $code } )+ } }) } // Source: https://github.com/rust-lang/rust/blob/master/src/libstd/sync/mpsc/mod.rs mod channel_tests { use super::*; use std::env; use std::thread; use std::time::Instant; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { Ok(val) => val.parse().unwrap(), Err(..) => 1, } } #[test] fn smoke() { let (tx, rx) = channel::(); tx.send(1).unwrap(); assert_eq!(rx.recv().unwrap(), 1); } #[test] fn drop_full() { let (tx, _rx) = channel::>(); tx.send(Box::new(1)).unwrap(); } #[test] fn drop_full_shared() { let (tx, _rx) = channel::>(); drop(tx.clone()); drop(tx.clone()); tx.send(Box::new(1)).unwrap(); } #[test] fn smoke_shared() { let (tx, rx) = channel::(); tx.send(1).unwrap(); assert_eq!(rx.recv().unwrap(), 1); let tx = tx.clone(); tx.send(1).unwrap(); assert_eq!(rx.recv().unwrap(), 1); } #[test] fn smoke_threads() { let (tx, rx) = channel::(); let t = thread::spawn(move || { tx.send(1).unwrap(); }); assert_eq!(rx.recv().unwrap(), 1); t.join().unwrap(); } #[test] fn smoke_port_gone() { let (tx, rx) = channel::(); drop(rx); assert!(tx.send(1).is_err()); } #[test] fn smoke_shared_port_gone() { let (tx, rx) = channel::(); drop(rx); assert!(tx.send(1).is_err()) } #[test] fn smoke_shared_port_gone2() { let (tx, rx) = channel::(); drop(rx); let tx2 = tx.clone(); drop(tx); assert!(tx2.send(1).is_err()); } #[test] fn port_gone_concurrent() { let (tx, rx) = channel::(); let t = thread::spawn(move || { rx.recv().unwrap(); }); while tx.send(1).is_ok() {} t.join().unwrap(); } #[test] fn port_gone_concurrent_shared() { let (tx, rx) = channel::(); let tx2 = tx.clone(); let t = thread::spawn(move || { rx.recv().unwrap(); }); while tx.send(1).is_ok() && tx2.send(1).is_ok() {} t.join().unwrap(); } #[test] fn smoke_chan_gone() { let (tx, rx) = channel::(); drop(tx); assert!(rx.recv().is_err()); } #[test] fn smoke_chan_gone_shared() { let (tx, rx) = channel::<()>(); let tx2 = tx.clone(); drop(tx); drop(tx2); assert!(rx.recv().is_err()); } #[test] fn chan_gone_concurrent() { let (tx, rx) = channel::(); let t = thread::spawn(move || { tx.send(1).unwrap(); tx.send(1).unwrap(); }); while rx.recv().is_ok() {} t.join().unwrap(); } #[test] fn stress() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10000; let (tx, rx) = channel::(); let t = thread::spawn(move || { for _ in 0..COUNT { tx.send(1).unwrap(); } }); for _ in 0..COUNT { assert_eq!(rx.recv().unwrap(), 1); } t.join().ok().unwrap(); } #[test] fn stress_shared() { let amt: u32 = if cfg!(miri) { 100 } else { 10_000 }; let nthreads: u32 = if cfg!(miri) { 4 } else { 8 }; let (tx, rx) = channel::(); let t = thread::spawn(move || { for _ in 0..amt * nthreads { assert_eq!(rx.recv().unwrap(), 1); } assert!(rx.try_recv().is_err()); }); let mut ts = Vec::with_capacity(nthreads as usize); for _ in 0..nthreads { let tx = tx.clone(); let t = thread::spawn(move || { for _ in 0..amt { tx.send(1).unwrap(); } }); ts.push(t); } drop(tx); t.join().ok().unwrap(); for t in ts { t.join().unwrap(); } } #[test] fn send_from_outside_runtime() { let (tx1, rx1) = channel::<()>(); let (tx2, rx2) = channel::(); let t1 = thread::spawn(move || { tx1.send(()).unwrap(); for _ in 0..40 { assert_eq!(rx2.recv().unwrap(), 1); } }); rx1.recv().unwrap(); let t2 = thread::spawn(move || { for _ in 0..40 { tx2.send(1).unwrap(); } }); t1.join().ok().unwrap(); t2.join().ok().unwrap(); } #[test] fn recv_from_outside_runtime() { let (tx, rx) = channel::(); let t = thread::spawn(move || { for _ in 0..40 { assert_eq!(rx.recv().unwrap(), 1); } }); for _ in 0..40 { tx.send(1).unwrap(); } t.join().ok().unwrap(); } #[test] fn no_runtime() { let (tx1, rx1) = channel::(); let (tx2, rx2) = channel::(); let t1 = thread::spawn(move || { assert_eq!(rx1.recv().unwrap(), 1); tx2.send(2).unwrap(); }); let t2 = thread::spawn(move || { tx1.send(1).unwrap(); assert_eq!(rx2.recv().unwrap(), 2); }); t1.join().ok().unwrap(); t2.join().ok().unwrap(); } #[test] fn oneshot_single_thread_close_port_first() { // Simple test of closing without sending let (_tx, rx) = channel::(); drop(rx); } #[test] fn oneshot_single_thread_close_chan_first() { // Simple test of closing without sending let (tx, _rx) = channel::(); drop(tx); } #[test] fn oneshot_single_thread_send_port_close() { // Testing that the sender cleans up the payload if receiver is closed let (tx, rx) = channel::>(); drop(rx); assert!(tx.send(Box::new(0)).is_err()); } #[test] fn oneshot_single_thread_recv_chan_close() { let (tx, rx) = channel::(); drop(tx); assert_eq!(rx.recv(), Err(RecvError)); } #[test] fn oneshot_single_thread_send_then_recv() { let (tx, rx) = channel::>(); tx.send(Box::new(10)).unwrap(); assert!(*rx.recv().unwrap() == 10); } #[test] fn oneshot_single_thread_try_send_open() { let (tx, rx) = channel::(); assert!(tx.send(10).is_ok()); assert!(rx.recv().unwrap() == 10); } #[test] fn oneshot_single_thread_try_send_closed() { let (tx, rx) = channel::(); drop(rx); assert!(tx.send(10).is_err()); } #[test] fn oneshot_single_thread_try_recv_open() { let (tx, rx) = channel::(); tx.send(10).unwrap(); assert!(rx.recv() == Ok(10)); } #[test] fn oneshot_single_thread_try_recv_closed() { let (tx, rx) = channel::(); drop(tx); assert!(rx.recv().is_err()); } #[test] fn oneshot_single_thread_peek_data() { let (tx, rx) = channel::(); assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); tx.send(10).unwrap(); assert_eq!(rx.try_recv(), Ok(10)); } #[test] fn oneshot_single_thread_peek_close() { let (tx, rx) = channel::(); drop(tx); assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); } #[test] fn oneshot_single_thread_peek_open() { let (_tx, rx) = channel::(); assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); } #[test] fn oneshot_multi_task_recv_then_send() { let (tx, rx) = channel::>(); let t = thread::spawn(move || { assert!(*rx.recv().unwrap() == 10); }); tx.send(Box::new(10)).unwrap(); t.join().unwrap(); } #[test] fn oneshot_multi_task_recv_then_close() { let (tx, rx) = channel::>(); let t = thread::spawn(move || { drop(tx); }); thread::spawn(move || { assert_eq!(rx.recv(), Err(RecvError)); }) .join() .unwrap(); t.join().unwrap(); } #[test] fn oneshot_multi_thread_close_stress() { let stress_factor = stress_factor(); let mut ts = Vec::with_capacity(stress_factor); for _ in 0..stress_factor { let (tx, rx) = channel::(); let t = thread::spawn(move || { drop(rx); }); ts.push(t); drop(tx); } for t in ts { t.join().unwrap(); } } #[test] fn oneshot_multi_thread_send_close_stress() { let stress_factor = stress_factor(); let mut ts = Vec::with_capacity(2 * stress_factor); for _ in 0..stress_factor { let (tx, rx) = channel::(); let t = thread::spawn(move || { drop(rx); }); ts.push(t); thread::spawn(move || { let _ = tx.send(1); }) .join() .unwrap(); } for t in ts { t.join().unwrap(); } } #[test] fn oneshot_multi_thread_recv_close_stress() { let stress_factor = stress_factor(); let mut ts = Vec::with_capacity(2 * stress_factor); for _ in 0..stress_factor { let (tx, rx) = channel::(); let t = thread::spawn(move || { thread::spawn(move || { assert_eq!(rx.recv(), Err(RecvError)); }) .join() .unwrap(); }); ts.push(t); let t2 = thread::spawn(move || { let t = thread::spawn(move || { drop(tx); }); t.join().unwrap(); }); ts.push(t2); } for t in ts { t.join().unwrap(); } } #[test] fn oneshot_multi_thread_send_recv_stress() { let stress_factor = stress_factor(); let mut ts = Vec::with_capacity(stress_factor); for _ in 0..stress_factor { let (tx, rx) = channel::>(); let t = thread::spawn(move || { tx.send(Box::new(10)).unwrap(); }); ts.push(t); assert!(*rx.recv().unwrap() == 10); } for t in ts { t.join().unwrap(); } } #[test] fn stream_send_recv_stress() { let stress_factor = stress_factor(); let mut ts = Vec::with_capacity(2 * stress_factor); for _ in 0..stress_factor { let (tx, rx) = channel(); if let Some(t) = send(tx, 0) { ts.push(t); } if let Some(t2) = recv(rx, 0) { ts.push(t2); } fn send(tx: Sender>, i: i32) -> Option> { if i == 10 { return None; } Some(thread::spawn(move || { tx.send(Box::new(i)).unwrap(); send(tx, i + 1); })) } fn recv(rx: Receiver>, i: i32) -> Option> { if i == 10 { return None; } Some(thread::spawn(move || { assert!(*rx.recv().unwrap() == i); recv(rx, i + 1); })) } } for t in ts { t.join().unwrap(); } } #[test] fn oneshot_single_thread_recv_timeout() { let (tx, rx) = channel(); tx.send(()).unwrap(); assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); assert_eq!( rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout) ); tx.send(()).unwrap(); assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); } #[test] fn stress_recv_timeout_two_threads() { let (tx, rx) = channel(); let stress = stress_factor() + 100; let timeout = Duration::from_millis(100); let t = thread::spawn(move || { for i in 0..stress { if i % 2 == 0 { thread::sleep(timeout * 2); } tx.send(1usize).unwrap(); } }); let mut recv_count = 0; loop { match rx.recv_timeout(timeout) { Ok(n) => { assert_eq!(n, 1usize); recv_count += 1; } Err(RecvTimeoutError::Timeout) => continue, Err(RecvTimeoutError::Disconnected) => break, } } assert_eq!(recv_count, stress); t.join().unwrap() } #[test] fn recv_timeout_upgrade() { let (tx, rx) = channel::<()>(); let timeout = Duration::from_millis(1); let _tx_clone = tx.clone(); let start = Instant::now(); assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); assert!(Instant::now() >= start + timeout); } #[test] fn stress_recv_timeout_shared() { let (tx, rx) = channel(); let stress = stress_factor() + 100; let mut ts = Vec::with_capacity(stress); for i in 0..stress { let tx = tx.clone(); let t = thread::spawn(move || { thread::sleep(Duration::from_millis(i as u64 * 10)); tx.send(1usize).unwrap(); }); ts.push(t); } drop(tx); let mut recv_count = 0; loop { match rx.recv_timeout(Duration::from_millis(10)) { Ok(n) => { assert_eq!(n, 1usize); recv_count += 1; } Err(RecvTimeoutError::Timeout) => continue, Err(RecvTimeoutError::Disconnected) => break, } } assert_eq!(recv_count, stress); for t in ts { t.join().unwrap(); } } #[test] fn recv_a_lot() { #[cfg(miri)] const N: usize = 50; #[cfg(not(miri))] const N: usize = 10000; // Regression test that we don't run out of stack in scheduler context let (tx, rx) = channel(); for _ in 0..N { tx.send(()).unwrap(); } for _ in 0..N { rx.recv().unwrap(); } } #[test] fn shared_recv_timeout() { let (tx, rx) = channel(); let total = 5; let mut ts = Vec::with_capacity(total); for _ in 0..total { let tx = tx.clone(); let t = thread::spawn(move || { tx.send(()).unwrap(); }); ts.push(t); } for _ in 0..total { rx.recv().unwrap(); } assert_eq!( rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout) ); tx.send(()).unwrap(); assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); for t in ts { t.join().unwrap(); } } #[test] fn shared_chan_stress() { let (tx, rx) = channel(); let total = stress_factor() + 100; let mut ts = Vec::with_capacity(total); for _ in 0..total { let tx = tx.clone(); let t = thread::spawn(move || { tx.send(()).unwrap(); }); ts.push(t); } for _ in 0..total { rx.recv().unwrap(); } for t in ts { t.join().unwrap(); } } #[test] fn test_nested_recv_iter() { let (tx, rx) = channel::(); let (total_tx, total_rx) = channel::(); let t = thread::spawn(move || { let mut acc = 0; for x in rx.iter() { acc += x; } total_tx.send(acc).unwrap(); }); tx.send(3).unwrap(); tx.send(1).unwrap(); tx.send(2).unwrap(); drop(tx); assert_eq!(total_rx.recv().unwrap(), 6); t.join().unwrap(); } #[test] fn test_recv_iter_break() { let (tx, rx) = channel::(); let (count_tx, count_rx) = channel(); let t = thread::spawn(move || { let mut count = 0; for x in rx.iter() { if count >= 3 { break; } else { count += x; } } count_tx.send(count).unwrap(); }); tx.send(2).unwrap(); tx.send(2).unwrap(); tx.send(2).unwrap(); let _ = tx.send(2); drop(tx); assert_eq!(count_rx.recv().unwrap(), 4); t.join().unwrap(); } #[test] fn test_recv_try_iter() { let (request_tx, request_rx) = channel(); let (response_tx, response_rx) = channel(); // Request `x`s until we have `6`. let t = thread::spawn(move || { let mut count = 0; loop { for x in response_rx.try_iter() { count += x; if count == 6 { return count; } } request_tx.send(()).unwrap(); } }); for _ in request_rx.iter() { if response_tx.send(2).is_err() { break; } } assert_eq!(t.join().unwrap(), 6); } #[test] fn test_recv_into_iter_owned() { let mut iter = { let (tx, rx) = channel::(); tx.send(1).unwrap(); tx.send(2).unwrap(); rx.into_iter() }; assert_eq!(iter.next().unwrap(), 1); assert_eq!(iter.next().unwrap(), 2); assert!(iter.next().is_none()); } #[test] fn test_recv_into_iter_borrowed() { let (tx, rx) = channel::(); tx.send(1).unwrap(); tx.send(2).unwrap(); drop(tx); let mut iter = (&rx).into_iter(); assert_eq!(iter.next().unwrap(), 1); assert_eq!(iter.next().unwrap(), 2); assert!(iter.next().is_none()); } #[test] fn try_recv_states() { let (tx1, rx1) = channel::(); let (tx2, rx2) = channel::<()>(); let (tx3, rx3) = channel::<()>(); let t = thread::spawn(move || { rx2.recv().unwrap(); tx1.send(1).unwrap(); tx3.send(()).unwrap(); rx2.recv().unwrap(); drop(tx1); tx3.send(()).unwrap(); }); assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); tx2.send(()).unwrap(); rx3.recv().unwrap(); assert_eq!(rx1.try_recv(), Ok(1)); assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); tx2.send(()).unwrap(); rx3.recv().unwrap(); assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); t.join().unwrap(); } // This bug used to end up in a livelock inside of the Receiver destructor // because the internal state of the Shared packet was corrupted #[test] fn destroy_upgraded_shared_port_when_sender_still_active() { let (tx, rx) = channel(); let (tx2, rx2) = channel(); let t = thread::spawn(move || { rx.recv().unwrap(); // wait on a oneshot drop(rx); // destroy a shared tx2.send(()).unwrap(); }); // make sure the other thread has gone to sleep for _ in 0..5000 { thread::yield_now(); } // upgrade to a shared chan and send a message let tx2 = tx.clone(); drop(tx); tx2.send(()).unwrap(); // wait for the child thread to exit before we exit rx2.recv().unwrap(); t.join().unwrap(); } #[test] fn issue_32114() { let (tx, _) = channel(); let _ = tx.send(123); assert_eq!(tx.send(123), Err(SendError(123))); } } // Source: https://github.com/rust-lang/rust/blob/master/src/libstd/sync/mpsc/mod.rs mod sync_channel_tests { use super::*; use std::env; use std::thread; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { Ok(val) => val.parse().unwrap(), Err(..) => 1, } } #[test] fn smoke() { let (tx, rx) = sync_channel::(1); tx.send(1).unwrap(); assert_eq!(rx.recv().unwrap(), 1); } #[test] fn drop_full() { let (tx, _rx) = sync_channel::>(1); tx.send(Box::new(1)).unwrap(); } #[test] fn smoke_shared() { let (tx, rx) = sync_channel::(1); tx.send(1).unwrap(); assert_eq!(rx.recv().unwrap(), 1); let tx = tx.clone(); tx.send(1).unwrap(); assert_eq!(rx.recv().unwrap(), 1); } #[test] fn recv_timeout() { let (tx, rx) = sync_channel::(1); assert_eq!( rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout) ); tx.send(1).unwrap(); assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1)); } #[test] fn smoke_threads() { let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { tx.send(1).unwrap(); }); assert_eq!(rx.recv().unwrap(), 1); t.join().unwrap(); } #[test] fn smoke_port_gone() { let (tx, rx) = sync_channel::(0); drop(rx); assert!(tx.send(1).is_err()); } #[test] fn smoke_shared_port_gone2() { let (tx, rx) = sync_channel::(0); drop(rx); let tx2 = tx.clone(); drop(tx); assert!(tx2.send(1).is_err()); } #[test] fn port_gone_concurrent() { let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { rx.recv().unwrap(); }); while tx.send(1).is_ok() {} t.join().unwrap(); } #[test] fn port_gone_concurrent_shared() { let (tx, rx) = sync_channel::(0); let tx2 = tx.clone(); let t = thread::spawn(move || { rx.recv().unwrap(); }); while tx.send(1).is_ok() && tx2.send(1).is_ok() {} t.join().unwrap(); } #[test] fn smoke_chan_gone() { let (tx, rx) = sync_channel::(0); drop(tx); assert!(rx.recv().is_err()); } #[test] fn smoke_chan_gone_shared() { let (tx, rx) = sync_channel::<()>(0); let tx2 = tx.clone(); drop(tx); drop(tx2); assert!(rx.recv().is_err()); } #[test] fn chan_gone_concurrent() { let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { tx.send(1).unwrap(); tx.send(1).unwrap(); }); while rx.recv().is_ok() {} t.join().unwrap(); } #[test] fn stress() { #[cfg(miri)] const N: usize = 100; #[cfg(not(miri))] const N: usize = 10000; let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { for _ in 0..N { tx.send(1).unwrap(); } }); for _ in 0..N { assert_eq!(rx.recv().unwrap(), 1); } t.join().unwrap(); } #[test] fn stress_recv_timeout_two_threads() { #[cfg(miri)] const N: usize = 100; #[cfg(not(miri))] const N: usize = 10000; let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { for _ in 0..N { tx.send(1).unwrap(); } }); let mut recv_count = 0; loop { match rx.recv_timeout(Duration::from_millis(1)) { Ok(v) => { assert_eq!(v, 1); recv_count += 1; } Err(RecvTimeoutError::Timeout) => continue, Err(RecvTimeoutError::Disconnected) => break, } } assert_eq!(recv_count, N); t.join().unwrap(); } #[test] fn stress_recv_timeout_shared() { #[cfg(miri)] const AMT: u32 = 100; #[cfg(not(miri))] const AMT: u32 = 1000; const NTHREADS: u32 = 8; let (tx, rx) = sync_channel::(0); let (dtx, drx) = sync_channel::<()>(0); let t = thread::spawn(move || { let mut recv_count = 0; loop { match rx.recv_timeout(Duration::from_millis(10)) { Ok(v) => { assert_eq!(v, 1); recv_count += 1; } Err(RecvTimeoutError::Timeout) => continue, Err(RecvTimeoutError::Disconnected) => break, } } assert_eq!(recv_count, AMT * NTHREADS); assert!(rx.try_recv().is_err()); dtx.send(()).unwrap(); }); let mut ts = Vec::with_capacity(NTHREADS as usize); for _ in 0..NTHREADS { let tx = tx.clone(); let t = thread::spawn(move || { for _ in 0..AMT { tx.send(1).unwrap(); } }); ts.push(t); } drop(tx); drx.recv().unwrap(); for t in ts { t.join().unwrap(); } t.join().unwrap(); } #[test] fn stress_shared() { #[cfg(miri)] const AMT: u32 = 100; #[cfg(not(miri))] const AMT: u32 = 1000; const NTHREADS: u32 = 8; let (tx, rx) = sync_channel::(0); let (dtx, drx) = sync_channel::<()>(0); let t = thread::spawn(move || { for _ in 0..AMT * NTHREADS { assert_eq!(rx.recv().unwrap(), 1); } assert!(rx.try_recv().is_err()); dtx.send(()).unwrap(); }); let mut ts = Vec::with_capacity(NTHREADS as usize); for _ in 0..NTHREADS { let tx = tx.clone(); let t = thread::spawn(move || { for _ in 0..AMT { tx.send(1).unwrap(); } }); ts.push(t); } drop(tx); drx.recv().unwrap(); for t in ts { t.join().unwrap(); } t.join().unwrap(); } #[test] fn oneshot_single_thread_close_port_first() { // Simple test of closing without sending let (_tx, rx) = sync_channel::(0); drop(rx); } #[test] fn oneshot_single_thread_close_chan_first() { // Simple test of closing without sending let (tx, _rx) = sync_channel::(0); drop(tx); } #[test] fn oneshot_single_thread_send_port_close() { // Testing that the sender cleans up the payload if receiver is closed let (tx, rx) = sync_channel::>(0); drop(rx); assert!(tx.send(Box::new(0)).is_err()); } #[test] fn oneshot_single_thread_recv_chan_close() { let (tx, rx) = sync_channel::(0); drop(tx); assert_eq!(rx.recv(), Err(RecvError)); } #[test] fn oneshot_single_thread_send_then_recv() { let (tx, rx) = sync_channel::>(1); tx.send(Box::new(10)).unwrap(); assert!(*rx.recv().unwrap() == 10); } #[test] fn oneshot_single_thread_try_send_open() { let (tx, rx) = sync_channel::(1); assert_eq!(tx.try_send(10), Ok(())); assert!(rx.recv().unwrap() == 10); } #[test] fn oneshot_single_thread_try_send_closed() { let (tx, rx) = sync_channel::(0); drop(rx); assert_eq!(tx.try_send(10), Err(TrySendError::Disconnected(10))); } #[test] fn oneshot_single_thread_try_send_closed2() { let (tx, _rx) = sync_channel::(0); assert_eq!(tx.try_send(10), Err(TrySendError::Full(10))); } #[test] fn oneshot_single_thread_try_recv_open() { let (tx, rx) = sync_channel::(1); tx.send(10).unwrap(); assert!(rx.recv() == Ok(10)); } #[test] fn oneshot_single_thread_try_recv_closed() { let (tx, rx) = sync_channel::(0); drop(tx); assert!(rx.recv().is_err()); } #[test] fn oneshot_single_thread_try_recv_closed_with_data() { let (tx, rx) = sync_channel::(1); tx.send(10).unwrap(); drop(tx); assert_eq!(rx.try_recv(), Ok(10)); assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); } #[test] fn oneshot_single_thread_peek_data() { let (tx, rx) = sync_channel::(1); assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); tx.send(10).unwrap(); assert_eq!(rx.try_recv(), Ok(10)); } #[test] fn oneshot_single_thread_peek_close() { let (tx, rx) = sync_channel::(0); drop(tx); assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); } #[test] fn oneshot_single_thread_peek_open() { let (_tx, rx) = sync_channel::(0); assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); } #[test] fn oneshot_multi_task_recv_then_send() { let (tx, rx) = sync_channel::>(0); let t = thread::spawn(move || { assert!(*rx.recv().unwrap() == 10); }); tx.send(Box::new(10)).unwrap(); t.join().unwrap(); } #[test] fn oneshot_multi_task_recv_then_close() { let (tx, rx) = sync_channel::>(0); let t = thread::spawn(move || { drop(tx); }); thread::spawn(move || { assert_eq!(rx.recv(), Err(RecvError)); }) .join() .unwrap(); t.join().unwrap(); } #[test] fn oneshot_multi_thread_close_stress() { let stress_factor = stress_factor(); let mut ts = Vec::with_capacity(stress_factor); for _ in 0..stress_factor { let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { drop(rx); }); ts.push(t); drop(tx); } for t in ts { t.join().unwrap(); } } #[test] fn oneshot_multi_thread_send_close_stress() { let stress_factor = stress_factor(); let mut ts = Vec::with_capacity(stress_factor); for _ in 0..stress_factor { let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { drop(rx); }); ts.push(t); thread::spawn(move || { let _ = tx.send(1); }) .join() .unwrap(); } for t in ts { t.join().unwrap(); } } #[test] fn oneshot_multi_thread_recv_close_stress() { let stress_factor = stress_factor(); let mut ts = Vec::with_capacity(2 * stress_factor); for _ in 0..stress_factor { let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { thread::spawn(move || { assert_eq!(rx.recv(), Err(RecvError)); }) .join() .unwrap(); }); ts.push(t); let t2 = thread::spawn(move || { thread::spawn(move || { drop(tx); }); }); ts.push(t2); } for t in ts { t.join().unwrap(); } } #[test] fn oneshot_multi_thread_send_recv_stress() { let stress_factor = stress_factor(); let mut ts = Vec::with_capacity(stress_factor); for _ in 0..stress_factor { let (tx, rx) = sync_channel::>(0); let t = thread::spawn(move || { tx.send(Box::new(10)).unwrap(); }); ts.push(t); assert!(*rx.recv().unwrap() == 10); } for t in ts { t.join().unwrap(); } } #[test] fn stream_send_recv_stress() { let stress_factor = stress_factor(); let mut ts = Vec::with_capacity(2 * stress_factor); for _ in 0..stress_factor { let (tx, rx) = sync_channel::>(0); if let Some(t) = send(tx, 0) { ts.push(t); } if let Some(t) = recv(rx, 0) { ts.push(t); } fn send(tx: SyncSender>, i: i32) -> Option> { if i == 10 { return None; } Some(thread::spawn(move || { tx.send(Box::new(i)).unwrap(); send(tx, i + 1); })) } fn recv(rx: Receiver>, i: i32) -> Option> { if i == 10 { return None; } Some(thread::spawn(move || { assert!(*rx.recv().unwrap() == i); recv(rx, i + 1); })) } } for t in ts { t.join().unwrap(); } } #[test] fn recv_a_lot() { #[cfg(miri)] const N: usize = 100; #[cfg(not(miri))] const N: usize = 10000; // Regression test that we don't run out of stack in scheduler context let (tx, rx) = sync_channel(N); for _ in 0..N { tx.send(()).unwrap(); } for _ in 0..N { rx.recv().unwrap(); } } #[test] fn shared_chan_stress() { let (tx, rx) = sync_channel(0); let total = stress_factor() + 100; let mut ts = Vec::with_capacity(total); for _ in 0..total { let tx = tx.clone(); let t = thread::spawn(move || { tx.send(()).unwrap(); }); ts.push(t); } for _ in 0..total { rx.recv().unwrap(); } for t in ts { t.join().unwrap(); } } #[test] fn test_nested_recv_iter() { let (tx, rx) = sync_channel::(0); let (total_tx, total_rx) = sync_channel::(0); let t = thread::spawn(move || { let mut acc = 0; for x in rx.iter() { acc += x; } total_tx.send(acc).unwrap(); }); tx.send(3).unwrap(); tx.send(1).unwrap(); tx.send(2).unwrap(); drop(tx); assert_eq!(total_rx.recv().unwrap(), 6); t.join().unwrap(); } #[test] fn test_recv_iter_break() { let (tx, rx) = sync_channel::(0); let (count_tx, count_rx) = sync_channel(0); let t = thread::spawn(move || { let mut count = 0; for x in rx.iter() { if count >= 3 { break; } else { count += x; } } count_tx.send(count).unwrap(); }); tx.send(2).unwrap(); tx.send(2).unwrap(); tx.send(2).unwrap(); let _ = tx.try_send(2); drop(tx); assert_eq!(count_rx.recv().unwrap(), 4); t.join().unwrap(); } #[test] fn try_recv_states() { let (tx1, rx1) = sync_channel::(1); let (tx2, rx2) = sync_channel::<()>(1); let (tx3, rx3) = sync_channel::<()>(1); let t = thread::spawn(move || { rx2.recv().unwrap(); tx1.send(1).unwrap(); tx3.send(()).unwrap(); rx2.recv().unwrap(); drop(tx1); tx3.send(()).unwrap(); }); assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); tx2.send(()).unwrap(); rx3.recv().unwrap(); assert_eq!(rx1.try_recv(), Ok(1)); assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); tx2.send(()).unwrap(); rx3.recv().unwrap(); assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); t.join().unwrap(); } // This bug used to end up in a livelock inside of the Receiver destructor // because the internal state of the Shared packet was corrupted #[test] fn destroy_upgraded_shared_port_when_sender_still_active() { let (tx, rx) = sync_channel::<()>(0); let (tx2, rx2) = sync_channel::<()>(0); let t = thread::spawn(move || { rx.recv().unwrap(); // wait on a oneshot drop(rx); // destroy a shared tx2.send(()).unwrap(); }); // make sure the other thread has gone to sleep for _ in 0..5000 { thread::yield_now(); } // upgrade to a shared chan and send a message let tx2 = tx.clone(); drop(tx); tx2.send(()).unwrap(); // wait for the child thread to exit before we exit rx2.recv().unwrap(); t.join().unwrap(); } #[test] fn send1() { let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { rx.recv().unwrap(); }); assert_eq!(tx.send(1), Ok(())); t.join().unwrap(); } #[test] fn send2() { let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { drop(rx); }); assert!(tx.send(1).is_err()); t.join().unwrap(); } #[test] fn send3() { let (tx, rx) = sync_channel::(1); assert_eq!(tx.send(1), Ok(())); let t = thread::spawn(move || { drop(rx); }); assert!(tx.send(1).is_err()); t.join().unwrap(); } #[test] fn send4() { let (tx, rx) = sync_channel::(0); let tx2 = tx.clone(); let (done, donerx) = channel(); let done2 = done.clone(); let t = thread::spawn(move || { assert!(tx.send(1).is_err()); done.send(()).unwrap(); }); let t2 = thread::spawn(move || { assert!(tx2.send(2).is_err()); done2.send(()).unwrap(); }); drop(rx); donerx.recv().unwrap(); donerx.recv().unwrap(); t.join().unwrap(); t2.join().unwrap(); } #[test] fn try_send1() { let (tx, _rx) = sync_channel::(0); assert_eq!(tx.try_send(1), Err(TrySendError::Full(1))); } #[test] fn try_send2() { let (tx, _rx) = sync_channel::(1); assert_eq!(tx.try_send(1), Ok(())); assert_eq!(tx.try_send(1), Err(TrySendError::Full(1))); } #[test] fn try_send3() { let (tx, rx) = sync_channel::(1); assert_eq!(tx.try_send(1), Ok(())); drop(rx); assert_eq!(tx.try_send(1), Err(TrySendError::Disconnected(1))); } #[test] fn issue_15761() { fn repro() { let (tx1, rx1) = sync_channel::<()>(3); let (tx2, rx2) = sync_channel::<()>(3); let _t = thread::spawn(move || { rx1.recv().unwrap(); tx2.try_send(()).unwrap(); }); tx1.try_send(()).unwrap(); rx2.recv().unwrap(); } for _ in 0..100 { repro() } } } // Source: https://github.com/rust-lang/rust/blob/master/src/libstd/sync/mpsc/select.rs mod select_tests { use super::*; use std::thread; #[test] fn smoke() { let (tx1, rx1) = channel::(); let (tx2, rx2) = channel::(); tx1.send(1).unwrap(); select! { foo = rx1.recv() => assert_eq!(foo.unwrap(), 1), _bar = rx2.recv() => panic!() } tx2.send(2).unwrap(); select! { _foo = rx1.recv() => panic!(), bar = rx2.recv() => assert_eq!(bar.unwrap(), 2) } drop(tx1); select! { foo = rx1.recv() => assert!(foo.is_err()), _bar = rx2.recv() => panic!() } drop(tx2); select! { bar = rx2.recv() => assert!(bar.is_err()) } } #[test] fn smoke2() { let (_tx1, rx1) = channel::(); let (_tx2, rx2) = channel::(); let (_tx3, rx3) = channel::(); let (_tx4, rx4) = channel::(); let (tx5, rx5) = channel::(); tx5.send(4).unwrap(); select! { _foo = rx1.recv() => panic!("1"), _foo = rx2.recv() => panic!("2"), _foo = rx3.recv() => panic!("3"), _foo = rx4.recv() => panic!("4"), foo = rx5.recv() => assert_eq!(foo.unwrap(), 4) } } #[test] fn closed() { let (_tx1, rx1) = channel::(); let (tx2, rx2) = channel::(); drop(tx2); select! { _a1 = rx1.recv() => panic!(), a2 = rx2.recv() => assert!(a2.is_err()) } } #[test] fn unblocks() { let (tx1, rx1) = channel::(); let (_tx2, rx2) = channel::(); let (tx3, rx3) = channel::(); let t = thread::spawn(move || { for _ in 0..20 { thread::yield_now(); } tx1.send(1).unwrap(); rx3.recv().unwrap(); for _ in 0..20 { thread::yield_now(); } }); select! { a = rx1.recv() => assert_eq!(a.unwrap(), 1), _b = rx2.recv() => panic!() } tx3.send(1).unwrap(); select! { a = rx1.recv() => assert!(a.is_err()), _b = rx2.recv() => panic!() } t.join().unwrap(); } #[test] fn both_ready() { let (tx1, rx1) = channel::(); let (tx2, rx2) = channel::(); let (tx3, rx3) = channel::<()>(); let t = thread::spawn(move || { for _ in 0..20 { thread::yield_now(); } tx1.send(1).unwrap(); tx2.send(2).unwrap(); rx3.recv().unwrap(); }); select! { a = rx1.recv() => { assert_eq!(a.unwrap(), 1); }, a = rx2.recv() => { assert_eq!(a.unwrap(), 2); } } select! { a = rx1.recv() => { assert_eq!(a.unwrap(), 1); }, a = rx2.recv() => { assert_eq!(a.unwrap(), 2); } } assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); assert_eq!(rx2.try_recv(), Err(TryRecvError::Empty)); tx3.send(()).unwrap(); t.join().unwrap(); } #[test] fn stress() { #[cfg(miri)] const AMT: i32 = 100; #[cfg(not(miri))] const AMT: i32 = 10000; let (tx1, rx1) = channel::(); let (tx2, rx2) = channel::(); let (tx3, rx3) = channel::<()>(); let t = thread::spawn(move || { for i in 0..AMT { if i % 2 == 0 { tx1.send(i).unwrap(); } else { tx2.send(i).unwrap(); } rx3.recv().unwrap(); } }); for i in 0..AMT { select! { i1 = rx1.recv() => { assert!(i % 2 == 0 && i == i1.unwrap()); }, i2 = rx2.recv() => { assert!(i % 2 == 1 && i == i2.unwrap()); } } tx3.send(()).unwrap(); } t.join().unwrap(); } #[allow(unused_must_use)] #[test] fn cloning() { let (tx1, rx1) = channel::(); let (_tx2, rx2) = channel::(); let (tx3, rx3) = channel::<()>(); let t = thread::spawn(move || { rx3.recv().unwrap(); tx1.clone(); assert_eq!(rx3.try_recv(), Err(TryRecvError::Empty)); tx1.send(2).unwrap(); rx3.recv().unwrap(); }); tx3.send(()).unwrap(); select! { _i1 = rx1.recv() => {}, _i2 = rx2.recv() => panic!() } tx3.send(()).unwrap(); t.join().unwrap(); } #[allow(unused_must_use)] #[test] fn cloning2() { let (tx1, rx1) = channel::(); let (_tx2, rx2) = channel::(); let (tx3, rx3) = channel::<()>(); let t = thread::spawn(move || { rx3.recv().unwrap(); tx1.clone(); assert_eq!(rx3.try_recv(), Err(TryRecvError::Empty)); tx1.send(2).unwrap(); rx3.recv().unwrap(); }); tx3.send(()).unwrap(); select! { _i1 = rx1.recv() => {}, _i2 = rx2.recv() => panic!() } tx3.send(()).unwrap(); t.join().unwrap(); } #[test] fn cloning3() { let (tx1, rx1) = channel::<()>(); let (tx2, rx2) = channel::<()>(); let (tx3, rx3) = channel::<()>(); let t = thread::spawn(move || { select! { _ = rx1.recv() => panic!(), _ = rx2.recv() => {} } tx3.send(()).unwrap(); }); for _ in 0..1000 { thread::yield_now(); } drop(tx1.clone()); tx2.send(()).unwrap(); rx3.recv().unwrap(); t.join().unwrap(); } #[test] fn preflight1() { let (tx, rx) = channel(); tx.send(()).unwrap(); select! { _n = rx.recv() => {} } } #[test] fn preflight2() { let (tx, rx) = channel(); tx.send(()).unwrap(); tx.send(()).unwrap(); select! { _n = rx.recv() => {} } } #[test] fn preflight3() { let (tx, rx) = channel(); drop(tx.clone()); tx.send(()).unwrap(); select! { _n = rx.recv() => {} } } #[test] fn preflight4() { let (tx, rx) = channel(); tx.send(()).unwrap(); select! { _ = rx.recv() => {} } } #[test] fn preflight5() { let (tx, rx) = channel(); tx.send(()).unwrap(); tx.send(()).unwrap(); select! { _ = rx.recv() => {} } } #[test] fn preflight6() { let (tx, rx) = channel(); drop(tx.clone()); tx.send(()).unwrap(); select! { _ = rx.recv() => {} } } #[test] fn preflight7() { let (tx, rx) = channel::<()>(); drop(tx); select! { _ = rx.recv() => {} } } #[test] fn preflight8() { let (tx, rx) = channel(); tx.send(()).unwrap(); drop(tx); rx.recv().unwrap(); select! { _ = rx.recv() => {} } } #[test] fn preflight9() { let (tx, rx) = channel(); drop(tx.clone()); tx.send(()).unwrap(); drop(tx); rx.recv().unwrap(); select! { _ = rx.recv() => {} } } #[test] fn oneshot_data_waiting() { let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); let t = thread::spawn(move || { select! { _n = rx1.recv() => {} } tx2.send(()).unwrap(); }); for _ in 0..100 { thread::yield_now() } tx1.send(()).unwrap(); rx2.recv().unwrap(); t.join().unwrap(); } #[test] fn stream_data_waiting() { let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); tx1.send(()).unwrap(); tx1.send(()).unwrap(); rx1.recv().unwrap(); rx1.recv().unwrap(); let t = thread::spawn(move || { select! { _n = rx1.recv() => {} } tx2.send(()).unwrap(); }); for _ in 0..100 { thread::yield_now() } tx1.send(()).unwrap(); rx2.recv().unwrap(); t.join().unwrap(); } #[test] fn shared_data_waiting() { let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); drop(tx1.clone()); tx1.send(()).unwrap(); rx1.recv().unwrap(); let t = thread::spawn(move || { select! { _n = rx1.recv() => {} } tx2.send(()).unwrap(); }); for _ in 0..100 { thread::yield_now() } tx1.send(()).unwrap(); rx2.recv().unwrap(); t.join().unwrap(); } #[test] fn sync1() { let (tx, rx) = sync_channel::(1); tx.send(1).unwrap(); select! { n = rx.recv() => { assert_eq!(n.unwrap(), 1); } } } #[test] fn sync2() { let (tx, rx) = sync_channel::(0); let t = thread::spawn(move || { for _ in 0..100 { thread::yield_now() } tx.send(1).unwrap(); }); select! { n = rx.recv() => { assert_eq!(n.unwrap(), 1); } } t.join().unwrap(); } #[test] fn sync3() { let (tx1, rx1) = sync_channel::(0); let (tx2, rx2): (Sender, Receiver) = channel(); let t = thread::spawn(move || { tx1.send(1).unwrap(); }); let t2 = thread::spawn(move || { tx2.send(2).unwrap(); }); select! { n = rx1.recv() => { let n = n.unwrap(); assert_eq!(n, 1); assert_eq!(rx2.recv().unwrap(), 2); }, n = rx2.recv() => { let n = n.unwrap(); assert_eq!(n, 2); assert_eq!(rx1.recv().unwrap(), 1); } } t.join().unwrap(); t2.join().unwrap(); } } crossbeam-channel-0.5.14/tests/never.rs000064400000000000000000000033251046102023000161310ustar 00000000000000//! Tests for the never channel flavor. use std::thread; use std::time::{Duration, Instant}; use crossbeam_channel::{never, select, tick, unbounded}; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] fn smoke() { select! { recv(never::()) -> _ => panic!(), default => {} } } #[test] fn optional() { let (s, r) = unbounded::(); s.send(1).unwrap(); s.send(2).unwrap(); let mut r = Some(&r); select! { recv(r.unwrap_or(&never())) -> _ => {} default => panic!(), } r = None; select! { recv(r.unwrap_or(&never())) -> _ => panic!(), default => {} } } #[test] fn tick_n() { let mut r = tick(ms(100)); let mut step = 0; loop { select! { recv(r) -> _ => step += 1, default(ms(500)) => break, } if step == 10 { r = never(); } } assert_eq!(step, 10); } #[test] fn capacity() { let r = never::(); assert_eq!(r.capacity(), Some(0)); } #[test] fn len_empty_full() { let r = never::(); assert_eq!(r.len(), 0); assert!(r.is_empty()); assert!(r.is_full()); } #[test] fn try_recv() { let r = never::(); assert!(r.try_recv().is_err()); thread::sleep(ms(100)); assert!(r.try_recv().is_err()); } #[test] fn recv_timeout() { let start = Instant::now(); let r = never::(); assert!(r.recv_timeout(ms(100)).is_err()); let now = Instant::now(); assert!(now - start >= ms(100)); assert!(now - start <= ms(150)); assert!(r.recv_timeout(ms(100)).is_err()); let now = Instant::now(); assert!(now - start >= ms(200)); assert!(now - start <= ms(250)); } crossbeam-channel-0.5.14/tests/ready.rs000064400000000000000000000512171046102023000161210ustar 00000000000000//! Tests for channel readiness using the `Select` struct. use std::any::Any; use std::cell::Cell; use std::thread; use std::time::{Duration, Instant}; use crossbeam_channel::{after, bounded, tick, unbounded}; use crossbeam_channel::{Receiver, Select, TryRecvError, TrySendError}; use crossbeam_utils::thread::scope; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] fn smoke1() { let (s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); s1.send(1).unwrap(); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); assert_eq!(sel.ready(), 0); assert_eq!(r1.try_recv(), Ok(1)); s2.send(2).unwrap(); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); assert_eq!(sel.ready(), 1); assert_eq!(r2.try_recv(), Ok(2)); } #[test] fn smoke2() { let (_s1, r1) = unbounded::(); let (_s2, r2) = unbounded::(); let (_s3, r3) = unbounded::(); let (_s4, r4) = unbounded::(); let (s5, r5) = unbounded::(); s5.send(5).unwrap(); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); sel.recv(&r3); sel.recv(&r4); sel.recv(&r5); assert_eq!(sel.ready(), 4); assert_eq!(r5.try_recv(), Ok(5)); } #[test] fn disconnected() { let (s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); scope(|scope| { scope.spawn(|_| { drop(s1); thread::sleep(ms(500)); s2.send(5).unwrap(); }); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); match sel.ready_timeout(ms(1000)) { Ok(0) => assert_eq!(r1.try_recv(), Err(TryRecvError::Disconnected)), _ => panic!(), } r2.recv().unwrap(); }) .unwrap(); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); match sel.ready_timeout(ms(1000)) { Ok(0) => assert_eq!(r1.try_recv(), Err(TryRecvError::Disconnected)), _ => panic!(), } scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); drop(s2); }); let mut sel = Select::new(); sel.recv(&r2); match sel.ready_timeout(ms(1000)) { Ok(0) => assert_eq!(r2.try_recv(), Err(TryRecvError::Disconnected)), _ => panic!(), } }) .unwrap(); } #[test] fn default() { let (s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); assert!(sel.try_ready().is_err()); drop(s1); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); match sel.try_ready() { Ok(0) => assert!(r1.try_recv().is_err()), _ => panic!(), } s2.send(2).unwrap(); let mut sel = Select::new(); sel.recv(&r2); match sel.try_ready() { Ok(0) => assert_eq!(r2.try_recv(), Ok(2)), _ => panic!(), } let mut sel = Select::new(); sel.recv(&r2); assert!(sel.try_ready().is_err()); let mut sel = Select::new(); assert!(sel.try_ready().is_err()); } #[test] fn timeout() { let (_s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(1500)); s2.send(2).unwrap(); }); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); assert!(sel.ready_timeout(ms(1000)).is_err()); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); match sel.ready_timeout(ms(1000)) { Ok(1) => assert_eq!(r2.try_recv(), Ok(2)), _ => panic!(), } }) .unwrap(); scope(|scope| { let (s, r) = unbounded::(); scope.spawn(move |_| { thread::sleep(ms(500)); drop(s); }); let mut sel = Select::new(); assert!(sel.ready_timeout(ms(1000)).is_err()); let mut sel = Select::new(); sel.recv(&r); match sel.try_ready() { Ok(0) => assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)), _ => panic!(), } }) .unwrap(); } #[test] fn default_when_disconnected() { let (_, r) = unbounded::(); let mut sel = Select::new(); sel.recv(&r); match sel.try_ready() { Ok(0) => assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)), _ => panic!(), } let (_, r) = unbounded::(); let mut sel = Select::new(); sel.recv(&r); match sel.ready_timeout(ms(1000)) { Ok(0) => assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)), _ => panic!(), } let (s, _) = bounded::(0); let mut sel = Select::new(); sel.send(&s); match sel.try_ready() { Ok(0) => assert_eq!(s.try_send(0), Err(TrySendError::Disconnected(0))), _ => panic!(), } let (s, _) = bounded::(0); let mut sel = Select::new(); sel.send(&s); match sel.ready_timeout(ms(1000)) { Ok(0) => assert_eq!(s.try_send(0), Err(TrySendError::Disconnected(0))), _ => panic!(), } } #[test] #[cfg_attr(miri, ignore)] // this test makes timing assumptions, but Miri is so slow it violates them fn default_only() { let start = Instant::now(); let mut sel = Select::new(); assert!(sel.try_ready().is_err()); let now = Instant::now(); assert!(now - start <= ms(50)); let start = Instant::now(); let mut sel = Select::new(); assert!(sel.ready_timeout(ms(500)).is_err()); let now = Instant::now(); assert!(now - start >= ms(450)); assert!(now - start <= ms(550)); } #[test] fn unblocks() { let (s1, r1) = bounded::(0); let (s2, r2) = bounded::(0); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); s2.send(2).unwrap(); }); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); match sel.ready_timeout(ms(1000)) { Ok(1) => assert_eq!(r2.try_recv(), Ok(2)), _ => panic!(), } }) .unwrap(); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); assert_eq!(r1.recv().unwrap(), 1); }); let mut sel = Select::new(); let oper1 = sel.send(&s1); let oper2 = sel.send(&s2); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => oper.send(&s1, 1).unwrap(), i if i == oper2 => panic!(), _ => unreachable!(), }, } }) .unwrap(); } #[test] fn both_ready() { let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); s1.send(1).unwrap(); assert_eq!(r2.recv().unwrap(), 2); }); for _ in 0..2 { let mut sel = Select::new(); sel.recv(&r1); sel.send(&s2); match sel.ready() { 0 => assert_eq!(r1.try_recv(), Ok(1)), 1 => s2.try_send(2).unwrap(), _ => panic!(), } } }) .unwrap(); } #[test] fn cloning1() { scope(|scope| { let (s1, r1) = unbounded::(); let (_s2, r2) = unbounded::(); let (s3, r3) = unbounded::<()>(); scope.spawn(move |_| { r3.recv().unwrap(); drop(s1.clone()); assert!(r3.try_recv().is_err()); s1.send(1).unwrap(); r3.recv().unwrap(); }); s3.send(()).unwrap(); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); match sel.ready() { 0 => drop(r1.try_recv()), 1 => drop(r2.try_recv()), _ => panic!(), } s3.send(()).unwrap(); }) .unwrap(); } #[test] fn cloning2() { let (s1, r1) = unbounded::<()>(); let (s2, r2) = unbounded::<()>(); let (_s3, _r3) = unbounded::<()>(); scope(|scope| { scope.spawn(move |_| { let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); match sel.ready() { 0 => panic!(), 1 => drop(r2.try_recv()), _ => panic!(), } }); thread::sleep(ms(500)); drop(s1.clone()); s2.send(()).unwrap(); }) .unwrap(); } #[test] fn preflight1() { let (s, r) = unbounded(); s.send(()).unwrap(); let mut sel = Select::new(); sel.recv(&r); match sel.ready() { 0 => drop(r.try_recv()), _ => panic!(), } } #[test] fn preflight2() { let (s, r) = unbounded(); drop(s.clone()); s.send(()).unwrap(); drop(s); let mut sel = Select::new(); sel.recv(&r); match sel.ready() { 0 => assert_eq!(r.try_recv(), Ok(())), _ => panic!(), } assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)); } #[test] fn preflight3() { let (s, r) = unbounded(); drop(s.clone()); s.send(()).unwrap(); drop(s); r.recv().unwrap(); let mut sel = Select::new(); sel.recv(&r); match sel.ready() { 0 => assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)), _ => panic!(), } } #[test] fn duplicate_operations() { let (s, r) = unbounded::(); let hit = vec![Cell::new(false); 4]; while hit.iter().map(|h| h.get()).any(|hit| !hit) { let mut sel = Select::new(); sel.recv(&r); sel.recv(&r); sel.send(&s); sel.send(&s); match sel.ready() { 0 => { assert!(r.try_recv().is_ok()); hit[0].set(true); } 1 => { assert!(r.try_recv().is_ok()); hit[1].set(true); } 2 => { assert!(s.try_send(0).is_ok()); hit[2].set(true); } 3 => { assert!(s.try_send(0).is_ok()); hit[3].set(true); } _ => panic!(), } } } #[test] fn nesting() { let (s, r) = unbounded::(); let mut sel = Select::new(); sel.send(&s); match sel.ready() { 0 => { assert!(s.try_send(0).is_ok()); let mut sel = Select::new(); sel.recv(&r); match sel.ready() { 0 => { assert_eq!(r.try_recv(), Ok(0)); let mut sel = Select::new(); sel.send(&s); match sel.ready() { 0 => { assert!(s.try_send(1).is_ok()); let mut sel = Select::new(); sel.recv(&r); match sel.ready() { 0 => { assert_eq!(r.try_recv(), Ok(1)); } _ => panic!(), } } _ => panic!(), } } _ => panic!(), } } _ => panic!(), } } #[test] fn stress_recv() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = unbounded(); let (s2, r2) = bounded(5); let (s3, r3) = bounded(0); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { s1.send(i).unwrap(); r3.recv().unwrap(); s2.send(i).unwrap(); r3.recv().unwrap(); } }); for i in 0..COUNT { for _ in 0..2 { let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); match sel.ready() { 0 => assert_eq!(r1.try_recv(), Ok(i)), 1 => assert_eq!(r2.try_recv(), Ok(i)), _ => panic!(), } s3.send(()).unwrap(); } } }) .unwrap(); } #[test] fn stress_send() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); let (s3, r3) = bounded(100); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { assert_eq!(r1.recv().unwrap(), i); assert_eq!(r2.recv().unwrap(), i); r3.recv().unwrap(); } }); for i in 0..COUNT { for _ in 0..2 { let mut sel = Select::new(); sel.send(&s1); sel.send(&s2); match sel.ready() { 0 => assert!(s1.try_send(i).is_ok()), 1 => assert!(s2.try_send(i).is_ok()), _ => panic!(), } } s3.send(()).unwrap(); } }) .unwrap(); } #[test] fn stress_mixed() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); let (s3, r3) = bounded(100); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { s1.send(i).unwrap(); assert_eq!(r2.recv().unwrap(), i); r3.recv().unwrap(); } }); for i in 0..COUNT { for _ in 0..2 { let mut sel = Select::new(); sel.recv(&r1); sel.send(&s2); match sel.ready() { 0 => assert_eq!(r1.try_recv(), Ok(i)), 1 => assert!(s2.try_send(i).is_ok()), _ => panic!(), } } s3.send(()).unwrap(); } }) .unwrap(); } #[test] fn stress_timeout_two_threads() { const COUNT: usize = 20; let (s, r) = bounded(2); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(500)); } loop { let mut sel = Select::new(); sel.send(&s); match sel.ready_timeout(ms(100)) { Err(_) => {} Ok(0) => { assert!(s.try_send(i).is_ok()); break; } Ok(_) => panic!(), } } } }); scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(500)); } loop { let mut sel = Select::new(); sel.recv(&r); match sel.ready_timeout(ms(100)) { Err(_) => {} Ok(0) => { assert_eq!(r.try_recv(), Ok(i)); break; } Ok(_) => panic!(), } } } }); }) .unwrap(); } #[test] fn send_recv_same_channel() { let (s, r) = bounded::(0); let mut sel = Select::new(); sel.send(&s); sel.recv(&r); assert!(sel.ready_timeout(ms(100)).is_err()); let (s, r) = unbounded::(); let mut sel = Select::new(); sel.send(&s); sel.recv(&r); match sel.ready_timeout(ms(100)) { Err(_) => panic!(), Ok(0) => assert!(s.try_send(0).is_ok()), Ok(_) => panic!(), } } #[test] fn channel_through_channel() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 1000; type T = Box; for cap in 1..4 { let (s, r) = bounded::(cap); scope(|scope| { scope.spawn(move |_| { let mut s = s; for _ in 0..COUNT { let (new_s, new_r) = bounded(cap); let new_r: T = Box::new(Some(new_r)); { let mut sel = Select::new(); sel.send(&s); match sel.ready() { 0 => assert!(s.try_send(new_r).is_ok()), _ => panic!(), } } s = new_s; } }); scope.spawn(move |_| { let mut r = r; for _ in 0..COUNT { let new = { let mut sel = Select::new(); sel.recv(&r); match sel.ready() { 0 => r .try_recv() .unwrap() .downcast_mut::>>() .unwrap() .take() .unwrap(), _ => panic!(), } }; r = new; } }); }) .unwrap(); } } #[test] fn fairness1() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded::<()>(COUNT); let (s2, r2) = unbounded::<()>(); for _ in 0..COUNT { s1.send(()).unwrap(); s2.send(()).unwrap(); } let hits = vec![Cell::new(0usize); 4]; for _ in 0..COUNT { let after = after(ms(0)); let tick = tick(ms(0)); let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); sel.recv(&after); sel.recv(&tick); match sel.ready() { 0 => { r1.try_recv().unwrap(); hits[0].set(hits[0].get() + 1); } 1 => { r2.try_recv().unwrap(); hits[1].set(hits[1].get() + 1); } 2 => { after.try_recv().unwrap(); hits[2].set(hits[2].get() + 1); } 3 => { tick.try_recv().unwrap(); hits[3].set(hits[3].get() + 1); } _ => panic!(), } } assert!(hits.iter().all(|x| x.get() >= COUNT / hits.len() / 2)); } #[test] fn fairness2() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 100_000; let (s1, r1) = unbounded::<()>(); let (s2, r2) = bounded::<()>(1); let (s3, r3) = bounded::<()>(0); scope(|scope| { scope.spawn(|_| { for _ in 0..COUNT { let mut sel = Select::new(); let mut oper1 = None; let mut oper2 = None; if s1.is_empty() { oper1 = Some(sel.send(&s1)); } if s2.is_empty() { oper2 = Some(sel.send(&s2)); } let oper3 = sel.send(&s3); let oper = sel.select(); match oper.index() { i if Some(i) == oper1 => assert!(oper.send(&s1, ()).is_ok()), i if Some(i) == oper2 => assert!(oper.send(&s2, ()).is_ok()), i if i == oper3 => assert!(oper.send(&s3, ()).is_ok()), _ => unreachable!(), } } }); let hits = vec![Cell::new(0usize); 3]; for _ in 0..COUNT { let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); sel.recv(&r3); loop { match sel.ready() { 0 => { if r1.try_recv().is_ok() { hits[0].set(hits[0].get() + 1); break; } } 1 => { if r2.try_recv().is_ok() { hits[1].set(hits[1].get() + 1); break; } } 2 => { if r3.try_recv().is_ok() { hits[2].set(hits[2].get() + 1); break; } } _ => unreachable!(), } } } assert!(hits.iter().all(|x| x.get() > 0)); }) .unwrap(); } crossbeam-channel-0.5.14/tests/same_channel.rs000064400000000000000000000045511046102023000174310ustar 00000000000000use std::time::Duration; use crossbeam_channel::{after, bounded, never, tick, unbounded}; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] fn after_same_channel() { let r = after(ms(50)); let r2 = r.clone(); assert!(r.same_channel(&r2)); let r3 = after(ms(50)); assert!(!r.same_channel(&r3)); assert!(!r2.same_channel(&r3)); let r4 = after(ms(100)); assert!(!r.same_channel(&r4)); assert!(!r2.same_channel(&r4)); } #[test] fn array_same_channel() { let (s, r) = bounded::(1); let s2 = s.clone(); assert!(s.same_channel(&s2)); let r2 = r.clone(); assert!(r.same_channel(&r2)); let (s3, r3) = bounded::(1); assert!(!s.same_channel(&s3)); assert!(!s2.same_channel(&s3)); assert!(!r.same_channel(&r3)); assert!(!r2.same_channel(&r3)); } #[test] fn list_same_channel() { let (s, r) = unbounded::(); let s2 = s.clone(); assert!(s.same_channel(&s2)); let r2 = r.clone(); assert!(r.same_channel(&r2)); let (s3, r3) = unbounded::(); assert!(!s.same_channel(&s3)); assert!(!s2.same_channel(&s3)); assert!(!r.same_channel(&r3)); assert!(!r2.same_channel(&r3)); } #[test] fn never_same_channel() { let r = never::(); let r2 = r.clone(); assert!(r.same_channel(&r2)); // Never channel are always equal to one another. let r3 = never::(); assert!(r.same_channel(&r3)); assert!(r2.same_channel(&r3)); } #[test] fn tick_same_channel() { let r = tick(ms(50)); let r2 = r.clone(); assert!(r.same_channel(&r2)); let r3 = tick(ms(50)); assert!(!r.same_channel(&r3)); assert!(!r2.same_channel(&r3)); let r4 = tick(ms(100)); assert!(!r.same_channel(&r4)); assert!(!r2.same_channel(&r4)); } #[test] fn zero_same_channel() { let (s, r) = bounded::(0); let s2 = s.clone(); assert!(s.same_channel(&s2)); let r2 = r.clone(); assert!(r.same_channel(&r2)); let (s3, r3) = bounded::(0); assert!(!s.same_channel(&s3)); assert!(!s2.same_channel(&s3)); assert!(!r.same_channel(&r3)); assert!(!r2.same_channel(&r3)); } #[test] fn different_flavors_same_channel() { let (s1, r1) = bounded::(0); let (s2, r2) = unbounded::(); assert!(!s1.same_channel(&s2)); assert!(!r1.same_channel(&r2)); } crossbeam-channel-0.5.14/tests/select.rs000064400000000000000000001066311046102023000162750ustar 00000000000000//! Tests for channel selection using the `Select` struct. use std::any::Any; use std::cell::Cell; use std::thread; use std::time::{Duration, Instant}; use crossbeam_channel::{after, bounded, tick, unbounded, Receiver, Select, TryRecvError}; use crossbeam_utils::thread::scope; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] fn smoke1() { let (s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); s1.send(1).unwrap(); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select(); match oper.index() { i if i == oper1 => assert_eq!(oper.recv(&r1), Ok(1)), i if i == oper2 => panic!(), _ => unreachable!(), } s2.send(2).unwrap(); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select(); match oper.index() { i if i == oper1 => panic!(), i if i == oper2 => assert_eq!(oper.recv(&r2), Ok(2)), _ => unreachable!(), } } #[test] fn smoke2() { let (_s1, r1) = unbounded::(); let (_s2, r2) = unbounded::(); let (_s3, r3) = unbounded::(); let (_s4, r4) = unbounded::(); let (s5, r5) = unbounded::(); s5.send(5).unwrap(); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper3 = sel.recv(&r3); let oper4 = sel.recv(&r4); let oper5 = sel.recv(&r5); let oper = sel.select(); match oper.index() { i if i == oper1 => panic!(), i if i == oper2 => panic!(), i if i == oper3 => panic!(), i if i == oper4 => panic!(), i if i == oper5 => assert_eq!(oper.recv(&r5), Ok(5)), _ => unreachable!(), } } #[test] fn disconnected() { let (s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); scope(|scope| { scope.spawn(|_| { drop(s1); thread::sleep(ms(500)); s2.send(5).unwrap(); }); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => assert!(oper.recv(&r1).is_err()), i if i == oper2 => panic!(), _ => unreachable!(), }, } r2.recv().unwrap(); }) .unwrap(); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => assert!(oper.recv(&r1).is_err()), i if i == oper2 => panic!(), _ => unreachable!(), }, } scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); drop(s2); }); let mut sel = Select::new(); let oper1 = sel.recv(&r2); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => assert!(oper.recv(&r2).is_err()), _ => unreachable!(), }, } }) .unwrap(); } #[test] fn default() { let (s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); let mut sel = Select::new(); let _oper1 = sel.recv(&r1); let _oper2 = sel.recv(&r2); let oper = sel.try_select(); match oper { Err(_) => {} Ok(_) => panic!(), } drop(s1); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.try_select(); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => assert!(oper.recv(&r1).is_err()), i if i == oper2 => panic!(), _ => unreachable!(), }, } s2.send(2).unwrap(); let mut sel = Select::new(); let oper1 = sel.recv(&r2); let oper = sel.try_select(); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => assert_eq!(oper.recv(&r2), Ok(2)), _ => unreachable!(), }, } let mut sel = Select::new(); let _oper1 = sel.recv(&r2); let oper = sel.try_select(); match oper { Err(_) => {} Ok(_) => panic!(), } let mut sel = Select::new(); let oper = sel.try_select(); match oper { Err(_) => {} Ok(_) => panic!(), } } #[test] fn timeout() { let (_s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(1500)); s2.send(2).unwrap(); }); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => {} Ok(oper) => match oper.index() { i if i == oper1 => panic!(), i if i == oper2 => panic!(), _ => unreachable!(), }, } let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => panic!(), i if i == oper2 => assert_eq!(oper.recv(&r2), Ok(2)), _ => unreachable!(), }, } }) .unwrap(); scope(|scope| { let (s, r) = unbounded::(); scope.spawn(move |_| { thread::sleep(ms(500)); drop(s); }); let mut sel = Select::new(); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => { let mut sel = Select::new(); let oper1 = sel.recv(&r); let oper = sel.try_select(); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => assert!(oper.recv(&r).is_err()), _ => unreachable!(), }, } } Ok(_) => unreachable!(), } }) .unwrap(); } #[test] fn default_when_disconnected() { let (_, r) = unbounded::(); let mut sel = Select::new(); let oper1 = sel.recv(&r); let oper = sel.try_select(); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => assert!(oper.recv(&r).is_err()), _ => unreachable!(), }, } let (_, r) = unbounded::(); let mut sel = Select::new(); let oper1 = sel.recv(&r); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => assert!(oper.recv(&r).is_err()), _ => unreachable!(), }, } let (s, _) = bounded::(0); let mut sel = Select::new(); let oper1 = sel.send(&s); let oper = sel.try_select(); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => assert!(oper.send(&s, 0).is_err()), _ => unreachable!(), }, } let (s, _) = bounded::(0); let mut sel = Select::new(); let oper1 = sel.send(&s); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => assert!(oper.send(&s, 0).is_err()), _ => unreachable!(), }, } } #[test] fn default_only() { let start = Instant::now(); let mut sel = Select::new(); let oper = sel.try_select(); assert!(oper.is_err()); let now = Instant::now(); assert!(now - start <= ms(50)); let start = Instant::now(); let mut sel = Select::new(); let oper = sel.select_timeout(ms(500)); assert!(oper.is_err()); let now = Instant::now(); assert!(now - start >= ms(450)); assert!(now - start <= ms(550)); } #[test] fn unblocks() { let (s1, r1) = bounded::(0); let (s2, r2) = bounded::(0); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); s2.send(2).unwrap(); }); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => panic!(), i if i == oper2 => assert_eq!(oper.recv(&r2), Ok(2)), _ => unreachable!(), }, } }) .unwrap(); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); assert_eq!(r1.recv().unwrap(), 1); }); let mut sel = Select::new(); let oper1 = sel.send(&s1); let oper2 = sel.send(&s2); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { i if i == oper1 => oper.send(&s1, 1).unwrap(), i if i == oper2 => panic!(), _ => unreachable!(), }, } }) .unwrap(); } #[test] fn both_ready() { let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); s1.send(1).unwrap(); assert_eq!(r2.recv().unwrap(), 2); }); for _ in 0..2 { let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.send(&s2); let oper = sel.select(); match oper.index() { i if i == oper1 => assert_eq!(oper.recv(&r1), Ok(1)), i if i == oper2 => oper.send(&s2, 2).unwrap(), _ => unreachable!(), } } }) .unwrap(); } #[test] fn loop_try() { const RUNS: usize = 20; for _ in 0..RUNS { let (s1, r1) = bounded::(0); let (s2, r2) = bounded::(0); let (s_end, r_end) = bounded::<()>(0); scope(|scope| { scope.spawn(|_| loop { let mut done = false; let mut sel = Select::new(); let oper1 = sel.send(&s1); let oper = sel.try_select(); match oper { Err(_) => {} Ok(oper) => match oper.index() { i if i == oper1 => { let _ = oper.send(&s1, 1); done = true; } _ => unreachable!(), }, } if done { break; } let mut sel = Select::new(); let oper1 = sel.recv(&r_end); let oper = sel.try_select(); match oper { Err(_) => {} Ok(oper) => match oper.index() { i if i == oper1 => { let _ = oper.recv(&r_end); done = true; } _ => unreachable!(), }, } if done { break; } }); scope.spawn(|_| loop { if let Ok(x) = r2.try_recv() { assert_eq!(x, 2); break; } let mut done = false; let mut sel = Select::new(); let oper1 = sel.recv(&r_end); let oper = sel.try_select(); match oper { Err(_) => {} Ok(oper) => match oper.index() { i if i == oper1 => { let _ = oper.recv(&r_end); done = true; } _ => unreachable!(), }, } if done { break; } }); scope.spawn(|_| { thread::sleep(ms(500)); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.send(&s2); let oper = sel.select_timeout(ms(1000)); match oper { Err(_) => {} Ok(oper) => match oper.index() { i if i == oper1 => assert_eq!(oper.recv(&r1), Ok(1)), i if i == oper2 => assert!(oper.send(&s2, 2).is_ok()), _ => unreachable!(), }, } drop(s_end); }); }) .unwrap(); } } #[test] fn cloning1() { scope(|scope| { let (s1, r1) = unbounded::(); let (_s2, r2) = unbounded::(); let (s3, r3) = unbounded::<()>(); scope.spawn(move |_| { r3.recv().unwrap(); drop(s1.clone()); assert!(r3.try_recv().is_err()); s1.send(1).unwrap(); r3.recv().unwrap(); }); s3.send(()).unwrap(); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select(); match oper.index() { i if i == oper1 => drop(oper.recv(&r1)), i if i == oper2 => drop(oper.recv(&r2)), _ => unreachable!(), } s3.send(()).unwrap(); }) .unwrap(); } #[test] fn cloning2() { let (s1, r1) = unbounded::<()>(); let (s2, r2) = unbounded::<()>(); let (_s3, _r3) = unbounded::<()>(); scope(|scope| { scope.spawn(move |_| { let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select(); match oper.index() { i if i == oper1 => panic!(), i if i == oper2 => drop(oper.recv(&r2)), _ => unreachable!(), } }); thread::sleep(ms(500)); drop(s1.clone()); s2.send(()).unwrap(); }) .unwrap(); } #[test] fn preflight1() { let (s, r) = unbounded(); s.send(()).unwrap(); let mut sel = Select::new(); let oper1 = sel.recv(&r); let oper = sel.select(); match oper.index() { i if i == oper1 => drop(oper.recv(&r)), _ => unreachable!(), } } #[test] fn preflight2() { let (s, r) = unbounded(); drop(s.clone()); s.send(()).unwrap(); drop(s); let mut sel = Select::new(); let oper1 = sel.recv(&r); let oper = sel.select(); match oper.index() { i if i == oper1 => assert_eq!(oper.recv(&r), Ok(())), _ => unreachable!(), } assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)); } #[test] fn preflight3() { let (s, r) = unbounded(); drop(s.clone()); s.send(()).unwrap(); drop(s); r.recv().unwrap(); let mut sel = Select::new(); let oper1 = sel.recv(&r); let oper = sel.select(); match oper.index() { i if i == oper1 => assert!(oper.recv(&r).is_err()), _ => unreachable!(), } } #[test] fn duplicate_operations() { let (s, r) = unbounded::(); let hit = vec![Cell::new(false); 4]; while hit.iter().map(|h| h.get()).any(|hit| !hit) { let mut sel = Select::new(); let oper0 = sel.recv(&r); let oper1 = sel.recv(&r); let oper2 = sel.send(&s); let oper3 = sel.send(&s); let oper = sel.select(); match oper.index() { i if i == oper0 => { assert!(oper.recv(&r).is_ok()); hit[0].set(true); } i if i == oper1 => { assert!(oper.recv(&r).is_ok()); hit[1].set(true); } i if i == oper2 => { assert!(oper.send(&s, 0).is_ok()); hit[2].set(true); } i if i == oper3 => { assert!(oper.send(&s, 0).is_ok()); hit[3].set(true); } _ => unreachable!(), } } } #[test] fn nesting() { let (s, r) = unbounded::(); let mut sel = Select::new(); let oper1 = sel.send(&s); let oper = sel.select(); match oper.index() { i if i == oper1 => { assert!(oper.send(&s, 0).is_ok()); let mut sel = Select::new(); let oper1 = sel.recv(&r); let oper = sel.select(); match oper.index() { i if i == oper1 => { assert_eq!(oper.recv(&r), Ok(0)); let mut sel = Select::new(); let oper1 = sel.send(&s); let oper = sel.select(); match oper.index() { i if i == oper1 => { assert!(oper.send(&s, 1).is_ok()); let mut sel = Select::new(); let oper1 = sel.recv(&r); let oper = sel.select(); match oper.index() { i if i == oper1 => { assert_eq!(oper.recv(&r), Ok(1)); } _ => unreachable!(), } } _ => unreachable!(), } } _ => unreachable!(), } } _ => unreachable!(), } } #[test] fn stress_recv() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = unbounded(); let (s2, r2) = bounded(5); let (s3, r3) = bounded(100); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { s1.send(i).unwrap(); r3.recv().unwrap(); s2.send(i).unwrap(); r3.recv().unwrap(); } }); for i in 0..COUNT { for _ in 0..2 { let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select(); match oper.index() { ix if ix == oper1 => assert_eq!(oper.recv(&r1), Ok(i)), ix if ix == oper2 => assert_eq!(oper.recv(&r2), Ok(i)), _ => unreachable!(), } s3.send(()).unwrap(); } } }) .unwrap(); } #[test] fn stress_send() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); let (s3, r3) = bounded(100); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { assert_eq!(r1.recv().unwrap(), i); assert_eq!(r2.recv().unwrap(), i); r3.recv().unwrap(); } }); for i in 0..COUNT { for _ in 0..2 { let mut sel = Select::new(); let oper1 = sel.send(&s1); let oper2 = sel.send(&s2); let oper = sel.select(); match oper.index() { ix if ix == oper1 => assert!(oper.send(&s1, i).is_ok()), ix if ix == oper2 => assert!(oper.send(&s2, i).is_ok()), _ => unreachable!(), } } s3.send(()).unwrap(); } }) .unwrap(); } #[test] fn stress_mixed() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); let (s3, r3) = bounded(100); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { s1.send(i).unwrap(); assert_eq!(r2.recv().unwrap(), i); r3.recv().unwrap(); } }); for i in 0..COUNT { for _ in 0..2 { let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.send(&s2); let oper = sel.select(); match oper.index() { ix if ix == oper1 => assert_eq!(oper.recv(&r1), Ok(i)), ix if ix == oper2 => assert!(oper.send(&s2, i).is_ok()), _ => unreachable!(), } } s3.send(()).unwrap(); } }) .unwrap(); } #[test] fn stress_timeout_two_threads() { const COUNT: usize = 20; let (s, r) = bounded(2); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(500)); } loop { let mut sel = Select::new(); let oper1 = sel.send(&s); let oper = sel.select_timeout(ms(100)); match oper { Err(_) => {} Ok(oper) => match oper.index() { ix if ix == oper1 => { assert!(oper.send(&s, i).is_ok()); break; } _ => unreachable!(), }, } } } }); scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(500)); } loop { let mut sel = Select::new(); let oper1 = sel.recv(&r); let oper = sel.select_timeout(ms(100)); match oper { Err(_) => {} Ok(oper) => match oper.index() { ix if ix == oper1 => { assert_eq!(oper.recv(&r), Ok(i)); break; } _ => unreachable!(), }, } } } }); }) .unwrap(); } #[test] fn send_recv_same_channel() { let (s, r) = bounded::(0); let mut sel = Select::new(); let oper1 = sel.send(&s); let oper2 = sel.recv(&r); let oper = sel.select_timeout(ms(100)); match oper { Err(_) => {} Ok(oper) => match oper.index() { ix if ix == oper1 => panic!(), ix if ix == oper2 => panic!(), _ => unreachable!(), }, } let (s, r) = unbounded::(); let mut sel = Select::new(); let oper1 = sel.send(&s); let oper2 = sel.recv(&r); let oper = sel.select_timeout(ms(100)); match oper { Err(_) => panic!(), Ok(oper) => match oper.index() { ix if ix == oper1 => assert!(oper.send(&s, 0).is_ok()), ix if ix == oper2 => panic!(), _ => unreachable!(), }, } } #[test] fn matching() { const THREADS: usize = 44; let (s, r) = &bounded::(0); scope(|scope| { for i in 0..THREADS { scope.spawn(move |_| { let mut sel = Select::new(); let oper1 = sel.recv(r); let oper2 = sel.send(s); let oper = sel.select(); match oper.index() { ix if ix == oper1 => assert_ne!(oper.recv(r), Ok(i)), ix if ix == oper2 => assert!(oper.send(s, i).is_ok()), _ => unreachable!(), } }); } }) .unwrap(); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[test] fn matching_with_leftover() { const THREADS: usize = 55; let (s, r) = &bounded::(0); scope(|scope| { for i in 0..THREADS { scope.spawn(move |_| { let mut sel = Select::new(); let oper1 = sel.recv(r); let oper2 = sel.send(s); let oper = sel.select(); match oper.index() { ix if ix == oper1 => assert_ne!(oper.recv(r), Ok(i)), ix if ix == oper2 => assert!(oper.send(s, i).is_ok()), _ => unreachable!(), } }); } s.send(!0).unwrap(); }) .unwrap(); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[test] fn channel_through_channel() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 1000; type T = Box; for cap in 0..3 { let (s, r) = bounded::(cap); scope(|scope| { scope.spawn(move |_| { let mut s = s; for _ in 0..COUNT { let (new_s, new_r) = bounded(cap); let new_r: T = Box::new(Some(new_r)); { let mut sel = Select::new(); let oper1 = sel.send(&s); let oper = sel.select(); match oper.index() { ix if ix == oper1 => assert!(oper.send(&s, new_r).is_ok()), _ => unreachable!(), } } s = new_s; } }); scope.spawn(move |_| { let mut r = r; for _ in 0..COUNT { let new = { let mut sel = Select::new(); let oper1 = sel.recv(&r); let oper = sel.select(); match oper.index() { ix if ix == oper1 => oper .recv(&r) .unwrap() .downcast_mut::>>() .unwrap() .take() .unwrap(), _ => unreachable!(), } }; r = new; } }); }) .unwrap(); } } #[test] fn linearizable_try() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 100_000; for step in 0..2 { let (start_s, start_r) = bounded::<()>(0); let (end_s, end_r) = bounded::<()>(0); let ((s1, r1), (s2, r2)) = if step == 0 { (bounded::(1), bounded::(1)) } else { (unbounded::(), unbounded::()) }; scope(|scope| { scope.spawn(|_| { for _ in 0..COUNT { start_s.send(()).unwrap(); s1.send(1).unwrap(); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.try_select(); match oper { Err(_) => unreachable!(), Ok(oper) => match oper.index() { ix if ix == oper1 => assert!(oper.recv(&r1).is_ok()), ix if ix == oper2 => assert!(oper.recv(&r2).is_ok()), _ => unreachable!(), }, } end_s.send(()).unwrap(); let _ = r2.try_recv(); } }); for _ in 0..COUNT { start_r.recv().unwrap(); s2.send(1).unwrap(); let _ = r1.try_recv(); end_r.recv().unwrap(); } }) .unwrap(); } } #[test] fn linearizable_timeout() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 100_000; for step in 0..2 { let (start_s, start_r) = bounded::<()>(0); let (end_s, end_r) = bounded::<()>(0); let ((s1, r1), (s2, r2)) = if step == 0 { (bounded::(1), bounded::(1)) } else { (unbounded::(), unbounded::()) }; scope(|scope| { scope.spawn(|_| { for _ in 0..COUNT { start_s.send(()).unwrap(); s1.send(1).unwrap(); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper = sel.select_timeout(ms(0)); match oper { Err(_) => unreachable!(), Ok(oper) => match oper.index() { ix if ix == oper1 => assert!(oper.recv(&r1).is_ok()), ix if ix == oper2 => assert!(oper.recv(&r2).is_ok()), _ => unreachable!(), }, } end_s.send(()).unwrap(); let _ = r2.try_recv(); } }); for _ in 0..COUNT { start_r.recv().unwrap(); s2.send(1).unwrap(); let _ = r1.try_recv(); end_r.recv().unwrap(); } }) .unwrap(); } } #[test] fn fairness1() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded::<()>(COUNT); let (s2, r2) = unbounded::<()>(); for _ in 0..COUNT { s1.send(()).unwrap(); s2.send(()).unwrap(); } let hits = vec![Cell::new(0usize); 4]; for _ in 0..COUNT { let after = after(ms(0)); let tick = tick(ms(0)); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper3 = sel.recv(&after); let oper4 = sel.recv(&tick); let oper = sel.select(); match oper.index() { i if i == oper1 => { oper.recv(&r1).unwrap(); hits[0].set(hits[0].get() + 1); } i if i == oper2 => { oper.recv(&r2).unwrap(); hits[1].set(hits[1].get() + 1); } i if i == oper3 => { oper.recv(&after).unwrap(); hits[2].set(hits[2].get() + 1); } i if i == oper4 => { oper.recv(&tick).unwrap(); hits[3].set(hits[3].get() + 1); } _ => unreachable!(), } } assert!(hits.iter().all(|x| x.get() >= COUNT / hits.len() / 2)); } #[test] fn fairness2() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = unbounded::<()>(); let (s2, r2) = bounded::<()>(1); let (s3, r3) = bounded::<()>(0); scope(|scope| { scope.spawn(|_| { for _ in 0..COUNT { let mut sel = Select::new(); let mut oper1 = None; let mut oper2 = None; if s1.is_empty() { oper1 = Some(sel.send(&s1)); } if s2.is_empty() { oper2 = Some(sel.send(&s2)); } let oper3 = sel.send(&s3); let oper = sel.select(); match oper.index() { i if Some(i) == oper1 => assert!(oper.send(&s1, ()).is_ok()), i if Some(i) == oper2 => assert!(oper.send(&s2, ()).is_ok()), i if i == oper3 => assert!(oper.send(&s3, ()).is_ok()), _ => unreachable!(), } } }); let hits = vec![Cell::new(0usize); 3]; for _ in 0..COUNT { let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper3 = sel.recv(&r3); let oper = sel.select(); match oper.index() { i if i == oper1 => { oper.recv(&r1).unwrap(); hits[0].set(hits[0].get() + 1); } i if i == oper2 => { oper.recv(&r2).unwrap(); hits[1].set(hits[1].get() + 1); } i if i == oper3 => { oper.recv(&r3).unwrap(); hits[2].set(hits[2].get() + 1); } _ => unreachable!(), } } assert!(hits.iter().all(|x| x.get() >= COUNT / hits.len() / 50)); }) .unwrap(); } #[test] fn sync_and_clone() { const THREADS: usize = 20; let (s, r) = &bounded::(0); let mut sel = Select::new(); let oper1 = sel.recv(r); let oper2 = sel.send(s); let sel = &sel; scope(|scope| { for i in 0..THREADS { scope.spawn(move |_| { let mut sel = sel.clone(); let oper = sel.select(); match oper.index() { ix if ix == oper1 => assert_ne!(oper.recv(r), Ok(i)), ix if ix == oper2 => assert!(oper.send(s, i).is_ok()), _ => unreachable!(), } }); } }) .unwrap(); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[test] fn send_and_clone() { const THREADS: usize = 20; let (s, r) = &bounded::(0); let mut sel = Select::new(); let oper1 = sel.recv(r); let oper2 = sel.send(s); scope(|scope| { for i in 0..THREADS { let mut sel = sel.clone(); scope.spawn(move |_| { let oper = sel.select(); match oper.index() { ix if ix == oper1 => assert_ne!(oper.recv(r), Ok(i)), ix if ix == oper2 => assert!(oper.send(s, i).is_ok()), _ => unreachable!(), } }); } }) .unwrap(); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[test] fn reuse() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); let (s3, r3) = bounded(100); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { s1.send(i).unwrap(); assert_eq!(r2.recv().unwrap(), i); r3.recv().unwrap(); } }); let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.send(&s2); for i in 0..COUNT { for _ in 0..2 { let oper = sel.select(); match oper.index() { ix if ix == oper1 => assert_eq!(oper.recv(&r1), Ok(i)), ix if ix == oper2 => assert!(oper.send(&s2, i).is_ok()), _ => unreachable!(), } } s3.send(()).unwrap(); } }) .unwrap(); } crossbeam-channel-0.5.14/tests/select_macro.rs000064400000000000000000001052001046102023000174450ustar 00000000000000//! Tests for the `select!` macro. #![forbid(unsafe_code)] // select! is safe. #![allow(clippy::match_single_binding)] use std::any::Any; use std::cell::Cell; use std::ops::Deref; use std::thread; use std::time::{Duration, Instant}; use crossbeam_channel::{after, bounded, never, select, select_biased, tick, unbounded}; use crossbeam_channel::{Receiver, RecvError, SendError, Sender, TryRecvError}; use crossbeam_utils::thread::scope; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] fn smoke1() { let (s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); s1.send(1).unwrap(); select! { recv(r1) -> v => assert_eq!(v, Ok(1)), recv(r2) -> _ => panic!(), } s2.send(2).unwrap(); select! { recv(r1) -> _ => panic!(), recv(r2) -> v => assert_eq!(v, Ok(2)), } } #[test] fn smoke2() { let (_s1, r1) = unbounded::(); let (_s2, r2) = unbounded::(); let (_s3, r3) = unbounded::(); let (_s4, r4) = unbounded::(); let (s5, r5) = unbounded::(); s5.send(5).unwrap(); select! { recv(r1) -> _ => panic!(), recv(r2) -> _ => panic!(), recv(r3) -> _ => panic!(), recv(r4) -> _ => panic!(), recv(r5) -> v => assert_eq!(v, Ok(5)), } } #[test] fn disconnected() { let (s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); scope(|scope| { scope.spawn(|_| { drop(s1); thread::sleep(ms(500)); s2.send(5).unwrap(); }); select! { recv(r1) -> v => assert!(v.is_err()), recv(r2) -> _ => panic!(), default(ms(1000)) => panic!(), } r2.recv().unwrap(); }) .unwrap(); select! { recv(r1) -> v => assert!(v.is_err()), recv(r2) -> _ => panic!(), default(ms(1000)) => panic!(), } scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); drop(s2); }); select! { recv(r2) -> v => assert!(v.is_err()), default(ms(1000)) => panic!(), } }) .unwrap(); } #[test] fn default() { let (s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); select! { recv(r1) -> _ => panic!(), recv(r2) -> _ => panic!(), default => {} } drop(s1); select! { recv(r1) -> v => assert!(v.is_err()), recv(r2) -> _ => panic!(), default => panic!(), } s2.send(2).unwrap(); select! { recv(r2) -> v => assert_eq!(v, Ok(2)), default => panic!(), } select! { recv(r2) -> _ => panic!(), default => {}, } select! { default => {}, } } #[test] fn timeout() { let (_s1, r1) = unbounded::(); let (s2, r2) = unbounded::(); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(1500)); s2.send(2).unwrap(); }); select! { recv(r1) -> _ => panic!(), recv(r2) -> _ => panic!(), default(ms(1000)) => {}, } select! { recv(r1) -> _ => panic!(), recv(r2) -> v => assert_eq!(v, Ok(2)), default(ms(1000)) => panic!(), } }) .unwrap(); scope(|scope| { let (s, r) = unbounded::(); scope.spawn(move |_| { thread::sleep(ms(500)); drop(s); }); select! { default(ms(1000)) => { select! { recv(r) -> v => assert!(v.is_err()), default => panic!(), } } } }) .unwrap(); } #[test] fn default_when_disconnected() { let (_, r) = unbounded::(); select! { recv(r) -> res => assert!(res.is_err()), default => panic!(), } let (_, r) = unbounded::(); select! { recv(r) -> res => assert!(res.is_err()), default(ms(1000)) => panic!(), } let (s, _) = bounded::(0); select! { send(s, 0) -> res => assert!(res.is_err()), default => panic!(), } let (s, _) = bounded::(0); select! { send(s, 0) -> res => assert!(res.is_err()), default(ms(1000)) => panic!(), } } #[test] fn default_only() { let start = Instant::now(); select! { default => {} } let now = Instant::now(); assert!(now - start <= ms(50)); let start = Instant::now(); select! { default(ms(500)) => {} } let now = Instant::now(); assert!(now - start >= ms(450)); assert!(now - start <= ms(550)); } #[test] fn unblocks() { let (s1, r1) = bounded::(0); let (s2, r2) = bounded::(0); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); s2.send(2).unwrap(); }); select! { recv(r1) -> _ => panic!(), recv(r2) -> v => assert_eq!(v, Ok(2)), default(ms(1000)) => panic!(), } }) .unwrap(); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); assert_eq!(r1.recv().unwrap(), 1); }); select! { send(s1, 1) -> _ => {}, send(s2, 2) -> _ => panic!(), default(ms(1000)) => panic!(), } }) .unwrap(); } #[test] fn both_ready() { let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(500)); s1.send(1).unwrap(); assert_eq!(r2.recv().unwrap(), 2); }); for _ in 0..2 { select! { recv(r1) -> v => assert_eq!(v, Ok(1)), send(s2, 2) -> _ => {}, } } }) .unwrap(); } #[test] fn loop_try() { const RUNS: usize = 20; for _ in 0..RUNS { let (s1, r1) = bounded::(0); let (s2, r2) = bounded::(0); let (s_end, r_end) = bounded::<()>(0); scope(|scope| { scope.spawn(|_| loop { select! { send(s1, 1) -> _ => break, default => {} } select! { recv(r_end) -> _ => break, default => {} } }); scope.spawn(|_| loop { if let Ok(x) = r2.try_recv() { assert_eq!(x, 2); break; } select! { recv(r_end) -> _ => break, default => {} } }); scope.spawn(|_| { thread::sleep(ms(500)); select! { recv(r1) -> v => assert_eq!(v, Ok(1)), send(s2, 2) -> _ => {}, default(ms(500)) => panic!(), } drop(s_end); }); }) .unwrap(); } } #[test] fn cloning1() { scope(|scope| { let (s1, r1) = unbounded::(); let (_s2, r2) = unbounded::(); let (s3, r3) = unbounded::<()>(); scope.spawn(move |_| { r3.recv().unwrap(); drop(s1.clone()); assert_eq!(r3.try_recv(), Err(TryRecvError::Empty)); s1.send(1).unwrap(); r3.recv().unwrap(); }); s3.send(()).unwrap(); select! { recv(r1) -> _ => {}, recv(r2) -> _ => {}, } s3.send(()).unwrap(); }) .unwrap(); } #[test] fn cloning2() { let (s1, r1) = unbounded::<()>(); let (s2, r2) = unbounded::<()>(); let (_s3, _r3) = unbounded::<()>(); scope(|scope| { scope.spawn(move |_| { select! { recv(r1) -> _ => panic!(), recv(r2) -> _ => {}, } }); thread::sleep(ms(500)); drop(s1.clone()); s2.send(()).unwrap(); }) .unwrap(); } #[test] fn preflight1() { let (s, r) = unbounded(); s.send(()).unwrap(); select! { recv(r) -> _ => {} } } #[test] fn preflight2() { let (s, r) = unbounded(); drop(s.clone()); s.send(()).unwrap(); drop(s); select! { recv(r) -> v => assert!(v.is_ok()), } assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)); } #[test] fn preflight3() { let (s, r) = unbounded(); drop(s.clone()); s.send(()).unwrap(); drop(s); r.recv().unwrap(); select! { recv(r) -> v => assert!(v.is_err()) } } #[test] fn duplicate_operations() { let (s, r) = unbounded::(); let mut hit = [false; 4]; while hit.iter().any(|hit| !hit) { select! { recv(r) -> _ => hit[0] = true, recv(r) -> _ => hit[1] = true, send(s, 0) -> _ => hit[2] = true, send(s, 0) -> _ => hit[3] = true, } } } #[test] fn nesting() { let (s, r) = unbounded::(); select! { send(s, 0) -> _ => { select! { recv(r) -> v => { assert_eq!(v, Ok(0)); select! { send(s, 1) -> _ => { select! { recv(r) -> v => { assert_eq!(v, Ok(1)); } } } } } } } } } #[test] #[should_panic(expected = "send panicked")] fn panic_sender() { fn get() -> Sender { panic!("send panicked") } #[allow(unreachable_code)] { select! { send(get(), panic!()) -> _ => {} } } } #[test] #[should_panic(expected = "recv panicked")] fn panic_receiver() { fn get() -> Receiver { panic!("recv panicked") } select! { recv(get()) -> _ => {} } } #[test] fn stress_recv() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = unbounded(); let (s2, r2) = bounded(5); let (s3, r3) = bounded(100); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { s1.send(i).unwrap(); r3.recv().unwrap(); s2.send(i).unwrap(); r3.recv().unwrap(); } }); for i in 0..COUNT { for _ in 0..2 { select! { recv(r1) -> v => assert_eq!(v, Ok(i)), recv(r2) -> v => assert_eq!(v, Ok(i)), } s3.send(()).unwrap(); } } }) .unwrap(); } #[test] fn stress_send() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); let (s3, r3) = bounded(100); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { assert_eq!(r1.recv().unwrap(), i); assert_eq!(r2.recv().unwrap(), i); r3.recv().unwrap(); } }); for i in 0..COUNT { for _ in 0..2 { select! { send(s1, i) -> _ => {}, send(s2, i) -> _ => {}, } } s3.send(()).unwrap(); } }) .unwrap(); } #[test] fn stress_mixed() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded(0); let (s2, r2) = bounded(0); let (s3, r3) = bounded(100); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { s1.send(i).unwrap(); assert_eq!(r2.recv().unwrap(), i); r3.recv().unwrap(); } }); for i in 0..COUNT { for _ in 0..2 { select! { recv(r1) -> v => assert_eq!(v, Ok(i)), send(s2, i) -> _ => {}, } } s3.send(()).unwrap(); } }) .unwrap(); } #[test] fn stress_timeout_two_threads() { const COUNT: usize = 20; let (s, r) = bounded(2); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(500)); } loop { select! { send(s, i) -> _ => break, default(ms(100)) => {} } } } }); scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(500)); } loop { select! { recv(r) -> v => { assert_eq!(v, Ok(i)); break; } default(ms(100)) => {} } } } }); }) .unwrap(); } #[test] fn send_recv_same_channel() { let (s, r) = bounded::(0); select! { send(s, 0) -> _ => panic!(), recv(r) -> _ => panic!(), default(ms(500)) => {} } let (s, r) = unbounded::(); select! { send(s, 0) -> _ => {}, recv(r) -> _ => panic!(), default(ms(500)) => panic!(), } } #[test] fn matching() { const THREADS: usize = 44; let (s, r) = &bounded::(0); scope(|scope| { for i in 0..THREADS { scope.spawn(move |_| { select! { recv(r) -> v => assert_ne!(v.unwrap(), i), send(s, i) -> _ => {}, } }); } }) .unwrap(); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[test] fn matching_with_leftover() { const THREADS: usize = 55; let (s, r) = &bounded::(0); scope(|scope| { for i in 0..THREADS { scope.spawn(move |_| { select! { recv(r) -> v => assert_ne!(v.unwrap(), i), send(s, i) -> _ => {}, } }); } s.send(!0).unwrap(); }) .unwrap(); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[test] fn channel_through_channel() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 1000; type T = Box; for cap in 0..3 { let (s, r) = bounded::(cap); scope(|scope| { scope.spawn(move |_| { let mut s = s; for _ in 0..COUNT { let (new_s, new_r) = bounded(cap); let new_r: T = Box::new(Some(new_r)); select! { send(s, new_r) -> _ => {} } s = new_s; } }); scope.spawn(move |_| { let mut r = r; for _ in 0..COUNT { r = select! { recv(r) -> msg => { msg.unwrap() .downcast_mut::>>() .unwrap() .take() .unwrap() } } } }); }) .unwrap(); } } #[test] fn linearizable_default() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 100_000; for step in 0..2 { let (start_s, start_r) = bounded::<()>(0); let (end_s, end_r) = bounded::<()>(0); let ((s1, r1), (s2, r2)) = if step == 0 { (bounded::(1), bounded::(1)) } else { (unbounded::(), unbounded::()) }; scope(|scope| { scope.spawn(|_| { for _ in 0..COUNT { start_s.send(()).unwrap(); s1.send(1).unwrap(); select! { recv(r1) -> _ => {} recv(r2) -> _ => {} default => unreachable!() } end_s.send(()).unwrap(); let _ = r2.try_recv(); } }); for _ in 0..COUNT { start_r.recv().unwrap(); s2.send(1).unwrap(); let _ = r1.try_recv(); end_r.recv().unwrap(); } }) .unwrap(); } } #[test] fn linearizable_timeout() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 100_000; for step in 0..2 { let (start_s, start_r) = bounded::<()>(0); let (end_s, end_r) = bounded::<()>(0); let ((s1, r1), (s2, r2)) = if step == 0 { (bounded::(1), bounded::(1)) } else { (unbounded::(), unbounded::()) }; scope(|scope| { scope.spawn(|_| { for _ in 0..COUNT { start_s.send(()).unwrap(); s1.send(1).unwrap(); select! { recv(r1) -> _ => {} recv(r2) -> _ => {} default(ms(0)) => unreachable!() } end_s.send(()).unwrap(); let _ = r2.try_recv(); } }); for _ in 0..COUNT { start_r.recv().unwrap(); s2.send(1).unwrap(); let _ = r1.try_recv(); end_r.recv().unwrap(); } }) .unwrap(); } } #[test] fn fairness1() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded::<()>(COUNT); let (s2, r2) = unbounded::<()>(); for _ in 0..COUNT { s1.send(()).unwrap(); s2.send(()).unwrap(); } let mut hits = [0usize; 4]; for _ in 0..COUNT { select! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, recv(after(ms(0))) -> _ => hits[2] += 1, recv(tick(ms(0))) -> _ => hits[3] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); } #[cfg_attr(crossbeam_sanitize, ignore)] // TODO: flaky: https://github.com/crossbeam-rs/crossbeam/issues/1094 #[test] fn fairness2() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = unbounded::<()>(); let (s2, r2) = bounded::<()>(1); let (s3, r3) = bounded::<()>(0); scope(|scope| { scope.spawn(|_| { let (hole, _r) = bounded(0); for _ in 0..COUNT { let s1 = if s1.is_empty() { &s1 } else { &hole }; let s2 = if s2.is_empty() { &s2 } else { &hole }; select! { send(s1, ()) -> res => assert!(res.is_ok()), send(s2, ()) -> res => assert!(res.is_ok()), send(s3, ()) -> res => assert!(res.is_ok()), } } }); let hits = vec![Cell::new(0usize); 3]; for _ in 0..COUNT { select! { recv(r1) -> _ => hits[0].set(hits[0].get() + 1), recv(r2) -> _ => hits[1].set(hits[1].get() + 1), recv(r3) -> _ => hits[2].set(hits[2].get() + 1), } } assert!(hits.iter().all(|x| x.get() >= COUNT / hits.len() / 50)); }) .unwrap(); } #[test] fn fairness_recv() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded::<()>(COUNT); let (s2, r2) = unbounded::<()>(); for _ in 0..COUNT { s1.send(()).unwrap(); s2.send(()).unwrap(); } let mut hits = [0usize; 2]; while hits[0] + hits[1] < COUNT { select! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / 4)); } #[test] fn fairness_send() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, _r1) = bounded::<()>(COUNT); let (s2, _r2) = unbounded::<()>(); let mut hits = [0usize; 2]; for _ in 0..COUNT { select! { send(s1, ()) -> _ => hits[0] += 1, send(s2, ()) -> _ => hits[1] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / 4)); } #[test] fn unfairness() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = unbounded::<()>(); let (s2, r2) = unbounded::<()>(); let (s3, r3) = unbounded::<()>(); for _ in 0..COUNT { s1.send(()).unwrap(); s2.send(()).unwrap(); } s3.send(()).unwrap(); let mut hits = [0usize; 3]; for _ in 0..COUNT { select_biased! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, recv(r3) -> _ => hits[2] += 1, } } assert_eq!(hits, [COUNT, 0, 0]); for _ in 0..COUNT { select_biased! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, recv(r3) -> _ => hits[2] += 1, } } assert_eq!(hits, [COUNT, COUNT, 0]); } #[test] fn unfairness_timeout() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = unbounded::<()>(); let (s2, r2) = unbounded::<()>(); let (s3, r3) = unbounded::<()>(); for _ in 0..COUNT { s1.send(()).unwrap(); s2.send(()).unwrap(); } s3.send(()).unwrap(); let mut hits = [0usize; 3]; for _ in 0..COUNT { select_biased! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, recv(r3) -> _ => hits[2] += 1, default(ms(1000)) => unreachable!(), } } assert_eq!(hits, [COUNT, 0, 0]); for _ in 0..COUNT { select_biased! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, recv(r3) -> _ => hits[2] += 1, default(ms(1000)) => unreachable!(), } } assert_eq!(hits, [COUNT, COUNT, 0]); } #[test] fn unfairness_try() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = unbounded::<()>(); let (s2, r2) = unbounded::<()>(); let (s3, r3) = unbounded::<()>(); for _ in 0..COUNT { s1.send(()).unwrap(); s2.send(()).unwrap(); } s3.send(()).unwrap(); let mut hits = [0usize; 3]; for _ in 0..COUNT { select_biased! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, recv(r3) -> _ => hits[2] += 1, default() => unreachable!(), } } assert_eq!(hits, [COUNT, 0, 0]); for _ in 0..COUNT { select_biased! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, recv(r3) -> _ => hits[2] += 1, default() => unreachable!(), } } assert_eq!(hits, [COUNT, COUNT, 0]); } #[allow(clippy::or_fun_call, clippy::unnecessary_literal_unwrap)] // This is intentional. #[test] fn references() { let (s, r) = unbounded::(); select! { send(s, 0) -> _ => {} recv(r) -> _ => {} } select! { send(&&&&s, 0) -> _ => {} recv(&&&&r) -> _ => {} } select! { recv(Some(&r).unwrap_or(&never())) -> _ => {}, default => {} } select! { recv(Some(r).unwrap_or(never())) -> _ => {}, default => {} } } #[test] fn case_blocks() { let (s, r) = unbounded::(); select! { recv(r) -> _ => 3.0, recv(r) -> _ => loop { unreachable!() }, recv(r) -> _ => match 7 + 3 { _ => unreachable!() }, default => 7. }; select! { recv(r) -> msg => if msg.is_ok() { unreachable!() }, default => () } drop(s); } #[allow(clippy::redundant_closure_call)] // This is intentional. #[test] fn move_handles() { let (s, r) = unbounded::(); select! { recv((move || r)()) -> _ => {} send((move || s)(), 0) -> _ => {} } } #[test] fn infer_types() { let (s, r) = unbounded(); select! { recv(r) -> _ => {} default => {} } s.send(()).unwrap(); let (s, r) = unbounded(); select! { send(s, ()) -> _ => {} } r.recv().unwrap(); } #[test] fn default_syntax() { let (s, r) = bounded::(0); select! { recv(r) -> _ => panic!(), default => {} } select! { send(s, 0) -> _ => panic!(), default() => {} } select! { default => {} } select! { default() => {} } } #[test] fn same_variable_name() { let (_, r) = unbounded::(); select! { recv(r) -> r => assert!(r.is_err()), } } #[test] fn handles_on_heap() { let (s, r) = unbounded::(); let (s, r) = (Box::new(s), Box::new(r)); select! { send(*s, 0) -> _ => {} recv(*r) -> _ => {} default => {} } drop(s); drop(r); } #[test] fn once_blocks() { let (s, r) = unbounded::(); let once = Box::new(()); select! { send(s, 0) -> _ => drop(once), } let once = Box::new(()); select! { recv(r) -> _ => drop(once), } let once1 = Box::new(()); let once2 = Box::new(()); select! { send(s, 0) -> _ => drop(once1), default => drop(once2), } let once1 = Box::new(()); let once2 = Box::new(()); select! { recv(r) -> _ => drop(once1), default => drop(once2), } let once1 = Box::new(()); let once2 = Box::new(()); select! { recv(r) -> _ => drop(once1), send(s, 0) -> _ => drop(once2), } } #[test] fn once_receiver() { let (_, r) = unbounded::(); let once = Box::new(()); let get = move || { drop(once); r }; select! { recv(get()) -> _ => {} } } #[test] fn once_sender() { let (s, _) = unbounded::(); let once = Box::new(()); let get = move || { drop(once); s }; select! { send(get(), 5) -> _ => {} } } #[test] fn parse_nesting() { let (_, r) = unbounded::(); select! { recv(r) -> _ => {} recv(r) -> _ => { select! { recv(r) -> _ => {} recv(r) -> _ => { select! { recv(r) -> _ => {} recv(r) -> _ => { select! { default => {} } } } } } } } } #[test] fn evaluate() { let (s, r) = unbounded::(); let v = select! { recv(r) -> _ => "foo".into(), send(s, 0) -> _ => "bar".to_owned(), default => "baz".to_string(), }; assert_eq!(v, "bar"); let v = select! { recv(r) -> _ => "foo".into(), default => "baz".to_string(), }; assert_eq!(v, "foo"); let v = select! { recv(r) -> _ => "foo".into(), default => "baz".to_string(), }; assert_eq!(v, "baz"); } #[test] fn deref() { use crossbeam_channel as cc; struct Sender(cc::Sender); struct Receiver(cc::Receiver); impl Deref for Receiver { type Target = cc::Receiver; fn deref(&self) -> &Self::Target { &self.0 } } impl Deref for Sender { type Target = cc::Sender; fn deref(&self) -> &Self::Target { &self.0 } } let (s, r) = bounded::(0); let (s, r) = (Sender(s), Receiver(r)); select! { send(s, 0) -> _ => panic!(), recv(r) -> _ => panic!(), default => {} } } #[test] fn result_types() { let (s, _) = bounded::(0); let (_, r) = bounded::(0); select! { recv(r) -> res => { let _: Result = res; }, } select! { recv(r) -> res => { let _: Result = res; }, default => {} } select! { recv(r) -> res => { let _: Result = res; }, default(ms(0)) => {} } select! { send(s, 0) -> res => { let _: Result<(), SendError> = res; }, } select! { send(s, 0) -> res => { let _: Result<(), SendError> = res; }, default => {} } select! { send(s, 0) -> res => { let _: Result<(), SendError> = res; }, default(ms(0)) => {} } select! { send(s, 0) -> res => { let _: Result<(), SendError> = res; }, recv(r) -> res => { let _: Result = res; }, } } #[test] fn try_recv() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { select! { recv(r) -> _ => panic!(), default => {} } thread::sleep(ms(1500)); select! { recv(r) -> v => assert_eq!(v, Ok(7)), default => panic!(), } thread::sleep(ms(500)); select! { recv(r) -> v => assert_eq!(v, Err(RecvError)), default => panic!(), } }); scope.spawn(move |_| { thread::sleep(ms(1000)); select! { send(s, 7) -> res => res.unwrap(), } }); }) .unwrap(); } #[test] fn recv() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { select! { recv(r) -> v => assert_eq!(v, Ok(7)), } thread::sleep(ms(1000)); select! { recv(r) -> v => assert_eq!(v, Ok(8)), } thread::sleep(ms(1000)); select! { recv(r) -> v => assert_eq!(v, Ok(9)), } select! { recv(r) -> v => assert_eq!(v, Err(RecvError)), } }); scope.spawn(move |_| { thread::sleep(ms(1500)); select! { send(s, 7) -> res => res.unwrap(), } select! { send(s, 8) -> res => res.unwrap(), } select! { send(s, 9) -> res => res.unwrap(), } }); }) .unwrap(); } #[test] fn recv_timeout() { let (s, r) = bounded::(0); scope(|scope| { scope.spawn(move |_| { select! { recv(r) -> _ => panic!(), default(ms(1000)) => {} } select! { recv(r) -> v => assert_eq!(v, Ok(7)), default(ms(1000)) => panic!(), } select! { recv(r) -> v => assert_eq!(v, Err(RecvError)), default(ms(1000)) => panic!(), } }); scope.spawn(move |_| { thread::sleep(ms(1500)); select! { send(s, 7) -> res => res.unwrap(), } }); }) .unwrap(); } #[test] fn try_send() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { select! { send(s, 7) -> _ => panic!(), default => {} } thread::sleep(ms(1500)); select! { send(s, 8) -> res => res.unwrap(), default => panic!(), } thread::sleep(ms(500)); select! { send(s, 8) -> res => assert_eq!(res, Err(SendError(8))), default => panic!(), } }); scope.spawn(move |_| { thread::sleep(ms(1000)); select! { recv(r) -> v => assert_eq!(v, Ok(8)), } }); }) .unwrap(); } #[test] fn send() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { select! { send(s, 7) -> res => res.unwrap(), } thread::sleep(ms(1000)); select! { send(s, 8) -> res => res.unwrap(), } thread::sleep(ms(1000)); select! { send(s, 9) -> res => res.unwrap(), } }); scope.spawn(move |_| { thread::sleep(ms(1500)); select! { recv(r) -> v => assert_eq!(v, Ok(7)), } select! { recv(r) -> v => assert_eq!(v, Ok(8)), } select! { recv(r) -> v => assert_eq!(v, Ok(9)), } }); }) .unwrap(); } #[test] fn send_timeout() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { select! { send(s, 7) -> _ => panic!(), default(ms(1000)) => {} } select! { send(s, 8) -> res => res.unwrap(), default(ms(1000)) => panic!(), } select! { send(s, 9) -> res => assert_eq!(res, Err(SendError(9))), default(ms(1000)) => panic!(), } }); scope.spawn(move |_| { thread::sleep(ms(1500)); select! { recv(r) -> v => assert_eq!(v, Ok(8)), } }); }) .unwrap(); } #[test] fn disconnect_wakes_sender() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { select! { send(s, ()) -> res => assert_eq!(res, Err(SendError(()))), } }); scope.spawn(move |_| { thread::sleep(ms(1000)); drop(r); }); }) .unwrap(); } #[test] fn disconnect_wakes_receiver() { let (s, r) = bounded::<()>(0); scope(|scope| { scope.spawn(move |_| { select! { recv(r) -> res => assert_eq!(res, Err(RecvError)), } }); scope.spawn(move |_| { thread::sleep(ms(1000)); drop(s); }); }) .unwrap(); } #[test] fn trailing_comma() { let (s, r) = unbounded::(); select! { send(s, 1,) -> _ => {}, recv(r,) -> _ => {}, default(ms(1000),) => {}, } } crossbeam-channel-0.5.14/tests/thread_locals.rs000064400000000000000000000027261046102023000176220ustar 00000000000000//! Tests that make sure accessing thread-locals while exiting the thread doesn't cause panics. #![cfg(not(miri))] // Miri detects that this test is buggy: the destructor of `FOO` uses `std::thread::current()`! use std::thread; use std::time::Duration; use crossbeam_channel::{select, unbounded}; use crossbeam_utils::thread::scope; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] #[cfg_attr(target_os = "macos", ignore = "TLS is destroyed too early on macOS")] fn use_while_exiting() { struct Foo; impl Drop for Foo { fn drop(&mut self) { // A blocking operation after the thread-locals have been dropped. This will attempt to // use the thread-locals and must not panic. let (_s, r) = unbounded::<()>(); select! { recv(r) -> _ => {} default(ms(100)) => {} } } } thread_local! { static FOO: Foo = const { Foo }; } let (s, r) = unbounded::<()>(); scope(|scope| { scope.spawn(|_| { // First initialize `FOO`, then the thread-locals related to crossbeam-channel. FOO.with(|_| ()); r.recv().unwrap(); // At thread exit, thread-locals related to crossbeam-channel get dropped first and // `FOO` is dropped last. }); scope.spawn(|_| { thread::sleep(ms(100)); s.send(()).unwrap(); }); }) .unwrap(); } crossbeam-channel-0.5.14/tests/tick.rs000064400000000000000000000206331046102023000157450ustar 00000000000000//! Tests for the tick channel flavor. #![cfg(not(miri))] // TODO: many assertions failed due to Miri is slow use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use std::thread; use std::time::{Duration, Instant}; use crossbeam_channel::{after, select, tick, Select, TryRecvError}; use crossbeam_utils::thread::scope; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] fn fire() { let start = Instant::now(); let r = tick(ms(50)); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); thread::sleep(ms(100)); let fired = r.try_recv().unwrap(); assert!(start < fired); assert!(fired - start >= ms(50)); let now = Instant::now(); assert!(fired < now); assert!(now - fired >= ms(50)); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); select! { recv(r) -> _ => panic!(), default => {} } select! { recv(r) -> _ => {} recv(tick(ms(200))) -> _ => panic!(), } } #[test] fn intervals() { let start = Instant::now(); let r = tick(ms(50)); let t1 = r.recv().unwrap(); assert!(start + ms(50) <= t1); assert!(start + ms(100) > t1); thread::sleep(ms(300)); let t2 = r.try_recv().unwrap(); assert!(start + ms(100) <= t2); assert!(start + ms(150) > t2); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); let t3 = r.recv().unwrap(); assert!(start + ms(400) <= t3); assert!(start + ms(450) > t3); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[test] fn capacity() { const COUNT: usize = 10; for i in 0..COUNT { let r = tick(ms(i as u64)); assert_eq!(r.capacity(), Some(1)); } } #[test] fn len_empty_full() { let r = tick(ms(50)); assert_eq!(r.len(), 0); assert!(r.is_empty()); assert!(!r.is_full()); thread::sleep(ms(100)); assert_eq!(r.len(), 1); assert!(!r.is_empty()); assert!(r.is_full()); r.try_recv().unwrap(); assert_eq!(r.len(), 0); assert!(r.is_empty()); assert!(!r.is_full()); } #[test] fn try_recv() { let r = tick(ms(200)); assert!(r.try_recv().is_err()); thread::sleep(ms(100)); assert!(r.try_recv().is_err()); thread::sleep(ms(200)); assert!(r.try_recv().is_ok()); assert!(r.try_recv().is_err()); thread::sleep(ms(200)); assert!(r.try_recv().is_ok()); assert!(r.try_recv().is_err()); } #[test] fn recv() { let start = Instant::now(); let r = tick(ms(50)); let fired = r.recv().unwrap(); assert!(start < fired); assert!(fired - start >= ms(50)); let now = Instant::now(); assert!(fired < now); assert!(now - fired < fired - start); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[cfg(not(crossbeam_sanitize))] // TODO: assertions failed due to tsan is slow #[test] fn recv_timeout() { let start = Instant::now(); let r = tick(ms(200)); assert!(r.recv_timeout(ms(100)).is_err()); let now = Instant::now(); assert!(now - start >= ms(100)); assert!(now - start <= ms(150)); let fired = r.recv_timeout(ms(200)).unwrap(); assert!(fired - start >= ms(200)); assert!(fired - start <= ms(250)); assert!(r.recv_timeout(ms(100)).is_err()); let now = Instant::now(); assert!(now - start >= ms(300)); assert!(now - start <= ms(350)); let fired = r.recv_timeout(ms(200)).unwrap(); assert!(fired - start >= ms(400)); assert!(fired - start <= ms(450)); } #[test] fn recv_two() { let r1 = tick(ms(50)); let r2 = tick(ms(50)); scope(|scope| { scope.spawn(|_| { for _ in 0..10 { select! { recv(r1) -> _ => {} recv(r2) -> _ => {} } } }); scope.spawn(|_| { for _ in 0..10 { select! { recv(r1) -> _ => {} recv(r2) -> _ => {} } } }); }) .unwrap(); } #[test] fn recv_race() { select! { recv(tick(ms(50))) -> _ => {} recv(tick(ms(100))) -> _ => panic!(), } select! { recv(tick(ms(100))) -> _ => panic!(), recv(tick(ms(50))) -> _ => {} } } #[test] fn stress_default() { const COUNT: usize = 10; for _ in 0..COUNT { select! { recv(tick(ms(0))) -> _ => {} default => panic!(), } } for _ in 0..COUNT { select! { recv(tick(ms(100))) -> _ => panic!(), default => {} } } } #[test] fn select() { const THREADS: usize = 4; let hits = AtomicUsize::new(0); let r1 = tick(ms(200)); let r2 = tick(ms(300)); scope(|scope| { for _ in 0..THREADS { scope.spawn(|_| { let timeout = after(ms(1100)); loop { let mut sel = Select::new(); let oper1 = sel.recv(&r1); let oper2 = sel.recv(&r2); let oper3 = sel.recv(&timeout); let oper = sel.select(); match oper.index() { i if i == oper1 => { oper.recv(&r1).unwrap(); hits.fetch_add(1, Ordering::SeqCst); } i if i == oper2 => { oper.recv(&r2).unwrap(); hits.fetch_add(1, Ordering::SeqCst); } i if i == oper3 => { oper.recv(&timeout).unwrap(); break; } _ => unreachable!(), } } }); } }) .unwrap(); assert_eq!(hits.load(Ordering::SeqCst), 8); } #[cfg(not(crossbeam_sanitize))] // TODO: assertions failed due to tsan is slow #[test] fn ready() { const THREADS: usize = 4; let hits = AtomicUsize::new(0); let r1 = tick(ms(200)); let r2 = tick(ms(300)); scope(|scope| { for _ in 0..THREADS { scope.spawn(|_| { let timeout = after(ms(1100)); 'outer: loop { let mut sel = Select::new(); sel.recv(&r1); sel.recv(&r2); sel.recv(&timeout); loop { match sel.ready() { 0 => { if r1.try_recv().is_ok() { hits.fetch_add(1, Ordering::SeqCst); break; } } 1 => { if r2.try_recv().is_ok() { hits.fetch_add(1, Ordering::SeqCst); break; } } 2 => { if timeout.try_recv().is_ok() { break 'outer; } } _ => unreachable!(), } } } }); } }) .unwrap(); assert_eq!(hits.load(Ordering::SeqCst), 8); } #[test] fn fairness() { const COUNT: usize = 30; for &dur in &[0, 1] { let mut hits = [0usize; 2]; for _ in 0..COUNT { let r1 = tick(ms(dur)); let r2 = tick(ms(dur)); for _ in 0..COUNT { select! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, } } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); } } #[test] fn fairness_duplicates() { const COUNT: usize = 30; for &dur in &[0, 1] { let mut hits = [0usize; 5]; for _ in 0..COUNT { let r = tick(ms(dur)); for _ in 0..COUNT { select! { recv(r) -> _ => hits[0] += 1, recv(r) -> _ => hits[1] += 1, recv(r) -> _ => hits[2] += 1, recv(r) -> _ => hits[3] += 1, recv(r) -> _ => hits[4] += 1, } } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); } } crossbeam-channel-0.5.14/tests/zero.rs000064400000000000000000000323271046102023000157750ustar 00000000000000//! Tests for the zero channel flavor. use std::any::Any; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use std::thread; use std::time::Duration; use crossbeam_channel::{bounded, select, Receiver}; use crossbeam_channel::{RecvError, RecvTimeoutError, TryRecvError}; use crossbeam_channel::{SendError, SendTimeoutError, TrySendError}; use crossbeam_utils::thread::scope; use rand::{thread_rng, Rng}; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] fn smoke() { let (s, r) = bounded(0); assert_eq!(s.try_send(7), Err(TrySendError::Full(7))); assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); } #[test] fn capacity() { let (s, r) = bounded::<()>(0); assert_eq!(s.capacity(), Some(0)); assert_eq!(r.capacity(), Some(0)); } #[test] fn len_empty_full() { let (s, r) = bounded(0); assert_eq!(s.len(), 0); assert!(s.is_empty()); assert!(s.is_full()); assert_eq!(r.len(), 0); assert!(r.is_empty()); assert!(r.is_full()); scope(|scope| { scope.spawn(|_| s.send(0).unwrap()); scope.spawn(|_| r.recv().unwrap()); }) .unwrap(); assert_eq!(s.len(), 0); assert!(s.is_empty()); assert!(s.is_full()); assert_eq!(r.len(), 0); assert!(r.is_empty()); assert!(r.is_full()); } #[test] fn try_recv() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); thread::sleep(ms(1500)); assert_eq!(r.try_recv(), Ok(7)); thread::sleep(ms(500)); assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)); }); scope.spawn(move |_| { thread::sleep(ms(1000)); s.send(7).unwrap(); }); }) .unwrap(); } #[test] fn recv() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.recv(), Ok(7)); thread::sleep(ms(1000)); assert_eq!(r.recv(), Ok(8)); thread::sleep(ms(1000)); assert_eq!(r.recv(), Ok(9)); assert_eq!(r.recv(), Err(RecvError)); }); scope.spawn(move |_| { thread::sleep(ms(1500)); s.send(7).unwrap(); s.send(8).unwrap(); s.send(9).unwrap(); }); }) .unwrap(); } #[test] fn recv_timeout() { let (s, r) = bounded::(0); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout)); assert_eq!(r.recv_timeout(ms(1000)), Ok(7)); assert_eq!( r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Disconnected) ); }); scope.spawn(move |_| { thread::sleep(ms(1500)); s.send(7).unwrap(); }); }) .unwrap(); } #[test] fn try_send() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { assert_eq!(s.try_send(7), Err(TrySendError::Full(7))); thread::sleep(ms(1500)); assert_eq!(s.try_send(8), Ok(())); thread::sleep(ms(500)); assert_eq!(s.try_send(9), Err(TrySendError::Disconnected(9))); }); scope.spawn(move |_| { thread::sleep(ms(1000)); assert_eq!(r.recv(), Ok(8)); }); }) .unwrap(); } #[test] fn send() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { s.send(7).unwrap(); thread::sleep(ms(1000)); s.send(8).unwrap(); thread::sleep(ms(1000)); s.send(9).unwrap(); }); scope.spawn(move |_| { thread::sleep(ms(1500)); assert_eq!(r.recv(), Ok(7)); assert_eq!(r.recv(), Ok(8)); assert_eq!(r.recv(), Ok(9)); }); }) .unwrap(); } #[test] fn send_timeout() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { assert_eq!( s.send_timeout(7, ms(1000)), Err(SendTimeoutError::Timeout(7)) ); assert_eq!(s.send_timeout(8, ms(1000)), Ok(())); assert_eq!( s.send_timeout(9, ms(1000)), Err(SendTimeoutError::Disconnected(9)) ); }); scope.spawn(move |_| { thread::sleep(ms(1500)); assert_eq!(r.recv(), Ok(8)); }); }) .unwrap(); } #[test] fn len() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 25_000; let (s, r) = bounded(0); assert_eq!(s.len(), 0); assert_eq!(r.len(), 0); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { assert_eq!(r.recv(), Ok(i)); assert_eq!(r.len(), 0); } }); scope.spawn(|_| { for i in 0..COUNT { s.send(i).unwrap(); assert_eq!(s.len(), 0); } }); }) .unwrap(); assert_eq!(s.len(), 0); assert_eq!(r.len(), 0); } #[test] fn disconnect_wakes_sender() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { assert_eq!(s.send(()), Err(SendError(()))); }); scope.spawn(move |_| { thread::sleep(ms(1000)); drop(r); }); }) .unwrap(); } #[test] fn disconnect_wakes_receiver() { let (s, r) = bounded::<()>(0); scope(|scope| { scope.spawn(move |_| { assert_eq!(r.recv(), Err(RecvError)); }); scope.spawn(move |_| { thread::sleep(ms(1000)); drop(s); }); }) .unwrap(); } #[test] fn spsc() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 100_000; let (s, r) = bounded(0); scope(|scope| { scope.spawn(move |_| { for i in 0..COUNT { assert_eq!(r.recv(), Ok(i)); } assert_eq!(r.recv(), Err(RecvError)); }); scope.spawn(move |_| { for i in 0..COUNT { s.send(i).unwrap(); } }); }) .unwrap(); } #[test] fn mpmc() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 25_000; const THREADS: usize = 4; let (s, r) = bounded::(0); let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); scope(|scope| { for _ in 0..THREADS { scope.spawn(|_| { for _ in 0..COUNT { let n = r.recv().unwrap(); v[n].fetch_add(1, Ordering::SeqCst); } }); } for _ in 0..THREADS { scope.spawn(|_| { for i in 0..COUNT { s.send(i).unwrap(); } }); } }) .unwrap(); for c in v { assert_eq!(c.load(Ordering::SeqCst), THREADS); } } #[test] fn stress_oneshot() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 10_000; for _ in 0..COUNT { let (s, r) = bounded(1); scope(|scope| { scope.spawn(|_| r.recv().unwrap()); scope.spawn(|_| s.send(0).unwrap()); }) .unwrap(); } } #[test] fn stress_iter() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 1000; let (request_s, request_r) = bounded(0); let (response_s, response_r) = bounded(0); scope(|scope| { scope.spawn(move |_| { let mut count = 0; loop { for x in response_r.try_iter() { count += x; if count == COUNT { return; } } let _ = request_s.try_send(()); } }); for _ in request_r.iter() { if response_s.send(1).is_err() { break; } } }) .unwrap(); } #[test] fn stress_timeout_two_threads() { const COUNT: usize = 100; let (s, r) = bounded(0); scope(|scope| { scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(50)); } loop { if let Ok(()) = s.send_timeout(i, ms(10)) { break; } } } }); scope.spawn(|_| { for i in 0..COUNT { if i % 2 == 0 { thread::sleep(ms(50)); } loop { if let Ok(x) = r.recv_timeout(ms(10)) { assert_eq!(x, i); break; } } } }); }) .unwrap(); } #[test] fn drops() { #[cfg(miri)] const RUNS: usize = 20; #[cfg(not(miri))] const RUNS: usize = 100; #[cfg(miri)] const STEPS: usize = 100; #[cfg(not(miri))] const STEPS: usize = 10_000; static DROPS: AtomicUsize = AtomicUsize::new(0); #[derive(Debug, PartialEq)] struct DropCounter; impl Drop for DropCounter { fn drop(&mut self) { DROPS.fetch_add(1, Ordering::SeqCst); } } let mut rng = thread_rng(); for _ in 0..RUNS { let steps = rng.gen_range(0..STEPS); DROPS.store(0, Ordering::SeqCst); let (s, r) = bounded::(0); scope(|scope| { scope.spawn(|_| { for _ in 0..steps { r.recv().unwrap(); } }); scope.spawn(|_| { for _ in 0..steps { s.send(DropCounter).unwrap(); } }); }) .unwrap(); assert_eq!(DROPS.load(Ordering::SeqCst), steps); drop(s); drop(r); assert_eq!(DROPS.load(Ordering::SeqCst), steps); } } #[test] fn fairness() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s1, r1) = bounded::<()>(0); let (s2, r2) = bounded::<()>(0); scope(|scope| { scope.spawn(|_| { let mut hits = [0usize; 2]; for _ in 0..COUNT { select! { recv(r1) -> _ => hits[0] += 1, recv(r2) -> _ => hits[1] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); }); let mut hits = [0usize; 2]; for _ in 0..COUNT { select! { send(s1, ()) -> _ => hits[0] += 1, send(s2, ()) -> _ => hits[1] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); }) .unwrap(); } #[test] fn fairness_duplicates() { #[cfg(miri)] const COUNT: usize = 100; #[cfg(not(miri))] const COUNT: usize = 10_000; let (s, r) = bounded::<()>(0); scope(|scope| { scope.spawn(|_| { let mut hits = [0usize; 5]; for _ in 0..COUNT { select! { recv(r) -> _ => hits[0] += 1, recv(r) -> _ => hits[1] += 1, recv(r) -> _ => hits[2] += 1, recv(r) -> _ => hits[3] += 1, recv(r) -> _ => hits[4] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); }); let mut hits = [0usize; 5]; for _ in 0..COUNT { select! { send(s, ()) -> _ => hits[0] += 1, send(s, ()) -> _ => hits[1] += 1, send(s, ()) -> _ => hits[2] += 1, send(s, ()) -> _ => hits[3] += 1, send(s, ()) -> _ => hits[4] += 1, } } assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); }) .unwrap(); } #[test] fn recv_in_send() { let (s, r) = bounded(0); scope(|scope| { scope.spawn(|_| { thread::sleep(ms(100)); r.recv() }); scope.spawn(|_| { thread::sleep(ms(500)); s.send(()).unwrap(); }); select! { send(s, r.recv().unwrap()) -> _ => {} } }) .unwrap(); } #[test] fn channel_through_channel() { #[cfg(miri)] const COUNT: usize = 50; #[cfg(not(miri))] const COUNT: usize = 1000; type T = Box; let (s, r) = bounded::(0); scope(|scope| { scope.spawn(move |_| { let mut s = s; for _ in 0..COUNT { let (new_s, new_r) = bounded(0); let new_r: T = Box::new(Some(new_r)); s.send(new_r).unwrap(); s = new_s; } }); scope.spawn(move |_| { let mut r = r; for _ in 0..COUNT { r = r .recv() .unwrap() .downcast_mut::>>() .unwrap() .take() .unwrap() } }); }) .unwrap(); }