Skip to content

Commit b3db5cd

Browse files
committed
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.
1 parent 868c702 commit b3db5cd

File tree

3 files changed

+397
-1
lines changed

3 files changed

+397
-1
lines changed

library/std/src/io/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,12 @@ 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};
284+
#[unstable(feature = "stdio_locked", issue = "none")]
285+
pub use self::stdio::{StderrOwnedLock, StdinOwnedLock, StdoutOwnedLock};
282286
#[unstable(feature = "print_internals", issue = "none")]
283287
pub use self::stdio::{_eprint, _print};
284288
#[stable(feature = "rust1", since = "1.0.0")]

library/std/src/io/stdio.rs

Lines changed: 259 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,21 @@ pub struct StdinLock<'a> {
261261
inner: MutexGuard<'a, BufReader<StdinRaw>>,
262262
}
263263

264+
/// Owned locked [`Stdin`] handle, returned by [`Stdin::into_lock`] and
265+
/// [`io::stdin_locked`].
266+
///
267+
/// This is exactly like [`StdinLock`], except that it can outlive the
268+
/// [`Stdin`] handle that was used to create it. See the [`StdinLock`]
269+
/// documentation for more details.
270+
///
271+
/// ### Note: Windows Portability Consideration
272+
///
273+
/// When operating in a console, the Windows implementation of this stream does not support
274+
/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return
275+
/// an error.
276+
#[unstable(feature = "stdio_locked", issue = "none")]
277+
pub type StdinOwnedLock = StdinLock<'static>;
278+
264279
/// Constructs a new handle to the standard input of the current process.
265280
///
266281
/// Each handle returned is a reference to a shared global buffer whose access
@@ -310,6 +325,48 @@ pub fn stdin() -> Stdin {
310325
}
311326
}
312327

328+
/// Constructs a new locked handle to the standard input of the current
329+
/// process.
330+
///
331+
/// Each handle returned is a guard granting locked access to a shared
332+
/// global buffer whose access is synchronized via a mutex. If you need
333+
/// more explicit control over locking, for example, in a multi-threaded
334+
/// program, use the [`io::stdin`] function to obtain an unlocked handle,
335+
/// along with the [`Stdin::lock`] method.
336+
///
337+
/// The lock is released when the returned guard goes out of scope. The
338+
/// returned guard also implements the [`Read`] and [`BufRead`] traits for
339+
/// accessing the underlying data.
340+
///
341+
/// **Note**: The mutex locked by this handle is not reentrant. Even in a
342+
/// single-threaded program, calling other code that accesses [`Stdin`]
343+
/// could cause a deadlock or panic, if this locked handle is held across
344+
/// that call.
345+
///
346+
/// ### Note: Windows Portability Consideration
347+
/// When operating in a console, the Windows implementation of this stream does not support
348+
/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return
349+
/// an error.
350+
///
351+
/// # Examples
352+
///
353+
/// ```no_run
354+
/// #![feature(stdio_locked)]
355+
/// use std::io::{self, Read};
356+
///
357+
/// fn main() -> io::Result<()> {
358+
/// let mut buffer = String::new();
359+
/// let mut handle = io::stdin_locked();
360+
///
361+
/// handle.read_to_string(&mut buffer)?;
362+
/// Ok(())
363+
/// }
364+
/// ```
365+
#[unstable(feature = "stdio_locked", issue = "none")]
366+
pub fn stdin_locked() -> StdinOwnedLock {
367+
stdin().into_lock()
368+
}
369+
313370
impl Stdin {
314371
/// Locks this handle to the standard input stream, returning a readable
315372
/// guard.
@@ -334,7 +391,7 @@ impl Stdin {
334391
/// ```
335392
#[stable(feature = "rust1", since = "1.0.0")]
336393
pub fn lock(&self) -> StdinLock<'_> {
337-
StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) }
394+
self.lock_any()
338395
}
339396

340397
/// Locks this handle and reads a line of input, appending it to the specified buffer.
@@ -367,6 +424,43 @@ impl Stdin {
367424
pub fn read_line(&self, buf: &mut String) -> io::Result<usize> {
368425
self.lock().read_line(buf)
369426
}
427+
428+
// Locks this handle with any lifetime. This depends on the
429+
// implementation detail that the underlying `Mutex` is static.
430+
fn lock_any<'a>(&self) -> StdinLock<'a> {
431+
StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) }
432+
}
433+
434+
/// Consumes this handle to the standard input stream, locking the
435+
/// shared global buffer associated with the stream and returning a
436+
/// readable guard.
437+
///
438+
/// The lock is released when the returned guard goes out of scope. The
439+
/// returned guard also implements the [`Read`] and [`BufRead`] traits
440+
/// for accessing the underlying data.
441+
///
442+
/// It is often simpler to directly get a locked handle using the
443+
/// [`stdin_locked`] function instead, unless nearby code also needs to
444+
/// use an unlocked handle.
445+
///
446+
/// # Examples
447+
///
448+
/// ```no_run
449+
/// #![feature(stdio_locked)]
450+
/// use std::io::{self, Read};
451+
///
452+
/// fn main() -> io::Result<()> {
453+
/// let mut buffer = String::new();
454+
/// let mut handle = io::stdin().into_lock();
455+
///
456+
/// handle.read_to_string(&mut buffer)?;
457+
/// Ok(())
458+
/// }
459+
/// ```
460+
#[unstable(feature = "stdio_locked", issue = "none")]
461+
pub fn into_lock(self) -> StdinOwnedLock {
462+
self.lock_any()
463+
}
370464
}
371465

372466
#[stable(feature = "std_debug", since = "1.16.0")]
@@ -507,6 +601,20 @@ pub struct StdoutLock<'a> {
507601
inner: ReentrantMutexGuard<'a, RefCell<LineWriter<StdoutRaw>>>,
508602
}
509603

604+
/// Owned locked [`Stdout`] handle, returned by [`Stdout::into_lock`] and
605+
/// [`io::stdout_locked`].
606+
///
607+
/// This is exactly like [`StdoutLock`], except that it can outlive the
608+
/// [`Stdout`] handle that was used to create it. See the [`StdoutLock`]
609+
/// documentation for more details.
610+
///
611+
/// ### Note: Windows Portability Consideration
612+
/// When operating in a console, the Windows implementation of this stream does not support
613+
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
614+
/// an error.
615+
#[unstable(feature = "stdio_locked", issue = "none")]
616+
pub type StdoutOwnedLock = StdoutLock<'static>;
617+
510618
static STDOUT: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = SyncOnceCell::new();
511619

512620
/// Constructs a new handle to the standard output of the current process.
@@ -558,6 +666,42 @@ pub fn stdout() -> Stdout {
558666
}
559667
}
560668

669+
/// Constructs a new locked handle to the standard output of the current
670+
/// process.
671+
///
672+
/// Each handle returned is a guard granting locked access to a shared
673+
/// global buffer whose access is synchronized via a mutex. If you need
674+
/// more explicit control over locking, for example, in a multi-threaded
675+
/// program, use the [`io::stdout`] function to obtain an unlocked handle,
676+
/// along with the [`Stdout::lock`] method.
677+
///
678+
/// The lock is released when the returned guard goes out of scope. The
679+
/// returned guard also implements the [`Write`] trait for writing data.
680+
///
681+
/// ### Note: Windows Portability Consideration
682+
/// When operating in a console, the Windows implementation of this stream does not support
683+
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
684+
/// an error.
685+
///
686+
/// # Examples
687+
///
688+
/// ```no_run
689+
/// #![feature(stdio_locked)]
690+
/// use std::io::{self, Write};
691+
///
692+
/// fn main() -> io::Result<()> {
693+
/// let mut handle = io::stdout_locked();
694+
///
695+
/// handle.write_all(b"hello world")?;
696+
///
697+
/// Ok(())
698+
/// }
699+
/// ```
700+
#[unstable(feature = "stdio_locked", issue = "none")]
701+
pub fn stdout_locked() -> StdoutLock<'static> {
702+
stdout().into_lock()
703+
}
704+
561705
pub fn cleanup() {
562706
if let Some(instance) = STDOUT.get() {
563707
// Flush the data and disable buffering during shutdown
@@ -595,8 +739,45 @@ impl Stdout {
595739
/// ```
596740
#[stable(feature = "rust1", since = "1.0.0")]
597741
pub fn lock(&self) -> StdoutLock<'_> {
742+
self.lock_any()
743+
}
744+
745+
// Locks this handle with any lifetime. This depends on the
746+
// implementation detail that the underlying `ReentrantMutex` is
747+
// static.
748+
fn lock_any<'a>(&self) -> StdoutLock<'a> {
598749
StdoutLock { inner: self.inner.lock() }
599750
}
751+
752+
/// Consumes this handle to the standard output stream, locking the
753+
/// shared global buffer associated with the stream and returning a
754+
/// writable guard.
755+
///
756+
/// The lock is released when the returned lock goes out of scope. The
757+
/// returned guard also implements the [`Write`] trait for writing data.
758+
///
759+
/// It is often simpler to directly get a locked handle using the
760+
/// [`io::stdout_locked`] function instead, unless nearby code also
761+
/// needs to use an unlocked handle.
762+
///
763+
/// # Examples
764+
///
765+
/// ```no_run
766+
/// #![feature(stdio_locked)]
767+
/// use std::io::{self, Write};
768+
///
769+
/// fn main() -> io::Result<()> {
770+
/// let mut handle = io::stdout().into_lock();
771+
///
772+
/// handle.write_all(b"hello world")?;
773+
///
774+
/// Ok(())
775+
/// }
776+
/// ```
777+
#[unstable(feature = "stdio_locked", issue = "none")]
778+
pub fn into_lock(self) -> StdoutOwnedLock {
779+
self.lock_any()
780+
}
600781
}
601782

602783
#[stable(feature = "std_debug", since = "1.16.0")]
@@ -717,6 +898,20 @@ pub struct StderrLock<'a> {
717898
inner: ReentrantMutexGuard<'a, RefCell<StderrRaw>>,
718899
}
719900

901+
/// Owned locked [`Stderr`] handle, returned by [`Stderr::into_lock`] and
902+
/// [`io::stderr_locked`].
903+
///
904+
/// This is exactly like [`StderrLock`], except that it can outlive the the
905+
/// [`Stderr`] handle that was used to create it. See the [`StderrLock`]
906+
/// documentation for more details.
907+
///
908+
/// ### Note: Windows Portability Consideration
909+
/// When operating in a console, the Windows implementation of this stream does not support
910+
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
911+
/// an error.
912+
#[unstable(feature = "stdio_locked", issue = "none")]
913+
pub type StderrOwnedLock = StderrLock<'static>;
914+
720915
/// Constructs a new handle to the standard error of the current process.
721916
///
722917
/// This handle is not buffered.
@@ -769,6 +964,35 @@ pub fn stderr() -> Stderr {
769964
}
770965
}
771966

967+
/// Constructs a new locked handle to the standard error of the current
968+
/// process.
969+
///
970+
/// This handle is not buffered.
971+
///
972+
/// ### Note: Windows Portability Consideration
973+
/// When operating in a console, the Windows implementation of this stream does not support
974+
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
975+
/// an error.
976+
///
977+
/// # Example
978+
///
979+
/// ```no_run
980+
/// #![feature(stdio_locked)]
981+
/// use std::io::{self, Write};
982+
///
983+
/// fn main() -> io::Result<()> {
984+
/// let mut handle = io::stderr_locked();
985+
///
986+
/// handle.write_all(b"hello world")?;
987+
///
988+
/// Ok(())
989+
/// }
990+
/// ```
991+
#[unstable(feature = "stdio_locked", issue = "none")]
992+
pub fn stderr_locked() -> StderrOwnedLock {
993+
stderr().into_lock()
994+
}
995+
772996
impl Stderr {
773997
/// Locks this handle to the standard error stream, returning a writable
774998
/// guard.
@@ -792,8 +1016,42 @@ impl Stderr {
7921016
/// ```
7931017
#[stable(feature = "rust1", since = "1.0.0")]
7941018
pub fn lock(&self) -> StderrLock<'_> {
1019+
self.lock_any()
1020+
}
1021+
1022+
// Locks this handle with any lifetime. This depends on the
1023+
// implementation detail that the underlying `ReentrantMutex` is
1024+
// static.
1025+
fn lock_any<'a>(&self) -> StderrLock<'a> {
7951026
StderrLock { inner: self.inner.lock() }
7961027
}
1028+
1029+
/// Locks and consumes this handle to the standard error stream,
1030+
/// returning a writable guard.
1031+
///
1032+
/// The lock is released when the returned guard goes out of scope. The
1033+
/// returned guard also implements the [`Write`] trait for writing
1034+
/// data.
1035+
///
1036+
/// # Examples
1037+
///
1038+
/// ```
1039+
/// #![feature(stdio_locked)]
1040+
/// use std::io::{self, Write};
1041+
///
1042+
/// fn foo() -> io::Result<()> {
1043+
/// let stderr = io::stderr();
1044+
/// let mut handle = stderr.into_lock();
1045+
///
1046+
/// handle.write_all(b"hello world")?;
1047+
///
1048+
/// Ok(())
1049+
/// }
1050+
/// ```
1051+
#[unstable(feature = "stdio_locked", issue = "none")]
1052+
pub fn into_lock(self) -> StderrOwnedLock {
1053+
self.lock_any()
1054+
}
7971055
}
7981056

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

0 commit comments

Comments
 (0)