├── .gitignore ├── .travis.yml ├── benchmarks ├── run.sh ├── Cargo.toml ├── shared.rs ├── README.md ├── bus.rs ├── crossbeam-deque.rs ├── msqueue.rs ├── segqueue.rs ├── atomicringqueue.rs ├── mpmc.rs ├── plot.py ├── atomicring.rs ├── go.go ├── crossbeam-channel.rs ├── mpsc.rs ├── futures-channel.rs └── chan.rs ├── src ├── flavors │ ├── mod.rs │ ├── never.rs │ ├── tick.rs │ ├── after.rs │ └── zero.rs ├── utils.rs ├── context.rs ├── waker.rs ├── err.rs └── lib.rs ├── examples ├── fibonacci.rs ├── stopwatch.rs └── matching.rs ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── tests ├── thread_locals.rs ├── never.rs ├── iter.rs ├── tick.rs ├── after.rs ├── list.rs ├── zero.rs └── array.rs ├── CHANGELOG.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | /benchmarks/*.txt 4 | /benchmarks/*.png 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | - 1.26.0 8 | 9 | script: 10 | - export RUSTFLAGS="-D warnings" 11 | - cargo test -- --test-threads=1 12 | - | 13 | if [[ $TRAVIS_RUST_VERSION == nightly ]]; then 14 | cd benchmarks 15 | cargo build --bins 16 | fi 17 | -------------------------------------------------------------------------------- /benchmarks/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | set -ex 5 | 6 | cargo run --release --bin chan | tee chan.txt 7 | cargo run --release --bin crossbeam-channel | tee crossbeam-channel.txt 8 | cargo run --release --bin futures-channel | tee futures-channel.txt 9 | cargo run --release --bin mpsc | tee mpsc.txt 10 | go run go.go | tee go.txt 11 | 12 | ./plot.py *.txt 13 | -------------------------------------------------------------------------------- /src/flavors/mod.rs: -------------------------------------------------------------------------------- 1 | //! Channel flavors. 2 | //! 3 | //! There are six flavors: 4 | //! 5 | //! 1. `after` - 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 mod after; 13 | pub mod array; 14 | pub mod list; 15 | pub mod never; 16 | pub mod tick; 17 | pub mod zero; 18 | -------------------------------------------------------------------------------- /examples/fibonacci.rs: -------------------------------------------------------------------------------- 1 | //! An asynchronous fibonacci sequence generator. 2 | 3 | extern crate crossbeam_channel; 4 | 5 | use std::thread; 6 | 7 | use crossbeam_channel::{bounded, Sender}; 8 | 9 | // Sends the Fibonacci sequence into the channel until it becomes disconnected. 10 | fn fibonacci(sender: Sender) { 11 | let (mut x, mut y) = (0, 1); 12 | while sender.send(x).is_ok() { 13 | let tmp = x; 14 | x = y; 15 | y = tmp + y; 16 | } 17 | } 18 | 19 | fn main() { 20 | let (s, r) = bounded(0); 21 | thread::spawn(|| fibonacci(s)); 22 | 23 | // Print the first 20 Fibonacci numbers. 24 | for num in r.iter().take(20) { 25 | println!("{}", num); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crossbeam-channel" 3 | version = "0.3.0" 4 | authors = ["The Crossbeam Project Developers"] 5 | license = "MIT/Apache-2.0" 6 | readme = "README.md" 7 | repository = "https://github.com/crossbeam-rs/crossbeam-channel" 8 | homepage = "https://github.com/crossbeam-rs/crossbeam-channel" 9 | documentation = "https://docs.rs/crossbeam-channel" 10 | description = "Multi-producer multi-consumer channels for message passing" 11 | keywords = ["channel", "mpmc", "select", "golang", "message"] 12 | categories = ["algorithms", "concurrency", "data-structures"] 13 | 14 | [dependencies] 15 | crossbeam-epoch = "0.6.0" 16 | crossbeam-utils = "0.5.0" 17 | parking_lot = "0.6.3" 18 | rand = "0.5.3" 19 | smallvec = "0.6.2" 20 | 21 | [dev-dependencies] 22 | crossbeam = "0.3.0" 23 | signal-hook = "0.1.5" 24 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /benchmarks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "benchmarks" 3 | version = "0.1.0" 4 | 5 | [profile.release] 6 | opt-level = 3 7 | lto = true 8 | codegen-units = 1 9 | incremental = false 10 | panic = 'abort' 11 | 12 | [dependencies] 13 | atomicring = "0.5.3" 14 | bus = "2.0.0" 15 | chan = "0.1.19" 16 | crossbeam = "0.3.0" 17 | crossbeam-deque = "0.4.1" 18 | crossbeam-channel = { path = ".." } 19 | futures-preview = "0.2.2" 20 | mpmc = "0.1.5" 21 | 22 | [[bin]] 23 | name = "atomicring" 24 | path = "atomicring.rs" 25 | 26 | [[bin]] 27 | name = "atomicringqueue" 28 | path = "atomicringqueue.rs" 29 | 30 | [[bin]] 31 | name = "bus" 32 | path = "bus.rs" 33 | 34 | [[bin]] 35 | name = "chan" 36 | path = "chan.rs" 37 | 38 | [[bin]] 39 | name = "crossbeam-channel" 40 | path = "crossbeam-channel.rs" 41 | 42 | [[bin]] 43 | name = "crossbeam-deque" 44 | path = "crossbeam-deque.rs" 45 | 46 | [[bin]] 47 | name = "futures-channel" 48 | path = "futures-channel.rs" 49 | 50 | [[bin]] 51 | name = "mpsc" 52 | path = "mpsc.rs" 53 | 54 | [[bin]] 55 | name = "msqueue" 56 | path = "msqueue.rs" 57 | 58 | [[bin]] 59 | name = "segqueue" 60 | path = "segqueue.rs" 61 | 62 | [[bin]] 63 | name = "mpmc" 64 | path = "mpmc.rs" 65 | -------------------------------------------------------------------------------- /benchmarks/shared.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy)] 2 | pub struct Message(i32); 3 | 4 | #[inline] 5 | pub fn message(msg: usize) -> Message { 6 | Message(msg as i32) 7 | } 8 | 9 | #[allow(dead_code)] 10 | pub fn shuffle(v: &mut [T]) { 11 | use std::cell::Cell; 12 | use std::num::Wrapping; 13 | 14 | let len = v.len(); 15 | if len <= 1 { 16 | return; 17 | } 18 | 19 | thread_local! { 20 | static RNG: Cell> = Cell::new(Wrapping(1)); 21 | } 22 | 23 | RNG.with(|rng| { 24 | for i in 1..len { 25 | // This is the 32-bit variant of Xorshift. 26 | // https://en.wikipedia.org/wiki/Xorshift 27 | let mut x = rng.get(); 28 | x ^= x << 13; 29 | x ^= x >> 17; 30 | x ^= x << 5; 31 | rng.set(x); 32 | 33 | let x = x.0; 34 | let n = i + 1; 35 | 36 | // This is a fast alternative to `let j = x % n`. 37 | // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ 38 | let j = ((x as u64).wrapping_mul(n as u64) >> 32) as u32 as usize; 39 | 40 | v.swap(i, j); 41 | } 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /benchmarks/README.md: -------------------------------------------------------------------------------- 1 | # Benchmarks 2 | 3 | ### Tests 4 | 5 | * `seq`: A single thread sends `N` messages. Then it receives `N` messages. 6 | * `spsc`: One thread sends `N` messages. Another thread receives `N` messages. 7 | * `mpsc`: `T` threads send `N / T` messages each. One thread receives `N` messages. 8 | * `mpmc`: `T` threads send `N / T` messages each. `T` other threads receive `N / T` messages each. 9 | * `select_rx`: `T` threads send `N / T` messages each into a separate channel. Another thread receives `N` messages by selecting over the `T` channels. 10 | * `select_both`: `T` threads send `N / T` messages each by selecting over `T` channels. `T` other threads receive `N / T` messages each by selecting over the `T` channels. 11 | 12 | Default configuration: 13 | 14 | - `N = 5000000` 15 | - `T = 4` 16 | 17 | ### Running 18 | 19 | Runs benchmarks, stores results into `*.txt` files, and generates `plot.png`: 20 | 21 | ``` 22 | ./run_all.sh 23 | ``` 24 | 25 | Dependencies: 26 | 27 | - Rust (nightly) 28 | - Go 29 | - Bash 30 | - Python 2 31 | - Matplotlib 32 | 33 | ### Results 34 | 35 | Machine: Intel(R) Core(TM) i7-5600U (2 physical cores, 4 logical cores) 36 | 37 | Rust: `rustc 1.30.0-nightly (90d36fb59 2018-09-13)` 38 | 39 | Go: `go version go1.10.3 linux/amd64` 40 | 41 | Commit: `6cde88a` (2018-09-17) 42 | 43 | ![Benchmark results](https://i.imgur.com/Kw2dQcy.png) 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NOTE: This crate has been moved into the [crossbeam](https://github.com/crossbeam-rs/crossbeam) repository. 2 | 3 | Do not use this repository. 4 | 5 | # Multi-producer multi-consumer channels for message passing 6 | 7 | [![Build Status](https://travis-ci.org/crossbeam-rs/crossbeam-channel.svg?branch=master)](https://travis-ci.org/crossbeam-rs/crossbeam-channel) 8 | [![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/crossbeam-rs/crossbeam-channel) 9 | [![Cargo](https://img.shields.io/crates/v/crossbeam-channel.svg)](https://crates.io/crates/crossbeam-channel) 10 | [![Documentation](https://docs.rs/crossbeam-channel/badge.svg)](https://docs.rs/crossbeam-channel) 11 | 12 | This library is an alternative to [`std::sync::mpsc`] with more features and better performance. 13 | 14 | [`std::sync::mpsc`]: https://doc.rust-lang.org/std/sync/mpsc/index.html 15 | 16 | ## Usage 17 | 18 | Add this to your `Cargo.toml`: 19 | 20 | ```toml 21 | [dependencies] 22 | crossbeam-channel = "0.3" 23 | ``` 24 | 25 | Next, add this to your crate: 26 | 27 | ```rust 28 | #[macro_use] 29 | extern crate crossbeam_channel; 30 | ``` 31 | 32 | The minimum required Rust version is 1.26. 33 | 34 | ## License 35 | 36 | Licensed under the terms of MIT license and the Apache License (Version 2.0). 37 | 38 | See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE) for details. 39 | -------------------------------------------------------------------------------- /benchmarks/bus.rs: -------------------------------------------------------------------------------- 1 | extern crate bus; 2 | extern crate crossbeam; 3 | 4 | use bus::Bus; 5 | use shared::message; 6 | 7 | mod shared; 8 | 9 | const MESSAGES: usize = 5_000_000; 10 | 11 | fn seq(cap: usize) { 12 | let mut tx = Bus::new(cap); 13 | let mut rx = tx.add_rx(); 14 | 15 | for i in 0..MESSAGES { 16 | tx.broadcast(message(i)); 17 | } 18 | 19 | for _ in 0..MESSAGES { 20 | rx.recv().unwrap(); 21 | } 22 | } 23 | 24 | fn spsc(cap: usize) { 25 | let mut tx = Bus::new(cap); 26 | let mut rx = tx.add_rx(); 27 | 28 | crossbeam::scope(|scope| { 29 | scope.spawn(|| { 30 | for i in 0..MESSAGES { 31 | tx.broadcast(message(i)); 32 | } 33 | }); 34 | 35 | for _ in 0..MESSAGES { 36 | rx.recv().unwrap(); 37 | } 38 | }); 39 | } 40 | 41 | fn main() { 42 | macro_rules! run { 43 | ($name:expr, $f:expr) => { 44 | let now = ::std::time::Instant::now(); 45 | $f; 46 | let elapsed = now.elapsed(); 47 | println!( 48 | "{:25} {:15} {:7.3} sec", 49 | $name, 50 | "Rust bus", 51 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 52 | ); 53 | } 54 | } 55 | 56 | run!("bounded1_spsc", spsc(1)); 57 | 58 | run!("bounded_seq", seq(MESSAGES)); 59 | run!("bounded_spsc", spsc(MESSAGES)); 60 | } 61 | -------------------------------------------------------------------------------- /examples/stopwatch.rs: -------------------------------------------------------------------------------- 1 | //! Prints the elapsed time every 1 second and quits on Ctrl+C. 2 | 3 | #[macro_use] 4 | extern crate crossbeam_channel; 5 | extern crate signal_hook; 6 | 7 | use std::io; 8 | use std::time::{Duration, Instant}; 9 | use std::thread; 10 | 11 | use crossbeam_channel::{tick, unbounded, Receiver}; 12 | use signal_hook::SIGINT; 13 | use signal_hook::iterator::Signals; 14 | 15 | // Creates a channel that gets a message every time `SIGINT` is signalled. 16 | fn sigint_notifier() -> io::Result> { 17 | let (s, r) = unbounded(); 18 | let signals = Signals::new(&[SIGINT])?; 19 | 20 | thread::spawn(move || { 21 | for _ in signals.forever() { 22 | if s.send(()).is_err() { 23 | break; 24 | } 25 | } 26 | }); 27 | 28 | Ok(r) 29 | } 30 | 31 | // Prints the elapsed time. 32 | fn show(dur: Duration) { 33 | println!("Elapsed: {}.{:03} sec", dur.as_secs(), dur.subsec_nanos() / 1_000_000); 34 | } 35 | 36 | fn main() { 37 | let start = Instant::now(); 38 | let update = tick(Duration::from_secs(1)); 39 | let ctrl_c = sigint_notifier().unwrap(); 40 | 41 | loop { 42 | select! { 43 | recv(update) -> _ => { 44 | show(start.elapsed()); 45 | } 46 | recv(ctrl_c) -> _ => { 47 | println!(); 48 | println!("Goodbye!"); 49 | show(start.elapsed()); 50 | break; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/thread_locals.rs: -------------------------------------------------------------------------------- 1 | //! Tests that make sure accessing thread-locals while exiting the thread doesn't cause panics. 2 | 3 | extern crate crossbeam; 4 | #[macro_use] 5 | extern crate crossbeam_channel; 6 | 7 | use std::thread; 8 | use std::time::Duration; 9 | 10 | use crossbeam_channel::unbounded; 11 | 12 | fn ms(ms: u64) -> Duration { 13 | Duration::from_millis(ms) 14 | } 15 | 16 | #[test] 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 = Foo; 34 | } 35 | 36 | let (s, r) = unbounded::<()>(); 37 | 38 | crossbeam::scope(|scope| { 39 | scope.spawn(|| { 40 | // First initialize `FOO`, then the thread-locals related to crossbeam-channel and 41 | // crossbeam-epoch. 42 | FOO.with(|_| ()); 43 | r.recv().unwrap(); 44 | // At thread exit, the crossbeam-related thread-locals get dropped first and `FOO` is 45 | // dropped last. 46 | }); 47 | 48 | scope.spawn(|| { 49 | thread::sleep(ms(100)); 50 | s.send(()).unwrap(); 51 | }); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /benchmarks/crossbeam-deque.rs: -------------------------------------------------------------------------------- 1 | extern crate crossbeam; 2 | extern crate crossbeam_deque as deque; 3 | 4 | use std::thread; 5 | 6 | use deque::{Deque, Steal}; 7 | 8 | const MESSAGES: usize = 5_000_000; 9 | 10 | fn seq() { 11 | let tx = Deque::new(); 12 | let rx = tx.stealer(); 13 | 14 | for i in 0..MESSAGES { 15 | tx.push(i as i32); 16 | } 17 | 18 | for _ in 0..MESSAGES { 19 | match rx.steal() { 20 | Steal::Data(_) => break, 21 | Steal::Retry => panic!(), 22 | Steal::Empty => panic!(), 23 | } 24 | } 25 | } 26 | 27 | fn spsc() { 28 | let tx = Deque::new(); 29 | let rx = tx.stealer(); 30 | 31 | crossbeam::scope(|scope| { 32 | scope.spawn(move || { 33 | for i in 0..MESSAGES { 34 | tx.push(i as i32); 35 | } 36 | }); 37 | 38 | scope.spawn(move || { 39 | for _ in 0..MESSAGES { 40 | loop { 41 | match rx.steal() { 42 | Steal::Data(_) => break, 43 | Steal::Retry | Steal::Empty => thread::yield_now(), 44 | } 45 | } 46 | } 47 | }); 48 | }); 49 | } 50 | 51 | fn main() { 52 | macro_rules! run { 53 | ($name:expr, $f:expr) => { 54 | let now = ::std::time::Instant::now(); 55 | $f; 56 | let elapsed = now.elapsed(); 57 | println!( 58 | "{:25} {:15} {:7.3} sec", 59 | $name, 60 | "Rust crossbeam-deque", 61 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 62 | ); 63 | } 64 | } 65 | 66 | run!("unbounded_seq", seq()); 67 | run!("unbounded_spsc", spsc()); 68 | } 69 | -------------------------------------------------------------------------------- /tests/never.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the never channel flavor. 2 | 3 | extern crate crossbeam; 4 | #[macro_use] 5 | extern crate crossbeam_channel; 6 | extern crate rand; 7 | 8 | use std::thread; 9 | use std::time::{Duration, Instant}; 10 | 11 | use crossbeam_channel::{never, tick, unbounded}; 12 | 13 | fn ms(ms: u64) -> Duration { 14 | Duration::from_millis(ms) 15 | } 16 | 17 | #[test] 18 | fn smoke() { 19 | select! { 20 | recv(never::()) -> _ => panic!(), 21 | default => {} 22 | } 23 | } 24 | 25 | #[test] 26 | fn optional() { 27 | let (s, r) = unbounded::(); 28 | s.send(1).unwrap(); 29 | s.send(2).unwrap(); 30 | 31 | let mut r = Some(&r); 32 | select! { 33 | recv(r.unwrap_or(&never())) -> _ => {} 34 | default => panic!(), 35 | } 36 | 37 | r = None; 38 | select! { 39 | recv(r.unwrap_or(&never())) -> _ => panic!(), 40 | default => {} 41 | } 42 | } 43 | 44 | 45 | #[test] 46 | fn tick_n() { 47 | let mut r = tick(ms(100)); 48 | let mut step = 0; 49 | 50 | loop { 51 | select! { 52 | recv(r) -> _ => step += 1, 53 | default(ms(500)) => break, 54 | } 55 | 56 | if step == 10 { 57 | r = never(); 58 | } 59 | } 60 | 61 | assert_eq!(step, 10); 62 | } 63 | 64 | #[test] 65 | fn capacity() { 66 | let r = never::(); 67 | assert_eq!(r.capacity(), Some(0)); 68 | } 69 | 70 | #[test] 71 | fn len_empty_full() { 72 | let r = never::(); 73 | assert_eq!(r.len(), 0); 74 | assert_eq!(r.is_empty(), true); 75 | assert_eq!(r.is_full(), true); 76 | } 77 | 78 | #[test] 79 | fn try_recv() { 80 | let r = never::(); 81 | assert!(r.try_recv().is_err()); 82 | 83 | thread::sleep(ms(100)); 84 | assert!(r.try_recv().is_err()); 85 | } 86 | 87 | #[test] 88 | fn recv_timeout() { 89 | let start = Instant::now(); 90 | let r = never::(); 91 | 92 | assert!(r.recv_timeout(ms(100)).is_err()); 93 | let now = Instant::now(); 94 | assert!(now - start >= ms(100)); 95 | assert!(now - start <= ms(150)); 96 | 97 | assert!(r.recv_timeout(ms(100)).is_err()); 98 | let now = Instant::now(); 99 | assert!(now - start >= ms(200)); 100 | assert!(now - start <= ms(250)); 101 | } 102 | -------------------------------------------------------------------------------- /examples/matching.rs: -------------------------------------------------------------------------------- 1 | //! Using `select!` to send and receive on the same channel at the same time. 2 | //! 3 | //! # Copyright 4 | //! 5 | //! This example is based on the following program in Go. 6 | //! 7 | //! Author: Stefan Nilsson 8 | //! License: Creative Commons Attribution 3.0 Unported License. 9 | //! Sources: 10 | //! - https://web.archive.org/web/20171209034309/https://www.nada.kth.se/~snilsson/concurrency 11 | //! - http://www.nada.kth.se/~snilsson/concurrency/src/matching.go 12 | //! 13 | //! ```go 14 | //! func main() { 15 | //! people := []string{"Anna", "Bob", "Cody", "Dave", "Eva"} 16 | //! match := make(chan string, 1) // Make room for one unmatched send. 17 | //! wg := new(sync.WaitGroup) 18 | //! for _, name := range people { 19 | //! wg.Add(1) 20 | //! go Seek(name, match, wg) 21 | //! } 22 | //! wg.Wait() 23 | //! select { 24 | //! case name := <-match: 25 | //! fmt.Printf("No one received %s’s message.\n", name) 26 | //! default: 27 | //! // There was no pending send operation. 28 | //! } 29 | //! } 30 | //! 31 | //! // Seek either sends or receives, whichever possible, a name on the match 32 | //! // channel and notifies the wait group when done. 33 | //! func Seek(name string, match chan string, wg *sync.WaitGroup) { 34 | //! select { 35 | //! case peer := <-match: 36 | //! fmt.Printf("%s received a message from %s.\n", name, peer) 37 | //! case match <- name: 38 | //! // Wait for someone to receive my message. 39 | //! } 40 | //! wg.Done() 41 | //! } 42 | //! ``` 43 | 44 | extern crate crossbeam; 45 | #[macro_use] 46 | extern crate crossbeam_channel; 47 | 48 | use crossbeam_channel::bounded; 49 | 50 | fn main() { 51 | let people = vec!["Anna", "Bob", "Cody", "Dave", "Eva"]; 52 | let (s, r) = bounded(1); // Make room for one unmatched send. 53 | 54 | // Either send my name into the channel or receive someone else's, whatever happens first. 55 | let seek = |name, s, r| { 56 | select! { 57 | recv(r) -> peer => println!("{} received a message from {}.", name, peer.unwrap()), 58 | send(s, name) -> _ => {}, // Wait for someone to receive my message. 59 | } 60 | }; 61 | 62 | crossbeam::scope(|scope| { 63 | for name in people { 64 | let (s, r) = (s.clone(), r.clone()); 65 | scope.spawn(move || seek(name, s, r)); 66 | } 67 | }); 68 | 69 | // Check if there is a pending send operation. 70 | if let Ok(name) = r.try_recv() { 71 | println!("No one received {}’s message.", name); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /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 context::Context; 9 | use err::{RecvTimeoutError, TryRecvError}; 10 | use select::{Operation, SelectHandle, Token}; 11 | use utils; 12 | 13 | /// This flavor doesn't need a token. 14 | pub type NeverToken = (); 15 | 16 | /// Channel that never delivers messages. 17 | pub struct Channel { 18 | _marker: PhantomData, 19 | } 20 | 21 | impl Channel { 22 | /// Creates a channel that never delivers messages. 23 | #[inline] 24 | pub fn new() -> Self { 25 | Channel { 26 | _marker: PhantomData, 27 | } 28 | } 29 | 30 | /// Attempts to receive a message without blocking. 31 | #[inline] 32 | pub fn try_recv(&self) -> Result { 33 | Err(TryRecvError::Empty) 34 | } 35 | 36 | /// Receives a message from the channel. 37 | #[inline] 38 | pub 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 unsafe fn read(&self, _token: &mut Token) -> Result { 46 | Err(()) 47 | } 48 | 49 | /// Returns `true` if the channel is empty. 50 | #[inline] 51 | pub fn is_empty(&self) -> bool { 52 | true 53 | } 54 | 55 | /// Returns `true` if the channel is full. 56 | #[inline] 57 | pub fn is_full(&self) -> bool { 58 | true 59 | } 60 | 61 | /// Returns the number of messages in the channel. 62 | #[inline] 63 | pub fn len(&self) -> usize { 64 | 0 65 | } 66 | 67 | /// Returns the capacity of the channel. 68 | #[inline] 69 | pub fn capacity(&self) -> Option { 70 | Some(0) 71 | } 72 | } 73 | 74 | impl Clone for Channel { 75 | #[inline] 76 | fn clone(&self) -> Channel { 77 | Channel::new() 78 | } 79 | } 80 | 81 | impl SelectHandle for Channel { 82 | #[inline] 83 | fn try(&self, _token: &mut Token) -> bool { 84 | false 85 | } 86 | 87 | #[inline] 88 | fn retry(&self, _token: &mut Token) -> bool { 89 | false 90 | } 91 | 92 | #[inline] 93 | fn deadline(&self) -> Option { 94 | None 95 | } 96 | 97 | #[inline] 98 | fn register(&self, _token: &mut Token, _oper: Operation, _cx: &Context) -> bool { 99 | true 100 | } 101 | 102 | #[inline] 103 | fn unregister(&self, _oper: Operation) {} 104 | 105 | #[inline] 106 | fn accept(&self, _token: &mut Token, _cx: &Context) -> bool { 107 | false 108 | } 109 | 110 | #[inline] 111 | fn state(&self) -> usize { 112 | 0 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /benchmarks/msqueue.rs: -------------------------------------------------------------------------------- 1 | extern crate crossbeam; 2 | 3 | use crossbeam::sync::MsQueue; 4 | use shared::message; 5 | use std::thread; 6 | 7 | mod shared; 8 | 9 | const MESSAGES: usize = 5_000_000; 10 | const THREADS: usize = 4; 11 | 12 | fn seq() { 13 | let q = MsQueue::new(); 14 | 15 | for i in 0..MESSAGES { 16 | q.push(message(i)); 17 | } 18 | 19 | for _ in 0..MESSAGES { 20 | q.try_pop().unwrap(); 21 | } 22 | } 23 | 24 | fn spsc() { 25 | let q = MsQueue::new(); 26 | 27 | crossbeam::scope(|scope| { 28 | scope.spawn(|| { 29 | for i in 0..MESSAGES { 30 | q.push(message(i)); 31 | } 32 | }); 33 | 34 | for _ in 0..MESSAGES { 35 | loop { 36 | if q.try_pop().is_none() { 37 | thread::yield_now(); 38 | } else { 39 | break; 40 | } 41 | } 42 | } 43 | }); 44 | } 45 | 46 | fn mpsc() { 47 | let q = MsQueue::new(); 48 | 49 | crossbeam::scope(|scope| { 50 | for _ in 0..THREADS { 51 | scope.spawn(|| { 52 | for i in 0..MESSAGES / THREADS { 53 | q.push(message(i)); 54 | } 55 | }); 56 | } 57 | 58 | for _ in 0..MESSAGES { 59 | loop { 60 | if q.try_pop().is_none() { 61 | thread::yield_now(); 62 | } else { 63 | break; 64 | } 65 | } 66 | } 67 | }); 68 | } 69 | 70 | fn mpmc() { 71 | let q = MsQueue::new(); 72 | 73 | crossbeam::scope(|scope| { 74 | for _ in 0..THREADS { 75 | scope.spawn(|| { 76 | for i in 0..MESSAGES / THREADS { 77 | q.push(message(i)); 78 | } 79 | }); 80 | } 81 | 82 | for _ in 0..THREADS { 83 | scope.spawn(|| { 84 | for _ in 0..MESSAGES / THREADS { 85 | loop { 86 | if q.try_pop().is_none() { 87 | thread::yield_now(); 88 | } else { 89 | break; 90 | } 91 | } 92 | } 93 | }); 94 | } 95 | }); 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 msqueue", 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 | -------------------------------------------------------------------------------- /tests/iter.rs: -------------------------------------------------------------------------------- 1 | //! Tests for iteration over receivers. 2 | 3 | extern crate crossbeam; 4 | extern crate crossbeam_channel; 5 | 6 | use crossbeam_channel::unbounded; 7 | 8 | #[test] 9 | fn nested_recv_iter() { 10 | let (s, r) = unbounded::(); 11 | let (total_s, total_r) = unbounded::(); 12 | 13 | crossbeam::scope(|scope| { 14 | scope.spawn(move || { 15 | let mut acc = 0; 16 | for x in r.iter() { 17 | acc += x; 18 | } 19 | total_s.send(acc).unwrap(); 20 | }); 21 | 22 | s.send(3).unwrap(); 23 | s.send(1).unwrap(); 24 | s.send(2).unwrap(); 25 | drop(s); 26 | assert_eq!(total_r.recv().unwrap(), 6); 27 | }); 28 | } 29 | 30 | #[test] 31 | fn recv_iter_break() { 32 | let (s, r) = unbounded::(); 33 | let (count_s, count_r) = unbounded(); 34 | 35 | crossbeam::scope(|scope| { 36 | scope.spawn(move || { 37 | let mut count = 0; 38 | for x in r.iter() { 39 | if count >= 3 { 40 | break; 41 | } else { 42 | count += x; 43 | } 44 | } 45 | count_s.send(count).unwrap(); 46 | }); 47 | 48 | s.send(2).unwrap(); 49 | s.send(2).unwrap(); 50 | s.send(2).unwrap(); 51 | let _ = s.send(2); 52 | drop(s); 53 | assert_eq!(count_r.recv().unwrap(), 4); 54 | }) 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 | crossbeam::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 | assert_eq!(count, 6); 70 | return; 71 | } 72 | } 73 | request_s.send(()).unwrap(); 74 | } 75 | }); 76 | 77 | for _ in request_r.iter() { 78 | if response_s.send(2).is_err() { 79 | break; 80 | } 81 | } 82 | }) 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_eq!(iter.next().is_none(), true); 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_eq!(iter.next().is_none(), true); 110 | } 111 | -------------------------------------------------------------------------------- /benchmarks/segqueue.rs: -------------------------------------------------------------------------------- 1 | extern crate crossbeam; 2 | 3 | use crossbeam::sync::SegQueue; 4 | use shared::message; 5 | use std::thread; 6 | 7 | mod shared; 8 | 9 | const MESSAGES: usize = 5_000_000; 10 | const THREADS: usize = 4; 11 | 12 | fn seq() { 13 | let q = SegQueue::new(); 14 | 15 | for i in 0..MESSAGES { 16 | q.push(message(i)); 17 | } 18 | 19 | for _ in 0..MESSAGES { 20 | q.try_pop().unwrap(); 21 | } 22 | } 23 | 24 | fn spsc() { 25 | let q = SegQueue::new(); 26 | 27 | crossbeam::scope(|scope| { 28 | scope.spawn(|| { 29 | for i in 0..MESSAGES { 30 | q.push(message(i)); 31 | } 32 | }); 33 | 34 | for _ in 0..MESSAGES { 35 | loop { 36 | if q.try_pop().is_none() { 37 | thread::yield_now(); 38 | } else { 39 | break; 40 | } 41 | } 42 | } 43 | }); 44 | } 45 | 46 | fn mpsc() { 47 | let q = SegQueue::new(); 48 | 49 | crossbeam::scope(|scope| { 50 | for _ in 0..THREADS { 51 | scope.spawn(|| { 52 | for i in 0..MESSAGES / THREADS { 53 | q.push(message(i)); 54 | } 55 | }); 56 | } 57 | 58 | for _ in 0..MESSAGES { 59 | loop { 60 | if q.try_pop().is_none() { 61 | thread::yield_now(); 62 | } else { 63 | break; 64 | } 65 | } 66 | } 67 | }); 68 | } 69 | 70 | fn mpmc() { 71 | let q = SegQueue::new(); 72 | 73 | crossbeam::scope(|scope| { 74 | for _ in 0..THREADS { 75 | scope.spawn(|| { 76 | for i in 0..MESSAGES / THREADS { 77 | q.push(message(i)); 78 | } 79 | }); 80 | } 81 | 82 | for _ in 0..THREADS { 83 | scope.spawn(|| { 84 | for _ in 0..MESSAGES / THREADS { 85 | loop { 86 | if q.try_pop().is_none() { 87 | thread::yield_now(); 88 | } else { 89 | break; 90 | } 91 | } 92 | } 93 | }); 94 | } 95 | }); 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 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Miscellaneous utilities. 2 | 3 | use std::cell::Cell; 4 | use std::num::Wrapping; 5 | use std::sync::atomic; 6 | use std::thread; 7 | use std::time::{Duration, Instant}; 8 | 9 | use rand; 10 | 11 | /// A counter that performs exponential backoff in spin loops. 12 | pub struct Backoff(u32); 13 | 14 | impl Backoff { 15 | /// Creates a new `Backoff`. 16 | #[inline] 17 | pub fn new() -> Self { 18 | Backoff(0) 19 | } 20 | 21 | /// Backs off in a spin loop. 22 | /// 23 | /// This method may yield the current processor. Use it in lock-free retry loops. 24 | #[inline] 25 | pub fn spin(&mut self) { 26 | for _ in 0..1 << self.0.min(6) { 27 | atomic::spin_loop_hint(); 28 | } 29 | self.0 = self.0.wrapping_add(1); 30 | } 31 | 32 | /// Backs off in a wait loop. 33 | /// 34 | /// Returns `true` if snoozing has reached a threshold where we should consider parking the 35 | /// thread instead. 36 | /// 37 | /// This method may yield the current processor or the current thread. Use it when waiting on a 38 | /// resource. 39 | #[inline] 40 | pub fn snooze(&mut self) -> bool { 41 | if self.0 <= 6 { 42 | for _ in 0..1 << self.0 { 43 | atomic::spin_loop_hint(); 44 | } 45 | } else { 46 | thread::yield_now(); 47 | } 48 | 49 | self.0 = self.0.wrapping_add(1); 50 | self.0 <= 10 51 | } 52 | } 53 | 54 | /// Randomly shuffles a slice. 55 | pub fn shuffle(v: &mut [T]) { 56 | let len = v.len(); 57 | if len <= 1 { 58 | return; 59 | } 60 | 61 | thread_local! { 62 | static RNG: Cell> = { 63 | let init = rand::random::() | 1; 64 | Cell::new(Wrapping(init)) 65 | } 66 | } 67 | 68 | let _ = RNG.try_with(|rng| { 69 | for i in 1..len { 70 | // This is the 32-bit variant of Xorshift. 71 | // 72 | // Source: https://en.wikipedia.org/wiki/Xorshift 73 | let mut x = rng.get(); 74 | x ^= x << 13; 75 | x ^= x >> 17; 76 | x ^= x << 5; 77 | rng.set(x); 78 | 79 | let x = x.0; 80 | let n = i + 1; 81 | 82 | // This is a fast alternative to `let j = x % n`. 83 | // 84 | // Author: Daniel Lemire 85 | // Source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ 86 | let j = ((x as u64).wrapping_mul(n as u64) >> 32) as u32 as usize; 87 | 88 | v.swap(i, j); 89 | } 90 | }); 91 | } 92 | 93 | /// Sleeps until the deadline, or forever if the deadline isn't specified. 94 | pub fn sleep_until(deadline: Option) { 95 | loop { 96 | match deadline { 97 | None => thread::sleep(Duration::from_secs(1000)), 98 | Some(d) => { 99 | let now = Instant::now(); 100 | if now >= d { 101 | break; 102 | } 103 | thread::sleep(d - now); 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /benchmarks/atomicringqueue.rs: -------------------------------------------------------------------------------- 1 | extern crate atomicring; 2 | extern crate crossbeam; 3 | 4 | use atomicring::AtomicRingQueue; 5 | use shared::message; 6 | use std::thread; 7 | 8 | mod shared; 9 | 10 | const MESSAGES: usize = 5_000_000; 11 | const THREADS: usize = 4; 12 | 13 | fn seq(cap: usize) { 14 | let q = AtomicRingQueue::with_capacity(cap); 15 | 16 | for i in 0..MESSAGES { 17 | loop { 18 | if q.try_push(message(i)).is_ok() { 19 | break; 20 | } else { 21 | thread::yield_now(); 22 | } 23 | } 24 | } 25 | 26 | for _ in 0..MESSAGES { 27 | q.pop(); 28 | } 29 | } 30 | 31 | fn spsc(cap: usize) { 32 | let q = AtomicRingQueue::with_capacity(cap); 33 | 34 | crossbeam::scope(|scope| { 35 | scope.spawn(|| { 36 | for i in 0..MESSAGES { 37 | loop { 38 | if q.try_push(message(i)).is_ok() { 39 | break; 40 | } else { 41 | thread::yield_now(); 42 | } 43 | } 44 | } 45 | }); 46 | 47 | for _ in 0..MESSAGES { 48 | q.pop(); 49 | } 50 | }); 51 | } 52 | 53 | fn mpsc(cap: usize) { 54 | let q = AtomicRingQueue::with_capacity(cap); 55 | 56 | crossbeam::scope(|scope| { 57 | for _ in 0..THREADS { 58 | scope.spawn(|| { 59 | for i in 0..MESSAGES / THREADS { 60 | loop { 61 | if q.try_push(message(i)).is_ok() { 62 | break; 63 | } else { 64 | thread::yield_now(); 65 | } 66 | } 67 | } 68 | }); 69 | } 70 | 71 | for _ in 0..MESSAGES { 72 | q.pop(); 73 | } 74 | }); 75 | } 76 | 77 | fn mpmc(cap: usize) { 78 | let q = AtomicRingQueue::with_capacity(cap); 79 | 80 | crossbeam::scope(|scope| { 81 | for _ in 0..THREADS { 82 | scope.spawn(|| { 83 | for i in 0..MESSAGES / THREADS { 84 | loop { 85 | if q.try_push(message(i)).is_ok() { 86 | break; 87 | } else { 88 | thread::yield_now(); 89 | } 90 | } 91 | } 92 | }); 93 | } 94 | 95 | for _ in 0..THREADS { 96 | scope.spawn(|| { 97 | for _ in 0..MESSAGES / THREADS { 98 | q.pop(); 99 | } 100 | }); 101 | } 102 | }); 103 | } 104 | 105 | fn main() { 106 | macro_rules! run { 107 | ($name:expr, $f:expr) => { 108 | let now = ::std::time::Instant::now(); 109 | $f; 110 | let elapsed = now.elapsed(); 111 | println!( 112 | "{:25} {:15} {:7.3} sec", 113 | $name, 114 | "Rust atomicringqueue", 115 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 116 | ); 117 | } 118 | } 119 | 120 | run!("bounded_mpmc", mpmc(MESSAGES)); 121 | run!("bounded_mpsc", mpsc(MESSAGES)); 122 | run!("bounded_seq", seq(MESSAGES)); 123 | run!("bounded_spsc", spsc(MESSAGES)); 124 | } 125 | -------------------------------------------------------------------------------- /benchmarks/mpmc.rs: -------------------------------------------------------------------------------- 1 | extern crate mpmc; 2 | extern crate crossbeam; 3 | 4 | use shared::message; 5 | use std::thread; 6 | 7 | mod shared; 8 | 9 | const MESSAGES: usize = 5_000_000; 10 | const THREADS: usize = 4; 11 | 12 | fn seq(cap: usize) { 13 | let q = mpmc::Queue::with_capacity(cap); 14 | 15 | for i in 0..MESSAGES { 16 | loop { 17 | if q.push(message(i)).is_ok() { 18 | break; 19 | } else { 20 | thread::yield_now(); 21 | } 22 | } 23 | } 24 | 25 | for _ in 0..MESSAGES { 26 | q.pop().unwrap(); 27 | } 28 | } 29 | 30 | fn spsc(cap: usize) { 31 | let q = mpmc::Queue::with_capacity(cap); 32 | 33 | crossbeam::scope(|scope| { 34 | scope.spawn(|| { 35 | for i in 0..MESSAGES { 36 | loop { 37 | if q.push(message(i)).is_ok() { 38 | break; 39 | } else { 40 | thread::yield_now(); 41 | } 42 | } 43 | } 44 | }); 45 | 46 | for _ in 0..MESSAGES { 47 | loop { 48 | if q.pop().is_none() { 49 | thread::yield_now(); 50 | } else { 51 | break; 52 | } 53 | } 54 | } 55 | }); 56 | } 57 | 58 | fn mpsc(cap: usize) { 59 | let q = mpmc::Queue::with_capacity(cap); 60 | 61 | crossbeam::scope(|scope| { 62 | for _ in 0..THREADS { 63 | scope.spawn(|| { 64 | for i in 0..MESSAGES / THREADS { 65 | loop { 66 | if q.push(message(i)).is_ok() { 67 | break; 68 | } else { 69 | thread::yield_now(); 70 | } 71 | } 72 | } 73 | }); 74 | } 75 | 76 | for _ in 0..MESSAGES { 77 | loop { 78 | if q.pop().is_none() { 79 | thread::yield_now(); 80 | } else { 81 | break; 82 | } 83 | } 84 | } 85 | }); 86 | } 87 | 88 | fn mpmc(cap: usize) { 89 | let q = mpmc::Queue::with_capacity(cap); 90 | 91 | crossbeam::scope(|scope| { 92 | for _ in 0..THREADS { 93 | scope.spawn(|| { 94 | for i in 0..MESSAGES / THREADS { 95 | loop { 96 | if q.push(message(i)).is_ok() { 97 | break; 98 | } else { 99 | thread::yield_now(); 100 | } 101 | } 102 | } 103 | }); 104 | } 105 | 106 | for _ in 0..THREADS { 107 | scope.spawn(|| { 108 | for _ in 0..MESSAGES / THREADS { 109 | loop { 110 | if q.pop().is_none() { 111 | thread::yield_now(); 112 | } else { 113 | break; 114 | } 115 | } 116 | } 117 | }); 118 | } 119 | }); 120 | } 121 | 122 | fn main() { 123 | macro_rules! run { 124 | ($name:expr, $f:expr) => { 125 | let now = ::std::time::Instant::now(); 126 | $f; 127 | let elapsed = now.elapsed(); 128 | println!( 129 | "{:25} {:15} {:7.3} sec", 130 | $name, 131 | "Rust mpmc", 132 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 133 | ); 134 | } 135 | } 136 | 137 | run!("bounded_mpmc", mpmc(MESSAGES)); 138 | run!("bounded_mpsc", mpsc(MESSAGES)); 139 | run!("bounded_seq", seq(MESSAGES)); 140 | run!("bounded_spsc", spsc(MESSAGES)); 141 | } 142 | -------------------------------------------------------------------------------- /benchmarks/plot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import sys 4 | import matplotlib.pyplot as plt 5 | import matplotlib.patches as mpatches 6 | 7 | results = [] 8 | for f in sys.argv[1:]: 9 | with open(f) as f: 10 | for line in f.readlines(): 11 | test, lang, impl, secs, _ = line.split() 12 | results.append((test, lang, impl, float(secs))) 13 | 14 | fig = plt.figure(figsize=(10, 10)) 15 | 16 | 17 | def plot(subplot, title, prefix, runs): 18 | runs.reverse() 19 | 20 | ys = [6 * (i+1) for i in xrange(len(runs))] 21 | ax = fig.add_subplot(subplot) 22 | ax.set_title(title) 23 | ax.set_yticks(ys) 24 | ax.set_yticklabels(runs) 25 | ax.tick_params(which='major', length=0) 26 | ax.set_xlabel('seconds') 27 | 28 | go = [0] * len(runs) 29 | mpsc = [0] * len(runs) 30 | futures_channel = [0] * len(runs) 31 | chan = [0] * len(runs) 32 | crossbeam_channel = [0] * len(runs) 33 | 34 | for (i, run) in enumerate(runs): 35 | for (test, lang, impl, secs) in results: 36 | if test == prefix + '_' + run: 37 | if lang == 'Go' and impl == 'chan': 38 | go[i] = secs 39 | if lang == 'Rust' and impl == 'mpsc': 40 | mpsc[i] = secs 41 | if lang == 'Rust' and impl == 'futures-channel': 42 | futures_channel[i] = secs 43 | if lang == 'Rust' and impl == 'chan': 44 | chan[i] = secs 45 | if lang == 'Rust' and impl == 'crossbeam-channel': 46 | crossbeam_channel[i] = secs 47 | 48 | opts = dict(height=0.7, align='center') 49 | ax.barh([y-2 for y in ys], go, color='skyblue', **opts) 50 | ax.barh([y-1 for y in ys], crossbeam_channel, color='red', **opts) 51 | ax.barh([y+0 for y in ys], chan, color='orange', **opts) 52 | ax.barh([y+1 for y in ys], mpsc, color='black', **opts) 53 | ax.barh([y+2 for y in ys], futures_channel, color='blue', **opts) 54 | 55 | m = int(max(go + mpsc + futures_channel + chan + crossbeam_channel) * 1.3) 56 | if m < 10: 57 | ax.set_xticks(range(m + 1)) 58 | elif m < 50: 59 | ax.set_xticks([x*5 for x in range(m / 5 + 1)]) 60 | elif m < 100: 61 | ax.set_xticks([x*10 for x in range(m / 10 + 1)]) 62 | elif m < 100: 63 | ax.set_xticks([x*20 for x in range(m / 20 + 1)]) 64 | else: 65 | ax.set_xticks([x*100 for x in range(m / 100 + 1)]) 66 | 67 | for (x, y) in zip(go, ys): 68 | if x > 0: 69 | ax.text(x+m/200., y-2-0.3, 'Go', fontsize=9) 70 | for (x, y) in zip(crossbeam_channel, ys): 71 | if x > 0: 72 | ax.text(x+m/200., y-1-0.3, 'crossbeam-channel', fontsize=9) 73 | for (x, y) in zip(chan, ys): 74 | if x > 0: 75 | ax.text(x+m/200., y+0-0.3, 'chan', fontsize=9) 76 | for (x, y) in zip(mpsc, ys): 77 | if x > 0: 78 | ax.text(x+m/200., y+1-0.3, 'mpsc', fontsize=9) 79 | for (x, y) in zip(futures_channel, ys): 80 | if x > 0: 81 | ax.text(x+m/200., y+2-0.3, 'futures-channel', fontsize=9) 82 | 83 | plot( 84 | 221, 85 | "Bounded channel of capacity 0", 86 | 'bounded0', 87 | ['spsc', 'mpsc', 'mpmc', 'select_rx', 'select_both'], 88 | ) 89 | 90 | plot( 91 | 222, 92 | "Bounded channel of capacity 1", 93 | 'bounded1', 94 | ['spsc', 'mpsc', 'mpmc', 'select_rx', 'select_both'], 95 | ) 96 | 97 | plot( 98 | 223, 99 | "Bounded channel of capacity N", 100 | 'bounded', 101 | ['seq', 'spsc', 'mpsc', 'mpmc', 'select_rx', 'select_both'], 102 | ) 103 | 104 | plot( 105 | 224, 106 | "Unbounded channel", 107 | 'unbounded', 108 | ['seq', 'spsc', 'mpsc', 'mpmc', 'select_rx', 'select_both'], 109 | ) 110 | 111 | plt.subplots_adjust( 112 | top=0.95, 113 | bottom=0.05, 114 | left=0.1, 115 | right=0.95, 116 | wspace=0.3, 117 | hspace=0.2, 118 | ) 119 | plt.savefig('plot.png') 120 | # plt.show() 121 | -------------------------------------------------------------------------------- /benchmarks/atomicring.rs: -------------------------------------------------------------------------------- 1 | extern crate atomicring; 2 | extern crate crossbeam; 3 | 4 | use atomicring::AtomicRingBuffer; 5 | use shared::message; 6 | use std::thread; 7 | 8 | mod shared; 9 | 10 | const MESSAGES: usize = 5_000_000; 11 | const THREADS: usize = 4; 12 | 13 | fn seq(cap: usize) { 14 | let q = AtomicRingBuffer::with_capacity(cap); 15 | 16 | for i in 0..MESSAGES { 17 | loop { 18 | if q.try_push(message(i)).is_ok() { 19 | break; 20 | } else { 21 | thread::yield_now(); 22 | } 23 | } 24 | } 25 | 26 | for _ in 0..MESSAGES { 27 | q.try_pop().unwrap(); 28 | } 29 | } 30 | 31 | fn spsc(cap: usize) { 32 | let q = AtomicRingBuffer::with_capacity(cap); 33 | 34 | crossbeam::scope(|scope| { 35 | scope.spawn(|| { 36 | for i in 0..MESSAGES { 37 | loop { 38 | if q.try_push(message(i)).is_ok() { 39 | break; 40 | } else { 41 | thread::yield_now(); 42 | } 43 | } 44 | } 45 | }); 46 | 47 | for _ in 0..MESSAGES { 48 | loop { 49 | if q.try_pop().is_none() { 50 | thread::yield_now(); 51 | } else { 52 | break; 53 | } 54 | } 55 | } 56 | }); 57 | } 58 | 59 | fn mpsc(cap: usize) { 60 | let q = AtomicRingBuffer::with_capacity(cap); 61 | 62 | crossbeam::scope(|scope| { 63 | for _ in 0..THREADS { 64 | scope.spawn(|| { 65 | for i in 0..MESSAGES / THREADS { 66 | loop { 67 | if q.try_push(message(i)).is_ok() { 68 | break; 69 | } else { 70 | thread::yield_now(); 71 | } 72 | } 73 | } 74 | }); 75 | } 76 | 77 | for _ in 0..MESSAGES { 78 | loop { 79 | if q.try_pop().is_none() { 80 | thread::yield_now(); 81 | } else { 82 | break; 83 | } 84 | } 85 | } 86 | }); 87 | } 88 | 89 | fn mpmc(cap: usize) { 90 | let q = AtomicRingBuffer::with_capacity(cap); 91 | 92 | crossbeam::scope(|scope| { 93 | for _ in 0..THREADS { 94 | scope.spawn(|| { 95 | for i in 0..MESSAGES / THREADS { 96 | loop { 97 | if q.try_push(message(i)).is_ok() { 98 | break; 99 | } else { 100 | thread::yield_now(); 101 | } 102 | } 103 | } 104 | }); 105 | } 106 | for _ in 0..THREADS { 107 | scope.spawn(|| { 108 | for _ in 0..MESSAGES / THREADS { 109 | loop { 110 | if q.try_pop().is_none() { 111 | thread::yield_now(); 112 | } else { 113 | break; 114 | } 115 | } 116 | } 117 | }); 118 | } 119 | }); 120 | } 121 | 122 | fn main() { 123 | macro_rules! run { 124 | ($name:expr, $f:expr) => { 125 | let now = ::std::time::Instant::now(); 126 | $f; 127 | let elapsed = now.elapsed(); 128 | println!( 129 | "{:25} {:15} {:7.3} sec", 130 | $name, 131 | "Rust atomicring", 132 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 133 | ); 134 | } 135 | } 136 | 137 | run!("bounded_mpmc", mpmc(MESSAGES)); 138 | run!("bounded_mpsc", mpsc(MESSAGES)); 139 | run!("bounded_seq", seq(MESSAGES)); 140 | run!("bounded_spsc", spsc(MESSAGES)); 141 | } 142 | -------------------------------------------------------------------------------- /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 = int 10 | 11 | func message(msg int) Message { 12 | return msg 13 | } 14 | 15 | func seq(cap int) { 16 | var c = make(chan Message, cap) 17 | 18 | for i := 0; i < MESSAGES; i++ { 19 | c <- i 20 | } 21 | 22 | for i := 0; i < MESSAGES; i++ { 23 | <-c 24 | } 25 | } 26 | 27 | func spsc(cap int) { 28 | var c = make(chan Message, cap) 29 | var done = make(chan bool) 30 | 31 | go func() { 32 | for i := 0; i < MESSAGES; i++ { 33 | c <- i 34 | } 35 | done <- true 36 | }() 37 | 38 | for i := 0; i < MESSAGES; i++ { 39 | <-c 40 | } 41 | 42 | <-done 43 | } 44 | 45 | func mpsc(cap int) { 46 | var c = make(chan Message, cap) 47 | var done = make(chan bool) 48 | 49 | for t := 0; t < THREADS; t++ { 50 | go func() { 51 | for i := 0; i < MESSAGES / THREADS; i++ { 52 | c <- i 53 | } 54 | done <- true 55 | }() 56 | } 57 | 58 | for i := 0; i < MESSAGES; i++ { 59 | <-c 60 | } 61 | 62 | for t := 0; t < THREADS; t++ { 63 | <-done 64 | } 65 | } 66 | 67 | func mpmc(cap int) { 68 | var c = make(chan Message, cap) 69 | var done = make(chan bool) 70 | 71 | for t := 0; t < THREADS; t++ { 72 | go func() { 73 | for i := 0; i < MESSAGES / THREADS; i++ { 74 | c <- i 75 | } 76 | done <- true 77 | }() 78 | 79 | } 80 | 81 | for t := 0; t < THREADS; t++ { 82 | go func() { 83 | for i := 0; i < MESSAGES / THREADS; i++ { 84 | <-c 85 | } 86 | done <- true 87 | }() 88 | } 89 | 90 | for t := 0; t < THREADS; t++ { 91 | <-done 92 | <-done 93 | } 94 | } 95 | 96 | func select_rx(cap int) { 97 | if THREADS != 4 { 98 | panic("assumed there are 4 threads") 99 | } 100 | 101 | var c0 = make(chan Message, cap) 102 | var c1 = make(chan Message, cap) 103 | var c2 = make(chan Message, cap) 104 | var c3 = make(chan Message, cap) 105 | var done = make(chan bool) 106 | 107 | var producer = func(c chan Message) { 108 | for i := 0; i < MESSAGES / THREADS; i++ { 109 | c <- i 110 | } 111 | done <- true 112 | } 113 | go producer(c0) 114 | go producer(c1) 115 | go producer(c2) 116 | go producer(c3) 117 | 118 | for i := 0; i < MESSAGES; i++ { 119 | select { 120 | case <-c0: 121 | case <-c1: 122 | case <-c2: 123 | case <-c3: 124 | } 125 | } 126 | 127 | for t := 0; t < THREADS; t++ { 128 | <-done 129 | } 130 | } 131 | 132 | func select_both(cap int) { 133 | if THREADS != 4 { 134 | panic("assumed there are 4 threads") 135 | } 136 | 137 | var c0 = make(chan Message, cap) 138 | var c1 = make(chan Message, cap) 139 | var c2 = make(chan Message, cap) 140 | var c3 = make(chan Message, cap) 141 | var done = make(chan bool) 142 | 143 | var producer = func(c chan Message) { 144 | for i := 0; i < MESSAGES / THREADS; i++ { 145 | c <- i 146 | } 147 | done <- true 148 | } 149 | go producer(c0) 150 | go producer(c1) 151 | go producer(c2) 152 | go producer(c3) 153 | 154 | for t := 0; t < THREADS; t++ { 155 | go func() { 156 | for i := 0; i < MESSAGES / THREADS; i++ { 157 | select { 158 | case <-c0: 159 | case <-c1: 160 | case <-c2: 161 | case <-c3: 162 | } 163 | } 164 | done <- true 165 | }() 166 | } 167 | 168 | for t := 0; t < THREADS; t++ { 169 | <-done 170 | <-done 171 | } 172 | } 173 | 174 | func run(name string, f func(int), cap int) { 175 | var now = time.Now() 176 | f(cap) 177 | var elapsed = time.Now().Sub(now) 178 | fmt.Printf("%-25v %-15v %7.3f sec\n", name, "Go chan", float64(elapsed) / float64(time.Second)) 179 | } 180 | 181 | func main() { 182 | run("bounded0_mpmc", mpmc, 0) 183 | run("bounded0_mpsc", mpsc, 0) 184 | run("bounded0_select_both", select_both, 0) 185 | run("bounded0_select_rx", select_rx, 0) 186 | run("bounded0_spsc", spsc, 0) 187 | 188 | run("bounded1_mpmc", mpmc, 1) 189 | run("bounded1_mpsc", mpsc, 1) 190 | run("bounded1_select_both", select_both, 1) 191 | run("bounded1_select_rx", select_rx, 1) 192 | run("bounded1_spsc", spsc, 1) 193 | 194 | run("bounded_mpmc", mpmc, MESSAGES) 195 | run("bounded_mpsc", mpsc, MESSAGES) 196 | run("bounded_select_both", select_both, MESSAGES) 197 | run("bounded_select_rx", select_rx, MESSAGES) 198 | run("bounded_seq", seq, MESSAGES) 199 | run("bounded_spsc", spsc, MESSAGES) 200 | } 201 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.3.0] - 2018-11-04 10 | ### Added 11 | - Add a special `never` channel type. 12 | 13 | ### Changed 14 | - Dropping all receivers now closes the channel. 15 | - The interface of sending and receiving methods is now very similar to those in v0.1. 16 | - The syntax for `send` in `select!` is now `send(sender, msg) -> res => body`. 17 | - The syntax for `recv` in `select!` is now `recv(receiver) -> res => body`. 18 | - New, more efficient interface for `Select` without callbacks. 19 | - Timeouts can be specified in `select!`. 20 | 21 | ## [0.2.6] - 2018-09-17 22 | ### Added 23 | - `Select` struct that can add cases dynamically. 24 | - More documentation (in particular, the FAQ section). 25 | 26 | ### Changed 27 | - Optimize contended sends/receives in unbounded channels. 28 | 29 | ## [0.2.5] - 2018-09-11 30 | ### Changed 31 | - Use `LocalKey::try_with` instead of `LocalKey::with`. 32 | 33 | ### Removed 34 | - Remove helper macros `__crossbeam_channel*`. 35 | 36 | ## [0.2.4] - 2018-08-02 37 | ### Changed 38 | - Make `select!` linearizable with other channel operations. 39 | - Update `crossbeam-utils` to `0.5.0`. 40 | - Update `parking_lot` to `0.6.3`. 41 | 42 | ### Removed 43 | - Remove Mac OS X tests. 44 | 45 | ## [0.2.3] - 2018-07-21 46 | ### Added 47 | - Add Mac OS X tests. 48 | 49 | ### Changed 50 | - Lower some memory orderings. 51 | 52 | ### Fixed 53 | - Eliminate calls to `mem::unitialized`, which caused bugs with ZST. 54 | 55 | ## [0.2.2] - 2018-07-10 56 | ### Added 57 | - Add more tests. 58 | 59 | ### Changed 60 | - Update `crossbeam-epoch` to 0.5.0 61 | - Initialize the RNG seed to a random value. 62 | - Replace `libc::abort` with `std::process::abort`. 63 | 64 | ### Fixed 65 | - Ignore clippy warnings in `select!`. 66 | - Better interaction of `select!` with the NLL borrow checker. 67 | 68 | ## [0.2.1] - 2018-06-12 69 | ### Changed 70 | - Fix compilation errors when using `select!` with `#[deny(unsafe_code)]`. 71 | 72 | ## [0.2.0] - 2018-06-11 73 | ### Added 74 | - Implement `IntoIterator` for `Receiver`. 75 | - Add a new `select!` macro. 76 | - Add special channels `after` and `tick`. 77 | 78 | ### Changed 79 | - Dropping receivers doesn't close the channel anymore. 80 | - Change the signature of `recv`, `send`, and `try_recv`. 81 | 82 | ### Removed 83 | - Remove `Sender::is_closed` and `Receiver::is_closed`. 84 | - Remove `Sender::close` and `Receiver::close`. 85 | - Remove `Sender::send_timeout` and `Receiver::recv_timeout`. 86 | - Remove `Sender::try_send`. 87 | - Remove `Select` and `select_loop!`. 88 | - Remove all error types. 89 | - Remove `Iter`, `TryIter`, and `IntoIter`. 90 | - Remove the `nightly` feature. 91 | - Remove ordering operators for `Sender` and `Receiver`. 92 | 93 | ## [0.1.3] - 2018-05-23 94 | ### Added 95 | - Add `Sender::disconnect` and `Receiver::disconnect`. 96 | - Implement comparison operators for `Sender` and `Receiver`. 97 | - Allow arbitrary patterns in place of `msg` in `recv(r, msg)`. 98 | - Add a few conversion impls between error types. 99 | - Add benchmarks for `atomicring` and `mpmc`. 100 | - Add benchmarks for different message sizes. 101 | 102 | ### Changed 103 | - Documentation improvements. 104 | - Update `crossbeam-epoch` to 0.4.0 105 | - Update `crossbeam-utils` to 0.3.0 106 | - Update `parking_lot` to 0.5 107 | - Update `rand` to 0.4 108 | 109 | ## [0.1.2] - 2017-12-12 110 | ### Added 111 | - Allow conditional cases in `select_loop!` macro. 112 | 113 | ### Fixed 114 | - Fix typos in documentation. 115 | - Fix deadlock in selection when all channels are disconnected and a timeout is specified. 116 | 117 | ## [0.1.1] - 2017-11-27 118 | ### Added 119 | - Implement `Debug` for `Sender`, `Receiver`, `Iter`, `TryIter`, `IntoIter`, and `Select`. 120 | - Implement `Default` for `Select`. 121 | 122 | ## 0.1.0 - 2017-11-26 123 | ### Added 124 | - First implementation of the channels. 125 | - Add `select_loop!` macro by @TimNN. 126 | 127 | [Unreleased]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.3.0...HEAD 128 | [0.3.0]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.2.6...v0.3.0 129 | [0.2.6]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.2.5...v0.2.6 130 | [0.2.5]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.2.4...v0.2.5 131 | [0.2.4]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.2.3...v0.2.4 132 | [0.2.3]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.2.2...v0.2.3 133 | [0.2.2]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.2.1...v0.2.2 134 | [0.2.1]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.2.0...v0.2.1 135 | [0.2.0]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.1.3...v0.2.0 136 | [0.1.3]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.1.2...v0.1.3 137 | [0.1.2]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.1.1...v0.1.2 138 | [0.1.1]: https://github.com/crossbeam-rs/crossbeam-channel/compare/v0.1.0...v0.1.1 139 | -------------------------------------------------------------------------------- /benchmarks/crossbeam-channel.rs: -------------------------------------------------------------------------------- 1 | extern crate crossbeam; 2 | extern crate crossbeam_channel; 3 | 4 | use crossbeam_channel::{bounded, unbounded, Receiver, Select, Sender}; 5 | use shared::message; 6 | 7 | mod shared; 8 | 9 | const MESSAGES: usize = 5_000_000; 10 | const THREADS: usize = 4; 11 | 12 | fn new(cap: Option) -> (Sender, Receiver) { 13 | match cap { 14 | None => unbounded(), 15 | Some(cap) => bounded(cap), 16 | } 17 | } 18 | 19 | fn seq(cap: Option) { 20 | let (tx, rx) = new(cap); 21 | 22 | for i in 0..MESSAGES { 23 | tx.send(message(i)).unwrap(); 24 | } 25 | 26 | for _ in 0..MESSAGES { 27 | rx.recv().unwrap(); 28 | } 29 | } 30 | 31 | fn spsc(cap: Option) { 32 | let (tx, rx) = new(cap); 33 | 34 | crossbeam::scope(|scope| { 35 | scope.spawn(|| { 36 | for i in 0..MESSAGES { 37 | tx.send(message(i)).unwrap(); 38 | } 39 | }); 40 | 41 | for _ in 0..MESSAGES { 42 | rx.recv().unwrap(); 43 | } 44 | }); 45 | } 46 | 47 | fn mpsc(cap: Option) { 48 | let (tx, rx) = new(cap); 49 | 50 | crossbeam::scope(|scope| { 51 | for _ in 0..THREADS { 52 | scope.spawn(|| { 53 | for i in 0..MESSAGES / THREADS { 54 | tx.send(message(i)).unwrap(); 55 | } 56 | }); 57 | } 58 | 59 | for _ in 0..MESSAGES { 60 | rx.recv().unwrap(); 61 | } 62 | }); 63 | } 64 | 65 | fn mpmc(cap: Option) { 66 | let (tx, rx) = new(cap); 67 | 68 | crossbeam::scope(|scope| { 69 | for _ in 0..THREADS { 70 | scope.spawn(|| { 71 | for i in 0..MESSAGES / THREADS { 72 | tx.send(message(i)).unwrap(); 73 | } 74 | }); 75 | } 76 | 77 | for _ in 0..THREADS { 78 | scope.spawn(|| { 79 | for _ in 0..MESSAGES / THREADS { 80 | rx.recv().unwrap(); 81 | } 82 | }); 83 | } 84 | }); 85 | } 86 | 87 | fn select_rx(cap: Option) { 88 | let chans = (0..THREADS).map(|_| new(cap)).collect::>(); 89 | 90 | crossbeam::scope(|scope| { 91 | for (tx, _) in &chans { 92 | let tx = tx.clone(); 93 | scope.spawn(move || { 94 | for i in 0..MESSAGES / THREADS { 95 | tx.send(message(i)).unwrap(); 96 | } 97 | }); 98 | } 99 | 100 | for _ in 0..MESSAGES { 101 | let mut sel = Select::new(); 102 | for (_, rx) in &chans { 103 | sel.recv(rx); 104 | } 105 | let case = sel.select(); 106 | let index = case.index(); 107 | case.recv(&chans[index].1).unwrap(); 108 | } 109 | }); 110 | } 111 | 112 | fn select_both(cap: Option) { 113 | let chans = (0..THREADS).map(|_| new(cap)).collect::>(); 114 | 115 | crossbeam::scope(|scope| { 116 | for _ in 0..THREADS { 117 | scope.spawn(|| { 118 | for i in 0..MESSAGES / THREADS { 119 | let mut sel = Select::new(); 120 | for (tx, _) in &chans { 121 | sel.send(tx); 122 | } 123 | let case = sel.select(); 124 | let index = case.index(); 125 | case.send(&chans[index].0, message(i)).unwrap(); 126 | } 127 | }); 128 | } 129 | 130 | for _ in 0..THREADS { 131 | scope.spawn(|| { 132 | for _ in 0..MESSAGES / THREADS { 133 | let mut sel = Select::new(); 134 | for (_, rx) in &chans { 135 | sel.recv(rx); 136 | } 137 | let case = sel.select(); 138 | let index = case.index(); 139 | case.recv(&chans[index].1).unwrap(); 140 | } 141 | }); 142 | } 143 | }); 144 | } 145 | 146 | fn main() { 147 | macro_rules! run { 148 | ($name:expr, $f:expr) => { 149 | let now = ::std::time::Instant::now(); 150 | $f; 151 | let elapsed = now.elapsed(); 152 | println!( 153 | "{:25} {:15} {:7.3} sec", 154 | $name, 155 | "Rust crossbeam-channel", 156 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 157 | ); 158 | } 159 | } 160 | 161 | run!("bounded0_mpmc", mpmc(Some(0))); 162 | run!("bounded0_mpsc", mpsc(Some(0))); 163 | run!("bounded0_select_both", select_both(Some(0))); 164 | run!("bounded0_select_rx", select_rx(Some(0))); 165 | run!("bounded0_spsc", spsc(Some(0))); 166 | 167 | run!("bounded1_mpmc", mpmc(Some(1))); 168 | run!("bounded1_mpsc", mpsc(Some(1))); 169 | run!("bounded1_select_both", select_both(Some(1))); 170 | run!("bounded1_select_rx", select_rx(Some(1))); 171 | run!("bounded1_spsc", spsc(Some(1))); 172 | 173 | run!("bounded_mpmc", mpmc(Some(MESSAGES))); 174 | run!("bounded_mpsc", mpsc(Some(MESSAGES))); 175 | run!("bounded_select_both", select_both(Some(MESSAGES))); 176 | run!("bounded_select_rx", select_rx(Some(MESSAGES))); 177 | run!("bounded_seq", seq(Some(MESSAGES))); 178 | run!("bounded_spsc", spsc(Some(MESSAGES))); 179 | 180 | run!("unbounded_mpmc", mpmc(None)); 181 | run!("unbounded_mpsc", mpsc(None)); 182 | run!("unbounded_select_both", select_both(None)); 183 | run!("unbounded_select_rx", select_rx(None)); 184 | run!("unbounded_seq", seq(None)); 185 | run!("unbounded_spsc", spsc(None)); 186 | } 187 | -------------------------------------------------------------------------------- /benchmarks/mpsc.rs: -------------------------------------------------------------------------------- 1 | #![feature(mpsc_select)] 2 | 3 | extern crate crossbeam; 4 | 5 | use std::sync::mpsc; 6 | use shared::{message, shuffle}; 7 | 8 | mod shared; 9 | 10 | const MESSAGES: usize = 5_000_000; 11 | const THREADS: usize = 4; 12 | 13 | fn seq_async() { 14 | let (tx, rx) = mpsc::channel(); 15 | 16 | for i in 0..MESSAGES { 17 | tx.send(message(i)).unwrap(); 18 | } 19 | 20 | for _ in 0..MESSAGES { 21 | rx.recv().unwrap(); 22 | } 23 | } 24 | 25 | fn seq_sync(cap: usize) { 26 | let (tx, rx) = mpsc::sync_channel(cap); 27 | 28 | for i in 0..MESSAGES { 29 | tx.send(message(i)).unwrap(); 30 | } 31 | 32 | for _ in 0..MESSAGES { 33 | rx.recv().unwrap(); 34 | } 35 | } 36 | 37 | fn spsc_async() { 38 | let (tx, rx) = mpsc::channel(); 39 | 40 | crossbeam::scope(|scope| { 41 | scope.spawn(move || { 42 | for i in 0..MESSAGES { 43 | tx.send(message(i)).unwrap(); 44 | } 45 | }); 46 | 47 | for _ in 0..MESSAGES { 48 | rx.recv().unwrap(); 49 | } 50 | }); 51 | } 52 | 53 | fn spsc_sync(cap: usize) { 54 | let (tx, rx) = mpsc::sync_channel(cap); 55 | 56 | crossbeam::scope(|scope| { 57 | scope.spawn(move || { 58 | for i in 0..MESSAGES { 59 | tx.send(message(i)).unwrap(); 60 | } 61 | }); 62 | 63 | for _ in 0..MESSAGES { 64 | rx.recv().unwrap(); 65 | } 66 | }); 67 | } 68 | 69 | fn mpsc_async() { 70 | let (tx, rx) = mpsc::channel(); 71 | 72 | crossbeam::scope(|scope| { 73 | for _ in 0..THREADS { 74 | let tx = tx.clone(); 75 | scope.spawn(move || { 76 | for i in 0..MESSAGES / THREADS { 77 | tx.send(message(i)).unwrap(); 78 | } 79 | }); 80 | } 81 | 82 | for _ in 0..MESSAGES { 83 | rx.recv().unwrap(); 84 | } 85 | }); 86 | } 87 | 88 | fn mpsc_sync(cap: usize) { 89 | let (tx, rx) = mpsc::sync_channel(cap); 90 | 91 | crossbeam::scope(|scope| { 92 | for _ in 0..THREADS { 93 | let tx = tx.clone(); 94 | scope.spawn(move || { 95 | for i in 0..MESSAGES / THREADS { 96 | tx.send(message(i)).unwrap(); 97 | } 98 | }); 99 | } 100 | 101 | for _ in 0..MESSAGES { 102 | rx.recv().unwrap(); 103 | } 104 | }); 105 | } 106 | 107 | fn select_rx_async() { 108 | assert_eq!(THREADS, 4); 109 | let mut chans = (0..THREADS).map(|_| mpsc::channel()).collect::>(); 110 | 111 | crossbeam::scope(|scope| { 112 | for &(ref tx, _) in &chans { 113 | let tx = tx.clone(); 114 | scope.spawn(move || { 115 | for i in 0..MESSAGES / THREADS { 116 | tx.send(message(i)).unwrap(); 117 | } 118 | }); 119 | } 120 | 121 | for _ in 0..MESSAGES { 122 | shuffle(&mut chans); 123 | let rx0 = &chans[0].1; 124 | let rx1 = &chans[1].1; 125 | let rx2 = &chans[2].1; 126 | let rx3 = &chans[3].1; 127 | 128 | select! { 129 | m = rx0.recv() => assert!(m.is_ok()), 130 | m = rx1.recv() => assert!(m.is_ok()), 131 | m = rx2.recv() => assert!(m.is_ok()), 132 | m = rx3.recv() => assert!(m.is_ok()) 133 | }; 134 | } 135 | }); 136 | } 137 | 138 | fn select_rx_sync(cap: usize) { 139 | assert_eq!(THREADS, 4); 140 | let mut chans = (0..THREADS).map(|_| mpsc::sync_channel(cap)).collect::>(); 141 | 142 | crossbeam::scope(|scope| { 143 | for &(ref tx, _) in &chans { 144 | let tx = tx.clone(); 145 | scope.spawn(move || { 146 | for i in 0..MESSAGES / THREADS { 147 | tx.send(message(i)).unwrap(); 148 | } 149 | }); 150 | } 151 | 152 | for _ in 0..MESSAGES { 153 | shuffle(&mut chans); 154 | let rx0 = &chans[0].1; 155 | let rx1 = &chans[1].1; 156 | let rx2 = &chans[2].1; 157 | let rx3 = &chans[3].1; 158 | 159 | select! { 160 | m = rx0.recv() => assert!(m.is_ok()), 161 | m = rx1.recv() => assert!(m.is_ok()), 162 | m = rx2.recv() => assert!(m.is_ok()), 163 | m = rx3.recv() => assert!(m.is_ok()) 164 | } 165 | } 166 | }); 167 | } 168 | 169 | fn main() { 170 | macro_rules! run { 171 | ($name:expr, $f:expr) => { 172 | let now = ::std::time::Instant::now(); 173 | $f; 174 | let elapsed = now.elapsed(); 175 | println!( 176 | "{:25} {:15} {:7.3} sec", 177 | $name, 178 | "Rust mpsc", 179 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 180 | ); 181 | } 182 | } 183 | 184 | run!("bounded0_mpsc", mpsc_sync(0)); 185 | run!("bounded0_select_rx", select_rx_sync(0)); 186 | run!("bounded0_spsc", spsc_sync(0)); 187 | 188 | run!("bounded1_mpsc", mpsc_sync(1)); 189 | run!("bounded1_select_rx", select_rx_sync(1)); 190 | run!("bounded1_spsc", spsc_sync(1)); 191 | 192 | run!("bounded_mpsc", mpsc_sync(MESSAGES)); 193 | run!("bounded_select_rx", select_rx_sync(MESSAGES)); 194 | run!("bounded_seq", seq_sync(MESSAGES)); 195 | run!("bounded_spsc", spsc_sync(MESSAGES)); 196 | 197 | run!("unbounded_mpsc", mpsc_async()); 198 | run!("unbounded_select_rx", select_rx_async()); 199 | run!("unbounded_seq", seq_async()); 200 | run!("unbounded_spsc", spsc_async()); 201 | } 202 | -------------------------------------------------------------------------------- /benchmarks/futures-channel.rs: -------------------------------------------------------------------------------- 1 | extern crate crossbeam; 2 | extern crate futures; 3 | 4 | use futures::channel::mpsc; 5 | use futures::executor::ThreadPool; 6 | use futures::prelude::*; 7 | use futures::{SinkExt, StreamExt, future, stream}; 8 | 9 | use shared::message; 10 | 11 | mod shared; 12 | 13 | const MESSAGES: usize = 5_000_000; 14 | const THREADS: usize = 4; 15 | 16 | fn seq_unbounded() { 17 | ThreadPool::new().unwrap().run(future::lazy(|_| { 18 | let (tx, rx) = mpsc::unbounded(); 19 | for i in 0..MESSAGES { 20 | tx.unbounded_send(message(i)).unwrap(); 21 | } 22 | drop(tx); 23 | 24 | rx.for_each(|_| future::ok(())) 25 | })).unwrap(); 26 | } 27 | 28 | fn seq_bounded(cap: usize) { 29 | let (mut tx, rx) = mpsc::channel(cap); 30 | ThreadPool::new().unwrap().run(future::lazy(|_| { 31 | for i in 0..MESSAGES { 32 | tx.try_send(message(i)).unwrap(); 33 | } 34 | drop(tx); 35 | 36 | rx.for_each(|_| future::ok(())) 37 | })).unwrap(); 38 | } 39 | 40 | fn spsc_unbounded() { 41 | ThreadPool::new().unwrap().run(future::lazy(|cx| { 42 | let (tx, rx) = mpsc::unbounded(); 43 | 44 | cx.spawn(future::lazy(move |_| { 45 | tx.send_all(stream::iter_ok((0..MESSAGES).map(|i| message(i)))) 46 | .map_err(|_| panic!()) 47 | .and_then(|_| future::ok(())) 48 | })); 49 | 50 | rx.for_each(|_| future::ok(())) 51 | })).unwrap(); 52 | } 53 | 54 | fn spsc_bounded(cap: usize) { 55 | ThreadPool::new().unwrap().run(future::lazy(|cx| { 56 | let (tx, rx) = mpsc::channel(cap); 57 | 58 | cx.spawn(future::lazy(move |_| { 59 | tx.send_all(stream::iter_ok((0..MESSAGES).map(|i| message(i)))) 60 | .map_err(|_| panic!()) 61 | .and_then(|_| future::ok(())) 62 | })); 63 | 64 | rx.for_each(|_| future::ok(())) 65 | })).unwrap(); 66 | } 67 | 68 | fn mpsc_unbounded() { 69 | ThreadPool::new().unwrap().run(future::lazy(|cx| { 70 | let (tx, rx) = mpsc::unbounded(); 71 | 72 | for _ in 0..THREADS { 73 | let tx = tx.clone(); 74 | cx.spawn(future::lazy(move |_| { 75 | tx.send_all(stream::iter_ok((0..MESSAGES / THREADS).map(|i| message(i)))) 76 | .map_err(|_| panic!()) 77 | .and_then(|_| future::ok(())) 78 | })); 79 | } 80 | drop(tx); 81 | 82 | rx.for_each(|_| future::ok(())) 83 | })).unwrap(); 84 | } 85 | 86 | fn mpsc_bounded(cap: usize) { 87 | ThreadPool::new().unwrap().run(future::lazy(|cx| { 88 | let (tx, rx) = mpsc::channel(cap); 89 | 90 | for _ in 0..THREADS { 91 | let tx = tx.clone(); 92 | cx.spawn(future::lazy(move |_| { 93 | tx.send_all(stream::iter_ok((0..MESSAGES / THREADS).map(|i| message(i)))) 94 | .map_err(|_| panic!()) 95 | .and_then(|_| future::ok(())) 96 | })); 97 | } 98 | drop(tx); 99 | 100 | rx.for_each(|_| future::ok(())) 101 | })).unwrap(); 102 | } 103 | 104 | fn select_rx_unbounded() { 105 | ThreadPool::new().unwrap().run(future::lazy(|cx| { 106 | let chans = (0..THREADS) 107 | .map(|_| mpsc::unbounded()) 108 | .collect::>(); 109 | 110 | for (tx, _) in &chans { 111 | let tx = tx.clone(); 112 | cx.spawn(future::lazy(move |_| { 113 | for i in 0..MESSAGES / THREADS { 114 | tx.unbounded_send(message(i)).unwrap(); 115 | } 116 | future::ok(()) 117 | })); 118 | } 119 | 120 | stream::select_all(chans.into_iter().map(|(_, rx)| rx)) 121 | .for_each(|_| future::ok(())) 122 | .and_then(|_| future::ok(())) 123 | })).unwrap(); 124 | } 125 | 126 | fn select_rx_bounded(cap: usize) { 127 | ThreadPool::new().unwrap().run(future::lazy(|cx| { 128 | let chans = (0..THREADS) 129 | .map(|_| mpsc::channel(cap)) 130 | .collect::>(); 131 | 132 | for (tx, _) in &chans { 133 | let tx = tx.clone(); 134 | cx.spawn(future::lazy(move |_| { 135 | tx.send_all(stream::iter_ok((0..MESSAGES / THREADS).map(|i| message(i)))) 136 | .map_err(|_| panic!()) 137 | .and_then(|_| future::ok(())) 138 | })); 139 | } 140 | 141 | stream::select_all(chans.into_iter().map(|(_, rx)| rx)) 142 | .for_each(|_| future::ok(())) 143 | .and_then(|_| future::ok(())) 144 | })).unwrap(); 145 | } 146 | 147 | fn main() { 148 | macro_rules! run { 149 | ($name:expr, $f:expr) => { 150 | let now = ::std::time::Instant::now(); 151 | $f; 152 | let elapsed = now.elapsed(); 153 | println!( 154 | "{:25} {:15} {:7.3} sec", 155 | $name, 156 | "Rust futures-channel", 157 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 158 | ); 159 | } 160 | } 161 | 162 | run!("bounded0_mpsc", mpsc_bounded(0)); 163 | run!("bounded0_select_rx", select_rx_bounded(0)); 164 | run!("bounded0_spsc", spsc_bounded(0)); 165 | 166 | run!("bounded1_mpsc", mpsc_bounded(1)); 167 | run!("bounded1_select_rx", select_rx_bounded(1)); 168 | run!("bounded1_spsc", spsc_bounded(1)); 169 | 170 | run!("bounded_mpsc", mpsc_bounded(MESSAGES)); 171 | run!("bounded_select_rx", select_rx_bounded(MESSAGES)); 172 | run!("bounded_seq", seq_bounded(MESSAGES)); 173 | run!("bounded_spsc", spsc_bounded(MESSAGES)); 174 | 175 | run!("unbounded_mpsc", mpsc_unbounded()); 176 | run!("unbounded_select_rx", select_rx_unbounded()); 177 | run!("unbounded_seq", seq_unbounded()); 178 | run!("unbounded_spsc", spsc_unbounded()); 179 | } 180 | -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | //! Thread-local context used in select. 2 | 3 | use std::cell::Cell; 4 | use std::sync::Arc; 5 | use std::sync::atomic::{AtomicUsize, Ordering}; 6 | use std::thread::{self, Thread, ThreadId}; 7 | use std::time::Instant; 8 | 9 | use select::Selected; 10 | use utils::Backoff; 11 | 12 | /// Thread-local context used in select. 13 | #[derive(Clone)] 14 | pub struct Context { 15 | inner: Arc, 16 | } 17 | 18 | /// Inner representation of `Context`. 19 | struct Inner { 20 | /// Selected operation. 21 | select: AtomicUsize, 22 | 23 | /// A slot into which another thread may store a pointer to its `Packet`. 24 | packet: AtomicUsize, 25 | 26 | /// Thread handle. 27 | thread: Thread, 28 | 29 | /// Thread id. 30 | thread_id: ThreadId, 31 | } 32 | 33 | impl Context { 34 | /// Creates a new context for the duration of the closure. 35 | #[inline] 36 | pub fn with(f: F) -> R 37 | where 38 | F: FnOnce(&Context) -> R, 39 | { 40 | thread_local! { 41 | /// Cached thread-local context. 42 | static CONTEXT: Cell> = Cell::new(Some(Context::new())); 43 | } 44 | 45 | let mut f = Some(f); 46 | let mut f = move |cx: &Context| -> R { 47 | let f = f.take().unwrap(); 48 | f(cx) 49 | }; 50 | 51 | CONTEXT.try_with(|cell| { 52 | match cell.take() { 53 | None => f(&Context::new()), 54 | Some(cx) => { 55 | cx.reset(); 56 | let res = f(&cx); 57 | cell.set(Some(cx)); 58 | res 59 | } 60 | } 61 | }).unwrap_or_else(|_| { 62 | f(&Context::new()) 63 | }) 64 | } 65 | 66 | /// Creates a new `Context`. 67 | #[cold] 68 | fn new() -> Context { 69 | Context { 70 | inner: Arc::new(Inner { 71 | select: AtomicUsize::new(Selected::Waiting.into()), 72 | packet: AtomicUsize::new(0), 73 | thread: thread::current(), 74 | thread_id: thread::current().id(), 75 | }), 76 | } 77 | } 78 | 79 | /// Resets `select` and `packet`. 80 | #[inline] 81 | fn reset(&self) { 82 | self.inner.select.store(Selected::Waiting.into(), Ordering::Release); 83 | self.inner.packet.store(0, Ordering::Release); 84 | } 85 | 86 | /// Attempts to select an operation. 87 | /// 88 | /// On failure, the previously selected operation is returned. 89 | #[inline] 90 | pub fn try_select(&self, select: Selected) -> Result<(), Selected> { 91 | self.inner 92 | .select 93 | .compare_exchange( 94 | Selected::Waiting.into(), 95 | select.into(), 96 | Ordering::AcqRel, 97 | Ordering::Acquire, 98 | ) 99 | .map(|_| ()) 100 | .map_err(|e| e.into()) 101 | } 102 | 103 | /// Returns the selected operation. 104 | #[inline] 105 | pub fn selected(&self) -> Selected { 106 | Selected::from(self.inner.select.load(Ordering::Acquire)) 107 | } 108 | 109 | /// Stores a packet. 110 | /// 111 | /// This method must be called after `try_select` succeeds and there is a packet to provide. 112 | #[inline] 113 | pub fn store_packet(&self, packet: usize) { 114 | if packet != 0 { 115 | self.inner.packet.store(packet, Ordering::Release); 116 | } 117 | } 118 | 119 | /// Waits until a packet is provided and returns it. 120 | #[inline] 121 | pub fn wait_packet(&self) -> usize { 122 | let mut backoff = Backoff::new(); 123 | loop { 124 | let packet = self.inner.packet.load(Ordering::Acquire); 125 | if packet != 0 { 126 | return packet; 127 | } 128 | backoff.snooze(); 129 | } 130 | } 131 | 132 | /// Waits until an operation is selected and returns it. 133 | /// 134 | /// If the deadline is reached, `Selected::Aborted` will be selected. 135 | #[inline] 136 | pub fn wait_until(&self, deadline: Option) -> Selected { 137 | // Spin for a short time, waiting until an operation is selected. 138 | let mut backoff = Backoff::new(); 139 | loop { 140 | let sel = Selected::from(self.inner.select.load(Ordering::Acquire)); 141 | if sel != Selected::Waiting { 142 | return sel; 143 | } 144 | 145 | if !backoff.snooze() { 146 | break; 147 | } 148 | } 149 | 150 | loop { 151 | // Check whether an operation has been selected. 152 | let sel = Selected::from(self.inner.select.load(Ordering::Acquire)); 153 | if sel != Selected::Waiting { 154 | return sel; 155 | } 156 | 157 | // If there's a deadline, park the current thread until the deadline is reached. 158 | if let Some(end) = deadline { 159 | let now = Instant::now(); 160 | 161 | if now < end { 162 | thread::park_timeout(end - now); 163 | } else { 164 | // The deadline has been reached. Try aborting select. 165 | return match self.try_select(Selected::Aborted) { 166 | Ok(()) => Selected::Aborted, 167 | Err(s) => s, 168 | }; 169 | } 170 | } else { 171 | thread::park(); 172 | } 173 | } 174 | } 175 | 176 | /// Unparks the thread this context belongs to. 177 | #[inline] 178 | pub fn unpark(&self) { 179 | self.inner.thread.unpark(); 180 | } 181 | 182 | /// Returns the id of the thread this context belongs to. 183 | #[inline] 184 | pub fn thread_id(&self) -> ThreadId { 185 | self.inner.thread_id 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /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::num::Wrapping; 6 | use std::sync::Arc; 7 | use std::thread; 8 | use std::time::{Duration, Instant}; 9 | 10 | use parking_lot::Mutex; 11 | 12 | use context::Context; 13 | use err::{RecvTimeoutError, TryRecvError}; 14 | use select::{Operation, SelectHandle, Token}; 15 | 16 | /// Result of a receive operation. 17 | pub type TickToken = Option; 18 | 19 | /// Channel state. 20 | struct Inner { 21 | /// The instant at which the next message will be delivered. 22 | next_tick: Instant, 23 | 24 | /// The index of the next message to be received. 25 | index: Wrapping, 26 | } 27 | 28 | /// Channel that delivers messages periodically. 29 | pub struct Channel { 30 | /// The state of the channel. 31 | // TODO: Use `Arc>` here once we implement `AtomicCell`. 32 | inner: Arc>, 33 | 34 | /// The time interval in which messages get delivered. 35 | duration: Duration, 36 | } 37 | 38 | impl Channel { 39 | /// Creates a channel that delivers messages periodically. 40 | #[inline] 41 | pub fn new(dur: Duration) -> Self { 42 | Channel { 43 | inner: Arc::new(Mutex::new(Inner { 44 | next_tick: Instant::now() + dur, 45 | index: Wrapping(0), 46 | })), 47 | duration: dur, 48 | } 49 | } 50 | 51 | /// Attempts to receive a message without blocking. 52 | #[inline] 53 | pub fn try_recv(&self) -> Result { 54 | let mut inner = self.inner.lock(); 55 | let now = Instant::now(); 56 | 57 | // If the next tick time has been reached, we can receive the next message. 58 | if now >= inner.next_tick { 59 | let msg = inner.next_tick; 60 | inner.next_tick = now + self.duration; 61 | inner.index += Wrapping(1); 62 | Ok(msg) 63 | } else { 64 | Err(TryRecvError::Empty) 65 | } 66 | } 67 | 68 | /// Receives a message from the channel. 69 | #[inline] 70 | pub fn recv(&self, deadline: Option) -> Result { 71 | loop { 72 | // Compute the time to sleep until the next message or the deadline. 73 | let offset = { 74 | let mut inner = self.inner.lock(); 75 | let now = Instant::now(); 76 | 77 | // Check if we can receive the next message. 78 | if now >= inner.next_tick { 79 | let msg = inner.next_tick; 80 | inner.next_tick = now + self.duration; 81 | inner.index += Wrapping(1); 82 | return Ok(msg); 83 | } 84 | 85 | // Check if the operation deadline has been reached. 86 | if let Some(d) = deadline { 87 | if now >= d { 88 | return Err(RecvTimeoutError::Timeout); 89 | } 90 | 91 | inner.next_tick.min(d) - now 92 | } else { 93 | inner.next_tick - now 94 | } 95 | }; 96 | 97 | thread::sleep(offset); 98 | } 99 | } 100 | 101 | /// Reads a message from the channel. 102 | #[inline] 103 | pub unsafe fn read(&self, token: &mut Token) -> Result { 104 | token.tick.ok_or(()) 105 | } 106 | 107 | /// Returns `true` if the channel is empty. 108 | #[inline] 109 | pub fn is_empty(&self) -> bool { 110 | let inner = self.inner.lock(); 111 | Instant::now() < inner.next_tick 112 | } 113 | 114 | /// Returns `true` if the channel is full. 115 | #[inline] 116 | pub fn is_full(&self) -> bool { 117 | !self.is_empty() 118 | } 119 | 120 | /// Returns the number of messages in the channel. 121 | #[inline] 122 | pub fn len(&self) -> usize { 123 | if self.is_empty() { 124 | 0 125 | } else { 126 | 1 127 | } 128 | } 129 | 130 | /// Returns the capacity of the channel. 131 | #[inline] 132 | pub fn capacity(&self) -> Option { 133 | Some(1) 134 | } 135 | } 136 | 137 | impl Clone for Channel { 138 | #[inline] 139 | fn clone(&self) -> Channel { 140 | Channel { 141 | inner: self.inner.clone(), 142 | duration: self.duration, 143 | } 144 | } 145 | } 146 | 147 | impl SelectHandle for Channel { 148 | #[inline] 149 | fn try(&self, token: &mut Token) -> bool { 150 | match self.try_recv() { 151 | Ok(msg) => { 152 | token.tick = Some(msg); 153 | true 154 | } 155 | Err(TryRecvError::Disconnected) => { 156 | token.tick = None; 157 | true 158 | } 159 | Err(TryRecvError::Empty) => { 160 | false 161 | } 162 | } 163 | } 164 | 165 | #[inline] 166 | fn retry(&self, token: &mut Token) -> bool { 167 | self.try(token) 168 | } 169 | 170 | #[inline] 171 | fn deadline(&self) -> Option { 172 | Some(self.inner.lock().next_tick) 173 | } 174 | 175 | #[inline] 176 | fn register(&self, _token: &mut Token, _oper: Operation, _cx: &Context) -> bool { 177 | true 178 | } 179 | 180 | #[inline] 181 | fn unregister(&self, _oper: Operation) {} 182 | 183 | #[inline] 184 | fn accept(&self, token: &mut Token, _cx: &Context) -> bool { 185 | self.try(token) 186 | } 187 | 188 | #[inline] 189 | fn state(&self) -> usize { 190 | // Return the index of the next message to be delivered to the channel. 191 | let inner = self.inner.lock(); 192 | let index = if Instant::now() < inner.next_tick { 193 | inner.index 194 | } else { 195 | inner.index + Wrapping(1) 196 | }; 197 | index.0 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /benchmarks/chan.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate chan; 3 | extern crate crossbeam; 4 | 5 | use shared::message; 6 | 7 | mod shared; 8 | 9 | const MESSAGES: usize = 5_000_000; 10 | const THREADS: usize = 4; 11 | 12 | fn new(cap: Option) -> (chan::Sender, chan::Receiver) { 13 | match cap { 14 | None => chan::async(), 15 | Some(cap) => chan::sync(cap) 16 | } 17 | } 18 | 19 | fn seq(cap: Option) { 20 | let (tx, rx) = new(cap); 21 | 22 | for i in 0..MESSAGES { 23 | tx.send(message(i)); 24 | } 25 | 26 | for _ in 0..MESSAGES { 27 | rx.recv().unwrap(); 28 | } 29 | } 30 | 31 | fn spsc(cap: Option) { 32 | let (tx, rx) = new(cap); 33 | 34 | crossbeam::scope(|scope| { 35 | scope.spawn(|| { 36 | for i in 0..MESSAGES { 37 | tx.send(message(i)); 38 | } 39 | }); 40 | 41 | for _ in 0..MESSAGES { 42 | rx.recv().unwrap(); 43 | } 44 | }); 45 | } 46 | 47 | fn mpsc(cap: Option) { 48 | let (tx, rx) = new(cap); 49 | 50 | crossbeam::scope(|scope| { 51 | for _ in 0..THREADS { 52 | scope.spawn(|| { 53 | for i in 0..MESSAGES / THREADS { 54 | tx.send(message(i)); 55 | } 56 | }); 57 | } 58 | 59 | for _ in 0..MESSAGES { 60 | rx.recv().unwrap(); 61 | } 62 | }); 63 | } 64 | 65 | fn mpmc(cap: Option) { 66 | let (tx, rx) = new(cap); 67 | 68 | crossbeam::scope(|scope| { 69 | for _ in 0..THREADS { 70 | scope.spawn(|| { 71 | for i in 0..MESSAGES / THREADS { 72 | tx.send(message(i)); 73 | } 74 | }); 75 | } 76 | 77 | for _ in 0..THREADS { 78 | scope.spawn(|| { 79 | for _ in 0..MESSAGES / THREADS { 80 | rx.recv().unwrap(); 81 | } 82 | }); 83 | } 84 | }); 85 | } 86 | 87 | fn select_rx(cap: Option) { 88 | assert_eq!(THREADS, 4); 89 | let chans = (0..THREADS).map(|_| new(cap)).collect::>(); 90 | 91 | crossbeam::scope(|scope| { 92 | for (tx, _) in &chans { 93 | let tx = tx.clone(); 94 | scope.spawn(move || { 95 | for i in 0..MESSAGES / THREADS { 96 | tx.send(message(i)); 97 | } 98 | }); 99 | } 100 | 101 | let rx0 = &chans[0].1; 102 | let rx1 = &chans[1].1; 103 | let rx2 = &chans[2].1; 104 | let rx3 = &chans[3].1; 105 | for _ in 0..MESSAGES { 106 | chan_select! { 107 | rx0.recv() -> m => assert!(m.is_some()), 108 | rx1.recv() -> m => assert!(m.is_some()), 109 | rx2.recv() -> m => assert!(m.is_some()), 110 | rx3.recv() -> m => assert!(m.is_some()), 111 | } 112 | } 113 | }); 114 | } 115 | 116 | fn select_both(cap: Option) { 117 | assert_eq!(THREADS, 4); 118 | let chans = (0..THREADS).map(|_| new(cap)).collect::>(); 119 | 120 | crossbeam::scope(|scope| { 121 | for _ in 0..THREADS { 122 | let chans = chans.clone(); 123 | scope.spawn(move || { 124 | let tx0 = &chans[0].0; 125 | let tx1 = &chans[1].0; 126 | let tx2 = &chans[2].0; 127 | let tx3 = &chans[3].0; 128 | 129 | for i in 0..MESSAGES / THREADS { 130 | chan_select! { 131 | tx0.send(message(i)) => {}, 132 | tx1.send(message(i)) => {}, 133 | tx2.send(message(i)) => {}, 134 | tx3.send(message(i)) => {}, 135 | } 136 | } 137 | }); 138 | } 139 | 140 | for _ in 0..THREADS { 141 | let chans = chans.clone(); 142 | scope.spawn(move || { 143 | let rx0 = &chans[0].1; 144 | let rx1 = &chans[1].1; 145 | let rx2 = &chans[2].1; 146 | let rx3 = &chans[3].1; 147 | 148 | for _ in 0..MESSAGES / THREADS { 149 | chan_select! { 150 | rx0.recv() -> m => assert!(m.is_some()), 151 | rx1.recv() -> m => assert!(m.is_some()), 152 | rx2.recv() -> m => assert!(m.is_some()), 153 | rx3.recv() -> m => assert!(m.is_some()), 154 | } 155 | } 156 | }); 157 | } 158 | }); 159 | } 160 | 161 | fn main() { 162 | macro_rules! run { 163 | ($name:expr, $f:expr) => { 164 | let now = ::std::time::Instant::now(); 165 | $f; 166 | let elapsed = now.elapsed(); 167 | println!( 168 | "{:25} {:15} {:7.3} sec", 169 | $name, 170 | "Rust chan", 171 | elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1e9 172 | ); 173 | } 174 | } 175 | 176 | run!("bounded0_mpmc", mpmc(Some(0))); 177 | run!("bounded0_mpsc", mpsc(Some(0))); 178 | run!("bounded0_select_rx", select_rx(Some(0))); 179 | run!("bounded0_spsc", spsc(Some(0))); 180 | 181 | run!("bounded1_mpmc", mpmc(Some(1))); 182 | run!("bounded1_mpsc", mpsc(Some(1))); 183 | run!("bounded1_select_both", select_both(Some(1))); 184 | run!("bounded1_select_rx", select_rx(Some(1))); 185 | run!("bounded1_spsc", spsc(Some(1))); 186 | 187 | run!("bounded_mpmc", mpmc(Some(MESSAGES))); 188 | run!("bounded_mpsc", mpsc(Some(MESSAGES))); 189 | run!("bounded_select_both", select_both(Some(MESSAGES))); 190 | run!("bounded_select_rx", select_rx(Some(MESSAGES))); 191 | run!("bounded_seq", seq(Some(MESSAGES))); 192 | run!("bounded_spsc", spsc(Some(MESSAGES))); 193 | 194 | run!("unbounded_mpmc", mpmc(None)); 195 | run!("unbounded_mpsc", mpsc(None)); 196 | run!("unbounded_select_both", select_both(None)); 197 | run!("unbounded_select_rx", select_rx(None)); 198 | run!("unbounded_seq", seq(None)); 199 | run!("unbounded_spsc", spsc(None)); 200 | } 201 | -------------------------------------------------------------------------------- /src/flavors/after.rs: -------------------------------------------------------------------------------- 1 | //! Channel that delivers a message after a certain amount of time. 2 | //! 3 | //! Messages cannot be sent into this kind of channel; they are materialized on demand. 4 | 5 | use std::sync::Arc; 6 | use std::sync::atomic::{AtomicBool, Ordering}; 7 | use std::thread; 8 | use std::time::{Duration, Instant}; 9 | 10 | use context::Context; 11 | use err::{RecvTimeoutError, TryRecvError}; 12 | use select::{Operation, SelectHandle, Token}; 13 | use utils; 14 | 15 | /// Result of a receive operation. 16 | pub type AfterToken = Option; 17 | 18 | /// Channel that delivers a message after a certain amount of time. 19 | pub struct Channel { 20 | /// The instant at which the message will be delivered. 21 | delivery_time: Instant, 22 | 23 | /// `true` if the message has been received. 24 | received: Arc, 25 | } 26 | 27 | impl Channel { 28 | /// Creates a channel that delivers a message after a certain duration of time. 29 | #[inline] 30 | pub fn new(dur: Duration) -> Self { 31 | Channel { 32 | delivery_time: Instant::now() + dur, 33 | received: Arc::new(AtomicBool::new(false)), 34 | } 35 | } 36 | 37 | /// Attempts to receive a message without blocking. 38 | #[inline] 39 | pub fn try_recv(&self) -> Result { 40 | // We use relaxed ordering because this is just an optional optimistic check. 41 | if self.received.load(Ordering::Relaxed) { 42 | // The message has already been received. 43 | return Err(TryRecvError::Empty); 44 | } 45 | 46 | if Instant::now() < self.delivery_time { 47 | // The message was not delivered yet. 48 | return Err(TryRecvError::Empty); 49 | } 50 | 51 | // Try receiving the message if it is still available. 52 | if !self.received.swap(true, Ordering::SeqCst) { 53 | // Success! Return delivery time as the message. 54 | Ok(self.delivery_time) 55 | } else { 56 | // The message was already received. 57 | Err(TryRecvError::Empty) 58 | } 59 | } 60 | 61 | /// Receives a message from the channel. 62 | #[inline] 63 | pub fn recv(&self, deadline: Option) -> Result { 64 | // We use relaxed ordering because this is just an optional optimistic check. 65 | if self.received.load(Ordering::Relaxed) { 66 | // The message has already been received. 67 | utils::sleep_until(deadline); 68 | return Err(RecvTimeoutError::Timeout); 69 | } 70 | 71 | // Wait until the message is received or the deadline is reached. 72 | loop { 73 | let now = Instant::now(); 74 | 75 | // Check if we can receive the next message. 76 | if now >= self.delivery_time { 77 | break; 78 | } 79 | 80 | // Check if the deadline has been reached. 81 | if let Some(d) = deadline { 82 | if now >= d { 83 | return Err(RecvTimeoutError::Timeout); 84 | } 85 | 86 | thread::sleep(self.delivery_time.min(d) - now); 87 | } else { 88 | thread::sleep(self.delivery_time - now); 89 | } 90 | } 91 | 92 | // Try receiving the message if it is still available. 93 | if !self.received.swap(true, Ordering::SeqCst) { 94 | // Success! Return the message, which is the instant at which it was delivered. 95 | Ok(self.delivery_time) 96 | } else { 97 | // The message was already received. Block forever. 98 | utils::sleep_until(None); 99 | unreachable!() 100 | } 101 | } 102 | 103 | /// Reads a message from the channel. 104 | #[inline] 105 | pub unsafe fn read(&self, token: &mut Token) -> Result { 106 | token.after.ok_or(()) 107 | } 108 | 109 | /// Returns `true` if the channel is empty. 110 | #[inline] 111 | pub fn is_empty(&self) -> bool { 112 | // We use relaxed ordering because this is just an optional optimistic check. 113 | if self.received.load(Ordering::Relaxed) { 114 | return true; 115 | } 116 | 117 | // If the delivery time hasn't been reached yet, the channel is empty. 118 | if Instant::now() < self.delivery_time { 119 | return true; 120 | } 121 | 122 | // The delivery time has been reached. The channel is empty only if the message has already 123 | // been received. 124 | self.received.load(Ordering::SeqCst) 125 | } 126 | 127 | /// Returns `true` if the channel is full. 128 | #[inline] 129 | pub fn is_full(&self) -> bool { 130 | !self.is_empty() 131 | } 132 | 133 | /// Returns the number of messages in the channel. 134 | #[inline] 135 | pub fn len(&self) -> usize { 136 | if self.is_empty() { 137 | 0 138 | } else { 139 | 1 140 | } 141 | } 142 | 143 | /// Returns the capacity of the channel. 144 | #[inline] 145 | pub fn capacity(&self) -> Option { 146 | Some(1) 147 | } 148 | } 149 | 150 | impl Clone for Channel { 151 | #[inline] 152 | fn clone(&self) -> Channel { 153 | Channel { 154 | delivery_time: self.delivery_time, 155 | received: self.received.clone(), 156 | } 157 | } 158 | } 159 | 160 | impl SelectHandle for Channel { 161 | #[inline] 162 | fn try(&self, token: &mut Token) -> bool { 163 | match self.try_recv() { 164 | Ok(msg) => { 165 | token.after = Some(msg); 166 | true 167 | } 168 | Err(TryRecvError::Disconnected) => { 169 | token.after = None; 170 | true 171 | } 172 | Err(TryRecvError::Empty) => { 173 | false 174 | } 175 | } 176 | } 177 | 178 | #[inline] 179 | fn retry(&self, token: &mut Token) -> bool { 180 | self.try(token) 181 | } 182 | 183 | #[inline] 184 | fn deadline(&self) -> Option { 185 | Some(self.delivery_time) 186 | } 187 | 188 | #[inline] 189 | fn register(&self, _token: &mut Token, _oper: Operation, _cx: &Context) -> bool { 190 | true 191 | } 192 | 193 | #[inline] 194 | fn unregister(&self, _oper: Operation) {} 195 | 196 | #[inline] 197 | fn accept(&self, token: &mut Token, _cx: &Context) -> bool { 198 | self.try(token) 199 | } 200 | 201 | #[inline] 202 | fn state(&self) -> usize { 203 | // Return 1 if the deadline has been reached and 0 otherwise. 204 | if self.received.load(Ordering::SeqCst) { 205 | 1 206 | } else if Instant::now() < self.delivery_time { 207 | 0 208 | } else { 209 | 1 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /tests/tick.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the tick channel flavor. 2 | 3 | extern crate crossbeam; 4 | #[macro_use] 5 | extern crate crossbeam_channel; 6 | extern crate rand; 7 | 8 | use std::sync::atomic::AtomicUsize; 9 | use std::sync::atomic::Ordering; 10 | use std::thread; 11 | use std::time::{Duration, Instant}; 12 | 13 | use crossbeam_channel::{after, tick, TryRecvError}; 14 | 15 | fn ms(ms: u64) -> Duration { 16 | Duration::from_millis(ms) 17 | } 18 | 19 | #[test] 20 | fn fire() { 21 | let start = Instant::now(); 22 | let r = tick(ms(50)); 23 | 24 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 25 | thread::sleep(ms(100)); 26 | 27 | let fired = r.try_recv().unwrap(); 28 | assert!(start < fired); 29 | assert!(fired - start >= ms(50)); 30 | 31 | let now = Instant::now(); 32 | assert!(fired < now); 33 | assert!(now - fired >= ms(50)); 34 | 35 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 36 | 37 | select! { 38 | recv(r) -> _ => panic!(), 39 | default => {} 40 | } 41 | 42 | select! { 43 | recv(r) -> _ => {} 44 | recv(tick(ms(200))) -> _ => panic!(), 45 | } 46 | } 47 | 48 | #[test] 49 | fn intervals() { 50 | let start = Instant::now(); 51 | let r = tick(ms(50)); 52 | 53 | let t1 = r.recv().unwrap(); 54 | assert!(start + ms(50) <= t1); 55 | assert!(start + ms(100) > t1); 56 | 57 | thread::sleep(ms(300)); 58 | let t2 = r.try_recv().unwrap(); 59 | assert!(start + ms(100) <= t2); 60 | assert!(start + ms(150) > t2); 61 | 62 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 63 | let t3 = r.recv().unwrap(); 64 | assert!(start + ms(400) <= t3); 65 | assert!(start + ms(450) > t3); 66 | 67 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 68 | } 69 | 70 | #[test] 71 | fn capacity() { 72 | const COUNT: usize = 10; 73 | 74 | for i in 0..COUNT { 75 | let r = tick(ms(i as u64)); 76 | assert_eq!(r.capacity(), Some(1)); 77 | } 78 | } 79 | 80 | #[test] 81 | fn len_empty_full() { 82 | let r = tick(ms(50)); 83 | 84 | assert_eq!(r.len(), 0); 85 | assert_eq!(r.is_empty(), true); 86 | assert_eq!(r.is_full(), false); 87 | 88 | thread::sleep(ms(100)); 89 | 90 | assert_eq!(r.len(), 1); 91 | assert_eq!(r.is_empty(), false); 92 | assert_eq!(r.is_full(), true); 93 | 94 | r.try_recv().unwrap(); 95 | 96 | assert_eq!(r.len(), 0); 97 | assert_eq!(r.is_empty(), true); 98 | assert_eq!(r.is_full(), false); 99 | } 100 | 101 | #[test] 102 | fn try_recv() { 103 | let r = tick(ms(200)); 104 | assert!(r.try_recv().is_err()); 105 | 106 | thread::sleep(ms(100)); 107 | assert!(r.try_recv().is_err()); 108 | 109 | thread::sleep(ms(200)); 110 | assert!(r.try_recv().is_ok()); 111 | assert!(r.try_recv().is_err()); 112 | 113 | thread::sleep(ms(200)); 114 | assert!(r.try_recv().is_ok()); 115 | assert!(r.try_recv().is_err()); 116 | } 117 | 118 | #[test] 119 | fn recv() { 120 | let start = Instant::now(); 121 | let r = tick(ms(50)); 122 | 123 | let fired = r.recv().unwrap(); 124 | assert!(start < fired); 125 | assert!(fired - start >= ms(50)); 126 | 127 | let now = Instant::now(); 128 | assert!(fired < now); 129 | assert!(now - fired < fired - start); 130 | 131 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 132 | } 133 | 134 | #[test] 135 | fn recv_timeout() { 136 | let start = Instant::now(); 137 | let r = tick(ms(200)); 138 | 139 | assert!(r.recv_timeout(ms(100)).is_err()); 140 | let now = Instant::now(); 141 | assert!(now - start >= ms(100)); 142 | assert!(now - start <= ms(150)); 143 | 144 | let fired = r.recv_timeout(ms(200)).unwrap(); 145 | assert!(fired - start >= ms(200)); 146 | assert!(fired - start <= ms(250)); 147 | 148 | assert!(r.recv_timeout(ms(100)).is_err()); 149 | let now = Instant::now(); 150 | assert!(now - start >= ms(300)); 151 | assert!(now - start <= ms(350)); 152 | 153 | let fired = r.recv_timeout(ms(200)).unwrap(); 154 | assert!(fired - start >= ms(400)); 155 | assert!(fired - start <= ms(450)); 156 | } 157 | 158 | #[test] 159 | fn recv_two() { 160 | let r1 = tick(ms(50)); 161 | let r2 = tick(ms(50)); 162 | 163 | crossbeam::scope(|scope| { 164 | scope.spawn(|| { 165 | for _ in 0..10 { 166 | select! { 167 | recv(r1) -> _ => {} 168 | recv(r2) -> _ => {} 169 | } 170 | } 171 | }); 172 | scope.spawn(|| { 173 | for _ in 0..10 { 174 | select! { 175 | recv(r1) -> _ => {} 176 | recv(r2) -> _ => {} 177 | } 178 | } 179 | }); 180 | }); 181 | } 182 | 183 | #[test] 184 | fn recv_race() { 185 | select! { 186 | recv(tick(ms(50))) -> _ => {} 187 | recv(tick(ms(100))) -> _ => panic!(), 188 | } 189 | 190 | select! { 191 | recv(tick(ms(100))) -> _ => panic!(), 192 | recv(tick(ms(50))) -> _ => {} 193 | } 194 | } 195 | 196 | #[test] 197 | fn stress_default() { 198 | const COUNT: usize = 10; 199 | 200 | for _ in 0..COUNT { 201 | select! { 202 | recv(tick(ms(0))) -> _ => {} 203 | default => panic!(), 204 | } 205 | } 206 | 207 | for _ in 0..COUNT { 208 | select! { 209 | recv(tick(ms(100))) -> _ => panic!(), 210 | default => {} 211 | } 212 | } 213 | } 214 | 215 | #[test] 216 | fn select() { 217 | const THREADS: usize = 4; 218 | 219 | let hits = AtomicUsize::new(0); 220 | let r1 = tick(ms(200)); 221 | let r2 = tick(ms(300)); 222 | 223 | crossbeam::scope(|scope| { 224 | for _ in 0..THREADS { 225 | scope.spawn(|| { 226 | let timeout = after(ms(1100)); 227 | loop { 228 | select! { 229 | recv(r1) -> _ => { 230 | hits.fetch_add(1, Ordering::SeqCst); 231 | } 232 | recv(r2) -> _ => { 233 | hits.fetch_add(1, Ordering::SeqCst); 234 | } 235 | recv(timeout) -> _ => break 236 | } 237 | } 238 | }); 239 | } 240 | }); 241 | 242 | assert_eq!(hits.load(Ordering::SeqCst), 8); 243 | } 244 | 245 | #[test] 246 | fn fairness() { 247 | const COUNT: usize = 30; 248 | 249 | for &dur in &[0, 1] { 250 | let mut hits = [0usize; 2]; 251 | 252 | for _ in 0..COUNT { 253 | let r1 = tick(ms(dur)); 254 | let r2 = tick(ms(dur)); 255 | 256 | for _ in 0..COUNT { 257 | select! { 258 | recv(r1) -> _ => hits[0] += 1, 259 | recv(r2) -> _ => hits[1] += 1, 260 | } 261 | } 262 | } 263 | 264 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 265 | } 266 | } 267 | 268 | #[test] 269 | fn fairness_duplicates() { 270 | const COUNT: usize = 30; 271 | 272 | for &dur in &[0, 1] { 273 | let mut hits = [0usize; 5]; 274 | 275 | for _ in 0..COUNT { 276 | let r = tick(ms(dur)); 277 | 278 | for _ in 0..COUNT { 279 | select! { 280 | recv(r) -> _ => hits[0] += 1, 281 | recv(r) -> _ => hits[1] += 1, 282 | recv(r) -> _ => hits[2] += 1, 283 | recv(r) -> _ => hits[3] += 1, 284 | recv(r) -> _ => hits[4] += 1, 285 | } 286 | } 287 | } 288 | 289 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /src/waker.rs: -------------------------------------------------------------------------------- 1 | //! Waking mechanism for threads blocked on channel operations. 2 | 3 | use std::collections::VecDeque; 4 | use std::num::Wrapping; 5 | use std::sync::atomic::{AtomicUsize, Ordering}; 6 | use std::thread::{self, ThreadId}; 7 | 8 | use parking_lot::Mutex; 9 | 10 | use context::Context; 11 | use select::{Operation, Selected}; 12 | 13 | /// Represents a thread blocked on a specific channel operation. 14 | pub struct Entry { 15 | /// Context associated with the thread owning this operation. 16 | pub context: Context, 17 | 18 | /// The operation. 19 | pub oper: Operation, 20 | 21 | /// Optional packet. 22 | pub packet: usize, 23 | } 24 | 25 | /// A queue of threads blocked on channel operations. 26 | /// 27 | /// This data structure is used by threads to register blocking operations and get woken up once 28 | /// an operation becomes ready. 29 | pub struct Waker { 30 | /// The list of registered blocking operations. 31 | entries: VecDeque, 32 | 33 | /// The number of calls to `register` and `register_with_packet`. 34 | register_count: Wrapping, 35 | } 36 | 37 | impl Waker { 38 | /// Creates a new `Waker`. 39 | #[inline] 40 | pub fn new() -> Self { 41 | Waker { 42 | entries: VecDeque::new(), 43 | register_count: Wrapping(0), 44 | } 45 | } 46 | 47 | /// Registers the current thread with an operation. 48 | #[inline] 49 | pub fn register(&mut self, oper: Operation, cx: &Context) { 50 | self.register_with_packet(oper, 0, cx); 51 | } 52 | 53 | /// Registers the current thread with an operation and a packet. 54 | #[inline] 55 | pub fn register_with_packet(&mut self, oper: Operation, packet: usize, cx: &Context) { 56 | self.entries.push_back(Entry { 57 | context: cx.clone(), 58 | oper, 59 | packet, 60 | }); 61 | self.register_count += Wrapping(1); 62 | } 63 | 64 | /// Unregisters an operation previously registered by the current thread. 65 | #[inline] 66 | pub fn unregister(&mut self, oper: Operation) -> Option { 67 | if let Some((i, _)) = self.entries 68 | .iter() 69 | .enumerate() 70 | .find(|&(_, entry)| entry.oper == oper) 71 | { 72 | let entry = self.entries.remove(i); 73 | Self::maybe_shrink(&mut self.entries); 74 | entry 75 | } else { 76 | None 77 | } 78 | } 79 | 80 | /// Attempts to find one thread (not the current one), select its operation, and wake it up. 81 | #[inline] 82 | pub fn wake_one(&mut self) -> Option { 83 | if !self.entries.is_empty() { 84 | let thread_id = current_thread_id(); 85 | 86 | for i in 0..self.entries.len() { 87 | // Does the entry belong to a different thread? 88 | if self.entries[i].context.thread_id() != thread_id { 89 | // Try selecting this operation. 90 | let sel = Selected::Operation(self.entries[i].oper); 91 | let res = self.entries[i].context.try_select(sel); 92 | 93 | if res.is_ok() { 94 | // Provide the packet. 95 | self.entries[i].context.store_packet(self.entries[i].packet); 96 | // Wake the thread up. 97 | self.entries[i].context.unpark(); 98 | 99 | // Remove the entry from the queue to keep it clean and improve 100 | // performance. 101 | let entry = self.entries.remove(i).unwrap(); 102 | Self::maybe_shrink(&mut self.entries); 103 | return Some(entry); 104 | } 105 | } 106 | } 107 | } 108 | 109 | None 110 | } 111 | 112 | /// Notifies all threads that the channel is disconnected. 113 | #[inline] 114 | pub fn disconnect(&mut self) { 115 | for entry in self.entries.iter() { 116 | if entry.context.try_select(Selected::Disconnected).is_ok() { 117 | // Wake the thread up. 118 | // 119 | // Here we don't remove the entry from the queue. Registered threads must 120 | // unregister from the waker by themselves. They might also want to recover the 121 | // packet value and destroy it, if necessary. 122 | entry.context.unpark(); 123 | } 124 | } 125 | } 126 | 127 | /// Returns `true` if there is an entry which can be woken up by the current thread. 128 | #[inline] 129 | pub fn can_wake_one(&self) -> bool { 130 | if self.entries.is_empty() { 131 | false 132 | } else { 133 | let thread_id = current_thread_id(); 134 | 135 | self.entries.iter().any(|entry| { 136 | entry.context.thread_id() != thread_id 137 | && entry.context.selected() == Selected::Waiting 138 | }) 139 | } 140 | } 141 | 142 | /// Returns the number of entries in the queue. 143 | #[inline] 144 | pub fn len(&self) -> usize { 145 | self.entries.len() 146 | } 147 | 148 | #[inline] 149 | pub fn register_count(&self) -> usize { 150 | self.register_count.0 151 | } 152 | 153 | /// Shrinks the internal queue if its capacity is much larger than length. 154 | #[inline] 155 | fn maybe_shrink(entries: &mut VecDeque) { 156 | if entries.capacity() > 32 && entries.len() < entries.capacity() / 4 { 157 | let mut v = VecDeque::with_capacity(entries.capacity() / 2); 158 | v.extend(entries.drain(..)); 159 | *entries = v; 160 | } 161 | } 162 | } 163 | 164 | impl Drop for Waker { 165 | #[inline] 166 | fn drop(&mut self) { 167 | debug_assert!(self.entries.is_empty()); 168 | } 169 | } 170 | 171 | /// A waker that can be shared among threads without locking. 172 | /// 173 | /// This is a simple wrapper around `Waker` that internally uses a mutex for synchronization. 174 | pub struct SyncWaker { 175 | /// The inner `Waker`. 176 | inner: Mutex, 177 | 178 | /// Number of operations in the waker. 179 | len: AtomicUsize, 180 | } 181 | 182 | impl SyncWaker { 183 | /// Creates a new `SyncWaker`. 184 | #[inline] 185 | pub fn new() -> Self { 186 | SyncWaker { 187 | inner: Mutex::new(Waker::new()), 188 | len: AtomicUsize::new(0), 189 | } 190 | } 191 | 192 | /// Registers the current thread with an operation. 193 | #[inline] 194 | pub fn register(&self, oper: Operation, cx: &Context) { 195 | let mut inner = self.inner.lock(); 196 | inner.register(oper, cx); 197 | self.len.store(inner.len(), Ordering::SeqCst); 198 | } 199 | 200 | /// Unregisters an operation previously registered by the current thread. 201 | #[inline] 202 | pub fn unregister(&self, oper: Operation) -> Option { 203 | if self.len.load(Ordering::SeqCst) > 0 { 204 | let mut inner = self.inner.lock(); 205 | let entry = inner.unregister(oper); 206 | self.len.store(inner.len(), Ordering::SeqCst); 207 | entry 208 | } else { 209 | None 210 | } 211 | } 212 | 213 | /// Attempts to find one thread (not the current one), select its operation, and wake it up. 214 | #[inline] 215 | pub fn wake_one(&self) -> Option { 216 | if self.len.load(Ordering::SeqCst) > 0 { 217 | let mut inner = self.inner.lock(); 218 | let entry = inner.wake_one(); 219 | self.len.store(inner.len(), Ordering::SeqCst); 220 | entry 221 | } else { 222 | None 223 | } 224 | } 225 | 226 | /// Notifies all threads that the channel is disconnected. 227 | pub fn disconnect(&self) { 228 | self.inner.lock().disconnect(); 229 | } 230 | } 231 | 232 | impl Drop for SyncWaker { 233 | #[inline] 234 | fn drop(&mut self) { 235 | debug_assert_eq!(self.inner.lock().len(), 0); 236 | debug_assert_eq!(self.len.load(Ordering::SeqCst), 0); 237 | } 238 | } 239 | 240 | /// Returns the id of the current thread. 241 | #[inline] 242 | fn current_thread_id() -> ThreadId { 243 | thread_local! { 244 | /// Cached thread-local id. 245 | static THREAD_ID: ThreadId = thread::current().id(); 246 | } 247 | 248 | THREAD_ID 249 | .try_with(|id| *id) 250 | .unwrap_or_else(|_| thread::current().id()) 251 | } 252 | -------------------------------------------------------------------------------- /tests/after.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the after channel flavor. 2 | 3 | extern crate crossbeam; 4 | #[macro_use] 5 | extern crate crossbeam_channel; 6 | extern crate rand; 7 | 8 | use std::sync::atomic::AtomicUsize; 9 | use std::sync::atomic::Ordering; 10 | use std::thread; 11 | use std::time::{Duration, Instant}; 12 | 13 | use crossbeam_channel::{after, Select, TryRecvError}; 14 | 15 | fn ms(ms: u64) -> Duration { 16 | Duration::from_millis(ms) 17 | } 18 | 19 | #[test] 20 | fn fire() { 21 | let start = Instant::now(); 22 | let r = after(ms(50)); 23 | 24 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 25 | thread::sleep(ms(100)); 26 | 27 | let fired = r.try_recv().unwrap(); 28 | assert!(start < fired); 29 | assert!(fired - start >= ms(50)); 30 | 31 | let now = Instant::now(); 32 | assert!(fired < now); 33 | assert!(now - fired >= ms(50)); 34 | 35 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 36 | 37 | select! { 38 | recv(r) -> _ => panic!(), 39 | default => {} 40 | } 41 | 42 | select! { 43 | recv(r) -> _ => panic!(), 44 | recv(after(ms(200))) -> _ => {} 45 | } 46 | } 47 | 48 | #[test] 49 | fn capacity() { 50 | const COUNT: usize = 10; 51 | 52 | for i in 0..COUNT { 53 | let r = after(ms(i as u64)); 54 | assert_eq!(r.capacity(), Some(1)); 55 | } 56 | } 57 | 58 | #[test] 59 | fn len_empty_full() { 60 | let r = after(ms(50)); 61 | 62 | assert_eq!(r.len(), 0); 63 | assert_eq!(r.is_empty(), true); 64 | assert_eq!(r.is_full(), false); 65 | 66 | thread::sleep(ms(100)); 67 | 68 | assert_eq!(r.len(), 1); 69 | assert_eq!(r.is_empty(), false); 70 | assert_eq!(r.is_full(), true); 71 | 72 | r.try_recv().unwrap(); 73 | 74 | assert_eq!(r.len(), 0); 75 | assert_eq!(r.is_empty(), true); 76 | assert_eq!(r.is_full(), false); 77 | } 78 | 79 | #[test] 80 | fn try_recv() { 81 | let r = after(ms(200)); 82 | assert!(r.try_recv().is_err()); 83 | 84 | thread::sleep(ms(100)); 85 | assert!(r.try_recv().is_err()); 86 | 87 | thread::sleep(ms(200)); 88 | assert!(r.try_recv().is_ok()); 89 | assert!(r.try_recv().is_err()); 90 | 91 | thread::sleep(ms(200)); 92 | assert!(r.try_recv().is_err()); 93 | } 94 | 95 | #[test] 96 | fn recv() { 97 | let start = Instant::now(); 98 | let r = after(ms(50)); 99 | 100 | let fired = r.recv().unwrap(); 101 | assert!(start < fired); 102 | assert!(fired - start >= ms(50)); 103 | 104 | let now = Instant::now(); 105 | assert!(fired < now); 106 | assert!(now - fired < fired - start); 107 | 108 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 109 | } 110 | 111 | #[test] 112 | fn recv_timeout() { 113 | let start = Instant::now(); 114 | let r = after(ms(200)); 115 | 116 | assert!(r.recv_timeout(ms(100)).is_err()); 117 | let now = Instant::now(); 118 | assert!(now - start >= ms(100)); 119 | assert!(now - start <= ms(150)); 120 | 121 | let fired = r.recv_timeout(ms(200)).unwrap(); 122 | assert!(fired - start >= ms(200)); 123 | assert!(fired - start <= ms(250)); 124 | 125 | assert!(r.recv_timeout(ms(200)).is_err()); 126 | let now = Instant::now(); 127 | assert!(now - start >= ms(400)); 128 | assert!(now - start <= ms(450)); 129 | 130 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 131 | } 132 | 133 | #[test] 134 | fn recv_two() { 135 | let r1 = after(ms(50)); 136 | let r2 = after(ms(50)); 137 | 138 | crossbeam::scope(|scope| { 139 | scope.spawn(|| { 140 | select! { 141 | recv(r1) -> _ => {} 142 | recv(r2) -> _ => {} 143 | } 144 | }); 145 | scope.spawn(|| { 146 | select! { 147 | recv(r1) -> _ => {} 148 | recv(r2) -> _ => {} 149 | } 150 | }); 151 | }); 152 | } 153 | 154 | #[test] 155 | fn recv_race() { 156 | select! { 157 | recv(after(ms(50))) -> _ => {} 158 | recv(after(ms(100))) -> _ => panic!(), 159 | } 160 | 161 | select! { 162 | recv(after(ms(100))) -> _ => panic!(), 163 | recv(after(ms(50))) -> _ => {} 164 | } 165 | } 166 | 167 | #[test] 168 | fn stress_default() { 169 | const COUNT: usize = 10; 170 | 171 | for _ in 0..COUNT { 172 | select! { 173 | recv(after(ms(0))) -> _ => {} 174 | default => panic!(), 175 | } 176 | } 177 | 178 | for _ in 0..COUNT { 179 | select! { 180 | recv(after(ms(100))) -> _ => panic!(), 181 | default => {} 182 | } 183 | } 184 | } 185 | 186 | #[test] 187 | fn select_shared() { 188 | const THREADS: usize = 4; 189 | const COUNT: usize = 1000; 190 | const TIMEOUT_MS: u64 = 100; 191 | 192 | let v = (0..COUNT).map(|i| after(ms(i as u64 / TIMEOUT_MS / 2))) 193 | .collect::>(); 194 | let hits = AtomicUsize::new(0); 195 | 196 | crossbeam::scope(|scope| { 197 | for _ in 0..THREADS { 198 | scope.spawn(|| { 199 | let v: Vec<&_> = v.iter().collect(); 200 | 201 | loop { 202 | let timeout = after(ms(TIMEOUT_MS)); 203 | let mut sel = Select::new(); 204 | for r in &v { 205 | sel.recv(r); 206 | } 207 | let oper_timeout = sel.recv(&timeout); 208 | 209 | let oper = sel.select(); 210 | match oper.index() { 211 | i if i == oper_timeout => { 212 | oper.recv(&timeout).unwrap(); 213 | break; 214 | } 215 | i => { 216 | oper.recv(&v[i]).unwrap(); 217 | hits.fetch_add(1, Ordering::SeqCst); 218 | } 219 | } 220 | } 221 | }); 222 | } 223 | }); 224 | 225 | assert_eq!(hits.load(Ordering::SeqCst), COUNT); 226 | } 227 | 228 | #[test] 229 | fn select_cloned() { 230 | const THREADS: usize = 4; 231 | const COUNT: usize = 1000; 232 | const TIMEOUT_MS: u64 = 100; 233 | 234 | let v = (0..COUNT).map(|i| after(ms(i as u64 / TIMEOUT_MS / 2))) 235 | .collect::>(); 236 | let hits = AtomicUsize::new(0); 237 | 238 | crossbeam::scope(|scope| { 239 | for _ in 0..THREADS { 240 | scope.spawn(|| { 241 | loop { 242 | let timeout = after(ms(TIMEOUT_MS)); 243 | let mut sel = Select::new(); 244 | for r in &v { 245 | sel.recv(r); 246 | } 247 | let oper_timeout = sel.recv(&timeout); 248 | 249 | let oper = sel.select(); 250 | match oper.index() { 251 | i if i == oper_timeout => { 252 | oper.recv(&timeout).unwrap(); 253 | break; 254 | } 255 | i => { 256 | oper.recv(&v[i]).unwrap(); 257 | hits.fetch_add(1, Ordering::SeqCst); 258 | } 259 | } 260 | } 261 | }); 262 | } 263 | }); 264 | 265 | assert_eq!(hits.load(Ordering::SeqCst), COUNT); 266 | } 267 | 268 | #[test] 269 | fn stress_clone() { 270 | const RUNS: usize = 1000; 271 | const THREADS: usize = 10; 272 | const COUNT: usize = 50; 273 | 274 | for i in 0..RUNS { 275 | let r = after(ms(i as u64)); 276 | 277 | crossbeam::scope(|scope| { 278 | for _ in 0..THREADS { 279 | scope.spawn(|| { 280 | let r = r.clone(); 281 | let _ = r.try_recv(); 282 | 283 | for _ in 0..COUNT { 284 | drop(r.clone()); 285 | thread::yield_now(); 286 | } 287 | }); 288 | } 289 | }); 290 | } 291 | } 292 | 293 | #[test] 294 | fn fairness() { 295 | const COUNT: usize = 1000; 296 | 297 | for &dur in &[0, 1] { 298 | let mut hits = [0usize; 2]; 299 | 300 | for _ in 0..COUNT { 301 | select! { 302 | recv(after(ms(dur))) -> _ => hits[0] += 1, 303 | recv(after(ms(dur))) -> _ => hits[1] += 1, 304 | } 305 | } 306 | 307 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 308 | } 309 | } 310 | 311 | #[test] 312 | fn fairness_duplicates() { 313 | const COUNT: usize = 1000; 314 | 315 | for &dur in &[0, 1] { 316 | let mut hits = [0usize; 5]; 317 | 318 | for _ in 0..COUNT { 319 | let r = after(ms(dur)); 320 | select! { 321 | recv(r) -> _ => hits[0] += 1, 322 | recv(r) -> _ => hits[1] += 1, 323 | recv(r) -> _ => hits[2] += 1, 324 | recv(r) -> _ => hits[3] += 1, 325 | recv(r) -> _ => hits[4] += 1, 326 | } 327 | } 328 | 329 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/err.rs: -------------------------------------------------------------------------------- 1 | use std::error; 2 | use std::fmt; 3 | 4 | /// An error returned from the [`send`] method. 5 | /// 6 | /// The message could not be sent because the channel is disconnected. 7 | /// 8 | /// The error contains the message so it can be recovered. 9 | /// 10 | /// [`send`]: struct.Sender.html#method.send 11 | #[derive(PartialEq, Eq, Clone, Copy)] 12 | pub struct SendError(pub T); 13 | 14 | /// An error returned from the [`try_send`] method. 15 | /// 16 | /// The error contains the message being sent so it can be recovered. 17 | /// 18 | /// [`try_send`]: struct.Sender.html#method.try_send 19 | #[derive(PartialEq, Eq, Clone, Copy)] 20 | pub enum TrySendError { 21 | /// The message could not be sent because the channel is full. 22 | /// 23 | /// If this is a zero-capacity channel, then the error indicates that there was no receiver 24 | /// available to receive the message at the time. 25 | Full(T), 26 | 27 | /// The message could not be sent because the channel is disconnected. 28 | Disconnected(T), 29 | } 30 | 31 | /// An error returned from the [`send_timeout`] method. 32 | /// 33 | /// The error contains the message being sent so it can be recovered. 34 | /// 35 | /// [`send_timeout`]: struct.Sender.html#method.send_timeout 36 | #[derive(PartialEq, Eq, Clone, Copy)] 37 | pub enum SendTimeoutError { 38 | /// The message could not be sent because the channel is full and the operation timed out. 39 | /// 40 | /// If this is a zero-capacity channel, then the error indicates that there was no receiver 41 | /// available to receive the message and the operation timed out. 42 | Timeout(T), 43 | 44 | /// The message could not be sent because the channel is disconnected. 45 | Disconnected(T), 46 | } 47 | 48 | /// An error returned from the [`recv`] method. 49 | /// 50 | /// A message could not be received because the channel is empty and disconnected. 51 | /// 52 | /// [`recv`]: struct.Receiver.html#method.recv 53 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] 54 | pub struct RecvError; 55 | 56 | /// An error returned from the [`try_recv`] method. 57 | /// 58 | /// [`try_recv`]: struct.Receiver.html#method.recv 59 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] 60 | pub enum TryRecvError { 61 | /// A message could not be received because the channel is empty. 62 | /// 63 | /// If this is a zero-capacity channel, then the error indicates that there was no sender 64 | /// available to send a message at the time. 65 | Empty, 66 | 67 | /// The message could not be received because the channel is empty and disconnected. 68 | Disconnected, 69 | } 70 | 71 | /// An error returned from the [`recv_timeout`] method. 72 | /// 73 | /// [`recv_timeout`]: struct.Receiver.html#method.recv_timeout 74 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] 75 | pub enum RecvTimeoutError { 76 | /// A message could not be received because the channel is empty and the operation timed out. 77 | /// 78 | /// If this is a zero-capacity channel, then the error indicates that there was no sender 79 | /// available to send a message and the operation timed out. 80 | Timeout, 81 | 82 | /// The message could not be received because the channel is empty and disconnected. 83 | Disconnected, 84 | } 85 | 86 | /// An error returned from the [`try_select`] method. 87 | /// 88 | /// Failed because none of the channel operations were ready. 89 | /// 90 | /// [`try_select`]: struct.Select.html#method.try_select 91 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] 92 | pub struct TrySelectError; 93 | 94 | /// An error returned from the [`select_timeout`] method. 95 | /// 96 | /// Failed because none of the channel operations became ready before the timeout. 97 | /// 98 | /// [`select_timeout`]: struct.Select.html#method.select_timeout 99 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] 100 | pub struct SelectTimeoutError; 101 | 102 | impl fmt::Debug for SendError { 103 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 104 | "SendError(..)".fmt(f) 105 | } 106 | } 107 | 108 | impl fmt::Display for SendError { 109 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 110 | "sending on a disconnected channel".fmt(f) 111 | } 112 | } 113 | 114 | impl error::Error for SendError { 115 | fn description(&self) -> &str { 116 | "sending on a disconnected channel" 117 | } 118 | 119 | fn cause(&self) -> Option<&error::Error> { 120 | None 121 | } 122 | } 123 | 124 | impl SendError { 125 | /// Unwraps the message. 126 | /// 127 | /// # Examples 128 | /// 129 | /// ```rust 130 | /// use crossbeam_channel::unbounded; 131 | /// 132 | /// let (s, r) = unbounded(); 133 | /// drop(r); 134 | /// 135 | /// if let Err(err) = s.send("foo") { 136 | /// assert_eq!(err.into_inner(), "foo"); 137 | /// } 138 | /// ``` 139 | pub fn into_inner(self) -> T { 140 | self.0 141 | } 142 | } 143 | 144 | impl fmt::Debug for TrySendError { 145 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 146 | match *self { 147 | TrySendError::Full(..) => "Full(..)".fmt(f), 148 | TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f), 149 | } 150 | } 151 | } 152 | 153 | impl fmt::Display for TrySendError { 154 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 155 | match *self { 156 | TrySendError::Full(..) => "sending on a full channel".fmt(f), 157 | TrySendError::Disconnected(..) => "sending on a disconnected channel".fmt(f), 158 | } 159 | } 160 | } 161 | 162 | impl error::Error for TrySendError { 163 | fn description(&self) -> &str { 164 | match *self { 165 | TrySendError::Full(..) => "sending on a full channel", 166 | TrySendError::Disconnected(..) => "sending on a disconnected channel", 167 | } 168 | } 169 | 170 | fn cause(&self) -> Option<&error::Error> { 171 | None 172 | } 173 | } 174 | 175 | impl From> for TrySendError { 176 | fn from(err: SendError) -> TrySendError { 177 | match err { 178 | SendError(t) => TrySendError::Disconnected(t), 179 | } 180 | } 181 | } 182 | 183 | impl TrySendError { 184 | /// Unwraps the message. 185 | /// 186 | /// # Examples 187 | /// 188 | /// ```rust 189 | /// use crossbeam_channel::bounded; 190 | /// 191 | /// let (s, r) = bounded(0); 192 | /// 193 | /// if let Err(err) = s.try_send("foo") { 194 | /// assert_eq!(err.into_inner(), "foo"); 195 | /// } 196 | /// ``` 197 | pub fn into_inner(self) -> T { 198 | match self { 199 | TrySendError::Full(v) => v, 200 | TrySendError::Disconnected(v) => v, 201 | } 202 | } 203 | } 204 | 205 | impl fmt::Debug for SendTimeoutError { 206 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 207 | "SendTimeoutError(..)".fmt(f) 208 | } 209 | } 210 | 211 | impl fmt::Display for SendTimeoutError { 212 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 213 | match *self { 214 | SendTimeoutError::Timeout(..) => "timed out waiting on send operation".fmt(f), 215 | SendTimeoutError::Disconnected(..) => "sending on a disconnected channel".fmt(f), 216 | } 217 | } 218 | } 219 | 220 | impl error::Error for SendTimeoutError { 221 | fn description(&self) -> &str { 222 | "sending on an empty and disconnected channel" 223 | } 224 | 225 | fn cause(&self) -> Option<&error::Error> { 226 | None 227 | } 228 | } 229 | 230 | impl From> for SendTimeoutError { 231 | fn from(err: SendError) -> SendTimeoutError { 232 | match err { 233 | SendError(e) => SendTimeoutError::Disconnected(e), 234 | } 235 | } 236 | } 237 | 238 | impl SendTimeoutError { 239 | /// Unwraps the message. 240 | /// 241 | /// # Examples 242 | /// 243 | /// ```rust 244 | /// use std::time::Duration; 245 | /// use crossbeam_channel::unbounded; 246 | /// 247 | /// let (s, r) = unbounded(); 248 | /// 249 | /// if let Err(err) = s.send_timeout("foo", Duration::from_secs(1)) { 250 | /// assert_eq!(err.into_inner(), "foo"); 251 | /// } 252 | /// ``` 253 | pub fn into_inner(self) -> T { 254 | match self { 255 | SendTimeoutError::Timeout(v) => v, 256 | SendTimeoutError::Disconnected(v) => v, 257 | } 258 | } 259 | } 260 | 261 | impl fmt::Display for RecvError { 262 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 263 | "receiving on an empty and disconnected channel".fmt(f) 264 | } 265 | } 266 | 267 | impl error::Error for RecvError { 268 | fn description(&self) -> &str { 269 | "receiving on an empty and disconnected channel" 270 | } 271 | 272 | fn cause(&self) -> Option<&error::Error> { 273 | None 274 | } 275 | } 276 | 277 | impl fmt::Display for TryRecvError { 278 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 279 | match *self { 280 | TryRecvError::Empty => "receiving on an empty channel".fmt(f), 281 | TryRecvError::Disconnected => "receiving on an empty and disconnected channel".fmt(f), 282 | } 283 | } 284 | } 285 | 286 | impl error::Error for TryRecvError { 287 | fn description(&self) -> &str { 288 | match *self { 289 | TryRecvError::Empty => "receiving on an empty channel", 290 | TryRecvError::Disconnected => "receiving on an empty and disconnected channel", 291 | } 292 | } 293 | 294 | fn cause(&self) -> Option<&error::Error> { 295 | None 296 | } 297 | } 298 | 299 | impl From for TryRecvError { 300 | fn from(err: RecvError) -> TryRecvError { 301 | match err { 302 | RecvError => TryRecvError::Disconnected, 303 | } 304 | } 305 | } 306 | 307 | impl fmt::Display for RecvTimeoutError { 308 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 309 | match *self { 310 | RecvTimeoutError::Timeout => "timed out waiting on receive operation".fmt(f), 311 | RecvTimeoutError::Disconnected => "channel is empty and disconnected".fmt(f), 312 | } 313 | } 314 | } 315 | 316 | impl error::Error for RecvTimeoutError { 317 | fn description(&self) -> &str { 318 | match *self { 319 | RecvTimeoutError::Timeout => "timed out waiting on receive operation", 320 | RecvTimeoutError::Disconnected => "channel is empty and disconnected", 321 | } 322 | } 323 | 324 | fn cause(&self) -> Option<&error::Error> { 325 | None 326 | } 327 | } 328 | 329 | impl From for RecvTimeoutError { 330 | fn from(err: RecvError) -> RecvTimeoutError { 331 | match err { 332 | RecvError => RecvTimeoutError::Disconnected, 333 | } 334 | } 335 | } 336 | 337 | impl fmt::Display for TrySelectError { 338 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 339 | "all operations in select would block".fmt(f) 340 | } 341 | } 342 | 343 | impl error::Error for TrySelectError { 344 | fn description(&self) -> &str { 345 | "all operations in select would block" 346 | } 347 | 348 | fn cause(&self) -> Option<&error::Error> { 349 | None 350 | } 351 | } 352 | 353 | impl fmt::Display for SelectTimeoutError { 354 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 355 | "timed out waiting on select".fmt(f) 356 | } 357 | } 358 | 359 | impl error::Error for SelectTimeoutError { 360 | fn description(&self) -> &str { 361 | "timed out waiting on select" 362 | } 363 | 364 | fn cause(&self) -> Option<&error::Error> { 365 | None 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /tests/list.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the list channel flavor. 2 | 3 | extern crate crossbeam; 4 | #[macro_use] 5 | extern crate crossbeam_channel; 6 | extern crate rand; 7 | 8 | use std::sync::atomic::AtomicUsize; 9 | use std::sync::atomic::Ordering; 10 | use std::thread; 11 | use std::time::Duration; 12 | 13 | use crossbeam_channel::{unbounded}; 14 | use crossbeam_channel::{RecvError, RecvTimeoutError, TryRecvError}; 15 | use crossbeam_channel::{SendError, SendTimeoutError, TrySendError}; 16 | use rand::{thread_rng, Rng}; 17 | 18 | fn ms(ms: u64) -> Duration { 19 | Duration::from_millis(ms) 20 | } 21 | 22 | #[test] 23 | fn smoke() { 24 | let (s, r) = unbounded(); 25 | s.try_send(7).unwrap(); 26 | assert_eq!(r.try_recv(), Ok(7)); 27 | 28 | s.send(8).unwrap(); 29 | assert_eq!(r.recv(), Ok(8)); 30 | 31 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 32 | assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout)); 33 | } 34 | 35 | #[test] 36 | fn capacity() { 37 | let (s, r) = unbounded::<()>(); 38 | assert_eq!(s.capacity(), None); 39 | assert_eq!(r.capacity(), None); 40 | } 41 | 42 | #[test] 43 | fn len_empty_full() { 44 | let (s, r) = unbounded(); 45 | 46 | assert_eq!(s.len(), 0); 47 | assert_eq!(s.is_empty(), true); 48 | assert_eq!(s.is_full(), false); 49 | assert_eq!(r.len(), 0); 50 | assert_eq!(r.is_empty(), true); 51 | assert_eq!(r.is_full(), false); 52 | 53 | s.send(()).unwrap(); 54 | 55 | assert_eq!(s.len(), 1); 56 | assert_eq!(s.is_empty(), false); 57 | assert_eq!(s.is_full(), false); 58 | assert_eq!(r.len(), 1); 59 | assert_eq!(r.is_empty(), false); 60 | assert_eq!(r.is_full(), false); 61 | 62 | r.recv().unwrap(); 63 | 64 | assert_eq!(s.len(), 0); 65 | assert_eq!(s.is_empty(), true); 66 | assert_eq!(s.is_full(), false); 67 | assert_eq!(r.len(), 0); 68 | assert_eq!(r.is_empty(), true); 69 | assert_eq!(r.is_full(), false); 70 | } 71 | 72 | #[test] 73 | fn try_recv() { 74 | let (s, r) = unbounded(); 75 | 76 | crossbeam::scope(|scope| { 77 | scope.spawn(move || { 78 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 79 | thread::sleep(ms(1500)); 80 | assert_eq!(r.try_recv(), Ok(7)); 81 | thread::sleep(ms(500)); 82 | assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)); 83 | }); 84 | scope.spawn(move || { 85 | thread::sleep(ms(1000)); 86 | s.send(7).unwrap(); 87 | }); 88 | }); 89 | } 90 | 91 | #[test] 92 | fn recv() { 93 | let (s, r) = unbounded(); 94 | 95 | crossbeam::scope(|scope| { 96 | scope.spawn(move || { 97 | assert_eq!(r.recv(), Ok(7)); 98 | thread::sleep(ms(1000)); 99 | assert_eq!(r.recv(), Ok(8)); 100 | thread::sleep(ms(1000)); 101 | assert_eq!(r.recv(), Ok(9)); 102 | assert_eq!(r.recv(), Err(RecvError)); 103 | }); 104 | scope.spawn(move || { 105 | thread::sleep(ms(1500)); 106 | s.send(7).unwrap(); 107 | s.send(8).unwrap(); 108 | s.send(9).unwrap(); 109 | }); 110 | }); 111 | } 112 | 113 | #[test] 114 | fn recv_timeout() { 115 | let (s, r) = unbounded::(); 116 | 117 | crossbeam::scope(|scope| { 118 | scope.spawn(move || { 119 | assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout)); 120 | assert_eq!(r.recv_timeout(ms(1000)), Ok(7)); 121 | assert_eq!( 122 | r.recv_timeout(ms(1000)), 123 | Err(RecvTimeoutError::Disconnected) 124 | ); 125 | }); 126 | scope.spawn(move || { 127 | thread::sleep(ms(1500)); 128 | s.send(7).unwrap(); 129 | }); 130 | }); 131 | } 132 | 133 | #[test] 134 | fn try_send() { 135 | let (s, r) = unbounded(); 136 | for i in 0..1000 { 137 | assert_eq!(s.try_send(i), Ok(())); 138 | } 139 | 140 | drop(r); 141 | assert_eq!(s.try_send(777), Err(TrySendError::Disconnected(777))); 142 | } 143 | 144 | #[test] 145 | fn send() { 146 | let (s, r) = unbounded(); 147 | for i in 0..1000 { 148 | assert_eq!(s.send(i), Ok(())); 149 | } 150 | 151 | drop(r); 152 | assert_eq!(s.send(777), Err(SendError(777))); 153 | } 154 | 155 | #[test] 156 | fn send_timeout() { 157 | let (s, r) = unbounded(); 158 | for i in 0..1000 { 159 | assert_eq!(s.send_timeout(i, ms(i as u64)), Ok(())); 160 | } 161 | 162 | drop(r); 163 | assert_eq!(s.send_timeout(777, ms(0)), Err(SendTimeoutError::Disconnected(777))); 164 | } 165 | 166 | #[test] 167 | fn send_after_disconnect() { 168 | let (s, r) = unbounded(); 169 | 170 | s.send(1).unwrap(); 171 | s.send(2).unwrap(); 172 | s.send(3).unwrap(); 173 | 174 | drop(r); 175 | 176 | assert_eq!(s.send(4), Err(SendError(4))); 177 | assert_eq!(s.try_send(5), Err(TrySendError::Disconnected(5))); 178 | assert_eq!(s.send_timeout(6, ms(0)), Err(SendTimeoutError::Disconnected(6))); 179 | } 180 | 181 | #[test] 182 | fn recv_after_disconnect() { 183 | let (s, r) = unbounded(); 184 | 185 | s.send(1).unwrap(); 186 | s.send(2).unwrap(); 187 | s.send(3).unwrap(); 188 | 189 | drop(s); 190 | 191 | assert_eq!(r.recv(), Ok(1)); 192 | assert_eq!(r.recv(), Ok(2)); 193 | assert_eq!(r.recv(), Ok(3)); 194 | assert_eq!(r.recv(), Err(RecvError)); 195 | } 196 | 197 | #[test] 198 | fn len() { 199 | let (s, r) = unbounded(); 200 | 201 | assert_eq!(s.len(), 0); 202 | assert_eq!(r.len(), 0); 203 | 204 | for i in 0..50 { 205 | s.send(i).unwrap(); 206 | assert_eq!(s.len(), i + 1); 207 | } 208 | 209 | for i in 0..50 { 210 | r.recv().unwrap(); 211 | assert_eq!(r.len(), 50 - i - 1); 212 | } 213 | 214 | assert_eq!(s.len(), 0); 215 | assert_eq!(r.len(), 0); 216 | } 217 | 218 | #[test] 219 | fn disconnect_wakes_receiver() { 220 | let (s, r) = unbounded::<()>(); 221 | 222 | crossbeam::scope(|scope| { 223 | scope.spawn(move || { 224 | assert_eq!(r.recv(), Err(RecvError)); 225 | }); 226 | scope.spawn(move || { 227 | thread::sleep(ms(1000)); 228 | drop(s); 229 | }); 230 | }); 231 | } 232 | 233 | #[test] 234 | fn spsc() { 235 | const COUNT: usize = 100_000; 236 | 237 | let (s, r) = unbounded(); 238 | 239 | crossbeam::scope(|scope| { 240 | scope.spawn(move || { 241 | for i in 0..COUNT { 242 | assert_eq!(r.recv(), Ok(i)); 243 | } 244 | assert_eq!(r.recv(), Err(RecvError)); 245 | }); 246 | scope.spawn(move || { 247 | for i in 0..COUNT { 248 | s.send(i).unwrap(); 249 | } 250 | }); 251 | }); 252 | } 253 | 254 | #[test] 255 | fn mpmc() { 256 | const COUNT: usize = 25_000; 257 | const THREADS: usize = 4; 258 | 259 | let (s, r) = unbounded::(); 260 | let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); 261 | 262 | crossbeam::scope(|scope| { 263 | for _ in 0..THREADS { 264 | scope.spawn(|| { 265 | for _ in 0..COUNT { 266 | let n = r.recv().unwrap(); 267 | v[n].fetch_add(1, Ordering::SeqCst); 268 | } 269 | }); 270 | } 271 | for _ in 0..THREADS { 272 | scope.spawn(|| { 273 | for i in 0..COUNT { 274 | s.send(i).unwrap(); 275 | } 276 | }); 277 | } 278 | }); 279 | 280 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 281 | 282 | for c in v { 283 | assert_eq!(c.load(Ordering::SeqCst), THREADS); 284 | } 285 | } 286 | 287 | #[test] 288 | fn stress_timeout_two_threads() { 289 | const COUNT: usize = 100; 290 | 291 | let (s, r) = unbounded(); 292 | 293 | crossbeam::scope(|scope| { 294 | scope.spawn(|| { 295 | for i in 0..COUNT { 296 | if i % 2 == 0 { 297 | thread::sleep(ms(50)); 298 | } 299 | s.send(i).unwrap(); 300 | } 301 | }); 302 | 303 | scope.spawn(|| { 304 | for i in 0..COUNT { 305 | if i % 2 == 0 { 306 | thread::sleep(ms(50)); 307 | } 308 | loop { 309 | if let Ok(x) = r.recv_timeout(ms(10)) { 310 | assert_eq!(x, i); 311 | break; 312 | } 313 | } 314 | } 315 | }); 316 | }); 317 | } 318 | 319 | #[test] 320 | fn drops() { 321 | static DROPS: AtomicUsize = AtomicUsize::new(0); 322 | 323 | #[derive(Debug, PartialEq)] 324 | struct DropCounter; 325 | 326 | impl Drop for DropCounter { 327 | fn drop(&mut self) { 328 | DROPS.fetch_add(1, Ordering::SeqCst); 329 | } 330 | } 331 | 332 | let mut rng = thread_rng(); 333 | 334 | for _ in 0..100 { 335 | let steps = rng.gen_range(0, 10_000); 336 | let additional = rng.gen_range(0, 1000); 337 | 338 | DROPS.store(0, Ordering::SeqCst); 339 | let (s, r) = unbounded::(); 340 | 341 | crossbeam::scope(|scope| { 342 | scope.spawn(|| { 343 | for _ in 0..steps { 344 | r.recv().unwrap(); 345 | } 346 | }); 347 | 348 | scope.spawn(|| { 349 | for _ in 0..steps { 350 | s.send(DropCounter).unwrap(); 351 | } 352 | }); 353 | }); 354 | 355 | for _ in 0..additional { 356 | s.try_send(DropCounter).unwrap(); 357 | } 358 | 359 | assert_eq!(DROPS.load(Ordering::SeqCst), steps); 360 | drop(s); 361 | drop(r); 362 | assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional); 363 | } 364 | } 365 | 366 | #[test] 367 | fn linearizable() { 368 | const COUNT: usize = 25_000; 369 | const THREADS: usize = 4; 370 | 371 | let (s, r) = unbounded(); 372 | 373 | crossbeam::scope(|scope| { 374 | for _ in 0..THREADS { 375 | scope.spawn(|| { 376 | for _ in 0..COUNT { 377 | s.send(0).unwrap(); 378 | r.try_recv().unwrap(); 379 | } 380 | }); 381 | } 382 | }); 383 | } 384 | 385 | #[test] 386 | fn fairness() { 387 | const COUNT: usize = 10_000; 388 | 389 | let (s1, r1) = unbounded::<()>(); 390 | let (s2, r2) = unbounded::<()>(); 391 | 392 | for _ in 0..COUNT { 393 | s1.send(()).unwrap(); 394 | s2.send(()).unwrap(); 395 | } 396 | 397 | let mut hits = [0usize; 2]; 398 | for _ in 0..COUNT { 399 | select! { 400 | recv(r1) -> _ => hits[0] += 1, 401 | recv(r2) -> _ => hits[1] += 1, 402 | } 403 | } 404 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 405 | } 406 | 407 | #[test] 408 | fn fairness_duplicates() { 409 | const COUNT: usize = 10_000; 410 | 411 | let (s, r) = unbounded(); 412 | 413 | for _ in 0..COUNT { 414 | s.send(()).unwrap(); 415 | } 416 | 417 | let mut hits = [0usize; 5]; 418 | for _ in 0..COUNT { 419 | select! { 420 | recv(r) -> _ => hits[0] += 1, 421 | recv(r) -> _ => hits[1] += 1, 422 | recv(r) -> _ => hits[2] += 1, 423 | recv(r) -> _ => hits[3] += 1, 424 | recv(r) -> _ => hits[4] += 1, 425 | } 426 | } 427 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 428 | } 429 | 430 | #[test] 431 | fn recv_in_send() { 432 | let (s, r) = unbounded(); 433 | s.send(()).unwrap(); 434 | 435 | select! { 436 | send(s, assert_eq!(r.recv(), Ok(()))) -> _ => {} 437 | } 438 | } 439 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(missing_docs, missing_debug_implementations)] 2 | 3 | //! Multi-producer multi-consumer channels for message passing. 4 | //! 5 | //! This library is an alternative to [`std::sync::mpsc`] with more features and better 6 | //! performance. 7 | //! 8 | //! # Hello, world! 9 | //! 10 | //! ``` 11 | //! use crossbeam_channel::unbounded; 12 | //! 13 | //! // Create a channel of unbounded capacity. 14 | //! let (s, r) = unbounded(); 15 | //! 16 | //! // Send a message into the channel. 17 | //! s.send("Hello, world!").unwrap(); 18 | //! 19 | //! // Receive the message from the channel. 20 | //! assert_eq!(r.recv(), Ok("Hello, world!")); 21 | //! ``` 22 | //! 23 | //! # Channel types 24 | //! 25 | //! Channels can be created using two functions: 26 | //! 27 | //! * [`bounded`] creates a channel of bounded capacity, i.e. there is a limit to how many messages 28 | //! it can hold at a time. 29 | //! 30 | //! * [`unbounded`] creates a channel of unbounded capacity, i.e. it can hold any number of 31 | //! messages at a time. 32 | //! 33 | //! Both functions return a [`Sender`] and a [`Receiver`], which represent the two opposite sides 34 | //! of a channel. 35 | //! 36 | //! Creating a bounded channel: 37 | //! 38 | //! ``` 39 | //! use crossbeam_channel::bounded; 40 | //! 41 | //! // Create a channel that can hold at most 5 messages at a time. 42 | //! let (s, r) = bounded(5); 43 | //! 44 | //! // Can send only 5 messages without blocking. 45 | //! for i in 0..5 { 46 | //! s.send(i).unwrap(); 47 | //! } 48 | //! 49 | //! // Another call to `send` would block because the channel is full. 50 | //! // s.send(5).unwrap(); 51 | //! ``` 52 | //! 53 | //! Creating an unbounded channel: 54 | //! 55 | //! ``` 56 | //! use crossbeam_channel::unbounded; 57 | //! 58 | //! // Create an unbounded channel. 59 | //! let (s, r) = unbounded(); 60 | //! 61 | //! // Can send any number of messages into the channel without blocking. 62 | //! for i in 0..1000 { 63 | //! s.send(i).unwrap(); 64 | //! } 65 | //! ``` 66 | //! 67 | //! A special case is zero-capacity channel, which cannot hold any messages. Instead, send and 68 | //! receive operations must appear at the same time in order to pair up and pass the message over: 69 | //! 70 | //! ``` 71 | //! use std::thread; 72 | //! use crossbeam_channel::bounded; 73 | //! 74 | //! // Create a zero-capacity channel. 75 | //! let (s, r) = bounded(0); 76 | //! 77 | //! // Sending blocks until a receive operation appears on the other side. 78 | //! thread::spawn(move || s.send("Hi!").unwrap()); 79 | //! 80 | //! // Receiving blocks until a send operation appears on the other side. 81 | //! assert_eq!(r.recv(), Ok("Hi!")); 82 | //! ``` 83 | //! 84 | //! # Sharing channels 85 | //! 86 | //! Senders and receivers can be cloned and sent to other threads: 87 | //! 88 | //! ``` 89 | //! use std::thread; 90 | //! use crossbeam_channel::bounded; 91 | //! 92 | //! let (s1, r1) = bounded(0); 93 | //! let (s2, r2) = (s1.clone(), r1.clone()); 94 | //! 95 | //! // Spawn a thread that receives a message and then sends one. 96 | //! thread::spawn(move || { 97 | //! r2.recv().unwrap(); 98 | //! s2.send(2).unwrap(); 99 | //! }); 100 | //! 101 | //! // Send a message and then receive one. 102 | //! s1.send(1).unwrap(); 103 | //! r1.recv().unwrap(); 104 | //! ``` 105 | //! 106 | //! Note that cloning only creates a new handle to the same sending or receiving side. It does not 107 | //! create a separate stream of messages in any way: 108 | //! 109 | //! ``` 110 | //! use crossbeam_channel::unbounded; 111 | //! 112 | //! let (s1, r1) = unbounded(); 113 | //! let (s2, r2) = (s1.clone(), r1.clone()); 114 | //! let (s3, r3) = (s2.clone(), r2.clone()); 115 | //! 116 | //! s1.send(10).unwrap(); 117 | //! s2.send(20).unwrap(); 118 | //! s3.send(30).unwrap(); 119 | //! 120 | //! assert_eq!(r3.recv(), Ok(10)); 121 | //! assert_eq!(r1.recv(), Ok(20)); 122 | //! assert_eq!(r2.recv(), Ok(30)); 123 | //! ``` 124 | //! 125 | //! It's also possible to share senders and receivers by reference: 126 | //! 127 | //! ``` 128 | //! # extern crate crossbeam; 129 | //! # extern crate crossbeam_channel; 130 | //! # fn main() { 131 | //! use std::thread; 132 | //! use crossbeam; 133 | //! use crossbeam_channel::bounded; 134 | //! 135 | //! let (s, r) = bounded(0); 136 | //! 137 | //! crossbeam::scope(|scope| { 138 | //! // Spawn a thread that receives a message and then sends one. 139 | //! scope.spawn(|| { 140 | //! r.recv().unwrap(); 141 | //! s.send(2).unwrap(); 142 | //! }); 143 | //! 144 | //! // Send a message and then receive one. 145 | //! s.send(1).unwrap(); 146 | //! r.recv().unwrap(); 147 | //! }); 148 | //! # } 149 | //! ``` 150 | //! 151 | //! # Disconnection 152 | //! 153 | //! When all senders or all receivers associated with a channel get dropped, the channel becomes 154 | //! disconnected. No more messages can be sent, but any remaining messages can still be received. 155 | //! Send and receive operations on a disconnected channel never block. 156 | //! 157 | //! ``` 158 | //! use crossbeam_channel::{unbounded, RecvError}; 159 | //! 160 | //! let (s, r) = unbounded(); 161 | //! s.send(1).unwrap(); 162 | //! s.send(2).unwrap(); 163 | //! s.send(3).unwrap(); 164 | //! 165 | //! // The only sender is dropped, disconnecting the channel. 166 | //! drop(s); 167 | //! 168 | //! // The remaining messages can be received. 169 | //! assert_eq!(r.recv(), Ok(1)); 170 | //! assert_eq!(r.recv(), Ok(2)); 171 | //! assert_eq!(r.recv(), Ok(3)); 172 | //! 173 | //! // There are no more messages in the channel. 174 | //! assert!(r.is_empty()); 175 | //! 176 | //! // Note that calling `r.recv()` does not block. 177 | //! // Instead, `Err(RecvError)` is returned immediately. 178 | //! assert_eq!(r.recv(), Err(RecvError)); 179 | //! ``` 180 | //! 181 | //! # Blocking operations 182 | //! 183 | //! Send and receive operations come in three flavors: 184 | //! 185 | //! * Non-blocking (returns immediately with success or failure). 186 | //! * Blocking (waits until the operation succeeds or the channel becomes disconnected). 187 | //! * Blocking with a timeout (blocks only for a certain duration of time). 188 | //! 189 | //! A simple example showing the difference between non-blocking and blocking operations: 190 | //! 191 | //! ``` 192 | //! use crossbeam_channel::{bounded, RecvError, TryRecvError}; 193 | //! 194 | //! let (s, r) = bounded(1); 195 | //! 196 | //! // Send a message into the channel. 197 | //! s.send("foo").unwrap(); 198 | //! 199 | //! // This call would block because the channel is full. 200 | //! // s.send("bar").unwrap(); 201 | //! 202 | //! // Receive the message. 203 | //! assert_eq!(r.recv(), Ok("foo")); 204 | //! 205 | //! // This call would block because the channel is empty. 206 | //! // r.recv(); 207 | //! 208 | //! // Try receiving a message without blocking. 209 | //! assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 210 | //! 211 | //! // Disconnect the channel. 212 | //! drop(s); 213 | //! 214 | //! // This call doesn't block because the channel is now disconnected. 215 | //! assert_eq!(r.recv(), Err(RecvError)); 216 | //! ``` 217 | //! 218 | //! # Iteration 219 | //! 220 | //! Receivers can be used as iterators. For example, method [`iter`] creates an iterator that 221 | //! receives messages until the channel becomes empty and disconnected. Note that iteration may 222 | //! block waiting for next message to arrive. 223 | //! 224 | //! ``` 225 | //! use std::thread; 226 | //! use crossbeam_channel::unbounded; 227 | //! 228 | //! let (s, r) = unbounded(); 229 | //! 230 | //! thread::spawn(move || { 231 | //! s.send(1).unwrap(); 232 | //! s.send(2).unwrap(); 233 | //! s.send(3).unwrap(); 234 | //! drop(s); // Disconnect the channel. 235 | //! }); 236 | //! 237 | //! // Collect all messages from the channel. 238 | //! // Note that the call to `collect` blocks until the sender is dropped. 239 | //! let v: Vec<_> = r.iter().collect(); 240 | //! 241 | //! assert_eq!(v, [1, 2, 3]); 242 | //! ``` 243 | //! 244 | //! A non-blocking iterator can be created using [`try_iter`], which receives all available 245 | //! messages without blocking: 246 | //! 247 | //! ``` 248 | //! use crossbeam_channel::unbounded; 249 | //! 250 | //! let (s, r) = unbounded(); 251 | //! s.send(1).unwrap(); 252 | //! s.send(2).unwrap(); 253 | //! s.send(3).unwrap(); 254 | //! // No need to drop the sender. 255 | //! 256 | //! // Receive all messages currently in the channel. 257 | //! let v: Vec<_> = r.try_iter().collect(); 258 | //! 259 | //! assert_eq!(v, [1, 2, 3]); 260 | //! ``` 261 | //! 262 | //! # Selection 263 | //! 264 | //! The [`select!`] macro allows you to define a set of channel operations, wait until any one of 265 | //! them becomes ready, and finally execute it. If multiple operations are ready at the same time, 266 | //! a random one among them is selected. 267 | //! 268 | //! It is also possible to define a `default` case that gets executed if none of the operations are 269 | //! ready, either right away or for a certain duration of time. 270 | //! 271 | //! An operation is considered to be ready if it doesn't have to block. Note that it is ready even 272 | //! when it will simply return an error because the channel is disconnected. 273 | //! 274 | //! An example of receiving a message from two channels: 275 | //! 276 | //! ``` 277 | //! # #[macro_use] 278 | //! # extern crate crossbeam_channel; 279 | //! # fn main() { 280 | //! use std::thread; 281 | //! use std::time::Duration; 282 | //! use crossbeam_channel::unbounded; 283 | //! 284 | //! let (s1, r1) = unbounded(); 285 | //! let (s2, r2) = unbounded(); 286 | //! 287 | //! thread::spawn(move || s1.send(10).unwrap()); 288 | //! thread::spawn(move || s2.send(20).unwrap()); 289 | //! 290 | //! // At most one of these two receive operations will be executed. 291 | //! select! { 292 | //! recv(r1) -> msg => assert_eq!(msg, Ok(10)), 293 | //! recv(r2) -> msg => assert_eq!(msg, Ok(20)), 294 | //! default(Duration::from_secs(1)) => println!("timed out"), 295 | //! } 296 | //! # } 297 | //! ``` 298 | //! 299 | //! If you need to select over a dynamically created list of channel operations, use [`Select`] 300 | //! instead. The [`select!`] macro is just a convenience wrapper around [`Select`]. 301 | //! 302 | //! # Extra channels 303 | //! 304 | //! Three functions can create special kinds of channels, all of which return just a [`Receiver`] 305 | //! handle: 306 | //! 307 | //! * [`after`] creates a channel that delivers a single message after a certain duration of time. 308 | //! * [`tick`] creates a channel that delivers messages periodically. 309 | //! * [`never`] creates a channel that never delivers messages. 310 | //! 311 | //! These channels are very efficient because messages get lazily generated on receive operations. 312 | //! 313 | //! An example that prints elapsed time every 50 milliseconds for the duration of 1 second: 314 | //! 315 | //! ``` 316 | //! # #[macro_use] 317 | //! # extern crate crossbeam_channel; 318 | //! # fn main() { 319 | //! use std::time::{Duration, Instant}; 320 | //! use crossbeam_channel::{after, tick}; 321 | //! 322 | //! let start = Instant::now(); 323 | //! let ticker = tick(Duration::from_millis(50)); 324 | //! let timeout = after(Duration::from_secs(1)); 325 | //! 326 | //! loop { 327 | //! select! { 328 | //! recv(ticker) -> _ => println!("elapsed: {:?}", start.elapsed()), 329 | //! recv(timeout) -> _ => break, 330 | //! } 331 | //! } 332 | //! # } 333 | //! ``` 334 | //! 335 | //! [`std::sync::mpsc`]: https://doc.rust-lang.org/std/sync/mpsc/index.html 336 | //! [`unbounded`]: fn.unbounded.html 337 | //! [`bounded`]: fn.bounded.html 338 | //! [`after`]: fn.after.html 339 | //! [`tick`]: fn.tick.html 340 | //! [`never`]: fn.never.html 341 | //! [`send`]: struct.Sender.html#method.send 342 | //! [`recv`]: struct.Receiver.html#method.recv 343 | //! [`iter`]: struct.Receiver.html#method.iter 344 | //! [`try_iter`]: struct.Receiver.html#method.try_iter 345 | //! [`select!`]: macro.select.html 346 | //! [`Select`]: struct.Select.html 347 | //! [`Sender`]: struct.Sender.html 348 | //! [`Receiver`]: struct.Receiver.html 349 | 350 | extern crate crossbeam_epoch; 351 | extern crate crossbeam_utils; 352 | extern crate parking_lot; 353 | extern crate rand; 354 | extern crate smallvec; 355 | 356 | mod channel; 357 | mod context; 358 | mod err; 359 | mod flavors; 360 | mod select; 361 | mod select_macro; 362 | mod utils; 363 | mod waker; 364 | 365 | pub use channel::{Receiver, Sender}; 366 | pub use channel::{bounded, unbounded}; 367 | pub use channel::{after, never, tick}; 368 | pub use channel::{IntoIter, Iter, TryIter}; 369 | 370 | pub use select::{Select, SelectedOperation}; 371 | 372 | pub use err::{RecvError, RecvTimeoutError, TryRecvError}; 373 | pub use err::{SendError, SendTimeoutError, TrySendError}; 374 | pub use err::{SelectTimeoutError, TrySelectError}; 375 | -------------------------------------------------------------------------------- /tests/zero.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the zero channel flavor. 2 | 3 | extern crate crossbeam; 4 | #[macro_use] 5 | extern crate crossbeam_channel; 6 | extern crate rand; 7 | 8 | use std::sync::atomic::AtomicUsize; 9 | use std::sync::atomic::Ordering; 10 | use std::thread; 11 | use std::time::Duration; 12 | 13 | use crossbeam_channel::{bounded}; 14 | use crossbeam_channel::{RecvError, RecvTimeoutError, TryRecvError}; 15 | use crossbeam_channel::{SendError, SendTimeoutError, TrySendError}; 16 | use rand::{thread_rng, Rng}; 17 | 18 | fn ms(ms: u64) -> Duration { 19 | Duration::from_millis(ms) 20 | } 21 | 22 | #[test] 23 | fn smoke() { 24 | let (s, r) = bounded(0); 25 | assert_eq!(s.try_send(7), Err(TrySendError::Full(7))); 26 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 27 | } 28 | 29 | #[test] 30 | fn capacity() { 31 | let (s, r) = bounded::<()>(0); 32 | assert_eq!(s.capacity(), Some(0)); 33 | assert_eq!(r.capacity(), Some(0)); 34 | } 35 | 36 | #[test] 37 | fn len_empty_full() { 38 | let (s, r) = bounded(0); 39 | 40 | assert_eq!(s.len(), 0); 41 | assert_eq!(s.is_empty(), true); 42 | assert_eq!(s.is_full(), true); 43 | assert_eq!(r.len(), 0); 44 | assert_eq!(r.is_empty(), true); 45 | assert_eq!(r.is_full(), true); 46 | 47 | crossbeam::scope(|scope| { 48 | scope.spawn(|| s.send(0).unwrap()); 49 | scope.spawn(|| r.recv().unwrap()); 50 | }); 51 | 52 | assert_eq!(s.len(), 0); 53 | assert_eq!(s.is_empty(), true); 54 | assert_eq!(s.is_full(), true); 55 | assert_eq!(r.len(), 0); 56 | assert_eq!(r.is_empty(), true); 57 | assert_eq!(r.is_full(), true); 58 | } 59 | 60 | #[test] 61 | fn try_recv() { 62 | let (s, r) = bounded(0); 63 | 64 | crossbeam::scope(|scope| { 65 | scope.spawn(move || { 66 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 67 | thread::sleep(ms(1500)); 68 | assert_eq!(r.try_recv(), Ok(7)); 69 | thread::sleep(ms(500)); 70 | assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)); 71 | }); 72 | scope.spawn(move || { 73 | thread::sleep(ms(1000)); 74 | s.send(7).unwrap(); 75 | }); 76 | }); 77 | } 78 | 79 | #[test] 80 | fn recv() { 81 | let (s, r) = bounded(0); 82 | 83 | crossbeam::scope(|scope| { 84 | scope.spawn(move || { 85 | assert_eq!(r.recv(), Ok(7)); 86 | thread::sleep(ms(1000)); 87 | assert_eq!(r.recv(), Ok(8)); 88 | thread::sleep(ms(1000)); 89 | assert_eq!(r.recv(), Ok(9)); 90 | assert_eq!(r.recv(), Err(RecvError)); 91 | }); 92 | scope.spawn(move || { 93 | thread::sleep(ms(1500)); 94 | s.send(7).unwrap(); 95 | s.send(8).unwrap(); 96 | s.send(9).unwrap(); 97 | }); 98 | }); 99 | } 100 | 101 | #[test] 102 | fn recv_timeout() { 103 | let (s, r) = bounded::(0); 104 | 105 | crossbeam::scope(|scope| { 106 | scope.spawn(move || { 107 | assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout)); 108 | assert_eq!(r.recv_timeout(ms(1000)), Ok(7)); 109 | assert_eq!( 110 | r.recv_timeout(ms(1000)), 111 | Err(RecvTimeoutError::Disconnected) 112 | ); 113 | }); 114 | scope.spawn(move || { 115 | thread::sleep(ms(1500)); 116 | s.send(7).unwrap(); 117 | }); 118 | }); 119 | } 120 | 121 | #[test] 122 | fn try_send() { 123 | let (s, r) = bounded(0); 124 | 125 | crossbeam::scope(|scope| { 126 | scope.spawn(move || { 127 | assert_eq!(s.try_send(7), Err(TrySendError::Full(7))); 128 | thread::sleep(ms(1500)); 129 | assert_eq!(s.try_send(8), Ok(())); 130 | thread::sleep(ms(500)); 131 | assert_eq!(s.try_send(9), Err(TrySendError::Disconnected(9))); 132 | }); 133 | scope.spawn(move || { 134 | thread::sleep(ms(1000)); 135 | assert_eq!(r.recv(), Ok(8)); 136 | }); 137 | }); 138 | } 139 | 140 | #[test] 141 | fn send() { 142 | let (s, r) = bounded(0); 143 | 144 | crossbeam::scope(|scope| { 145 | scope.spawn(move || { 146 | s.send(7).unwrap(); 147 | thread::sleep(ms(1000)); 148 | s.send(8).unwrap(); 149 | thread::sleep(ms(1000)); 150 | s.send(9).unwrap(); 151 | }); 152 | scope.spawn(move || { 153 | thread::sleep(ms(1500)); 154 | assert_eq!(r.recv(), Ok(7)); 155 | assert_eq!(r.recv(), Ok(8)); 156 | assert_eq!(r.recv(), Ok(9)); 157 | }); 158 | }); 159 | } 160 | 161 | #[test] 162 | fn send_timeout() { 163 | let (s, r) = bounded(0); 164 | 165 | crossbeam::scope(|scope| { 166 | scope.spawn(move || { 167 | assert_eq!( 168 | s.send_timeout(7, ms(1000)), 169 | Err(SendTimeoutError::Timeout(7)) 170 | ); 171 | assert_eq!(s.send_timeout(8, ms(1000)), Ok(())); 172 | assert_eq!( 173 | s.send_timeout(9, ms(1000)), 174 | Err(SendTimeoutError::Disconnected(9)) 175 | ); 176 | }); 177 | scope.spawn(move || { 178 | thread::sleep(ms(1500)); 179 | assert_eq!(r.recv(), Ok(8)); 180 | }); 181 | }); 182 | } 183 | 184 | #[test] 185 | fn len() { 186 | const COUNT: usize = 25_000; 187 | 188 | let (s, r) = bounded(0); 189 | 190 | assert_eq!(s.len(), 0); 191 | assert_eq!(r.len(), 0); 192 | 193 | crossbeam::scope(|scope| { 194 | scope.spawn(|| { 195 | for i in 0..COUNT { 196 | assert_eq!(r.recv(), Ok(i)); 197 | assert_eq!(r.len(), 0); 198 | } 199 | }); 200 | 201 | scope.spawn(|| { 202 | for i in 0..COUNT { 203 | s.send(i).unwrap(); 204 | assert_eq!(s.len(), 0); 205 | } 206 | }); 207 | }); 208 | 209 | assert_eq!(s.len(), 0); 210 | assert_eq!(r.len(), 0); 211 | } 212 | 213 | #[test] 214 | fn disconnect_wakes_sender() { 215 | let (s, r) = bounded(0); 216 | 217 | crossbeam::scope(|scope| { 218 | scope.spawn(move || { 219 | assert_eq!(s.send(()), Err(SendError(()))); 220 | }); 221 | scope.spawn(move || { 222 | thread::sleep(ms(1000)); 223 | drop(r); 224 | }); 225 | }); 226 | } 227 | 228 | #[test] 229 | fn disconnect_wakes_receiver() { 230 | let (s, r) = bounded::<()>(0); 231 | 232 | crossbeam::scope(|scope| { 233 | scope.spawn(move || { 234 | assert_eq!(r.recv(), Err(RecvError)); 235 | }); 236 | scope.spawn(move || { 237 | thread::sleep(ms(1000)); 238 | drop(s); 239 | }); 240 | }); 241 | } 242 | 243 | #[test] 244 | fn spsc() { 245 | const COUNT: usize = 100_000; 246 | 247 | let (s, r) = bounded(0); 248 | 249 | crossbeam::scope(|scope| { 250 | scope.spawn(move || { 251 | for i in 0..COUNT { 252 | assert_eq!(r.recv(), Ok(i)); 253 | } 254 | assert_eq!(r.recv(), Err(RecvError)); 255 | }); 256 | scope.spawn(move || { 257 | for i in 0..COUNT { 258 | s.send(i).unwrap(); 259 | } 260 | }); 261 | }); 262 | } 263 | 264 | #[test] 265 | fn mpmc() { 266 | const COUNT: usize = 25_000; 267 | const THREADS: usize = 4; 268 | 269 | let (s, r) = bounded::(0); 270 | let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); 271 | 272 | crossbeam::scope(|scope| { 273 | for _ in 0..THREADS { 274 | scope.spawn(|| { 275 | for _ in 0..COUNT { 276 | let n = r.recv().unwrap(); 277 | v[n].fetch_add(1, Ordering::SeqCst); 278 | } 279 | }); 280 | } 281 | for _ in 0..THREADS { 282 | scope.spawn(|| { 283 | for i in 0..COUNT { 284 | s.send(i).unwrap(); 285 | } 286 | }); 287 | } 288 | }); 289 | 290 | for c in v { 291 | assert_eq!(c.load(Ordering::SeqCst), THREADS); 292 | } 293 | } 294 | 295 | #[test] 296 | fn stress_timeout_two_threads() { 297 | const COUNT: usize = 100; 298 | 299 | let (s, r) = bounded(0); 300 | 301 | crossbeam::scope(|scope| { 302 | scope.spawn(|| { 303 | for i in 0..COUNT { 304 | if i % 2 == 0 { 305 | thread::sleep(ms(50)); 306 | } 307 | loop { 308 | if let Ok(()) = s.send_timeout(i, ms(10)) { 309 | break; 310 | } 311 | } 312 | } 313 | }); 314 | 315 | scope.spawn(|| { 316 | for i in 0..COUNT { 317 | if i % 2 == 0 { 318 | thread::sleep(ms(50)); 319 | } 320 | loop { 321 | if let Ok(x) = r.recv_timeout(ms(10)) { 322 | assert_eq!(x, i); 323 | break; 324 | } 325 | } 326 | } 327 | }); 328 | }); 329 | } 330 | 331 | #[test] 332 | fn drops() { 333 | static DROPS: AtomicUsize = AtomicUsize::new(0); 334 | 335 | #[derive(Debug, PartialEq)] 336 | struct DropCounter; 337 | 338 | impl Drop for DropCounter { 339 | fn drop(&mut self) { 340 | DROPS.fetch_add(1, Ordering::SeqCst); 341 | } 342 | } 343 | 344 | let mut rng = thread_rng(); 345 | 346 | for _ in 0..100 { 347 | let steps = rng.gen_range(0, 3_000); 348 | 349 | DROPS.store(0, Ordering::SeqCst); 350 | let (s, r) = bounded::(0); 351 | 352 | crossbeam::scope(|scope| { 353 | scope.spawn(|| { 354 | for _ in 0..steps { 355 | r.recv().unwrap(); 356 | } 357 | }); 358 | 359 | scope.spawn(|| { 360 | for _ in 0..steps { 361 | s.send(DropCounter).unwrap(); 362 | } 363 | }); 364 | }); 365 | 366 | assert_eq!(DROPS.load(Ordering::SeqCst), steps); 367 | drop(s); 368 | drop(r); 369 | assert_eq!(DROPS.load(Ordering::SeqCst), steps); 370 | } 371 | } 372 | 373 | #[test] 374 | fn fairness() { 375 | const COUNT: usize = 10_000; 376 | 377 | let (s1, r1) = bounded::<()>(0); 378 | let (s2, r2) = bounded::<()>(0); 379 | 380 | crossbeam::scope(|scope| { 381 | scope.spawn(|| { 382 | let mut hits = [0usize; 2]; 383 | for _ in 0..COUNT { 384 | select! { 385 | recv(r1) -> _ => hits[0] += 1, 386 | recv(r2) -> _ => hits[1] += 1, 387 | } 388 | } 389 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 390 | }); 391 | 392 | let mut hits = [0usize; 2]; 393 | for _ in 0..COUNT { 394 | select! { 395 | send(s1, ()) -> _ => hits[0] += 1, 396 | send(s2, ()) -> _ => hits[1] += 1, 397 | } 398 | } 399 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 400 | }); 401 | } 402 | 403 | #[test] 404 | fn fairness_duplicates() { 405 | const COUNT: usize = 10_000; 406 | 407 | let (s, r) = bounded::<()>(0); 408 | 409 | crossbeam::scope(|scope| { 410 | scope.spawn(|| { 411 | let mut hits = [0usize; 5]; 412 | for _ in 0..COUNT { 413 | select! { 414 | recv(r) -> _ => hits[0] += 1, 415 | recv(r) -> _ => hits[1] += 1, 416 | recv(r) -> _ => hits[2] += 1, 417 | recv(r) -> _ => hits[3] += 1, 418 | recv(r) -> _ => hits[4] += 1, 419 | } 420 | } 421 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 422 | }); 423 | 424 | let mut hits = [0usize; 5]; 425 | for _ in 0..COUNT { 426 | select! { 427 | send(s, ()) -> _ => hits[0] += 1, 428 | send(s, ()) -> _ => hits[1] += 1, 429 | send(s, ()) -> _ => hits[2] += 1, 430 | send(s, ()) -> _ => hits[3] += 1, 431 | send(s, ()) -> _ => hits[4] += 1, 432 | } 433 | } 434 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 435 | }); 436 | } 437 | 438 | #[test] 439 | fn recv_in_send() { 440 | let (s, r) = bounded(0); 441 | 442 | crossbeam::scope(|scope| { 443 | scope.spawn(|| { 444 | thread::sleep(ms(100)); 445 | r.recv() 446 | }); 447 | 448 | scope.spawn(|| { 449 | thread::sleep(ms(500)); 450 | s.send(()).unwrap(); 451 | }); 452 | 453 | select! { 454 | send(s, r.recv().unwrap()) -> _ => {} 455 | } 456 | }); 457 | } 458 | -------------------------------------------------------------------------------- /tests/array.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the array channel flavor. 2 | 3 | extern crate crossbeam; 4 | #[macro_use] 5 | extern crate crossbeam_channel; 6 | extern crate rand; 7 | 8 | use std::sync::atomic::AtomicUsize; 9 | use std::sync::atomic::Ordering; 10 | use std::thread; 11 | use std::time::Duration; 12 | 13 | use crossbeam_channel::{bounded}; 14 | use crossbeam_channel::{RecvError, RecvTimeoutError, TryRecvError}; 15 | use crossbeam_channel::{SendError, SendTimeoutError, TrySendError}; 16 | use rand::{thread_rng, Rng}; 17 | 18 | fn ms(ms: u64) -> Duration { 19 | Duration::from_millis(ms) 20 | } 21 | 22 | #[test] 23 | fn smoke() { 24 | let (s, r) = bounded(1); 25 | s.send(7).unwrap(); 26 | assert_eq!(r.try_recv(), Ok(7)); 27 | 28 | s.send(8).unwrap(); 29 | assert_eq!(r.recv(), Ok(8)); 30 | 31 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 32 | assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout)); 33 | } 34 | 35 | #[test] 36 | fn capacity() { 37 | for i in 1..10 { 38 | let (s, r) = bounded::<()>(i); 39 | assert_eq!(s.capacity(), Some(i)); 40 | assert_eq!(r.capacity(), Some(i)); 41 | } 42 | } 43 | 44 | #[test] 45 | fn len_empty_full() { 46 | let (s, r) = bounded(2); 47 | 48 | assert_eq!(s.len(), 0); 49 | assert_eq!(s.is_empty(), true); 50 | assert_eq!(s.is_full(), false); 51 | assert_eq!(r.len(), 0); 52 | assert_eq!(r.is_empty(), true); 53 | assert_eq!(r.is_full(), false); 54 | 55 | s.send(()).unwrap(); 56 | 57 | assert_eq!(s.len(), 1); 58 | assert_eq!(s.is_empty(), false); 59 | assert_eq!(s.is_full(), false); 60 | assert_eq!(r.len(), 1); 61 | assert_eq!(r.is_empty(), false); 62 | assert_eq!(r.is_full(), false); 63 | 64 | s.send(()).unwrap(); 65 | 66 | assert_eq!(s.len(), 2); 67 | assert_eq!(s.is_empty(), false); 68 | assert_eq!(s.is_full(), true); 69 | assert_eq!(r.len(), 2); 70 | assert_eq!(r.is_empty(), false); 71 | assert_eq!(r.is_full(), true); 72 | 73 | r.recv().unwrap(); 74 | 75 | assert_eq!(s.len(), 1); 76 | assert_eq!(s.is_empty(), false); 77 | assert_eq!(s.is_full(), false); 78 | assert_eq!(r.len(), 1); 79 | assert_eq!(r.is_empty(), false); 80 | assert_eq!(r.is_full(), false); 81 | } 82 | 83 | #[test] 84 | fn try_recv() { 85 | let (s, r) = bounded(100); 86 | 87 | crossbeam::scope(|scope| { 88 | scope.spawn(move || { 89 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 90 | thread::sleep(ms(1500)); 91 | assert_eq!(r.try_recv(), Ok(7)); 92 | thread::sleep(ms(500)); 93 | assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)); 94 | }); 95 | scope.spawn(move || { 96 | thread::sleep(ms(1000)); 97 | s.send(7).unwrap(); 98 | }); 99 | }); 100 | } 101 | 102 | #[test] 103 | fn recv() { 104 | let (s, r) = bounded(100); 105 | 106 | crossbeam::scope(|scope| { 107 | scope.spawn(move || { 108 | assert_eq!(r.recv(), Ok(7)); 109 | thread::sleep(ms(1000)); 110 | assert_eq!(r.recv(), Ok(8)); 111 | thread::sleep(ms(1000)); 112 | assert_eq!(r.recv(), Ok(9)); 113 | assert_eq!(r.recv(), Err(RecvError)); 114 | }); 115 | scope.spawn(move || { 116 | thread::sleep(ms(1500)); 117 | s.send(7).unwrap(); 118 | s.send(8).unwrap(); 119 | s.send(9).unwrap(); 120 | }); 121 | }); 122 | } 123 | 124 | #[test] 125 | fn recv_timeout() { 126 | let (s, r) = bounded::(100); 127 | 128 | crossbeam::scope(|scope| { 129 | scope.spawn(move || { 130 | assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout)); 131 | assert_eq!(r.recv_timeout(ms(1000)), Ok(7)); 132 | assert_eq!( 133 | r.recv_timeout(ms(1000)), 134 | Err(RecvTimeoutError::Disconnected) 135 | ); 136 | }); 137 | scope.spawn(move || { 138 | thread::sleep(ms(1500)); 139 | s.send(7).unwrap(); 140 | }); 141 | }); 142 | } 143 | 144 | #[test] 145 | fn try_send() { 146 | let (s, r) = bounded(1); 147 | 148 | crossbeam::scope(|scope| { 149 | scope.spawn(move || { 150 | assert_eq!(s.try_send(1), Ok(())); 151 | assert_eq!(s.try_send(2), Err(TrySendError::Full(2))); 152 | thread::sleep(ms(1500)); 153 | assert_eq!(s.try_send(3), Ok(())); 154 | thread::sleep(ms(500)); 155 | assert_eq!(s.try_send(4), Err(TrySendError::Disconnected(4))); 156 | }); 157 | scope.spawn(move || { 158 | thread::sleep(ms(1000)); 159 | assert_eq!(r.try_recv(), Ok(1)); 160 | assert_eq!(r.try_recv(), Err(TryRecvError::Empty)); 161 | assert_eq!(r.recv(), Ok(3)); 162 | }); 163 | }); 164 | } 165 | 166 | #[test] 167 | fn send() { 168 | let (s, r) = bounded(1); 169 | 170 | crossbeam::scope(|scope| { 171 | scope.spawn(|| { 172 | s.send(7).unwrap(); 173 | thread::sleep(ms(1000)); 174 | s.send(8).unwrap(); 175 | thread::sleep(ms(1000)); 176 | s.send(9).unwrap(); 177 | thread::sleep(ms(1000)); 178 | s.send(10).unwrap(); 179 | }); 180 | scope.spawn(|| { 181 | thread::sleep(ms(1500)); 182 | assert_eq!(r.recv(), Ok(7)); 183 | assert_eq!(r.recv(), Ok(8)); 184 | assert_eq!(r.recv(), Ok(9)); 185 | }); 186 | }); 187 | } 188 | 189 | #[test] 190 | fn send_timeout() { 191 | let (s, r) = bounded(2); 192 | 193 | crossbeam::scope(|scope| { 194 | scope.spawn(move || { 195 | assert_eq!(s.send_timeout(1, ms(1000)), Ok(())); 196 | assert_eq!(s.send_timeout(2, ms(1000)), Ok(())); 197 | assert_eq!( 198 | s.send_timeout(3, ms(500)), 199 | Err(SendTimeoutError::Timeout(3)) 200 | ); 201 | thread::sleep(ms(1000)); 202 | assert_eq!(s.send_timeout(4, ms(1000)), Ok(())); 203 | thread::sleep(ms(1000)); 204 | assert_eq!(s.send(5), Err(SendError(5))); 205 | }); 206 | scope.spawn(move || { 207 | thread::sleep(ms(1000)); 208 | assert_eq!(r.recv(), Ok(1)); 209 | thread::sleep(ms(1000)); 210 | assert_eq!(r.recv(), Ok(2)); 211 | assert_eq!(r.recv(), Ok(4)); 212 | }); 213 | }); 214 | } 215 | 216 | #[test] 217 | fn send_after_disconnect() { 218 | let (s, r) = bounded(100); 219 | 220 | s.send(1).unwrap(); 221 | s.send(2).unwrap(); 222 | s.send(3).unwrap(); 223 | 224 | drop(r); 225 | 226 | assert_eq!(s.send(4), Err(SendError(4))); 227 | assert_eq!(s.try_send(5), Err(TrySendError::Disconnected(5))); 228 | assert_eq!(s.send_timeout(6, ms(500)), Err(SendTimeoutError::Disconnected(6))); 229 | } 230 | 231 | #[test] 232 | fn recv_after_disconnect() { 233 | let (s, r) = bounded(100); 234 | 235 | s.send(1).unwrap(); 236 | s.send(2).unwrap(); 237 | s.send(3).unwrap(); 238 | 239 | drop(s); 240 | 241 | assert_eq!(r.recv(), Ok(1)); 242 | assert_eq!(r.recv(), Ok(2)); 243 | assert_eq!(r.recv(), Ok(3)); 244 | assert_eq!(r.recv(), Err(RecvError)); 245 | } 246 | 247 | #[test] 248 | fn len() { 249 | const COUNT: usize = 25_000; 250 | const CAP: usize = 1000; 251 | 252 | let (s, r) = bounded(CAP); 253 | 254 | assert_eq!(s.len(), 0); 255 | assert_eq!(r.len(), 0); 256 | 257 | for _ in 0..CAP / 10 { 258 | for i in 0..50 { 259 | s.send(i).unwrap(); 260 | assert_eq!(s.len(), i + 1); 261 | } 262 | 263 | for i in 0..50 { 264 | r.recv().unwrap(); 265 | assert_eq!(r.len(), 50 - i - 1); 266 | } 267 | } 268 | 269 | assert_eq!(s.len(), 0); 270 | assert_eq!(r.len(), 0); 271 | 272 | for i in 0..CAP { 273 | s.send(i).unwrap(); 274 | assert_eq!(s.len(), i + 1); 275 | } 276 | 277 | for _ in 0..CAP { 278 | r.recv().unwrap(); 279 | } 280 | 281 | assert_eq!(s.len(), 0); 282 | assert_eq!(r.len(), 0); 283 | 284 | crossbeam::scope(|scope| { 285 | scope.spawn(|| { 286 | for i in 0..COUNT { 287 | assert_eq!(r.recv(), Ok(i)); 288 | let len = r.len(); 289 | assert!(len <= CAP); 290 | } 291 | }); 292 | 293 | scope.spawn(|| { 294 | for i in 0..COUNT { 295 | s.send(i).unwrap(); 296 | let len = s.len(); 297 | assert!(len <= CAP); 298 | } 299 | }); 300 | }); 301 | 302 | assert_eq!(s.len(), 0); 303 | assert_eq!(r.len(), 0); 304 | } 305 | 306 | #[test] 307 | fn disconnect_wakes_sender() { 308 | let (s, r) = bounded(1); 309 | 310 | crossbeam::scope(|scope| { 311 | scope.spawn(move || { 312 | assert_eq!(s.send(()), Ok(())); 313 | assert_eq!(s.send(()), Err(SendError(()))); 314 | }); 315 | scope.spawn(move || { 316 | thread::sleep(ms(1000)); 317 | drop(r); 318 | }); 319 | }); 320 | } 321 | 322 | #[test] 323 | fn disconnect_wakes_receiver() { 324 | let (s, r) = bounded::<()>(1); 325 | 326 | crossbeam::scope(|scope| { 327 | scope.spawn(move || { 328 | assert_eq!(r.recv(), Err(RecvError)); 329 | }); 330 | scope.spawn(move || { 331 | thread::sleep(ms(1000)); 332 | drop(s); 333 | }); 334 | }); 335 | } 336 | 337 | #[test] 338 | fn spsc() { 339 | const COUNT: usize = 100_000; 340 | 341 | let (s, r) = bounded(3); 342 | 343 | crossbeam::scope(|scope| { 344 | scope.spawn(move || { 345 | for i in 0..COUNT { 346 | assert_eq!(r.recv(), Ok(i)); 347 | } 348 | assert_eq!(r.recv(), Err(RecvError)); 349 | }); 350 | scope.spawn(move || { 351 | for i in 0..COUNT { 352 | s.send(i).unwrap(); 353 | } 354 | }); 355 | }); 356 | } 357 | 358 | #[test] 359 | fn mpmc() { 360 | const COUNT: usize = 25_000; 361 | const THREADS: usize = 4; 362 | 363 | let (s, r) = bounded::(3); 364 | let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); 365 | 366 | crossbeam::scope(|scope| { 367 | for _ in 0..THREADS { 368 | scope.spawn(|| { 369 | for _ in 0..COUNT { 370 | let n = r.recv().unwrap(); 371 | v[n].fetch_add(1, Ordering::SeqCst); 372 | } 373 | }); 374 | } 375 | for _ in 0..THREADS { 376 | scope.spawn(|| { 377 | for i in 0..COUNT { 378 | s.send(i).unwrap(); 379 | } 380 | }); 381 | } 382 | }); 383 | 384 | for c in v { 385 | assert_eq!(c.load(Ordering::SeqCst), THREADS); 386 | } 387 | } 388 | 389 | #[test] 390 | fn stress_timeout_two_threads() { 391 | const COUNT: usize = 100; 392 | 393 | let (s, r) = bounded(2); 394 | 395 | crossbeam::scope(|scope| { 396 | scope.spawn(|| { 397 | for i in 0..COUNT { 398 | if i % 2 == 0 { 399 | thread::sleep(ms(50)); 400 | } 401 | loop { 402 | if let Ok(()) = s.send_timeout(i, ms(10)) { 403 | break; 404 | } 405 | } 406 | } 407 | }); 408 | 409 | scope.spawn(|| { 410 | for i in 0..COUNT { 411 | if i % 2 == 0 { 412 | thread::sleep(ms(50)); 413 | } 414 | loop { 415 | if let Ok(x) = r.recv_timeout(ms(10)) { 416 | assert_eq!(x, i); 417 | break; 418 | } 419 | } 420 | } 421 | }); 422 | }); 423 | } 424 | 425 | #[test] 426 | fn drops() { 427 | const RUNS: usize = 100; 428 | 429 | static DROPS: AtomicUsize = AtomicUsize::new(0); 430 | 431 | #[derive(Debug, PartialEq)] 432 | struct DropCounter; 433 | 434 | impl Drop for DropCounter { 435 | fn drop(&mut self) { 436 | DROPS.fetch_add(1, Ordering::SeqCst); 437 | } 438 | } 439 | 440 | let mut rng = thread_rng(); 441 | 442 | for _ in 0..RUNS { 443 | let steps = rng.gen_range(0, 10_000); 444 | let additional = rng.gen_range(0, 50); 445 | 446 | DROPS.store(0, Ordering::SeqCst); 447 | let (s, r) = bounded::(50); 448 | 449 | crossbeam::scope(|scope| { 450 | scope.spawn(|| { 451 | for _ in 0..steps { 452 | r.recv().unwrap(); 453 | } 454 | }); 455 | 456 | scope.spawn(|| { 457 | for _ in 0..steps { 458 | s.send(DropCounter).unwrap(); 459 | } 460 | }); 461 | }); 462 | 463 | for _ in 0..additional { 464 | s.send(DropCounter).unwrap(); 465 | } 466 | 467 | assert_eq!(DROPS.load(Ordering::SeqCst), steps); 468 | drop(s); 469 | drop(r); 470 | assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional); 471 | } 472 | } 473 | 474 | #[test] 475 | fn linearizable() { 476 | const COUNT: usize = 25_000; 477 | const THREADS: usize = 4; 478 | 479 | let (s, r) = bounded(THREADS); 480 | 481 | crossbeam::scope(|scope| { 482 | for _ in 0..THREADS { 483 | scope.spawn(|| { 484 | for _ in 0..COUNT { 485 | s.send(0).unwrap(); 486 | r.try_recv().unwrap(); 487 | } 488 | }); 489 | } 490 | }); 491 | } 492 | 493 | #[test] 494 | fn fairness() { 495 | const COUNT: usize = 10_000; 496 | 497 | let (s1, r1) = bounded::<()>(COUNT); 498 | let (s2, r2) = bounded::<()>(COUNT); 499 | 500 | for _ in 0..COUNT { 501 | s1.send(()).unwrap(); 502 | s2.send(()).unwrap(); 503 | } 504 | 505 | let mut hits = [0usize; 2]; 506 | for _ in 0..COUNT { 507 | select! { 508 | recv(r1) -> _ => hits[0] += 1, 509 | recv(r2) -> _ => hits[1] += 1, 510 | } 511 | } 512 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 513 | } 514 | 515 | #[test] 516 | fn fairness_duplicates() { 517 | const COUNT: usize = 10_000; 518 | 519 | let (s, r) = bounded::<()>(COUNT); 520 | 521 | for _ in 0..COUNT { 522 | s.send(()).unwrap(); 523 | } 524 | 525 | let mut hits = [0usize; 5]; 526 | for _ in 0..COUNT { 527 | select! { 528 | recv(r) -> _ => hits[0] += 1, 529 | recv(r) -> _ => hits[1] += 1, 530 | recv(r) -> _ => hits[2] += 1, 531 | recv(r) -> _ => hits[3] += 1, 532 | recv(r) -> _ => hits[4] += 1, 533 | } 534 | } 535 | assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2)); 536 | } 537 | 538 | #[test] 539 | fn recv_in_send() { 540 | let (s, _r) = bounded(1); 541 | s.send(()).unwrap(); 542 | 543 | #[allow(unreachable_code)] 544 | { 545 | select! { 546 | send(s, panic!()) -> _ => panic!(), 547 | default => {} 548 | } 549 | } 550 | 551 | let (s, r) = bounded(2); 552 | s.send(()).unwrap(); 553 | 554 | select! { 555 | send(s, assert_eq!(r.recv(), Ok(()))) -> _ => {} 556 | } 557 | } 558 | -------------------------------------------------------------------------------- /src/flavors/zero.rs: -------------------------------------------------------------------------------- 1 | //! Zero-capacity channel. 2 | //! 3 | //! This kind of channel is also known as *rendezvous* channel. 4 | 5 | use std::cell::UnsafeCell; 6 | use std::marker::PhantomData; 7 | use std::sync::atomic::{AtomicBool, Ordering}; 8 | use std::thread; 9 | use std::time::Instant; 10 | 11 | use parking_lot::Mutex; 12 | 13 | use context::Context; 14 | use err::{RecvTimeoutError, SendTimeoutError, TryRecvError, TrySendError}; 15 | use select::{Operation, SelectHandle, Selected, Token}; 16 | use utils::Backoff; 17 | use waker::Waker; 18 | 19 | /// A pointer to a packet. 20 | pub type ZeroToken = usize; 21 | 22 | /// A slot for passing one message from a sender to a receiver. 23 | struct Packet { 24 | /// Equals `true` if the packet is allocated on the stack. 25 | on_stack: bool, 26 | 27 | /// Equals `true` once the packet is ready for reading or writing. 28 | ready: AtomicBool, 29 | 30 | /// The message. 31 | msg: UnsafeCell>, 32 | } 33 | 34 | impl Packet { 35 | /// Creates an empty packet on the stack. 36 | fn empty_on_stack() -> Packet { 37 | Packet { 38 | on_stack: true, 39 | ready: AtomicBool::new(false), 40 | msg: UnsafeCell::new(None), 41 | } 42 | } 43 | 44 | /// Creates an empty packet on the heap. 45 | fn empty_on_heap() -> Box> { 46 | Box::new(Packet { 47 | on_stack: false, 48 | ready: AtomicBool::new(false), 49 | msg: UnsafeCell::new(None), 50 | }) 51 | } 52 | 53 | /// Creates a packet on the stack, containing a message. 54 | fn message_on_stack(msg: T) -> Packet { 55 | Packet { 56 | on_stack: true, 57 | ready: AtomicBool::new(false), 58 | msg: UnsafeCell::new(Some(msg)), 59 | } 60 | } 61 | 62 | /// Waits until the packet becomes ready for reading or writing. 63 | fn wait_ready(&self) { 64 | let mut backoff = Backoff::new(); 65 | while !self.ready.load(Ordering::Acquire) { 66 | backoff.snooze(); 67 | } 68 | } 69 | } 70 | 71 | /// Inner representation of a zero-capacity channel. 72 | struct Inner { 73 | /// Senders waiting to pair up with a receive operation. 74 | senders: Waker, 75 | 76 | /// Receivers waiting to pair up with a send operation. 77 | receivers: Waker, 78 | 79 | /// Equals `true` when the channel is disconnected. 80 | is_disconnected: bool, 81 | } 82 | 83 | /// Zero-capacity channel. 84 | pub struct Channel { 85 | /// Inner representation of the channel. 86 | inner: Mutex, 87 | 88 | /// Indicates that dropping a `Channel` may drop values of type `T`. 89 | _marker: PhantomData, 90 | } 91 | 92 | impl Channel { 93 | /// Constructs a new zero-capacity channel. 94 | pub fn new() -> Self { 95 | Channel { 96 | inner: Mutex::new(Inner { 97 | senders: Waker::new(), 98 | receivers: Waker::new(), 99 | is_disconnected: false, 100 | }), 101 | _marker: PhantomData, 102 | } 103 | } 104 | 105 | /// Returns a receiver handle to the channel. 106 | pub fn receiver(&self) -> Receiver { 107 | Receiver(self) 108 | } 109 | 110 | /// Returns a sender handle to the channel. 111 | pub fn sender(&self) -> Sender { 112 | Sender(self) 113 | } 114 | 115 | /// Attempts to reserve a slot for sending a message. 116 | fn start_send(&self, token: &mut Token, short_pause: bool) -> bool { 117 | let mut inner = self.inner.lock(); 118 | 119 | // If there's a waiting receiver, pair up with it. 120 | if let Some(operation) = inner.receivers.wake_one() { 121 | token.zero = operation.packet; 122 | return true; 123 | } else if inner.is_disconnected { 124 | token.zero = 0; 125 | return true; 126 | } 127 | 128 | if !short_pause { 129 | return false; 130 | } 131 | 132 | Context::with(|cx| { 133 | // Register this send operation so that another receiver can pair up with it. 134 | let oper = Operation::hook(token); 135 | let packet = Box::into_raw(Packet::::empty_on_heap()); 136 | inner 137 | .senders 138 | .register_with_packet(oper, packet as usize, cx); 139 | drop(inner); 140 | 141 | // Yield to give receivers a chance to pair up with this operation. 142 | thread::yield_now(); 143 | 144 | let sel = match cx.try_select(Selected::Aborted) { 145 | Ok(()) => Selected::Aborted, 146 | Err(s) => s, 147 | }; 148 | 149 | match sel { 150 | Selected::Waiting => unreachable!(), 151 | Selected::Disconnected => { 152 | // Unregister and destroy the packet. 153 | let operation = self.inner.lock().senders.unregister(oper).unwrap(); 154 | unsafe { 155 | drop(Box::from_raw(operation.packet as *mut Packet)); 156 | } 157 | 158 | // All receivers have just been dropped. 159 | token.zero = 0; 160 | true 161 | } 162 | Selected::Aborted => { 163 | // Unregister and destroy the packet. 164 | let operation = self.inner.lock().senders.unregister(oper).unwrap(); 165 | unsafe { 166 | drop(Box::from_raw(operation.packet as *mut Packet)); 167 | } 168 | false 169 | } 170 | Selected::Operation(_) => { 171 | // Success! A receiver has paired up with this operation. 172 | token.zero = cx.wait_packet(); 173 | true 174 | } 175 | } 176 | }) 177 | } 178 | 179 | /// Writes a message into the packet. 180 | pub unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> { 181 | // If there is no packet, the channel is disconnected. 182 | if token.zero == 0 { 183 | return Err(msg); 184 | } 185 | 186 | let packet = &*(token.zero as *const Packet); 187 | packet.msg.get().write(Some(msg)); 188 | packet.ready.store(true, Ordering::Release); 189 | Ok(()) 190 | } 191 | 192 | /// Attempts to pair up with a sender. 193 | fn start_recv(&self, token: &mut Token, short_pause: bool) -> bool { 194 | let mut inner = self.inner.lock(); 195 | 196 | // If there's a waiting sender, pair up with it. 197 | if let Some(operation) = inner.senders.wake_one() { 198 | token.zero = operation.packet; 199 | return true; 200 | } else if inner.is_disconnected { 201 | token.zero = 0; 202 | return true; 203 | } 204 | 205 | if !short_pause { 206 | return false; 207 | } 208 | 209 | Context::with(|cx| { 210 | // Register this receive operation so that another sender can pair up with it. 211 | let oper = Operation::hook(token); 212 | let packet = Box::into_raw(Packet::::empty_on_heap()); 213 | inner 214 | .receivers 215 | .register_with_packet(oper, packet as usize, cx); 216 | drop(inner); 217 | 218 | // Yield to give senders a chance to pair up with this operation. 219 | thread::yield_now(); 220 | 221 | let sel = match cx.try_select(Selected::Aborted) { 222 | Ok(()) => Selected::Aborted, 223 | Err(s) => s, 224 | }; 225 | 226 | match sel { 227 | Selected::Waiting => unreachable!(), 228 | Selected::Aborted => { 229 | // Unregister and destroy the packet. 230 | let operation = self.inner.lock().receivers.unregister(oper).unwrap(); 231 | unsafe { 232 | drop(Box::from_raw(operation.packet as *mut Packet)); 233 | } 234 | false 235 | } 236 | Selected::Disconnected => { 237 | // Unregister and destroy the packet. 238 | let operation = self.inner.lock().receivers.unregister(oper).unwrap(); 239 | unsafe { 240 | drop(Box::from_raw(operation.packet as *mut Packet)); 241 | } 242 | 243 | // All senders have just been dropped. 244 | token.zero = 0; 245 | true 246 | } 247 | Selected::Operation(_) => { 248 | // Success! A sender has paired up with this operation. 249 | token.zero = cx.wait_packet(); 250 | true 251 | } 252 | } 253 | }) 254 | } 255 | 256 | /// Reads a message from the packet. 257 | pub unsafe fn read(&self, token: &mut Token) -> Result { 258 | // If there is no packet, the channel is disconnected. 259 | if token.zero == 0 { 260 | return Err(()); 261 | } 262 | 263 | let packet = &*(token.zero as *const Packet); 264 | 265 | if packet.on_stack { 266 | // The message has been in the packet from the beginning, so there is no need to wait 267 | // for it. However, after reading the message, we need to set `ready` to `true` in 268 | // order to signal that the packet can be destroyed. 269 | let msg = packet.msg.get().replace(None).unwrap(); 270 | packet.ready.store(true, Ordering::Release); 271 | Ok(msg) 272 | } else { 273 | // Wait until the message becomes available, then read it and destroy the 274 | // heap-allocated packet. 275 | packet.wait_ready(); 276 | let msg = packet.msg.get().replace(None).unwrap(); 277 | drop(Box::from_raw(packet as *const Packet as *mut Packet)); 278 | Ok(msg) 279 | } 280 | } 281 | 282 | /// Attempts to send a message into the channel. 283 | pub fn try_send(&self, msg: T) -> Result<(), TrySendError> { 284 | let token = &mut Token::default(); 285 | let mut inner = self.inner.lock(); 286 | 287 | // If there's a waiting receiver, pair up with it. 288 | if let Some(operation) = inner.receivers.wake_one() { 289 | token.zero = operation.packet; 290 | drop(inner); 291 | unsafe { 292 | self.write(token, msg).ok().unwrap(); 293 | } 294 | Ok(()) 295 | } else if inner.is_disconnected { 296 | Err(TrySendError::Disconnected(msg)) 297 | } else { 298 | Err(TrySendError::Full(msg)) 299 | } 300 | } 301 | 302 | /// Sends a message into the channel. 303 | pub fn send(&self, msg: T, deadline: Option) -> Result<(), SendTimeoutError> { 304 | let token = &mut Token::default(); 305 | let mut inner = self.inner.lock(); 306 | 307 | // If there's a waiting receiver, pair up with it. 308 | if let Some(operation) = inner.receivers.wake_one() { 309 | token.zero = operation.packet; 310 | drop(inner); 311 | unsafe { 312 | self.write(token, msg).ok().unwrap(); 313 | } 314 | return Ok(()); 315 | } 316 | 317 | if inner.is_disconnected { 318 | return Err(SendTimeoutError::Disconnected(msg)); 319 | } 320 | 321 | Context::with(|cx| { 322 | // Prepare for blocking until a receiver wakes us up. 323 | let oper = Operation::hook(token); 324 | let packet = Packet::::message_on_stack(msg); 325 | inner 326 | .senders 327 | .register_with_packet(oper, &packet as *const Packet as usize, cx); 328 | drop(inner); 329 | 330 | // Block the current thread. 331 | let sel = cx.wait_until(deadline); 332 | 333 | match sel { 334 | Selected::Waiting => unreachable!(), 335 | Selected::Aborted => { 336 | self.inner.lock().senders.unregister(oper).unwrap(); 337 | let msg = unsafe { packet.msg.get().replace(None).unwrap() }; 338 | Err(SendTimeoutError::Timeout(msg)) 339 | } 340 | Selected::Disconnected => { 341 | self.inner.lock().senders.unregister(oper).unwrap(); 342 | let msg = unsafe { packet.msg.get().replace(None).unwrap() }; 343 | Err(SendTimeoutError::Disconnected(msg)) 344 | } 345 | Selected::Operation(_) => { 346 | // Wait until the message is read, then drop the packet. 347 | packet.wait_ready(); 348 | Ok(()) 349 | } 350 | } 351 | }) 352 | } 353 | 354 | /// Attempts to receive a message without blocking. 355 | pub fn try_recv(&self) -> Result { 356 | let token = &mut Token::default(); 357 | let mut inner = self.inner.lock(); 358 | 359 | // If there's a waiting sender, pair up with it. 360 | if let Some(operation) = inner.senders.wake_one() { 361 | token.zero = operation.packet; 362 | drop(inner); 363 | unsafe { 364 | self.read(token).map_err(|_| TryRecvError::Disconnected) 365 | } 366 | } else if inner.is_disconnected { 367 | Err(TryRecvError::Disconnected) 368 | } else { 369 | Err(TryRecvError::Empty) 370 | } 371 | } 372 | 373 | /// Receives a message from the channel. 374 | pub fn recv(&self, deadline: Option) -> Result { 375 | let token = &mut Token::default(); 376 | let mut inner = self.inner.lock(); 377 | 378 | // If there's a waiting sender, pair up with it. 379 | if let Some(operation) = inner.senders.wake_one() { 380 | token.zero = operation.packet; 381 | drop(inner); 382 | unsafe { 383 | return self.read(token).map_err(|_| RecvTimeoutError::Disconnected); 384 | } 385 | } 386 | 387 | if inner.is_disconnected { 388 | return Err(RecvTimeoutError::Disconnected); 389 | } 390 | 391 | Context::with(|cx| { 392 | // Prepare for blocking until a sender wakes us up. 393 | let oper = Operation::hook(token); 394 | let packet = Packet::::empty_on_stack(); 395 | inner 396 | .receivers 397 | .register_with_packet(oper, &packet as *const Packet as usize, cx); 398 | drop(inner); 399 | 400 | // Block the current thread. 401 | let sel = cx.wait_until(deadline); 402 | 403 | match sel { 404 | Selected::Waiting => unreachable!(), 405 | Selected::Aborted => { 406 | self.inner.lock().receivers.unregister(oper).unwrap(); 407 | Err(RecvTimeoutError::Timeout) 408 | } 409 | Selected::Disconnected => { 410 | self.inner.lock().receivers.unregister(oper).unwrap(); 411 | Err(RecvTimeoutError::Disconnected) 412 | } 413 | Selected::Operation(_) => { 414 | // Wait until the message is provided, then read it. 415 | packet.wait_ready(); 416 | unsafe { Ok(packet.msg.get().replace(None).unwrap()) } 417 | } 418 | } 419 | }) 420 | } 421 | 422 | /// Disconnects the channel and wakes up all blocked receivers. 423 | pub fn disconnect(&self) { 424 | let mut inner = self.inner.lock(); 425 | 426 | if !inner.is_disconnected { 427 | inner.is_disconnected = true; 428 | inner.senders.disconnect(); 429 | inner.receivers.disconnect(); 430 | } 431 | } 432 | 433 | /// Returns the current number of messages inside the channel. 434 | pub fn len(&self) -> usize { 435 | 0 436 | } 437 | 438 | /// Returns the capacity of the channel. 439 | pub fn capacity(&self) -> Option { 440 | Some(0) 441 | } 442 | 443 | /// Returns `true` if the channel is empty. 444 | pub fn is_empty(&self) -> bool { 445 | true 446 | } 447 | 448 | /// Returns `true` if the channel is full. 449 | pub fn is_full(&self) -> bool { 450 | true 451 | } 452 | } 453 | 454 | /// Receiver handle to a channel. 455 | pub struct Receiver<'a, T: 'a>(&'a Channel); 456 | 457 | /// Sender handle to a channel. 458 | pub struct Sender<'a, T: 'a>(&'a Channel); 459 | 460 | impl<'a, T> SelectHandle for Receiver<'a, T> { 461 | fn try(&self, token: &mut Token) -> bool { 462 | self.0.start_recv(token, false) 463 | } 464 | 465 | fn retry(&self, token: &mut Token) -> bool { 466 | self.0.start_recv(token, true) 467 | } 468 | 469 | fn deadline(&self) -> Option { 470 | None 471 | } 472 | 473 | fn register(&self, _token: &mut Token, oper: Operation, cx: &Context) -> bool { 474 | let packet = Box::into_raw(Packet::::empty_on_heap()); 475 | 476 | let mut inner = self.0.inner.lock(); 477 | inner 478 | .receivers 479 | .register_with_packet(oper, packet as usize, cx); 480 | !inner.senders.can_wake_one() && !inner.is_disconnected 481 | } 482 | 483 | fn unregister(&self, oper: Operation) { 484 | if let Some(operation) = self.0.inner.lock().receivers.unregister(oper) { 485 | unsafe { 486 | drop(Box::from_raw(operation.packet as *mut Packet)); 487 | } 488 | } 489 | } 490 | 491 | fn accept(&self, token: &mut Token, cx: &Context) -> bool { 492 | token.zero = cx.wait_packet(); 493 | true 494 | } 495 | 496 | fn state(&self) -> usize { 497 | self.0.inner.lock().senders.register_count() 498 | } 499 | } 500 | 501 | impl<'a, T> SelectHandle for Sender<'a, T> { 502 | fn try(&self, token: &mut Token) -> bool { 503 | self.0.start_send(token, false) 504 | } 505 | 506 | fn retry(&self, token: &mut Token) -> bool { 507 | self.0.start_send(token, true) 508 | } 509 | 510 | fn deadline(&self) -> Option { 511 | None 512 | } 513 | 514 | fn register(&self, _token: &mut Token, oper: Operation, cx: &Context) -> bool { 515 | let packet = Box::into_raw(Packet::::empty_on_heap()); 516 | 517 | let mut inner = self.0.inner.lock(); 518 | inner 519 | .senders 520 | .register_with_packet(oper, packet as usize, cx); 521 | !inner.receivers.can_wake_one() && !inner.is_disconnected 522 | } 523 | 524 | fn unregister(&self, oper: Operation) { 525 | if let Some(operation) = self.0.inner.lock().senders.unregister(oper) { 526 | unsafe { 527 | drop(Box::from_raw(operation.packet as *mut Packet)); 528 | } 529 | } 530 | } 531 | 532 | fn accept(&self, token: &mut Token, cx: &Context) -> bool { 533 | token.zero = cx.wait_packet(); 534 | true 535 | } 536 | 537 | fn state(&self) -> usize { 538 | self.0.inner.lock().receivers.register_count() 539 | } 540 | } 541 | --------------------------------------------------------------------------------