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}