Skip to content

Commit a8b8558

Browse files
committed
Auto merge of #86799 - tlyu:stdio-locked, r=joshtriplett
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. Fixes #85383; enables #86412. r? `@joshtriplett` `@rustbot` label +A-io +C-enhancement +D-newcomer-roadblock +T-libs-api
2 parents 7014963 + c58ceb7 commit a8b8558

File tree

3 files changed

+337
-1
lines changed

3 files changed

+337
-1
lines changed

library/std/src/io/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ pub use self::error::{Error, ErrorKind, Result};
277277
pub use self::stdio::set_output_capture;
278278
#[stable(feature = "rust1", since = "1.0.0")]
279279
pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout};
280+
#[unstable(feature = "stdio_locked", issue = "none")]
281+
pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked};
280282
#[stable(feature = "rust1", since = "1.0.0")]
281283
pub use self::stdio::{StderrLock, StdinLock, StdoutLock};
282284
#[unstable(feature = "print_internals", issue = "none")]

library/std/src/io/stdio.rs

+216-1
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,48 @@ pub fn stdin() -> Stdin {
310310
}
311311
}
312312

313+
/// Constructs a new locked handle to the standard input of the current
314+
/// process.
315+
///
316+
/// Each handle returned is a guard granting locked access to a shared
317+
/// global buffer whose access is synchronized via a mutex. If you need
318+
/// more explicit control over locking, for example, in a multi-threaded
319+
/// program, use the [`io::stdin`] function to obtain an unlocked handle,
320+
/// along with the [`Stdin::lock`] method.
321+
///
322+
/// The lock is released when the returned guard goes out of scope. The
323+
/// returned guard also implements the [`Read`] and [`BufRead`] traits for
324+
/// accessing the underlying data.
325+
///
326+
/// **Note**: The mutex locked by this handle is not reentrant. Even in a
327+
/// single-threaded program, calling other code that accesses [`Stdin`]
328+
/// could cause a deadlock or panic, if this locked handle is held across
329+
/// that call.
330+
///
331+
/// ### Note: Windows Portability Consideration
332+
/// When operating in a console, the Windows implementation of this stream does not support
333+
/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return
334+
/// an error.
335+
///
336+
/// # Examples
337+
///
338+
/// ```no_run
339+
/// #![feature(stdio_locked)]
340+
/// use std::io::{self, Read};
341+
///
342+
/// fn main() -> io::Result<()> {
343+
/// let mut buffer = String::new();
344+
/// let mut handle = io::stdin_locked();
345+
///
346+
/// handle.read_to_string(&mut buffer)?;
347+
/// Ok(())
348+
/// }
349+
/// ```
350+
#[unstable(feature = "stdio_locked", issue = "none")]
351+
pub fn stdin_locked() -> StdinLock<'static> {
352+
stdin().into_locked()
353+
}
354+
313355
impl Stdin {
314356
/// Locks this handle to the standard input stream, returning a readable
315357
/// guard.
@@ -334,7 +376,7 @@ impl Stdin {
334376
/// ```
335377
#[stable(feature = "rust1", since = "1.0.0")]
336378
pub fn lock(&self) -> StdinLock<'_> {
337-
StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) }
379+
self.lock_any()
338380
}
339381

340382
/// Locks this handle and reads a line of input, appending it to the specified buffer.
@@ -367,6 +409,43 @@ impl Stdin {
367409
pub fn read_line(&self, buf: &mut String) -> io::Result<usize> {
368410
self.lock().read_line(buf)
369411
}
412+
413+
// Locks this handle with any lifetime. This depends on the
414+
// implementation detail that the underlying `Mutex` is static.
415+
fn lock_any<'a>(&self) -> StdinLock<'a> {
416+
StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) }
417+
}
418+
419+
/// Consumes this handle to the standard input stream, locking the
420+
/// shared global buffer associated with the stream and returning a
421+
/// readable guard.
422+
///
423+
/// The lock is released when the returned guard goes out of scope. The
424+
/// returned guard also implements the [`Read`] and [`BufRead`] traits
425+
/// for accessing the underlying data.
426+
///
427+
/// It is often simpler to directly get a locked handle using the
428+
/// [`stdin_locked`] function instead, unless nearby code also needs to
429+
/// use an unlocked handle.
430+
///
431+
/// # Examples
432+
///
433+
/// ```no_run
434+
/// #![feature(stdio_locked)]
435+
/// use std::io::{self, Read};
436+
///
437+
/// fn main() -> io::Result<()> {
438+
/// let mut buffer = String::new();
439+
/// let mut handle = io::stdin().into_locked();
440+
///
441+
/// handle.read_to_string(&mut buffer)?;
442+
/// Ok(())
443+
/// }
444+
/// ```
445+
#[unstable(feature = "stdio_locked", issue = "none")]
446+
pub fn into_locked(self) -> StdinLock<'static> {
447+
self.lock_any()
448+
}
370449
}
371450

372451
#[stable(feature = "std_debug", since = "1.16.0")]
@@ -558,6 +637,42 @@ pub fn stdout() -> Stdout {
558637
}
559638
}
560639

640+
/// Constructs a new locked handle to the standard output of the current
641+
/// process.
642+
///
643+
/// Each handle returned is a guard granting locked access to a shared
644+
/// global buffer whose access is synchronized via a mutex. If you need
645+
/// more explicit control over locking, for example, in a multi-threaded
646+
/// program, use the [`io::stdout`] function to obtain an unlocked handle,
647+
/// along with the [`Stdout::lock`] method.
648+
///
649+
/// The lock is released when the returned guard goes out of scope. The
650+
/// returned guard also implements the [`Write`] trait for writing data.
651+
///
652+
/// ### Note: Windows Portability Consideration
653+
/// When operating in a console, the Windows implementation of this stream does not support
654+
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
655+
/// an error.
656+
///
657+
/// # Examples
658+
///
659+
/// ```no_run
660+
/// #![feature(stdio_locked)]
661+
/// use std::io::{self, Write};
662+
///
663+
/// fn main() -> io::Result<()> {
664+
/// let mut handle = io::stdout_locked();
665+
///
666+
/// handle.write_all(b"hello world")?;
667+
///
668+
/// Ok(())
669+
/// }
670+
/// ```
671+
#[unstable(feature = "stdio_locked", issue = "none")]
672+
pub fn stdout_locked() -> StdoutLock<'static> {
673+
stdout().into_locked()
674+
}
675+
561676
pub fn cleanup() {
562677
if let Some(instance) = STDOUT.get() {
563678
// Flush the data and disable buffering during shutdown
@@ -595,8 +710,45 @@ impl Stdout {
595710
/// ```
596711
#[stable(feature = "rust1", since = "1.0.0")]
597712
pub fn lock(&self) -> StdoutLock<'_> {
713+
self.lock_any()
714+
}
715+
716+
// Locks this handle with any lifetime. This depends on the
717+
// implementation detail that the underlying `ReentrantMutex` is
718+
// static.
719+
fn lock_any<'a>(&self) -> StdoutLock<'a> {
598720
StdoutLock { inner: self.inner.lock() }
599721
}
722+
723+
/// Consumes this handle to the standard output stream, locking the
724+
/// shared global buffer associated with the stream and returning a
725+
/// writable guard.
726+
///
727+
/// The lock is released when the returned lock goes out of scope. The
728+
/// returned guard also implements the [`Write`] trait for writing data.
729+
///
730+
/// It is often simpler to directly get a locked handle using the
731+
/// [`io::stdout_locked`] function instead, unless nearby code also
732+
/// needs to use an unlocked handle.
733+
///
734+
/// # Examples
735+
///
736+
/// ```no_run
737+
/// #![feature(stdio_locked)]
738+
/// use std::io::{self, Write};
739+
///
740+
/// fn main() -> io::Result<()> {
741+
/// let mut handle = io::stdout().into_locked();
742+
///
743+
/// handle.write_all(b"hello world")?;
744+
///
745+
/// Ok(())
746+
/// }
747+
/// ```
748+
#[unstable(feature = "stdio_locked", issue = "none")]
749+
pub fn into_locked(self) -> StdoutLock<'static> {
750+
self.lock_any()
751+
}
600752
}
601753

602754
#[stable(feature = "std_debug", since = "1.16.0")]
@@ -769,6 +921,35 @@ pub fn stderr() -> Stderr {
769921
}
770922
}
771923

924+
/// Constructs a new locked handle to the standard error of the current
925+
/// process.
926+
///
927+
/// This handle is not buffered.
928+
///
929+
/// ### Note: Windows Portability Consideration
930+
/// When operating in a console, the Windows implementation of this stream does not support
931+
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
932+
/// an error.
933+
///
934+
/// # Example
935+
///
936+
/// ```no_run
937+
/// #![feature(stdio_locked)]
938+
/// use std::io::{self, Write};
939+
///
940+
/// fn main() -> io::Result<()> {
941+
/// let mut handle = io::stderr_locked();
942+
///
943+
/// handle.write_all(b"hello world")?;
944+
///
945+
/// Ok(())
946+
/// }
947+
/// ```
948+
#[unstable(feature = "stdio_locked", issue = "none")]
949+
pub fn stderr_locked() -> StderrLock<'static> {
950+
stderr().into_locked()
951+
}
952+
772953
impl Stderr {
773954
/// Locks this handle to the standard error stream, returning a writable
774955
/// guard.
@@ -792,8 +973,42 @@ impl Stderr {
792973
/// ```
793974
#[stable(feature = "rust1", since = "1.0.0")]
794975
pub fn lock(&self) -> StderrLock<'_> {
976+
self.lock_any()
977+
}
978+
979+
// Locks this handle with any lifetime. This depends on the
980+
// implementation detail that the underlying `ReentrantMutex` is
981+
// static.
982+
fn lock_any<'a>(&self) -> StderrLock<'a> {
795983
StderrLock { inner: self.inner.lock() }
796984
}
985+
986+
/// Locks and consumes this handle to the standard error stream,
987+
/// returning a writable guard.
988+
///
989+
/// The lock is released when the returned guard goes out of scope. The
990+
/// returned guard also implements the [`Write`] trait for writing
991+
/// data.
992+
///
993+
/// # Examples
994+
///
995+
/// ```
996+
/// #![feature(stdio_locked)]
997+
/// use std::io::{self, Write};
998+
///
999+
/// fn foo() -> io::Result<()> {
1000+
/// let stderr = io::stderr();
1001+
/// let mut handle = stderr.into_locked();
1002+
///
1003+
/// handle.write_all(b"hello world")?;
1004+
///
1005+
/// Ok(())
1006+
/// }
1007+
/// ```
1008+
#[unstable(feature = "stdio_locked", issue = "none")]
1009+
pub fn into_locked(self) -> StderrLock<'static> {
1010+
self.lock_any()
1011+
}
7971012
}
7981013

7991014
#[stable(feature = "std_debug", since = "1.16.0")]

0 commit comments

Comments
 (0)