@@ -26,7 +26,12 @@ use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
2626/// Write to signal, read to consume. Multiple signals coalesce into one.
2727/// The fd can be registered with any event loop (epoll, io_uring, kqueue fallback).
2828pub struct EventFd {
29+ #[ cfg( target_os = "linux" ) ]
2930 fd : OwnedFd ,
31+ #[ cfg( not( target_os = "linux" ) ) ]
32+ read_fd : OwnedFd ,
33+ #[ cfg( not( target_os = "linux" ) ) ]
34+ write_fd : OwnedFd ,
3035}
3136
3237impl EventFd {
@@ -35,14 +40,36 @@ impl EventFd {
3540 /// `EFD_NONBLOCK` ensures reads/writes never block the calling thread.
3641 /// `EFD_CLOEXEC` prevents fd leaks across fork/exec.
3742 pub fn new ( ) -> io:: Result < Self > {
38- // SAFETY: eventfd2 is a standard Linux syscall. Flags are valid.
39- let fd = unsafe { libc:: eventfd ( 0 , libc:: EFD_NONBLOCK | libc:: EFD_CLOEXEC ) } ;
40- if fd < 0 {
41- return Err ( io:: Error :: last_os_error ( ) ) ;
43+ #[ cfg( target_os = "linux" ) ]
44+ {
45+ // SAFETY: eventfd2 is a standard Linux syscall. Flags are valid.
46+ let fd = unsafe { libc:: eventfd ( 0 , libc:: EFD_NONBLOCK | libc:: EFD_CLOEXEC ) } ;
47+ if fd < 0 {
48+ return Err ( io:: Error :: last_os_error ( ) ) ;
49+ }
50+ // SAFETY: fd is a valid file descriptor returned by eventfd().
51+ let fd = unsafe { OwnedFd :: from_raw_fd ( fd) } ;
52+ Ok ( Self { fd } )
53+ }
54+ #[ cfg( not( target_os = "linux" ) ) ]
55+ {
56+ let mut fds = [ 0 ; 2 ] ;
57+ // SAFETY: `fds` points to two valid c_int slots for pipe to fill.
58+ if unsafe { libc:: pipe ( fds. as_mut_ptr ( ) ) } < 0 {
59+ return Err ( io:: Error :: last_os_error ( ) ) ;
60+ }
61+ if let Err ( err) = set_pipe_flags ( fds[ 0 ] ) . and_then ( |( ) | set_pipe_flags ( fds[ 1 ] ) ) {
62+ unsafe {
63+ libc:: close ( fds[ 0 ] ) ;
64+ libc:: close ( fds[ 1 ] ) ;
65+ }
66+ return Err ( err) ;
67+ }
68+ // SAFETY: fds were returned by pipe() and ownership moves into OwnedFd.
69+ let read_fd = unsafe { OwnedFd :: from_raw_fd ( fds[ 0 ] ) } ;
70+ let write_fd = unsafe { OwnedFd :: from_raw_fd ( fds[ 1 ] ) } ;
71+ Ok ( Self { read_fd, write_fd } )
4272 }
43- // SAFETY: fd is a valid file descriptor returned by eventfd().
44- let fd = unsafe { OwnedFd :: from_raw_fd ( fd) } ;
45- Ok ( Self { fd } )
4673 }
4774
4875 /// Signal the other side to wake up.
@@ -51,14 +78,12 @@ impl EventFd {
5178 /// a single read clears all pending signals.
5279 pub fn notify ( & self ) -> io:: Result < ( ) > {
5380 let val: u64 = 1 ;
81+ #[ cfg( target_os = "linux" ) ]
82+ let fd = self . fd . as_raw_fd ( ) ;
83+ #[ cfg( not( target_os = "linux" ) ) ]
84+ let fd = self . write_fd . as_raw_fd ( ) ;
5485 // SAFETY: writing 8 bytes to a valid eventfd.
55- let ret = unsafe {
56- libc:: write (
57- self . fd . as_raw_fd ( ) ,
58- & val as * const u64 as * const libc:: c_void ,
59- 8 ,
60- )
61- } ;
86+ let ret = unsafe { libc:: write ( fd, & val as * const u64 as * const libc:: c_void , 8 ) } ;
6287 if ret < 0 {
6388 Err ( io:: Error :: last_os_error ( ) )
6489 } else {
@@ -71,24 +96,54 @@ impl EventFd {
7196 /// Returns `Ok(0)` if no signal was pending (EAGAIN on non-blocking fd).
7297 /// Returns `Ok(n)` where n is the accumulated signal count.
7398 pub fn try_read ( & self ) -> io:: Result < u64 > {
74- let mut val: u64 = 0 ;
75- // SAFETY: reading 8 bytes from a valid eventfd.
76- let ret = unsafe {
77- libc:: read (
78- self . fd . as_raw_fd ( ) ,
79- & mut val as * mut u64 as * mut libc:: c_void ,
80- 8 ,
81- )
82- } ;
83- if ret < 0 {
84- let err = io:: Error :: last_os_error ( ) ;
85- if err. kind ( ) == io:: ErrorKind :: WouldBlock {
86- Ok ( 0 )
99+ #[ cfg( target_os = "linux" ) ]
100+ {
101+ let mut val: u64 = 0 ;
102+ // SAFETY: reading 8 bytes from a valid eventfd.
103+ let ret = unsafe {
104+ libc:: read (
105+ self . fd . as_raw_fd ( ) ,
106+ & mut val as * mut u64 as * mut libc:: c_void ,
107+ 8 ,
108+ )
109+ } ;
110+ if ret < 0 {
111+ let err = io:: Error :: last_os_error ( ) ;
112+ if err. kind ( ) == io:: ErrorKind :: WouldBlock {
113+ Ok ( 0 )
114+ } else {
115+ Err ( err)
116+ }
87117 } else {
88- Err ( err)
118+ Ok ( val)
119+ }
120+ }
121+ #[ cfg( not( target_os = "linux" ) ) ]
122+ {
123+ let mut count = 0u64 ;
124+ loop {
125+ let mut val: u64 = 0 ;
126+ // SAFETY: reading 8 bytes from the nonblocking pipe read end.
127+ let ret = unsafe {
128+ libc:: read (
129+ self . read_fd . as_raw_fd ( ) ,
130+ & mut val as * mut u64 as * mut libc:: c_void ,
131+ 8 ,
132+ )
133+ } ;
134+ if ret == 8 {
135+ count = count. saturating_add ( val. max ( 1 ) ) ;
136+ continue ;
137+ }
138+ if ret < 0 {
139+ let err = io:: Error :: last_os_error ( ) ;
140+ if err. kind ( ) == io:: ErrorKind :: WouldBlock {
141+ return Ok ( count) ;
142+ }
143+ return Err ( err) ;
144+ }
145+ return Ok ( count) ;
89146 }
90- } else {
91- Ok ( val)
92147 }
93148 }
94149
@@ -99,8 +154,39 @@ impl EventFd {
99154 /// - Glommio: `GlommioDma::from_raw_fd()` or similar
100155 /// - io_uring: `IORING_OP_READ` on the fd
101156 pub fn as_fd ( & self ) -> RawFd {
102- self . fd . as_raw_fd ( )
157+ #[ cfg( target_os = "linux" ) ]
158+ {
159+ self . fd . as_raw_fd ( )
160+ }
161+ #[ cfg( not( target_os = "linux" ) ) ]
162+ {
163+ self . read_fd . as_raw_fd ( )
164+ }
165+ }
166+ }
167+
168+ #[ cfg( not( target_os = "linux" ) ) ]
169+ fn set_pipe_flags ( fd : RawFd ) -> io:: Result < ( ) > {
170+ // SAFETY: fcntl is called with a valid fd returned by pipe().
171+ let status_flags = unsafe { libc:: fcntl ( fd, libc:: F_GETFL ) } ;
172+ if status_flags < 0 {
173+ return Err ( io:: Error :: last_os_error ( ) ) ;
174+ }
175+ // SAFETY: fcntl F_SETFL updates status flags for this fd.
176+ if unsafe { libc:: fcntl ( fd, libc:: F_SETFL , status_flags | libc:: O_NONBLOCK ) } < 0 {
177+ return Err ( io:: Error :: last_os_error ( ) ) ;
178+ }
179+
180+ // SAFETY: fcntl is called with a valid fd returned by pipe().
181+ let fd_flags = unsafe { libc:: fcntl ( fd, libc:: F_GETFD ) } ;
182+ if fd_flags < 0 {
183+ return Err ( io:: Error :: last_os_error ( ) ) ;
184+ }
185+ // SAFETY: fcntl F_SETFD updates close-on-exec flags for this fd.
186+ if unsafe { libc:: fcntl ( fd, libc:: F_SETFD , fd_flags | libc:: FD_CLOEXEC ) } < 0 {
187+ return Err ( io:: Error :: last_os_error ( ) ) ;
103188 }
189+ Ok ( ( ) )
104190}
105191
106192// SAFETY: eventfd is a kernel object. The fd can be shared across threads.
0 commit comments