orbit_core/ring/
readiness.rs1#![cfg(any(target_os = "linux", target_os = "freebsd"))]
10
11use std::fmt;
12use std::io;
13use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
14use std::sync::Arc;
15use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
16use std::thread::JoinHandle;
17
18use super::shm::ShmRing;
19
20pub struct RingEventFd {
27 fd: OwnedFd,
28 ring: Arc<ShmRing>,
29 stop: Arc<AtomicBool>,
30 driver: Option<JoinHandle<()>>,
31}
32
33impl RingEventFd {
34 pub(crate) fn new(ring: Arc<ShmRing>) -> io::Result<Self> {
35 let raw_fd = unsafe { libc::eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK) };
36 if raw_fd < 0 {
37 return Err(io::Error::last_os_error());
38 }
39 let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
40 let driver_fd = fd.try_clone()?;
41 let stop = Arc::new(AtomicBool::new(false));
42 let driver_stop = stop.clone();
43 let driver_ring = ring.clone();
44 let mut observed = ring.notification_generation().load(Ordering::Acquire);
45
46 let driver = std::thread::Builder::new()
47 .name(format!("orbit-ring-{}-eventfd", ring.kind()))
48 .spawn(move || {
49 while !driver_stop.load(Ordering::Acquire) {
50 let current = driver_ring
51 .notification_generation()
52 .load(Ordering::Acquire);
53 if current != observed {
54 observed = current;
55 if signal_event_fd(driver_fd.as_raw_fd()).is_err() {
56 break;
57 }
58 continue;
59 }
60
61 if wait_for_generation(driver_ring.notification_generation(), observed).is_err()
62 {
63 break;
64 }
65 }
66 })?;
67
68 Ok(Self {
69 fd,
70 ring,
71 stop,
72 driver: Some(driver),
73 })
74 }
75
76 pub(crate) fn notify(ring: &ShmRing) -> io::Result<()> {
77 ring.notification_generation()
78 .fetch_add(1, Ordering::Release);
79 wake_all_generation_waiters(ring.notification_generation())
80 }
81
82 pub fn drain(&self) -> io::Result<u64> {
87 let mut total = 0u64;
88 loop {
89 let mut value = 0u64;
90 let read = unsafe {
91 libc::read(
92 self.fd.as_raw_fd(),
93 (&mut value as *mut u64).cast(),
94 std::mem::size_of::<u64>(),
95 )
96 };
97 if read == std::mem::size_of::<u64>() as isize {
98 total = total.saturating_add(value);
99 continue;
100 }
101 if read == 0 {
102 return Err(io::Error::new(
103 io::ErrorKind::UnexpectedEof,
104 "Orbit eventfd closed while draining",
105 ));
106 }
107 if read < 0 {
108 let error = io::Error::last_os_error();
109 match error.raw_os_error() {
110 Some(libc::EINTR) => continue,
111 Some(libc::EAGAIN) => return Ok(total),
112 _ => return Err(error),
113 }
114 }
115 return Err(io::Error::new(
116 io::ErrorKind::InvalidData,
117 "Orbit eventfd returned a partial counter",
118 ));
119 }
120 }
121}
122
123impl AsRawFd for RingEventFd {
124 fn as_raw_fd(&self) -> RawFd {
125 self.fd.as_raw_fd()
126 }
127}
128
129impl AsFd for RingEventFd {
130 fn as_fd(&self) -> BorrowedFd<'_> {
131 self.fd.as_fd()
132 }
133}
134
135impl fmt::Debug for RingEventFd {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.debug_struct("RingEventFd")
138 .field("fd", &self.fd.as_raw_fd())
139 .field("ring_kind", &self.ring.kind())
140 .finish_non_exhaustive()
141 }
142}
143
144impl Drop for RingEventFd {
145 fn drop(&mut self) {
146 self.stop.store(true, Ordering::Release);
147 self.ring
151 .notification_generation()
152 .fetch_add(1, Ordering::Release);
153 let _ = wake_all_generation_waiters(self.ring.notification_generation());
154 if let Some(driver) = self.driver.take() {
155 let _ = driver.join();
156 }
157 }
158}
159
160#[cfg(target_os = "linux")]
161fn wait_for_generation(word: &AtomicU32, expected: u32) -> io::Result<()> {
162 let result = unsafe {
163 libc::syscall(
164 libc::SYS_futex,
165 word.as_ptr(),
166 libc::FUTEX_WAIT,
167 expected,
168 std::ptr::null::<libc::timespec>(),
169 std::ptr::null::<u32>(),
170 0,
171 )
172 };
173 if result == 0 {
174 return Ok(());
175 }
176
177 let error = io::Error::last_os_error();
178 match error.raw_os_error() {
179 Some(libc::EAGAIN) | Some(libc::EINTR) => Ok(()),
182 _ => Err(error),
183 }
184}
185
186#[cfg(target_os = "linux")]
187fn wake_all_generation_waiters(word: &AtomicU32) -> io::Result<()> {
188 let result = unsafe {
189 libc::syscall(
190 libc::SYS_futex,
191 word.as_ptr(),
192 libc::FUTEX_WAKE,
193 i32::MAX,
194 std::ptr::null::<libc::timespec>(),
195 std::ptr::null::<u32>(),
196 0,
197 )
198 };
199 if result >= 0 {
200 Ok(())
201 } else {
202 Err(io::Error::last_os_error())
203 }
204}
205
206#[cfg(target_os = "freebsd")]
207fn wait_for_generation(word: &AtomicU32, expected: u32) -> io::Result<()> {
208 let result = unsafe {
209 libc::_umtx_op(
210 word.as_ptr().cast(),
211 libc::UMTX_OP_WAIT_UINT,
212 expected as libc::c_ulong,
213 std::ptr::null_mut(),
214 std::ptr::null_mut(),
215 )
216 };
217 if result == 0 {
218 return Ok(());
219 }
220
221 let error = io::Error::last_os_error();
222 match error.raw_os_error() {
223 Some(libc::EINTR) => Ok(()),
226 _ => Err(error),
227 }
228}
229
230#[cfg(target_os = "freebsd")]
231fn wake_all_generation_waiters(word: &AtomicU32) -> io::Result<()> {
232 let result = unsafe {
233 libc::_umtx_op(
234 word.as_ptr().cast(),
235 libc::UMTX_OP_WAKE,
236 i32::MAX as libc::c_ulong,
237 std::ptr::null_mut(),
238 std::ptr::null_mut(),
239 )
240 };
241 if result == 0 {
242 Ok(())
243 } else {
244 Err(io::Error::last_os_error())
245 }
246}
247
248fn signal_event_fd(fd: RawFd) -> io::Result<()> {
249 let value = 1u64;
250 loop {
251 let written = unsafe {
252 libc::write(
253 fd,
254 (&value as *const u64).cast(),
255 std::mem::size_of::<u64>(),
256 )
257 };
258 if written == std::mem::size_of::<u64>() as isize {
259 return Ok(());
260 }
261 if written < 0 {
262 let error = io::Error::last_os_error();
263 match error.raw_os_error() {
264 Some(libc::EINTR) => continue,
265 Some(libc::EAGAIN) => return Ok(()),
268 _ => return Err(error),
269 }
270 }
271 return Err(io::Error::new(
272 io::ErrorKind::WriteZero,
273 "Orbit eventfd accepted a partial counter",
274 ));
275 }
276}