about summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
authorjoboet <jonasboettiger@icloud.com>2024-03-12 14:55:06 +0100
committerjoboet <jonasboettiger@icloud.com>2024-03-12 15:41:06 +0100
commit22a5267c83a3e17f2b763279eb24bb632c45dc6b (patch)
tree81eca4925b77ec92b4f2fd66962af948fe59d094 /library/std/src/sys
parent3b85d2c7fc6d1698e68b94f7bc1a5c9633f2554d (diff)
downloadrust-22a5267c83a3e17f2b763279eb24bb632c45dc6b.tar.gz
rust-22a5267c83a3e17f2b763279eb24bb632c45dc6b.zip
std: move `Once` implementations to `sys`
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/mod.rs2
-rw-r--r--library/std/src/sys/pal/teeos/mod.rs2
-rw-r--r--library/std/src/sys/pal/uefi/mod.rs2
-rw-r--r--library/std/src/sys/pal/unsupported/mod.rs1
-rw-r--r--library/std/src/sys/pal/wasi/mod.rs2
-rw-r--r--library/std/src/sys/pal/wasip2/mod.rs4
-rw-r--r--library/std/src/sys/pal/wasm/mod.rs2
-rw-r--r--library/std/src/sys/pal/zkvm/mod.rs2
-rw-r--r--library/std/src/sys/sync/condvar/futex.rs (renamed from library/std/src/sys/locks/condvar/futex.rs)2
-rw-r--r--library/std/src/sys/sync/condvar/itron.rs (renamed from library/std/src/sys/locks/condvar/itron.rs)2
-rw-r--r--library/std/src/sys/sync/condvar/mod.rs (renamed from library/std/src/sys/locks/condvar/mod.rs)0
-rw-r--r--library/std/src/sys/sync/condvar/no_threads.rs (renamed from library/std/src/sys/locks/condvar/no_threads.rs)2
-rw-r--r--library/std/src/sys/sync/condvar/pthread.rs (renamed from library/std/src/sys/locks/condvar/pthread.rs)2
-rw-r--r--library/std/src/sys/sync/condvar/sgx.rs (renamed from library/std/src/sys/locks/condvar/sgx.rs)2
-rw-r--r--library/std/src/sys/sync/condvar/teeos.rs (renamed from library/std/src/sys/locks/condvar/teeos.rs)2
-rw-r--r--library/std/src/sys/sync/condvar/windows7.rs (renamed from library/std/src/sys/locks/condvar/windows7.rs)2
-rw-r--r--library/std/src/sys/sync/condvar/xous.rs (renamed from library/std/src/sys/locks/condvar/xous.rs)2
-rw-r--r--library/std/src/sys/sync/mod.rs (renamed from library/std/src/sys/locks/mod.rs)2
-rw-r--r--library/std/src/sys/sync/mutex/fuchsia.rs (renamed from library/std/src/sys/locks/mutex/fuchsia.rs)0
-rw-r--r--library/std/src/sys/sync/mutex/futex.rs (renamed from library/std/src/sys/locks/mutex/futex.rs)0
-rw-r--r--library/std/src/sys/sync/mutex/itron.rs (renamed from library/std/src/sys/locks/mutex/itron.rs)0
-rw-r--r--library/std/src/sys/sync/mutex/mod.rs (renamed from library/std/src/sys/locks/mutex/mod.rs)0
-rw-r--r--library/std/src/sys/sync/mutex/no_threads.rs (renamed from library/std/src/sys/locks/mutex/no_threads.rs)0
-rw-r--r--library/std/src/sys/sync/mutex/pthread.rs (renamed from library/std/src/sys/locks/mutex/pthread.rs)0
-rw-r--r--library/std/src/sys/sync/mutex/sgx.rs (renamed from library/std/src/sys/locks/mutex/sgx.rs)0
-rw-r--r--library/std/src/sys/sync/mutex/windows7.rs (renamed from library/std/src/sys/locks/mutex/windows7.rs)0
-rw-r--r--library/std/src/sys/sync/mutex/xous.rs (renamed from library/std/src/sys/locks/mutex/xous.rs)0
-rw-r--r--library/std/src/sys/sync/once/futex.rs146
-rw-r--r--library/std/src/sys/sync/once/mod.rs36
-rw-r--r--library/std/src/sys/sync/once/no_threads.rs (renamed from library/std/src/sys/pal/unsupported/once.rs)0
-rw-r--r--library/std/src/sys/sync/once/queue.rs294
-rw-r--r--library/std/src/sys/sync/rwlock/futex.rs (renamed from library/std/src/sys/locks/rwlock/futex.rs)0
-rw-r--r--library/std/src/sys/sync/rwlock/mod.rs (renamed from library/std/src/sys/locks/rwlock/mod.rs)0
-rw-r--r--library/std/src/sys/sync/rwlock/no_threads.rs (renamed from library/std/src/sys/locks/rwlock/no_threads.rs)0
-rw-r--r--library/std/src/sys/sync/rwlock/queue.rs (renamed from library/std/src/sys/locks/rwlock/queue.rs)0
-rw-r--r--library/std/src/sys/sync/rwlock/sgx.rs (renamed from library/std/src/sys/locks/rwlock/sgx.rs)0
-rw-r--r--library/std/src/sys/sync/rwlock/sgx/tests.rs (renamed from library/std/src/sys/locks/rwlock/sgx/tests.rs)0
-rw-r--r--library/std/src/sys/sync/rwlock/solid.rs (renamed from library/std/src/sys/locks/rwlock/solid.rs)0
-rw-r--r--library/std/src/sys/sync/rwlock/teeos.rs (renamed from library/std/src/sys/locks/rwlock/teeos.rs)2
-rw-r--r--library/std/src/sys/sync/rwlock/windows7.rs (renamed from library/std/src/sys/locks/rwlock/windows7.rs)0
-rw-r--r--library/std/src/sys/sync/rwlock/xous.rs (renamed from library/std/src/sys/locks/rwlock/xous.rs)0
41 files changed, 488 insertions, 25 deletions
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index 81200e0061e..bbd1d840e92 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -6,9 +6,9 @@ mod pal;
 mod personality;
 
 pub mod cmath;
-pub mod locks;
 pub mod os_str;
 pub mod path;
+pub mod sync;
 #[allow(dead_code)]
 #[allow(unused_imports)]
 pub mod thread_local;
diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs
index 1fb9d5438de..c392a0ea264 100644
--- a/library/std/src/sys/pal/teeos/mod.rs
+++ b/library/std/src/sys/pal/teeos/mod.rs
@@ -19,8 +19,6 @@ pub mod fs;
 #[path = "../unsupported/io.rs"]
 pub mod io;
 pub mod net;
-#[path = "../unsupported/once.rs"]
-pub mod once;
 pub mod os;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs
index 7c5b37fb490..562b00c2c01 100644
--- a/library/std/src/sys/pal/uefi/mod.rs
+++ b/library/std/src/sys/pal/uefi/mod.rs
@@ -21,8 +21,6 @@ pub mod fs;
 pub mod io;
 #[path = "../unsupported/net.rs"]
 pub mod net;
-#[path = "../unsupported/once.rs"]
-pub mod once;
 pub mod os;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs
index 9ce275ee72d..be344fb7cae 100644
--- a/library/std/src/sys/pal/unsupported/mod.rs
+++ b/library/std/src/sys/pal/unsupported/mod.rs
@@ -6,7 +6,6 @@ pub mod env;
 pub mod fs;
 pub mod io;
 pub mod net;
-pub mod once;
 pub mod os;
 pub mod pipe;
 pub mod process;
diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs
index 308dd296004..a78547261ad 100644
--- a/library/std/src/sys/pal/wasi/mod.rs
+++ b/library/std/src/sys/pal/wasi/mod.rs
@@ -41,8 +41,6 @@ pub mod time;
 
 cfg_if::cfg_if! {
     if #[cfg(not(target_feature = "atomics"))] {
-        #[path = "../unsupported/once.rs"]
-        pub mod once;
         #[path = "../unsupported/thread_parking.rs"]
         pub mod thread_parking;
     }
diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs
index b12a8d5ea11..d1d444d7b79 100644
--- a/library/std/src/sys/pal/wasip2/mod.rs
+++ b/library/std/src/sys/pal/wasip2/mod.rs
@@ -51,10 +51,6 @@ cfg_if::cfg_if! {
     if #[cfg(target_feature = "atomics")] {
         compile_error!("The wasm32-wasip2 target does not support atomics");
     } 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/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs
index 40b15120e6d..5cbc3e45341 100644
--- a/library/std/src/sys/pal/wasm/mod.rs
+++ b/library/std/src/sys/pal/wasm/mod.rs
@@ -48,8 +48,6 @@ cfg_if::cfg_if! {
         #[path = "atomics/thread.rs"]
         pub mod thread;
     } else {
-        #[path = "../unsupported/once.rs"]
-        pub mod once;
         #[path = "../unsupported/thread.rs"]
         pub mod thread;
         #[path = "../unsupported/thread_parking.rs"]
diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs
index 6c714f76309..228a976dbab 100644
--- a/library/std/src/sys/pal/zkvm/mod.rs
+++ b/library/std/src/sys/pal/zkvm/mod.rs
@@ -21,8 +21,6 @@ pub mod fs;
 pub mod io;
 #[path = "../unsupported/net.rs"]
 pub mod net;
-#[path = "../unsupported/once.rs"]
-pub mod once;
 pub mod os;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
diff --git a/library/std/src/sys/locks/condvar/futex.rs b/library/std/src/sys/sync/condvar/futex.rs
index 3ad93ce07f7..4586d0fd941 100644
--- a/library/std/src/sys/locks/condvar/futex.rs
+++ b/library/std/src/sys/sync/condvar/futex.rs
@@ -1,6 +1,6 @@
 use crate::sync::atomic::{AtomicU32, Ordering::Relaxed};
 use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
-use crate::sys::locks::Mutex;
+use crate::sys::sync::Mutex;
 use crate::time::Duration;
 
 pub struct Condvar {
diff --git a/library/std/src/sys/locks/condvar/itron.rs b/library/std/src/sys/sync/condvar/itron.rs
index 4c6f5e9dad2..9b64d241efd 100644
--- a/library/std/src/sys/locks/condvar/itron.rs
+++ b/library/std/src/sys/sync/condvar/itron.rs
@@ -2,7 +2,7 @@
 use crate::sys::pal::itron::{
     abi, error::expect_success_aborting, spin::SpinMutex, task, time::with_tmos_strong,
 };
-use crate::{mem::replace, ptr::NonNull, sys::locks::Mutex, time::Duration};
+use crate::{mem::replace, ptr::NonNull, sys::sync::Mutex, time::Duration};
 
 // The implementation is inspired by the queue-based implementation shown in
 // Andrew D. Birrell's paper "Implementing Condition Variables with Semaphores"
diff --git a/library/std/src/sys/locks/condvar/mod.rs b/library/std/src/sys/sync/condvar/mod.rs
index 6849cacf88e..6849cacf88e 100644
--- a/library/std/src/sys/locks/condvar/mod.rs
+++ b/library/std/src/sys/sync/condvar/mod.rs
diff --git a/library/std/src/sys/locks/condvar/no_threads.rs b/library/std/src/sys/sync/condvar/no_threads.rs
index 3f0943b50ee..36b89c5f5be 100644
--- a/library/std/src/sys/locks/condvar/no_threads.rs
+++ b/library/std/src/sys/sync/condvar/no_threads.rs
@@ -1,4 +1,4 @@
-use crate::sys::locks::Mutex;
+use crate::sys::sync::Mutex;
 use crate::time::Duration;
 
 pub struct Condvar {}
diff --git a/library/std/src/sys/locks/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs
index 094738d5a3f..728371685ee 100644
--- a/library/std/src/sys/locks/condvar/pthread.rs
+++ b/library/std/src/sys/sync/condvar/pthread.rs
@@ -1,7 +1,7 @@
 use crate::cell::UnsafeCell;
 use crate::ptr;
 use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed};
-use crate::sys::locks::{mutex, Mutex};
+use crate::sys::sync::{mutex, Mutex};
 #[cfg(not(target_os = "nto"))]
 use crate::sys::time::TIMESPEC_MAX;
 #[cfg(target_os = "nto")]
diff --git a/library/std/src/sys/locks/condvar/sgx.rs b/library/std/src/sys/sync/condvar/sgx.rs
index cabd3250275..ecb5872f60d 100644
--- a/library/std/src/sys/locks/condvar/sgx.rs
+++ b/library/std/src/sys/sync/condvar/sgx.rs
@@ -1,5 +1,5 @@
-use crate::sys::locks::Mutex;
 use crate::sys::pal::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
+use crate::sys::sync::Mutex;
 use crate::sys_common::lazy_box::{LazyBox, LazyInit};
 use crate::time::Duration;
 
diff --git a/library/std/src/sys/locks/condvar/teeos.rs b/library/std/src/sys/sync/condvar/teeos.rs
index c08e8145b8c..0a931f407d2 100644
--- a/library/std/src/sys/locks/condvar/teeos.rs
+++ b/library/std/src/sys/sync/condvar/teeos.rs
@@ -1,7 +1,7 @@
 use crate::cell::UnsafeCell;
 use crate::ptr;
 use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed};
-use crate::sys::locks::mutex::{self, Mutex};
+use crate::sys::sync::mutex::{self, Mutex};
 use crate::sys::time::TIMESPEC_MAX;
 use crate::sys_common::lazy_box::{LazyBox, LazyInit};
 use crate::time::Duration;
diff --git a/library/std/src/sys/locks/condvar/windows7.rs b/library/std/src/sys/sync/condvar/windows7.rs
index 28a288335d2..07fa5fdd698 100644
--- a/library/std/src/sys/locks/condvar/windows7.rs
+++ b/library/std/src/sys/sync/condvar/windows7.rs
@@ -1,7 +1,7 @@
 use crate::cell::UnsafeCell;
 use crate::sys::c;
-use crate::sys::locks::{mutex, Mutex};
 use crate::sys::os;
+use crate::sys::sync::{mutex, Mutex};
 use crate::time::Duration;
 
 pub struct Condvar {
diff --git a/library/std/src/sys/locks/condvar/xous.rs b/library/std/src/sys/sync/condvar/xous.rs
index 0e51449e0af..7b218818ef8 100644
--- a/library/std/src/sys/locks/condvar/xous.rs
+++ b/library/std/src/sys/sync/condvar/xous.rs
@@ -1,6 +1,6 @@
 use crate::os::xous::ffi::{blocking_scalar, scalar};
 use crate::os::xous::services::{ticktimer_server, TicktimerScalar};
-use crate::sys::locks::Mutex;
+use crate::sys::sync::Mutex;
 use crate::time::Duration;
 use core::sync::atomic::{AtomicUsize, Ordering};
 
diff --git a/library/std/src/sys/locks/mod.rs b/library/std/src/sys/sync/mod.rs
index 0bdc4a1e1db..623e6bccd51 100644
--- a/library/std/src/sys/locks/mod.rs
+++ b/library/std/src/sys/sync/mod.rs
@@ -1,7 +1,9 @@
 mod condvar;
 mod mutex;
+mod once;
 mod rwlock;
 
 pub use condvar::Condvar;
 pub use mutex::Mutex;
+pub use once::{Once, OnceState};
 pub use rwlock::RwLock;
diff --git a/library/std/src/sys/locks/mutex/fuchsia.rs b/library/std/src/sys/sync/mutex/fuchsia.rs
index 5d89e5a13fd..5d89e5a13fd 100644
--- a/library/std/src/sys/locks/mutex/fuchsia.rs
+++ b/library/std/src/sys/sync/mutex/fuchsia.rs
diff --git a/library/std/src/sys/locks/mutex/futex.rs b/library/std/src/sys/sync/mutex/futex.rs
index 7427cae94d6..7427cae94d6 100644
--- a/library/std/src/sys/locks/mutex/futex.rs
+++ b/library/std/src/sys/sync/mutex/futex.rs
diff --git a/library/std/src/sys/locks/mutex/itron.rs b/library/std/src/sys/sync/mutex/itron.rs
index a134eb2d1be..a134eb2d1be 100644
--- a/library/std/src/sys/locks/mutex/itron.rs
+++ b/library/std/src/sys/sync/mutex/itron.rs
diff --git a/library/std/src/sys/locks/mutex/mod.rs b/library/std/src/sys/sync/mutex/mod.rs
index 73d9bd273de..73d9bd273de 100644
--- a/library/std/src/sys/locks/mutex/mod.rs
+++ b/library/std/src/sys/sync/mutex/mod.rs
diff --git a/library/std/src/sys/locks/mutex/no_threads.rs b/library/std/src/sys/sync/mutex/no_threads.rs
index 4a13c55fb8b..4a13c55fb8b 100644
--- a/library/std/src/sys/locks/mutex/no_threads.rs
+++ b/library/std/src/sys/sync/mutex/no_threads.rs
diff --git a/library/std/src/sys/locks/mutex/pthread.rs b/library/std/src/sys/sync/mutex/pthread.rs
index ee0794334fb..ee0794334fb 100644
--- a/library/std/src/sys/locks/mutex/pthread.rs
+++ b/library/std/src/sys/sync/mutex/pthread.rs
diff --git a/library/std/src/sys/locks/mutex/sgx.rs b/library/std/src/sys/sync/mutex/sgx.rs
index d37bd02adf8..d37bd02adf8 100644
--- a/library/std/src/sys/locks/mutex/sgx.rs
+++ b/library/std/src/sys/sync/mutex/sgx.rs
diff --git a/library/std/src/sys/locks/mutex/windows7.rs b/library/std/src/sys/sync/mutex/windows7.rs
index ef2f84082cd..ef2f84082cd 100644
--- a/library/std/src/sys/locks/mutex/windows7.rs
+++ b/library/std/src/sys/sync/mutex/windows7.rs
diff --git a/library/std/src/sys/locks/mutex/xous.rs b/library/std/src/sys/sync/mutex/xous.rs
index a8c9518ff0b..a8c9518ff0b 100644
--- a/library/std/src/sys/locks/mutex/xous.rs
+++ b/library/std/src/sys/sync/mutex/xous.rs
diff --git a/library/std/src/sys/sync/once/futex.rs b/library/std/src/sys/sync/once/futex.rs
new file mode 100644
index 00000000000..609085dcd47
--- /dev/null
+++ b/library/std/src/sys/sync/once/futex.rs
@@ -0,0 +1,146 @@
+use crate::cell::Cell;
+use crate::sync as public;
+use crate::sync::atomic::{
+    AtomicU32,
+    Ordering::{Acquire, Relaxed, Release},
+};
+use crate::sync::once::ExclusiveState;
+use crate::sys::futex::{futex_wait, futex_wake_all};
+
+// On some platforms, the OS is very nice and handles the waiter queue for us.
+// This means we only need one atomic value with 5 states:
+
+/// No initialization has run yet, and no thread is currently using the Once.
+const INCOMPLETE: u32 = 0;
+/// Some thread has previously attempted to initialize the Once, but it panicked,
+/// so the Once is now poisoned. There are no other threads currently accessing
+/// this Once.
+const POISONED: u32 = 1;
+/// Some thread is currently attempting to run initialization. It may succeed,
+/// so all future threads need to wait for it to finish.
+const RUNNING: u32 = 2;
+/// Some thread is currently attempting to run initialization and there are threads
+/// waiting for it to finish.
+const QUEUED: u32 = 3;
+/// Initialization has completed and all future calls should finish immediately.
+const COMPLETE: u32 = 4;
+
+// Threads wait by setting the state to QUEUED and calling `futex_wait` on the state
+// variable. When the running thread finishes, it will wake all waiting threads using
+// `futex_wake_all`.
+
+pub struct OnceState {
+    poisoned: bool,
+    set_state_to: Cell<u32>,
+}
+
+impl OnceState {
+    #[inline]
+    pub fn is_poisoned(&self) -> bool {
+        self.poisoned
+    }
+
+    #[inline]
+    pub fn poison(&self) {
+        self.set_state_to.set(POISONED);
+    }
+}
+
+struct CompletionGuard<'a> {
+    state: &'a AtomicU32,
+    set_state_on_drop_to: u32,
+}
+
+impl<'a> Drop for CompletionGuard<'a> {
+    fn drop(&mut self) {
+        // Use release ordering to propagate changes to all threads checking
+        // up on the Once. `futex_wake_all` does its own synchronization, hence
+        // we do not need `AcqRel`.
+        if self.state.swap(self.set_state_on_drop_to, Release) == QUEUED {
+            futex_wake_all(&self.state);
+        }
+    }
+}
+
+pub struct Once {
+    state: AtomicU32,
+}
+
+impl Once {
+    #[inline]
+    pub const fn new() -> Once {
+        Once { state: AtomicU32::new(INCOMPLETE) }
+    }
+
+    #[inline]
+    pub fn is_completed(&self) -> bool {
+        // Use acquire ordering to make all initialization changes visible to the
+        // current thread.
+        self.state.load(Acquire) == COMPLETE
+    }
+
+    #[inline]
+    pub(crate) fn state(&mut self) -> ExclusiveState {
+        match *self.state.get_mut() {
+            INCOMPLETE => ExclusiveState::Incomplete,
+            POISONED => ExclusiveState::Poisoned,
+            COMPLETE => ExclusiveState::Complete,
+            _ => unreachable!("invalid Once state"),
+        }
+    }
+
+    // This uses FnMut to match the API of the generic implementation. As this
+    // implementation is quite light-weight, it is generic over the closure and
+    // so avoids the cost of dynamic dispatch.
+    #[cold]
+    #[track_caller]
+    pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {
+        let mut state = self.state.load(Acquire);
+        loop {
+            match state {
+                POISONED if !ignore_poisoning => {
+                    // Panic to propagate the poison.
+                    panic!("Once instance has previously been poisoned");
+                }
+                INCOMPLETE | POISONED => {
+                    // Try to register the current thread as the one running.
+                    if let Err(new) =
+                        self.state.compare_exchange_weak(state, RUNNING, Acquire, Acquire)
+                    {
+                        state = new;
+                        continue;
+                    }
+                    // `waiter_queue` will manage other waiting threads, and
+                    // wake them up on drop.
+                    let mut waiter_queue =
+                        CompletionGuard { state: &self.state, set_state_on_drop_to: POISONED };
+                    // Run the function, letting it know if we're poisoned or not.
+                    let f_state = public::OnceState {
+                        inner: OnceState {
+                            poisoned: state == POISONED,
+                            set_state_to: Cell::new(COMPLETE),
+                        },
+                    };
+                    f(&f_state);
+                    waiter_queue.set_state_on_drop_to = f_state.inner.set_state_to.get();
+                    return;
+                }
+                RUNNING | QUEUED => {
+                    // Set the state to QUEUED if it is not already.
+                    if state == RUNNING
+                        && let Err(new) =
+                            self.state.compare_exchange_weak(RUNNING, QUEUED, Relaxed, Acquire)
+                    {
+                        state = new;
+                        continue;
+                    }
+
+                    futex_wait(&self.state, QUEUED, None);
+                    state = self.state.load(Acquire);
+                }
+                COMPLETE => return,
+                _ => unreachable!("state is never set to invalid values"),
+            }
+        }
+    }
+}
diff --git a/library/std/src/sys/sync/once/mod.rs b/library/std/src/sys/sync/once/mod.rs
new file mode 100644
index 00000000000..61b29713fa1
--- /dev/null
+++ b/library/std/src/sys/sync/once/mod.rs
@@ -0,0 +1,36 @@
+// A "once" is a relatively simple primitive, and it's also typically provided
+// by the OS as well (see `pthread_once` or `InitOnceExecuteOnce`). The OS
+// primitives, however, tend to have surprising restrictions, such as the Unix
+// one doesn't allow an argument to be passed to the function.
+//
+// As a result, we end up implementing it ourselves in the standard library.
+// This also gives us the opportunity to optimize the implementation a bit which
+// should help the fast path on call sites.
+
+cfg_if::cfg_if! {
+    if #[cfg(any(
+        target_os = "linux",
+        target_os = "android",
+        all(target_arch = "wasm32", target_feature = "atomics"),
+        target_os = "freebsd",
+        target_os = "openbsd",
+        target_os = "dragonfly",
+        target_os = "fuchsia",
+        target_os = "hermit",
+    ))] {
+        mod futex;
+        pub use futex::{Once, OnceState};
+    } else if #[cfg(any(
+        windows,
+        target_family = "unix",
+        all(target_vendor = "fortanix", target_env = "sgx"),
+        target_os = "solid_asp3",
+        target_os = "xous",
+    ))] {
+        mod queue;
+        pub use queue::{Once, OnceState};
+    } else {
+        mod no_threads;
+        pub use no_threads::{Once, OnceState};
+    }
+}
diff --git a/library/std/src/sys/pal/unsupported/once.rs b/library/std/src/sys/sync/once/no_threads.rs
index 11fde1888ba..11fde1888ba 100644
--- a/library/std/src/sys/pal/unsupported/once.rs
+++ b/library/std/src/sys/sync/once/no_threads.rs
diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs
new file mode 100644
index 00000000000..730cdb768bd
--- /dev/null
+++ b/library/std/src/sys/sync/once/queue.rs
@@ -0,0 +1,294 @@
+// Each `Once` has one word of atomic state, and this state is CAS'd on to
+// determine what to do. There are four possible state of a `Once`:
+//
+// * Incomplete - no initialization has run yet, and no thread is currently
+//                using the Once.
+// * Poisoned - some thread has previously attempted to initialize the Once, but
+//              it panicked, so the Once is now poisoned. There are no other
+//              threads currently accessing this Once.
+// * Running - some thread is currently attempting to run initialization. It may
+//             succeed, so all future threads need to wait for it to finish.
+//             Note that this state is accompanied with a payload, described
+//             below.
+// * Complete - initialization has completed and all future calls should finish
+//              immediately.
+//
+// With 4 states we need 2 bits to encode this, and we use the remaining bits
+// in the word we have allocated as a queue of threads waiting for the thread
+// responsible for entering the RUNNING state. This queue is just a linked list
+// of Waiter nodes which is monotonically increasing in size. Each node is
+// allocated on the stack, and whenever the running closure finishes it will
+// consume the entire queue and notify all waiters they should try again.
+//
+// You'll find a few more details in the implementation, but that's the gist of
+// it!
+//
+// Atomic orderings:
+// When running `Once` we deal with multiple atomics:
+// `Once.state_and_queue` and an unknown number of `Waiter.signaled`.
+// * `state_and_queue` is used (1) as a state flag, (2) for synchronizing the
+//   result of the `Once`, and (3) for synchronizing `Waiter` nodes.
+//     - At the end of the `call` function we have to make sure the result
+//       of the `Once` is acquired. So every load which can be the only one to
+//       load COMPLETED must have at least acquire ordering, which means all
+//       three of them.
+//     - `WaiterQueue::drop` is the only place that may store COMPLETED, and
+//       must do so with release ordering to make the result available.
+//     - `wait` inserts `Waiter` nodes as a pointer in `state_and_queue`, and
+//       needs to make the nodes available with release ordering. The load in
+//       its `compare_exchange` can be relaxed because it only has to compare
+//       the atomic, not to read other data.
+//     - `WaiterQueue::drop` must see the `Waiter` nodes, so it must load
+//       `state_and_queue` with acquire ordering.
+//     - There is just one store where `state_and_queue` is used only as a
+//       state flag, without having to synchronize data: switching the state
+//       from INCOMPLETE to RUNNING in `call`. This store can be Relaxed,
+//       but the read has to be Acquire because of the requirements mentioned
+//       above.
+// * `Waiter.signaled` is both used as a flag, and to protect a field with
+//   interior mutability in `Waiter`. `Waiter.thread` is changed in
+//   `WaiterQueue::drop` which then sets `signaled` with release ordering.
+//   After `wait` loads `signaled` with acquire ordering and sees it is true,
+//   it needs to see the changes to drop the `Waiter` struct correctly.
+// * There is one place where the two atomics `Once.state_and_queue` and
+//   `Waiter.signaled` come together, and might be reordered by the compiler or
+//   processor. Because both use acquire ordering such a reordering is not
+//   allowed, so no need for `SeqCst`.
+
+use crate::cell::Cell;
+use crate::fmt;
+use crate::ptr;
+use crate::sync as public;
+use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
+use crate::sync::once::ExclusiveState;
+use crate::thread::{self, Thread};
+
+type Masked = ();
+
+pub struct Once {
+    state_and_queue: AtomicPtr<Masked>,
+}
+
+pub struct OnceState {
+    poisoned: bool,
+    set_state_on_drop_to: Cell<*mut Masked>,
+}
+
+// Four states that a Once can be in, encoded into the lower bits of
+// `state_and_queue` in the Once structure.
+const INCOMPLETE: usize = 0x0;
+const POISONED: usize = 0x1;
+const RUNNING: usize = 0x2;
+const COMPLETE: usize = 0x3;
+
+// Mask to learn about the state. All other bits are the queue of waiters if
+// this is in the RUNNING state.
+const STATE_MASK: usize = 0x3;
+
+// Representation of a node in the linked list of waiters, used while in the
+// RUNNING state.
+// Note: `Waiter` can't hold a mutable pointer to the next thread, because then
+// `wait` would both hand out a mutable reference to its `Waiter` node, and keep
+// a shared reference to check `signaled`. Instead we hold shared references and
+// use interior mutability.
+#[repr(align(4))] // Ensure the two lower bits are free to use as state bits.
+struct Waiter {
+    thread: Cell<Option<Thread>>,
+    signaled: AtomicBool,
+    next: *const Waiter,
+}
+
+// Head of a linked list of waiters.
+// Every node is a struct on the stack of a waiting thread.
+// Will wake up the waiters when it gets dropped, i.e. also on panic.
+struct WaiterQueue<'a> {
+    state_and_queue: &'a AtomicPtr<Masked>,
+    set_state_on_drop_to: *mut Masked,
+}
+
+impl Once {
+    #[inline]
+    #[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
+    pub const fn new() -> Once {
+        Once { state_and_queue: AtomicPtr::new(ptr::without_provenance_mut(INCOMPLETE)) }
+    }
+
+    #[inline]
+    pub fn is_completed(&self) -> bool {
+        // An `Acquire` load is enough because that makes all the initialization
+        // operations visible to us, and, this being a fast path, weaker
+        // ordering helps with performance. This `Acquire` synchronizes with
+        // `Release` operations on the slow path.
+        self.state_and_queue.load(Ordering::Acquire).addr() == COMPLETE
+    }
+
+    #[inline]
+    pub(crate) fn state(&mut self) -> ExclusiveState {
+        match self.state_and_queue.get_mut().addr() {
+            INCOMPLETE => ExclusiveState::Incomplete,
+            POISONED => ExclusiveState::Poisoned,
+            COMPLETE => ExclusiveState::Complete,
+            _ => unreachable!("invalid Once state"),
+        }
+    }
+
+    // This is a non-generic function to reduce the monomorphization cost of
+    // using `call_once` (this isn't exactly a trivial or small implementation).
+    //
+    // Additionally, this is tagged with `#[cold]` as it should indeed be cold
+    // and it helps let LLVM know that calls to this function should be off the
+    // fast path. Essentially, this should help generate more straight line code
+    // in LLVM.
+    //
+    // Finally, this takes an `FnMut` instead of a `FnOnce` because there's
+    // currently no way to take an `FnOnce` and call it via virtual dispatch
+    // without some allocation overhead.
+    #[cold]
+    #[track_caller]
+    pub fn call(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&public::OnceState)) {
+        let mut state_and_queue = self.state_and_queue.load(Ordering::Acquire);
+        loop {
+            match state_and_queue.addr() {
+                COMPLETE => break,
+                POISONED if !ignore_poisoning => {
+                    // Panic to propagate the poison.
+                    panic!("Once instance has previously been poisoned");
+                }
+                POISONED | INCOMPLETE => {
+                    // Try to register this thread as the one RUNNING.
+                    let exchange_result = self.state_and_queue.compare_exchange(
+                        state_and_queue,
+                        ptr::without_provenance_mut(RUNNING),
+                        Ordering::Acquire,
+                        Ordering::Acquire,
+                    );
+                    if let Err(old) = exchange_result {
+                        state_and_queue = old;
+                        continue;
+                    }
+                    // `waiter_queue` will manage other waiting threads, and
+                    // wake them up on drop.
+                    let mut waiter_queue = WaiterQueue {
+                        state_and_queue: &self.state_and_queue,
+                        set_state_on_drop_to: ptr::without_provenance_mut(POISONED),
+                    };
+                    // Run the initialization function, letting it know if we're
+                    // poisoned or not.
+                    let init_state = public::OnceState {
+                        inner: OnceState {
+                            poisoned: state_and_queue.addr() == POISONED,
+                            set_state_on_drop_to: Cell::new(ptr::without_provenance_mut(COMPLETE)),
+                        },
+                    };
+                    init(&init_state);
+                    waiter_queue.set_state_on_drop_to = init_state.inner.set_state_on_drop_to.get();
+                    break;
+                }
+                _ => {
+                    // All other values must be RUNNING with possibly a
+                    // pointer to the waiter queue in the more significant bits.
+                    assert!(state_and_queue.addr() & STATE_MASK == RUNNING);
+                    wait(&self.state_and_queue, state_and_queue);
+                    state_and_queue = self.state_and_queue.load(Ordering::Acquire);
+                }
+            }
+        }
+    }
+}
+
+fn wait(state_and_queue: &AtomicPtr<Masked>, mut current_state: *mut Masked) {
+    // Note: the following code was carefully written to avoid creating a
+    // mutable reference to `node` that gets aliased.
+    loop {
+        // Don't queue this thread if the status is no longer running,
+        // otherwise we will not be woken up.
+        if current_state.addr() & STATE_MASK != RUNNING {
+            return;
+        }
+
+        // Create the node for our current thread.
+        let node = Waiter {
+            thread: Cell::new(Some(thread::current())),
+            signaled: AtomicBool::new(false),
+            next: current_state.with_addr(current_state.addr() & !STATE_MASK) as *const Waiter,
+        };
+        let me = core::ptr::addr_of!(node) as *const Masked as *mut Masked;
+
+        // Try to slide in the node at the head of the linked list, making sure
+        // that another thread didn't just replace the head of the linked list.
+        let exchange_result = state_and_queue.compare_exchange(
+            current_state,
+            me.with_addr(me.addr() | RUNNING),
+            Ordering::Release,
+            Ordering::Relaxed,
+        );
+        if let Err(old) = exchange_result {
+            current_state = old;
+            continue;
+        }
+
+        // We have enqueued ourselves, now lets wait.
+        // It is important not to return before being signaled, otherwise we
+        // would drop our `Waiter` node and leave a hole in the linked list
+        // (and a dangling reference). Guard against spurious wakeups by
+        // reparking ourselves until we are signaled.
+        while !node.signaled.load(Ordering::Acquire) {
+            // If the managing thread happens to signal and unpark us before we
+            // can park ourselves, the result could be this thread never gets
+            // unparked. Luckily `park` comes with the guarantee that if it got
+            // an `unpark` just before on an unparked thread it does not park.
+            thread::park();
+        }
+        break;
+    }
+}
+
+#[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 Drop for WaiterQueue<'_> {
+    fn drop(&mut self) {
+        // Swap out our state with however we finished.
+        let state_and_queue =
+            self.state_and_queue.swap(self.set_state_on_drop_to, Ordering::AcqRel);
+
+        // We should only ever see an old state which was RUNNING.
+        assert_eq!(state_and_queue.addr() & STATE_MASK, RUNNING);
+
+        // Walk the entire linked list of waiters and wake them up (in lifo
+        // order, last to register is first to wake up).
+        unsafe {
+            // Right after setting `node.signaled = true` the other thread may
+            // free `node` if there happens to be has a spurious wakeup.
+            // So we have to take out the `thread` field and copy the pointer to
+            // `next` first.
+            let mut queue =
+                state_and_queue.with_addr(state_and_queue.addr() & !STATE_MASK) as *const Waiter;
+            while !queue.is_null() {
+                let next = (*queue).next;
+                let thread = (*queue).thread.take().unwrap();
+                (*queue).signaled.store(true, Ordering::Release);
+                // ^- FIXME (maybe): This is another case of issue #55005
+                // `store()` has a potentially dangling ref to `signaled`.
+                queue = next;
+                thread.unpark();
+            }
+        }
+    }
+}
+
+impl OnceState {
+    #[inline]
+    pub fn is_poisoned(&self) -> bool {
+        self.poisoned
+    }
+
+    #[inline]
+    pub fn poison(&self) {
+        self.set_state_on_drop_to.set(ptr::without_provenance_mut(POISONED));
+    }
+}
diff --git a/library/std/src/sys/locks/rwlock/futex.rs b/library/std/src/sys/sync/rwlock/futex.rs
index aa0de900238..aa0de900238 100644
--- a/library/std/src/sys/locks/rwlock/futex.rs
+++ b/library/std/src/sys/sync/rwlock/futex.rs
diff --git a/library/std/src/sys/locks/rwlock/mod.rs b/library/std/src/sys/sync/rwlock/mod.rs
index 675931c64bd..675931c64bd 100644
--- a/library/std/src/sys/locks/rwlock/mod.rs
+++ b/library/std/src/sys/sync/rwlock/mod.rs
diff --git a/library/std/src/sys/locks/rwlock/no_threads.rs b/library/std/src/sys/sync/rwlock/no_threads.rs
index 789ef9b29e5..789ef9b29e5 100644
--- a/library/std/src/sys/locks/rwlock/no_threads.rs
+++ b/library/std/src/sys/sync/rwlock/no_threads.rs
diff --git a/library/std/src/sys/locks/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs
index dce966086b8..dce966086b8 100644
--- a/library/std/src/sys/locks/rwlock/queue.rs
+++ b/library/std/src/sys/sync/rwlock/queue.rs
diff --git a/library/std/src/sys/locks/rwlock/sgx.rs b/library/std/src/sys/sync/rwlock/sgx.rs
index 136dea597bb..136dea597bb 100644
--- a/library/std/src/sys/locks/rwlock/sgx.rs
+++ b/library/std/src/sys/sync/rwlock/sgx.rs
diff --git a/library/std/src/sys/locks/rwlock/sgx/tests.rs b/library/std/src/sys/sync/rwlock/sgx/tests.rs
index 5fd6670afd4..5fd6670afd4 100644
--- a/library/std/src/sys/locks/rwlock/sgx/tests.rs
+++ b/library/std/src/sys/sync/rwlock/sgx/tests.rs
diff --git a/library/std/src/sys/locks/rwlock/solid.rs b/library/std/src/sys/sync/rwlock/solid.rs
index 9bf6f5dbb73..9bf6f5dbb73 100644
--- a/library/std/src/sys/locks/rwlock/solid.rs
+++ b/library/std/src/sys/sync/rwlock/solid.rs
diff --git a/library/std/src/sys/locks/rwlock/teeos.rs b/library/std/src/sys/sync/rwlock/teeos.rs
index 27cdb88788f..ef9b1ab5154 100644
--- a/library/std/src/sys/locks/rwlock/teeos.rs
+++ b/library/std/src/sys/sync/rwlock/teeos.rs
@@ -1,4 +1,4 @@
-use crate::sys::locks::mutex::Mutex;
+use crate::sys::sync::mutex::Mutex;
 
 /// we do not supported rwlock, so use mutex to simulate rwlock.
 /// it's useful because so many code in std will use rwlock.
diff --git a/library/std/src/sys/locks/rwlock/windows7.rs b/library/std/src/sys/sync/rwlock/windows7.rs
index e69415baac4..e69415baac4 100644
--- a/library/std/src/sys/locks/rwlock/windows7.rs
+++ b/library/std/src/sys/sync/rwlock/windows7.rs
diff --git a/library/std/src/sys/locks/rwlock/xous.rs b/library/std/src/sys/sync/rwlock/xous.rs
index ab45b33e1f6..ab45b33e1f6 100644
--- a/library/std/src/sys/locks/rwlock/xous.rs
+++ b/library/std/src/sys/sync/rwlock/xous.rs