diff options
Diffstat (limited to 'library/std/src')
| -rw-r--r-- | library/std/src/collections/hash/map.rs | 4 | ||||
| -rw-r--r-- | library/std/src/collections/hash/set.rs | 4 | ||||
| -rw-r--r-- | library/std/src/collections/mod.rs | 8 | ||||
| -rw-r--r-- | library/std/src/error.rs | 21 | ||||
| -rw-r--r-- | library/std/src/ffi/os_str.rs | 3 | ||||
| -rw-r--r-- | library/std/src/future.rs | 17 | ||||
| -rw-r--r-- | library/std/src/io/buffered/bufreader.rs | 1 | ||||
| -rw-r--r-- | library/std/src/io/buffered/bufwriter.rs | 14 | ||||
| -rw-r--r-- | library/std/src/io/copy.rs | 80 | ||||
| -rw-r--r-- | library/std/src/io/mod.rs | 7 | ||||
| -rw-r--r-- | library/std/src/io/util.rs | 17 | ||||
| -rw-r--r-- | library/std/src/io/util/tests.rs | 70 | ||||
| -rw-r--r-- | library/std/src/lib.rs | 12 | ||||
| -rw-r--r-- | library/std/src/macros.rs | 16 | ||||
| -rw-r--r-- | library/std/src/net/ip.rs | 14 | ||||
| -rw-r--r-- | library/std/src/panic.rs | 35 | ||||
| -rw-r--r-- | library/std/src/sys/windows/c.rs | 1 | ||||
| -rw-r--r-- | library/std/src/sys/windows/compat.rs | 153 | ||||
| -rw-r--r-- | library/std/src/sys/windows/thread_parker.rs | 12 |
19 files changed, 364 insertions, 125 deletions
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 829fc3817af..28a25572dd8 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -658,8 +658,7 @@ where /// down no lower than the supplied limit while maintaining the internal rules /// and possibly leaving some space in accordance with the resize policy. /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. + /// If the current capacity is less than the lower limit, this is a no-op. /// /// # Examples /// @@ -679,7 +678,6 @@ where #[inline] #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] pub fn shrink_to(&mut self, min_capacity: usize) { - assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity"); self.base.shrink_to(min_capacity); } diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index baa3026ff75..b08510d6b01 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -462,9 +462,7 @@ where /// down no lower than the supplied limit while maintaining the internal rules /// and possibly leaving some space in accordance with the resize policy. /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. - /// + /// If the current capacity is less than the lower limit, this is a no-op. /// # Examples /// /// ``` diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index a1aab767eb2..80a13d52a2a 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -110,10 +110,10 @@ //! //! For Sets, all operations have the cost of the equivalent Map operation. //! -//! | | get | insert | remove | predecessor | append | -//! |--------------|-----------|-----------|-----------|-------------|--------| -//! | [`HashMap`] | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | -//! | [`BTreeMap`] | O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) | O(n+m) | +//! | | get | insert | remove | range | append | +//! |--------------|-----------|-----------|-----------|-----------|--------| +//! | [`HashMap`] | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | +//! | [`BTreeMap`] | O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) | O(n+m) | //! //! # Correct and Efficient Usage of Collections //! diff --git a/library/std/src/error.rs b/library/std/src/error.rs index ca83c409822..605d953f5da 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -486,6 +486,27 @@ impl<T: Error> Error for Box<T> { } } +#[stable(feature = "error_by_ref", since = "1.51.0")] +impl<'a, T: Error + ?Sized> Error for &'a T { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + Error::description(&**self) + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn Error> { + Error::cause(&**self) + } + + fn source(&self) -> Option<&(dyn Error + 'static)> { + Error::source(&**self) + } + + fn backtrace(&self) -> Option<&Backtrace> { + Error::backtrace(&**self) + } +} + #[stable(feature = "fmt_error", since = "1.11.0")] impl Error for fmt::Error { #[allow(deprecated)] diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 32f0f8a52f8..21060182d60 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -304,8 +304,7 @@ impl OsString { /// The capacity will remain at least as large as both the length /// and the supplied value. /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. + /// If the current capacity is less than the lower limit, this is a no-op. /// /// # Examples /// diff --git a/library/std/src/future.rs b/library/std/src/future.rs deleted file mode 100644 index 9d9c36e9afb..00000000000 --- a/library/std/src/future.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Asynchronous values. - -#[doc(inline)] -#[stable(feature = "futures_api", since = "1.36.0")] -pub use core::future::Future; - -#[doc(inline)] -#[unstable(feature = "gen_future", issue = "50547")] -pub use core::future::{from_generator, get_context, ResumeTy}; - -#[doc(inline)] -#[stable(feature = "future_readiness_fns", since = "1.48.0")] -pub use core::future::{pending, ready, Pending, Ready}; - -#[doc(inline)] -#[unstable(feature = "into_future", issue = "67644")] -pub use core::future::IntoFuture; diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 8bae3da1273..987371f50ec 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -410,7 +410,6 @@ impl<R: Seek> Seek for BufReader<R> { /// # Example /// /// ```no_run - /// #![feature(seek_convenience)] /// use std::{ /// io::{self, BufRead, BufReader, Seek}, /// fs::File, diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index fa7bd0d72d1..65bc2fcf00a 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -117,7 +117,7 @@ impl<W: Write> BufWriter<W> { /// "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(super) fn flush_buf(&mut self) -> io::Result<()> { + 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. @@ -243,6 +243,18 @@ impl<W: Write> BufWriter<W> { &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 diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index b88bca2f2b4..3780f2044cb 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,4 +1,4 @@ -use crate::io::{self, ErrorKind, Read, Write}; +use super::{BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE}; use crate::mem::MaybeUninit; /// Copies the entire contents of a reader into a writer. @@ -40,7 +40,7 @@ use crate::mem::MaybeUninit; /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64> +pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64> where R: Read, W: Write, @@ -54,14 +54,82 @@ where } } -/// The general read-write-loop implementation of -/// `io::copy` that is used when specializations are not available or not applicable. -pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64> +/// 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. +pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64> where R: Read, W: Write, { - let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit(); + BufferedCopySpec::copy_to(reader, writer) +} + +/// Specialization of the read-write loop that either uses a stack buffer +/// or reuses the internal buffer of a BufWriter +trait BufferedCopySpec: Write { + fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64>; +} + +impl<W: Write + ?Sized> BufferedCopySpec for W { + default fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> { + stack_buffer_copy(reader, writer) + } +} + +impl<I: Write> BufferedCopySpec for BufWriter<I> { + fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> { + if writer.capacity() < DEFAULT_BUF_SIZE { + return stack_buffer_copy(reader, writer); + } + + // FIXME: #42788 + // + // - This creates a (mut) reference to a slice of + // _uninitialized_ integers, which is **undefined behavior** + // + // - Only the standard library gets to soundly "ignore" this, + // based on its privileged knowledge of unstable rustc + // internals; + unsafe { + let spare_cap = writer.buffer_mut().spare_capacity_mut(); + reader.initializer().initialize(MaybeUninit::slice_assume_init_mut(spare_cap)); + } + + let mut len = 0; + + loop { + let buf = writer.buffer_mut(); + let spare_cap = buf.spare_capacity_mut(); + + if spare_cap.len() >= DEFAULT_BUF_SIZE { + match reader.read(unsafe { MaybeUninit::slice_assume_init_mut(spare_cap) }) { + Ok(0) => return Ok(len), // EOF reached + Ok(bytes_read) => { + assert!(bytes_read <= spare_cap.len()); + // Safety: The initializer contract guarantees that either it or `read` + // will have initialized these bytes. And we just checked that the number + // of bytes is within the buffer capacity. + unsafe { buf.set_len(buf.len() + bytes_read) }; + len += bytes_read as u64; + // Read again if the buffer still has enough capacity, as BufWriter itself would do + // This will occur if the reader returns short reads + continue; + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + } + } + + writer.flush_buf()?; + } + } +} + +fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>( + reader: &mut R, + writer: &mut W, +) -> Result<u64> { + let mut buf = MaybeUninit::<[u8; DEFAULT_BUF_SIZE]>::uninit(); // FIXME: #42788 // // - This creates a (mut) reference to a slice of diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index c87a56586c6..db3b0e2628f 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1671,7 +1671,7 @@ pub trait Seek { /// # Example /// /// ```no_run - /// #![feature(seek_convenience)] + /// #![feature(seek_stream_len)] /// use std::{ /// io::{self, Seek}, /// fs::File, @@ -1685,7 +1685,7 @@ pub trait Seek { /// Ok(()) /// } /// ``` - #[unstable(feature = "seek_convenience", issue = "59359")] + #[unstable(feature = "seek_stream_len", issue = "59359")] fn stream_len(&mut self) -> Result<u64> { let old_pos = self.stream_position()?; let len = self.seek(SeekFrom::End(0))?; @@ -1706,7 +1706,6 @@ pub trait Seek { /// # Example /// /// ```no_run - /// #![feature(seek_convenience)] /// use std::{ /// io::{self, BufRead, BufReader, Seek}, /// fs::File, @@ -1723,7 +1722,7 @@ pub trait Seek { /// Ok(()) /// } /// ``` - #[unstable(feature = "seek_convenience", issue = "59359")] + #[stable(feature = "seek_convenience", since = "1.51.0")] fn stream_position(&mut self) -> Result<u64> { self.seek(SeekFrom::Current(0)) } diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index db845457c96..e43ce4cdb4b 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -4,7 +4,7 @@ mod tests; use crate::fmt; -use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Write}; +use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; /// A reader which is always at EOF. /// @@ -58,6 +58,21 @@ impl BufRead for Empty { fn consume(&mut self, _n: usize) {} } +#[stable(feature = "empty_seek", since = "1.51.0")] +impl Seek for Empty { + fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> { + Ok(0) + } + + fn stream_len(&mut self) -> io::Result<u64> { + Ok(0) + } + + fn stream_position(&mut self) -> io::Result<u64> { + Ok(0) + } +} + #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Empty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index 9450b1ee124..7632eaf872a 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -1,5 +1,8 @@ +use crate::cmp::{max, min}; use crate::io::prelude::*; -use crate::io::{copy, empty, repeat, sink, Empty, Repeat, Sink}; +use crate::io::{ + copy, empty, repeat, sink, BufWriter, Empty, Repeat, Result, SeekFrom, Sink, DEFAULT_BUF_SIZE, +}; #[test] fn copy_copies() { @@ -11,6 +14,51 @@ fn copy_copies() { 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); + 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 sink_sinks() { let mut s = sink(); @@ -30,6 +78,26 @@ fn empty_reads() { } #[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 repeat_repeats() { let mut r = repeat(4); let mut b = [0; 1024]; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5ba13c2f913..b6929f59395 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -224,6 +224,7 @@ #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] +#![feature(async_stream)] #![feature(arbitrary_self_types)] #![feature(array_error_internals)] #![feature(asm)] @@ -258,6 +259,7 @@ #![feature(dropck_eyepatch)] #![feature(duration_constants)] #![feature(duration_zero)] +#![feature(edition_panic)] #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] #![feature(extend_one)] @@ -282,6 +284,7 @@ #![feature(maybe_uninit_extra)] #![feature(maybe_uninit_ref)] #![feature(maybe_uninit_slice)] +#![feature(maybe_uninit_uninit_array)] #![feature(min_specialization)] #![feature(needs_panic_runtime)] #![feature(negative_impls)] @@ -298,7 +301,6 @@ #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(raw)] -#![feature(raw_ref_macros)] #![feature(ready_macro)] #![feature(rustc_attrs)] #![feature(rustc_private)] @@ -326,6 +328,7 @@ #![feature(unsafe_cell_raw_get)] #![feature(unwind_attributes)] #![feature(vec_into_raw_parts)] +#![feature(vec_spare_capacity)] #![feature(wake_trait)] // NB: the above list is sorted to minimize merge conflicts. #![default_lib_allocator] @@ -406,6 +409,8 @@ pub use core::cmp; pub use core::convert; #[stable(feature = "rust1", since = "1.0.0")] pub use core::default; +#[stable(feature = "futures_api", since = "1.36.0")] +pub use core::future; #[stable(feature = "rust1", since = "1.0.0")] pub use core::hash; #[stable(feature = "core_hint", since = "1.27.0")] @@ -448,6 +453,8 @@ pub use core::ptr; pub use core::raw; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; +#[unstable(feature = "async_stream", issue = "79024")] +pub use core::stream; #[stable(feature = "i128", since = "1.26.0")] #[allow(deprecated, deprecated_in_future)] pub use core::u128; @@ -505,9 +512,6 @@ pub mod task { pub use alloc::task::*; } -#[stable(feature = "futures_api", since = "1.36.0")] -pub mod future; - // Platform-abstraction modules #[macro_use] mod sys_common; diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index e466f315152..0ce6542cb72 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -4,6 +4,7 @@ //! library. Each macro is available for use when linking against the standard //! library. +#[cfg(bootstrap)] #[doc(include = "../../core/src/macros/panic.md")] #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] @@ -17,6 +18,21 @@ macro_rules! panic { }); } +#[cfg(not(bootstrap))] +#[doc(include = "../../core/src/macros/panic.md")] +#[macro_export] +#[rustc_builtin_macro = "std_panic"] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow_internal_unstable(edition_panic)] +#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")] +macro_rules! panic { + // Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021` + // depending on the edition of the caller. + ($($arg:tt)*) => { + /* compiler built-in */ + }; +} + /// Prints to the standard output. /// /// Equivalent to the [`println!`] macro except that a newline is not printed at diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index 84449e48767..a8a4127d40a 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -1217,8 +1217,8 @@ impl Ipv6Addr { /// Returns [`true`] if the address is a unicast link-local address (`fe80::/64`). /// - /// A common mis-conception is to think that "unicast link-local addresses start with - /// `fe80::`", but the [IETF RFC 4291] actually defines a stricter format for these addresses: + /// A common misconception is to think that "unicast link-local addresses start with + /// `fe80::`", but [IETF RFC 4291] actually defines a stricter format for these addresses: /// /// ```no_rust /// | 10 | @@ -1228,9 +1228,9 @@ impl Ipv6Addr { /// +----------+-------------------------+----------------------------+ /// ``` /// - /// This method validates the format defined in the RFC and won't recognize the following - /// addresses such as `fe80:0:0:1::` or `fe81::` as unicast link-local addresses for example. - /// If you need a less strict validation use [`Ipv6Addr::is_unicast_link_local()`] instead. + /// This method validates the format defined in the RFC and won't recognize addresses + /// like `fe80:0:0:1::` or `fe81::` as unicast link-local addresses. + /// If you need a less strict validation, use [`Ipv6Addr::is_unicast_link_local()`] instead. /// /// # Examples /// @@ -1282,7 +1282,7 @@ impl Ipv6Addr { /// +----------+-------------------------+----------------------------+ /// ``` /// - /// As a result, this method consider addresses such as `fe80:0:0:1::` or `fe81::` to be + /// As a result, this method considers addresses such as `fe80:0:0:1::` or `fe81::` to be /// unicast link-local addresses, whereas [`Ipv6Addr::is_unicast_link_local_strict()`] does not. /// If you need a strict validation fully compliant with the RFC, use /// [`Ipv6Addr::is_unicast_link_local_strict()`] instead. @@ -1362,7 +1362,7 @@ impl Ipv6Addr { } /// Returns [`true`] if this is an address reserved for documentation - /// (2001:db8::/32). + /// (`2001:db8::/32`). /// /// This property is defined in [IETF RFC 3849]. /// diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 0f568da459b..89a822a7229 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -12,11 +12,33 @@ use crate::panicking; use crate::pin::Pin; use crate::ptr::{NonNull, Unique}; use crate::rc::Rc; +use crate::stream::Stream; use crate::sync::atomic; use crate::sync::{Arc, Mutex, RwLock}; use crate::task::{Context, Poll}; use crate::thread::Result; +#[doc(hidden)] +#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] +#[allow_internal_unstable(libstd_sys_internals)] +#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")] +#[rustc_macro_transparency = "semitransparent"] +pub macro panic_2015 { + () => ({ + $crate::rt::begin_panic("explicit panic") + }), + ($msg:expr $(,)?) => ({ + $crate::rt::begin_panic($msg) + }), + ($fmt:expr, $($arg:tt)+) => ({ + $crate::rt::begin_panic_fmt(&$crate::format_args!($fmt, $($arg)+)) + }), +} + +#[doc(hidden)] +#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] +pub use core::panic::panic_2021; + #[stable(feature = "panic_hooks", since = "1.10.0")] pub use crate::panicking::{set_hook, take_hook}; @@ -340,6 +362,19 @@ impl<F: Future> Future for AssertUnwindSafe<F> { } } +#[unstable(feature = "async_stream", issue = "79024")] +impl<S: Stream> Stream for AssertUnwindSafe<S> { + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> { + unsafe { self.map_unchecked_mut(|x| &mut x.0) }.poll_next(cx) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.0.size_hint() + } +} + /// Invokes a closure, capturing the cause of an unwinding panic if one occurs. /// /// This function will return `Ok` with the closure's result if the closure diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index f43a19d91b6..dec88620810 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -975,6 +975,7 @@ extern "system" { pub fn freeaddrinfo(res: *mut ADDRINFOA); pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void; + pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE; pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME); diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index e9588e29758..017a4bbe97c 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -1,93 +1,116 @@ -//! A "compatibility layer" for spanning XP and Windows 7 +//! A "compatibility layer" for supporting older versions of Windows //! -//! The standard library currently binds many functions that are not available -//! on Windows XP, but we would also like to support building executables that -//! run on XP. To do this we specify all non-XP APIs as having a fallback -//! implementation to do something reasonable. +//! The standard library uses some Windows API functions that are not present +//! on older versions of Windows. (Note that the oldest version of Windows +//! that Rust supports is Windows 7 (client) and Windows Server 2008 (server).) +//! This module implements a form of delayed DLL import binding, using +//! `GetModuleHandle` and `GetProcAddress` to look up DLL entry points at +//! runtime. //! -//! This dynamic runtime detection of whether a function is available is -//! implemented with `GetModuleHandle` and `GetProcAddress` paired with a -//! static-per-function which caches the result of the first check. In this -//! manner we pay a semi-large one-time cost up front for detecting whether a -//! function is available but afterwards it's just a load and a jump. - -use crate::ffi::CString; -use crate::sys::c; - -pub fn lookup(module: &str, symbol: &str) -> Option<usize> { - let mut module: Vec<u16> = module.encode_utf16().collect(); - module.push(0); - let symbol = CString::new(symbol).unwrap(); - unsafe { - let handle = c::GetModuleHandleW(module.as_ptr()); - match c::GetProcAddress(handle, symbol.as_ptr()) as usize { - 0 => None, - n => Some(n), - } - } -} +//! This implementation uses a static initializer to look up the DLL entry +//! points. The CRT (C runtime) executes static initializers before `main` +//! is called (for binaries) and before `DllMain` is called (for DLLs). +//! This is the ideal time to look up DLL imports, because we are guaranteed +//! that no other threads will attempt to call these entry points. Thus, +//! we can look up the imports and store them in `static mut` fields +//! without any synchronization. +//! +//! This has an additional advantage: Because the DLL import lookup happens +//! at module initialization, the cost of these lookups is deterministic, +//! and is removed from the code paths that actually call the DLL imports. +//! That is, there is no unpredictable "cache miss" that occurs when calling +//! a DLL import. For applications that benefit from predictable delays, +//! this is a benefit. This also eliminates the comparison-and-branch +//! from the hot path. +//! +//! Currently, the standard library uses only a small number of dynamic +//! DLL imports. If this number grows substantially, then the cost of +//! performing all of the lookups at initialization time might become +//! substantial. +//! +//! The mechanism of registering a static initializer with the CRT is +//! documented in +//! [CRT Initialization](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160). +//! It works by contributing a global symbol to the `.CRT$XCU` section. +//! The linker builds a table of all static initializer functions. +//! The CRT startup code then iterates that table, calling each +//! initializer function. +//! +//! # **WARNING!!* +//! The environment that a static initializer function runs in is highly +//! constrained. There are **many** restrictions on what static initializers +//! can safely do. Static initializer functions **MUST NOT** do any of the +//! following (this list is not comprehensive): +//! * touch any other static field that is used by a different static +//! initializer, because the order that static initializers run in +//! is not defined. +//! * call `LoadLibrary` or any other function that acquires the DLL +//! loader lock. +//! * call any Rust function or CRT function that touches any static +//! (global) state. macro_rules! compat_fn { ($module:literal: $( $(#[$meta:meta])* - pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $body:block + pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block )*) => ($( $(#[$meta])* pub mod $symbol { #[allow(unused_imports)] use super::*; - use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::mem; type F = unsafe extern "system" fn($($argtype),*) -> $rettype; - static PTR: AtomicUsize = AtomicUsize::new(0); - - #[allow(unused_variables)] - unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype $body - - /// This address is stored in `PTR` to incidate an unavailable API. - /// - /// This way, call() will end up calling fallback() if it is unavailable. - /// - /// This is a `static` to avoid rustc duplicating `fn fallback()` - /// into both load() and is_available(), which would break - /// is_available()'s comparison. By using the same static variable - /// in both places, they'll refer to the same (copy of the) - /// function. + /// Points to the DLL import, or the fallback function. /// - /// LLVM merging the address of fallback with other functions - /// (because of unnamed_addr) is fine, since it's only compared to - /// an address from GetProcAddress from an external dll. - static FALLBACK: F = fallback; + /// This static can be an ordinary, unsynchronized, mutable static because + /// we guarantee that all of the writes finish during CRT initialization, + /// and all of the reads occur after CRT initialization. + static mut PTR: Option<F> = None; - #[cold] - fn load() -> usize { - // There is no locking here. It's okay if this is executed by multiple threads in - // parallel. `lookup` will result in the same value, and it's okay if they overwrite - // eachothers result as long as they do so atomically. We don't need any guarantees - // about memory ordering, as this involves just a single atomic variable which is - // not used to protect or order anything else. - let addr = crate::sys::compat::lookup($module, stringify!($symbol)) - .unwrap_or(FALLBACK as usize); - PTR.store(addr, Ordering::Relaxed); - addr - } + /// This symbol is what allows the CRT to find the `init` function and call it. + /// It is marked `#[used]` because otherwise Rust would assume that it was not + /// used, and would remove it. + #[used] + #[link_section = ".CRT$XCU"] + static INIT_TABLE_ENTRY: fn() = init; - fn addr() -> usize { - match PTR.load(Ordering::Relaxed) { - 0 => load(), - addr => addr, + fn init() { + // There is no locking here. This code is executed before main() is entered, and + // is guaranteed to be single-threaded. + // + // DO NOT do anything interesting or complicated in this function! DO NOT call + // any Rust functions or CRT functions, if those functions touch any global state, + // because this function runs during global initialization. For example, DO NOT + // do any dynamic allocation, don't call LoadLibrary, etc. + unsafe { + let module_name: *const u8 = concat!($module, "\0").as_ptr(); + let symbol_name: *const u8 = concat!(stringify!($symbol), "\0").as_ptr(); + let module_handle = $crate::sys::c::GetModuleHandleA(module_name as *const i8); + if !module_handle.is_null() { + match $crate::sys::c::GetProcAddress(module_handle, symbol_name as *const i8) as usize { + 0 => {} + n => { + PTR = Some(mem::transmute::<usize, F>(n)); + } + } + } } } #[allow(dead_code)] - pub fn is_available() -> bool { - addr() != FALLBACK as usize + pub fn option() -> Option<F> { + unsafe { PTR } } + #[allow(dead_code)] pub unsafe fn call($($argname: $argtype),*) -> $rettype { - mem::transmute::<usize, F>(addr())($($argname),*) + if let Some(ptr) = PTR { + ptr($($argname),*) + } else { + $fallback_body + } } } diff --git a/library/std/src/sys/windows/thread_parker.rs b/library/std/src/sys/windows/thread_parker.rs index 9e4c9aa0a51..4f59d4dd452 100644 --- a/library/std/src/sys/windows/thread_parker.rs +++ b/library/std/src/sys/windows/thread_parker.rs @@ -108,10 +108,10 @@ impl Parker { return; } - if c::WaitOnAddress::is_available() { + if let Some(wait_on_address) = c::WaitOnAddress::option() { loop { // Wait for something to happen, assuming it's still set to PARKED. - c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE); + wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE); // Change NOTIFIED=>EMPTY but leave PARKED alone. if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() { // Actually woken up by unpark(). @@ -140,9 +140,9 @@ impl Parker { return; } - if c::WaitOnAddress::is_available() { + if let Some(wait_on_address) = c::WaitOnAddress::option() { // Wait for something to happen, assuming it's still set to PARKED. - c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout)); + wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout)); // Set the state back to EMPTY (from either PARKED or NOTIFIED). // Note that we don't just write EMPTY, but use swap() to also // include an acquire-ordered read to synchronize with unpark()'s @@ -192,9 +192,9 @@ impl Parker { // purpose, to make sure every unpark() has a release-acquire ordering // with park(). if self.state.swap(NOTIFIED, Release) == PARKED { - if c::WakeByAddressSingle::is_available() { + if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() { unsafe { - c::WakeByAddressSingle(self.ptr()); + wake_by_address_single(self.ptr()); } } else { // If we run NtReleaseKeyedEvent before the waiting thread runs |
