pal_event/
unix.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![cfg(unix)]
5
6use crate::Event;
7use std::os::unix::prelude::*;
8
9pub type Inner = OwnedFd;
10
11/// Runs f() until it stop failing with EINTR (as indicated by errno).
12fn while_eintr<F, R>(mut f: F) -> std::io::Result<R>
13where
14    F: FnMut() -> std::io::Result<R>,
15{
16    loop {
17        match f() {
18            Err(err) if err.raw_os_error() == Some(libc::EINTR) => {}
19            r => break r,
20        }
21    }
22}
23
24fn syscall_result<T: PartialOrd + Default>(result: T) -> std::io::Result<T> {
25    if result >= T::default() {
26        Ok(result)
27    } else {
28        Err(std::io::Error::last_os_error())
29    }
30}
31
32#[cfg(target_os = "linux")]
33mod eventfd {
34    use super::syscall_result;
35    use super::while_eintr;
36    use crate::Event;
37
38    use std::os::unix::prelude::*;
39
40    impl Event {
41        pub(crate) fn new_inner() -> std::io::Result<Self> {
42            // Create an event fd.
43            // SAFETY: calling C APIs as documented, with no special requirements, and validating its
44            // return value before passing it to from_raw_fd.
45            let fd = unsafe {
46                let fd = syscall_result(libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC))?;
47                OwnedFd::from_raw_fd(fd)
48            };
49            Ok(Self(fd))
50        }
51
52        pub(crate) fn try_wait_inner(&self) -> bool {
53            let mut c: u64 = 0;
54            // SAFETY: fd holds a valid and open file descriptor.
55            let n = while_eintr(|| unsafe {
56                syscall_result(libc::read(
57                    self.0.as_raw_fd(),
58                    std::ptr::from_mut(&mut c).cast::<std::ffi::c_void>(),
59                    size_of_val(&c),
60                ))
61            });
62            match n {
63                Ok(n) => {
64                    assert!(n == size_of_val(&c) as isize);
65                    true
66                }
67                Err(err) => {
68                    assert_eq!(err.raw_os_error(), Some(libc::EAGAIN));
69                    false
70                }
71            }
72        }
73    }
74}
75
76#[cfg(not(target_os = "linux"))]
77mod fifo {
78    use super::syscall_result;
79    use super::while_eintr;
80    use crate::Event;
81    use std::fs::File;
82
83    use std::os::unix::prelude::*;
84
85    impl Event {
86        // Create an anonymous FIFO to emulate a Linux eventfd.
87        pub(crate) fn new_inner() -> std::io::Result<Self> {
88            // Create a random path.
89            let mut path = std::env::temp_dir();
90            let mut val = [0; 16];
91            getrandom::fill(&mut val).unwrap();
92            path.push(u128::from_ne_bytes(val).to_string());
93
94            // Create the FIFO.
95            let cpath = std::ffi::CString::new(path.as_os_str().as_bytes()).unwrap();
96            // SAFETY: calling C APIs as documented, with no special requirements.
97            syscall_result(unsafe { libc::mkfifo(cpath.as_ptr(), 0o600) })?;
98
99            // Open the FIFO.
100            let fifo = File::options()
101                .read(true)
102                .write(true)
103                .custom_flags(libc::O_NONBLOCK)
104                .open(&path)?;
105
106            // Unlink it so that it can't be opened again.
107            let _ = std::fs::remove_file(&path);
108            Ok(Self(fifo.into()))
109        }
110
111        /// Consumes the ready state of an emulated eventfd on non-Linux platforms.
112        ///
113        /// Reads using a large buffer in case the underlying FIFO was signaled multiple
114        /// times.
115        pub(crate) fn try_wait_inner(&self) -> bool {
116            let mut c = [0u8; 512];
117            // SAFETY: fd holds a valid and open file descriptor.
118            let n = while_eintr(|| unsafe {
119                syscall_result(libc::read(
120                    self.0.as_raw_fd(),
121                    c.as_mut_ptr().cast::<std::ffi::c_void>(),
122                    size_of_val(&c),
123                ))
124            });
125            match n {
126                Ok(_) => true,
127                Err(err) => {
128                    assert_eq!(err.raw_os_error(), Some(libc::EAGAIN));
129                    false
130                }
131            }
132        }
133    }
134}
135
136impl Event {
137    pub(crate) fn signal_inner(&self) {
138        let c: u64 = 1;
139        // SAFETY: fd holds a valid and open file descriptor.
140        let r = unsafe {
141            syscall_result(libc::write(
142                self.0.as_raw_fd(),
143                std::ptr::from_ref::<u64>(&c).cast::<libc::c_void>(),
144                size_of_val(&c),
145            ))
146        };
147        match r {
148            Ok(n) if n == size_of_val(&c) as isize => {}
149            Err(err) if err.raw_os_error() == Some(libc::EAGAIN) => {}
150            r => {
151                panic!("unexpected event write result: {:?}", r);
152            }
153        }
154    }
155
156    fn poll(&self) {
157        let mut pollfds = [libc::pollfd {
158            fd: self.0.as_raw_fd(),
159            events: libc::POLLIN,
160            revents: 0,
161        }];
162        // SAFETY: fd holds a valid and open file descriptor.
163        while_eintr(|| unsafe { syscall_result(libc::poll(pollfds.as_mut_ptr(), 1, !0)) }).unwrap();
164    }
165
166    pub(crate) fn wait_inner(&self) {
167        while !self.try_wait_inner() {
168            self.poll();
169        }
170    }
171}
172
173impl Clone for Event {
174    fn clone(&self) -> Self {
175        Self(self.0.try_clone().expect("out of resources dup eventfd"))
176    }
177}
178
179impl From<OwnedFd> for Event {
180    fn from(fd: OwnedFd) -> Self {
181        Self(fd)
182    }
183}
184
185impl From<Event> for OwnedFd {
186    fn from(event: Event) -> Self {
187        event.0
188    }
189}
190
191impl AsFd for Event {
192    fn as_fd(&self) -> BorrowedFd<'_> {
193        self.0.as_fd()
194    }
195}
196
197#[cfg(feature = "mesh")]
198mesh_protobuf::os_resource!(Event, OwnedFd);