#channel #broadcast #lock-free

bin+lib bus

A lock-free, bounded, single-producer, multi-consumer, broadcast channel

17 stable releases

✓ Uses Rust 2018 edition

2.2.1 Mar 8, 2019
2.0.1 Dec 4, 2018
2.0.0 May 6, 2018
1.4.1 Jan 10, 2018
0.2.1 Jun 3, 2016

#35 in Concurrency

Download history 168/week @ 2018-11-23 176/week @ 2018-11-30 153/week @ 2018-12-07 346/week @ 2018-12-14 97/week @ 2018-12-21 96/week @ 2018-12-28 83/week @ 2019-01-04 130/week @ 2019-01-11 134/week @ 2019-01-18 140/week @ 2019-01-25 145/week @ 2019-02-01 125/week @ 2019-02-08 100/week @ 2019-02-15 168/week @ 2019-02-22 297/week @ 2019-03-01

675 downloads per month
Used in 9 crates

MIT license

458 lines


Crates.io Documentation Build Status Codecov

Bus provides a lock-free, bounded, single-producer, multi-consumer, broadcast channel.

It uses a circular buffer and atomic instructions to implement a lock-free single-producer, multi-consumer channel. The interface is similar to that of the std::sync::mpsc channels, except that multiple consumers (readers of the channel) can be produced, whereas only a single sender can exist. Furthermore, in contrast to most multi-consumer FIFO queues, bus is broadcast; every send goes to every consumer.

I haven't seen this particular implementation in literature (some extra bookkeeping is necessary to allow multiple consumers), but a lot of related reading can be found in Ross Bencina's blog post "Some notes on lock-free and wait-free algorithms".

Bus achieves broadcast by cloning the element in question, which is why T must implement Clone. However, Bus is clever about only cloning when necessary. Specifically, the last consumer to see a given value will move it instead of cloning, which means no cloning is happening for the single-consumer case. For cases where cloning is expensive, Arc should be used instead.

In a single-producer, single-consumer setup, Bus is ~7x faster then std::sync::mpsc::sync_channel on my machine. It's ~2x slower than crossbeam-channel. YMMV. You can check your performance on Nightly using

$ cargo bench --features bench

To see multi-consumer results, run the benchmark utility instead (should work on stable too)

$ cargo build --bin bench --release
$ target/release/bench


Single-send, multi-consumer example

use bus::Bus;
let mut bus = Bus::new(10);
let mut rx1 = bus.add_rx();
let mut rx2 = bus.add_rx();

assert_eq!(rx1.recv(), Ok("Hello"));
assert_eq!(rx2.recv(), Ok("Hello"));

Multi-send, multi-consumer example

use bus::Bus;
use std::thread;

let mut bus = Bus::new(10);
let mut rx1 = bus.add_rx();
let mut rx2 = bus.add_rx();

// start a thread that sends 1..100
let j = thread::spawn(move || {
    for i in 1..100 {

// every value should be received by both receivers
for i in 1..100 {
    // rx1
    assert_eq!(rx1.recv(), Ok(i));
    // and rx2
    assert_eq!(rx2.recv(), Ok(i));


Many-to-many channel using a dispatcher

use bus::Bus;

use std::thread;
use std::sync::mpsc;

// set up fan-in
let (tx1, mix_rx) = mpsc::sync_channel(100);
let tx2 = tx1.clone();
// set up fan-out
let mut mix_tx = Bus::new(100);
let mut rx1 = mix_tx.add_rx();
let mut rx2 = mix_tx.add_rx();
// start dispatcher
thread::spawn(move || {
    for m in mix_rx.iter() {

// sends on tx1 are received ...

// ... by both receiver rx1 ...
assert_eq!(rx1.recv(), Ok("Hello"));
// ... and receiver rx2
assert_eq!(rx2.recv(), Ok("Hello"));

// same with sends on tx2
assert_eq!(rx1.recv(), Ok("world"));
assert_eq!(rx2.recv(), Ok("world"));


~22K SLoC