parent
6b9fbf212a
commit
d0d0e78208
@ -113,6 +113,11 @@ fn flush(&mut self) -> io::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl io::LocalOutput for Sink {
|
||||||
|
fn clone_box(&self) -> Box<dyn io::LocalOutput> {
|
||||||
|
Box::new(Self(self.0.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need
|
/// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need
|
||||||
/// for `'static` bounds.
|
/// for `'static` bounds.
|
||||||
|
@ -213,13 +213,13 @@ fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// This impl is only used by printing logic, so any error returned is always
|
/// This impl is only used by printing logic, so any error returned is always
|
||||||
/// of kind `Other`, and should be ignored.
|
/// of kind `Other`, and should be ignored.
|
||||||
impl Write for Box<dyn (::realstd::io::Write) + Send> {
|
impl Write for dyn ::realstd::io::LocalOutput {
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
(**self).write(buf).map_err(|_| ErrorKind::Other.into())
|
(*self).write(buf).map_err(|_| ErrorKind::Other.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
(**self).flush().map_err(|_| ErrorKind::Other.into())
|
(*self).flush().map_err(|_| ErrorKind::Other.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,10 +277,12 @@
|
|||||||
pub use self::stdio::{_eprint, _print};
|
pub use self::stdio::{_eprint, _print};
|
||||||
#[unstable(feature = "libstd_io_internals", issue = "42788")]
|
#[unstable(feature = "libstd_io_internals", issue = "42788")]
|
||||||
#[doc(no_inline, hidden)]
|
#[doc(no_inline, hidden)]
|
||||||
pub use self::stdio::{set_panic, set_print};
|
pub use self::stdio::{set_panic, set_print, LocalOutput};
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub use self::util::{copy, empty, repeat, sink, Empty, Repeat, Sink};
|
pub use self::util::{copy, empty, repeat, sink, Empty, Repeat, Sink};
|
||||||
|
|
||||||
|
pub(crate) use self::stdio::clone_io;
|
||||||
|
|
||||||
mod buffered;
|
mod buffered;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
mod error;
|
mod error;
|
||||||
|
@ -18,14 +18,14 @@
|
|||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
/// Used by the test crate to capture the output of the print! and println! macros.
|
/// Used by the test crate to capture the output of the print! and println! macros.
|
||||||
static LOCAL_STDOUT: RefCell<Option<Box<dyn Write + Send>>> = {
|
static LOCAL_STDOUT: RefCell<Option<Box<dyn LocalOutput>>> = {
|
||||||
RefCell::new(None)
|
RefCell::new(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
/// Used by the test crate to capture the output of the eprint! and eprintln! macros, and panics.
|
/// Used by the test crate to capture the output of the eprint! and eprintln! macros, and panics.
|
||||||
static LOCAL_STDERR: RefCell<Option<Box<dyn Write + Send>>> = {
|
static LOCAL_STDERR: RefCell<Option<Box<dyn LocalOutput>>> = {
|
||||||
RefCell::new(None)
|
RefCell::new(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -888,6 +888,18 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A writer than can be cloned to new threads.
|
||||||
|
#[unstable(
|
||||||
|
feature = "set_stdio",
|
||||||
|
reason = "this trait may disappear completely or be replaced \
|
||||||
|
with a more general mechanism",
|
||||||
|
issue = "none"
|
||||||
|
)]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub trait LocalOutput: Write + Send {
|
||||||
|
fn clone_box(&self) -> Box<dyn LocalOutput>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Resets the thread-local stderr handle to the specified writer
|
/// Resets the thread-local stderr handle to the specified writer
|
||||||
///
|
///
|
||||||
/// This will replace the current thread's stderr handle, returning the old
|
/// This will replace the current thread's stderr handle, returning the old
|
||||||
@ -903,7 +915,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|||||||
issue = "none"
|
issue = "none"
|
||||||
)]
|
)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn set_panic(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + Send>> {
|
pub fn set_panic(sink: Option<Box<dyn LocalOutput>>) -> Option<Box<dyn LocalOutput>> {
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
|
if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
|
||||||
// LOCAL_STDERR is definitely None since LOCAL_STREAMS is false.
|
// LOCAL_STDERR is definitely None since LOCAL_STREAMS is false.
|
||||||
@ -934,7 +946,7 @@ pub fn set_panic(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
|
|||||||
issue = "none"
|
issue = "none"
|
||||||
)]
|
)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + Send>> {
|
pub fn set_print(sink: Option<Box<dyn LocalOutput>>) -> Option<Box<dyn LocalOutput>> {
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
|
if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
|
||||||
// LOCAL_STDOUT is definitely None since LOCAL_STREAMS is false.
|
// LOCAL_STDOUT is definitely None since LOCAL_STREAMS is false.
|
||||||
@ -950,6 +962,17 @@ pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clone_io() -> (Option<Box<dyn LocalOutput>>, Option<Box<dyn LocalOutput>>) {
|
||||||
|
LOCAL_STDOUT.with(|stdout| {
|
||||||
|
LOCAL_STDERR.with(|stderr| {
|
||||||
|
(
|
||||||
|
stdout.borrow().as_ref().map(|o| o.clone_box()),
|
||||||
|
stderr.borrow().as_ref().map(|o| o.clone_box()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Write `args` to output stream `local_s` if possible, `global_s`
|
/// Write `args` to output stream `local_s` if possible, `global_s`
|
||||||
/// otherwise. `label` identifies the stream in a panic message.
|
/// otherwise. `label` identifies the stream in a panic message.
|
||||||
///
|
///
|
||||||
@ -962,7 +985,7 @@ pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
|
|||||||
/// However, if the actual I/O causes an error, this function does panic.
|
/// However, if the actual I/O causes an error, this function does panic.
|
||||||
fn print_to<T>(
|
fn print_to<T>(
|
||||||
args: fmt::Arguments<'_>,
|
args: fmt::Arguments<'_>,
|
||||||
local_s: &'static LocalKey<RefCell<Option<Box<dyn Write + Send>>>>,
|
local_s: &'static LocalKey<RefCell<Option<Box<dyn LocalOutput>>>>,
|
||||||
global_s: fn() -> T,
|
global_s: fn() -> T,
|
||||||
label: &str,
|
label: &str,
|
||||||
) where
|
) where
|
||||||
|
@ -220,7 +220,7 @@ fn default_hook(info: &PanicInfo<'_>) {
|
|||||||
|
|
||||||
if let Some(mut local) = set_panic(None) {
|
if let Some(mut local) = set_panic(None) {
|
||||||
// NB. In `cfg(test)` this uses the forwarding impl
|
// NB. In `cfg(test)` this uses the forwarding impl
|
||||||
// for `Box<dyn (::realstd::io::Write) + Send>`.
|
// for `dyn ::realstd::io::LocalOutput`.
|
||||||
write(&mut local);
|
write(&mut local);
|
||||||
set_panic(Some(local));
|
set_panic(Some(local));
|
||||||
} else if let Some(mut out) = panic_output() {
|
} else if let Some(mut out) = panic_output() {
|
||||||
|
@ -457,11 +457,16 @@ pub unsafe fn spawn_unchecked<'a, F, T>(self, f: F) -> io::Result<JoinHandle<T>>
|
|||||||
let my_packet: Arc<UnsafeCell<Option<Result<T>>>> = Arc::new(UnsafeCell::new(None));
|
let my_packet: Arc<UnsafeCell<Option<Result<T>>>> = Arc::new(UnsafeCell::new(None));
|
||||||
let their_packet = my_packet.clone();
|
let their_packet = my_packet.clone();
|
||||||
|
|
||||||
|
let (stdout, stderr) = crate::io::clone_io();
|
||||||
|
|
||||||
let main = move || {
|
let main = move || {
|
||||||
if let Some(name) = their_thread.cname() {
|
if let Some(name) = their_thread.cname() {
|
||||||
imp::Thread::set_name(name);
|
imp::Thread::set_name(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate::io::set_print(stdout);
|
||||||
|
crate::io::set_panic(stderr);
|
||||||
|
|
||||||
// SAFETY: the stack guard passed is the one for the current thread.
|
// SAFETY: the stack guard passed is the one for the current thread.
|
||||||
// This means the current thread's stack and the new thread's stack
|
// This means the current thread's stack and the new thread's stack
|
||||||
// are properly set and protected from each other.
|
// are properly set and protected from each other.
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Sink(Arc<Mutex<Vec<u8>>>);
|
pub struct Sink(Arc<Mutex<Vec<u8>>>);
|
||||||
|
|
||||||
impl Sink {
|
impl Sink {
|
||||||
@ -14,6 +15,12 @@ pub fn new_boxed(data: &Arc<Mutex<Vec<u8>>>) -> Box<Self> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl io::LocalOutput for Sink {
|
||||||
|
fn clone_box(&self) -> Box<dyn io::LocalOutput> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Write for Sink {
|
impl Write for Sink {
|
||||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||||
Write::write(&mut *self.0.lock().unwrap(), data)
|
Write::write(&mut *self.0.lock().unwrap(), data)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io::set_panic;
|
use std::io::{self, set_panic, LocalOutput, Write};
|
||||||
|
|
||||||
pub struct A;
|
pub struct A;
|
||||||
|
|
||||||
@ -15,8 +15,23 @@ fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Sink;
|
||||||
|
impl Write for Sink {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl LocalOutput for Sink {
|
||||||
|
fn clone_box(&self) -> Box<dyn LocalOutput> {
|
||||||
|
Box::new(Sink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
set_panic(Some(Box::new(Vec::new())));
|
set_panic(Some(Box::new(Sink)));
|
||||||
assert!(std::panic::catch_unwind(|| {
|
assert!(std::panic::catch_unwind(|| {
|
||||||
eprintln!("{}", A);
|
eprintln!("{}", A);
|
||||||
})
|
})
|
||||||
|
30
src/test/ui/test-thread-capture.rs
Normal file
30
src/test/ui/test-thread-capture.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// compile-flags: --test
|
||||||
|
// run-fail
|
||||||
|
// run-flags: --test-threads=1
|
||||||
|
// check-run-results
|
||||||
|
// exec-env:RUST_BACKTRACE=0
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thready_pass() {
|
||||||
|
println!("fee");
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
println!("fie");
|
||||||
|
println!("foe");
|
||||||
|
})
|
||||||
|
.join()
|
||||||
|
.unwrap();
|
||||||
|
println!("fum");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thready_fail() {
|
||||||
|
println!("fee");
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
println!("fie");
|
||||||
|
println!("foe");
|
||||||
|
})
|
||||||
|
.join()
|
||||||
|
.unwrap();
|
||||||
|
println!("fum");
|
||||||
|
panic!();
|
||||||
|
}
|
21
src/test/ui/test-thread-capture.run.stdout
Normal file
21
src/test/ui/test-thread-capture.run.stdout
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
running 2 tests
|
||||||
|
test thready_fail ... FAILED
|
||||||
|
test thready_pass ... ok
|
||||||
|
|
||||||
|
failures:
|
||||||
|
|
||||||
|
---- thready_fail stdout ----
|
||||||
|
fee
|
||||||
|
fie
|
||||||
|
foe
|
||||||
|
fum
|
||||||
|
thread 'main' panicked at 'explicit panic', $DIR/test-thread-capture.rs:29:5
|
||||||
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||||
|
|
||||||
|
|
||||||
|
failures:
|
||||||
|
thready_fail
|
||||||
|
|
||||||
|
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||||
|
|
30
src/test/ui/test-thread-nocapture.rs
Normal file
30
src/test/ui/test-thread-nocapture.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// compile-flags: --test
|
||||||
|
// run-fail
|
||||||
|
// run-flags: --test-threads=1 --nocapture
|
||||||
|
// check-run-results
|
||||||
|
// exec-env:RUST_BACKTRACE=0
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thready_pass() {
|
||||||
|
println!("fee");
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
println!("fie");
|
||||||
|
println!("foe");
|
||||||
|
})
|
||||||
|
.join()
|
||||||
|
.unwrap();
|
||||||
|
println!("fum");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thready_fail() {
|
||||||
|
println!("fee");
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
println!("fie");
|
||||||
|
println!("foe");
|
||||||
|
})
|
||||||
|
.join()
|
||||||
|
.unwrap();
|
||||||
|
println!("fum");
|
||||||
|
panic!();
|
||||||
|
}
|
2
src/test/ui/test-thread-nocapture.run.stderr
Normal file
2
src/test/ui/test-thread-nocapture.run.stderr
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
thread 'main' panicked at 'explicit panic', $DIR/test-thread-nocapture.rs:29:5
|
||||||
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
20
src/test/ui/test-thread-nocapture.run.stdout
Normal file
20
src/test/ui/test-thread-nocapture.run.stdout
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
running 2 tests
|
||||||
|
test thready_fail ... fee
|
||||||
|
fie
|
||||||
|
foe
|
||||||
|
fum
|
||||||
|
FAILED
|
||||||
|
test thready_pass ... fee
|
||||||
|
fie
|
||||||
|
foe
|
||||||
|
fum
|
||||||
|
ok
|
||||||
|
|
||||||
|
failures:
|
||||||
|
|
||||||
|
failures:
|
||||||
|
thready_fail
|
||||||
|
|
||||||
|
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||||
|
|
@ -16,6 +16,11 @@ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
|||||||
}
|
}
|
||||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||||
}
|
}
|
||||||
|
impl io::LocalOutput for Sink {
|
||||||
|
fn clone_box(&self) -> Box<dyn io::LocalOutput> {
|
||||||
|
Box::new(Sink(self.0.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let data = Arc::new(Mutex::new(Vec::new()));
|
let data = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
Loading…
Reference in New Issue
Block a user