1#![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, AtomicU32, 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 wait_for_generation(driver_ring.notification_generation(), observed).is_err()
65 {
66 break;
67 }
68 }
69 })?;
70
71 Ok(Self {
72 fd,
73 ring,
74 stop,
75 driver: Some(driver),
76 })
77 }
78
79 pub(crate) fn notify(ring: &ShmRing) -> io::Result<()> {
80 ring.notification_generation()
81 .fetch_add(1, Ordering::Release);
82 wake_all_generation_waiters(ring.notification_generation())
83 }
84
85 pub fn drain(&self) -> io::Result<u64> {
90 let mut total = 0u64;
91 loop {
92 let mut value = 0u64;
93 let read = unsafe {
94 libc::read(
95 self.fd.as_raw_fd(),
96 (&mut value as *mut u64).cast(),
97 std::mem::size_of::<u64>(),
98 )
99 };
100 if read == std::mem::size_of::<u64>() as isize {
101 #[cfg(target_os = "macos")]
102 debug_assert_eq!(value, 1, "local pipe carries unit readiness tokens only");
103 total = total.saturating_add(value);
104 continue;
105 }
106 if read == 0 {
107 return Err(io::Error::new(
108 io::ErrorKind::UnexpectedEof,
109 "Orbit eventfd closed while draining",
110 ));
111 }
112 if read < 0 {
113 let error = io::Error::last_os_error();
114 match error.raw_os_error() {
115 Some(libc::EINTR) => continue,
116 Some(libc::EAGAIN) => return Ok(total),
117 _ => return Err(error),
118 }
119 }
120 return Err(io::Error::new(
121 io::ErrorKind::InvalidData,
122 "Orbit eventfd returned a partial counter",
123 ));
124 }
125 }
126}
127
128impl AsRawFd for RingEventFd {
129 fn as_raw_fd(&self) -> RawFd {
130 self.fd.as_raw_fd()
131 }
132}
133
134impl AsFd for RingEventFd {
135 fn as_fd(&self) -> BorrowedFd<'_> {
136 self.fd.as_fd()
137 }
138}
139
140impl fmt::Debug for RingEventFd {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 f.debug_struct("RingEventFd")
143 .field("fd", &self.fd.as_raw_fd())
144 .field("ring_kind", &self.ring.kind())
145 .finish_non_exhaustive()
146 }
147}
148
149impl Drop for RingEventFd {
150 fn drop(&mut self) {
151 self.stop.store(true, Ordering::Release);
152 self.ring
156 .notification_generation()
157 .fetch_add(1, Ordering::Release);
158 let _ = wake_all_generation_waiters(self.ring.notification_generation());
159 if let Some(driver) = self.driver.take() {
160 let _ = driver.join();
161 }
162 }
163}
164
165#[cfg(target_os = "linux")]
166fn wait_for_generation(word: &AtomicU32, expected: u32) -> io::Result<()> {
167 let result = unsafe {
168 libc::syscall(
169 libc::SYS_futex,
170 word.as_ptr(),
171 libc::FUTEX_WAIT,
172 expected,
173 std::ptr::null::<libc::timespec>(),
174 std::ptr::null::<u32>(),
175 0,
176 )
177 };
178 if result == 0 {
179 return Ok(());
180 }
181
182 let error = io::Error::last_os_error();
183 match error.raw_os_error() {
184 Some(libc::EAGAIN) | Some(libc::EINTR) => Ok(()),
187 _ => Err(error),
188 }
189}
190
191#[cfg(target_os = "linux")]
192fn wake_all_generation_waiters(word: &AtomicU32) -> io::Result<()> {
193 let result = unsafe {
194 libc::syscall(
195 libc::SYS_futex,
196 word.as_ptr(),
197 libc::FUTEX_WAKE,
198 i32::MAX,
199 std::ptr::null::<libc::timespec>(),
200 std::ptr::null::<u32>(),
201 0,
202 )
203 };
204 if result >= 0 {
205 Ok(())
206 } else {
207 Err(io::Error::last_os_error())
208 }
209}
210
211#[cfg(target_os = "freebsd")]
212fn wait_for_generation(word: &AtomicU32, expected: u32) -> io::Result<()> {
213 let result = unsafe {
214 libc::_umtx_op(
215 word.as_ptr().cast(),
216 libc::UMTX_OP_WAIT_UINT,
217 expected as libc::c_ulong,
218 std::ptr::null_mut(),
219 std::ptr::null_mut(),
220 )
221 };
222 if result == 0 {
223 return Ok(());
224 }
225
226 let error = io::Error::last_os_error();
227 match error.raw_os_error() {
228 Some(libc::EINTR) => Ok(()),
231 _ => Err(error),
232 }
233}
234
235#[cfg(target_os = "freebsd")]
236fn wake_all_generation_waiters(word: &AtomicU32) -> io::Result<()> {
237 let result = unsafe {
238 libc::_umtx_op(
239 word.as_ptr().cast(),
240 libc::UMTX_OP_WAKE,
241 i32::MAX as libc::c_ulong,
242 std::ptr::null_mut(),
243 std::ptr::null_mut(),
244 )
245 };
246 if result == 0 {
247 Ok(())
248 } else {
249 Err(io::Error::last_os_error())
250 }
251}
252
253fn signal_event_fd(fd: RawFd) -> io::Result<()> {
254 let value = 1u64;
255 loop {
256 let written = unsafe {
257 libc::write(
258 fd,
259 (&value as *const u64).cast(),
260 std::mem::size_of::<u64>(),
261 )
262 };
263 if written == std::mem::size_of::<u64>() as isize {
264 return Ok(());
265 }
266 if written < 0 {
267 let error = io::Error::last_os_error();
268 match error.raw_os_error() {
269 Some(libc::EINTR) => continue,
270 Some(libc::EAGAIN) => return Ok(()),
273 _ => return Err(error),
274 }
275 }
276 return Err(io::Error::new(
277 io::ErrorKind::WriteZero,
278 "Orbit eventfd accepted a partial counter",
279 ));
280 }
281}
282
283#[cfg(any(target_os = "linux", target_os = "freebsd"))]
284fn local_notification_pair() -> io::Result<(OwnedFd, OwnedFd)> {
285 let raw = unsafe { libc::eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK) };
286 if raw < 0 {
287 return Err(io::Error::last_os_error());
288 }
289 let fd = unsafe { OwnedFd::from_raw_fd(raw) };
290 let driver = fd.try_clone()?;
291 Ok((fd, driver))
292}
293
294#[cfg(target_os = "macos")]
295fn local_notification_pair() -> io::Result<(OwnedFd, OwnedFd)> {
296 if macos::api().is_none() {
297 return Err(io::Error::new(
298 io::ErrorKind::Unsupported,
299 "Orbit native readiness requires macOS 14.4 or later",
300 ));
301 }
302 let mut raw = [-1; 2];
303 if unsafe { libc::pipe(raw.as_mut_ptr()) } < 0 {
304 return Err(io::Error::last_os_error());
305 }
306 let read = unsafe { OwnedFd::from_raw_fd(raw[0]) };
309 let write = unsafe { OwnedFd::from_raw_fd(raw[1]) };
310 for fd in [&read, &write] {
311 if unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC) } < 0
312 || unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK) } < 0
313 {
314 return Err(io::Error::last_os_error());
315 }
316 }
317 Ok((read, write))
318}
319
320#[cfg(target_os = "macos")]
321fn wait_for_generation(word: &AtomicU32, expected: u32) -> io::Result<()> {
322 let api = macos::api().ok_or_else(|| {
323 io::Error::new(
324 io::ErrorKind::Unsupported,
325 "macOS shared address waits unavailable",
326 )
327 })?;
328 debug_assert_eq!(
329 (word.as_ptr() as usize) % size_of::<u32>(),
330 0,
331 "shared wait word must be naturally aligned"
332 );
333 let result = unsafe {
336 (api.wait)(
337 word.as_ptr().cast(),
338 u64::from(expected),
339 size_of::<u32>(),
340 macos::SHARED,
341 )
342 };
343 if result >= 0 {
344 return Ok(());
345 }
346 let error = io::Error::last_os_error();
347 match error.raw_os_error() {
348 Some(libc::EINTR) => Ok(()),
349 _ => Err(error),
350 }
351}
352
353#[cfg(target_os = "macos")]
354fn wake_all_generation_waiters(word: &AtomicU32) -> io::Result<()> {
355 debug_assert_eq!(
356 (word.as_ptr() as usize) % size_of::<u32>(),
357 0,
358 "shared wake word must be naturally aligned"
359 );
360 let Some(api) = macos::api() else {
363 return Ok(());
364 };
365 loop {
366 let result =
367 unsafe { (api.wake_all)(word.as_ptr().cast(), size_of::<u32>(), macos::SHARED) };
368 if result >= 0 {
369 return Ok(());
370 }
371 let error = io::Error::last_os_error();
372 match error.raw_os_error() {
373 Some(libc::ENOENT) => return Ok(()),
376 Some(libc::EINTR) => continue,
377 _ => return Err(error),
378 }
379 }
380}
381
382#[cfg(target_os = "macos")]
383mod macos {
384 use std::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
385
386 pub(super) const SHARED: u32 = 1;
389
390 type Wait = unsafe extern "C" fn(*mut libc::c_void, u64, usize, u32) -> libc::c_int;
391 type Wake = unsafe extern "C" fn(*mut libc::c_void, usize, u32) -> libc::c_int;
392
393 #[derive(Clone, Copy)]
394 pub(super) struct Api {
395 pub(super) wait: Wait,
396 pub(super) wake_all: Wake,
397 }
398
399 const UNRESOLVED: u8 = 0;
400 const UNAVAILABLE: u8 = 1;
401 const READY: u8 = 2;
402
403 static STATE: AtomicU8 = AtomicU8::new(UNRESOLVED);
404 static WAIT: AtomicUsize = AtomicUsize::new(0);
405 static WAKE: AtomicUsize = AtomicUsize::new(0);
406
407 pub(super) fn api() -> Option<Api> {
423 match STATE.load(Ordering::Acquire) {
424 READY => Some(load()),
425 UNAVAILABLE => None,
426 _ => resolve(),
427 }
428 }
429
430 fn load() -> Api {
431 unsafe {
432 Api {
433 wait: std::mem::transmute::<usize, Wait>(WAIT.load(Ordering::Acquire)),
434 wake_all: std::mem::transmute::<usize, Wake>(WAKE.load(Ordering::Acquire)),
435 }
436 }
437 }
438
439 fn resolve() -> Option<Api> {
440 let wait = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"os_sync_wait_on_address".as_ptr()) };
441 let wake =
442 unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"os_sync_wake_by_address_all".as_ptr()) };
443 if wait.is_null() || wake.is_null() {
444 STATE.store(UNAVAILABLE, Ordering::Release);
445 return None;
446 }
447 WAIT.store(wait as usize, Ordering::Release);
449 WAKE.store(wake as usize, Ordering::Release);
450 STATE.store(READY, Ordering::Release);
451 Some(load())
452 }
453}
454
455#[cfg(all(test, target_os = "macos"))]
456mod tests {
457 use super::*;
458
459 #[test]
460 fn full_local_pipe_coalesces_and_rearms() {
461 let (read, write) = local_notification_pair().unwrap();
462 for fd in [&read, &write] {
463 assert_ne!(
464 unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_GETFL) } & libc::O_NONBLOCK,
465 0
466 );
467 assert_ne!(
468 unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_GETFD) } & libc::FD_CLOEXEC,
469 0
470 );
471 }
472 let token = 1u64;
474 loop {
475 let n = unsafe { libc::write(write.as_raw_fd(), (&token as *const u64).cast(), 8) };
476 if n < 0 {
477 assert_eq!(
478 io::Error::last_os_error().raw_os_error(),
479 Some(libc::EAGAIN)
480 );
481 break;
482 }
483 assert_eq!(n, 8);
484 }
485 signal_event_fd(write.as_raw_fd()).unwrap();
486 let mut buffer = [0u64; 128];
487 loop {
488 let n = unsafe {
489 libc::read(
490 read.as_raw_fd(),
491 buffer.as_mut_ptr().cast(),
492 size_of_val(&buffer),
493 )
494 };
495 if n < 0 {
496 assert_eq!(
497 io::Error::last_os_error().raw_os_error(),
498 Some(libc::EAGAIN)
499 );
500 break;
501 }
502 assert!(n > 0);
503 }
504 signal_event_fd(write.as_raw_fd()).unwrap();
505 let mut value = 0u64;
506 assert_eq!(
507 unsafe { libc::read(read.as_raw_fd(), (&mut value as *mut u64).cast(), 8) },
508 8
509 );
510 assert_eq!(value, 1);
511 }
512}