zx/
futex.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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/// A safe wrapper around zx_futex_t, generally called as part of higher-level synchronization
13/// primitives.
14#[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    /// See https://fuchsia.dev/reference/syscalls/futex_wake.
38    #[inline]
39    pub fn wake(&self, wake_count: u32) {
40        // SAFETY: Arguments for this system call do not have any liveness or validity requirements.
41        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    /// Wakes the maximum number of waiters possible.
46    ///
47    /// See https://fuchsia.dev/reference/syscalls/futex_wake.
48    #[inline]
49    pub fn wake_all(&self) {
50        self.wake(u32::MAX)
51    }
52
53    /// See https://fuchsia.dev/reference/syscalls/futex_wake_single_owner.
54    #[inline]
55    pub fn wake_single_owner(&self) {
56        // SAFETY: Arguments for this system call do not have any liveness or validity requirements.
57        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    /// See https://fuchsia.dev/reference/syscalls/futex_wake_handle_close_thread_exit.
62    pub fn wake_handle_close_thread_exit(
63        &self,
64        wake_count: u32,
65        new_value: i32,
66        to_close: Handle,
67    ) -> ! {
68        // SAFETY: Arguments for this system call do not have any liveness or validity requirements.
69        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    /// See https://fuchsia.dev/reference/syscalls/futex_requeue.
81    #[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        // SAFETY: Arguments for this system call do not have any liveness or validity requirements.
91        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    /// See https://fuchsia.dev/reference/syscalls/futex_requeue_single_owner.
104    #[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        // SAFETY: Arguments for this system call do not have any liveness or validity requirements.
113        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    /// See https://fuchsia.dev/reference/syscalls/futex_wait.
125    #[inline]
126    pub fn wait(
127        &self,
128        current_value: i32,
129        new_owner: Option<&Thread>,
130        deadline: MonotonicInstant,
131    ) -> Result<(), Status> {
132        // SAFETY: Arguments for this system call do not have any liveness or validity requirements.
133        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    /// See https://fuchsia.dev/reference/syscalls/futex_get_owner.
144    pub fn get_owner(&self) -> Option<Koid> {
145        let mut koid = ZX_KOID_INVALID;
146
147        // SAFETY: Arguments for this system call do not have any liveness or validity requirements.
148        // `&mut koid` is valid for the entire duration of the call.
149        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        // SAFETY: converting between versions of the same types, the handle is valid
168        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}