chan

Multi-producer, multi-consumer concurrent channel for Rust.

394
14
394
Rust
public

This crate has reached its end-of-life and is now deprecated.

The intended successor of this crate is the
crossbeam-channel
crate. Its API is strikingly similar, but comes with a much better select!
macro, better performance, a better test suite and an all-around better
implementation.

If you were previously using this crate because of chan-signal, then it is
simple to reproduce a similar API with crossbeam-channel and the
signal-hook
crate. For example, here’s chan-signal’s notify function:

use crossbeam_channel::Receiver;

fn notify(signals: &[c_int]) -> Result<Receiver<c_int>> {
    let (s, r) = crossbeam_channel::bounded(100);
    let signals = signal_hook::iterator::Signals::new(signals)?;
    thread::spawn(move || {
        for signal in signals.forever() {
            s.send(signal);
        }
    });
    Ok(r)
}

This crate may continue to receives bug fixes, but should otherwise be
considered dead.

chan

This crate provides experimental support for multi-producer/multi-consumer
channels. This includes rendezvous, synchronous and asynchronous channels.

Channels are never complete without a way to synchronize on multiple channels
at the same time. Therefore, this crate provides a chan_select! macro that
can select on any combination of channel send or receive operations.

Build status

Dual-licensed under MIT or the UNLICENSE.

Documentation

Please help me improve the documentation!

http://burntsushi.net/rustdoc/chan/

Example: selecting on multiple channels

#[macro_use]
extern crate chan;

use std::thread;

fn main() {
    let tick = chan::tick_ms(100);
    let boom = chan::after_ms(500);
    loop {
        chan_select! {
            default => { println!("   ."); thread::sleep_ms(50); },
            tick.recv() => println!("tick."),
            boom.recv() => { println!("BOOM!"); return; },
        }
    }
}

Example: wait for OS signals

This requires the chan-signal crate on crates.io.

#[macro_use]
extern crate chan;
extern crate chan_signal;

use chan_signal::Signal;
use std::thread;

fn main() {
    // Signal gets a value when the OS sent a INT or TERM signal.
    let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]);
    // When our work is complete, send a sentinel value on `sdone`.
    let (sdone, rdone) = chan::sync(0);
    // Run work.
    thread::spawn(move || run(sdone));

    // Wait for a signal or for work to be done.
    chan_select! {
        signal.recv() -> signal => {
            println!("received signal: {:?}", signal)
        },
        rdone.recv() => {
            println!("Program completed normally.");
        }
    }
}

fn run(_sdone: chan::Sender<()>) {
    println!("Running work for 5 seconds.");
    println!("Can you send a signal quickly enough?");
    // Do some work.
    thread::sleep_ms(5000);

    // _sdone gets dropped which closes the channel and causes `rdone`
    // to unblock.
}

Differences with std::sync::mpsc

Rust’s standard library has a multi-producer/single-consumer channel. Here are
the differences (which I believe amount to a massive increase in ergonomics):

  • chan channels are multi-producer/multi-consumer, so you can clone senders
    and receivers with reckless abandon.
  • chan uses no unsafe code.
  • chan’s asynchronous channels have lower throughput than std::sync::mpsc.
    (Run the benchmarks with cargo bench.)
  • chan’s synchronous channels have comparable throughput with
    std::sync::mpsc.
  • chan has a select macro that works on stable and can synchronize across
    channel send or receive operations. It also supports a “default” case
    that is executed when all channel operations are blocked. (This effectively
    replaces uses of try_send/try_recv in std::sync::mpsc. Indeed, chan
    omits these methods entirely.)
  • chan’s send operations will deadlock if all receivers have been dropped.
    This is a deliberate choice inspired by Go. I’m open to changing it.
  • chan’s channels implement Sync. When we get a working scoped API, you
    won’t need to clone channels to pass them into other threads (when you can
    prove that the channel outlives the thread of course).

Motivation

The purpose of this crate is to provide a safe concurrent abstraction for
communicating sequential processes. Performance takes a second seat to
semantics and ergonomics. If you’re looking for high performance
synchronization, chan is probably not the right fit.

Moreover, chan synchronizes across operating system threads, so in order to
spin up multiple concurrent routines, you need to launch a thread for each one.
This may be more cost than you’re willing to pay.

Bottom line: chan doesn’t have any place in a high performance web server,
but it might have a place in structuring coarse grained concurrency in
applications.

Also…

chan-signal is a Rust library
for using channels in chan to receive OS signals. (Unix only at the moment.)

cmail is a program to periodically
email the output of long running commands. I ported this from an
earlier version that I wrote in Go.

v0.3.3[beta]