Auto merge of #96510 - m-ou-se:futex-bsd, r=Amanieu · rust-lang/rust@7f9e013 (original) (raw)
1
1
`#![cfg(any(
`
2
2
` target_os = "linux",
`
3
3
` target_os = "android",
`
4
``
`-
all(target_os = "emscripten", target_feature = "atomics")
`
``
4
`+
all(target_os = "emscripten", target_feature = "atomics"),
`
``
5
`+
target_os = "freebsd",
`
``
6
`+
target_os = "openbsd",
`
``
7
`+
target_os = "dragonfly",
`
5
8
`))]
`
6
9
``
7
10
`use crate::sync::atomic::AtomicU32;
`
`@@ -12,7 +15,7 @@ use crate::time::Duration;
`
12
15
`/// Returns directly if the futex doesn't hold the expected value.
`
13
16
`///
`
14
17
`/// Returns false on timeout, and true in all other cases.
`
15
``
`-
#[cfg(any(target_os = "linux", target_os = "android"))]
`
``
18
`+
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
`
16
19
`pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool {
`
17
20
`use super::time::Timespec;
`
18
21
`use crate::ptr::null;
`
`@@ -30,18 +33,43 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -
`
30
33
`return true;
`
31
34
`}
`
32
35
``
33
``
`-
// Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
`
34
``
`-
// absolute time rather than a relative time.
`
35
36
`let r = unsafe {
`
36
``
`-
libc::syscall(
`
37
``
`-
libc::SYS_futex,
`
38
``
`-
futex as *const AtomicU32,
`
39
``
`-
libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
`
40
``
`-
expected,
`
41
``
`-
timespec.as_ref().map_or(null(), |t| &t.t as *const libc::timespec),
`
42
``
`-
null::(), // This argument is unused for FUTEX_WAIT_BITSET.
`
43
``
`-
!0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT.
`
44
``
`-
)
`
``
37
`+
cfg_if::cfg_if! {
`
``
38
`+
if #[cfg(target_os = "freebsd")] {
`
``
39
`+
// FreeBSD doesn't have futex(), but it has
`
``
40
`+
// _umtx_op(UMTX_OP_WAIT_UINT_PRIVATE), which is nearly
`
``
41
`+
// identical. It supports absolute timeouts through a flag
`
``
42
`+
// in the _umtx_time struct.
`
``
43
`+
let umtx_timeout = timespec.map(|t| libc::_umtx_time {
`
``
44
`+
_timeout: t.t,
`
``
45
`+
_flags: libc::UMTX_ABSTIME,
`
``
46
`+
_clockid: libc::CLOCK_MONOTONIC as u32,
`
``
47
`+
});
`
``
48
`+
let umtx_timeout_ptr = umtx_timeout.as_ref().map_or(null(), |t| t as *const _);
`
``
49
`+
let umtx_timeout_size = umtx_timeout.as_ref().map_or(0, |t| crate::mem::size_of_val(t));
`
``
50
`+
libc::_umtx_op(
`
``
51
`+
futex as *const AtomicU32 as *mut _,
`
``
52
`+
libc::UMTX_OP_WAIT_UINT_PRIVATE,
`
``
53
`+
expected as libc::c_ulong,
`
``
54
`+
crate::ptr::invalid_mut(umtx_timeout_size),
`
``
55
`+
umtx_timeout_ptr as *mut _,
`
``
56
`+
)
`
``
57
`+
} else if #[cfg(any(target_os = "linux", target_os = "android"))] {
`
``
58
`+
// Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
`
``
59
`+
// absolute time rather than a relative time.
`
``
60
`+
libc::syscall(
`
``
61
`+
libc::SYS_futex,
`
``
62
`+
futex as *const AtomicU32,
`
``
63
`+
libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
`
``
64
`+
expected,
`
``
65
`+
timespec.as_ref().map_or(null(), |t| &t.t as *const libc::timespec),
`
``
66
`+
null::(), // This argument is unused for FUTEX_WAIT_BITSET.
`
``
67
`+
!0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT.
`
``
68
`+
)
`
``
69
`+
} else {
`
``
70
`+
compile_error!("unknown target_os");
`
``
71
`+
}
`
``
72
`+
}
`
45
73
`};
`
46
74
``
47
75
`match (r < 0).then(super::os::errno) {
`
`@@ -56,31 +84,133 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -
`
56
84
`///
`
57
85
`/// Returns true if this actually woke up such a thread,
`
58
86
`/// or false if no thread was waiting on this futex.
`
``
87
`+
///
`
``
88
`+
/// On some platforms, this always returns false.
`
``
89
`+
#[cfg(any(target_os = "linux", target_os = "android"))]
`
``
90
`+
pub fn futex_wake(futex: &AtomicU32) -> bool {
`
``
91
`+
let ptr = futex as *const AtomicU32;
`
``
92
`+
let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
`
``
93
`+
unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) > 0 }
`
``
94
`+
}
`
``
95
+
``
96
`+
/// Wake up all threads that are waiting on futex_wait on this futex.
`
59
97
`#[cfg(any(target_os = "linux", target_os = "android"))]
`
``
98
`+
pub fn futex_wake_all(futex: &AtomicU32) {
`
``
99
`+
let ptr = futex as *const AtomicU32;
`
``
100
`+
let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
`
``
101
`+
unsafe {
`
``
102
`+
libc::syscall(libc::SYS_futex, ptr, op, i32::MAX);
`
``
103
`+
}
`
``
104
`+
}
`
``
105
+
``
106
`+
// FreeBSD doesn't tell us how many threads are woken up, so this always returns false.
`
``
107
`+
#[cfg(target_os = "freebsd")]
`
60
108
`pub fn futex_wake(futex: &AtomicU32) -> bool {
`
``
109
`+
use crate::ptr::null_mut;
`
61
110
`unsafe {
`
62
``
`-
libc::syscall(
`
63
``
`-
libc::SYS_futex,
`
64
``
`-
futex as *const AtomicU32,
`
65
``
`-
libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
`
``
111
`+
libc::_umtx_op(
`
``
112
`+
futex as *const AtomicU32 as *mut _,
`
``
113
`+
libc::UMTX_OP_WAKE_PRIVATE,
`
66
114
`1,
`
67
``
`-
) > 0
`
``
115
`+
null_mut(),
`
``
116
`+
null_mut(),
`
``
117
`+
)
`
``
118
`+
};
`
``
119
`+
false
`
``
120
`+
}
`
``
121
+
``
122
`+
#[cfg(target_os = "freebsd")]
`
``
123
`+
pub fn futex_wake_all(futex: &AtomicU32) {
`
``
124
`+
use crate::ptr::null_mut;
`
``
125
`+
unsafe {
`
``
126
`+
libc::_umtx_op(
`
``
127
`+
futex as *const AtomicU32 as *mut _,
`
``
128
`+
libc::UMTX_OP_WAKE_PRIVATE,
`
``
129
`+
i32::MAX as libc::c_ulong,
`
``
130
`+
null_mut(),
`
``
131
`+
null_mut(),
`
``
132
`+
)
`
``
133
`+
};
`
``
134
`+
}
`
``
135
+
``
136
`+
#[cfg(target_os = "openbsd")]
`
``
137
`+
pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool {
`
``
138
`+
use crate::convert::TryInto;
`
``
139
`+
use crate::ptr::{null, null_mut};
`
``
140
`+
let timespec = timeout.and_then(|d| {
`
``
141
`+
Some(libc::timespec {
`
``
142
`+
// Sleep forever if the timeout is longer than fits in a timespec.
`
``
143
`+
tv_sec: d.as_secs().try_into().ok()?,
`
``
144
`+
// This conversion never truncates, as subsec_nanos is always <1e9.
`
``
145
`+
tv_nsec: d.subsec_nanos() as _,
`
``
146
`+
})
`
``
147
`+
});
`
``
148
+
``
149
`+
let r = unsafe {
`
``
150
`+
libc::futex(
`
``
151
`+
futex as *const AtomicU32 as *mut u32,
`
``
152
`+
libc::FUTEX_WAIT,
`
``
153
`+
expected as i32,
`
``
154
`+
timespec.as_ref().map_or(null(), |t| t as *const libc::timespec),
`
``
155
`+
null_mut(),
`
``
156
`+
)
`
``
157
`+
};
`
``
158
+
``
159
`+
r == 0 || super::os::errno() != libc::ETIMEDOUT
`
``
160
`+
}
`
``
161
+
``
162
`+
#[cfg(target_os = "openbsd")]
`
``
163
`+
pub fn futex_wake(futex: &AtomicU32) -> bool {
`
``
164
`+
use crate::ptr::{null, null_mut};
`
``
165
`+
unsafe {
`
``
166
`+
libc::futex(futex as *const AtomicU32 as *mut u32, libc::FUTEX_WAKE, 1, null(), null_mut())
`
``
167
`+
0
`
68
168
`}
`
69
169
`}
`
70
170
``
71
``
`-
/// Wake up all threads that are waiting on futex_wait on this futex.
`
72
``
`-
#[cfg(any(target_os = "linux", target_os = "android"))]
`
``
171
`+
#[cfg(target_os = "openbsd")]
`
73
172
`pub fn futex_wake_all(futex: &AtomicU32) {
`
``
173
`+
use crate::ptr::{null, null_mut};
`
74
174
`unsafe {
`
75
``
`-
libc::syscall(
`
76
``
`-
libc::SYS_futex,
`
77
``
`-
futex as *const AtomicU32,
`
78
``
`-
libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
`
``
175
`+
libc::futex(
`
``
176
`+
futex as *const AtomicU32 as *mut u32,
`
``
177
`+
libc::FUTEX_WAKE,
`
79
178
` i32::MAX,
`
``
179
`+
null(),
`
``
180
`+
null_mut(),
`
80
181
`);
`
81
182
`}
`
82
183
`}
`
83
184
``
``
185
`+
#[cfg(target_os = "dragonfly")]
`
``
186
`+
pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool {
`
``
187
`+
use crate::convert::TryFrom;
`
``
188
+
``
189
`+
// A timeout of 0 means infinite.
`
``
190
`+
// We round smaller timeouts up to 1 millisecond.
`
``
191
`+
// Overflows are rounded up to an infinite timeout.
`
``
192
`+
let timeout_ms =
`
``
193
`+
timeout.and_then(|d| Some(i32::try_from(d.as_millis()).ok()?.max(1))).unwrap_or(0);
`
``
194
+
``
195
`+
let r = unsafe {
`
``
196
`+
libc::umtx_sleep(futex as *const AtomicU32 as *const i32, expected as i32, timeout_ms)
`
``
197
`+
};
`
``
198
+
``
199
`+
r == 0 || super::os::errno() != libc::ETIMEDOUT
`
``
200
`+
}
`
``
201
+
``
202
`+
// DragonflyBSD doesn't tell us how many threads are woken up, so this always returns false.
`
``
203
`+
#[cfg(target_os = "dragonfly")]
`
``
204
`+
pub fn futex_wake(futex: &AtomicU32) -> bool {
`
``
205
`+
unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, 1) };
`
``
206
`+
false
`
``
207
`+
}
`
``
208
+
``
209
`+
#[cfg(target_os = "dragonfly")]
`
``
210
`+
pub fn futex_wake_all(futex: &AtomicU32) {
`
``
211
`+
unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, i32::MAX) };
`
``
212
`+
}
`
``
213
+
84
214
`#[cfg(target_os = "emscripten")]
`
85
215
`extern "C" {
`
86
216
`fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int;
`