Skip to content

Commit 0885c97

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 0885c97

File tree

7 files changed

+55
-38
lines changed

7 files changed

+55
-38
lines changed

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

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ use crate::ptr::null;
33
use crate::sync::atomic::AtomicU32;
44
use crate::time::Duration;
55

6+
/// An atomic for use as a futex that is at least 32-bits but may be larger
7+
pub type Atomic = AtomicU32;
8+
/// Must be the underlying type of Atomic
9+
pub type Primitive = u32;
10+
611
/// An atomic for use as a futex that is at least 8-bits but may be larger.
712
pub type SmallAtomic = AtomicU32;
813
/// Must be the underlying type of SmallAtomic

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

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
use crate::sync::atomic::AtomicU32;
1212
use crate::time::Duration;
1313

14+
/// An atomic for use as a futex that is at least 32-bits but may be larger
15+
pub type Atomic = AtomicU32;
16+
/// Must be the underlying type of Atomic
17+
pub type Primitive = u32;
18+
1419
/// An atomic for use as a futex that is at least 8-bits but may be larger.
1520
pub type SmallAtomic = AtomicU32;
1621
/// Must be the underlying type of SmallAtomic

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

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ use core::arch::wasm64 as wasm;
66
use crate::sync::atomic::AtomicU32;
77
use crate::time::Duration;
88

9+
/// An atomic for use as a futex that is at least 32-bits but may be larger
10+
pub type Atomic = AtomicU32;
11+
/// Must be the underlying type of Atomic
12+
pub type Primitive = u32;
13+
914
/// An atomic for use as a futex that is at least 8-bits but may be larger.
1015
pub type SmallAtomic = AtomicU32;
1116
/// Must be the underlying type of SmallAtomic

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

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ use core::{mem, ptr};
99
use super::api::{self, WinError};
1010
use crate::sys::{c, dur2timeout};
1111

12+
/// An atomic for use as a futex that is at least 32-bits but may be larger
13+
pub type Atomic = AtomicU32;
14+
/// Must be the underlying type of Atomic
15+
pub type Primitive = u32;
16+
1217
/// An atomic for use as a futex that is at least 8-bits but may be larger.
1318
pub type SmallAtomic = AtomicU8;
1419
/// Must be the underlying type of SmallAtomic

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

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
use crate::sync::atomic::AtomicU32;
21
use crate::sync::atomic::Ordering::Relaxed;
3-
use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
2+
use crate::sys::futex::{Atomic, futex_wait, futex_wake, futex_wake_all};
43
use crate::sys::sync::Mutex;
54
use crate::time::Duration;
65

76
pub struct Condvar {
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: Atomic,
1211
}
1312

1413
impl Condvar {
1514
#[inline]
1615
pub const fn new() -> Self {
17-
Self { futex: AtomicU32::new(0) }
16+
Self { futex: Atomic::new(0) }
1817
}
1918

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

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

+12-13
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,38 @@
11
use crate::cell::Cell;
22
use crate::sync as public;
3-
use crate::sync::atomic::AtomicU32;
43
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
54
use crate::sync::once::ExclusiveState;
6-
use crate::sys::futex::{futex_wait, futex_wake_all};
5+
use crate::sys::futex::{Atomic, 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-
const INCOMPLETE: u32 = 0;
11+
const INCOMPLETE: 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-
const POISONED: u32 = 1;
15+
const POISONED: 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-
const RUNNING: u32 = 2;
18+
const RUNNING: Primitive = 2;
2019
/// Initialization has completed and all future calls should finish immediately.
21-
const COMPLETE: u32 = 3;
20+
const COMPLETE: 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-
const QUEUED: u32 = 4;
25+
const QUEUED: 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-
const STATE_MASK: u32 = 0b11;
31+
const STATE_MASK: Primitive = 0b11;
3332

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

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

5150
struct CompletionGuard<'a> {
52-
state_and_queued: &'a AtomicU32,
53-
set_state_on_drop_to: u32,
51+
state_and_queued: &'a Atomic,
52+
set_state_on_drop_to: Primitive,
5453
}
5554

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

6766
pub struct Once {
68-
state_and_queued: AtomicU32,
67+
state_and_queued: Atomic,
6968
}
7069

7170
impl Once {
7271
#[inline]
7372
pub const fn new() -> Once {
74-
Once { state_and_queued: AtomicU32::new(INCOMPLETE) }
73+
Once { state_and_queued: Atomic::new(INCOMPLETE) }
7574
}
7675

7776
#[inline]

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

+20-21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use crate::sync::atomic::AtomicU32;
21
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
3-
use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
2+
use crate::sys::futex::{Atomic, Primitive, futex_wait, futex_wake, futex_wake_all};
43

54
pub struct RwLock {
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: Atomic,
1413
// The 'condition variable' to notify writers through.
1514
// Incremented on every signal.
16-
writer_notify: AtomicU32,
15+
writer_notify: Atomic,
1716
}
1817

19-
const READ_LOCKED: u32 = 1;
20-
const MASK: u32 = (1 << 30) - 1;
21-
const WRITE_LOCKED: u32 = MASK;
22-
const MAX_READERS: u32 = MASK - 1;
23-
const READERS_WAITING: u32 = 1 << 30;
24-
const WRITERS_WAITING: u32 = 1 << 31;
18+
const READ_LOCKED: Primitive = 1;
19+
const MASK: Primitive = (1 << 30) - 1;
20+
const WRITE_LOCKED: Primitive = MASK;
21+
const MAX_READERS: Primitive = MASK - 1;
22+
const READERS_WAITING: Primitive = 1 << 30;
23+
const WRITERS_WAITING: Primitive = 1 << 31;
2524

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

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

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

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

4645
#[inline]
47-
fn is_read_lockable(state: u32) -> bool {
46+
fn is_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-
fn has_reached_max_readers(state: u32) -> bool {
57+
fn has_reached_max_readers(state: Primitive) -> bool {
5958
state & MASK == MAX_READERS
6059
}
6160

6261
impl RwLock {
6362
#[inline]
6463
pub const fn new() -> Self {
65-
Self { state: AtomicU32::new(0), writer_notify: AtomicU32::new(0) }
64+
Self { state: Atomic::new(0), writer_notify: Atomic::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-
fn wake_writer_or_readers(&self, mut state: u32) {
227+
fn wake_writer_or_readers(&self, mut state: 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-
fn spin_until(&self, f: impl Fn(u32) -> bool) -> u32 {
292+
fn spin_until(&self, f: impl Fn(Primitive) -> bool) -> Primitive {
294293
let mut 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-
fn spin_write(&self) -> u32 {
305+
fn spin_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-
fn spin_read(&self) -> u32 {
311+
fn spin_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)