Skip to content

Commit cf7ff15

Browse files
committed
Abstract the state type for futexes
In the same way that we expose SmallAtomic and SmallPrimitive to allow Windows to use a value other than an AtomicU32 for its futex state, this patch switches the primary futex state type from AtomicU32 to futex::Atomic. The futex::Atomic type should be usable as an atomic value with underlying primitive type equal to futex::Primitive. This allows supporting the futex API on systems where the underlying kernel futex implementation requires more state than simply an AtomicU32. All in-tree futex implementations simply define {Atomic,Primitive} directly as {AtomicU32,u32}.
1 parent 3a85d3f commit cf7ff15

10 files changed

Lines changed: 83 additions & 66 deletions

File tree

‎library/std/src/sys/pal/hermit/futex.rs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ use crate::ptr::null;
33
usecrate::sync::atomic::AtomicU32;
44
usecrate::time::Duration;
55

6+
/// An atomic for use as a futex that is at least 32-bits but may be larger
7+
pubtypeFutex = AtomicU32;
8+
/// Must be the underlying type of Futex
9+
pubtypePrimitive = u32;
10+
611
/// An atomic for use as a futex that is at least 8-bits but may be larger.
7-
pubtypeSmallAtomic = AtomicU32;
8-
/// Must be the underlying type of SmallAtomic
12+
pubtypeSmallFutex = AtomicU32;
13+
/// Must be the underlying type of SmallFutex
914
pubtypeSmallPrimitive = u32;
1015

1116
pubfnfutex_wait(futex:&AtomicU32,expected:u32,timeout:Option<Duration>) -> bool{

‎library/std/src/sys/pal/unix/futex.rs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@
1111
usecrate::sync::atomic::AtomicU32;
1212
usecrate::time::Duration;
1313

14+
/// An atomic for use as a futex that is at least 32-bits but may be larger
15+
pubtypeFutex = AtomicU32;
16+
/// Must be the underlying type of Futex
17+
pubtypePrimitive = u32;
18+
1419
/// An atomic for use as a futex that is at least 8-bits but may be larger.
15-
pubtypeSmallAtomic = AtomicU32;
16-
/// Must be the underlying type of SmallAtomic
20+
pubtypeSmallFutex = AtomicU32;
21+
/// Must be the underlying type of SmallFutex
1722
pubtypeSmallPrimitive = u32;
1823

1924
/// Waits for a `futex_wake` operation to wake us.

‎library/std/src/sys/pal/wasm/atomics/futex.rs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ use core::arch::wasm64 as wasm;
66
usecrate::sync::atomic::AtomicU32;
77
usecrate::time::Duration;
88

9+
/// An atomic for use as a futex that is at least 32-bits but may be larger
10+
pubtypeFutex = AtomicU32;
11+
/// Must be the underlying type of Futex
12+
pubtypePrimitive = u32;
13+
914
/// An atomic for use as a futex that is at least 8-bits but may be larger.
10-
pubtypeSmallAtomic = AtomicU32;
11-
/// Must be the underlying type of SmallAtomic
15+
pubtypeSmallFutex = AtomicU32;
16+
/// Must be the underlying type of SmallFutex
1217
pubtypeSmallPrimitive = u32;
1318

1419
/// Wait for a futex_wake operation to wake us.

‎library/std/src/sys/pal/windows/futex.rs‎

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,27 @@ use core::{mem, ptr};
99
usesuper::api::{self,WinError};
1010
usecrate::sys::{c, dur2timeout};
1111

12+
/// An atomic for use as a futex that is at least 32-bits but may be larger
13+
pubtypeFutex = AtomicU32;
14+
/// Must be the underlying type of Futex
15+
pubtypePrimitive = u32;
16+
1217
/// An atomic for use as a futex that is at least 8-bits but may be larger.
13-
pubtypeSmallAtomic = AtomicU8;
14-
/// Must be the underlying type of SmallAtomic
18+
pubtypeSmallFutex = AtomicU8;
19+
/// Must be the underlying type of SmallFutex
1520
pubtypeSmallPrimitive = u8;
1621

17-
pubunsafetraitFutex{}
22+
pubunsafetraitFutexable{}
1823
pubunsafetraitWaitable{
19-
typeAtomic;
24+
typeFutex;
2025
}
2126
macro_rules! unsafe_waitable_int {
2227
($(($int:ty, $atomic:ty)),*$(,)?) => {
2328
$(
2429
unsafeimplWaitablefor $int {
25-
typeAtomic = $atomic;
30+
typeFutex = $atomic;
2631
}
27-
unsafeimplFutexfor $atomic {}
32+
unsafeimplFutexablefor $atomic {}
2833
)*
2934
};
3035
}
@@ -42,15 +47,15 @@ unsafe_waitable_int! {
4247
(usize,AtomicUsize),
4348
}
4449
unsafeimpl<T>Waitablefor*constT{
45-
typeAtomic = AtomicPtr<T>;
50+
typeFutex = AtomicPtr<T>;
4651
}
4752
unsafeimpl<T>Waitablefor*mutT{
48-
typeAtomic = AtomicPtr<T>;
53+
typeFutex = AtomicPtr<T>;
4954
}
50-
unsafeimpl<T>FutexforAtomicPtr<T>{}
55+
unsafeimpl<T>FutexableforAtomicPtr<T>{}
5156

5257
pubfnwait_on_address<W:Waitable>(
53-
address:&W::Atomic,
58+
address:&W::Futex,
5459
compare:W,
5560
timeout:Option<Duration>,
5661
) -> bool{
@@ -63,30 +68,30 @@ pub fn wait_on_address<W: Waitable>(
6368
}
6469
}
6570

66-
pubfnwake_by_address_single<T:Futex>(address:&T){
71+
pubfnwake_by_address_single<T:Futexable>(address:&T){
6772
unsafe{
6873
let addr = ptr::from_ref(address).cast::<c_void>();
6974
c::WakeByAddressSingle(addr);
7075
}
7176
}
7277

73-
pubfnwake_by_address_all<T:Futex>(address:&T){
78+
pubfnwake_by_address_all<T:Futexable>(address:&T){
7479
unsafe{
7580
let addr = ptr::from_ref(address).cast::<c_void>();
7681
c::WakeByAddressAll(addr);
7782
}
7883
}
7984

80-
pubfnfutex_wait<W:Waitable>(futex:&W::Atomic,expected:W,timeout:Option<Duration>) -> bool{
85+
pubfnfutex_wait<W:Waitable>(futex:&W::Futex,expected:W,timeout:Option<Duration>) -> bool{
8186
// return false only on timeout
8287
wait_on_address(futex, expected, timeout) || api::get_last_error() != WinError::TIMEOUT
8388
}
8489

85-
pubfnfutex_wake<T:Futex>(futex:&T) -> bool{
90+
pubfnfutex_wake<T:Futexable>(futex:&T) -> bool{
8691
wake_by_address_single(futex);
8792
false
8893
}
8994

90-
pubfnfutex_wake_all<T:Futex>(futex:&T){
95+
pubfnfutex_wake_all<T:Futexable>(futex:&T){
9196
wake_by_address_all(futex)
9297
}

‎library/std/src/sys/sync/condvar/futex.rs‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
usecrate::sync::atomic::AtomicU32;
21
usecrate::sync::atomic::Ordering::Relaxed;
3-
usecrate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
2+
usecrate::sys::futex::{Futex,futex_wait, futex_wake, futex_wake_all};
43
usecrate::sys::sync::Mutex;
54
usecrate::time::Duration;
65

76
pubstructCondvar{
87
// The value of this atomic is simply incremented on every notification.
98
// This is used by `.wait()` to not miss any notifications after
109
// unlocking the mutex and before waiting for notifications.
11-
futex:AtomicU32,
10+
futex:Futex,
1211
}
1312

1413
implCondvar{
1514
#[inline]
1615
pubconstfnnew() -> Self{
17-
Self{futex:AtomicU32::new(0)}
16+
Self{futex:Futex::new(0)}
1817
}
1918

2019
// All the memory orderings here are `Relaxed`,

‎library/std/src/sys/sync/mutex/futex.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
usecrate::sync::atomic::Ordering::{Acquire,Relaxed,Release};
22
usecrate::sys::futex::{self, futex_wait, futex_wake};
33

4-
typeAtomic = futex::SmallAtomic;
4+
typeFutex = futex::SmallFutex;
55
typeState = futex::SmallPrimitive;
66

77
pubstructMutex{
8-
futex:Atomic,
8+
futex:Futex,
99
}
1010

1111
constUNLOCKED:State = 0;
@@ -15,7 +15,7 @@ const CONTENDED: State = 2; // locked, and other threads waiting (contended)
1515
implMutex{
1616
#[inline]
1717
pubconstfnnew() -> Self{
18-
Self{futex:Atomic::new(UNLOCKED)}
18+
Self{futex:Futex::new(UNLOCKED)}
1919
}
2020

2121
#[inline]

‎library/std/src/sys/sync/once/futex.rs‎

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,38 @@
11
usecrate::cell::Cell;
22
usecrate::sync as public;
3-
usecrate::sync::atomic::AtomicU32;
43
usecrate::sync::atomic::Ordering::{Acquire,Relaxed,Release};
54
usecrate::sync::once::ExclusiveState;
6-
usecrate::sys::futex::{futex_wait, futex_wake_all};
5+
usecrate::sys::futex::{Futex,Primitive,futex_wait, futex_wake_all};
76

87
// On some platforms, the OS is very nice and handles the waiter queue for us.
98
// This means we only need one atomic value with 4 states:
109

1110
/// No initialization has run yet, and no thread is currently using the Once.
12-
constINCOMPLETE:u32 = 0;
11+
constINCOMPLETE:Primitive = 0;
1312
/// Some thread has previously attempted to initialize the Once, but it panicked,
1413
/// so the Once is now poisoned. There are no other threads currently accessing
1514
/// this Once.
16-
constPOISONED:u32 = 1;
15+
constPOISONED:Primitive = 1;
1716
/// Some thread is currently attempting to run initialization. It may succeed,
1817
/// so all future threads need to wait for it to finish.
19-
constRUNNING:u32 = 2;
18+
constRUNNING:Primitive = 2;
2019
/// Initialization has completed and all future calls should finish immediately.
21-
constCOMPLETE:u32 = 3;
20+
constCOMPLETE:Primitive = 3;
2221

2322
// An additional bit indicates whether there are waiting threads:
2423

2524
/// May only be set if the state is not COMPLETE.
26-
constQUEUED:u32 = 4;
25+
constQUEUED:Primitive = 4;
2726

2827
// Threads wait by setting the QUEUED bit and calling `futex_wait` on the state
2928
// variable. When the running thread finishes, it will wake all waiting threads using
3029
// `futex_wake_all`.
3130

32-
constSTATE_MASK:u32 = 0b11;
31+
constSTATE_MASK:Primitive = 0b11;
3332

3433
pubstructOnceState{
3534
poisoned:bool,
36-
set_state_to:Cell<u32>,
35+
set_state_to:Cell<Primitive>,
3736
}
3837

3938
implOnceState{
@@ -49,8 +48,8 @@ impl OnceState {
4948
}
5049

5150
structCompletionGuard<'a>{
52-
state_and_queued:&'aAtomicU32,
53-
set_state_on_drop_to:u32,
51+
state_and_queued:&'aFutex,
52+
set_state_on_drop_to:Primitive,
5453
}
5554

5655
impl<'a>DropforCompletionGuard<'a>{
@@ -65,13 +64,13 @@ impl<'a> Drop for CompletionGuard<'a> {
6564
}
6665

6766
pubstructOnce{
68-
state_and_queued:AtomicU32,
67+
state_and_queued:Futex,
6968
}
7069

7170
implOnce{
7271
#[inline]
7372
pubconstfnnew() -> Once{
74-
Once{state_and_queued:AtomicU32::new(INCOMPLETE)}
73+
Once{state_and_queued:Futex::new(INCOMPLETE)}
7574
}
7675

7776
#[inline]

‎library/std/src/sys/sync/once/queue.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
// You'll find a few more details in the implementation, but that's the gist of
2424
// it!
2525
//
26-
// Atomic orderings:
26+
// Futex orderings:
2727
// When running `Once` we deal with multiple atomics:
2828
// `Once.state_and_queue` and an unknown number of `Waiter.signaled`.
2929
// * `state_and_queue` is used (1) as a state flag, (2) for synchronizing the

‎library/std/src/sys/sync/rwlock/futex.rs‎

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
usecrate::sync::atomic::AtomicU32;
21
usecrate::sync::atomic::Ordering::{Acquire,Relaxed,Release};
3-
usecrate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
2+
usecrate::sys::futex::{Futex,Primitive,futex_wait, futex_wake, futex_wake_all};
43

54
pubstructRwLock{
65
// The state consists of a 30-bit reader counter, a 'readers waiting' flag, and a 'writers waiting' flag.
@@ -10,41 +9,41 @@ pub struct RwLock {
109
// 0x3FFF_FFFF: Write locked
1110
// Bit 30: Readers are waiting on this futex.
1211
// Bit 31: Writers are waiting on the writer_notify futex.
13-
state:AtomicU32,
12+
state:Futex,
1413
// The 'condition variable' to notify writers through.
1514
// Incremented on every signal.
16-
writer_notify:AtomicU32,
15+
writer_notify:Futex,
1716
}
1817

19-
constREAD_LOCKED:u32 = 1;
20-
constMASK:u32 = (1 << 30) - 1;
21-
constWRITE_LOCKED:u32 = MASK;
22-
constMAX_READERS:u32 = MASK - 1;
23-
constREADERS_WAITING:u32 = 1 << 30;
24-
constWRITERS_WAITING:u32 = 1 << 31;
18+
constREAD_LOCKED:Primitive = 1;
19+
constMASK:Primitive = (1 << 30) - 1;
20+
constWRITE_LOCKED:Primitive = MASK;
21+
constMAX_READERS:Primitive = MASK - 1;
22+
constREADERS_WAITING:Primitive = 1 << 30;
23+
constWRITERS_WAITING:Primitive = 1 << 31;
2524

2625
#[inline]
27-
fnis_unlocked(state:u32) -> bool{
26+
fnis_unlocked(state:Primitive) -> bool{
2827
state &MASK == 0
2928
}
3029

3130
#[inline]
32-
fnis_write_locked(state:u32) -> bool{
31+
fnis_write_locked(state:Primitive) -> bool{
3332
state &MASK == WRITE_LOCKED
3433
}
3534

3635
#[inline]
37-
fnhas_readers_waiting(state:u32) -> bool{
36+
fnhas_readers_waiting(state:Primitive) -> bool{
3837
state &READERS_WAITING != 0
3938
}
4039

4140
#[inline]
42-
fnhas_writers_waiting(state:u32) -> bool{
41+
fnhas_writers_waiting(state:Primitive) -> bool{
4342
state &WRITERS_WAITING != 0
4443
}
4544

4645
#[inline]
47-
fnis_read_lockable(state:u32) -> bool{
46+
fnis_read_lockable(state:Primitive) -> bool{
4847
// This also returns false if the counter could overflow if we tried to read lock it.
4948
//
5049
// We don't allow read-locking if there's readers waiting, even if the lock is unlocked
@@ -55,14 +54,14 @@ fn is_read_lockable(state: u32) -> bool {
5554
}
5655

5756
#[inline]
58-
fnhas_reached_max_readers(state:u32) -> bool{
57+
fnhas_reached_max_readers(state:Primitive) -> bool{
5958
state &MASK == MAX_READERS
6059
}
6160

6261
implRwLock{
6362
#[inline]
6463
pubconstfnnew() -> Self{
65-
Self{state:AtomicU32::new(0),writer_notify:AtomicU32::new(0)}
64+
Self{state:Futex::new(0),writer_notify:Futex::new(0)}
6665
}
6766

6867
#[inline]
@@ -225,7 +224,7 @@ impl RwLock {
225224
/// If both are waiting, this will wake up only one writer, but will fall
226225
/// back to waking up readers if there was no writer to wake up.
227226
#[cold]
228-
fnwake_writer_or_readers(&self,mutstate:u32){
227+
fnwake_writer_or_readers(&self,mutstate:Primitive){
229228
assert!(is_unlocked(state));
230229

231230
// The readers waiting bit might be turned on at any point now,
@@ -290,7 +289,7 @@ impl RwLock {
290289

291290
/// Spin for a while, but stop directly at the given condition.
292291
#[inline]
293-
fnspin_until(&self,f:implFn(u32) -> bool) -> u32{
292+
fnspin_until(&self,f:implFn(Primitive) -> bool) -> Primitive{
294293
letmut spin = 100;// Chosen by fair dice roll.
295294
loop{
296295
let state = self.state.load(Relaxed);
@@ -303,13 +302,13 @@ impl RwLock {
303302
}
304303

305304
#[inline]
306-
fnspin_write(&self) -> u32{
305+
fnspin_write(&self) -> Primitive{
307306
// Stop spinning when it's unlocked or when there's waiting writers, to keep things somewhat fair.
308307
self.spin_until(|state| is_unlocked(state) || has_writers_waiting(state))
309308
}
310309

311310
#[inline]
312-
fnspin_read(&self) -> u32{
311+
fnspin_read(&self) -> Primitive{
313312
// Stop spinning when it's unlocked or read locked, or when there's waiting threads.
314313
self.spin_until(|state| {
315314
!is_write_locked(state) || has_readers_waiting(state) || has_writers_waiting(state)

0 commit comments

Comments
 (0)