about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/backtrace.rs59
-rw-r--r--library/std/src/backtrace/tests.rs13
-rw-r--r--library/std/src/error.rs3
-rw-r--r--library/std/src/ffi/mod.rs2
-rw-r--r--library/std/src/io/util.rs100
-rw-r--r--library/std/src/io/util/tests.rs13
-rw-r--r--library/std/src/lib.rs1
-rw-r--r--library/std/src/os/android/raw.rs2
-rw-r--r--library/std/src/panicking.rs4
-rw-r--r--library/std/src/sync/condvar.rs18
-rw-r--r--library/std/src/sync/lazy_lock.rs17
-rw-r--r--library/std/src/sync/mutex.rs8
-rw-r--r--library/std/src/sync/once_lock.rs8
-rw-r--r--library/std/src/sync/rwlock.rs8
-rw-r--r--library/std/src/sys/unix/rand.rs22
-rw-r--r--library/std/src/sys/wasi/mod.rs22
-rw-r--r--library/std/src/sys/wasi/os.rs5
-rw-r--r--library/std/src/sys/wasi/thread.rs124
-rw-r--r--library/std/src/sys_common/backtrace.rs19
19 files changed, 324 insertions, 124 deletions
diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs
index 7543ffadd41..e7110aebdea 100644
--- a/library/std/src/backtrace.rs
+++ b/library/std/src/backtrace.rs
@@ -89,12 +89,12 @@ mod tests;
 // a backtrace or actually symbolizing it.
 
 use crate::backtrace_rs::{self, BytesOrWideString};
-use crate::cell::UnsafeCell;
 use crate::env;
 use crate::ffi::c_void;
 use crate::fmt;
+use crate::panic::UnwindSafe;
 use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed};
-use crate::sync::Once;
+use crate::sync::LazyLock;
 use crate::sys_common::backtrace::{lock, output_filename};
 use crate::vec::Vec;
 
@@ -133,12 +133,11 @@ pub enum BacktraceStatus {
 enum Inner {
     Unsupported,
     Disabled,
-    Captured(LazilyResolvedCapture),
+    Captured(LazyLock<Capture, LazyResolve>),
 }
 
 struct Capture {
     actual_start: usize,
-    resolved: bool,
     frames: Vec<BacktraceFrame>,
 }
 
@@ -179,7 +178,7 @@ impl fmt::Debug for Backtrace {
         let capture = match &self.inner {
             Inner::Unsupported => return fmt.write_str("<unsupported>"),
             Inner::Disabled => return fmt.write_str("<disabled>"),
-            Inner::Captured(c) => c.force(),
+            Inner::Captured(c) => &**c,
         };
 
         let frames = &capture.frames[capture.actual_start..];
@@ -347,11 +346,10 @@ impl Backtrace {
         let inner = if frames.is_empty() {
             Inner::Unsupported
         } else {
-            Inner::Captured(LazilyResolvedCapture::new(Capture {
+            Inner::Captured(LazyLock::new(lazy_resolve(Capture {
                 actual_start: actual_start.unwrap_or(0),
                 frames,
-                resolved: false,
-            }))
+            })))
         };
 
         Backtrace { inner }
@@ -376,7 +374,7 @@ impl<'a> Backtrace {
     #[must_use]
     #[unstable(feature = "backtrace_frames", issue = "79676")]
     pub fn frames(&'a self) -> &'a [BacktraceFrame] {
-        if let Inner::Captured(c) = &self.inner { &c.force().frames } else { &[] }
+        if let Inner::Captured(c) = &self.inner { &c.frames } else { &[] }
     }
 }
 
@@ -386,7 +384,7 @@ impl fmt::Display for Backtrace {
         let capture = match &self.inner {
             Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
             Inner::Disabled => return fmt.write_str("disabled backtrace"),
-            Inner::Captured(c) => c.force(),
+            Inner::Captured(c) => &**c,
         };
 
         let full = fmt.alternate();
@@ -430,46 +428,15 @@ impl fmt::Display for Backtrace {
     }
 }
 
-struct LazilyResolvedCapture {
-    sync: Once,
-    capture: UnsafeCell<Capture>,
-}
-
-impl LazilyResolvedCapture {
-    fn new(capture: Capture) -> Self {
-        LazilyResolvedCapture { sync: Once::new(), capture: UnsafeCell::new(capture) }
-    }
-
-    fn force(&self) -> &Capture {
-        self.sync.call_once(|| {
-            // SAFETY: This exclusive reference can't overlap with any others
-            // `Once` guarantees callers will block until this closure returns
-            // `Once` also guarantees only a single caller will enter this closure
-            unsafe { &mut *self.capture.get() }.resolve();
-        });
-
-        // SAFETY: This shared reference can't overlap with the exclusive reference above
-        unsafe { &*self.capture.get() }
-    }
-}
-
-// SAFETY: Access to the inner value is synchronized using a thread-safe `Once`
-// So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too
-unsafe impl Sync for LazilyResolvedCapture where Capture: Sync {}
-
-impl Capture {
-    fn resolve(&mut self) {
-        // If we're already resolved, nothing to do!
-        if self.resolved {
-            return;
-        }
-        self.resolved = true;
+type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe;
 
+fn lazy_resolve(mut capture: Capture) -> LazyResolve {
+    move || {
         // Use the global backtrace lock to synchronize this as it's a
         // requirement of the `backtrace` crate, and then actually resolve
         // everything.
         let _lock = lock();
-        for frame in self.frames.iter_mut() {
+        for frame in capture.frames.iter_mut() {
             let symbols = &mut frame.symbols;
             let frame = match &frame.frame {
                 RawFrame::Actual(frame) => frame,
@@ -490,6 +457,8 @@ impl Capture {
                 });
             }
         }
+
+        capture
     }
 }
 
diff --git a/library/std/src/backtrace/tests.rs b/library/std/src/backtrace/tests.rs
index 4dfbf88e83e..73543a3af54 100644
--- a/library/std/src/backtrace/tests.rs
+++ b/library/std/src/backtrace/tests.rs
@@ -1,4 +1,5 @@
 use super::*;
+use crate::panic::{RefUnwindSafe, UnwindSafe};
 
 fn generate_fake_frames() -> Vec<BacktraceFrame> {
     vec![
@@ -43,9 +44,8 @@ fn generate_fake_frames() -> Vec<BacktraceFrame> {
 #[test]
 fn test_debug() {
     let backtrace = Backtrace {
-        inner: Inner::Captured(LazilyResolvedCapture::new(Capture {
+        inner: Inner::Captured(LazyLock::preinit(Capture {
             actual_start: 1,
-            resolved: true,
             frames: generate_fake_frames(),
         })),
     };
@@ -66,9 +66,8 @@ fn test_debug() {
 #[test]
 fn test_frames() {
     let backtrace = Backtrace {
-        inner: Inner::Captured(LazilyResolvedCapture::new(Capture {
+        inner: Inner::Captured(LazyLock::preinit(Capture {
             actual_start: 1,
-            resolved: true,
             frames: generate_fake_frames(),
         })),
     };
@@ -93,3 +92,9 @@ fn test_frames() {
 
     assert!(iter.all(|(f, e)| format!("{f:#?}") == *e));
 }
+
+#[test]
+fn backtrace_unwind_safe() {
+    fn assert_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {}
+    assert_unwind_safe::<Backtrace>();
+}
diff --git a/library/std/src/error.rs b/library/std/src/error.rs
index 05f8fd8de32..ee5eddebfaf 100644
--- a/library/std/src/error.rs
+++ b/library/std/src/error.rs
@@ -121,7 +121,8 @@ mod private {
 /// This example produces the following output:
 ///
 /// ```console
-/// thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!', src/error.rs:34:40
+/// thread 'main' panicked at src/error.rs:34:40:
+/// called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!
 /// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 /// ```
 ///
diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs
index 3ddb8748753..b0484474712 100644
--- a/library/std/src/ffi/mod.rs
+++ b/library/std/src/ffi/mod.rs
@@ -156,6 +156,8 @@
 
 #[stable(feature = "alloc_c_string", since = "1.64.0")]
 pub use alloc::ffi::{CString, FromVecWithNulError, IntoStringError, NulError};
+#[stable(feature = "cstr_from_bytes_until_nul", since = "CURRENT_RUSTC_VERSION")]
+pub use core::ffi::FromBytesUntilNulError;
 #[stable(feature = "core_c_str", since = "1.64.0")]
 pub use core::ffi::{CStr, FromBytesWithNulError};
 
diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs
index f076ee0923c..3840ffe7eec 100644
--- a/library/std/src/io/util.rs
+++ b/library/std/src/io/util.rs
@@ -8,24 +8,41 @@ use crate::io::{
     self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write,
 };
 
-/// A reader which is always at EOF.
+/// `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.
+/// 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, Default)]
+#[derive(Copy, Clone, Debug, Default)]
 pub struct Empty;
 
-/// Constructs a new handle to an empty reader.
+/// Creates a value that is always at EOF for reads, and ignores all data written.
 ///
-/// All reads from the returned reader will return <code>[Ok]\(0)</code>.
+/// 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
 ///
-/// A slightly sad example of not reading anything into a buffer:
+/// ```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();
@@ -76,13 +93,6 @@ impl Seek for Empty {
     }
 }
 
-#[stable(feature = "std_debug", since = "1.16.0")]
-impl fmt::Debug for Empty {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Empty").finish_non_exhaustive()
-    }
-}
-
 impl SizeHint for Empty {
     #[inline]
     fn upper_bound(&self) -> Option<usize> {
@@ -90,6 +100,54 @@ impl SizeHint for Empty {
     }
 }
 
+#[stable(feature = "empty_write", since = "CURRENT_RUSTC_VERSION")]
+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 flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+#[stable(feature = "empty_write", since = "CURRENT_RUSTC_VERSION")]
+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 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
@@ -182,19 +240,20 @@ impl fmt::Debug for Repeat {
 
 /// A writer which will move data into the void.
 ///
-/// This struct is generally created by calling [`sink`]. Please
+/// 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, Default)]
+#[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())`
+/// 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
 ///
@@ -259,10 +318,3 @@ impl Write for &Sink {
         Ok(())
     }
 }
-
-#[stable(feature = "std_debug", since = "1.16.0")]
-impl fmt::Debug for Sink {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Sink").finish_non_exhaustive()
-    }
-}
diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs
index 1baa94e64c9..6de91e29c77 100644
--- a/library/std/src/io/util/tests.rs
+++ b/library/std/src/io/util/tests.rs
@@ -18,7 +18,7 @@ fn empty_reads() {
     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!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0);
+    assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0);
 
     let buf: &mut [MaybeUninit<_>] = &mut [];
     let mut buf: BorrowedBuf<'_> = buf.into();
@@ -40,7 +40,7 @@ fn empty_reads() {
 
     let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
     let mut buf: BorrowedBuf<'_> = buf.into();
-    e.by_ref().read_buf(buf.unfilled()).unwrap();
+    Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap();
     assert_eq!(buf.len(), 0);
     assert_eq!(buf.init_len(), 0);
 }
@@ -66,6 +66,15 @@ fn empty_seeks() {
 }
 
 #[test]
+fn empty_sinks() {
+    let mut e = empty();
+    assert_eq!(e.write(&[]).unwrap(), 0);
+    assert_eq!(e.write(&[0]).unwrap(), 1);
+    assert_eq!(e.write(&[0; 1024]).unwrap(), 1024);
+    assert_eq!(Write::by_ref(&mut e).write(&[0; 1024]).unwrap(), 1024);
+}
+
+#[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 4cd251d0ac2..238c74229cc 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -272,6 +272,7 @@
 #![feature(staged_api)]
 #![feature(thread_local)]
 #![feature(try_blocks)]
+#![feature(type_alias_impl_trait)]
 #![feature(utf8_chunks)]
 // tidy-alphabetical-end
 //
diff --git a/library/std/src/os/android/raw.rs b/library/std/src/os/android/raw.rs
index daaf3d3eac6..175f8eac971 100644
--- a/library/std/src/os/android/raw.rs
+++ b/library/std/src/os/android/raw.rs
@@ -89,7 +89,7 @@ mod arch {
     }
 }
 
-#[cfg(target_arch = "aarch64")]
+#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
 mod arch {
     use crate::os::raw::{c_int, c_long, c_uint, c_ulong};
     use crate::os::unix::raw::{gid_t, uid_t};
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 0e90d618ad4..6ff7b19f293 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -266,7 +266,7 @@ pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path
     let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
 
     let write = |err: &mut dyn crate::io::Write, backtrace: Option<BacktraceStyle>| {
-        let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}");
+        let _ = writeln!(err, "thread '{name}' panicked at {location}:\n{msg}");
 
         static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
 
@@ -300,7 +300,7 @@ pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path
     };
 
     if let Some(path) = path
-        && let Ok(mut out) = crate::fs::File::options().create(true).write(true).open(&path)
+        && let Ok(mut out) = crate::fs::File::options().create(true).append(true).open(&path)
     {
         write(&mut out, BacktraceStyle::full());
     }
diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs
index 76a1b4a2a86..9c4b926b7ec 100644
--- a/library/std/src/sync/condvar.rs
+++ b/library/std/src/sync/condvar.rs
@@ -21,11 +21,11 @@ impl WaitTimeoutResult {
     ///
     /// # Examples
     ///
-    /// This example spawns a thread which will update the boolean value and
-    /// then wait 100 milliseconds before notifying the condvar.
+    /// This example spawns a thread which will sleep 20 milliseconds before
+    /// updating a boolean value and then notifying the condvar.
     ///
-    /// The main thread will wait with a timeout on the condvar and then leave
-    /// once the boolean has been updated and notified.
+    /// The main thread will wait with a 10 millisecond timeout on the condvar
+    /// and will leave the loop upon timeout.
     ///
     /// ```
     /// use std::sync::{Arc, Condvar, Mutex};
@@ -49,14 +49,12 @@ impl WaitTimeoutResult {
     ///
     /// // Wait for the thread to start up.
     /// let (lock, cvar) = &*pair;
-    /// let mut started = lock.lock().unwrap();
     /// loop {
     ///     // Let's put a timeout on the condvar's wait.
-    ///     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.
+    ///     let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap();
+    ///     // 10 milliseconds have passed.
+    ///     if result.1.timed_out() {
+    ///         // timed out now and we can leave.
     ///         break
     ///     }
     /// }
diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs
index 37bdec5abcc..3598598cfa0 100644
--- a/library/std/src/sync/lazy_lock.rs
+++ b/library/std/src/sync/lazy_lock.rs
@@ -89,6 +89,15 @@ impl<T, F: FnOnce() -> T> 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.
@@ -213,10 +222,12 @@ impl<T: Default> Default for LazyLock<T> {
 #[unstable(feature = "lazy_cell", issue = "109736")]
 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 self.get() {
-            Some(v) => f.debug_tuple("LazyLock").field(v).finish(),
-            None => f.write_str("LazyLock(Uninit)"),
-        }
+            Some(v) => d.field(v),
+            None => d.field(&format_args!("<uninit>")),
+        };
+        d.finish()
     }
 }
 
diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs
index b8fec6902a0..b4ae6b7e07e 100644
--- a/library/std/src/sync/mutex.rs
+++ b/library/std/src/sync/mutex.rs
@@ -490,13 +490,7 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
                 d.field("data", &&**err.get_ref());
             }
             Err(TryLockError::WouldBlock) => {
-                struct LockedPlaceholder;
-                impl fmt::Debug for LockedPlaceholder {
-                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                        f.write_str("<locked>")
-                    }
-                }
-                d.field("data", &LockedPlaceholder);
+                d.field("data", &format_args!("<locked>"));
             }
         }
         d.field("poisoned", &self.poison.get());
diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs
index e83bc35ee98..e2b7b893cb5 100644
--- a/library/std/src/sync/once_lock.rs
+++ b/library/std/src/sync/once_lock.rs
@@ -365,10 +365,12 @@ impl<T> Default for OnceLock<T> {
 #[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) => f.debug_tuple("Once").field(v).finish(),
-            None => f.write_str("Once(Uninit)"),
-        }
+            Some(v) => d.field(v),
+            None => d.field(&format_args!("<uninit>")),
+        };
+        d.finish()
     }
 }
 
diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs
index 7c409cb3e97..26aaa2414c9 100644
--- a/library/std/src/sync/rwlock.rs
+++ b/library/std/src/sync/rwlock.rs
@@ -485,13 +485,7 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> {
                 d.field("data", &&**err.get_ref());
             }
             Err(TryLockError::WouldBlock) => {
-                struct LockedPlaceholder;
-                impl fmt::Debug for LockedPlaceholder {
-                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                        f.write_str("<locked>")
-                    }
-                }
-                d.field("data", &LockedPlaceholder);
+                d.field("data", &format_args!("<locked>"));
             }
         }
         d.field("poisoned", &self.poison.get());
diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
index d471be33ed5..fbf158f56fc 100644
--- a/library/std/src/sys/unix/rand.rs
+++ b/library/std/src/sys/unix/rand.rs
@@ -17,7 +17,6 @@ pub fn hashmap_random_keys() -> (u64, u64) {
     not(target_os = "tvos"),
     not(target_os = "watchos"),
     not(target_os = "openbsd"),
-    not(target_os = "freebsd"),
     not(target_os = "netbsd"),
     not(target_os = "fuchsia"),
     not(target_os = "redox"),
@@ -68,11 +67,25 @@ mod imp {
         unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
     }
 
+    #[cfg(target_os = "freebsd")]
+    fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
+        // FIXME: using the above when libary std's libc is updated
+        extern "C" {
+            fn getrandom(
+                buffer: *mut libc::c_void,
+                length: libc::size_t,
+                flags: libc::c_uint,
+            ) -> libc::ssize_t;
+        }
+        unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
+    }
+
     #[cfg(not(any(
         target_os = "linux",
         target_os = "android",
         target_os = "espidf",
-        target_os = "horizon"
+        target_os = "horizon",
+        target_os = "freebsd"
     )))]
     fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool {
         false
@@ -82,7 +95,8 @@ mod imp {
         target_os = "linux",
         target_os = "android",
         target_os = "espidf",
-        target_os = "horizon"
+        target_os = "horizon",
+        target_os = "freebsd"
     ))]
     fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
         use crate::sync::atomic::{AtomicBool, Ordering};
@@ -222,7 +236,7 @@ mod imp {
     }
 }
 
-#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
+#[cfg(target_os = "netbsd")]
 mod imp {
     use crate::ptr;
 
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
index a2223708024..98517da1d0f 100644
--- a/library/std/src/sys/wasi/mod.rs
+++ b/library/std/src/sys/wasi/mod.rs
@@ -29,8 +29,7 @@ pub mod fs;
 #[path = "../wasm/atomics/futex.rs"]
 pub mod futex;
 pub mod io;
-#[path = "../unsupported/locks/mod.rs"]
-pub mod locks;
+
 pub mod net;
 pub mod os;
 #[path = "../unix/os_str.rs"]
@@ -47,14 +46,27 @@ pub mod thread;
 pub mod thread_local_dtor;
 #[path = "../unsupported/thread_local_key.rs"]
 pub mod thread_local_key;
-#[path = "../unsupported/thread_parking.rs"]
-pub mod thread_parking;
 pub mod time;
 
 cfg_if::cfg_if! {
-    if #[cfg(not(target_feature = "atomics"))] {
+    if #[cfg(target_feature = "atomics")] {
+        #[path = "../unix/locks"]
+        pub mod locks {
+            #![allow(unsafe_op_in_unsafe_fn)]
+            mod futex_condvar;
+            mod futex_mutex;
+            mod futex_rwlock;
+            pub(crate) use futex_condvar::Condvar;
+            pub(crate) use futex_mutex::Mutex;
+            pub(crate) use futex_rwlock::RwLock;
+        }
+    } else {
+        #[path = "../unsupported/locks/mod.rs"]
+        pub mod locks;
         #[path = "../unsupported/once.rs"]
         pub mod once;
+        #[path = "../unsupported/thread_parking.rs"]
+        pub mod thread_parking;
     }
 }
 
diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs
index 9919dc7087e..197bdeda4fb 100644
--- a/library/std/src/sys/wasi/os.rs
+++ b/library/std/src/sys/wasi/os.rs
@@ -224,6 +224,11 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> {
     })
 }
 
+#[allow(dead_code)]
+pub fn page_size() -> usize {
+    unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
+}
+
 pub fn temp_dir() -> PathBuf {
     panic!("no filesystem on wasm")
 }
diff --git a/library/std/src/sys/wasi/thread.rs b/library/std/src/sys/wasi/thread.rs
index e7a6ab4be82..d27b7a2e0f5 100644
--- a/library/std/src/sys/wasi/thread.rs
+++ b/library/std/src/sys/wasi/thread.rs
@@ -1,5 +1,3 @@
-#![deny(unsafe_op_in_unsafe_fn)]
-
 use crate::ffi::CStr;
 use crate::io;
 use crate::mem;
@@ -7,14 +5,116 @@ use crate::num::NonZeroUsize;
 use crate::sys::unsupported;
 use crate::time::Duration;
 
-pub struct Thread(!);
+cfg_if::cfg_if! {
+    if #[cfg(target_feature = "atomics")] {
+        use crate::cmp;
+        use crate::ptr;
+        use crate::sys::os;
+        // Add a few symbols not in upstream `libc` just yet.
+        mod libc {
+            pub use crate::ffi;
+            pub use crate::mem;
+            pub use libc::*;
+
+            // defined in wasi-libc
+            // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108
+            #[repr(C)]
+            union pthread_attr_union {
+                __i: [ffi::c_int; if mem::size_of::<ffi::c_int>() == 8 { 14 } else { 9 }],
+                __vi: [ffi::c_int; if mem::size_of::<ffi::c_int>() == 8 { 14 } else { 9 }],
+                __s: [ffi::c_ulong; if mem::size_of::<ffi::c_int>() == 8 { 7 } else { 9 }],
+            }
+
+            #[repr(C)]
+            pub struct pthread_attr_t {
+                __u: pthread_attr_union,
+            }
+
+            #[allow(non_camel_case_types)]
+            pub type pthread_t = *mut ffi::c_void;
+
+            extern "C" {
+                pub fn pthread_create(
+                    native: *mut pthread_t,
+                    attr: *const pthread_attr_t,
+                    f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void,
+                    value: *mut ffi::c_void,
+                ) -> ffi::c_int;
+                pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int;
+                pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int;
+                pub fn pthread_attr_setstacksize(
+                    attr: *mut pthread_attr_t,
+                    stack_size: libc::size_t,
+                ) -> ffi::c_int;
+                pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int;
+            }
+        }
+
+        pub struct Thread {
+            id: libc::pthread_t,
+        }
+    } else {
+        pub struct Thread(!);
+    }
+}
 
 pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
 
 impl Thread {
     // unsafe: see thread::Builder::spawn_unchecked for safety requirements
-    pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
-        unsupported()
+    cfg_if::cfg_if! {
+        if #[cfg(target_feature = "atomics")] {
+            pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+                let p = Box::into_raw(Box::new(p));
+                let mut native: libc::pthread_t = mem::zeroed();
+                let mut attr: libc::pthread_attr_t = mem::zeroed();
+                assert_eq!(libc::pthread_attr_init(&mut attr), 0);
+
+                let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE);
+
+                match libc::pthread_attr_setstacksize(&mut attr, stack_size) {
+                    0 => {}
+                    n => {
+                        assert_eq!(n, libc::EINVAL);
+                        // EINVAL means |stack_size| is either too small or not a
+                        // multiple of the system page size. Because it's definitely
+                        // >= PTHREAD_STACK_MIN, it must be an alignment issue.
+                        // Round up to the nearest page and try again.
+                        let page_size = os::page_size();
+                        let stack_size =
+                            (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);
+                        assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0);
+                    }
+                };
+
+                let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _);
+                // Note: if the thread creation fails and this assert fails, then p will
+                // be leaked. However, an alternative design could cause double-free
+                // which is clearly worse.
+                assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+
+                return if ret != 0 {
+                    // The thread failed to start and as a result p was not consumed. Therefore, it is
+                    // safe to reconstruct the box so that it gets deallocated.
+                    drop(Box::from_raw(p));
+                    Err(io::Error::from_raw_os_error(ret))
+                } else {
+                    Ok(Thread { id: native })
+                };
+
+                extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
+                    unsafe {
+                        // Finally, let's run some code.
+                        Box::from_raw(main as *mut Box<dyn FnOnce()>)();
+                    }
+                    ptr::null_mut()
+                }
+            }
+        } else {
+            pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+                unsupported()
+            }
+        }
     }
 
     pub fn yield_now() {
@@ -62,7 +162,19 @@ impl Thread {
     }
 
     pub fn join(self) {
-        self.0
+        cfg_if::cfg_if! {
+            if #[cfg(target_feature = "atomics")] {
+                unsafe {
+                    let ret = libc::pthread_join(self.id, ptr::null_mut());
+                    mem::forget(self);
+                    if ret != 0 {
+                        rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret));
+                    }
+                }
+            } else {
+                self.0
+            }
+        }
     }
 }
 
diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs
index 6f020940df1..84e2c5d8d7f 100644
--- a/library/std/src/sys_common/backtrace.rs
+++ b/library/std/src/sys_common/backtrace.rs
@@ -60,6 +60,8 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
     bt_fmt.add_context()?;
     let mut idx = 0;
     let mut res = Ok(());
+    let mut omitted_count: usize = 0;
+    let mut first_omit = true;
     // Start immediately if we're not using a short backtrace.
     let mut start = print_fmt != PrintFmt::Short;
     backtrace_rs::trace_unsynchronized(|frame| {
@@ -85,10 +87,27 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
                         start = true;
                         return;
                     }
+                    if !start {
+                        omitted_count += 1;
+                    }
                 }
             }
 
             if start {
+                if omitted_count > 0 {
+                    debug_assert!(print_fmt == PrintFmt::Short);
+                    // only print the message between the middle of frames
+                    if !first_omit {
+                        let _ = writeln!(
+                            bt_fmt.formatter(),
+                            "      [... omitted {} frame{} ...]",
+                            omitted_count,
+                            if omitted_count > 1 { "s" } else { "" }
+                        );
+                    }
+                    first_omit = false;
+                    omitted_count = 0;
+                }
                 res = bt_fmt.frame().symbol(frame, symbol);
             }
         });