Skip to content

Commit e08bce6

Browse files
committed
Auto merge of rust-lang#131841 - paulmenage:futex-abstraction, r=joboet
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, switch the primary futex state type from `AtomicU32` to `futex::Futex`. The `futex::Futex` type should be usable as an atomic value with underlying primitive type equal to `futex::Primitive`. (`SmallAtomic` is also renamed to `SmallFutex`). This allows supporting the futex API on systems where the underlying kernel futex implementation requires more user state than simply an `AtomicU32`. All in-tree futex implementations simply define {`Futex`,`Primitive`} directly as {`AtomicU32`,`u32`}.
2 parents eba461c + 01bce29 commit e08bce6

File tree

10 files changed

+83
-66
lines changed

10 files changed

+83
-66
lines changed

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ 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 Futex = AtomicU32;
8+
/// Must be the underlying type of Futex
9+
pub type Primitive = u32;
10+
611
/// An atomic for use as a futex that is at least 8-bits but may be larger.
7-
pub type SmallAtomic = AtomicU32;
8-
/// Must be the underlying type of SmallAtomic
12+
pub type SmallFutex = AtomicU32;
13+
/// Must be the underlying type of SmallFutex
914
pub type SmallPrimitive = u32;
1015

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

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@
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 Futex = AtomicU32;
16+
/// Must be the underlying type of Futex
17+
pub type Primitive = u32;
18+
1419
/// An atomic for use as a futex that is at least 8-bits but may be larger.
15-
pub type SmallAtomic = AtomicU32;
16-
/// Must be the underlying type of SmallAtomic
20+
pub type SmallFutex = AtomicU32;
21+
/// Must be the underlying type of SmallFutex
1722
pub type SmallPrimitive = u32;
1823

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

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ 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 Futex = AtomicU32;
11+
/// Must be the underlying type of Futex
12+
pub type Primitive = u32;
13+
914
/// An atomic for use as a futex that is at least 8-bits but may be larger.
10-
pub type SmallAtomic = AtomicU32;
11-
/// Must be the underlying type of SmallAtomic
15+
pub type SmallFutex = AtomicU32;
16+
/// Must be the underlying type of SmallFutex
1217
pub type SmallPrimitive = u32;
1318

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

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

+20-15
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,27 @@ 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 Futex = AtomicU32;
14+
/// Must be the underlying type of Futex
15+
pub type Primitive = u32;
16+
1217
/// An atomic for use as a futex that is at least 8-bits but may be larger.
13-
pub type SmallAtomic = AtomicU8;
14-
/// Must be the underlying type of SmallAtomic
18+
pub type SmallFutex = AtomicU8;
19+
/// Must be the underlying type of SmallFutex
1520
pub type SmallPrimitive = u8;
1621

17-
pub unsafe trait Futex {}
22+
pub unsafe trait Futexable {}
1823
pub unsafe trait Waitable {
19-
type Atomic;
24+
type Futex;
2025
}
2126
macro_rules! unsafe_waitable_int {
2227
($(($int:ty, $atomic:ty)),*$(,)?) => {
2328
$(
2429
unsafe impl Waitable for $int {
25-
type Atomic = $atomic;
30+
type Futex = $atomic;
2631
}
27-
unsafe impl Futex for $atomic {}
32+
unsafe impl Futexable for $atomic {}
2833
)*
2934
};
3035
}
@@ -42,15 +47,15 @@ unsafe_waitable_int! {
4247
(usize, AtomicUsize),
4348
}
4449
unsafe impl<T> Waitable for *const T {
45-
type Atomic = AtomicPtr<T>;
50+
type Futex = AtomicPtr<T>;
4651
}
4752
unsafe impl<T> Waitable for *mut T {
48-
type Atomic = AtomicPtr<T>;
53+
type Futex = AtomicPtr<T>;
4954
}
50-
unsafe impl<T> Futex for AtomicPtr<T> {}
55+
unsafe impl<T> Futexable for AtomicPtr<T> {}
5156

5257
pub fn wait_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-
pub fn wake_by_address_single<T: Futex>(address: &T) {
71+
pub fn wake_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-
pub fn wake_by_address_all<T: Futex>(address: &T) {
78+
pub fn wake_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-
pub fn futex_wait<W: Waitable>(futex: &W::Atomic, expected: W, timeout: Option<Duration>) -> bool {
85+
pub fn futex_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-
pub fn futex_wake<T: Futex>(futex: &T) -> bool {
90+
pub fn futex_wake<T: Futexable>(futex: &T) -> bool {
8691
wake_by_address_single(futex);
8792
false
8893
}
8994

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

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::{Futex, 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: Futex,
1211
}
1312

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

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

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
22
use crate::sys::futex::{self, futex_wait, futex_wake};
33

4-
type Atomic = futex::SmallAtomic;
4+
type Futex = futex::SmallFutex;
55
type State = futex::SmallPrimitive;
66

77
pub struct Mutex {
8-
futex: Atomic,
8+
futex: Futex,
99
}
1010

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

2121
#[inline]

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::{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-
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 Futex,
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: Futex,
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: Futex::new(INCOMPLETE) }
7574
}
7675

7776
#[inline]

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

+1-1
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

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::{Futex, 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: 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-
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: 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-
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)