Compare commits
3 Commits
72989d86a8
...
9413fba265
| Author | SHA1 | Date | |
|---|---|---|---|
| 9413fba265 | |||
| fd0770f813 | |||
| 10be5b301c |
4
crates/std/src/bstr.rs
Normal file
4
crates/std/src/bstr.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
//! The `ByteStr` and `ByteString` types and trait implementations.
|
||||
|
||||
#[unstable(feature = "bstr", issue = "134915")]
|
||||
pub use alloc_crate::bstr::{ByteStr, ByteString};
|
||||
3034
crates/std/src/io.rs
3034
crates/std/src/io.rs
File diff suppressed because it is too large
Load Diff
592
crates/std/src/io/buffered/bufreader.rs
Normal file
592
crates/std/src/io/buffered/bufreader.rs
Normal file
@@ -0,0 +1,592 @@
|
||||
mod buffer;
|
||||
|
||||
use buffer::Buffer;
|
||||
|
||||
use crate::fmt;
|
||||
use crate::io::{
|
||||
self, BorrowedCursor, BufRead, DEFAULT_BUF_SIZE, IoSliceMut, Read, Seek, SeekFrom, SizeHint,
|
||||
SpecReadByte, uninlined_slow_read_byte,
|
||||
};
|
||||
|
||||
/// The `BufReader<R>` struct adds buffering to any reader.
|
||||
///
|
||||
/// It can be excessively inefficient to work directly with a [`Read`] instance.
|
||||
/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`]
|
||||
/// results in a system call. A `BufReader<R>` performs large, infrequent reads on
|
||||
/// the underlying [`Read`] and maintains an in-memory buffer of the results.
|
||||
///
|
||||
/// `BufReader<R>` can improve the speed of programs that make *small* and
|
||||
/// *repeated* read calls to the same file or network socket. It does not
|
||||
/// help when reading very large amounts at once, or reading just one or a few
|
||||
/// times. It also provides no advantage when reading from a source that is
|
||||
/// already in memory, like a <code>[Vec]\<u8></code>.
|
||||
///
|
||||
/// When the `BufReader<R>` is dropped, the contents of its buffer will be
|
||||
/// discarded. Creating multiple instances of a `BufReader<R>` on the same
|
||||
/// stream can cause data loss. Reading from the underlying reader after
|
||||
/// unwrapping the `BufReader<R>` with [`BufReader::into_inner`] can also cause
|
||||
/// data loss.
|
||||
///
|
||||
/// [`TcpStream::read`]: crate::net::TcpStream::read
|
||||
/// [`TcpStream`]: crate::net::TcpStream
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f);
|
||||
///
|
||||
/// let mut line = String::new();
|
||||
/// let len = reader.read_line(&mut line)?;
|
||||
/// println!("First line is {len} bytes long");
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct BufReader<R: ?Sized> {
|
||||
buf: Buffer,
|
||||
inner: R,
|
||||
}
|
||||
|
||||
impl<R: Read> BufReader<R> {
|
||||
/// Creates a new `BufReader<R>` with a default buffer capacity. The default is currently 8 KiB,
|
||||
/// but may change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let reader = BufReader::new(f);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(inner: R) -> BufReader<R> {
|
||||
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||
}
|
||||
|
||||
pub(crate) fn try_new_buffer() -> io::Result<Buffer> {
|
||||
Buffer::try_with_capacity(DEFAULT_BUF_SIZE)
|
||||
}
|
||||
|
||||
pub(crate) fn with_buffer(inner: R, buf: Buffer) -> Self {
|
||||
Self { inner, buf }
|
||||
}
|
||||
|
||||
/// Creates a new `BufReader<R>` with the specified buffer capacity.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a buffer with ten bytes of capacity:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let reader = BufReader::with_capacity(10, f);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> {
|
||||
BufReader { inner, buf: Buffer::with_capacity(capacity) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + ?Sized> BufReader<R> {
|
||||
/// Attempt to look ahead `n` bytes.
|
||||
///
|
||||
/// `n` must be less than or equal to `capacity`.
|
||||
///
|
||||
/// The returned slice may be less than `n` bytes long if
|
||||
/// end of file is reached.
|
||||
///
|
||||
/// After calling this method, you may call [`consume`](BufRead::consume)
|
||||
/// with a value less than or equal to `n` to advance over some or all of
|
||||
/// the returned bytes.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(bufreader_peek)]
|
||||
/// use std::io::{Read, BufReader};
|
||||
///
|
||||
/// let mut bytes = &b"oh, hello there"[..];
|
||||
/// let mut rdr = BufReader::with_capacity(6, &mut bytes);
|
||||
/// assert_eq!(rdr.peek(2).unwrap(), b"oh");
|
||||
/// let mut buf = [0; 4];
|
||||
/// rdr.read(&mut buf[..]).unwrap();
|
||||
/// assert_eq!(&buf, b"oh, ");
|
||||
/// assert_eq!(rdr.peek(5).unwrap(), b"hello");
|
||||
/// let mut s = String::new();
|
||||
/// rdr.read_to_string(&mut s).unwrap();
|
||||
/// assert_eq!(&s, "hello there");
|
||||
/// assert_eq!(rdr.peek(1).unwrap().len(), 0);
|
||||
/// ```
|
||||
#[unstable(feature = "bufreader_peek", issue = "128405")]
|
||||
pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> {
|
||||
assert!(n <= self.capacity());
|
||||
while n > self.buf.buffer().len() {
|
||||
if self.buf.pos() > 0 {
|
||||
self.buf.backshift();
|
||||
}
|
||||
let new = self.buf.read_more(&mut self.inner)?;
|
||||
if new == 0 {
|
||||
// end of file, no more bytes to read
|
||||
return Ok(&self.buf.buffer()[..]);
|
||||
}
|
||||
debug_assert_eq!(self.buf.pos(), 0);
|
||||
}
|
||||
Ok(&self.buf.buffer()[..n])
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ?Sized> BufReader<R> {
|
||||
/// Gets a reference to the underlying reader.
|
||||
///
|
||||
/// It is inadvisable to directly read from the underlying reader.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f1 = File::open("log.txt")?;
|
||||
/// let reader = BufReader::new(f1);
|
||||
///
|
||||
/// let f2 = reader.get_ref();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_ref(&self) -> &R {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying reader.
|
||||
///
|
||||
/// It is inadvisable to directly read from the underlying reader.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f1 = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f1);
|
||||
///
|
||||
/// let f2 = reader.get_mut();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_mut(&mut self) -> &mut R {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Returns a reference to the internally buffered data.
|
||||
///
|
||||
/// Unlike [`fill_buf`], this will not attempt to fill the buffer if it is empty.
|
||||
///
|
||||
/// [`fill_buf`]: BufRead::fill_buf
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::{BufReader, BufRead};
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f);
|
||||
/// assert!(reader.buffer().is_empty());
|
||||
///
|
||||
/// if reader.fill_buf()?.len() > 0 {
|
||||
/// assert!(!reader.buffer().is_empty());
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "bufreader_buffer", since = "1.37.0")]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
self.buf.buffer()
|
||||
}
|
||||
|
||||
/// Returns the number of bytes the internal buffer can hold at once.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::{BufReader, BufRead};
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f);
|
||||
///
|
||||
/// let capacity = reader.capacity();
|
||||
/// let buffer = reader.fill_buf()?;
|
||||
/// assert!(buffer.len() <= capacity);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "buffered_io_capacity", since = "1.46.0")]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buf.capacity()
|
||||
}
|
||||
|
||||
/// Unwraps this `BufReader<R>`, returning the underlying reader.
|
||||
///
|
||||
/// Note that any leftover data in the internal buffer is lost. Therefore,
|
||||
/// a following read from the underlying reader may lead to data loss.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f1 = File::open("log.txt")?;
|
||||
/// let reader = BufReader::new(f1);
|
||||
///
|
||||
/// let f2 = reader.into_inner();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> R
|
||||
where
|
||||
R: Sized,
|
||||
{
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Invalidates all data in the internal buffer.
|
||||
#[inline]
|
||||
pub(in crate::io) fn discard_buffer(&mut self) {
|
||||
self.buf.discard_buffer()
|
||||
}
|
||||
}
|
||||
|
||||
// This is only used by a test which asserts that the initialization-tracking is correct.
|
||||
#[cfg(test)]
|
||||
impl<R: ?Sized> BufReader<R> {
|
||||
#[allow(missing_docs)]
|
||||
pub fn initialized(&self) -> usize {
|
||||
self.buf.initialized()
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ?Sized + Seek> BufReader<R> {
|
||||
/// Seeks relative to the current position. If the new position lies within the buffer,
|
||||
/// the buffer will not be flushed, allowing for more efficient seeks.
|
||||
/// This method does not return the location of the underlying reader, so the caller
|
||||
/// must track this information themselves if it is required.
|
||||
#[stable(feature = "bufreader_seek_relative", since = "1.53.0")]
|
||||
pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
let pos = self.buf.pos() as u64;
|
||||
if offset < 0 {
|
||||
if let Some(_) = pos.checked_sub((-offset) as u64) {
|
||||
self.buf.unconsume((-offset) as usize);
|
||||
return Ok(());
|
||||
}
|
||||
} else if let Some(new_pos) = pos.checked_add(offset as u64) {
|
||||
if new_pos <= self.buf.filled() as u64 {
|
||||
self.buf.consume(offset as usize);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
self.seek(SeekFrom::Current(offset)).map(drop)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> SpecReadByte for BufReader<R>
|
||||
where
|
||||
Self: Read,
|
||||
{
|
||||
#[inline]
|
||||
fn spec_read_byte(&mut self) -> Option<io::Result<u8>> {
|
||||
let mut byte = 0;
|
||||
if self.buf.consume_with(1, |claimed| byte = claimed[0]) {
|
||||
return Some(Ok(byte));
|
||||
}
|
||||
|
||||
// Fallback case, only reached once per buffer refill.
|
||||
uninlined_slow_read_byte(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: ?Sized + Read> Read for BufReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// If we don't have any buffered data and we're doing a massive read
|
||||
// (larger than our internal buffer), bypass our internal buffer
|
||||
// entirely.
|
||||
if self.buf.pos() == self.buf.filled() && buf.len() >= self.capacity() {
|
||||
self.discard_buffer();
|
||||
return self.inner.read(buf);
|
||||
}
|
||||
let mut rem = self.fill_buf()?;
|
||||
let nread = rem.read(buf)?;
|
||||
self.consume(nread);
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
// If we don't have any buffered data and we're doing a massive read
|
||||
// (larger than our internal buffer), bypass our internal buffer
|
||||
// entirely.
|
||||
if self.buf.pos() == self.buf.filled() && cursor.capacity() >= self.capacity() {
|
||||
self.discard_buffer();
|
||||
return self.inner.read_buf(cursor);
|
||||
}
|
||||
|
||||
let prev = cursor.written();
|
||||
|
||||
let mut rem = self.fill_buf()?;
|
||||
rem.read_buf(cursor.reborrow())?; // actually never fails
|
||||
|
||||
self.consume(cursor.written() - prev); //slice impl of read_buf known to never unfill buf
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Small read_exacts from a BufReader are extremely common when used with a deserializer.
|
||||
// The default implementation calls read in a loop, which results in surprisingly poor code
|
||||
// generation for the common path where the buffer has enough bytes to fill the passed-in
|
||||
// buffer.
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
if self.buf.consume_with(buf.len(), |claimed| buf.copy_from_slice(claimed)) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
crate::io::default_read_exact(self, buf)
|
||||
}
|
||||
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
if self.buf.consume_with(cursor.capacity(), |claimed| cursor.append(claimed)) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
crate::io::default_read_buf_exact(self, cursor)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
|
||||
if self.buf.pos() == self.buf.filled() && total_len >= self.capacity() {
|
||||
self.discard_buffer();
|
||||
return self.inner.read_vectored(bufs);
|
||||
}
|
||||
let mut rem = self.fill_buf()?;
|
||||
let nread = rem.read_vectored(bufs)?;
|
||||
|
||||
self.consume(nread);
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
self.inner.is_read_vectored()
|
||||
}
|
||||
|
||||
// The inner reader might have an optimized `read_to_end`. Drain our buffer and then
|
||||
// delegate to the inner implementation.
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let inner_buf = self.buffer();
|
||||
buf.try_reserve(inner_buf.len())?;
|
||||
buf.extend_from_slice(inner_buf);
|
||||
let nread = inner_buf.len();
|
||||
self.discard_buffer();
|
||||
Ok(nread + self.inner.read_to_end(buf)?)
|
||||
}
|
||||
|
||||
// The inner reader might have an optimized `read_to_end`. Drain our buffer and then
|
||||
// delegate to the inner implementation.
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
// In the general `else` case below we must read bytes into a side buffer, check
|
||||
// that they are valid UTF-8, and then append them to `buf`. This requires a
|
||||
// potentially large memcpy.
|
||||
//
|
||||
// If `buf` is empty--the most common case--we can leverage `append_to_string`
|
||||
// to read directly into `buf`'s internal byte buffer, saving an allocation and
|
||||
// a memcpy.
|
||||
if buf.is_empty() {
|
||||
// `append_to_string`'s safety relies on the buffer only being appended to since
|
||||
// it only checks the UTF-8 validity of new data. If there were existing content in
|
||||
// `buf` then an untrustworthy reader (i.e. `self.inner`) could not only append
|
||||
// bytes but also modify existing bytes and render them invalid. On the other hand,
|
||||
// if `buf` is empty then by definition any writes must be appends and
|
||||
// `append_to_string` will validate all of the new bytes.
|
||||
unsafe { crate::io::append_to_string(buf, |b| self.read_to_end(b)) }
|
||||
} else {
|
||||
// We cannot append our byte buffer directly onto the `buf` String as there could
|
||||
// be an incomplete UTF-8 sequence that has only been partially read. We must read
|
||||
// everything into a side buffer first and then call `from_utf8` on the complete
|
||||
// buffer.
|
||||
let mut bytes = Vec::new();
|
||||
self.read_to_end(&mut bytes)?;
|
||||
let string = crate::str::from_utf8(&bytes).map_err(|_| io::Error::INVALID_UTF8)?;
|
||||
*buf += string;
|
||||
Ok(string.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: ?Sized + Read> BufRead for BufReader<R> {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
self.buf.fill_buf(&mut self.inner)
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.buf.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R> fmt::Debug for BufReader<R>
|
||||
where
|
||||
R: ?Sized + fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("BufReader")
|
||||
.field("reader", &&self.inner)
|
||||
.field(
|
||||
"buffer",
|
||||
&format_args!("{}/{}", self.buf.filled() - self.buf.pos(), self.capacity()),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: ?Sized + Seek> Seek for BufReader<R> {
|
||||
/// Seek to an offset, in bytes, in the underlying reader.
|
||||
///
|
||||
/// The position used for seeking with <code>[SeekFrom::Current]\(_)</code> is the
|
||||
/// position the underlying reader would be at if the `BufReader<R>` had no
|
||||
/// internal buffer.
|
||||
///
|
||||
/// Seeking always discards the internal buffer, even if the seek position
|
||||
/// would otherwise fall within it. This guarantees that calling
|
||||
/// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader
|
||||
/// at the same position.
|
||||
///
|
||||
/// To seek without discarding the internal buffer, use [`BufReader::seek_relative`].
|
||||
///
|
||||
/// See [`std::io::Seek`] for more details.
|
||||
///
|
||||
/// Note: In the edge case where you're seeking with <code>[SeekFrom::Current]\(n)</code>
|
||||
/// where `n` minus the internal buffer length overflows an `i64`, two
|
||||
/// seeks will be performed instead of one. If the second seek returns
|
||||
/// [`Err`], the underlying reader will be left at the same position it would
|
||||
/// have if you called `seek` with <code>[SeekFrom::Current]\(0)</code>.
|
||||
///
|
||||
/// [`std::io::Seek`]: Seek
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
let result: u64;
|
||||
if let SeekFrom::Current(n) = pos {
|
||||
let remainder = (self.buf.filled() - self.buf.pos()) as i64;
|
||||
// it should be safe to assume that remainder fits within an i64 as the alternative
|
||||
// means we managed to allocate 8 exbibytes and that's absurd.
|
||||
// But it's not out of the realm of possibility for some weird underlying reader to
|
||||
// support seeking by i64::MIN so we need to handle underflow when subtracting
|
||||
// remainder.
|
||||
if let Some(offset) = n.checked_sub(remainder) {
|
||||
result = self.inner.seek(SeekFrom::Current(offset))?;
|
||||
} else {
|
||||
// seek backwards by our remainder, and then by the offset
|
||||
self.inner.seek(SeekFrom::Current(-remainder))?;
|
||||
self.discard_buffer();
|
||||
result = self.inner.seek(SeekFrom::Current(n))?;
|
||||
}
|
||||
} else {
|
||||
// Seeking with Start/End doesn't care about our buffer length.
|
||||
result = self.inner.seek(pos)?;
|
||||
}
|
||||
self.discard_buffer();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Returns the current seek position from the start of the stream.
|
||||
///
|
||||
/// The value returned is equivalent to `self.seek(SeekFrom::Current(0))`
|
||||
/// but does not flush the internal buffer. Due to this optimization the
|
||||
/// function does not guarantee that calling `.into_inner()` immediately
|
||||
/// afterwards will yield the underlying reader at the same position. Use
|
||||
/// [`BufReader::seek`] instead if you require that guarantee.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the position of the inner reader is smaller
|
||||
/// than the amount of buffered data. That can happen if the inner reader
|
||||
/// has an incorrect implementation of [`Seek::stream_position`], or if the
|
||||
/// position has gone out of sync due to calling [`Seek::seek`] directly on
|
||||
/// the underlying reader.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::{
|
||||
/// io::{self, BufRead, BufReader, Seek},
|
||||
/// fs::File,
|
||||
/// };
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut f = BufReader::new(File::open("foo.txt")?);
|
||||
///
|
||||
/// let before = f.stream_position()?;
|
||||
/// f.read_line(&mut String::new())?;
|
||||
/// let after = f.stream_position()?;
|
||||
///
|
||||
/// println!("The first line was {} bytes long", after - before);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
let remainder = (self.buf.filled() - self.buf.pos()) as u64;
|
||||
self.inner.stream_position().map(|pos| {
|
||||
pos.checked_sub(remainder).expect(
|
||||
"overflow when subtracting remaining buffer size from inner stream position",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Seeks relative to the current position.
|
||||
///
|
||||
/// If the new position lies within the buffer, the buffer will not be
|
||||
/// flushed, allowing for more efficient seeks. This method does not return
|
||||
/// the location of the underlying reader, so the caller must track this
|
||||
/// information themselves if it is required.
|
||||
fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
self.seek_relative(offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> SizeHint for BufReader<T> {
|
||||
#[inline]
|
||||
fn lower_bound(&self) -> usize {
|
||||
SizeHint::lower_bound(self.get_ref()) + self.buffer().len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn upper_bound(&self) -> Option<usize> {
|
||||
SizeHint::upper_bound(self.get_ref()).and_then(|up| self.buffer().len().checked_add(up))
|
||||
}
|
||||
}
|
||||
155
crates/std/src/io/buffered/bufreader/buffer.rs
Normal file
155
crates/std/src/io/buffered/bufreader/buffer.rs
Normal file
@@ -0,0 +1,155 @@
|
||||
//! An encapsulation of `BufReader`'s buffer management logic.
|
||||
//!
|
||||
//! This module factors out the basic functionality of `BufReader` in order to protect two core
|
||||
//! invariants:
|
||||
//! * `filled` bytes of `buf` are always initialized
|
||||
//! * `pos` is always <= `filled`
|
||||
//! Since this module encapsulates the buffer management logic, we can ensure that the range
|
||||
//! `pos..filled` is always a valid index into the initialized region of the buffer. This means
|
||||
//! that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so
|
||||
//! without encountering any runtime bounds checks.
|
||||
|
||||
use crate::cmp;
|
||||
use crate::io::{self, BorrowedBuf, ErrorKind, Read};
|
||||
use crate::mem::MaybeUninit;
|
||||
|
||||
pub struct Buffer {
|
||||
// The buffer.
|
||||
buf: Box<[MaybeUninit<u8>]>,
|
||||
// The current seek offset into `buf`, must always be <= `filled`.
|
||||
pos: usize,
|
||||
// Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are
|
||||
// initialized with bytes from a read.
|
||||
filled: usize,
|
||||
// This is the max number of bytes returned across all `fill_buf` calls. We track this so that we
|
||||
// can accurately tell `read_buf` how many bytes of buf are initialized, to bypass as much of its
|
||||
// defensive initialization as possible. Note that while this often the same as `filled`, it
|
||||
// doesn't need to be. Calls to `fill_buf` are not required to actually fill the buffer, and
|
||||
// omitting this is a huge perf regression for `Read` impls that do not.
|
||||
initialized: usize,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let buf = Box::new_uninit_slice(capacity);
|
||||
Self { buf, pos: 0, filled: 0, initialized: 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_with_capacity(capacity: usize) -> io::Result<Self> {
|
||||
match Box::try_new_uninit_slice(capacity) {
|
||||
Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }),
|
||||
Err(_) => {
|
||||
Err(io::const_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
// SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and
|
||||
// that region is initialized because those are all invariants of this type.
|
||||
unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buf.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn filled(&self) -> usize {
|
||||
self.filled
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pos(&self) -> usize {
|
||||
self.pos
|
||||
}
|
||||
|
||||
// This is only used by a test which asserts that the initialization-tracking is correct.
|
||||
#[cfg(test)]
|
||||
pub fn initialized(&self) -> usize {
|
||||
self.initialized
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn discard_buffer(&mut self) {
|
||||
self.pos = 0;
|
||||
self.filled = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn consume(&mut self, amt: usize) {
|
||||
self.pos = cmp::min(self.pos + amt, self.filled);
|
||||
}
|
||||
|
||||
/// If there are `amt` bytes available in the buffer, pass a slice containing those bytes to
|
||||
/// `visitor` and return true. If there are not enough bytes available, return false.
|
||||
#[inline]
|
||||
pub fn consume_with<V>(&mut self, amt: usize, mut visitor: V) -> bool
|
||||
where
|
||||
V: FnMut(&[u8]),
|
||||
{
|
||||
if let Some(claimed) = self.buffer().get(..amt) {
|
||||
visitor(claimed);
|
||||
// If the indexing into self.buffer() succeeds, amt must be a valid increment.
|
||||
self.pos += amt;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unconsume(&mut self, amt: usize) {
|
||||
self.pos = self.pos.saturating_sub(amt);
|
||||
}
|
||||
|
||||
/// Read more bytes into the buffer without discarding any of its contents
|
||||
pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<usize> {
|
||||
let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]);
|
||||
let old_init = self.initialized - self.filled;
|
||||
unsafe {
|
||||
buf.set_init(old_init);
|
||||
}
|
||||
reader.read_buf(buf.unfilled())?;
|
||||
self.filled += buf.len();
|
||||
self.initialized += buf.init_len() - old_init;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
/// Remove bytes that have already been read from the buffer.
|
||||
pub fn backshift(&mut self) {
|
||||
self.buf.copy_within(self.pos..self.filled, 0);
|
||||
self.filled -= self.pos;
|
||||
self.pos = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> {
|
||||
// If we've reached the end of our internal buffer then we need to fetch
|
||||
// some more data from the reader.
|
||||
// Branch using `>=` instead of the more correct `==`
|
||||
// to tell the compiler that the pos..cap slice is always valid.
|
||||
if self.pos >= self.filled {
|
||||
debug_assert!(self.pos == self.filled);
|
||||
|
||||
let mut buf = BorrowedBuf::from(&mut *self.buf);
|
||||
// SAFETY: `self.filled` bytes will always have been initialized.
|
||||
unsafe {
|
||||
buf.set_init(self.initialized);
|
||||
}
|
||||
|
||||
let result = reader.read_buf(buf.unfilled());
|
||||
|
||||
self.pos = 0;
|
||||
self.filled = buf.len();
|
||||
self.initialized = buf.init_len();
|
||||
|
||||
result?;
|
||||
}
|
||||
Ok(self.buffer())
|
||||
}
|
||||
}
|
||||
680
crates/std/src/io/buffered/bufwriter.rs
Normal file
680
crates/std/src/io/buffered/bufwriter.rs
Normal file
@@ -0,0 +1,680 @@
|
||||
use crate::io::{
|
||||
self, DEFAULT_BUF_SIZE, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write,
|
||||
};
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::{error, fmt, ptr};
|
||||
|
||||
/// Wraps a writer and buffers its output.
|
||||
///
|
||||
/// It can be excessively inefficient to work directly with something that
|
||||
/// implements [`Write`]. For example, every call to
|
||||
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
|
||||
/// `BufWriter<W>` keeps an in-memory buffer of data and writes it to an underlying
|
||||
/// writer in large, infrequent batches.
|
||||
///
|
||||
/// `BufWriter<W>` can improve the speed of programs that make *small* and
|
||||
/// *repeated* write calls to the same file or network socket. It does not
|
||||
/// help when writing very large amounts at once, or writing just one or a few
|
||||
/// times. It also provides no advantage when writing to a destination that is
|
||||
/// in memory, like a <code>[Vec]\<u8></code>.
|
||||
///
|
||||
/// It is critical to call [`flush`] before `BufWriter<W>` is dropped. Though
|
||||
/// dropping will attempt to flush the contents of the buffer, any errors
|
||||
/// that happen in the process of dropping will be ignored. Calling [`flush`]
|
||||
/// ensures that the buffer is empty and thus dropping will not even attempt
|
||||
/// file operations.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Let's write the numbers one through ten to a [`TcpStream`]:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap();
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// stream.write(&[i+1]).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Because we're not buffering, we write each one in turn, incurring the
|
||||
/// overhead of a system call per byte written. We can fix this with a
|
||||
/// `BufWriter<W>`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// stream.write(&[i+1]).unwrap();
|
||||
/// }
|
||||
/// stream.flush().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// By wrapping the stream with a `BufWriter<W>`, these ten writes are all grouped
|
||||
/// together by the buffer and will all be written out in one system call when
|
||||
/// the `stream` is flushed.
|
||||
///
|
||||
/// [`TcpStream::write`]: crate::net::TcpStream::write
|
||||
/// [`TcpStream`]: crate::net::TcpStream
|
||||
/// [`flush`]: BufWriter::flush
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct BufWriter<W: ?Sized + Write> {
|
||||
// The buffer. Avoid using this like a normal `Vec` in common code paths.
|
||||
// That is, don't use `buf.push`, `buf.extend_from_slice`, or any other
|
||||
// methods that require bounds checking or the like. This makes an enormous
|
||||
// difference to performance (we may want to stop using a `Vec` entirely).
|
||||
buf: Vec<u8>,
|
||||
// #30888: If the inner writer panics in a call to write, we don't want to
|
||||
// write the buffered data a second time in BufWriter's destructor. This
|
||||
// flag tells the Drop impl if it should skip the flush.
|
||||
panicked: bool,
|
||||
inner: W,
|
||||
}
|
||||
|
||||
impl<W: Write> BufWriter<W> {
|
||||
/// Creates a new `BufWriter<W>` with a default buffer capacity. The default is currently 8 KiB,
|
||||
/// but may change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(inner: W) -> BufWriter<W> {
|
||||
BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||
}
|
||||
|
||||
pub(crate) fn try_new_buffer() -> io::Result<Vec<u8>> {
|
||||
Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| {
|
||||
io::const_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer")
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn with_buffer(inner: W, buf: Vec<u8>) -> Self {
|
||||
Self { inner, buf, panicked: false }
|
||||
}
|
||||
|
||||
/// Creates a new `BufWriter<W>` with at least the specified buffer capacity.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a buffer with a buffer of at least a hundred bytes.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let stream = TcpStream::connect("127.0.0.1:34254").unwrap();
|
||||
/// let mut buffer = BufWriter::with_capacity(100, stream);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn with_capacity(capacity: usize, inner: W) -> BufWriter<W> {
|
||||
BufWriter { inner, buf: Vec::with_capacity(capacity), panicked: false }
|
||||
}
|
||||
|
||||
/// Unwraps this `BufWriter<W>`, returning the underlying writer.
|
||||
///
|
||||
/// The buffer is written out before returning the writer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An [`Err`] will be returned if an error occurs while flushing the buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // unwrap the TcpStream and flush the buffer
|
||||
/// let stream = buffer.into_inner().unwrap();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
|
||||
match self.flush_buf() {
|
||||
Err(e) => Err(IntoInnerError::new(self, e)),
|
||||
Ok(()) => Ok(self.into_parts().0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Disassembles this `BufWriter<W>`, returning the underlying writer, and any buffered but
|
||||
/// unwritten data.
|
||||
///
|
||||
/// If the underlying writer panicked, it is not known what portion of the data was written.
|
||||
/// In this case, we return `WriterPanicked` for the buffered data (from which the buffer
|
||||
/// contents can still be recovered).
|
||||
///
|
||||
/// `into_parts` makes no attempt to flush data and cannot fail.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::{BufWriter, Write};
|
||||
///
|
||||
/// let mut buffer = [0u8; 10];
|
||||
/// let mut stream = BufWriter::new(buffer.as_mut());
|
||||
/// write!(stream, "too much data").unwrap();
|
||||
/// stream.flush().expect_err("it doesn't fit");
|
||||
/// let (recovered_writer, buffered_data) = stream.into_parts();
|
||||
/// assert_eq!(recovered_writer.len(), 0);
|
||||
/// assert_eq!(&buffered_data.unwrap(), b"ata");
|
||||
/// ```
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub fn into_parts(self) -> (W, Result<Vec<u8>, WriterPanicked>) {
|
||||
let mut this = ManuallyDrop::new(self);
|
||||
let buf = mem::take(&mut this.buf);
|
||||
let buf = if !this.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
|
||||
|
||||
// SAFETY: double-drops are prevented by putting `this` in a ManuallyDrop that is never dropped
|
||||
let inner = unsafe { ptr::read(&this.inner) };
|
||||
|
||||
(inner, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized + Write> BufWriter<W> {
|
||||
/// Send data in our local buffer into the inner writer, looping as
|
||||
/// necessary until either it's all been sent or an error occurs.
|
||||
///
|
||||
/// Because all the data in the buffer has been reported to our owner as
|
||||
/// "successfully written" (by returning nonzero success values from
|
||||
/// `write`), any 0-length writes from `inner` must be reported as i/o
|
||||
/// errors from this method.
|
||||
pub(in crate::io) fn flush_buf(&mut self) -> io::Result<()> {
|
||||
/// Helper struct to ensure the buffer is updated after all the writes
|
||||
/// are complete. It tracks the number of written bytes and drains them
|
||||
/// all from the front of the buffer when dropped.
|
||||
struct BufGuard<'a> {
|
||||
buffer: &'a mut Vec<u8>,
|
||||
written: usize,
|
||||
}
|
||||
|
||||
impl<'a> BufGuard<'a> {
|
||||
fn new(buffer: &'a mut Vec<u8>) -> Self {
|
||||
Self { buffer, written: 0 }
|
||||
}
|
||||
|
||||
/// The unwritten part of the buffer
|
||||
fn remaining(&self) -> &[u8] {
|
||||
&self.buffer[self.written..]
|
||||
}
|
||||
|
||||
/// Flag some bytes as removed from the front of the buffer
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.written += amt;
|
||||
}
|
||||
|
||||
/// true if all of the bytes have been written
|
||||
fn done(&self) -> bool {
|
||||
self.written >= self.buffer.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BufGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
if self.written > 0 {
|
||||
self.buffer.drain(..self.written);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut guard = BufGuard::new(&mut self.buf);
|
||||
while !guard.done() {
|
||||
self.panicked = true;
|
||||
let r = self.inner.write(guard.remaining());
|
||||
self.panicked = false;
|
||||
|
||||
match r {
|
||||
Ok(0) => {
|
||||
return Err(io::const_error!(
|
||||
ErrorKind::WriteZero,
|
||||
"failed to write the buffered data",
|
||||
));
|
||||
}
|
||||
Ok(n) => guard.consume(n),
|
||||
Err(ref e) if e.is_interrupted() => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Buffer some data without flushing it, regardless of the size of the
|
||||
/// data. Writes as much as possible without exceeding capacity. Returns
|
||||
/// the number of bytes written.
|
||||
pub(super) fn write_to_buf(&mut self, buf: &[u8]) -> usize {
|
||||
let available = self.spare_capacity();
|
||||
let amt_to_buffer = available.min(buf.len());
|
||||
|
||||
// SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(&buf[..amt_to_buffer]);
|
||||
}
|
||||
|
||||
amt_to_buffer
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying writer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // we can use reference just like buffer
|
||||
/// let reference = buffer.get_ref();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_ref(&self) -> &W {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying writer.
|
||||
///
|
||||
/// It is inadvisable to directly write to the underlying writer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // we can use reference just like buffer
|
||||
/// let reference = buffer.get_mut();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_mut(&mut self) -> &mut W {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Returns a reference to the internally buffered data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // See how many bytes are currently buffered
|
||||
/// let bytes_buffered = buf_writer.buffer().len();
|
||||
/// ```
|
||||
#[stable(feature = "bufreader_buffer", since = "1.37.0")]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
&self.buf
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the internal buffer.
|
||||
///
|
||||
/// This can be used to write data directly into the buffer without triggering writers
|
||||
/// to the underlying writer.
|
||||
///
|
||||
/// That the buffer is a `Vec` is an implementation detail.
|
||||
/// Callers should not modify the capacity as there currently is no public API to do so
|
||||
/// and thus any capacity changes would be unexpected by the user.
|
||||
pub(in crate::io) fn buffer_mut(&mut self) -> &mut Vec<u8> {
|
||||
&mut self.buf
|
||||
}
|
||||
|
||||
/// Returns the number of bytes the internal buffer can hold without flushing.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // Check the capacity of the inner buffer
|
||||
/// let capacity = buf_writer.capacity();
|
||||
/// // Calculate how many bytes can be written without flushing
|
||||
/// let without_flush = capacity - buf_writer.buffer().len();
|
||||
/// ```
|
||||
#[stable(feature = "buffered_io_capacity", since = "1.46.0")]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buf.capacity()
|
||||
}
|
||||
|
||||
// Ensure this function does not get inlined into `write`, so that it
|
||||
// remains inlineable and its common path remains as short as possible.
|
||||
// If this function ends up being called frequently relative to `write`,
|
||||
// it's likely a sign that the client is using an improperly sized buffer
|
||||
// or their write patterns are somewhat pathological.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn write_cold(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if buf.len() > self.spare_capacity() {
|
||||
self.flush_buf()?;
|
||||
}
|
||||
|
||||
// Why not len > capacity? To avoid a needless trip through the buffer when the input
|
||||
// exactly fills it. We'd just need to flush it to the underlying writer anyway.
|
||||
if buf.len() >= self.buf.capacity() {
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write(buf);
|
||||
self.panicked = false;
|
||||
r
|
||||
} else {
|
||||
// Write to the buffer. In this case, we write to the buffer even if it fills it
|
||||
// exactly. Doing otherwise would mean flushing the buffer, then writing this
|
||||
// input to the inner writer, which in many cases would be a worse strategy.
|
||||
|
||||
// SAFETY: There was either enough spare capacity already, or there wasn't and we
|
||||
// flushed the buffer to ensure that there is. In the latter case, we know that there
|
||||
// is because flushing ensured that our entire buffer is spare capacity, and we entered
|
||||
// this block because the input buffer length is less than that capacity. In either
|
||||
// case, it's safe to write the input buffer to our buffer.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure this function does not get inlined into `write_all`, so that it
|
||||
// remains inlineable and its common path remains as short as possible.
|
||||
// If this function ends up being called frequently relative to `write_all`,
|
||||
// it's likely a sign that the client is using an improperly sized buffer
|
||||
// or their write patterns are somewhat pathological.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn write_all_cold(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
// Normally, `write_all` just calls `write` in a loop. We can do better
|
||||
// by calling `self.get_mut().write_all()` directly, which avoids
|
||||
// round trips through the buffer in the event of a series of partial
|
||||
// writes in some circumstances.
|
||||
|
||||
if buf.len() > self.spare_capacity() {
|
||||
self.flush_buf()?;
|
||||
}
|
||||
|
||||
// Why not len > capacity? To avoid a needless trip through the buffer when the input
|
||||
// exactly fills it. We'd just need to flush it to the underlying writer anyway.
|
||||
if buf.len() >= self.buf.capacity() {
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write_all(buf);
|
||||
self.panicked = false;
|
||||
r
|
||||
} else {
|
||||
// Write to the buffer. In this case, we write to the buffer even if it fills it
|
||||
// exactly. Doing otherwise would mean flushing the buffer, then writing this
|
||||
// input to the inner writer, which in many cases would be a worse strategy.
|
||||
|
||||
// SAFETY: There was either enough spare capacity already, or there wasn't and we
|
||||
// flushed the buffer to ensure that there is. In the latter case, we know that there
|
||||
// is because flushing ensured that our entire buffer is spare capacity, and we entered
|
||||
// this block because the input buffer length is less than that capacity. In either
|
||||
// case, it's safe to write the input buffer to our buffer.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Requires `buf.len() <= self.buf.capacity() - self.buf.len()`,
|
||||
// i.e., that input buffer length is less than or equal to spare capacity.
|
||||
#[inline]
|
||||
unsafe fn write_to_buffer_unchecked(&mut self, buf: &[u8]) {
|
||||
debug_assert!(buf.len() <= self.spare_capacity());
|
||||
let old_len = self.buf.len();
|
||||
let buf_len = buf.len();
|
||||
let src = buf.as_ptr();
|
||||
unsafe {
|
||||
let dst = self.buf.as_mut_ptr().add(old_len);
|
||||
ptr::copy_nonoverlapping(src, dst, buf_len);
|
||||
self.buf.set_len(old_len + buf_len);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn spare_capacity(&self) -> usize {
|
||||
self.buf.capacity() - self.buf.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
/// Error returned for the buffered data from `BufWriter::into_parts`, when the underlying
|
||||
/// writer has previously panicked. Contains the (possibly partly written) buffered data.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::{self, BufWriter, Write};
|
||||
/// use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
///
|
||||
/// struct PanickingWriter;
|
||||
/// impl Write for PanickingWriter {
|
||||
/// fn write(&mut self, buf: &[u8]) -> io::Result<usize> { panic!() }
|
||||
/// fn flush(&mut self) -> io::Result<()> { panic!() }
|
||||
/// }
|
||||
///
|
||||
/// let mut stream = BufWriter::new(PanickingWriter);
|
||||
/// write!(stream, "some data").unwrap();
|
||||
/// let result = catch_unwind(AssertUnwindSafe(|| {
|
||||
/// stream.flush().unwrap()
|
||||
/// }));
|
||||
/// assert!(result.is_err());
|
||||
/// let (recovered_writer, buffered_data) = stream.into_parts();
|
||||
/// assert!(matches!(recovered_writer, PanickingWriter));
|
||||
/// assert_eq!(buffered_data.unwrap_err().into_inner(), b"some data");
|
||||
/// ```
|
||||
pub struct WriterPanicked {
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl WriterPanicked {
|
||||
/// Returns the perhaps-unwritten data. Some of this data may have been written by the
|
||||
/// panicking call(s) to the underlying writer, so simply writing it again is not a good idea.
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub fn into_inner(self) -> Vec<u8> {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
impl error::Error for WriterPanicked {}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
impl fmt::Display for WriterPanicked {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"BufWriter inner writer panicked, what data remains unwritten is not known".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
impl fmt::Debug for WriterPanicked {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("WriterPanicked")
|
||||
.field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> Write for BufWriter<W> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// Use < instead of <= to avoid a needless trip through the buffer in some cases.
|
||||
// See `write_cold` for details.
|
||||
if buf.len() < self.spare_capacity() {
|
||||
// SAFETY: safe by above conditional.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
self.write_cold(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
// Use < instead of <= to avoid a needless trip through the buffer in some cases.
|
||||
// See `write_all_cold` for details.
|
||||
if buf.len() < self.spare_capacity() {
|
||||
// SAFETY: safe by above conditional.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
self.write_all_cold(buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
// FIXME: Consider applying `#[inline]` / `#[inline(never)]` optimizations already applied
|
||||
// to `write` and `write_all`. The performance benefits can be significant. See #79930.
|
||||
if self.get_ref().is_write_vectored() {
|
||||
// We have to handle the possibility that the total length of the buffers overflows
|
||||
// `usize` (even though this can only happen if multiple `IoSlice`s reference the
|
||||
// same underlying buffer, as otherwise the buffers wouldn't fit in memory). If the
|
||||
// computation overflows, then surely the input cannot fit in our buffer, so we forward
|
||||
// to the inner writer's `write_vectored` method to let it handle it appropriately.
|
||||
let mut saturated_total_len: usize = 0;
|
||||
|
||||
for buf in bufs {
|
||||
saturated_total_len = saturated_total_len.saturating_add(buf.len());
|
||||
|
||||
if saturated_total_len > self.spare_capacity() && !self.buf.is_empty() {
|
||||
// Flush if the total length of the input exceeds our buffer's spare capacity.
|
||||
// If we would have overflowed, this condition also holds, and we need to flush.
|
||||
self.flush_buf()?;
|
||||
}
|
||||
|
||||
if saturated_total_len >= self.buf.capacity() {
|
||||
// Forward to our inner writer if the total length of the input is greater than or
|
||||
// equal to our buffer capacity. If we would have overflowed, this condition also
|
||||
// holds, and we punt to the inner writer.
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write_vectored(bufs);
|
||||
self.panicked = false;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// `saturated_total_len < self.buf.capacity()` implies that we did not saturate.
|
||||
|
||||
// SAFETY: We checked whether or not the spare capacity was large enough above. If
|
||||
// it was, then we're safe already. If it wasn't, we flushed, making sufficient
|
||||
// room for any input <= the buffer size, which includes this input.
|
||||
unsafe {
|
||||
bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b));
|
||||
};
|
||||
|
||||
Ok(saturated_total_len)
|
||||
} else {
|
||||
let mut iter = bufs.iter();
|
||||
let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) {
|
||||
// This is the first non-empty slice to write, so if it does
|
||||
// not fit in the buffer, we still get to flush and proceed.
|
||||
if buf.len() > self.spare_capacity() {
|
||||
self.flush_buf()?;
|
||||
}
|
||||
if buf.len() >= self.buf.capacity() {
|
||||
// The slice is at least as large as the buffering capacity,
|
||||
// so it's better to write it directly, bypassing the buffer.
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write(buf);
|
||||
self.panicked = false;
|
||||
return r;
|
||||
} else {
|
||||
// SAFETY: We checked whether or not the spare capacity was large enough above.
|
||||
// If it was, then we're safe already. If it wasn't, we flushed, making
|
||||
// sufficient room for any input <= the buffer size, which includes this input.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
buf.len()
|
||||
}
|
||||
} else {
|
||||
return Ok(0);
|
||||
};
|
||||
debug_assert!(total_written != 0);
|
||||
for buf in iter {
|
||||
if buf.len() <= self.spare_capacity() {
|
||||
// SAFETY: safe by above conditional.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
// This cannot overflow `usize`. If we are here, we've written all of the bytes
|
||||
// so far to our buffer, and we've ensured that we never exceed the buffer's
|
||||
// capacity. Therefore, `total_written` <= `self.buf.capacity()` <= `usize::MAX`.
|
||||
total_written += buf.len();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(total_written)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.flush_buf().and_then(|()| self.get_mut().flush())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> fmt::Debug for BufWriter<W>
|
||||
where
|
||||
W: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("BufWriter")
|
||||
.field("writer", &&self.inner)
|
||||
.field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write + Seek> Seek for BufWriter<W> {
|
||||
/// Seek to the offset, in bytes, in the underlying writer.
|
||||
///
|
||||
/// Seeking always writes out the internal buffer before seeking.
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
self.flush_buf()?;
|
||||
self.get_mut().seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> Drop for BufWriter<W> {
|
||||
fn drop(&mut self) {
|
||||
if !self.panicked {
|
||||
// dtors should not panic, so we ignore a failed flush
|
||||
let _r = self.flush_buf();
|
||||
}
|
||||
}
|
||||
}
|
||||
235
crates/std/src/io/buffered/linewriter.rs
Normal file
235
crates/std/src/io/buffered/linewriter.rs
Normal file
@@ -0,0 +1,235 @@
|
||||
use crate::fmt;
|
||||
use crate::io::buffered::LineWriterShim;
|
||||
use crate::io::{self, BufWriter, IntoInnerError, IoSlice, Write};
|
||||
|
||||
/// Wraps a writer and buffers output to it, flushing whenever a newline
|
||||
/// (`0x0a`, `'\n'`) is detected.
|
||||
///
|
||||
/// The [`BufWriter`] struct wraps a writer and buffers its output.
|
||||
/// But it only does this batched write when it goes out of scope, or when the
|
||||
/// internal buffer is full. Sometimes, you'd prefer to write each line as it's
|
||||
/// completed, rather than the entire buffer at once. Enter `LineWriter`. It
|
||||
/// does exactly that.
|
||||
///
|
||||
/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the
|
||||
/// `LineWriter` goes out of scope or when its internal buffer is full.
|
||||
///
|
||||
/// If there's still a partial line in the buffer when the `LineWriter` is
|
||||
/// dropped, it will flush those contents.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// We can use `LineWriter` to write one line at a time, significantly
|
||||
/// reducing the number of actual writes to the file.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::{self, File};
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let road_not_taken = b"I shall be telling this with a sigh
|
||||
/// Somewhere ages and ages hence:
|
||||
/// Two roads diverged in a wood, and I -
|
||||
/// I took the one less traveled by,
|
||||
/// And that has made all the difference.";
|
||||
///
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let mut file = LineWriter::new(file);
|
||||
///
|
||||
/// file.write_all(b"I shall be telling this with a sigh")?;
|
||||
///
|
||||
/// // No bytes are written until a newline is encountered (or
|
||||
/// // the internal buffer is filled).
|
||||
/// assert_eq!(fs::read_to_string("poem.txt")?, "");
|
||||
/// file.write_all(b"\n")?;
|
||||
/// assert_eq!(
|
||||
/// fs::read_to_string("poem.txt")?,
|
||||
/// "I shall be telling this with a sigh\n",
|
||||
/// );
|
||||
///
|
||||
/// // Write the rest of the poem.
|
||||
/// file.write_all(b"Somewhere ages and ages hence:
|
||||
/// Two roads diverged in a wood, and I -
|
||||
/// I took the one less traveled by,
|
||||
/// And that has made all the difference.")?;
|
||||
///
|
||||
/// // The last line of the poem doesn't end in a newline, so
|
||||
/// // we have to flush or drop the `LineWriter` to finish
|
||||
/// // writing.
|
||||
/// file.flush()?;
|
||||
///
|
||||
/// // Confirm the whole poem was written.
|
||||
/// assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct LineWriter<W: ?Sized + Write> {
|
||||
inner: BufWriter<W>,
|
||||
}
|
||||
|
||||
impl<W: Write> LineWriter<W> {
|
||||
/// Creates a new `LineWriter`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let file = LineWriter::new(file);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(inner: W) -> LineWriter<W> {
|
||||
// Lines typically aren't that long, don't use a giant buffer
|
||||
LineWriter::with_capacity(1024, inner)
|
||||
}
|
||||
|
||||
/// Creates a new `LineWriter` with at least the specified capacity for the
|
||||
/// internal buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let file = LineWriter::with_capacity(100, file);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn with_capacity(capacity: usize, inner: W) -> LineWriter<W> {
|
||||
LineWriter { inner: BufWriter::with_capacity(capacity, inner) }
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying writer.
|
||||
///
|
||||
/// Caution must be taken when calling methods on the mutable reference
|
||||
/// returned as extra writes could corrupt the output stream.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let mut file = LineWriter::new(file);
|
||||
///
|
||||
/// // we can use reference just like file
|
||||
/// let reference = file.get_mut();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_mut(&mut self) -> &mut W {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
|
||||
/// Unwraps this `LineWriter`, returning the underlying writer.
|
||||
///
|
||||
/// The internal buffer is written out before returning the writer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An [`Err`] will be returned if an error occurs while flushing the buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
///
|
||||
/// let writer: LineWriter<File> = LineWriter::new(file);
|
||||
///
|
||||
/// let file: File = writer.into_inner()?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
|
||||
self.inner.into_inner().map_err(|err| err.new_wrapped(|inner| LineWriter { inner }))
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized + Write> LineWriter<W> {
|
||||
/// Gets a reference to the underlying writer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let file = LineWriter::new(file);
|
||||
///
|
||||
/// let reference = file.get_ref();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_ref(&self) -> &W {
|
||||
self.inner.get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> Write for LineWriter<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
LineWriterShim::new(&mut self.inner).write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
LineWriterShim::new(&mut self.inner).write_vectored(bufs)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.inner.is_write_vectored()
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
LineWriterShim::new(&mut self.inner).write_all(buf)
|
||||
}
|
||||
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
LineWriterShim::new(&mut self.inner).write_all_vectored(bufs)
|
||||
}
|
||||
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
LineWriterShim::new(&mut self.inner).write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> fmt::Debug for LineWriter<W>
|
||||
where
|
||||
W: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("LineWriter")
|
||||
.field("writer", &self.get_ref())
|
||||
.field(
|
||||
"buffer",
|
||||
&format_args!("{}/{}", self.inner.buffer().len(), self.inner.capacity()),
|
||||
)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
297
crates/std/src/io/buffered/linewritershim.rs
Normal file
297
crates/std/src/io/buffered/linewritershim.rs
Normal file
@@ -0,0 +1,297 @@
|
||||
use core::slice::memchr;
|
||||
|
||||
use crate::io::{self, BufWriter, IoSlice, Write};
|
||||
|
||||
/// Private helper struct for implementing the line-buffered writing logic.
|
||||
///
|
||||
/// This shim temporarily wraps a BufWriter, and uses its internals to
|
||||
/// implement a line-buffered writer (specifically by using the internal
|
||||
/// methods like write_to_buf and flush_buf). In this way, a more
|
||||
/// efficient abstraction can be created than one that only had access to
|
||||
/// `write` and `flush`, without needlessly duplicating a lot of the
|
||||
/// implementation details of BufWriter. This also allows existing
|
||||
/// `BufWriters` to be temporarily given line-buffering logic; this is what
|
||||
/// enables Stdout to be alternately in line-buffered or block-buffered mode.
|
||||
#[derive(Debug)]
|
||||
pub struct LineWriterShim<'a, W: ?Sized + Write> {
|
||||
buffer: &'a mut BufWriter<W>,
|
||||
}
|
||||
|
||||
impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> {
|
||||
pub fn new(buffer: &'a mut BufWriter<W>) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
|
||||
/// Gets a reference to the inner writer (that is, the writer
|
||||
/// wrapped by the BufWriter).
|
||||
fn inner(&self) -> &W {
|
||||
self.buffer.get_ref()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the inner writer (that is, the writer
|
||||
/// wrapped by the BufWriter). Be careful with this writer, as writes to
|
||||
/// it will bypass the buffer.
|
||||
fn inner_mut(&mut self) -> &mut W {
|
||||
self.buffer.get_mut()
|
||||
}
|
||||
|
||||
/// Gets the content currently buffered in self.buffer
|
||||
fn buffered(&self) -> &[u8] {
|
||||
self.buffer.buffer()
|
||||
}
|
||||
|
||||
/// Flushes the buffer iff the last byte is a newline (indicating that an
|
||||
/// earlier write only succeeded partially, and we want to retry flushing
|
||||
/// the buffered line before continuing with a subsequent write).
|
||||
fn flush_if_completed_line(&mut self) -> io::Result<()> {
|
||||
match self.buffered().last().copied() {
|
||||
Some(b'\n') => self.buffer.flush_buf(),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> {
|
||||
/// Writes some data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// the last newline is sent directly to the underlying writer, and data
|
||||
/// after it is buffered. Returns the number of bytes written.
|
||||
///
|
||||
/// This function operates on a "best effort basis"; in keeping with the
|
||||
/// convention of `Write::write`, it makes at most one attempt to write
|
||||
/// new data to the underlying writer. If that write only reports a partial
|
||||
/// success, the remaining data will be buffered.
|
||||
///
|
||||
/// Because this function attempts to send completed lines to the underlying
|
||||
/// writer, it will also flush the existing buffer if it ends with a
|
||||
/// newline, even if the incoming data does not contain any newlines.
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let newline_idx = match memchr::memrchr(b'\n', buf) {
|
||||
// If there are no new newlines (that is, if this write is less than
|
||||
// one line), just do a regular buffered write (which may flush if
|
||||
// we exceed the inner buffer's size)
|
||||
None => {
|
||||
self.flush_if_completed_line()?;
|
||||
return self.buffer.write(buf);
|
||||
}
|
||||
// Otherwise, arrange for the lines to be written directly to the
|
||||
// inner writer.
|
||||
Some(newline_idx) => newline_idx + 1,
|
||||
};
|
||||
|
||||
// Flush existing content to prepare for our write. We have to do this
|
||||
// before attempting to write `buf` in order to maintain consistency;
|
||||
// if we add `buf` to the buffer then try to flush it all at once,
|
||||
// we're obligated to return Ok(), which would mean suppressing any
|
||||
// errors that occur during flush.
|
||||
self.buffer.flush_buf()?;
|
||||
|
||||
// This is what we're going to try to write directly to the inner
|
||||
// writer. The rest will be buffered, if nothing goes wrong.
|
||||
let lines = &buf[..newline_idx];
|
||||
|
||||
// Write `lines` directly to the inner writer. In keeping with the
|
||||
// `write` convention, make at most one attempt to add new (unbuffered)
|
||||
// data. Because this write doesn't touch the BufWriter state directly,
|
||||
// and the buffer is known to be empty, we don't need to worry about
|
||||
// self.buffer.panicked here.
|
||||
let flushed = self.inner_mut().write(lines)?;
|
||||
|
||||
// If buffer returns Ok(0), propagate that to the caller without
|
||||
// doing additional buffering; otherwise we're just guaranteeing
|
||||
// an "ErrorKind::WriteZero" later.
|
||||
if flushed == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Now that the write has succeeded, buffer the rest (or as much of
|
||||
// the rest as possible). If there were any unwritten newlines, we
|
||||
// only buffer out to the last unwritten newline that fits in the
|
||||
// buffer; this helps prevent flushing partial lines on subsequent
|
||||
// calls to LineWriterShim::write.
|
||||
|
||||
// Handle the cases in order of most-common to least-common, under
|
||||
// the presumption that most writes succeed in totality, and that most
|
||||
// writes are smaller than the buffer.
|
||||
// - Is this a partial line (ie, no newlines left in the unwritten tail)
|
||||
// - If not, does the data out to the last unwritten newline fit in
|
||||
// the buffer?
|
||||
// - If not, scan for the last newline that *does* fit in the buffer
|
||||
let tail = if flushed >= newline_idx {
|
||||
let tail = &buf[flushed..];
|
||||
// Avoid unnecessary short writes by not splitting the remaining
|
||||
// bytes if they're larger than the buffer.
|
||||
// They can be written in full by the next call to write.
|
||||
if tail.len() >= self.buffer.capacity() {
|
||||
return Ok(flushed);
|
||||
}
|
||||
tail
|
||||
} else if newline_idx - flushed <= self.buffer.capacity() {
|
||||
&buf[flushed..newline_idx]
|
||||
} else {
|
||||
let scan_area = &buf[flushed..];
|
||||
let scan_area = &scan_area[..self.buffer.capacity()];
|
||||
match memchr::memrchr(b'\n', scan_area) {
|
||||
Some(newline_idx) => &scan_area[..newline_idx + 1],
|
||||
None => scan_area,
|
||||
}
|
||||
};
|
||||
|
||||
let buffered = self.buffer.write_to_buf(tail);
|
||||
Ok(flushed + buffered)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.buffer.flush()
|
||||
}
|
||||
|
||||
/// Writes some vectored data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// and including the buffer containing the last newline is sent directly to
|
||||
/// the inner writer, and the data after it is buffered. Returns the number
|
||||
/// of bytes written.
|
||||
///
|
||||
/// This function operates on a "best effort basis"; in keeping with the
|
||||
/// convention of `Write::write`, it makes at most one attempt to write
|
||||
/// new data to the underlying writer.
|
||||
///
|
||||
/// Because this function attempts to send completed lines to the underlying
|
||||
/// writer, it will also flush the existing buffer if it contains any
|
||||
/// newlines.
|
||||
///
|
||||
/// Because sorting through an array of `IoSlice` can be a bit convoluted,
|
||||
/// This method differs from write in the following ways:
|
||||
///
|
||||
/// - It attempts to write the full content of all the buffers up to and
|
||||
/// including the one containing the last newline. This means that it
|
||||
/// may attempt to write a partial line, that buffer has data past the
|
||||
/// newline.
|
||||
/// - If the write only reports partial success, it does not attempt to
|
||||
/// find the precise location of the written bytes and buffer the rest.
|
||||
///
|
||||
/// If the underlying vector doesn't support vectored writing, we instead
|
||||
/// simply write the first non-empty buffer with `write`. This way, we
|
||||
/// get the benefits of more granular partial-line handling without losing
|
||||
/// anything in efficiency
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
// If there's no specialized behavior for write_vectored, just use
|
||||
// write. This has the benefit of more granular partial-line handling.
|
||||
if !self.is_write_vectored() {
|
||||
return match bufs.iter().find(|buf| !buf.is_empty()) {
|
||||
Some(buf) => self.write(buf),
|
||||
None => Ok(0),
|
||||
};
|
||||
}
|
||||
|
||||
// Find the buffer containing the last newline
|
||||
// FIXME: This is overly slow if there are very many bufs and none contain
|
||||
// newlines. e.g. writev() on Linux only writes up to 1024 slices, so
|
||||
// scanning the rest is wasted effort. This makes write_all_vectored()
|
||||
// quadratic.
|
||||
let last_newline_buf_idx = bufs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i));
|
||||
|
||||
// If there are no new newlines (that is, if this write is less than
|
||||
// one line), just do a regular buffered write
|
||||
let last_newline_buf_idx = match last_newline_buf_idx {
|
||||
// No newlines; just do a normal buffered write
|
||||
None => {
|
||||
self.flush_if_completed_line()?;
|
||||
return self.buffer.write_vectored(bufs);
|
||||
}
|
||||
Some(i) => i,
|
||||
};
|
||||
|
||||
// Flush existing content to prepare for our write
|
||||
self.buffer.flush_buf()?;
|
||||
|
||||
// This is what we're going to try to write directly to the inner
|
||||
// writer. The rest will be buffered, if nothing goes wrong.
|
||||
let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1);
|
||||
|
||||
// Write `lines` directly to the inner writer. In keeping with the
|
||||
// `write` convention, make at most one attempt to add new (unbuffered)
|
||||
// data. Because this write doesn't touch the BufWriter state directly,
|
||||
// and the buffer is known to be empty, we don't need to worry about
|
||||
// self.panicked here.
|
||||
let flushed = self.inner_mut().write_vectored(lines)?;
|
||||
|
||||
// If inner returns Ok(0), propagate that to the caller without
|
||||
// doing additional buffering; otherwise we're just guaranteeing
|
||||
// an "ErrorKind::WriteZero" later.
|
||||
if flushed == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Don't try to reconstruct the exact amount written; just bail
|
||||
// in the event of a partial write
|
||||
let mut lines_len: usize = 0;
|
||||
for buf in lines {
|
||||
// With overlapping/duplicate slices the total length may in theory
|
||||
// exceed usize::MAX
|
||||
lines_len = lines_len.saturating_add(buf.len());
|
||||
if flushed < lines_len {
|
||||
return Ok(flushed);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the write has succeeded, buffer the rest (or as much of the
|
||||
// rest as possible)
|
||||
let buffered: usize = tail
|
||||
.iter()
|
||||
.filter(|buf| !buf.is_empty())
|
||||
.map(|buf| self.buffer.write_to_buf(buf))
|
||||
.take_while(|&n| n > 0)
|
||||
.sum();
|
||||
|
||||
Ok(flushed + buffered)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.inner().is_write_vectored()
|
||||
}
|
||||
|
||||
/// Writes some data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// the last newline is sent directly to the underlying writer, and data
|
||||
/// after it is buffered.
|
||||
///
|
||||
/// Because this function attempts to send completed lines to the underlying
|
||||
/// writer, it will also flush the existing buffer if it contains any
|
||||
/// newlines, even if the incoming data does not contain any newlines.
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
match memchr::memrchr(b'\n', buf) {
|
||||
// If there are no new newlines (that is, if this write is less than
|
||||
// one line), just do a regular buffered write (which may flush if
|
||||
// we exceed the inner buffer's size)
|
||||
None => {
|
||||
self.flush_if_completed_line()?;
|
||||
self.buffer.write_all(buf)
|
||||
}
|
||||
Some(newline_idx) => {
|
||||
let (lines, tail) = buf.split_at(newline_idx + 1);
|
||||
|
||||
if self.buffered().is_empty() {
|
||||
self.inner_mut().write_all(lines)?;
|
||||
} else {
|
||||
// If there is any buffered data, we add the incoming lines
|
||||
// to that buffer before flushing, which saves us at least
|
||||
// one write call. We can't really do this with `write`,
|
||||
// since we can't do this *and* not suppress errors *and*
|
||||
// report a consistent state to the caller in a return
|
||||
// value, but here in write_all it's fine.
|
||||
self.buffer.write_all(lines)?;
|
||||
self.buffer.flush_buf()?;
|
||||
}
|
||||
|
||||
self.buffer.write_all(tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
189
crates/std/src/io/buffered/mod.rs
Normal file
189
crates/std/src/io/buffered/mod.rs
Normal file
@@ -0,0 +1,189 @@
|
||||
//! Buffering wrappers for I/O traits
|
||||
|
||||
mod bufreader;
|
||||
mod bufwriter;
|
||||
mod linewriter;
|
||||
mod linewritershim;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub use bufwriter::WriterPanicked;
|
||||
use linewritershim::LineWriterShim;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter};
|
||||
use crate::io::Error;
|
||||
use crate::{error, fmt};
|
||||
|
||||
/// An error returned by [`BufWriter::into_inner`] which combines an error that
|
||||
/// happened while writing out the buffer, and the buffered writer object
|
||||
/// which may be used to recover from the condition.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // do stuff with the stream
|
||||
///
|
||||
/// // we want to get our `TcpStream` back, so let's try:
|
||||
///
|
||||
/// let stream = match stream.into_inner() {
|
||||
/// Ok(s) => s,
|
||||
/// Err(e) => {
|
||||
/// // Here, e is an IntoInnerError
|
||||
/// panic!("An error occurred");
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct IntoInnerError<W>(W, Error);
|
||||
|
||||
impl<W> IntoInnerError<W> {
|
||||
/// Constructs a new IntoInnerError
|
||||
fn new(writer: W, error: Error) -> Self {
|
||||
Self(writer, error)
|
||||
}
|
||||
|
||||
/// Helper to construct a new IntoInnerError; intended to help with
|
||||
/// adapters that wrap other adapters
|
||||
fn new_wrapped<W2>(self, f: impl FnOnce(W) -> W2) -> IntoInnerError<W2> {
|
||||
let Self(writer, error) = self;
|
||||
IntoInnerError::new(f(writer), error)
|
||||
}
|
||||
|
||||
/// Returns the error which caused the call to [`BufWriter::into_inner()`]
|
||||
/// to fail.
|
||||
///
|
||||
/// This error was returned when attempting to write the internal buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // do stuff with the stream
|
||||
///
|
||||
/// // we want to get our `TcpStream` back, so let's try:
|
||||
///
|
||||
/// let stream = match stream.into_inner() {
|
||||
/// Ok(s) => s,
|
||||
/// Err(e) => {
|
||||
/// // Here, e is an IntoInnerError, let's log the inner error.
|
||||
/// //
|
||||
/// // We'll just 'log' to stdout for this example.
|
||||
/// println!("{}", e.error());
|
||||
///
|
||||
/// panic!("An unexpected error occurred.");
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn error(&self) -> &Error {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Returns the buffered writer instance which generated the error.
|
||||
///
|
||||
/// The returned object can be used for error recovery, such as
|
||||
/// re-inspecting the buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // do stuff with the stream
|
||||
///
|
||||
/// // we want to get our `TcpStream` back, so let's try:
|
||||
///
|
||||
/// let stream = match stream.into_inner() {
|
||||
/// Ok(s) => s,
|
||||
/// Err(e) => {
|
||||
/// // Here, e is an IntoInnerError, let's re-examine the buffer:
|
||||
/// let buffer = e.into_inner();
|
||||
///
|
||||
/// // do stuff to try to recover
|
||||
///
|
||||
/// // afterwards, let's just return the stream
|
||||
/// buffer.into_inner().unwrap()
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> W {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Consumes the [`IntoInnerError`] and returns the error which caused the call to
|
||||
/// [`BufWriter::into_inner()`] to fail. Unlike `error`, this can be used to
|
||||
/// obtain ownership of the underlying error.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use std::io::{BufWriter, ErrorKind, Write};
|
||||
///
|
||||
/// let mut not_enough_space = [0u8; 10];
|
||||
/// let mut stream = BufWriter::new(not_enough_space.as_mut());
|
||||
/// write!(stream, "this cannot be actually written").unwrap();
|
||||
/// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small");
|
||||
/// let err = into_inner_err.into_error();
|
||||
/// assert_eq!(err.kind(), ErrorKind::WriteZero);
|
||||
/// ```
|
||||
#[stable(feature = "io_into_inner_error_parts", since = "1.55.0")]
|
||||
pub fn into_error(self) -> Error {
|
||||
self.1
|
||||
}
|
||||
|
||||
/// Consumes the [`IntoInnerError`] and returns the error which caused the call to
|
||||
/// [`BufWriter::into_inner()`] to fail, and the underlying writer.
|
||||
///
|
||||
/// This can be used to simply obtain ownership of the underlying error; it can also be used for
|
||||
/// advanced error recovery.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use std::io::{BufWriter, ErrorKind, Write};
|
||||
///
|
||||
/// let mut not_enough_space = [0u8; 10];
|
||||
/// let mut stream = BufWriter::new(not_enough_space.as_mut());
|
||||
/// write!(stream, "this cannot be actually written").unwrap();
|
||||
/// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small");
|
||||
/// let (err, recovered_writer) = into_inner_err.into_parts();
|
||||
/// assert_eq!(err.kind(), ErrorKind::WriteZero);
|
||||
/// assert_eq!(recovered_writer.buffer(), b"t be actually written");
|
||||
/// ```
|
||||
#[stable(feature = "io_into_inner_error_parts", since = "1.55.0")]
|
||||
pub fn into_parts(self) -> (Error, W) {
|
||||
(self.1, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W> From<IntoInnerError<W>> for Error {
|
||||
fn from(iie: IntoInnerError<W>) -> Error {
|
||||
iie.1
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: Send + fmt::Debug> error::Error for IntoInnerError<W> {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W> fmt::Display for IntoInnerError<W> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.error().fmt(f)
|
||||
}
|
||||
}
|
||||
297
crates/std/src/io/copy.rs
Normal file
297
crates/std/src/io/copy.rs
Normal file
@@ -0,0 +1,297 @@
|
||||
use super::{BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, Read, Result, Write};
|
||||
use crate::alloc::Allocator;
|
||||
use crate::cmp;
|
||||
use alloc_crate::collections::VecDeque;
|
||||
use crate::io::IoSlice;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::sys::io::{CopyState, kernel_copy};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// Copies the entire contents of a reader into a writer.
|
||||
///
|
||||
/// This function will continuously read data from `reader` and then
|
||||
/// write it into `writer` in a streaming fashion until `reader`
|
||||
/// returns EOF.
|
||||
///
|
||||
/// On success, the total number of bytes that were copied from
|
||||
/// `reader` to `writer` is returned.
|
||||
///
|
||||
/// If you want to copy the contents of one file to another and you’re
|
||||
/// working with filesystem paths, see the [`fs::copy`] function.
|
||||
///
|
||||
/// [`fs::copy`]: crate::fs::copy
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error immediately if any call to [`read`] or
|
||||
/// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are
|
||||
/// handled by this function and the underlying operation is retried.
|
||||
///
|
||||
/// [`read`]: Read::read
|
||||
/// [`write`]: Write::write
|
||||
/// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut reader: &[u8] = b"hello";
|
||||
/// let mut writer: Vec<u8> = vec![];
|
||||
///
|
||||
/// io::copy(&mut reader, &mut writer)?;
|
||||
///
|
||||
/// assert_eq!(&b"hello"[..], &writer[..]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// On Linux (including Android), this function uses `copy_file_range(2)`,
|
||||
/// `sendfile(2)` or `splice(2)` syscalls to move data directly between file
|
||||
/// descriptors if possible.
|
||||
///
|
||||
/// Note that platform-specific behavior [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: crate::io#platform-specific-behavior
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64>
|
||||
where
|
||||
R: Read,
|
||||
W: Write,
|
||||
{
|
||||
match kernel_copy(reader, writer)? {
|
||||
CopyState::Ended(copied) => Ok(copied),
|
||||
CopyState::Fallback(copied) => {
|
||||
generic_copy(reader, writer).map(|additional| copied + additional)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The userspace read-write-loop implementation of `io::copy` that is used when
|
||||
/// OS-specific specializations for copy offloading are not available or not applicable.
|
||||
fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64>
|
||||
where
|
||||
R: Read,
|
||||
W: Write,
|
||||
{
|
||||
let read_buf = BufferedReaderSpec::buffer_size(reader);
|
||||
let write_buf = BufferedWriterSpec::buffer_size(writer);
|
||||
|
||||
if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf {
|
||||
return BufferedReaderSpec::copy_to(reader, writer);
|
||||
}
|
||||
|
||||
BufferedWriterSpec::copy_from(writer, reader)
|
||||
}
|
||||
|
||||
/// Specialization of the read-write loop that reuses the internal
|
||||
/// buffer of a BufReader. If there's no buffer then the writer side
|
||||
/// should be used instead.
|
||||
trait BufferedReaderSpec {
|
||||
fn buffer_size(&self) -> usize;
|
||||
|
||||
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64>;
|
||||
}
|
||||
|
||||
impl<T> BufferedReaderSpec for T
|
||||
where
|
||||
Self: Read,
|
||||
T: ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
default fn buffer_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result<u64> {
|
||||
unreachable!("only called from specializations")
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferedReaderSpec for &[u8] {
|
||||
fn buffer_size(&self) -> usize {
|
||||
// prefer this specialization since the source "buffer" is all we'll ever need,
|
||||
// even if it's small
|
||||
usize::MAX
|
||||
}
|
||||
|
||||
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
|
||||
let len = self.len();
|
||||
to.write_all(self)?;
|
||||
*self = &self[len..];
|
||||
Ok(len as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Allocator> BufferedReaderSpec for VecDeque<u8, A> {
|
||||
fn buffer_size(&self) -> usize {
|
||||
// prefer this specialization since the source "buffer" is all we'll ever need,
|
||||
// even if it's small
|
||||
usize::MAX
|
||||
}
|
||||
|
||||
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
|
||||
let len = self.len();
|
||||
let (front, back) = self.as_slices();
|
||||
let bufs = &mut [IoSlice::new(front), IoSlice::new(back)];
|
||||
to.write_all_vectored(bufs)?;
|
||||
self.clear();
|
||||
Ok(len as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> BufferedReaderSpec for BufReader<I>
|
||||
where
|
||||
Self: Read,
|
||||
I: ?Sized,
|
||||
{
|
||||
fn buffer_size(&self) -> usize {
|
||||
self.capacity()
|
||||
}
|
||||
|
||||
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
|
||||
let mut len = 0;
|
||||
|
||||
loop {
|
||||
// Hack: this relies on `impl Read for BufReader` always calling fill_buf
|
||||
// if the buffer is empty, even for empty slices.
|
||||
// It can't be called directly here since specialization prevents us
|
||||
// from adding I: Read
|
||||
match self.read(&mut []) {
|
||||
Ok(_) => {}
|
||||
Err(e) if e.is_interrupted() => continue,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
let buf = self.buffer();
|
||||
if self.buffer().len() == 0 {
|
||||
return Ok(len);
|
||||
}
|
||||
|
||||
// In case the writer side is a BufWriter then its write_all
|
||||
// implements an optimization that passes through large
|
||||
// buffers to the underlying writer. That code path is #[cold]
|
||||
// but we're still avoiding redundant memcopies when doing
|
||||
// a copy between buffered inputs and outputs.
|
||||
to.write_all(buf)?;
|
||||
len += buf.len() as u64;
|
||||
self.discard_buffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialization of the read-write loop that either uses a stack buffer
|
||||
/// or reuses the internal buffer of a BufWriter
|
||||
trait BufferedWriterSpec: Write {
|
||||
fn buffer_size(&self) -> usize;
|
||||
|
||||
fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64>;
|
||||
}
|
||||
|
||||
impl<W: Write + ?Sized> BufferedWriterSpec for W {
|
||||
#[inline]
|
||||
default fn buffer_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
default fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
|
||||
stack_buffer_copy(reader, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> {
|
||||
fn buffer_size(&self) -> usize {
|
||||
self.capacity()
|
||||
}
|
||||
|
||||
fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
|
||||
if self.capacity() < DEFAULT_BUF_SIZE {
|
||||
return stack_buffer_copy(reader, self);
|
||||
}
|
||||
|
||||
let mut len = 0;
|
||||
let mut init = 0;
|
||||
|
||||
loop {
|
||||
let buf = self.buffer_mut();
|
||||
let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into();
|
||||
|
||||
unsafe {
|
||||
// SAFETY: init is either 0 or the init_len from the previous iteration.
|
||||
read_buf.set_init(init);
|
||||
}
|
||||
|
||||
if read_buf.capacity() >= DEFAULT_BUF_SIZE {
|
||||
let mut cursor = read_buf.unfilled();
|
||||
match reader.read_buf(cursor.reborrow()) {
|
||||
Ok(()) => {
|
||||
let bytes_read = cursor.written();
|
||||
|
||||
if bytes_read == 0 {
|
||||
return Ok(len);
|
||||
}
|
||||
|
||||
init = read_buf.init_len() - bytes_read;
|
||||
len += bytes_read as u64;
|
||||
|
||||
// SAFETY: BorrowedBuf guarantees all of its filled bytes are init
|
||||
unsafe { buf.set_len(buf.len() + bytes_read) };
|
||||
|
||||
// Read again if the buffer still has enough capacity, as BufWriter itself would do
|
||||
// This will occur if the reader returns short reads
|
||||
}
|
||||
Err(ref e) if e.is_interrupted() => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
} else {
|
||||
// All the bytes that were already in the buffer are initialized,
|
||||
// treat them as such when the buffer is flushed.
|
||||
init += buf.len();
|
||||
|
||||
self.flush_buf()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferedWriterSpec for Vec<u8> {
|
||||
fn buffer_size(&self) -> usize {
|
||||
cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len())
|
||||
}
|
||||
|
||||
fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
|
||||
reader.read_to_end(self).map(|bytes| u64::try_from(bytes).expect("usize overflowed u64"))
|
||||
}
|
||||
}
|
||||
|
||||
fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
|
||||
reader: &mut R,
|
||||
writer: &mut W,
|
||||
) -> Result<u64> {
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); DEFAULT_BUF_SIZE];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
|
||||
let mut len = 0;
|
||||
|
||||
loop {
|
||||
match reader.read_buf(buf.unfilled()) {
|
||||
Ok(()) => {}
|
||||
Err(e) if e.is_interrupted() => continue,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
if buf.filled().is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
len += buf.filled().len() as u64;
|
||||
writer.write_all(buf.filled())?;
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
147
crates/std/src/io/copy/tests.rs
Normal file
147
crates/std/src/io/copy/tests.rs
Normal file
@@ -0,0 +1,147 @@
|
||||
use crate::cmp::{max, min};
|
||||
use alloc_crate::collections::VecDeque;
|
||||
use crate::io;
|
||||
use crate::io::*;
|
||||
|
||||
#[test]
|
||||
fn copy_copies() {
|
||||
let mut r = repeat(0).take(4);
|
||||
let mut w = sink();
|
||||
assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
|
||||
|
||||
let mut r = repeat(0).take(1 << 17);
|
||||
assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
|
||||
}
|
||||
|
||||
struct ShortReader {
|
||||
cap: usize,
|
||||
read_size: usize,
|
||||
observed_buffer: usize,
|
||||
}
|
||||
|
||||
impl Read for ShortReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let bytes = min(self.cap, self.read_size).min(buf.len());
|
||||
self.cap -= bytes;
|
||||
self.observed_buffer = max(self.observed_buffer, buf.len());
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
struct WriteObserver {
|
||||
observed_buffer: usize,
|
||||
}
|
||||
|
||||
impl Write for WriteObserver {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
self.observed_buffer = max(self.observed_buffer, buf.len());
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_specializes_bufwriter() {
|
||||
let cap = 117 * 1024;
|
||||
let buf_sz = 16 * 1024;
|
||||
let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
|
||||
let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 });
|
||||
assert_eq!(
|
||||
copy(&mut r, &mut w).unwrap(),
|
||||
cap as u64,
|
||||
"expected the whole capacity to be copied"
|
||||
);
|
||||
assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader");
|
||||
assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_specializes_bufreader() {
|
||||
let mut source = vec![0; 768 * 1024];
|
||||
source[1] = 42;
|
||||
let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source));
|
||||
|
||||
let mut sink = Vec::new();
|
||||
assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64);
|
||||
assert_eq!(source.as_slice(), sink.as_slice());
|
||||
|
||||
let buf_sz = 71 * 1024;
|
||||
assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition");
|
||||
|
||||
let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source));
|
||||
let mut sink = WriteObserver { observed_buffer: 0 };
|
||||
assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64);
|
||||
assert_eq!(
|
||||
sink.observed_buffer, buf_sz,
|
||||
"expected a large buffer to be provided to the writer"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_specializes_to_vec() {
|
||||
let cap = DEFAULT_BUF_SIZE * 10;
|
||||
let mut source = ShortReader { cap, observed_buffer: 0, read_size: DEFAULT_BUF_SIZE };
|
||||
let mut sink = Vec::new();
|
||||
let copied = io::copy(&mut source, &mut sink).unwrap();
|
||||
assert_eq!(cap as u64, copied);
|
||||
assert_eq!(sink.len() as u64, copied);
|
||||
assert!(
|
||||
source.observed_buffer > DEFAULT_BUF_SIZE,
|
||||
"expected a large buffer to be provided to the reader, got {}",
|
||||
source.observed_buffer
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_specializes_from_vecdeque() {
|
||||
let mut source = VecDeque::with_capacity(100 * 1024);
|
||||
for _ in 0..20 * 1024 {
|
||||
source.push_front(0);
|
||||
}
|
||||
for _ in 0..20 * 1024 {
|
||||
source.push_back(0);
|
||||
}
|
||||
let mut sink = WriteObserver { observed_buffer: 0 };
|
||||
assert_eq!(40 * 1024u64, io::copy(&mut source, &mut sink).unwrap());
|
||||
assert_eq!(20 * 1024, sink.observed_buffer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_specializes_from_slice() {
|
||||
let mut source = [1; 60 * 1024].as_slice();
|
||||
let mut sink = WriteObserver { observed_buffer: 0 };
|
||||
assert_eq!(60 * 1024u64, io::copy(&mut source, &mut sink).unwrap());
|
||||
assert_eq!(60 * 1024, sink.observed_buffer);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
mod io_benches {
|
||||
use test::Bencher;
|
||||
|
||||
use crate::fs::{File, OpenOptions};
|
||||
use crate::io::BufReader;
|
||||
use crate::io::prelude::*;
|
||||
|
||||
#[bench]
|
||||
#[cfg_attr(target_os = "emscripten", ignore)] // no /dev
|
||||
fn bench_copy_buf_reader(b: &mut Bencher) {
|
||||
let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed");
|
||||
// use dyn to avoid specializations unrelated to readbuf
|
||||
let dyn_in = &mut file_in as &mut dyn Read;
|
||||
let mut reader = BufReader::with_capacity(256 * 1024, dyn_in.take(0));
|
||||
let mut writer =
|
||||
OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed");
|
||||
|
||||
const BYTES: u64 = 1024 * 1024;
|
||||
|
||||
b.bytes = BYTES;
|
||||
|
||||
b.iter(|| {
|
||||
reader.get_mut().set_limit(BYTES);
|
||||
crate::io::copy(&mut reader, &mut writer).unwrap()
|
||||
});
|
||||
}
|
||||
}
|
||||
757
crates/std/src/io/cursor.rs
Normal file
757
crates/std/src/io/cursor.rs
Normal file
@@ -0,0 +1,757 @@
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::alloc::Allocator;
|
||||
use crate::cmp;
|
||||
use crate::io::prelude::*;
|
||||
use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom};
|
||||
|
||||
/// A `Cursor` wraps an in-memory buffer and provides it with a
|
||||
/// [`Seek`] implementation.
|
||||
///
|
||||
/// `Cursor`s are used with in-memory buffers, anything implementing
|
||||
/// <code>[AsRef]<\[u8]></code>, to allow them to implement [`Read`] and/or [`Write`],
|
||||
/// allowing these buffers to be used anywhere you might use a reader or writer
|
||||
/// that does actual I/O.
|
||||
///
|
||||
/// The standard library implements some I/O traits on various types which
|
||||
/// are commonly used as a buffer, like <code>Cursor<[Vec]\<u8>></code> and
|
||||
/// <code>Cursor<[&\[u8\]][bytes]></code>.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// We may want to write bytes to a [`File`] in our production
|
||||
/// code, but use an in-memory buffer in our tests. We can do this with
|
||||
/// `Cursor`:
|
||||
///
|
||||
/// [bytes]: crate::slice "slice"
|
||||
/// [`File`]: crate::fs::File
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::{self, SeekFrom};
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// // a library function we've written
|
||||
/// fn write_ten_bytes_at_end<W: Write + Seek>(mut writer: W) -> io::Result<()> {
|
||||
/// writer.seek(SeekFrom::End(-10))?;
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// writer.write(&[i])?;
|
||||
/// }
|
||||
///
|
||||
/// // all went well
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// # fn foo() -> io::Result<()> {
|
||||
/// // Here's some code that uses this library function.
|
||||
/// //
|
||||
/// // We might want to use a BufReader here for efficiency, but let's
|
||||
/// // keep this example focused.
|
||||
/// let mut file = File::create("foo.txt")?;
|
||||
/// // First, we need to allocate 10 bytes to be able to write into.
|
||||
/// file.set_len(10)?;
|
||||
///
|
||||
/// write_ten_bytes_at_end(&mut file)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
///
|
||||
/// // now let's write a test
|
||||
/// #[test]
|
||||
/// fn test_writes_bytes() {
|
||||
/// // setting up a real File is much slower than an in-memory buffer,
|
||||
/// // let's use a cursor instead
|
||||
/// use std::io::Cursor;
|
||||
/// let mut buff = Cursor::new(vec![0; 15]);
|
||||
///
|
||||
/// write_ten_bytes_at_end(&mut buff).unwrap();
|
||||
///
|
||||
/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[derive(Debug, Default, Eq, PartialEq)]
|
||||
pub struct Cursor<T> {
|
||||
inner: T,
|
||||
pos: u64,
|
||||
}
|
||||
|
||||
impl<T> Cursor<T> {
|
||||
/// Creates a new cursor wrapping the provided underlying in-memory buffer.
|
||||
///
|
||||
/// Cursor initial position is `0` even if underlying buffer (e.g., [`Vec`])
|
||||
/// is not empty. So writing to cursor starts with overwriting [`Vec`]
|
||||
/// content, not with appending to it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn new(inner: T) -> Cursor<T> {
|
||||
Cursor { pos: 0, inner }
|
||||
}
|
||||
|
||||
/// Consumes this cursor, returning the underlying value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let vec = buff.into_inner();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying value in this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let reference = buff.get_ref();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying value in this cursor.
|
||||
///
|
||||
/// Care should be taken to avoid modifying the internal I/O state of the
|
||||
/// underlying value as it may corrupt this cursor's position.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let reference = buff.get_mut();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_mut_cursor", since = "1.86.0")]
|
||||
pub const fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Returns the current position of this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::SeekFrom;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.position(), 0);
|
||||
///
|
||||
/// buff.seek(SeekFrom::Current(2)).unwrap();
|
||||
/// assert_eq!(buff.position(), 2);
|
||||
///
|
||||
/// buff.seek(SeekFrom::Current(-1)).unwrap();
|
||||
/// assert_eq!(buff.position(), 1);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn position(&self) -> u64 {
|
||||
self.pos
|
||||
}
|
||||
|
||||
/// Sets the position of this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.position(), 0);
|
||||
///
|
||||
/// buff.set_position(2);
|
||||
/// assert_eq!(buff.position(), 2);
|
||||
///
|
||||
/// buff.set_position(4);
|
||||
/// assert_eq!(buff.position(), 4);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_mut_cursor", since = "1.86.0")]
|
||||
pub const fn set_position(&mut self, pos: u64) {
|
||||
self.pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
/// Splits the underlying slice at the cursor position and returns them.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(cursor_split)]
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.split(), ([].as_slice(), [1, 2, 3, 4, 5].as_slice()));
|
||||
///
|
||||
/// buff.set_position(2);
|
||||
/// assert_eq!(buff.split(), ([1, 2].as_slice(), [3, 4, 5].as_slice()));
|
||||
///
|
||||
/// buff.set_position(6);
|
||||
/// assert_eq!(buff.split(), ([1, 2, 3, 4, 5].as_slice(), [].as_slice()));
|
||||
/// ```
|
||||
#[unstable(feature = "cursor_split", issue = "86369")]
|
||||
pub fn split(&self) -> (&[u8], &[u8]) {
|
||||
let slice = self.inner.as_ref();
|
||||
let pos = self.pos.min(slice.len() as u64);
|
||||
slice.split_at(pos as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Cursor<T>
|
||||
where
|
||||
T: AsMut<[u8]>,
|
||||
{
|
||||
/// Splits the underlying slice at the cursor position and returns them
|
||||
/// mutably.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(cursor_split)]
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.split_mut(), ([].as_mut_slice(), [1, 2, 3, 4, 5].as_mut_slice()));
|
||||
///
|
||||
/// buff.set_position(2);
|
||||
/// assert_eq!(buff.split_mut(), ([1, 2].as_mut_slice(), [3, 4, 5].as_mut_slice()));
|
||||
///
|
||||
/// buff.set_position(6);
|
||||
/// assert_eq!(buff.split_mut(), ([1, 2, 3, 4, 5].as_mut_slice(), [].as_mut_slice()));
|
||||
/// ```
|
||||
#[unstable(feature = "cursor_split", issue = "86369")]
|
||||
pub fn split_mut(&mut self) -> (&mut [u8], &mut [u8]) {
|
||||
let slice = self.inner.as_mut();
|
||||
let pos = self.pos.min(slice.len() as u64);
|
||||
slice.split_at_mut(pos as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Clone for Cursor<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Cursor { inner: self.inner.clone(), pos: self.pos }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_from(&mut self, other: &Self) {
|
||||
self.inner.clone_from(&other.inner);
|
||||
self.pos = other.pos;
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> io::Seek for Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> {
|
||||
let (base_pos, offset) = match style {
|
||||
SeekFrom::Start(n) => {
|
||||
self.pos = n;
|
||||
return Ok(n);
|
||||
}
|
||||
SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n),
|
||||
SeekFrom::Current(n) => (self.pos, n),
|
||||
};
|
||||
match base_pos.checked_add_signed(offset) {
|
||||
Some(n) => {
|
||||
self.pos = n;
|
||||
Ok(self.pos)
|
||||
}
|
||||
None => Err(io::const_error!(
|
||||
ErrorKind::InvalidInput,
|
||||
"invalid seek to a negative or overflowing position",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
Ok(self.inner.as_ref().len() as u64)
|
||||
}
|
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
Ok(self.pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Read for Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let n = Read::read(&mut Cursor::split(self).1, buf)?;
|
||||
self.pos += n as u64;
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let prev_written = cursor.written();
|
||||
|
||||
Read::read_buf(&mut Cursor::split(self).1, cursor.reborrow())?;
|
||||
|
||||
self.pos += (cursor.written() - prev_written) as u64;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let mut nread = 0;
|
||||
for buf in bufs {
|
||||
let n = self.read(buf)?;
|
||||
nread += n;
|
||||
if n < buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
let result = Read::read_exact(&mut Cursor::split(self).1, buf);
|
||||
|
||||
match result {
|
||||
Ok(_) => self.pos += buf.len() as u64,
|
||||
// The only possible error condition is EOF, so place the cursor at "EOF"
|
||||
Err(_) => self.pos = self.inner.as_ref().len() as u64,
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let prev_written = cursor.written();
|
||||
|
||||
let result = Read::read_buf_exact(&mut Cursor::split(self).1, cursor.reborrow());
|
||||
self.pos += (cursor.written() - prev_written) as u64;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let content = Cursor::split(self).1;
|
||||
let len = content.len();
|
||||
buf.try_reserve(len)?;
|
||||
buf.extend_from_slice(content);
|
||||
self.pos += len as u64;
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
let content =
|
||||
crate::str::from_utf8(Cursor::split(self).1).map_err(|_| io::Error::INVALID_UTF8)?;
|
||||
let len = content.len();
|
||||
buf.try_reserve(len)?;
|
||||
buf.push_str(content);
|
||||
self.pos += len as u64;
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> BufRead for Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
Ok(Cursor::split(self).1)
|
||||
}
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.pos += amt as u64;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-resizing write implementation
|
||||
#[inline]
|
||||
fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<usize> {
|
||||
let pos = cmp::min(*pos_mut, slice.len() as u64);
|
||||
let amt = (&mut slice[(pos as usize)..]).write(buf)?;
|
||||
*pos_mut += amt as u64;
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn slice_write_vectored(
|
||||
pos_mut: &mut u64,
|
||||
slice: &mut [u8],
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> io::Result<usize> {
|
||||
let mut nwritten = 0;
|
||||
for buf in bufs {
|
||||
let n = slice_write(pos_mut, slice, buf)?;
|
||||
nwritten += n;
|
||||
if n < buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(nwritten)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> {
|
||||
let n = slice_write(pos_mut, slice, buf)?;
|
||||
if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn slice_write_all_vectored(
|
||||
pos_mut: &mut u64,
|
||||
slice: &mut [u8],
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> io::Result<()> {
|
||||
for buf in bufs {
|
||||
let n = slice_write(pos_mut, slice, buf)?;
|
||||
if n < buf.len() {
|
||||
return Err(io::Error::WRITE_ALL_EOF);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reserves the required space, and pads the vec with 0s if necessary.
|
||||
fn reserve_and_pad<A: Allocator>(
|
||||
pos_mut: &mut u64,
|
||||
vec: &mut Vec<u8, A>,
|
||||
buf_len: usize,
|
||||
) -> io::Result<usize> {
|
||||
let pos: usize = (*pos_mut).try_into().map_err(|_| {
|
||||
io::const_error!(
|
||||
ErrorKind::InvalidInput,
|
||||
"cursor position exceeds maximum possible vector length",
|
||||
)
|
||||
})?;
|
||||
|
||||
// For safety reasons, we don't want these numbers to overflow
|
||||
// otherwise our allocation won't be enough
|
||||
let desired_cap = pos.saturating_add(buf_len);
|
||||
if desired_cap > vec.capacity() {
|
||||
// We want our vec's total capacity
|
||||
// to have room for (pos+buf_len) bytes. Reserve allocates
|
||||
// based on additional elements from the length, so we need to
|
||||
// reserve the difference
|
||||
vec.reserve(desired_cap - vec.len());
|
||||
}
|
||||
// Pad if pos is above the current len.
|
||||
if pos > vec.len() {
|
||||
let diff = pos - vec.len();
|
||||
// Unfortunately, `resize()` would suffice but the optimiser does not
|
||||
// realise the `reserve` it does can be eliminated. So we do it manually
|
||||
// to eliminate that extra branch
|
||||
let spare = vec.spare_capacity_mut();
|
||||
debug_assert!(spare.len() >= diff);
|
||||
// Safety: we have allocated enough capacity for this.
|
||||
// And we are only writing, not reading
|
||||
unsafe {
|
||||
spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0));
|
||||
vec.set_len(pos);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(pos)
|
||||
}
|
||||
|
||||
/// Writes the slice to the vec without allocating.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `vec` must have `buf.len()` spare capacity.
|
||||
unsafe fn vec_write_all_unchecked<A>(pos: usize, vec: &mut Vec<u8, A>, buf: &[u8]) -> usize
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
debug_assert!(vec.capacity() >= pos + buf.len());
|
||||
unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) };
|
||||
pos + buf.len()
|
||||
}
|
||||
|
||||
/// Resizing `write_all` implementation for [`Cursor`].
|
||||
///
|
||||
/// Cursor is allowed to have a pre-allocated and initialised
|
||||
/// vector body, but with a position of 0. This means the [`Write`]
|
||||
/// will overwrite the contents of the vec.
|
||||
///
|
||||
/// This also allows for the vec body to be empty, but with a position of N.
|
||||
/// This means that [`Write`] will pad the vec with 0 initially,
|
||||
/// before writing anything from that point
|
||||
fn vec_write_all<A>(pos_mut: &mut u64, vec: &mut Vec<u8, A>, buf: &[u8]) -> io::Result<usize>
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
let buf_len = buf.len();
|
||||
let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?;
|
||||
|
||||
// Write the buf then progress the vec forward if necessary
|
||||
// Safety: we have ensured that the capacity is available
|
||||
// and that all bytes get written up to pos
|
||||
unsafe {
|
||||
pos = vec_write_all_unchecked(pos, vec, buf);
|
||||
if pos > vec.len() {
|
||||
vec.set_len(pos);
|
||||
}
|
||||
};
|
||||
|
||||
// Bump us forward
|
||||
*pos_mut += buf_len as u64;
|
||||
Ok(buf_len)
|
||||
}
|
||||
|
||||
/// Resizing `write_all_vectored` implementation for [`Cursor`].
|
||||
///
|
||||
/// Cursor is allowed to have a pre-allocated and initialised
|
||||
/// vector body, but with a position of 0. This means the [`Write`]
|
||||
/// will overwrite the contents of the vec.
|
||||
///
|
||||
/// This also allows for the vec body to be empty, but with a position of N.
|
||||
/// This means that [`Write`] will pad the vec with 0 initially,
|
||||
/// before writing anything from that point
|
||||
fn vec_write_all_vectored<A>(
|
||||
pos_mut: &mut u64,
|
||||
vec: &mut Vec<u8, A>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> io::Result<usize>
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
// For safety reasons, we don't want this sum to overflow ever.
|
||||
// If this saturates, the reserve should panic to avoid any unsound writing.
|
||||
let buf_len = bufs.iter().fold(0usize, |a, b| a.saturating_add(b.len()));
|
||||
let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?;
|
||||
|
||||
// Write the buf then progress the vec forward if necessary
|
||||
// Safety: we have ensured that the capacity is available
|
||||
// and that all bytes get written up to the last pos
|
||||
unsafe {
|
||||
for buf in bufs {
|
||||
pos = vec_write_all_unchecked(pos, vec, buf);
|
||||
}
|
||||
if pos > vec.len() {
|
||||
vec.set_len(pos);
|
||||
}
|
||||
}
|
||||
|
||||
// Bump us forward
|
||||
*pos_mut += buf_len as u64;
|
||||
Ok(buf_len)
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Write for Cursor<&mut [u8]> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
slice_write(&mut self.pos, self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
slice_write_vectored(&mut self.pos, self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
slice_write_all(&mut self.pos, self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
slice_write_all_vectored(&mut self.pos, self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cursor_mut_vec", since = "1.25.0")]
|
||||
impl<A> Write for Cursor<&mut Vec<u8, A>>
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
vec_write_all(&mut self.pos, self.inner, buf)
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
vec_write_all_vectored(&mut self.pos, self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
vec_write_all(&mut self.pos, self.inner, buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
vec_write_all_vectored(&mut self.pos, self.inner, bufs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<A> Write for Cursor<Vec<u8, A>>
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
vec_write_all(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
vec_write_all_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
vec_write_all(&mut self.pos, &mut self.inner, buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
vec_write_all_vectored(&mut self.pos, &mut self.inner, bufs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cursor_box_slice", since = "1.5.0")]
|
||||
impl<A> Write for Cursor<Box<[u8], A>>
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
slice_write(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
slice_write_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
slice_write_all(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
slice_write_all_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cursor_array", since = "1.61.0")]
|
||||
impl<const N: usize> Write for Cursor<[u8; N]> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
slice_write(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
slice_write_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
slice_write_all(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
slice_write_all_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
567
crates/std/src/io/cursor/tests.rs
Normal file
567
crates/std/src/io/cursor/tests.rs
Normal file
@@ -0,0 +1,567 @@
|
||||
use crate::io::prelude::*;
|
||||
use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom};
|
||||
|
||||
#[test]
|
||||
fn test_vec_writer() {
|
||||
let mut writer = Vec::new();
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(
|
||||
writer
|
||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(writer, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_writer() {
|
||||
let mut writer = Cursor::new(Vec::new());
|
||||
writer.set_position(10);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(
|
||||
writer
|
||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(&writer.get_ref()[..10], &[0; 10]);
|
||||
assert_eq!(&writer.get_ref()[10..], b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_writer_preallocated() {
|
||||
let mut writer = Cursor::new(vec![0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 10]);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_mut_writer() {
|
||||
let mut vec = Vec::new();
|
||||
let mut writer = Cursor::new(&mut vec);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(
|
||||
writer
|
||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
}
|
||||
|
||||
fn test_slice_writer<T>(writer: &mut Cursor<T>)
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
Cursor<T>: Write,
|
||||
{
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(writer.position(), 8);
|
||||
assert_eq!(writer.write(&[]).unwrap(), 0);
|
||||
assert_eq!(writer.position(), 8);
|
||||
|
||||
assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[10]).unwrap(), 0);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||
assert_eq!(writer.get_ref().as_ref(), b);
|
||||
}
|
||||
|
||||
fn test_slice_writer_vectored<T>(writer: &mut Cursor<T>)
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
Cursor<T>: Write,
|
||||
{
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(
|
||||
writer.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7]),]).unwrap(),
|
||||
7,
|
||||
);
|
||||
assert_eq!(writer.position(), 8);
|
||||
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
||||
assert_eq!(writer.position(), 8);
|
||||
|
||||
assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1);
|
||||
assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||
assert_eq!(writer.get_ref().as_ref(), b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_box_slice_writer() {
|
||||
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
||||
test_slice_writer(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_box_slice_writer_vectored() {
|
||||
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
||||
test_slice_writer_vectored(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_writer() {
|
||||
let mut writer = Cursor::new([0u8; 9]);
|
||||
test_slice_writer(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_writer_vectored() {
|
||||
let mut writer = Cursor::new([0u8; 9]);
|
||||
test_slice_writer_vectored(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer() {
|
||||
let mut buf = [0 as u8; 9];
|
||||
let mut writer = Cursor::new(&mut buf[..]);
|
||||
test_slice_writer(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer_vectored() {
|
||||
let mut buf = [0 as u8; 9];
|
||||
let mut writer = Cursor::new(&mut buf[..]);
|
||||
test_slice_writer_vectored(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer_seek() {
|
||||
let mut buf = [0 as u8; 8];
|
||||
{
|
||||
let mut writer = Cursor::new(&mut buf[..]);
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[1]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2);
|
||||
assert_eq!(writer.position(), 2);
|
||||
assert_eq!(writer.write(&[2]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 3);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(writer.write(&[3]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 2);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
|
||||
assert_eq!(writer.position(), 7);
|
||||
assert_eq!(writer.write(&[4]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 8);
|
||||
}
|
||||
let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
|
||||
assert_eq!(buf, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer_error() {
|
||||
let mut buf = [0 as u8; 2];
|
||||
let mut writer = Cursor::new(&mut buf[..]);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[0, 0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[0, 0]).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_reader() {
|
||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||
assert_eq!(reader.position(), 5);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(buf, b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_reader_vectored() {
|
||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(
|
||||
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
||||
1,
|
||||
);
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf1 = [0; 4];
|
||||
let mut buf2 = [0; 4];
|
||||
assert_eq!(
|
||||
reader
|
||||
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),])
|
||||
.unwrap(),
|
||||
7,
|
||||
);
|
||||
let b1: &[_] = &[1, 2, 3, 4];
|
||||
let b2: &[_] = &[5, 6, 7];
|
||||
assert_eq!(buf1, b1);
|
||||
assert_eq!(&buf2[..3], b2);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boxed_slice_reader() {
|
||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||
assert_eq!(reader.position(), 5);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(buf, b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boxed_slice_reader_vectored() {
|
||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(
|
||||
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
||||
1,
|
||||
);
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf1 = [0; 4];
|
||||
let mut buf2 = [0; 4];
|
||||
assert_eq!(
|
||||
reader
|
||||
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
|
||||
.unwrap(),
|
||||
7,
|
||||
);
|
||||
let b1: &[_] = &[1, 2, 3, 4];
|
||||
let b2: &[_] = &[5, 6, 7];
|
||||
assert_eq!(buf1, b1);
|
||||
assert_eq!(&buf2[..3], b2);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_end() {
|
||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
let mut v = Vec::new();
|
||||
reader.read_to_end(&mut v).unwrap();
|
||||
assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice_reader() {
|
||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let reader = &mut &in_buf[..];
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||
assert_eq!(reader.len(), 7);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(&buf[..], b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||
assert_eq!(reader.len(), 3);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(&buf[..], b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice_reader_vectored() {
|
||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let reader = &mut &in_buf[..];
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(
|
||||
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
||||
1,
|
||||
);
|
||||
assert_eq!(reader.len(), 7);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf1 = [0; 4];
|
||||
let mut buf2 = [0; 4];
|
||||
assert_eq!(
|
||||
reader
|
||||
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
|
||||
.unwrap(),
|
||||
7,
|
||||
);
|
||||
let b1: &[_] = &[1, 2, 3, 4];
|
||||
let b2: &[_] = &[5, 6, 7];
|
||||
assert_eq!(buf1, b1);
|
||||
assert_eq!(&buf2[..3], b2);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_exact() {
|
||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let reader = &mut &in_buf[..];
|
||||
let mut buf = [];
|
||||
assert!(reader.read_exact(&mut buf).is_ok());
|
||||
let mut buf = [8];
|
||||
assert!(reader.read_exact(&mut buf).is_ok());
|
||||
assert_eq!(buf[0], 0);
|
||||
assert_eq!(reader.len(), 7);
|
||||
let mut buf = [0, 0, 0, 0, 0, 0, 0];
|
||||
assert!(reader.read_exact(&mut buf).is_ok());
|
||||
assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]);
|
||||
assert_eq!(reader.len(), 0);
|
||||
let mut buf = [0];
|
||||
assert!(reader.read_exact(&mut buf).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_reader() {
|
||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let mut reader = Cursor::new(&in_buf[..]);
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||
assert_eq!(reader.position(), 5);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(buf, b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_past_end() {
|
||||
let buf = [0xff];
|
||||
let mut r = Cursor::new(&buf[..]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||
assert_eq!(r.read(&mut [0]).unwrap(), 0);
|
||||
|
||||
let mut r = Cursor::new(vec![10]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||
assert_eq!(r.read(&mut [0]).unwrap(), 0);
|
||||
|
||||
let mut buf = [0];
|
||||
let mut r = Cursor::new(&mut buf[..]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||
assert_eq!(r.write(&[3]).unwrap(), 0);
|
||||
|
||||
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||
assert_eq!(r.write(&[3]).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_past_i64() {
|
||||
let buf = [0xff];
|
||||
let mut r = Cursor::new(&buf[..]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||
|
||||
let mut r = Cursor::new(vec![10]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||
|
||||
let mut buf = [0];
|
||||
let mut r = Cursor::new(&mut buf[..]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||
|
||||
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_before_0() {
|
||||
let buf = [0xff];
|
||||
let mut r = Cursor::new(&buf[..]);
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
|
||||
let mut r = Cursor::new(vec![10]);
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
|
||||
let mut buf = [0];
|
||||
let mut r = Cursor::new(&mut buf[..]);
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
|
||||
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seekable_mem_writer() {
|
||||
let mut writer = Cursor::new(Vec::<u8>::new());
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(writer.position(), 8);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0);
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[3, 4]).unwrap(), 2);
|
||||
let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[0, 1]).unwrap(), 2);
|
||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
|
||||
assert_eq!(writer.write(&[1, 2]).unwrap(), 2);
|
||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10);
|
||||
assert_eq!(writer.write(&[1]).unwrap(), 1);
|
||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_seek_past_end() {
|
||||
let mut r = Cursor::new(Vec::new());
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||
assert_eq!(r.write(&[3]).unwrap(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_seek_before_0() {
|
||||
let mut r = Cursor::new(Vec::new());
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
fn vec_seek_and_write_past_usize_max() {
|
||||
let mut c = Cursor::new(Vec::new());
|
||||
c.set_position(usize::MAX as u64 + 1);
|
||||
assert!(c.write_all(&[1, 2, 3]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_eq() {
|
||||
assert_eq!(Cursor::new(Vec::<u8>::new()), Cursor::new(Vec::<u8>::new()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
struct AssertEq<T: Eq>(pub T);
|
||||
|
||||
let _: AssertEq<Cursor<Vec<u8>>> = AssertEq(Cursor::new(Vec::new()));
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn const_cursor() {
|
||||
const CURSOR: Cursor<&[u8]> = Cursor::new(&[0]);
|
||||
const _: &&[u8] = CURSOR.get_ref();
|
||||
const _: u64 = CURSOR.position();
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_write_vec(b: &mut test::Bencher) {
|
||||
let slice = &[1; 128];
|
||||
|
||||
b.iter(|| {
|
||||
let mut buf = b"some random data to overwrite".to_vec();
|
||||
let mut cursor = Cursor::new(&mut buf);
|
||||
|
||||
let _ = cursor.write_all(slice);
|
||||
test::black_box(&cursor);
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_write_vec_vectored(b: &mut test::Bencher) {
|
||||
let slices = [
|
||||
IoSlice::new(&[1; 128]),
|
||||
IoSlice::new(&[2; 256]),
|
||||
IoSlice::new(&[3; 512]),
|
||||
IoSlice::new(&[4; 1024]),
|
||||
IoSlice::new(&[5; 2048]),
|
||||
IoSlice::new(&[6; 4096]),
|
||||
IoSlice::new(&[7; 8192]),
|
||||
IoSlice::new(&[8; 8192 * 2]),
|
||||
];
|
||||
|
||||
b.iter(|| {
|
||||
let mut buf = b"some random data to overwrite".to_vec();
|
||||
let mut cursor = Cursor::new(&mut buf);
|
||||
|
||||
let mut slices = slices;
|
||||
let _ = cursor.write_all_vectored(&mut slices);
|
||||
test::black_box(&cursor);
|
||||
})
|
||||
}
|
||||
1085
crates/std/src/io/error.rs
Normal file
1085
crates/std/src/io/error.rs
Normal file
File diff suppressed because it is too large
Load Diff
411
crates/std/src/io/error/repr_bitpacked.rs
Normal file
411
crates/std/src/io/error/repr_bitpacked.rs
Normal file
@@ -0,0 +1,411 @@
|
||||
//! This is a densely packed error representation which is used on targets with
|
||||
//! 64-bit pointers.
|
||||
//!
|
||||
//! (Note that `bitpacked` vs `unpacked` here has no relationship to
|
||||
//! `#[repr(packed)]`, it just refers to attempting to use any available bits in
|
||||
//! a more clever manner than `rustc`'s default layout algorithm would).
|
||||
//!
|
||||
//! Conceptually, it stores the same data as the "unpacked" equivalent we use on
|
||||
//! other targets. Specifically, you can imagine it as an optimized version of
|
||||
//! the following enum (which is roughly equivalent to what's stored by
|
||||
//! `repr_unpacked::Repr`, e.g. `super::ErrorData<Box<Custom>>`):
|
||||
//!
|
||||
//! ```ignore (exposition-only)
|
||||
//! enum ErrorData {
|
||||
//! Os(i32),
|
||||
//! Simple(ErrorKind),
|
||||
//! SimpleMessage(&'static SimpleMessage),
|
||||
//! Custom(Box<Custom>),
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! However, it packs this data into a 64bit non-zero value.
|
||||
//!
|
||||
//! This optimization not only allows `io::Error` to occupy a single pointer,
|
||||
//! but improves `io::Result` as well, especially for situations like
|
||||
//! `io::Result<()>` (which is now 64 bits) or `io::Result<u64>` (which is now
|
||||
//! 128 bits), which are quite common.
|
||||
//!
|
||||
//! # Layout
|
||||
//! Tagged values are 64 bits, with the 2 least significant bits used for the
|
||||
//! tag. This means there are 4 "variants":
|
||||
//!
|
||||
//! - **Tag 0b00**: The first variant is equivalent to
|
||||
//! `ErrorData::SimpleMessage`, and holds a `&'static SimpleMessage` directly.
|
||||
//!
|
||||
//! `SimpleMessage` has an alignment >= 4 (which is requested with
|
||||
//! `#[repr(align)]` and checked statically at the bottom of this file), which
|
||||
//! means every `&'static SimpleMessage` should have the both tag bits as 0,
|
||||
//! meaning its tagged and untagged representation are equivalent.
|
||||
//!
|
||||
//! This means we can skip tagging it, which is necessary as this variant can
|
||||
//! be constructed from a `const fn`, which probably cannot tag pointers (or
|
||||
//! at least it would be difficult).
|
||||
//!
|
||||
//! - **Tag 0b01**: The other pointer variant holds the data for
|
||||
//! `ErrorData::Custom` and the remaining 62 bits are used to store a
|
||||
//! `Box<Custom>`. `Custom` also has alignment >= 4, so the bottom two bits
|
||||
//! are free to use for the tag.
|
||||
//!
|
||||
//! The only important thing to note is that `ptr::wrapping_add` and
|
||||
//! `ptr::wrapping_sub` are used to tag the pointer, rather than bitwise
|
||||
//! operations. This should preserve the pointer's provenance, which would
|
||||
//! otherwise be lost.
|
||||
//!
|
||||
//! - **Tag 0b10**: Holds the data for `ErrorData::Os(i32)`. We store the `i32`
|
||||
//! in the pointer's most significant 32 bits, and don't use the bits `2..32`
|
||||
//! for anything. Using the top 32 bits is just to let us easily recover the
|
||||
//! `i32` code with the correct sign.
|
||||
//!
|
||||
//! - **Tag 0b11**: Holds the data for `ErrorData::Simple(ErrorKind)`. This
|
||||
//! stores the `ErrorKind` in the top 32 bits as well, although it doesn't
|
||||
//! occupy nearly that many. Most of the bits are unused here, but it's not
|
||||
//! like we need them for anything else yet.
|
||||
//!
|
||||
//! # Use of `NonNull<()>`
|
||||
//!
|
||||
//! Everything is stored in a `NonNull<()>`, which is odd, but actually serves a
|
||||
//! purpose.
|
||||
//!
|
||||
//! Conceptually you might think of this more like:
|
||||
//!
|
||||
//! ```ignore (exposition-only)
|
||||
//! union Repr {
|
||||
//! // holds integer (Simple/Os) variants, and
|
||||
//! // provides access to the tag bits.
|
||||
//! bits: NonZero<u64>,
|
||||
//! // Tag is 0, so this is stored untagged.
|
||||
//! msg: &'static SimpleMessage,
|
||||
//! // Tagged (offset) `Box<Custom>` pointer.
|
||||
//! tagged_custom: NonNull<()>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! But there are a few problems with this:
|
||||
//!
|
||||
//! 1. Union access is equivalent to a transmute, so this representation would
|
||||
//! require we transmute between integers and pointers in at least one
|
||||
//! direction, which may be UB (and even if not, it is likely harder for a
|
||||
//! compiler to reason about than explicit ptr->int operations).
|
||||
//!
|
||||
//! 2. Even if all fields of a union have a niche, the union itself doesn't,
|
||||
//! although this may change in the future. This would make things like
|
||||
//! `io::Result<()>` and `io::Result<usize>` larger, which defeats part of
|
||||
//! the motivation of this bitpacking.
|
||||
//!
|
||||
//! Storing everything in a `NonZero<usize>` (or some other integer) would be a
|
||||
//! bit more traditional for pointer tagging, but it would lose provenance
|
||||
//! information, couldn't be constructed from a `const fn`, and would probably
|
||||
//! run into other issues as well.
|
||||
//!
|
||||
//! The `NonNull<()>` seems like the only alternative, even if it's fairly odd
|
||||
//! to use a pointer type to store something that may hold an integer, some of
|
||||
//! the time.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::num::NonZeroUsize;
|
||||
use core::ptr::NonNull;
|
||||
|
||||
use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage};
|
||||
|
||||
// The 2 least-significant bits are used as tag.
|
||||
const TAG_MASK: usize = 0b11;
|
||||
const TAG_SIMPLE_MESSAGE: usize = 0b00;
|
||||
const TAG_CUSTOM: usize = 0b01;
|
||||
const TAG_OS: usize = 0b10;
|
||||
const TAG_SIMPLE: usize = 0b11;
|
||||
|
||||
/// The internal representation.
|
||||
///
|
||||
/// See the module docs for more, this is just a way to hack in a check that we
|
||||
/// indeed are not unwind-safe.
|
||||
///
|
||||
/// ```compile_fail,E0277
|
||||
/// fn is_unwind_safe<T: core::panic::UnwindSafe>() {}
|
||||
/// is_unwind_safe::<std::io::Error>();
|
||||
/// ```
|
||||
#[repr(transparent)]
|
||||
#[rustc_insignificant_dtor]
|
||||
pub(super) struct Repr(NonNull<()>, PhantomData<ErrorData<Box<Custom>>>);
|
||||
|
||||
// All the types `Repr` stores internally are Send + Sync, and so is it.
|
||||
unsafe impl Send for Repr {}
|
||||
unsafe impl Sync for Repr {}
|
||||
|
||||
impl Repr {
|
||||
pub(super) fn new_custom(b: Box<Custom>) -> Self {
|
||||
let p = Box::into_raw(b).cast::<u8>();
|
||||
// Should only be possible if an allocator handed out a pointer with
|
||||
// wrong alignment.
|
||||
debug_assert_eq!(p.addr() & TAG_MASK, 0);
|
||||
// Note: We know `TAG_CUSTOM <= size_of::<Custom>()` (static_assert at
|
||||
// end of file), and both the start and end of the expression must be
|
||||
// valid without address space wraparound due to `Box`'s semantics.
|
||||
//
|
||||
// This means it would be correct to implement this using `ptr::add`
|
||||
// (rather than `ptr::wrapping_add`), but it's unclear this would give
|
||||
// any benefit, so we just use `wrapping_add` instead.
|
||||
let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>();
|
||||
// Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`,
|
||||
// because `p`'s alignment means it isn't allowed to have any of the
|
||||
// `TAG_BITS` set (you can verify that addition and bitwise-or are the
|
||||
// same when the operands have no bits in common using a truth table).
|
||||
//
|
||||
// Then, `TAG_CUSTOM | p` is not zero, as that would require
|
||||
// `TAG_CUSTOM` and `p` both be zero, and neither is (as `p` came from a
|
||||
// box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore,
|
||||
// `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the
|
||||
// `new_unchecked` is safe.
|
||||
let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData);
|
||||
// quickly smoke-check we encoded the right thing (This generally will
|
||||
// only run in std's tests, unless the user uses -Zbuild-std)
|
||||
debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed");
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn new_os(code: RawOsError) -> Self {
|
||||
let utagged = ((code as usize) << 32) | TAG_OS;
|
||||
// Safety: `TAG_OS` is not zero, so the result of the `|` is not 0.
|
||||
let res = Self(
|
||||
NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }),
|
||||
PhantomData,
|
||||
);
|
||||
// quickly smoke-check we encoded the right thing (This generally will
|
||||
// only run in std's tests, unless the user uses -Zbuild-std)
|
||||
debug_assert!(
|
||||
matches!(res.data(), ErrorData::Os(c) if c == code),
|
||||
"repr(os) encoding failed for {code}"
|
||||
);
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn new_simple(kind: ErrorKind) -> Self {
|
||||
let utagged = ((kind as usize) << 32) | TAG_SIMPLE;
|
||||
// Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0.
|
||||
let res = Self(
|
||||
NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }),
|
||||
PhantomData,
|
||||
);
|
||||
// quickly smoke-check we encoded the right thing (This generally will
|
||||
// only run in std's tests, unless the user uses -Zbuild-std)
|
||||
debug_assert!(
|
||||
matches!(res.data(), ErrorData::Simple(k) if k == kind),
|
||||
"repr(simple) encoding failed {:?}",
|
||||
kind,
|
||||
);
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self {
|
||||
// Safety: References are never null.
|
||||
Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn data(&self) -> ErrorData<&Custom> {
|
||||
// Safety: We're a Repr, decode_repr is fine.
|
||||
unsafe { decode_repr(self.0, |c| &*c) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> {
|
||||
// Safety: We're a Repr, decode_repr is fine.
|
||||
unsafe { decode_repr(self.0, |c| &mut *c) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn into_data(self) -> ErrorData<Box<Custom>> {
|
||||
let this = core::mem::ManuallyDrop::new(self);
|
||||
// Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is
|
||||
// safe because we prevent double-drop using `ManuallyDrop`.
|
||||
unsafe { decode_repr(this.0, |p| Box::from_raw(p)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Repr {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
// Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is
|
||||
// safe because we're being dropped.
|
||||
unsafe {
|
||||
let _ = decode_repr(self.0, |p| Box::<Custom>::from_raw(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shared helper to decode a `Repr`'s internal pointer into an ErrorData.
|
||||
//
|
||||
// Safety: `ptr`'s bits should be encoded as described in the document at the
|
||||
// top (it should `some_repr.0`)
|
||||
#[inline]
|
||||
unsafe fn decode_repr<C, F>(ptr: NonNull<()>, make_custom: F) -> ErrorData<C>
|
||||
where
|
||||
F: FnOnce(*mut Custom) -> C,
|
||||
{
|
||||
let bits = ptr.as_ptr().addr();
|
||||
match bits & TAG_MASK {
|
||||
TAG_OS => {
|
||||
let code = ((bits as i64) >> 32) as RawOsError;
|
||||
ErrorData::Os(code)
|
||||
}
|
||||
TAG_SIMPLE => {
|
||||
let kind_bits = (bits >> 32) as u32;
|
||||
let kind = kind_from_prim(kind_bits).unwrap_or_else(|| {
|
||||
debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits);
|
||||
// This means the `ptr` passed in was not valid, which violates
|
||||
// the unsafe contract of `decode_repr`.
|
||||
//
|
||||
// Using this rather than unwrap meaningfully improves the code
|
||||
// for callers which only care about one variant (usually
|
||||
// `Custom`)
|
||||
unsafe { core::hint::unreachable_unchecked() };
|
||||
});
|
||||
ErrorData::Simple(kind)
|
||||
}
|
||||
TAG_SIMPLE_MESSAGE => {
|
||||
// SAFETY: per tag
|
||||
unsafe { ErrorData::SimpleMessage(&*ptr.cast::<SimpleMessage>().as_ptr()) }
|
||||
}
|
||||
TAG_CUSTOM => {
|
||||
// It would be correct for us to use `ptr::byte_sub` here (see the
|
||||
// comment above the `wrapping_add` call in `new_custom` for why),
|
||||
// but it isn't clear that it makes a difference, so we don't.
|
||||
let custom = ptr.as_ptr().wrapping_byte_sub(TAG_CUSTOM).cast::<Custom>();
|
||||
ErrorData::Custom(make_custom(custom))
|
||||
}
|
||||
_ => {
|
||||
// Can't happen, and compiler can tell
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This compiles to the same code as the check+transmute, but doesn't require
|
||||
// unsafe, or to hard-code max ErrorKind or its size in a way the compiler
|
||||
// couldn't verify.
|
||||
#[inline]
|
||||
fn kind_from_prim(ek: u32) -> Option<ErrorKind> {
|
||||
macro_rules! from_prim {
|
||||
($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{
|
||||
// Force a compile error if the list gets out of date.
|
||||
const _: fn(e: $Enum) = |e: $Enum| match e {
|
||||
$($Enum::$Variant => ()),*
|
||||
};
|
||||
match $prim {
|
||||
$(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)*
|
||||
_ => None,
|
||||
}
|
||||
}}
|
||||
}
|
||||
from_prim!(ek => ErrorKind {
|
||||
NotFound,
|
||||
PermissionDenied,
|
||||
ConnectionRefused,
|
||||
ConnectionReset,
|
||||
HostUnreachable,
|
||||
NetworkUnreachable,
|
||||
ConnectionAborted,
|
||||
NotConnected,
|
||||
AddrInUse,
|
||||
AddrNotAvailable,
|
||||
NetworkDown,
|
||||
BrokenPipe,
|
||||
AlreadyExists,
|
||||
WouldBlock,
|
||||
NotADirectory,
|
||||
IsADirectory,
|
||||
DirectoryNotEmpty,
|
||||
ReadOnlyFilesystem,
|
||||
FilesystemLoop,
|
||||
StaleNetworkFileHandle,
|
||||
InvalidInput,
|
||||
InvalidData,
|
||||
TimedOut,
|
||||
WriteZero,
|
||||
StorageFull,
|
||||
NotSeekable,
|
||||
QuotaExceeded,
|
||||
FileTooLarge,
|
||||
ResourceBusy,
|
||||
ExecutableFileBusy,
|
||||
Deadlock,
|
||||
CrossesDevices,
|
||||
TooManyLinks,
|
||||
InvalidFilename,
|
||||
ArgumentListTooLong,
|
||||
Interrupted,
|
||||
Other,
|
||||
UnexpectedEof,
|
||||
Unsupported,
|
||||
OutOfMemory,
|
||||
InProgress,
|
||||
Uncategorized,
|
||||
})
|
||||
}
|
||||
|
||||
// Some static checking to alert us if a change breaks any of the assumptions
|
||||
// that our encoding relies on for correctness and soundness. (Some of these are
|
||||
// a bit overly thorough/cautious, admittedly)
|
||||
//
|
||||
// If any of these are hit on a platform that std supports, we should likely
|
||||
// just use `repr_unpacked.rs` there instead (unless the fix is easy).
|
||||
macro_rules! static_assert {
|
||||
($condition:expr) => {
|
||||
const _: () = assert!($condition);
|
||||
};
|
||||
(@usize_eq: $lhs:expr, $rhs:expr) => {
|
||||
const _: [(); $lhs] = [(); $rhs];
|
||||
};
|
||||
}
|
||||
|
||||
// The bitpacking we use requires pointers be exactly 64 bits.
|
||||
static_assert!(@usize_eq: size_of::<NonNull<()>>(), 8);
|
||||
|
||||
// We also require pointers and usize be the same size.
|
||||
static_assert!(@usize_eq: size_of::<NonNull<()>>(), size_of::<usize>());
|
||||
|
||||
// `Custom` and `SimpleMessage` need to be thin pointers.
|
||||
static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8);
|
||||
static_assert!(@usize_eq: size_of::<Box<Custom>>(), 8);
|
||||
|
||||
static_assert!((TAG_MASK + 1).is_power_of_two());
|
||||
// And they must have sufficient alignment.
|
||||
static_assert!(align_of::<SimpleMessage>() >= TAG_MASK + 1);
|
||||
static_assert!(align_of::<Custom>() >= TAG_MASK + 1);
|
||||
|
||||
static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE_MESSAGE, TAG_SIMPLE_MESSAGE);
|
||||
static_assert!(@usize_eq: TAG_MASK & TAG_CUSTOM, TAG_CUSTOM);
|
||||
static_assert!(@usize_eq: TAG_MASK & TAG_OS, TAG_OS);
|
||||
static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE, TAG_SIMPLE);
|
||||
|
||||
// This is obviously true (`TAG_CUSTOM` is `0b01`), but in `Repr::new_custom` we
|
||||
// offset a pointer by this value, and expect it to both be within the same
|
||||
// object, and to not wrap around the address space. See the comment in that
|
||||
// function for further details.
|
||||
//
|
||||
// Actually, at the moment we use `ptr::wrapping_add`, not `ptr::add`, so this
|
||||
// check isn't needed for that one, although the assertion that we don't
|
||||
// actually wrap around in that wrapping_add does simplify the safety reasoning
|
||||
// elsewhere considerably.
|
||||
static_assert!(size_of::<Custom>() >= TAG_CUSTOM);
|
||||
|
||||
// These two store a payload which is allowed to be zero, so they must be
|
||||
// non-zero to preserve the `NonNull`'s range invariant.
|
||||
static_assert!(TAG_OS != 0);
|
||||
static_assert!(TAG_SIMPLE != 0);
|
||||
// We can't tag `SimpleMessage`s, the tag must be 0.
|
||||
static_assert!(@usize_eq: TAG_SIMPLE_MESSAGE, 0);
|
||||
|
||||
// Check that the point of all of this still holds.
|
||||
//
|
||||
// We'd check against `io::Error`, but *technically* it's allowed to vary,
|
||||
// as it's not `#[repr(transparent)]`/`#[repr(C)]`. We could add that, but
|
||||
// the `#[repr()]` would show up in rustdoc, which might be seen as a stable
|
||||
// commitment.
|
||||
static_assert!(@usize_eq: size_of::<Repr>(), 8);
|
||||
static_assert!(@usize_eq: size_of::<Option<Repr>>(), 8);
|
||||
static_assert!(@usize_eq: size_of::<Result<(), Repr>>(), 8);
|
||||
static_assert!(@usize_eq: size_of::<Result<usize, Repr>>(), 16);
|
||||
50
crates/std/src/io/error/repr_unpacked.rs
Normal file
50
crates/std/src/io/error/repr_unpacked.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
//! This is a fairly simple unpacked error representation that's used on
|
||||
//! non-64bit targets, where the packed 64 bit representation wouldn't work, and
|
||||
//! would have no benefit.
|
||||
|
||||
use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage};
|
||||
|
||||
type Inner = ErrorData<Box<Custom>>;
|
||||
|
||||
pub(super) struct Repr(Inner);
|
||||
|
||||
impl Repr {
|
||||
#[inline]
|
||||
pub(super) fn new_custom(b: Box<Custom>) -> Self {
|
||||
Self(Inner::Custom(b))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn new_os(code: RawOsError) -> Self {
|
||||
Self(Inner::Os(code))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn new_simple(kind: ErrorKind) -> Self {
|
||||
Self(Inner::Simple(kind))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self {
|
||||
Self(Inner::SimpleMessage(m))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn into_data(self) -> ErrorData<Box<Custom>> {
|
||||
self.0
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn data(&self) -> ErrorData<&Custom> {
|
||||
match &self.0 {
|
||||
Inner::Os(c) => ErrorData::Os(*c),
|
||||
Inner::Simple(k) => ErrorData::Simple(*k),
|
||||
Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m),
|
||||
Inner::Custom(m) => ErrorData::Custom(&*m),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> {
|
||||
match &mut self.0 {
|
||||
Inner::Os(c) => ErrorData::Os(*c),
|
||||
Inner::Simple(k) => ErrorData::Simple(*k),
|
||||
Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m),
|
||||
Inner::Custom(m) => ErrorData::Custom(&mut *m),
|
||||
}
|
||||
}
|
||||
}
|
||||
191
crates/std/src/io/error/tests.rs
Normal file
191
crates/std/src/io/error/tests.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error};
|
||||
use crate::sys::io::{decode_error_kind, error_string};
|
||||
use crate::{assert_matches, error, fmt};
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
assert!(size_of::<Error>() <= size_of::<[usize; 2]>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_error() {
|
||||
let code = 6;
|
||||
let msg = error_string(code);
|
||||
let kind = decode_error_kind(code);
|
||||
let err = Error {
|
||||
repr: Repr::new_custom(Box::new(Custom {
|
||||
kind: ErrorKind::InvalidInput,
|
||||
error: Box::new(Error { repr: super::Repr::new_os(code) }),
|
||||
})),
|
||||
};
|
||||
let expected = format!(
|
||||
"Custom {{ \
|
||||
kind: InvalidInput, \
|
||||
error: Os {{ \
|
||||
code: {:?}, \
|
||||
kind: {:?}, \
|
||||
message: {:?} \
|
||||
}} \
|
||||
}}",
|
||||
code, kind, msg
|
||||
);
|
||||
assert_eq!(format!("{err:?}"), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downcasting() {
|
||||
#[derive(Debug)]
|
||||
struct TestError;
|
||||
|
||||
impl fmt::Display for TestError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("asdf")
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for TestError {}
|
||||
|
||||
// we have to call all of these UFCS style right now since method
|
||||
// resolution won't implicitly drop the Send+Sync bounds
|
||||
let mut err = Error::new(ErrorKind::Other, TestError);
|
||||
assert!(err.get_ref().unwrap().is::<TestError>());
|
||||
assert_eq!("asdf", err.get_ref().unwrap().to_string());
|
||||
assert!(err.get_mut().unwrap().is::<TestError>());
|
||||
let extracted = err.into_inner().unwrap();
|
||||
extracted.downcast::<TestError>().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_const() {
|
||||
const E: Error = const_error!(ErrorKind::NotFound, "hello");
|
||||
|
||||
assert_eq!(E.kind(), ErrorKind::NotFound);
|
||||
assert_eq!(E.to_string(), "hello");
|
||||
assert!(format!("{E:?}").contains("\"hello\""));
|
||||
assert!(format!("{E:?}").contains("NotFound"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_packing() {
|
||||
for code in -20..20 {
|
||||
let e = Error::from_raw_os_error(code);
|
||||
assert_eq!(e.raw_os_error(), Some(code));
|
||||
assert_matches!(
|
||||
e.repr.data(),
|
||||
ErrorData::Os(c) if c == code,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_errorkind_packing() {
|
||||
assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound);
|
||||
assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied);
|
||||
assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized);
|
||||
// Check that the innards look like what we want.
|
||||
assert_matches!(
|
||||
Error::from(ErrorKind::OutOfMemory).repr.data(),
|
||||
ErrorData::Simple(ErrorKind::OutOfMemory),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_message_packing() {
|
||||
use super::ErrorKind::*;
|
||||
use super::SimpleMessage;
|
||||
macro_rules! check_simple_msg {
|
||||
($err:expr, $kind:ident, $msg:literal) => {{
|
||||
let e = &$err;
|
||||
// Check that the public api is right.
|
||||
assert_eq!(e.kind(), $kind);
|
||||
assert!(format!("{e:?}").contains($msg));
|
||||
// and we got what we expected
|
||||
assert_matches!(
|
||||
e.repr.data(),
|
||||
ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg })
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
||||
let not_static = const_error!(Uncategorized, "not a constant!");
|
||||
check_simple_msg!(not_static, Uncategorized, "not a constant!");
|
||||
|
||||
const CONST: Error = const_error!(NotFound, "definitely a constant!");
|
||||
check_simple_msg!(CONST, NotFound, "definitely a constant!");
|
||||
|
||||
static STATIC: Error = const_error!(BrokenPipe, "a constant, sort of!");
|
||||
check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!");
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Bojji(bool);
|
||||
impl error::Error for Bojji {}
|
||||
impl fmt::Display for Bojji {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ah! {:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_error_packing() {
|
||||
use super::Custom;
|
||||
let test = Error::new(ErrorKind::Uncategorized, Bojji(true));
|
||||
assert_matches!(
|
||||
test.repr.data(),
|
||||
ErrorData::Custom(Custom {
|
||||
kind: ErrorKind::Uncategorized,
|
||||
error,
|
||||
}) if error.downcast_ref::<Bojji>().as_deref() == Some(&Bojji(true)),
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct E;
|
||||
|
||||
impl fmt::Display for E {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for E {}
|
||||
|
||||
#[test]
|
||||
fn test_std_io_error_downcast() {
|
||||
// Case 1: custom error, downcast succeeds
|
||||
let io_error = Error::new(ErrorKind::Other, Bojji(true));
|
||||
let e: Bojji = io_error.downcast().unwrap();
|
||||
assert!(e.0);
|
||||
|
||||
// Case 2: custom error, downcast fails
|
||||
let io_error = Error::new(ErrorKind::Other, Bojji(true));
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
// ensures that the custom error is intact
|
||||
assert_eq!(ErrorKind::Other, io_error.kind());
|
||||
let e: Bojji = io_error.downcast().unwrap();
|
||||
assert!(e.0);
|
||||
|
||||
// Case 3: os error
|
||||
let errno = 20;
|
||||
let io_error = Error::from_raw_os_error(errno);
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
assert_eq!(errno, io_error.raw_os_error().unwrap());
|
||||
|
||||
// Case 4: simple
|
||||
let kind = ErrorKind::OutOfMemory;
|
||||
let io_error: Error = kind.into();
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
assert_eq!(kind, io_error.kind());
|
||||
|
||||
// Case 5: simple message
|
||||
const SIMPLE_MESSAGE: SimpleMessage =
|
||||
SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" };
|
||||
let io_error = Error::from_static_message(&SIMPLE_MESSAGE);
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind());
|
||||
assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}"));
|
||||
}
|
||||
717
crates/std/src/io/impls.rs
Normal file
717
crates/std/src/io/impls.rs
Normal file
@@ -0,0 +1,717 @@
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::alloc::Allocator;
|
||||
use alloc_crate::collections::VecDeque;
|
||||
use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||
use crate::{cmp, fmt, mem, str};
|
||||
|
||||
// =============================================================================
|
||||
// Forwarding implementations
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: Read + ?Sized> Read for &mut R {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
(**self).read(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
(**self).read_buf(cursor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
(**self).read_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
(**self).is_read_vectored()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_to_end(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_to_string(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
(**self).read_exact(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
(**self).read_buf_exact(cursor)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: Write + ?Sized> Write for &mut W {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
(**self).write(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
(**self).write_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
(**self).is_write_vectored()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
(**self).flush()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
(**self).write_all(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
(**self).write_all_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
(**self).write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<S: Seek + ?Sized> Seek for &mut S {
|
||||
#[inline]
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
(**self).seek(pos)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rewind(&mut self) -> io::Result<()> {
|
||||
(**self).rewind()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
(**self).stream_len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
(**self).stream_position()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
(**self).seek_relative(offset)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<B: BufRead + ?Sized> BufRead for &mut B {
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
(**self).fill_buf()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, amt: usize) {
|
||||
(**self).consume(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_data_left(&mut self) -> io::Result<bool> {
|
||||
(**self).has_data_left()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_until(byte, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_until(&mut self, byte: u8) -> io::Result<usize> {
|
||||
(**self).skip_until(byte)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_line(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: Read + ?Sized> Read for Box<R> {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
(**self).read(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
(**self).read_buf(cursor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
(**self).read_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
(**self).is_read_vectored()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_to_end(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_to_string(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
(**self).read_exact(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
(**self).read_buf_exact(cursor)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: Write + ?Sized> Write for Box<W> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
(**self).write(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
(**self).write_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
(**self).is_write_vectored()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
(**self).flush()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
(**self).write_all(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
(**self).write_all_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
(**self).write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<S: Seek + ?Sized> Seek for Box<S> {
|
||||
#[inline]
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
(**self).seek(pos)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rewind(&mut self) -> io::Result<()> {
|
||||
(**self).rewind()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
(**self).stream_len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
(**self).stream_position()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
(**self).seek_relative(offset)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<B: BufRead + ?Sized> BufRead for Box<B> {
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
(**self).fill_buf()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, amt: usize) {
|
||||
(**self).consume(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_data_left(&mut self) -> io::Result<bool> {
|
||||
(**self).has_data_left()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_until(byte, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_until(&mut self, byte: u8) -> io::Result<usize> {
|
||||
(**self).skip_until(byte)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_line(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// In-memory buffer implementations
|
||||
|
||||
/// Read is implemented for `&[u8]` by copying from the slice.
|
||||
///
|
||||
/// Note that reading updates the slice to point to the yet unread part.
|
||||
/// The slice will be empty when EOF is reached.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Read for &[u8] {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let amt = cmp::min(buf.len(), self.len());
|
||||
let (a, b) = self.split_at(amt);
|
||||
|
||||
// First check if the amount of bytes we want to read is small:
|
||||
// `copy_from_slice` will generally expand to a call to `memcpy`, and
|
||||
// for a single byte the overhead is significant.
|
||||
if amt == 1 {
|
||||
buf[0] = a[0];
|
||||
} else {
|
||||
buf[..amt].copy_from_slice(a);
|
||||
}
|
||||
|
||||
*self = b;
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let amt = cmp::min(cursor.capacity(), self.len());
|
||||
let (a, b) = self.split_at(amt);
|
||||
|
||||
cursor.append(a);
|
||||
|
||||
*self = b;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let mut nread = 0;
|
||||
for buf in bufs {
|
||||
nread += self.read(buf)?;
|
||||
if self.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
if buf.len() > self.len() {
|
||||
// `read_exact` makes no promise about the content of `buf` if it
|
||||
// fails so don't bother about that.
|
||||
*self = &self[self.len()..];
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
let (a, b) = self.split_at(buf.len());
|
||||
|
||||
// First check if the amount of bytes we want to read is small:
|
||||
// `copy_from_slice` will generally expand to a call to `memcpy`, and
|
||||
// for a single byte the overhead is significant.
|
||||
if buf.len() == 1 {
|
||||
buf[0] = a[0];
|
||||
} else {
|
||||
buf.copy_from_slice(a);
|
||||
}
|
||||
|
||||
*self = b;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
if cursor.capacity() > self.len() {
|
||||
// Append everything we can to the cursor.
|
||||
cursor.append(*self);
|
||||
*self = &self[self.len()..];
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
let (a, b) = self.split_at(cursor.capacity());
|
||||
|
||||
cursor.append(a);
|
||||
|
||||
*self = b;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let len = self.len();
|
||||
buf.try_reserve(len)?;
|
||||
buf.extend_from_slice(*self);
|
||||
*self = &self[len..];
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
let content = str::from_utf8(self).map_err(|_| io::Error::INVALID_UTF8)?;
|
||||
let len = self.len();
|
||||
buf.try_reserve(len)?;
|
||||
buf.push_str(content);
|
||||
*self = &self[len..];
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl BufRead for &[u8] {
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
Ok(*self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, amt: usize) {
|
||||
*self = &self[amt..];
|
||||
}
|
||||
}
|
||||
|
||||
/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting
|
||||
/// its data.
|
||||
///
|
||||
/// Note that writing updates the slice to point to the yet unwritten part.
|
||||
/// The slice will be empty when it has been completely overwritten.
|
||||
///
|
||||
/// If the number of bytes to be written exceeds the size of the slice, write operations will
|
||||
/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of
|
||||
/// kind `ErrorKind::WriteZero`.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Write for &mut [u8] {
|
||||
#[inline]
|
||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
let amt = cmp::min(data.len(), self.len());
|
||||
let (a, b) = mem::take(self).split_at_mut(amt);
|
||||
a.copy_from_slice(&data[..amt]);
|
||||
*self = b;
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let mut nwritten = 0;
|
||||
for buf in bufs {
|
||||
nwritten += self.write(buf)?;
|
||||
if self.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(nwritten)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
|
||||
if self.write(data)? < data.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
for buf in bufs {
|
||||
if self.write(buf)? < buf.len() {
|
||||
return Err(io::Error::WRITE_ALL_EOF);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Write is implemented for `Vec<u8>` by appending to the vector.
|
||||
/// The vector will grow as needed.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<A: Allocator> Write for Vec<u8, A> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let len = bufs.iter().map(|b| b.len()).sum();
|
||||
self.reserve(len);
|
||||
for buf in bufs {
|
||||
self.extend_from_slice(buf);
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
self.extend_from_slice(buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
self.write_vectored(bufs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Read is implemented for `VecDeque<u8>` by consuming bytes from the front of the `VecDeque`.
|
||||
#[stable(feature = "vecdeque_read_write", since = "1.63.0")]
|
||||
impl<A: Allocator> Read for VecDeque<u8, A> {
|
||||
/// Fill `buf` with the contents of the "front" slice as returned by
|
||||
/// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are
|
||||
/// discontiguous, multiple calls to `read` will be needed to read the entire content.
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let (ref mut front, _) = self.as_slices();
|
||||
let n = Read::read(front, buf)?;
|
||||
self.drain(..n);
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
let (front, back) = self.as_slices();
|
||||
|
||||
// Use only the front buffer if it is big enough to fill `buf`, else use
|
||||
// the back buffer too.
|
||||
match buf.split_at_mut_checked(front.len()) {
|
||||
None => buf.copy_from_slice(&front[..buf.len()]),
|
||||
Some((buf_front, buf_back)) => match back.split_at_checked(buf_back.len()) {
|
||||
Some((back, _)) => {
|
||||
buf_front.copy_from_slice(front);
|
||||
buf_back.copy_from_slice(back);
|
||||
}
|
||||
None => {
|
||||
self.clear();
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
self.drain(..buf.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let (ref mut front, _) = self.as_slices();
|
||||
let n = cmp::min(cursor.capacity(), front.len());
|
||||
Read::read_buf(front, cursor)?;
|
||||
self.drain(..n);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let len = cursor.capacity();
|
||||
let (front, back) = self.as_slices();
|
||||
|
||||
match front.split_at_checked(cursor.capacity()) {
|
||||
Some((front, _)) => cursor.append(front),
|
||||
None => {
|
||||
cursor.append(front);
|
||||
match back.split_at_checked(cursor.capacity()) {
|
||||
Some((back, _)) => cursor.append(back),
|
||||
None => {
|
||||
cursor.append(back);
|
||||
self.clear();
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.drain(..len);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
// The total len is known upfront so we can reserve it in a single call.
|
||||
let len = self.len();
|
||||
buf.try_reserve(len)?;
|
||||
|
||||
let (front, back) = self.as_slices();
|
||||
buf.extend_from_slice(front);
|
||||
buf.extend_from_slice(back);
|
||||
self.clear();
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
// SAFETY: We only append to the buffer
|
||||
unsafe { io::append_to_string(buf, |buf| self.read_to_end(buf)) }
|
||||
}
|
||||
}
|
||||
|
||||
/// BufRead is implemented for `VecDeque<u8>` by reading bytes from the front of the `VecDeque`.
|
||||
#[stable(feature = "vecdeque_buf_read", since = "1.75.0")]
|
||||
impl<A: Allocator> BufRead for VecDeque<u8, A> {
|
||||
/// Returns the contents of the "front" slice as returned by
|
||||
/// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are
|
||||
/// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content.
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
let (front, _) = self.as_slices();
|
||||
Ok(front)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.drain(..amt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Write is implemented for `VecDeque<u8>` by appending to the `VecDeque`, growing it as needed.
|
||||
#[stable(feature = "vecdeque_read_write", since = "1.63.0")]
|
||||
impl<A: Allocator> Write for VecDeque<u8, A> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.extend(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let len = bufs.iter().map(|b| b.len()).sum();
|
||||
self.reserve(len);
|
||||
for buf in bufs {
|
||||
self.extend(&**buf);
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
self.extend(buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
self.write_vectored(bufs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "read_buf", issue = "78485")]
|
||||
impl<'a> io::Write for core::io::BorrowedCursor<'a> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let amt = cmp::min(buf.len(), self.capacity());
|
||||
self.append(&buf[..amt]);
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let mut nwritten = 0;
|
||||
for buf in bufs {
|
||||
let n = self.write(buf)?;
|
||||
nwritten += n;
|
||||
if n < buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(nwritten)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
if self.write(buf)? < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
for buf in bufs {
|
||||
if self.write(buf)? < buf.len() {
|
||||
return Err(io::Error::WRITE_ALL_EOF);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
57
crates/std/src/io/impls/tests.rs
Normal file
57
crates/std/src/io/impls/tests.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::io::prelude::*;
|
||||
|
||||
#[bench]
|
||||
fn bench_read_slice(b: &mut test::Bencher) {
|
||||
let buf = [5; 1024];
|
||||
let mut dst = [0; 128];
|
||||
|
||||
b.iter(|| {
|
||||
let mut rd = &buf[..];
|
||||
for _ in 0..8 {
|
||||
let _ = rd.read(&mut dst);
|
||||
test::black_box(&dst);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_write_slice(b: &mut test::Bencher) {
|
||||
let mut buf = [0; 1024];
|
||||
let src = [5; 128];
|
||||
|
||||
b.iter(|| {
|
||||
let mut wr = &mut buf[..];
|
||||
for _ in 0..8 {
|
||||
let _ = wr.write_all(&src);
|
||||
test::black_box(&wr);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_read_vec(b: &mut test::Bencher) {
|
||||
let buf = vec![5; 1024];
|
||||
let mut dst = [0; 128];
|
||||
|
||||
b.iter(|| {
|
||||
let mut rd = &buf[..];
|
||||
for _ in 0..8 {
|
||||
let _ = rd.read(&mut dst);
|
||||
test::black_box(&dst);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_write_vec(b: &mut test::Bencher) {
|
||||
let mut buf = Vec::with_capacity(1024);
|
||||
let src = [5; 128];
|
||||
|
||||
b.iter(|| {
|
||||
let mut wr = &mut buf[..];
|
||||
for _ in 0..8 {
|
||||
let _ = wr.write_all(&src);
|
||||
test::black_box(&wr);
|
||||
}
|
||||
})
|
||||
}
|
||||
295
crates/std/src/io/pipe.rs
Normal file
295
crates/std/src/io/pipe.rs
Normal file
@@ -0,0 +1,295 @@
|
||||
use crate::io;
|
||||
use crate::sys::{FromInner, IntoInner, pipe as imp};
|
||||
|
||||
/// Creates an anonymous pipe.
|
||||
///
|
||||
/// # Behavior
|
||||
///
|
||||
/// A pipe is a one-way data channel provided by the OS, which works across processes. A pipe is
|
||||
/// typically used to communicate between two or more separate processes, as there are better,
|
||||
/// faster ways to communicate within a single process.
|
||||
///
|
||||
/// In particular:
|
||||
///
|
||||
/// * A read on a [`PipeReader`] blocks until the pipe is non-empty.
|
||||
/// * A write on a [`PipeWriter`] blocks when the pipe is full.
|
||||
/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`]
|
||||
/// returns EOF.
|
||||
/// * [`PipeWriter`] can be shared, and multiple processes or threads can write to it at once, but
|
||||
/// writes (above a target-specific threshold) may have their data interleaved.
|
||||
/// * [`PipeReader`] can be shared, and multiple processes or threads can read it at once. Any
|
||||
/// given byte will only get consumed by one reader. There are no guarantees about data
|
||||
/// interleaving.
|
||||
/// * Portable applications cannot assume any atomicity of messages larger than a single byte.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `pipe` function on Unix and the
|
||||
/// `CreatePipe` function on Windows.
|
||||
///
|
||||
/// Note that this [may change in the future][changes].
|
||||
///
|
||||
/// # Capacity
|
||||
///
|
||||
/// Pipe capacity is platform dependent. To quote the Linux [man page]:
|
||||
///
|
||||
/// > Different implementations have different limits for the pipe capacity. Applications should
|
||||
/// > not rely on a particular capacity: an application should be designed so that a reading process
|
||||
/// > consumes data as soon as it is available, so that a writing process does not remain blocked.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #[cfg(miri)] fn main() {}
|
||||
/// # #[cfg(not(miri))]
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// use std::io::{Read, Write, pipe};
|
||||
/// use std::process::Command;
|
||||
/// let (ping_reader, mut ping_writer) = pipe()?;
|
||||
/// let (mut pong_reader, pong_writer) = pipe()?;
|
||||
///
|
||||
/// // Spawn a child process that echoes its input.
|
||||
/// let mut echo_command = Command::new("cat");
|
||||
/// echo_command.stdin(ping_reader);
|
||||
/// echo_command.stdout(pong_writer);
|
||||
/// let mut echo_child = echo_command.spawn()?;
|
||||
///
|
||||
/// // Send input to the child process. Note that because we're writing all the input before we
|
||||
/// // read any output, this could deadlock if the child's input and output pipe buffers both
|
||||
/// // filled up. Those buffers are usually at least a few KB, so "hello" is fine, but for longer
|
||||
/// // inputs we'd need to read and write at the same time, e.g. using threads.
|
||||
/// ping_writer.write_all(b"hello")?;
|
||||
///
|
||||
/// // `cat` exits when it reads EOF from stdin, but that can't happen while any ping writer
|
||||
/// // remains open. We need to drop our ping writer, or read_to_string will deadlock below.
|
||||
/// drop(ping_writer);
|
||||
///
|
||||
/// // The pong reader can't report EOF while any pong writer remains open. Our Command object is
|
||||
/// // holding a pong writer, and again read_to_string will deadlock if we don't drop it.
|
||||
/// drop(echo_command);
|
||||
///
|
||||
/// let mut buf = String::new();
|
||||
/// // Block until `cat` closes its stdout (a pong writer).
|
||||
/// pong_reader.read_to_string(&mut buf)?;
|
||||
/// assert_eq!(&buf, "hello");
|
||||
///
|
||||
/// // At this point we know `cat` has exited, but we still need to wait to clean up the "zombie".
|
||||
/// echo_child.wait()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
/// [changes]: io#platform-specific-behavior
|
||||
/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
#[inline]
|
||||
pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> {
|
||||
imp::pipe().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer)))
|
||||
}
|
||||
|
||||
/// Read end of an anonymous pipe.
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
#[derive(Debug)]
|
||||
pub struct PipeReader(pub(crate) imp::Pipe);
|
||||
|
||||
/// Write end of an anonymous pipe.
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
#[derive(Debug)]
|
||||
pub struct PipeWriter(pub(crate) imp::Pipe);
|
||||
|
||||
impl FromInner<imp::Pipe> for PipeReader {
|
||||
fn from_inner(inner: imp::Pipe) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoInner<imp::Pipe> for PipeReader {
|
||||
fn into_inner(self) -> imp::Pipe {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromInner<imp::Pipe> for PipeWriter {
|
||||
fn from_inner(inner: imp::Pipe) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoInner<imp::Pipe> for PipeWriter {
|
||||
fn into_inner(self) -> imp::Pipe {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PipeReader {
|
||||
/// Creates a new [`PipeReader`] instance that shares the same underlying file description.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #[cfg(miri)] fn main() {}
|
||||
/// # #[cfg(not(miri))]
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// use std::fs;
|
||||
/// use std::io::{pipe, Write};
|
||||
/// use std::process::Command;
|
||||
/// const NUM_SLOT: u8 = 2;
|
||||
/// const NUM_PROC: u8 = 5;
|
||||
/// const OUTPUT: &str = "work.txt";
|
||||
///
|
||||
/// let mut jobs = vec![];
|
||||
/// let (reader, mut writer) = pipe()?;
|
||||
///
|
||||
/// // Write NUM_SLOT characters the pipe.
|
||||
/// writer.write_all(&[b'|'; NUM_SLOT as usize])?;
|
||||
///
|
||||
/// // Spawn several processes that read a character from the pipe, do some work, then
|
||||
/// // write back to the pipe. When the pipe is empty, the processes block, so only
|
||||
/// // NUM_SLOT processes can be working at any given time.
|
||||
/// for _ in 0..NUM_PROC {
|
||||
/// jobs.push(
|
||||
/// Command::new("bash")
|
||||
/// .args(["-c",
|
||||
/// &format!(
|
||||
/// "read -n 1\n\
|
||||
/// echo -n 'x' >> '{OUTPUT}'\n\
|
||||
/// echo -n '|'",
|
||||
/// ),
|
||||
/// ])
|
||||
/// .stdin(reader.try_clone()?)
|
||||
/// .stdout(writer.try_clone()?)
|
||||
/// .spawn()?,
|
||||
/// );
|
||||
/// }
|
||||
///
|
||||
/// // Wait for all jobs to finish.
|
||||
/// for mut job in jobs {
|
||||
/// job.wait()?;
|
||||
/// }
|
||||
///
|
||||
/// // Check our work and clean up.
|
||||
/// let xs = fs::read_to_string(OUTPUT)?;
|
||||
/// fs::remove_file(OUTPUT)?;
|
||||
/// assert_eq!(xs, "x".repeat(NUM_PROC.into()));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
pub fn try_clone(&self) -> io::Result<Self> {
|
||||
self.0.try_clone().map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PipeWriter {
|
||||
/// Creates a new [`PipeWriter`] instance that shares the same underlying file description.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #[cfg(miri)] fn main() {}
|
||||
/// # #[cfg(not(miri))]
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// use std::process::Command;
|
||||
/// use std::io::{pipe, Read};
|
||||
/// let (mut reader, writer) = pipe()?;
|
||||
///
|
||||
/// // Spawn a process that writes to stdout and stderr.
|
||||
/// let mut peer = Command::new("bash")
|
||||
/// .args([
|
||||
/// "-c",
|
||||
/// "echo -n foo\n\
|
||||
/// echo -n bar >&2"
|
||||
/// ])
|
||||
/// .stdout(writer.try_clone()?)
|
||||
/// .stderr(writer)
|
||||
/// .spawn()?;
|
||||
///
|
||||
/// // Read and check the result.
|
||||
/// let mut msg = String::new();
|
||||
/// reader.read_to_string(&mut msg)?;
|
||||
/// assert_eq!(&msg, "foobar");
|
||||
///
|
||||
/// peer.wait()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
pub fn try_clone(&self) -> io::Result<Self> {
|
||||
self.0.try_clone().map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
impl io::Read for &PipeReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.read_vectored(bufs)
|
||||
}
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
self.0.is_read_vectored()
|
||||
}
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
self.0.read_to_end(buf)
|
||||
}
|
||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.0.read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
impl io::Read for PipeReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.read_vectored(bufs)
|
||||
}
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
self.0.is_read_vectored()
|
||||
}
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
self.0.read_to_end(buf)
|
||||
}
|
||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.0.read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
impl io::Write for &PipeWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.write_vectored(bufs)
|
||||
}
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.0.is_write_vectored()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
impl io::Write for PipeWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.write_vectored(bufs)
|
||||
}
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.0.is_write_vectored()
|
||||
}
|
||||
}
|
||||
18
crates/std/src/io/pipe/tests.rs
Normal file
18
crates/std/src/io/pipe/tests.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use crate::io::{Read, Write, pipe};
|
||||
|
||||
#[test]
|
||||
#[cfg(all(any(unix, windows), not(miri)))]
|
||||
fn pipe_creation_clone_and_rw() {
|
||||
let (rx, tx) = pipe().unwrap();
|
||||
|
||||
tx.try_clone().unwrap().write_all(b"12345").unwrap();
|
||||
drop(tx);
|
||||
|
||||
let mut rx2 = rx.try_clone().unwrap();
|
||||
drop(rx);
|
||||
|
||||
let mut s = String::new();
|
||||
rx2.read_to_string(&mut s).unwrap();
|
||||
drop(rx2);
|
||||
assert_eq!(s, "12345");
|
||||
}
|
||||
14
crates/std/src/io/prelude.rs
Normal file
14
crates/std/src/io/prelude.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
//! The I/O Prelude.
|
||||
//!
|
||||
//! The purpose of this module is to alleviate imports of many common I/O traits
|
||||
//! by adding a glob import to the top of I/O heavy modules:
|
||||
//!
|
||||
//! ```
|
||||
//! # #![allow(unused_imports)]
|
||||
//! use std::io::prelude::*;
|
||||
//! ```
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use super::{BufRead, Read, Seek, Write};
|
||||
1290
crates/std/src/io/stdio.rs
Normal file
1290
crates/std/src/io/stdio.rs
Normal file
File diff suppressed because it is too large
Load Diff
947
crates/std/src/io/tests.rs
Normal file
947
crates/std/src/io/tests.rs
Normal file
@@ -0,0 +1,947 @@
|
||||
use super::{BorrowedBuf, Cursor, SeekFrom, repeat};
|
||||
use crate::cmp::{self, min};
|
||||
use crate::io::{
|
||||
self, BufRead, BufReader, DEFAULT_BUF_SIZE, IoSlice, IoSliceMut, Read, Seek, Write,
|
||||
};
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ops::Deref;
|
||||
|
||||
#[test]
|
||||
fn read_until() {
|
||||
let mut buf = Cursor::new(&b"12"[..]);
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 2);
|
||||
assert_eq!(v, b"12");
|
||||
|
||||
let mut buf = Cursor::new(&b"1233"[..]);
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 3);
|
||||
assert_eq!(v, b"123");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 1);
|
||||
assert_eq!(v, b"3");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 0);
|
||||
assert_eq!(v, []);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_until() {
|
||||
let bytes: &[u8] = b"read\0ignore\0read\0ignore\0read\0ignore\0";
|
||||
let mut reader = BufReader::new(bytes);
|
||||
|
||||
// read from the bytes, alternating between
|
||||
// consuming `read\0`s and skipping `ignore\0`s
|
||||
loop {
|
||||
// consume `read\0`
|
||||
let mut out = Vec::new();
|
||||
let read = reader.read_until(0, &mut out).unwrap();
|
||||
if read == 0 {
|
||||
// eof
|
||||
break;
|
||||
} else {
|
||||
assert_eq!(out, b"read\0");
|
||||
assert_eq!(read, b"read\0".len());
|
||||
}
|
||||
|
||||
// skip past `ignore\0`
|
||||
let skipped = reader.skip_until(0).unwrap();
|
||||
assert_eq!(skipped, b"ignore\0".len());
|
||||
}
|
||||
|
||||
// ensure we are at the end of the byte slice and that we can skip no further
|
||||
// also ensure skip_until matches the behavior of read_until at EOF
|
||||
let skipped = reader.skip_until(0).unwrap();
|
||||
assert_eq!(skipped, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split() {
|
||||
let buf = Cursor::new(&b"12"[..]);
|
||||
let mut s = buf.split(b'3');
|
||||
assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']);
|
||||
assert!(s.next().is_none());
|
||||
|
||||
let buf = Cursor::new(&b"1233"[..]);
|
||||
let mut s = buf.split(b'3');
|
||||
assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']);
|
||||
assert_eq!(s.next().unwrap().unwrap(), vec![]);
|
||||
assert!(s.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_line() {
|
||||
let mut buf = Cursor::new(&b"12"[..]);
|
||||
let mut v = String::new();
|
||||
assert_eq!(buf.read_line(&mut v).unwrap(), 2);
|
||||
assert_eq!(v, "12");
|
||||
|
||||
let mut buf = Cursor::new(&b"12\n\n"[..]);
|
||||
let mut v = String::new();
|
||||
assert_eq!(buf.read_line(&mut v).unwrap(), 3);
|
||||
assert_eq!(v, "12\n");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_line(&mut v).unwrap(), 1);
|
||||
assert_eq!(v, "\n");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_line(&mut v).unwrap(), 0);
|
||||
assert_eq!(v, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines() {
|
||||
let buf = Cursor::new(&b"12\r"[..]);
|
||||
let mut s = buf.lines();
|
||||
assert_eq!(s.next().unwrap().unwrap(), "12\r".to_string());
|
||||
assert!(s.next().is_none());
|
||||
|
||||
let buf = Cursor::new(&b"12\r\n\n"[..]);
|
||||
let mut s = buf.lines();
|
||||
assert_eq!(s.next().unwrap().unwrap(), "12".to_string());
|
||||
assert_eq!(s.next().unwrap().unwrap(), "".to_string());
|
||||
assert!(s.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buf_read_has_data_left() {
|
||||
let mut buf = Cursor::new(&b"abcd"[..]);
|
||||
assert!(buf.has_data_left().unwrap());
|
||||
buf.read_exact(&mut [0; 2]).unwrap();
|
||||
assert!(buf.has_data_left().unwrap());
|
||||
buf.read_exact(&mut [0; 2]).unwrap();
|
||||
assert!(!buf.has_data_left().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_end() {
|
||||
let mut c = Cursor::new(&b""[..]);
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(c.read_to_end(&mut v).unwrap(), 0);
|
||||
assert_eq!(v, []);
|
||||
|
||||
let mut c = Cursor::new(&b"1"[..]);
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(c.read_to_end(&mut v).unwrap(), 1);
|
||||
assert_eq!(v, b"1");
|
||||
|
||||
let cap = if cfg!(miri) { 1024 } else { 1024 * 1024 };
|
||||
let data = (0..cap).map(|i| (i / 3) as u8).collect::<Vec<_>>();
|
||||
let mut v = Vec::new();
|
||||
let (a, b) = data.split_at(data.len() / 2);
|
||||
assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len());
|
||||
assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len());
|
||||
assert_eq!(v, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_string() {
|
||||
let mut c = Cursor::new(&b""[..]);
|
||||
let mut v = String::new();
|
||||
assert_eq!(c.read_to_string(&mut v).unwrap(), 0);
|
||||
assert_eq!(v, "");
|
||||
|
||||
let mut c = Cursor::new(&b"1"[..]);
|
||||
let mut v = String::new();
|
||||
assert_eq!(c.read_to_string(&mut v).unwrap(), 1);
|
||||
assert_eq!(v, "1");
|
||||
|
||||
let mut c = Cursor::new(&b"\xff"[..]);
|
||||
let mut v = String::new();
|
||||
assert!(c.read_to_string(&mut v).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_exact() {
|
||||
let mut buf = [0; 4];
|
||||
|
||||
let mut c = Cursor::new(&b""[..]);
|
||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
|
||||
let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..]));
|
||||
c.read_exact(&mut buf).unwrap();
|
||||
assert_eq!(&buf, b"1234");
|
||||
c.read_exact(&mut buf).unwrap();
|
||||
assert_eq!(&buf, b"5678");
|
||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_exact_slice() {
|
||||
let mut buf = [0; 4];
|
||||
|
||||
let mut c = &b""[..];
|
||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
|
||||
let mut c = &b"123"[..];
|
||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
// make sure the optimized (early returning) method is being used
|
||||
assert_eq!(&buf, &[0; 4]);
|
||||
|
||||
let mut c = &b"1234"[..];
|
||||
c.read_exact(&mut buf).unwrap();
|
||||
assert_eq!(&buf, b"1234");
|
||||
|
||||
let mut c = &b"56789"[..];
|
||||
c.read_exact(&mut buf).unwrap();
|
||||
assert_eq!(&buf, b"5678");
|
||||
assert_eq!(c, b"9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_exact() {
|
||||
let buf: &mut [_] = &mut [0; 4];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
|
||||
let mut c = Cursor::new(&b""[..]);
|
||||
assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
|
||||
let mut c = Cursor::new(&b"123456789"[..]);
|
||||
c.read_buf_exact(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.filled(), b"1234");
|
||||
|
||||
buf.clear();
|
||||
|
||||
c.read_buf_exact(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.filled(), b"5678");
|
||||
|
||||
buf.clear();
|
||||
|
||||
assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn borrowed_cursor_advance_overflow() {
|
||||
let mut buf = [0; 512];
|
||||
let mut buf = BorrowedBuf::from(&mut buf[..]);
|
||||
buf.unfilled().advance(1);
|
||||
buf.unfilled().advance(usize::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_eof() {
|
||||
struct R;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
Err(io::const_error!(io::ErrorKind::Other, ""))
|
||||
}
|
||||
}
|
||||
impl BufRead for R {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
Err(io::const_error!(io::ErrorKind::Other, ""))
|
||||
}
|
||||
fn consume(&mut self, _amt: usize) {}
|
||||
}
|
||||
|
||||
let mut buf = [0; 1];
|
||||
assert_eq!(0, R.take(0).read(&mut buf).unwrap());
|
||||
assert_eq!(b"", R.take(0).fill_buf().unwrap());
|
||||
}
|
||||
|
||||
fn cmp_bufread<Br1: BufRead, Br2: BufRead>(mut br1: Br1, mut br2: Br2, exp: &[u8]) {
|
||||
let mut cat = Vec::new();
|
||||
loop {
|
||||
let consume = {
|
||||
let buf1 = br1.fill_buf().unwrap();
|
||||
let buf2 = br2.fill_buf().unwrap();
|
||||
let minlen = if buf1.len() < buf2.len() { buf1.len() } else { buf2.len() };
|
||||
assert_eq!(buf1[..minlen], buf2[..minlen]);
|
||||
cat.extend_from_slice(&buf1[..minlen]);
|
||||
minlen
|
||||
};
|
||||
if consume == 0 {
|
||||
break;
|
||||
}
|
||||
br1.consume(consume);
|
||||
br2.consume(consume);
|
||||
}
|
||||
assert_eq!(br1.fill_buf().unwrap().len(), 0);
|
||||
assert_eq!(br2.fill_buf().unwrap().len(), 0);
|
||||
assert_eq!(&cat[..], &exp[..])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_bufread() {
|
||||
let testdata = b"ABCDEFGHIJKL";
|
||||
let chain1 =
|
||||
(&testdata[..3]).chain(&testdata[3..6]).chain(&testdata[6..9]).chain(&testdata[9..]);
|
||||
let chain2 = (&testdata[..4]).chain(&testdata[4..8]).chain(&testdata[8..]);
|
||||
cmp_bufread(chain1, chain2, &testdata[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_splitted_char() {
|
||||
let chain = b"\xc3".chain(b"\xa9".as_slice());
|
||||
assert_eq!(crate::io::read_to_string(chain).unwrap(), "é");
|
||||
|
||||
let mut chain = b"\xc3".chain(b"\xa9\n".as_slice());
|
||||
let mut buf = String::new();
|
||||
assert_eq!(chain.read_line(&mut buf).unwrap(), 3);
|
||||
assert_eq!(buf, "é\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bufreader_size_hint() {
|
||||
let testdata = b"ABCDEFGHIJKL";
|
||||
let mut buf_reader = BufReader::new(&testdata[..]);
|
||||
assert_eq!(buf_reader.buffer().len(), 0);
|
||||
|
||||
let buffer_length = testdata.len();
|
||||
buf_reader.fill_buf().unwrap();
|
||||
|
||||
// Check that size hint matches buffer contents
|
||||
let mut buffered_bytes = buf_reader.bytes();
|
||||
let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
|
||||
assert_eq!(lower_bound, buffer_length);
|
||||
|
||||
// Check that size hint matches buffer contents after advancing
|
||||
buffered_bytes.next().unwrap().unwrap();
|
||||
let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
|
||||
assert_eq!(lower_bound, buffer_length - 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_size_hint() {
|
||||
let size_hint = io::empty().bytes().size_hint();
|
||||
assert_eq!(size_hint, (0, Some(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slice_size_hint() {
|
||||
let size_hint = (&[1, 2, 3]).bytes().size_hint();
|
||||
assert_eq!(size_hint, (3, Some(3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_size_hint() {
|
||||
let size_hint = (&[1, 2, 3]).take(2).bytes().size_hint();
|
||||
assert_eq!(size_hint, (2, Some(2)));
|
||||
|
||||
let size_hint = (&[1, 2, 3]).take(4).bytes().size_hint();
|
||||
assert_eq!(size_hint, (3, Some(3)));
|
||||
|
||||
let size_hint = io::repeat(0).take(3).bytes().size_hint();
|
||||
assert_eq!(size_hint, (3, Some(3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_empty_size_hint() {
|
||||
let chain = io::empty().chain(io::empty());
|
||||
let size_hint = chain.bytes().size_hint();
|
||||
assert_eq!(size_hint, (0, Some(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_size_hint() {
|
||||
let testdata = b"ABCDEFGHIJKL";
|
||||
let mut buf_reader_1 = BufReader::new(&testdata[..6]);
|
||||
let mut buf_reader_2 = BufReader::new(&testdata[6..]);
|
||||
|
||||
buf_reader_1.fill_buf().unwrap();
|
||||
buf_reader_2.fill_buf().unwrap();
|
||||
|
||||
let chain = buf_reader_1.chain(buf_reader_2);
|
||||
let size_hint = chain.bytes().size_hint();
|
||||
assert_eq!(size_hint, (testdata.len(), Some(testdata.len())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_zero_length_read_is_not_eof() {
|
||||
let a = b"A";
|
||||
let b = b"B";
|
||||
let mut s = String::new();
|
||||
let mut chain = (&a[..]).chain(&b[..]);
|
||||
chain.read(&mut []).unwrap();
|
||||
chain.read_to_string(&mut s).unwrap();
|
||||
assert_eq!("AB", s);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
#[cfg_attr(miri, ignore)] // Miri isn't fast...
|
||||
fn bench_read_to_end(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
let mut lr = repeat(1).take(10000000);
|
||||
let mut vec = Vec::with_capacity(1024);
|
||||
super::default_read_to_end(&mut lr, &mut vec, None)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_len() -> io::Result<()> {
|
||||
let mut c = Cursor::new(vec![0; 15]);
|
||||
assert_eq!(c.stream_len()?, 15);
|
||||
|
||||
c.seek(SeekFrom::End(0))?;
|
||||
let old_pos = c.stream_position()?;
|
||||
assert_eq!(c.stream_len()?, 15);
|
||||
assert_eq!(c.stream_position()?, old_pos);
|
||||
|
||||
c.seek(SeekFrom::Start(7))?;
|
||||
c.seek(SeekFrom::Current(2))?;
|
||||
let old_pos = c.stream_position()?;
|
||||
assert_eq!(c.stream_len()?, 15);
|
||||
assert_eq!(c.stream_position()?, old_pos);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_position() -> io::Result<()> {
|
||||
// All `asserts` are duplicated here to make sure the method does not
|
||||
// change anything about the seek state.
|
||||
let mut c = Cursor::new(vec![0; 15]);
|
||||
assert_eq!(c.stream_position()?, 0);
|
||||
assert_eq!(c.stream_position()?, 0);
|
||||
|
||||
c.seek(SeekFrom::End(0))?;
|
||||
assert_eq!(c.stream_position()?, 15);
|
||||
assert_eq!(c.stream_position()?, 15);
|
||||
|
||||
c.seek(SeekFrom::Start(7))?;
|
||||
c.seek(SeekFrom::Current(2))?;
|
||||
assert_eq!(c.stream_position()?, 9);
|
||||
assert_eq!(c.stream_position()?, 9);
|
||||
|
||||
c.seek(SeekFrom::End(-3))?;
|
||||
c.seek(SeekFrom::Current(1))?;
|
||||
c.seek(SeekFrom::Current(-5))?;
|
||||
assert_eq!(c.stream_position()?, 8);
|
||||
assert_eq!(c.stream_position()?, 8);
|
||||
|
||||
c.rewind()?;
|
||||
assert_eq!(c.stream_position()?, 0);
|
||||
assert_eq!(c.stream_position()?, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_seek() -> io::Result<()> {
|
||||
let mut buf = Cursor::new(b"0123456789");
|
||||
buf.set_position(2);
|
||||
let mut take = buf.by_ref().take(4);
|
||||
let mut buf1 = [0u8; 1];
|
||||
let mut buf2 = [0u8; 2];
|
||||
assert_eq!(take.position(), 0);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Start(0))?, 0);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'2', b'3']);
|
||||
assert_eq!(take.seek(SeekFrom::Start(1))?, 1);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'3', b'4']);
|
||||
assert_eq!(take.seek(SeekFrom::Start(2))?, 2);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'4', b'5']);
|
||||
assert_eq!(take.seek(SeekFrom::Start(3))?, 3);
|
||||
take.read_exact(&mut buf1)?;
|
||||
assert_eq!(buf1, [b'5']);
|
||||
assert_eq!(take.seek(SeekFrom::Start(4))?, 4);
|
||||
assert_eq!(take.read(&mut buf1)?, 0);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::End(0))?, 4);
|
||||
assert_eq!(take.seek(SeekFrom::End(-1))?, 3);
|
||||
take.read_exact(&mut buf1)?;
|
||||
assert_eq!(buf1, [b'5']);
|
||||
assert_eq!(take.seek(SeekFrom::End(-2))?, 2);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'4', b'5']);
|
||||
assert_eq!(take.seek(SeekFrom::End(-3))?, 1);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'3', b'4']);
|
||||
assert_eq!(take.seek(SeekFrom::End(-4))?, 0);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'2', b'3']);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Current(0))?, 2);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'4', b'5']);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Current(-3))?, 1);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'3', b'4']);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Current(-1))?, 2);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'4', b'5']);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Current(-4))?, 0);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'2', b'3']);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Current(2))?, 4);
|
||||
assert_eq!(take.read(&mut buf1)?, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_seek_error() {
|
||||
let buf = Cursor::new(b"0123456789");
|
||||
let mut take = buf.take(2);
|
||||
assert!(take.seek(SeekFrom::Start(3)).is_err());
|
||||
assert!(take.seek(SeekFrom::End(1)).is_err());
|
||||
assert!(take.seek(SeekFrom::End(-3)).is_err());
|
||||
assert!(take.seek(SeekFrom::Current(-1)).is_err());
|
||||
assert!(take.seek(SeekFrom::Current(3)).is_err());
|
||||
}
|
||||
|
||||
struct ExampleHugeRangeOfZeroes {
|
||||
position: u64,
|
||||
}
|
||||
|
||||
impl Read for ExampleHugeRangeOfZeroes {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let max = buf.len().min(usize::MAX);
|
||||
for i in 0..max {
|
||||
if self.position == u64::MAX {
|
||||
return Ok(i);
|
||||
}
|
||||
self.position += 1;
|
||||
buf[i] = 0;
|
||||
}
|
||||
Ok(max)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for ExampleHugeRangeOfZeroes {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
match pos {
|
||||
io::SeekFrom::Start(i) => self.position = i,
|
||||
io::SeekFrom::End(i) if i >= 0 => self.position = u64::MAX,
|
||||
io::SeekFrom::End(i) => self.position = self.position - i.unsigned_abs(),
|
||||
io::SeekFrom::Current(i) => {
|
||||
self.position = if i >= 0 {
|
||||
self.position.saturating_add(i.unsigned_abs())
|
||||
} else {
|
||||
self.position.saturating_sub(i.unsigned_abs())
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(self.position)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_seek_big_offsets() -> io::Result<()> {
|
||||
let inner = ExampleHugeRangeOfZeroes { position: 1 };
|
||||
let mut take = inner.take(u64::MAX - 2);
|
||||
assert_eq!(take.seek(io::SeekFrom::Start(u64::MAX - 2))?, u64::MAX - 2);
|
||||
assert_eq!(take.inner.position, u64::MAX - 1);
|
||||
assert_eq!(take.seek(io::SeekFrom::Start(0))?, 0);
|
||||
assert_eq!(take.inner.position, 1);
|
||||
assert_eq!(take.seek(io::SeekFrom::End(-1))?, u64::MAX - 3);
|
||||
assert_eq!(take.inner.position, u64::MAX - 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// A simple example reader which uses the default implementation of
|
||||
// read_to_end.
|
||||
struct ExampleSliceReader<'a> {
|
||||
slice: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> Read for ExampleSliceReader<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let len = cmp::min(self.slice.len(), buf.len());
|
||||
buf[..len].copy_from_slice(&self.slice[..len]);
|
||||
self.slice = &self.slice[len..];
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_to_end_capacity() -> io::Result<()> {
|
||||
let input = &b"foo"[..];
|
||||
|
||||
// read_to_end() takes care not to over-allocate when a buffer is the
|
||||
// exact size needed.
|
||||
let mut vec1 = Vec::with_capacity(input.len());
|
||||
ExampleSliceReader { slice: input }.read_to_end(&mut vec1)?;
|
||||
assert_eq!(vec1.len(), input.len());
|
||||
assert_eq!(vec1.capacity(), input.len(), "did not allocate more");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn io_slice_mut_advance_slices() {
|
||||
let mut buf1 = [1; 8];
|
||||
let mut buf2 = [2; 16];
|
||||
let mut buf3 = [3; 8];
|
||||
let mut bufs = &mut [
|
||||
IoSliceMut::new(&mut buf1),
|
||||
IoSliceMut::new(&mut buf2),
|
||||
IoSliceMut::new(&mut buf3),
|
||||
][..];
|
||||
|
||||
// Only in a single buffer..
|
||||
IoSliceMut::advance_slices(&mut bufs, 1);
|
||||
assert_eq!(bufs[0].deref(), [1; 7].as_ref());
|
||||
assert_eq!(bufs[1].deref(), [2; 16].as_ref());
|
||||
assert_eq!(bufs[2].deref(), [3; 8].as_ref());
|
||||
|
||||
// Removing a buffer, leaving others as is.
|
||||
IoSliceMut::advance_slices(&mut bufs, 7);
|
||||
assert_eq!(bufs[0].deref(), [2; 16].as_ref());
|
||||
assert_eq!(bufs[1].deref(), [3; 8].as_ref());
|
||||
|
||||
// Removing a buffer and removing from the next buffer.
|
||||
IoSliceMut::advance_slices(&mut bufs, 18);
|
||||
assert_eq!(bufs[0].deref(), [3; 6].as_ref());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn io_slice_mut_advance_slices_empty_slice() {
|
||||
let mut empty_bufs = &mut [][..];
|
||||
IoSliceMut::advance_slices(&mut empty_bufs, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn io_slice_mut_advance_slices_beyond_total_length() {
|
||||
let mut buf1 = [1; 8];
|
||||
let mut bufs = &mut [IoSliceMut::new(&mut buf1)][..];
|
||||
|
||||
IoSliceMut::advance_slices(&mut bufs, 9);
|
||||
assert!(bufs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn io_slice_advance_slices() {
|
||||
let buf1 = [1; 8];
|
||||
let buf2 = [2; 16];
|
||||
let buf3 = [3; 8];
|
||||
let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..];
|
||||
|
||||
// Only in a single buffer..
|
||||
IoSlice::advance_slices(&mut bufs, 1);
|
||||
assert_eq!(bufs[0].deref(), [1; 7].as_ref());
|
||||
assert_eq!(bufs[1].deref(), [2; 16].as_ref());
|
||||
assert_eq!(bufs[2].deref(), [3; 8].as_ref());
|
||||
|
||||
// Removing a buffer, leaving others as is.
|
||||
IoSlice::advance_slices(&mut bufs, 7);
|
||||
assert_eq!(bufs[0].deref(), [2; 16].as_ref());
|
||||
assert_eq!(bufs[1].deref(), [3; 8].as_ref());
|
||||
|
||||
// Removing a buffer and removing from the next buffer.
|
||||
IoSlice::advance_slices(&mut bufs, 18);
|
||||
assert_eq!(bufs[0].deref(), [3; 6].as_ref());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn io_slice_advance_slices_empty_slice() {
|
||||
let mut empty_bufs = &mut [][..];
|
||||
IoSlice::advance_slices(&mut empty_bufs, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn io_slice_advance_slices_beyond_total_length() {
|
||||
let buf1 = [1; 8];
|
||||
let mut bufs = &mut [IoSlice::new(&buf1)][..];
|
||||
|
||||
IoSlice::advance_slices(&mut bufs, 9);
|
||||
assert!(bufs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn io_slice_as_slice() {
|
||||
let buf = [1; 8];
|
||||
let slice = IoSlice::new(&buf).as_slice();
|
||||
assert_eq!(slice, buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn io_slice_into_slice() {
|
||||
let mut buf = [1; 8];
|
||||
let slice = IoSliceMut::new(&mut buf).into_slice();
|
||||
assert_eq!(slice, [1; 8]);
|
||||
}
|
||||
|
||||
/// Creates a new writer that reads from at most `n_bufs` and reads
|
||||
/// `per_call` bytes (in total) per call to write.
|
||||
fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter {
|
||||
TestWriter { n_bufs, per_call, written: Vec::new() }
|
||||
}
|
||||
|
||||
struct TestWriter {
|
||||
n_bufs: usize,
|
||||
per_call: usize,
|
||||
written: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Write for TestWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.write_vectored(&[IoSlice::new(buf)])
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let mut left = self.per_call;
|
||||
let mut written = 0;
|
||||
for buf in bufs.iter().take(self.n_bufs) {
|
||||
let n = min(left, buf.len());
|
||||
self.written.extend_from_slice(&buf[0..n]);
|
||||
left -= n;
|
||||
written += n;
|
||||
}
|
||||
Ok(written)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_writer_read_from_one_buf() {
|
||||
let mut writer = test_writer(1, 2);
|
||||
|
||||
assert_eq!(writer.write(&[]).unwrap(), 0);
|
||||
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
||||
|
||||
// Read at most 2 bytes.
|
||||
assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2);
|
||||
let bufs = &[IoSlice::new(&[2, 2, 2])];
|
||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 2);
|
||||
|
||||
// Only read from first buf.
|
||||
let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])];
|
||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 1);
|
||||
|
||||
assert_eq!(writer.written, &[1, 1, 2, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_writer_read_from_multiple_bufs() {
|
||||
let mut writer = test_writer(3, 3);
|
||||
|
||||
// Read at most 3 bytes from two buffers.
|
||||
let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])];
|
||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
|
||||
|
||||
// Read at most 3 bytes from three buffers.
|
||||
let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])];
|
||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
|
||||
|
||||
assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_all_vectored() {
|
||||
#[rustfmt::skip] // Becomes unreadable otherwise.
|
||||
let tests: Vec<(_, &'static [u8])> = vec![
|
||||
(vec![], &[]),
|
||||
(vec![IoSlice::new(&[]), IoSlice::new(&[])], &[]),
|
||||
(vec![IoSlice::new(&[1])], &[1]),
|
||||
(vec![IoSlice::new(&[1, 2])], &[1, 2]),
|
||||
(vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]),
|
||||
(vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]),
|
||||
(vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]),
|
||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]),
|
||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]),
|
||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]),
|
||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]),
|
||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]),
|
||||
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]),
|
||||
];
|
||||
|
||||
let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)];
|
||||
|
||||
for (n_bufs, per_call) in writer_configs.iter().copied() {
|
||||
for (mut input, wanted) in tests.clone().into_iter() {
|
||||
let mut writer = test_writer(n_bufs, per_call);
|
||||
assert!(writer.write_all_vectored(&mut *input).is_ok());
|
||||
assert_eq!(&*writer.written, &*wanted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 94981
|
||||
#[test]
|
||||
#[should_panic = "number of read bytes exceeds limit"]
|
||||
fn test_take_wrong_length() {
|
||||
struct LieAboutSize(bool);
|
||||
|
||||
impl Read for LieAboutSize {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// Lie about the read size at first time of read.
|
||||
if core::mem::take(&mut self.0) { Ok(buf.len() + 1) } else { Ok(buf.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
let mut buffer = vec![0; 4];
|
||||
let mut reader = LieAboutSize(true).take(4);
|
||||
// Primed the `Limit` by lying about the read size.
|
||||
let _ = reader.read(&mut buffer[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slice_read_exact_eof() {
|
||||
let slice = &b"123456"[..];
|
||||
|
||||
let mut r = slice;
|
||||
assert!(r.read_exact(&mut [0; 10]).is_err());
|
||||
assert!(r.is_empty());
|
||||
|
||||
let mut r = slice;
|
||||
let buf = &mut [0; 10];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
assert!(r.read_buf_exact(buf.unfilled()).is_err());
|
||||
assert!(r.is_empty());
|
||||
assert_eq!(buf.filled(), b"123456");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cursor_read_exact_eof() {
|
||||
let slice = Cursor::new(b"123456");
|
||||
|
||||
let mut r = slice.clone();
|
||||
assert!(r.read_exact(&mut [0; 10]).is_err());
|
||||
assert!(Cursor::split(&r).1.is_empty());
|
||||
|
||||
let mut r = slice;
|
||||
let buf = &mut [0; 10];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
assert!(r.read_buf_exact(buf.unfilled()).is_err());
|
||||
assert!(Cursor::split(&r).1.is_empty());
|
||||
assert_eq!(buf.filled(), b"123456");
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_take_read(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
let mut buf = [0; 64];
|
||||
|
||||
[255; 128].take(64).read(&mut buf).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_take_read_buf(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 64];
|
||||
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
|
||||
[255; 128].take(64).read_buf(buf.unfilled()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
// Issue #120603
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn read_buf_broken_read() {
|
||||
struct MalformedRead;
|
||||
|
||||
impl Read for MalformedRead {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// broken length calculation
|
||||
Ok(buf.len() + 1)
|
||||
}
|
||||
}
|
||||
|
||||
let _ = BufReader::new(MalformedRead).fill_buf();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_full_read() {
|
||||
struct FullRead;
|
||||
|
||||
impl Read for FullRead {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(BufReader::new(FullRead).fill_buf().unwrap().len(), DEFAULT_BUF_SIZE);
|
||||
}
|
||||
|
||||
struct DataAndErrorReader(&'static [u8]);
|
||||
|
||||
impl Read for DataAndErrorReader {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
panic!("We want tests to use `read_buf`")
|
||||
}
|
||||
|
||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.0.read_buf(buf).unwrap();
|
||||
Err(io::Error::other("error"))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_data_and_error_take() {
|
||||
let mut buf = [0; 64];
|
||||
let mut buf = io::BorrowedBuf::from(buf.as_mut_slice());
|
||||
|
||||
let mut r = DataAndErrorReader(&[4, 5, 6]).take(1);
|
||||
assert!(r.read_buf(buf.unfilled()).is_err());
|
||||
assert_eq!(buf.filled(), &[4]);
|
||||
|
||||
assert!(r.read_buf(buf.unfilled()).is_ok());
|
||||
assert_eq!(buf.filled(), &[4]);
|
||||
assert_eq!(r.get_ref().0, &[5, 6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_data_and_error_buf() {
|
||||
let mut r = BufReader::new(DataAndErrorReader(&[4, 5, 6]));
|
||||
|
||||
assert!(r.fill_buf().is_err());
|
||||
assert_eq!(r.fill_buf().unwrap(), &[4, 5, 6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_data_and_error_read_to_end() {
|
||||
let mut r = DataAndErrorReader(&[4, 5, 6]);
|
||||
|
||||
let mut v = Vec::with_capacity(200);
|
||||
assert!(r.read_to_end(&mut v).is_err());
|
||||
|
||||
assert_eq!(v, &[4, 5, 6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_end_error() {
|
||||
struct ErrorReader;
|
||||
|
||||
impl Read for ErrorReader {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
Err(io::Error::other("error"))
|
||||
}
|
||||
}
|
||||
|
||||
let mut r = [4, 5, 6].chain(ErrorReader);
|
||||
|
||||
let mut v = Vec::with_capacity(200);
|
||||
assert!(r.read_to_end(&mut v).is_err());
|
||||
|
||||
assert_eq!(v, &[4, 5, 6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_oom_error() {
|
||||
use alloc::alloc::Layout;
|
||||
use alloc::collections::{TryReserveError, TryReserveErrorKind};
|
||||
|
||||
// We simulate a `Vec::try_reserve` error rather than attempting a huge size for real. This way
|
||||
// we're not subject to the whims of optimization that might skip the actual allocation, and it
|
||||
// also works for 32-bit targets and miri that might not OOM at all.
|
||||
let layout = Layout::new::<u8>();
|
||||
let kind = TryReserveErrorKind::AllocError { layout, non_exhaustive: () };
|
||||
let reserve_err = TryReserveError::from(kind);
|
||||
|
||||
let io_err = io::Error::from(reserve_err);
|
||||
assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind());
|
||||
}
|
||||
448
crates/std/src/io/util.rs
Normal file
448
crates/std/src/io/util.rs
Normal file
@@ -0,0 +1,448 @@
|
||||
#![allow(missing_copy_implementations)]
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::fmt;
|
||||
use crate::io::{
|
||||
self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write,
|
||||
};
|
||||
|
||||
/// `Empty` ignores any data written via [`Write`], and will always be empty
|
||||
/// (returning zero bytes) when read via [`Read`].
|
||||
///
|
||||
/// This struct is generally created by calling [`empty()`]. Please
|
||||
/// see the documentation of [`empty()`] for more details.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Empty;
|
||||
|
||||
/// Creates a value that is always at EOF for reads, and ignores all data written.
|
||||
///
|
||||
/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`]
|
||||
/// and the contents of the buffer will not be inspected.
|
||||
///
|
||||
/// All calls to [`read`] from the returned reader will return [`Ok(0)`].
|
||||
///
|
||||
/// [`Ok(buf.len())`]: Ok
|
||||
/// [`Ok(0)`]: Ok
|
||||
///
|
||||
/// [`write`]: Write::write
|
||||
/// [`read`]: Read::read
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::{self, Write};
|
||||
///
|
||||
/// let buffer = vec![1, 2, 3, 5, 8];
|
||||
/// let num_bytes = io::empty().write(&buffer).unwrap();
|
||||
/// assert_eq!(num_bytes, 5);
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::{self, Read};
|
||||
///
|
||||
/// let mut buffer = String::new();
|
||||
/// io::empty().read_to_string(&mut buffer).unwrap();
|
||||
/// assert!(buffer.is_empty());
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn empty() -> Empty {
|
||||
Empty
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Read for Empty {
|
||||
#[inline]
|
||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
// Do not force `Chain<Empty, T>` or `Chain<T, Empty>` to use vectored
|
||||
// reads, unless the other reader is vectored.
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
if cursor.capacity() != 0 { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl BufRead for Empty {
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
Ok(&[])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, _n: usize) {}
|
||||
|
||||
#[inline]
|
||||
fn has_data_left(&mut self) -> io::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_until(&mut self, _byte: u8, _buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_until(&mut self, _byte: u8) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_line(&mut self, _buf: &mut String) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "empty_seek", since = "1.51.0")]
|
||||
impl Seek for Empty {
|
||||
#[inline]
|
||||
fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl SizeHint for Empty {
|
||||
#[inline]
|
||||
fn upper_bound(&self) -> Option<usize> {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "empty_write", since = "1.73.0")]
|
||||
impl Write for Empty {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "empty_write", since = "1.73.0")]
|
||||
impl Write for &Empty {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A reader which yields one byte over and over and over and over and over and...
|
||||
///
|
||||
/// This struct is generally created by calling [`repeat()`]. Please
|
||||
/// see the documentation of [`repeat()`] for more details.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Repeat {
|
||||
byte: u8,
|
||||
}
|
||||
|
||||
/// Creates an instance of a reader that infinitely repeats one byte.
|
||||
///
|
||||
/// All reads from this reader will succeed by filling the specified buffer with
|
||||
/// the given byte.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::{self, Read};
|
||||
///
|
||||
/// let mut buffer = [0; 3];
|
||||
/// io::repeat(0b101).read_exact(&mut buffer).unwrap();
|
||||
/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn repeat(byte: u8) -> Repeat {
|
||||
Repeat { byte }
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Read for Repeat {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
buf.fill(self.byte);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
buf.fill(self.byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
// SAFETY: No uninit bytes are being written.
|
||||
unsafe { buf.as_mut() }.write_filled(self.byte);
|
||||
// SAFETY: the entire unfilled portion of buf has been initialized.
|
||||
unsafe { buf.advance_unchecked(buf.capacity()) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.read_buf(buf)
|
||||
}
|
||||
|
||||
/// This function is not supported by `io::Repeat`, because there's no end of its data
|
||||
fn read_to_end(&mut self, _: &mut Vec<u8>) -> io::Result<usize> {
|
||||
Err(io::Error::from(io::ErrorKind::OutOfMemory))
|
||||
}
|
||||
|
||||
/// This function is not supported by `io::Repeat`, because there's no end of its data
|
||||
fn read_to_string(&mut self, _: &mut String) -> io::Result<usize> {
|
||||
Err(io::Error::from(io::ErrorKind::OutOfMemory))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let mut nwritten = 0;
|
||||
for buf in bufs {
|
||||
nwritten += self.read(buf)?;
|
||||
}
|
||||
Ok(nwritten)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl SizeHint for Repeat {
|
||||
#[inline]
|
||||
fn lower_bound(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn upper_bound(&self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Repeat {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Repeat").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
/// A writer which will move data into the void.
|
||||
///
|
||||
/// This struct is generally created by calling [`sink()`]. Please
|
||||
/// see the documentation of [`sink()`] for more details.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Sink;
|
||||
|
||||
/// Creates an instance of a writer which will successfully consume all data.
|
||||
///
|
||||
/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`]
|
||||
/// and the contents of the buffer will not be inspected.
|
||||
///
|
||||
/// [`write`]: Write::write
|
||||
/// [`Ok(buf.len())`]: Ok
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::{self, Write};
|
||||
///
|
||||
/// let buffer = vec![1, 2, 3, 5, 8];
|
||||
/// let num_bytes = io::sink().write(&buffer).unwrap();
|
||||
/// assert_eq!(num_bytes, 5);
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn sink() -> Sink {
|
||||
Sink
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Write for Sink {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "write_mt", since = "1.48.0")]
|
||||
impl Write for &Sink {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
185
crates/std/src/io/util/tests.rs
Normal file
185
crates/std/src/io/util/tests.rs
Normal file
@@ -0,0 +1,185 @@
|
||||
use crate::fmt;
|
||||
use crate::io::prelude::*;
|
||||
use crate::io::{
|
||||
BorrowedBuf, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, SeekFrom, Sink, empty, repeat, sink,
|
||||
};
|
||||
use crate::mem::MaybeUninit;
|
||||
|
||||
struct ErrorDisplay;
|
||||
|
||||
impl fmt::Display for ErrorDisplay {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
}
|
||||
|
||||
struct PanicDisplay;
|
||||
|
||||
impl fmt::Display for PanicDisplay {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn test_sinking<W: Write>(mut w: W) {
|
||||
assert_eq!(w.write(&[]).unwrap(), 0);
|
||||
assert_eq!(w.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(w.write(&[0; 1024]).unwrap(), 1024);
|
||||
w.write_all(&[]).unwrap();
|
||||
w.write_all(&[0]).unwrap();
|
||||
w.write_all(&[0; 1024]).unwrap();
|
||||
let mut bufs =
|
||||
[IoSlice::new(&[]), IoSlice::new(&[0]), IoSlice::new(&[0; 1024]), IoSlice::new(&[])];
|
||||
assert!(w.is_write_vectored());
|
||||
assert_eq!(w.write_vectored(&[]).unwrap(), 0);
|
||||
assert_eq!(w.write_vectored(&bufs).unwrap(), 1025);
|
||||
w.write_all_vectored(&mut []).unwrap();
|
||||
w.write_all_vectored(&mut bufs).unwrap();
|
||||
assert!(w.flush().is_ok());
|
||||
assert_eq!(w.by_ref().write(&[0; 1024]).unwrap(), 1024);
|
||||
// Ignores fmt arguments
|
||||
w.write_fmt(format_args!("{}", ErrorDisplay)).unwrap();
|
||||
w.write_fmt(format_args!("{}", PanicDisplay)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sink_sinks() {
|
||||
test_sinking(sink());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_reads() {
|
||||
let mut e = empty();
|
||||
assert_eq!(e.read(&mut []).unwrap(), 0);
|
||||
assert_eq!(e.read(&mut [0]).unwrap(), 0);
|
||||
assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
|
||||
assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0);
|
||||
|
||||
e.read_exact(&mut []).unwrap();
|
||||
assert_eq!(e.read_exact(&mut [0]).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
||||
assert_eq!(e.read_exact(&mut [0; 1024]).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
||||
|
||||
assert!(!e.is_read_vectored());
|
||||
assert_eq!(e.read_vectored(&mut []).unwrap(), 0);
|
||||
let (mut buf1, mut buf1024) = ([0], [0; 1024]);
|
||||
let bufs = &mut [
|
||||
IoSliceMut::new(&mut []),
|
||||
IoSliceMut::new(&mut buf1),
|
||||
IoSliceMut::new(&mut buf1024),
|
||||
IoSliceMut::new(&mut []),
|
||||
];
|
||||
assert_eq!(e.read_vectored(bufs).unwrap(), 0);
|
||||
|
||||
let buf: &mut [MaybeUninit<_>] = &mut [];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
e.read_buf(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit()];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
e.read_buf(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
e.read_buf(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [MaybeUninit<_>] = &mut [];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
e.read_buf_exact(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit()];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
assert_eq!(
|
||||
Read::by_ref(&mut e).read_buf_exact(buf.unfilled()).unwrap_err().kind(),
|
||||
ErrorKind::UnexpectedEof,
|
||||
);
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
|
||||
assert_eq!(buf, vec![]);
|
||||
let mut buf = vec![1, 2, 3];
|
||||
assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
|
||||
assert_eq!(buf, vec![1, 2, 3]);
|
||||
|
||||
let mut buf = String::new();
|
||||
assert_eq!(e.read_to_string(&mut buf).unwrap(), 0);
|
||||
assert_eq!(buf, "");
|
||||
let mut buf = "hello".to_owned();
|
||||
assert_eq!(e.read_to_string(&mut buf).unwrap(), 0);
|
||||
assert_eq!(buf, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_seeks() {
|
||||
let mut e = empty();
|
||||
assert!(matches!(e.seek(SeekFrom::Start(0)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Start(1)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Start(u64::MAX)), Ok(0)));
|
||||
|
||||
assert!(matches!(e.seek(SeekFrom::End(i64::MIN)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::End(-1)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::End(0)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::End(1)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::End(i64::MAX)), Ok(0)));
|
||||
|
||||
assert!(matches!(e.seek(SeekFrom::Current(i64::MIN)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Current(-1)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Current(0)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Current(1)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Current(i64::MAX)), Ok(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_sinks() {
|
||||
test_sinking(empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeat_repeats() {
|
||||
let mut r = repeat(4);
|
||||
let mut b = [0; 1024];
|
||||
assert_eq!(r.read(&mut b).unwrap(), 1024);
|
||||
assert!(b.iter().all(|b| *b == 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_some_bytes() {
|
||||
assert_eq!(repeat(4).take(100).bytes().count(), 100);
|
||||
assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4);
|
||||
assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn const_utils() {
|
||||
const _: Empty = empty();
|
||||
const _: Repeat = repeat(b'c');
|
||||
const _: Sink = sink();
|
||||
}
|
||||
@@ -183,7 +183,12 @@
|
||||
//
|
||||
#![feature(c_size_t, unsafe_binders)]
|
||||
#![allow(clippy::doc_lazy_continuation, clippy::all)]
|
||||
#![allow(stable_features, incomplete_features, unexpected_cfgs)]
|
||||
#![allow(
|
||||
stable_features,
|
||||
incomplete_features,
|
||||
unexpected_cfgs,
|
||||
unfulfilled_lint_expectations
|
||||
)]
|
||||
#![allow(unused)]
|
||||
|
||||
#[macro_use]
|
||||
@@ -274,6 +279,7 @@ pub mod process;
|
||||
#[macro_use]
|
||||
pub mod rt;
|
||||
pub mod alloc;
|
||||
pub mod bstr;
|
||||
pub mod sync;
|
||||
pub mod sys;
|
||||
pub mod thread;
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
pub mod once {
|
||||
use crate::sys::sync as sys;
|
||||
pub struct OnceState {
|
||||
pub(crate) inner: sys::OnceState,
|
||||
}
|
||||
/// Used for the internal implementation of `sys::sync::once` on different platforms and the
|
||||
/// [`LazyLock`](crate::sync::LazyLock) implementation.
|
||||
pub(crate) enum OnceExclusiveState {
|
||||
Incomplete,
|
||||
Poisoned,
|
||||
Complete,
|
||||
}
|
||||
}
|
||||
pub mod barrier;
|
||||
pub mod lazy_lock;
|
||||
pub mod mpmc;
|
||||
pub mod mpsc;
|
||||
pub mod nonpoison;
|
||||
pub mod once;
|
||||
pub mod once_lock;
|
||||
pub mod poison;
|
||||
pub mod reentrant_lock;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::sync::atomic;
|
||||
|
||||
pub use once::Once;
|
||||
pub use once::OnceState;
|
||||
|
||||
pub use poison::LockResult;
|
||||
pub use poison::Mutex;
|
||||
pub use poison::PoisonError;
|
||||
pub use poison::TryLockError;
|
||||
pub use poison::TryLockResult;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||
pub struct WaitTimeoutResult(bool);
|
||||
|
||||
167
crates/std/src/sync/barrier.rs
Normal file
167
crates/std/src/sync/barrier.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
use crate::fmt;
|
||||
use core::panic::RefUnwindSafe;
|
||||
use crate::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
/// A barrier enables multiple threads to synchronize the beginning
|
||||
/// of some computation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let n = 10;
|
||||
/// let barrier = Barrier::new(n);
|
||||
/// thread::scope(|s| {
|
||||
/// for _ in 0..n {
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// s.spawn(|| {
|
||||
/// println!("before wait");
|
||||
/// barrier.wait();
|
||||
/// println!("after wait");
|
||||
/// });
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Barrier {
|
||||
lock: Mutex<BarrierState>,
|
||||
cvar: Condvar,
|
||||
num_threads: usize,
|
||||
}
|
||||
|
||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
|
||||
impl RefUnwindSafe for Barrier {}
|
||||
|
||||
// The inner state of a double barrier
|
||||
struct BarrierState {
|
||||
count: usize,
|
||||
generation_id: usize,
|
||||
}
|
||||
|
||||
/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads
|
||||
/// in the [`Barrier`] have rendezvoused.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
///
|
||||
/// let barrier = Barrier::new(1);
|
||||
/// let barrier_wait_result = barrier.wait();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct BarrierWaitResult(bool);
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Barrier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Barrier").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl Barrier {
|
||||
/// Creates a new barrier that can block a given number of threads.
|
||||
///
|
||||
/// A barrier will block all threads which call [`wait()`] until the `n`th thread calls [`wait()`],
|
||||
/// and then wake up all threads at once.
|
||||
///
|
||||
/// [`wait()`]: Barrier::wait
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
///
|
||||
/// let barrier = Barrier::new(10);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_barrier", since = "1.78.0")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn new(n: usize) -> Barrier {
|
||||
Barrier {
|
||||
lock: Mutex::new(BarrierState { count: 0, generation_id: 0 }),
|
||||
cvar: Condvar::new(),
|
||||
num_threads: n,
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until all threads have rendezvoused here.
|
||||
///
|
||||
/// Barriers are re-usable after all threads have rendezvoused once, and can
|
||||
/// be used continuously.
|
||||
///
|
||||
/// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
|
||||
/// returns `true` from [`BarrierWaitResult::is_leader()`] when returning
|
||||
/// from this function, and all other threads will receive a result that
|
||||
/// will return `false` from [`BarrierWaitResult::is_leader()`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let n = 10;
|
||||
/// let barrier = Barrier::new(n);
|
||||
/// thread::scope(|s| {
|
||||
/// for _ in 0..n {
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// s.spawn(|| {
|
||||
/// println!("before wait");
|
||||
/// barrier.wait();
|
||||
/// println!("after wait");
|
||||
/// });
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn wait(&self) -> BarrierWaitResult {
|
||||
let mut lock = self.lock.lock();
|
||||
let local_gen = lock.generation_id;
|
||||
lock.count += 1;
|
||||
if lock.count < self.num_threads {
|
||||
self.cvar.wait_while(&mut lock, |state| local_gen == state.generation_id);
|
||||
BarrierWaitResult(false)
|
||||
} else {
|
||||
lock.count = 0;
|
||||
lock.generation_id = lock.generation_id.wrapping_add(1);
|
||||
self.cvar.notify_all();
|
||||
BarrierWaitResult(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for BarrierWaitResult {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("BarrierWaitResult").field("is_leader", &self.is_leader()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl BarrierWaitResult {
|
||||
/// Returns `true` if this thread is the "leader thread" for the call to
|
||||
/// [`Barrier::wait()`].
|
||||
///
|
||||
/// Only one thread will have `true` returned from their result, all other
|
||||
/// threads will have `false` returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
///
|
||||
/// let barrier = Barrier::new(1);
|
||||
/// let barrier_wait_result = barrier.wait();
|
||||
/// println!("{:?}", barrier_wait_result.is_leader());
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[must_use]
|
||||
pub fn is_leader(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
422
crates/std/src/sync/lazy_lock.rs
Normal file
422
crates/std/src/sync/lazy_lock.rs
Normal file
@@ -0,0 +1,422 @@
|
||||
use super::once::OnceExclusiveState;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::Once;
|
||||
use crate::{fmt, ptr};
|
||||
|
||||
// We use the state of a Once as discriminant value. Upon creation, the state is
|
||||
// "incomplete" and `f` contains the initialization closure. In the first call to
|
||||
// `call_once`, `f` is taken and run. If it succeeds, `value` is set and the state
|
||||
// is changed to "complete". If it panics, the Once is poisoned, so none of the
|
||||
// two fields is initialized.
|
||||
union Data<T, F> {
|
||||
value: ManuallyDrop<T>,
|
||||
f: ManuallyDrop<F>,
|
||||
}
|
||||
|
||||
/// A value which is initialized on the first access.
|
||||
///
|
||||
/// This type is a thread-safe [`LazyCell`], and can be used in statics.
|
||||
/// Since initialization may be called from multiple threads, any
|
||||
/// dereferencing call will block the calling thread if another
|
||||
/// initialization routine is currently running.
|
||||
///
|
||||
/// [`LazyCell`]: crate::cell::LazyCell
|
||||
///
|
||||
/// # Poisoning
|
||||
///
|
||||
/// If the initialization closure passed to [`LazyLock::new`] panics, the lock will be poisoned.
|
||||
/// Once the lock is poisoned, any threads that attempt to access this lock (via a dereference
|
||||
/// or via an explicit call to [`force()`]) will panic.
|
||||
///
|
||||
/// This concept is similar to that of poisoning in the [`std::sync::poison`] module. A key
|
||||
/// difference, however, is that poisoning in `LazyLock` is _unrecoverable_. All future accesses of
|
||||
/// the lock from other threads will panic, whereas a type in [`std::sync::poison`] like
|
||||
/// [`std::sync::poison::Mutex`] allows recovery via [`PoisonError::into_inner()`].
|
||||
///
|
||||
/// [`force()`]: LazyLock::force
|
||||
/// [`std::sync::poison`]: crate::sync::poison
|
||||
/// [`std::sync::poison::Mutex`]: crate::sync::poison::Mutex
|
||||
/// [`PoisonError::into_inner()`]: crate::sync::poison::PoisonError::into_inner
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Initialize static variables with `LazyLock`.
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// // Note: static items do not call [`Drop`] on program termination, so this won't be deallocated.
|
||||
/// // this is fine, as the OS can deallocate the terminated program faster than we can free memory
|
||||
/// // but tools like valgrind might report "memory leaks" as it isn't obvious this is intentional.
|
||||
/// static DEEP_THOUGHT: LazyLock<String> = LazyLock::new(|| {
|
||||
/// # mod another_crate {
|
||||
/// # pub fn great_question() -> String { "42".to_string() }
|
||||
/// # }
|
||||
/// // M3 Ultra takes about 16 million years in --release config
|
||||
/// another_crate::great_question()
|
||||
/// });
|
||||
///
|
||||
/// // The `String` is built, stored in the `LazyLock`, and returned as `&String`.
|
||||
/// let _ = &*DEEP_THOUGHT;
|
||||
/// ```
|
||||
///
|
||||
/// Initialize fields with `LazyLock`.
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// struct UseCellLock {
|
||||
/// number: LazyLock<u32>,
|
||||
/// }
|
||||
/// fn main() {
|
||||
/// let lock: LazyLock<u32> = LazyLock::new(|| 0u32);
|
||||
///
|
||||
/// let data = UseCellLock { number: lock };
|
||||
/// println!("{}", *data.number);
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
pub struct LazyLock<T, F = fn() -> T> {
|
||||
// FIXME(nonpoison_once): if possible, switch to nonpoison version once it is available
|
||||
once: Once,
|
||||
data: UnsafeCell<Data<T, F>>,
|
||||
}
|
||||
|
||||
impl<T, F: FnOnce() -> T> LazyLock<T, F> {
|
||||
/// Creates a new lazy value with the given initializing function.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let hello = "Hello, World!".to_string();
|
||||
///
|
||||
/// let lazy = LazyLock::new(|| hello.to_uppercase());
|
||||
///
|
||||
/// assert_eq!(&*lazy, "HELLO, WORLD!");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
#[rustc_const_stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
pub const fn new(f: F) -> LazyLock<T, F> {
|
||||
LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) }
|
||||
}
|
||||
|
||||
/// Creates a new lazy value that is already initialized.
|
||||
#[inline]
|
||||
#[cfg(test)]
|
||||
pub(crate) fn preinit(value: T) -> LazyLock<T, F> {
|
||||
let once = Once::new();
|
||||
once.call_once(|| {});
|
||||
LazyLock { once, data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }) }
|
||||
}
|
||||
|
||||
/// Consumes this `LazyLock` returning the stored value.
|
||||
///
|
||||
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the lock is poisoned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lazy_cell_into_inner)]
|
||||
///
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let hello = "Hello, World!".to_string();
|
||||
///
|
||||
/// let lazy = LazyLock::new(|| hello.to_uppercase());
|
||||
///
|
||||
/// assert_eq!(&*lazy, "HELLO, WORLD!");
|
||||
/// assert_eq!(LazyLock::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string()));
|
||||
/// ```
|
||||
#[unstable(feature = "lazy_cell_into_inner", issue = "125623")]
|
||||
pub fn into_inner(mut this: Self) -> Result<T, F> {
|
||||
let state = this.once.state();
|
||||
match state {
|
||||
OnceExclusiveState::Poisoned => panic_poisoned(),
|
||||
state => {
|
||||
let this = ManuallyDrop::new(this);
|
||||
let data = unsafe { ptr::read(&this.data) }.into_inner();
|
||||
match state {
|
||||
OnceExclusiveState::Incomplete => {
|
||||
Err(ManuallyDrop::into_inner(unsafe { data.f }))
|
||||
}
|
||||
OnceExclusiveState::Complete => {
|
||||
Ok(ManuallyDrop::into_inner(unsafe { data.value }))
|
||||
}
|
||||
OnceExclusiveState::Poisoned => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Forces the evaluation of this lazy value and returns a mutable reference to
|
||||
/// the result.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
||||
///
|
||||
/// [`new()`]: LazyLock::new
|
||||
/// [`force()`]: LazyLock::force
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let mut lazy = LazyLock::new(|| 92);
|
||||
///
|
||||
/// let p = LazyLock::force_mut(&mut lazy);
|
||||
/// assert_eq!(*p, 92);
|
||||
/// *p = 44;
|
||||
/// assert_eq!(*lazy, 44);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "lazy_get", since = "1.94.0")]
|
||||
pub fn force_mut(this: &mut LazyLock<T, F>) -> &mut T {
|
||||
#[cold]
|
||||
/// # Safety
|
||||
/// May only be called when the state is `Incomplete`.
|
||||
unsafe fn really_init_mut<T, F: FnOnce() -> T>(this: &mut LazyLock<T, F>) -> &mut T {
|
||||
struct PoisonOnPanic<'a, T, F>(&'a mut LazyLock<T, F>);
|
||||
impl<T, F> Drop for PoisonOnPanic<'_, T, F> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
self.0.once.set_state(OnceExclusiveState::Poisoned);
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: We always poison if the initializer panics (then we never check the data),
|
||||
// or set the data on success.
|
||||
let f = unsafe { ManuallyDrop::take(&mut this.data.get_mut().f) };
|
||||
// INVARIANT: Initiated from mutable reference, don't drop because we read it.
|
||||
let guard = PoisonOnPanic(this);
|
||||
let data = f();
|
||||
guard.0.data.get_mut().value = ManuallyDrop::new(data);
|
||||
guard.0.once.set_state(OnceExclusiveState::Complete);
|
||||
core::mem::forget(guard);
|
||||
// SAFETY: We put the value there above.
|
||||
unsafe { &mut this.data.get_mut().value }
|
||||
}
|
||||
|
||||
let state = this.once.state();
|
||||
match state {
|
||||
OnceExclusiveState::Poisoned => panic_poisoned(),
|
||||
// SAFETY: The `Once` states we completed the initialization.
|
||||
OnceExclusiveState::Complete => unsafe { &mut this.data.get_mut().value },
|
||||
// SAFETY: The state is `Incomplete`.
|
||||
OnceExclusiveState::Incomplete => unsafe { really_init_mut(this) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Forces the evaluation of this lazy value and returns a reference to
|
||||
/// result. This is equivalent to the `Deref` impl, but is explicit.
|
||||
///
|
||||
/// This method will block the calling thread if another initialization
|
||||
/// routine is currently running.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
||||
///
|
||||
/// [`new()`]: LazyLock::new
|
||||
/// [`force()`]: LazyLock::force
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let lazy = LazyLock::new(|| 92);
|
||||
///
|
||||
/// assert_eq!(LazyLock::force(&lazy), &92);
|
||||
/// assert_eq!(&*lazy, &92);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn force(this: &LazyLock<T, F>) -> &T {
|
||||
this.once.call_once_force(|state| {
|
||||
if state.is_poisoned() {
|
||||
panic_poisoned();
|
||||
}
|
||||
|
||||
// SAFETY: `call_once` only runs this closure once, ever.
|
||||
let data = unsafe { &mut *this.data.get() };
|
||||
let f = unsafe { ManuallyDrop::take(&mut data.f) };
|
||||
let value = f();
|
||||
data.value = ManuallyDrop::new(value);
|
||||
});
|
||||
|
||||
// SAFETY:
|
||||
// There are four possible scenarios:
|
||||
// * the closure was called and initialized `value`.
|
||||
// * the closure was called and panicked, so this point is never reached.
|
||||
// * the closure was not called, but a previous call initialized `value`.
|
||||
// * the closure was not called because the Once is poisoned, which we handled above.
|
||||
// So `value` has definitely been initialized and will not be modified again.
|
||||
unsafe { &*(*this.data.get()).value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F> LazyLock<T, F> {
|
||||
/// Returns a mutable reference to the value if initialized. Otherwise (if uninitialized or
|
||||
/// poisoned), returns `None`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let mut lazy = LazyLock::new(|| 92);
|
||||
///
|
||||
/// assert_eq!(LazyLock::get_mut(&mut lazy), None);
|
||||
/// let _ = LazyLock::force(&lazy);
|
||||
/// *LazyLock::get_mut(&mut lazy).unwrap() = 44;
|
||||
/// assert_eq!(*lazy, 44);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "lazy_get", since = "1.94.0")]
|
||||
pub fn get_mut(this: &mut LazyLock<T, F>) -> Option<&mut T> {
|
||||
// `state()` does not perform an atomic load, so prefer it over `is_complete()`.
|
||||
let state = this.once.state();
|
||||
match state {
|
||||
// SAFETY:
|
||||
// The closure has been run successfully, so `value` has been initialized.
|
||||
OnceExclusiveState::Complete => Some(unsafe { &mut this.data.get_mut().value }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the value if initialized. Otherwise (if uninitialized or poisoned),
|
||||
/// returns `None`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let lazy = LazyLock::new(|| 92);
|
||||
///
|
||||
/// assert_eq!(LazyLock::get(&lazy), None);
|
||||
/// let _ = LazyLock::force(&lazy);
|
||||
/// assert_eq!(LazyLock::get(&lazy), Some(&92));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "lazy_get", since = "1.94.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn get(this: &LazyLock<T, F>) -> Option<&T> {
|
||||
if this.once.is_completed() {
|
||||
// SAFETY:
|
||||
// The closure has been run successfully, so `value` has been initialized
|
||||
// and will not be modified again.
|
||||
Some(unsafe { &(*this.data.get()).value })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T, F> Drop for LazyLock<T, F> {
|
||||
fn drop(&mut self) {
|
||||
match self.once.state() {
|
||||
OnceExclusiveState::Incomplete => unsafe {
|
||||
ManuallyDrop::drop(&mut self.data.get_mut().f)
|
||||
},
|
||||
OnceExclusiveState::Complete => unsafe {
|
||||
ManuallyDrop::drop(&mut self.data.get_mut().value)
|
||||
},
|
||||
OnceExclusiveState::Poisoned => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
|
||||
type Target = T;
|
||||
|
||||
/// Dereferences the value.
|
||||
///
|
||||
/// This method will block the calling thread if another initialization
|
||||
/// routine is currently running.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
||||
///
|
||||
/// [`new()`]: LazyLock::new
|
||||
/// [`force()`]: LazyLock::force
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
LazyLock::force(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "lazy_deref_mut", since = "1.89.0")]
|
||||
impl<T, F: FnOnce() -> T> DerefMut for LazyLock<T, F> {
|
||||
/// # Panics
|
||||
///
|
||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
||||
///
|
||||
/// [`new()`]: LazyLock::new
|
||||
/// [`force()`]: LazyLock::force
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
LazyLock::force_mut(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T: Default> Default for LazyLock<T> {
|
||||
/// Creates a new lazy value using `Default` as the initializing function.
|
||||
#[inline]
|
||||
fn default() -> LazyLock<T> {
|
||||
LazyLock::new(T::default)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_tuple("LazyLock");
|
||||
match LazyLock::get(self) {
|
||||
Some(v) => d.field(v),
|
||||
None => d.field(&format_args!("<uninit>")),
|
||||
};
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn panic_poisoned() -> ! {
|
||||
panic!("LazyLock instance has previously been poisoned")
|
||||
}
|
||||
|
||||
// We never create a `&F` from a `&LazyLock<T, F>` so it is fine
|
||||
// to not impl `Sync` for `F`.
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {}
|
||||
// auto-derived `Send` impl is OK.
|
||||
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T: RefUnwindSafe + UnwindSafe, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> {}
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T: UnwindSafe, F: UnwindSafe> UnwindSafe for LazyLock<T, F> {}
|
||||
569
crates/std/src/sync/mpmc/array.rs
Normal file
569
crates/std/src/sync/mpmc/array.rs
Normal file
@@ -0,0 +1,569 @@
|
||||
//! Bounded channel based on a preallocated array.
|
||||
//!
|
||||
//! This flavor has a fixed, positive capacity.
|
||||
//!
|
||||
//! The implementation is based on Dmitry Vyukov's bounded MPMC queue.
|
||||
//!
|
||||
//! Source:
|
||||
//! - <http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue>
|
||||
//! - <https://docs.google.com/document/d/1yIAYmbvL3JxOKOjuCyon7JhW4cSv1wy5hC0ApeGMV9s/pub>
|
||||
|
||||
use super::context::Context;
|
||||
use super::error::*;
|
||||
use super::select::{Operation, Selected, Token};
|
||||
use super::utils::{Backoff, CachePadded};
|
||||
use super::waker::SyncWaker;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{self, Atomic, AtomicUsize, Ordering};
|
||||
use crate::time::Instant;
|
||||
|
||||
/// A slot in a channel.
|
||||
struct Slot<T> {
|
||||
/// The current stamp.
|
||||
stamp: Atomic<usize>,
|
||||
|
||||
/// The message in this slot. Either read out in `read` or dropped through
|
||||
/// `discard_all_messages`.
|
||||
msg: UnsafeCell<MaybeUninit<T>>,
|
||||
}
|
||||
|
||||
/// The token type for the array flavor.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ArrayToken {
|
||||
/// Slot to read from or write to.
|
||||
slot: *const u8,
|
||||
|
||||
/// Stamp to store into the slot after reading or writing.
|
||||
stamp: usize,
|
||||
}
|
||||
|
||||
impl Default for ArrayToken {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
ArrayToken { slot: ptr::null(), stamp: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// Bounded channel based on a preallocated array.
|
||||
pub(crate) struct Channel<T> {
|
||||
/// The head of the channel.
|
||||
///
|
||||
/// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but
|
||||
/// packed into a single `usize`. The lower bits represent the index, while the upper bits
|
||||
/// represent the lap. The mark bit in the head is always zero.
|
||||
///
|
||||
/// Messages are popped from the head of the channel.
|
||||
head: CachePadded<Atomic<usize>>,
|
||||
|
||||
/// The tail of the channel.
|
||||
///
|
||||
/// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but
|
||||
/// packed into a single `usize`. The lower bits represent the index, while the upper bits
|
||||
/// represent the lap. The mark bit indicates that the channel is disconnected.
|
||||
///
|
||||
/// Messages are pushed into the tail of the channel.
|
||||
tail: CachePadded<Atomic<usize>>,
|
||||
|
||||
/// The buffer holding slots.
|
||||
buffer: Box<[Slot<T>]>,
|
||||
|
||||
/// The channel capacity.
|
||||
cap: usize,
|
||||
|
||||
/// A stamp with the value of `{ lap: 1, mark: 0, index: 0 }`.
|
||||
one_lap: usize,
|
||||
|
||||
/// If this bit is set in the tail, that means the channel is disconnected.
|
||||
mark_bit: usize,
|
||||
|
||||
/// Senders waiting while the channel is full.
|
||||
senders: SyncWaker,
|
||||
|
||||
/// Receivers waiting while the channel is empty and not disconnected.
|
||||
receivers: SyncWaker,
|
||||
}
|
||||
|
||||
impl<T> Channel<T> {
|
||||
/// Creates a bounded channel of capacity `cap`.
|
||||
pub(crate) fn with_capacity(cap: usize) -> Self {
|
||||
assert!(cap > 0, "capacity must be positive");
|
||||
|
||||
// Compute constants `mark_bit` and `one_lap`.
|
||||
let mark_bit = (cap + 1).next_power_of_two();
|
||||
let one_lap = mark_bit * 2;
|
||||
|
||||
// Head is initialized to `{ lap: 0, mark: 0, index: 0 }`.
|
||||
let head = 0;
|
||||
// Tail is initialized to `{ lap: 0, mark: 0, index: 0 }`.
|
||||
let tail = 0;
|
||||
|
||||
// Allocate a buffer of `cap` slots initialized
|
||||
// with stamps.
|
||||
let buffer: Box<[Slot<T>]> = (0..cap)
|
||||
.map(|i| {
|
||||
// Set the stamp to `{ lap: 0, mark: 0, index: i }`.
|
||||
Slot { stamp: AtomicUsize::new(i), msg: UnsafeCell::new(MaybeUninit::uninit()) }
|
||||
})
|
||||
.collect();
|
||||
|
||||
Channel {
|
||||
buffer,
|
||||
cap,
|
||||
one_lap,
|
||||
mark_bit,
|
||||
head: CachePadded::new(AtomicUsize::new(head)),
|
||||
tail: CachePadded::new(AtomicUsize::new(tail)),
|
||||
senders: SyncWaker::new(),
|
||||
receivers: SyncWaker::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to reserve a slot for sending a message.
|
||||
fn start_send(&self, token: &mut Token) -> bool {
|
||||
let backoff = Backoff::new();
|
||||
let mut tail = self.tail.load(Ordering::Relaxed);
|
||||
|
||||
loop {
|
||||
// Check if the channel is disconnected.
|
||||
if tail & self.mark_bit != 0 {
|
||||
token.array.slot = ptr::null();
|
||||
token.array.stamp = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Deconstruct the tail.
|
||||
let index = tail & (self.mark_bit - 1);
|
||||
let lap = tail & !(self.one_lap - 1);
|
||||
|
||||
// Inspect the corresponding slot.
|
||||
debug_assert!(index < self.buffer.len());
|
||||
let slot = unsafe { self.buffer.get_unchecked(index) };
|
||||
let stamp = slot.stamp.load(Ordering::Acquire);
|
||||
|
||||
// If the tail and the stamp match, we may attempt to push.
|
||||
if tail == stamp {
|
||||
let new_tail = if index + 1 < self.cap {
|
||||
// Same lap, incremented index.
|
||||
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
|
||||
tail + 1
|
||||
} else {
|
||||
// One lap forward, index wraps around to zero.
|
||||
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
|
||||
lap.wrapping_add(self.one_lap)
|
||||
};
|
||||
|
||||
// Try moving the tail.
|
||||
match self.tail.compare_exchange_weak(
|
||||
tail,
|
||||
new_tail,
|
||||
Ordering::SeqCst,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => {
|
||||
// Prepare the token for the follow-up call to `write`.
|
||||
token.array.slot = slot as *const Slot<T> as *const u8;
|
||||
token.array.stamp = tail + 1;
|
||||
return true;
|
||||
}
|
||||
Err(_) => {
|
||||
backoff.spin_light();
|
||||
tail = self.tail.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
} else if stamp.wrapping_add(self.one_lap) == tail + 1 {
|
||||
atomic::fence(Ordering::SeqCst);
|
||||
let head = self.head.load(Ordering::Relaxed);
|
||||
|
||||
// If the head lags one lap behind the tail as well...
|
||||
if head.wrapping_add(self.one_lap) == tail {
|
||||
// ...then the channel is full.
|
||||
return false;
|
||||
}
|
||||
|
||||
backoff.spin_light();
|
||||
tail = self.tail.load(Ordering::Relaxed);
|
||||
} else {
|
||||
// Snooze because we need to wait for the stamp to get updated.
|
||||
backoff.spin_heavy();
|
||||
tail = self.tail.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a message into the channel.
|
||||
pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
|
||||
// If there is no slot, the channel is disconnected.
|
||||
if token.array.slot.is_null() {
|
||||
return Err(msg);
|
||||
}
|
||||
|
||||
// Write the message into the slot and update the stamp.
|
||||
unsafe {
|
||||
let slot: &Slot<T> = &*(token.array.slot as *const Slot<T>);
|
||||
slot.msg.get().write(MaybeUninit::new(msg));
|
||||
slot.stamp.store(token.array.stamp, Ordering::Release);
|
||||
}
|
||||
|
||||
// Wake a sleeping receiver.
|
||||
self.receivers.notify();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attempts to reserve a slot for receiving a message.
|
||||
fn start_recv(&self, token: &mut Token) -> bool {
|
||||
let backoff = Backoff::new();
|
||||
let mut head = self.head.load(Ordering::Relaxed);
|
||||
|
||||
loop {
|
||||
// Deconstruct the head.
|
||||
let index = head & (self.mark_bit - 1);
|
||||
let lap = head & !(self.one_lap - 1);
|
||||
|
||||
// Inspect the corresponding slot.
|
||||
debug_assert!(index < self.buffer.len());
|
||||
let slot = unsafe { self.buffer.get_unchecked(index) };
|
||||
let stamp = slot.stamp.load(Ordering::Acquire);
|
||||
|
||||
// If the stamp is ahead of the head by 1, we may attempt to pop.
|
||||
if head + 1 == stamp {
|
||||
let new = if index + 1 < self.cap {
|
||||
// Same lap, incremented index.
|
||||
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
|
||||
head + 1
|
||||
} else {
|
||||
// One lap forward, index wraps around to zero.
|
||||
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
|
||||
lap.wrapping_add(self.one_lap)
|
||||
};
|
||||
|
||||
// Try moving the head.
|
||||
match self.head.compare_exchange_weak(
|
||||
head,
|
||||
new,
|
||||
Ordering::SeqCst,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => {
|
||||
// Prepare the token for the follow-up call to `read`.
|
||||
token.array.slot = slot as *const Slot<T> as *const u8;
|
||||
token.array.stamp = head.wrapping_add(self.one_lap);
|
||||
return true;
|
||||
}
|
||||
Err(_) => {
|
||||
backoff.spin_light();
|
||||
head = self.head.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
} else if stamp == head {
|
||||
atomic::fence(Ordering::SeqCst);
|
||||
let tail = self.tail.load(Ordering::Relaxed);
|
||||
|
||||
// If the tail equals the head, that means the channel is empty.
|
||||
if (tail & !self.mark_bit) == head {
|
||||
// If the channel is disconnected...
|
||||
if tail & self.mark_bit != 0 {
|
||||
// ...then receive an error.
|
||||
token.array.slot = ptr::null();
|
||||
token.array.stamp = 0;
|
||||
return true;
|
||||
} else {
|
||||
// Otherwise, the receive operation is not ready.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
backoff.spin_light();
|
||||
head = self.head.load(Ordering::Relaxed);
|
||||
} else {
|
||||
// Snooze because we need to wait for the stamp to get updated.
|
||||
backoff.spin_heavy();
|
||||
head = self.head.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a message from the channel.
|
||||
pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
|
||||
if token.array.slot.is_null() {
|
||||
// The channel is disconnected.
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Read the message from the slot and update the stamp.
|
||||
let msg = unsafe {
|
||||
let slot: &Slot<T> = &*(token.array.slot as *const Slot<T>);
|
||||
|
||||
let msg = slot.msg.get().read().assume_init();
|
||||
slot.stamp.store(token.array.stamp, Ordering::Release);
|
||||
msg
|
||||
};
|
||||
|
||||
// Wake a sleeping sender.
|
||||
self.senders.notify();
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Attempts to send a message into the channel.
|
||||
pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
|
||||
let token = &mut Token::default();
|
||||
if self.start_send(token) {
|
||||
unsafe { self.write(token, msg).map_err(TrySendError::Disconnected) }
|
||||
} else {
|
||||
Err(TrySendError::Full(msg))
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a message into the channel.
|
||||
pub(crate) fn send(
|
||||
&self,
|
||||
msg: T,
|
||||
deadline: Option<Instant>,
|
||||
) -> Result<(), SendTimeoutError<T>> {
|
||||
let token = &mut Token::default();
|
||||
loop {
|
||||
// Try sending a message.
|
||||
if self.start_send(token) {
|
||||
let res = unsafe { self.write(token, msg) };
|
||||
return res.map_err(SendTimeoutError::Disconnected);
|
||||
}
|
||||
|
||||
if let Some(d) = deadline {
|
||||
if Instant::now() >= d {
|
||||
return Err(SendTimeoutError::Timeout(msg));
|
||||
}
|
||||
}
|
||||
|
||||
Context::with(|cx| {
|
||||
// Prepare for blocking until a receiver wakes us up.
|
||||
let oper = Operation::hook(token);
|
||||
self.senders.register(oper, cx);
|
||||
|
||||
// Has the channel become ready just now?
|
||||
if !self.is_full() || self.is_disconnected() {
|
||||
let _ = cx.try_select(Selected::Aborted);
|
||||
}
|
||||
|
||||
// Block the current thread.
|
||||
// SAFETY: the context belongs to the current thread.
|
||||
let sel = unsafe { cx.wait_until(deadline) };
|
||||
|
||||
match sel {
|
||||
Selected::Waiting => unreachable!(),
|
||||
Selected::Aborted | Selected::Disconnected => {
|
||||
self.senders.unregister(oper).unwrap();
|
||||
}
|
||||
Selected::Operation(_) => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to receive a message without blocking.
|
||||
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
|
||||
let token = &mut Token::default();
|
||||
|
||||
if self.start_recv(token) {
|
||||
unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
|
||||
} else {
|
||||
Err(TryRecvError::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives a message from the channel.
|
||||
pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
|
||||
let token = &mut Token::default();
|
||||
loop {
|
||||
// Try receiving a message.
|
||||
if self.start_recv(token) {
|
||||
let res = unsafe { self.read(token) };
|
||||
return res.map_err(|_| RecvTimeoutError::Disconnected);
|
||||
}
|
||||
|
||||
if let Some(d) = deadline {
|
||||
if Instant::now() >= d {
|
||||
return Err(RecvTimeoutError::Timeout);
|
||||
}
|
||||
}
|
||||
|
||||
Context::with(|cx| {
|
||||
// Prepare for blocking until a sender wakes us up.
|
||||
let oper = Operation::hook(token);
|
||||
self.receivers.register(oper, cx);
|
||||
|
||||
// Has the channel become ready just now?
|
||||
if !self.is_empty() || self.is_disconnected() {
|
||||
let _ = cx.try_select(Selected::Aborted);
|
||||
}
|
||||
|
||||
// Block the current thread.
|
||||
// SAFETY: the context belongs to the current thread.
|
||||
let sel = unsafe { cx.wait_until(deadline) };
|
||||
|
||||
match sel {
|
||||
Selected::Waiting => unreachable!(),
|
||||
Selected::Aborted | Selected::Disconnected => {
|
||||
self.receivers.unregister(oper).unwrap();
|
||||
// If the channel was disconnected, we still have to check for remaining
|
||||
// messages.
|
||||
}
|
||||
Selected::Operation(_) => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current number of messages inside the channel.
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
loop {
|
||||
// Load the tail, then load the head.
|
||||
let tail = self.tail.load(Ordering::SeqCst);
|
||||
let head = self.head.load(Ordering::SeqCst);
|
||||
|
||||
// If the tail didn't change, we've got consistent values to work with.
|
||||
if self.tail.load(Ordering::SeqCst) == tail {
|
||||
let hix = head & (self.mark_bit - 1);
|
||||
let tix = tail & (self.mark_bit - 1);
|
||||
|
||||
return if hix < tix {
|
||||
tix - hix
|
||||
} else if hix > tix {
|
||||
self.cap - hix + tix
|
||||
} else if (tail & !self.mark_bit) == head {
|
||||
0
|
||||
} else {
|
||||
self.cap
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the capacity of the channel.
|
||||
#[allow(clippy::unnecessary_wraps)] // This is intentional.
|
||||
pub(crate) fn capacity(&self) -> Option<usize> {
|
||||
Some(self.cap)
|
||||
}
|
||||
|
||||
/// Disconnects senders and wakes up all blocked receivers.
|
||||
///
|
||||
/// Returns `true` if this call disconnected the channel.
|
||||
pub(crate) fn disconnect_senders(&self) -> bool {
|
||||
let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
|
||||
|
||||
if tail & self.mark_bit == 0 {
|
||||
self.receivers.disconnect();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Disconnects receivers and wakes up all blocked senders.
|
||||
///
|
||||
/// Returns `true` if this call disconnected the channel.
|
||||
///
|
||||
/// # Safety
|
||||
/// May only be called once upon dropping the last receiver. The
|
||||
/// destruction of all other receivers must have been observed with acquire
|
||||
/// ordering or stronger.
|
||||
pub(crate) unsafe fn disconnect_receivers(&self) -> bool {
|
||||
let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
|
||||
let disconnected = if tail & self.mark_bit == 0 {
|
||||
self.senders.disconnect();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
unsafe { self.discard_all_messages(tail) };
|
||||
disconnected
|
||||
}
|
||||
|
||||
/// Discards all messages.
|
||||
///
|
||||
/// `tail` should be the current (and therefore last) value of `tail`.
|
||||
///
|
||||
/// # Panicking
|
||||
/// If a destructor panics, the remaining messages are leaked, matching the
|
||||
/// behavior of the unbounded channel.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method must only be called when dropping the last receiver. The
|
||||
/// destruction of all other receivers must have been observed with acquire
|
||||
/// ordering or stronger.
|
||||
unsafe fn discard_all_messages(&self, tail: usize) {
|
||||
debug_assert!(self.is_disconnected());
|
||||
|
||||
// Only receivers modify `head`, so since we are the last one,
|
||||
// this value will not change and will not be observed (since
|
||||
// no new messages can be sent after disconnection).
|
||||
let mut head = self.head.load(Ordering::Relaxed);
|
||||
let tail = tail & !self.mark_bit;
|
||||
|
||||
let backoff = Backoff::new();
|
||||
loop {
|
||||
// Deconstruct the head.
|
||||
let index = head & (self.mark_bit - 1);
|
||||
let lap = head & !(self.one_lap - 1);
|
||||
|
||||
// Inspect the corresponding slot.
|
||||
debug_assert!(index < self.buffer.len());
|
||||
let slot = unsafe { self.buffer.get_unchecked(index) };
|
||||
let stamp = slot.stamp.load(Ordering::Acquire);
|
||||
|
||||
// If the stamp is ahead of the head by 1, we may drop the message.
|
||||
if head + 1 == stamp {
|
||||
head = if index + 1 < self.cap {
|
||||
// Same lap, incremented index.
|
||||
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
|
||||
head + 1
|
||||
} else {
|
||||
// One lap forward, index wraps around to zero.
|
||||
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
|
||||
lap.wrapping_add(self.one_lap)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
(*slot.msg.get()).assume_init_drop();
|
||||
}
|
||||
// If the tail equals the head, that means the channel is empty.
|
||||
} else if tail == head {
|
||||
return;
|
||||
// Otherwise, a sender is about to write into the slot, so we need
|
||||
// to wait for it to update the stamp.
|
||||
} else {
|
||||
backoff.spin_heavy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is disconnected.
|
||||
pub(crate) fn is_disconnected(&self) -> bool {
|
||||
self.tail.load(Ordering::SeqCst) & self.mark_bit != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is empty.
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
let head = self.head.load(Ordering::SeqCst);
|
||||
let tail = self.tail.load(Ordering::SeqCst);
|
||||
|
||||
// Is the tail equal to the head?
|
||||
//
|
||||
// Note: If the head changes just before we load the tail, that means there was a moment
|
||||
// when the channel was not empty, so it is safe to just return `false`.
|
||||
(tail & !self.mark_bit) == head
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is full.
|
||||
pub(crate) fn is_full(&self) -> bool {
|
||||
let tail = self.tail.load(Ordering::SeqCst);
|
||||
let head = self.head.load(Ordering::SeqCst);
|
||||
|
||||
// Is the head lagging one lap behind tail?
|
||||
//
|
||||
// Note: If the tail changes just before we load the head, that means there was a moment
|
||||
// when the channel was not full, so it is safe to just return `false`.
|
||||
head.wrapping_add(self.one_lap) == tail & !self.mark_bit
|
||||
}
|
||||
}
|
||||
159
crates/std/src/sync/mpmc/context.rs
Normal file
159
crates/std/src/sync/mpmc/context.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
//! Thread-local channel context.
|
||||
|
||||
use super::select::Selected;
|
||||
use super::waker::current_thread_id;
|
||||
use crate::cell::Cell;
|
||||
use crate::ptr;
|
||||
use alloc_crate::sync::Arc;
|
||||
use crate::sync::atomic::{Atomic, AtomicPtr, AtomicUsize, Ordering};
|
||||
use crate::thread::{self, Thread};
|
||||
use crate::time::Instant;
|
||||
|
||||
/// Thread-local context.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Context {
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
/// Inner representation of `Context`.
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
/// Selected operation.
|
||||
select: Atomic<usize>,
|
||||
|
||||
/// A slot into which another thread may store a pointer to its `Packet`.
|
||||
packet: Atomic<*mut ()>,
|
||||
|
||||
/// Thread handle.
|
||||
thread: Thread,
|
||||
|
||||
/// Thread id.
|
||||
thread_id: usize,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Creates a new context for the duration of the closure.
|
||||
#[inline]
|
||||
pub fn with<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&Context) -> R,
|
||||
{
|
||||
thread_local! {
|
||||
/// Cached thread-local context.
|
||||
static CONTEXT: Cell<Option<Context>> = Cell::new(Some(Context::new()));
|
||||
}
|
||||
|
||||
let mut f = Some(f);
|
||||
let mut f = |cx: &Context| -> R {
|
||||
let f = f.take().unwrap();
|
||||
f(cx)
|
||||
};
|
||||
|
||||
CONTEXT
|
||||
.try_with(|cell| match cell.take() {
|
||||
None => f(&Context::new()),
|
||||
Some(cx) => {
|
||||
cx.reset();
|
||||
let res = f(&cx);
|
||||
cell.set(Some(cx));
|
||||
res
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|_| f(&Context::new()))
|
||||
}
|
||||
|
||||
/// Creates a new `Context`.
|
||||
#[cold]
|
||||
fn new() -> Context {
|
||||
Context {
|
||||
inner: Arc::new(Inner {
|
||||
select: AtomicUsize::new(Selected::Waiting.into()),
|
||||
packet: AtomicPtr::new(ptr::null_mut()),
|
||||
thread: thread::current_or_unnamed(),
|
||||
thread_id: current_thread_id(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets `select` and `packet`.
|
||||
#[inline]
|
||||
fn reset(&self) {
|
||||
self.inner.select.store(Selected::Waiting.into(), Ordering::Release);
|
||||
self.inner.packet.store(ptr::null_mut(), Ordering::Release);
|
||||
}
|
||||
|
||||
/// Attempts to select an operation.
|
||||
///
|
||||
/// On failure, the previously selected operation is returned.
|
||||
#[inline]
|
||||
pub fn try_select(&self, select: Selected) -> Result<(), Selected> {
|
||||
self.inner
|
||||
.select
|
||||
.compare_exchange(
|
||||
Selected::Waiting.into(),
|
||||
select.into(),
|
||||
Ordering::AcqRel,
|
||||
Ordering::Acquire,
|
||||
)
|
||||
.map(|_| ())
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Stores a packet.
|
||||
///
|
||||
/// This method must be called after `try_select` succeeds and there is a packet to provide.
|
||||
#[inline]
|
||||
pub fn store_packet(&self, packet: *mut ()) {
|
||||
if !packet.is_null() {
|
||||
self.inner.packet.store(packet, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits until an operation is selected and returns it.
|
||||
///
|
||||
/// If the deadline is reached, `Selected::Aborted` will be selected.
|
||||
///
|
||||
/// # Safety
|
||||
/// This may only be called from the thread this `Context` belongs to.
|
||||
#[inline]
|
||||
pub unsafe fn wait_until(&self, deadline: Option<Instant>) -> Selected {
|
||||
loop {
|
||||
// Check whether an operation has been selected.
|
||||
let sel = Selected::from(self.inner.select.load(Ordering::Acquire));
|
||||
if sel != Selected::Waiting {
|
||||
return sel;
|
||||
}
|
||||
|
||||
// If there's a deadline, park the current thread until the deadline is reached.
|
||||
if let Some(end) = deadline {
|
||||
let now = Instant::now();
|
||||
|
||||
if now < end {
|
||||
// SAFETY: guaranteed by caller.
|
||||
unsafe { self.inner.thread.park_timeout(end - now) };
|
||||
} else {
|
||||
// The deadline has been reached. Try aborting select.
|
||||
return match self.try_select(Selected::Aborted) {
|
||||
Ok(()) => Selected::Aborted,
|
||||
Err(s) => s,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// SAFETY: guaranteed by caller.
|
||||
unsafe { self.inner.thread.park() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Unparks the thread this context belongs to.
|
||||
#[inline]
|
||||
pub fn unpark(&self) {
|
||||
self.inner.thread.unpark();
|
||||
}
|
||||
|
||||
/// Returns the id of the thread this context belongs to.
|
||||
#[inline]
|
||||
pub fn thread_id(&self) -> usize {
|
||||
self.inner.thread_id
|
||||
}
|
||||
}
|
||||
136
crates/std/src/sync/mpmc/counter.rs
Normal file
136
crates/std/src/sync/mpmc/counter.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering};
|
||||
use crate::{ops, process};
|
||||
|
||||
/// Reference counter internals.
|
||||
struct Counter<C> {
|
||||
/// The number of senders associated with the channel.
|
||||
senders: Atomic<usize>,
|
||||
|
||||
/// The number of receivers associated with the channel.
|
||||
receivers: Atomic<usize>,
|
||||
|
||||
/// Set to `true` if the last sender or the last receiver reference deallocates the channel.
|
||||
destroy: Atomic<bool>,
|
||||
|
||||
/// The internal channel.
|
||||
chan: C,
|
||||
}
|
||||
|
||||
/// Wraps a channel into the reference counter.
|
||||
pub(crate) fn new<C>(chan: C) -> (Sender<C>, Receiver<C>) {
|
||||
let counter = Box::into_raw(Box::new(Counter {
|
||||
senders: AtomicUsize::new(1),
|
||||
receivers: AtomicUsize::new(1),
|
||||
destroy: AtomicBool::new(false),
|
||||
chan,
|
||||
}));
|
||||
let s = Sender { counter };
|
||||
let r = Receiver { counter };
|
||||
(s, r)
|
||||
}
|
||||
|
||||
/// The sending side.
|
||||
pub(crate) struct Sender<C> {
|
||||
counter: *mut Counter<C>,
|
||||
}
|
||||
|
||||
impl<C> Sender<C> {
|
||||
/// Returns the internal `Counter`.
|
||||
fn counter(&self) -> &Counter<C> {
|
||||
unsafe { &*self.counter }
|
||||
}
|
||||
|
||||
/// Acquires another sender reference.
|
||||
pub(crate) fn acquire(&self) -> Sender<C> {
|
||||
let count = self.counter().senders.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
// Cloning senders and calling `mem::forget` on the clones could potentially overflow the
|
||||
// counter. It's very difficult to recover sensibly from such degenerate scenarios so we
|
||||
// just abort when the count becomes very large.
|
||||
if count > isize::MAX as usize {
|
||||
process::abort();
|
||||
}
|
||||
|
||||
Sender { counter: self.counter }
|
||||
}
|
||||
|
||||
/// Releases the sender reference.
|
||||
///
|
||||
/// Function `disconnect` will be called if this is the last sender reference.
|
||||
pub(crate) unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
|
||||
if self.counter().senders.fetch_sub(1, Ordering::AcqRel) == 1 {
|
||||
disconnect(&self.counter().chan);
|
||||
|
||||
if self.counter().destroy.swap(true, Ordering::AcqRel) {
|
||||
drop(unsafe { Box::from_raw(self.counter) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> ops::Deref for Sender<C> {
|
||||
type Target = C;
|
||||
|
||||
fn deref(&self) -> &C {
|
||||
&self.counter().chan
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> PartialEq for Sender<C> {
|
||||
fn eq(&self, other: &Sender<C>) -> bool {
|
||||
self.counter == other.counter
|
||||
}
|
||||
}
|
||||
|
||||
/// The receiving side.
|
||||
pub(crate) struct Receiver<C> {
|
||||
counter: *mut Counter<C>,
|
||||
}
|
||||
|
||||
impl<C> Receiver<C> {
|
||||
/// Returns the internal `Counter`.
|
||||
fn counter(&self) -> &Counter<C> {
|
||||
unsafe { &*self.counter }
|
||||
}
|
||||
|
||||
/// Acquires another receiver reference.
|
||||
pub(crate) fn acquire(&self) -> Receiver<C> {
|
||||
let count = self.counter().receivers.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
// Cloning receivers and calling `mem::forget` on the clones could potentially overflow the
|
||||
// counter. It's very difficult to recover sensibly from such degenerate scenarios so we
|
||||
// just abort when the count becomes very large.
|
||||
if count > isize::MAX as usize {
|
||||
process::abort();
|
||||
}
|
||||
|
||||
Receiver { counter: self.counter }
|
||||
}
|
||||
|
||||
/// Releases the receiver reference.
|
||||
///
|
||||
/// Function `disconnect` will be called if this is the last receiver reference.
|
||||
pub(crate) unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
|
||||
if self.counter().receivers.fetch_sub(1, Ordering::AcqRel) == 1 {
|
||||
disconnect(&self.counter().chan);
|
||||
|
||||
if self.counter().destroy.swap(true, Ordering::AcqRel) {
|
||||
drop(unsafe { Box::from_raw(self.counter) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> ops::Deref for Receiver<C> {
|
||||
type Target = C;
|
||||
|
||||
fn deref(&self) -> &C {
|
||||
&self.counter().chan
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> PartialEq for Receiver<C> {
|
||||
fn eq(&self, other: &Receiver<C>) -> bool {
|
||||
self.counter == other.counter
|
||||
}
|
||||
}
|
||||
49
crates/std/src/sync/mpmc/error.rs
Normal file
49
crates/std/src/sync/mpmc/error.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
pub use crate::sync::mpsc::{RecvError, RecvTimeoutError, SendError, TryRecvError, TrySendError};
|
||||
use crate::{error, fmt};
|
||||
|
||||
/// An error returned from the [`send_timeout`] method.
|
||||
///
|
||||
/// The error contains the message being sent so it can be recovered.
|
||||
///
|
||||
/// [`send_timeout`]: super::Sender::send_timeout
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
pub enum SendTimeoutError<T> {
|
||||
/// The message could not be sent because the channel is full and the operation timed out.
|
||||
///
|
||||
/// If this is a zero-capacity channel, then the error indicates that there was no receiver
|
||||
/// available to receive the message and the operation timed out.
|
||||
Timeout(T),
|
||||
|
||||
/// The message could not be sent because the channel is disconnected.
|
||||
Disconnected(T),
|
||||
}
|
||||
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
impl<T> fmt::Debug for SendTimeoutError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"SendTimeoutError(..)".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
impl<T> fmt::Display for SendTimeoutError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
SendTimeoutError::Timeout(..) => "timed out waiting on send operation".fmt(f),
|
||||
SendTimeoutError::Disconnected(..) => "sending on a disconnected channel".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
impl<T> error::Error for SendTimeoutError<T> {}
|
||||
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
impl<T> From<SendError<T>> for SendTimeoutError<T> {
|
||||
fn from(err: SendError<T>) -> SendTimeoutError<T> {
|
||||
match err {
|
||||
SendError(e) => SendTimeoutError::Disconnected(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
668
crates/std/src/sync/mpmc/list.rs
Normal file
668
crates/std/src/sync/mpmc/list.rs
Normal file
@@ -0,0 +1,668 @@
|
||||
//! Unbounded channel implemented as a linked list.
|
||||
|
||||
use super::context::Context;
|
||||
use super::error::*;
|
||||
use super::select::{Operation, Selected, Token};
|
||||
use super::utils::{Backoff, CachePadded};
|
||||
use super::waker::SyncWaker;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{self, Atomic, AtomicPtr, AtomicUsize, Ordering};
|
||||
use crate::time::Instant;
|
||||
|
||||
// Bits indicating the state of a slot:
|
||||
// * If a message has been written into the slot, `WRITE` is set.
|
||||
// * If a message has been read from the slot, `READ` is set.
|
||||
// * If the block is being destroyed, `DESTROY` is set.
|
||||
const WRITE: usize = 1;
|
||||
const READ: usize = 2;
|
||||
const DESTROY: usize = 4;
|
||||
|
||||
// Each block covers one "lap" of indices.
|
||||
const LAP: usize = 32;
|
||||
// The maximum number of messages a block can hold.
|
||||
const BLOCK_CAP: usize = LAP - 1;
|
||||
// How many lower bits are reserved for metadata.
|
||||
const SHIFT: usize = 1;
|
||||
// Has two different purposes:
|
||||
// * If set in head, indicates that the block is not the last one.
|
||||
// * If set in tail, indicates that the channel is disconnected.
|
||||
const MARK_BIT: usize = 1;
|
||||
|
||||
/// A slot in a block.
|
||||
struct Slot<T> {
|
||||
/// The message.
|
||||
msg: UnsafeCell<MaybeUninit<T>>,
|
||||
|
||||
/// The state of the slot.
|
||||
state: Atomic<usize>,
|
||||
}
|
||||
|
||||
impl<T> Slot<T> {
|
||||
/// Waits until a message is written into the slot.
|
||||
fn wait_write(&self) {
|
||||
let backoff = Backoff::new();
|
||||
while self.state.load(Ordering::Acquire) & WRITE == 0 {
|
||||
backoff.spin_heavy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A block in a linked list.
|
||||
///
|
||||
/// Each block in the list can hold up to `BLOCK_CAP` messages.
|
||||
struct Block<T> {
|
||||
/// The next block in the linked list.
|
||||
next: Atomic<*mut Block<T>>,
|
||||
|
||||
/// Slots for messages.
|
||||
slots: [Slot<T>; BLOCK_CAP],
|
||||
}
|
||||
|
||||
impl<T> Block<T> {
|
||||
/// Creates an empty block.
|
||||
fn new() -> Box<Block<T>> {
|
||||
// SAFETY: This is safe because:
|
||||
// [1] `Block::next` (Atomic<*mut _>) may be safely zero initialized.
|
||||
// [2] `Block::slots` (Array) may be safely zero initialized because of [3, 4].
|
||||
// [3] `Slot::msg` (UnsafeCell) may be safely zero initialized because it
|
||||
// holds a MaybeUninit.
|
||||
// [4] `Slot::state` (Atomic<usize>) may be safely zero initialized.
|
||||
unsafe { Box::new_zeroed().assume_init() }
|
||||
}
|
||||
|
||||
/// Waits until the next pointer is set.
|
||||
fn wait_next(&self) -> *mut Block<T> {
|
||||
let backoff = Backoff::new();
|
||||
loop {
|
||||
let next = self.next.load(Ordering::Acquire);
|
||||
if !next.is_null() {
|
||||
return next;
|
||||
}
|
||||
backoff.spin_heavy();
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the `DESTROY` bit in slots starting from `start` and destroys the block.
|
||||
unsafe fn destroy(this: *mut Block<T>, start: usize) {
|
||||
// It is not necessary to set the `DESTROY` bit in the last slot because that slot has
|
||||
// begun destruction of the block.
|
||||
for i in start..BLOCK_CAP - 1 {
|
||||
let slot = unsafe { (*this).slots.get_unchecked(i) };
|
||||
|
||||
// Mark the `DESTROY` bit if a thread is still using the slot.
|
||||
if slot.state.load(Ordering::Acquire) & READ == 0
|
||||
&& slot.state.fetch_or(DESTROY, Ordering::AcqRel) & READ == 0
|
||||
{
|
||||
// If a thread is still using the slot, it will continue destruction of the block.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No thread is using the block, now it is safe to destroy it.
|
||||
drop(unsafe { Box::from_raw(this) });
|
||||
}
|
||||
}
|
||||
|
||||
/// A position in a channel.
|
||||
#[derive(Debug)]
|
||||
struct Position<T> {
|
||||
/// The index in the channel.
|
||||
index: Atomic<usize>,
|
||||
|
||||
/// The block in the linked list.
|
||||
block: Atomic<*mut Block<T>>,
|
||||
}
|
||||
|
||||
/// The token type for the list flavor.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ListToken {
|
||||
/// The block of slots.
|
||||
block: *const u8,
|
||||
|
||||
/// The offset into the block.
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl Default for ListToken {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
ListToken { block: ptr::null(), offset: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// Unbounded channel implemented as a linked list.
|
||||
///
|
||||
/// Each message sent into the channel is assigned a sequence number, i.e. an index. Indices are
|
||||
/// represented as numbers of type `usize` and wrap on overflow.
|
||||
///
|
||||
/// Consecutive messages are grouped into blocks in order to put less pressure on the allocator and
|
||||
/// improve cache efficiency.
|
||||
pub(crate) struct Channel<T> {
|
||||
/// The head of the channel.
|
||||
head: CachePadded<Position<T>>,
|
||||
|
||||
/// The tail of the channel.
|
||||
tail: CachePadded<Position<T>>,
|
||||
|
||||
/// Receivers waiting while the channel is empty and not disconnected.
|
||||
receivers: SyncWaker,
|
||||
|
||||
/// Indicates that dropping a `Channel<T>` may drop messages of type `T`.
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Channel<T> {
|
||||
/// Creates a new unbounded channel.
|
||||
pub(crate) fn new() -> Self {
|
||||
Channel {
|
||||
head: CachePadded::new(Position {
|
||||
block: AtomicPtr::new(ptr::null_mut()),
|
||||
index: AtomicUsize::new(0),
|
||||
}),
|
||||
tail: CachePadded::new(Position {
|
||||
block: AtomicPtr::new(ptr::null_mut()),
|
||||
index: AtomicUsize::new(0),
|
||||
}),
|
||||
receivers: SyncWaker::new(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to reserve a slot for sending a message.
|
||||
fn start_send(&self, token: &mut Token) -> bool {
|
||||
let backoff = Backoff::new();
|
||||
let mut tail = self.tail.index.load(Ordering::Acquire);
|
||||
let mut block = self.tail.block.load(Ordering::Acquire);
|
||||
let mut next_block = None;
|
||||
|
||||
loop {
|
||||
// Check if the channel is disconnected.
|
||||
if tail & MARK_BIT != 0 {
|
||||
token.list.block = ptr::null();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate the offset of the index into the block.
|
||||
let offset = (tail >> SHIFT) % LAP;
|
||||
|
||||
// If we reached the end of the block, wait until the next one is installed.
|
||||
if offset == BLOCK_CAP {
|
||||
backoff.spin_heavy();
|
||||
tail = self.tail.index.load(Ordering::Acquire);
|
||||
block = self.tail.block.load(Ordering::Acquire);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're going to have to install the next block, allocate it in advance in order to
|
||||
// make the wait for other threads as short as possible.
|
||||
if offset + 1 == BLOCK_CAP && next_block.is_none() {
|
||||
next_block = Some(Block::<T>::new());
|
||||
}
|
||||
|
||||
// If this is the first message to be sent into the channel, we need to allocate the
|
||||
// first block and install it.
|
||||
if block.is_null() {
|
||||
let new = Box::into_raw(Block::<T>::new());
|
||||
|
||||
if self
|
||||
.tail
|
||||
.block
|
||||
.compare_exchange(block, new, Ordering::Release, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
// This yield point leaves the channel in a half-initialized state where the
|
||||
// tail.block pointer is set but the head.block is not. This is used to
|
||||
// facilitate the test in src/tools/miri/tests/pass/issues/issue-139553.rs
|
||||
#[cfg(miri)]
|
||||
crate::thread::yield_now();
|
||||
self.head.block.store(new, Ordering::Release);
|
||||
block = new;
|
||||
} else {
|
||||
next_block = unsafe { Some(Box::from_raw(new)) };
|
||||
tail = self.tail.index.load(Ordering::Acquire);
|
||||
block = self.tail.block.load(Ordering::Acquire);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let new_tail = tail + (1 << SHIFT);
|
||||
|
||||
// Try advancing the tail forward.
|
||||
match self.tail.index.compare_exchange_weak(
|
||||
tail,
|
||||
new_tail,
|
||||
Ordering::SeqCst,
|
||||
Ordering::Acquire,
|
||||
) {
|
||||
Ok(_) => unsafe {
|
||||
// If we've reached the end of the block, install the next one.
|
||||
if offset + 1 == BLOCK_CAP {
|
||||
let next_block = Box::into_raw(next_block.unwrap());
|
||||
self.tail.block.store(next_block, Ordering::Release);
|
||||
self.tail.index.fetch_add(1 << SHIFT, Ordering::Release);
|
||||
(*block).next.store(next_block, Ordering::Release);
|
||||
}
|
||||
|
||||
token.list.block = block as *const u8;
|
||||
token.list.offset = offset;
|
||||
return true;
|
||||
},
|
||||
Err(_) => {
|
||||
backoff.spin_light();
|
||||
tail = self.tail.index.load(Ordering::Acquire);
|
||||
block = self.tail.block.load(Ordering::Acquire);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a message into the channel.
|
||||
pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
|
||||
// If there is no slot, the channel is disconnected.
|
||||
if token.list.block.is_null() {
|
||||
return Err(msg);
|
||||
}
|
||||
|
||||
// Write the message into the slot.
|
||||
let block = token.list.block as *mut Block<T>;
|
||||
let offset = token.list.offset;
|
||||
unsafe {
|
||||
let slot = (*block).slots.get_unchecked(offset);
|
||||
slot.msg.get().write(MaybeUninit::new(msg));
|
||||
slot.state.fetch_or(WRITE, Ordering::Release);
|
||||
}
|
||||
|
||||
// Wake a sleeping receiver.
|
||||
self.receivers.notify();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attempts to reserve a slot for receiving a message.
|
||||
fn start_recv(&self, token: &mut Token) -> bool {
|
||||
let backoff = Backoff::new();
|
||||
let mut head = self.head.index.load(Ordering::Acquire);
|
||||
let mut block = self.head.block.load(Ordering::Acquire);
|
||||
|
||||
loop {
|
||||
// Calculate the offset of the index into the block.
|
||||
let offset = (head >> SHIFT) % LAP;
|
||||
|
||||
// If we reached the end of the block, wait until the next one is installed.
|
||||
if offset == BLOCK_CAP {
|
||||
backoff.spin_heavy();
|
||||
head = self.head.index.load(Ordering::Acquire);
|
||||
block = self.head.block.load(Ordering::Acquire);
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut new_head = head + (1 << SHIFT);
|
||||
|
||||
if new_head & MARK_BIT == 0 {
|
||||
atomic::fence(Ordering::SeqCst);
|
||||
let tail = self.tail.index.load(Ordering::Relaxed);
|
||||
|
||||
// If the tail equals the head, that means the channel is empty.
|
||||
if head >> SHIFT == tail >> SHIFT {
|
||||
// If the channel is disconnected...
|
||||
if tail & MARK_BIT != 0 {
|
||||
// ...then receive an error.
|
||||
token.list.block = ptr::null();
|
||||
return true;
|
||||
} else {
|
||||
// Otherwise, the receive operation is not ready.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If head and tail are not in the same block, set `MARK_BIT` in head.
|
||||
if (head >> SHIFT) / LAP != (tail >> SHIFT) / LAP {
|
||||
new_head |= MARK_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// The block can be null here only if the first message is being sent into the channel.
|
||||
// In that case, just wait until it gets initialized.
|
||||
if block.is_null() {
|
||||
backoff.spin_heavy();
|
||||
head = self.head.index.load(Ordering::Acquire);
|
||||
block = self.head.block.load(Ordering::Acquire);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try moving the head index forward.
|
||||
match self.head.index.compare_exchange_weak(
|
||||
head,
|
||||
new_head,
|
||||
Ordering::SeqCst,
|
||||
Ordering::Acquire,
|
||||
) {
|
||||
Ok(_) => unsafe {
|
||||
// If we've reached the end of the block, move to the next one.
|
||||
if offset + 1 == BLOCK_CAP {
|
||||
let next = (*block).wait_next();
|
||||
let mut next_index = (new_head & !MARK_BIT).wrapping_add(1 << SHIFT);
|
||||
if !(*next).next.load(Ordering::Relaxed).is_null() {
|
||||
next_index |= MARK_BIT;
|
||||
}
|
||||
|
||||
self.head.block.store(next, Ordering::Release);
|
||||
self.head.index.store(next_index, Ordering::Release);
|
||||
}
|
||||
|
||||
token.list.block = block as *const u8;
|
||||
token.list.offset = offset;
|
||||
return true;
|
||||
},
|
||||
Err(_) => {
|
||||
backoff.spin_light();
|
||||
head = self.head.index.load(Ordering::Acquire);
|
||||
block = self.head.block.load(Ordering::Acquire);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a message from the channel.
|
||||
pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
|
||||
if token.list.block.is_null() {
|
||||
// The channel is disconnected.
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Read the message.
|
||||
let block = token.list.block as *mut Block<T>;
|
||||
let offset = token.list.offset;
|
||||
unsafe {
|
||||
let slot = (*block).slots.get_unchecked(offset);
|
||||
slot.wait_write();
|
||||
let msg = slot.msg.get().read().assume_init();
|
||||
|
||||
// Destroy the block if we've reached the end, or if another thread wanted to destroy but
|
||||
// couldn't because we were busy reading from the slot.
|
||||
if offset + 1 == BLOCK_CAP {
|
||||
Block::destroy(block, 0);
|
||||
} else if slot.state.fetch_or(READ, Ordering::AcqRel) & DESTROY != 0 {
|
||||
Block::destroy(block, offset + 1);
|
||||
}
|
||||
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to send a message into the channel.
|
||||
pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
|
||||
self.send(msg, None).map_err(|err| match err {
|
||||
SendTimeoutError::Disconnected(msg) => TrySendError::Disconnected(msg),
|
||||
SendTimeoutError::Timeout(_) => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Sends a message into the channel.
|
||||
pub(crate) fn send(
|
||||
&self,
|
||||
msg: T,
|
||||
_deadline: Option<Instant>,
|
||||
) -> Result<(), SendTimeoutError<T>> {
|
||||
let token = &mut Token::default();
|
||||
assert!(self.start_send(token));
|
||||
unsafe { self.write(token, msg).map_err(SendTimeoutError::Disconnected) }
|
||||
}
|
||||
|
||||
/// Attempts to receive a message without blocking.
|
||||
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
|
||||
let token = &mut Token::default();
|
||||
|
||||
if self.start_recv(token) {
|
||||
unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
|
||||
} else {
|
||||
Err(TryRecvError::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives a message from the channel.
|
||||
pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
|
||||
let token = &mut Token::default();
|
||||
loop {
|
||||
if self.start_recv(token) {
|
||||
unsafe {
|
||||
return self.read(token).map_err(|_| RecvTimeoutError::Disconnected);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(d) = deadline {
|
||||
if Instant::now() >= d {
|
||||
return Err(RecvTimeoutError::Timeout);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for blocking until a sender wakes us up.
|
||||
Context::with(|cx| {
|
||||
let oper = Operation::hook(token);
|
||||
self.receivers.register(oper, cx);
|
||||
|
||||
// Has the channel become ready just now?
|
||||
if !self.is_empty() || self.is_disconnected() {
|
||||
let _ = cx.try_select(Selected::Aborted);
|
||||
}
|
||||
|
||||
// Block the current thread.
|
||||
// SAFETY: the context belongs to the current thread.
|
||||
let sel = unsafe { cx.wait_until(deadline) };
|
||||
|
||||
match sel {
|
||||
Selected::Waiting => unreachable!(),
|
||||
Selected::Aborted | Selected::Disconnected => {
|
||||
self.receivers.unregister(oper).unwrap();
|
||||
// If the channel was disconnected, we still have to check for remaining
|
||||
// messages.
|
||||
}
|
||||
Selected::Operation(_) => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current number of messages inside the channel.
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
loop {
|
||||
// Load the tail index, then load the head index.
|
||||
let mut tail = self.tail.index.load(Ordering::SeqCst);
|
||||
let mut head = self.head.index.load(Ordering::SeqCst);
|
||||
|
||||
// If the tail index didn't change, we've got consistent indices to work with.
|
||||
if self.tail.index.load(Ordering::SeqCst) == tail {
|
||||
// Erase the lower bits.
|
||||
tail &= !((1 << SHIFT) - 1);
|
||||
head &= !((1 << SHIFT) - 1);
|
||||
|
||||
// Fix up indices if they fall onto block ends.
|
||||
if (tail >> SHIFT) & (LAP - 1) == LAP - 1 {
|
||||
tail = tail.wrapping_add(1 << SHIFT);
|
||||
}
|
||||
if (head >> SHIFT) & (LAP - 1) == LAP - 1 {
|
||||
head = head.wrapping_add(1 << SHIFT);
|
||||
}
|
||||
|
||||
// Rotate indices so that head falls into the first block.
|
||||
let lap = (head >> SHIFT) / LAP;
|
||||
tail = tail.wrapping_sub((lap * LAP) << SHIFT);
|
||||
head = head.wrapping_sub((lap * LAP) << SHIFT);
|
||||
|
||||
// Remove the lower bits.
|
||||
tail >>= SHIFT;
|
||||
head >>= SHIFT;
|
||||
|
||||
// Return the difference minus the number of blocks between tail and head.
|
||||
return tail - head - tail / LAP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the capacity of the channel.
|
||||
pub(crate) fn capacity(&self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Disconnects senders and wakes up all blocked receivers.
|
||||
///
|
||||
/// Returns `true` if this call disconnected the channel.
|
||||
pub(crate) fn disconnect_senders(&self) -> bool {
|
||||
let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst);
|
||||
|
||||
if tail & MARK_BIT == 0 {
|
||||
self.receivers.disconnect();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Disconnects receivers.
|
||||
///
|
||||
/// Returns `true` if this call disconnected the channel.
|
||||
pub(crate) fn disconnect_receivers(&self) -> bool {
|
||||
let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst);
|
||||
|
||||
if tail & MARK_BIT == 0 {
|
||||
// If receivers are dropped first, discard all messages to free
|
||||
// memory eagerly.
|
||||
self.discard_all_messages();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Discards all messages.
|
||||
///
|
||||
/// This method should only be called when all receivers are dropped.
|
||||
fn discard_all_messages(&self) {
|
||||
let backoff = Backoff::new();
|
||||
let mut tail = self.tail.index.load(Ordering::Acquire);
|
||||
loop {
|
||||
let offset = (tail >> SHIFT) % LAP;
|
||||
if offset != BLOCK_CAP {
|
||||
break;
|
||||
}
|
||||
|
||||
// New updates to tail will be rejected by MARK_BIT and aborted unless it's
|
||||
// at boundary. We need to wait for the updates take affect otherwise there
|
||||
// can be memory leaks.
|
||||
backoff.spin_heavy();
|
||||
tail = self.tail.index.load(Ordering::Acquire);
|
||||
}
|
||||
|
||||
let mut head = self.head.index.load(Ordering::Acquire);
|
||||
// The channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts
|
||||
// to initialize the first block before noticing that the receivers disconnected. Late allocations
|
||||
// will be deallocated by the sender in Drop.
|
||||
let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel);
|
||||
|
||||
// If we're going to be dropping messages we need to synchronize with initialization
|
||||
if head >> SHIFT != tail >> SHIFT {
|
||||
// The block can be null here only if a sender is in the process of initializing the
|
||||
// channel while another sender managed to send a message by inserting it into the
|
||||
// semi-initialized channel and advanced the tail.
|
||||
// In that case, just wait until it gets initialized.
|
||||
while block.is_null() {
|
||||
backoff.spin_heavy();
|
||||
block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel);
|
||||
}
|
||||
}
|
||||
// After this point `head.block` is not modified again and it will be deallocated if it's
|
||||
// non-null. The `Drop` code of the channel, which runs after this function, also attempts
|
||||
// to deallocate `head.block` if it's non-null. Therefore this function must maintain the
|
||||
// invariant that if a deallocation of head.block is attempted then it must also be set to
|
||||
// NULL. Failing to do so will lead to the Drop code attempting a double free. For this
|
||||
// reason both reads above do an atomic swap instead of a simple atomic load.
|
||||
|
||||
unsafe {
|
||||
// Drop all messages between head and tail and deallocate the heap-allocated blocks.
|
||||
while head >> SHIFT != tail >> SHIFT {
|
||||
let offset = (head >> SHIFT) % LAP;
|
||||
|
||||
if offset < BLOCK_CAP {
|
||||
// Drop the message in the slot.
|
||||
let slot = (*block).slots.get_unchecked(offset);
|
||||
slot.wait_write();
|
||||
let p = &mut *slot.msg.get();
|
||||
p.as_mut_ptr().drop_in_place();
|
||||
} else {
|
||||
(*block).wait_next();
|
||||
// Deallocate the block and move to the next one.
|
||||
let next = (*block).next.load(Ordering::Acquire);
|
||||
drop(Box::from_raw(block));
|
||||
block = next;
|
||||
}
|
||||
|
||||
head = head.wrapping_add(1 << SHIFT);
|
||||
}
|
||||
|
||||
// Deallocate the last remaining block.
|
||||
if !block.is_null() {
|
||||
drop(Box::from_raw(block));
|
||||
}
|
||||
}
|
||||
|
||||
head &= !MARK_BIT;
|
||||
self.head.index.store(head, Ordering::Release);
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is disconnected.
|
||||
pub(crate) fn is_disconnected(&self) -> bool {
|
||||
self.tail.index.load(Ordering::SeqCst) & MARK_BIT != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is empty.
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
let head = self.head.index.load(Ordering::SeqCst);
|
||||
let tail = self.tail.index.load(Ordering::SeqCst);
|
||||
head >> SHIFT == tail >> SHIFT
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is full.
|
||||
pub(crate) fn is_full(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Channel<T> {
|
||||
fn drop(&mut self) {
|
||||
let mut head = self.head.index.load(Ordering::Relaxed);
|
||||
let mut tail = self.tail.index.load(Ordering::Relaxed);
|
||||
let mut block = self.head.block.load(Ordering::Relaxed);
|
||||
|
||||
// Erase the lower bits.
|
||||
head &= !((1 << SHIFT) - 1);
|
||||
tail &= !((1 << SHIFT) - 1);
|
||||
|
||||
unsafe {
|
||||
// Drop all messages between head and tail and deallocate the heap-allocated blocks.
|
||||
while head != tail {
|
||||
let offset = (head >> SHIFT) % LAP;
|
||||
|
||||
if offset < BLOCK_CAP {
|
||||
// Drop the message in the slot.
|
||||
let slot = (*block).slots.get_unchecked(offset);
|
||||
let p = &mut *slot.msg.get();
|
||||
p.as_mut_ptr().drop_in_place();
|
||||
} else {
|
||||
// Deallocate the block and move to the next one.
|
||||
let next = (*block).next.load(Ordering::Relaxed);
|
||||
drop(Box::from_raw(block));
|
||||
block = next;
|
||||
}
|
||||
|
||||
head = head.wrapping_add(1 << SHIFT);
|
||||
}
|
||||
|
||||
// Deallocate the last remaining block.
|
||||
if !block.is_null() {
|
||||
drop(Box::from_raw(block));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1388
crates/std/src/sync/mpmc/mod.rs
Normal file
1388
crates/std/src/sync/mpmc/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
71
crates/std/src/sync/mpmc/select.rs
Normal file
71
crates/std/src/sync/mpmc/select.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
/// Temporary data that gets initialized during a blocking operation, and is consumed by
|
||||
/// `read` or `write`.
|
||||
///
|
||||
/// Each field contains data associated with a specific channel flavor.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Token {
|
||||
pub(crate) array: super::array::ArrayToken,
|
||||
pub(crate) list: super::list::ListToken,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) zero: super::zero::ZeroToken,
|
||||
}
|
||||
|
||||
/// Identifier associated with an operation by a specific thread on a specific channel.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Operation(usize);
|
||||
|
||||
impl Operation {
|
||||
/// Creates an operation identifier from a mutable reference.
|
||||
///
|
||||
/// This function essentially just turns the address of the reference into a number. The
|
||||
/// reference should point to a variable that is specific to the thread and the operation,
|
||||
/// and is alive for the entire duration of a blocking operation.
|
||||
#[inline]
|
||||
pub fn hook<T>(r: &mut T) -> Operation {
|
||||
let val = r as *mut T as usize;
|
||||
// Make sure that the pointer address doesn't equal the numerical representation of
|
||||
// `Selected::{Waiting, Aborted, Disconnected}`.
|
||||
assert!(val > 2);
|
||||
Operation(val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Current state of a blocking operation.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Selected {
|
||||
/// Still waiting for an operation.
|
||||
Waiting,
|
||||
|
||||
/// The attempt to block the current thread has been aborted.
|
||||
Aborted,
|
||||
|
||||
/// An operation became ready because a channel is disconnected.
|
||||
Disconnected,
|
||||
|
||||
/// An operation became ready because a message can be sent or received.
|
||||
Operation(Operation),
|
||||
}
|
||||
|
||||
impl From<usize> for Selected {
|
||||
#[inline]
|
||||
fn from(val: usize) -> Selected {
|
||||
match val {
|
||||
0 => Selected::Waiting,
|
||||
1 => Selected::Aborted,
|
||||
2 => Selected::Disconnected,
|
||||
oper => Selected::Operation(Operation(oper)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<usize> for Selected {
|
||||
#[inline]
|
||||
fn into(self) -> usize {
|
||||
match self {
|
||||
Selected::Waiting => 0,
|
||||
Selected::Aborted => 1,
|
||||
Selected::Disconnected => 2,
|
||||
Selected::Operation(Operation(val)) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
14
crates/std/src/sync/mpmc/tests.rs
Normal file
14
crates/std/src/sync/mpmc/tests.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
// Ensure that thread_local init with `const { 0 }` still has unique address at run-time
|
||||
#[test]
|
||||
fn waker_current_thread_id() {
|
||||
let first = super::waker::current_thread_id();
|
||||
let t = crate::thread::spawn(move || {
|
||||
let second = super::waker::current_thread_id();
|
||||
assert_ne!(first, second);
|
||||
assert_eq!(second, super::waker::current_thread_id());
|
||||
});
|
||||
|
||||
assert_eq!(first, super::waker::current_thread_id());
|
||||
t.join().unwrap();
|
||||
assert_eq!(first, super::waker::current_thread_id());
|
||||
}
|
||||
137
crates/std/src/sync/mpmc/utils.rs
Normal file
137
crates/std/src/sync/mpmc/utils.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use crate::cell::Cell;
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
|
||||
/// Pads and aligns a value to the length of a cache line.
|
||||
#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
|
||||
// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache
|
||||
// lines at a time, so we have to align to 128 bytes rather than 64.
|
||||
//
|
||||
// Sources:
|
||||
// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
|
||||
// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107
|
||||
//
|
||||
// ARM's big.LITTLE architecture has asymmetric cores and "big" cores have 128-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://www.mono-project.com/news/2016/09/12/arm64-icache/
|
||||
//
|
||||
// powerpc64 has 128-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9
|
||||
#[cfg_attr(
|
||||
any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64",),
|
||||
repr(align(128))
|
||||
)]
|
||||
// arm, mips and mips64 have 32-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9
|
||||
#[cfg_attr(
|
||||
any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips32r6",
|
||||
target_arch = "mips64",
|
||||
target_arch = "mips64r6",
|
||||
),
|
||||
repr(align(32))
|
||||
)]
|
||||
// s390x has 256-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7
|
||||
#[cfg_attr(target_arch = "s390x", repr(align(256)))]
|
||||
// x86, wasm and riscv have 64-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7
|
||||
// - https://github.com/golang/go/blob/5e31f78c8a4ed1b872ddc194f0cd1ae931b37d7e/src/internal/cpu/cpu_riscv64.go#L7
|
||||
//
|
||||
// All others are assumed to have 64-byte cache line size.
|
||||
#[cfg_attr(
|
||||
not(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips32r6",
|
||||
target_arch = "mips64",
|
||||
target_arch = "mips64r6",
|
||||
target_arch = "s390x",
|
||||
)),
|
||||
repr(align(64))
|
||||
)]
|
||||
pub struct CachePadded<T> {
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl<T> CachePadded<T> {
|
||||
/// Pads and aligns a value to the length of a cache line.
|
||||
pub fn new(value: T) -> CachePadded<T> {
|
||||
CachePadded::<T> { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for CachePadded<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for CachePadded<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
const SPIN_LIMIT: u32 = 6;
|
||||
|
||||
/// Performs quadratic backoff in spin loops.
|
||||
pub struct Backoff {
|
||||
step: Cell<u32>,
|
||||
}
|
||||
|
||||
impl Backoff {
|
||||
/// Creates a new `Backoff`.
|
||||
pub fn new() -> Self {
|
||||
Backoff { step: Cell::new(0) }
|
||||
}
|
||||
|
||||
/// Backs off using lightweight spinning.
|
||||
///
|
||||
/// This method should be used for retrying an operation because another thread made
|
||||
/// progress. i.e. on CAS failure.
|
||||
#[inline]
|
||||
pub fn spin_light(&self) {
|
||||
let step = self.step.get().min(SPIN_LIMIT);
|
||||
for _ in 0..step.pow(2) {
|
||||
crate::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.step.set(self.step.get() + 1);
|
||||
}
|
||||
|
||||
/// Backs off using heavyweight spinning.
|
||||
///
|
||||
/// This method should be used in blocking loops where parking the thread is not an option.
|
||||
#[inline]
|
||||
pub fn spin_heavy(&self) {
|
||||
if self.step.get() <= SPIN_LIMIT {
|
||||
for _ in 0..self.step.get().pow(2) {
|
||||
crate::hint::spin_loop()
|
||||
}
|
||||
} else {
|
||||
crate::thread::yield_now();
|
||||
}
|
||||
|
||||
self.step.set(self.step.get() + 1);
|
||||
}
|
||||
}
|
||||
209
crates/std/src/sync/mpmc/waker.rs
Normal file
209
crates/std/src/sync/mpmc/waker.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
//! Waking mechanism for threads blocked on channel operations.
|
||||
|
||||
use super::context::Context;
|
||||
use super::select::{Operation, Selected};
|
||||
use crate::ptr;
|
||||
use crate::sync::Mutex;
|
||||
use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
|
||||
|
||||
/// Represents a thread blocked on a specific channel operation.
|
||||
pub(crate) struct Entry {
|
||||
/// The operation.
|
||||
pub(crate) oper: Operation,
|
||||
|
||||
/// Optional packet.
|
||||
pub(crate) packet: *mut (),
|
||||
|
||||
/// Context associated with the thread owning this operation.
|
||||
pub(crate) cx: Context,
|
||||
}
|
||||
|
||||
/// A queue of threads blocked on channel operations.
|
||||
///
|
||||
/// This data structure is used by threads to register blocking operations and get woken up once
|
||||
/// an operation becomes ready.
|
||||
pub(crate) struct Waker {
|
||||
/// A list of select operations.
|
||||
selectors: Vec<Entry>,
|
||||
|
||||
/// A list of operations waiting to be ready.
|
||||
observers: Vec<Entry>,
|
||||
}
|
||||
|
||||
impl Waker {
|
||||
/// Creates a new `Waker`.
|
||||
#[inline]
|
||||
pub(crate) fn new() -> Self {
|
||||
Waker { selectors: Vec::new(), observers: Vec::new() }
|
||||
}
|
||||
|
||||
/// Registers a select operation.
|
||||
#[inline]
|
||||
pub(crate) fn register(&mut self, oper: Operation, cx: &Context) {
|
||||
self.register_with_packet(oper, ptr::null_mut(), cx);
|
||||
}
|
||||
|
||||
/// Registers a select operation and a packet.
|
||||
#[inline]
|
||||
pub(crate) fn register_with_packet(&mut self, oper: Operation, packet: *mut (), cx: &Context) {
|
||||
self.selectors.push(Entry { oper, packet, cx: cx.clone() });
|
||||
}
|
||||
|
||||
/// Unregisters a select operation.
|
||||
#[inline]
|
||||
pub(crate) fn unregister(&mut self, oper: Operation) -> Option<Entry> {
|
||||
if let Some((i, _)) =
|
||||
self.selectors.iter().enumerate().find(|&(_, entry)| entry.oper == oper)
|
||||
{
|
||||
let entry = self.selectors.remove(i);
|
||||
Some(entry)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to find another thread's entry, select the operation, and wake it up.
|
||||
#[inline]
|
||||
pub(crate) fn try_select(&mut self) -> Option<Entry> {
|
||||
if self.selectors.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let thread_id = current_thread_id();
|
||||
|
||||
self.selectors
|
||||
.iter()
|
||||
.position(|selector| {
|
||||
// Does the entry belong to a different thread?
|
||||
selector.cx.thread_id() != thread_id
|
||||
&& selector // Try selecting this operation.
|
||||
.cx
|
||||
.try_select(Selected::Operation(selector.oper))
|
||||
.is_ok()
|
||||
&& {
|
||||
// Provide the packet.
|
||||
selector.cx.store_packet(selector.packet);
|
||||
// Wake the thread up.
|
||||
selector.cx.unpark();
|
||||
true
|
||||
}
|
||||
})
|
||||
// Remove the entry from the queue to keep it clean and improve
|
||||
// performance.
|
||||
.map(|pos| self.selectors.remove(pos))
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifies all operations waiting to be ready.
|
||||
#[inline]
|
||||
pub(crate) fn notify(&mut self) {
|
||||
for entry in self.observers.drain(..) {
|
||||
if entry.cx.try_select(Selected::Operation(entry.oper)).is_ok() {
|
||||
entry.cx.unpark();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifies all registered operations that the channel is disconnected.
|
||||
#[inline]
|
||||
pub(crate) fn disconnect(&mut self) {
|
||||
for entry in self.selectors.iter() {
|
||||
if entry.cx.try_select(Selected::Disconnected).is_ok() {
|
||||
// Wake the thread up.
|
||||
//
|
||||
// Here we don't remove the entry from the queue. Registered threads must
|
||||
// unregister from the waker by themselves. They might also want to recover the
|
||||
// packet value and destroy it, if necessary.
|
||||
entry.cx.unpark();
|
||||
}
|
||||
}
|
||||
|
||||
self.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Waker {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
debug_assert_eq!(self.selectors.len(), 0);
|
||||
debug_assert_eq!(self.observers.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// A waker that can be shared among threads without locking.
|
||||
///
|
||||
/// This is a simple wrapper around `Waker` that internally uses a mutex for synchronization.
|
||||
pub(crate) struct SyncWaker {
|
||||
/// The inner `Waker`.
|
||||
inner: Mutex<Waker>,
|
||||
|
||||
/// `true` if the waker is empty.
|
||||
is_empty: Atomic<bool>,
|
||||
}
|
||||
|
||||
impl SyncWaker {
|
||||
/// Creates a new `SyncWaker`.
|
||||
#[inline]
|
||||
pub(crate) fn new() -> Self {
|
||||
SyncWaker { inner: Mutex::new(Waker::new()), is_empty: AtomicBool::new(true) }
|
||||
}
|
||||
|
||||
/// Registers the current thread with an operation.
|
||||
#[inline]
|
||||
pub(crate) fn register(&self, oper: Operation, cx: &Context) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.register(oper, cx);
|
||||
self.is_empty
|
||||
.store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Unregisters an operation previously registered by the current thread.
|
||||
#[inline]
|
||||
pub(crate) fn unregister(&self, oper: Operation) -> Option<Entry> {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let entry = inner.unregister(oper);
|
||||
self.is_empty
|
||||
.store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
|
||||
entry
|
||||
}
|
||||
|
||||
/// Attempts to find one thread (not the current one), select its operation, and wake it up.
|
||||
#[inline]
|
||||
pub(crate) fn notify(&self) {
|
||||
if !self.is_empty.load(Ordering::SeqCst) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
if !self.is_empty.load(Ordering::SeqCst) {
|
||||
inner.try_select();
|
||||
inner.notify();
|
||||
self.is_empty.store(
|
||||
inner.selectors.is_empty() && inner.observers.is_empty(),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifies all threads that the channel is disconnected.
|
||||
#[inline]
|
||||
pub(crate) fn disconnect(&self) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.disconnect();
|
||||
self.is_empty
|
||||
.store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SyncWaker {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
debug_assert!(self.is_empty.load(Ordering::SeqCst));
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a unique id for the current thread.
|
||||
#[inline]
|
||||
pub fn current_thread_id() -> usize {
|
||||
// `u8` is not drop so this variable will be available during thread destruction,
|
||||
// whereas `thread::current()` would not be
|
||||
thread_local! { static DUMMY: u8 = const { 0 } }
|
||||
DUMMY.with(|x| (x as *const u8).addr())
|
||||
}
|
||||
319
crates/std/src/sync/mpmc/zero.rs
Normal file
319
crates/std/src/sync/mpmc/zero.rs
Normal file
@@ -0,0 +1,319 @@
|
||||
//! Zero-capacity channel.
|
||||
//!
|
||||
//! This kind of channel is also known as *rendezvous* channel.
|
||||
|
||||
use super::context::Context;
|
||||
use super::error::*;
|
||||
use super::select::{Operation, Selected, Token};
|
||||
use super::utils::Backoff;
|
||||
use super::waker::Waker;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::sync::Mutex;
|
||||
use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
|
||||
use crate::time::Instant;
|
||||
use crate::{fmt, ptr};
|
||||
|
||||
/// A pointer to a packet.
|
||||
pub(crate) struct ZeroToken(*mut ());
|
||||
|
||||
impl Default for ZeroToken {
|
||||
fn default() -> Self {
|
||||
Self(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ZeroToken {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&(self.0 as usize), f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A slot for passing one message from a sender to a receiver.
|
||||
struct Packet<T> {
|
||||
/// Equals `true` if the packet is allocated on the stack.
|
||||
on_stack: bool,
|
||||
|
||||
/// Equals `true` once the packet is ready for reading or writing.
|
||||
ready: Atomic<bool>,
|
||||
|
||||
/// The message.
|
||||
msg: UnsafeCell<Option<T>>,
|
||||
}
|
||||
|
||||
impl<T> Packet<T> {
|
||||
/// Creates an empty packet on the stack.
|
||||
fn empty_on_stack() -> Packet<T> {
|
||||
Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(None) }
|
||||
}
|
||||
|
||||
/// Creates a packet on the stack, containing a message.
|
||||
fn message_on_stack(msg: T) -> Packet<T> {
|
||||
Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(Some(msg)) }
|
||||
}
|
||||
|
||||
/// Waits until the packet becomes ready for reading or writing.
|
||||
fn wait_ready(&self) {
|
||||
let backoff = Backoff::new();
|
||||
while !self.ready.load(Ordering::Acquire) {
|
||||
backoff.spin_heavy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner representation of a zero-capacity channel.
|
||||
struct Inner {
|
||||
/// Senders waiting to pair up with a receive operation.
|
||||
senders: Waker,
|
||||
|
||||
/// Receivers waiting to pair up with a send operation.
|
||||
receivers: Waker,
|
||||
|
||||
/// Equals `true` when the channel is disconnected.
|
||||
is_disconnected: bool,
|
||||
}
|
||||
|
||||
/// Zero-capacity channel.
|
||||
pub(crate) struct Channel<T> {
|
||||
/// Inner representation of the channel.
|
||||
inner: Mutex<Inner>,
|
||||
|
||||
/// Indicates that dropping a `Channel<T>` may drop values of type `T`.
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Channel<T> {
|
||||
/// Constructs a new zero-capacity channel.
|
||||
pub(crate) fn new() -> Self {
|
||||
Channel {
|
||||
inner: Mutex::new(Inner {
|
||||
senders: Waker::new(),
|
||||
receivers: Waker::new(),
|
||||
is_disconnected: false,
|
||||
}),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a message into the packet.
|
||||
pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
|
||||
// If there is no packet, the channel is disconnected.
|
||||
if token.zero.0.is_null() {
|
||||
return Err(msg);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let packet = &*(token.zero.0 as *const Packet<T>);
|
||||
packet.msg.get().write(Some(msg));
|
||||
packet.ready.store(true, Ordering::Release);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads a message from the packet.
|
||||
pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
|
||||
// If there is no packet, the channel is disconnected.
|
||||
if token.zero.0.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let packet = unsafe { &*(token.zero.0 as *const Packet<T>) };
|
||||
|
||||
if packet.on_stack {
|
||||
// The message has been in the packet from the beginning, so there is no need to wait
|
||||
// for it. However, after reading the message, we need to set `ready` to `true` in
|
||||
// order to signal that the packet can be destroyed.
|
||||
let msg = unsafe { packet.msg.get().replace(None) }.unwrap();
|
||||
packet.ready.store(true, Ordering::Release);
|
||||
Ok(msg)
|
||||
} else {
|
||||
// Wait until the message becomes available, then read it and destroy the
|
||||
// heap-allocated packet.
|
||||
packet.wait_ready();
|
||||
unsafe {
|
||||
let msg = packet.msg.get().replace(None).unwrap();
|
||||
drop(Box::from_raw(token.zero.0 as *mut Packet<T>));
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to send a message into the channel.
|
||||
pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
|
||||
let token = &mut Token::default();
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
// If there's a waiting receiver, pair up with it.
|
||||
if let Some(operation) = inner.receivers.try_select() {
|
||||
token.zero.0 = operation.packet;
|
||||
drop(inner);
|
||||
unsafe {
|
||||
self.write(token, msg).ok().unwrap();
|
||||
}
|
||||
Ok(())
|
||||
} else if inner.is_disconnected {
|
||||
Err(TrySendError::Disconnected(msg))
|
||||
} else {
|
||||
Err(TrySendError::Full(msg))
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a message into the channel.
|
||||
pub(crate) fn send(
|
||||
&self,
|
||||
msg: T,
|
||||
deadline: Option<Instant>,
|
||||
) -> Result<(), SendTimeoutError<T>> {
|
||||
let token = &mut Token::default();
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
// If there's a waiting receiver, pair up with it.
|
||||
if let Some(operation) = inner.receivers.try_select() {
|
||||
token.zero.0 = operation.packet;
|
||||
drop(inner);
|
||||
unsafe {
|
||||
self.write(token, msg).ok().unwrap();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if inner.is_disconnected {
|
||||
return Err(SendTimeoutError::Disconnected(msg));
|
||||
}
|
||||
|
||||
Context::with(|cx| {
|
||||
// Prepare for blocking until a receiver wakes us up.
|
||||
let oper = Operation::hook(token);
|
||||
let mut packet = Packet::<T>::message_on_stack(msg);
|
||||
inner.senders.register_with_packet(oper, (&raw mut packet) as *mut (), cx);
|
||||
inner.receivers.notify();
|
||||
drop(inner);
|
||||
|
||||
// Block the current thread.
|
||||
// SAFETY: the context belongs to the current thread.
|
||||
let sel = unsafe { cx.wait_until(deadline) };
|
||||
|
||||
match sel {
|
||||
Selected::Waiting => unreachable!(),
|
||||
Selected::Aborted => {
|
||||
self.inner.lock().unwrap().senders.unregister(oper).unwrap();
|
||||
let msg = unsafe { packet.msg.get().replace(None).unwrap() };
|
||||
Err(SendTimeoutError::Timeout(msg))
|
||||
}
|
||||
Selected::Disconnected => {
|
||||
self.inner.lock().unwrap().senders.unregister(oper).unwrap();
|
||||
let msg = unsafe { packet.msg.get().replace(None).unwrap() };
|
||||
Err(SendTimeoutError::Disconnected(msg))
|
||||
}
|
||||
Selected::Operation(_) => {
|
||||
// Wait until the message is read, then drop the packet.
|
||||
packet.wait_ready();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Attempts to receive a message without blocking.
|
||||
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
|
||||
let token = &mut Token::default();
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
// If there's a waiting sender, pair up with it.
|
||||
if let Some(operation) = inner.senders.try_select() {
|
||||
token.zero.0 = operation.packet;
|
||||
drop(inner);
|
||||
unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
|
||||
} else if inner.is_disconnected {
|
||||
Err(TryRecvError::Disconnected)
|
||||
} else {
|
||||
Err(TryRecvError::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives a message from the channel.
|
||||
pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
|
||||
let token = &mut Token::default();
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
// If there's a waiting sender, pair up with it.
|
||||
if let Some(operation) = inner.senders.try_select() {
|
||||
token.zero.0 = operation.packet;
|
||||
drop(inner);
|
||||
unsafe {
|
||||
return self.read(token).map_err(|_| RecvTimeoutError::Disconnected);
|
||||
}
|
||||
}
|
||||
|
||||
if inner.is_disconnected {
|
||||
return Err(RecvTimeoutError::Disconnected);
|
||||
}
|
||||
|
||||
Context::with(|cx| {
|
||||
// Prepare for blocking until a sender wakes us up.
|
||||
let oper = Operation::hook(token);
|
||||
let mut packet = Packet::<T>::empty_on_stack();
|
||||
inner.receivers.register_with_packet(oper, (&raw mut packet) as *mut (), cx);
|
||||
inner.senders.notify();
|
||||
drop(inner);
|
||||
|
||||
// Block the current thread.
|
||||
// SAFETY: the context belongs to the current thread.
|
||||
let sel = unsafe { cx.wait_until(deadline) };
|
||||
|
||||
match sel {
|
||||
Selected::Waiting => unreachable!(),
|
||||
Selected::Aborted => {
|
||||
self.inner.lock().unwrap().receivers.unregister(oper).unwrap();
|
||||
Err(RecvTimeoutError::Timeout)
|
||||
}
|
||||
Selected::Disconnected => {
|
||||
self.inner.lock().unwrap().receivers.unregister(oper).unwrap();
|
||||
Err(RecvTimeoutError::Disconnected)
|
||||
}
|
||||
Selected::Operation(_) => {
|
||||
// Wait until the message is provided, then read it.
|
||||
packet.wait_ready();
|
||||
unsafe { Ok(packet.msg.get().replace(None).unwrap()) }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Disconnects the channel and wakes up all blocked senders and receivers.
|
||||
///
|
||||
/// Returns `true` if this call disconnected the channel.
|
||||
pub(crate) fn disconnect(&self) -> bool {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
if !inner.is_disconnected {
|
||||
inner.is_disconnected = true;
|
||||
inner.senders.disconnect();
|
||||
inner.receivers.disconnect();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current number of messages inside the channel.
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
/// Returns the capacity of the channel.
|
||||
#[allow(clippy::unnecessary_wraps)] // This is intentional.
|
||||
pub(crate) fn capacity(&self) -> Option<usize> {
|
||||
Some(0)
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is empty.
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is full.
|
||||
pub(crate) fn is_full(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
1214
crates/std/src/sync/mpsc.rs
Normal file
1214
crates/std/src/sync/mpsc.rs
Normal file
File diff suppressed because it is too large
Load Diff
45
crates/std/src/sync/nonpoison.rs
Normal file
45
crates/std/src/sync/nonpoison.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
//! Non-poisoning synchronous locks.
|
||||
//!
|
||||
//! The difference from the locks in the [`poison`] module is that the locks in this module will not
|
||||
//! become poisoned when a thread panics while holding a guard.
|
||||
//!
|
||||
//! [`poison`]: super::poison
|
||||
|
||||
use crate::fmt;
|
||||
|
||||
/// A type alias for the result of a nonblocking locking method.
|
||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
||||
pub type TryLockResult<Guard> = Result<Guard, WouldBlock>;
|
||||
|
||||
/// A lock could not be acquired at this time because the operation would otherwise block.
|
||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
||||
pub struct WouldBlock;
|
||||
|
||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
||||
impl fmt::Debug for WouldBlock {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"WouldBlock".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
||||
impl fmt::Display for WouldBlock {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"try_lock failed because the operation would block".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub use self::condvar::Condvar;
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub use self::mutex::MappedMutexGuard;
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub use self::mutex::{Mutex, MutexGuard};
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard};
|
||||
#[unstable(feature = "nonpoison_rwlock", issue = "134645")]
|
||||
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
mod condvar;
|
||||
mod mutex;
|
||||
mod rwlock;
|
||||
444
crates/std/src/sync/nonpoison/condvar.rs
Normal file
444
crates/std/src/sync/nonpoison/condvar.rs
Normal file
@@ -0,0 +1,444 @@
|
||||
use crate::fmt;
|
||||
use crate::ops::DerefMut;
|
||||
use crate::sync::WaitTimeoutResult;
|
||||
use crate::sync::nonpoison::{MutexGuard, mutex};
|
||||
use crate::sys::sync as sys;
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
/// A Condition Variable
|
||||
///
|
||||
/// For more information about condition variables, check out the documentation for the poisoning
|
||||
/// variant of this type at [`poison::Condvar`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Note that this `Condvar` does **not** propagate information about threads that panic while
|
||||
/// holding a lock. If you need this functionality, see [`poison::Mutex`] and [`poison::Condvar`].
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// // Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// while !*started {
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`poison::Mutex`]: crate::sync::poison::Mutex
|
||||
/// [`poison::Condvar`]: crate::sync::poison::Condvar
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub struct Condvar {
|
||||
inner: sys::Condvar,
|
||||
}
|
||||
|
||||
impl Condvar {
|
||||
/// Creates a new condition variable which is ready to be waited on and
|
||||
/// notified.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Condvar;
|
||||
///
|
||||
/// let condvar = Condvar::new();
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn new() -> Condvar {
|
||||
Condvar { inner: sys::Condvar::new() }
|
||||
}
|
||||
|
||||
/// Blocks the current thread until this condition variable receives a
|
||||
/// notification.
|
||||
///
|
||||
/// This function will atomically unlock the mutex specified (represented by
|
||||
/// `guard`) and block the current thread. This means that any calls
|
||||
/// to [`notify_one`] or [`notify_all`] which happen logically after the
|
||||
/// mutex is unlocked are candidates to wake this thread up. When this
|
||||
/// function call returns, the lock specified will have been re-acquired.
|
||||
///
|
||||
/// Note that this function is susceptible to spurious wakeups. Condition
|
||||
/// variables normally have a boolean predicate associated with them, and
|
||||
/// the predicate must always be checked each time this function returns to
|
||||
/// protect against spurious wakeups.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may [`panic!`] if it is used with more than one mutex
|
||||
/// over time.
|
||||
///
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait<T>(&self, guard: &mut MutexGuard<'_, T>) {
|
||||
unsafe {
|
||||
let lock = mutex::guard_lock(guard);
|
||||
self.inner.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until the provided condition becomes false.
|
||||
///
|
||||
/// `condition` is checked immediately; if not met (returns `true`), this
|
||||
/// will [`wait`] for the next notification then check again. This repeats
|
||||
/// until `condition` returns `false`, in which case this function returns.
|
||||
///
|
||||
/// This function will atomically unlock the mutex specified (represented by
|
||||
/// `guard`) and block the current thread. This means that any calls
|
||||
/// to [`notify_one`] or [`notify_all`] which happen logically after the
|
||||
/// mutex is unlocked are candidates to wake this thread up. When this
|
||||
/// function call returns, the lock specified will have been re-acquired.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut pending = lock.lock();
|
||||
/// *pending = false;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// // As long as the value inside the `Mutex<bool>` is `true`, we wait.
|
||||
/// let mut guard = lock.lock();
|
||||
/// cvar.wait_while(&mut guard, |pending| { *pending });
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait_while<T, F>(&self, guard: &mut MutexGuard<'_, T>, mut condition: F)
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
while condition(guard.deref_mut()) {
|
||||
self.wait(guard);
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait`] except that
|
||||
/// the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that might not cause the maximum
|
||||
/// amount of time waited to be precisely `dur`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time. This function is susceptible to spurious wakeups.
|
||||
/// Condition variables normally have a boolean predicate associated with
|
||||
/// them, and the predicate must always be checked each time this function
|
||||
/// returns to protect against spurious wakeups. Furthermore, since the timeout
|
||||
/// is given relative to the moment this function is called, it needs to be adjusted
|
||||
/// when this function is called in a loop. The [`wait_timeout_while`] method
|
||||
/// lets you wait with a timeout while a predicate is true, taking care of all these concerns.
|
||||
///
|
||||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed.
|
||||
///
|
||||
/// Like [`wait`], the lock specified will have been re-acquired when this function
|
||||
/// returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`wait_timeout_while`]: Self::wait_timeout_while
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // wait for the thread to start up
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// // as long as the value inside the `Mutex<bool>` is `false`, we wait
|
||||
/// loop {
|
||||
/// let result = cvar.wait_timeout(&mut started, Duration::from_millis(10));
|
||||
/// // 10 milliseconds have passed, or maybe the value changed!
|
||||
/// if *started == true {
|
||||
/// // We received the notification and the value has been updated, we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait_timeout<T>(
|
||||
&self,
|
||||
guard: &mut MutexGuard<'_, T>,
|
||||
dur: Duration,
|
||||
) -> WaitTimeoutResult {
|
||||
let success = unsafe {
|
||||
let lock = mutex::guard_lock(guard);
|
||||
self.inner.wait_timeout(lock, dur)
|
||||
};
|
||||
WaitTimeoutResult(!success)
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait_while`] except
|
||||
/// that the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that might not cause the maximum
|
||||
/// amount of time waited to be precisely `dur`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time.
|
||||
///
|
||||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed without the condition being met.
|
||||
///
|
||||
/// Like [`wait_while`], the lock specified will have been re-acquired when this
|
||||
/// function returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait_while`]: Self::wait_while
|
||||
/// [`wait_timeout`]: Self::wait_timeout
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut pending = lock.lock();
|
||||
/// *pending = false;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // wait for the thread to start up
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut guard = lock.lock();
|
||||
/// let result = cvar.wait_timeout_while(
|
||||
/// &mut guard,
|
||||
/// Duration::from_millis(100),
|
||||
/// |&mut pending| pending,
|
||||
/// );
|
||||
/// if result.timed_out() {
|
||||
/// // timed-out without the condition ever evaluating to false.
|
||||
/// }
|
||||
/// // access the locked mutex via guard
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait_timeout_while<T, F>(
|
||||
&self,
|
||||
guard: &mut MutexGuard<'_, T>,
|
||||
dur: Duration,
|
||||
mut condition: F,
|
||||
) -> WaitTimeoutResult
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
let start = Instant::now();
|
||||
|
||||
while condition(guard.deref_mut()) {
|
||||
let timeout = match dur.checked_sub(start.elapsed()) {
|
||||
Some(timeout) => timeout,
|
||||
None => return WaitTimeoutResult(true),
|
||||
};
|
||||
|
||||
self.wait_timeout(guard, timeout);
|
||||
}
|
||||
|
||||
WaitTimeoutResult(false)
|
||||
}
|
||||
|
||||
/// Wakes up one blocked thread on this condvar.
|
||||
///
|
||||
/// If there is a blocked thread on this condition variable, then it will
|
||||
/// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to
|
||||
/// `notify_one` are not buffered in any way.
|
||||
///
|
||||
/// To wake up all threads, see [`notify_all`].
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`wait_timeout`]: Self::wait_timeout
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn notify_one(&self) {
|
||||
self.inner.notify_one()
|
||||
}
|
||||
|
||||
/// Wakes up all blocked threads on this condvar.
|
||||
///
|
||||
/// This method will ensure that any current waiters on the condition
|
||||
/// variable are awoken. Calls to `notify_all()` are not buffered in any
|
||||
/// way.
|
||||
///
|
||||
/// To wake up only one thread, see [`notify_one`].
|
||||
///
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_all();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn notify_all(&self) {
|
||||
self.inner.notify_all()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
impl fmt::Debug for Condvar {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Condvar").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
impl Default for Condvar {
|
||||
/// Creates a `Condvar` which is ready to be waited on and notified.
|
||||
fn default() -> Condvar {
|
||||
Condvar::new()
|
||||
}
|
||||
}
|
||||
649
crates/std/src/sync/nonpoison/mutex.rs
Normal file
649
crates/std/src/sync/nonpoison/mutex.rs
Normal file
@@ -0,0 +1,649 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::nonpoison::{TryLockResult, WouldBlock};
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A mutual exclusion primitive useful for protecting shared data that does not keep track of
|
||||
/// lock poisoning.
|
||||
///
|
||||
/// For more information about mutexes, check out the documentation for the poisoning variant of
|
||||
/// this lock at [`poison::Mutex`].
|
||||
///
|
||||
/// [`poison::Mutex`]: crate::sync::poison::Mutex
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Note that this `Mutex` does **not** propagate threads that panic while holding the lock via
|
||||
/// poisoning. If you need this functionality, see [`poison::Mutex`].
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
///
|
||||
/// use std::thread;
|
||||
/// use std::sync::{Arc, nonpoison::Mutex};
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(0u32));
|
||||
/// let mut handles = Vec::new();
|
||||
///
|
||||
/// for n in 0..10 {
|
||||
/// let m = Arc::clone(&mutex);
|
||||
/// let handle = thread::spawn(move || {
|
||||
/// let mut guard = m.lock();
|
||||
/// *guard += 1;
|
||||
/// panic!("panic from thread {n} {guard}")
|
||||
/// });
|
||||
/// handles.push(handle);
|
||||
/// }
|
||||
///
|
||||
/// for h in handles {
|
||||
/// let _ = h.join();
|
||||
/// }
|
||||
///
|
||||
/// println!("Finished, locked {} times", mutex.lock());
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutex")]
|
||||
pub struct Mutex<T: ?Sized> {
|
||||
inner: sys::Mutex,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire
|
||||
/// the owned `T` from the `Mutex` via [`into_inner`].
|
||||
///
|
||||
/// [`into_inner`]: Mutex::into_inner
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
|
||||
|
||||
/// `T` must be `Send` for [`Mutex`] to be `Sync`.
|
||||
/// This ensures that the protected data can be accessed safely from multiple threads
|
||||
/// without causing data races or other unsafe behavior.
|
||||
///
|
||||
/// [`Mutex<T>`] provides mutable access to `T` to one thread at a time. However, it's essential
|
||||
/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in
|
||||
/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer,
|
||||
/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap
|
||||
/// allocation with a non-atomic reference count. If we were to use `Mutex<Rc<_>>`, it would
|
||||
/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable
|
||||
/// to potential data races.
|
||||
///
|
||||
/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available
|
||||
/// to one thread at a time if `T` is not `Sync`.
|
||||
///
|
||||
/// [`Rc`]: crate::rc::Rc
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
|
||||
|
||||
/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
|
||||
/// dropped (falls out of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// [`Deref`] and [`DerefMut`] implementations.
|
||||
///
|
||||
/// This structure is created by the [`lock`] and [`try_lock`] methods on
|
||||
/// [`Mutex`].
|
||||
///
|
||||
/// [`lock`]: Mutex::lock
|
||||
/// [`try_lock`]: Mutex::try_lock
|
||||
#[must_use = "if unused the Mutex will immediately unlock"]
|
||||
#[must_not_suspend = "holding a MutexGuard across suspend \
|
||||
points can cause deadlocks, delays, \
|
||||
and cause Futures to not implement `Send`"]
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
#[clippy::has_significant_drop]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutexGuard")]
|
||||
pub struct MutexGuard<'a, T: ?Sized + 'a> {
|
||||
lock: &'a Mutex<T>,
|
||||
}
|
||||
|
||||
/// A [`MutexGuard`] is not `Send` to maximize platform portability.
|
||||
///
|
||||
/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to
|
||||
/// release mutex locks on the same thread they were acquired.
|
||||
/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from
|
||||
/// another thread.
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
|
||||
|
||||
/// `T` must be `Sync` for a [`MutexGuard<T>`] to be `Sync`
|
||||
/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`).
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
|
||||
|
||||
/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a
|
||||
/// subfield of the protected data. When this structure is dropped (falls out
|
||||
/// of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The main difference between `MappedMutexGuard` and [`MutexGuard`] is that the
|
||||
/// former cannot be used with [`Condvar`], since that could introduce soundness issues if the
|
||||
/// locked object is modified by another thread while the `Mutex` is unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// [`Deref`] and [`DerefMut`] implementations.
|
||||
///
|
||||
/// This structure is created by the [`map`] and [`filter_map`] methods on
|
||||
/// [`MutexGuard`].
|
||||
///
|
||||
/// [`map`]: MutexGuard::map
|
||||
/// [`filter_map`]: MutexGuard::filter_map
|
||||
/// [`Condvar`]: crate::sync::nonpoison::Condvar
|
||||
#[must_use = "if unused the Mutex will immediately unlock"]
|
||||
#[must_not_suspend = "holding a MappedMutexGuard across suspend \
|
||||
points can cause deadlocks, delays, \
|
||||
and cause Futures to not implement `Send`"]
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
#[clippy::has_significant_drop]
|
||||
pub struct MappedMutexGuard<'a, T: ?Sized + 'a> {
|
||||
// NB: we use a pointer instead of `&'a mut T` to avoid `noalias` violations, because a
|
||||
// `MappedMutexGuard` argument doesn't hold uniqueness for its whole scope, only until it drops.
|
||||
// `NonNull` is covariant over `T`, so we add a `PhantomData<&'a mut T>` field
|
||||
// below for the correct variance over `T` (invariance).
|
||||
data: NonNull<T>,
|
||||
inner: &'a sys::Mutex,
|
||||
_variance: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> !Send for MappedMutexGuard<'_, T> {}
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
unsafe impl<T: ?Sized + Sync> Sync for MappedMutexGuard<'_, T> {}
|
||||
|
||||
impl<T> Mutex<T> {
|
||||
/// Creates a new mutex in an unlocked state ready for use.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mutex = Mutex::new(0);
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
#[inline]
|
||||
pub const fn new(t: T) -> Mutex<T> {
|
||||
Mutex { inner: sys::Mutex::new(), data: UnsafeCell::new(t) }
|
||||
}
|
||||
|
||||
/// Returns the contained value by cloning it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.get_cloned(), 7);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn get_cloned(&self) -> T
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
self.lock().clone()
|
||||
}
|
||||
|
||||
/// Sets the contained value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.get_cloned(), 7);
|
||||
/// mutex.set(11);
|
||||
/// assert_eq!(mutex.get_cloned(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn set(&self, value: T) {
|
||||
if mem::needs_drop::<T>() {
|
||||
// If the contained value has a non-trivial destructor, we
|
||||
// call that destructor after the lock has been released.
|
||||
drop(self.replace(value))
|
||||
} else {
|
||||
*self.lock() = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the contained value with `value`, and returns the old contained value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.replace(11), 7);
|
||||
/// assert_eq!(mutex.get_cloned(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn replace(&self, value: T) -> T {
|
||||
let mut guard = self.lock();
|
||||
mem::replace(&mut *guard, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Mutex<T> {
|
||||
/// Acquires a mutex, blocking the current thread until it is able to do so.
|
||||
///
|
||||
/// This function will block the local thread until it is available to acquire
|
||||
/// the mutex. Upon returning, the thread is the only thread with the lock
|
||||
/// held. An RAII guard is returned to allow scoped unlock of the lock. When
|
||||
/// the guard goes out of scope, the mutex will be unlocked.
|
||||
///
|
||||
/// The exact behavior on locking a mutex in the thread which already holds
|
||||
/// the lock is left unspecified. However, this function will not return on
|
||||
/// the second call (it might panic or deadlock, for example).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function might panic when called if the lock is already held by
|
||||
/// the current thread.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
///
|
||||
/// use std::sync::{Arc, nonpoison::Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(0));
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// *c_mutex.lock() = 10;
|
||||
/// }).join().expect("thread::spawn failed");
|
||||
/// assert_eq!(*mutex.lock(), 10);
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn lock(&self) -> MutexGuard<'_, T> {
|
||||
unsafe {
|
||||
self.inner.lock();
|
||||
MutexGuard::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to acquire this lock.
|
||||
///
|
||||
/// This function does not block. If the lock could not be acquired at this time, then
|
||||
/// [`WouldBlock`] is returned. Otherwise, an RAII guard is returned.
|
||||
///
|
||||
/// The lock will be unlocked when the guard is dropped.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the mutex could not be acquired because it is already locked, then this call will return
|
||||
/// the [`WouldBlock`] error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(0));
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let mut lock = c_mutex.try_lock();
|
||||
/// if let Ok(ref mut mutex) = lock {
|
||||
/// **mutex = 10;
|
||||
/// } else {
|
||||
/// println!("try_lock failed");
|
||||
/// }
|
||||
/// }).join().expect("thread::spawn failed");
|
||||
/// assert_eq!(*mutex.lock().unwrap(), 10);
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
|
||||
unsafe { if self.inner.try_lock() { Ok(MutexGuard::new(self)) } else { Err(WouldBlock) } }
|
||||
}
|
||||
|
||||
/// Consumes this mutex, returning the underlying data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mutex = Mutex::new(0);
|
||||
/// assert_eq!(mutex.into_inner(), 0);
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn into_inner(self) -> T
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
self.data.into_inner()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying data.
|
||||
///
|
||||
/// Since this call borrows the `Mutex` mutably, no actual locking needs to
|
||||
/// take place -- the mutable borrow statically guarantees no locks exist.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(0);
|
||||
/// *mutex.get_mut() = 10;
|
||||
/// assert_eq!(*mutex.lock(), 10);
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.data.get_mut()
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the underlying data.
|
||||
///
|
||||
/// The returned pointer is always non-null and properly aligned, but it is
|
||||
/// the user's responsibility to ensure that any reads and writes through it
|
||||
/// are properly synchronized to avoid data races, and that it is not read
|
||||
/// or written through after the mutex is dropped.
|
||||
#[unstable(feature = "mutex_data_ptr", issue = "140368")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub const fn data_ptr(&self) -> *mut T {
|
||||
self.data.get()
|
||||
}
|
||||
|
||||
/// Acquires the mutex and provides mutable access to the underlying data by passing
|
||||
/// a mutable reference to the given closure.
|
||||
///
|
||||
/// This method acquires the lock, calls the provided closure with a mutable reference
|
||||
/// to the data, and returns the result of the closure. The lock is released after
|
||||
/// the closure completes, even if it panics.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors, nonpoison_mutex)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mutex = Mutex::new(2);
|
||||
///
|
||||
/// let result = mutex.with_mut(|data| {
|
||||
/// *data += 3;
|
||||
///
|
||||
/// *data + 5
|
||||
/// });
|
||||
///
|
||||
/// assert_eq!(*mutex.lock(), 5);
|
||||
/// assert_eq!(result, 10);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn with_mut<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut T) -> R,
|
||||
{
|
||||
f(&mut self.lock())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T> From<T> for Mutex<T> {
|
||||
/// Creates a new mutex in an unlocked state ready for use.
|
||||
/// This is equivalent to [`Mutex::new`].
|
||||
fn from(t: T) -> Self {
|
||||
Mutex::new(t)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: Default> Default for Mutex<T> {
|
||||
/// Creates a `Mutex<T>`, with the `Default` value for T.
|
||||
fn default() -> Mutex<T> {
|
||||
Mutex::new(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_struct("Mutex");
|
||||
match self.try_lock() {
|
||||
Ok(guard) => {
|
||||
d.field("data", &&*guard);
|
||||
}
|
||||
Err(WouldBlock) => {
|
||||
d.field("data", &"<locked>");
|
||||
}
|
||||
}
|
||||
d.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
|
||||
unsafe fn new(lock: &'mutex Mutex<T>) -> MutexGuard<'mutex, T> {
|
||||
return MutexGuard { lock };
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.lock.inner.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// For use in [`nonpoison::condvar`](super::condvar).
|
||||
pub(super) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
|
||||
&guard.lock.inner
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> MutexGuard<'a, T> {
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
|
||||
/// an enum variant.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MutexGuard::map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn map<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U>
|
||||
where
|
||||
F: FnOnce(&mut T) -> &mut U,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() }));
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData }
|
||||
}
|
||||
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The
|
||||
/// original guard is returned as an `Err(...)` if the closure returns
|
||||
/// `None`.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MutexGuard::filter_map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
|
||||
where
|
||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
match f(unsafe { &mut *orig.lock.data.get() }) {
|
||||
Some(data) => {
|
||||
let data = NonNull::from(data);
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
Ok(MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData })
|
||||
}
|
||||
None => Err(orig),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> Deref for MappedMutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { self.data.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> DerefMut for MappedMutexGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { self.data.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> Drop for MappedMutexGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.inner.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedMutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for MappedMutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
|
||||
/// an enum variant.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MappedMutexGuard::map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn map<U, F>(mut orig: Self, f: F) -> MappedMutexGuard<'a, U>
|
||||
where
|
||||
F: FnOnce(&mut T) -> &mut U,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
let data = NonNull::from(f(unsafe { orig.data.as_mut() }));
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData }
|
||||
}
|
||||
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The
|
||||
/// original guard is returned as an `Err(...)` if the closure returns
|
||||
/// `None`.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MappedMutexGuard::filter_map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn filter_map<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
|
||||
where
|
||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
match f(unsafe { orig.data.as_mut() }) {
|
||||
Some(data) => {
|
||||
let data = NonNull::from(data);
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
Ok(MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData })
|
||||
}
|
||||
None => Err(orig),
|
||||
}
|
||||
}
|
||||
}
|
||||
1140
crates/std/src/sync/nonpoison/rwlock.rs
Normal file
1140
crates/std/src/sync/nonpoison/rwlock.rs
Normal file
File diff suppressed because it is too large
Load Diff
395
crates/std/src/sync/once.rs
Normal file
395
crates/std/src/sync/once.rs
Normal file
@@ -0,0 +1,395 @@
|
||||
//! A "once initialization" primitive
|
||||
//!
|
||||
//! This primitive is meant to be used to run one-time initialization. An
|
||||
//! example use case would be for initializing an FFI library.
|
||||
|
||||
use crate::fmt;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A low-level synchronization primitive for one-time global execution.
|
||||
///
|
||||
/// Previously this was the only "execute once" synchronization in `std`.
|
||||
/// Other libraries implemented novel synchronizing types with `Once`, like
|
||||
/// [`OnceLock<T>`] or [`LazyLock<T, F>`], before those were added to `std`.
|
||||
/// `OnceLock<T>` in particular supersedes `Once` in functionality and should
|
||||
/// be preferred for the common case where the `Once` is associated with data.
|
||||
///
|
||||
/// This type can only be constructed with [`Once::new()`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
///
|
||||
/// static START: Once = Once::new();
|
||||
///
|
||||
/// START.call_once(|| {
|
||||
/// // run initialization here
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// [`OnceLock<T>`]: crate::sync::OnceLock
|
||||
/// [`LazyLock<T, F>`]: crate::sync::LazyLock
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Once {
|
||||
inner: sys::Once,
|
||||
}
|
||||
|
||||
#[stable(feature = "sync_once_unwind_safe", since = "1.59.0")]
|
||||
impl UnwindSafe for Once {}
|
||||
|
||||
#[stable(feature = "sync_once_unwind_safe", since = "1.59.0")]
|
||||
impl RefUnwindSafe for Once {}
|
||||
|
||||
/// State yielded to [`Once::call_once_force()`]’s closure parameter. The state
|
||||
/// can be used to query the poison status of the [`Once`].
|
||||
#[stable(feature = "once_poison", since = "1.51.0")]
|
||||
pub struct OnceState {
|
||||
pub(crate) inner: sys::OnceState,
|
||||
}
|
||||
|
||||
/// Used for the internal implementation of `sys::sync::once` on different platforms and the
|
||||
/// [`LazyLock`](crate::sync::LazyLock) implementation.
|
||||
pub(crate) enum OnceExclusiveState {
|
||||
Incomplete,
|
||||
Poisoned,
|
||||
Complete,
|
||||
}
|
||||
|
||||
/// Initialization value for static [`Once`] values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Once, ONCE_INIT};
|
||||
///
|
||||
/// static START: Once = ONCE_INIT;
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[deprecated(
|
||||
since = "1.38.0",
|
||||
note = "the `Once::new()` function is now preferred",
|
||||
suggestion = "Once::new()"
|
||||
)]
|
||||
pub const ONCE_INIT: Once = Once::new();
|
||||
|
||||
impl Once {
|
||||
/// Creates a new `Once` value.
|
||||
#[inline]
|
||||
#[stable(feature = "once_new", since = "1.2.0")]
|
||||
#[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
|
||||
#[must_use]
|
||||
pub const fn new() -> Once {
|
||||
Once { inner: sys::Once::new() }
|
||||
}
|
||||
|
||||
/// Performs an initialization routine once and only once. The given closure
|
||||
/// will be executed if this is the first time `call_once` has been called,
|
||||
/// and otherwise the routine will *not* be invoked.
|
||||
///
|
||||
/// This method will block the calling thread if another initialization
|
||||
/// routine is currently running.
|
||||
///
|
||||
/// When this function returns, it is guaranteed that some initialization
|
||||
/// has run and completed (it might not be the closure specified). It is also
|
||||
/// guaranteed that any memory writes performed by the executed closure can
|
||||
/// be reliably observed by other threads at this point (there is a
|
||||
/// happens-before relation between the closure and code executing after the
|
||||
/// return).
|
||||
///
|
||||
/// If the given closure recursively invokes `call_once` on the same [`Once`]
|
||||
/// instance, the exact behavior is not specified: allowed outcomes are
|
||||
/// a panic or a deadlock.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
///
|
||||
/// static mut VAL: usize = 0;
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// // Accessing a `static mut` is unsafe much of the time, but if we do so
|
||||
/// // in a synchronized fashion (e.g., write once or read all) then we're
|
||||
/// // good to go!
|
||||
/// //
|
||||
/// // This function will only call `expensive_computation` once, and will
|
||||
/// // otherwise always return the value returned from the first invocation.
|
||||
/// fn get_cached_val() -> usize {
|
||||
/// unsafe {
|
||||
/// INIT.call_once(|| {
|
||||
/// VAL = expensive_computation();
|
||||
/// });
|
||||
/// VAL
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn expensive_computation() -> usize {
|
||||
/// // ...
|
||||
/// # 2
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The closure `f` will only be executed once even if this is called
|
||||
/// concurrently amongst many threads. If that closure panics, however, then
|
||||
/// it will *poison* this [`Once`] instance, causing all future invocations of
|
||||
/// `call_once` to also panic.
|
||||
///
|
||||
/// This is similar to [poisoning with mutexes][poison], but this mechanism
|
||||
/// is guaranteed to never skip panics within `f`.
|
||||
///
|
||||
/// [poison]: struct.Mutex.html#poisoning
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[track_caller]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn call_once<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
// Fast path check
|
||||
if self.inner.is_completed() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut f = Some(f);
|
||||
self.inner.call(false, &mut |_| f.take().unwrap()());
|
||||
}
|
||||
|
||||
/// Performs the same function as [`call_once()`] except ignores poisoning.
|
||||
///
|
||||
/// Unlike [`call_once()`], if this [`Once`] has been poisoned (i.e., a previous
|
||||
/// call to [`call_once()`] or [`call_once_force()`] caused a panic), calling
|
||||
/// [`call_once_force()`] will still invoke the closure `f` and will _not_
|
||||
/// result in an immediate panic. If `f` panics, the [`Once`] will remain
|
||||
/// in a poison state. If `f` does _not_ panic, the [`Once`] will no
|
||||
/// longer be in a poison state and all future calls to [`call_once()`] or
|
||||
/// [`call_once_force()`] will be no-ops.
|
||||
///
|
||||
/// The closure `f` is yielded a [`OnceState`] structure which can be used
|
||||
/// to query the poison status of the [`Once`].
|
||||
///
|
||||
/// [`call_once()`]: Once::call_once
|
||||
/// [`call_once_force()`]: Once::call_once_force
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// // poison the once
|
||||
/// let handle = thread::spawn(|| {
|
||||
/// INIT.call_once(|| panic!());
|
||||
/// });
|
||||
/// assert!(handle.join().is_err());
|
||||
///
|
||||
/// // poisoning propagates
|
||||
/// let handle = thread::spawn(|| {
|
||||
/// INIT.call_once(|| {});
|
||||
/// });
|
||||
/// assert!(handle.join().is_err());
|
||||
///
|
||||
/// // call_once_force will still run and reset the poisoned state
|
||||
/// INIT.call_once_force(|state| {
|
||||
/// assert!(state.is_poisoned());
|
||||
/// });
|
||||
///
|
||||
/// // once any success happens, we stop propagating the poison
|
||||
/// INIT.call_once(|| {});
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_poison", since = "1.51.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn call_once_force<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce(&OnceState),
|
||||
{
|
||||
// Fast path check
|
||||
if self.inner.is_completed() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut f = Some(f);
|
||||
self.inner.call(true, &mut |p| f.take().unwrap()(p));
|
||||
}
|
||||
|
||||
/// Returns `true` if some [`call_once()`] call has completed
|
||||
/// successfully. Specifically, `is_completed` will return false in
|
||||
/// the following situations:
|
||||
/// * [`call_once()`] was not called at all,
|
||||
/// * [`call_once()`] was called, but has not yet completed,
|
||||
/// * the [`Once`] instance is poisoned
|
||||
///
|
||||
/// This function returning `false` does not mean that [`Once`] has not been
|
||||
/// executed. For example, it may have been executed in the time between
|
||||
/// when `is_completed` starts executing and when it returns, in which case
|
||||
/// the `false` return value would be stale (but still permissible).
|
||||
///
|
||||
/// [`call_once()`]: Once::call_once
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
///
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// assert_eq!(INIT.is_completed(), false);
|
||||
/// INIT.call_once(|| {
|
||||
/// assert_eq!(INIT.is_completed(), false);
|
||||
/// });
|
||||
/// assert_eq!(INIT.is_completed(), true);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// assert_eq!(INIT.is_completed(), false);
|
||||
/// let handle = thread::spawn(|| {
|
||||
/// INIT.call_once(|| panic!());
|
||||
/// });
|
||||
/// assert!(handle.join().is_err());
|
||||
/// assert_eq!(INIT.is_completed(), false);
|
||||
/// ```
|
||||
#[stable(feature = "once_is_completed", since = "1.43.0")]
|
||||
#[inline]
|
||||
pub fn is_completed(&self) -> bool {
|
||||
self.inner.is_completed()
|
||||
}
|
||||
|
||||
/// Blocks the current thread until initialization has completed.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::sync::Once;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// static READY: Once = Once::new();
|
||||
///
|
||||
/// let thread = thread::spawn(|| {
|
||||
/// READY.wait();
|
||||
/// println!("everything is ready");
|
||||
/// });
|
||||
///
|
||||
/// READY.call_once(|| println!("performing setup"));
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If this [`Once`] has been poisoned because an initialization closure has
|
||||
/// panicked, this method will also panic. Use [`wait_force`](Self::wait_force)
|
||||
/// if this behavior is not desired.
|
||||
#[stable(feature = "once_wait", since = "1.86.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait(&self) {
|
||||
if !self.inner.is_completed() {
|
||||
self.inner.wait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until initialization has completed, ignoring
|
||||
/// poisoning.
|
||||
///
|
||||
/// If this [`Once`] has been poisoned, this function blocks until it
|
||||
/// becomes completed, unlike [`Once::wait()`], which panics in this case.
|
||||
#[stable(feature = "once_wait", since = "1.86.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait_force(&self) {
|
||||
if !self.inner.is_completed() {
|
||||
self.inner.wait(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current state of the `Once` instance.
|
||||
///
|
||||
/// Since this takes a mutable reference, no initialization can currently
|
||||
/// be running, so the state must be either "incomplete", "poisoned" or
|
||||
/// "complete".
|
||||
#[inline]
|
||||
pub(crate) fn state(&mut self) -> OnceExclusiveState {
|
||||
self.inner.state()
|
||||
}
|
||||
|
||||
/// Sets current state of the `Once` instance.
|
||||
///
|
||||
/// Since this takes a mutable reference, no initialization can currently
|
||||
/// be running, so the state must be either "incomplete", "poisoned" or
|
||||
/// "complete".
|
||||
#[inline]
|
||||
pub(crate) fn set_state(&mut self, new_state: OnceExclusiveState) {
|
||||
self.inner.set_state(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Once {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Once").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl OnceState {
|
||||
/// Returns `true` if the associated [`Once`] was poisoned prior to the
|
||||
/// invocation of the closure passed to [`Once::call_once_force()`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// A poisoned [`Once`]:
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// // poison the once
|
||||
/// let handle = thread::spawn(|| {
|
||||
/// INIT.call_once(|| panic!());
|
||||
/// });
|
||||
/// assert!(handle.join().is_err());
|
||||
///
|
||||
/// INIT.call_once_force(|state| {
|
||||
/// assert!(state.is_poisoned());
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// An unpoisoned [`Once`]:
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
///
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// INIT.call_once_force(|state| {
|
||||
/// assert!(!state.is_poisoned());
|
||||
/// });
|
||||
#[stable(feature = "once_poison", since = "1.51.0")]
|
||||
#[inline]
|
||||
pub fn is_poisoned(&self) -> bool {
|
||||
self.inner.is_poisoned()
|
||||
}
|
||||
|
||||
/// Poison the associated [`Once`] without explicitly panicking.
|
||||
// NOTE: This is currently only exposed for `OnceLock`.
|
||||
#[inline]
|
||||
pub(crate) fn poison(&self) {
|
||||
self.inner.poison();
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for OnceState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OnceState").field("poisoned", &self.is_poisoned()).finish()
|
||||
}
|
||||
}
|
||||
709
crates/std/src/sync/once_lock.rs
Normal file
709
crates/std/src/sync/once_lock.rs
Normal file
@@ -0,0 +1,709 @@
|
||||
use super::once::OnceExclusiveState;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::MaybeUninit;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::Once;
|
||||
|
||||
/// A synchronization primitive which can nominally be written to only once.
|
||||
///
|
||||
/// This type is a thread-safe [`OnceCell`], and can be used in statics.
|
||||
/// In many simple cases, you can use [`LazyLock<T, F>`] instead to get the benefits of this type
|
||||
/// with less effort: `LazyLock<T, F>` "looks like" `&T` because it initializes with `F` on deref!
|
||||
/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock
|
||||
/// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`].
|
||||
///
|
||||
/// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes
|
||||
/// initialized once written.
|
||||
///
|
||||
/// Unlike [`Mutex`](crate::sync::Mutex), `OnceLock` is never poisoned on panic.
|
||||
///
|
||||
/// [`OnceCell`]: crate::cell::OnceCell
|
||||
/// [`LazyLock<T, F>`]: crate::sync::LazyLock
|
||||
/// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Writing to a `OnceLock` from a separate thread:
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// static CELL: OnceLock<usize> = OnceLock::new();
|
||||
///
|
||||
/// // `OnceLock` has not been written to yet.
|
||||
/// assert!(CELL.get().is_none());
|
||||
///
|
||||
/// // Spawn a thread and write to `OnceLock`.
|
||||
/// std::thread::spawn(|| {
|
||||
/// let value = CELL.get_or_init(|| 12345);
|
||||
/// assert_eq!(value, &12345);
|
||||
/// })
|
||||
/// .join()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // `OnceLock` now contains the value.
|
||||
/// assert_eq!(
|
||||
/// CELL.get(),
|
||||
/// Some(&12345),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// You can use `OnceLock` to implement a type that requires "append-only" logic:
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// struct OnceList<T> {
|
||||
/// data: OnceLock<T>,
|
||||
/// next: OnceLock<Box<OnceList<T>>>,
|
||||
/// }
|
||||
/// impl<T> OnceList<T> {
|
||||
/// const fn new() -> OnceList<T> {
|
||||
/// OnceList { data: OnceLock::new(), next: OnceLock::new() }
|
||||
/// }
|
||||
/// fn push(&self, value: T) {
|
||||
/// // FIXME: this impl is concise, but is also slow for long lists or many threads.
|
||||
/// // as an exercise, consider how you might improve on it while preserving the behavior
|
||||
/// if let Err(value) = self.data.set(value) {
|
||||
/// let next = self.next.get_or_init(|| Box::new(OnceList::new()));
|
||||
/// next.push(value)
|
||||
/// };
|
||||
/// }
|
||||
/// fn contains(&self, example: &T) -> bool
|
||||
/// where
|
||||
/// T: PartialEq,
|
||||
/// {
|
||||
/// self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| {
|
||||
/// self.next.get().map(|next| next.contains(example)).unwrap_or(false)
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Let's exercise this new Sync append-only list by doing a little counting
|
||||
/// static LIST: OnceList<u32> = OnceList::new();
|
||||
/// static COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||
///
|
||||
/// # const LEN: u32 = if cfg!(miri) { 50 } else { 1000 };
|
||||
/// # /*
|
||||
/// const LEN: u32 = 1000;
|
||||
/// # */
|
||||
/// thread::scope(|s| {
|
||||
/// for _ in 0..thread::available_parallelism().unwrap().get() {
|
||||
/// s.spawn(|| {
|
||||
/// while let i @ 0..LEN = COUNTER.fetch_add(1, Ordering::Relaxed) {
|
||||
/// LIST.push(i);
|
||||
/// }
|
||||
/// });
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// for i in 0..LEN {
|
||||
/// assert!(LIST.contains(&i));
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub struct OnceLock<T> {
|
||||
// FIXME(nonpoison_once): switch to nonpoison version once it is available
|
||||
once: Once,
|
||||
// Whether or not the value is initialized is tracked by `once.is_completed()`.
|
||||
value: UnsafeCell<MaybeUninit<T>>,
|
||||
/// `PhantomData` to make sure dropck understands we're dropping T in our Drop impl.
|
||||
///
|
||||
/// ```compile_fail,E0597
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// struct A<'a>(&'a str);
|
||||
///
|
||||
/// impl<'a> Drop for A<'a> {
|
||||
/// fn drop(&mut self) {}
|
||||
/// }
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// {
|
||||
/// let s = String::new();
|
||||
/// let _ = cell.set(A(&s));
|
||||
/// }
|
||||
/// ```
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> OnceLock<T> {
|
||||
/// Creates a new uninitialized cell.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
#[rustc_const_stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub const fn new() -> OnceLock<T> {
|
||||
OnceLock {
|
||||
once: Once::new(),
|
||||
value: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the reference to the underlying value.
|
||||
///
|
||||
/// Returns `None` if the cell is uninitialized, or being initialized.
|
||||
/// This method never blocks.
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn get(&self) -> Option<&T> {
|
||||
if self.initialized() {
|
||||
// Safe b/c checked initialized
|
||||
Some(unsafe { self.get_unchecked() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the mutable reference to the underlying value.
|
||||
///
|
||||
/// Returns `None` if the cell is uninitialized.
|
||||
///
|
||||
/// This method never blocks. Since it borrows the `OnceLock` mutably,
|
||||
/// it is statically guaranteed that no active borrows to the `OnceLock`
|
||||
/// exist, including from other threads.
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub fn get_mut(&mut self) -> Option<&mut T> {
|
||||
if self.initialized_mut() {
|
||||
// Safe b/c checked initialized and we have a unique access
|
||||
Some(unsafe { self.get_unchecked_mut() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until the cell is initialized.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Waiting for a computation on another thread to finish:
|
||||
/// ```rust
|
||||
/// use std::thread;
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let value = OnceLock::new();
|
||||
///
|
||||
/// thread::scope(|s| {
|
||||
/// s.spawn(|| value.set(1 + 1));
|
||||
///
|
||||
/// let result = value.wait();
|
||||
/// assert_eq!(result, &2);
|
||||
/// })
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_wait", since = "1.86.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait(&self) -> &T {
|
||||
self.once.wait_force();
|
||||
|
||||
unsafe { self.get_unchecked() }
|
||||
}
|
||||
|
||||
/// Initializes the contents of the cell to `value`.
|
||||
///
|
||||
/// May block if another thread is currently attempting to initialize the cell. The cell is
|
||||
/// guaranteed to contain a value when `set` returns, though not necessarily the one provided.
|
||||
///
|
||||
/// Returns `Ok(())` if the cell was uninitialized and
|
||||
/// `Err(value)` if the cell was already initialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// static CELL: OnceLock<i32> = OnceLock::new();
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert!(CELL.get().is_none());
|
||||
///
|
||||
/// std::thread::spawn(|| {
|
||||
/// assert_eq!(CELL.set(92), Ok(()));
|
||||
/// }).join().unwrap();
|
||||
///
|
||||
/// assert_eq!(CELL.set(62), Err(62));
|
||||
/// assert_eq!(CELL.get(), Some(&92));
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn set(&self, value: T) -> Result<(), T> {
|
||||
match self.try_insert(value) {
|
||||
Ok(_) => Ok(()),
|
||||
Err((_, value)) => Err(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the contents of the cell to `value` if the cell was uninitialized,
|
||||
/// then returns a reference to it.
|
||||
///
|
||||
/// May block if another thread is currently attempting to initialize the cell. The cell is
|
||||
/// guaranteed to contain a value when `try_insert` returns, though not necessarily the
|
||||
/// one provided.
|
||||
///
|
||||
/// Returns `Ok(&value)` if the cell was uninitialized and
|
||||
/// `Err((¤t_value, value))` if it was already initialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell_try_insert)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// static CELL: OnceLock<i32> = OnceLock::new();
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert!(CELL.get().is_none());
|
||||
///
|
||||
/// std::thread::spawn(|| {
|
||||
/// assert_eq!(CELL.try_insert(92), Ok(&92));
|
||||
/// }).join().unwrap();
|
||||
///
|
||||
/// assert_eq!(CELL.try_insert(62), Err((&92, 62)));
|
||||
/// assert_eq!(CELL.get(), Some(&92));
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "once_cell_try_insert", issue = "116693")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> {
|
||||
let mut value = Some(value);
|
||||
let res = self.get_or_init(|| value.take().unwrap());
|
||||
match value {
|
||||
None => Ok(res),
|
||||
Some(value) => Err((res, value)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the contents of the cell, initializing it to `f()` if the cell
|
||||
/// was uninitialized.
|
||||
///
|
||||
/// Many threads may call `get_or_init` concurrently with different
|
||||
/// initializing functions, but it is guaranteed that only one function
|
||||
/// will be executed if the function doesn't panic.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f()` panics, the panic is propagated to the caller, and the cell
|
||||
/// remains uninitialized.
|
||||
///
|
||||
/// It is an error to reentrantly initialize the cell from `f`. The
|
||||
/// exact outcome is unspecified. Current implementation deadlocks, but
|
||||
/// this may be changed to a panic in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// let value = cell.get_or_init(|| 92);
|
||||
/// assert_eq!(value, &92);
|
||||
/// let value = cell.get_or_init(|| unreachable!());
|
||||
/// assert_eq!(value, &92);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn get_or_init<F>(&self, f: F) -> &T
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
match self.get_or_try_init(|| Ok::<T, !>(f())) {
|
||||
Ok(val) => val,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the mutable reference of the contents of the cell, initializing
|
||||
/// it to `f()` if the cell was uninitialized.
|
||||
///
|
||||
/// This method never blocks. Since it borrows the `OnceLock` mutably,
|
||||
/// it is statically guaranteed that no active borrows to the `OnceLock`
|
||||
/// exist, including from other threads.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f()` panics, the panic is propagated to the caller, and the cell
|
||||
/// remains uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell_get_mut)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let mut cell = OnceLock::new();
|
||||
/// let value = cell.get_mut_or_init(|| 92);
|
||||
/// assert_eq!(*value, 92);
|
||||
///
|
||||
/// *value += 2;
|
||||
/// assert_eq!(*value, 94);
|
||||
///
|
||||
/// let value = cell.get_mut_or_init(|| unreachable!());
|
||||
/// assert_eq!(*value, 94);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "once_cell_get_mut", issue = "121641")]
|
||||
pub fn get_mut_or_init<F>(&mut self, f: F) -> &mut T
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
match self.get_mut_or_try_init(|| Ok::<T, !>(f())) {
|
||||
Ok(val) => val,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the contents of the cell, initializing it to `f()` if
|
||||
/// the cell was uninitialized. If the cell was uninitialized
|
||||
/// and `f()` failed, an error is returned.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f()` panics, the panic is propagated to the caller, and
|
||||
/// the cell remains uninitialized.
|
||||
///
|
||||
/// It is an error to reentrantly initialize the cell from `f`.
|
||||
/// The exact outcome is unspecified. Current implementation
|
||||
/// deadlocks, but this may be changed to a panic in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell_try)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
|
||||
/// assert!(cell.get().is_none());
|
||||
/// let value = cell.get_or_try_init(|| -> Result<i32, ()> {
|
||||
/// Ok(92)
|
||||
/// });
|
||||
/// assert_eq!(value, Ok(&92));
|
||||
/// assert_eq!(cell.get(), Some(&92))
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "once_cell_try", issue = "109737")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
|
||||
where
|
||||
F: FnOnce() -> Result<T, E>,
|
||||
{
|
||||
// Fast path check
|
||||
// NOTE: We need to perform an acquire on the state in this method
|
||||
// in order to correctly synchronize `LazyLock::force`. This is
|
||||
// currently done by calling `self.get()`, which in turn calls
|
||||
// `self.initialized()`, which in turn performs the acquire.
|
||||
if let Some(value) = self.get() {
|
||||
return Ok(value);
|
||||
}
|
||||
self.initialize(f)?;
|
||||
|
||||
// SAFETY: The inner value has been initialized
|
||||
Ok(unsafe { self.get_unchecked() })
|
||||
}
|
||||
|
||||
/// Gets the mutable reference of the contents of the cell, initializing
|
||||
/// it to `f()` if the cell was uninitialized. If the cell was uninitialized
|
||||
/// and `f()` failed, an error is returned.
|
||||
///
|
||||
/// This method never blocks. Since it borrows the `OnceLock` mutably,
|
||||
/// it is statically guaranteed that no active borrows to the `OnceLock`
|
||||
/// exist, including from other threads.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f()` panics, the panic is propagated to the caller, and
|
||||
/// the cell remains uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell_get_mut)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let mut cell: OnceLock<u32> = OnceLock::new();
|
||||
///
|
||||
/// // Failed attempts to initialize the cell do not change its contents
|
||||
/// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err());
|
||||
/// assert!(cell.get().is_none());
|
||||
///
|
||||
/// let value = cell.get_mut_or_try_init(|| "1234".parse());
|
||||
/// assert_eq!(value, Ok(&mut 1234));
|
||||
/// *value.unwrap() += 2;
|
||||
/// assert_eq!(cell.get(), Some(&1236))
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "once_cell_get_mut", issue = "121641")]
|
||||
pub fn get_mut_or_try_init<F, E>(&mut self, f: F) -> Result<&mut T, E>
|
||||
where
|
||||
F: FnOnce() -> Result<T, E>,
|
||||
{
|
||||
if self.get_mut().is_none() {
|
||||
self.initialize(f)?;
|
||||
}
|
||||
|
||||
// SAFETY: The inner value has been initialized
|
||||
Ok(unsafe { self.get_unchecked_mut() })
|
||||
}
|
||||
|
||||
/// Consumes the `OnceLock`, returning the wrapped value. Returns
|
||||
/// `None` if the cell was uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let cell: OnceLock<String> = OnceLock::new();
|
||||
/// assert_eq!(cell.into_inner(), None);
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// cell.set("hello".to_string()).unwrap();
|
||||
/// assert_eq!(cell.into_inner(), Some("hello".to_string()));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub fn into_inner(mut self) -> Option<T> {
|
||||
self.take()
|
||||
}
|
||||
|
||||
/// Takes the value out of this `OnceLock`, moving it back to an uninitialized state.
|
||||
///
|
||||
/// Has no effect and returns `None` if the `OnceLock` was uninitialized.
|
||||
///
|
||||
/// Since this method borrows the `OnceLock` mutably, it is statically guaranteed that
|
||||
/// no active borrows to the `OnceLock` exist, including from other threads.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let mut cell: OnceLock<String> = OnceLock::new();
|
||||
/// assert_eq!(cell.take(), None);
|
||||
///
|
||||
/// let mut cell = OnceLock::new();
|
||||
/// cell.set("hello".to_string()).unwrap();
|
||||
/// assert_eq!(cell.take(), Some("hello".to_string()));
|
||||
/// assert_eq!(cell.get(), None);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub fn take(&mut self) -> Option<T> {
|
||||
if self.initialized_mut() {
|
||||
self.once = Once::new();
|
||||
// SAFETY: `self.value` is initialized and contains a valid `T`.
|
||||
// `self.once` is reset, so `initialized()` will be false again
|
||||
// which prevents the value from being read twice.
|
||||
unsafe { Some(self.value.get_mut().assume_init_read()) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initialized(&self) -> bool {
|
||||
self.once.is_completed()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initialized_mut(&mut self) -> bool {
|
||||
// `state()` does not perform an atomic load, so prefer it over `is_complete()`.
|
||||
let state = self.once.state();
|
||||
match state {
|
||||
OnceExclusiveState::Complete => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[optimize(size)]
|
||||
fn initialize<F, E>(&self, f: F) -> Result<(), E>
|
||||
where
|
||||
F: FnOnce() -> Result<T, E>,
|
||||
{
|
||||
let mut res: Result<(), E> = Ok(());
|
||||
let slot = &self.value;
|
||||
|
||||
// Ignore poisoning from other threads
|
||||
// If another thread panics, then we'll be able to run our closure
|
||||
self.once.call_once_force(|p| {
|
||||
match f() {
|
||||
Ok(value) => {
|
||||
unsafe { (&mut *slot.get()).write(value) };
|
||||
}
|
||||
Err(e) => {
|
||||
res = Err(e);
|
||||
|
||||
// Treat the underlying `Once` as poisoned since we
|
||||
// failed to initialize our value.
|
||||
p.poison();
|
||||
}
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The cell must be initialized
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(&self) -> &T {
|
||||
debug_assert!(self.initialized());
|
||||
unsafe { (&*self.value.get()).assume_init_ref() }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The cell must be initialized
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(&mut self) -> &mut T {
|
||||
debug_assert!(self.initialized_mut());
|
||||
unsafe { self.value.get_mut().assume_init_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
// Why do we need `T: Send`?
|
||||
// Thread A creates a `OnceLock` and shares it with
|
||||
// scoped thread B, which fills the cell, which is
|
||||
// then destroyed by A. That is, destructor observes
|
||||
// a sent value.
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
unsafe impl<T: Send> Send for OnceLock<T> {}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceLock<T> {}
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
#[rustc_const_unstable(feature = "const_default", issue = "143894")]
|
||||
impl<T> const Default for OnceLock<T> {
|
||||
/// Creates a new uninitialized cell.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert_eq!(OnceLock::<()>::new(), OnceLock::default());
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
fn default() -> OnceLock<T> {
|
||||
OnceLock::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: fmt::Debug> fmt::Debug for OnceLock<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_tuple("OnceLock");
|
||||
match self.get() {
|
||||
Some(v) => d.field(v),
|
||||
None => d.field(&format_args!("<uninit>")),
|
||||
};
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: Clone> Clone for OnceLock<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> OnceLock<T> {
|
||||
let cell = Self::new();
|
||||
if let Some(value) = self.get() {
|
||||
match cell.set(value.clone()) {
|
||||
Ok(()) => (),
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
cell
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T> From<T> for OnceLock<T> {
|
||||
/// Creates a new cell with its contents set to `value`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// # fn main() -> Result<(), i32> {
|
||||
/// let a = OnceLock::from(3);
|
||||
/// let b = OnceLock::new();
|
||||
/// b.set(3)?;
|
||||
/// assert_eq!(a, b);
|
||||
/// Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
fn from(value: T) -> Self {
|
||||
let cell = Self::new();
|
||||
match cell.set(value) {
|
||||
Ok(()) => cell,
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: PartialEq> PartialEq for OnceLock<T> {
|
||||
/// Equality for two `OnceLock`s.
|
||||
///
|
||||
/// Two `OnceLock`s are equal if they either both contain values and their
|
||||
/// values are equal, or if neither contains a value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let five = OnceLock::new();
|
||||
/// five.set(5).unwrap();
|
||||
///
|
||||
/// let also_five = OnceLock::new();
|
||||
/// also_five.set(5).unwrap();
|
||||
///
|
||||
/// assert!(five == also_five);
|
||||
///
|
||||
/// assert!(OnceLock::<u32>::new() == OnceLock::<u32>::new());
|
||||
/// ```
|
||||
#[inline]
|
||||
fn eq(&self, other: &OnceLock<T>) -> bool {
|
||||
self.get() == other.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: Eq> Eq for OnceLock<T> {}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
unsafe impl<#[may_dangle] T> Drop for OnceLock<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if self.initialized_mut() {
|
||||
// SAFETY: The cell is initialized and being dropped, so it can't
|
||||
// be accessed again. We also don't touch the `T` other than
|
||||
// dropping it, which validates our usage of #[may_dangle].
|
||||
unsafe { self.value.get_mut().assume_init_drop() };
|
||||
}
|
||||
}
|
||||
}
|
||||
389
crates/std/src/sync/poison.rs
Normal file
389
crates/std/src/sync/poison.rs
Normal file
@@ -0,0 +1,389 @@
|
||||
//! Synchronization objects that employ poisoning.
|
||||
//!
|
||||
//! # Poisoning
|
||||
//!
|
||||
//! All synchronization objects in this module implement a strategy called
|
||||
//! "poisoning" where a primitive becomes poisoned if it recognizes that some
|
||||
//! thread has panicked while holding the exclusive access granted by the
|
||||
//! primitive. This information is then propagated to all other threads
|
||||
//! to signify that the data protected by this primitive is likely tainted
|
||||
//! (some invariant is not being upheld).
|
||||
//!
|
||||
//! The specifics of how this "poisoned" state affects other threads and whether
|
||||
//! the panics are recognized reliably or on a best-effort basis depend on the
|
||||
//! primitive. See [Overview](#overview) below.
|
||||
//!
|
||||
//! The synchronization objects in this module have alternative implementations that do not employ
|
||||
//! poisoning in the [`std::sync::nonpoison`] module.
|
||||
//!
|
||||
//! [`std::sync::nonpoison`]: crate::sync::nonpoison
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! Below is a list of synchronization objects provided by this module
|
||||
//! with a high-level overview for each object and a description
|
||||
//! of how it employs "poisoning".
|
||||
//!
|
||||
//! - [`Condvar`]: Condition Variable, providing the ability to block
|
||||
//! a thread while waiting for an event to occur.
|
||||
//!
|
||||
//! Condition variables are typically associated with
|
||||
//! a boolean predicate (a condition) and a mutex.
|
||||
//! This implementation is associated with [`poison::Mutex`](Mutex),
|
||||
//! which employs poisoning.
|
||||
//! For this reason, [`Condvar::wait()`] will return a [`LockResult`],
|
||||
//! just like [`poison::Mutex::lock()`](Mutex::lock) does.
|
||||
//!
|
||||
//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at
|
||||
//! most one thread at a time is able to access some data.
|
||||
//!
|
||||
//! Panicking while holding the lock typically poisons the mutex, but it is
|
||||
//! not guaranteed to detect this condition in all circumstances.
|
||||
//! [`Mutex::lock()`] returns a [`LockResult`], providing a way to deal with
|
||||
//! the poisoned state. See [`Mutex`'s documentation](Mutex#poisoning) for more.
|
||||
//!
|
||||
//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows
|
||||
//! multiple readers at the same time, while allowing only one
|
||||
//! writer at a time. In some cases, this can be more efficient than
|
||||
//! a mutex.
|
||||
//!
|
||||
//! This implementation, like [`Mutex`], usually becomes poisoned on a panic.
|
||||
//! Note, however, that an `RwLock` may only be poisoned if a panic occurs
|
||||
//! while it is locked exclusively (write mode). If a panic occurs in any reader,
|
||||
//! then the lock will not be poisoned.
|
||||
//!
|
||||
//! Note that the [`Once`] type also employs poisoning, but since it has non-poisoning `force`
|
||||
//! methods available on it, there is no separate `nonpoison` and `poison` version.
|
||||
//!
|
||||
//! [`Once`]: crate::sync::Once
|
||||
|
||||
// If we are not unwinding, `PoisonError` is uninhabited.
|
||||
#![cfg_attr(not(panic = "unwind"), expect(unreachable_code))]
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::condvar::Condvar;
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub use self::mutex::MappedMutexGuard;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::mutex::{Mutex, MutexGuard};
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use crate::error::Error;
|
||||
use crate::fmt;
|
||||
#[cfg(panic = "unwind")]
|
||||
use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
|
||||
#[cfg(panic = "unwind")]
|
||||
use crate::thread;
|
||||
|
||||
mod condvar;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
mod mutex;
|
||||
mod rwlock;
|
||||
|
||||
pub(crate) struct Flag {
|
||||
#[cfg(panic = "unwind")]
|
||||
failed: Atomic<bool>,
|
||||
}
|
||||
|
||||
// Note that the Ordering uses to access the `failed` field of `Flag` below is
|
||||
// always `Relaxed`, and that's because this isn't actually protecting any data,
|
||||
// it's just a flag whether we've panicked or not.
|
||||
//
|
||||
// The actual location that this matters is when a mutex is **locked** which is
|
||||
// where we have external synchronization ensuring that we see memory
|
||||
// reads/writes to this flag.
|
||||
//
|
||||
// As a result, if it matters, we should see the correct value for `failed` in
|
||||
// all cases.
|
||||
|
||||
impl Flag {
|
||||
#[inline]
|
||||
pub const fn new() -> Flag {
|
||||
Flag {
|
||||
#[cfg(panic = "unwind")]
|
||||
failed: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the flag for an unguarded borrow, where we only care about existing poison.
|
||||
#[inline]
|
||||
pub fn borrow(&self) -> LockResult<()> {
|
||||
if self.get() { Err(PoisonError::new(())) } else { Ok(()) }
|
||||
}
|
||||
|
||||
/// Checks the flag for a guarded borrow, where we may also set poison when `done`.
|
||||
#[inline]
|
||||
pub fn guard(&self) -> LockResult<Guard> {
|
||||
let ret = Guard {
|
||||
#[cfg(panic = "unwind")]
|
||||
panicking: thread::panicking(),
|
||||
};
|
||||
if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(panic = "unwind")]
|
||||
pub fn done(&self, guard: &Guard) {
|
||||
if !guard.panicking && thread::panicking() {
|
||||
self.failed.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
pub fn done(&self, _guard: &Guard) {}
|
||||
|
||||
#[inline]
|
||||
#[cfg(panic = "unwind")]
|
||||
pub fn get(&self) -> bool {
|
||||
self.failed.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
pub fn get(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&self) {
|
||||
#[cfg(panic = "unwind")]
|
||||
self.failed.store(false, Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Guard {
|
||||
#[cfg(panic = "unwind")]
|
||||
panicking: bool,
|
||||
}
|
||||
|
||||
/// A type of error which can be returned whenever a lock is acquired.
|
||||
///
|
||||
/// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock
|
||||
/// is held. The precise semantics for when a lock is poisoned is documented on
|
||||
/// each lock. For a lock in the poisoned state, unless the state is cleared manually,
|
||||
/// all future acquisitions will return this error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(1));
|
||||
///
|
||||
/// // poison the mutex
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
/// let _ = thread::spawn(move || {
|
||||
/// let mut data = c_mutex.lock().unwrap();
|
||||
/// *data = 2;
|
||||
/// panic!();
|
||||
/// }).join();
|
||||
///
|
||||
/// match mutex.lock() {
|
||||
/// Ok(_) => unreachable!(),
|
||||
/// Err(p_err) => {
|
||||
/// let data = p_err.get_ref();
|
||||
/// println!("recovered: {data}");
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
/// [`Mutex`]: crate::sync::Mutex
|
||||
/// [`RwLock`]: crate::sync::RwLock
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct PoisonError<T> {
|
||||
data: T,
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
_never: !,
|
||||
}
|
||||
|
||||
/// An enumeration of possible errors associated with a [`TryLockResult`] which
|
||||
/// can occur while trying to acquire a lock, from the [`try_lock`] method on a
|
||||
/// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`].
|
||||
///
|
||||
/// [`try_lock`]: crate::sync::Mutex::try_lock
|
||||
/// [`try_read`]: crate::sync::RwLock::try_read
|
||||
/// [`try_write`]: crate::sync::RwLock::try_write
|
||||
/// [`Mutex`]: crate::sync::Mutex
|
||||
/// [`RwLock`]: crate::sync::RwLock
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub enum TryLockError<T> {
|
||||
/// The lock could not be acquired because another thread failed while holding
|
||||
/// the lock.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
Poisoned(#[stable(feature = "rust1", since = "1.0.0")] PoisonError<T>),
|
||||
/// The lock could not be acquired at this time because the operation would
|
||||
/// otherwise block.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
WouldBlock,
|
||||
}
|
||||
|
||||
/// A type alias for the result of a lock method which can be poisoned.
|
||||
///
|
||||
/// The [`Ok`] variant of this result indicates that the primitive was not
|
||||
/// poisoned, and the operation result is contained within. The [`Err`] variant indicates
|
||||
/// that the primitive was poisoned. Note that the [`Err`] variant *also* carries
|
||||
/// an associated value assigned by the lock method, and it can be acquired through the
|
||||
/// [`into_inner`] method. The semantics of the associated value depends on the corresponding
|
||||
/// lock method.
|
||||
///
|
||||
/// [`into_inner`]: PoisonError::into_inner
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub type LockResult<T> = Result<T, PoisonError<T>>;
|
||||
|
||||
/// A type alias for the result of a nonblocking locking method.
|
||||
///
|
||||
/// For more information, see [`LockResult`]. A `TryLockResult` doesn't
|
||||
/// necessarily hold the associated guard in the [`Err`] type as the lock might not
|
||||
/// have been acquired for other reasons.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> fmt::Debug for PoisonError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PoisonError").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> fmt::Display for PoisonError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"poisoned lock: another task failed inside".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Error for PoisonError<T> {}
|
||||
|
||||
impl<T> PoisonError<T> {
|
||||
/// Creates a `PoisonError`.
|
||||
///
|
||||
/// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock)
|
||||
/// or [`RwLock::read`](crate::sync::RwLock::read).
|
||||
///
|
||||
/// This method may panic if std was built with `panic="abort"`.
|
||||
#[cfg(panic = "unwind")]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn new(data: T) -> PoisonError<T> {
|
||||
PoisonError { data }
|
||||
}
|
||||
|
||||
/// Creates a `PoisonError`.
|
||||
///
|
||||
/// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock)
|
||||
/// or [`RwLock::read`](crate::sync::RwLock::read).
|
||||
///
|
||||
/// This method may panic if std was built with `panic="abort"`.
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
#[track_caller]
|
||||
pub fn new(_data: T) -> PoisonError<T> {
|
||||
panic!("PoisonError created in a libstd built with panic=\"abort\"")
|
||||
}
|
||||
|
||||
/// Consumes this error indicating that a lock is poisoned, returning the
|
||||
/// associated data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashSet;
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(HashSet::new()));
|
||||
///
|
||||
/// // poison the mutex
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
/// let _ = thread::spawn(move || {
|
||||
/// let mut data = c_mutex.lock().unwrap();
|
||||
/// data.insert(10);
|
||||
/// panic!();
|
||||
/// }).join();
|
||||
///
|
||||
/// let p_err = mutex.lock().unwrap_err();
|
||||
/// let data = p_err.into_inner();
|
||||
/// println!("recovered {} items", data.len());
|
||||
/// ```
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Reaches into this error indicating that a lock is poisoned, returning a
|
||||
/// reference to the associated data.
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.data
|
||||
}
|
||||
|
||||
/// Reaches into this error indicating that a lock is poisoned, returning a
|
||||
/// mutable reference to the associated data.
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> From<PoisonError<T>> for TryLockError<T> {
|
||||
fn from(err: PoisonError<T>) -> TryLockError<T> {
|
||||
TryLockError::Poisoned(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> fmt::Debug for TryLockError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
#[cfg(panic = "unwind")]
|
||||
TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
TryLockError::Poisoned(ref p) => match p._never {},
|
||||
TryLockError::WouldBlock => "WouldBlock".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> fmt::Display for TryLockError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
#[cfg(panic = "unwind")]
|
||||
TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
TryLockError::Poisoned(ref p) => match p._never {},
|
||||
TryLockError::WouldBlock => "try_lock failed because the operation would block",
|
||||
}
|
||||
.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Error for TryLockError<T> {
|
||||
#[allow(deprecated)]
|
||||
fn cause(&self) -> Option<&dyn Error> {
|
||||
match *self {
|
||||
#[cfg(panic = "unwind")]
|
||||
TryLockError::Poisoned(ref p) => Some(p),
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
TryLockError::Poisoned(ref p) => match p._never {},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
match result {
|
||||
Ok(t) => Ok(f(t)),
|
||||
#[cfg(panic = "unwind")]
|
||||
Err(PoisonError { data }) => Err(PoisonError::new(f(data))),
|
||||
}
|
||||
}
|
||||
510
crates/std/src/sync/poison/condvar.rs
Normal file
510
crates/std/src/sync/poison/condvar.rs
Normal file
@@ -0,0 +1,510 @@
|
||||
use crate::fmt;
|
||||
use crate::sync::WaitTimeoutResult;
|
||||
use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex};
|
||||
use crate::sys::sync as sys;
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
/// A Condition Variable
|
||||
///
|
||||
/// Condition variables represent the ability to block a thread such that it
|
||||
/// consumes no CPU time while waiting for an event to occur. Condition
|
||||
/// variables are typically associated with a boolean predicate (a condition)
|
||||
/// and a mutex. The predicate is always verified inside of the mutex before
|
||||
/// determining that a thread must block.
|
||||
///
|
||||
/// Functions in this module will block the current **thread** of execution.
|
||||
/// Note that any attempt to use multiple mutexes on the same condition
|
||||
/// variable may result in a runtime panic.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// // Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Condvar {
|
||||
inner: sys::Condvar,
|
||||
}
|
||||
|
||||
impl Condvar {
|
||||
/// Creates a new condition variable which is ready to be waited on and
|
||||
/// notified.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Condvar;
|
||||
///
|
||||
/// let condvar = Condvar::new();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn new() -> Condvar {
|
||||
Condvar { inner: sys::Condvar::new() }
|
||||
}
|
||||
|
||||
/// Blocks the current thread until this condition variable receives a
|
||||
/// notification.
|
||||
///
|
||||
/// This function will atomically unlock the mutex specified (represented by
|
||||
/// `guard`) and block the current thread. This means that any calls
|
||||
/// to [`notify_one`] or [`notify_all`] which happen logically after the
|
||||
/// mutex is unlocked are candidates to wake this thread up. When this
|
||||
/// function call returns, the lock specified will have been re-acquired.
|
||||
///
|
||||
/// Note that this function is susceptible to spurious wakeups. Condition
|
||||
/// variables normally have a boolean predicate associated with them, and
|
||||
/// the predicate must always be checked each time this function returns to
|
||||
/// protect against spurious wakeups.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the mutex being waited on is
|
||||
/// poisoned when this thread re-acquires the lock. For more information,
|
||||
/// see information about [poisoning] on the [`Mutex`] type.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may [`panic!`] if it is used with more than one mutex
|
||||
/// over time.
|
||||
///
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
/// [poisoning]: super::Mutex#poisoning
|
||||
/// [`Mutex`]: super::Mutex
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
|
||||
let poisoned = unsafe {
|
||||
let lock = mutex::guard_lock(&guard);
|
||||
self.inner.wait(lock);
|
||||
mutex::guard_poison(&guard).get()
|
||||
};
|
||||
if poisoned { Err(PoisonError::new(guard)) } else { Ok(guard) }
|
||||
}
|
||||
|
||||
/// Blocks the current thread until the provided condition becomes false.
|
||||
///
|
||||
/// `condition` is checked immediately; if not met (returns `true`), this
|
||||
/// will [`wait`] for the next notification then check again. This repeats
|
||||
/// until `condition` returns `false`, in which case this function returns.
|
||||
///
|
||||
/// This function will atomically unlock the mutex specified (represented by
|
||||
/// `guard`) and block the current thread. This means that any calls
|
||||
/// to [`notify_one`] or [`notify_all`] which happen logically after the
|
||||
/// mutex is unlocked are candidates to wake this thread up. When this
|
||||
/// function call returns, the lock specified will have been re-acquired.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the mutex being waited on is
|
||||
/// poisoned when this thread re-acquires the lock. For more information,
|
||||
/// see information about [poisoning] on the [`Mutex`] type.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
/// [poisoning]: super::Mutex#poisoning
|
||||
/// [`Mutex`]: super::Mutex
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut pending = lock.lock().unwrap();
|
||||
/// *pending = false;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// // As long as the value inside the `Mutex<bool>` is `true`, we wait.
|
||||
/// let _guard = cvar.wait_while(lock.lock().unwrap(), |pending| { *pending }).unwrap();
|
||||
/// ```
|
||||
#[stable(feature = "wait_until", since = "1.42.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait_while<'a, T, F>(
|
||||
&self,
|
||||
mut guard: MutexGuard<'a, T>,
|
||||
mut condition: F,
|
||||
) -> LockResult<MutexGuard<'a, T>>
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
while condition(&mut *guard) {
|
||||
guard = self.wait(guard)?;
|
||||
}
|
||||
Ok(guard)
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait`]
|
||||
/// except that the thread will be blocked for roughly no longer
|
||||
/// than `ms` milliseconds. This method should not be used for
|
||||
/// precise timing due to anomalies such as preemption or platform
|
||||
/// differences that might not cause the maximum amount of time
|
||||
/// waited to be precisely `ms`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time.
|
||||
///
|
||||
/// The returned boolean is `false` only if the timeout is known
|
||||
/// to have elapsed.
|
||||
///
|
||||
/// Like [`wait`], the lock specified will be re-acquired when this function
|
||||
/// returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// loop {
|
||||
/// let result = cvar.wait_timeout_ms(started, 10).unwrap();
|
||||
/// // 10 milliseconds have passed, or maybe the value changed!
|
||||
/// started = result.0;
|
||||
/// if *started == true {
|
||||
/// // We received the notification and the value has been updated, we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
#[deprecated(since = "1.6.0", note = "replaced by `std::sync::Condvar::wait_timeout`")]
|
||||
pub fn wait_timeout_ms<'a, T>(
|
||||
&self,
|
||||
guard: MutexGuard<'a, T>,
|
||||
ms: u32,
|
||||
) -> LockResult<(MutexGuard<'a, T>, bool)> {
|
||||
let res = self.wait_timeout(guard, Duration::from_millis(ms as u64));
|
||||
poison::map_result(res, |(a, b)| (a, !b.timed_out()))
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait`] except that
|
||||
/// the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that might not cause the maximum
|
||||
/// amount of time waited to be precisely `dur`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time. This function is susceptible to spurious wakeups.
|
||||
/// Condition variables normally have a boolean predicate associated with
|
||||
/// them, and the predicate must always be checked each time this function
|
||||
/// returns to protect against spurious wakeups. Furthermore, since the timeout
|
||||
/// is given relative to the moment this function is called, it needs to be adjusted
|
||||
/// when this function is called in a loop. The [`wait_timeout_while`] method
|
||||
/// lets you wait with a timeout while a predicate is true, taking care of all these concerns.
|
||||
///
|
||||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed.
|
||||
///
|
||||
/// Like [`wait`], the lock specified will be re-acquired when this function
|
||||
/// returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`wait_timeout_while`]: Self::wait_timeout_while
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // wait for the thread to start up
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // as long as the value inside the `Mutex<bool>` is `false`, we wait
|
||||
/// loop {
|
||||
/// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap();
|
||||
/// // 10 milliseconds have passed, or maybe the value changed!
|
||||
/// started = result.0;
|
||||
/// if *started == true {
|
||||
/// // We received the notification and the value has been updated, we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait_timeout<'a, T>(
|
||||
&self,
|
||||
guard: MutexGuard<'a, T>,
|
||||
dur: Duration,
|
||||
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> {
|
||||
let (poisoned, result) = unsafe {
|
||||
let lock = mutex::guard_lock(&guard);
|
||||
let success = self.inner.wait_timeout(lock, dur);
|
||||
(mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success))
|
||||
};
|
||||
if poisoned { Err(PoisonError::new((guard, result))) } else { Ok((guard, result)) }
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait_while`] except
|
||||
/// that the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that might not cause the maximum
|
||||
/// amount of time waited to be precisely `dur`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time.
|
||||
///
|
||||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed without the condition being met.
|
||||
///
|
||||
/// Like [`wait_while`], the lock specified will be re-acquired when this
|
||||
/// function returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait_while`]: Self::wait_while
|
||||
/// [`wait_timeout`]: Self::wait_timeout
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut pending = lock.lock().unwrap();
|
||||
/// *pending = false;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // wait for the thread to start up
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let result = cvar.wait_timeout_while(
|
||||
/// lock.lock().unwrap(),
|
||||
/// Duration::from_millis(100),
|
||||
/// |&mut pending| pending,
|
||||
/// ).unwrap();
|
||||
/// if result.1.timed_out() {
|
||||
/// // timed-out without the condition ever evaluating to false.
|
||||
/// }
|
||||
/// // access the locked mutex via result.0
|
||||
/// ```
|
||||
#[stable(feature = "wait_timeout_until", since = "1.42.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait_timeout_while<'a, T, F>(
|
||||
&self,
|
||||
mut guard: MutexGuard<'a, T>,
|
||||
dur: Duration,
|
||||
mut condition: F,
|
||||
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)>
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
if !condition(&mut *guard) {
|
||||
return Ok((guard, WaitTimeoutResult(false)));
|
||||
}
|
||||
let timeout = match dur.checked_sub(start.elapsed()) {
|
||||
Some(timeout) => timeout,
|
||||
None => return Ok((guard, WaitTimeoutResult(true))),
|
||||
};
|
||||
guard = self.wait_timeout(guard, timeout)?.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Wakes up one blocked thread on this condvar.
|
||||
///
|
||||
/// If there is a blocked thread on this condition variable, then it will
|
||||
/// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to
|
||||
/// `notify_one` are not buffered in any way.
|
||||
///
|
||||
/// To wake up all threads, see [`notify_all`].
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`wait_timeout`]: Self::wait_timeout
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn notify_one(&self) {
|
||||
self.inner.notify_one()
|
||||
}
|
||||
|
||||
/// Wakes up all blocked threads on this condvar.
|
||||
///
|
||||
/// This method will ensure that any current waiters on the condition
|
||||
/// variable are awoken. Calls to `notify_all()` are not buffered in any
|
||||
/// way.
|
||||
///
|
||||
/// To wake up only one thread, see [`notify_one`].
|
||||
///
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_all();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn notify_all(&self) {
|
||||
self.inner.notify_all()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Condvar {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Condvar").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "condvar_default", since = "1.10.0")]
|
||||
impl Default for Condvar {
|
||||
/// Creates a `Condvar` which is ready to be waited on and notified.
|
||||
fn default() -> Condvar {
|
||||
Condvar::new()
|
||||
}
|
||||
}
|
||||
946
crates/std/src/sync/poison/mutex.rs
Normal file
946
crates/std/src/sync/poison/mutex.rs
Normal file
@@ -0,0 +1,946 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison};
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A mutual exclusion primitive useful for protecting shared data
|
||||
///
|
||||
/// This mutex will block threads waiting for the lock to become available. The
|
||||
/// mutex can be created via a [`new`] constructor. Each mutex has a type parameter
|
||||
/// which represents the data that it is protecting. The data can only be accessed
|
||||
/// through the RAII guards returned from [`lock`] and [`try_lock`], which
|
||||
/// guarantees that the data is only ever accessed when the mutex is locked.
|
||||
///
|
||||
/// # Poisoning
|
||||
///
|
||||
/// The mutexes in this module implement a strategy called "poisoning" where a
|
||||
/// mutex becomes poisoned if it recognizes that the thread holding it has
|
||||
/// panicked.
|
||||
///
|
||||
/// Once a mutex is poisoned, all other threads are unable to access the data by
|
||||
/// default as it is likely tainted (some invariant is not being upheld). For a
|
||||
/// mutex, this means that the [`lock`] and [`try_lock`] methods return a
|
||||
/// [`Result`] which indicates whether a mutex has been poisoned or not. Most
|
||||
/// usage of a mutex will simply [`unwrap()`] these results, propagating panics
|
||||
/// among threads to ensure that a possibly invalid invariant is not witnessed.
|
||||
///
|
||||
/// Poisoning is only advisory: the [`PoisonError`] type has an [`into_inner`]
|
||||
/// method which will return the guard that would have otherwise been returned
|
||||
/// on a successful lock. This allows access to the data, despite the lock being
|
||||
/// poisoned.
|
||||
///
|
||||
/// In addition, the panic detection is not ideal, so even unpoisoned mutexes
|
||||
/// need to be handled with care, since certain panics may have been skipped.
|
||||
/// Here is a non-exhaustive list of situations where this might occur:
|
||||
///
|
||||
/// - If a mutex is locked while a panic is underway, e.g. within a [`Drop`]
|
||||
/// implementation or a [panic hook], panicking for the second time while the
|
||||
/// lock is held will leave the mutex unpoisoned. Note that while double panic
|
||||
/// usually aborts the program, [`catch_unwind`] can prevent this.
|
||||
///
|
||||
/// - Locking and unlocking the mutex across different panic contexts, e.g. by
|
||||
/// storing the guard to a [`Cell`] within [`Drop::drop`] and accessing it
|
||||
/// outside, or vice versa, can affect poisoning status in an unexpected way.
|
||||
///
|
||||
/// - Foreign exceptions do not currently trigger poisoning even in absence of
|
||||
/// other panics.
|
||||
///
|
||||
/// While this rarely happens in realistic code, `unsafe` code cannot rely on
|
||||
/// poisoning for soundness, since the behavior of poisoning can depend on
|
||||
/// outside context. Here's an example of **incorrect** use of poisoning:
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// struct MutexBox<T> {
|
||||
/// data: Mutex<*mut T>,
|
||||
/// }
|
||||
///
|
||||
/// impl<T> MutexBox<T> {
|
||||
/// pub fn new(value: T) -> Self {
|
||||
/// Self {
|
||||
/// data: Mutex::new(Box::into_raw(Box::new(value))),
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// pub fn replace_with(&self, f: impl FnOnce(T) -> T) {
|
||||
/// let ptr = self.data.lock().expect("poisoned");
|
||||
/// // While `f` is running, the data is moved out of `*ptr`. If `f`
|
||||
/// // panics, `*ptr` keeps pointing at a dropped value. The intention
|
||||
/// // is that this will poison the mutex, so the following calls to
|
||||
/// // `replace_with` will panic without reading `*ptr`. But since
|
||||
/// // poisoning is not guaranteed to occur if this is run from a panic
|
||||
/// // hook, this can lead to use-after-free.
|
||||
/// unsafe {
|
||||
/// (*ptr).write(f((*ptr).read()));
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`new`]: Self::new
|
||||
/// [`lock`]: Self::lock
|
||||
/// [`try_lock`]: Self::try_lock
|
||||
/// [`unwrap()`]: Result::unwrap
|
||||
/// [`PoisonError`]: super::PoisonError
|
||||
/// [`into_inner`]: super::PoisonError::into_inner
|
||||
/// [panic hook]: core::panic::set_hook
|
||||
/// [`catch_unwind`]: core::panic::catch_unwind
|
||||
/// [`Cell`]: crate::cell::Cell
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
/// use std::sync::mpsc::channel;
|
||||
///
|
||||
/// const N: usize = 10;
|
||||
///
|
||||
/// // Spawn a few threads to increment a shared variable (non-atomically), and
|
||||
/// // let the main thread know once all increments are done.
|
||||
/// //
|
||||
/// // Here we're using an Arc to share memory among threads, and the data inside
|
||||
/// // the Arc is protected with a mutex.
|
||||
/// let data = Arc::new(Mutex::new(0));
|
||||
///
|
||||
/// let (tx, rx) = channel();
|
||||
/// for _ in 0..N {
|
||||
/// let (data, tx) = (Arc::clone(&data), tx.clone());
|
||||
/// thread::spawn(move || {
|
||||
/// // The shared state can only be accessed once the lock is held.
|
||||
/// // Our non-atomic increment is safe because we're the only thread
|
||||
/// // which can access the shared state when the lock is held.
|
||||
/// //
|
||||
/// // We unwrap() the return value to assert that we are not expecting
|
||||
/// // threads to ever fail while holding the lock.
|
||||
/// let mut data = data.lock().unwrap();
|
||||
/// *data += 1;
|
||||
/// if *data == N {
|
||||
/// tx.send(()).unwrap();
|
||||
/// }
|
||||
/// // the lock is unlocked here when `data` goes out of scope.
|
||||
/// });
|
||||
/// }
|
||||
///
|
||||
/// rx.recv().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// To recover from a poisoned mutex:
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let lock = Arc::new(Mutex::new(0_u32));
|
||||
/// let lock2 = Arc::clone(&lock);
|
||||
///
|
||||
/// let _ = thread::spawn(move || -> () {
|
||||
/// // This thread will acquire the mutex first, unwrapping the result of
|
||||
/// // `lock` because the lock has not been poisoned.
|
||||
/// let _guard = lock2.lock().unwrap();
|
||||
///
|
||||
/// // This panic while holding the lock (`_guard` is in scope) will poison
|
||||
/// // the mutex.
|
||||
/// panic!();
|
||||
/// }).join();
|
||||
///
|
||||
/// // The lock is poisoned by this point, but the returned result can be
|
||||
/// // pattern matched on to return the underlying guard on both branches.
|
||||
/// let mut guard = match lock.lock() {
|
||||
/// Ok(guard) => guard,
|
||||
/// Err(poisoned) => poisoned.into_inner(),
|
||||
/// };
|
||||
///
|
||||
/// *guard += 1;
|
||||
/// ```
|
||||
///
|
||||
/// To unlock a mutex guard sooner than the end of the enclosing scope,
|
||||
/// either create an inner scope or drop the guard manually.
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// const N: usize = 3;
|
||||
///
|
||||
/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));
|
||||
/// let res_mutex = Arc::new(Mutex::new(0));
|
||||
///
|
||||
/// let mut threads = Vec::with_capacity(N);
|
||||
/// (0..N).for_each(|_| {
|
||||
/// let data_mutex_clone = Arc::clone(&data_mutex);
|
||||
/// let res_mutex_clone = Arc::clone(&res_mutex);
|
||||
///
|
||||
/// threads.push(thread::spawn(move || {
|
||||
/// // Here we use a block to limit the lifetime of the lock guard.
|
||||
/// let result = {
|
||||
/// let mut data = data_mutex_clone.lock().unwrap();
|
||||
/// // This is the result of some important and long-ish work.
|
||||
/// let result = data.iter().fold(0, |acc, x| acc + x * 2);
|
||||
/// data.push(result);
|
||||
/// result
|
||||
/// // The mutex guard gets dropped here, together with any other values
|
||||
/// // created in the critical section.
|
||||
/// };
|
||||
/// // The guard created here is a temporary dropped at the end of the statement, i.e.
|
||||
/// // the lock would not remain being held even if the thread did some additional work.
|
||||
/// *res_mutex_clone.lock().unwrap() += result;
|
||||
/// }));
|
||||
/// });
|
||||
///
|
||||
/// let mut data = data_mutex.lock().unwrap();
|
||||
/// // This is the result of some important and long-ish work.
|
||||
/// let result = data.iter().fold(0, |acc, x| acc + x * 2);
|
||||
/// data.push(result);
|
||||
/// // We drop the `data` explicitly because it's not necessary anymore and the
|
||||
/// // thread still has work to do. This allows other threads to start working on
|
||||
/// // the data immediately, without waiting for the rest of the unrelated work
|
||||
/// // to be done here.
|
||||
/// //
|
||||
/// // It's even more important here than in the threads because we `.join` the
|
||||
/// // threads after that. If we had not dropped the mutex guard, a thread could
|
||||
/// // be waiting forever for it, causing a deadlock.
|
||||
/// // As in the threads, a block could have been used instead of calling the
|
||||
/// // `drop` function.
|
||||
/// drop(data);
|
||||
/// // Here the mutex guard is not assigned to a variable and so, even if the
|
||||
/// // scope does not end after this line, the mutex is still released: there is
|
||||
/// // no deadlock.
|
||||
/// *res_mutex.lock().unwrap() += result;
|
||||
///
|
||||
/// threads.into_iter().for_each(|thread| {
|
||||
/// thread
|
||||
/// .join()
|
||||
/// .expect("The thread creating or execution failed !")
|
||||
/// });
|
||||
///
|
||||
/// assert_eq!(*res_mutex.lock().unwrap(), 800);
|
||||
/// ```
|
||||
///
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "Mutex")]
|
||||
pub struct Mutex<T: ?Sized> {
|
||||
inner: sys::Mutex,
|
||||
poison: poison::Flag,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire
|
||||
/// the owned `T` from the `Mutex` via [`into_inner`].
|
||||
///
|
||||
/// [`into_inner`]: Mutex::into_inner
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
|
||||
|
||||
/// `T` must be `Send` for [`Mutex`] to be `Sync`.
|
||||
/// This ensures that the protected data can be accessed safely from multiple threads
|
||||
/// without causing data races or other unsafe behavior.
|
||||
///
|
||||
/// [`Mutex<T>`] provides mutable access to `T` to one thread at a time. However, it's essential
|
||||
/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in
|
||||
/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer,
|
||||
/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap
|
||||
/// allocation with a non-atomic reference count. If we were to use `Mutex<Rc<_>>`, it would
|
||||
/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable
|
||||
/// to potential data races.
|
||||
///
|
||||
/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available
|
||||
/// to one thread at a time if `T` is not `Sync`.
|
||||
///
|
||||
/// [`Rc`]: crate::rc::Rc
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
|
||||
|
||||
/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
|
||||
/// dropped (falls out of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// [`Deref`] and [`DerefMut`] implementations.
|
||||
///
|
||||
/// This structure is created by the [`lock`] and [`try_lock`] methods on
|
||||
/// [`Mutex`].
|
||||
///
|
||||
/// [`lock`]: Mutex::lock
|
||||
/// [`try_lock`]: Mutex::try_lock
|
||||
#[must_use = "if unused the Mutex will immediately unlock"]
|
||||
#[must_not_suspend = "holding a MutexGuard across suspend \
|
||||
points can cause deadlocks, delays, \
|
||||
and cause Futures to not implement `Send`"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[clippy::has_significant_drop]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "MutexGuard")]
|
||||
pub struct MutexGuard<'a, T: ?Sized + 'a> {
|
||||
lock: &'a Mutex<T>,
|
||||
poison: poison::Guard,
|
||||
}
|
||||
|
||||
/// A [`MutexGuard`] is not `Send` to maximize platform portability.
|
||||
///
|
||||
/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to
|
||||
/// release mutex locks on the same thread they were acquired.
|
||||
/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from
|
||||
/// another thread.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
|
||||
|
||||
/// `T` must be `Sync` for a [`MutexGuard<T>`] to be `Sync`
|
||||
/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`).
|
||||
#[stable(feature = "mutexguard", since = "1.19.0")]
|
||||
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
|
||||
|
||||
/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a
|
||||
/// subfield of the protected data. When this structure is dropped (falls out
|
||||
/// of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The main difference between `MappedMutexGuard` and [`MutexGuard`] is that the
|
||||
/// former cannot be used with [`Condvar`], since that
|
||||
/// could introduce soundness issues if the locked object is modified by another
|
||||
/// thread while the `Mutex` is unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// [`Deref`] and [`DerefMut`] implementations.
|
||||
///
|
||||
/// This structure is created by the [`map`] and [`filter_map`] methods on
|
||||
/// [`MutexGuard`].
|
||||
///
|
||||
/// [`map`]: MutexGuard::map
|
||||
/// [`filter_map`]: MutexGuard::filter_map
|
||||
/// [`Condvar`]: crate::sync::Condvar
|
||||
#[must_use = "if unused the Mutex will immediately unlock"]
|
||||
#[must_not_suspend = "holding a MappedMutexGuard across suspend \
|
||||
points can cause deadlocks, delays, \
|
||||
and cause Futures to not implement `Send`"]
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
#[clippy::has_significant_drop]
|
||||
pub struct MappedMutexGuard<'a, T: ?Sized + 'a> {
|
||||
// NB: we use a pointer instead of `&'a mut T` to avoid `noalias` violations, because a
|
||||
// `MappedMutexGuard` argument doesn't hold uniqueness for its whole scope, only until it drops.
|
||||
// `NonNull` is covariant over `T`, so we add a `PhantomData<&'a mut T>` field
|
||||
// below for the correct variance over `T` (invariance).
|
||||
data: NonNull<T>,
|
||||
inner: &'a sys::Mutex,
|
||||
poison_flag: &'a poison::Flag,
|
||||
poison: poison::Guard,
|
||||
_variance: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> !Send for MappedMutexGuard<'_, T> {}
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
unsafe impl<T: ?Sized + Sync> Sync for MappedMutexGuard<'_, T> {}
|
||||
|
||||
impl<T> Mutex<T> {
|
||||
/// Creates a new mutex in an unlocked state ready for use.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mutex = Mutex::new(0);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
|
||||
#[inline]
|
||||
pub const fn new(t: T) -> Mutex<T> {
|
||||
Mutex { inner: sys::Mutex::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) }
|
||||
}
|
||||
|
||||
/// Returns the contained value by cloning it.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 7);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn get_cloned(&self) -> Result<T, PoisonError<()>>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
match self.lock() {
|
||||
Ok(guard) => Ok((*guard).clone()),
|
||||
Err(_) => Err(PoisonError::new(())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the contained value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error containing the provided `value` instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 7);
|
||||
/// mutex.set(11).unwrap();
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn set(&self, value: T) -> Result<(), PoisonError<T>> {
|
||||
if mem::needs_drop::<T>() {
|
||||
// If the contained value has non-trivial destructor, we
|
||||
// call that destructor after the lock being released.
|
||||
self.replace(value).map(drop)
|
||||
} else {
|
||||
match self.lock() {
|
||||
Ok(mut guard) => {
|
||||
*guard = value;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(PoisonError::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the contained value with `value`, and returns the old contained value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error containing the provided `value` instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.replace(11).unwrap(), 7);
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn replace(&self, value: T) -> LockResult<T> {
|
||||
match self.lock() {
|
||||
Ok(mut guard) => Ok(mem::replace(&mut *guard, value)),
|
||||
Err(_) => Err(PoisonError::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Mutex<T> {
|
||||
/// Acquires a mutex, blocking the current thread until it is able to do so.
|
||||
///
|
||||
/// This function will block the local thread until it is available to acquire
|
||||
/// the mutex. Upon returning, the thread is the only thread with the lock
|
||||
/// held. An RAII guard is returned to allow scoped unlock of the lock. When
|
||||
/// the guard goes out of scope, the mutex will be unlocked.
|
||||
///
|
||||
/// The exact behavior on locking a mutex in the thread which already holds
|
||||
/// the lock is left unspecified. However, this function will not return on
|
||||
/// the second call (it might panic or deadlock, for example).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error once the mutex is acquired. The acquired
|
||||
/// mutex guard will be contained in the returned error.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function might panic when called if the lock is already held by
|
||||
/// the current thread.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(0));
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// *c_mutex.lock().unwrap() = 10;
|
||||
/// }).join().expect("thread::spawn failed");
|
||||
/// assert_eq!(*mutex.lock().unwrap(), 10);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
|
||||
unsafe {
|
||||
self.inner.lock();
|
||||
MutexGuard::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to acquire this lock.
|
||||
///
|
||||
/// If the lock could not be acquired at this time, then [`Err`] is returned.
|
||||
/// Otherwise, an RAII guard is returned. The lock will be unlocked when the
|
||||
/// guard is dropped.
|
||||
///
|
||||
/// This function does not block.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return the [`Poisoned`] error if the mutex would
|
||||
/// otherwise be acquired. An acquired lock guard will be contained
|
||||
/// in the returned error.
|
||||
///
|
||||
/// If the mutex could not be acquired because it is already locked, then
|
||||
/// this call will return the [`WouldBlock`] error.
|
||||
///
|
||||
/// [`Poisoned`]: TryLockError::Poisoned
|
||||
/// [`WouldBlock`]: TryLockError::WouldBlock
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(0));
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let mut lock = c_mutex.try_lock();
|
||||
/// if let Ok(ref mut mutex) = lock {
|
||||
/// **mutex = 10;
|
||||
/// } else {
|
||||
/// println!("try_lock failed");
|
||||
/// }
|
||||
/// }).join().expect("thread::spawn failed");
|
||||
/// assert_eq!(*mutex.lock().unwrap(), 10);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
|
||||
unsafe {
|
||||
if self.inner.try_lock() {
|
||||
Ok(MutexGuard::new(self)?)
|
||||
} else {
|
||||
Err(TryLockError::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines whether the mutex is poisoned.
|
||||
///
|
||||
/// If another thread is active, the mutex can still become poisoned at any
|
||||
/// time. You should not trust a `false` value for program correctness
|
||||
/// without additional synchronization.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(0));
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
///
|
||||
/// let _ = thread::spawn(move || {
|
||||
/// let _lock = c_mutex.lock().unwrap();
|
||||
/// panic!(); // the mutex gets poisoned
|
||||
/// }).join();
|
||||
/// assert_eq!(mutex.is_poisoned(), true);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn is_poisoned(&self) -> bool {
|
||||
self.poison.get()
|
||||
}
|
||||
|
||||
/// Clear the poisoned state from a mutex.
|
||||
///
|
||||
/// If the mutex is poisoned, it will remain poisoned until this function is called. This
|
||||
/// allows recovering from a poisoned state and marking that it has recovered. For example, if
|
||||
/// the value is overwritten by a known-good value, then the mutex can be marked as
|
||||
/// un-poisoned. Or possibly, the value could be inspected to determine if it is in a
|
||||
/// consistent state, and if so the poison is removed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(0));
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
///
|
||||
/// let _ = thread::spawn(move || {
|
||||
/// let _lock = c_mutex.lock().unwrap();
|
||||
/// panic!(); // the mutex gets poisoned
|
||||
/// }).join();
|
||||
///
|
||||
/// assert_eq!(mutex.is_poisoned(), true);
|
||||
/// let x = mutex.lock().unwrap_or_else(|mut e| {
|
||||
/// **e.get_mut() = 1;
|
||||
/// mutex.clear_poison();
|
||||
/// e.into_inner()
|
||||
/// });
|
||||
/// assert_eq!(mutex.is_poisoned(), false);
|
||||
/// assert_eq!(*x, 1);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "mutex_unpoison", since = "1.77.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn clear_poison(&self) {
|
||||
self.poison.clear();
|
||||
}
|
||||
|
||||
/// Consumes this mutex, returning the underlying data.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error containing the underlying data
|
||||
/// instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mutex = Mutex::new(0);
|
||||
/// assert_eq!(mutex.into_inner().unwrap(), 0);
|
||||
/// ```
|
||||
#[stable(feature = "mutex_into_inner", since = "1.6.0")]
|
||||
pub fn into_inner(self) -> LockResult<T>
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
let data = self.data.into_inner();
|
||||
poison::map_result(self.poison.borrow(), |()| data)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying data.
|
||||
///
|
||||
/// Since this call borrows the `Mutex` mutably, no actual locking needs to
|
||||
/// take place -- the mutable borrow statically guarantees no new locks can be acquired
|
||||
/// while this reference exists. Note that this method does not clear any previous abandoned locks
|
||||
/// (e.g., via [`forget()`] on a [`MutexGuard`]).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error containing a mutable reference to the
|
||||
/// underlying data instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(0);
|
||||
/// *mutex.get_mut().unwrap() = 10;
|
||||
/// assert_eq!(*mutex.lock().unwrap(), 10);
|
||||
/// ```
|
||||
///
|
||||
/// [`forget()`]: mem::forget
|
||||
#[stable(feature = "mutex_get_mut", since = "1.6.0")]
|
||||
pub fn get_mut(&mut self) -> LockResult<&mut T> {
|
||||
let data = self.data.get_mut();
|
||||
poison::map_result(self.poison.borrow(), |()| data)
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the underlying data.
|
||||
///
|
||||
/// The returned pointer is always non-null and properly aligned, but it is
|
||||
/// the user's responsibility to ensure that any reads and writes through it
|
||||
/// are properly synchronized to avoid data races, and that it is not read
|
||||
/// or written through after the mutex is dropped.
|
||||
#[unstable(feature = "mutex_data_ptr", issue = "140368")]
|
||||
pub const fn data_ptr(&self) -> *mut T {
|
||||
self.data.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "mutex_from", since = "1.24.0")]
|
||||
impl<T> From<T> for Mutex<T> {
|
||||
/// Creates a new mutex in an unlocked state ready for use.
|
||||
/// This is equivalent to [`Mutex::new`].
|
||||
fn from(t: T) -> Self {
|
||||
Mutex::new(t)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "mutex_default", since = "1.10.0")]
|
||||
impl<T: Default> Default for Mutex<T> {
|
||||
/// Creates a `Mutex<T>`, with the `Default` value for T.
|
||||
fn default() -> Mutex<T> {
|
||||
Mutex::new(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_struct("Mutex");
|
||||
match self.try_lock() {
|
||||
Ok(guard) => {
|
||||
d.field("data", &&*guard);
|
||||
}
|
||||
Err(TryLockError::Poisoned(err)) => {
|
||||
d.field("data", &&**err.get_ref());
|
||||
}
|
||||
Err(TryLockError::WouldBlock) => {
|
||||
d.field("data", &"<locked>");
|
||||
}
|
||||
}
|
||||
d.field("poisoned", &self.poison.get());
|
||||
d.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
|
||||
unsafe fn new(lock: &'mutex Mutex<T>) -> LockResult<MutexGuard<'mutex, T>> {
|
||||
poison::map_result(lock.poison.guard(), |guard| MutexGuard { lock, poison: guard })
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.lock.poison.done(&self.poison);
|
||||
self.lock.inner.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_guard_impls", since = "1.20.0")]
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// For use in [`nonpoison::condvar`](super::condvar).
|
||||
pub(super) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
|
||||
&guard.lock.inner
|
||||
}
|
||||
|
||||
/// For use in [`nonpoison::condvar`](super::condvar).
|
||||
pub(super) fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag {
|
||||
&guard.lock.poison
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> MutexGuard<'a, T> {
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
|
||||
/// an enum variant.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MutexGuard::map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub fn map<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U>
|
||||
where
|
||||
F: FnOnce(&mut T) -> &mut U,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() }));
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
MappedMutexGuard {
|
||||
data,
|
||||
inner: &orig.lock.inner,
|
||||
poison_flag: &orig.lock.poison,
|
||||
poison: orig.poison.clone(),
|
||||
_variance: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The
|
||||
/// original guard is returned as an `Err(...)` if the closure returns
|
||||
/// `None`.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MutexGuard::filter_map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
|
||||
where
|
||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
match f(unsafe { &mut *orig.lock.data.get() }) {
|
||||
Some(data) => {
|
||||
let data = NonNull::from(data);
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
Ok(MappedMutexGuard {
|
||||
data,
|
||||
inner: &orig.lock.inner,
|
||||
poison_flag: &orig.lock.poison,
|
||||
poison: orig.poison.clone(),
|
||||
_variance: PhantomData,
|
||||
})
|
||||
}
|
||||
None => Err(orig),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> Deref for MappedMutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { self.data.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> DerefMut for MappedMutexGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { self.data.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> Drop for MappedMutexGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.poison_flag.done(&self.poison);
|
||||
self.inner.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedMutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for MappedMutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
|
||||
/// an enum variant.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MappedMutexGuard::map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub fn map<U, F>(mut orig: Self, f: F) -> MappedMutexGuard<'a, U>
|
||||
where
|
||||
F: FnOnce(&mut T) -> &mut U,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
let data = NonNull::from(f(unsafe { orig.data.as_mut() }));
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
MappedMutexGuard {
|
||||
data,
|
||||
inner: orig.inner,
|
||||
poison_flag: orig.poison_flag,
|
||||
poison: orig.poison.clone(),
|
||||
_variance: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The
|
||||
/// original guard is returned as an `Err(...)` if the closure returns
|
||||
/// `None`.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MappedMutexGuard::filter_map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub fn filter_map<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
|
||||
where
|
||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
match f(unsafe { orig.data.as_mut() }) {
|
||||
Some(data) => {
|
||||
let data = NonNull::from(data);
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
Ok(MappedMutexGuard {
|
||||
data,
|
||||
inner: orig.inner,
|
||||
poison_flag: orig.poison_flag,
|
||||
poison: orig.poison.clone(),
|
||||
_variance: PhantomData,
|
||||
})
|
||||
}
|
||||
None => Err(orig),
|
||||
}
|
||||
}
|
||||
}
|
||||
1274
crates/std/src/sync/poison/rwlock.rs
Normal file
1274
crates/std/src/sync/poison/rwlock.rs
Normal file
File diff suppressed because it is too large
Load Diff
432
crates/std/src/sync/reentrant_lock.rs
Normal file
432
crates/std/src/sync/reentrant_lock.rs
Normal file
@@ -0,0 +1,432 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::ops::Deref;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sys::sync as sys;
|
||||
use crate::thread::{ThreadId, current_id};
|
||||
|
||||
/// A re-entrant mutual exclusion lock
|
||||
///
|
||||
/// This lock will block *other* threads waiting for the lock to become
|
||||
/// available. The thread which has already locked the mutex can lock it
|
||||
/// multiple times without blocking, preventing a common source of deadlocks.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Allow recursively calling a function needing synchronization from within
|
||||
/// a callback (this is how [`StdoutLock`](crate::io::StdoutLock) is currently
|
||||
/// implemented):
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(reentrant_lock)]
|
||||
///
|
||||
/// use std::cell::RefCell;
|
||||
/// use std::sync::ReentrantLock;
|
||||
///
|
||||
/// pub struct Log {
|
||||
/// data: RefCell<String>,
|
||||
/// }
|
||||
///
|
||||
/// impl Log {
|
||||
/// pub fn append(&self, msg: &str) {
|
||||
/// self.data.borrow_mut().push_str(msg);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// static LOG: ReentrantLock<Log> = ReentrantLock::new(Log { data: RefCell::new(String::new()) });
|
||||
///
|
||||
/// pub fn with_log<R>(f: impl FnOnce(&Log) -> R) -> R {
|
||||
/// let log = LOG.lock();
|
||||
/// f(&*log)
|
||||
/// }
|
||||
///
|
||||
/// with_log(|log| {
|
||||
/// log.append("Hello");
|
||||
/// with_log(|log| log.append(" there!"));
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
// # Implementation details
|
||||
//
|
||||
// The 'owner' field tracks which thread has locked the mutex.
|
||||
//
|
||||
// We use thread::current_id() as the thread identifier, which is just the
|
||||
// current thread's ThreadId, so it's unique across the process lifetime.
|
||||
//
|
||||
// If `owner` is set to the identifier of the current thread,
|
||||
// we assume the mutex is already locked and instead of locking it again,
|
||||
// we increment `lock_count`.
|
||||
//
|
||||
// When unlocking, we decrement `lock_count`, and only unlock the mutex when
|
||||
// it reaches zero.
|
||||
//
|
||||
// `lock_count` is protected by the mutex and only accessed by the thread that has
|
||||
// locked the mutex, so needs no synchronization.
|
||||
//
|
||||
// `owner` can be checked by other threads that want to see if they already
|
||||
// hold the lock, so needs to be atomic. If it compares equal, we're on the
|
||||
// same thread that holds the mutex and memory access can use relaxed ordering
|
||||
// since we're not dealing with multiple threads. If it's not equal,
|
||||
// synchronization is left to the mutex, making relaxed memory ordering for
|
||||
// the `owner` field fine in all cases.
|
||||
//
|
||||
// On systems without 64 bit atomics we also store the address of a TLS variable
|
||||
// along the 64-bit TID. We then first check that address against the address
|
||||
// of that variable on the current thread, and only if they compare equal do we
|
||||
// compare the actual TIDs. Because we only ever read the TID on the same thread
|
||||
// that it was written on (or a thread sharing the TLS block with that writer thread),
|
||||
// we don't need to further synchronize the TID accesses, so they can be regular 64-bit
|
||||
// non-atomic accesses.
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
pub struct ReentrantLock<T: ?Sized> {
|
||||
mutex: sys::Mutex,
|
||||
owner: Tid,
|
||||
lock_count: UnsafeCell<u32>,
|
||||
data: T,
|
||||
}
|
||||
|
||||
cfg_select!(
|
||||
target_has_atomic = "64" => {
|
||||
use crate::sync::atomic::{Atomic, AtomicU64, Ordering::Relaxed};
|
||||
|
||||
struct Tid(Atomic<u64>);
|
||||
|
||||
impl Tid {
|
||||
const fn new() -> Self {
|
||||
Self(AtomicU64::new(0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn contains(&self, owner: ThreadId) -> bool {
|
||||
owner.as_u64().get() == self.0.load(Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// This is just unsafe to match the API of the Tid type below.
|
||||
unsafe fn set(&self, tid: Option<ThreadId>) {
|
||||
let value = tid.map_or(0, |tid| tid.as_u64().get());
|
||||
self.0.store(value, Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
/// Returns the address of a TLS variable. This is guaranteed to
|
||||
/// be unique across all currently alive threads.
|
||||
fn tls_addr() -> usize {
|
||||
thread_local! { static X: u8 = const { 0u8 } };
|
||||
|
||||
X.with(|p| <*const u8>::addr(p))
|
||||
}
|
||||
|
||||
use crate::sync::atomic::{
|
||||
Atomic,
|
||||
AtomicUsize,
|
||||
Ordering,
|
||||
};
|
||||
|
||||
struct Tid {
|
||||
// When a thread calls `set()`, this value gets updated to
|
||||
// the address of a thread local on that thread. This is
|
||||
// used as a first check in `contains()`; if the `tls_addr`
|
||||
// doesn't match the TLS address of the current thread, then
|
||||
// the ThreadId also can't match. Only if the TLS addresses do
|
||||
// match do we read out the actual TID.
|
||||
// Note also that we can use relaxed atomic operations here, because
|
||||
// we only ever read from the tid if `tls_addr` matches the current
|
||||
// TLS address. In that case, either the tid has been set by
|
||||
// the current thread, or by a thread that has terminated before
|
||||
// the current thread's `tls_addr` was allocated. In either case, no further
|
||||
// synchronization is needed (as per <https://github.com/rust-lang/miri/issues/3450>)
|
||||
tls_addr: Atomic<usize>,
|
||||
tid: UnsafeCell<u64>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Tid {}
|
||||
unsafe impl Sync for Tid {}
|
||||
|
||||
impl Tid {
|
||||
const fn new() -> Self {
|
||||
Self { tls_addr: AtomicUsize::new(0), tid: UnsafeCell::new(0) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// NOTE: This assumes that `owner` is the ID of the current
|
||||
// thread, and may spuriously return `false` if that's not the case.
|
||||
fn contains(&self, owner: ThreadId) -> bool {
|
||||
// We must call `tls_addr()` *before* doing the load to ensure that if we reuse an
|
||||
// earlier thread's address, the `tls_addr.load()` below happens-after everything
|
||||
// that thread did.
|
||||
let tls_addr = tls_addr();
|
||||
// SAFETY: See the comments in the struct definition.
|
||||
self.tls_addr.load(Ordering::Relaxed) == tls_addr
|
||||
&& unsafe { *self.tid.get() } == owner.as_u64().get()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// This may only be called by one thread at a time, and can lead to
|
||||
// race conditions otherwise.
|
||||
unsafe fn set(&self, tid: Option<ThreadId>) {
|
||||
// It's important that we set `self.tls_addr` to 0 if the tid is
|
||||
// cleared. Otherwise, there might be race conditions between
|
||||
// `set()` and `get()`.
|
||||
let tls_addr = if tid.is_some() { tls_addr() } else { 0 };
|
||||
let value = tid.map_or(0, |tid| tid.as_u64().get());
|
||||
self.tls_addr.store(tls_addr, Ordering::Relaxed);
|
||||
unsafe { *self.tid.get() = value };
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
unsafe impl<T: Send + ?Sized> Send for ReentrantLock<T> {}
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
unsafe impl<T: Send + ?Sized> Sync for ReentrantLock<T> {}
|
||||
|
||||
// Because of the `UnsafeCell`, these traits are not implemented automatically
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: UnwindSafe + ?Sized> UnwindSafe for ReentrantLock<T> {}
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: RefUnwindSafe + ?Sized> RefUnwindSafe for ReentrantLock<T> {}
|
||||
|
||||
/// An RAII implementation of a "scoped lock" of a re-entrant lock. When this
|
||||
/// structure is dropped (falls out of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// [`Deref`] implementation.
|
||||
///
|
||||
/// This structure is created by the [`lock`](ReentrantLock::lock) method on
|
||||
/// [`ReentrantLock`].
|
||||
///
|
||||
/// # Mutability
|
||||
///
|
||||
/// Unlike [`MutexGuard`](super::MutexGuard), `ReentrantLockGuard` does not
|
||||
/// implement [`DerefMut`](crate::ops::DerefMut), because implementation of
|
||||
/// the trait would violate Rust’s reference aliasing rules. Use interior
|
||||
/// mutability (usually [`RefCell`](crate::cell::RefCell)) in order to mutate
|
||||
/// the guarded data.
|
||||
#[must_use = "if unused the ReentrantLock will immediately unlock"]
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
pub struct ReentrantLockGuard<'a, T: ?Sized + 'a> {
|
||||
lock: &'a ReentrantLock<T>,
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: ?Sized> !Send for ReentrantLockGuard<'_, T> {}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
unsafe impl<T: ?Sized + Sync> Sync for ReentrantLockGuard<'_, T> {}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T> ReentrantLock<T> {
|
||||
/// Creates a new re-entrant lock in an unlocked state ready for use.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(reentrant_lock)]
|
||||
/// use std::sync::ReentrantLock;
|
||||
///
|
||||
/// let lock = ReentrantLock::new(0);
|
||||
/// ```
|
||||
pub const fn new(t: T) -> ReentrantLock<T> {
|
||||
ReentrantLock {
|
||||
mutex: sys::Mutex::new(),
|
||||
owner: Tid::new(),
|
||||
lock_count: UnsafeCell::new(0),
|
||||
data: t,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes this lock, returning the underlying data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(reentrant_lock)]
|
||||
///
|
||||
/// use std::sync::ReentrantLock;
|
||||
///
|
||||
/// let lock = ReentrantLock::new(0);
|
||||
/// assert_eq!(lock.into_inner(), 0);
|
||||
/// ```
|
||||
pub fn into_inner(self) -> T {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: ?Sized> ReentrantLock<T> {
|
||||
/// Acquires the lock, blocking the current thread until it is able to do
|
||||
/// so.
|
||||
///
|
||||
/// This function will block the caller until it is available to acquire
|
||||
/// the lock. Upon returning, the thread is the only thread with the lock
|
||||
/// held. When the thread calling this method already holds the lock, the
|
||||
/// call succeeds without blocking.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(reentrant_lock)]
|
||||
/// use std::cell::Cell;
|
||||
/// use std::sync::{Arc, ReentrantLock};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let lock = Arc::new(ReentrantLock::new(Cell::new(0)));
|
||||
/// let c_lock = Arc::clone(&lock);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// c_lock.lock().set(10);
|
||||
/// }).join().expect("thread::spawn failed");
|
||||
/// assert_eq!(lock.lock().get(), 10);
|
||||
/// ```
|
||||
pub fn lock(&self) -> ReentrantLockGuard<'_, T> {
|
||||
let this_thread = current_id();
|
||||
// Safety: We only touch lock_count when we own the inner mutex.
|
||||
// Additionally, we only call `self.owner.set()` while holding
|
||||
// the inner mutex, so no two threads can call it concurrently.
|
||||
unsafe {
|
||||
if self.owner.contains(this_thread) {
|
||||
self.increment_lock_count().expect("lock count overflow in reentrant mutex");
|
||||
} else {
|
||||
self.mutex.lock();
|
||||
self.owner.set(Some(this_thread));
|
||||
debug_assert_eq!(*self.lock_count.get(), 0);
|
||||
*self.lock_count.get() = 1;
|
||||
}
|
||||
}
|
||||
ReentrantLockGuard { lock: self }
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying data.
|
||||
///
|
||||
/// Since this call borrows the `ReentrantLock` mutably, no actual locking
|
||||
/// needs to take place -- the mutable borrow statically guarantees no locks
|
||||
/// exist.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(reentrant_lock)]
|
||||
/// use std::sync::ReentrantLock;
|
||||
///
|
||||
/// let mut lock = ReentrantLock::new(0);
|
||||
/// *lock.get_mut() = 10;
|
||||
/// assert_eq!(*lock.lock(), 10);
|
||||
/// ```
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.data
|
||||
}
|
||||
|
||||
/// Attempts to acquire this lock.
|
||||
///
|
||||
/// If the lock could not be acquired at this time, then `None` is returned.
|
||||
/// Otherwise, an RAII guard is returned.
|
||||
///
|
||||
/// This function does not block.
|
||||
// FIXME maybe make it a public part of the API?
|
||||
#[unstable(issue = "none", feature = "std_internals")]
|
||||
#[doc(hidden)]
|
||||
pub fn try_lock(&self) -> Option<ReentrantLockGuard<'_, T>> {
|
||||
let this_thread = current_id();
|
||||
// Safety: We only touch lock_count when we own the inner mutex.
|
||||
// Additionally, we only call `self.owner.set()` while holding
|
||||
// the inner mutex, so no two threads can call it concurrently.
|
||||
unsafe {
|
||||
if self.owner.contains(this_thread) {
|
||||
self.increment_lock_count()?;
|
||||
Some(ReentrantLockGuard { lock: self })
|
||||
} else if self.mutex.try_lock() {
|
||||
self.owner.set(Some(this_thread));
|
||||
debug_assert_eq!(*self.lock_count.get(), 0);
|
||||
*self.lock_count.get() = 1;
|
||||
Some(ReentrantLockGuard { lock: self })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the underlying data.
|
||||
///
|
||||
/// The returned pointer is always non-null and properly aligned, but it is
|
||||
/// the user's responsibility to ensure that any reads through it are
|
||||
/// properly synchronized to avoid data races, and that it is not read
|
||||
/// through after the lock is dropped.
|
||||
#[unstable(feature = "reentrant_lock_data_ptr", issue = "140368")]
|
||||
pub const fn data_ptr(&self) -> *const T {
|
||||
&raw const self.data
|
||||
}
|
||||
|
||||
unsafe fn increment_lock_count(&self) -> Option<()> {
|
||||
unsafe {
|
||||
*self.lock_count.get() = (*self.lock_count.get()).checked_add(1)?;
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: fmt::Debug + ?Sized> fmt::Debug for ReentrantLock<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_struct("ReentrantLock");
|
||||
match self.try_lock() {
|
||||
Some(v) => d.field("data", &&*v),
|
||||
None => d.field("data", &format_args!("<locked>")),
|
||||
};
|
||||
d.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: Default> Default for ReentrantLock<T> {
|
||||
fn default() -> Self {
|
||||
Self::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T> From<T> for ReentrantLock<T> {
|
||||
fn from(t: T) -> Self {
|
||||
Self::new(t)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: ?Sized> Deref for ReentrantLockGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.lock.data
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: fmt::Debug + ?Sized> fmt::Debug for ReentrantLockGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: fmt::Display + ?Sized> fmt::Display for ReentrantLockGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: ?Sized> Drop for ReentrantLockGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
// Safety: We own the lock.
|
||||
unsafe {
|
||||
*self.lock.lock_count.get() -= 1;
|
||||
if *self.lock.lock_count.get() == 0 {
|
||||
self.lock.owner.set(None);
|
||||
self.lock.mutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
crates/std/src/sys/io/error/generic.rs
Normal file
15
crates/std/src/sys/io/error/generic.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
pub fn errno() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
pub fn is_interrupted(_code: i32) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
|
||||
crate::io::ErrorKind::Uncategorized
|
||||
}
|
||||
|
||||
pub fn error_string(_errno: i32) -> String {
|
||||
"operation successful".to_string()
|
||||
}
|
||||
56
crates/std/src/sys/io/error/mod.rs
Normal file
56
crates/std/src/sys/io/error/mod.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
cfg_select! {
|
||||
target_os = "hermit" => {
|
||||
mod hermit;
|
||||
pub use hermit::*;
|
||||
}
|
||||
target_os = "motor" => {
|
||||
mod motor;
|
||||
pub use motor::*;
|
||||
}
|
||||
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||
mod sgx;
|
||||
pub use sgx::*;
|
||||
}
|
||||
target_os = "solid_asp3" => {
|
||||
mod solid;
|
||||
pub use solid::*;
|
||||
}
|
||||
target_os = "teeos" => {
|
||||
mod teeos;
|
||||
pub use teeos::*;
|
||||
}
|
||||
target_os = "uefi" => {
|
||||
mod uefi;
|
||||
pub use uefi::*;
|
||||
}
|
||||
target_family = "unix" => {
|
||||
mod unix;
|
||||
pub use unix::*;
|
||||
}
|
||||
target_os = "wasi" => {
|
||||
mod wasi;
|
||||
pub use wasi::*;
|
||||
}
|
||||
target_os = "windows" => {
|
||||
mod windows;
|
||||
pub use windows::*;
|
||||
}
|
||||
target_os = "xous" => {
|
||||
mod xous;
|
||||
pub use xous::*;
|
||||
}
|
||||
any(
|
||||
target_os = "vexos",
|
||||
target_family = "wasm",
|
||||
target_os = "zkvm",
|
||||
target_os = "survos",
|
||||
) => {
|
||||
mod generic;
|
||||
pub use generic::*;
|
||||
}
|
||||
}
|
||||
|
||||
pub type RawOsError = cfg_select! {
|
||||
target_os = "uefi" => usize,
|
||||
_ => i32,
|
||||
};
|
||||
52
crates/std/src/sys/io/io_slice/unsupported.rs
Normal file
52
crates/std/src/sys/io/io_slice/unsupported.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use crate::mem;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct IoSlice<'a>(&'a [u8]);
|
||||
|
||||
impl<'a> IoSlice<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
|
||||
IoSlice(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
self.0 = &self.0[n..]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn as_slice(&self) -> &'a [u8] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IoSliceMut<'a>(&'a mut [u8]);
|
||||
|
||||
impl<'a> IoSliceMut<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
|
||||
IoSliceMut(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
let slice = mem::take(&mut self.0);
|
||||
let (_, remaining) = slice.split_at_mut(n);
|
||||
self.0 = remaining;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn into_slice(self) -> &'a mut [u8] {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
3
crates/std/src/sys/io/is_terminal/unsupported.rs
Normal file
3
crates/std/src/sys/io/is_terminal/unsupported.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub fn is_terminal<T>(_: &T) -> bool {
|
||||
false
|
||||
}
|
||||
23
crates/std/src/sys/io/kernel_copy/mod.rs
Normal file
23
crates/std/src/sys/io/kernel_copy/mod.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
pub enum CopyState {
|
||||
#[cfg_attr(not(any(target_os = "linux", target_os = "android")), expect(dead_code))]
|
||||
Ended(u64),
|
||||
Fallback(u64),
|
||||
}
|
||||
|
||||
cfg_select! {
|
||||
any(target_os = "linux", target_os = "android") => {
|
||||
mod linux;
|
||||
pub use linux::kernel_copy;
|
||||
}
|
||||
_ => {
|
||||
use crate::io::{Result, Read, Write};
|
||||
|
||||
pub fn kernel_copy<R: ?Sized, W: ?Sized>(_reader: &mut R, _writer: &mut W) -> Result<CopyState>
|
||||
where
|
||||
R: Read,
|
||||
W: Write,
|
||||
{
|
||||
Ok(CopyState::Fallback(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
72
crates/std/src/sys/io/mod.rs
Normal file
72
crates/std/src/sys/io/mod.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
mod error;
|
||||
|
||||
mod io_slice {
|
||||
cfg_select! {
|
||||
any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", target_os = "wasi") => {
|
||||
mod iovec;
|
||||
pub use iovec::*;
|
||||
}
|
||||
target_os = "windows" => {
|
||||
mod windows;
|
||||
pub use windows::*;
|
||||
}
|
||||
target_os = "uefi" => {
|
||||
mod uefi;
|
||||
pub use uefi::*;
|
||||
}
|
||||
_ => {
|
||||
mod unsupported;
|
||||
pub use unsupported::*;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod is_terminal {
|
||||
cfg_select! {
|
||||
any(target_family = "unix", target_os = "wasi") => {
|
||||
mod isatty;
|
||||
pub use isatty::*;
|
||||
}
|
||||
target_os = "windows" => {
|
||||
mod windows;
|
||||
pub use windows::*;
|
||||
}
|
||||
target_os = "hermit" => {
|
||||
mod hermit;
|
||||
pub use hermit::*;
|
||||
}
|
||||
target_os = "motor" => {
|
||||
mod motor;
|
||||
pub use motor::*;
|
||||
}
|
||||
_ => {
|
||||
mod unsupported;
|
||||
pub use unsupported::*;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod kernel_copy;
|
||||
|
||||
#[cfg_attr(not(target_os = "linux"), allow(unused_imports))]
|
||||
#[cfg(all(
|
||||
target_family = "unix",
|
||||
not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems"))
|
||||
))]
|
||||
pub use error::errno_location;
|
||||
#[cfg_attr(not(target_os = "linux"), allow(unused_imports))]
|
||||
#[cfg(any(
|
||||
all(target_family = "unix", not(any(target_os = "vxworks", target_os = "rtems"))),
|
||||
target_os = "wasi",
|
||||
))]
|
||||
pub use error::set_errno;
|
||||
pub use error::{RawOsError, decode_error_kind, errno, error_string, is_interrupted};
|
||||
pub use io_slice::{IoSlice, IoSliceMut};
|
||||
pub use is_terminal::is_terminal;
|
||||
pub use kernel_copy::{CopyState, kernel_copy};
|
||||
|
||||
// Bare metal platforms usually have very small amounts of RAM
|
||||
// (in the order of hundreds of KB)
|
||||
pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 };
|
||||
@@ -11,6 +11,9 @@ pub mod time;
|
||||
pub mod random;
|
||||
pub mod thread_local;
|
||||
pub mod alloc;
|
||||
pub mod io;
|
||||
pub mod pipe;
|
||||
pub mod stdio;
|
||||
// pub mod fs;
|
||||
|
||||
/// A trait for viewing representations from std types.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use core::clone::CloneToUninit;
|
||||
|
||||
use crate::borrow::Cow;
|
||||
use alloc_crate::bstr::ByteStr;
|
||||
use crate::bstr::ByteStr;
|
||||
use alloc_crate::collections::TryReserveError;
|
||||
use crate::rc::Rc;
|
||||
use alloc_crate::sync::Arc;
|
||||
|
||||
20
crates/std/src/sys/pipe/mod.rs
Normal file
20
crates/std/src/sys/pipe/mod.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
cfg_select! {
|
||||
unix => {
|
||||
mod unix;
|
||||
pub use unix::{Pipe, pipe};
|
||||
}
|
||||
windows => {
|
||||
mod windows;
|
||||
pub use windows::{Pipe, pipe};
|
||||
}
|
||||
target_os = "motor" => {
|
||||
mod motor;
|
||||
pub use motor::{Pipe, pipe};
|
||||
}
|
||||
_ => {
|
||||
mod unsupported;
|
||||
pub use unsupported::{Pipe, pipe};
|
||||
}
|
||||
}
|
||||
101
crates/std/src/sys/pipe/unsupported.rs
Normal file
101
crates/std/src/sys/pipe/unsupported.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use crate::fmt;
|
||||
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
|
||||
|
||||
pub struct Pipe(!);
|
||||
|
||||
#[inline]
|
||||
pub fn pipe() -> io::Result<(Pipe, Pipe)> {
|
||||
Err(io::Error::UNSUPPORTED_PLATFORM)
|
||||
}
|
||||
|
||||
impl Pipe {
|
||||
pub fn try_clone(&self) -> io::Result<Self> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn is_read_vectored(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn read_to_end(&self, _buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn diverge(&self) -> ! {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Pipe {
|
||||
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
|
||||
mod unix_traits {
|
||||
use super::Pipe;
|
||||
use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||
use crate::sys::{FromInner, IntoInner};
|
||||
|
||||
impl AsRawFd for Pipe {
|
||||
#[inline]
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsFd for Pipe {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Pipe {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawFd for Pipe {
|
||||
unsafe fn from_raw_fd(_: RawFd) -> Self {
|
||||
panic!("creating pipe on this platform is unsupported!")
|
||||
}
|
||||
}
|
||||
|
||||
impl FromInner<OwnedFd> for Pipe {
|
||||
fn from_inner(_: OwnedFd) -> Self {
|
||||
panic!("creating pipe on this platform is unsupported!")
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoInner<OwnedFd> for Pipe {
|
||||
fn into_inner(self) -> OwnedFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
52
crates/std/src/sys/stdio/mod.rs
Normal file
52
crates/std/src/sys/stdio/mod.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
cfg_select! {
|
||||
any(target_family = "unix", target_os = "hermit", target_os = "wasi") => {
|
||||
mod unix;
|
||||
pub use unix::*;
|
||||
}
|
||||
target_os = "windows" => {
|
||||
mod windows;
|
||||
pub use windows::*;
|
||||
}
|
||||
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||
mod sgx;
|
||||
pub use sgx::*;
|
||||
}
|
||||
target_os = "motor" => {
|
||||
mod motor;
|
||||
pub use motor::*;
|
||||
}
|
||||
target_os = "solid_asp3" => {
|
||||
mod solid;
|
||||
pub use solid::*;
|
||||
}
|
||||
target_os = "teeos" => {
|
||||
mod teeos;
|
||||
pub use teeos::*;
|
||||
}
|
||||
target_os = "trusty" => {
|
||||
mod trusty;
|
||||
pub use trusty::*;
|
||||
}
|
||||
target_os = "uefi" => {
|
||||
mod uefi;
|
||||
pub use uefi::*;
|
||||
}
|
||||
target_os = "vexos" => {
|
||||
mod vexos;
|
||||
pub use vexos::*;
|
||||
}
|
||||
target_os = "xous" => {
|
||||
mod xous;
|
||||
pub use xous::*;
|
||||
}
|
||||
target_os = "zkvm" => {
|
||||
mod zkvm;
|
||||
pub use zkvm::*;
|
||||
}
|
||||
_ => {
|
||||
mod unsupported;
|
||||
pub use unsupported::*;
|
||||
}
|
||||
}
|
||||
106
crates/std/src/sys/stdio/unsupported.rs
Normal file
106
crates/std/src/sys/stdio/unsupported.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
|
||||
|
||||
pub struct Stdin;
|
||||
pub struct Stdout;
|
||||
pub type Stderr = Stdout;
|
||||
|
||||
impl Stdin {
|
||||
pub const fn new() -> Stdin {
|
||||
Stdin
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for Stdin {
|
||||
#[inline]
|
||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
// Do not force `Chain<Empty, T>` or `Chain<T, Empty>` to use vectored
|
||||
// reads, unless the other reader is vectored.
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
if cursor.capacity() != 0 { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stdout {
|
||||
pub const fn new() -> Stdout {
|
||||
Stdout
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for Stdout {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Keep the default write_fmt so the `fmt::Arguments` are still evaluated.
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub const STDIN_BUF_SIZE: usize = 0;
|
||||
|
||||
pub fn is_ebadf(_err: &io::Error) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn panic_output() -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use crate::cell::Cell;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::ops::Deref;
|
||||
use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
|
||||
use core::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
|
||||
use crate::ptr::{self, NonNull};
|
||||
|
||||
#[doc(hidden)]
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
pub mod builder;
|
||||
pub mod current;
|
||||
pub mod functions;
|
||||
pub mod id;
|
||||
pub mod join_handle;
|
||||
pub mod lifecycle;
|
||||
pub mod local;
|
||||
pub mod main_thread;
|
||||
pub mod thread;
|
||||
|
||||
pub use crate::sys::thread::*;
|
||||
use core::any::Any;
|
||||
|
||||
pub use crate::sys::thread::yield_now;
|
||||
pub(crate) use current::current_or_unnamed;
|
||||
pub use current::current_id;
|
||||
pub use functions::sleep;
|
||||
pub use id::ThreadId;
|
||||
pub use local::LocalKey;
|
||||
pub use thread::Thread;
|
||||
pub(crate) use lifecycle::ThreadInit;
|
||||
|
||||
// Implementation details used by the thread_local!{} macro.
|
||||
#[doc(hidden)]
|
||||
@@ -11,14 +27,6 @@ pub mod local_impl {
|
||||
pub use crate::sys::thread_local::*;
|
||||
}
|
||||
|
||||
pub struct ThreadInit {
|
||||
pub handle: Thread,
|
||||
pub rust_start: Box<dyn FnOnce() + Send>,
|
||||
}
|
||||
impl ThreadInit {
|
||||
/// Initialize the 'current thread' mechanism on this thread, returning the
|
||||
/// Rust entry point.
|
||||
pub fn init(self: Box<Self>) -> Box<dyn FnOnce() + Send> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(search_unbox)]
|
||||
pub type Result<T> = crate::result::Result<T, Box<dyn Any + Send + 'static>>;
|
||||
|
||||
263
crates/std/src/thread/builder.rs
Normal file
263
crates/std/src/thread/builder.rs
Normal file
@@ -0,0 +1,263 @@
|
||||
use super::join_handle::JoinHandle;
|
||||
use super::lifecycle::spawn_unchecked;
|
||||
use crate::io;
|
||||
|
||||
/// Thread factory, which can be used in order to configure the properties of
|
||||
/// a new thread.
|
||||
///
|
||||
/// Methods can be chained on it in order to configure it.
|
||||
///
|
||||
/// The two configurations available are:
|
||||
///
|
||||
/// - [`name`]: specifies an [associated name for the thread][naming-threads]
|
||||
/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size]
|
||||
///
|
||||
/// The [`spawn`] method will take ownership of the builder and create an
|
||||
/// [`io::Result`] to the thread handle with the given configuration.
|
||||
///
|
||||
/// The [`thread::spawn`] free function uses a `Builder` with default
|
||||
/// configuration and [`unwrap`]s its return value.
|
||||
///
|
||||
/// You may want to use [`spawn`] instead of [`thread::spawn`], when you want
|
||||
/// to recover from a failure to launch a thread, indeed the free function will
|
||||
/// panic where the `Builder` method will return a [`io::Result`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new();
|
||||
///
|
||||
/// let handler = builder.spawn(|| {
|
||||
/// // thread code
|
||||
/// }).unwrap();
|
||||
///
|
||||
/// handler.join().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [`stack_size`]: Builder::stack_size
|
||||
/// [`name`]: Builder::name
|
||||
/// [`spawn`]: Builder::spawn
|
||||
/// [`thread::spawn`]: super::spawn
|
||||
/// [`unwrap`]: crate::result::Result::unwrap
|
||||
/// [naming-threads]: ./index.html#naming-threads
|
||||
/// [stack-size]: ./index.html#stack-size
|
||||
#[must_use = "must eventually spawn the thread"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
/// A name for the thread-to-be, for identification in panic messages
|
||||
pub(super) name: Option<String>,
|
||||
/// The size of the stack for the spawned thread in bytes
|
||||
pub(super) stack_size: Option<usize>,
|
||||
/// Skip running and inheriting the thread spawn hooks
|
||||
pub(super) no_hooks: bool,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Generates the base configuration for spawning a thread, from which
|
||||
/// configuration methods can be chained.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new()
|
||||
/// .name("foo".into())
|
||||
/// .stack_size(32 * 1024);
|
||||
///
|
||||
/// let handler = builder.spawn(|| {
|
||||
/// // thread code
|
||||
/// }).unwrap();
|
||||
///
|
||||
/// handler.join().unwrap();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new() -> Builder {
|
||||
Builder { name: None, stack_size: None, no_hooks: false }
|
||||
}
|
||||
|
||||
/// Names the thread-to-be. Currently the name is used for identification
|
||||
/// only in panic messages.
|
||||
///
|
||||
/// The name must not contain null bytes (`\0`).
|
||||
///
|
||||
/// For more information about named threads, see
|
||||
/// [this module-level documentation][naming-threads].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new()
|
||||
/// .name("foo".into());
|
||||
///
|
||||
/// let handler = builder.spawn(|| {
|
||||
/// assert_eq!(thread::current().name(), Some("foo"))
|
||||
/// }).unwrap();
|
||||
///
|
||||
/// handler.join().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [naming-threads]: ./index.html#naming-threads
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn name(mut self, name: String) -> Builder {
|
||||
self.name = Some(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the size of the stack (in bytes) for the new thread.
|
||||
///
|
||||
/// The actual stack size may be greater than this value if
|
||||
/// the platform specifies a minimal stack size.
|
||||
///
|
||||
/// For more information about the stack size for threads, see
|
||||
/// [this module-level documentation][stack-size].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new().stack_size(32 * 1024);
|
||||
/// ```
|
||||
///
|
||||
/// [stack-size]: ./index.html#stack-size
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn stack_size(mut self, size: usize) -> Builder {
|
||||
self.stack_size = Some(size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Disables running and inheriting [spawn hooks].
|
||||
///
|
||||
/// Use this if the parent thread is in no way relevant for the child thread.
|
||||
/// For example, when lazily spawning threads for a thread pool.
|
||||
///
|
||||
/// [spawn hooks]: super::add_spawn_hook
|
||||
#[unstable(feature = "thread_spawn_hook", issue = "132951")]
|
||||
pub fn no_hooks(mut self) -> Builder {
|
||||
self.no_hooks = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Spawns a new thread by taking ownership of the `Builder`, and returns an
|
||||
/// [`io::Result`] to its [`JoinHandle`].
|
||||
///
|
||||
/// The spawned thread may outlive the caller (unless the caller thread
|
||||
/// is the main thread; the whole process is terminated when the main
|
||||
/// thread finishes). The join handle can be used to block on
|
||||
/// termination of the spawned thread, including recovering its panics.
|
||||
///
|
||||
/// For a more complete documentation see [`thread::spawn`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Unlike the [`spawn`] free function, this method yields an
|
||||
/// [`io::Result`] to capture any failure to create the thread at
|
||||
/// the OS level.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a thread name was set and it contained null bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new();
|
||||
///
|
||||
/// let handler = builder.spawn(|| {
|
||||
/// // thread code
|
||||
/// }).unwrap();
|
||||
///
|
||||
/// handler.join().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [`thread::spawn`]: super::spawn
|
||||
/// [`spawn`]: super::spawn
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
F: Send + 'static,
|
||||
T: Send + 'static,
|
||||
{
|
||||
unsafe { self.spawn_unchecked(f) }
|
||||
}
|
||||
|
||||
/// Spawns a new thread without any lifetime restrictions by taking ownership
|
||||
/// of the `Builder`, and returns an [`io::Result`] to its [`JoinHandle`].
|
||||
///
|
||||
/// The spawned thread may outlive the caller (unless the caller thread
|
||||
/// is the main thread; the whole process is terminated when the main
|
||||
/// thread finishes). The join handle can be used to block on
|
||||
/// termination of the spawned thread, including recovering its panics.
|
||||
///
|
||||
/// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`],
|
||||
/// except for the relaxed lifetime bounds, which render it unsafe.
|
||||
/// For a more complete documentation see [`thread::spawn`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Unlike the [`spawn`] free function, this method yields an
|
||||
/// [`io::Result`] to capture any failure to create the thread at
|
||||
/// the OS level.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a thread name was set and it contained null bytes.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller has to ensure that the spawned thread does not outlive any
|
||||
/// references in the supplied thread closure and its return type.
|
||||
/// This can be guaranteed in two ways:
|
||||
///
|
||||
/// - ensure that [`join`][`JoinHandle::join`] is called before any referenced
|
||||
/// data is dropped
|
||||
/// - use only types with `'static` lifetime bounds, i.e., those with no or only
|
||||
/// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`]
|
||||
/// and [`thread::spawn`] enforce this property statically)
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new();
|
||||
///
|
||||
/// let x = 1;
|
||||
/// let thread_x = &x;
|
||||
///
|
||||
/// let handler = unsafe {
|
||||
/// builder.spawn_unchecked(move || {
|
||||
/// println!("x = {}", *thread_x);
|
||||
/// }).unwrap()
|
||||
/// };
|
||||
///
|
||||
/// // caller has to ensure `join()` is called, otherwise
|
||||
/// // it is possible to access freed memory if `x` gets
|
||||
/// // dropped before the thread closure is executed!
|
||||
/// handler.join().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [`thread::spawn`]: super::spawn
|
||||
/// [`spawn`]: super::spawn
|
||||
#[stable(feature = "thread_spawn_unchecked", since = "1.82.0")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub unsafe fn spawn_unchecked<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
F: Send,
|
||||
T: Send,
|
||||
{
|
||||
let Builder { name, stack_size, no_hooks } = self;
|
||||
Ok(JoinHandle(unsafe { spawn_unchecked(name, stack_size, no_hooks, None, f) }?))
|
||||
}
|
||||
}
|
||||
332
crates/std/src/thread/current.rs
Normal file
332
crates/std/src/thread/current.rs
Normal file
@@ -0,0 +1,332 @@
|
||||
use super::id::ThreadId;
|
||||
use super::main_thread;
|
||||
use super::thread::Thread;
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::ptr;
|
||||
use crate::sys::thread as imp;
|
||||
use crate::sys::thread_local::local_pointer;
|
||||
|
||||
const NONE: *mut () = ptr::null_mut();
|
||||
const BUSY: *mut () = ptr::without_provenance_mut(1);
|
||||
const DESTROYED: *mut () = ptr::without_provenance_mut(2);
|
||||
|
||||
local_pointer! {
|
||||
static CURRENT;
|
||||
}
|
||||
|
||||
/// Persistent storage for the thread ID.
|
||||
///
|
||||
/// We store the thread ID so that it never gets destroyed during the lifetime
|
||||
/// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s.
|
||||
pub(super) mod id {
|
||||
use super::*;
|
||||
|
||||
cfg_select! {
|
||||
target_thread_local => {
|
||||
use crate::cell::Cell;
|
||||
|
||||
#[thread_local]
|
||||
static ID: Cell<Option<ThreadId>> = Cell::new(None);
|
||||
|
||||
pub(super) const CHEAP: bool = true;
|
||||
|
||||
pub(crate) fn get() -> Option<ThreadId> {
|
||||
ID.get()
|
||||
}
|
||||
|
||||
pub(super) fn set(id: ThreadId) {
|
||||
ID.set(Some(id))
|
||||
}
|
||||
}
|
||||
target_pointer_width = "16" => {
|
||||
local_pointer! {
|
||||
static ID0;
|
||||
static ID16;
|
||||
static ID32;
|
||||
static ID48;
|
||||
}
|
||||
|
||||
pub(super) const CHEAP: bool = false;
|
||||
|
||||
pub(crate) fn get() -> Option<ThreadId> {
|
||||
let id0 = ID0.get().addr() as u64;
|
||||
let id16 = ID16.get().addr() as u64;
|
||||
let id32 = ID32.get().addr() as u64;
|
||||
let id48 = ID48.get().addr() as u64;
|
||||
ThreadId::from_u64((id48 << 48) + (id32 << 32) + (id16 << 16) + id0)
|
||||
}
|
||||
|
||||
pub(super) fn set(id: ThreadId) {
|
||||
let val = id.as_u64().get();
|
||||
ID0.set(ptr::without_provenance_mut(val as usize));
|
||||
ID16.set(ptr::without_provenance_mut((val >> 16) as usize));
|
||||
ID32.set(ptr::without_provenance_mut((val >> 32) as usize));
|
||||
ID48.set(ptr::without_provenance_mut((val >> 48) as usize));
|
||||
}
|
||||
}
|
||||
target_pointer_width = "32" => {
|
||||
local_pointer! {
|
||||
static ID0;
|
||||
static ID32;
|
||||
}
|
||||
|
||||
pub(super) const CHEAP: bool = false;
|
||||
|
||||
pub(crate) fn get() -> Option<ThreadId> {
|
||||
let id0 = ID0.get().addr() as u64;
|
||||
let id32 = ID32.get().addr() as u64;
|
||||
ThreadId::from_u64((id32 << 32) + id0)
|
||||
}
|
||||
|
||||
pub(super) fn set(id: ThreadId) {
|
||||
let val = id.as_u64().get();
|
||||
ID0.set(ptr::without_provenance_mut(val as usize));
|
||||
ID32.set(ptr::without_provenance_mut((val >> 32) as usize));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
local_pointer! {
|
||||
static ID;
|
||||
}
|
||||
|
||||
pub(super) const CHEAP: bool = true;
|
||||
|
||||
pub(crate) fn get() -> Option<ThreadId> {
|
||||
let id = ID.get().addr() as u64;
|
||||
ThreadId::from_u64(id)
|
||||
}
|
||||
|
||||
pub(super) fn set(id: ThreadId) {
|
||||
let val = id.as_u64().get();
|
||||
ID.set(ptr::without_provenance_mut(val as usize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn get_or_init() -> ThreadId {
|
||||
get().unwrap_or_else(
|
||||
#[cold]
|
||||
|| {
|
||||
let id = ThreadId::new();
|
||||
id::set(id);
|
||||
id
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to set the thread handle for the current thread. Fails if a handle was
|
||||
/// already set or if the thread ID of `thread` would change an already-set ID.
|
||||
pub(super) fn set_current(thread: Thread) -> Result<(), Thread> {
|
||||
if CURRENT.get() != NONE {
|
||||
return Err(thread);
|
||||
}
|
||||
|
||||
match id::get() {
|
||||
Some(id) if id == thread.id() => {}
|
||||
None => id::set(thread.id()),
|
||||
_ => return Err(thread),
|
||||
}
|
||||
|
||||
// Make sure that `crate::rt::thread_cleanup` will be run, which will
|
||||
// call `drop_current`.
|
||||
crate::sys::thread_local::guard::enable();
|
||||
CURRENT.set(thread.into_raw().cast_mut());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the unique identifier of the thread which invokes it.
|
||||
///
|
||||
/// Calling this function may be more efficient than accessing the current
|
||||
/// thread id through the current thread handle. i.e. `thread::current().id()`.
|
||||
///
|
||||
/// This function will always succeed, will always return the same value for
|
||||
/// one thread and is guaranteed not to call the global allocator.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(current_thread_id)]
|
||||
///
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let other_thread = thread::spawn(|| {
|
||||
/// thread::current_id()
|
||||
/// });
|
||||
///
|
||||
/// let other_thread_id = other_thread.join().unwrap();
|
||||
/// assert_ne!(thread::current_id(), other_thread_id);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "current_thread_id", issue = "147194")]
|
||||
pub fn current_id() -> ThreadId {
|
||||
// If accessing the persistent thread ID takes multiple TLS accesses, try
|
||||
// to retrieve it from the current thread handle, which will only take one
|
||||
// TLS access.
|
||||
if !id::CHEAP {
|
||||
if let Some(id) = try_with_current(|t| t.map(|t| t.id())) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
id::get_or_init()
|
||||
}
|
||||
|
||||
/// Gets the OS thread ID of the thread that invokes it, if available. If not, return the Rust
|
||||
/// thread ID.
|
||||
///
|
||||
/// We use a `u64` to all possible platform IDs without excess `cfg`; most use `int`, some use a
|
||||
/// pointer, and Apple uses `uint64_t`. This is a "best effort" approach for diagnostics and is
|
||||
/// allowed to fall back to a non-OS ID (such as the Rust thread ID) or a non-unique ID (such as a
|
||||
/// PID) if the thread ID cannot be retrieved.
|
||||
pub(crate) fn current_os_id() -> u64 {
|
||||
imp::current_os_id().unwrap_or_else(|| current_id().as_u64().get())
|
||||
}
|
||||
|
||||
/// Gets a reference to the handle of the thread that invokes it, if the handle
|
||||
/// has been initialized.
|
||||
fn try_with_current<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(Option<&Thread>) -> R,
|
||||
{
|
||||
let current = CURRENT.get();
|
||||
if current > DESTROYED {
|
||||
// SAFETY: `Arc` does not contain interior mutability, so it does not
|
||||
// matter that the address of the handle might be different depending
|
||||
// on where this is called.
|
||||
unsafe {
|
||||
let current = ManuallyDrop::new(Thread::from_raw(current));
|
||||
f(Some(¤t))
|
||||
}
|
||||
} else {
|
||||
f(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a function with the current thread's name.
|
||||
///
|
||||
/// Modulo thread local accesses, this function is safe to call from signal
|
||||
/// handlers and in similar circumstances where allocations are not possible.
|
||||
pub(crate) fn with_current_name<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(Option<&str>) -> R,
|
||||
{
|
||||
try_with_current(|thread| {
|
||||
let name = if let Some(thread) = thread {
|
||||
// If there is a current thread handle, try to use the name stored
|
||||
// there.
|
||||
thread.name()
|
||||
} else if let Some(main) = main_thread::get()
|
||||
&& let Some(id) = id::get()
|
||||
&& id == main
|
||||
{
|
||||
// The main thread doesn't always have a thread handle, we must
|
||||
// identify it through its ID instead. The checks are ordered so
|
||||
// that the current ID is only loaded if it is actually needed,
|
||||
// since loading it from TLS might need multiple expensive accesses.
|
||||
Some("main")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
f(name)
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets a handle to the thread that invokes it. If the handle stored in thread-
|
||||
/// local storage was already destroyed, this creates a new unnamed temporary
|
||||
/// handle to allow thread parking in nearly all situations.
|
||||
pub(crate) fn current_or_unnamed() -> Thread {
|
||||
let current = CURRENT.get();
|
||||
if current > DESTROYED {
|
||||
unsafe {
|
||||
let current = ManuallyDrop::new(Thread::from_raw(current));
|
||||
(*current).clone()
|
||||
}
|
||||
} else if current == DESTROYED {
|
||||
Thread::new(id::get_or_init(), None)
|
||||
} else {
|
||||
init_current(current)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a handle to the thread that invokes it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Getting a handle to the current thread with `thread::current()`:
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let handler = thread::Builder::new()
|
||||
/// .name("named thread".into())
|
||||
/// .spawn(|| {
|
||||
/// let handle = thread::current();
|
||||
/// assert_eq!(handle.name(), Some("named thread"));
|
||||
/// })
|
||||
/// .unwrap();
|
||||
///
|
||||
/// handler.join().unwrap();
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn current() -> Thread {
|
||||
let current = CURRENT.get();
|
||||
if current > DESTROYED {
|
||||
unsafe {
|
||||
let current = ManuallyDrop::new(Thread::from_raw(current));
|
||||
(*current).clone()
|
||||
}
|
||||
} else {
|
||||
init_current(current)
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn init_current(current: *mut ()) -> Thread {
|
||||
if current == NONE {
|
||||
CURRENT.set(BUSY);
|
||||
// If the thread ID was initialized already, use it.
|
||||
let id = id::get_or_init();
|
||||
let thread = Thread::new(id, None);
|
||||
|
||||
// Make sure that `crate::rt::thread_cleanup` will be run, which will
|
||||
// call `drop_current`.
|
||||
crate::sys::thread_local::guard::enable();
|
||||
CURRENT.set(thread.clone().into_raw().cast_mut());
|
||||
thread
|
||||
} else if current == BUSY {
|
||||
// BUSY exists solely for this check, but as it is in the slow path, the
|
||||
// extra TLS write above shouldn't matter. The alternative is nearly always
|
||||
// a stack overflow.
|
||||
//
|
||||
// If we reach this point it means our initialization routine ended up
|
||||
// calling current() either directly, or indirectly through the global
|
||||
// allocator, which is a bug either way as we may not call the global
|
||||
// allocator in current().
|
||||
rtabort!(
|
||||
"init_current() was re-entrant, which indicates a bug in the Rust threading implementation"
|
||||
)
|
||||
} else {
|
||||
debug_assert_eq!(current, DESTROYED);
|
||||
panic!(
|
||||
"use of std::thread::current() is not possible after the thread's \
|
||||
local data has been destroyed"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// This should be run in [`crate::rt::thread_cleanup`] to reset the thread
|
||||
/// handle.
|
||||
pub(crate) fn drop_current() {
|
||||
let current = CURRENT.get();
|
||||
if current > DESTROYED {
|
||||
unsafe {
|
||||
CURRENT.set(DESTROYED);
|
||||
drop(Thread::from_raw(current));
|
||||
}
|
||||
}
|
||||
}
|
||||
692
crates/std/src/thread/functions.rs
Normal file
692
crates/std/src/thread/functions.rs
Normal file
@@ -0,0 +1,692 @@
|
||||
//! Free functions.
|
||||
|
||||
use super::builder::Builder;
|
||||
use super::current::current;
|
||||
use super::join_handle::JoinHandle;
|
||||
use crate::mem::forget;
|
||||
use crate::num::NonZero;
|
||||
use crate::sys::thread as imp;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::io;use core::panicking;
|
||||
|
||||
/// Spawns a new thread, returning a [`JoinHandle`] for it.
|
||||
///
|
||||
/// The join handle provides a [`join`] method that can be used to join the spawned
|
||||
/// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing
|
||||
/// the argument given to [`panic!`].
|
||||
///
|
||||
/// If the join handle is dropped, the spawned thread will implicitly be *detached*.
|
||||
/// In this case, the spawned thread may no longer be joined.
|
||||
/// (It is the responsibility of the program to either eventually join threads it
|
||||
/// creates or detach them; otherwise, a resource leak will result.)
|
||||
///
|
||||
/// This function creates a thread with the default parameters of [`Builder`].
|
||||
/// To specify the new thread's stack size or the name, use [`Builder::spawn`].
|
||||
///
|
||||
/// As you can see in the signature of `spawn` there are two constraints on
|
||||
/// both the closure given to `spawn` and its return value, let's explain them:
|
||||
///
|
||||
/// - The `'static` constraint means that the closure and its return value
|
||||
/// must have a lifetime of the whole program execution. The reason for this
|
||||
/// is that threads can outlive the lifetime they have been created in.
|
||||
///
|
||||
/// Indeed if the thread, and by extension its return value, can outlive their
|
||||
/// caller, we need to make sure that they will be valid afterwards, and since
|
||||
/// we *can't* know when it will return we need to have them valid as long as
|
||||
/// possible, that is until the end of the program, hence the `'static`
|
||||
/// lifetime.
|
||||
/// - The [`Send`] constraint is because the closure will need to be passed
|
||||
/// *by value* from the thread where it is spawned to the new thread. Its
|
||||
/// return value will need to be passed from the new thread to the thread
|
||||
/// where it is `join`ed.
|
||||
/// As a reminder, the [`Send`] marker trait expresses that it is safe to be
|
||||
/// passed from thread to thread. [`Sync`] expresses that it is safe to have a
|
||||
/// reference be passed from thread to thread.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the OS fails to create a thread; use [`Builder::spawn`]
|
||||
/// to recover from such errors.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a thread.
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let handler = thread::spawn(|| {
|
||||
/// // thread code
|
||||
/// });
|
||||
///
|
||||
/// handler.join().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// As mentioned in the module documentation, threads are usually made to
|
||||
/// communicate using [`channels`], here is how it usually looks.
|
||||
///
|
||||
/// This example also shows how to use `move`, in order to give ownership
|
||||
/// of values to a thread.
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
/// use std::sync::mpsc::channel;
|
||||
///
|
||||
/// let (tx, rx) = channel();
|
||||
///
|
||||
/// let sender = thread::spawn(move || {
|
||||
/// tx.send("Hello, thread".to_owned())
|
||||
/// .expect("Unable to send on channel");
|
||||
/// });
|
||||
///
|
||||
/// let receiver = thread::spawn(move || {
|
||||
/// let value = rx.recv().expect("Unable to receive from channel");
|
||||
/// println!("{value}");
|
||||
/// });
|
||||
///
|
||||
/// sender.join().expect("The sender thread has panicked");
|
||||
/// receiver.join().expect("The receiver thread has panicked");
|
||||
/// ```
|
||||
///
|
||||
/// A thread can also return a value through its [`JoinHandle`], you can use
|
||||
/// this to make asynchronous computations (futures might be more appropriate
|
||||
/// though).
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let computation = thread::spawn(|| {
|
||||
/// // Some expensive computation.
|
||||
/// 42
|
||||
/// });
|
||||
///
|
||||
/// let result = computation.join().unwrap();
|
||||
/// println!("{result}");
|
||||
/// ```
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// This function has the same minimal guarantee regarding "foreign" unwinding operations (e.g.
|
||||
/// an exception thrown from C++ code, or a `panic!` in Rust code compiled or linked with a
|
||||
/// different runtime) as [`catch_unwind`]; namely, if the thread created with `thread::spawn`
|
||||
/// unwinds all the way to the root with such an exception, one of two behaviors are possible,
|
||||
/// and it is unspecified which will occur:
|
||||
///
|
||||
/// * The process aborts.
|
||||
/// * The process does not abort, and [`join`] will return a `Result::Err`
|
||||
/// containing an opaque type.
|
||||
///
|
||||
/// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html
|
||||
/// [`channels`]: crate::sync::mpsc
|
||||
/// [`join`]: JoinHandle::join
|
||||
/// [`Err`]: crate::result::Result::Err
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
F: Send + 'static,
|
||||
T: Send + 'static,
|
||||
{
|
||||
Builder::new().spawn(f).expect("failed to spawn thread")
|
||||
}
|
||||
|
||||
/// Cooperatively gives up a timeslice to the OS scheduler.
|
||||
///
|
||||
/// This calls the underlying OS scheduler's yield primitive, signaling
|
||||
/// that the calling thread is willing to give up its remaining timeslice
|
||||
/// so that the OS may schedule other threads on the CPU.
|
||||
///
|
||||
/// A drawback of yielding in a loop is that if the OS does not have any
|
||||
/// other ready threads to run on the current CPU, the thread will effectively
|
||||
/// busy-wait, which wastes CPU time and energy.
|
||||
///
|
||||
/// Therefore, when waiting for events of interest, a programmer's first
|
||||
/// choice should be to use synchronization devices such as [`channel`]s,
|
||||
/// [`Condvar`]s, [`Mutex`]es or [`join`] since these primitives are
|
||||
/// implemented in a blocking manner, giving up the CPU until the event
|
||||
/// of interest has occurred which avoids repeated yielding.
|
||||
///
|
||||
/// `yield_now` should thus be used only rarely, mostly in situations where
|
||||
/// repeated polling is required because there is no other suitable way to
|
||||
/// learn when an event of interest has occurred.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// thread::yield_now();
|
||||
/// ```
|
||||
///
|
||||
/// [`channel`]: crate::sync::mpsc
|
||||
/// [`join`]: JoinHandle::join
|
||||
/// [`Condvar`]: crate::sync::Condvar
|
||||
/// [`Mutex`]: crate::sync::Mutex
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn yield_now() {
|
||||
imp::yield_now()
|
||||
}
|
||||
|
||||
/// Determines whether the current thread is unwinding because of panic.
|
||||
///
|
||||
/// A common use of this feature is to poison shared resources when writing
|
||||
/// unsafe code, by checking `panicking` when the `drop` is called.
|
||||
///
|
||||
/// This is usually not needed when writing safe code, as [`Mutex`es][Mutex]
|
||||
/// already poison themselves when a thread panics while holding the lock.
|
||||
///
|
||||
/// This can also be used in multithreaded applications, in order to send a
|
||||
/// message to other threads warning that a thread has panicked (e.g., for
|
||||
/// monitoring purposes).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use std::thread;
|
||||
///
|
||||
/// struct SomeStruct;
|
||||
///
|
||||
/// impl Drop for SomeStruct {
|
||||
/// fn drop(&mut self) {
|
||||
/// if thread::panicking() {
|
||||
/// println!("dropped while unwinding");
|
||||
/// } else {
|
||||
/// println!("dropped while not unwinding");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// {
|
||||
/// print!("a: ");
|
||||
/// let a = SomeStruct;
|
||||
/// }
|
||||
///
|
||||
/// {
|
||||
/// print!("b: ");
|
||||
/// let b = SomeStruct;
|
||||
/// panic!()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [Mutex]: crate::sync::Mutex
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn panicking() -> bool {
|
||||
panicking::panicking()
|
||||
}
|
||||
|
||||
/// Uses [`sleep`].
|
||||
///
|
||||
/// Puts the current thread to sleep for at least the specified amount of time.
|
||||
///
|
||||
/// The thread may sleep longer than the duration specified due to scheduling
|
||||
/// specifics or platform-dependent functionality. It will never sleep less.
|
||||
///
|
||||
/// This function is blocking, and should not be used in `async` functions.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// On Unix platforms, the underlying syscall may be interrupted by a
|
||||
/// spurious wakeup or signal handler. To ensure the sleep occurs for at least
|
||||
/// the specified duration, this function may invoke that system call multiple
|
||||
/// times.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::thread;
|
||||
///
|
||||
/// // Let's sleep for 2 seconds:
|
||||
/// thread::sleep_ms(2000);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[deprecated(since = "1.6.0", note = "replaced by `std::thread::sleep`")]
|
||||
pub fn sleep_ms(ms: u32) {
|
||||
sleep(Duration::from_millis(ms as u64))
|
||||
}
|
||||
|
||||
/// Puts the current thread to sleep for at least the specified amount of time.
|
||||
///
|
||||
/// The thread may sleep longer than the duration specified due to scheduling
|
||||
/// specifics or platform-dependent functionality. It will never sleep less.
|
||||
///
|
||||
/// This function is blocking, and should not be used in `async` functions.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// On Unix platforms, the underlying syscall may be interrupted by a
|
||||
/// spurious wakeup or signal handler. To ensure the sleep occurs for at least
|
||||
/// the specified duration, this function may invoke that system call multiple
|
||||
/// times.
|
||||
/// Platforms which do not support nanosecond precision for sleeping will
|
||||
/// have `dur` rounded up to the nearest granularity of time they can sleep for.
|
||||
///
|
||||
/// Currently, specifying a zero duration on Unix platforms returns immediately
|
||||
/// without invoking the underlying [`nanosleep`] syscall, whereas on Windows
|
||||
/// platforms the underlying [`Sleep`] syscall is always invoked.
|
||||
/// If the intention is to yield the current time-slice you may want to use
|
||||
/// [`yield_now`] instead.
|
||||
///
|
||||
/// [`nanosleep`]: https://linux.die.net/man/2/nanosleep
|
||||
/// [`Sleep`]: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::{thread, time};
|
||||
///
|
||||
/// let ten_millis = time::Duration::from_millis(10);
|
||||
/// let now = time::Instant::now();
|
||||
///
|
||||
/// thread::sleep(ten_millis);
|
||||
///
|
||||
/// assert!(now.elapsed() >= ten_millis);
|
||||
/// ```
|
||||
#[stable(feature = "thread_sleep", since = "1.4.0")]
|
||||
pub fn sleep(dur: Duration) {
|
||||
imp::sleep(dur)
|
||||
}
|
||||
|
||||
/// Puts the current thread to sleep until the specified deadline has passed.
|
||||
///
|
||||
/// The thread may still be asleep after the deadline specified due to
|
||||
/// scheduling specifics or platform-dependent functionality. It will never
|
||||
/// wake before.
|
||||
///
|
||||
/// This function is blocking, and should not be used in `async` functions.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// In most cases this function will call an OS specific function. Where that
|
||||
/// is not supported [`sleep`] is used. Those platforms are referred to as other
|
||||
/// in the table below.
|
||||
///
|
||||
/// # Underlying System calls
|
||||
///
|
||||
/// The following system calls are [currently] being used:
|
||||
///
|
||||
/// | Platform | System call |
|
||||
/// |-----------|----------------------------------------------------------------------|
|
||||
/// | Linux | [clock_nanosleep] (Monotonic clock) |
|
||||
/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock)] |
|
||||
/// | Android | [clock_nanosleep] (Monotonic Clock)] |
|
||||
/// | Solaris | [clock_nanosleep] (Monotonic Clock)] |
|
||||
/// | Illumos | [clock_nanosleep] (Monotonic Clock)] |
|
||||
/// | Dragonfly | [clock_nanosleep] (Monotonic Clock)] |
|
||||
/// | Hurd | [clock_nanosleep] (Monotonic Clock)] |
|
||||
/// | Vxworks | [clock_nanosleep] (Monotonic Clock)] |
|
||||
/// | Apple | `mach_wait_until` |
|
||||
/// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself |
|
||||
///
|
||||
/// [currently]: crate::io#platform-specific-behavior
|
||||
/// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep
|
||||
///
|
||||
/// **Disclaimer:** These system calls might change over time.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// A simple game loop that limits the game to 60 frames per second.
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(thread_sleep_until)]
|
||||
/// # use std::time::{Duration, Instant};
|
||||
/// # use std::thread;
|
||||
/// #
|
||||
/// # fn update() {}
|
||||
/// # fn render() {}
|
||||
/// #
|
||||
/// let max_fps = 60.0;
|
||||
/// let frame_time = Duration::from_secs_f32(1.0/max_fps);
|
||||
/// let mut next_frame = Instant::now();
|
||||
/// loop {
|
||||
/// thread::sleep_until(next_frame);
|
||||
/// next_frame += frame_time;
|
||||
/// update();
|
||||
/// render();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// A slow API we must not call too fast and which takes a few
|
||||
/// tries before succeeding. By using `sleep_until` the time the
|
||||
/// API call takes does not influence when we retry or when we give up
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(thread_sleep_until)]
|
||||
/// # use std::time::{Duration, Instant};
|
||||
/// # use std::thread;
|
||||
/// #
|
||||
/// # enum Status {
|
||||
/// # Ready(usize),
|
||||
/// # Waiting,
|
||||
/// # }
|
||||
/// # fn slow_web_api_call() -> Status { Status::Ready(42) }
|
||||
/// #
|
||||
/// # const MAX_DURATION: Duration = Duration::from_secs(10);
|
||||
/// #
|
||||
/// # fn try_api_call() -> Result<usize, ()> {
|
||||
/// let deadline = Instant::now() + MAX_DURATION;
|
||||
/// let delay = Duration::from_millis(250);
|
||||
/// let mut next_attempt = Instant::now();
|
||||
/// loop {
|
||||
/// if Instant::now() > deadline {
|
||||
/// break Err(());
|
||||
/// }
|
||||
/// if let Status::Ready(data) = slow_web_api_call() {
|
||||
/// break Ok(data);
|
||||
/// }
|
||||
///
|
||||
/// next_attempt = deadline.min(next_attempt + delay);
|
||||
/// thread::sleep_until(next_attempt);
|
||||
/// }
|
||||
/// # }
|
||||
/// # let _data = try_api_call();
|
||||
/// ```
|
||||
#[unstable(feature = "thread_sleep_until", issue = "113752")]
|
||||
pub fn sleep_until(deadline: Instant) {
|
||||
imp::sleep_until(deadline)
|
||||
}
|
||||
|
||||
/// Used to ensure that `park` and `park_timeout` do not unwind, as that can
|
||||
/// cause undefined behavior if not handled correctly (see #102398 for context).
|
||||
struct PanicGuard;
|
||||
|
||||
impl Drop for PanicGuard {
|
||||
fn drop(&mut self) {
|
||||
rtabort!("an irrecoverable error occurred while synchronizing threads")
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks unless or until the current thread's token is made available.
|
||||
///
|
||||
/// A call to `park` does not guarantee that the thread will remain parked
|
||||
/// forever, and callers should be prepared for this possibility. However,
|
||||
/// it is guaranteed that this function will not panic (it may abort the
|
||||
/// process if the implementation encounters some rare errors).
|
||||
///
|
||||
/// # `park` and `unpark`
|
||||
///
|
||||
/// Every thread is equipped with some basic low-level blocking support, via the
|
||||
/// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`]
|
||||
/// method. [`park`] blocks the current thread, which can then be resumed from
|
||||
/// another thread by calling the [`unpark`] method on the blocked thread's
|
||||
/// handle.
|
||||
///
|
||||
/// Conceptually, each [`Thread`] handle has an associated token, which is
|
||||
/// initially not present:
|
||||
///
|
||||
/// * The [`thread::park`][`park`] function blocks the current thread unless or
|
||||
/// until the token is available for its thread handle, at which point it
|
||||
/// atomically consumes the token. It may also return *spuriously*, without
|
||||
/// consuming the token. [`thread::park_timeout`] does the same, but allows
|
||||
/// specifying a maximum time to block the thread for.
|
||||
///
|
||||
/// * The [`unpark`] method on a [`Thread`] atomically makes the token available
|
||||
/// if it wasn't already. Because the token can be held by a thread even if it is currently not
|
||||
/// parked, [`unpark`] followed by [`park`] will result in the second call returning immediately.
|
||||
/// However, note that to rely on this guarantee, you need to make sure that your `unpark` happens
|
||||
/// after all `park` that may be done by other data structures!
|
||||
///
|
||||
/// The API is typically used by acquiring a handle to the current thread, placing that handle in a
|
||||
/// shared data structure so that other threads can find it, and then `park`ing in a loop. When some
|
||||
/// desired condition is met, another thread calls [`unpark`] on the handle. The last bullet point
|
||||
/// above guarantees that even if the `unpark` occurs before the thread is finished `park`ing, it
|
||||
/// will be woken up properly.
|
||||
///
|
||||
/// Note that the coordination via the shared data structure is crucial: If you `unpark` a thread
|
||||
/// without first establishing that it is about to be `park`ing within your code, that `unpark` may
|
||||
/// get consumed by a *different* `park` in the same thread, leading to a deadlock. This also means
|
||||
/// you must not call unknown code between setting up for parking and calling `park`; for instance,
|
||||
/// if you invoke `println!`, that may itself call `park` and thus consume your `unpark` and cause a
|
||||
/// deadlock.
|
||||
///
|
||||
/// The motivation for this design is twofold:
|
||||
///
|
||||
/// * It avoids the need to allocate mutexes and condvars when building new
|
||||
/// synchronization primitives; the threads already provide basic
|
||||
/// blocking/signaling.
|
||||
///
|
||||
/// * It can be implemented very efficiently on many platforms.
|
||||
///
|
||||
/// # Memory Ordering
|
||||
///
|
||||
/// Calls to `unpark` _synchronize-with_ calls to `park`, meaning that memory
|
||||
/// operations performed before a call to `unpark` are made visible to the thread that
|
||||
/// consumes the token and returns from `park`. Note that all `park` and `unpark`
|
||||
/// operations for a given thread form a total order and _all_ prior `unpark` operations
|
||||
/// synchronize-with `park`.
|
||||
///
|
||||
/// In atomic ordering terms, `unpark` performs a `Release` operation and `park`
|
||||
/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same
|
||||
/// thread form a [release sequence].
|
||||
///
|
||||
/// Note that being unblocked does not imply a call was made to `unpark`, because
|
||||
/// wakeups can also be spurious. For example, a valid, but inefficient,
|
||||
/// implementation could have `park` and `unpark` return immediately without doing anything,
|
||||
/// making *all* wakeups spurious.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
/// use std::sync::atomic::{Ordering, AtomicBool};
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// static QUEUED: AtomicBool = AtomicBool::new(false);
|
||||
/// static FLAG: AtomicBool = AtomicBool::new(false);
|
||||
///
|
||||
/// let parked_thread = thread::spawn(move || {
|
||||
/// println!("Thread spawned");
|
||||
/// // Signal that we are going to `park`. Between this store and our `park`, there may
|
||||
/// // be no other `park`, or else that `park` could consume our `unpark` token!
|
||||
/// QUEUED.store(true, Ordering::Release);
|
||||
/// // We want to wait until the flag is set. We *could* just spin, but using
|
||||
/// // park/unpark is more efficient.
|
||||
/// while !FLAG.load(Ordering::Acquire) {
|
||||
/// // We can *not* use `println!` here since that could use thread parking internally.
|
||||
/// thread::park();
|
||||
/// // We *could* get here spuriously, i.e., way before the 10ms below are over!
|
||||
/// // But that is no problem, we are in a loop until the flag is set anyway.
|
||||
/// }
|
||||
/// println!("Flag received");
|
||||
/// });
|
||||
///
|
||||
/// // Let some time pass for the thread to be spawned.
|
||||
/// thread::sleep(Duration::from_millis(10));
|
||||
///
|
||||
/// // Ensure the thread is about to park.
|
||||
/// // This is crucial! It guarantees that the `unpark` below is not consumed
|
||||
/// // by some other code in the parked thread (e.g. inside `println!`).
|
||||
/// while !QUEUED.load(Ordering::Acquire) {
|
||||
/// // Spinning is of course inefficient; in practice, this would more likely be
|
||||
/// // a dequeue where we have no work to do if there's nobody queued.
|
||||
/// std::hint::spin_loop();
|
||||
/// }
|
||||
///
|
||||
/// // Set the flag, and let the thread wake up.
|
||||
/// // There is no race condition here: if `unpark`
|
||||
/// // happens first, `park` will return immediately.
|
||||
/// // There is also no other `park` that could consume this token,
|
||||
/// // since we waited until the other thread got queued.
|
||||
/// // Hence there is no risk of a deadlock.
|
||||
/// FLAG.store(true, Ordering::Release);
|
||||
/// println!("Unpark the thread");
|
||||
/// parked_thread.thread().unpark();
|
||||
///
|
||||
/// parked_thread.join().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [`Thread`]: super::Thread
|
||||
/// [`unpark`]: super::Thread::unpark
|
||||
/// [`thread::park_timeout`]: park_timeout
|
||||
/// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn park() {
|
||||
let guard = PanicGuard;
|
||||
// SAFETY: park_timeout is called on the parker owned by this thread.
|
||||
unsafe {
|
||||
current().park();
|
||||
}
|
||||
// No panic occurred, do not abort.
|
||||
forget(guard);
|
||||
}
|
||||
|
||||
/// Uses [`park_timeout`].
|
||||
///
|
||||
/// Blocks unless or until the current thread's token is made available or
|
||||
/// the specified duration has been reached (may wake spuriously).
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`park`] except
|
||||
/// that the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that might not cause the maximum
|
||||
/// amount of time waited to be precisely `ms` long.
|
||||
///
|
||||
/// See the [park documentation][`park`] for more detail.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[deprecated(since = "1.6.0", note = "replaced by `std::thread::park_timeout`")]
|
||||
pub fn park_timeout_ms(ms: u32) {
|
||||
park_timeout(Duration::from_millis(ms as u64))
|
||||
}
|
||||
|
||||
/// Blocks unless or until the current thread's token is made available or
|
||||
/// the specified duration has been reached (may wake spuriously).
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`park`][park] except
|
||||
/// that the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that might not cause the maximum
|
||||
/// amount of time waited to be precisely `dur` long.
|
||||
///
|
||||
/// See the [park documentation][park] for more details.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// Platforms which do not support nanosecond precision for sleeping will have
|
||||
/// `dur` rounded up to the nearest granularity of time they can sleep for.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Waiting for the complete expiration of the timeout:
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use std::thread::park_timeout;
|
||||
/// use std::time::{Instant, Duration};
|
||||
///
|
||||
/// let timeout = Duration::from_secs(2);
|
||||
/// let beginning_park = Instant::now();
|
||||
///
|
||||
/// let mut timeout_remaining = timeout;
|
||||
/// loop {
|
||||
/// park_timeout(timeout_remaining);
|
||||
/// let elapsed = beginning_park.elapsed();
|
||||
/// if elapsed >= timeout {
|
||||
/// break;
|
||||
/// }
|
||||
/// println!("restarting park_timeout after {elapsed:?}");
|
||||
/// timeout_remaining = timeout - elapsed;
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "park_timeout", since = "1.4.0")]
|
||||
pub fn park_timeout(dur: Duration) {
|
||||
let guard = PanicGuard;
|
||||
// SAFETY: park_timeout is called on a handle owned by this thread.
|
||||
unsafe {
|
||||
current().park_timeout(dur);
|
||||
}
|
||||
// No panic occurred, do not abort.
|
||||
forget(guard);
|
||||
}
|
||||
|
||||
/// Returns an estimate of the default amount of parallelism a program should use.
|
||||
///
|
||||
/// Parallelism is a resource. A given machine provides a certain capacity for
|
||||
/// parallelism, i.e., a bound on the number of computations it can perform
|
||||
/// simultaneously. This number often corresponds to the amount of CPUs a
|
||||
/// computer has, but it may diverge in various cases.
|
||||
///
|
||||
/// Host environments such as VMs or container orchestrators may want to
|
||||
/// restrict the amount of parallelism made available to programs in them. This
|
||||
/// is often done to limit the potential impact of (unintentionally)
|
||||
/// resource-intensive programs on other programs running on the same machine.
|
||||
///
|
||||
/// # Limitations
|
||||
///
|
||||
/// The purpose of this API is to provide an easy and portable way to query
|
||||
/// the default amount of parallelism the program should use. Among other things it
|
||||
/// does not expose information on NUMA regions, does not account for
|
||||
/// differences in (co)processor capabilities or current system load,
|
||||
/// and will not modify the program's global state in order to more accurately
|
||||
/// query the amount of available parallelism.
|
||||
///
|
||||
/// Where both fixed steady-state and burst limits are available the steady-state
|
||||
/// capacity will be used to ensure more predictable latencies.
|
||||
///
|
||||
/// Resource limits can be changed during the runtime of a program, therefore the value is
|
||||
/// not cached and instead recomputed every time this function is called. It should not be
|
||||
/// called from hot code.
|
||||
///
|
||||
/// The value returned by this function should be considered a simplified
|
||||
/// approximation of the actual amount of parallelism available at any given
|
||||
/// time. To get a more detailed or precise overview of the amount of
|
||||
/// parallelism available to the program, you may wish to use
|
||||
/// platform-specific APIs as well. The following platform limitations currently
|
||||
/// apply to `available_parallelism`:
|
||||
///
|
||||
/// On Windows:
|
||||
/// - It may undercount the amount of parallelism available on systems with more
|
||||
/// than 64 logical CPUs. However, programs typically need specific support to
|
||||
/// take advantage of more than 64 logical CPUs, and in the absence of such
|
||||
/// support, the number returned by this function accurately reflects the
|
||||
/// number of logical CPUs the program can use by default.
|
||||
/// - It may overcount the amount of parallelism available on systems limited by
|
||||
/// process-wide affinity masks, or job object limitations.
|
||||
///
|
||||
/// On Linux:
|
||||
/// - It may overcount the amount of parallelism available when limited by a
|
||||
/// process-wide affinity mask or cgroup quotas and `sched_getaffinity()` or cgroup fs can't be
|
||||
/// queried, e.g. due to sandboxing.
|
||||
/// - It may undercount the amount of parallelism if the current thread's affinity mask
|
||||
/// does not reflect the process' cpuset, e.g. due to pinned threads.
|
||||
/// - If the process is in a cgroup v1 cpu controller, this may need to
|
||||
/// scan mountpoints to find the corresponding cgroup v1 controller,
|
||||
/// which may take time on systems with large numbers of mountpoints.
|
||||
/// (This does not apply to cgroup v2, or to processes not in a
|
||||
/// cgroup.)
|
||||
/// - It does not attempt to take `ulimit` into account. If there is a limit set on the number of
|
||||
/// threads, `available_parallelism` cannot know how much of that limit a Rust program should
|
||||
/// take, or know in a reliable and race-free way how much of that limit is already taken.
|
||||
///
|
||||
/// On all targets:
|
||||
/// - It may overcount the amount of parallelism available when running in a VM
|
||||
/// with CPU usage limits (e.g. an overcommitted host).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will, but is not limited to, return errors in the following
|
||||
/// cases:
|
||||
///
|
||||
/// - If the amount of parallelism is not known for the target platform.
|
||||
/// - If the program lacks permission to query the amount of parallelism made
|
||||
/// available to it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(dead_code)]
|
||||
/// use std::{io, thread};
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let count = thread::available_parallelism()?.get();
|
||||
/// assert!(count >= 1_usize);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(alias = "available_concurrency")] // Alias for a previous name we gave this API on unstable.
|
||||
#[doc(alias = "hardware_concurrency")] // Alias for C++ `std::thread::hardware_concurrency`.
|
||||
#[doc(alias = "num_cpus")] // Alias for a popular ecosystem crate which provides similar functionality.
|
||||
#[stable(feature = "available_parallelism", since = "1.59.0")]
|
||||
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||
imp::available_parallelism()
|
||||
}
|
||||
120
crates/std/src/thread/id.rs
Normal file
120
crates/std/src/thread/id.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
use crate::num::NonZero;
|
||||
use crate::sync::atomic::{Atomic, Ordering};
|
||||
|
||||
/// A unique identifier for a running thread.
|
||||
///
|
||||
/// A `ThreadId` is an opaque object that uniquely identifies each thread
|
||||
/// created during the lifetime of a process. `ThreadId`s are guaranteed not to
|
||||
/// be reused, even when a thread terminates. `ThreadId`s are under the control
|
||||
/// of Rust's standard library and there may not be any relationship between
|
||||
/// `ThreadId` and the underlying platform's notion of a thread identifier --
|
||||
/// the two concepts cannot, therefore, be used interchangeably. A `ThreadId`
|
||||
/// can be retrieved from the [`id`] method on a [`Thread`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let other_thread = thread::spawn(|| {
|
||||
/// thread::current().id()
|
||||
/// });
|
||||
///
|
||||
/// let other_thread_id = other_thread.join().unwrap();
|
||||
/// assert!(thread::current().id() != other_thread_id);
|
||||
/// ```
|
||||
///
|
||||
/// [`Thread`]: super::Thread
|
||||
/// [`id`]: super::Thread::id
|
||||
#[stable(feature = "thread_id", since = "1.19.0")]
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
|
||||
pub struct ThreadId(NonZero<u64>);
|
||||
|
||||
impl ThreadId {
|
||||
// Generate a new unique thread ID.
|
||||
pub(crate) fn new() -> ThreadId {
|
||||
#[cold]
|
||||
fn exhausted() -> ! {
|
||||
panic!("failed to generate unique thread ID: bitspace exhausted")
|
||||
}
|
||||
|
||||
cfg_select! {
|
||||
target_has_atomic = "64" => {
|
||||
use crate::sync::atomic::AtomicU64;
|
||||
|
||||
static COUNTER: Atomic<u64> = AtomicU64::new(0);
|
||||
|
||||
let mut last = COUNTER.load(Ordering::Relaxed);
|
||||
loop {
|
||||
let Some(id) = last.checked_add(1) else {
|
||||
exhausted();
|
||||
};
|
||||
|
||||
match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {
|
||||
Ok(_) => return ThreadId(NonZero::new(id).unwrap()),
|
||||
Err(id) => last = id,
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
use crate::cell::SyncUnsafeCell;
|
||||
use crate::hint::spin_loop;
|
||||
use crate::sync::atomic::AtomicBool;
|
||||
use crate::thread::yield_now;
|
||||
|
||||
// If we don't have a 64-bit atomic we use a small spinlock. We don't use Mutex
|
||||
// here as we might be trying to get the current thread id in the global allocator,
|
||||
// and on some platforms Mutex requires allocation.
|
||||
static COUNTER_LOCKED: Atomic<bool> = AtomicBool::new(false);
|
||||
static COUNTER: SyncUnsafeCell<u64> = SyncUnsafeCell::new(0);
|
||||
|
||||
// Acquire lock.
|
||||
let mut spin = 0;
|
||||
// Miri doesn't like it when we yield here as it interferes with deterministically
|
||||
// scheduling threads, so avoid `compare_exchange_weak` to avoid spurious yields.
|
||||
while COUNTER_LOCKED.swap(true, Ordering::Acquire) {
|
||||
if spin <= 3 {
|
||||
for _ in 0..(1 << spin) {
|
||||
spin_loop();
|
||||
}
|
||||
} else {
|
||||
yield_now();
|
||||
}
|
||||
spin += 1;
|
||||
}
|
||||
// This was `false` before the swap, so we got the lock.
|
||||
|
||||
// SAFETY: we have an exclusive lock on the counter.
|
||||
unsafe {
|
||||
if let Some(id) = (*COUNTER.get()).checked_add(1) {
|
||||
*COUNTER.get() = id;
|
||||
COUNTER_LOCKED.store(false, Ordering::Release);
|
||||
ThreadId(NonZero::new(id).unwrap())
|
||||
} else {
|
||||
COUNTER_LOCKED.store(false, Ordering::Release);
|
||||
exhausted()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_thread_local), target_has_atomic = "64"))]
|
||||
pub(super) fn from_u64(v: u64) -> Option<ThreadId> {
|
||||
NonZero::new(v).map(ThreadId)
|
||||
}
|
||||
|
||||
/// This returns a numeric identifier for the thread identified by this
|
||||
/// `ThreadId`.
|
||||
///
|
||||
/// As noted in the documentation for the type itself, it is essentially an
|
||||
/// opaque ID, but is guaranteed to be unique for each thread. The returned
|
||||
/// value is entirely opaque -- only equality testing is stable. Note that
|
||||
/// it is not guaranteed which values new threads will return, and this may
|
||||
/// change across Rust versions.
|
||||
#[must_use]
|
||||
#[unstable(feature = "thread_id_value", issue = "67939")]
|
||||
pub fn as_u64(&self) -> NonZero<u64> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
186
crates/std/src/thread/join_handle.rs
Normal file
186
crates/std/src/thread/join_handle.rs
Normal file
@@ -0,0 +1,186 @@
|
||||
use super::Result;
|
||||
use super::lifecycle::JoinInner;
|
||||
use super::thread::Thread;
|
||||
use crate::fmt;
|
||||
use crate::sys::{AsInner, IntoInner, thread as imp};
|
||||
|
||||
/// An owned permission to join on a thread (block on its termination).
|
||||
///
|
||||
/// A `JoinHandle` *detaches* the associated thread when it is dropped, which
|
||||
/// means that there is no longer any handle to the thread and no way to `join`
|
||||
/// on it.
|
||||
///
|
||||
/// Due to platform restrictions, it is not possible to [`Clone`] this
|
||||
/// handle: the ability to join a thread is a uniquely-owned permission.
|
||||
///
|
||||
/// This `struct` is created by the [`thread::spawn`] function and the
|
||||
/// [`thread::Builder::spawn`] method.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creation from [`thread::spawn`]:
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| {
|
||||
/// // some work here
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// Creation from [`thread::Builder::spawn`]:
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new();
|
||||
///
|
||||
/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| {
|
||||
/// // some work here
|
||||
/// }).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// A thread being detached and outliving the thread that spawned it:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let original_thread = thread::spawn(|| {
|
||||
/// let _detached_thread = thread::spawn(|| {
|
||||
/// // Here we sleep to make sure that the first thread returns before.
|
||||
/// thread::sleep(Duration::from_millis(10));
|
||||
/// // This will be called, even though the JoinHandle is dropped.
|
||||
/// println!("♫ Still alive ♫");
|
||||
/// });
|
||||
/// });
|
||||
///
|
||||
/// original_thread.join().expect("The thread being joined has panicked");
|
||||
/// println!("Original thread is joined.");
|
||||
///
|
||||
/// // We make sure that the new thread has time to run, before the main
|
||||
/// // thread returns.
|
||||
///
|
||||
/// thread::sleep(Duration::from_millis(1000));
|
||||
/// ```
|
||||
///
|
||||
/// [`thread::Builder::spawn`]: super::Builder::spawn
|
||||
/// [`thread::spawn`]: super::spawn
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(target_os = "teeos", must_use)]
|
||||
pub struct JoinHandle<T>(pub(super) JoinInner<'static, T>);
|
||||
|
||||
#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")]
|
||||
unsafe impl<T> Send for JoinHandle<T> {}
|
||||
#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")]
|
||||
unsafe impl<T> Sync for JoinHandle<T> {}
|
||||
|
||||
impl<T> JoinHandle<T> {
|
||||
/// Extracts a handle to the underlying thread.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new();
|
||||
///
|
||||
/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| {
|
||||
/// // some work here
|
||||
/// }).unwrap();
|
||||
///
|
||||
/// let thread = join_handle.thread();
|
||||
/// println!("thread id: {:?}", thread.id());
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[must_use]
|
||||
pub fn thread(&self) -> &Thread {
|
||||
self.0.thread()
|
||||
}
|
||||
|
||||
/// Waits for the associated thread to finish.
|
||||
///
|
||||
/// This function will return immediately if the associated thread has already finished.
|
||||
/// Otherwise, it fully waits for the thread to finish, including all destructors
|
||||
/// for thread-local variables that might be running after the main function of the thread.
|
||||
///
|
||||
/// In terms of [atomic memory orderings], the completion of the associated
|
||||
/// thread synchronizes with this function returning. In other words, all
|
||||
/// operations performed by that thread [happen
|
||||
/// before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) all
|
||||
/// operations that happen after `join` returns.
|
||||
///
|
||||
/// If the associated thread panics, [`Err`] is returned with the parameter given
|
||||
/// to [`panic!`] (though see the Notes below).
|
||||
///
|
||||
/// [`Err`]: crate::result::Result::Err
|
||||
/// [atomic memory orderings]: crate::sync::atomic
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may panic on some platforms if a thread attempts to join
|
||||
/// itself or otherwise may create a deadlock with joining threads.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new();
|
||||
///
|
||||
/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| {
|
||||
/// // some work here
|
||||
/// }).unwrap();
|
||||
/// join_handle.join().expect("Couldn't join on the associated thread");
|
||||
/// ```
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// If a "foreign" unwinding operation (e.g. an exception thrown from C++
|
||||
/// code, or a `panic!` in Rust code compiled or linked with a different
|
||||
/// runtime) unwinds all the way to the thread root, the process may be
|
||||
/// aborted; see the Notes on [`thread::spawn`]. If the process is not
|
||||
/// aborted, this function will return a `Result::Err` containing an opaque
|
||||
/// type.
|
||||
///
|
||||
/// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html
|
||||
/// [`thread::spawn`]: super::spawn
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn join(self) -> Result<T> {
|
||||
self.0.join()
|
||||
}
|
||||
|
||||
/// Checks if the associated thread has finished running its main function.
|
||||
///
|
||||
/// `is_finished` supports implementing a non-blocking join operation, by checking
|
||||
/// `is_finished`, and calling `join` if it returns `true`. This function does not block. To
|
||||
/// block while waiting on the thread to finish, use [`join`][Self::join].
|
||||
///
|
||||
/// This might return `true` for a brief moment after the thread's main
|
||||
/// function has returned, but before the thread itself has stopped running.
|
||||
/// However, once this returns `true`, [`join`][Self::join] can be expected
|
||||
/// to return quickly, without blocking for any significant amount of time.
|
||||
#[stable(feature = "thread_is_running", since = "1.61.0")]
|
||||
pub fn is_finished(&self) -> bool {
|
||||
self.0.is_finished()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsInner<imp::Thread> for JoinHandle<T> {
|
||||
fn as_inner(&self) -> &imp::Thread {
|
||||
self.0.as_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoInner<imp::Thread> for JoinHandle<T> {
|
||||
fn into_inner(self) -> imp::Thread {
|
||||
self.0.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl<T> fmt::Debug for JoinHandle<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("JoinHandle").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
265
crates/std/src/thread/lifecycle.rs
Normal file
265
crates/std/src/thread/lifecycle.rs
Normal file
@@ -0,0 +1,265 @@
|
||||
//! The inner logic for thread spawning and joining.
|
||||
|
||||
use super::current::set_current;
|
||||
use super::id::ThreadId;
|
||||
use super::scoped::ScopeData;
|
||||
use super::thread::Thread;
|
||||
use super::{Result, spawnhook};
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::{ManuallyDrop, MaybeUninit};
|
||||
use alloc_crate::sync::Arc;
|
||||
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
|
||||
use crate::sys::{AsInner, IntoInner, thread as imp};
|
||||
use crate::{env, io};use core::panic;
|
||||
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub(super) unsafe fn spawn_unchecked<'scope, F, T>(
|
||||
name: Option<String>,
|
||||
stack_size: Option<usize>,
|
||||
no_hooks: bool,
|
||||
scope_data: Option<Arc<ScopeData>>,
|
||||
f: F,
|
||||
) -> io::Result<JoinInner<'scope, T>>
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
F: Send,
|
||||
T: Send,
|
||||
{
|
||||
let stack_size = stack_size.unwrap_or_else(|| {
|
||||
static MIN: Atomic<usize> = AtomicUsize::new(0);
|
||||
|
||||
match MIN.load(Ordering::Relaxed) {
|
||||
0 => {}
|
||||
n => return n - 1,
|
||||
}
|
||||
|
||||
let amt = env::var_os("RUST_MIN_STACK")
|
||||
.and_then(|s| s.to_str().and_then(|s| s.parse().ok()))
|
||||
.unwrap_or(imp::DEFAULT_MIN_STACK_SIZE);
|
||||
|
||||
// 0 is our sentinel value, so ensure that we'll never see 0 after
|
||||
// initialization has run
|
||||
MIN.store(amt + 1, Ordering::Relaxed);
|
||||
amt
|
||||
});
|
||||
|
||||
let id = ThreadId::new();
|
||||
let thread = Thread::new(id, name);
|
||||
|
||||
let hooks = if no_hooks {
|
||||
spawnhook::ChildSpawnHooks::default()
|
||||
} else {
|
||||
spawnhook::run_spawn_hooks(&thread)
|
||||
};
|
||||
|
||||
let my_packet: Arc<Packet<'scope, T>> =
|
||||
Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None), _marker: PhantomData });
|
||||
let their_packet = my_packet.clone();
|
||||
|
||||
// Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*.
|
||||
// See <https://github.com/rust-lang/rust/issues/101983> for more details.
|
||||
// To prevent leaks we use a wrapper that drops its contents.
|
||||
#[repr(transparent)]
|
||||
struct MaybeDangling<T>(MaybeUninit<T>);
|
||||
impl<T> MaybeDangling<T> {
|
||||
fn new(x: T) -> Self {
|
||||
MaybeDangling(MaybeUninit::new(x))
|
||||
}
|
||||
fn into_inner(self) -> T {
|
||||
// Make sure we don't drop.
|
||||
let this = ManuallyDrop::new(self);
|
||||
// SAFETY: we are always initialized.
|
||||
unsafe { this.0.assume_init_read() }
|
||||
}
|
||||
}
|
||||
impl<T> Drop for MaybeDangling<T> {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: we are always initialized.
|
||||
unsafe { self.0.assume_init_drop() };
|
||||
}
|
||||
}
|
||||
|
||||
let f = MaybeDangling::new(f);
|
||||
|
||||
// The entrypoint of the Rust thread, after platform-specific thread
|
||||
// initialization is done.
|
||||
let rust_start = move || {
|
||||
let f = f.into_inner();
|
||||
let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run());
|
||||
crate::sys::backtrace::__rust_begin_short_backtrace(f)
|
||||
}));
|
||||
// SAFETY: `their_packet` as been built just above and moved by the
|
||||
// closure (it is an Arc<...>) and `my_packet` will be stored in the
|
||||
// same `JoinInner` as this closure meaning the mutation will be
|
||||
// safe (not modify it and affect a value far away).
|
||||
unsafe { *their_packet.result.get() = Some(try_result) };
|
||||
// Here `their_packet` gets dropped, and if this is the last `Arc` for that packet that
|
||||
// will call `decrement_num_running_threads` and therefore signal that this thread is
|
||||
// done.
|
||||
drop(their_packet);
|
||||
// Here, the lifetime `'scope` can end. `main` keeps running for a bit
|
||||
// after that before returning itself.
|
||||
};
|
||||
|
||||
if let Some(scope_data) = &my_packet.scope {
|
||||
scope_data.increment_num_running_threads();
|
||||
}
|
||||
|
||||
// SAFETY: dynamic size and alignment of the Box remain the same. See below for why the
|
||||
// lifetime change is justified.
|
||||
let rust_start = unsafe {
|
||||
let ptr = Box::into_raw(Box::new(rust_start));
|
||||
let ptr = crate::mem::transmute::<
|
||||
*mut (dyn FnOnce() + Send + '_),
|
||||
*mut (dyn FnOnce() + Send + 'static),
|
||||
>(ptr);
|
||||
Box::from_raw(ptr)
|
||||
};
|
||||
|
||||
let init = Box::new(ThreadInit { handle: thread.clone(), rust_start });
|
||||
|
||||
Ok(JoinInner {
|
||||
// SAFETY:
|
||||
//
|
||||
// `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed
|
||||
// through FFI or otherwise used with low-level threading primitives that have no
|
||||
// notion of or way to enforce lifetimes.
|
||||
//
|
||||
// As mentioned in the `Safety` section of this function's documentation, the caller of
|
||||
// this function needs to guarantee that the passed-in lifetime is sufficiently long
|
||||
// for the lifetime of the thread.
|
||||
//
|
||||
// Similarly, the `sys` implementation must guarantee that no references to the closure
|
||||
// exist after the thread has terminated, which is signaled by `Thread::join`
|
||||
// returning.
|
||||
native: unsafe { imp::Thread::new(stack_size, init)? },
|
||||
thread,
|
||||
packet: my_packet,
|
||||
})
|
||||
}
|
||||
|
||||
/// The data passed to the spawned thread for thread initialization. Any thread
|
||||
/// implementation should start a new thread by calling .init() on this before
|
||||
/// doing anything else to ensure the current thread is properly initialized and
|
||||
/// the global allocator works.
|
||||
pub(crate) struct ThreadInit {
|
||||
pub handle: Thread,
|
||||
pub rust_start: Box<dyn FnOnce() + Send>,
|
||||
}
|
||||
|
||||
impl ThreadInit {
|
||||
/// Initialize the 'current thread' mechanism on this thread, returning the
|
||||
/// Rust entry point.
|
||||
pub fn init(self: Box<Self>) -> Box<dyn FnOnce() + Send> {
|
||||
// Set the current thread before any (de)allocations on the global allocator occur,
|
||||
// so that it may call std::thread::current() in its implementation. This is also
|
||||
// why we take Box<Self>, to ensure the Box is not destroyed until after this point.
|
||||
// Cloning the handle does not invoke the global allocator, it is an Arc.
|
||||
if let Err(_thread) = set_current(self.handle.clone()) {
|
||||
// The current thread should not have set yet. Use an abort to save binary size (see #123356).
|
||||
rtabort!("current thread handle already set during thread spawn");
|
||||
}
|
||||
|
||||
if let Some(name) = self.handle.cname() {
|
||||
imp::set_name(name);
|
||||
}
|
||||
|
||||
self.rust_start
|
||||
}
|
||||
}
|
||||
|
||||
// This packet is used to communicate the return value between the spawned
|
||||
// thread and the rest of the program. It is shared through an `Arc` and
|
||||
// there's no need for a mutex here because synchronization happens with `join()`
|
||||
// (the caller will never read this packet until the thread has exited).
|
||||
//
|
||||
// An Arc to the packet is stored into a `JoinInner` which in turns is placed
|
||||
// in `JoinHandle`.
|
||||
struct Packet<'scope, T> {
|
||||
scope: Option<Arc<ScopeData>>,
|
||||
result: UnsafeCell<Option<Result<T>>>,
|
||||
_marker: PhantomData<Option<&'scope ScopeData>>,
|
||||
}
|
||||
|
||||
// Due to the usage of `UnsafeCell` we need to manually implement Sync.
|
||||
// The type `T` should already always be Send (otherwise the thread could not
|
||||
// have been created) and the Packet is Sync because all access to the
|
||||
// `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync.
|
||||
unsafe impl<'scope, T: Send> Sync for Packet<'scope, T> {}
|
||||
|
||||
impl<'scope, T> Drop for Packet<'scope, T> {
|
||||
fn drop(&mut self) {
|
||||
// If this packet was for a thread that ran in a scope, the thread
|
||||
// panicked, and nobody consumed the panic payload, we make sure
|
||||
// the scope function will panic.
|
||||
let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_)));
|
||||
// Drop the result without causing unwinding.
|
||||
// This is only relevant for threads that aren't join()ed, as
|
||||
// join() will take the `result` and set it to None, such that
|
||||
// there is nothing left to drop here.
|
||||
// If this panics, we should handle that, because we're outside the
|
||||
// outermost `catch_unwind` of our thread.
|
||||
// We just abort in that case, since there's nothing else we can do.
|
||||
// (And even if we tried to handle it somehow, we'd also need to handle
|
||||
// the case where the panic payload we get out of it also panics on
|
||||
// drop, and so on. See issue #86027.)
|
||||
if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
*self.result.get_mut() = None;
|
||||
})) {
|
||||
rtabort!("thread result panicked on drop");
|
||||
}
|
||||
// Book-keeping so the scope knows when it's done.
|
||||
if let Some(scope) = &self.scope {
|
||||
// Now that there will be no more user code running on this thread
|
||||
// that can use 'scope, mark the thread as 'finished'.
|
||||
// It's important we only do this after the `result` has been dropped,
|
||||
// since dropping it might still use things it borrowed from 'scope.
|
||||
scope.decrement_num_running_threads(unhandled_panic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner representation for JoinHandle
|
||||
pub(super) struct JoinInner<'scope, T> {
|
||||
native: imp::Thread,
|
||||
thread: Thread,
|
||||
packet: Arc<Packet<'scope, T>>,
|
||||
}
|
||||
|
||||
impl<'scope, T> JoinInner<'scope, T> {
|
||||
pub(super) fn is_finished(&self) -> bool {
|
||||
Arc::strong_count(&self.packet) == 1
|
||||
}
|
||||
|
||||
pub(super) fn thread(&self) -> &Thread {
|
||||
&self.thread
|
||||
}
|
||||
|
||||
pub(super) fn join(mut self) -> Result<T> {
|
||||
self.native.join();
|
||||
Arc::get_mut(&mut self.packet)
|
||||
// FIXME(fuzzypixelz): returning an error instead of panicking here
|
||||
// would require updating the documentation of
|
||||
// `std::thread::Result`; currently we can return `Err` if and only
|
||||
// if the thread had panicked.
|
||||
.expect("threads should not terminate unexpectedly")
|
||||
.result
|
||||
.get_mut()
|
||||
.take()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsInner<imp::Thread> for JoinInner<'static, T> {
|
||||
fn as_inner(&self) -> &imp::Thread {
|
||||
&self.native
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoInner<imp::Thread> for JoinInner<'static, T> {
|
||||
fn into_inner(self) -> imp::Thread {
|
||||
self.native
|
||||
}
|
||||
}
|
||||
56
crates/std/src/thread/main_thread.rs
Normal file
56
crates/std/src/thread/main_thread.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
//! Store the ID of the main thread.
|
||||
//!
|
||||
//! The thread handle for the main thread is created lazily, and this might even
|
||||
//! happen pre-main. Since not every platform has a way to identify the main
|
||||
//! thread when that happens – macOS's `pthread_main_np` function being a notable
|
||||
//! exception – we cannot assign it the right name right then. Instead, in our
|
||||
//! runtime startup code, we remember the thread ID of the main thread (through
|
||||
//! this modules `set` function) and use it to identify the main thread from then
|
||||
//! on. This works reliably and has the additional advantage that we can report
|
||||
//! the right thread name on main even after the thread handle has been destroyed.
|
||||
//! Note however that this also means that the name reported in pre-main functions
|
||||
//! will be incorrect, but that's just something we have to live with.
|
||||
|
||||
cfg_select! {
|
||||
target_has_atomic = "64" => {
|
||||
use super::id::ThreadId;
|
||||
use crate::sync::atomic::{Atomic, AtomicU64};
|
||||
use crate::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
static MAIN: Atomic<u64> = AtomicU64::new(0);
|
||||
|
||||
pub(super) fn get() -> Option<ThreadId> {
|
||||
ThreadId::from_u64(MAIN.load(Relaxed))
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// May only be called once.
|
||||
pub(crate) unsafe fn set(id: ThreadId) {
|
||||
MAIN.store(id.as_u64().get(), Relaxed)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
use super::id::ThreadId;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::sync::atomic::{Atomic, AtomicBool};
|
||||
use crate::sync::atomic::Ordering::{Acquire, Release};
|
||||
|
||||
static INIT: Atomic<bool> = AtomicBool::new(false);
|
||||
static mut MAIN: MaybeUninit<ThreadId> = MaybeUninit::uninit();
|
||||
|
||||
pub(super) fn get() -> Option<ThreadId> {
|
||||
if INIT.load(Acquire) {
|
||||
Some(unsafe { MAIN.assume_init() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// May only be called once.
|
||||
pub(crate) unsafe fn set(id: ThreadId) {
|
||||
unsafe { MAIN = MaybeUninit::new(id) };
|
||||
INIT.store(true, Release);
|
||||
}
|
||||
}
|
||||
}
|
||||
326
crates/std/src/thread/thread.rs
Normal file
326
crates/std/src/thread/thread.rs
Normal file
@@ -0,0 +1,326 @@
|
||||
use super::id::ThreadId;
|
||||
use super::main_thread;
|
||||
use crate::alloc::System;
|
||||
use crate::ffi::CStr;
|
||||
use crate::fmt;
|
||||
use crate::pin::Pin;
|
||||
use alloc_crate::sync::Arc;
|
||||
use crate::sys::sync::Parker;
|
||||
use crate::time::Duration;
|
||||
|
||||
// This module ensures private fields are kept private, which is necessary to enforce the safety requirements.
|
||||
mod thread_name_string {
|
||||
use crate::ffi::{CStr, CString};
|
||||
use crate::str;
|
||||
|
||||
/// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated.
|
||||
pub(crate) struct ThreadNameString {
|
||||
inner: CString,
|
||||
}
|
||||
|
||||
impl From<String> for ThreadNameString {
|
||||
fn from(s: String) -> Self {
|
||||
Self {
|
||||
inner: CString::new(s).expect("thread name may not contain interior null bytes"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadNameString {
|
||||
pub fn as_cstr(&self) -> &CStr {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
// SAFETY: `ThreadNameString` is guaranteed to be UTF-8.
|
||||
unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use thread_name_string::ThreadNameString;
|
||||
|
||||
/// The internal representation of a `Thread` handle
|
||||
///
|
||||
/// We explicitly set the alignment for our guarantee in Thread::into_raw. This
|
||||
/// allows applications to stuff extra metadata bits into the alignment, which
|
||||
/// can be rather useful when working with atomics.
|
||||
#[repr(align(8))]
|
||||
struct Inner {
|
||||
name: Option<ThreadNameString>,
|
||||
id: ThreadId,
|
||||
parker: Parker,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn parker(self: Pin<&Self>) -> Pin<&Parker> {
|
||||
unsafe { Pin::map_unchecked(self, |inner| &inner.parker) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
/// A handle to a thread.
|
||||
///
|
||||
/// Threads are represented via the `Thread` type, which you can get in one of
|
||||
/// two ways:
|
||||
///
|
||||
/// * By spawning a new thread, e.g., using the [`thread::spawn`]
|
||||
/// function, and calling [`thread`] on the [`JoinHandle`].
|
||||
/// * By requesting the current thread, using the [`thread::current`] function.
|
||||
///
|
||||
/// The [`thread::current`] function is available even for threads not spawned
|
||||
/// by the APIs of this module.
|
||||
///
|
||||
/// There is usually no need to create a `Thread` struct yourself, one
|
||||
/// should instead use a function like `spawn` to create new threads, see the
|
||||
/// docs of [`Builder`] and [`spawn`] for more details.
|
||||
///
|
||||
/// [`thread::spawn`]: super::spawn
|
||||
/// [`thread`]: super::JoinHandle::thread
|
||||
/// [`JoinHandle`]: super::JoinHandle
|
||||
/// [`thread::current`]: super::current::current
|
||||
/// [`Builder`]: super::Builder
|
||||
/// [`spawn`]: super::spawn
|
||||
pub struct Thread {
|
||||
// We use the System allocator such that creating or dropping this handle
|
||||
// does not interfere with a potential Global allocator using thread-local
|
||||
// storage.
|
||||
inner: Pin<Arc<Inner, System>>,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
pub(crate) fn new(id: ThreadId, name: Option<String>) -> Thread {
|
||||
let name = name.map(ThreadNameString::from);
|
||||
|
||||
// We have to use `unsafe` here to construct the `Parker` in-place,
|
||||
// which is required for the UNIX implementation.
|
||||
//
|
||||
// SAFETY: We pin the Arc immediately after creation, so its address never
|
||||
// changes.
|
||||
let inner = unsafe {
|
||||
let mut arc = Arc::<Inner, _>::new_uninit_in(System);
|
||||
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
|
||||
(&raw mut (*ptr).name).write(name);
|
||||
(&raw mut (*ptr).id).write(id);
|
||||
Parker::new_in_place(&raw mut (*ptr).parker);
|
||||
Pin::new_unchecked(arc.assume_init())
|
||||
};
|
||||
|
||||
Thread { inner }
|
||||
}
|
||||
|
||||
/// Like the public [`park`], but callable on any handle. This is used to
|
||||
/// allow parking in TLS destructors.
|
||||
///
|
||||
/// # Safety
|
||||
/// May only be called from the thread to which this handle belongs.
|
||||
///
|
||||
/// [`park`]: super::park
|
||||
pub(crate) unsafe fn park(&self) {
|
||||
unsafe { self.inner.as_ref().parker().park() }
|
||||
}
|
||||
|
||||
/// Like the public [`park_timeout`], but callable on any handle. This is
|
||||
/// used to allow parking in TLS destructors.
|
||||
///
|
||||
/// # Safety
|
||||
/// May only be called from the thread to which this handle belongs.
|
||||
///
|
||||
/// [`park_timeout`]: super::park_timeout
|
||||
pub(crate) unsafe fn park_timeout(&self, dur: Duration) {
|
||||
unsafe { self.inner.as_ref().parker().park_timeout(dur) }
|
||||
}
|
||||
|
||||
/// Atomically makes the handle's token available if it is not already.
|
||||
///
|
||||
/// Every thread is equipped with some basic low-level blocking support, via
|
||||
/// the [`park`] function and the `unpark()` method. These can be used as a
|
||||
/// more CPU-efficient implementation of a spinlock.
|
||||
///
|
||||
/// See the [park documentation] for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
/// use std::sync::atomic::{AtomicBool, Ordering};
|
||||
///
|
||||
/// static QUEUED: AtomicBool = AtomicBool::new(false);
|
||||
///
|
||||
/// let parked_thread = thread::Builder::new()
|
||||
/// .spawn(|| {
|
||||
/// println!("Parking thread");
|
||||
/// QUEUED.store(true, Ordering::Release);
|
||||
/// thread::park();
|
||||
/// println!("Thread unparked");
|
||||
/// })
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // Let some time pass for the thread to be spawned.
|
||||
/// thread::sleep(Duration::from_millis(10));
|
||||
///
|
||||
/// // Wait until the other thread is queued.
|
||||
/// // This is crucial! It guarantees that the `unpark` below is not consumed
|
||||
/// // by some other code in the parked thread (e.g. inside `println!`).
|
||||
/// while !QUEUED.load(Ordering::Acquire) {
|
||||
/// // Spinning is of course inefficient; in practice, this would more likely be
|
||||
/// // a dequeue where we have no work to do if there's nobody queued.
|
||||
/// std::hint::spin_loop();
|
||||
/// }
|
||||
///
|
||||
/// println!("Unpark the thread");
|
||||
/// parked_thread.thread().unpark();
|
||||
///
|
||||
/// parked_thread.join().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [`park`]: super::park
|
||||
/// [park documentation]: super::park
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
pub fn unpark(&self) {
|
||||
self.inner.as_ref().parker().unpark();
|
||||
}
|
||||
|
||||
/// Gets the thread's unique identifier.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let other_thread = thread::spawn(|| {
|
||||
/// thread::current().id()
|
||||
/// });
|
||||
///
|
||||
/// let other_thread_id = other_thread.join().unwrap();
|
||||
/// assert!(thread::current().id() != other_thread_id);
|
||||
/// ```
|
||||
#[stable(feature = "thread_id", since = "1.19.0")]
|
||||
#[must_use]
|
||||
pub fn id(&self) -> ThreadId {
|
||||
self.inner.id
|
||||
}
|
||||
|
||||
/// Gets the thread's name.
|
||||
///
|
||||
/// For more information about named threads, see
|
||||
/// [this module-level documentation][naming-threads].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Threads by default have no name specified:
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new();
|
||||
///
|
||||
/// let handler = builder.spawn(|| {
|
||||
/// assert!(thread::current().name().is_none());
|
||||
/// }).unwrap();
|
||||
///
|
||||
/// handler.join().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// Thread with a specified name:
|
||||
///
|
||||
/// ```
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let builder = thread::Builder::new()
|
||||
/// .name("foo".into());
|
||||
///
|
||||
/// let handler = builder.spawn(|| {
|
||||
/// assert_eq!(thread::current().name(), Some("foo"))
|
||||
/// }).unwrap();
|
||||
///
|
||||
/// handler.join().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [naming-threads]: ./index.html#naming-threads
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[must_use]
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
if let Some(name) = &self.inner.name {
|
||||
Some(name.as_str())
|
||||
} else if main_thread::get() == Some(self.inner.id) {
|
||||
Some("main")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the `Thread`, returning a raw pointer.
|
||||
///
|
||||
/// To avoid a memory leak the pointer must be converted
|
||||
/// back into a `Thread` using [`Thread::from_raw`]. The pointer is
|
||||
/// guaranteed to be aligned to at least 8 bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(thread_raw)]
|
||||
///
|
||||
/// use std::thread::{self, Thread};
|
||||
///
|
||||
/// let thread = thread::current();
|
||||
/// let id = thread.id();
|
||||
/// let ptr = Thread::into_raw(thread);
|
||||
/// unsafe {
|
||||
/// assert_eq!(Thread::from_raw(ptr).id(), id);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "thread_raw", issue = "97523")]
|
||||
pub fn into_raw(self) -> *const () {
|
||||
// Safety: We only expose an opaque pointer, which maintains the `Pin` invariant.
|
||||
let inner = unsafe { Pin::into_inner_unchecked(self.inner) };
|
||||
Arc::into_raw_with_allocator(inner).0 as *const ()
|
||||
}
|
||||
|
||||
/// Constructs a `Thread` from a raw pointer.
|
||||
///
|
||||
/// The raw pointer must have been previously returned
|
||||
/// by a call to [`Thread::into_raw`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because improper use may lead
|
||||
/// to memory unsafety, even if the returned `Thread` is never
|
||||
/// accessed.
|
||||
///
|
||||
/// Creating a `Thread` from a pointer other than one returned
|
||||
/// from [`Thread::into_raw`] is **undefined behavior**.
|
||||
///
|
||||
/// Calling this function twice on the same raw pointer can lead
|
||||
/// to a double-free if both `Thread` instances are dropped.
|
||||
#[unstable(feature = "thread_raw", issue = "97523")]
|
||||
pub unsafe fn from_raw(ptr: *const ()) -> Thread {
|
||||
// Safety: Upheld by caller.
|
||||
unsafe {
|
||||
Thread { inner: Pin::new_unchecked(Arc::from_raw_in(ptr as *const Inner, System)) }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cname(&self) -> Option<&CStr> {
|
||||
if let Some(name) = &self.inner.name {
|
||||
Some(name.as_cstr())
|
||||
} else if main_thread::get() == Some(self.inner.id) {
|
||||
Some(c"main")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl fmt::Debug for Thread {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Thread")
|
||||
.field("id", &self.id())
|
||||
.field("name", &self.name())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
67
justfile
67
justfile
@@ -27,6 +27,55 @@ update_std:
|
||||
# @just cp_std "process.rs"
|
||||
# @just cp_std "fs.rs"
|
||||
@just cp_std "time.rs"
|
||||
@just cp_std "bstr.rs"
|
||||
@just cp_std "io/error.rs"
|
||||
@just cp_std "io/error/repr_bitpacked.rs"
|
||||
@just cp_std "io/error/repr_unpacked.rs"
|
||||
@just cp_std "io/error/tests.rs"
|
||||
@just cp_std "io/cursor.rs"
|
||||
@just cp_std "io/cursor/tests.rs"
|
||||
@just cp_std "io/prelude.rs"
|
||||
@just cp_std "io/impls.rs"
|
||||
@just cp_std "io/impls/tests.rs"
|
||||
@just cp_std "io/tests.rs"
|
||||
@just cp_std "io/util.rs"
|
||||
@just cp_std "io/util/tests.rs"
|
||||
@just cp_std "io/copy.rs"
|
||||
@just cp_std "io/copy/tests.rs"
|
||||
@just cp_std "io/pipe.rs"
|
||||
@just cp_std "io/pipe/tests.rs"
|
||||
@just cp_std "io/stdio.rs"
|
||||
@just cp_std "io/buffered/mod.rs"
|
||||
@just cp_std "io/buffered/bufreader.rs"
|
||||
@just cp_std "io/buffered/bufreader/buffer.rs"
|
||||
@just cp_std "io/buffered/bufwriter.rs"
|
||||
@just cp_std "io/buffered/linewriter.rs"
|
||||
@just cp_std "io/buffered/linewritershim.rs"
|
||||
@just cp_std "sync/once.rs"
|
||||
@just cp_std "sync/once_lock.rs"
|
||||
@just cp_std "sync/lazy_lock.rs"
|
||||
@just cp_std "sync/nonpoison.rs"
|
||||
@just cp_std "sync/nonpoison/condvar.rs"
|
||||
@just cp_std "sync/nonpoison/mutex.rs"
|
||||
@just cp_std "sync/nonpoison/rwlock.rs"
|
||||
@just cp_std "sync/poison.rs"
|
||||
@just cp_std "sync/poison/condvar.rs"
|
||||
@just cp_std "sync/poison/mutex.rs"
|
||||
@just cp_std "sync/poison/rwlock.rs"
|
||||
@just cp_std "sync/barrier.rs"
|
||||
@just cp_std "sync/reentrant_lock.rs"
|
||||
@just cp_std "sync/mpsc.rs"
|
||||
@just cp_std "sync/mpmc/mod.rs"
|
||||
@just cp_std "sync/mpmc/array.rs"
|
||||
@just cp_std "sync/mpmc/context.rs"
|
||||
@just cp_std "sync/mpmc/counter.rs"
|
||||
@just cp_std "sync/mpmc/error.rs"
|
||||
@just cp_std "sync/mpmc/list.rs"
|
||||
@just cp_std "sync/mpmc/select.rs"
|
||||
@just cp_std "sync/mpmc/tests.rs"
|
||||
@just cp_std "sync/mpmc/utils.rs"
|
||||
@just cp_std "sync/mpmc/waker.rs"
|
||||
@just cp_std "sync/mpmc/zero.rs"
|
||||
@just cp_std "hash/mod.rs"
|
||||
@just cp_std "hash/random.rs"
|
||||
@just cp_std "num/mod.rs"
|
||||
@@ -35,6 +84,14 @@ update_std:
|
||||
@just cp_std "ffi/os_str.rs"
|
||||
@just cp_std "ffi/os_str/tests.rs"
|
||||
@just cp_std "thread/local.rs"
|
||||
@just cp_std "thread/thread.rs"
|
||||
@just cp_std "thread/id.rs"
|
||||
@just cp_std "thread/main_thread.rs"
|
||||
@just cp_std "thread/current.rs"
|
||||
@just cp_std "thread/join_handle.rs"
|
||||
@just cp_std "thread/functions.rs"
|
||||
@just cp_std "thread/lifecycle.rs"
|
||||
@just cp_std "thread/builder.rs"
|
||||
@just cp_std "sys/exit.rs"
|
||||
@just cp_std "sys/env_consts.rs"
|
||||
@just cp_std "sys/configure_builtins.rs"
|
||||
@@ -65,11 +122,21 @@ update_std:
|
||||
@just cp_std "sys/fs/mod.rs"
|
||||
@just cp_std "sys/fs/common.rs"
|
||||
@just cp_std "sys/fs/unsupported.rs"
|
||||
@just cp_std "sys/io/error/generic.rs"
|
||||
@just cp_std "sys/io/io_slice/unsupported.rs"
|
||||
@just cp_std "sys/io/is_terminal/unsupported.rs"
|
||||
@just cp_std "sys/io/kernel_copy/mod.rs"
|
||||
@just cp_std "sys/io/mod.rs"
|
||||
@just cp_std "sys/pipe/mod.rs"
|
||||
@just cp_std "sys/pipe/unsupported.rs"
|
||||
@just cp_std "sys/stdio/mod.rs"
|
||||
@just cp_std "sys/stdio/unsupported.rs"
|
||||
@just insert_target 108 "sys/random/mod.rs"
|
||||
@just insert_target 125 "sys/random/mod.rs"
|
||||
@# Copied but edited for the moment
|
||||
# @just cp_std "sys/thread_local/mod.rs"
|
||||
# @just cp_std "sys/alloc/mod.rs"
|
||||
# @just cp_std "sys/io/error/mod.rs"
|
||||
# @just cp_std "alloc.rs"
|
||||
# @just cp_std "path.rs"
|
||||
# @just cp_std "sys/path/unix.rs"
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
s|crate::bstr::ByteStr|alloc_crate::bstr::ByteStr|g
|
||||
s|crate::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
|
||||
s|crate::sync::Arc|alloc_crate::sync::Arc|g
|
||||
s|alloc::ffi|alloc_crate::ffi|g
|
||||
s|alloc::slice::Join|alloc_crate::slice::Join|g
|
||||
s|alloc::bstr|alloc_crate::bstr|g
|
||||
s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
|
||||
s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g
|
||||
s|crate::panic|core::panic|g
|
||||
s|use crate::{io, panicking};|use crate::io;use core::panicking;|g
|
||||
s|use crate::{env, io, panic};|use crate::{env, io};use core::panic;|g
|
||||
# /target_os = "xous",/a \ target_os = "survos",
|
||||
|
||||
# Ajouter d'autres modifications facilement ici :
|
||||
|
||||
@@ -5,3 +5,4 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
std = { path = "../../crates/std" }
|
||||
io = { path = "../../crates/io" }
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#![feature(custom_std)]
|
||||
#![allow(unused)]
|
||||
|
||||
use std::{
|
||||
io::{Read, Write, stdin},
|
||||
syscall,
|
||||
};
|
||||
use io::{Read, Write};
|
||||
use std::{io::stdin, syscall};
|
||||
|
||||
fn main() {
|
||||
// let mut input = String::new();
|
||||
@@ -18,7 +16,7 @@ fn main() {
|
||||
loop {
|
||||
let mut test = [0; 2];
|
||||
let len = stdin().read(&mut test).unwrap();
|
||||
tty.write_all(str::from_utf8(&test[..len as usize]).unwrap().as_bytes())
|
||||
tty.write(str::from_utf8(&test[..len as usize]).unwrap().as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user