1use crate::sys::{
6 zx_futex_get_owner, zx_futex_requeue, zx_futex_requeue_single_owner, zx_futex_t, zx_futex_wait,
7 zx_futex_wake, zx_futex_wake_handle_close_thread_exit, zx_futex_wake_single_owner,
8 ZX_HANDLE_INVALID, ZX_KOID_INVALID, ZX_OK,
9};
10use crate::{AsHandleRef, Handle, Koid, MonotonicInstant, Status, Thread};
11
12#[derive(Debug)]
15pub struct Futex {
16 inner: zx_futex_t,
17}
18
19impl std::ops::Deref for Futex {
20 type Target = zx_futex_t;
21 #[inline]
22 fn deref(&self) -> &Self::Target {
23 &self.inner
24 }
25}
26
27impl Futex {
28 #[inline]
29 pub const fn new(value: i32) -> Self {
30 Self { inner: zx_futex_t::new(value) }
31 }
32
33 fn value_ptr(&self) -> *const zx_futex_t {
34 std::ptr::addr_of!(self.inner)
35 }
36
37 #[inline]
39 pub fn wake(&self, wake_count: u32) {
40 let status = unsafe { zx_futex_wake(self.value_ptr(), wake_count) };
42 assert_eq!(status, ZX_OK, "only fails due to misaligned or unmapped pointers");
43 }
44
45 #[inline]
49 pub fn wake_all(&self) {
50 self.wake(u32::MAX)
51 }
52
53 #[inline]
55 pub fn wake_single_owner(&self) {
56 let status = unsafe { zx_futex_wake_single_owner(self.value_ptr()) };
58 assert_eq!(status, ZX_OK, "only fails due to misaligned or unampped pointers")
59 }
60
61 pub fn wake_handle_close_thread_exit(
63 &self,
64 wake_count: u32,
65 new_value: i32,
66 to_close: Handle,
67 ) -> ! {
68 unsafe {
70 zx_futex_wake_handle_close_thread_exit(
71 self.value_ptr(),
72 wake_count,
73 new_value,
74 to_close.raw_handle(),
75 );
76 }
77 unreachable!("zx_futex_wake_handle_close_thread_exit() does not return.");
78 }
79
80 #[inline]
82 pub fn requeue(
83 &self,
84 wake_count: u32,
85 current_value: i32,
86 requeue_to: &Self,
87 requeue_count: u32,
88 new_requeue_owner: Option<&Thread>,
89 ) -> Result<(), Status> {
90 Status::ok(unsafe {
92 zx_futex_requeue(
93 self.value_ptr(),
94 wake_count,
95 current_value,
96 requeue_to.value_ptr(),
97 requeue_count,
98 new_requeue_owner.map(|o| o.raw_handle()).unwrap_or(ZX_HANDLE_INVALID),
99 )
100 })
101 }
102
103 #[inline]
105 pub fn requeue_single_owner(
106 &self,
107 current_value: i32,
108 requeue_to: &Self,
109 requeue_count: u32,
110 new_requeue_owner: Option<&Thread>,
111 ) -> Result<(), Status> {
112 Status::ok(unsafe {
114 zx_futex_requeue_single_owner(
115 self.value_ptr(),
116 current_value,
117 requeue_to.value_ptr(),
118 requeue_count,
119 new_requeue_owner.map(|o| o.raw_handle()).unwrap_or(ZX_HANDLE_INVALID),
120 )
121 })
122 }
123
124 #[inline]
126 pub fn wait(
127 &self,
128 current_value: i32,
129 new_owner: Option<&Thread>,
130 deadline: MonotonicInstant,
131 ) -> Result<(), Status> {
132 Status::ok(unsafe {
134 zx_futex_wait(
135 self.value_ptr(),
136 current_value,
137 new_owner.map(|o| o.raw_handle()).unwrap_or(ZX_HANDLE_INVALID),
138 deadline.into_nanos(),
139 )
140 })
141 }
142
143 pub fn get_owner(&self) -> Option<Koid> {
145 let mut koid = ZX_KOID_INVALID;
146
147 Status::ok(unsafe { zx_futex_get_owner(self.value_ptr(), &mut koid) })
150 .expect("get_owner only fails due to misaligned or unmapped pointers");
151
152 if koid != ZX_KOID_INVALID {
153 Some(Koid::from_raw(koid))
154 } else {
155 None
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::{Duration, Unowned};
164
165 #[test]
166 fn basic_wait_wake() {
167 let main_thread =
169 unsafe { Unowned::from_raw_handle(fuchsia_runtime::thread_self().raw_handle()) };
170 let futex = Futex::new(0);
171 std::thread::scope(|s| {
172 s.spawn(|| futex.wait(0, Some(&*main_thread), MonotonicInstant::INFINITE).unwrap());
173 while futex.get_owner().is_none() {
174 std::thread::sleep(std::time::Duration::from_secs(1));
175 }
176 futex.wake_single_owner();
177 });
178 }
179
180 #[test]
181 fn cant_wait_with_wrong_value() {
182 let futex = Futex::new(0);
183 assert_eq!(futex.wait(5, None, MonotonicInstant::INFINITE).unwrap_err(), Status::BAD_STATE);
184 }
185
186 #[test]
187 fn wait_timed_out() {
188 assert_eq!(
189 Futex::new(0)
190 .wait(0, None, MonotonicInstant::after(Duration::from_seconds(1)))
191 .unwrap_err(),
192 Status::TIMED_OUT,
193 );
194 }
195}