]> git.proxmox.com Git - rustc.git/blob - src/libstd/io/stdio.rs
Imported Upstream version 1.8.0+dfsg1
[rustc.git] / src / libstd / io / stdio.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use prelude::v1::*;
12 use io::prelude::*;
13
14 use cell::{RefCell, BorrowState};
15 use cmp;
16 use fmt;
17 use io::lazy::Lazy;
18 use io::{self, BufReader, LineWriter};
19 use sync::{Arc, Mutex, MutexGuard};
20 use sys::stdio;
21 use sys_common::io::{read_to_end_uninitialized};
22 use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
23 use thread::LocalKeyState;
24
25 /// Stdout used by print! and println! macros
26 thread_local! {
27 static LOCAL_STDOUT: RefCell<Option<Box<Write + Send>>> = {
28 RefCell::new(None)
29 }
30 }
31
32 /// A handle to a raw instance of the standard input stream of this process.
33 ///
34 /// This handle is not synchronized or buffered in any fashion. Constructed via
35 /// the `std::io::stdio::stdin_raw` function.
36 struct StdinRaw(stdio::Stdin);
37
38 /// A handle to a raw instance of the standard output stream of this process.
39 ///
40 /// This handle is not synchronized or buffered in any fashion. Constructed via
41 /// the `std::io::stdio::stdout_raw` function.
42 struct StdoutRaw(stdio::Stdout);
43
44 /// A handle to a raw instance of the standard output stream of this process.
45 ///
46 /// This handle is not synchronized or buffered in any fashion. Constructed via
47 /// the `std::io::stdio::stderr_raw` function.
48 struct StderrRaw(stdio::Stderr);
49
50 /// Constructs a new raw handle to the standard input of this process.
51 ///
52 /// The returned handle does not interact with any other handles created nor
53 /// handles returned by `std::io::stdin`. Data buffered by the `std::io::stdin`
54 /// handles is **not** available to raw handles returned from this function.
55 ///
56 /// The returned handle has no external synchronization or buffering.
57 fn stdin_raw() -> io::Result<StdinRaw> { stdio::Stdin::new().map(StdinRaw) }
58
59 /// Constructs a new raw handle to the standard output stream of this process.
60 ///
61 /// The returned handle does not interact with any other handles created nor
62 /// handles returned by `std::io::stdout`. Note that data is buffered by the
63 /// `std::io::stdout` handles so writes which happen via this raw handle may
64 /// appear before previous writes.
65 ///
66 /// The returned handle has no external synchronization or buffering layered on
67 /// top.
68 fn stdout_raw() -> io::Result<StdoutRaw> { stdio::Stdout::new().map(StdoutRaw) }
69
70 /// Constructs a new raw handle to the standard error stream of this process.
71 ///
72 /// The returned handle does not interact with any other handles created nor
73 /// handles returned by `std::io::stderr`.
74 ///
75 /// The returned handle has no external synchronization or buffering layered on
76 /// top.
77 fn stderr_raw() -> io::Result<StderrRaw> { stdio::Stderr::new().map(StderrRaw) }
78
79 impl Read for StdinRaw {
80 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) }
81 }
82 impl Write for StdoutRaw {
83 fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) }
84 fn flush(&mut self) -> io::Result<()> { Ok(()) }
85 }
86 impl Write for StderrRaw {
87 fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) }
88 fn flush(&mut self) -> io::Result<()> { Ok(()) }
89 }
90
91 enum Maybe<T> {
92 Real(T),
93 Fake,
94 }
95
96 impl<W: io::Write> io::Write for Maybe<W> {
97 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
98 match *self {
99 Maybe::Real(ref mut w) => handle_ebadf(w.write(buf), buf.len()),
100 Maybe::Fake => Ok(buf.len())
101 }
102 }
103
104 fn flush(&mut self) -> io::Result<()> {
105 match *self {
106 Maybe::Real(ref mut w) => handle_ebadf(w.flush(), ()),
107 Maybe::Fake => Ok(())
108 }
109 }
110 }
111
112 impl<R: io::Read> io::Read for Maybe<R> {
113 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
114 match *self {
115 Maybe::Real(ref mut r) => handle_ebadf(r.read(buf), 0),
116 Maybe::Fake => Ok(0)
117 }
118 }
119 }
120
121 fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> {
122 #[cfg(windows)]
123 const ERR: i32 = ::sys::c::ERROR_INVALID_HANDLE as i32;
124 #[cfg(not(windows))]
125 const ERR: i32 = ::libc::EBADF as i32;
126
127 match r {
128 Err(ref e) if e.raw_os_error() == Some(ERR) => Ok(default),
129 r => r
130 }
131 }
132
133 /// A handle to the standard input stream of a process.
134 ///
135 /// Each handle is a shared reference to a global buffer of input data to this
136 /// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods
137 /// (e.g. `.lines()`). Writes to this handle are otherwise locked with respect
138 /// to other writes.
139 ///
140 /// This handle implements the `Read` trait, but beware that concurrent reads
141 /// of `Stdin` must be executed with care.
142 ///
143 /// Created by the [`io::stdin`] method.
144 ///
145 /// [`io::stdin`]: fn.stdin.html
146 /// [`BufRead`]: trait.BufRead.html
147 #[stable(feature = "rust1", since = "1.0.0")]
148 pub struct Stdin {
149 inner: Arc<Mutex<BufReader<Maybe<StdinRaw>>>>,
150 }
151
152 /// A locked reference to the `Stdin` handle.
153 ///
154 /// This handle implements both the [`Read`] and [`BufRead`] traits, and
155 /// is constructed via the [`Stdin::lock`] method.
156 ///
157 /// [`Read`]: trait.Read.html
158 /// [`BufRead`]: trait.BufRead.html
159 /// [`Stdin::lock`]: struct.Stdin.html#method.lock
160 #[stable(feature = "rust1", since = "1.0.0")]
161 pub struct StdinLock<'a> {
162 inner: MutexGuard<'a, BufReader<Maybe<StdinRaw>>>,
163 }
164
165 /// Constructs a new handle to the standard input of the current process.
166 ///
167 /// Each handle returned is a reference to a shared global buffer whose access
168 /// is synchronized via a mutex. If you need more explicit control over
169 /// locking, see the [`lock() method`][lock].
170 ///
171 /// [lock]: struct.Stdin.html#method.lock
172 ///
173 /// # Examples
174 ///
175 /// Using implicit synchronization:
176 ///
177 /// ```
178 /// use std::io::{self, Read};
179 ///
180 /// # fn foo() -> io::Result<String> {
181 /// let mut buffer = String::new();
182 /// try!(io::stdin().read_to_string(&mut buffer));
183 /// # Ok(buffer)
184 /// # }
185 /// ```
186 ///
187 /// Using explicit synchronization:
188 ///
189 /// ```
190 /// use std::io::{self, Read};
191 ///
192 /// # fn foo() -> io::Result<String> {
193 /// let mut buffer = String::new();
194 /// let stdin = io::stdin();
195 /// let mut handle = stdin.lock();
196 ///
197 /// try!(handle.read_to_string(&mut buffer));
198 /// # Ok(buffer)
199 /// # }
200 /// ```
201 #[stable(feature = "rust1", since = "1.0.0")]
202 pub fn stdin() -> Stdin {
203 static INSTANCE: Lazy<Mutex<BufReader<Maybe<StdinRaw>>>> = Lazy::new(stdin_init);
204 return Stdin {
205 inner: INSTANCE.get().expect("cannot access stdin during shutdown"),
206 };
207
208 fn stdin_init() -> Arc<Mutex<BufReader<Maybe<StdinRaw>>>> {
209 let stdin = match stdin_raw() {
210 Ok(stdin) => Maybe::Real(stdin),
211 _ => Maybe::Fake
212 };
213
214 // The default buffer capacity is 64k, but apparently windows
215 // doesn't like 64k reads on stdin. See #13304 for details, but the
216 // idea is that on windows we use a slightly smaller buffer that's
217 // been seen to be acceptable.
218 Arc::new(Mutex::new(if cfg!(windows) {
219 BufReader::with_capacity(8 * 1024, stdin)
220 } else {
221 BufReader::new(stdin)
222 }))
223 }
224 }
225
226 impl Stdin {
227 /// Locks this handle to the standard input stream, returning a readable
228 /// guard.
229 ///
230 /// The lock is released when the returned lock goes out of scope. The
231 /// returned guard also implements the [`Read`] and [`BufRead`] traits for
232 /// accessing the underlying data.
233 ///
234 /// [`Read`]: trait.Read.html
235 /// [`BufRead`]: trait.BufRead.html
236 #[stable(feature = "rust1", since = "1.0.0")]
237 pub fn lock(&self) -> StdinLock {
238 StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) }
239 }
240
241 /// Locks this handle and reads a line of input into the specified buffer.
242 ///
243 /// For detailed semantics of this method, see the documentation on
244 /// [`BufRead::read_line`].
245 ///
246 /// [`BufRead::read_line`]: trait.BufRead.html#method.read_line
247 ///
248 /// # Examples
249 ///
250 /// ```no_run
251 /// use std::io;
252 ///
253 /// let mut input = String::new();
254 /// match io::stdin().read_line(&mut input) {
255 /// Ok(n) => {
256 /// println!("{} bytes read", n);
257 /// println!("{}", input);
258 /// }
259 /// Err(error) => println!("error: {}", error),
260 /// }
261 /// ```
262 ///
263 /// You can run the example one of two ways:
264 ///
265 /// - Pipe some text to it, e.g. `printf foo | path/to/executable`
266 /// - Give it text interactively by running the executable directly,
267 /// in which case it will wait for the Enter key to be pressed before
268 /// continuing
269 #[stable(feature = "rust1", since = "1.0.0")]
270 pub fn read_line(&self, buf: &mut String) -> io::Result<usize> {
271 self.lock().read_line(buf)
272 }
273 }
274
275 #[stable(feature = "rust1", since = "1.0.0")]
276 impl Read for Stdin {
277 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
278 self.lock().read(buf)
279 }
280 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
281 self.lock().read_to_end(buf)
282 }
283 fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
284 self.lock().read_to_string(buf)
285 }
286 fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
287 self.lock().read_exact(buf)
288 }
289 }
290
291 #[stable(feature = "rust1", since = "1.0.0")]
292 impl<'a> Read for StdinLock<'a> {
293 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
294 self.inner.read(buf)
295 }
296 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
297 unsafe { read_to_end_uninitialized(self, buf) }
298 }
299 }
300
301 #[stable(feature = "rust1", since = "1.0.0")]
302 impl<'a> BufRead for StdinLock<'a> {
303 fn fill_buf(&mut self) -> io::Result<&[u8]> { self.inner.fill_buf() }
304 fn consume(&mut self, n: usize) { self.inner.consume(n) }
305 }
306
307 // As with stdin on windows, stdout often can't handle writes of large
308 // sizes. For an example, see #14940. For this reason, don't try to
309 // write the entire output buffer on windows. On unix we can just
310 // write the whole buffer all at once.
311 //
312 // For some other references, it appears that this problem has been
313 // encountered by others [1] [2]. We choose the number 8KB just because
314 // libuv does the same.
315 //
316 // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
317 // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
318 #[cfg(windows)]
319 const OUT_MAX: usize = 8192;
320 #[cfg(unix)]
321 const OUT_MAX: usize = ::usize::MAX;
322
323 /// A handle to the global standard output stream of the current process.
324 ///
325 /// Each handle shares a global buffer of data to be written to the standard
326 /// output stream. Access is also synchronized via a lock and explicit control
327 /// over locking is available via the `lock` method.
328 ///
329 /// Created by the [`io::stdout`] method.
330 ///
331 /// [`io::stdout`]: fn.stdout.html
332 #[stable(feature = "rust1", since = "1.0.0")]
333 pub struct Stdout {
334 // FIXME: this should be LineWriter or BufWriter depending on the state of
335 // stdout (tty or not). Note that if this is not line buffered it
336 // should also flush-on-panic or some form of flush-on-abort.
337 inner: Arc<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>>,
338 }
339
340 /// A locked reference to the `Stdout` handle.
341 ///
342 /// This handle implements the [`Write`] trait, and is constructed via
343 /// the [`Stdout::lock`] method.
344 ///
345 /// [`Write`]: trait.Write.html
346 /// [`Stdout::lock`]: struct.Stdout.html#method.lock
347 #[stable(feature = "rust1", since = "1.0.0")]
348 pub struct StdoutLock<'a> {
349 inner: ReentrantMutexGuard<'a, RefCell<LineWriter<Maybe<StdoutRaw>>>>,
350 }
351
352 /// Constructs a new handle to the standard output of the current process.
353 ///
354 /// Each handle returned is a reference to a shared global buffer whose access
355 /// is synchronized via a mutex. If you need more explicit control over
356 /// locking, see the [Stdout::lock] method.
357 ///
358 /// [Stdout::lock]: struct.Stdout.html#method.lock
359 ///
360 /// # Examples
361 ///
362 /// Using implicit synchronization:
363 ///
364 /// ```
365 /// use std::io::{self, Write};
366 ///
367 /// # fn foo() -> io::Result<()> {
368 /// try!(io::stdout().write(b"hello world"));
369 ///
370 /// # Ok(())
371 /// # }
372 /// ```
373 ///
374 /// Using explicit synchronization:
375 ///
376 /// ```
377 /// use std::io::{self, Write};
378 ///
379 /// # fn foo() -> io::Result<()> {
380 /// let stdout = io::stdout();
381 /// let mut handle = stdout.lock();
382 ///
383 /// try!(handle.write(b"hello world"));
384 ///
385 /// # Ok(())
386 /// # }
387 /// ```
388 #[stable(feature = "rust1", since = "1.0.0")]
389 pub fn stdout() -> Stdout {
390 static INSTANCE: Lazy<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>>
391 = Lazy::new(stdout_init);
392 return Stdout {
393 inner: INSTANCE.get().expect("cannot access stdout during shutdown"),
394 };
395
396 fn stdout_init() -> Arc<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>> {
397 let stdout = match stdout_raw() {
398 Ok(stdout) => Maybe::Real(stdout),
399 _ => Maybe::Fake,
400 };
401 Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout))))
402 }
403 }
404
405 impl Stdout {
406 /// Locks this handle to the standard output stream, returning a writable
407 /// guard.
408 ///
409 /// The lock is released when the returned lock goes out of scope. The
410 /// returned guard also implements the `Write` trait for writing data.
411 #[stable(feature = "rust1", since = "1.0.0")]
412 pub fn lock(&self) -> StdoutLock {
413 StdoutLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) }
414 }
415 }
416
417 #[stable(feature = "rust1", since = "1.0.0")]
418 impl Write for Stdout {
419 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
420 self.lock().write(buf)
421 }
422 fn flush(&mut self) -> io::Result<()> {
423 self.lock().flush()
424 }
425 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
426 self.lock().write_all(buf)
427 }
428 fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> {
429 self.lock().write_fmt(args)
430 }
431 }
432 #[stable(feature = "rust1", since = "1.0.0")]
433 impl<'a> Write for StdoutLock<'a> {
434 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
435 self.inner.borrow_mut().write(&buf[..cmp::min(buf.len(), OUT_MAX)])
436 }
437 fn flush(&mut self) -> io::Result<()> {
438 self.inner.borrow_mut().flush()
439 }
440 }
441
442 /// A handle to the standard error stream of a process.
443 ///
444 /// For more information, see the [`io::stderr`] method.
445 ///
446 /// [`io::stderr`]: fn.stderr.html
447 #[stable(feature = "rust1", since = "1.0.0")]
448 pub struct Stderr {
449 inner: Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>>,
450 }
451
452 /// A locked reference to the `Stderr` handle.
453 ///
454 /// This handle implements the `Write` trait and is constructed via
455 /// the [`Stderr::lock`] method.
456 ///
457 /// [`Stderr::lock`]: struct.Stderr.html#method.lock
458 #[stable(feature = "rust1", since = "1.0.0")]
459 pub struct StderrLock<'a> {
460 inner: ReentrantMutexGuard<'a, RefCell<Maybe<StderrRaw>>>,
461 }
462
463 /// Constructs a new handle to the standard error of the current process.
464 ///
465 /// This handle is not buffered.
466 ///
467 /// # Examples
468 ///
469 /// Using implicit synchronization:
470 ///
471 /// ```
472 /// use std::io::{self, Write};
473 ///
474 /// # fn foo() -> io::Result<()> {
475 /// try!(io::stderr().write(b"hello world"));
476 ///
477 /// # Ok(())
478 /// # }
479 /// ```
480 ///
481 /// Using explicit synchronization:
482 ///
483 /// ```
484 /// use std::io::{self, Write};
485 ///
486 /// # fn foo() -> io::Result<()> {
487 /// let stderr = io::stderr();
488 /// let mut handle = stderr.lock();
489 ///
490 /// try!(handle.write(b"hello world"));
491 ///
492 /// # Ok(())
493 /// # }
494 /// ```
495 #[stable(feature = "rust1", since = "1.0.0")]
496 pub fn stderr() -> Stderr {
497 static INSTANCE: Lazy<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> = Lazy::new(stderr_init);
498 return Stderr {
499 inner: INSTANCE.get().expect("cannot access stderr during shutdown"),
500 };
501
502 fn stderr_init() -> Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> {
503 let stderr = match stderr_raw() {
504 Ok(stderr) => Maybe::Real(stderr),
505 _ => Maybe::Fake,
506 };
507 Arc::new(ReentrantMutex::new(RefCell::new(stderr)))
508 }
509 }
510
511 impl Stderr {
512 /// Locks this handle to the standard error stream, returning a writable
513 /// guard.
514 ///
515 /// The lock is released when the returned lock goes out of scope. The
516 /// returned guard also implements the `Write` trait for writing data.
517 #[stable(feature = "rust1", since = "1.0.0")]
518 pub fn lock(&self) -> StderrLock {
519 StderrLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) }
520 }
521 }
522
523 #[stable(feature = "rust1", since = "1.0.0")]
524 impl Write for Stderr {
525 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
526 self.lock().write(buf)
527 }
528 fn flush(&mut self) -> io::Result<()> {
529 self.lock().flush()
530 }
531 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
532 self.lock().write_all(buf)
533 }
534 fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> {
535 self.lock().write_fmt(args)
536 }
537 }
538 #[stable(feature = "rust1", since = "1.0.0")]
539 impl<'a> Write for StderrLock<'a> {
540 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
541 self.inner.borrow_mut().write(&buf[..cmp::min(buf.len(), OUT_MAX)])
542 }
543 fn flush(&mut self) -> io::Result<()> {
544 self.inner.borrow_mut().flush()
545 }
546 }
547
548 /// Resets the thread-local stderr handle to the specified writer
549 ///
550 /// This will replace the current thread's stderr handle, returning the old
551 /// handle. All future calls to `panic!` and friends will emit their output to
552 /// this specified handle.
553 ///
554 /// Note that this does not need to be called for all new threads; the default
555 /// output handle is to the process's stderr stream.
556 #[unstable(feature = "set_stdio",
557 reason = "this function may disappear completely or be replaced \
558 with a more general mechanism",
559 issue = "0")]
560 #[doc(hidden)]
561 pub fn set_panic(sink: Box<Write + Send>) -> Option<Box<Write + Send>> {
562 use panicking::LOCAL_STDERR;
563 use mem;
564 LOCAL_STDERR.with(move |slot| {
565 mem::replace(&mut *slot.borrow_mut(), Some(sink))
566 }).and_then(|mut s| {
567 let _ = s.flush();
568 Some(s)
569 })
570 }
571
572 /// Resets the thread-local stdout handle to the specified writer
573 ///
574 /// This will replace the current thread's stdout handle, returning the old
575 /// handle. All future calls to `print!` and friends will emit their output to
576 /// this specified handle.
577 ///
578 /// Note that this does not need to be called for all new threads; the default
579 /// output handle is to the process's stdout stream.
580 #[unstable(feature = "set_stdio",
581 reason = "this function may disappear completely or be replaced \
582 with a more general mechanism",
583 issue = "0")]
584 #[doc(hidden)]
585 pub fn set_print(sink: Box<Write + Send>) -> Option<Box<Write + Send>> {
586 use mem;
587 LOCAL_STDOUT.with(move |slot| {
588 mem::replace(&mut *slot.borrow_mut(), Some(sink))
589 }).and_then(|mut s| {
590 let _ = s.flush();
591 Some(s)
592 })
593 }
594
595 #[unstable(feature = "print",
596 reason = "implementation detail which may disappear or be replaced at any time",
597 issue = "0")]
598 #[doc(hidden)]
599 pub fn _print(args: fmt::Arguments) {
600 // As an implementation of the `println!` macro, we want to try our best to
601 // not panic wherever possible and get the output somewhere. There are
602 // currently two possible vectors for panics we take care of here:
603 //
604 // 1. If the TLS key for the local stdout has been destroyed, accessing it
605 // would cause a panic. Note that we just lump in the uninitialized case
606 // here for convenience, we're not trying to avoid a panic.
607 // 2. If the local stdout is currently in use (e.g. we're in the middle of
608 // already printing) then accessing again would cause a panic.
609 //
610 // If, however, the actual I/O causes an error, we do indeed panic.
611 let result = match LOCAL_STDOUT.state() {
612 LocalKeyState::Uninitialized |
613 LocalKeyState::Destroyed => stdout().write_fmt(args),
614 LocalKeyState::Valid => {
615 LOCAL_STDOUT.with(|s| {
616 if s.borrow_state() == BorrowState::Unused {
617 if let Some(w) = s.borrow_mut().as_mut() {
618 return w.write_fmt(args);
619 }
620 }
621 stdout().write_fmt(args)
622 })
623 }
624 };
625 if let Err(e) = result {
626 panic!("failed printing to stdout: {}", e);
627 }
628 }
629
630 #[cfg(test)]
631 mod tests {
632 use thread;
633 use super::*;
634
635 #[test]
636 fn panic_doesnt_poison() {
637 thread::spawn(|| {
638 let _a = stdin();
639 let _a = _a.lock();
640 let _a = stdout();
641 let _a = _a.lock();
642 let _a = stderr();
643 let _a = _a.lock();
644 panic!();
645 }).join().unwrap_err();
646
647 let _a = stdin();
648 let _a = _a.lock();
649 let _a = stdout();
650 let _a = _a.lock();
651 let _a = stderr();
652 let _a = _a.lock();
653 }
654 }