Skip to content

Commit 5c4d9f8

Browse files
authored
Rollup merge of #96040 - m-ou-se:futex-u32, r=Amanieu
Use u32 instead of i32 for futexes. This changes futexes from i32 to u32. The [Linux man page](https://man7.org/linux/man-pages/man2/futex.2.html) uses `uint32_t` for them, so I'm not sure why I used i32 for them. Maybe because I first used them for thread parkers, where I used -1, 0, and 1 as the states. (Wasm's `memory.atomic.wait32` does use `i32`, because wasm doesn't support `u32`.) It doesn't matter much, but using the unsigned type probably results in fewer surprises when shifting bits around or using comparison operators. r? `@Amanieu`
2 parents 599af74 + 7a35c0f commit 5c4d9f8

File tree

5 files changed

+55
-53
lines changed

5 files changed

+55
-53
lines changed

Diff for: library/std/src/sys/unix/futex.rs

+14-16
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
all(target_os = "emscripten", target_feature = "atomics")
55
))]
66

7-
use crate::sync::atomic::AtomicI32;
7+
use crate::sync::atomic::AtomicU32;
88
use crate::time::Duration;
99

1010
/// Wait for a futex_wake operation to wake us.
@@ -13,7 +13,7 @@ use crate::time::Duration;
1313
///
1414
/// Returns false on timeout, and true in all other cases.
1515
#[cfg(any(target_os = "linux", target_os = "android"))]
16-
pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) -> bool {
16+
pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
1717
use super::time::Timespec;
1818
use crate::ptr::null;
1919
use crate::sync::atomic::Ordering::Relaxed;
@@ -35,7 +35,7 @@ pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) -
3535
let r = unsafe {
3636
libc::syscall(
3737
libc::SYS_futex,
38-
futex as *const AtomicI32,
38+
futex as *const AtomicU32,
3939
libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
4040
expected,
4141
timespec.as_ref().map_or(null(), |t| &t.t as *const libc::timespec),
@@ -53,21 +53,19 @@ pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) -
5353
}
5454

5555
#[cfg(target_os = "emscripten")]
56-
pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) {
56+
pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) {
5757
extern "C" {
5858
fn emscripten_futex_wait(
59-
addr: *const AtomicI32,
59+
addr: *const AtomicU32,
6060
val: libc::c_uint,
6161
max_wait_ms: libc::c_double,
6262
) -> libc::c_int;
6363
}
6464

6565
unsafe {
6666
emscripten_futex_wait(
67-
futex as *const AtomicI32,
68-
// `val` is declared unsigned to match the Emscripten headers, but since it's used as
69-
// an opaque value, we can ignore the meaning of signed vs. unsigned and cast here.
70-
expected as libc::c_uint,
67+
futex,
68+
expected,
7169
timeout.map_or(crate::f64::INFINITY, |d| d.as_secs_f64() * 1000.0),
7270
);
7371
}
@@ -78,11 +76,11 @@ pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) {
7876
/// Returns true if this actually woke up such a thread,
7977
/// or false if no thread was waiting on this futex.
8078
#[cfg(any(target_os = "linux", target_os = "android"))]
81-
pub fn futex_wake(futex: &AtomicI32) -> bool {
79+
pub fn futex_wake(futex: &AtomicU32) -> bool {
8280
unsafe {
8381
libc::syscall(
8482
libc::SYS_futex,
85-
futex as *const AtomicI32,
83+
futex as *const AtomicU32,
8684
libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
8785
1,
8886
) > 0
@@ -91,22 +89,22 @@ pub fn futex_wake(futex: &AtomicI32) -> bool {
9189

9290
/// Wake up all threads that are waiting on futex_wait on this futex.
9391
#[cfg(any(target_os = "linux", target_os = "android"))]
94-
pub fn futex_wake_all(futex: &AtomicI32) {
92+
pub fn futex_wake_all(futex: &AtomicU32) {
9593
unsafe {
9694
libc::syscall(
9795
libc::SYS_futex,
98-
futex as *const AtomicI32,
96+
futex as *const AtomicU32,
9997
libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
10098
i32::MAX,
10199
);
102100
}
103101
}
104102

105103
#[cfg(target_os = "emscripten")]
106-
pub fn futex_wake(futex: &AtomicI32) -> bool {
104+
pub fn futex_wake(futex: &AtomicU32) -> bool {
107105
extern "C" {
108-
fn emscripten_futex_wake(addr: *const AtomicI32, count: libc::c_int) -> libc::c_int;
106+
fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int;
109107
}
110108

111-
unsafe { emscripten_futex_wake(futex as *const AtomicI32, 1) > 0 }
109+
unsafe { emscripten_futex_wake(futex, 1) > 0 }
112110
}

Diff for: library/std/src/sys/unix/locks/futex.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::cell::UnsafeCell;
22
use crate::sync::atomic::{
3-
AtomicI32, AtomicUsize,
3+
AtomicU32, AtomicUsize,
44
Ordering::{Acquire, Relaxed, Release},
55
};
66
use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
@@ -13,13 +13,13 @@ pub struct Mutex {
1313
/// 0: unlocked
1414
/// 1: locked, no other threads waiting
1515
/// 2: locked, and other threads waiting (contended)
16-
futex: AtomicI32,
16+
futex: AtomicU32,
1717
}
1818

1919
impl Mutex {
2020
#[inline]
2121
pub const fn new() -> Self {
22-
Self { futex: AtomicI32::new(0) }
22+
Self { futex: AtomicU32::new(0) }
2323
}
2424

2525
#[inline]
@@ -71,7 +71,7 @@ impl Mutex {
7171
}
7272
}
7373

74-
fn spin(&self) -> i32 {
74+
fn spin(&self) -> u32 {
7575
let mut spin = 100;
7676
loop {
7777
// We only use `load` (and not `swap` or `compare_exchange`)
@@ -110,13 +110,13 @@ pub struct Condvar {
110110
// The value of this atomic is simply incremented on every notification.
111111
// This is used by `.wait()` to not miss any notifications after
112112
// unlocking the mutex and before waiting for notifications.
113-
futex: AtomicI32,
113+
futex: AtomicU32,
114114
}
115115

116116
impl Condvar {
117117
#[inline]
118118
pub const fn new() -> Self {
119-
Self { futex: AtomicI32::new(0) }
119+
Self { futex: AtomicU32::new(0) }
120120
}
121121

122122
#[inline]

Diff for: library/std/src/sys/unix/locks/futex_rwlock.rs

+20-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::sync::atomic::{
2-
AtomicI32,
2+
AtomicU32,
33
Ordering::{Acquire, Relaxed, Release},
44
};
55
use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
@@ -14,36 +14,36 @@ pub struct RwLock {
1414
// 0x3FFF_FFFF: Write locked
1515
// Bit 30: Readers are waiting on this futex.
1616
// Bit 31: Writers are waiting on the writer_notify futex.
17-
state: AtomicI32,
17+
state: AtomicU32,
1818
// The 'condition variable' to notify writers through.
1919
// Incremented on every signal.
20-
writer_notify: AtomicI32,
20+
writer_notify: AtomicU32,
2121
}
2222

23-
const READ_LOCKED: i32 = 1;
24-
const MASK: i32 = (1 << 30) - 1;
25-
const WRITE_LOCKED: i32 = MASK;
26-
const MAX_READERS: i32 = MASK - 1;
27-
const READERS_WAITING: i32 = 1 << 30;
28-
const WRITERS_WAITING: i32 = 1 << 31;
23+
const READ_LOCKED: u32 = 1;
24+
const MASK: u32 = (1 << 30) - 1;
25+
const WRITE_LOCKED: u32 = MASK;
26+
const MAX_READERS: u32 = MASK - 1;
27+
const READERS_WAITING: u32 = 1 << 30;
28+
const WRITERS_WAITING: u32 = 1 << 31;
2929

30-
fn is_unlocked(state: i32) -> bool {
30+
fn is_unlocked(state: u32) -> bool {
3131
state & MASK == 0
3232
}
3333

34-
fn is_write_locked(state: i32) -> bool {
34+
fn is_write_locked(state: u32) -> bool {
3535
state & MASK == WRITE_LOCKED
3636
}
3737

38-
fn has_readers_waiting(state: i32) -> bool {
38+
fn has_readers_waiting(state: u32) -> bool {
3939
state & READERS_WAITING != 0
4040
}
4141

42-
fn has_writers_waiting(state: i32) -> bool {
42+
fn has_writers_waiting(state: u32) -> bool {
4343
state & WRITERS_WAITING != 0
4444
}
4545

46-
fn is_read_lockable(state: i32) -> bool {
46+
fn is_read_lockable(state: u32) -> bool {
4747
// This also returns false if the counter could overflow if we tried to read lock it.
4848
//
4949
// We don't allow read-locking if there's readers waiting, even if the lock is unlocked
@@ -53,14 +53,14 @@ fn is_read_lockable(state: i32) -> bool {
5353
state & MASK < MAX_READERS && !has_readers_waiting(state) && !has_writers_waiting(state)
5454
}
5555

56-
fn has_reached_max_readers(state: i32) -> bool {
56+
fn has_reached_max_readers(state: u32) -> bool {
5757
state & MASK == MAX_READERS
5858
}
5959

6060
impl RwLock {
6161
#[inline]
6262
pub const fn new() -> Self {
63-
Self { state: AtomicI32::new(0), writer_notify: AtomicI32::new(0) }
63+
Self { state: AtomicU32::new(0), writer_notify: AtomicU32::new(0) }
6464
}
6565

6666
#[inline]
@@ -227,7 +227,7 @@ impl RwLock {
227227
/// If both are waiting, this will wake up only one writer, but will fall
228228
/// back to waking up readers if there was no writer to wake up.
229229
#[cold]
230-
fn wake_writer_or_readers(&self, mut state: i32) {
230+
fn wake_writer_or_readers(&self, mut state: u32) {
231231
assert!(is_unlocked(state));
232232

233233
// The readers waiting bit might be turned on at any point now,
@@ -287,7 +287,7 @@ impl RwLock {
287287
}
288288

289289
/// Spin for a while, but stop directly at the given condition.
290-
fn spin_until(&self, f: impl Fn(i32) -> bool) -> i32 {
290+
fn spin_until(&self, f: impl Fn(u32) -> bool) -> u32 {
291291
let mut spin = 100; // Chosen by fair dice roll.
292292
loop {
293293
let state = self.state.load(Relaxed);
@@ -299,12 +299,12 @@ impl RwLock {
299299
}
300300
}
301301

302-
fn spin_write(&self) -> i32 {
302+
fn spin_write(&self) -> u32 {
303303
// Stop spinning when it's unlocked or when there's waiting writers, to keep things somewhat fair.
304304
self.spin_until(|state| is_unlocked(state) || has_writers_waiting(state))
305305
}
306306

307-
fn spin_read(&self) -> i32 {
307+
fn spin_read(&self) -> u32 {
308308
// Stop spinning when it's unlocked or read locked, or when there's waiting threads.
309309
self.spin_until(|state| {
310310
!is_write_locked(state) || has_readers_waiting(state) || has_writers_waiting(state)

Diff for: library/std/src/sys/wasm/atomics/futex.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
use crate::arch::wasm32;
22
use crate::convert::TryInto;
3-
use crate::sync::atomic::AtomicI32;
3+
use crate::sync::atomic::AtomicU32;
44
use crate::time::Duration;
55

6-
pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) {
6+
pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) {
77
let timeout = timeout.and_then(|t| t.as_nanos().try_into().ok()).unwrap_or(-1);
88
unsafe {
9-
wasm32::memory_atomic_wait32(futex as *const AtomicI32 as *mut i32, expected, timeout);
9+
wasm32::memory_atomic_wait32(
10+
futex as *const AtomicU32 as *mut i32,
11+
expected as i32,
12+
timeout,
13+
);
1014
}
1115
}
1216

13-
pub fn futex_wake(futex: &AtomicI32) {
17+
pub fn futex_wake(futex: &AtomicU32) {
1418
unsafe {
15-
wasm32::memory_atomic_notify(futex as *const AtomicI32 as *mut i32, 1);
19+
wasm32::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, 1);
1620
}
1721
}

Diff for: library/std/src/sys_common/thread_parker/futex.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
use crate::sync::atomic::AtomicI32;
1+
use crate::sync::atomic::AtomicU32;
22
use crate::sync::atomic::Ordering::{Acquire, Release};
33
use crate::sys::futex::{futex_wait, futex_wake};
44
use crate::time::Duration;
55

6-
const PARKED: i32 = -1;
7-
const EMPTY: i32 = 0;
8-
const NOTIFIED: i32 = 1;
6+
const PARKED: u32 = u32::MAX;
7+
const EMPTY: u32 = 0;
8+
const NOTIFIED: u32 = 1;
99

1010
pub struct Parker {
11-
state: AtomicI32,
11+
state: AtomicU32,
1212
}
1313

1414
// Notes about memory ordering:
@@ -34,7 +34,7 @@ pub struct Parker {
3434
impl Parker {
3535
#[inline]
3636
pub const fn new() -> Self {
37-
Parker { state: AtomicI32::new(EMPTY) }
37+
Parker { state: AtomicU32::new(EMPTY) }
3838
}
3939

4040
// Assumes this is only called by the thread that owns the Parker,

0 commit comments

Comments
 (0)