orbit_core/ring/
readiness.rs1#![cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
11
12use std::fmt;
13use std::io;
14use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
15use std::sync::Arc;
16use std::sync::atomic::{AtomicBool, Ordering};
17use std::thread::JoinHandle;
18
19use super::shm::ShmRing;
20
21pub struct RingEventFd {
32 fd: OwnedFd,
33 ring: Arc<ShmRing>,
34 stop: Arc<AtomicBool>,
35 driver: Option<JoinHandle<()>>,
36}
37
38impl RingEventFd {
39 pub(crate) fn new(ring: Arc<ShmRing>) -> io::Result<Self> {
40 let (fd, driver_fd) = local_notification_pair()?;
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 #[cfg(target_os = "macos")]
48 debug_assert_ne!(fd.as_raw_fd(), driver_fd.as_raw_fd());
49
50 let driver = std::thread::Builder::new()
51 .name(format!("orbit-ring-{}-eventfd", ring.kind()))
52 .spawn(move || {
53 while !driver_stop.load(Ordering::Acquire) {
54 let current = driver_ring
55 .notification_generation()
56 .load(Ordering::Acquire);
57 if current != observed {
58 observed = current;
59 if signal_event_fd(driver_fd.as_raw_fd()).is_err() {
60 break;
61 }
62 continue;
63 }
64 if crate::sync::wait_word(driver_ring.notification_generation(), observed)
65 .is_err()
66 {
67 break;
68 }
69 }
70 })?;
71
72 Ok(Self {
73 fd,
74 ring,
75 stop,
76 driver: Some(driver),
77 })
78 }
79
80 pub(crate) fn notify(ring: &ShmRing) -> io::Result<()> {
81 ring.notification_generation()
82 .fetch_add(1, Ordering::Release);
83 crate::sync::wake_word(ring.notification_generation())
84 }
85
86 pub fn drain(&self) -> io::Result<u64> {
91 let mut total = 0u64;
92 loop {
93 let mut value = 0u64;
94 let read = unsafe {
95 libc::read(
96 self.fd.as_raw_fd(),
97 (&mut value as *mut u64).cast(),
98 std::mem::size_of::<u64>(),
99 )
100 };
101 if read == std::mem::size_of::<u64>() as isize {
102 #[cfg(target_os = "macos")]
103 debug_assert_eq!(value, 1, "local pipe carries unit readiness tokens only");
104 total = total.saturating_add(value);
105 continue;
106 }
107 if read == 0 {
108 return Err(io::Error::new(
109 io::ErrorKind::UnexpectedEof,
110 "Orbit eventfd closed while draining",
111 ));
112 }
113 if read < 0 {
114 let error = io::Error::last_os_error();
115 match error.raw_os_error() {
116 Some(libc::EINTR) => continue,
117 Some(libc::EAGAIN) => return Ok(total),
118 _ => return Err(error),
119 }
120 }
121 return Err(io::Error::new(
122 io::ErrorKind::InvalidData,
123 "Orbit eventfd returned a partial counter",
124 ));
125 }
126 }
127}
128
129impl AsRawFd for RingEventFd {
130 fn as_raw_fd(&self) -> RawFd {
131 self.fd.as_raw_fd()
132 }
133}
134
135impl AsFd for RingEventFd {
136 fn as_fd(&self) -> BorrowedFd<'_> {
137 self.fd.as_fd()
138 }
139}
140
141impl fmt::Debug for RingEventFd {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 f.debug_struct("RingEventFd")
144 .field("fd", &self.fd.as_raw_fd())
145 .field("ring_kind", &self.ring.kind())
146 .finish_non_exhaustive()
147 }
148}
149
150impl Drop for RingEventFd {
151 fn drop(&mut self) {
152 self.stop.store(true, Ordering::Release);
153 self.ring
157 .notification_generation()
158 .fetch_add(1, Ordering::Release);
159 let _ = crate::sync::wake_word(self.ring.notification_generation());
160 if let Some(driver) = self.driver.take() {
161 let _ = driver.join();
162 }
163 }
164}
165
166fn signal_event_fd(fd: RawFd) -> io::Result<()> {
167 let value = 1u64;
168 loop {
169 let written = unsafe {
170 libc::write(
171 fd,
172 (&value as *const u64).cast(),
173 std::mem::size_of::<u64>(),
174 )
175 };
176 if written == std::mem::size_of::<u64>() as isize {
177 return Ok(());
178 }
179 if written < 0 {
180 let error = io::Error::last_os_error();
181 match error.raw_os_error() {
182 Some(libc::EINTR) => continue,
183 Some(libc::EAGAIN) => return Ok(()),
186 _ => return Err(error),
187 }
188 }
189 return Err(io::Error::new(
190 io::ErrorKind::WriteZero,
191 "Orbit eventfd accepted a partial counter",
192 ));
193 }
194}
195
196#[cfg(any(target_os = "linux", target_os = "freebsd"))]
197fn local_notification_pair() -> io::Result<(OwnedFd, OwnedFd)> {
198 let raw = unsafe { libc::eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK) };
199 if raw < 0 {
200 return Err(io::Error::last_os_error());
201 }
202 let fd = unsafe { OwnedFd::from_raw_fd(raw) };
203 let driver = fd.try_clone()?;
204 Ok((fd, driver))
205}
206
207#[cfg(target_os = "macos")]
208fn local_notification_pair() -> io::Result<(OwnedFd, OwnedFd)> {
209 if crate::sync::macos::api().is_none() {
210 return Err(io::Error::new(
211 io::ErrorKind::Unsupported,
212 "Orbit native readiness requires macOS 14.4 or later",
213 ));
214 }
215 let mut raw = [-1; 2];
216 if unsafe { libc::pipe(raw.as_mut_ptr()) } < 0 {
217 return Err(io::Error::last_os_error());
218 }
219 let read = unsafe { OwnedFd::from_raw_fd(raw[0]) };
222 let write = unsafe { OwnedFd::from_raw_fd(raw[1]) };
223 for fd in [&read, &write] {
224 if unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC) } < 0
225 || unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK) } < 0
226 {
227 return Err(io::Error::last_os_error());
228 }
229 }
230 Ok((read, write))
231}
232
233#[cfg(all(test, target_os = "macos"))]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn full_local_pipe_coalesces_and_rearms() {
239 let (read, write) = local_notification_pair().unwrap();
240 for fd in [&read, &write] {
241 assert_ne!(
242 unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_GETFL) } & libc::O_NONBLOCK,
243 0
244 );
245 assert_ne!(
246 unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_GETFD) } & libc::FD_CLOEXEC,
247 0
248 );
249 }
250 let token = 1u64;
252 loop {
253 let n = unsafe { libc::write(write.as_raw_fd(), (&token as *const u64).cast(), 8) };
254 if n < 0 {
255 assert_eq!(
256 io::Error::last_os_error().raw_os_error(),
257 Some(libc::EAGAIN)
258 );
259 break;
260 }
261 assert_eq!(n, 8);
262 }
263 signal_event_fd(write.as_raw_fd()).unwrap();
264 let mut buffer = [0u64; 128];
265 loop {
266 let n = unsafe {
267 libc::read(
268 read.as_raw_fd(),
269 buffer.as_mut_ptr().cast(),
270 size_of_val(&buffer),
271 )
272 };
273 if n < 0 {
274 assert_eq!(
275 io::Error::last_os_error().raw_os_error(),
276 Some(libc::EAGAIN)
277 );
278 break;
279 }
280 assert!(n > 0);
281 }
282 signal_event_fd(write.as_raw_fd()).unwrap();
283 let mut value = 0u64;
284 assert_eq!(
285 unsafe { libc::read(read.as_raw_fd(), (&mut value as *mut u64).cast(), 8) },
286 8
287 );
288 assert_eq!(value, 1);
289 }
290}