├── crossbeam-utils ├── no_atomic.rs ├── build-common.rs ├── src │ ├── sync │ │ ├── mod.rs │ │ ├── once_lock.rs │ │ └── wait_group.rs │ ├── atomic │ │ ├── mod.rs │ │ ├── seq_lock.rs │ │ └── consume.rs │ └── lib.rs ├── LICENSE-MIT ├── tests │ ├── parker.rs │ ├── wait_group.rs │ └── cache_padded.rs ├── Cargo.toml ├── build.rs ├── README.md └── benches │ └── atomic_cell.rs ├── crossbeam-epoch ├── src │ ├── sync │ │ ├── once_lock.rs │ │ └── mod.rs │ ├── default.rs │ ├── deferred.rs │ └── epoch.rs ├── benches │ ├── pin.rs │ ├── flush.rs │ └── defer.rs ├── LICENSE-MIT ├── Cargo.toml ├── examples │ └── sanitize.rs ├── README.md └── tests │ └── loom.rs ├── .gitignore ├── crossbeam-channel ├── benchmarks │ ├── plot.png │ ├── message.rs │ ├── run.sh │ ├── README.md │ ├── bus.rs │ ├── Cargo.toml │ ├── crossbeam-deque.rs │ ├── lockfree.rs │ ├── segqueue.rs │ ├── atomicringqueue.rs │ ├── mpmc.rs │ ├── atomicring.rs │ ├── plot.py │ ├── mpsc.rs │ ├── flume.rs │ └── go.go ├── src │ ├── flavors │ │ ├── mod.rs │ │ ├── never.rs │ │ └── tick.rs │ ├── utils.rs │ └── counter.rs ├── examples │ ├── fibonacci.rs │ ├── stopwatch.rs │ └── matching.rs ├── LICENSE-MIT ├── Cargo.toml ├── tests │ ├── thread_locals.rs │ ├── never.rs │ ├── iter.rs │ └── same_channel.rs └── README.md ├── ci ├── careful.sh ├── dependencies.sh ├── crossbeam-epoch-loom.sh ├── tsan ├── test.sh ├── no_atomic.sh ├── san.sh ├── check-features.sh └── miri.sh ├── no_atomic.rs ├── .github ├── dependabot.yml └── workflows │ └── release.yml ├── crossbeam-skiplist ├── CHANGELOG.md ├── examples │ └── simple.rs ├── LICENSE-MIT ├── Cargo.toml ├── src │ └── equivalent.rs ├── benches │ ├── hash.rs │ ├── skipmap.rs │ ├── btree.rs │ └── skiplist.rs └── README.md ├── .cirrus.yml ├── tests └── subcrates.rs ├── crossbeam-queue ├── src │ └── lib.rs ├── LICENSE-MIT ├── Cargo.toml ├── CHANGELOG.md └── README.md ├── LICENSE-MIT ├── tools └── publish.sh ├── crossbeam-deque ├── LICENSE-MIT ├── Cargo.toml ├── README.md ├── src │ └── lib.rs ├── CHANGELOG.md └── tests │ └── steal.rs ├── src └── lib.rs ├── Cargo.toml └── CHANGELOG.md /crossbeam-utils/no_atomic.rs: -------------------------------------------------------------------------------- 1 | ../no_atomic.rs -------------------------------------------------------------------------------- /crossbeam-utils/build-common.rs: -------------------------------------------------------------------------------- 1 | ../build-common.rs -------------------------------------------------------------------------------- /crossbeam-epoch/src/sync/once_lock.rs: -------------------------------------------------------------------------------- 1 | ../../../crossbeam-utils/src/sync/once_lock.rs -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /crossbeam-channel/benchmarks/*.txt 2 | /crossbeam-channel/benchmarks/*.png 3 | target/ 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex/crossbeam/master/crossbeam-channel/benchmarks/plot.png -------------------------------------------------------------------------------- /crossbeam-epoch/src/sync/mod.rs: -------------------------------------------------------------------------------- 1 | //! Synchronization primitives. 2 | 3 | pub(crate) mod list; 4 | #[cfg(feature = "std")] 5 | #[cfg(not(crossbeam_loom))] 6 | pub(crate) mod once_lock; 7 | pub(crate) mod queue; 8 | -------------------------------------------------------------------------------- /ci/careful.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")"/.. 5 | 6 | export RUSTFLAGS="${RUSTFLAGS:-} -Z randomize-layout" 7 | 8 | cargo careful test --all --all-features --exclude benchmarks -- --test-threads=1 9 | -------------------------------------------------------------------------------- /ci/dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")"/.. 5 | 6 | cargo tree 7 | cargo tree --duplicate 8 | 9 | # Check minimal versions. 10 | cargo minimal-versions build --workspace --all-features --exclude benchmarks 11 | -------------------------------------------------------------------------------- /no_atomic.rs: -------------------------------------------------------------------------------- 1 | // This file is @generated by no_atomic.sh. 2 | // It is not intended for manual editing. 3 | 4 | const NO_ATOMIC: &[&str] = &[ 5 | "bpfeb-unknown-none", 6 | "bpfel-unknown-none", 7 | "mipsel-sony-psx", 8 | "msp430-none-elf", 9 | ]; 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | commit-message: 8 | prefix: '' 9 | labels: [] 10 | - package-ecosystem: github-actions 11 | directory: / 12 | schedule: 13 | interval: weekly 14 | commit-message: 15 | prefix: '' 16 | labels: [] 17 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/message.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | const LEN: usize = 1; 4 | 5 | #[derive(Clone, Copy)] 6 | pub(crate) struct Message(#[allow(dead_code)] [usize; LEN]); 7 | 8 | impl fmt::Debug for Message { 9 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 10 | f.pad("Message") 11 | } 12 | } 13 | 14 | #[inline] 15 | pub(crate) fn new(num: usize) -> Message { 16 | Message([num; LEN]) 17 | } 18 | -------------------------------------------------------------------------------- /ci/crossbeam-epoch-loom.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")"/../crossbeam-epoch 5 | 6 | export RUSTFLAGS="${RUSTFLAGS:-} --cfg crossbeam_loom --cfg crossbeam_sanitize" 7 | 8 | # With MAX_PREEMPTIONS=2 the loom tests (currently) take around 11m. 9 | # If we were to run with =3, they would take several times that, 10 | # which is probably too costly for CI. 11 | env LOOM_MAX_PREEMPTIONS=2 cargo test --test loom --release --features loom -- --nocapture 12 | -------------------------------------------------------------------------------- /ci/tsan: -------------------------------------------------------------------------------- 1 | # TSAN suppressions file for crossbeam 2 | 3 | # The epoch-based GC uses fences. 4 | race:crossbeam_epoch 5 | 6 | # Push and steal operations in crossbeam-deque may cause data races, but such 7 | # data races are safe. If a data race happens, the value read by `steal` is 8 | # forgotten and the steal operation is then retried. 9 | race:crossbeam_deque*push 10 | race:crossbeam_deque*steal 11 | 12 | # Non-lock-free AtomicCell uses SeqLock which uses fences. 13 | race:crossbeam_utils::atomic::atomic_cell::atomic_compare_exchange_weak 14 | -------------------------------------------------------------------------------- /crossbeam-skiplist/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 0.1.3 2 | 3 | - Remove dependency on `cfg-if`. (#1072) 4 | 5 | # Version 0.1.2 6 | 7 | - Bump the minimum supported Rust version to 1.61. (#1037) 8 | - Add `compare_insert`. (#976) 9 | - Improve support for targets without atomic CAS. (#1037) 10 | - Remove build script. (#1037) 11 | - Remove dependency on `scopeguard`. (#1045) 12 | 13 | # Version 0.1.1 14 | 15 | - Fix `get_unchecked` panic by raw pointer calculation. (#940) 16 | 17 | # Version 0.1.0 18 | 19 | **Note:** This release has been yanked due to bug fixed in 0.1.1. 20 | 21 | - Initial implementation. 22 | -------------------------------------------------------------------------------- /crossbeam-channel/src/flavors/mod.rs: -------------------------------------------------------------------------------- 1 | //! Channel flavors. 2 | //! 3 | //! There are six flavors: 4 | //! 5 | //! 1. `at` - Channel that delivers a message after a certain amount of time. 6 | //! 2. `array` - Bounded channel based on a preallocated array. 7 | //! 3. `list` - Unbounded channel implemented as a linked list. 8 | //! 4. `never` - Channel that never delivers messages. 9 | //! 5. `tick` - Channel that delivers messages periodically. 10 | //! 6. `zero` - Zero-capacity channel. 11 | 12 | pub(crate) mod array; 13 | pub(crate) mod at; 14 | pub(crate) mod list; 15 | pub(crate) mod never; 16 | pub(crate) mod tick; 17 | pub(crate) mod zero; 18 | -------------------------------------------------------------------------------- /crossbeam-utils/src/sync/mod.rs: -------------------------------------------------------------------------------- 1 | //! Thread synchronization primitives. 2 | //! 3 | //! * [`Parker`], a thread parking primitive. 4 | //! * [`ShardedLock`], a sharded reader-writer lock with fast concurrent reads. 5 | //! * [`WaitGroup`], for synchronizing the beginning or end of some computation. 6 | 7 | #[cfg(not(crossbeam_loom))] 8 | mod once_lock; 9 | mod parker; 10 | #[cfg(not(crossbeam_loom))] 11 | mod sharded_lock; 12 | mod wait_group; 13 | 14 | pub use self::parker::{Parker, UnparkReason, Unparker}; 15 | #[cfg(not(crossbeam_loom))] 16 | pub use self::sharded_lock::{ShardedLock, ShardedLockReadGuard, ShardedLockWriteGuard}; 17 | pub use self::wait_group::WaitGroup; 18 | -------------------------------------------------------------------------------- /crossbeam-channel/examples/fibonacci.rs: -------------------------------------------------------------------------------- 1 | //! An asynchronous fibonacci sequence generator. 2 | 3 | use std::thread; 4 | 5 | use crossbeam_channel::{bounded, Sender}; 6 | 7 | // Sends the Fibonacci sequence into the channel until it becomes disconnected. 8 | fn fibonacci(sender: Sender) { 9 | let (mut x, mut y) = (0, 1); 10 | while sender.send(x).is_ok() { 11 | let tmp = x; 12 | x = y; 13 | y += tmp; 14 | } 15 | } 16 | 17 | fn main() { 18 | let (s, r) = bounded(0); 19 | thread::spawn(|| fibonacci(s)); 20 | 21 | // Print the first 20 Fibonacci numbers. 22 | for num in r.iter().take(20) { 23 | println!("{}", num); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crossbeam-epoch/benches/pin.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use crossbeam_epoch as epoch; 6 | use crossbeam_utils::thread::scope; 7 | use test::Bencher; 8 | 9 | #[bench] 10 | fn single_pin(b: &mut Bencher) { 11 | b.iter(epoch::pin); 12 | } 13 | 14 | #[bench] 15 | fn multi_pin(b: &mut Bencher) { 16 | const THREADS: usize = 16; 17 | const STEPS: usize = 100_000; 18 | 19 | b.iter(|| { 20 | scope(|s| { 21 | for _ in 0..THREADS { 22 | s.spawn(|_| { 23 | for _ in 0..STEPS { 24 | epoch::pin(); 25 | } 26 | }); 27 | } 28 | }) 29 | .unwrap(); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /crossbeam-skiplist/examples/simple.rs: -------------------------------------------------------------------------------- 1 | // use std::time::Instant; 2 | 3 | fn main() { 4 | // let map = crossbeam_skiplist::SkipMap::new(); 5 | // // let mut map = std::collections::BTreeMap::new(); 6 | // // let mut map = std::collections::HashMap::new(); 7 | // 8 | // let now = Instant::now(); 9 | // 10 | // let mut num = 0u64; 11 | // for _ in 0..1_000_000 { 12 | // num = num.wrapping_mul(17).wrapping_add(255); 13 | // map.insert(num, !num); 14 | // } 15 | // 16 | // let dur = Instant::now() - now; 17 | // println!("insert: {} sec", dur.as_secs() as f64 + dur.subsec_nanos() as f64 * 1e-9); 18 | // 19 | // let now = Instant::now(); 20 | // 21 | // for _ in map.iter() {} 22 | // 23 | // let dur = Instant::now() - now; 24 | // println!("iterate: {} sec", dur.as_secs() as f64 + dur.subsec_nanos() as f64 * 1e-9); 25 | } 26 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'master') 2 | auto_cancellation: $CIRRUS_PR != '' 3 | env: 4 | CARGO_INCREMENTAL: '0' 5 | CARGO_NET_RETRY: '10' 6 | CARGO_TERM_COLOR: always 7 | RUST_BACKTRACE: '1' 8 | RUSTDOCFLAGS: -D warnings 9 | RUSTFLAGS: -D warnings 10 | RUSTUP_MAX_RETRIES: '10' 11 | 12 | aarch64_macos_task: 13 | name: test ($TARGET) 14 | env: 15 | TARGET: aarch64-apple-darwin 16 | macos_instance: 17 | image: ghcr.io/cirruslabs/macos-ventura-xcode 18 | setup_script: 19 | - curl --proto '=https' --tlsv1.2 -fsSL --retry 10 --retry-connrefused https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain nightly --no-modify-path 20 | test_script: 21 | - . "$HOME/.cargo/env" 22 | - cargo test --all --all-features --exclude benchmarks -- --test-threads=1 23 | - cargo test --all --all-features --exclude benchmarks --release -- --test-threads=1 24 | -------------------------------------------------------------------------------- /ci/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")"/.. 5 | 6 | # shellcheck disable=SC2086 7 | if [[ -n "${RUST_TARGET:-}" ]]; then 8 | cargo test --all --all-features --target "$RUST_TARGET" --exclude benchmarks ${DOCTEST_XCOMPILE:-} -- --test-threads=1 9 | cargo test --all --all-features --target "$RUST_TARGET" --exclude benchmarks --release ${DOCTEST_XCOMPILE:-} -- --test-threads=1 10 | 11 | # For now, the non-host target only runs tests. 12 | exit 0 13 | fi 14 | 15 | # Otherwise, run tests and checks with the host target. 16 | cargo test --all --all-features --exclude benchmarks -- --test-threads=1 17 | cargo test --all --all-features --exclude benchmarks --release -- --test-threads=1 18 | 19 | if [[ "$RUST_VERSION" == "nightly"* ]]; then 20 | # Benchmarks are only checked on nightly because depending on unstable features. 21 | cargo check --all --all-features --all-targets 22 | fi 23 | -------------------------------------------------------------------------------- /tests/subcrates.rs: -------------------------------------------------------------------------------- 1 | //! Makes sure subcrates are properly re-exported. 2 | 3 | use crossbeam::select; 4 | 5 | #[test] 6 | fn channel() { 7 | let (s, r) = crossbeam::channel::bounded(1); 8 | 9 | select! { 10 | send(s, 0) -> res => res.unwrap(), 11 | recv(r) -> res => assert!(res.is_ok()), 12 | } 13 | } 14 | 15 | #[test] 16 | fn deque() { 17 | let w = crossbeam::deque::Worker::new_fifo(); 18 | w.push(1); 19 | let _ = w.pop(); 20 | } 21 | 22 | #[test] 23 | fn epoch() { 24 | crossbeam::epoch::pin(); 25 | } 26 | 27 | #[test] 28 | fn queue() { 29 | let a = crossbeam::queue::ArrayQueue::new(10); 30 | let _ = a.push(1); 31 | let _ = a.pop(); 32 | } 33 | 34 | #[test] 35 | fn utils() { 36 | crossbeam::utils::CachePadded::new(7); 37 | 38 | crossbeam::scope(|scope| { 39 | scope.spawn(|_| ()); 40 | }) 41 | .unwrap(); 42 | 43 | crossbeam::thread::scope(|scope| { 44 | scope.spawn(|_| ()); 45 | }) 46 | .unwrap(); 47 | } 48 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")" 5 | 6 | cargo run --release --bin crossbeam-channel | tee crossbeam-channel.txt 7 | cargo run --release --bin futures-channel | tee futures-channel.txt 8 | cargo run --release --bin mpsc | tee mpsc.txt 9 | cargo run --release --bin flume | tee flume.txt 10 | go run go.go | tee go.txt 11 | 12 | # These can also be run, but too many plot bars mess 13 | # up the plot (they start to overlap). So only 5 contenders 14 | # with the most tests are included by default. 15 | 16 | # cargo run --release --bin atomicringqueue | tee atomicringqueue.txt 17 | # cargo run --release --bin atomicring | tee atomicring.txt 18 | # cargo run --release --bin bus | tee bus.txt 19 | # cargo run --release --bin crossbeam-deque | tee crossbeam-deque.txt 20 | # cargo run --release --bin lockfree | tee lockfree.txt 21 | # cargo run --release --bin segqueue | tee segqueue.txt 22 | # cargo run --release --bin mpmc | tee mpmc.txt 23 | 24 | ./plot.py ./*.txt 25 | -------------------------------------------------------------------------------- /crossbeam-queue/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Concurrent queues. 2 | //! 3 | //! This crate provides concurrent queues that can be shared among threads: 4 | //! 5 | //! * [`ArrayQueue`], a bounded MPMC queue that allocates a fixed-capacity buffer on construction. 6 | //! * [`SegQueue`], an unbounded MPMC queue that allocates small buffers, segments, on demand. 7 | 8 | #![no_std] 9 | #![doc(test( 10 | no_crate_inject, 11 | attr( 12 | deny(warnings, rust_2018_idioms, single_use_lifetimes), 13 | allow(dead_code, unused_assignments, unused_variables) 14 | ) 15 | ))] 16 | #![warn(missing_docs, unsafe_op_in_unsafe_fn)] 17 | 18 | #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] 19 | extern crate alloc; 20 | #[cfg(feature = "std")] 21 | extern crate std; 22 | 23 | #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] 24 | mod array_queue; 25 | #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] 26 | mod seg_queue; 27 | 28 | #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] 29 | pub use crate::{array_queue::ArrayQueue, seg_queue::SegQueue}; 30 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 The Crossbeam Project Developers 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /tools/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")"/.. 5 | 6 | # Publish a new release. 7 | # 8 | # USAGE: 9 | # ./tools/publish.sh 10 | 11 | bail() { 12 | echo >&2 "error: $*" 13 | exit 1 14 | } 15 | 16 | crate="${1:?}" 17 | version="${2:?}" 18 | version="${version#v}" 19 | tag="${crate}-${version}" 20 | if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z\.-]+)?(\+[0-9A-Za-z\.-]+)?$ ]]; then 21 | bail "invalid version format '${version}'" 22 | fi 23 | if [[ $# -gt 2 ]]; then 24 | bail "invalid argument '$3'" 25 | fi 26 | 27 | # Make sure there is no uncommitted change. 28 | git diff --exit-code 29 | git diff --exit-code --staged 30 | 31 | # Make sure the same release has not been created in the past. 32 | if gh release view "${tag}" &>/dev/null; then 33 | bail "tag '${tag}' has already been created and pushed" 34 | fi 35 | 36 | if ! git branch | grep -q '\* master'; then 37 | bail "current branch is not 'master'" 38 | fi 39 | 40 | git tag "${tag}" 41 | 42 | ( 43 | if [[ "${crate}" != "crossbeam" ]]; then 44 | cd "${crate}" 45 | fi 46 | cargo +stable publish 47 | ) 48 | 49 | git push origin --tags 50 | -------------------------------------------------------------------------------- /crossbeam-deque/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 The Crossbeam Project Developers 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /crossbeam-epoch/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 The Crossbeam Project Developers 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /crossbeam-queue/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 The Crossbeam Project Developers 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /crossbeam-utils/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 The Crossbeam Project Developers 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /crossbeam-channel/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 The Crossbeam Project Developers 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /crossbeam-skiplist/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 The Crossbeam Project Developers 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /crossbeam-channel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crossbeam-channel" 3 | # When publishing a new version: 4 | # - Update CHANGELOG.md 5 | # - Update README.md (when increasing major or minor version) 6 | # - Run './tools/publish.sh crossbeam-channel ' 7 | version = "0.5.15" 8 | edition = "2021" 9 | rust-version = "1.60" 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/crossbeam-rs/crossbeam" 12 | homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel" 13 | description = "Multi-producer multi-consumer channels for message passing" 14 | keywords = ["channel", "mpmc", "select", "golang", "message"] 15 | categories = ["algorithms", "concurrency", "data-structures"] 16 | 17 | [features] 18 | default = ["std"] 19 | 20 | # Enable to use APIs that require `std`. 21 | # This is enabled by default. 22 | # 23 | # NOTE: Disabling `std` feature is not supported yet. 24 | std = ["crossbeam-utils/std"] 25 | 26 | [dependencies] 27 | crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-features = false, features = ["atomic"] } 28 | 29 | [dev-dependencies] 30 | fastrand = "2" 31 | num_cpus = "1.13.0" 32 | signal-hook = "0.3" 33 | 34 | [lints] 35 | workspace = true 36 | -------------------------------------------------------------------------------- /crossbeam-deque/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crossbeam-deque" 3 | # When publishing a new version: 4 | # - Update CHANGELOG.md 5 | # - Update README.md (when increasing major or minor version) 6 | # - Run './tools/publish.sh crossbeam-deque ' 7 | version = "0.8.6" 8 | edition = "2021" 9 | rust-version = "1.61" 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/crossbeam-rs/crossbeam" 12 | homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-deque" 13 | description = "Concurrent work-stealing deque" 14 | keywords = ["chase-lev", "lock-free", "scheduler", "scheduling"] 15 | categories = ["algorithms", "concurrency", "data-structures"] 16 | 17 | [features] 18 | default = ["std"] 19 | 20 | # Enable to use APIs that require `std`. 21 | # This is enabled by default. 22 | # 23 | # NOTE: Disabling `std` feature is not supported yet. 24 | std = ["crossbeam-epoch/std", "crossbeam-utils/std"] 25 | 26 | [dependencies] 27 | crossbeam-epoch = { version = "0.9.17", path = "../crossbeam-epoch", default-features = false } 28 | crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-features = false } 29 | 30 | [dev-dependencies] 31 | fastrand = "2" 32 | 33 | [lints] 34 | workspace = true 35 | -------------------------------------------------------------------------------- /crossbeam-epoch/benches/flush.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use std::sync::Barrier; 6 | 7 | use crossbeam_epoch as epoch; 8 | use crossbeam_utils::thread::scope; 9 | use test::Bencher; 10 | 11 | #[bench] 12 | fn single_flush(b: &mut Bencher) { 13 | const THREADS: usize = 16; 14 | 15 | let start = Barrier::new(THREADS + 1); 16 | let end = Barrier::new(THREADS + 1); 17 | 18 | scope(|s| { 19 | for _ in 0..THREADS { 20 | s.spawn(|_| { 21 | epoch::pin(); 22 | start.wait(); 23 | end.wait(); 24 | }); 25 | } 26 | 27 | start.wait(); 28 | b.iter(|| epoch::pin().flush()); 29 | end.wait(); 30 | }) 31 | .unwrap(); 32 | } 33 | 34 | #[bench] 35 | fn multi_flush(b: &mut Bencher) { 36 | const THREADS: usize = 16; 37 | const STEPS: usize = 10_000; 38 | 39 | b.iter(|| { 40 | scope(|s| { 41 | for _ in 0..THREADS { 42 | s.spawn(|_| { 43 | for _ in 0..STEPS { 44 | let guard = &epoch::pin(); 45 | guard.flush(); 46 | } 47 | }); 48 | } 49 | }) 50 | .unwrap(); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /crossbeam-queue/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crossbeam-queue" 3 | # When publishing a new version: 4 | # - Update CHANGELOG.md 5 | # - Update README.md (when increasing major or minor version) 6 | # - Run './tools/publish.sh crossbeam-queue ' 7 | version = "0.3.12" 8 | edition = "2021" 9 | rust-version = "1.60" 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/crossbeam-rs/crossbeam" 12 | homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-queue" 13 | description = "Concurrent queues" 14 | keywords = ["queue", "mpmc", "lock-free", "producer", "consumer"] 15 | categories = ["concurrency", "data-structures", "no-std"] 16 | 17 | [features] 18 | default = ["std"] 19 | 20 | # Enable to use APIs that require `std`. 21 | # This is enabled by default. 22 | std = ["alloc", "crossbeam-utils/std"] 23 | 24 | # Enable to use APIs that require `alloc`. 25 | # This is enabled by default and also enabled if the `std` feature is enabled. 26 | # 27 | # NOTE: Disabling both `std` *and* `alloc` features is not supported yet. 28 | alloc = [] 29 | 30 | [dependencies] 31 | crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-features = false } 32 | 33 | [dev-dependencies] 34 | fastrand = "2" 35 | 36 | [lints] 37 | workspace = true 38 | -------------------------------------------------------------------------------- /ci/no_atomic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")"/.. 5 | 6 | # Update the list of targets that do not support atomic/CAS operations. 7 | # 8 | # Usage: 9 | # ./ci/no_atomic.sh 10 | 11 | file=no_atomic.rs 12 | 13 | # `"max-atomic-width" == 0` means that atomic is not supported at all. 14 | # We do not have a cfg for targets with {8,16}-bit atomic only, so 15 | # for now we treat them the same as targets that do not support atomic. 16 | # It is not clear exactly what `"max-atomic-width" == null` means, but they 17 | # actually seem to have the same max-atomic-width as the target-pointer-width. 18 | # The targets currently included in this group are "mipsel-sony-psp", 19 | # "thumbv4t-none-eabi", "thumbv6m-none-eabi", all of which are 20 | # `"target-pointer-width" == "32"`, so assuming them `"max-atomic-width" == 32` 21 | # for now. 22 | no_atomic=$(RUSTC_BOOTSTRAP=1 rustc +stable -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | select((.value."max-atomic-width" == 0) or (.value."min-atomic-width" and .value."min-atomic-width" != 8)) | " \"" + .key + "\","') 23 | 24 | cat >"${file}" < { 41 | let now = ::std::time::Instant::now(); 42 | $f; 43 | let elapsed = now.elapsed(); 44 | println!( 45 | "{:25} {:15} {:7.3} sec", 46 | $name, 47 | "Rust bus", 48 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 49 | ); 50 | }; 51 | } 52 | 53 | run!("bounded1_spsc", spsc(1)); 54 | 55 | run!("bounded_seq", seq(MESSAGES)); 56 | run!("bounded_spsc", spsc(MESSAGES)); 57 | } 58 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "benchmarks" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | atomicring = "1.2.9" 9 | bus = "2.3.0" 10 | crossbeam = { path = "../.." } 11 | crossbeam-channel = { path = ".." } 12 | crossbeam-deque = { path = "../../crossbeam-deque" } 13 | flume = "0.11" 14 | futures = { version = "0.3", features = ["thread-pool"] } 15 | lockfree = "0.5.1" 16 | mpmc = "0.1.6" 17 | 18 | [[bin]] 19 | name = "atomicring" 20 | path = "atomicring.rs" 21 | doc = false 22 | 23 | [[bin]] 24 | name = "atomicringqueue" 25 | path = "atomicringqueue.rs" 26 | doc = false 27 | 28 | [[bin]] 29 | name = "bus" 30 | path = "bus.rs" 31 | doc = false 32 | 33 | [[bin]] 34 | name = "crossbeam-channel" 35 | path = "crossbeam-channel.rs" 36 | doc = false 37 | 38 | [[bin]] 39 | name = "crossbeam-deque" 40 | path = "crossbeam-deque.rs" 41 | doc = false 42 | 43 | [[bin]] 44 | name = "flume" 45 | path = "flume.rs" 46 | doc = false 47 | 48 | [[bin]] 49 | name = "futures-channel" 50 | path = "futures-channel.rs" 51 | doc = false 52 | 53 | [[bin]] 54 | name = "lockfree" 55 | path = "lockfree.rs" 56 | doc = false 57 | 58 | [[bin]] 59 | name = "mpsc" 60 | path = "mpsc.rs" 61 | doc = false 62 | 63 | [[bin]] 64 | name = "segqueue" 65 | path = "segqueue.rs" 66 | doc = false 67 | 68 | [[bin]] 69 | name = "mpmc" 70 | path = "mpmc.rs" 71 | doc = false 72 | 73 | [lints] 74 | workspace = true 75 | -------------------------------------------------------------------------------- /crossbeam-skiplist/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crossbeam-skiplist" 3 | # When publishing a new version: 4 | # - Update CHANGELOG.md 5 | # - Update README.md (when increasing major or minor version) 6 | # - Run './tools/publish.sh crossbeam-skiplist ' 7 | version = "0.1.3" 8 | edition = "2021" 9 | rust-version = "1.61" 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/crossbeam-rs/crossbeam" 12 | homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-skiplist" 13 | description = "A concurrent skip list" 14 | keywords = ["map", "set", "skiplist", "lock-free"] 15 | categories = ["algorithms", "concurrency", "data-structures", "no-std"] 16 | 17 | [features] 18 | default = ["std"] 19 | 20 | # Enable to use APIs that require `std`. 21 | # This is enabled by default. 22 | std = ["alloc", "crossbeam-epoch/std", "crossbeam-utils/std"] 23 | 24 | # Enable to use APIs that require `alloc`. 25 | # This is enabled by default and also enabled if the `std` feature is enabled. 26 | # 27 | # NOTE: Disabling both `std` *and* `alloc` features is not supported yet. 28 | alloc = ["crossbeam-epoch/alloc"] 29 | 30 | [dependencies] 31 | crossbeam-epoch = { version = "0.9.17", path = "../crossbeam-epoch", default-features = false } 32 | crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-features = false } 33 | 34 | [dev-dependencies] 35 | fastrand = "2" 36 | 37 | [lints] 38 | workspace = true 39 | -------------------------------------------------------------------------------- /crossbeam-utils/src/atomic/mod.rs: -------------------------------------------------------------------------------- 1 | //! Atomic types. 2 | //! 3 | //! * [`AtomicCell`], a thread-safe mutable memory location. 4 | //! * [`AtomicConsume`], for reading from primitive atomic types with "consume" ordering. 5 | 6 | #[cfg(target_has_atomic = "ptr")] 7 | #[cfg(not(crossbeam_loom))] 8 | // Use "wide" sequence lock if the pointer width <= 32 for preventing its counter against wrap 9 | // around. 10 | // 11 | // In narrow architectures (pointer width <= 16), the counter is still <= 32-bit and may be 12 | // vulnerable to wrap around. But it's mostly okay, since in such a primitive hardware, the 13 | // counter will not be increased that fast. 14 | // Note that Rust (and C99) pointers must be at least 16-bit (i.e., 8-bit targets are impossible): https://github.com/rust-lang/rust/pull/49305 15 | #[cfg_attr( 16 | any(target_pointer_width = "16", target_pointer_width = "32"), 17 | path = "seq_lock_wide.rs" 18 | )] 19 | mod seq_lock; 20 | 21 | #[cfg(target_has_atomic = "ptr")] 22 | // We cannot provide AtomicCell under cfg(crossbeam_loom) because loom's atomic 23 | // types have a different in-memory representation than the underlying type. 24 | // TODO: The latest loom supports fences, so fallback using seqlock may be available. 25 | #[cfg(not(crossbeam_loom))] 26 | mod atomic_cell; 27 | #[cfg(target_has_atomic = "ptr")] 28 | #[cfg(not(crossbeam_loom))] 29 | pub use atomic_cell::AtomicCell; 30 | 31 | mod consume; 32 | pub use consume::AtomicConsume; 33 | -------------------------------------------------------------------------------- /ci/san.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")"/.. 5 | 6 | if [[ "$OSTYPE" != "linux"* ]]; then 7 | exit 0 8 | fi 9 | 10 | # Run address sanitizer 11 | # TODO: Once `cfg(sanitize = "..")` is stable, replace 12 | # `cfg(crossbeam_sanitize)` with `cfg(sanitize = "..")` and remove 13 | # `--cfg crossbeam_sanitize`. 14 | cargo clean 15 | RUSTFLAGS="${RUSTFLAGS:-} -Z sanitizer=address --cfg crossbeam_sanitize" \ 16 | cargo test -Z build-std --all --all-features --release --target x86_64-unknown-linux-gnu --tests --exclude benchmarks -- --test-threads=1 17 | 18 | RUSTFLAGS="${RUSTFLAGS:-} -Z sanitizer=address --cfg crossbeam_sanitize" \ 19 | cargo run -Z build-std \ 20 | --all-features \ 21 | --release \ 22 | --target x86_64-unknown-linux-gnu \ 23 | --example sanitize \ 24 | --manifest-path crossbeam-epoch/Cargo.toml 25 | 26 | # Run memory sanitizer 27 | cargo clean 28 | RUSTFLAGS="${RUSTFLAGS:-} -Z sanitizer=memory --cfg crossbeam_sanitize" \ 29 | cargo test -Z build-std --all --all-features --release --target x86_64-unknown-linux-gnu --tests --exclude benchmarks -- --test-threads=1 30 | 31 | # Run thread sanitizer 32 | cargo clean 33 | TSAN_OPTIONS="suppressions=$(pwd)/ci/tsan" \ 34 | RUSTFLAGS="${RUSTFLAGS:-} -Z sanitizer=thread --cfg crossbeam_sanitize" \ 35 | cargo test -Z build-std --all --all-features --release --target x86_64-unknown-linux-gnu --tests --exclude benchmarks -- --test-threads=1 36 | -------------------------------------------------------------------------------- /crossbeam-utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crossbeam-utils" 3 | # When publishing a new version: 4 | # - Update CHANGELOG.md 5 | # - Update README.md (when increasing major or minor version) 6 | # - Run './tools/publish.sh crossbeam-utils ' 7 | version = "0.8.21" 8 | edition = "2021" 9 | rust-version = "1.56" 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/crossbeam-rs/crossbeam" 12 | homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils" 13 | description = "Utilities for concurrent programming" 14 | keywords = ["scoped", "thread", "atomic", "cache"] 15 | categories = ["algorithms", "concurrency", "data-structures", "no-std"] 16 | 17 | [package.metadata.docs.rs] 18 | all-features = true 19 | 20 | [features] 21 | default = ["std"] 22 | 23 | # Enable to use APIs that require `std`. 24 | # This is enabled by default. 25 | std = [] 26 | 27 | # Enable `atomic` module. 28 | # This requires Rust 1.60. 29 | atomic = ["atomic-maybe-uninit"] 30 | 31 | [dependencies] 32 | atomic-maybe-uninit = { version = "0.3.4", optional = true } 33 | 34 | # Enable the use of loom for concurrency testing. 35 | # 36 | # NOTE: This feature is outside of the normal semver guarantees and minor or 37 | # patch versions of crossbeam may make breaking changes to them at any time. 38 | [target.'cfg(crossbeam_loom)'.dependencies] 39 | loom = { version = "0.7.1", optional = true } 40 | 41 | [dev-dependencies] 42 | fastrand = "2" 43 | 44 | [lints] 45 | workspace = true 46 | -------------------------------------------------------------------------------- /crossbeam-channel/examples/stopwatch.rs: -------------------------------------------------------------------------------- 1 | //! Prints the elapsed time every 1 second and quits on Ctrl+C. 2 | 3 | #[cfg(windows)] // signal_hook::iterator does not work on windows 4 | fn main() { 5 | println!("This example does not work on Windows"); 6 | } 7 | 8 | #[cfg(not(windows))] 9 | fn main() { 10 | use std::io; 11 | use std::thread; 12 | use std::time::{Duration, Instant}; 13 | 14 | use crossbeam_channel::{bounded, select, tick, Receiver}; 15 | use signal_hook::consts::SIGINT; 16 | use signal_hook::iterator::Signals; 17 | 18 | // Creates a channel that gets a message every time `SIGINT` is signalled. 19 | fn sigint_notifier() -> io::Result> { 20 | let (s, r) = bounded(100); 21 | let mut signals = Signals::new([SIGINT])?; 22 | 23 | thread::spawn(move || { 24 | for _ in signals.forever() { 25 | if s.send(()).is_err() { 26 | break; 27 | } 28 | } 29 | }); 30 | 31 | Ok(r) 32 | } 33 | 34 | // Prints the elapsed time. 35 | fn show(dur: Duration) { 36 | println!("Elapsed: {}.{:03} sec", dur.as_secs(), dur.subsec_millis()); 37 | } 38 | 39 | let start = Instant::now(); 40 | let update = tick(Duration::from_secs(1)); 41 | let ctrl_c = sigint_notifier().unwrap(); 42 | 43 | loop { 44 | select! { 45 | recv(update) -> _ => { 46 | show(start.elapsed()); 47 | } 48 | recv(ctrl_c) -> _ => { 49 | println!(); 50 | println!("Goodbye!"); 51 | show(start.elapsed()); 52 | break; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ci/check-features.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")"/.. 5 | 6 | # * `--feature-powerset` - run for the feature powerset which includes --no-default-features and default features of package 7 | # * `--no-dev-deps` - build without dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 8 | # * `--exclude benchmarks` - benchmarks doesn't published. 9 | if [[ "${RUST_VERSION}" == "msrv" ]]; then 10 | cargo hack build --all --feature-powerset --no-dev-deps --exclude crossbeam-utils --exclude benchmarks --rust-version 11 | # atomic feature requires Rust 1.60. 12 | cargo hack build -p crossbeam-utils --feature-powerset --no-dev-deps --rust-version --exclude-features atomic 13 | cargo +1.60 hack build -p crossbeam-utils --feature-powerset --no-dev-deps 14 | else 15 | cargo hack build --all --feature-powerset --no-dev-deps --exclude benchmarks 16 | fi 17 | 18 | if [[ "${RUST_VERSION}" == "nightly"* ]]; then 19 | # Build for no_std environment. 20 | # thumbv7m-none-eabi supports atomic CAS. 21 | # thumbv6m-none-eabi supports atomic, but not atomic CAS. 22 | # riscv32i-unknown-none-elf does not support atomic at all. 23 | rustup target add thumbv7m-none-eabi 24 | rustup target add thumbv6m-none-eabi 25 | rustup target add riscv32i-unknown-none-elf 26 | cargo hack build --all --feature-powerset --no-dev-deps --exclude benchmarks --target thumbv7m-none-eabi --skip std,default 27 | cargo hack build --all --feature-powerset --no-dev-deps --exclude benchmarks --target thumbv6m-none-eabi --skip std,default 28 | cargo hack build --all --feature-powerset --no-dev-deps --exclude benchmarks --target riscv32i-unknown-none-elf --skip std,default 29 | fi 30 | -------------------------------------------------------------------------------- /crossbeam-channel/tests/thread_locals.rs: -------------------------------------------------------------------------------- 1 | //! Tests that make sure accessing thread-locals while exiting the thread doesn't cause panics. 2 | 3 | #![cfg(not(miri))] // Miri detects that this test is buggy: the destructor of `FOO` uses `std::thread::current()`! 4 | 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | use crossbeam_channel::{select, unbounded}; 9 | use crossbeam_utils::thread::scope; 10 | 11 | fn ms(ms: u64) -> Duration { 12 | Duration::from_millis(ms) 13 | } 14 | 15 | #[test] 16 | #[cfg_attr(target_os = "macos", ignore = "TLS is destroyed too early on macOS")] 17 | fn use_while_exiting() { 18 | struct Foo; 19 | 20 | impl Drop for Foo { 21 | fn drop(&mut self) { 22 | // A blocking operation after the thread-locals have been dropped. This will attempt to 23 | // use the thread-locals and must not panic. 24 | let (_s, r) = unbounded::<()>(); 25 | select! { 26 | recv(r) -> _ => {} 27 | default(ms(100)) => {} 28 | } 29 | } 30 | } 31 | 32 | thread_local! { 33 | static FOO: Foo = const { Foo }; 34 | } 35 | 36 | let (s, r) = unbounded::<()>(); 37 | 38 | scope(|scope| { 39 | scope.spawn(|_| { 40 | // First initialize `FOO`, then the thread-locals related to crossbeam-channel. 41 | FOO.with(|_| ()); 42 | r.recv().unwrap(); 43 | // At thread exit, thread-locals related to crossbeam-channel get dropped first and 44 | // `FOO` is dropped last. 45 | }); 46 | 47 | scope.spawn(|_| { 48 | thread::sleep(ms(100)); 49 | s.send(()).unwrap(); 50 | }); 51 | }) 52 | .unwrap(); 53 | } 54 | -------------------------------------------------------------------------------- /crossbeam-utils/tests/wait_group.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc; 2 | use std::thread; 3 | use std::time::Duration; 4 | 5 | use crossbeam_utils::sync::WaitGroup; 6 | 7 | const THREADS: usize = 10; 8 | 9 | #[test] 10 | fn wait() { 11 | let wg = WaitGroup::new(); 12 | let (tx, rx) = mpsc::channel(); 13 | 14 | for _ in 0..THREADS { 15 | let wg = wg.clone(); 16 | let tx = tx.clone(); 17 | 18 | thread::spawn(move || { 19 | wg.wait(); 20 | tx.send(()).unwrap(); 21 | }); 22 | } 23 | 24 | thread::sleep(Duration::from_millis(100)); 25 | 26 | // At this point, all spawned threads should be blocked, so we shouldn't get anything from the 27 | // channel. 28 | assert!(rx.try_recv().is_err()); 29 | 30 | wg.wait(); 31 | 32 | // Now, the wait group is cleared and we should receive messages. 33 | for _ in 0..THREADS { 34 | rx.recv().unwrap(); 35 | } 36 | } 37 | 38 | #[test] 39 | fn wait_and_drop() { 40 | let wg = WaitGroup::new(); 41 | let wg2 = WaitGroup::new(); 42 | let (tx, rx) = mpsc::channel(); 43 | 44 | for _ in 0..THREADS { 45 | let wg = wg.clone(); 46 | let wg2 = wg2.clone(); 47 | let tx = tx.clone(); 48 | 49 | thread::spawn(move || { 50 | wg2.wait(); 51 | tx.send(()).unwrap(); 52 | drop(wg); 53 | }); 54 | } 55 | 56 | // At this point, no thread has gotten past `wg2.wait()`, so we shouldn't get anything from the 57 | // channel. 58 | assert!(rx.try_recv().is_err()); 59 | drop(wg2); 60 | 61 | wg.wait(); 62 | 63 | // Now, the wait group is cleared and we should receive messages. 64 | for _ in 0..THREADS { 65 | rx.try_recv().unwrap(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /crossbeam-channel/src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Miscellaneous utilities. 2 | 3 | use std::cell::Cell; 4 | use std::num::Wrapping; 5 | use std::thread; 6 | use std::time::{Duration, Instant}; 7 | 8 | /// Randomly shuffles a slice. 9 | pub(crate) fn shuffle(v: &mut [T]) { 10 | let len = v.len(); 11 | if len <= 1 { 12 | return; 13 | } 14 | 15 | std::thread_local! { 16 | static RNG: Cell> = const { Cell::new(Wrapping(1_406_868_647)) }; 17 | } 18 | 19 | let _ = RNG.try_with(|rng| { 20 | for i in 1..len { 21 | // This is the 32-bit variant of Xorshift. 22 | // 23 | // Source: https://en.wikipedia.org/wiki/Xorshift 24 | let mut x = rng.get(); 25 | x ^= x << 13; 26 | x ^= x >> 17; 27 | x ^= x << 5; 28 | rng.set(x); 29 | 30 | let x = x.0; 31 | let n = i + 1; 32 | 33 | // This is a fast alternative to `let j = x % n`. 34 | // 35 | // Author: Daniel Lemire 36 | // Source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ 37 | let j = ((x as u64).wrapping_mul(n as u64) >> 32) as u32 as usize; 38 | 39 | v.swap(i, j); 40 | } 41 | }); 42 | } 43 | 44 | /// Sleeps until the deadline, or forever if the deadline isn't specified. 45 | pub(crate) fn sleep_until(deadline: Option) { 46 | loop { 47 | match deadline { 48 | None => thread::sleep(Duration::from_secs(1000)), 49 | Some(d) => { 50 | let now = Instant::now(); 51 | if now >= d { 52 | break; 53 | } 54 | thread::sleep(d - now); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/crossbeam-deque.rs: -------------------------------------------------------------------------------- 1 | use crossbeam_deque::{Steal, Worker}; 2 | use std::thread; 3 | 4 | mod message; 5 | 6 | const MESSAGES: usize = 5_000_000; 7 | 8 | fn seq() { 9 | let tx = Worker::new_lifo(); 10 | let rx = tx.stealer(); 11 | 12 | for i in 0..MESSAGES { 13 | tx.push(message::new(i)); 14 | } 15 | 16 | for _ in 0..MESSAGES { 17 | match rx.steal() { 18 | Steal::Success(_) => {} 19 | Steal::Retry => panic!(), 20 | Steal::Empty => panic!(), 21 | } 22 | } 23 | } 24 | 25 | fn spsc() { 26 | let tx = Worker::new_lifo(); 27 | let rx = tx.stealer(); 28 | 29 | crossbeam::scope(|scope| { 30 | scope.spawn(move |_| { 31 | for i in 0..MESSAGES { 32 | tx.push(message::new(i)); 33 | } 34 | }); 35 | 36 | scope.spawn(move |_| { 37 | for _ in 0..MESSAGES { 38 | loop { 39 | match rx.steal() { 40 | Steal::Success(_) => break, 41 | Steal::Retry | Steal::Empty => thread::yield_now(), 42 | } 43 | } 44 | } 45 | }); 46 | }) 47 | .unwrap(); 48 | } 49 | 50 | fn main() { 51 | macro_rules! run { 52 | ($name:expr, $f:expr) => { 53 | let now = ::std::time::Instant::now(); 54 | $f; 55 | let elapsed = now.elapsed(); 56 | println!( 57 | "{:25} {:15} {:7.3} sec", 58 | $name, 59 | "Rust crossbeam-deque", 60 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 61 | ); 62 | }; 63 | } 64 | 65 | run!("unbounded_seq", seq()); 66 | run!("unbounded_spsc", spsc()); 67 | } 68 | -------------------------------------------------------------------------------- /crossbeam-skiplist/src/equivalent.rs: -------------------------------------------------------------------------------- 1 | // These traits are based on `equivalent` crate, but `K` and `Q` are flipped to avoid type inference issues: 2 | // https://github.com/indexmap-rs/equivalent/issues/5 3 | 4 | //! Traits for key comparison in maps. 5 | 6 | use core::{borrow::Borrow, cmp::Ordering}; 7 | 8 | /// Key equivalence trait. 9 | /// 10 | /// This trait allows hash table lookup to be customized. It has one blanket 11 | /// implementation that uses the regular solution with `Borrow` and `Eq`, just 12 | /// like `HashMap` does, so that you can pass `&str` to lookup into a map with 13 | /// `String` keys and so on. 14 | /// 15 | /// # Contract 16 | /// 17 | /// The implementor **must** hash like `Q`, if it is hashable. 18 | pub trait Equivalent { 19 | /// Compare self to `key` and return `true` if they are equal. 20 | fn equivalent(&self, key: &Q) -> bool; 21 | } 22 | 23 | impl Equivalent for K 24 | where 25 | K: Borrow, 26 | Q: Eq, 27 | { 28 | #[inline] 29 | fn equivalent(&self, key: &Q) -> bool { 30 | PartialEq::eq(self.borrow(), key) 31 | } 32 | } 33 | 34 | /// Key ordering trait. 35 | /// 36 | /// This trait allows ordered map lookup to be customized. It has one blanket 37 | /// implementation that uses the regular solution with `Borrow` and `Ord`, just 38 | /// like `BTreeMap` does, so that you can pass `&str` to lookup into a map with 39 | /// `String` keys and so on. 40 | pub trait Comparable: Equivalent { 41 | /// Compare self to `key` and return their ordering. 42 | fn compare(&self, key: &Q) -> Ordering; 43 | } 44 | 45 | impl Comparable for K 46 | where 47 | K: Borrow, 48 | Q: Ord, 49 | { 50 | #[inline] 51 | fn compare(&self, key: &Q) -> Ordering { 52 | Ord::cmp(self.borrow(), key) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /crossbeam-epoch/benches/defer.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use crossbeam_epoch::{self as epoch, Owned}; 6 | use crossbeam_utils::thread::scope; 7 | use test::Bencher; 8 | 9 | #[bench] 10 | fn single_alloc_defer_free(b: &mut Bencher) { 11 | b.iter(|| { 12 | let guard = &epoch::pin(); 13 | let p = Owned::new(1).into_shared(guard); 14 | unsafe { 15 | guard.defer_destroy(p); 16 | } 17 | }); 18 | } 19 | 20 | #[bench] 21 | fn single_defer(b: &mut Bencher) { 22 | b.iter(|| { 23 | let guard = &epoch::pin(); 24 | guard.defer(move || ()); 25 | }); 26 | } 27 | 28 | #[bench] 29 | fn multi_alloc_defer_free(b: &mut Bencher) { 30 | const THREADS: usize = 16; 31 | const STEPS: usize = 10_000; 32 | 33 | b.iter(|| { 34 | scope(|s| { 35 | for _ in 0..THREADS { 36 | s.spawn(|_| { 37 | for _ in 0..STEPS { 38 | let guard = &epoch::pin(); 39 | let p = Owned::new(1).into_shared(guard); 40 | unsafe { 41 | guard.defer_destroy(p); 42 | } 43 | } 44 | }); 45 | } 46 | }) 47 | .unwrap(); 48 | }); 49 | } 50 | 51 | #[bench] 52 | fn multi_defer(b: &mut Bencher) { 53 | const THREADS: usize = 16; 54 | const STEPS: usize = 10_000; 55 | 56 | b.iter(|| { 57 | scope(|s| { 58 | for _ in 0..THREADS { 59 | s.spawn(|_| { 60 | for _ in 0..STEPS { 61 | let guard = &epoch::pin(); 62 | guard.defer(move || ()); 63 | } 64 | }); 65 | } 66 | }) 67 | .unwrap(); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /crossbeam-skiplist/benches/hash.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use test::{black_box, Bencher}; 6 | 7 | use std::collections::HashMap as Map; 8 | 9 | #[bench] 10 | fn insert(b: &mut Bencher) { 11 | b.iter(|| { 12 | let mut map = Map::new(); 13 | 14 | let mut num = 0u64; 15 | for _ in 0..1_000 { 16 | num = num.wrapping_mul(17).wrapping_add(255); 17 | map.insert(num, !num); 18 | } 19 | }); 20 | } 21 | 22 | #[bench] 23 | fn iter(b: &mut Bencher) { 24 | let mut map = Map::new(); 25 | 26 | let mut num = 0u64; 27 | for _ in 0..1_000 { 28 | num = num.wrapping_mul(17).wrapping_add(255); 29 | map.insert(num, !num); 30 | } 31 | 32 | b.iter(|| { 33 | for x in map.iter() { 34 | black_box(x); 35 | } 36 | }); 37 | } 38 | 39 | #[bench] 40 | fn lookup(b: &mut Bencher) { 41 | let mut map = Map::new(); 42 | 43 | let mut num = 0u64; 44 | for _ in 0..1_000 { 45 | num = num.wrapping_mul(17).wrapping_add(255); 46 | map.insert(num, !num); 47 | } 48 | 49 | b.iter(|| { 50 | let mut num = 0u64; 51 | 52 | for _ in 0..1_000 { 53 | num = num.wrapping_mul(17).wrapping_add(255); 54 | black_box(map.get(&num)); 55 | } 56 | }); 57 | } 58 | 59 | #[bench] 60 | fn insert_remove(b: &mut Bencher) { 61 | b.iter(|| { 62 | let mut map = Map::new(); 63 | 64 | let mut num = 0u64; 65 | for _ in 0..1_000 { 66 | num = num.wrapping_mul(17).wrapping_add(255); 67 | map.insert(num, !num); 68 | } 69 | 70 | let mut num = 0u64; 71 | for _ in 0..1_000 { 72 | num = num.wrapping_mul(17).wrapping_add(255); 73 | black_box(map.remove(&num).unwrap()); 74 | } 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /crossbeam-deque/README.md: -------------------------------------------------------------------------------- 1 | # Crossbeam Deque 2 | 3 | [![Build Status](https://github.com/crossbeam-rs/crossbeam/workflows/CI/badge.svg)]( 4 | https://github.com/crossbeam-rs/crossbeam/actions) 5 | [![License](https://img.shields.io/badge/license-MIT_OR_Apache--2.0-blue.svg)]( 6 | https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-deque#license) 7 | [![Cargo](https://img.shields.io/crates/v/crossbeam-deque.svg)]( 8 | https://crates.io/crates/crossbeam-deque) 9 | [![Documentation](https://docs.rs/crossbeam-deque/badge.svg)]( 10 | https://docs.rs/crossbeam-deque) 11 | [![Rust 1.61+](https://img.shields.io/badge/rust-1.61+-lightgray.svg)]( 12 | https://www.rust-lang.org) 13 | [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) 14 | 15 | This crate provides work-stealing deques, which are primarily intended for 16 | building task schedulers. 17 | 18 | ## Usage 19 | 20 | Add this to your `Cargo.toml`: 21 | 22 | ```toml 23 | [dependencies] 24 | crossbeam-deque = "0.8" 25 | ``` 26 | 27 | ## Compatibility 28 | 29 | Crossbeam Deque supports stable Rust releases going back at least six months, 30 | and every time the minimum supported Rust version is increased, a new minor 31 | version is released. Currently, the minimum supported Rust version is 1.61. 32 | 33 | ## License 34 | 35 | Licensed under either of 36 | 37 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 38 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 39 | 40 | at your option. 41 | 42 | #### Contribution 43 | 44 | Unless you explicitly state otherwise, any contribution intentionally submitted 45 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 46 | dual licensed as above, without any additional terms or conditions. 47 | -------------------------------------------------------------------------------- /crossbeam-epoch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crossbeam-epoch" 3 | # When publishing a new version: 4 | # - Update CHANGELOG.md 5 | # - Update README.md (when increasing major or minor version) 6 | # - Run './tools/publish.sh crossbeam-epoch ' 7 | version = "0.9.18" 8 | edition = "2021" 9 | rust-version = "1.61" 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/crossbeam-rs/crossbeam" 12 | homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-epoch" 13 | description = "Epoch-based garbage collection" 14 | keywords = ["lock-free", "rcu", "atomic", "garbage"] 15 | categories = ["concurrency", "memory-management", "no-std"] 16 | 17 | [features] 18 | default = ["std"] 19 | 20 | # Enable to use APIs that require `std`. 21 | # This is enabled by default. 22 | std = ["alloc", "crossbeam-utils/std"] 23 | 24 | # Enable to use APIs that require `alloc`. 25 | # This is enabled by default and also enabled if the `std` feature is enabled. 26 | # 27 | # NOTE: Disabling both `std` *and* `alloc` features is not supported yet. 28 | alloc = [] 29 | 30 | # Enable the use of loom for concurrency testing. 31 | # 32 | # NOTE: This feature is outside of the normal semver guarantees and minor or 33 | # patch versions of crossbeam may make breaking changes to them at any time. 34 | loom = ["loom-crate", "crossbeam-utils/loom"] 35 | 36 | [dependencies] 37 | crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-features = false, features = ["atomic"] } 38 | 39 | # Enable the use of loom for concurrency testing. 40 | # 41 | # NOTE: This feature is outside of the normal semver guarantees and minor or 42 | # patch versions of crossbeam may make breaking changes to them at any time. 43 | [target.'cfg(crossbeam_loom)'.dependencies] 44 | loom-crate = { package = "loom", version = "0.7.1", optional = true } 45 | 46 | [dev-dependencies] 47 | fastrand = "2" 48 | 49 | [lints] 50 | workspace = true 51 | -------------------------------------------------------------------------------- /crossbeam-utils/build.rs: -------------------------------------------------------------------------------- 1 | // The rustc-cfg listed below are considered public API, but it is *unstable* 2 | // and outside of the normal semver guarantees: 3 | // 4 | // - `crossbeam_no_atomic` 5 | // Assume the target does *not* support any atomic operations. 6 | // This is usually detected automatically by the build script, but you may 7 | // need to enable it manually when building for custom targets or using 8 | // non-cargo build systems that don't run the build script. 9 | // 10 | // With the exceptions mentioned above, the rustc-cfg emitted by the build 11 | // script are *not* public API. 12 | 13 | use std::env; 14 | 15 | include!("no_atomic.rs"); 16 | include!("build-common.rs"); 17 | 18 | fn main() { 19 | println!("cargo:rerun-if-changed=no_atomic.rs"); 20 | println!("cargo:rustc-check-cfg=cfg(crossbeam_no_atomic,crossbeam_sanitize_thread,crossbeam_atomic_cell_force_fallback)"); 21 | 22 | let target = match env::var("TARGET") { 23 | Ok(target) => convert_custom_linux_target(target), 24 | Err(e) => { 25 | println!( 26 | "cargo:warning={}: unable to get TARGET environment variable: {}", 27 | env!("CARGO_PKG_NAME"), 28 | e 29 | ); 30 | return; 31 | } 32 | }; 33 | 34 | // Note that this is `no_`*, not `has_*`. This allows treating as the latest 35 | // stable rustc is used when the build script doesn't run. This is useful 36 | // for non-cargo build systems that don't run the build script. 37 | if NO_ATOMIC.contains(&&*target) { 38 | println!("cargo:rustc-cfg=crossbeam_no_atomic"); 39 | } 40 | 41 | // `cfg(sanitize = "..")` is not stabilized. 42 | if let Ok(sanitize) = env::var("CARGO_CFG_SANITIZE") { 43 | if sanitize.contains("thread") { 44 | println!("cargo:rustc-cfg=crossbeam_sanitize_thread"); 45 | } 46 | println!("cargo:rustc-cfg=crossbeam_atomic_cell_force_fallback"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /crossbeam-epoch/examples/sanitize.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::AtomicUsize; 2 | use std::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed}; 3 | use std::sync::Arc; 4 | use std::thread; 5 | use std::time::{Duration, Instant}; 6 | 7 | use crossbeam_epoch::{self as epoch, Atomic, Collector, LocalHandle, Owned, Shared}; 8 | 9 | fn worker(a: Arc>, handle: LocalHandle) -> usize { 10 | let mut rng = fastrand::Rng::new(); 11 | let mut sum = 0; 12 | 13 | if rng.bool() { 14 | thread::sleep(Duration::from_millis(1)); 15 | } 16 | let timeout = Duration::from_millis(rng.u64(0..10)); 17 | let now = Instant::now(); 18 | 19 | while now.elapsed() < timeout { 20 | for _ in 0..100 { 21 | let guard = &handle.pin(); 22 | guard.flush(); 23 | 24 | let val = if rng.bool() { 25 | let p = a.swap(Owned::new(AtomicUsize::new(sum)), AcqRel, guard); 26 | unsafe { 27 | guard.defer_destroy(p); 28 | guard.flush(); 29 | p.deref().load(Relaxed) 30 | } 31 | } else { 32 | let p = a.load(Acquire, guard); 33 | unsafe { p.deref().fetch_add(sum, Relaxed) } 34 | }; 35 | 36 | sum = sum.wrapping_add(val); 37 | } 38 | } 39 | 40 | sum 41 | } 42 | 43 | fn main() { 44 | for _ in 0..100 { 45 | let collector = Collector::new(); 46 | let a = Arc::new(Atomic::new(AtomicUsize::new(777))); 47 | 48 | let threads = (0..16) 49 | .map(|_| { 50 | let a = a.clone(); 51 | let c = collector.clone(); 52 | thread::spawn(move || worker(a, c.register())) 53 | }) 54 | .collect::>(); 55 | 56 | for t in threads { 57 | t.join().unwrap(); 58 | } 59 | 60 | unsafe { 61 | a.swap(Shared::null(), AcqRel, epoch::unprotected()) 62 | .into_owned(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /crossbeam-channel/tests/never.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the never channel flavor. 2 | 3 | use std::thread; 4 | use std::time::{Duration, Instant}; 5 | 6 | use crossbeam_channel::{never, select, tick, unbounded}; 7 | 8 | fn ms(ms: u64) -> Duration { 9 | Duration::from_millis(ms) 10 | } 11 | 12 | #[test] 13 | fn smoke() { 14 | select! { 15 | recv(never::()) -> _ => panic!(), 16 | default => {} 17 | } 18 | } 19 | 20 | #[test] 21 | fn optional() { 22 | let (s, r) = unbounded::(); 23 | s.send(1).unwrap(); 24 | s.send(2).unwrap(); 25 | 26 | let mut r = Some(&r); 27 | select! { 28 | recv(r.unwrap_or(&never())) -> _ => {} 29 | default => panic!(), 30 | } 31 | 32 | r = None; 33 | select! { 34 | recv(r.unwrap_or(&never())) -> _ => panic!(), 35 | default => {} 36 | } 37 | } 38 | 39 | #[test] 40 | fn tick_n() { 41 | let mut r = tick(ms(100)); 42 | let mut step = 0; 43 | 44 | loop { 45 | select! { 46 | recv(r) -> _ => step += 1, 47 | default(ms(500)) => break, 48 | } 49 | 50 | if step == 10 { 51 | r = never(); 52 | } 53 | } 54 | 55 | assert_eq!(step, 10); 56 | } 57 | 58 | #[test] 59 | fn capacity() { 60 | let r = never::(); 61 | assert_eq!(r.capacity(), Some(0)); 62 | } 63 | 64 | #[test] 65 | fn len_empty_full() { 66 | let r = never::(); 67 | assert_eq!(r.len(), 0); 68 | assert!(r.is_empty()); 69 | assert!(r.is_full()); 70 | } 71 | 72 | #[test] 73 | fn try_recv() { 74 | let r = never::(); 75 | assert!(r.try_recv().is_err()); 76 | 77 | thread::sleep(ms(100)); 78 | assert!(r.try_recv().is_err()); 79 | } 80 | 81 | #[test] 82 | fn recv_timeout() { 83 | let start = Instant::now(); 84 | let r = never::(); 85 | 86 | assert!(r.recv_timeout(ms(100)).is_err()); 87 | let now = Instant::now(); 88 | assert!(now - start >= ms(100)); 89 | assert!(now - start <= ms(150)); 90 | 91 | assert!(r.recv_timeout(ms(100)).is_err()); 92 | let now = Instant::now(); 93 | assert!(now - start >= ms(200)); 94 | assert!(now - start <= ms(250)); 95 | } 96 | -------------------------------------------------------------------------------- /crossbeam-queue/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 0.3.12 2 | 3 | - Fix stack overflow when pushing large value to `SegQueue`. (#1146, #1147, #1159) 4 | 5 | # Version 0.3.11 6 | 7 | - Remove dependency on `cfg-if`. (#1072) 8 | 9 | # Version 0.3.10 10 | 11 | - Relax the minimum supported Rust version to 1.60. (#1056) 12 | - Implement `UnwindSafe` and `RefUnwindSafe` for `ArrayQueue` and `SegQueue`. (#1053) 13 | - Optimize `Drop` implementation of `ArrayQueue`. (#1057) 14 | 15 | # Version 0.3.9 16 | 17 | - Bump the minimum supported Rust version to 1.61. (#1037) 18 | - Improve support for targets without atomic CAS. (#1037) 19 | - Remove build script. (#1037) 20 | 21 | # Version 0.3.8 22 | 23 | - Fix build script bug introduced in 0.3.7. (#932) 24 | 25 | # Version 0.3.7 26 | 27 | **Note:** This release has been yanked due to regression fixed in 0.3.8. 28 | 29 | - Improve support for custom targets. (#922) 30 | 31 | # Version 0.3.6 32 | 33 | - Bump the minimum supported Rust version to 1.38. (#877) 34 | 35 | # Version 0.3.5 36 | 37 | - Add `ArrayQueue::force_push`. (#789) 38 | 39 | # Version 0.3.4 40 | 41 | - Implement `IntoIterator` for `ArrayQueue` and `SegQueue`. (#772) 42 | 43 | # Version 0.3.3 44 | 45 | - Fix stacked borrows violation in `ArrayQueue` when `-Zmiri-tag-raw-pointers` is enabled. (#763) 46 | 47 | # Version 0.3.2 48 | 49 | - Support targets that do not have atomic CAS on stable Rust. (#698) 50 | 51 | # Version 0.3.1 52 | 53 | - Make `SegQueue::new` const fn. (#584) 54 | - Change license to "MIT OR Apache-2.0". 55 | 56 | # Version 0.3.0 57 | 58 | - Bump the minimum supported Rust version to 1.36. 59 | - Remove `PushError` and `PopError`. 60 | 61 | # Version 0.2.3 62 | 63 | - Fix bug in release (yanking 0.2.2) 64 | 65 | # Version 0.2.2 66 | 67 | - Fix unsoundness issues by adopting `MaybeUninit`. (#458) 68 | 69 | # Version 0.2.1 70 | 71 | - Add `no_std` support. 72 | 73 | # Version 0.2.0 74 | 75 | - Bump the minimum required version to 1.28. 76 | - Bump `crossbeam-utils` to `0.7`. 77 | 78 | # Version 0.1.2 79 | 80 | - Update `crossbeam-utils` to `0.6.5`. 81 | 82 | # Version 0.1.1 83 | 84 | - Update `crossbeam-utils` to `0.6.4`. 85 | 86 | # Version 0.1.0 87 | 88 | - Initial version with `ArrayQueue` and `SegQueue`. 89 | -------------------------------------------------------------------------------- /crossbeam-skiplist/benches/skipmap.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use test::{black_box, Bencher}; 6 | 7 | use crossbeam_skiplist::SkipMap as Map; 8 | 9 | #[bench] 10 | fn insert(b: &mut Bencher) { 11 | b.iter(|| { 12 | let map = Map::new(); 13 | 14 | let mut num = 0u64; 15 | for _ in 0..1_000 { 16 | num = num.wrapping_mul(17).wrapping_add(255); 17 | map.insert(num, !num); 18 | } 19 | }); 20 | } 21 | 22 | #[bench] 23 | fn iter(b: &mut Bencher) { 24 | let map = Map::new(); 25 | 26 | let mut num = 0u64; 27 | for _ in 0..1_000 { 28 | num = num.wrapping_mul(17).wrapping_add(255); 29 | map.insert(num, !num); 30 | } 31 | 32 | b.iter(|| { 33 | for x in map.iter() { 34 | black_box(x); 35 | } 36 | }); 37 | } 38 | 39 | #[bench] 40 | fn rev_iter(b: &mut Bencher) { 41 | let map = Map::new(); 42 | 43 | let mut num = 0u64; 44 | for _ in 0..1_000 { 45 | num = num.wrapping_mul(17).wrapping_add(255); 46 | map.insert(num, !num); 47 | } 48 | 49 | b.iter(|| { 50 | for x in map.iter().rev() { 51 | black_box(x); 52 | } 53 | }); 54 | } 55 | 56 | #[bench] 57 | fn lookup(b: &mut Bencher) { 58 | let map = Map::new(); 59 | 60 | let mut num = 0u64; 61 | for _ in 0..1_000 { 62 | num = num.wrapping_mul(17).wrapping_add(255); 63 | map.insert(num, !num); 64 | } 65 | 66 | b.iter(|| { 67 | let mut num = 0u64; 68 | 69 | for _ in 0..1_000 { 70 | num = num.wrapping_mul(17).wrapping_add(255); 71 | black_box(map.get(&num)); 72 | } 73 | }); 74 | } 75 | 76 | #[bench] 77 | fn insert_remove(b: &mut Bencher) { 78 | b.iter(|| { 79 | let map = Map::new(); 80 | 81 | let mut num = 0u64; 82 | for _ in 0..1_000 { 83 | num = num.wrapping_mul(17).wrapping_add(255); 84 | map.insert(num, !num); 85 | } 86 | 87 | let mut num = 0u64; 88 | for _ in 0..1_000 { 89 | num = num.wrapping_mul(17).wrapping_add(255); 90 | black_box(map.remove(&num).unwrap()); 91 | } 92 | }); 93 | } 94 | -------------------------------------------------------------------------------- /crossbeam-skiplist/benches/btree.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use test::{black_box, Bencher}; 6 | 7 | use std::collections::BTreeMap as Map; 8 | 9 | #[bench] 10 | fn insert(b: &mut Bencher) { 11 | b.iter(|| { 12 | let mut map = Map::new(); 13 | 14 | let mut num = 0u64; 15 | for _ in 0..1_000 { 16 | num = num.wrapping_mul(17).wrapping_add(255); 17 | map.insert(num, !num); 18 | } 19 | }); 20 | } 21 | 22 | #[bench] 23 | fn iter(b: &mut Bencher) { 24 | let mut map = Map::new(); 25 | 26 | let mut num = 0u64; 27 | for _ in 0..1_000 { 28 | num = num.wrapping_mul(17).wrapping_add(255); 29 | map.insert(num, !num); 30 | } 31 | 32 | b.iter(|| { 33 | for x in map.iter() { 34 | black_box(x); 35 | } 36 | }); 37 | } 38 | 39 | #[bench] 40 | fn rev_iter(b: &mut Bencher) { 41 | let mut map = Map::new(); 42 | 43 | let mut num = 0u64; 44 | for _ in 0..1_000 { 45 | num = num.wrapping_mul(17).wrapping_add(255); 46 | map.insert(num, !num); 47 | } 48 | 49 | b.iter(|| { 50 | for x in map.iter().rev() { 51 | black_box(x); 52 | } 53 | }); 54 | } 55 | 56 | #[bench] 57 | fn lookup(b: &mut Bencher) { 58 | let mut map = Map::new(); 59 | 60 | let mut num = 0u64; 61 | for _ in 0..1_000 { 62 | num = num.wrapping_mul(17).wrapping_add(255); 63 | map.insert(num, !num); 64 | } 65 | 66 | b.iter(|| { 67 | let mut num = 0u64; 68 | 69 | for _ in 0..1_000 { 70 | num = num.wrapping_mul(17).wrapping_add(255); 71 | black_box(map.get(&num)); 72 | } 73 | }); 74 | } 75 | 76 | #[bench] 77 | fn insert_remove(b: &mut Bencher) { 78 | b.iter(|| { 79 | let mut map = Map::new(); 80 | 81 | let mut num = 0u64; 82 | for _ in 0..1_000 { 83 | num = num.wrapping_mul(17).wrapping_add(255); 84 | map.insert(num, !num); 85 | } 86 | 87 | let mut num = 0u64; 88 | for _ in 0..1_000 { 89 | num = num.wrapping_mul(17).wrapping_add(255); 90 | black_box(map.remove(&num).unwrap()); 91 | } 92 | }); 93 | } 94 | -------------------------------------------------------------------------------- /crossbeam-epoch/README.md: -------------------------------------------------------------------------------- 1 | # Crossbeam Epoch 2 | 3 | [![Build Status](https://github.com/crossbeam-rs/crossbeam/workflows/CI/badge.svg)]( 4 | https://github.com/crossbeam-rs/crossbeam/actions) 5 | [![License](https://img.shields.io/badge/license-MIT_OR_Apache--2.0-blue.svg)]( 6 | https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-epoch#license) 7 | [![Cargo](https://img.shields.io/crates/v/crossbeam-epoch.svg)]( 8 | https://crates.io/crates/crossbeam-epoch) 9 | [![Documentation](https://docs.rs/crossbeam-epoch/badge.svg)]( 10 | https://docs.rs/crossbeam-epoch) 11 | [![Rust 1.61+](https://img.shields.io/badge/rust-1.61+-lightgray.svg)]( 12 | https://www.rust-lang.org) 13 | [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) 14 | 15 | This crate provides epoch-based garbage collection for building concurrent data structures. 16 | 17 | When a thread removes an object from a concurrent data structure, other threads 18 | may be still using pointers to it at the same time, so it cannot be destroyed 19 | immediately. Epoch-based GC is an efficient mechanism for deferring destruction of 20 | shared objects until no pointers to them can exist. 21 | 22 | Everything in this crate except the global GC can be used in `no_std` environments, provided that 23 | `alloc` feature is enabled. 24 | 25 | ## Usage 26 | 27 | Add this to your `Cargo.toml`: 28 | 29 | ```toml 30 | [dependencies] 31 | crossbeam-epoch = "0.9" 32 | ``` 33 | 34 | ## Compatibility 35 | 36 | Crossbeam Epoch supports stable Rust releases going back at least six months, 37 | and every time the minimum supported Rust version is increased, a new minor 38 | version is released. Currently, the minimum supported Rust version is 1.61. 39 | 40 | ## License 41 | 42 | Licensed under either of 43 | 44 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 45 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 46 | 47 | at your option. 48 | 49 | #### Contribution 50 | 51 | Unless you explicitly state otherwise, any contribution intentionally submitted 52 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 53 | dual licensed as above, without any additional terms or conditions. 54 | -------------------------------------------------------------------------------- /crossbeam-queue/README.md: -------------------------------------------------------------------------------- 1 | # Crossbeam Queue 2 | 3 | [![Build Status](https://github.com/crossbeam-rs/crossbeam/workflows/CI/badge.svg)]( 4 | https://github.com/crossbeam-rs/crossbeam/actions) 5 | [![License](https://img.shields.io/badge/license-MIT_OR_Apache--2.0-blue.svg)]( 6 | https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-queue#license) 7 | [![Cargo](https://img.shields.io/crates/v/crossbeam-queue.svg)]( 8 | https://crates.io/crates/crossbeam-queue) 9 | [![Documentation](https://docs.rs/crossbeam-queue/badge.svg)]( 10 | https://docs.rs/crossbeam-queue) 11 | [![Rust 1.60+](https://img.shields.io/badge/rust-1.60+-lightgray.svg)]( 12 | https://www.rust-lang.org) 13 | [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) 14 | 15 | This crate provides concurrent queues that can be shared among threads: 16 | 17 | * [`ArrayQueue`], a bounded MPMC queue that allocates a fixed-capacity buffer on construction. 18 | * [`SegQueue`], an unbounded MPMC queue that allocates small buffers, segments, on demand. 19 | 20 | Everything in this crate can be used in `no_std` environments, provided that `alloc` feature is 21 | enabled. 22 | 23 | [`ArrayQueue`]: https://docs.rs/crossbeam-queue/latest/crossbeam_queue/struct.ArrayQueue.html 24 | [`SegQueue`]: https://docs.rs/crossbeam-queue/latest/crossbeam_queue/struct.SegQueue.html 25 | 26 | ## Usage 27 | 28 | Add this to your `Cargo.toml`: 29 | 30 | ```toml 31 | [dependencies] 32 | crossbeam-queue = "0.3" 33 | ``` 34 | 35 | ## Compatibility 36 | 37 | Crossbeam Queue supports stable Rust releases going back at least six months, 38 | and every time the minimum supported Rust version is increased, a new minor 39 | version is released. Currently, the minimum supported Rust version is 1.60. 40 | 41 | ## License 42 | 43 | Licensed under either of 44 | 45 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 46 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 47 | 48 | at your option. 49 | 50 | #### Contribution 51 | 52 | Unless you explicitly state otherwise, any contribution intentionally submitted 53 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 54 | dual licensed as above, without any additional terms or conditions. 55 | -------------------------------------------------------------------------------- /crossbeam-skiplist/README.md: -------------------------------------------------------------------------------- 1 | # Crossbeam Skiplist 2 | 3 | [![Build Status](https://github.com/crossbeam-rs/crossbeam/workflows/CI/badge.svg)]( 4 | https://github.com/crossbeam-rs/crossbeam/actions) 5 | [![License](https://img.shields.io/badge/license-MIT_OR_Apache--2.0-blue.svg)]( 6 | https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-skiplist#license) 7 | [![Cargo](https://img.shields.io/crates/v/crossbeam-skiplist.svg)]( 8 | https://crates.io/crates/crossbeam-skiplist) 9 | [![Documentation](https://docs.rs/crossbeam-skiplist/badge.svg)]( 10 | https://docs.rs/crossbeam-skiplist) 11 | [![Rust 1.61+](https://img.shields.io/badge/rust-1.61+-lightgray.svg)]( 12 | https://www.rust-lang.org) 13 | [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) 14 | 15 | This crate provides the types [`SkipMap`] and [`SkipSet`]. 16 | These data structures provide an interface similar to `BTreeMap` and `BTreeSet`, 17 | respectively, except they support safe concurrent access across multiple threads. 18 | 19 | This crate can be used in `no_std` environments that implement `alloc`. The `alloc` feature of this crate needs to be enabled in `no_std` environments. 20 | 21 | [`SkipMap`]: https://docs.rs/crossbeam-skiplist/latest/crossbeam_skiplist/struct.SkipMap.html 22 | [`SkipSet`]: https://docs.rs/crossbeam-skiplist/latest/crossbeam_skiplist/struct.SkipSet.html 23 | 24 | ## Usage 25 | 26 | Add this to your `Cargo.toml`: 27 | 28 | ```toml 29 | [dependencies] 30 | crossbeam-skiplist = "0.1" 31 | ``` 32 | 33 | ## Compatibility 34 | 35 | Crossbeam Skiplist supports stable Rust releases going back at least six months, 36 | and every time the minimum supported Rust version is increased, a new minor 37 | version is released. Currently, the minimum supported Rust version is 1.61. 38 | 39 | ## License 40 | 41 | Licensed under either of 42 | 43 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 44 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 45 | 46 | at your option. 47 | 48 | #### Contribution 49 | 50 | Unless you explicitly state otherwise, any contribution intentionally submitted 51 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 52 | dual licensed as above, without any additional terms or conditions. 53 | -------------------------------------------------------------------------------- /ci/miri.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")"/.. 5 | 6 | group=$1 7 | 8 | # We need 'ts' for the per-line timing 9 | sudo apt-get -y install moreutils 10 | echo 11 | 12 | export RUSTFLAGS="${RUSTFLAGS:-} -Z randomize-layout" 13 | export RUSTDOCFLAGS="${RUSTDOCFLAGS:-} -Z randomize-layout" 14 | export MIRIFLAGS="${MIRIFLAGS:-} -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation" 15 | 16 | case "${group}" in 17 | channel) 18 | MIRI_LEAK_CHECK='1' \ 19 | cargo miri test --all-features \ 20 | -p crossbeam-channel 2>&1 | ts -i '%.s ' 21 | # -Zmiri-ignore-leaks is needed because we use detached threads in tests in tests/golang.rs: https://github.com/rust-lang/miri/issues/1371 22 | MIRIFLAGS="${MIRIFLAGS} -Zmiri-ignore-leaks" \ 23 | cargo miri test --all-features \ 24 | -p crossbeam-channel --test golang 2>&1 | ts -i '%.s ' 25 | ;; 26 | others) 27 | cargo miri test --all-features \ 28 | -p crossbeam-queue \ 29 | -p crossbeam-utils 2>&1 | ts -i '%.s ' 30 | # Use Tree Borrows instead of Stacked Borrows because epoch is not compatible with Stacked Borrows: https://github.com/crossbeam-rs/crossbeam/issues/545#issuecomment-1192785003 31 | MIRIFLAGS="${MIRIFLAGS} -Zmiri-tree-borrows" \ 32 | cargo miri test --all-features \ 33 | -p crossbeam-epoch \ 34 | -p crossbeam-skiplist \ 35 | -p crossbeam 2>&1 | ts -i '%.s ' 36 | # Use Tree Borrows instead of Stacked Borrows because epoch is not compatible with Stacked Borrows: https://github.com/crossbeam-rs/crossbeam/issues/545#issuecomment-1192785003 37 | # -Zmiri-compare-exchange-weak-failure-rate=0.0 is needed because some sequential tests (e.g., 38 | # doctest of Stealer::steal) incorrectly assume that sequential weak CAS will never fail. 39 | # -Zmiri-preemption-rate=0 is needed because this code technically has UB and Miri catches that. 40 | MIRIFLAGS="${MIRIFLAGS} -Zmiri-tree-borrows -Zmiri-compare-exchange-weak-failure-rate=0.0 -Zmiri-preemption-rate=0" \ 41 | cargo miri test --all-features \ 42 | -p crossbeam-deque 2>&1 | ts -i '%.s ' 43 | ;; 44 | *) 45 | echo "unknown crate group '${group}'" 46 | exit 1 47 | ;; 48 | esac 49 | -------------------------------------------------------------------------------- /crossbeam-channel/examples/matching.rs: -------------------------------------------------------------------------------- 1 | //! Using `select!` to send and receive on the same channel at the same time. 2 | //! 3 | //! This example is based on the following program in Go. 4 | //! 5 | //! Source: 6 | //! - https://web.archive.org/web/20171209034309/https://www.nada.kth.se/~snilsson/concurrency 7 | //! - http://www.nada.kth.se/~snilsson/concurrency/src/matching.go 8 | //! 9 | //! Copyright & License: 10 | //! - Stefan Nilsson 11 | //! - Creative Commons Attribution 3.0 Unported License 12 | //! - https://creativecommons.org/licenses/by/3.0/ 13 | //! 14 | //! ```go 15 | //! func main() { 16 | //! people := []string{"Anna", "Bob", "Cody", "Dave", "Eva"} 17 | //! match := make(chan string, 1) // Make room for one unmatched send. 18 | //! wg := new(sync.WaitGroup) 19 | //! for _, name := range people { 20 | //! wg.Add(1) 21 | //! go Seek(name, match, wg) 22 | //! } 23 | //! wg.Wait() 24 | //! select { 25 | //! case name := <-match: 26 | //! fmt.Printf("No one received %s’s message.\n", name) 27 | //! default: 28 | //! // There was no pending send operation. 29 | //! } 30 | //! } 31 | //! 32 | //! // Seek either sends or receives, whichever possible, a name on the match 33 | //! // channel and notifies the wait group when done. 34 | //! func Seek(name string, match chan string, wg *sync.WaitGroup) { 35 | //! select { 36 | //! case peer := <-match: 37 | //! fmt.Printf("%s received a message from %s.\n", name, peer) 38 | //! case match <- name: 39 | //! // Wait for someone to receive my message. 40 | //! } 41 | //! wg.Done() 42 | //! } 43 | //! ``` 44 | 45 | use crossbeam_channel::{bounded, select}; 46 | use crossbeam_utils::thread; 47 | 48 | fn main() { 49 | let people = vec!["Anna", "Bob", "Cody", "Dave", "Eva"]; 50 | let (s, r) = bounded(1); // Make room for one unmatched send. 51 | 52 | // Either send my name into the channel or receive someone else's, whatever happens first. 53 | let seek = |name, s, r| { 54 | select! { 55 | recv(r) -> peer => println!("{} received a message from {}.", name, peer.unwrap()), 56 | send(s, name) -> _ => {}, // Wait for someone to receive my message. 57 | } 58 | }; 59 | 60 | thread::scope(|scope| { 61 | for name in people { 62 | let (s, r) = (s.clone(), r.clone()); 63 | scope.spawn(move |_| seek(name, s, r)); 64 | } 65 | }) 66 | .unwrap(); 67 | 68 | // Check if there is a pending send operation. 69 | if let Ok(name) = r.try_recv() { 70 | println!("No one received {}’s message.", name); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Tools for concurrent programming. 2 | //! 3 | //! ## Atomics 4 | //! 5 | //! * [`AtomicCell`], a thread-safe mutable memory location. 6 | //! * [`AtomicConsume`], for reading from primitive atomic types with "consume" ordering. 7 | //! 8 | //! ## Data structures 9 | //! 10 | //! * [`deque`], work-stealing deques for building task schedulers. 11 | //! * [`ArrayQueue`], a bounded MPMC queue that allocates a fixed-capacity buffer on construction. 12 | //! * [`SegQueue`], an unbounded MPMC queue that allocates small buffers, segments, on demand. 13 | //! 14 | //! ## Memory management 15 | //! 16 | //! * [`epoch`], an epoch-based garbage collector. 17 | //! 18 | //! ## Thread synchronization 19 | //! 20 | //! * [`channel`], multi-producer multi-consumer channels for message passing. 21 | //! * [`Parker`], a thread parking primitive. 22 | //! * [`ShardedLock`], a sharded reader-writer lock with fast concurrent reads. 23 | //! * [`WaitGroup`], for synchronizing the beginning or end of some computation. 24 | //! 25 | //! ## Utilities 26 | //! 27 | //! * [`Backoff`], for exponential backoff in spin loops. 28 | //! * [`CachePadded`], for padding and aligning a value to the length of a cache line. 29 | //! * [`scope`], for spawning threads that borrow local variables from the stack. 30 | //! 31 | //! [`AtomicCell`]: atomic::AtomicCell 32 | //! [`AtomicConsume`]: atomic::AtomicConsume 33 | //! [`ArrayQueue`]: queue::ArrayQueue 34 | //! [`SegQueue`]: queue::SegQueue 35 | //! [`Parker`]: sync::Parker 36 | //! [`ShardedLock`]: sync::ShardedLock 37 | //! [`WaitGroup`]: sync::WaitGroup 38 | //! [`Backoff`]: utils::Backoff 39 | //! [`CachePadded`]: utils::CachePadded 40 | 41 | #![no_std] 42 | #![doc(test( 43 | no_crate_inject, 44 | attr( 45 | deny(warnings, rust_2018_idioms, single_use_lifetimes), 46 | allow(dead_code, unused_assignments, unused_variables) 47 | ) 48 | ))] 49 | #![warn(missing_docs, unsafe_op_in_unsafe_fn)] 50 | 51 | #[cfg(feature = "std")] 52 | extern crate std; 53 | 54 | pub use crossbeam_utils::atomic; 55 | 56 | pub mod utils { 57 | //! Miscellaneous utilities. 58 | //! 59 | //! * [`Backoff`], for exponential backoff in spin loops. 60 | //! * [`CachePadded`], for padding and aligning a value to the length of a cache line. 61 | 62 | pub use crossbeam_utils::Backoff; 63 | pub use crossbeam_utils::CachePadded; 64 | } 65 | 66 | #[cfg(feature = "alloc")] 67 | #[doc(inline)] 68 | pub use {crossbeam_epoch as epoch, crossbeam_queue as queue}; 69 | 70 | #[cfg(feature = "std")] 71 | #[doc(inline)] 72 | pub use { 73 | crossbeam_channel as channel, crossbeam_channel::select, crossbeam_deque as deque, 74 | crossbeam_utils::sync, 75 | }; 76 | 77 | #[cfg(feature = "std")] 78 | #[cfg(not(crossbeam_loom))] 79 | pub use crossbeam_utils::thread::{self, scope}; 80 | -------------------------------------------------------------------------------- /crossbeam-skiplist/benches/skiplist.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![allow(clippy::unit_arg)] 3 | 4 | extern crate test; 5 | 6 | use test::{black_box, Bencher}; 7 | 8 | use crossbeam_epoch as epoch; 9 | use crossbeam_skiplist::SkipList; 10 | 11 | #[bench] 12 | fn insert(b: &mut Bencher) { 13 | let guard = &epoch::pin(); 14 | 15 | b.iter(|| { 16 | let map = SkipList::new(epoch::default_collector().clone()); 17 | 18 | let mut num = 0u64; 19 | for _ in 0..1_000 { 20 | num = num.wrapping_mul(17).wrapping_add(255); 21 | map.insert(num, !num, guard); 22 | } 23 | }); 24 | } 25 | 26 | #[bench] 27 | fn iter(b: &mut Bencher) { 28 | let guard = &epoch::pin(); 29 | let map = SkipList::new(epoch::default_collector().clone()); 30 | 31 | let mut num = 0u64; 32 | for _ in 0..1_000 { 33 | num = num.wrapping_mul(17).wrapping_add(255); 34 | map.insert(num, !num, guard).release(guard); 35 | } 36 | 37 | b.iter(|| { 38 | for x in map.iter(guard) { 39 | black_box(x.key()); 40 | } 41 | }); 42 | } 43 | 44 | #[bench] 45 | fn rev_iter(b: &mut Bencher) { 46 | let guard = &epoch::pin(); 47 | let map = SkipList::new(epoch::default_collector().clone()); 48 | 49 | let mut num = 0u64; 50 | for _ in 0..1_000 { 51 | num = num.wrapping_mul(17).wrapping_add(255); 52 | map.insert(num, !num, guard).release(guard); 53 | } 54 | 55 | b.iter(|| { 56 | for x in map.iter(guard).rev() { 57 | black_box(x.key()); 58 | } 59 | }); 60 | } 61 | 62 | #[bench] 63 | fn lookup(b: &mut Bencher) { 64 | let guard = &epoch::pin(); 65 | let map = SkipList::new(epoch::default_collector().clone()); 66 | 67 | let mut num = 0u64; 68 | for _ in 0..1_000 { 69 | num = num.wrapping_mul(17).wrapping_add(255); 70 | map.insert(num, !num, guard).release(guard); 71 | } 72 | 73 | b.iter(|| { 74 | let mut num = 0u64; 75 | for _ in 0..1_000 { 76 | num = num.wrapping_mul(17).wrapping_add(255); 77 | black_box(map.get(&num, guard)); 78 | } 79 | }); 80 | } 81 | 82 | #[bench] 83 | fn insert_remove(b: &mut Bencher) { 84 | let guard = &epoch::pin(); 85 | 86 | b.iter(|| { 87 | let map = SkipList::new(epoch::default_collector().clone()); 88 | 89 | let mut num = 0u64; 90 | for _ in 0..1_000 { 91 | num = num.wrapping_mul(17).wrapping_add(255); 92 | map.insert(num, !num, guard).release(guard); 93 | } 94 | 95 | let mut num = 0u64; 96 | for _ in 0..1_000 { 97 | num = num.wrapping_mul(17).wrapping_add(255); 98 | black_box(map.remove(&num, guard).unwrap().release(guard)); 99 | } 100 | }); 101 | } 102 | -------------------------------------------------------------------------------- /crossbeam-epoch/src/default.rs: -------------------------------------------------------------------------------- 1 | //! The default garbage collector. 2 | //! 3 | //! For each thread, a participant is lazily initialized on its first use, when the current thread 4 | //! is registered in the default collector. If initialized, the thread's participant will get 5 | //! destructed on thread exit, which in turn unregisters the thread. 6 | 7 | use crate::collector::{Collector, LocalHandle}; 8 | use crate::guard::Guard; 9 | use crate::primitive::thread_local; 10 | #[cfg(not(crossbeam_loom))] 11 | use crate::sync::once_lock::OnceLock; 12 | 13 | fn collector() -> &'static Collector { 14 | #[cfg(not(crossbeam_loom))] 15 | { 16 | /// The global data for the default garbage collector. 17 | static COLLECTOR: OnceLock = OnceLock::new(); 18 | COLLECTOR.get_or_init(Collector::new) 19 | } 20 | // FIXME: loom does not currently provide the equivalent of Lazy: 21 | // https://github.com/tokio-rs/loom/issues/263 22 | #[cfg(crossbeam_loom)] 23 | { 24 | loom::lazy_static! { 25 | /// The global data for the default garbage collector. 26 | static ref COLLECTOR: Collector = Collector::new(); 27 | } 28 | &COLLECTOR 29 | } 30 | } 31 | 32 | thread_local! { 33 | /// The per-thread participant for the default garbage collector. 34 | static HANDLE: LocalHandle = collector().register(); 35 | } 36 | 37 | /// Pins the current thread. 38 | #[inline] 39 | pub fn pin() -> Guard { 40 | with_handle(|handle| handle.pin()) 41 | } 42 | 43 | /// Returns `true` if the current thread is pinned. 44 | #[inline] 45 | pub fn is_pinned() -> bool { 46 | with_handle(|handle| handle.is_pinned()) 47 | } 48 | 49 | /// Returns the default global collector. 50 | pub fn default_collector() -> &'static Collector { 51 | collector() 52 | } 53 | 54 | #[inline] 55 | fn with_handle(mut f: F) -> R 56 | where 57 | F: FnMut(&LocalHandle) -> R, 58 | { 59 | HANDLE 60 | .try_with(|h| f(h)) 61 | .unwrap_or_else(|_| f(&collector().register())) 62 | } 63 | 64 | #[cfg(all(test, not(crossbeam_loom)))] 65 | mod tests { 66 | use crossbeam_utils::thread; 67 | 68 | #[test] 69 | fn pin_while_exiting() { 70 | struct Foo; 71 | 72 | impl Drop for Foo { 73 | fn drop(&mut self) { 74 | // Pin after `HANDLE` has been dropped. This must not panic. 75 | super::pin(); 76 | } 77 | } 78 | 79 | std::thread_local! { 80 | static FOO: Foo = const { Foo }; 81 | } 82 | 83 | thread::scope(|scope| { 84 | scope.spawn(|_| { 85 | // Initialize `FOO` and then `HANDLE`. 86 | FOO.with(|_| ()); 87 | super::pin(); 88 | // At thread exit, `HANDLE` gets dropped first and `FOO` second. 89 | }); 90 | }) 91 | .unwrap(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /crossbeam-utils/tests/cache_padded.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::mem; 3 | 4 | use crossbeam_utils::CachePadded; 5 | 6 | #[test] 7 | fn default() { 8 | let x: CachePadded = Default::default(); 9 | assert_eq!(*x, 0); 10 | } 11 | 12 | #[test] 13 | fn store_u64() { 14 | let x: CachePadded = CachePadded::new(17); 15 | assert_eq!(*x, 17); 16 | } 17 | 18 | #[test] 19 | fn store_pair() { 20 | let x: CachePadded<(u64, u64)> = CachePadded::new((17, 37)); 21 | assert_eq!(x.0, 17); 22 | assert_eq!(x.1, 37); 23 | } 24 | 25 | #[test] 26 | fn distance() { 27 | let arr = [CachePadded::new(17u8), CachePadded::new(37u8)]; 28 | let a = &*arr[0] as *const u8; 29 | let b = &*arr[1] as *const u8; 30 | let align = mem::align_of::>(); 31 | assert!(align >= 32); 32 | assert_eq!(unsafe { a.add(align) }, b); 33 | } 34 | 35 | #[test] 36 | fn different_sizes() { 37 | CachePadded::new(17u8); 38 | CachePadded::new(17u16); 39 | CachePadded::new(17u32); 40 | CachePadded::new([17u64; 0]); 41 | CachePadded::new([17u64; 1]); 42 | CachePadded::new([17u64; 2]); 43 | CachePadded::new([17u64; 3]); 44 | CachePadded::new([17u64; 4]); 45 | CachePadded::new([17u64; 5]); 46 | CachePadded::new([17u64; 6]); 47 | CachePadded::new([17u64; 7]); 48 | CachePadded::new([17u64; 8]); 49 | } 50 | 51 | #[test] 52 | fn large() { 53 | let a = [17u64; 9]; 54 | let b = CachePadded::new(a); 55 | assert!(mem::size_of_val(&a) <= mem::size_of_val(&b)); 56 | } 57 | 58 | #[test] 59 | fn debug() { 60 | assert_eq!( 61 | format!("{:?}", CachePadded::new(17u64)), 62 | "CachePadded { value: 17 }" 63 | ); 64 | } 65 | 66 | #[test] 67 | fn drops() { 68 | let count = Cell::new(0); 69 | 70 | struct Foo<'a>(&'a Cell); 71 | 72 | impl Drop for Foo<'_> { 73 | fn drop(&mut self) { 74 | self.0.set(self.0.get() + 1); 75 | } 76 | } 77 | 78 | let a = CachePadded::new(Foo(&count)); 79 | let b = CachePadded::new(Foo(&count)); 80 | 81 | assert_eq!(count.get(), 0); 82 | drop(a); 83 | assert_eq!(count.get(), 1); 84 | drop(b); 85 | assert_eq!(count.get(), 2); 86 | } 87 | 88 | #[allow(clippy::clone_on_copy)] // This is intentional. 89 | #[test] 90 | fn clone() { 91 | let a = CachePadded::new(17); 92 | let b = a.clone(); 93 | assert_eq!(*a, *b); 94 | } 95 | 96 | #[test] 97 | fn runs_custom_clone() { 98 | let count = Cell::new(0); 99 | 100 | struct Foo<'a>(&'a Cell); 101 | 102 | impl Clone for Foo<'_> { 103 | fn clone(&self) -> Self { 104 | self.0.set(self.0.get() + 1); 105 | Self(self.0) 106 | } 107 | } 108 | 109 | let a = CachePadded::new(Foo(&count)); 110 | let _ = a.clone(); 111 | 112 | assert_eq!(count.get(), 1); 113 | } 114 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/lockfree.rs: -------------------------------------------------------------------------------- 1 | use lockfree::channel; 2 | 3 | mod message; 4 | 5 | const MESSAGES: usize = 5_000_000; 6 | const THREADS: usize = 4; 7 | 8 | use std::thread; 9 | 10 | fn seq() { 11 | let (mut tx, mut rx) = channel::spsc::create(); 12 | 13 | for i in 0..MESSAGES { 14 | tx.send(message::new(i)).unwrap(); 15 | } 16 | 17 | for _ in 0..MESSAGES { 18 | while rx.recv().is_err() { 19 | thread::yield_now(); 20 | } 21 | } 22 | } 23 | 24 | fn spsc() { 25 | let (mut tx, mut rx) = channel::spsc::create(); 26 | 27 | crossbeam::scope(|scope| { 28 | scope.spawn(|_| { 29 | for i in 0..MESSAGES { 30 | tx.send(message::new(i)).unwrap(); 31 | } 32 | }); 33 | 34 | for _ in 0..MESSAGES { 35 | while rx.recv().is_err() { 36 | thread::yield_now(); 37 | } 38 | } 39 | }) 40 | .unwrap(); 41 | } 42 | 43 | fn mpsc() { 44 | let (tx, mut rx) = channel::mpsc::create(); 45 | 46 | crossbeam::scope(|scope| { 47 | for _ in 0..THREADS { 48 | scope.spawn(|_| { 49 | for i in 0..MESSAGES / THREADS { 50 | tx.send(message::new(i)).unwrap(); 51 | } 52 | }); 53 | } 54 | 55 | for _ in 0..MESSAGES { 56 | while rx.recv().is_err() { 57 | thread::yield_now(); 58 | } 59 | } 60 | }) 61 | .unwrap(); 62 | } 63 | 64 | fn mpmc() { 65 | let (tx, rx) = channel::mpmc::create(); 66 | 67 | crossbeam::scope(|scope| { 68 | for _ in 0..THREADS { 69 | scope.spawn(|_| { 70 | for i in 0..MESSAGES / THREADS { 71 | tx.send(message::new(i)).unwrap(); 72 | } 73 | }); 74 | } 75 | 76 | for _ in 0..THREADS { 77 | scope.spawn(|_| { 78 | for _ in 0..MESSAGES / THREADS { 79 | while rx.recv().is_err() { 80 | thread::yield_now(); 81 | } 82 | } 83 | }); 84 | } 85 | }) 86 | .unwrap(); 87 | } 88 | 89 | fn main() { 90 | macro_rules! run { 91 | ($name:expr, $f:expr) => { 92 | let now = ::std::time::Instant::now(); 93 | $f; 94 | let elapsed = now.elapsed(); 95 | println!( 96 | "{:25} {:15} {:7.3} sec", 97 | $name, 98 | "Rust lockfree", 99 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 100 | ); 101 | }; 102 | } 103 | 104 | run!("unbounded_mpmc", mpmc()); 105 | run!("unbounded_mpsc", mpsc()); 106 | run!("unbounded_seq", seq()); 107 | run!("unbounded_spsc", spsc()); 108 | } 109 | -------------------------------------------------------------------------------- /crossbeam-utils/src/sync/once_lock.rs: -------------------------------------------------------------------------------- 1 | // Based on unstable std::sync::OnceLock. 2 | // 3 | // Source: https://github.com/rust-lang/rust/blob/8e9c93df464b7ada3fc7a1c8ccddd9dcb24ee0a0/library/std/src/sync/once_lock.rs 4 | 5 | use core::cell::UnsafeCell; 6 | use core::mem::MaybeUninit; 7 | use std::sync::Once; 8 | 9 | pub(crate) struct OnceLock { 10 | once: Once, 11 | value: UnsafeCell>, 12 | // Unlike std::sync::OnceLock, we don't need PhantomData here because 13 | // we don't use #[may_dangle]. 14 | } 15 | 16 | unsafe impl Sync for OnceLock {} 17 | unsafe impl Send for OnceLock {} 18 | 19 | impl OnceLock { 20 | /// Creates a new empty cell. 21 | #[must_use] 22 | pub(crate) const fn new() -> Self { 23 | Self { 24 | once: Once::new(), 25 | value: UnsafeCell::new(MaybeUninit::uninit()), 26 | } 27 | } 28 | 29 | /// Gets the contents of the cell, initializing it with `f` if the cell 30 | /// was empty. 31 | /// 32 | /// Many threads may call `get_or_init` concurrently with different 33 | /// initializing functions, but it is guaranteed that only one function 34 | /// will be executed. 35 | /// 36 | /// # Panics 37 | /// 38 | /// If `f` panics, the panic is propagated to the caller, and the cell 39 | /// remains uninitialized. 40 | /// 41 | /// It is an error to reentrantly initialize the cell from `f`. The 42 | /// exact outcome is unspecified. Current implementation deadlocks, but 43 | /// this may be changed to a panic in the future. 44 | pub(crate) fn get_or_init(&self, f: F) -> &T 45 | where 46 | F: FnOnce() -> T, 47 | { 48 | // Fast path check 49 | if self.once.is_completed() { 50 | // SAFETY: The inner value has been initialized 51 | return unsafe { self.get_unchecked() }; 52 | } 53 | self.initialize(f); 54 | 55 | // SAFETY: The inner value has been initialized 56 | unsafe { self.get_unchecked() } 57 | } 58 | 59 | #[cold] 60 | fn initialize(&self, f: F) 61 | where 62 | F: FnOnce() -> T, 63 | { 64 | let slot = self.value.get(); 65 | 66 | self.once.call_once(|| { 67 | let value = f(); 68 | unsafe { slot.write(MaybeUninit::new(value)) } 69 | }); 70 | } 71 | 72 | /// # Safety 73 | /// 74 | /// The value must be initialized 75 | unsafe fn get_unchecked(&self) -> &T { 76 | debug_assert!(self.once.is_completed()); 77 | unsafe { (*self.value.get()).assume_init_ref() } 78 | } 79 | } 80 | 81 | impl Drop for OnceLock { 82 | fn drop(&mut self) { 83 | if self.once.is_completed() { 84 | // SAFETY: The inner value has been initialized 85 | // assume_init_drop requires Rust 1.60 86 | // unsafe { (*self.value.get()).assume_init_drop() }; 87 | unsafe { self.value.get().cast::().drop_in_place() }; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /crossbeam-channel/tests/iter.rs: -------------------------------------------------------------------------------- 1 | //! Tests for iteration over receivers. 2 | 3 | use crossbeam_channel::unbounded; 4 | use crossbeam_utils::thread::scope; 5 | 6 | #[test] 7 | fn nested_recv_iter() { 8 | let (s, r) = unbounded::(); 9 | let (total_s, total_r) = unbounded::(); 10 | 11 | scope(|scope| { 12 | scope.spawn(move |_| { 13 | let mut acc = 0; 14 | for x in r.iter() { 15 | acc += x; 16 | } 17 | total_s.send(acc).unwrap(); 18 | }); 19 | 20 | s.send(3).unwrap(); 21 | s.send(1).unwrap(); 22 | s.send(2).unwrap(); 23 | drop(s); 24 | assert_eq!(total_r.recv().unwrap(), 6); 25 | }) 26 | .unwrap(); 27 | } 28 | 29 | #[test] 30 | fn recv_iter_break() { 31 | let (s, r) = unbounded::(); 32 | let (count_s, count_r) = unbounded(); 33 | 34 | scope(|scope| { 35 | scope.spawn(move |_| { 36 | let mut count = 0; 37 | for x in r.iter() { 38 | if count >= 3 { 39 | break; 40 | } else { 41 | count += x; 42 | } 43 | } 44 | count_s.send(count).unwrap(); 45 | }); 46 | 47 | s.send(2).unwrap(); 48 | s.send(2).unwrap(); 49 | s.send(2).unwrap(); 50 | let _ = s.send(2); 51 | drop(s); 52 | assert_eq!(count_r.recv().unwrap(), 4); 53 | }) 54 | .unwrap(); 55 | } 56 | 57 | #[test] 58 | fn recv_try_iter() { 59 | let (request_s, request_r) = unbounded(); 60 | let (response_s, response_r) = unbounded(); 61 | 62 | scope(|scope| { 63 | scope.spawn(move |_| { 64 | let mut count = 0; 65 | loop { 66 | for x in response_r.try_iter() { 67 | count += x; 68 | if count == 6 { 69 | return; 70 | } 71 | } 72 | request_s.send(()).unwrap(); 73 | } 74 | }); 75 | 76 | for _ in request_r.iter() { 77 | if response_s.send(2).is_err() { 78 | break; 79 | } 80 | } 81 | }) 82 | .unwrap(); 83 | } 84 | 85 | #[test] 86 | fn recv_into_iter_owned() { 87 | let mut iter = { 88 | let (s, r) = unbounded::(); 89 | s.send(1).unwrap(); 90 | s.send(2).unwrap(); 91 | r.into_iter() 92 | }; 93 | 94 | assert_eq!(iter.next().unwrap(), 1); 95 | assert_eq!(iter.next().unwrap(), 2); 96 | assert!(iter.next().is_none()); 97 | } 98 | 99 | #[test] 100 | fn recv_into_iter_borrowed() { 101 | let (s, r) = unbounded::(); 102 | s.send(1).unwrap(); 103 | s.send(2).unwrap(); 104 | drop(s); 105 | 106 | let mut iter = (&r).into_iter(); 107 | assert_eq!(iter.next().unwrap(), 1); 108 | assert_eq!(iter.next().unwrap(), 2); 109 | assert!(iter.next().is_none()); 110 | } 111 | -------------------------------------------------------------------------------- /crossbeam-channel/tests/same_channel.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::redundant_clone)] 2 | 3 | use std::time::Duration; 4 | 5 | use crossbeam_channel::{after, bounded, never, tick, unbounded}; 6 | 7 | fn ms(ms: u64) -> Duration { 8 | Duration::from_millis(ms) 9 | } 10 | 11 | #[test] 12 | fn after_same_channel() { 13 | let r = after(ms(50)); 14 | 15 | let r2 = r.clone(); 16 | assert!(r.same_channel(&r2)); 17 | 18 | let r3 = after(ms(50)); 19 | assert!(!r.same_channel(&r3)); 20 | assert!(!r2.same_channel(&r3)); 21 | 22 | let r4 = after(ms(100)); 23 | assert!(!r.same_channel(&r4)); 24 | assert!(!r2.same_channel(&r4)); 25 | } 26 | 27 | #[test] 28 | fn array_same_channel() { 29 | let (s, r) = bounded::(1); 30 | 31 | let s2 = s.clone(); 32 | assert!(s.same_channel(&s2)); 33 | 34 | let r2 = r.clone(); 35 | assert!(r.same_channel(&r2)); 36 | 37 | let (s3, r3) = bounded::(1); 38 | assert!(!s.same_channel(&s3)); 39 | assert!(!s2.same_channel(&s3)); 40 | assert!(!r.same_channel(&r3)); 41 | assert!(!r2.same_channel(&r3)); 42 | } 43 | 44 | #[test] 45 | fn list_same_channel() { 46 | let (s, r) = unbounded::(); 47 | 48 | let s2 = s.clone(); 49 | assert!(s.same_channel(&s2)); 50 | 51 | let r2 = r.clone(); 52 | assert!(r.same_channel(&r2)); 53 | 54 | let (s3, r3) = unbounded::(); 55 | assert!(!s.same_channel(&s3)); 56 | assert!(!s2.same_channel(&s3)); 57 | assert!(!r.same_channel(&r3)); 58 | assert!(!r2.same_channel(&r3)); 59 | } 60 | 61 | #[test] 62 | fn never_same_channel() { 63 | let r = never::(); 64 | 65 | let r2 = r.clone(); 66 | assert!(r.same_channel(&r2)); 67 | 68 | // Never channel are always equal to one another. 69 | let r3 = never::(); 70 | assert!(r.same_channel(&r3)); 71 | assert!(r2.same_channel(&r3)); 72 | } 73 | 74 | #[test] 75 | fn tick_same_channel() { 76 | let r = tick(ms(50)); 77 | 78 | let r2 = r.clone(); 79 | assert!(r.same_channel(&r2)); 80 | 81 | let r3 = tick(ms(50)); 82 | assert!(!r.same_channel(&r3)); 83 | assert!(!r2.same_channel(&r3)); 84 | 85 | let r4 = tick(ms(100)); 86 | assert!(!r.same_channel(&r4)); 87 | assert!(!r2.same_channel(&r4)); 88 | } 89 | 90 | #[test] 91 | fn zero_same_channel() { 92 | let (s, r) = bounded::(0); 93 | 94 | let s2 = s.clone(); 95 | assert!(s.same_channel(&s2)); 96 | 97 | let r2 = r.clone(); 98 | assert!(r.same_channel(&r2)); 99 | 100 | let (s3, r3) = bounded::(0); 101 | assert!(!s.same_channel(&s3)); 102 | assert!(!s2.same_channel(&s3)); 103 | assert!(!r.same_channel(&r3)); 104 | assert!(!r2.same_channel(&r3)); 105 | } 106 | 107 | #[test] 108 | fn different_flavors_same_channel() { 109 | let (s1, r1) = bounded::(0); 110 | let (s2, r2) = unbounded::(); 111 | 112 | assert!(!s1.same_channel(&s2)); 113 | assert!(!r1.same_channel(&r2)); 114 | } 115 | -------------------------------------------------------------------------------- /crossbeam-channel/src/flavors/never.rs: -------------------------------------------------------------------------------- 1 | //! Channel that never delivers messages. 2 | //! 3 | //! Messages cannot be sent into this kind of channel. 4 | 5 | use std::marker::PhantomData; 6 | use std::time::Instant; 7 | 8 | use crate::context::Context; 9 | use crate::err::{RecvTimeoutError, TryRecvError}; 10 | use crate::select::{Operation, SelectHandle, Token}; 11 | use crate::utils; 12 | 13 | /// This flavor doesn't need a token. 14 | pub(crate) type NeverToken = (); 15 | 16 | /// Channel that never delivers messages. 17 | pub(crate) struct Channel { 18 | _marker: PhantomData, 19 | } 20 | 21 | impl Channel { 22 | /// Creates a channel that never delivers messages. 23 | #[inline] 24 | pub(crate) fn new() -> Self { 25 | Self { 26 | _marker: PhantomData, 27 | } 28 | } 29 | 30 | /// Attempts to receive a message without blocking. 31 | #[inline] 32 | pub(crate) fn try_recv(&self) -> Result { 33 | Err(TryRecvError::Empty) 34 | } 35 | 36 | /// Receives a message from the channel. 37 | #[inline] 38 | pub(crate) fn recv(&self, deadline: Option) -> Result { 39 | utils::sleep_until(deadline); 40 | Err(RecvTimeoutError::Timeout) 41 | } 42 | 43 | /// Reads a message from the channel. 44 | #[inline] 45 | pub(crate) unsafe fn read(&self, _token: &mut Token) -> Result { 46 | Err(()) 47 | } 48 | 49 | /// Returns `true` if the channel is empty. 50 | #[inline] 51 | pub(crate) fn is_empty(&self) -> bool { 52 | true 53 | } 54 | 55 | /// Returns `true` if the channel is full. 56 | #[inline] 57 | pub(crate) fn is_full(&self) -> bool { 58 | true 59 | } 60 | 61 | /// Returns the number of messages in the channel. 62 | #[inline] 63 | pub(crate) fn len(&self) -> usize { 64 | 0 65 | } 66 | 67 | /// Returns the capacity of the channel. 68 | #[inline] 69 | pub(crate) fn capacity(&self) -> Option { 70 | Some(0) 71 | } 72 | } 73 | 74 | impl SelectHandle for Channel { 75 | #[inline] 76 | fn try_select(&self, _token: &mut Token) -> bool { 77 | false 78 | } 79 | 80 | #[inline] 81 | fn deadline(&self) -> Option { 82 | None 83 | } 84 | 85 | #[inline] 86 | fn register(&self, _oper: Operation, _cx: &Context) -> bool { 87 | self.is_ready() 88 | } 89 | 90 | #[inline] 91 | fn unregister(&self, _oper: Operation) {} 92 | 93 | #[inline] 94 | fn accept(&self, token: &mut Token, _cx: &Context) -> bool { 95 | self.try_select(token) 96 | } 97 | 98 | #[inline] 99 | fn is_ready(&self) -> bool { 100 | false 101 | } 102 | 103 | #[inline] 104 | fn watch(&self, _oper: Operation, _cx: &Context) -> bool { 105 | self.is_ready() 106 | } 107 | 108 | #[inline] 109 | fn unwatch(&self, _oper: Operation) {} 110 | } 111 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crossbeam" 3 | # When publishing a new version: 4 | # - Update CHANGELOG.md 5 | # - Update README.md (when increasing major or minor version) 6 | # - Run './tools/publish.sh crossbeam ' 7 | version = "0.8.4" 8 | edition = "2021" 9 | rust-version = "1.61" 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/crossbeam-rs/crossbeam" 12 | homepage = "https://github.com/crossbeam-rs/crossbeam" 13 | description = "Tools for concurrent programming" 14 | keywords = ["atomic", "garbage", "non-blocking", "lock-free", "rcu"] 15 | categories = ["concurrency", "memory-management", "data-structures", "no-std"] 16 | exclude = ["/.*", "/ci", "/tools"] 17 | 18 | [features] 19 | default = ["std"] 20 | 21 | # Enable to use APIs that require `std`. 22 | # This is enabled by default. 23 | std = [ 24 | "alloc", 25 | "crossbeam-channel/std", 26 | "crossbeam-deque/std", 27 | "crossbeam-epoch/std", 28 | "crossbeam-queue/std", 29 | "crossbeam-utils/std", 30 | ] 31 | 32 | # Enable to use APIs that require `alloc`. 33 | # This is enabled by default and also enabled if the `std` feature is enabled. 34 | alloc = ["crossbeam-epoch/alloc", "crossbeam-queue/alloc"] 35 | 36 | [dependencies] 37 | crossbeam-channel = { version = "0.5.10", path = "crossbeam-channel", default-features = false, optional = true } 38 | crossbeam-deque = { version = "0.8.4", path = "crossbeam-deque", default-features = false, optional = true } 39 | crossbeam-epoch = { version = "0.9.17", path = "crossbeam-epoch", default-features = false, optional = true } 40 | crossbeam-queue = { version = "0.3.10", path = "crossbeam-queue", default-features = false, optional = true } 41 | crossbeam-utils = { version = "0.8.18", path = "crossbeam-utils", default-features = false, features = ["atomic"] } 42 | 43 | [dev-dependencies] 44 | 45 | [lints] 46 | workspace = true 47 | 48 | [workspace] 49 | resolver = "2" 50 | members = [ 51 | ".", 52 | "crossbeam-channel", 53 | "crossbeam-channel/benchmarks", 54 | "crossbeam-deque", 55 | "crossbeam-epoch", 56 | "crossbeam-queue", 57 | "crossbeam-skiplist", 58 | "crossbeam-utils", 59 | ] 60 | 61 | [workspace.lints.rust] 62 | missing_debug_implementations = "warn" 63 | rust_2018_idioms = "warn" 64 | single_use_lifetimes = "warn" 65 | unexpected_cfgs = { level = "warn", check-cfg = [ 66 | 'cfg(crossbeam_loom)', 67 | 'cfg(crossbeam_sanitize)', 68 | ] } 69 | unreachable_pub = "warn" 70 | # unsafe_op_in_unsafe_fn = "warn" # Set at crate-level instead since https://github.com/rust-lang/rust/pull/100081 is not available on MSRV 71 | [workspace.lints.clippy] 72 | # Suppress buggy or noisy clippy lints 73 | declare_interior_mutable_const = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7665 74 | incompatible_msrv = { level = "allow", priority = 1 } # buggy: doesn't consider cfg, https://github.com/rust-lang/rust-clippy/issues/12280, https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 75 | lint_groups_priority = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12920 76 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/segqueue.rs: -------------------------------------------------------------------------------- 1 | use crossbeam::queue::SegQueue; 2 | use std::thread; 3 | 4 | mod message; 5 | 6 | const MESSAGES: usize = 5_000_000; 7 | const THREADS: usize = 4; 8 | 9 | fn seq() { 10 | let q = SegQueue::new(); 11 | 12 | for i in 0..MESSAGES { 13 | q.push(message::new(i)); 14 | } 15 | 16 | for _ in 0..MESSAGES { 17 | q.pop().unwrap(); 18 | } 19 | } 20 | 21 | fn spsc() { 22 | let q = SegQueue::new(); 23 | 24 | crossbeam::scope(|scope| { 25 | scope.spawn(|_| { 26 | for i in 0..MESSAGES { 27 | q.push(message::new(i)); 28 | } 29 | }); 30 | 31 | for _ in 0..MESSAGES { 32 | loop { 33 | if q.pop().is_none() { 34 | thread::yield_now(); 35 | } else { 36 | break; 37 | } 38 | } 39 | } 40 | }) 41 | .unwrap(); 42 | } 43 | 44 | fn mpsc() { 45 | let q = SegQueue::new(); 46 | 47 | crossbeam::scope(|scope| { 48 | for _ in 0..THREADS { 49 | scope.spawn(|_| { 50 | for i in 0..MESSAGES / THREADS { 51 | q.push(message::new(i)); 52 | } 53 | }); 54 | } 55 | 56 | for _ in 0..MESSAGES { 57 | loop { 58 | if q.pop().is_none() { 59 | thread::yield_now(); 60 | } else { 61 | break; 62 | } 63 | } 64 | } 65 | }) 66 | .unwrap(); 67 | } 68 | 69 | fn mpmc() { 70 | let q = SegQueue::new(); 71 | 72 | crossbeam::scope(|scope| { 73 | for _ in 0..THREADS { 74 | scope.spawn(|_| { 75 | for i in 0..MESSAGES / THREADS { 76 | q.push(message::new(i)); 77 | } 78 | }); 79 | } 80 | 81 | for _ in 0..THREADS { 82 | scope.spawn(|_| { 83 | for _ in 0..MESSAGES / THREADS { 84 | loop { 85 | if q.pop().is_none() { 86 | thread::yield_now(); 87 | } else { 88 | break; 89 | } 90 | } 91 | } 92 | }); 93 | } 94 | }) 95 | .unwrap(); 96 | } 97 | 98 | fn main() { 99 | macro_rules! run { 100 | ($name:expr, $f:expr) => { 101 | let now = ::std::time::Instant::now(); 102 | $f; 103 | let elapsed = now.elapsed(); 104 | println!( 105 | "{:25} {:15} {:7.3} sec", 106 | $name, 107 | "Rust segqueue", 108 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 109 | ); 110 | }; 111 | } 112 | 113 | run!("unbounded_mpmc", mpmc()); 114 | run!("unbounded_mpsc", mpsc()); 115 | run!("unbounded_seq", seq()); 116 | run!("unbounded_spsc", spsc()); 117 | } 118 | -------------------------------------------------------------------------------- /crossbeam-utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Miscellaneous tools for concurrent programming. 2 | //! 3 | //! ## Atomics 4 | //! 5 | //! * [`AtomicCell`], a thread-safe mutable memory location. 6 | //! * [`AtomicConsume`], for reading from primitive atomic types with "consume" ordering. 7 | //! 8 | //! ## Thread synchronization 9 | //! 10 | //! * [`Parker`], a thread parking primitive. 11 | //! * [`ShardedLock`], a sharded reader-writer lock with fast concurrent reads. 12 | //! * [`WaitGroup`], for synchronizing the beginning or end of some computation. 13 | //! 14 | //! ## Utilities 15 | //! 16 | //! * [`Backoff`], for exponential backoff in spin loops. 17 | //! * [`CachePadded`], for padding and aligning a value to the length of a cache line. 18 | //! * [`scope`], for spawning threads that borrow local variables from the stack. 19 | //! 20 | //! [`AtomicCell`]: atomic::AtomicCell 21 | //! [`AtomicConsume`]: atomic::AtomicConsume 22 | //! [`Parker`]: sync::Parker 23 | //! [`ShardedLock`]: sync::ShardedLock 24 | //! [`WaitGroup`]: sync::WaitGroup 25 | //! [`scope`]: thread::scope 26 | 27 | #![no_std] 28 | #![doc(test( 29 | no_crate_inject, 30 | attr( 31 | deny(warnings, rust_2018_idioms, single_use_lifetimes), 32 | allow(dead_code, unused_assignments, unused_variables) 33 | ) 34 | ))] 35 | #![warn(missing_docs, unsafe_op_in_unsafe_fn)] 36 | #![cfg_attr(docsrs, feature(doc_cfg))] 37 | 38 | #[cfg(feature = "std")] 39 | extern crate std; 40 | 41 | #[cfg(crossbeam_loom)] 42 | #[allow(unused_imports)] 43 | mod primitive { 44 | pub(crate) mod hint { 45 | pub(crate) use loom::hint::spin_loop; 46 | } 47 | pub(crate) mod sync { 48 | pub(crate) mod atomic { 49 | pub(crate) use loom::sync::atomic::{ 50 | AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, 51 | AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering, 52 | }; 53 | 54 | // FIXME: loom does not support compiler_fence at the moment. 55 | // https://github.com/tokio-rs/loom/issues/117 56 | // we use fence as a stand-in for compiler_fence for the time being. 57 | // this may miss some races since fence is stronger than compiler_fence, 58 | // but it's the best we can do for the time being. 59 | pub(crate) use loom::sync::atomic::fence as compiler_fence; 60 | } 61 | pub(crate) use loom::sync::{Arc, Condvar, Mutex}; 62 | } 63 | } 64 | #[cfg(not(crossbeam_loom))] 65 | #[allow(unused_imports)] 66 | mod primitive { 67 | pub(crate) mod hint { 68 | pub(crate) use core::hint::spin_loop; 69 | } 70 | pub(crate) mod sync { 71 | pub(crate) use core::sync::atomic; 72 | #[cfg(feature = "std")] 73 | pub(crate) use std::sync::{Arc, Condvar, Mutex}; 74 | } 75 | } 76 | 77 | #[cfg(feature = "atomic")] 78 | #[cfg_attr(docsrs, doc(cfg(feature = "atomic")))] 79 | pub mod atomic; 80 | 81 | mod cache_padded; 82 | pub use crate::cache_padded::CachePadded; 83 | 84 | mod backoff; 85 | pub use crate::backoff::Backoff; 86 | 87 | #[cfg(feature = "std")] 88 | pub mod sync; 89 | 90 | #[cfg(feature = "std")] 91 | #[cfg(not(crossbeam_loom))] 92 | pub mod thread; 93 | -------------------------------------------------------------------------------- /crossbeam-utils/README.md: -------------------------------------------------------------------------------- 1 | # Crossbeam Utils 2 | 3 | [![Build Status](https://github.com/crossbeam-rs/crossbeam/workflows/CI/badge.svg)]( 4 | https://github.com/crossbeam-rs/crossbeam/actions) 5 | [![License](https://img.shields.io/badge/license-MIT_OR_Apache--2.0-blue.svg)]( 6 | https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils#license) 7 | [![Cargo](https://img.shields.io/crates/v/crossbeam-utils.svg)]( 8 | https://crates.io/crates/crossbeam-utils) 9 | [![Documentation](https://docs.rs/crossbeam-utils/badge.svg)]( 10 | https://docs.rs/crossbeam-utils) 11 | [![Rust 1.60+](https://img.shields.io/badge/rust-1.60+-lightgray.svg)]( 12 | https://www.rust-lang.org) 13 | [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) 14 | 15 | This crate provides miscellaneous tools for concurrent programming: 16 | 17 | #### Atomics 18 | 19 | * [`AtomicCell`], a thread-safe mutable memory location.(no_std) 20 | * [`AtomicConsume`], for reading from primitive atomic types with "consume" ordering.(no_std) 21 | 22 | #### Thread synchronization 23 | 24 | * [`Parker`], a thread parking primitive. 25 | * [`ShardedLock`], a sharded reader-writer lock with fast concurrent reads. 26 | * [`WaitGroup`], for synchronizing the beginning or end of some computation. 27 | 28 | #### Utilities 29 | 30 | * [`Backoff`], for exponential backoff in spin loops.(no_std) 31 | * [`CachePadded`], for padding and aligning a value to the length of a cache line.(no_std) 32 | * [`scope`], for spawning threads that borrow local variables from the stack. 33 | 34 | *Features marked with (no_std) can be used in `no_std` environments.*
35 | 36 | [`AtomicCell`]: https://docs.rs/crossbeam-utils/latest/crossbeam_utils/atomic/struct.AtomicCell.html 37 | [`AtomicConsume`]: https://docs.rs/crossbeam-utils/latest/crossbeam_utils/atomic/trait.AtomicConsume.html 38 | [`Parker`]: https://docs.rs/crossbeam-utils/latest/crossbeam_utils/sync/struct.Parker.html 39 | [`ShardedLock`]: https://docs.rs/crossbeam-utils/latest/crossbeam_utils/sync/struct.ShardedLock.html 40 | [`WaitGroup`]: https://docs.rs/crossbeam-utils/latest/crossbeam_utils/sync/struct.WaitGroup.html 41 | [`Backoff`]: https://docs.rs/crossbeam-utils/latest/crossbeam_utils/struct.Backoff.html 42 | [`CachePadded`]: https://docs.rs/crossbeam-utils/latest/crossbeam_utils/struct.CachePadded.html 43 | [`scope`]: https://docs.rs/crossbeam-utils/latest/crossbeam_utils/thread/fn.scope.html 44 | 45 | ## Usage 46 | 47 | Add this to your `Cargo.toml`: 48 | 49 | ```toml 50 | [dependencies] 51 | crossbeam-utils = "0.8" 52 | ``` 53 | 54 | ## Compatibility 55 | 56 | Crossbeam Utils supports stable Rust releases going back at least six months, 57 | and every time the minimum supported Rust version is increased, a new minor 58 | version is released. Currently, the minimum supported Rust version is 1.56. 59 | 60 | ## License 61 | 62 | Licensed under either of 63 | 64 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 65 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 66 | 67 | at your option. 68 | 69 | #### Contribution 70 | 71 | Unless you explicitly state otherwise, any contribution intentionally submitted 72 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 73 | dual licensed as above, without any additional terms or conditions. 74 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/atomicringqueue.rs: -------------------------------------------------------------------------------- 1 | use atomicring::AtomicRingQueue; 2 | use std::thread; 3 | 4 | mod message; 5 | 6 | const MESSAGES: usize = 5_000_000; 7 | const THREADS: usize = 4; 8 | 9 | fn seq(cap: usize) { 10 | let q = AtomicRingQueue::with_capacity(cap); 11 | 12 | for i in 0..MESSAGES { 13 | loop { 14 | if q.try_push(message::new(i)).is_ok() { 15 | break; 16 | } else { 17 | thread::yield_now(); 18 | } 19 | } 20 | } 21 | 22 | for _ in 0..MESSAGES { 23 | q.pop(); 24 | } 25 | } 26 | 27 | fn spsc(cap: usize) { 28 | let q = AtomicRingQueue::with_capacity(cap); 29 | 30 | crossbeam::scope(|scope| { 31 | scope.spawn(|_| { 32 | for i in 0..MESSAGES { 33 | loop { 34 | if q.try_push(message::new(i)).is_ok() { 35 | break; 36 | } else { 37 | thread::yield_now(); 38 | } 39 | } 40 | } 41 | }); 42 | 43 | for _ in 0..MESSAGES { 44 | q.pop(); 45 | } 46 | }) 47 | .unwrap(); 48 | } 49 | 50 | fn mpsc(cap: usize) { 51 | let q = AtomicRingQueue::with_capacity(cap); 52 | 53 | crossbeam::scope(|scope| { 54 | for _ in 0..THREADS { 55 | scope.spawn(|_| { 56 | for i in 0..MESSAGES / THREADS { 57 | loop { 58 | if q.try_push(message::new(i)).is_ok() { 59 | break; 60 | } else { 61 | thread::yield_now(); 62 | } 63 | } 64 | } 65 | }); 66 | } 67 | 68 | for _ in 0..MESSAGES { 69 | q.pop(); 70 | } 71 | }) 72 | .unwrap(); 73 | } 74 | 75 | fn mpmc(cap: usize) { 76 | let q = AtomicRingQueue::with_capacity(cap); 77 | 78 | crossbeam::scope(|scope| { 79 | for _ in 0..THREADS { 80 | scope.spawn(|_| { 81 | for i in 0..MESSAGES / THREADS { 82 | loop { 83 | if q.try_push(message::new(i)).is_ok() { 84 | break; 85 | } else { 86 | thread::yield_now(); 87 | } 88 | } 89 | } 90 | }); 91 | } 92 | 93 | for _ in 0..THREADS { 94 | scope.spawn(|_| { 95 | for _ in 0..MESSAGES / THREADS { 96 | q.pop(); 97 | } 98 | }); 99 | } 100 | }) 101 | .unwrap(); 102 | } 103 | 104 | fn main() { 105 | macro_rules! run { 106 | ($name:expr, $f:expr) => { 107 | let now = ::std::time::Instant::now(); 108 | $f; 109 | let elapsed = now.elapsed(); 110 | println!( 111 | "{:25} {:15} {:7.3} sec", 112 | $name, 113 | "Rust atomicringqueue", 114 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 115 | ); 116 | }; 117 | } 118 | 119 | run!("bounded_mpmc", mpmc(MESSAGES)); 120 | run!("bounded_mpsc", mpsc(MESSAGES)); 121 | run!("bounded_seq", seq(MESSAGES)); 122 | run!("bounded_spsc", spsc(MESSAGES)); 123 | } 124 | -------------------------------------------------------------------------------- /crossbeam-utils/src/atomic/seq_lock.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::sync::atomic::{self, AtomicUsize, Ordering}; 3 | 4 | use crate::Backoff; 5 | 6 | /// A simple stamped lock. 7 | pub(crate) struct SeqLock { 8 | /// The current state of the lock. 9 | /// 10 | /// All bits except the least significant one hold the current stamp. When locked, the state 11 | /// equals 1 and doesn't contain a valid stamp. 12 | state: AtomicUsize, 13 | } 14 | 15 | impl SeqLock { 16 | pub(crate) const fn new() -> Self { 17 | Self { 18 | state: AtomicUsize::new(0), 19 | } 20 | } 21 | 22 | /// If not locked, returns the current stamp. 23 | /// 24 | /// This method should be called before optimistic reads. 25 | #[inline] 26 | pub(crate) fn optimistic_read(&self) -> Option { 27 | let state = self.state.load(Ordering::Acquire); 28 | if state == 1 { 29 | None 30 | } else { 31 | Some(state) 32 | } 33 | } 34 | 35 | /// Returns `true` if the current stamp is equal to `stamp`. 36 | /// 37 | /// This method should be called after optimistic reads to check whether they are valid. The 38 | /// argument `stamp` should correspond to the one returned by method `optimistic_read`. 39 | #[inline] 40 | pub(crate) fn validate_read(&self, stamp: usize) -> bool { 41 | atomic::fence(Ordering::Acquire); 42 | self.state.load(Ordering::Relaxed) == stamp 43 | } 44 | 45 | /// Grabs the lock for writing. 46 | #[inline] 47 | pub(crate) fn write(&'static self) -> SeqLockWriteGuard { 48 | let backoff = Backoff::new(); 49 | loop { 50 | let previous = self.state.swap(1, Ordering::Acquire); 51 | 52 | if previous != 1 { 53 | atomic::fence(Ordering::Release); 54 | 55 | return SeqLockWriteGuard { 56 | lock: self, 57 | state: previous, 58 | }; 59 | } 60 | 61 | backoff.snooze(); 62 | } 63 | } 64 | } 65 | 66 | /// An RAII guard that releases the lock and increments the stamp when dropped. 67 | pub(crate) struct SeqLockWriteGuard { 68 | /// The parent lock. 69 | lock: &'static SeqLock, 70 | 71 | /// The stamp before locking. 72 | state: usize, 73 | } 74 | 75 | impl SeqLockWriteGuard { 76 | /// Releases the lock without incrementing the stamp. 77 | #[inline] 78 | pub(crate) fn abort(self) { 79 | self.lock.state.store(self.state, Ordering::Release); 80 | 81 | // We specifically don't want to call drop(), since that's 82 | // what increments the stamp. 83 | mem::forget(self); 84 | } 85 | } 86 | 87 | impl Drop for SeqLockWriteGuard { 88 | #[inline] 89 | fn drop(&mut self) { 90 | // Release the lock and increment the stamp. 91 | self.lock 92 | .state 93 | .store(self.state.wrapping_add(2), Ordering::Release); 94 | } 95 | } 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | use super::SeqLock; 100 | 101 | #[test] 102 | fn test_abort() { 103 | static LK: SeqLock = SeqLock::new(); 104 | let before = LK.optimistic_read().unwrap(); 105 | { 106 | let guard = LK.write(); 107 | guard.abort(); 108 | } 109 | let after = LK.optimistic_read().unwrap(); 110 | assert_eq!(before, after, "aborted write does not update the stamp"); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 0.8.4 2 | 3 | - Remove dependency on `cfg-if`. (#1072) 4 | 5 | # Version 0.8.3 6 | 7 | - Bump the minimum supported Rust version to 1.61. (#1037) 8 | 9 | # Version 0.8.2 10 | 11 | - Bump the minimum supported Rust version to 1.38. (#877) 12 | 13 | # Version 0.8.1 14 | 15 | - Support targets that do not have atomic CAS on stable Rust (#698) 16 | 17 | # Version 0.8.0 18 | 19 | - Bump the minimum supported Rust version to 1.36. 20 | - Bump `crossbeam-channel` to `0.5`. 21 | - Bump `crossbeam-deque` to `0.8`. 22 | - Bump `crossbeam-epoch` to `0.9`. 23 | - Bump `crossbeam-queue` to `0.3`. 24 | - Bump `crossbeam-utils` to `0.8`. 25 | 26 | # Version 0.7.3 27 | 28 | - Fix breakage with nightly feature due to rust-lang/rust#65214. 29 | - Bump `crossbeam-channel` to `0.4`. 30 | - Bump `crossbeam-epoch` to `0.8`. 31 | - Bump `crossbeam-queue` to `0.2`. 32 | - Bump `crossbeam-utils` to `0.7`. 33 | 34 | # Version 0.7.2 35 | 36 | - Bump `crossbeam-channel` to `0.3.9`. 37 | - Bump `crossbeam-epoch` to `0.7.2`. 38 | - Bump `crossbeam-utils` to `0.6.6`. 39 | 40 | # Version 0.7.1 41 | 42 | - Bump `crossbeam-utils` to `0.6.5`. 43 | 44 | # Version 0.7.0 45 | 46 | - Remove `ArcCell`, `MsQueue`, and `TreiberStack`. 47 | - Change the interface of `ShardedLock` to match `RwLock`. 48 | - Add `SegQueue::len()`. 49 | - Rename `SegQueue::try_pop()` to `SegQueue::pop()`. 50 | - Change the return type of `SegQueue::pop()` to `Result`. 51 | - Introduce `ArrayQueue`. 52 | - Update dependencies. 53 | 54 | # Version 0.6.0 55 | 56 | - Update dependencies. 57 | 58 | # Version 0.5.0 59 | 60 | - Update `crossbeam-channel` to 0.3. 61 | - Update `crossbeam-utils` to 0.6. 62 | - Add `AtomicCell`, `SharedLock`, and `WaitGroup`. 63 | 64 | # Version 0.4.1 65 | 66 | - Fix a double-free bug in `MsQueue` and `SegQueue`. 67 | 68 | # Version 0.4 69 | 70 | - Switch to the new implementation of epoch-based reclamation in 71 | [`crossbeam-epoch`](https://github.com/crossbeam-rs/crossbeam-epoch), fixing numerous bugs in the 72 | old implementation. Its API is changed in a backward-incompatible way. 73 | - Switch to the new implementation of `CachePadded` and scoped thread in 74 | [`crossbeam-utils`](https://github.com/crossbeam-rs/crossbeam-utils). The scoped thread API is 75 | changed in a backward-incompatible way. 76 | - Switch to the new implementation of Chase-Lev deque in 77 | [`crossbeam-deque`](https://github.com/crossbeam-rs/crossbeam-deque). Its API is changed in a 78 | backward-incompatible way. 79 | - Export channel implemented in 80 | [`crossbeam-channel`](https://github.com/crossbeam-rs/crossbeam-channel). 81 | - Remove `AtomicOption`. 82 | - Implement `Default` and `From` traits. 83 | 84 | # Version 0.3 85 | 86 | - Introduced `ScopedThreadBuilder` with the ability to name threads and set stack size 87 | - `Worker` methods in the Chase-Lev deque don't require mutable access anymore 88 | - Fixed a bug when unblocking `pop()` in `MsQueue` 89 | - Implemented `Drop` for `MsQueue`, `SegQueue`, and `TreiberStack` 90 | - Implemented `Default` for `TreiberStack` 91 | - Added `is_empty` to `SegQueue` 92 | - Renamed `mem::epoch` to `epoch` 93 | - Other bug fixes 94 | 95 | # Version 0.2 96 | 97 | - Changed existing non-blocking `pop` methods to `try_pop` 98 | - Added blocking `pop` support to Michael-Scott queue 99 | - Added Chase-Lev work-stealing deque 100 | 101 | # Version 0.1 102 | 103 | - Added [epoch-based memory management](http://aturon.github.io/blog/2015/08/27/epoch/) 104 | - Added Michael-Scott queue 105 | - Added Segmented array queue 106 | -------------------------------------------------------------------------------- /crossbeam-utils/benches/atomic_cell.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use std::sync::Barrier; 6 | 7 | use crossbeam_utils::atomic::AtomicCell; 8 | use crossbeam_utils::thread; 9 | 10 | #[bench] 11 | fn load_u8(b: &mut test::Bencher) { 12 | let a = AtomicCell::new(0u8); 13 | let mut sum = 0; 14 | b.iter(|| sum += a.load()); 15 | test::black_box(sum); 16 | } 17 | 18 | #[bench] 19 | fn store_u8(b: &mut test::Bencher) { 20 | let a = AtomicCell::new(0u8); 21 | b.iter(|| a.store(1)); 22 | } 23 | 24 | #[bench] 25 | fn fetch_add_u8(b: &mut test::Bencher) { 26 | let a = AtomicCell::new(0u8); 27 | b.iter(|| a.fetch_add(1)); 28 | } 29 | 30 | #[bench] 31 | fn compare_exchange_u8(b: &mut test::Bencher) { 32 | let a = AtomicCell::new(0u8); 33 | let mut i = 0; 34 | b.iter(|| { 35 | let _ = a.compare_exchange(i, i.wrapping_add(1)); 36 | i = i.wrapping_add(1); 37 | }); 38 | } 39 | 40 | #[bench] 41 | fn concurrent_load_u8(b: &mut test::Bencher) { 42 | const THREADS: usize = 2; 43 | const STEPS: usize = 1_000_000; 44 | 45 | let start = Barrier::new(THREADS + 1); 46 | let end = Barrier::new(THREADS + 1); 47 | let exit = AtomicCell::new(false); 48 | 49 | let a = AtomicCell::new(0u8); 50 | 51 | thread::scope(|scope| { 52 | for _ in 0..THREADS { 53 | scope.spawn(|_| loop { 54 | start.wait(); 55 | 56 | let mut sum = 0; 57 | for _ in 0..STEPS { 58 | sum += a.load(); 59 | } 60 | test::black_box(sum); 61 | 62 | end.wait(); 63 | if exit.load() { 64 | break; 65 | } 66 | }); 67 | } 68 | 69 | start.wait(); 70 | end.wait(); 71 | 72 | b.iter(|| { 73 | start.wait(); 74 | end.wait(); 75 | }); 76 | 77 | start.wait(); 78 | exit.store(true); 79 | end.wait(); 80 | }) 81 | .unwrap(); 82 | } 83 | 84 | #[bench] 85 | fn load_usize(b: &mut test::Bencher) { 86 | let a = AtomicCell::new(0usize); 87 | let mut sum = 0; 88 | b.iter(|| sum += a.load()); 89 | test::black_box(sum); 90 | } 91 | 92 | #[bench] 93 | fn store_usize(b: &mut test::Bencher) { 94 | let a = AtomicCell::new(0usize); 95 | b.iter(|| a.store(1)); 96 | } 97 | 98 | #[bench] 99 | fn fetch_add_usize(b: &mut test::Bencher) { 100 | let a = AtomicCell::new(0usize); 101 | b.iter(|| a.fetch_add(1)); 102 | } 103 | 104 | #[bench] 105 | fn compare_exchange_usize(b: &mut test::Bencher) { 106 | let a = AtomicCell::new(0usize); 107 | let mut i = 0; 108 | b.iter(|| { 109 | let _ = a.compare_exchange(i, i.wrapping_add(1)); 110 | i = i.wrapping_add(1); 111 | }); 112 | } 113 | 114 | #[bench] 115 | fn concurrent_load_usize(b: &mut test::Bencher) { 116 | const THREADS: usize = 2; 117 | const STEPS: usize = 1_000_000; 118 | 119 | let start = Barrier::new(THREADS + 1); 120 | let end = Barrier::new(THREADS + 1); 121 | let exit = AtomicCell::new(false); 122 | 123 | let a = AtomicCell::new(0usize); 124 | 125 | thread::scope(|scope| { 126 | for _ in 0..THREADS { 127 | scope.spawn(|_| loop { 128 | start.wait(); 129 | 130 | let mut sum = 0; 131 | for _ in 0..STEPS { 132 | sum += a.load(); 133 | } 134 | test::black_box(sum); 135 | 136 | end.wait(); 137 | if exit.load() { 138 | break; 139 | } 140 | }); 141 | } 142 | 143 | start.wait(); 144 | end.wait(); 145 | 146 | b.iter(|| { 147 | start.wait(); 148 | end.wait(); 149 | }); 150 | 151 | start.wait(); 152 | exit.store(true); 153 | end.wait(); 154 | }) 155 | .unwrap(); 156 | } 157 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/mpmc.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | mod message; 4 | 5 | const MESSAGES: usize = 5_000_000; 6 | const THREADS: usize = 4; 7 | 8 | fn seq(cap: usize) { 9 | let q = mpmc::Queue::with_capacity(cap); 10 | 11 | for i in 0..MESSAGES { 12 | loop { 13 | if q.push(message::new(i)).is_ok() { 14 | break; 15 | } else { 16 | thread::yield_now(); 17 | } 18 | } 19 | } 20 | 21 | for _ in 0..MESSAGES { 22 | q.pop().unwrap(); 23 | } 24 | } 25 | 26 | fn spsc(cap: usize) { 27 | let q = mpmc::Queue::with_capacity(cap); 28 | 29 | crossbeam::scope(|scope| { 30 | scope.spawn(|_| { 31 | for i in 0..MESSAGES { 32 | loop { 33 | if q.push(message::new(i)).is_ok() { 34 | break; 35 | } else { 36 | thread::yield_now(); 37 | } 38 | } 39 | } 40 | }); 41 | 42 | for _ in 0..MESSAGES { 43 | loop { 44 | if q.pop().is_none() { 45 | thread::yield_now(); 46 | } else { 47 | break; 48 | } 49 | } 50 | } 51 | }) 52 | .unwrap(); 53 | } 54 | 55 | fn mpsc(cap: usize) { 56 | let q = mpmc::Queue::with_capacity(cap); 57 | 58 | crossbeam::scope(|scope| { 59 | for _ in 0..THREADS { 60 | scope.spawn(|_| { 61 | for i in 0..MESSAGES / THREADS { 62 | loop { 63 | if q.push(message::new(i)).is_ok() { 64 | break; 65 | } else { 66 | thread::yield_now(); 67 | } 68 | } 69 | } 70 | }); 71 | } 72 | 73 | for _ in 0..MESSAGES { 74 | loop { 75 | if q.pop().is_none() { 76 | thread::yield_now(); 77 | } else { 78 | break; 79 | } 80 | } 81 | } 82 | }) 83 | .unwrap(); 84 | } 85 | 86 | fn mpmc(cap: usize) { 87 | let q = mpmc::Queue::with_capacity(cap); 88 | 89 | crossbeam::scope(|scope| { 90 | for _ in 0..THREADS { 91 | scope.spawn(|_| { 92 | for i in 0..MESSAGES / THREADS { 93 | loop { 94 | if q.push(message::new(i)).is_ok() { 95 | break; 96 | } else { 97 | thread::yield_now(); 98 | } 99 | } 100 | } 101 | }); 102 | } 103 | 104 | for _ in 0..THREADS { 105 | scope.spawn(|_| { 106 | for _ in 0..MESSAGES / THREADS { 107 | loop { 108 | if q.pop().is_none() { 109 | thread::yield_now(); 110 | } else { 111 | break; 112 | } 113 | } 114 | } 115 | }); 116 | } 117 | }) 118 | .unwrap(); 119 | } 120 | 121 | fn main() { 122 | macro_rules! run { 123 | ($name:expr, $f:expr) => { 124 | let now = ::std::time::Instant::now(); 125 | $f; 126 | let elapsed = now.elapsed(); 127 | println!( 128 | "{:25} {:15} {:7.3} sec", 129 | $name, 130 | "Rust mpmc", 131 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 132 | ); 133 | }; 134 | } 135 | 136 | run!("bounded_mpmc", mpmc(MESSAGES)); 137 | run!("bounded_mpsc", mpsc(MESSAGES)); 138 | run!("bounded_seq", seq(MESSAGES)); 139 | run!("bounded_spsc", spsc(MESSAGES)); 140 | } 141 | -------------------------------------------------------------------------------- /crossbeam-channel/README.md: -------------------------------------------------------------------------------- 1 | # Crossbeam Channel 2 | 3 | [![Build Status](https://github.com/crossbeam-rs/crossbeam/workflows/CI/badge.svg)]( 4 | https://github.com/crossbeam-rs/crossbeam/actions) 5 | [![License](https://img.shields.io/badge/license-MIT_OR_Apache--2.0-blue.svg)]( 6 | https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel#license) 7 | [![Cargo](https://img.shields.io/crates/v/crossbeam-channel.svg)]( 8 | https://crates.io/crates/crossbeam-channel) 9 | [![Documentation](https://docs.rs/crossbeam-channel/badge.svg)]( 10 | https://docs.rs/crossbeam-channel) 11 | [![Rust 1.60+](https://img.shields.io/badge/rust-1.60+-lightgray.svg)]( 12 | https://www.rust-lang.org) 13 | [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ) 14 | 15 | This crate provides multi-producer multi-consumer channels for message passing. 16 | It is an alternative to [`std::sync::mpsc`] with more features and better performance. 17 | 18 | Some highlights: 19 | 20 | * [`Sender`]s and [`Receiver`]s can be cloned and shared among threads. 21 | * Two main kinds of channels are [`bounded`] and [`unbounded`]. 22 | * Convenient extra channels like [`after`], [`never`], and [`tick`]. 23 | * The [`select!`] macro can block on multiple channel operations. 24 | * [`Select`] can select over a dynamically built list of channel operations. 25 | * Channels use locks very sparingly for maximum [performance](benchmarks). 26 | 27 | [`std::sync::mpsc`]: https://doc.rust-lang.org/std/sync/mpsc/index.html 28 | [`Sender`]: https://docs.rs/crossbeam-channel/latest/crossbeam_channel/struct.Sender.html 29 | [`Receiver`]: https://docs.rs/crossbeam-channel/latest/crossbeam_channel/struct.Receiver.html 30 | [`bounded`]: https://docs.rs/crossbeam-channel/latest/crossbeam_channel/fn.bounded.html 31 | [`unbounded`]: https://docs.rs/crossbeam-channel/latest/crossbeam_channel/fn.unbounded.html 32 | [`after`]: https://docs.rs/crossbeam-channel/latest/crossbeam_channel/fn.after.html 33 | [`never`]: https://docs.rs/crossbeam-channel/latest/crossbeam_channel/fn.never.html 34 | [`tick`]: https://docs.rs/crossbeam-channel/latest/crossbeam_channel/fn.tick.html 35 | [`select!`]: https://docs.rs/crossbeam-channel/latest/crossbeam_channel/macro.select.html 36 | [`Select`]: https://docs.rs/crossbeam-channel/latest/crossbeam_channel/struct.Select.html 37 | 38 | ## Usage 39 | 40 | Add this to your `Cargo.toml`: 41 | 42 | ```toml 43 | [dependencies] 44 | crossbeam-channel = "0.5" 45 | ``` 46 | 47 | ## Compatibility 48 | 49 | Crossbeam Channel supports stable Rust releases going back at least six months, 50 | and every time the minimum supported Rust version is increased, a new minor 51 | version is released. Currently, the minimum supported Rust version is 1.60. 52 | 53 | ## License 54 | 55 | Licensed under either of 56 | 57 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 58 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 59 | 60 | at your option. 61 | 62 | #### Contribution 63 | 64 | Unless you explicitly state otherwise, any contribution intentionally submitted 65 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 66 | dual licensed as above, without any additional terms or conditions. 67 | 68 | #### Third party software 69 | 70 | This product includes copies and modifications of software developed by third parties: 71 | 72 | * [examples/matching.rs](examples/matching.rs) includes 73 | [matching.go](http://www.nada.kth.se/~snilsson/concurrency/src/matching.go) by Stefan Nilsson, 74 | licensed under Creative Commons Attribution 3.0 Unported License. 75 | 76 | * [tests/mpsc.rs](tests/mpsc.rs) includes modifications of code from The Rust Programming Language, 77 | licensed under the MIT License and the Apache License, Version 2.0. 78 | 79 | * [tests/golang.rs](tests/golang.rs) is based on code from The Go Programming Language, licensed 80 | under the 3-Clause BSD License. 81 | 82 | See the source code files for more details. 83 | 84 | Copies of third party licenses can be found in [LICENSE-THIRD-PARTY](LICENSE-THIRD-PARTY). 85 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/atomicring.rs: -------------------------------------------------------------------------------- 1 | use atomicring::AtomicRingBuffer; 2 | use std::thread; 3 | 4 | mod message; 5 | 6 | const MESSAGES: usize = 5_000_000; 7 | const THREADS: usize = 4; 8 | 9 | fn seq(cap: usize) { 10 | let q = AtomicRingBuffer::with_capacity(cap); 11 | 12 | for i in 0..MESSAGES { 13 | loop { 14 | if q.try_push(message::new(i)).is_ok() { 15 | break; 16 | } else { 17 | thread::yield_now(); 18 | } 19 | } 20 | } 21 | 22 | for _ in 0..MESSAGES { 23 | q.try_pop().unwrap(); 24 | } 25 | } 26 | 27 | fn spsc(cap: usize) { 28 | let q = AtomicRingBuffer::with_capacity(cap); 29 | 30 | crossbeam::scope(|scope| { 31 | scope.spawn(|_| { 32 | for i in 0..MESSAGES { 33 | loop { 34 | if q.try_push(message::new(i)).is_ok() { 35 | break; 36 | } else { 37 | thread::yield_now(); 38 | } 39 | } 40 | } 41 | }); 42 | 43 | for _ in 0..MESSAGES { 44 | loop { 45 | if q.try_pop().is_none() { 46 | thread::yield_now(); 47 | } else { 48 | break; 49 | } 50 | } 51 | } 52 | }) 53 | .unwrap(); 54 | } 55 | 56 | fn mpsc(cap: usize) { 57 | let q = AtomicRingBuffer::with_capacity(cap); 58 | 59 | crossbeam::scope(|scope| { 60 | for _ in 0..THREADS { 61 | scope.spawn(|_| { 62 | for i in 0..MESSAGES / THREADS { 63 | loop { 64 | if q.try_push(message::new(i)).is_ok() { 65 | break; 66 | } else { 67 | thread::yield_now(); 68 | } 69 | } 70 | } 71 | }); 72 | } 73 | 74 | for _ in 0..MESSAGES { 75 | loop { 76 | if q.try_pop().is_none() { 77 | thread::yield_now(); 78 | } else { 79 | break; 80 | } 81 | } 82 | } 83 | }) 84 | .unwrap(); 85 | } 86 | 87 | fn mpmc(cap: usize) { 88 | let q = AtomicRingBuffer::with_capacity(cap); 89 | 90 | crossbeam::scope(|scope| { 91 | for _ in 0..THREADS { 92 | scope.spawn(|_| { 93 | for i in 0..MESSAGES / THREADS { 94 | loop { 95 | if q.try_push(message::new(i)).is_ok() { 96 | break; 97 | } else { 98 | thread::yield_now(); 99 | } 100 | } 101 | } 102 | }); 103 | } 104 | for _ in 0..THREADS { 105 | scope.spawn(|_| { 106 | for _ in 0..MESSAGES / THREADS { 107 | loop { 108 | if q.try_pop().is_none() { 109 | thread::yield_now(); 110 | } else { 111 | break; 112 | } 113 | } 114 | } 115 | }); 116 | } 117 | }) 118 | .unwrap(); 119 | } 120 | 121 | fn main() { 122 | macro_rules! run { 123 | ($name:expr, $f:expr) => { 124 | let now = ::std::time::Instant::now(); 125 | $f; 126 | let elapsed = now.elapsed(); 127 | println!( 128 | "{:25} {:15} {:7.3} sec", 129 | $name, 130 | "Rust atomicring", 131 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 132 | ); 133 | }; 134 | } 135 | 136 | run!("bounded_mpmc", mpmc(MESSAGES)); 137 | run!("bounded_mpsc", mpsc(MESSAGES)); 138 | run!("bounded_seq", seq(MESSAGES)); 139 | run!("bounded_spsc", spsc(MESSAGES)); 140 | } 141 | -------------------------------------------------------------------------------- /crossbeam-utils/src/atomic/consume.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(crossbeam_no_atomic))] 2 | use core::sync::atomic::Ordering; 3 | 4 | /// Trait which allows reading from primitive atomic types with "consume" ordering. 5 | pub trait AtomicConsume { 6 | /// Type returned by `load_consume`. 7 | type Val; 8 | 9 | /// Loads a value from the atomic using a "consume" memory ordering. 10 | /// 11 | /// This is similar to the "acquire" ordering, except that an ordering is 12 | /// only guaranteed with operations that "depend on" the result of the load. 13 | /// However consume loads are usually much faster than acquire loads on 14 | /// architectures with a weak memory model since they don't require memory 15 | /// fence instructions. 16 | /// 17 | /// The exact definition of "depend on" is a bit vague, but it works as you 18 | /// would expect in practice since a lot of software, especially the Linux 19 | /// kernel, rely on this behavior. 20 | /// 21 | /// This is currently only implemented on ARM and AArch64, where a fence 22 | /// can be avoided. On other architectures this will fall back to a simple 23 | /// `load(Ordering::Acquire)`. 24 | fn load_consume(&self) -> Self::Val; 25 | } 26 | 27 | #[cfg(not(crossbeam_no_atomic))] 28 | // Miri and Loom don't support "consume" ordering and ThreadSanitizer doesn't treat 29 | // load(Relaxed) + compiler_fence(Acquire) as "consume" load. 30 | // LLVM generates machine code equivalent to fence(Acquire) in compiler_fence(Acquire) 31 | // on PowerPC, MIPS, etc. (https://godbolt.org/z/hffvjvW7h), so for now the fence 32 | // can be actually avoided here only on ARM and AArch64. See also 33 | // https://github.com/rust-lang/rust/issues/62256. 34 | #[cfg(all( 35 | any(target_arch = "arm", target_arch = "aarch64"), 36 | not(any(miri, crossbeam_loom, crossbeam_sanitize_thread)), 37 | ))] 38 | macro_rules! impl_consume { 39 | () => { 40 | #[inline] 41 | fn load_consume(&self) -> Self::Val { 42 | use crate::primitive::sync::atomic::compiler_fence; 43 | let result = self.load(Ordering::Relaxed); 44 | compiler_fence(Ordering::Acquire); 45 | result 46 | } 47 | }; 48 | } 49 | 50 | #[cfg(not(crossbeam_no_atomic))] 51 | #[cfg(not(all( 52 | any(target_arch = "arm", target_arch = "aarch64"), 53 | not(any(miri, crossbeam_loom, crossbeam_sanitize_thread)), 54 | )))] 55 | macro_rules! impl_consume { 56 | () => { 57 | #[inline] 58 | fn load_consume(&self) -> Self::Val { 59 | self.load(Ordering::Acquire) 60 | } 61 | }; 62 | } 63 | 64 | macro_rules! impl_atomic { 65 | ($atomic:ident, $val:ty) => { 66 | #[cfg(not(crossbeam_no_atomic))] 67 | impl AtomicConsume for core::sync::atomic::$atomic { 68 | type Val = $val; 69 | impl_consume!(); 70 | } 71 | #[cfg(crossbeam_loom)] 72 | impl AtomicConsume for loom::sync::atomic::$atomic { 73 | type Val = $val; 74 | impl_consume!(); 75 | } 76 | }; 77 | } 78 | 79 | impl_atomic!(AtomicBool, bool); 80 | impl_atomic!(AtomicUsize, usize); 81 | impl_atomic!(AtomicIsize, isize); 82 | impl_atomic!(AtomicU8, u8); 83 | impl_atomic!(AtomicI8, i8); 84 | impl_atomic!(AtomicU16, u16); 85 | impl_atomic!(AtomicI16, i16); 86 | #[cfg(any(target_has_atomic = "32", not(target_pointer_width = "16")))] 87 | impl_atomic!(AtomicU32, u32); 88 | #[cfg(any(target_has_atomic = "32", not(target_pointer_width = "16")))] 89 | impl_atomic!(AtomicI32, i32); 90 | #[cfg(any( 91 | target_has_atomic = "64", 92 | not(any(target_pointer_width = "16", target_pointer_width = "32")), 93 | ))] 94 | impl_atomic!(AtomicU64, u64); 95 | #[cfg(any( 96 | target_has_atomic = "64", 97 | not(any(target_pointer_width = "16", target_pointer_width = "32")), 98 | ))] 99 | impl_atomic!(AtomicI64, i64); 100 | 101 | #[cfg(not(crossbeam_no_atomic))] 102 | impl AtomicConsume for core::sync::atomic::AtomicPtr { 103 | type Val = *mut T; 104 | impl_consume!(); 105 | } 106 | 107 | #[cfg(crossbeam_loom)] 108 | impl AtomicConsume for loom::sync::atomic::AtomicPtr { 109 | type Val = *mut T; 110 | impl_consume!(); 111 | } 112 | -------------------------------------------------------------------------------- /crossbeam-deque/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Concurrent work-stealing deques. 2 | //! 3 | //! These data structures are most commonly used in work-stealing schedulers. The typical setup 4 | //! involves a number of threads, each having its own FIFO or LIFO queue (*worker*). There is also 5 | //! one global FIFO queue (*injector*) and a list of references to *worker* queues that are able to 6 | //! steal tasks (*stealers*). 7 | //! 8 | //! We spawn a new task onto the scheduler by pushing it into the *injector* queue. Each worker 9 | //! thread waits in a loop until it finds the next task to run and then runs it. To find a task, it 10 | //! first looks into its local *worker* queue, and then into the *injector* and *stealers*. 11 | //! 12 | //! # Queues 13 | //! 14 | //! [`Injector`] is a FIFO queue, where tasks are pushed and stolen from opposite ends. It is 15 | //! shared among threads and is usually the entry point for new tasks. 16 | //! 17 | //! [`Worker`] has two constructors: 18 | //! 19 | //! * [`new_fifo()`] - Creates a FIFO queue, in which tasks are pushed and popped from opposite 20 | //! ends. 21 | //! * [`new_lifo()`] - Creates a LIFO queue, in which tasks are pushed and popped from the same 22 | //! end. 23 | //! 24 | //! Each [`Worker`] is owned by a single thread and supports only push and pop operations. 25 | //! 26 | //! Method [`stealer()`] creates a [`Stealer`] that may be shared among threads and can only steal 27 | //! tasks from its [`Worker`]. Tasks are stolen from the end opposite to where they get pushed. 28 | //! 29 | //! # Stealing 30 | //! 31 | //! Steal operations come in three flavors: 32 | //! 33 | //! 1. [`steal()`] - Steals one task. 34 | //! 2. [`steal_batch()`] - Steals a batch of tasks and moves them into another worker. 35 | //! 3. [`steal_batch_and_pop()`] - Steals a batch of tasks, moves them into another queue, and pops 36 | //! one task from that worker. 37 | //! 38 | //! In contrast to push and pop operations, stealing can spuriously fail with [`Steal::Retry`], in 39 | //! which case the steal operation needs to be retried. 40 | //! 41 | //! # Examples 42 | //! 43 | //! Suppose a thread in a work-stealing scheduler is idle and looking for the next task to run. To 44 | //! find an available task, it might do the following: 45 | //! 46 | //! 1. Try popping one task from the local worker queue. 47 | //! 2. Try stealing a batch of tasks from the global injector queue. 48 | //! 3. Try stealing one task from another thread using the stealer list. 49 | //! 50 | //! An implementation of this work-stealing strategy: 51 | //! 52 | //! ``` 53 | //! use crossbeam_deque::{Injector, Stealer, Worker}; 54 | //! use std::iter; 55 | //! 56 | //! fn find_task( 57 | //! local: &Worker, 58 | //! global: &Injector, 59 | //! stealers: &[Stealer], 60 | //! ) -> Option { 61 | //! // Pop a task from the local queue, if not empty. 62 | //! local.pop().or_else(|| { 63 | //! // Otherwise, we need to look for a task elsewhere. 64 | //! iter::repeat_with(|| { 65 | //! // Try stealing a batch of tasks from the global queue. 66 | //! global.steal_batch_and_pop(local) 67 | //! // Or try stealing a task from one of the other threads. 68 | //! .or_else(|| stealers.iter().map(|s| s.steal()).collect()) 69 | //! }) 70 | //! // Loop while no task was stolen and any steal operation needs to be retried. 71 | //! .find(|s| !s.is_retry()) 72 | //! // Extract the stolen task, if there is one. 73 | //! .and_then(|s| s.success()) 74 | //! }) 75 | //! } 76 | //! ``` 77 | //! 78 | //! [`new_fifo()`]: Worker::new_fifo 79 | //! [`new_lifo()`]: Worker::new_lifo 80 | //! [`stealer()`]: Worker::stealer 81 | //! [`steal()`]: Stealer::steal 82 | //! [`steal_batch()`]: Stealer::steal_batch 83 | //! [`steal_batch_and_pop()`]: Stealer::steal_batch_and_pop 84 | 85 | #![no_std] 86 | #![doc(test( 87 | no_crate_inject, 88 | attr( 89 | deny(warnings, rust_2018_idioms, single_use_lifetimes), 90 | allow(dead_code, unused_assignments, unused_variables) 91 | ) 92 | ))] 93 | #![warn(missing_docs, unsafe_op_in_unsafe_fn)] 94 | 95 | #[cfg(feature = "std")] 96 | extern crate std; 97 | 98 | #[cfg(feature = "std")] 99 | mod deque; 100 | #[cfg(feature = "std")] 101 | pub use crate::deque::{Injector, Steal, Stealer, Worker}; 102 | -------------------------------------------------------------------------------- /crossbeam-deque/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 0.8.6 2 | 3 | - Fix stack overflow when pushing large value to `Injector`. (#1146, #1147, #1159) 4 | 5 | # Version 0.8.5 6 | 7 | - Remove dependency on `cfg-if`. (#1072) 8 | 9 | # Version 0.8.4 10 | 11 | - Bump the minimum supported Rust version to 1.61. (#1037) 12 | 13 | # Version 0.8.3 14 | 15 | - Add `Stealer::{steal_batch_with_limit, steal_batch_with_limit_and_pop}` methods. (#903) 16 | - Add `Injector::{steal_batch_with_limit, steal_batch_with_limit_and_pop}` methods. (#903) 17 | 18 | # Version 0.8.2 19 | 20 | - Bump the minimum supported Rust version to 1.38. (#877) 21 | 22 | # Version 0.8.1 23 | 24 | - Fix deque steal race condition. (#726) 25 | - Add `Stealer::len` method. (#708) 26 | 27 | # Version 0.8.0 28 | 29 | **Note:** This release has been yanked. See [GHSA-pqqp-xmhj-wgcw](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-pqqp-xmhj-wgcw) for details. 30 | 31 | - Bump the minimum supported Rust version to 1.36. 32 | - Add `Worker::len()` and `Injector::len()` methods. 33 | - Add `std` (enabled by default) feature for forward compatibility. 34 | 35 | # Version 0.7.4 36 | 37 | - Fix deque steal race condition. 38 | 39 | # Version 0.7.3 40 | 41 | **Note:** This release has been yanked. See [GHSA-pqqp-xmhj-wgcw](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-pqqp-xmhj-wgcw) for details. 42 | 43 | - Stop stealing from the same deque. (#448) 44 | - Fix unsoundness issues by adopting `MaybeUninit`. (#458) 45 | 46 | # Version 0.7.2 47 | 48 | **Note:** This release has been yanked. See [GHSA-pqqp-xmhj-wgcw](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-pqqp-xmhj-wgcw) for details. 49 | 50 | - Bump `crossbeam-epoch` to `0.8`. 51 | - Bump `crossbeam-utils` to `0.7`. 52 | 53 | # Version 0.7.1 54 | 55 | **Note:** This release has been yanked. See [GHSA-pqqp-xmhj-wgcw](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-pqqp-xmhj-wgcw) for details. 56 | 57 | - Bump the minimum required version of `crossbeam-utils`. 58 | 59 | # Version 0.7.0 60 | 61 | **Note:** This release has been yanked. See [GHSA-pqqp-xmhj-wgcw](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-pqqp-xmhj-wgcw) for details. 62 | 63 | - Make `Worker::pop()` faster in the FIFO case. 64 | - Replace `fifo()` nad `lifo()` with `Worker::new_fifo()` and `Worker::new_lifo()`. 65 | - Add more batched steal methods. 66 | - Introduce `Injector`, a MPMC queue. 67 | - Rename `Steal::Data` to `Steal::Success`. 68 | - Add `Steal::or_else()` and implement `FromIterator` for `Steal`. 69 | - Add `#[must_use]` to `Steal`. 70 | 71 | # Version 0.6.3 72 | 73 | - Bump `crossbeam-epoch` to `0.7`. 74 | 75 | # Version 0.6.2 76 | 77 | - Update `crosbeam-utils` to `0.6`. 78 | 79 | # Version 0.6.1 80 | 81 | - Change a few `Relaxed` orderings to `Release` in order to fix false positives by tsan. 82 | 83 | # Version 0.6.0 84 | 85 | - Add `Stealer::steal_many` for batched stealing. 86 | - Change the return type of `pop` to `Pop` so that spinning can be handled manually. 87 | 88 | # Version 0.5.2 89 | 90 | - Update `crossbeam-utils` to `0.5.0`. 91 | 92 | # Version 0.5.1 93 | 94 | - Minor optimizations. 95 | 96 | # Version 0.5.0 97 | 98 | - Add two deque constructors : `fifo()` and `lifo()`. 99 | - Update `rand` to `0.5.3`. 100 | - Rename `Deque` to `Worker`. 101 | - Return `Option` from `Stealer::steal`. 102 | - Remove methods `Deque::len` and `Stealer::len`. 103 | - Remove method `Deque::stealer`. 104 | - Remove method `Deque::steal`. 105 | 106 | # Version 0.4.1 107 | 108 | - Update `crossbeam-epoch` to `0.5.0`. 109 | 110 | # Version 0.4.0 111 | 112 | - Update `crossbeam-epoch` to `0.4.2`. 113 | - Update `crossbeam-utils` to `0.4.0`. 114 | - Require minimum Rust version 1.25. 115 | 116 | # Version 0.3.1 117 | 118 | - Add `Deque::capacity`. 119 | - Add `Deque::min_capacity`. 120 | - Add `Deque::shrink_to_fit`. 121 | - Update `crossbeam-epoch` to `0.3.0`. 122 | - Support Rust 1.20. 123 | - Shrink the buffer in `Deque::push` if necessary. 124 | 125 | # Version 0.3.0 126 | 127 | - Update `crossbeam-epoch` to `0.4.0`. 128 | - Drop support for Rust 1.13. 129 | 130 | # Version 0.2.0 131 | 132 | - Update `crossbeam-epoch` to `0.3.0`. 133 | - Support Rust 1.13. 134 | 135 | # Version 0.1.1 136 | 137 | - Update `crossbeam-epoch` to `0.2.0`. 138 | 139 | # Version 0.1.0 140 | 141 | - First implementation of the Chase-Lev deque. 142 | -------------------------------------------------------------------------------- /crossbeam-utils/src/sync/wait_group.rs: -------------------------------------------------------------------------------- 1 | use crate::primitive::sync::{Arc, Condvar, Mutex}; 2 | use std::fmt; 3 | 4 | /// Enables threads to synchronize the beginning or end of some computation. 5 | /// 6 | /// # Wait groups vs barriers 7 | /// 8 | /// `WaitGroup` is very similar to [`Barrier`], but there are a few differences: 9 | /// 10 | /// * [`Barrier`] needs to know the number of threads at construction, while `WaitGroup` is cloned to 11 | /// register more threads. 12 | /// 13 | /// * A [`Barrier`] can be reused even after all threads have synchronized, while a `WaitGroup` 14 | /// synchronizes threads only once. 15 | /// 16 | /// * All threads wait for others to reach the [`Barrier`]. With `WaitGroup`, each thread can choose 17 | /// to either wait for other threads or to continue without blocking. 18 | /// 19 | /// # Examples 20 | /// 21 | /// ``` 22 | /// use crossbeam_utils::sync::WaitGroup; 23 | /// use std::thread; 24 | /// 25 | /// // Create a new wait group. 26 | /// let wg = WaitGroup::new(); 27 | /// 28 | /// for _ in 0..4 { 29 | /// // Create another reference to the wait group. 30 | /// let wg = wg.clone(); 31 | /// 32 | /// thread::spawn(move || { 33 | /// // Do some work. 34 | /// 35 | /// // Drop the reference to the wait group. 36 | /// drop(wg); 37 | /// }); 38 | /// } 39 | /// 40 | /// // Block until all threads have finished their work. 41 | /// wg.wait(); 42 | /// # if cfg!(miri) { std::thread::sleep(std::time::Duration::from_millis(500)); } // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 43 | /// ``` 44 | /// 45 | /// [`Barrier`]: std::sync::Barrier 46 | pub struct WaitGroup { 47 | inner: Arc, 48 | } 49 | 50 | /// Inner state of a `WaitGroup`. 51 | struct Inner { 52 | cvar: Condvar, 53 | count: Mutex, 54 | } 55 | 56 | impl Default for WaitGroup { 57 | fn default() -> Self { 58 | Self { 59 | inner: Arc::new(Inner { 60 | cvar: Condvar::new(), 61 | count: Mutex::new(1), 62 | }), 63 | } 64 | } 65 | } 66 | 67 | impl WaitGroup { 68 | /// Creates a new wait group and returns the single reference to it. 69 | /// 70 | /// # Examples 71 | /// 72 | /// ``` 73 | /// use crossbeam_utils::sync::WaitGroup; 74 | /// 75 | /// let wg = WaitGroup::new(); 76 | /// ``` 77 | pub fn new() -> Self { 78 | Self::default() 79 | } 80 | 81 | /// Drops this reference and waits until all other references are dropped. 82 | /// 83 | /// # Examples 84 | /// 85 | /// ``` 86 | /// use crossbeam_utils::sync::WaitGroup; 87 | /// use std::thread; 88 | /// 89 | /// let wg = WaitGroup::new(); 90 | /// 91 | /// # let t = 92 | /// thread::spawn({ 93 | /// let wg = wg.clone(); 94 | /// move || { 95 | /// // Block until both threads have reached `wait()`. 96 | /// wg.wait(); 97 | /// } 98 | /// }); 99 | /// 100 | /// // Block until both threads have reached `wait()`. 101 | /// wg.wait(); 102 | /// # t.join().unwrap(); // join thread to avoid https://github.com/rust-lang/miri/issues/1371 103 | /// ``` 104 | pub fn wait(self) { 105 | if *self.inner.count.lock().unwrap() == 1 { 106 | return; 107 | } 108 | 109 | let inner = self.inner.clone(); 110 | drop(self); 111 | 112 | let mut count = inner.count.lock().unwrap(); 113 | while *count > 0 { 114 | count = inner.cvar.wait(count).unwrap(); 115 | } 116 | } 117 | } 118 | 119 | impl Drop for WaitGroup { 120 | fn drop(&mut self) { 121 | let mut count = self.inner.count.lock().unwrap(); 122 | *count -= 1; 123 | 124 | if *count == 0 { 125 | self.inner.cvar.notify_all(); 126 | } 127 | } 128 | } 129 | 130 | impl Clone for WaitGroup { 131 | fn clone(&self) -> Self { 132 | let mut count = self.inner.count.lock().unwrap(); 133 | *count += 1; 134 | 135 | Self { 136 | inner: self.inner.clone(), 137 | } 138 | } 139 | } 140 | 141 | impl fmt::Debug for WaitGroup { 142 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 143 | let count: &usize = &self.inner.count.lock().unwrap(); 144 | f.debug_struct("WaitGroup").field("count", count).finish() 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/plot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import random 3 | import sys 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | def read_data(files): 8 | results = [] 9 | for f in files: 10 | with open(f) as f: 11 | for line in f.readlines(): 12 | test, lang, impl, secs, _ = line.split() 13 | splt = test.split('_') 14 | results.append((splt[0], '_'.join(splt[1:]), lang, impl, float(secs))) 15 | return results 16 | 17 | 18 | def get_runs(results, prefix): 19 | runs = set() 20 | for pre, test, lang, impl, secs in results: 21 | if pre == prefix: 22 | runs.add(test) 23 | result = list(runs) 24 | result.sort() 25 | return result 26 | 27 | 28 | def find(s, x): 29 | for i in range(len(s)): 30 | if s[i] == x: 31 | return i 32 | return None 33 | 34 | 35 | color_set = { 36 | 'aqua': '#00ffff', 37 | 'azure': '#f0ffff', 38 | 'beige': '#f5f5dc', 39 | 'black': '#000000', 40 | 'blue': '#0000ff', 41 | 'brown': '#a52a2a', 42 | 'cyan': '#00ffff', 43 | 'darkblue': '#00008b', 44 | 'darkcyan': '#008b8b', 45 | 'darkgrey': '#a9a9a9', 46 | 'darkgreen': '#006400', 47 | 'darkkhaki': '#bdb76b', 48 | 'darkmagenta': '#8b008b', 49 | 'darkolivegreen': '#556b2f', 50 | 'darkorange': '#ff8c00', 51 | 'darkorchid': '#9932cc', 52 | 'darkred': '#8b0000', 53 | 'darksalmon': '#e9967a', 54 | 'darkviolet': '#9400d3', 55 | 'fuchsia': '#ff00ff', 56 | 'gold': '#ffd700', 57 | 'green': '#008000', 58 | 'indigo': '#4b0082', 59 | 'khaki': '#f0e68c', 60 | 'lightblue': '#add8e6', 61 | 'lightcyan': '#e0ffff', 62 | 'lightgreen': '#90ee90', 63 | 'lightgrey': '#d3d3d3', 64 | 'lightpink': '#ffb6c1', 65 | 'lightyellow': '#ffffe0', 66 | 'lime': '#00ff00', 67 | 'magenta': '#ff00ff', 68 | 'maroon': '#800000', 69 | 'navy': '#000080', 70 | 'olive': '#808000', 71 | 'orange': '#ffa500', 72 | 'pink': '#ffc0cb', 73 | 'purple': '#800080', 74 | 'red': '#ff0000', 75 | } 76 | saved_color = {} 77 | 78 | 79 | def get_color(name): 80 | if name not in saved_color: 81 | color = color_set.popitem() 82 | saved_color[name] = color 83 | return saved_color[name][1] 84 | 85 | 86 | def plot(results, fig, subplot, title, prefix): 87 | runs = get_runs(results, prefix) 88 | 89 | ys = [len(runs) * (i + 1) for i in range(len(runs))] 90 | 91 | ax = fig.add_subplot(subplot) 92 | ax.set_title(title) 93 | ax.set_yticks(ys) 94 | ax.set_yticklabels(runs) 95 | ax.tick_params(which='major', length=0) 96 | ax.set_xlabel('seconds') 97 | 98 | scores = {} 99 | 100 | for pre, test, lang, impl, secs in results: 101 | if pre == prefix: 102 | name = impl if lang == 'Rust' else impl + f' ({lang})' 103 | if name not in scores: 104 | scores[name] = [0] * len(runs) 105 | scores[name][find(runs, test)] = secs 106 | 107 | opts = dict(height=0.8, align='center') 108 | x_max = max(max(scores.values(), key=lambda x: max(x))) 109 | for i, (name, score) in enumerate(scores.items()): 110 | yy = [y + i - len(runs) // 2 + 0.2 for y in ys] 111 | ax.barh(yy, score, color=get_color(name), **opts) 112 | for xxx, yyy in zip(score, yy): 113 | if xxx: 114 | ax.text(min(x_max - len(name) * 0.018 * x_max, xxx), yyy - 0.25, name, fontsize=9) 115 | 116 | 117 | def plot_all(results, descriptions, labels): 118 | fig = plt.figure(figsize=(10, 10)) 119 | # TODO support more subplots 120 | subplot = [221, 222, 223, 224] 121 | for p, d, l in zip(subplot, descriptions, labels): 122 | plot(results, fig, p, d, l) 123 | plt.subplots_adjust( 124 | top=0.95, 125 | bottom=0.05, 126 | left=0.1, 127 | right=0.95, 128 | wspace=0.3, 129 | hspace=0.2, 130 | ) 131 | plt.savefig('plot.png') 132 | # plt.show() 133 | 134 | 135 | def main(): 136 | results = read_data(sys.argv[1:]) 137 | descriptions = [ 138 | 'Bounded channel of capacity 0', 139 | 'Bounded channel of capacity 1', 140 | 'Bounded channel of capacity N', 141 | 'Unbounded channel', 142 | ] 143 | labels = ['bounded0', 'bounded1', 'bounded', 'unbounded'] 144 | plot_all(results, descriptions, labels) 145 | 146 | 147 | if __name__ == '__main__': 148 | main() 149 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/mpsc.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc; 2 | 3 | mod message; 4 | 5 | const MESSAGES: usize = 5_000_000; 6 | const THREADS: usize = 4; 7 | 8 | pub fn shuffle(v: &mut [T]) { 9 | use std::cell::Cell; 10 | use std::num::Wrapping; 11 | 12 | let len = v.len(); 13 | if len <= 1 { 14 | return; 15 | } 16 | 17 | thread_local! { 18 | static RNG: Cell> = const { Cell::new(Wrapping(1)) }; 19 | } 20 | 21 | RNG.with(|rng| { 22 | for i in 1..len { 23 | // This is the 32-bit variant of Xorshift. 24 | // https://en.wikipedia.org/wiki/Xorshift 25 | let mut x = rng.get(); 26 | x ^= x << 13; 27 | x ^= x >> 17; 28 | x ^= x << 5; 29 | rng.set(x); 30 | 31 | let x = x.0; 32 | let n = i + 1; 33 | 34 | // This is a fast alternative to `let j = x % n`. 35 | // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ 36 | let j = ((x as u64).wrapping_mul(n as u64) >> 32) as u32 as usize; 37 | 38 | v.swap(i, j); 39 | } 40 | }); 41 | } 42 | 43 | fn seq_async() { 44 | let (tx, rx) = mpsc::channel(); 45 | 46 | for i in 0..MESSAGES { 47 | tx.send(message::new(i)).unwrap(); 48 | } 49 | 50 | for _ in 0..MESSAGES { 51 | rx.recv().unwrap(); 52 | } 53 | } 54 | 55 | fn seq_sync(cap: usize) { 56 | let (tx, rx) = mpsc::sync_channel(cap); 57 | 58 | for i in 0..MESSAGES { 59 | tx.send(message::new(i)).unwrap(); 60 | } 61 | 62 | for _ in 0..MESSAGES { 63 | rx.recv().unwrap(); 64 | } 65 | } 66 | 67 | fn spsc_async() { 68 | let (tx, rx) = mpsc::channel(); 69 | 70 | crossbeam::scope(|scope| { 71 | scope.spawn(move |_| { 72 | for i in 0..MESSAGES { 73 | tx.send(message::new(i)).unwrap(); 74 | } 75 | }); 76 | 77 | for _ in 0..MESSAGES { 78 | rx.recv().unwrap(); 79 | } 80 | }) 81 | .unwrap(); 82 | } 83 | 84 | fn spsc_sync(cap: usize) { 85 | let (tx, rx) = mpsc::sync_channel(cap); 86 | 87 | crossbeam::scope(|scope| { 88 | scope.spawn(move |_| { 89 | for i in 0..MESSAGES { 90 | tx.send(message::new(i)).unwrap(); 91 | } 92 | }); 93 | 94 | for _ in 0..MESSAGES { 95 | rx.recv().unwrap(); 96 | } 97 | }) 98 | .unwrap(); 99 | } 100 | 101 | fn mpsc_async() { 102 | let (tx, rx) = mpsc::channel(); 103 | 104 | crossbeam::scope(|scope| { 105 | for _ in 0..THREADS { 106 | let tx = tx.clone(); 107 | scope.spawn(move |_| { 108 | for i in 0..MESSAGES / THREADS { 109 | tx.send(message::new(i)).unwrap(); 110 | } 111 | }); 112 | } 113 | 114 | for _ in 0..MESSAGES { 115 | rx.recv().unwrap(); 116 | } 117 | }) 118 | .unwrap(); 119 | } 120 | 121 | fn mpsc_sync(cap: usize) { 122 | let (tx, rx) = mpsc::sync_channel(cap); 123 | 124 | crossbeam::scope(|scope| { 125 | for _ in 0..THREADS { 126 | let tx = tx.clone(); 127 | scope.spawn(move |_| { 128 | for i in 0..MESSAGES / THREADS { 129 | tx.send(message::new(i)).unwrap(); 130 | } 131 | }); 132 | } 133 | 134 | for _ in 0..MESSAGES { 135 | rx.recv().unwrap(); 136 | } 137 | }) 138 | .unwrap(); 139 | } 140 | 141 | fn main() { 142 | macro_rules! run { 143 | ($name:expr, $f:expr) => { 144 | let now = ::std::time::Instant::now(); 145 | $f; 146 | let elapsed = now.elapsed(); 147 | println!( 148 | "{:25} {:15} {:7.3} sec", 149 | $name, 150 | "Rust mpsc", 151 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 152 | ); 153 | }; 154 | } 155 | 156 | run!("bounded0_mpsc", mpsc_sync(0)); 157 | run!("bounded0_spsc", spsc_sync(0)); 158 | 159 | run!("bounded1_mpsc", mpsc_sync(1)); 160 | run!("bounded1_spsc", spsc_sync(1)); 161 | 162 | run!("bounded_mpsc", mpsc_sync(MESSAGES)); 163 | run!("bounded_seq", seq_sync(MESSAGES)); 164 | run!("bounded_spsc", spsc_sync(MESSAGES)); 165 | 166 | run!("unbounded_mpsc", mpsc_async()); 167 | run!("unbounded_seq", seq_async()); 168 | run!("unbounded_spsc", spsc_async()); 169 | } 170 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/flume.rs: -------------------------------------------------------------------------------- 1 | mod message; 2 | 3 | const MESSAGES: usize = 5_000_000; 4 | const THREADS: usize = 4; 5 | 6 | pub fn shuffle(v: &mut [T]) { 7 | use std::cell::Cell; 8 | use std::num::Wrapping; 9 | 10 | let len = v.len(); 11 | if len <= 1 { 12 | return; 13 | } 14 | 15 | thread_local! { 16 | static RNG: Cell> = const { Cell::new(Wrapping(1)) }; 17 | } 18 | 19 | RNG.with(|rng| { 20 | for i in 1..len { 21 | // This is the 32-bit variant of Xorshift. 22 | // https://en.wikipedia.org/wiki/Xorshift 23 | let mut x = rng.get(); 24 | x ^= x << 13; 25 | x ^= x >> 17; 26 | x ^= x << 5; 27 | rng.set(x); 28 | 29 | let x = x.0; 30 | let n = i + 1; 31 | 32 | // This is a fast alternative to `let j = x % n`. 33 | // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ 34 | let j = ((x as u64).wrapping_mul(n as u64) >> 32) as u32 as usize; 35 | 36 | v.swap(i, j); 37 | } 38 | }); 39 | } 40 | 41 | fn seq_unbounded() { 42 | let (tx, rx) = flume::unbounded(); 43 | 44 | for i in 0..MESSAGES { 45 | tx.send(message::new(i)).unwrap(); 46 | } 47 | 48 | for _ in 0..MESSAGES { 49 | rx.recv().unwrap(); 50 | } 51 | } 52 | 53 | fn seq_bounded(cap: usize) { 54 | let (tx, rx) = flume::bounded(cap); 55 | 56 | for i in 0..MESSAGES { 57 | tx.send(message::new(i)).unwrap(); 58 | } 59 | 60 | for _ in 0..MESSAGES { 61 | rx.recv().unwrap(); 62 | } 63 | } 64 | 65 | fn spsc_unbounded() { 66 | let (tx, rx) = flume::unbounded(); 67 | 68 | crossbeam::scope(|scope| { 69 | scope.spawn(move |_| { 70 | for i in 0..MESSAGES { 71 | tx.send(message::new(i)).unwrap(); 72 | } 73 | }); 74 | 75 | for _ in 0..MESSAGES { 76 | rx.recv().unwrap(); 77 | } 78 | }) 79 | .unwrap(); 80 | } 81 | 82 | fn spsc_bounded(cap: usize) { 83 | let (tx, rx) = flume::bounded(cap); 84 | 85 | crossbeam::scope(|scope| { 86 | scope.spawn(move |_| { 87 | for i in 0..MESSAGES { 88 | tx.send(message::new(i)).unwrap(); 89 | } 90 | }); 91 | 92 | for _ in 0..MESSAGES { 93 | rx.recv().unwrap(); 94 | } 95 | }) 96 | .unwrap(); 97 | } 98 | 99 | fn mpsc_unbounded() { 100 | let (tx, rx) = flume::unbounded(); 101 | 102 | crossbeam::scope(|scope| { 103 | for _ in 0..THREADS { 104 | let tx = tx.clone(); 105 | scope.spawn(move |_| { 106 | for i in 0..MESSAGES / THREADS { 107 | tx.send(message::new(i)).unwrap(); 108 | } 109 | }); 110 | } 111 | 112 | for _ in 0..MESSAGES { 113 | rx.recv().unwrap(); 114 | } 115 | }) 116 | .unwrap(); 117 | } 118 | 119 | fn mpsc_bounded(cap: usize) { 120 | let (tx, rx) = flume::bounded(cap); 121 | 122 | crossbeam::scope(|scope| { 123 | for _ in 0..THREADS { 124 | let tx = tx.clone(); 125 | scope.spawn(move |_| { 126 | for i in 0..MESSAGES / THREADS { 127 | tx.send(message::new(i)).unwrap(); 128 | } 129 | }); 130 | } 131 | 132 | for _ in 0..MESSAGES { 133 | rx.recv().unwrap(); 134 | } 135 | }) 136 | .unwrap(); 137 | } 138 | 139 | fn main() { 140 | macro_rules! run { 141 | ($name:expr, $f:expr) => { 142 | let now = ::std::time::Instant::now(); 143 | $f; 144 | let elapsed = now.elapsed(); 145 | println!( 146 | "{:25} {:15} {:7.3} sec", 147 | $name, 148 | "Rust flume", 149 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 150 | ); 151 | }; 152 | } 153 | 154 | // run!("bounded0_mpsc", mpsc_bounded(0)); 155 | // run!("bounded0_spsc", spsc_bounded(0)); 156 | 157 | run!("bounded1_mpsc", mpsc_bounded(1)); 158 | run!("bounded1_spsc", spsc_bounded(1)); 159 | 160 | run!("bounded_mpsc", mpsc_bounded(MESSAGES)); 161 | run!("bounded_seq", seq_bounded(MESSAGES)); 162 | run!("bounded_spsc", spsc_bounded(MESSAGES)); 163 | 164 | run!("unbounded_mpsc", mpsc_unbounded()); 165 | run!("unbounded_seq", seq_unbounded()); 166 | run!("unbounded_spsc", spsc_unbounded()); 167 | } 168 | -------------------------------------------------------------------------------- /crossbeam-channel/src/counter.rs: -------------------------------------------------------------------------------- 1 | //! Reference counter for channels. 2 | 3 | use std::boxed::Box; 4 | use std::ops; 5 | use std::process; 6 | use std::ptr::NonNull; 7 | use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; 8 | 9 | /// Reference counter internals. 10 | struct Counter { 11 | /// The number of senders associated with the channel. 12 | senders: AtomicUsize, 13 | 14 | /// The number of receivers associated with the channel. 15 | receivers: AtomicUsize, 16 | 17 | /// Set to `true` if the last sender or the last receiver reference deallocates the channel. 18 | destroy: AtomicBool, 19 | 20 | /// The internal channel. 21 | chan: C, 22 | } 23 | 24 | /// Wraps a channel into the reference counter. 25 | pub(crate) fn new(chan: C) -> (Sender, Receiver) { 26 | let counter = NonNull::from(Box::leak(Box::new(Counter { 27 | senders: AtomicUsize::new(1), 28 | receivers: AtomicUsize::new(1), 29 | destroy: AtomicBool::new(false), 30 | chan, 31 | }))); 32 | let s = Sender { counter }; 33 | let r = Receiver { counter }; 34 | (s, r) 35 | } 36 | 37 | /// The sending side. 38 | pub(crate) struct Sender { 39 | counter: NonNull>, 40 | } 41 | 42 | impl Sender { 43 | /// Returns the internal `Counter`. 44 | fn counter(&self) -> &Counter { 45 | unsafe { self.counter.as_ref() } 46 | } 47 | 48 | /// Acquires another sender reference. 49 | pub(crate) fn acquire(&self) -> Self { 50 | let count = self.counter().senders.fetch_add(1, Ordering::Relaxed); 51 | 52 | // Cloning senders and calling `mem::forget` on the clones could potentially overflow the 53 | // counter. It's very difficult to recover sensibly from such degenerate scenarios so we 54 | // just abort when the count becomes very large. 55 | if count > isize::MAX as usize { 56 | process::abort(); 57 | } 58 | 59 | Self { 60 | counter: self.counter, 61 | } 62 | } 63 | 64 | /// Releases the sender reference. 65 | /// 66 | /// Function `disconnect` will be called if this is the last sender reference. 67 | pub(crate) unsafe fn release bool>(&self, disconnect: F) { 68 | if self.counter().senders.fetch_sub(1, Ordering::AcqRel) == 1 { 69 | disconnect(&self.counter().chan); 70 | 71 | if self.counter().destroy.swap(true, Ordering::AcqRel) { 72 | drop(unsafe { Box::from_raw(self.counter.as_ptr()) }); 73 | } 74 | } 75 | } 76 | } 77 | 78 | impl ops::Deref for Sender { 79 | type Target = C; 80 | 81 | fn deref(&self) -> &C { 82 | &self.counter().chan 83 | } 84 | } 85 | 86 | impl PartialEq for Sender { 87 | fn eq(&self, other: &Self) -> bool { 88 | self.counter == other.counter 89 | } 90 | } 91 | 92 | /// The receiving side. 93 | pub(crate) struct Receiver { 94 | counter: NonNull>, 95 | } 96 | 97 | impl Receiver { 98 | /// Returns the internal `Counter`. 99 | fn counter(&self) -> &Counter { 100 | unsafe { self.counter.as_ref() } 101 | } 102 | 103 | /// Acquires another receiver reference. 104 | pub(crate) fn acquire(&self) -> Self { 105 | let count = self.counter().receivers.fetch_add(1, Ordering::Relaxed); 106 | 107 | // Cloning receivers and calling `mem::forget` on the clones could potentially overflow the 108 | // counter. It's very difficult to recover sensibly from such degenerate scenarios so we 109 | // just abort when the count becomes very large. 110 | if count > isize::MAX as usize { 111 | process::abort(); 112 | } 113 | 114 | Self { 115 | counter: self.counter, 116 | } 117 | } 118 | 119 | /// Releases the receiver reference. 120 | /// 121 | /// Function `disconnect` will be called if this is the last receiver reference. 122 | pub(crate) unsafe fn release bool>(&self, disconnect: F) { 123 | if self.counter().receivers.fetch_sub(1, Ordering::AcqRel) == 1 { 124 | disconnect(&self.counter().chan); 125 | 126 | if self.counter().destroy.swap(true, Ordering::AcqRel) { 127 | drop(unsafe { Box::from_raw(self.counter.as_ptr()) }); 128 | } 129 | } 130 | } 131 | } 132 | 133 | impl ops::Deref for Receiver { 134 | type Target = C; 135 | 136 | fn deref(&self) -> &C { 137 | &self.counter().chan 138 | } 139 | } 140 | 141 | impl PartialEq for Receiver { 142 | fn eq(&self, other: &Self) -> bool { 143 | self.counter == other.counter 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /crossbeam-epoch/src/deferred.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use core::fmt; 3 | use core::marker::PhantomData; 4 | use core::mem::{self, MaybeUninit}; 5 | use core::ptr; 6 | 7 | /// Number of words a piece of `Data` can hold. 8 | /// 9 | /// Three words should be enough for the majority of cases. For example, you can fit inside it the 10 | /// function pointer together with a fat pointer representing an object that needs to be destroyed. 11 | const DATA_WORDS: usize = 3; 12 | 13 | /// Some space to keep a `FnOnce()` object on the stack. 14 | type Data = [usize; DATA_WORDS]; 15 | 16 | /// A `FnOnce()` that is stored inline if small, or otherwise boxed on the heap. 17 | /// 18 | /// This is a handy way of keeping an unsized `FnOnce()` within a sized structure. 19 | pub(crate) struct Deferred { 20 | call: unsafe fn(*mut u8), 21 | data: MaybeUninit, 22 | _marker: PhantomData<*mut ()>, // !Send + !Sync 23 | } 24 | 25 | impl fmt::Debug for Deferred { 26 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 27 | f.pad("Deferred { .. }") 28 | } 29 | } 30 | 31 | impl Deferred { 32 | pub(crate) const NO_OP: Self = { 33 | fn no_op_call(_raw: *mut u8) {} 34 | Self { 35 | call: no_op_call, 36 | data: MaybeUninit::uninit(), 37 | _marker: PhantomData, 38 | } 39 | }; 40 | 41 | /// Constructs a new `Deferred` from a `FnOnce()`. 42 | pub(crate) fn new(f: F) -> Self { 43 | let size = mem::size_of::(); 44 | let align = mem::align_of::(); 45 | 46 | unsafe { 47 | if size <= mem::size_of::() && align <= mem::align_of::() { 48 | let mut data = MaybeUninit::::uninit(); 49 | ptr::write(data.as_mut_ptr().cast::(), f); 50 | 51 | unsafe fn call(raw: *mut u8) { 52 | let f: F = unsafe { ptr::read(raw.cast::()) }; 53 | f(); 54 | } 55 | 56 | Self { 57 | call: call::, 58 | data, 59 | _marker: PhantomData, 60 | } 61 | } else { 62 | let b: Box = Box::new(f); 63 | let mut data = MaybeUninit::::uninit(); 64 | ptr::write(data.as_mut_ptr().cast::>(), b); 65 | 66 | unsafe fn call(raw: *mut u8) { 67 | // It's safe to cast `raw` from `*mut u8` to `*mut Box`, because `raw` is 68 | // originally derived from `*mut Box`. 69 | let b: Box = unsafe { ptr::read(raw.cast::>()) }; 70 | (*b)(); 71 | } 72 | 73 | Self { 74 | call: call::, 75 | data, 76 | _marker: PhantomData, 77 | } 78 | } 79 | } 80 | } 81 | 82 | /// Calls the function. 83 | #[inline] 84 | pub(crate) fn call(mut self) { 85 | let call = self.call; 86 | unsafe { call(self.data.as_mut_ptr().cast::()) }; 87 | } 88 | } 89 | 90 | #[cfg(all(test, not(crossbeam_loom)))] 91 | mod tests { 92 | use super::Deferred; 93 | use std::boxed::Box; 94 | use std::cell::Cell; 95 | use std::convert::identity; 96 | use std::string::ToString; 97 | use std::vec; 98 | 99 | #[test] 100 | fn on_stack() { 101 | let fired = &Cell::new(false); 102 | let a = [0usize; 1]; 103 | 104 | let d = Deferred::new(move || { 105 | let _ = identity(a); 106 | fired.set(true); 107 | }); 108 | 109 | assert!(!fired.get()); 110 | d.call(); 111 | assert!(fired.get()); 112 | } 113 | 114 | #[test] 115 | fn on_heap() { 116 | let fired = &Cell::new(false); 117 | let a = [0usize; 10]; 118 | 119 | let d = Deferred::new(move || { 120 | let _ = identity(a); 121 | fired.set(true); 122 | }); 123 | 124 | assert!(!fired.get()); 125 | d.call(); 126 | assert!(fired.get()); 127 | } 128 | 129 | #[test] 130 | fn string() { 131 | let a = "hello".to_string(); 132 | let d = Deferred::new(move || assert_eq!(a, "hello")); 133 | d.call(); 134 | } 135 | 136 | #[test] 137 | fn boxed_slice_i32() { 138 | let a: Box<[i32]> = vec![2, 3, 5, 7].into_boxed_slice(); 139 | let d = Deferred::new(move || assert_eq!(*a, [2, 3, 5, 7])); 140 | d.call(); 141 | } 142 | 143 | #[test] 144 | fn long_slice_usize() { 145 | let a: [usize; 5] = [2, 3, 5, 7, 11]; 146 | let d = Deferred::new(move || assert_eq!(a, [2, 3, 5, 7, 11])); 147 | d.call(); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /crossbeam-channel/src/flavors/tick.rs: -------------------------------------------------------------------------------- 1 | //! Channel that delivers messages periodically. 2 | //! 3 | //! Messages cannot be sent into this kind of channel; they are materialized on demand. 4 | 5 | use std::thread; 6 | use std::time::{Duration, Instant}; 7 | 8 | use crossbeam_utils::atomic::AtomicCell; 9 | 10 | use crate::context::Context; 11 | use crate::err::{RecvTimeoutError, TryRecvError}; 12 | use crate::select::{Operation, SelectHandle, Token}; 13 | 14 | /// Result of a receive operation. 15 | pub(crate) type TickToken = Option; 16 | 17 | /// Channel that delivers messages periodically. 18 | pub(crate) struct Channel { 19 | /// The instant at which the next message will be delivered. 20 | delivery_time: AtomicCell, 21 | 22 | /// The time interval in which messages get delivered. 23 | duration: Duration, 24 | } 25 | 26 | impl Channel { 27 | /// Creates a channel that delivers messages periodically. 28 | #[inline] 29 | pub(crate) fn new(delivery_time: Instant, dur: Duration) -> Self { 30 | Self { 31 | delivery_time: AtomicCell::new(delivery_time), 32 | duration: dur, 33 | } 34 | } 35 | 36 | /// Attempts to receive a message without blocking. 37 | #[inline] 38 | pub(crate) fn try_recv(&self) -> Result { 39 | loop { 40 | let now = Instant::now(); 41 | let delivery_time = self.delivery_time.load(); 42 | 43 | if now < delivery_time { 44 | return Err(TryRecvError::Empty); 45 | } 46 | 47 | if self 48 | .delivery_time 49 | .compare_exchange(delivery_time, now + self.duration) 50 | .is_ok() 51 | { 52 | return Ok(delivery_time); 53 | } 54 | } 55 | } 56 | 57 | /// Receives a message from the channel. 58 | #[inline] 59 | pub(crate) fn recv(&self, deadline: Option) -> Result { 60 | loop { 61 | let delivery_time = self.delivery_time.load(); 62 | let now = Instant::now(); 63 | 64 | if let Some(d) = deadline { 65 | if d < delivery_time { 66 | if now < d { 67 | thread::sleep(d - now); 68 | } 69 | return Err(RecvTimeoutError::Timeout); 70 | } 71 | } 72 | 73 | if self 74 | .delivery_time 75 | .compare_exchange(delivery_time, delivery_time.max(now) + self.duration) 76 | .is_ok() 77 | { 78 | if now < delivery_time { 79 | thread::sleep(delivery_time - now); 80 | } 81 | return Ok(delivery_time); 82 | } 83 | } 84 | } 85 | 86 | /// Reads a message from the channel. 87 | #[inline] 88 | pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { 89 | token.tick.ok_or(()) 90 | } 91 | 92 | /// Returns `true` if the channel is empty. 93 | #[inline] 94 | pub(crate) fn is_empty(&self) -> bool { 95 | Instant::now() < self.delivery_time.load() 96 | } 97 | 98 | /// Returns `true` if the channel is full. 99 | #[inline] 100 | pub(crate) fn is_full(&self) -> bool { 101 | !self.is_empty() 102 | } 103 | 104 | /// Returns the number of messages in the channel. 105 | #[inline] 106 | pub(crate) fn len(&self) -> usize { 107 | usize::from(!self.is_empty()) 108 | } 109 | 110 | /// Returns the capacity of the channel. 111 | #[inline] 112 | pub(crate) fn capacity(&self) -> Option { 113 | Some(1) 114 | } 115 | } 116 | 117 | impl SelectHandle for Channel { 118 | #[inline] 119 | fn try_select(&self, token: &mut Token) -> bool { 120 | match self.try_recv() { 121 | Ok(msg) => { 122 | token.tick = Some(msg); 123 | true 124 | } 125 | Err(TryRecvError::Disconnected) => { 126 | token.tick = None; 127 | true 128 | } 129 | Err(TryRecvError::Empty) => false, 130 | } 131 | } 132 | 133 | #[inline] 134 | fn deadline(&self) -> Option { 135 | Some(self.delivery_time.load()) 136 | } 137 | 138 | #[inline] 139 | fn register(&self, _oper: Operation, _cx: &Context) -> bool { 140 | self.is_ready() 141 | } 142 | 143 | #[inline] 144 | fn unregister(&self, _oper: Operation) {} 145 | 146 | #[inline] 147 | fn accept(&self, token: &mut Token, _cx: &Context) -> bool { 148 | self.try_select(token) 149 | } 150 | 151 | #[inline] 152 | fn is_ready(&self) -> bool { 153 | !self.is_empty() 154 | } 155 | 156 | #[inline] 157 | fn watch(&self, _oper: Operation, _cx: &Context) -> bool { 158 | self.is_ready() 159 | } 160 | 161 | #[inline] 162 | fn unwatch(&self, _oper: Operation) {} 163 | } 164 | -------------------------------------------------------------------------------- /crossbeam-channel/benchmarks/go.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | import "time" 5 | 6 | const MESSAGES = 5 * 1000 * 1000 7 | const THREADS = 4 8 | 9 | type Message = uintptr 10 | 11 | func seq(cap int) { 12 | var c = make(chan Message, cap) 13 | 14 | for i := 0; i < MESSAGES; i++ { 15 | c <- Message(i) 16 | } 17 | 18 | for i := 0; i < MESSAGES; i++ { 19 | <-c 20 | } 21 | } 22 | 23 | func spsc(cap int) { 24 | var c = make(chan Message, cap) 25 | var done = make(chan bool) 26 | 27 | go func() { 28 | for i := 0; i < MESSAGES; i++ { 29 | c <- Message(i) 30 | } 31 | done <- true 32 | }() 33 | 34 | for i := 0; i < MESSAGES; i++ { 35 | <-c 36 | } 37 | 38 | <-done 39 | } 40 | 41 | func mpsc(cap int) { 42 | var c = make(chan Message, cap) 43 | var done = make(chan bool) 44 | 45 | for t := 0; t < THREADS; t++ { 46 | go func() { 47 | for i := 0; i < MESSAGES / THREADS; i++ { 48 | c <- Message(i) 49 | } 50 | done <- true 51 | }() 52 | } 53 | 54 | for i := 0; i < MESSAGES; i++ { 55 | <-c 56 | } 57 | 58 | for t := 0; t < THREADS; t++ { 59 | <-done 60 | } 61 | } 62 | 63 | func mpmc(cap int) { 64 | var c = make(chan Message, cap) 65 | var done = make(chan bool) 66 | 67 | for t := 0; t < THREADS; t++ { 68 | go func() { 69 | for i := 0; i < MESSAGES / THREADS; i++ { 70 | c <- Message(i) 71 | } 72 | done <- true 73 | }() 74 | 75 | } 76 | 77 | for t := 0; t < THREADS; t++ { 78 | go func() { 79 | for i := 0; i < MESSAGES / THREADS; i++ { 80 | <-c 81 | } 82 | done <- true 83 | }() 84 | } 85 | 86 | for t := 0; t < THREADS; t++ { 87 | <-done 88 | <-done 89 | } 90 | } 91 | 92 | func select_rx(cap int) { 93 | if THREADS != 4 { 94 | panic("assumed there are 4 threads") 95 | } 96 | 97 | var c0 = make(chan Message, cap) 98 | var c1 = make(chan Message, cap) 99 | var c2 = make(chan Message, cap) 100 | var c3 = make(chan Message, cap) 101 | var done = make(chan bool) 102 | 103 | var producer = func(c chan Message) { 104 | for i := 0; i < MESSAGES / THREADS; i++ { 105 | c <- Message(i) 106 | } 107 | done <- true 108 | } 109 | go producer(c0) 110 | go producer(c1) 111 | go producer(c2) 112 | go producer(c3) 113 | 114 | for i := 0; i < MESSAGES; i++ { 115 | select { 116 | case <-c0: 117 | case <-c1: 118 | case <-c2: 119 | case <-c3: 120 | } 121 | } 122 | 123 | for t := 0; t < THREADS; t++ { 124 | <-done 125 | } 126 | } 127 | 128 | func select_both(cap int) { 129 | if THREADS != 4 { 130 | panic("assumed there are 4 threads") 131 | } 132 | 133 | var c0 = make(chan Message, cap) 134 | var c1 = make(chan Message, cap) 135 | var c2 = make(chan Message, cap) 136 | var c3 = make(chan Message, cap) 137 | var done = make(chan bool) 138 | 139 | var producer = func(c0 chan Message, c1 chan Message, c2 chan Message, c3 chan Message) { 140 | for i := 0; i < MESSAGES / THREADS; i++ { 141 | select { 142 | case c0 <- Message(i): 143 | case c1 <- Message(i): 144 | case c2 <- Message(i): 145 | case c3 <- Message(i): 146 | } 147 | } 148 | done <- true 149 | } 150 | go producer(c0,c1,c2,c3) 151 | go producer(c0,c1,c2,c3) 152 | go producer(c0,c1,c2,c3) 153 | go producer(c0,c1,c2,c3) 154 | 155 | for t := 0; t < THREADS; t++ { 156 | go func() { 157 | for i := 0; i < MESSAGES / THREADS; i++ { 158 | select { 159 | case <-c0: 160 | case <-c1: 161 | case <-c2: 162 | case <-c3: 163 | } 164 | } 165 | done <- true 166 | }() 167 | } 168 | 169 | for t := 0; t < THREADS; t++ { 170 | <-done 171 | <-done 172 | } 173 | } 174 | 175 | func run(name string, f func(int), cap int) { 176 | var now = time.Now() 177 | f(cap) 178 | var elapsed = time.Now().Sub(now) 179 | fmt.Printf("%-25v %-15v %7.3f sec\n", name, "Go chan", float64(elapsed) / float64(time.Second)) 180 | } 181 | 182 | func main() { 183 | run("bounded0_mpmc", mpmc, 0) 184 | run("bounded0_mpsc", mpsc, 0) 185 | run("bounded0_select_both", select_both, 0) 186 | run("bounded0_select_rx", select_rx, 0) 187 | run("bounded0_spsc", spsc, 0) 188 | 189 | run("bounded1_mpmc", mpmc, 1) 190 | run("bounded1_mpsc", mpsc, 1) 191 | run("bounded1_select_both", select_both, 1) 192 | run("bounded1_select_rx", select_rx, 1) 193 | run("bounded1_spsc", spsc, 1) 194 | 195 | run("bounded_mpmc", mpmc, MESSAGES) 196 | run("bounded_mpsc", mpsc, MESSAGES) 197 | run("bounded_select_both", select_both, MESSAGES) 198 | run("bounded_select_rx", select_rx, MESSAGES) 199 | run("bounded_seq", seq, MESSAGES) 200 | run("bounded_spsc", spsc, MESSAGES) 201 | } 202 | -------------------------------------------------------------------------------- /crossbeam-epoch/src/epoch.rs: -------------------------------------------------------------------------------- 1 | //! The global epoch 2 | //! 3 | //! The last bit in this number is unused and is always zero. Every so often the global epoch is 4 | //! incremented, i.e. we say it "advances". A pinned participant may advance the global epoch only 5 | //! if all currently pinned participants have been pinned in the current epoch. 6 | //! 7 | //! If an object became garbage in some epoch, then we can be sure that after two advancements no 8 | //! participant will hold a reference to it. That is the crux of safe memory reclamation. 9 | 10 | use crate::primitive::sync::atomic::{AtomicUsize, Ordering}; 11 | 12 | /// An epoch that can be marked as pinned or unpinned. 13 | /// 14 | /// Internally, the epoch is represented as an integer that wraps around at some unspecified point 15 | /// and a flag that represents whether it is pinned or unpinned. 16 | #[derive(Copy, Clone, Default, Debug, Eq, PartialEq)] 17 | pub(crate) struct Epoch { 18 | /// The least significant bit is set if pinned. The rest of the bits hold the epoch. 19 | data: usize, 20 | } 21 | 22 | impl Epoch { 23 | /// Returns the starting epoch in unpinned state. 24 | #[inline] 25 | pub(crate) fn starting() -> Self { 26 | Self::default() 27 | } 28 | 29 | /// Returns the number of epochs `self` is ahead of `rhs`. 30 | /// 31 | /// Internally, epochs are represented as numbers in the range `(isize::MIN / 2) .. (isize::MAX 32 | /// / 2)`, so the returned distance will be in the same interval. 33 | pub(crate) fn wrapping_sub(self, rhs: Self) -> isize { 34 | // The result is the same with `(self.data & !1).wrapping_sub(rhs.data & !1) as isize >> 1`, 35 | // because the possible difference of LSB in `(self.data & !1).wrapping_sub(rhs.data & !1)` 36 | // will be ignored in the shift operation. 37 | self.data.wrapping_sub(rhs.data & !1) as isize >> 1 38 | } 39 | 40 | /// Returns `true` if the epoch is marked as pinned. 41 | #[inline] 42 | pub(crate) fn is_pinned(self) -> bool { 43 | (self.data & 1) == 1 44 | } 45 | 46 | /// Returns the same epoch, but marked as pinned. 47 | #[inline] 48 | pub(crate) fn pinned(self) -> Self { 49 | Self { 50 | data: self.data | 1, 51 | } 52 | } 53 | 54 | /// Returns the same epoch, but marked as unpinned. 55 | #[inline] 56 | pub(crate) fn unpinned(self) -> Self { 57 | Self { 58 | data: self.data & !1, 59 | } 60 | } 61 | 62 | /// Returns the successor epoch. 63 | /// 64 | /// The returned epoch will be marked as pinned only if the previous one was as well. 65 | #[inline] 66 | pub(crate) fn successor(self) -> Self { 67 | Self { 68 | data: self.data.wrapping_add(2), 69 | } 70 | } 71 | } 72 | 73 | /// An atomic value that holds an `Epoch`. 74 | #[derive(Default, Debug)] 75 | pub(crate) struct AtomicEpoch { 76 | /// Since `Epoch` is just a wrapper around `usize`, an `AtomicEpoch` is similarly represented 77 | /// using an `AtomicUsize`. 78 | data: AtomicUsize, 79 | } 80 | 81 | impl AtomicEpoch { 82 | /// Creates a new atomic epoch. 83 | #[inline] 84 | pub(crate) fn new(epoch: Epoch) -> Self { 85 | let data = AtomicUsize::new(epoch.data); 86 | Self { data } 87 | } 88 | 89 | /// Loads a value from the atomic epoch. 90 | #[inline] 91 | pub(crate) fn load(&self, ord: Ordering) -> Epoch { 92 | Epoch { 93 | data: self.data.load(ord), 94 | } 95 | } 96 | 97 | /// Stores a value into the atomic epoch. 98 | #[inline] 99 | pub(crate) fn store(&self, epoch: Epoch, ord: Ordering) { 100 | self.data.store(epoch.data, ord); 101 | } 102 | 103 | /// Stores a value into the atomic epoch if the current value is the same as `current`. 104 | /// 105 | /// The return value is a result indicating whether the new value was written and containing 106 | /// the previous value. On success this value is guaranteed to be equal to `current`. 107 | /// 108 | /// This method takes two `Ordering` arguments to describe the memory 109 | /// ordering of this operation. `success` describes the required ordering for the 110 | /// read-modify-write operation that takes place if the comparison with `current` succeeds. 111 | /// `failure` describes the required ordering for the load operation that takes place when 112 | /// the comparison fails. Using `Acquire` as success ordering makes the store part 113 | /// of this operation `Relaxed`, and using `Release` makes the successful load 114 | /// `Relaxed`. The failure ordering can only be `SeqCst`, `Acquire` or `Relaxed` 115 | /// and must be equivalent to or weaker than the success ordering. 116 | #[inline] 117 | pub(crate) fn compare_exchange( 118 | &self, 119 | current: Epoch, 120 | new: Epoch, 121 | success: Ordering, 122 | failure: Ordering, 123 | ) -> Result { 124 | match self 125 | .data 126 | .compare_exchange(current.data, new.data, success, failure) 127 | { 128 | Ok(data) => Ok(Epoch { data }), 129 | Err(data) => Err(Epoch { data }), 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /crossbeam-epoch/tests/loom.rs: -------------------------------------------------------------------------------- 1 | #![cfg(crossbeam_loom)] 2 | 3 | use crossbeam_epoch as epoch; 4 | use loom_crate as loom; 5 | 6 | use epoch::*; 7 | use epoch::{Atomic, Owned}; 8 | use loom::sync::atomic::Ordering::{self, Acquire, Relaxed, Release}; 9 | use loom::sync::Arc; 10 | use loom::thread::spawn; 11 | use std::mem::ManuallyDrop; 12 | use std::ptr; 13 | 14 | #[test] 15 | fn it_works() { 16 | loom::model(|| { 17 | let collector = Collector::new(); 18 | let item: Atomic = Atomic::from(Owned::new(String::from("boom"))); 19 | let item2 = item.clone(); 20 | let collector2 = collector.clone(); 21 | let guard = collector.register().pin(); 22 | 23 | let jh = loom::thread::spawn(move || { 24 | let guard = collector2.register().pin(); 25 | guard.defer(move || { 26 | // this isn't really safe, since other threads may still have pointers to the 27 | // value, but in this limited test scenario it's okay, since we know the test won't 28 | // access item after all the pins are released. 29 | let mut item = unsafe { item2.into_owned() }; 30 | // mutate it as a second measure to make sure the assert_eq below would fail 31 | item.retain(|c| c == 'o'); 32 | drop(item); 33 | }); 34 | }); 35 | 36 | let item = item.load(Ordering::SeqCst, &guard); 37 | // we pinned strictly before the call to defer_destroy, 38 | // so item cannot have been dropped yet 39 | assert_eq!(*unsafe { item.deref() }, "boom"); 40 | drop(guard); 41 | 42 | jh.join().unwrap(); 43 | 44 | drop(collector); 45 | }) 46 | } 47 | 48 | #[test] 49 | fn treiber_stack() { 50 | /// Treiber's lock-free stack. 51 | /// 52 | /// Usable with any number of producers and consumers. 53 | #[derive(Debug)] 54 | struct TreiberStack { 55 | head: Atomic>, 56 | } 57 | 58 | #[derive(Debug)] 59 | struct Node { 60 | data: ManuallyDrop, 61 | next: Atomic>, 62 | } 63 | 64 | impl TreiberStack { 65 | /// Creates a new, empty stack. 66 | fn new() -> Self { 67 | Self { 68 | head: Atomic::null(), 69 | } 70 | } 71 | 72 | /// Pushes a value on top of the stack. 73 | fn push(&self, t: T) { 74 | let mut n = Owned::new(Node { 75 | data: ManuallyDrop::new(t), 76 | next: Atomic::null(), 77 | }); 78 | 79 | let guard = epoch::pin(); 80 | 81 | loop { 82 | let head = self.head.load(Relaxed, &guard); 83 | n.next.store(head, Relaxed); 84 | 85 | match self 86 | .head 87 | .compare_exchange(head, n, Release, Relaxed, &guard) 88 | { 89 | Ok(_) => break, 90 | Err(e) => n = e.new, 91 | } 92 | } 93 | } 94 | 95 | /// Attempts to pop the top element from the stack. 96 | /// 97 | /// Returns `None` if the stack is empty. 98 | fn pop(&self) -> Option { 99 | let guard = epoch::pin(); 100 | loop { 101 | let head = self.head.load(Acquire, &guard); 102 | 103 | match unsafe { head.as_ref() } { 104 | Some(h) => { 105 | let next = h.next.load(Relaxed, &guard); 106 | 107 | if self 108 | .head 109 | .compare_exchange(head, next, Relaxed, Relaxed, &guard) 110 | .is_ok() 111 | { 112 | unsafe { 113 | guard.defer_destroy(head); 114 | return Some(ManuallyDrop::into_inner(ptr::read(&(*h).data))); 115 | } 116 | } 117 | } 118 | None => return None, 119 | } 120 | } 121 | } 122 | 123 | /// Returns `true` if the stack is empty. 124 | fn is_empty(&self) -> bool { 125 | let guard = epoch::pin(); 126 | self.head.load(Acquire, &guard).is_null() 127 | } 128 | } 129 | 130 | impl Drop for TreiberStack { 131 | fn drop(&mut self) { 132 | while self.pop().is_some() {} 133 | } 134 | } 135 | 136 | loom::model(|| { 137 | let stack1 = Arc::new(TreiberStack::new()); 138 | let stack2 = Arc::clone(&stack1); 139 | 140 | // use 5 since it's greater than the 4 used for the sanitize feature 141 | let jh = spawn(move || { 142 | for i in 0..5 { 143 | stack2.push(i); 144 | assert!(stack2.pop().is_some()); 145 | } 146 | }); 147 | 148 | for i in 0..5 { 149 | stack1.push(i); 150 | assert!(stack1.pop().is_some()); 151 | } 152 | 153 | jh.join().unwrap(); 154 | assert!(stack1.pop().is_none()); 155 | assert!(stack1.is_empty()); 156 | }); 157 | } 158 | -------------------------------------------------------------------------------- /crossbeam-deque/tests/steal.rs: -------------------------------------------------------------------------------- 1 | use crossbeam_deque::Steal::Success; 2 | use crossbeam_deque::{Injector, Worker}; 3 | 4 | #[test] 5 | fn steal_fifo() { 6 | let w = Worker::new_fifo(); 7 | for i in 1..=3 { 8 | w.push(i); 9 | } 10 | 11 | let s = w.stealer(); 12 | assert_eq!(s.steal(), Success(1)); 13 | assert_eq!(s.steal(), Success(2)); 14 | assert_eq!(s.steal(), Success(3)); 15 | } 16 | 17 | #[test] 18 | fn steal_lifo() { 19 | let w = Worker::new_lifo(); 20 | for i in 1..=3 { 21 | w.push(i); 22 | } 23 | 24 | let s = w.stealer(); 25 | assert_eq!(s.steal(), Success(1)); 26 | assert_eq!(s.steal(), Success(2)); 27 | assert_eq!(s.steal(), Success(3)); 28 | } 29 | 30 | #[test] 31 | fn steal_injector() { 32 | let q = Injector::new(); 33 | for i in 1..=3 { 34 | q.push(i); 35 | } 36 | 37 | assert_eq!(q.steal(), Success(1)); 38 | assert_eq!(q.steal(), Success(2)); 39 | assert_eq!(q.steal(), Success(3)); 40 | } 41 | 42 | #[test] 43 | fn steal_batch_fifo_fifo() { 44 | let w = Worker::new_fifo(); 45 | for i in 1..=4 { 46 | w.push(i); 47 | } 48 | 49 | let s = w.stealer(); 50 | let w2 = Worker::new_fifo(); 51 | 52 | assert_eq!(s.steal_batch(&w2), Success(())); 53 | assert_eq!(w2.pop(), Some(1)); 54 | assert_eq!(w2.pop(), Some(2)); 55 | } 56 | 57 | #[test] 58 | fn steal_batch_lifo_lifo() { 59 | let w = Worker::new_lifo(); 60 | for i in 1..=4 { 61 | w.push(i); 62 | } 63 | 64 | let s = w.stealer(); 65 | let w2 = Worker::new_lifo(); 66 | 67 | assert_eq!(s.steal_batch(&w2), Success(())); 68 | assert_eq!(w2.pop(), Some(2)); 69 | assert_eq!(w2.pop(), Some(1)); 70 | } 71 | 72 | #[test] 73 | fn steal_batch_fifo_lifo() { 74 | let w = Worker::new_fifo(); 75 | for i in 1..=4 { 76 | w.push(i); 77 | } 78 | 79 | let s = w.stealer(); 80 | let w2 = Worker::new_lifo(); 81 | 82 | assert_eq!(s.steal_batch(&w2), Success(())); 83 | assert_eq!(w2.pop(), Some(1)); 84 | assert_eq!(w2.pop(), Some(2)); 85 | } 86 | 87 | #[test] 88 | fn steal_batch_lifo_fifo() { 89 | let w = Worker::new_lifo(); 90 | for i in 1..=4 { 91 | w.push(i); 92 | } 93 | 94 | let s = w.stealer(); 95 | let w2 = Worker::new_fifo(); 96 | 97 | assert_eq!(s.steal_batch(&w2), Success(())); 98 | assert_eq!(w2.pop(), Some(2)); 99 | assert_eq!(w2.pop(), Some(1)); 100 | } 101 | 102 | #[test] 103 | fn steal_batch_injector_fifo() { 104 | let q = Injector::new(); 105 | for i in 1..=4 { 106 | q.push(i); 107 | } 108 | 109 | let w2 = Worker::new_fifo(); 110 | assert_eq!(q.steal_batch(&w2), Success(())); 111 | assert_eq!(w2.pop(), Some(1)); 112 | assert_eq!(w2.pop(), Some(2)); 113 | } 114 | 115 | #[test] 116 | fn steal_batch_injector_lifo() { 117 | let q = Injector::new(); 118 | for i in 1..=4 { 119 | q.push(i); 120 | } 121 | 122 | let w2 = Worker::new_lifo(); 123 | assert_eq!(q.steal_batch(&w2), Success(())); 124 | assert_eq!(w2.pop(), Some(1)); 125 | assert_eq!(w2.pop(), Some(2)); 126 | } 127 | 128 | #[test] 129 | fn steal_batch_and_pop_fifo_fifo() { 130 | let w = Worker::new_fifo(); 131 | for i in 1..=6 { 132 | w.push(i); 133 | } 134 | 135 | let s = w.stealer(); 136 | let w2 = Worker::new_fifo(); 137 | 138 | assert_eq!(s.steal_batch_and_pop(&w2), Success(1)); 139 | assert_eq!(w2.pop(), Some(2)); 140 | assert_eq!(w2.pop(), Some(3)); 141 | } 142 | 143 | #[test] 144 | fn steal_batch_and_pop_lifo_lifo() { 145 | let w = Worker::new_lifo(); 146 | for i in 1..=6 { 147 | w.push(i); 148 | } 149 | 150 | let s = w.stealer(); 151 | let w2 = Worker::new_lifo(); 152 | 153 | assert_eq!(s.steal_batch_and_pop(&w2), Success(3)); 154 | assert_eq!(w2.pop(), Some(2)); 155 | assert_eq!(w2.pop(), Some(1)); 156 | } 157 | 158 | #[test] 159 | fn steal_batch_and_pop_fifo_lifo() { 160 | let w = Worker::new_fifo(); 161 | for i in 1..=6 { 162 | w.push(i); 163 | } 164 | 165 | let s = w.stealer(); 166 | let w2 = Worker::new_lifo(); 167 | 168 | assert_eq!(s.steal_batch_and_pop(&w2), Success(1)); 169 | assert_eq!(w2.pop(), Some(2)); 170 | assert_eq!(w2.pop(), Some(3)); 171 | } 172 | 173 | #[test] 174 | fn steal_batch_and_pop_lifo_fifo() { 175 | let w = Worker::new_lifo(); 176 | for i in 1..=6 { 177 | w.push(i); 178 | } 179 | 180 | let s = w.stealer(); 181 | let w2 = Worker::new_fifo(); 182 | 183 | assert_eq!(s.steal_batch_and_pop(&w2), Success(3)); 184 | assert_eq!(w2.pop(), Some(2)); 185 | assert_eq!(w2.pop(), Some(1)); 186 | } 187 | 188 | #[test] 189 | fn steal_batch_and_pop_injector_fifo() { 190 | let q = Injector::new(); 191 | for i in 1..=6 { 192 | q.push(i); 193 | } 194 | 195 | let w2 = Worker::new_fifo(); 196 | assert_eq!(q.steal_batch_and_pop(&w2), Success(1)); 197 | assert_eq!(w2.pop(), Some(2)); 198 | assert_eq!(w2.pop(), Some(3)); 199 | } 200 | 201 | #[test] 202 | fn steal_batch_and_pop_injector_lifo() { 203 | let q = Injector::new(); 204 | for i in 1..=6 { 205 | q.push(i); 206 | } 207 | 208 | let w2 = Worker::new_lifo(); 209 | assert_eq!(q.steal_batch_and_pop(&w2), Success(1)); 210 | assert_eq!(w2.pop(), Some(2)); 211 | assert_eq!(w2.pop(), Some(3)); 212 | } 213 | --------------------------------------------------------------------------------