From b3db5cd46c6941563ae8792cf61160c5793d397a Mon Sep 17 00:00:00 2001 From: Taylor Yu Date: Thu, 1 Jul 2021 18:05:24 -0500 Subject: [PATCH 1/2] add owned locked stdio handles Add stderr_locked, stdin_locked, and stdout_locked free functions to obtain owned locked stdio handles in a single step. Also add into_lock methods to consume a stdio handle and return an owned lock. These methods will make it easier to use locked stdio handles without having to deal with lifetime problems or keeping bindings to the unlocked handles around. --- library/std/src/io/mod.rs | 4 + library/std/src/io/stdio.rs | 260 +++++++++++++++++++++++++++++- library/std/src/io/stdio/tests.rs | 134 +++++++++++++++ 3 files changed, 397 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 00b85604a3f67..9e8996a3e5630 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -277,8 +277,12 @@ pub use self::error::{Error, ErrorKind, Result}; pub use self::stdio::set_output_capture; #[stable(feature = "rust1", since = "1.0.0")] pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout}; +#[unstable(feature = "stdio_locked", issue = "none")] +pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::stdio::{StderrLock, StdinLock, StdoutLock}; +#[unstable(feature = "stdio_locked", issue = "none")] +pub use self::stdio::{StderrOwnedLock, StdinOwnedLock, StdoutOwnedLock}; #[unstable(feature = "print_internals", issue = "none")] pub use self::stdio::{_eprint, _print}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 2b0d2b7e0be23..026d278126311 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -261,6 +261,21 @@ pub struct StdinLock<'a> { inner: MutexGuard<'a, BufReader>, } +/// Owned locked [`Stdin`] handle, returned by [`Stdin::into_lock`] and +/// [`io::stdin_locked`]. +/// +/// This is exactly like [`StdinLock`], except that it can outlive the +/// [`Stdin`] handle that was used to create it. See the [`StdinLock`] +/// documentation for more details. +/// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return +/// an error. +#[unstable(feature = "stdio_locked", issue = "none")] +pub type StdinOwnedLock = StdinLock<'static>; + /// Constructs a new handle to the standard input of the current process. /// /// Each handle returned is a reference to a shared global buffer whose access @@ -310,6 +325,48 @@ pub fn stdin() -> Stdin { } } +/// Constructs a new locked handle to the standard input of the current +/// process. +/// +/// Each handle returned is a guard granting locked access to a shared +/// global buffer whose access is synchronized via a mutex. If you need +/// more explicit control over locking, for example, in a multi-threaded +/// program, use the [`io::stdin`] function to obtain an unlocked handle, +/// along with the [`Stdin::lock`] method. +/// +/// The lock is released when the returned guard goes out of scope. The +/// returned guard also implements the [`Read`] and [`BufRead`] traits for +/// accessing the underlying data. +/// +/// **Note**: The mutex locked by this handle is not reentrant. Even in a +/// single-threaded program, calling other code that accesses [`Stdin`] +/// could cause a deadlock or panic, if this locked handle is held across +/// that call. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(stdio_locked)] +/// use std::io::{self, Read}; +/// +/// fn main() -> io::Result<()> { +/// let mut buffer = String::new(); +/// let mut handle = io::stdin_locked(); +/// +/// handle.read_to_string(&mut buffer)?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "stdio_locked", issue = "none")] +pub fn stdin_locked() -> StdinOwnedLock { + stdin().into_lock() +} + impl Stdin { /// Locks this handle to the standard input stream, returning a readable /// guard. @@ -334,7 +391,7 @@ impl Stdin { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> StdinLock<'_> { - StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } + self.lock_any() } /// Locks this handle and reads a line of input, appending it to the specified buffer. @@ -367,6 +424,43 @@ impl Stdin { pub fn read_line(&self, buf: &mut String) -> io::Result { self.lock().read_line(buf) } + + // Locks this handle with any lifetime. This depends on the + // implementation detail that the underlying `Mutex` is static. + fn lock_any<'a>(&self) -> StdinLock<'a> { + StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } + } + + /// Consumes this handle to the standard input stream, locking the + /// shared global buffer associated with the stream and returning a + /// readable guard. + /// + /// The lock is released when the returned guard goes out of scope. The + /// returned guard also implements the [`Read`] and [`BufRead`] traits + /// for accessing the underlying data. + /// + /// It is often simpler to directly get a locked handle using the + /// [`stdin_locked`] function instead, unless nearby code also needs to + /// use an unlocked handle. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(stdio_locked)] + /// use std::io::{self, Read}; + /// + /// fn main() -> io::Result<()> { + /// let mut buffer = String::new(); + /// let mut handle = io::stdin().into_lock(); + /// + /// handle.read_to_string(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "stdio_locked", issue = "none")] + pub fn into_lock(self) -> StdinOwnedLock { + self.lock_any() + } } #[stable(feature = "std_debug", since = "1.16.0")] @@ -507,6 +601,20 @@ pub struct StdoutLock<'a> { inner: ReentrantMutexGuard<'a, RefCell>>, } +/// Owned locked [`Stdout`] handle, returned by [`Stdout::into_lock`] and +/// [`io::stdout_locked`]. +/// +/// This is exactly like [`StdoutLock`], except that it can outlive the +/// [`Stdout`] handle that was used to create it. See the [`StdoutLock`] +/// documentation for more details. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +#[unstable(feature = "stdio_locked", issue = "none")] +pub type StdoutOwnedLock = StdoutLock<'static>; + static STDOUT: SyncOnceCell>>> = SyncOnceCell::new(); /// Constructs a new handle to the standard output of the current process. @@ -558,6 +666,42 @@ pub fn stdout() -> Stdout { } } +/// Constructs a new locked handle to the standard output of the current +/// process. +/// +/// Each handle returned is a guard granting locked access to a shared +/// global buffer whose access is synchronized via a mutex. If you need +/// more explicit control over locking, for example, in a multi-threaded +/// program, use the [`io::stdout`] function to obtain an unlocked handle, +/// along with the [`Stdout::lock`] method. +/// +/// The lock is released when the returned guard goes out of scope. The +/// returned guard also implements the [`Write`] trait for writing data. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(stdio_locked)] +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// let mut handle = io::stdout_locked(); +/// +/// handle.write_all(b"hello world")?; +/// +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "stdio_locked", issue = "none")] +pub fn stdout_locked() -> StdoutLock<'static> { + stdout().into_lock() +} + pub fn cleanup() { if let Some(instance) = STDOUT.get() { // Flush the data and disable buffering during shutdown @@ -595,8 +739,45 @@ impl Stdout { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> StdoutLock<'_> { + self.lock_any() + } + + // Locks this handle with any lifetime. This depends on the + // implementation detail that the underlying `ReentrantMutex` is + // static. + fn lock_any<'a>(&self) -> StdoutLock<'a> { StdoutLock { inner: self.inner.lock() } } + + /// Consumes this handle to the standard output stream, locking the + /// shared global buffer associated with the stream and returning a + /// writable guard. + /// + /// The lock is released when the returned lock goes out of scope. The + /// returned guard also implements the [`Write`] trait for writing data. + /// + /// It is often simpler to directly get a locked handle using the + /// [`io::stdout_locked`] function instead, unless nearby code also + /// needs to use an unlocked handle. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(stdio_locked)] + /// use std::io::{self, Write}; + /// + /// fn main() -> io::Result<()> { + /// let mut handle = io::stdout().into_lock(); + /// + /// handle.write_all(b"hello world")?; + /// + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "stdio_locked", issue = "none")] + pub fn into_lock(self) -> StdoutOwnedLock { + self.lock_any() + } } #[stable(feature = "std_debug", since = "1.16.0")] @@ -717,6 +898,20 @@ pub struct StderrLock<'a> { inner: ReentrantMutexGuard<'a, RefCell>, } +/// Owned locked [`Stderr`] handle, returned by [`Stderr::into_lock`] and +/// [`io::stderr_locked`]. +/// +/// This is exactly like [`StderrLock`], except that it can outlive the the +/// [`Stderr`] handle that was used to create it. See the [`StderrLock`] +/// documentation for more details. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +#[unstable(feature = "stdio_locked", issue = "none")] +pub type StderrOwnedLock = StderrLock<'static>; + /// Constructs a new handle to the standard error of the current process. /// /// This handle is not buffered. @@ -769,6 +964,35 @@ pub fn stderr() -> Stderr { } } +/// Constructs a new locked handle to the standard error of the current +/// process. +/// +/// This handle is not buffered. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Example +/// +/// ```no_run +/// #![feature(stdio_locked)] +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// let mut handle = io::stderr_locked(); +/// +/// handle.write_all(b"hello world")?; +/// +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "stdio_locked", issue = "none")] +pub fn stderr_locked() -> StderrOwnedLock { + stderr().into_lock() +} + impl Stderr { /// Locks this handle to the standard error stream, returning a writable /// guard. @@ -792,8 +1016,42 @@ impl Stderr { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> StderrLock<'_> { + self.lock_any() + } + + // Locks this handle with any lifetime. This depends on the + // implementation detail that the underlying `ReentrantMutex` is + // static. + fn lock_any<'a>(&self) -> StderrLock<'a> { StderrLock { inner: self.inner.lock() } } + + /// Locks and consumes this handle to the standard error stream, + /// returning a writable guard. + /// + /// The lock is released when the returned guard goes out of scope. The + /// returned guard also implements the [`Write`] trait for writing + /// data. + /// + /// # Examples + /// + /// ``` + /// #![feature(stdio_locked)] + /// use std::io::{self, Write}; + /// + /// fn foo() -> io::Result<()> { + /// let stderr = io::stderr(); + /// let mut handle = stderr.into_lock(); + /// + /// handle.write_all(b"hello world")?; + /// + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "stdio_locked", issue = "none")] + pub fn into_lock(self) -> StderrOwnedLock { + self.lock_any() + } } #[stable(feature = "std_debug", since = "1.16.0")] diff --git a/library/std/src/io/stdio/tests.rs b/library/std/src/io/stdio/tests.rs index 04af500268f97..d84537e54de89 100644 --- a/library/std/src/io/stdio/tests.rs +++ b/library/std/src/io/stdio/tests.rs @@ -1,5 +1,6 @@ use super::*; use crate::panic::{RefUnwindSafe, UnwindSafe}; +use crate::sync::mpsc::sync_channel; use crate::thread; #[test] @@ -45,3 +46,136 @@ fn panic_doesnt_poison() { let _a = stderr(); let _a = _a.lock(); } + +#[test] +fn stderr_owned_lock_static() { + assert_static::(); +} +#[test] +fn stdin_owned_lock_static() { + assert_static::(); +} +#[test] +fn stdout_owned_lock_static() { + assert_static::(); +} + +fn assert_static() {} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_lock_stderr() { + test_lock(stderr, stderr_locked); +} +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_lock_stdin() { + test_lock(stdin, stdin_locked); +} +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_lock_stdout() { + test_lock(stdout, stdout_locked); +} + +// Helper trait to make lock testing function generic. +trait Stdio<'a>: 'static +where + Self::Lock: 'a, +{ + type Lock; + fn lock(&'a self) -> Self::Lock; +} +impl<'a> Stdio<'a> for Stderr { + type Lock = StderrLock<'a>; + fn lock(&'a self) -> StderrLock<'a> { + self.lock() + } +} +impl<'a> Stdio<'a> for Stdin { + type Lock = StdinLock<'a>; + fn lock(&'a self) -> StdinLock<'a> { + self.lock() + } +} +impl<'a> Stdio<'a> for Stdout { + type Lock = StdoutLock<'a>; + fn lock(&'a self) -> StdoutLock<'a> { + self.lock() + } +} + +// Helper trait to make lock testing function generic. +trait StdioOwnedLock: 'static {} +impl StdioOwnedLock for StderrOwnedLock {} +impl StdioOwnedLock for StdinOwnedLock {} +impl StdioOwnedLock for StdoutOwnedLock {} + +// Tests locking on stdio handles by starting two threads and checking that +// they block each other appropriately. +fn test_lock(get_handle: fn() -> T, get_locked: fn() -> U) +where + T: for<'a> Stdio<'a>, + U: StdioOwnedLock, +{ + // State enum to track different phases of the test, primarily when + // each lock is acquired and released. + #[derive(Debug, PartialEq)] + enum State { + Start1, + Acquire1, + Start2, + Release1, + Acquire2, + Release2, + } + use State::*; + // Logging vector to be checked to make sure lock acquisitions and + // releases happened in the correct order. + let log = Arc::new(Mutex::new(Vec::new())); + let ((tx1, rx1), (tx2, rx2)) = (sync_channel(0), sync_channel(0)); + let th1 = { + let (log, tx) = (Arc::clone(&log), tx1); + thread::spawn(move || { + log.lock().unwrap().push(Start1); + let handle = get_handle(); + { + let locked = handle.lock(); + log.lock().unwrap().push(Acquire1); + tx.send(Acquire1).unwrap(); // notify of acquisition + tx.send(Release1).unwrap(); // wait for release command + log.lock().unwrap().push(Release1); + } + tx.send(Acquire1).unwrap(); // wait for th2 acquire + { + let locked = handle.lock(); + log.lock().unwrap().push(Acquire1); + } + log.lock().unwrap().push(Release1); + }) + }; + let th2 = { + let (log, tx) = (Arc::clone(&log), tx2); + thread::spawn(move || { + tx.send(Start2).unwrap(); // wait for start command + let locked = get_locked(); + log.lock().unwrap().push(Acquire2); + tx.send(Acquire2).unwrap(); // notify of acquisition + tx.send(Release2).unwrap(); // wait for release command + log.lock().unwrap().push(Release2); + }) + }; + assert_eq!(rx1.recv().unwrap(), Acquire1); // wait for th1 acquire + log.lock().unwrap().push(Start2); + assert_eq!(rx2.recv().unwrap(), Start2); // block th2 + assert_eq!(rx1.recv().unwrap(), Release1); // release th1 + assert_eq!(rx2.recv().unwrap(), Acquire2); // wait for th2 acquire + assert_eq!(rx1.recv().unwrap(), Acquire1); // block th1 + assert_eq!(rx2.recv().unwrap(), Release2); // release th2 + th2.join().unwrap(); + th1.join().unwrap(); + assert_eq!( + *log.lock().unwrap(), + [Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1] + ); +} From c58ceb7a42b5e06e2d7ba5fe727b05af5d073d3e Mon Sep 17 00:00:00 2001 From: Taylor Yu Date: Fri, 2 Jul 2021 15:56:56 -0500 Subject: [PATCH 2/2] stdio_locked: updates based on feedback Rename methods to `into_locked`. Remove type aliases for owned locks. --- library/std/src/io/mod.rs | 2 - library/std/src/io/stdio.rs | 65 ++++++------------------------- library/std/src/io/stdio/tests.rs | 21 ++-------- 3 files changed, 14 insertions(+), 74 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 9e8996a3e5630..acd4e0d79892a 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -281,8 +281,6 @@ pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout}; pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::stdio::{StderrLock, StdinLock, StdoutLock}; -#[unstable(feature = "stdio_locked", issue = "none")] -pub use self::stdio::{StderrOwnedLock, StdinOwnedLock, StdoutOwnedLock}; #[unstable(feature = "print_internals", issue = "none")] pub use self::stdio::{_eprint, _print}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 026d278126311..293f0e31ce050 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -261,21 +261,6 @@ pub struct StdinLock<'a> { inner: MutexGuard<'a, BufReader>, } -/// Owned locked [`Stdin`] handle, returned by [`Stdin::into_lock`] and -/// [`io::stdin_locked`]. -/// -/// This is exactly like [`StdinLock`], except that it can outlive the -/// [`Stdin`] handle that was used to create it. See the [`StdinLock`] -/// documentation for more details. -/// -/// ### Note: Windows Portability Consideration -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return -/// an error. -#[unstable(feature = "stdio_locked", issue = "none")] -pub type StdinOwnedLock = StdinLock<'static>; - /// Constructs a new handle to the standard input of the current process. /// /// Each handle returned is a reference to a shared global buffer whose access @@ -363,8 +348,8 @@ pub fn stdin() -> Stdin { /// } /// ``` #[unstable(feature = "stdio_locked", issue = "none")] -pub fn stdin_locked() -> StdinOwnedLock { - stdin().into_lock() +pub fn stdin_locked() -> StdinLock<'static> { + stdin().into_locked() } impl Stdin { @@ -451,14 +436,14 @@ impl Stdin { /// /// fn main() -> io::Result<()> { /// let mut buffer = String::new(); - /// let mut handle = io::stdin().into_lock(); + /// let mut handle = io::stdin().into_locked(); /// /// handle.read_to_string(&mut buffer)?; /// Ok(()) /// } /// ``` #[unstable(feature = "stdio_locked", issue = "none")] - pub fn into_lock(self) -> StdinOwnedLock { + pub fn into_locked(self) -> StdinLock<'static> { self.lock_any() } } @@ -601,20 +586,6 @@ pub struct StdoutLock<'a> { inner: ReentrantMutexGuard<'a, RefCell>>, } -/// Owned locked [`Stdout`] handle, returned by [`Stdout::into_lock`] and -/// [`io::stdout_locked`]. -/// -/// This is exactly like [`StdoutLock`], except that it can outlive the -/// [`Stdout`] handle that was used to create it. See the [`StdoutLock`] -/// documentation for more details. -/// -/// ### Note: Windows Portability Consideration -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -#[unstable(feature = "stdio_locked", issue = "none")] -pub type StdoutOwnedLock = StdoutLock<'static>; - static STDOUT: SyncOnceCell>>> = SyncOnceCell::new(); /// Constructs a new handle to the standard output of the current process. @@ -699,7 +670,7 @@ pub fn stdout() -> Stdout { /// ``` #[unstable(feature = "stdio_locked", issue = "none")] pub fn stdout_locked() -> StdoutLock<'static> { - stdout().into_lock() + stdout().into_locked() } pub fn cleanup() { @@ -767,7 +738,7 @@ impl Stdout { /// use std::io::{self, Write}; /// /// fn main() -> io::Result<()> { - /// let mut handle = io::stdout().into_lock(); + /// let mut handle = io::stdout().into_locked(); /// /// handle.write_all(b"hello world")?; /// @@ -775,7 +746,7 @@ impl Stdout { /// } /// ``` #[unstable(feature = "stdio_locked", issue = "none")] - pub fn into_lock(self) -> StdoutOwnedLock { + pub fn into_locked(self) -> StdoutLock<'static> { self.lock_any() } } @@ -898,20 +869,6 @@ pub struct StderrLock<'a> { inner: ReentrantMutexGuard<'a, RefCell>, } -/// Owned locked [`Stderr`] handle, returned by [`Stderr::into_lock`] and -/// [`io::stderr_locked`]. -/// -/// This is exactly like [`StderrLock`], except that it can outlive the the -/// [`Stderr`] handle that was used to create it. See the [`StderrLock`] -/// documentation for more details. -/// -/// ### Note: Windows Portability Consideration -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -#[unstable(feature = "stdio_locked", issue = "none")] -pub type StderrOwnedLock = StderrLock<'static>; - /// Constructs a new handle to the standard error of the current process. /// /// This handle is not buffered. @@ -989,8 +946,8 @@ pub fn stderr() -> Stderr { /// } /// ``` #[unstable(feature = "stdio_locked", issue = "none")] -pub fn stderr_locked() -> StderrOwnedLock { - stderr().into_lock() +pub fn stderr_locked() -> StderrLock<'static> { + stderr().into_locked() } impl Stderr { @@ -1041,7 +998,7 @@ impl Stderr { /// /// fn foo() -> io::Result<()> { /// let stderr = io::stderr(); - /// let mut handle = stderr.into_lock(); + /// let mut handle = stderr.into_locked(); /// /// handle.write_all(b"hello world")?; /// @@ -1049,7 +1006,7 @@ impl Stderr { /// } /// ``` #[unstable(feature = "stdio_locked", issue = "none")] - pub fn into_lock(self) -> StderrOwnedLock { + pub fn into_locked(self) -> StderrLock<'static> { self.lock_any() } } diff --git a/library/std/src/io/stdio/tests.rs b/library/std/src/io/stdio/tests.rs index d84537e54de89..b1df6b7131c87 100644 --- a/library/std/src/io/stdio/tests.rs +++ b/library/std/src/io/stdio/tests.rs @@ -47,21 +47,6 @@ fn panic_doesnt_poison() { let _a = _a.lock(); } -#[test] -fn stderr_owned_lock_static() { - assert_static::(); -} -#[test] -fn stdin_owned_lock_static() { - assert_static::(); -} -#[test] -fn stdout_owned_lock_static() { - assert_static::(); -} - -fn assert_static() {} - #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn test_lock_stderr() { @@ -107,9 +92,9 @@ impl<'a> Stdio<'a> for Stdout { // Helper trait to make lock testing function generic. trait StdioOwnedLock: 'static {} -impl StdioOwnedLock for StderrOwnedLock {} -impl StdioOwnedLock for StdinOwnedLock {} -impl StdioOwnedLock for StdoutOwnedLock {} +impl StdioOwnedLock for StderrLock<'static> {} +impl StdioOwnedLock for StdinLock<'static> {} +impl StdioOwnedLock for StdoutLock<'static> {} // Tests locking on stdio handles by starting two threads and checking that // they block each other appropriately.