read_names/
read_names.rs

1// This example demonstrates how to do something almost useful with OS signals.
2// This program asks the user to inputs names. It then outputs these names
3// once the user inputs EOF. The catch is that it will *also* output these
4// names if the user sends the process SIGINT or SIGTERM.
5//
6// This is hard or impossible to do with regular asynchronous signal handlers.
7// But with a channel based API, it's easy to integrate with the rest of
8// your control flow.
9
10#![allow(deprecated)] // for connect=>join in 1.3
11
12#[macro_use] extern crate chan;
13extern crate chan_signal;
14
15use std::error::Error;
16use std::io::{self, BufRead, Write};
17use std::process;
18use std::thread;
19
20use chan_signal::{Signal, notify};
21
22fn main() {
23    // It is imperative that we start listening for signals as soon as
24    // possible. In particular, if `notify` is called after another thread
25    // has spawned, then signal masking won't be applied to it and signal
26    // handling won't work.
27    //
28    // See "Signal mask and pending signals" section of signal(7).
29    let signal = notify(&[Signal::INT, Signal::TERM]);
30    match run(signal) {
31        Ok(mut names) => {
32            names.sort();
33            println!("You entered {} names: {}",
34                     names.len(), names.connect(", "));
35        }
36        Err(err) => {
37            writeln!(&mut io::stderr(), "{}", err).unwrap();
38            process::exit(1);
39        }
40    }
41}
42
43type Result<T> = ::std::result::Result<T, Box<Error+Send+Sync>>;
44
45fn run(signal: chan::Receiver<Signal>) -> Result<Vec<String>> {
46    let lines = read_stdin_lines();
47    let mut names = vec![];
48    println!("Please enter some names, each on a new line:");
49    loop {
50        chan_select! {
51            lines.recv() -> line => {
52                match line {
53                    // If the channel closed (i.e., reads EOF), then quit
54                    // the loop and print what we've got.
55                    //
56                    // The rightward drift is painful...
57                    None => break,
58                    Some(line) => names.push(try!(line).trim().to_owned()),
59                }
60            },
61            // If we get SIGINT or SIGTERM, just stop the loop and print
62            // what we've got so far.
63            signal.recv() => break,
64        }
65    }
66    Ok(names)
67}
68
69// Spawns a new thread to read lines and sends the result on the returned
70// channel.
71fn read_stdin_lines() -> chan::Receiver<io::Result<String>> {
72    let (s, r) = chan::sync(0);
73    let stdin = io::stdin();
74    thread::spawn(move || {
75        let stdin = stdin.lock();
76        for line in stdin.lines() {
77            s.send(line);
78        }
79    });
80    r
81}