1mod errors;
17mod scope;
18mod sys;
19mod timeout;
20
21pub mod op;
22
23use op::OpAndCmp;
24use std::marker::PhantomData;
25use std::sync::atomic::AtomicU32;
26use std::time::Duration;
27use sys::{Error, FutexCall};
28use timeout::as_timespec;
29
30pub use errors::*;
31pub use scope::{Private, Scope, Shared};
32pub use timeout::Timeout;
33
34#[repr(transparent)]
40pub struct Futex<Scope> {
41 pub value: AtomicU32,
42 phantom: PhantomData<Scope>,
43}
44
45#[repr(transparent)]
59pub struct PiFutex<Scope> {
60 pub value: AtomicU32,
61 phantom: PhantomData<Scope>,
62}
63
64pub trait AsFutex<S> {
70 fn as_futex(&self) -> &Futex<S>;
71 fn as_pi_futex(&self) -> &PiFutex<S>;
72}
73
74impl<S> AsFutex<S> for AtomicU32 {
75 #[must_use]
76 #[inline]
77 fn as_futex(&self) -> &Futex<S> {
78 unsafe { std::mem::transmute(self) }
79 }
80 #[inline]
81 #[must_use]
82 fn as_pi_futex(&self) -> &PiFutex<S> {
83 unsafe { std::mem::transmute(self) }
84 }
85}
86
87impl<S> Futex<S> {
88 #[inline]
90 pub const fn new(value: u32) -> Self {
91 Self {
92 value: AtomicU32::new(value),
93 phantom: PhantomData,
94 }
95 }
96}
97
98impl<S> PiFutex<S> {
99 #[inline]
101 pub const fn new(value: u32) -> Self {
102 Self {
103 value: AtomicU32::new(value),
104 phantom: PhantomData,
105 }
106 }
107
108 pub const WAITERS: u32 = 0x8000_0000;
110
111 pub const OWNER_DIED: u32 = 0x4000_0000;
113
114 pub const TID_MASK: u32 = 0x3fffffff;
116}
117
118impl<S> Default for Futex<S> {
119 fn default() -> Self {
120 Self::new(0)
121 }
122}
123
124impl<S> Default for PiFutex<S> {
125 fn default() -> Self {
126 Self::new(0)
127 }
128}
129
130impl<S: Scope> Futex<S> {
131 #[inline]
136 pub fn wait(&self, expected_value: u32) -> Result<(), WaitError> {
137 let r = unsafe {
138 FutexCall::new()
139 .futex_op(libc::FUTEX_WAIT + S::futex_flag())
140 .uaddr(&self.value)
141 .val(expected_value)
142 .call()
143 };
144 match r {
145 Err(Error(libc::EAGAIN)) => Err(WaitError::WrongValue),
146 Err(Error(libc::EINTR)) => Err(WaitError::Interrupted),
147 Err(e) => e.panic("FUTEX_WAIT"),
148 Ok(_) => Ok(()),
149 }
150 }
151
152 #[inline]
160 pub fn wait_for(&self, expected_value: u32, timeout: Duration) -> Result<(), TimedWaitError> {
161 let timeout = as_timespec(timeout);
162 let r = unsafe {
163 FutexCall::new()
164 .futex_op(libc::FUTEX_WAIT + S::futex_flag())
165 .uaddr(&self.value)
166 .val(expected_value)
167 .timeout(&timeout)
168 .call()
169 };
170 match r {
171 Err(Error(libc::EAGAIN)) => Err(TimedWaitError::WrongValue),
172 Err(Error(libc::EINTR)) => Err(TimedWaitError::Interrupted),
173 Err(Error(libc::ETIMEDOUT)) => Err(TimedWaitError::TimedOut),
174 Err(e) => e.panic("FUTEX_WAIT"),
175 Ok(_) => Ok(()),
176 }
177 }
178
179 #[inline]
183 pub fn wake(&self, n: i32) -> i32 {
184 let r = unsafe {
185 FutexCall::new()
186 .futex_op(libc::FUTEX_WAKE + S::futex_flag())
187 .uaddr(&self.value)
188 .val(n as u32)
189 .call()
190 };
191 match r {
192 Err(e) => e.panic("FUTEX_WAKE"),
193 Ok(v) => v,
194 }
195 }
196
197 #[inline]
201 pub fn requeue(&self, n_wake: i32, to: &Futex<S>, n_requeue: i32) -> i32 {
202 let r = unsafe {
203 FutexCall::new()
204 .futex_op(libc::FUTEX_REQUEUE + S::futex_flag())
205 .uaddr(&self.value)
206 .uaddr2(&to.value)
207 .val(n_wake as u32)
208 .val2(n_requeue as u32)
209 .call()
210 };
211 match r {
212 Err(e) => e.panic("FUTEX_REQUEUE"),
213 Ok(v) => v,
214 }
215 }
216
217 #[inline]
224 pub fn cmp_requeue(
225 &self,
226 expected_value: u32,
227 n_wake: i32,
228 to: &Futex<S>,
229 n_requeue: i32,
230 ) -> Result<i32, WrongValueError> {
231 let r = unsafe {
232 FutexCall::new()
233 .futex_op(libc::FUTEX_CMP_REQUEUE + S::futex_flag())
234 .uaddr(&self.value)
235 .uaddr2(&to.value)
236 .val(n_wake as u32)
237 .val2(n_requeue as u32)
238 .val3(expected_value)
239 .call()
240 };
241 match r {
242 Err(Error(libc::EAGAIN)) => Err(WrongValueError::WrongValue),
243 Err(e) => e.panic("FUTEX_CMP_REQUEUE"),
244 Ok(v) => Ok(v),
245 }
246 }
247
248 #[inline]
256 pub fn wait_bitset(&self, expected_value: u32, bitset: u32) -> Result<(), WaitError> {
257 let r = unsafe {
258 FutexCall::new()
259 .uaddr(&self.value)
260 .futex_op(libc::FUTEX_WAIT_BITSET + S::futex_flag())
261 .val(expected_value)
262 .val3(bitset)
263 .call()
264 };
265 match r {
266 Err(Error(libc::EAGAIN)) => Err(WaitError::WrongValue),
267 Err(Error(libc::EINTR)) => Err(WaitError::Interrupted),
268 Err(e) => e.panic("FUTEX_WAIT_BITSET"),
269 Ok(_) => Ok(()),
270 }
271 }
272
273 #[inline]
281 pub fn wait_bitset_until(
282 &self,
283 expected_value: u32,
284 bitset: u32,
285 timeout: impl Timeout,
286 ) -> Result<(), TimedWaitError> {
287 let timeout = timeout.as_timespec();
288 let r = unsafe {
289 FutexCall::new()
290 .uaddr(&self.value)
291 .futex_op(libc::FUTEX_WAIT_BITSET + timeout.0 + S::futex_flag())
292 .val(expected_value)
293 .val3(bitset)
294 .timeout(&timeout.1)
295 .call()
296 };
297 match r {
298 Err(Error(libc::EAGAIN)) => Err(TimedWaitError::WrongValue),
299 Err(Error(libc::EINTR)) => Err(TimedWaitError::Interrupted),
300 Err(Error(libc::ETIMEDOUT)) => Err(TimedWaitError::TimedOut),
301 Err(e) => e.panic("FUTEX_WAIT_BITSET"),
302 Ok(_) => Ok(()),
303 }
304 }
305
306 #[inline]
315 pub fn wake_bitset(&self, n: i32, bitset: u32) -> i32 {
316 let r = unsafe {
317 FutexCall::new()
318 .futex_op(libc::FUTEX_WAKE_BITSET + S::futex_flag())
319 .uaddr(&self.value)
320 .val(n as u32)
321 .val3(bitset)
322 .call()
323 };
324 match r {
325 Err(e) => e.panic("FUTEX_WAKE_BITSET"),
326 Ok(v) => v,
327 }
328 }
329
330 #[inline]
338 pub fn wake_op(&self, n: i32, second: &Futex<S>, op: OpAndCmp, n2: i32) -> i32 {
339 let r = unsafe {
340 FutexCall::new()
341 .futex_op(libc::FUTEX_WAKE_OP + S::futex_flag())
342 .uaddr(&self.value)
343 .uaddr2(&second.value)
344 .val(n as u32)
345 .val2(n2 as u32)
346 .val3(op.raw_bits())
347 .call()
348 };
349 match r {
350 Err(e) => e.panic("FUTEX_WAKE_OP"),
351 Ok(v) => v,
352 }
353 }
354
355 #[inline]
365 pub fn cmp_requeue_pi(
366 &self,
367 expected_value: i32,
368 to: &PiFutex<S>,
369 n_requeue: i32,
370 ) -> Result<i32, TryAgainError> {
371 let r = unsafe {
372 FutexCall::new()
373 .futex_op(libc::FUTEX_CMP_REQUEUE_PI + S::futex_flag())
374 .uaddr(&self.value)
375 .uaddr2(&to.value)
376 .val(1)
377 .val2(n_requeue as u32)
378 .val3(expected_value as u32)
379 .call()
380 };
381 match r {
382 Err(Error(libc::EAGAIN)) => Err(TryAgainError::TryAgain),
383 Err(e) => e.panic("FUTEX_CMP_REQUEUE_PI"),
384 Ok(v) => Ok(v),
385 }
386 }
387
388 #[inline]
396 pub fn wait_requeue_pi(
397 &self,
398 expected_value: u32,
399 second: &PiFutex<S>,
400 ) -> Result<(), RequeuePiError> {
401 let r = unsafe {
402 FutexCall::new()
403 .futex_op(libc::FUTEX_WAIT_REQUEUE_PI + S::futex_flag())
404 .uaddr(&self.value)
405 .uaddr2(&second.value)
406 .val(expected_value)
407 .call()
408 };
409 match r {
410 Err(Error(libc::EAGAIN)) => Err(RequeuePiError::TryAgain),
411 Err(e) => e.panic("FUTEX_WAIT_REQUEUE_PI"),
412 Ok(_) => Ok(()),
413 }
414 }
415
416 #[inline]
424 pub fn wait_requeue_pi_until(
425 &self,
426 expected_value: u32,
427 second: &PiFutex<S>,
428 timeout: impl Timeout,
429 ) -> Result<(), TimedRequeuePiError> {
430 let timeout = timeout.as_timespec();
431 let r = unsafe {
432 FutexCall::new()
433 .futex_op(libc::FUTEX_WAIT_REQUEUE_PI + timeout.0 + S::futex_flag())
434 .uaddr(&self.value)
435 .uaddr2(&second.value)
436 .val(expected_value)
437 .timeout(&timeout.1)
438 .call()
439 };
440 match r {
441 Err(Error(libc::EAGAIN)) => Err(TimedRequeuePiError::TryAgain),
442 Err(Error(libc::ETIMEDOUT)) => Err(TimedRequeuePiError::TimedOut),
443 Err(e) => e.panic("FUTEX_WAIT_REQUEUE_PI"),
444 Ok(_) => Ok(()),
445 }
446 }
447}
448
449impl<S: Scope> PiFutex<S> {
450 #[inline]
452 pub fn lock_pi(&self) -> Result<(), TryAgainError> {
453 let r = unsafe {
454 FutexCall::new()
455 .futex_op(libc::FUTEX_LOCK_PI + S::futex_flag())
456 .uaddr(&self.value)
457 .call()
458 };
459 match r {
460 Err(Error(libc::EAGAIN)) => Err(TryAgainError::TryAgain),
461 Err(e) => e.panic("FUTEX_LOCK_PI"),
462 Ok(_) => Ok(()),
463 }
464 }
465
466 #[inline]
468 pub fn lock_pi_until(&self, timeout: impl Timeout) -> Result<(), TimedLockError> {
469 let (clock, timespec) = timeout.as_timespec();
470 let op = if clock == libc::FUTEX_CLOCK_REALTIME {
471 libc::FUTEX_LOCK_PI
472 } else {
473 libc::FUTEX_LOCK_PI2
475 };
476 let r = unsafe {
477 FutexCall::new()
478 .futex_op(op + S::futex_flag())
479 .uaddr(&self.value)
480 .timeout(×pec)
481 .call()
482 };
483 match r {
484 Err(Error(libc::EAGAIN)) => Err(TimedLockError::TryAgain),
485 Err(Error(libc::ETIMEDOUT)) => Err(TimedLockError::TimedOut),
486 Err(e) if op == libc::FUTEX_LOCK_PI2 => e.panic("FUTEX_LOCK_PI2"),
487 Err(e) => e.panic("FUTEX_LOCK_PI"),
488 Ok(_) => Ok(()),
489 }
490 }
491
492 #[inline]
494 pub fn trylock_pi(&self) -> Result<(), TryAgainError> {
495 let r = unsafe {
496 FutexCall::new()
497 .futex_op(libc::FUTEX_TRYLOCK_PI + S::futex_flag())
498 .uaddr(&self.value)
499 .call()
500 };
501 match r {
502 Err(Error(libc::EAGAIN)) => Err(TryAgainError::TryAgain),
503 Err(e) => e.panic("FUTEX_LOCK_PI"),
504 Ok(_) => Ok(()),
505 }
506 }
507
508 #[inline]
510 pub fn unlock_pi(&self) {
511 let r = unsafe {
512 FutexCall::new()
513 .futex_op(libc::FUTEX_UNLOCK_PI + S::futex_flag())
514 .uaddr(&self.value)
515 .call()
516 };
517 if let Err(e) = r {
518 e.panic("FUTEX_UNLOCK_PI");
519 }
520 }
521}
522
523impl<S> std::fmt::Debug for Futex<S> {
524 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
525 f.debug_struct("Futex")
526 .field("scope", &std::any::type_name::<S>())
527 .field("value", &self.value)
528 .finish()
529 }
530}
531
532impl<S> std::fmt::Debug for PiFutex<S> {
533 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
534 f.debug_struct("PiFutex")
535 .field("scope", &std::any::type_name::<S>())
536 .field("value", &self.value)
537 .finish()
538 }
539}