about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorjoboet <jonasboettiger@icloud.com>2022-09-05 10:19:12 +0200
committerjoboet <jonasboettiger@icloud.com>2022-09-05 10:19:12 +0200
commita40d300100a5e48cb66f5261738496dbacf11f99 (patch)
tree33623f1674c80b2b547314cb5890809b7c0fcc77 /library/std/src
parent633d46d0245bf7f60b341c8df8da230c0b689396 (diff)
downloadrust-a40d300100a5e48cb66f5261738496dbacf11f99.tar.gz
rust-a40d300100a5e48cb66f5261738496dbacf11f99.zip
std: clarify semantics of SGX parker
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/sys/sgx/thread_parker.rs44
1 files changed, 29 insertions, 15 deletions
diff --git a/library/std/src/sys/sgx/thread_parker.rs b/library/std/src/sys/sgx/thread_parker.rs
index f768abddd44..1c55bcffb1e 100644
--- a/library/std/src/sys/sgx/thread_parker.rs
+++ b/library/std/src/sys/sgx/thread_parker.rs
@@ -9,11 +9,17 @@ use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
 use crate::time::Duration;
 use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE};
 
-const EMPTY: *mut u8 = ptr::invalid_mut(0);
-/// The TCS structure must be page-aligned, so this cannot be a valid pointer
-const NOTIFIED: *mut u8 = ptr::invalid_mut(1);
+// The TCS structure must be page-aligned (this is checked by EENTER), so these cannot
+// be valid pointers
+const EMPTY: *mut u8 = ptr::invalid_mut(1);
+const NOTIFIED: *mut u8 = ptr::invalid_mut(2);
 
 pub struct Parker {
+    /// The park state. One of EMPTY, NOTIFIED or a TCS address.
+    /// A state change to NOTIFIED must be done with release ordering
+    /// and be observed with acquire ordering so that operations after
+    /// `thread::park` returns will not occur before the unpark message
+    /// was sent.
     state: AtomicPtr<u8>,
 }
 
@@ -30,23 +36,28 @@ impl Parker {
 
     // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
     pub unsafe fn park(self: Pin<&Self>) {
-        let tcs = thread::current().as_ptr();
-
         if self.state.load(Acquire) != NOTIFIED {
-            if self.state.compare_exchange(EMPTY, tcs, Acquire, Acquire).is_ok() {
-                // Loop to guard against spurious wakeups.
-                loop {
+            let mut prev = EMPTY;
+            loop {
+                // Guard against changing TCS addresses by always setting the state to
+                // the current value.
+                let tcs = thread::current().as_ptr();
+                if self.state.compare_exchange(prev, tcs, Relaxed, Acquire).is_ok() {
                     let event = usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap();
                     assert!(event & EV_UNPARK == EV_UNPARK);
-                    if self.state.load(Acquire) == NOTIFIED {
-                        break;
-                    }
+                    prev = tcs;
+                } else {
+                    // The state was definitely changed by another thread at this point.
+                    // The only time this occurs is when the state is changed to NOTIFIED.
+                    // We observed this change with acquire ordering, so we can simply
+                    // change the state to EMPTY with a relaxed store.
+                    break;
                 }
             }
         }
 
         // At this point, the token was definately read with acquire ordering,
-        // so this can be a store.
+        // so this can be a relaxed store.
         self.state.store(EMPTY, Relaxed);
     }
 
@@ -56,7 +67,7 @@ impl Parker {
         let tcs = thread::current().as_ptr();
 
         if self.state.load(Acquire) != NOTIFIED {
-            if self.state.compare_exchange(EMPTY, tcs, Acquire, Acquire).is_ok() {
+            if self.state.compare_exchange(EMPTY, tcs, Relaxed, Acquire).is_ok() {
                 match usercalls::wait(EV_UNPARK, timeout) {
                     Ok(event) => assert!(event & EV_UNPARK == EV_UNPARK),
                     Err(e) => {
@@ -85,8 +96,11 @@ impl Parker {
         if !matches!(state, EMPTY | NOTIFIED) {
             // There is a thread waiting, wake it up.
             let tcs = NonNull::new(state).unwrap();
-            // This will fail if the thread has already terminated by the time the signal is send,
-            // but that is OK.
+            // This will fail if the thread has already terminated or its TCS is destroyed
+            // by the time the signal is sent, but that is fine. If another thread receives
+            // the same TCS, it will receive this notification as a spurious wakeup, but
+            // all users of `wait` should and (internally) do guard against those where
+            // necessary.
             let _ = usercalls::send(EV_UNPARK, Some(tcs));
         }
     }