├── .gitignore ├── Cargo.toml ├── README.md ├── examples ├── cat.rs ├── line-by-line.rs └── loop.rs ├── examples2 ├── linebyline_asyncfn1.rs ├── linebyline_asyncfn3.rs ├── loop_asyncfn1.rs └── loop_asyncfn3.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tokio-stdin-stdout" 3 | version = "0.1.5" 4 | license = "MIT/Apache-2.0" 5 | authors = ["Vitaly _Vi Shukela "] 6 | repository = "https://github.com/vi/tokio-stdin-stdout" 7 | documentation = "https://docs.rs/tokio-stdin-stdout" 8 | categories = ["asynchronous"] 9 | description = """ 10 | Thread- and future::sync::mpsc-based AsyncRead/AsyncWrite stdin/stdout with little buffering 11 | """ 12 | 13 | [dependencies] 14 | futures = "0.1" 15 | tokio-io = "0.1" 16 | 17 | [dev-dependencies] 18 | tokio-core = "0.1" 19 | tokio-codec = "0.1.0" 20 | tokio = "0.1.7" 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tokio-stdin-stdout 2 | AsyncRead/AsyncWrite stdin/stdout for Tokio 3 | 4 | [Documentation](https://docs.rs/tokio-stdin-stdout) - more description there 5 | 6 | # Example 7 | 8 | ```rust 9 | let mut core = tokio_core::reactor::Core::new()?; 10 | 11 | let stdin = tokio_stdin_stdout::stdin(0); 12 | let stdout = tokio_stdin_stdout::stdout(0); 13 | 14 | core.run(tokio_io::io::copy(stdin, stdout))?; 15 | 16 | ``` 17 | 18 | ## Additional examples 19 | 20 | 1. [`loop.rs`](examples/loop.rs) - Write hello ten times 21 | 2. [`line-by-line.rs`](examples/line-by-line.rs) - Convert all input text to ASCII upper case, line by line. This example also demonstrates usage of [tokio-codec](https://docs.rs/tokio-codec) and various modes of starting Tokio programs (multithreaded, singlethreaded). 22 | 23 | 24 | ## async fn demo 25 | 26 | Not much related to tokio-stdin-stdout, but there are some `async fn` examples runnable by [`cargo script`](https://crates.io/crates/cargo-script). 27 | 28 | They require nightly Rust. 29 | 30 | * [loop example as async fn v1](examples2/loop_asyncfn1.rs) - The same as loop.rs above, but may be more readable.due to async fn backed by futures_await crate. 31 | * [line-by-line example as async fn v1](examples2/linebyline_asyncfn1.rs) - The same as line-by-line.rs, but more prodecural-looking. 32 | * [loop example as async fn v3](examples2/loop_asyncfn3.rs) - Another try with loop.rs, but this time using [new async engine](https://github.com/rust-lang/rfcs/blob/master/text/2394-async_await.md) built-in in Rust itself. As it intrefaces early alpha code, it may stop working after a while. 33 | 34 | # Alternatives 35 | 36 | 1. [tokio-stdin](https://crates.io/crates/tokio-stdin) no AsyncRead, only stdin, byte by byte 37 | 2. [tokio-file-unix](https://crates.io/crates/tokio-file-unix) - better, but only Unix 38 | -------------------------------------------------------------------------------- /examples/cat.rs: -------------------------------------------------------------------------------- 1 | extern crate tokio_core; 2 | extern crate tokio_io; 3 | 4 | extern crate tokio_stdin_stdout; 5 | 6 | use std::io::Result; 7 | 8 | fn run() -> Result<()> { 9 | let mut core = tokio_core::reactor::Core::new()?; 10 | //let handle = core.handle(); 11 | 12 | let stdin = tokio_stdin_stdout::stdin(0); 13 | let stdout = tokio_stdin_stdout::stdout(0); 14 | 15 | core.run(tokio_io::io::copy(stdin, stdout))?; 16 | Ok(()) 17 | } 18 | 19 | fn main() { 20 | if let Err(e) = run() { 21 | eprintln!("Something failed: {}", e); 22 | ::std::process::exit(1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/line-by-line.rs: -------------------------------------------------------------------------------- 1 | extern crate tokio_stdin_stdout; 2 | extern crate tokio; 3 | extern crate tokio_io; 4 | extern crate tokio_codec; 5 | 6 | use tokio::prelude::future::ok; 7 | use tokio::prelude::{Future, Stream}; 8 | use tokio_codec::{FramedRead, FramedWrite, LinesCodec}; 9 | 10 | fn async_op(input: String) -> Box + Send> { 11 | Box::new(ok(input.to_ascii_uppercase())) 12 | } 13 | 14 | fn main() { 15 | let stdin = tokio_stdin_stdout::stdin(0); 16 | let stdout = tokio_stdin_stdout::stdout(0); // .make_sendable(); 17 | 18 | let framed_stdin = FramedRead::new(stdin, LinesCodec::new()); 19 | let framed_stdout = FramedWrite::new(stdout, LinesCodec::new()); 20 | 21 | let future = framed_stdin 22 | .and_then(move |line| { 23 | // `and_then` above is not a Future's "and_then", it Stream's "and_then". 24 | async_op(line) 25 | .map_err(|err|panic!("Error: {:?}", err)) 26 | }) 27 | .forward(framed_stdout) 28 | .map(|(_framed_stdin,_framed_stdout)|{ 29 | // _framed_stdin is exhaused now 30 | // _framed_stdout is closed by `Stream::forward` above and is also unusable 31 | 32 | // You may try with `Sink::send_all` approach if you need other behaviour, 33 | // but remember to `AsyncWrite::shutdown` the `stdout` to ensure in-flight 34 | // data is actually delivered to stdout before existing. 35 | 36 | // this `map` is needed to bring the final type to (), 37 | // as typically required for executing a future. 38 | }) 39 | .map_err(|err| { 40 | panic!("Error: {:?}", err); 41 | }); 42 | 43 | // Here is a demonstration of various ways of running a Tokio program. 44 | 45 | // 1. Normal, fully multithreaded mode. Requires `make_sendable` above. 46 | //tokio::run(future); 47 | 48 | // 2. Bi-threaded mode: executor thread + reactor thread. 49 | // In this paricular example there are also stdin and stdout threads. 50 | //tokio::executor::current_thread::block_on_all(future).unwrap(); 51 | 52 | // 3. Singlethreaded mode: entire Tokio on one thread. 53 | // tokio-stdin-stdout however spawns separate threads for stdin and stdout anyway, 54 | // so there are still threads here in this example. 55 | tokio::runtime::current_thread::Runtime::new().unwrap().block_on(future).unwrap(); 56 | } 57 | -------------------------------------------------------------------------------- /examples/loop.rs: -------------------------------------------------------------------------------- 1 | extern crate tokio; 2 | extern crate tokio_io; 3 | extern crate tokio_stdin_stdout; 4 | use tokio::prelude::Future; 5 | 6 | use tokio_io::io::write_all as tokio_write_all; 7 | 8 | use tokio::prelude::Stream; 9 | 10 | fn main() { 11 | let stdout = tokio_stdin_stdout::stdout(0).make_sendable(); 12 | let stdout_ = stdout.clone(); 13 | 14 | let h = std::iter::repeat("hello\n").take(10); 15 | let s = tokio::prelude::stream::iter_ok::<_, ()>(h); 16 | let f = s.for_each(move |x| tokio_write_all(stdout_.clone(), x).map(drop).map_err(drop)); 17 | let prog = f.and_then(move |()| { 18 | tokio_io::io::shutdown(stdout.clone()) 19 | .map(drop) 20 | .map_err(drop) 21 | }); 22 | 23 | tokio::run(prog); 24 | } 25 | -------------------------------------------------------------------------------- /examples2/linebyline_asyncfn1.rs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env cargo-script 2 | //! 3 | //! ```cargo 4 | //! [dependencies] 5 | //! tokio = "0.1.7" 6 | //! tokio-io = "0.1" 7 | //! tokio-stdin-stdout = "0.1" 8 | //! tokio-codec = "0.1.0" 9 | //! futures-await = "0.1.1" 10 | //! 11 | //! ``` 12 | #![feature(proc_macro, proc_macro_non_items, generators)] 13 | #![allow(stable_features)] 14 | 15 | extern crate tokio; 16 | extern crate tokio_io; 17 | extern crate tokio_stdin_stdout; 18 | extern crate tokio_codec; 19 | extern crate futures_await as futures; 20 | use futures::prelude::{*,await}; 21 | 22 | 23 | use tokio_codec::{FramedRead, FramedWrite, LinesCodec}; 24 | 25 | #[async] 26 | fn async_op(input: String) -> Result { 27 | Ok(input.to_ascii_uppercase()) 28 | } 29 | 30 | #[async] 31 | fn prog() -> Result<(),std::io::Error> { 32 | let stdin = tokio_stdin_stdout::stdin(0).make_sendable(); 33 | let stdout = tokio_stdin_stdout::stdout(0).make_sendable(); 34 | 35 | let framed_stdin = FramedRead::new(stdin, LinesCodec::new()); 36 | let mut framed_stdout = FramedWrite::new(stdout, LinesCodec::new()); 37 | 38 | #[async] for line in framed_stdin { 39 | let line2 = await!(async_op(line))?; 40 | framed_stdout = await!(framed_stdout.send(line2))?; 41 | } 42 | 43 | await!(tokio_io::io::shutdown(framed_stdout.into_inner())); 44 | Ok(()) 45 | } 46 | 47 | fn main() { 48 | tokio::run(prog().map_err(|e|panic!("Error:{}",e))); 49 | } 50 | -------------------------------------------------------------------------------- /examples2/linebyline_asyncfn3.rs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env cargo-script 2 | //! 3 | //! ```cargo 4 | //! [dependencies] 5 | //! tokio = "0.1.7" 6 | //! tokio-io = "0.1" 7 | //! tokio-stdin-stdout = "0.1" 8 | //! tokio-codec = "0.1.0" 9 | //! futures-util-preview = {version="0.3.0-alpha.3", features=["tokio-compat"]} 10 | //! futures-preview = "0.3.0-alpha.3" 11 | //! 12 | //! ``` 13 | 14 | #![feature(await_macro,async_await,futures_api, stmt_expr_attributes, custom_attribute)] 15 | 16 | extern crate tokio; 17 | extern crate tokio_io; 18 | extern crate tokio_stdin_stdout; 19 | extern crate tokio_codec; 20 | 21 | extern crate futures_util; 22 | 23 | use futures_util::FutureExt; 24 | use futures_util::TryFutureExt; 25 | use futures_util::compat::Future01CompatExt; 26 | use futures_util::compat::TokioDefaultSpawn; 27 | use futures_util::compat::Stream01CompatExt; 28 | use futures_util::StreamExt; 29 | 30 | 31 | use tokio_codec::{FramedRead, FramedWrite, LinesCodec}; 32 | 33 | use tokio_io::io::write_all as tokio_write_all; 34 | 35 | async fn prog() { 36 | let stdin = tokio_stdin_stdout::stdin(0).make_sendable(); 37 | let stdout = tokio_stdin_stdout::stdout(0).make_sendable(); 38 | 39 | let framed_stdin = FramedRead::new(stdin, LinesCodec::new()); 40 | let framed_stdout = FramedWrite::new(stdout, LinesCodec::new()); 41 | 42 | async for line in framed_stdin.compat() { 43 | // N/A 44 | } 45 | 46 | await!(tokio_io::io::shutdown(framed_stdout.into_inner()).compat()); 47 | 48 | } 49 | 50 | fn main() { 51 | tokio::run(prog().boxed().unit_error().compat(TokioDefaultSpawn)); 52 | } 53 | -------------------------------------------------------------------------------- /examples2/loop_asyncfn1.rs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env cargo-script 2 | //! 3 | //! ```cargo 4 | //! [dependencies] 5 | //! tokio = "0.1.7" 6 | //! tokio-io = "0.1" 7 | //! tokio-stdin-stdout = "0.1" 8 | //! futures-await = "0.1.1" 9 | //! 10 | //! ``` 11 | 12 | #![feature(proc_macro, proc_macro_non_items, generators)] 13 | #![allow(stable_features)] 14 | 15 | extern crate tokio; 16 | extern crate tokio_io; 17 | extern crate tokio_stdin_stdout; 18 | extern crate futures_await as futures; 19 | use futures::prelude::{*,await}; 20 | 21 | use tokio_io::io::write_all as tokio_write_all; 22 | 23 | #[async] 24 | fn prog() -> Result<(),()> { 25 | let stdout = tokio_stdin_stdout::stdout(0).make_sendable(); 26 | 27 | for _ in 0..10 { 28 | await!(tokio_write_all(stdout.clone(), "hello\n")); 29 | } 30 | await!(tokio_io::io::shutdown(stdout)); 31 | 32 | Ok(()) 33 | } 34 | 35 | fn main() { 36 | tokio::run(prog()); 37 | } 38 | -------------------------------------------------------------------------------- /examples2/loop_asyncfn3.rs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env cargo-script 2 | //! 3 | //! ```cargo 4 | //! [dependencies] 5 | //! tokio = "0.1.7" 6 | //! tokio-io = "0.1" 7 | //! tokio-stdin-stdout = "0.1" 8 | //! futures-util-preview = {version="0.3.0-alpha.3", features=["tokio-compat"]} 9 | //! futures-preview = "0.3.0-alpha.3" 10 | //! 11 | //! ``` 12 | 13 | #![feature(await_macro,async_await,futures_api)] 14 | 15 | extern crate tokio; 16 | extern crate tokio_io; 17 | extern crate tokio_stdin_stdout; 18 | 19 | extern crate futures_util; 20 | 21 | use futures_util::FutureExt; 22 | use futures_util::TryFutureExt; 23 | use futures_util::compat::Future01CompatExt; 24 | use futures_util::compat::TokioDefaultSpawn; 25 | 26 | use tokio_io::io::write_all as tokio_write_all; 27 | 28 | async fn prog() { 29 | 30 | let stdout = tokio_stdin_stdout::stdout(0).make_sendable(); 31 | 32 | for _ in 0..10usize { 33 | await!(tokio_write_all(stdout.clone(), "hello\n").compat()); 34 | } 35 | await!(tokio_io::io::shutdown(stdout).compat()); 36 | 37 | } 38 | 39 | fn main() { 40 | tokio::run(prog().boxed().unit_error().compat(TokioDefaultSpawn)); 41 | } 42 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | 3 | //! A bridge between [std::io::std{in,out}][1] and Future's AsyncRead/AsyncWrite worlds. 4 | //! [1]:https://doc.rust-lang.org/std/io/fn.stdin.html 5 | //! 6 | //! Example: 7 | //! 8 | //! ```rust,no_run 9 | //! extern crate tokio_core; 10 | //! extern crate tokio_io; 11 | //! extern crate tokio_stdin_stdout; 12 | //! 13 | //! let mut core = tokio_core::reactor::Core::new().unwrap(); 14 | //! 15 | //! let stdin = tokio_stdin_stdout::stdin(0); 16 | //! let stdout = tokio_stdin_stdout::stdout(0); 17 | //! 18 | //! core.run(tokio_io::io::copy(stdin, stdout)).unwrap(); 19 | //! ``` 20 | //! 21 | //! It works by starting separate threads, which do actual synchronous I/O and communicating to 22 | //! the asynchronous world using [future::sync::mpsc](http://alexcrichton.com/futures-rs/futures/sync/mpsc/index.html). 23 | //! 24 | //! For Unix (Linux, OS X) better use [tokio-file-unix](https://crates.io/crates/tokio-file-unix). 25 | //! 26 | //! Concerns: 27 | //! 28 | //! * stdin/stdout are not expected to be ever normally used after using functions from this crate 29 | //! * Allocation-heavy. 30 | //! * All errors collapsed to ErrorKind::Other (for stdout) or ErrorKind::BrokenPipe (for stdin) 31 | //! * Failure to write to stdout is only seen after attempting to send there about 3 more buffers. 32 | 33 | extern crate futures; 34 | extern crate tokio_io; 35 | 36 | const BUFSIZ: usize = 8192; 37 | 38 | use futures::{Async, AsyncSink, Future, Poll, Sink, Stream}; 39 | use std::cell::RefCell; 40 | use std::io::{Error, ErrorKind, Read, Result, Write}; 41 | use std::rc::Rc; 42 | use std::sync::{Arc, LockResult, Mutex, MutexGuard, PoisonError, TryLockError, TryLockResult}; 43 | use std::thread::JoinHandle; 44 | use tokio_io::{AsyncRead, AsyncWrite}; 45 | 46 | type BBR = futures::sync::mpsc::Receiver>; 47 | type BBS = futures::sync::mpsc::Sender>; 48 | 49 | /// Asynchronous stdin 50 | pub struct ThreadedStdin { 51 | debt: Option>, 52 | rcv: BBR, 53 | } 54 | 55 | impl ThreadedStdin { 56 | /// Wrap into `Arc` to make it clonable and sendable 57 | pub fn make_sendable(self) -> SendableStdin { 58 | SendableStdin::new(self) 59 | } 60 | /// Wrap into `Rc` to make it clonable 61 | pub fn make_clonable(self) -> ClonableStdin { 62 | ClonableStdin::new(self) 63 | } 64 | } 65 | 66 | /// Constructor for the `ThreadedStdin` 67 | pub fn stdin(queue_size: usize) -> ThreadedStdin { 68 | let (snd_, rcv): (BBS, BBR) = futures::sync::mpsc::channel(queue_size); 69 | std::thread::spawn(move || { 70 | let mut snd = snd_; 71 | let sin = ::std::io::stdin(); 72 | let mut sin_lock = sin.lock(); 73 | let mut buf = vec![0; BUFSIZ]; 74 | while let Ok(ret) = sin_lock.read(&mut buf[..]) { 75 | let content = buf[0..ret].to_vec().into_boxed_slice(); 76 | snd = match snd.send(content).wait() { 77 | Ok(x) => x, 78 | Err(_) => break, 79 | } 80 | } 81 | }); 82 | ThreadedStdin { debt: None, rcv } 83 | } 84 | 85 | impl AsyncRead for ThreadedStdin {} 86 | impl Read for ThreadedStdin { 87 | fn read(&mut self, buf: &mut [u8]) -> Result { 88 | let mut handle_the_buffer = |incoming_buf: Box<[u8]>| { 89 | let l = buf.len(); 90 | let dl = incoming_buf.len(); 91 | if l >= dl { 92 | buf[0..dl].copy_from_slice(&incoming_buf); 93 | (None, Ok(dl)) 94 | } else { 95 | buf[0..l].copy_from_slice(&incoming_buf[0..l]); 96 | let newdebt = Some(incoming_buf[l..].to_vec().into_boxed_slice()); 97 | (newdebt, Ok(l)) 98 | } 99 | }; 100 | 101 | let (new_debt, ret) = if let Some(debt) = self.debt.take() { 102 | handle_the_buffer(debt) 103 | } else { 104 | match self.rcv.poll() { 105 | Ok(Async::Ready(Some(newbuf))) => handle_the_buffer(newbuf), 106 | Ok(Async::Ready(None)) => (None, Err(ErrorKind::BrokenPipe.into())), 107 | Ok(Async::NotReady) => (None, Err(ErrorKind::WouldBlock.into())), 108 | Err(_) => (None, Err(ErrorKind::Other.into())), 109 | } 110 | }; 111 | self.debt = new_debt; 112 | ret 113 | } 114 | } 115 | 116 | /// Asynchronous stdout 117 | pub struct ThreadedStdout { 118 | snd: BBS, 119 | jh: Option>, 120 | } 121 | 122 | impl ThreadedStdout { 123 | /// Wrap into `Arc` to make it clonable and sendable 124 | pub fn make_sendable(self) -> SendableStdout { 125 | SendableStdout::new(self) 126 | } 127 | /// Wrap into `Rc` to make it clonable 128 | pub fn make_clonable(self) -> ClonableStdout { 129 | ClonableStdout::new(self) 130 | } 131 | } 132 | /// Constructor for the `ThreadedStdout` 133 | pub fn stdout(queue_size: usize) -> ThreadedStdout { 134 | let (snd, rcv): (BBS, BBR) = futures::sync::mpsc::channel(queue_size); 135 | let jh = std::thread::spawn(move || { 136 | let sout = ::std::io::stdout(); 137 | let mut sout_lock = sout.lock(); 138 | for b in rcv.wait() { 139 | if let Ok(b) = b { 140 | if b.len() == 0 { 141 | break; 142 | } 143 | if sout_lock.write_all(&b).is_err() { 144 | break; 145 | } 146 | if sout_lock.flush().is_err() { 147 | break; 148 | } 149 | } else { 150 | break; 151 | } 152 | } 153 | let _ = sout_lock.write(&[]); 154 | }); 155 | ThreadedStdout { snd, jh: Some(jh) } 156 | } 157 | 158 | impl AsyncWrite for ThreadedStdout { 159 | fn shutdown(&mut self) -> Poll<(), Error> { 160 | // Signal the thread to exit. 161 | match self.snd.start_send(vec![].into_boxed_slice()) { 162 | Ok(AsyncSink::Ready) => (), 163 | Ok(AsyncSink::NotReady(_)) => return Ok(Async::NotReady), 164 | Err(_) => { 165 | return Ok(Async::Ready(())); 166 | } 167 | }; 168 | match self.snd.poll_complete() { 169 | Ok(Async::Ready(_)) => (), 170 | Ok(Async::NotReady) => return Ok(Async::NotReady), 171 | Err(_) => { 172 | return Ok(Async::Ready(())); 173 | } 174 | }; 175 | if self.snd.close().is_err() { 176 | return Ok(Async::Ready(())); 177 | }; 178 | if let Some(jh) = self.jh.take() { 179 | if jh.join().is_err() { 180 | return Err(ErrorKind::Other.into()); 181 | }; 182 | } 183 | Ok(Async::Ready(())) 184 | } 185 | } 186 | impl Write for ThreadedStdout { 187 | fn write(&mut self, buf: &[u8]) -> Result { 188 | if buf.is_empty() { 189 | return Ok(0); 190 | } 191 | 192 | match self.snd.start_send(buf.to_vec().into_boxed_slice()) { 193 | Ok(AsyncSink::Ready) => (), 194 | Ok(AsyncSink::NotReady(_)) => return Err(ErrorKind::WouldBlock.into()), 195 | Err(_) => return Err(ErrorKind::Other.into()), 196 | } 197 | 198 | Ok(buf.len()) 199 | } 200 | fn flush(&mut self) -> Result<()> { 201 | match self.snd.poll_complete() { 202 | Ok(Async::Ready(_)) => Ok(()), 203 | Ok(Async::NotReady) => Err(ErrorKind::WouldBlock.into()), 204 | Err(_) => Err(ErrorKind::Other.into()), 205 | } 206 | } 207 | } 208 | 209 | // XXX code duplication: 210 | 211 | /// Asynchronous stderr 212 | pub type ThreadedStderr = ThreadedStdout; 213 | /// Constructor for the `ThreadedStderr` 214 | pub fn stderr(queue_size: usize) -> ThreadedStderr { 215 | let (snd, rcv): (BBS, BBR) = futures::sync::mpsc::channel(queue_size); 216 | let jh = std::thread::spawn(move || { 217 | let sout = ::std::io::stderr(); 218 | let mut sout_lock = sout.lock(); 219 | for b in rcv.wait() { 220 | if let Ok(b) = b { 221 | if b.len() == 0 { 222 | break; 223 | } 224 | if sout_lock.write_all(&b).is_err() { 225 | break; 226 | } 227 | if sout_lock.flush().is_err() { 228 | break; 229 | } 230 | } else { 231 | break; 232 | } 233 | } 234 | let _ = sout_lock.write(&[]); 235 | }); 236 | ThreadedStdout { snd, jh: Some(jh) } 237 | } 238 | 239 | /// A sendable and clonable ThreadedStdout wrapper based on `Arc>` 240 | /// 241 | /// Note that a mutex is being locked every time a write is performed, 242 | /// so performance may be a problem, unless you use the `lock` method. 243 | /// 244 | /// Also note that if data is outputted using multiple `Write::write` calls from multiple tasks, the order of chunks is not specified. 245 | #[derive(Clone)] 246 | pub struct SendableStdout(Arc>); 247 | 248 | /// Result of `SendableStdout::lock` or `SendableStdout::try_lock` 249 | pub struct SendableStdoutGuard<'a>(MutexGuard<'a, ThreadedStdout>); 250 | 251 | impl SendableStdout { 252 | /// wrap ThreadedStdout or ThreadedStderr in a sendable/clonable wrapper 253 | pub fn new(so: ThreadedStdout) -> SendableStdout { 254 | SendableStdout(Arc::new(Mutex::new(so))) 255 | } 256 | 257 | /// Acquire more permanent mutex guard on stdout, like with `std::io::Stdout::lock` 258 | /// The returned guard also implements AsyncWrite 259 | pub fn lock(&self) -> LockResult { 260 | match self.0.lock() { 261 | Ok(x) => Ok(SendableStdoutGuard(x)), 262 | Err(e) => Err(PoisonError::new(SendableStdoutGuard(e.into_inner()))), 263 | } 264 | } 265 | /// Acquire more permanent mutex guard on stdout 266 | /// The returned guard also implements AsyncWrite 267 | pub fn try_lock(&self) -> TryLockResult { 268 | match self.0.try_lock() { 269 | Ok(x) => Ok(SendableStdoutGuard(x)), 270 | Err(TryLockError::Poisoned(e)) => Err(TryLockError::Poisoned(PoisonError::new( 271 | SendableStdoutGuard(e.into_inner()), 272 | ))), 273 | Err(TryLockError::WouldBlock) => Err(TryLockError::WouldBlock), 274 | } 275 | } 276 | } 277 | 278 | impl Write for SendableStdout { 279 | fn write(&mut self, buf: &[u8]) -> Result { 280 | match self.0.lock() { 281 | Ok(mut l) => l.write(buf), 282 | Err(e) => Err(Error::new(ErrorKind::Other, format!("{}", e))), 283 | } 284 | } 285 | fn flush(&mut self) -> Result<()> { 286 | match self.0.lock() { 287 | Ok(mut l) => l.flush(), 288 | Err(e) => Err(Error::new(ErrorKind::Other, format!("{}", e))), 289 | } 290 | } 291 | } 292 | impl AsyncWrite for SendableStdout { 293 | fn shutdown(&mut self) -> Poll<(), Error> { 294 | match self.0.lock() { 295 | Ok(mut l) => l.shutdown(), 296 | Err(e) => Err(Error::new(ErrorKind::Other, format!("{}", e))), 297 | } 298 | } 299 | } 300 | impl<'a> Write for SendableStdoutGuard<'a> { 301 | fn write(&mut self, buf: &[u8]) -> Result { 302 | self.0.write(buf) 303 | } 304 | fn flush(&mut self) -> Result<()> { 305 | self.0.flush() 306 | } 307 | } 308 | impl<'a> AsyncWrite for SendableStdoutGuard<'a> { 309 | fn shutdown(&mut self) -> Poll<(), Error> { 310 | self.0.shutdown() 311 | } 312 | } 313 | 314 | /// A clonable `ThreadedStdout` wrapper based on `Rc>` 315 | /// If you need `Send`, use SendableStdout 316 | #[derive(Clone)] 317 | pub struct ClonableStdout(Rc>); 318 | impl ClonableStdout { 319 | /// wrap ThreadedStdout or ThreadedStderr in a sendable/clonable wrapper 320 | pub fn new(so: ThreadedStdout) -> ClonableStdout { 321 | ClonableStdout(Rc::new(RefCell::new(so))) 322 | } 323 | } 324 | 325 | impl Write for ClonableStdout { 326 | fn write(&mut self, buf: &[u8]) -> Result { 327 | self.0.borrow_mut().write(buf) 328 | } 329 | fn flush(&mut self) -> Result<()> { 330 | self.0.borrow_mut().flush() 331 | } 332 | } 333 | impl AsyncWrite for ClonableStdout { 334 | fn shutdown(&mut self) -> Poll<(), Error> { 335 | self.0.borrow_mut().shutdown() 336 | } 337 | } 338 | 339 | /// Alias for SendableStdout to avoid confusion of SendableStdout being used for stderr. 340 | /// 341 | /// Note that a mutex is being locked every time a read is performed, 342 | /// so performance may be a problem. 343 | /// 344 | /// Also note that if data is outputted using multiple `Write::write` calls from multiple tasks, the order of chunks is not specified. 345 | pub type SendableStderr = SendableStdout; 346 | /// Result of `SendableStderr::lock` or `SendableStderr::try_lock` 347 | pub type SendableStderrGuard<'a> = SendableStdoutGuard<'a>; 348 | /// Alias for ClonableStdout to avoid confusion of ClonableStdout being used for stderr. 349 | pub type ClonableStderr = ClonableStdout; 350 | 351 | /// A clonable `ThreadedStdout` wrapper based on `Rc>` 352 | /// If you need `Send`, use SendableStdout 353 | /// 354 | /// Note that data being read is not duplicated across cloned readers used from multiple tasks. 355 | /// Be careful about corruption. 356 | #[derive(Clone)] 357 | pub struct ClonableStdin(Rc>); 358 | impl ClonableStdin { 359 | /// wrap ThreadedStdout or ThreadedStderr in a sendable/clonable wrapper 360 | pub fn new(so: ThreadedStdin) -> ClonableStdin { 361 | ClonableStdin(Rc::new(RefCell::new(so))) 362 | } 363 | } 364 | 365 | impl Read for ClonableStdin { 366 | fn read(&mut self, buf: &mut [u8]) -> Result { 367 | self.0.borrow_mut().read(buf) 368 | } 369 | } 370 | impl AsyncRead for ClonableStdin {} 371 | /// A sendable and clonable ThreadedStdin wrapper based on `Arc>` 372 | /// 373 | /// Note that data being read is not duplicated across cloned readers used from multiple tasks. 374 | /// Be careful about corruption. 375 | #[derive(Clone)] 376 | pub struct SendableStdin(Arc>); 377 | 378 | /// Result of `SendableStdin::lock` or `SendableStdin::try_lock` 379 | pub struct SendableStdinGuard<'a>(MutexGuard<'a, ThreadedStdin>); 380 | 381 | impl SendableStdin { 382 | /// wrap ThreadedStdin in a sendable/clonable wrapper 383 | pub fn new(si: ThreadedStdin) -> SendableStdin { 384 | SendableStdin(Arc::new(Mutex::new(si))) 385 | } 386 | 387 | /// Acquire more permanent mutex guard on stdout, like with `std::io::Stdout::lock` 388 | /// The returned guard also implements AsyncWrite 389 | pub fn lock(&self) -> LockResult { 390 | match self.0.lock() { 391 | Ok(x) => Ok(SendableStdinGuard(x)), 392 | Err(e) => Err(PoisonError::new(SendableStdinGuard(e.into_inner()))), 393 | } 394 | } 395 | /// Acquire more permanent mutex guard on stdout 396 | /// The returned guard also implements AsyncWrite 397 | pub fn try_lock(&self) -> TryLockResult { 398 | match self.0.try_lock() { 399 | Ok(x) => Ok(SendableStdinGuard(x)), 400 | Err(TryLockError::Poisoned(e)) => Err(TryLockError::Poisoned(PoisonError::new( 401 | SendableStdinGuard(e.into_inner()), 402 | ))), 403 | Err(TryLockError::WouldBlock) => Err(TryLockError::WouldBlock), 404 | } 405 | } 406 | } 407 | 408 | impl Read for SendableStdin { 409 | fn read(&mut self, buf: &mut [u8]) -> Result { 410 | match self.0.lock() { 411 | Ok(mut l) => l.read(buf), 412 | Err(e) => Err(Error::new(ErrorKind::Other, format!("{}", e))), 413 | } 414 | } 415 | } 416 | impl AsyncRead for SendableStdin {} 417 | 418 | impl<'a> Read for SendableStdinGuard<'a> { 419 | fn read(&mut self, buf: &mut [u8]) -> Result { 420 | self.0.read(buf) 421 | } 422 | } 423 | impl<'a> AsyncRead for SendableStdinGuard<'a> {} 424 | --------------------------------------------------------------------------------