microsandbox_utils/
wake_pipe.rs1#[cfg(unix)]
7use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
8#[cfg(windows)]
9use std::os::windows::io::RawHandle;
10use std::time::Duration;
11#[cfg(windows)]
12use windows_sys::Win32::Foundation::{
13 CloseHandle, HANDLE, WAIT_FAILED, WAIT_OBJECT_0, WAIT_TIMEOUT,
14};
15#[cfg(windows)]
16use windows_sys::Win32::System::Threading::{
17 CreateEventW, ResetEvent, SetEvent, WaitForSingleObject,
18};
19
20#[cfg(unix)]
30pub struct WakePipe {
31 read_fd: OwnedFd,
32 write_fd: OwnedFd,
33}
34
35#[cfg(windows)]
42pub struct WakePipe {
43 handle: HANDLE,
44 async_wake: tokio::sync::Notify,
45}
46
47impl WakePipe {
52 #[cfg(unix)]
56 pub fn new() -> Self {
57 let mut fds = [0i32; 2];
58
59 let ret = unsafe { libc::pipe(fds.as_mut_ptr()) };
62 assert!(
63 ret == 0,
64 "pipe() failed: {}",
65 std::io::Error::last_os_error()
66 );
67
68 unsafe {
71 set_nonblock_cloexec(fds[0]);
72 set_nonblock_cloexec(fds[1]);
73 }
74
75 Self {
76 read_fd: unsafe { OwnedFd::from_raw_fd(fds[0]) },
78 write_fd: unsafe { OwnedFd::from_raw_fd(fds[1]) },
79 }
80 }
81
82 #[cfg(windows)]
84 pub fn new() -> Self {
85 let handle = unsafe { CreateEventW(std::ptr::null(), 1, 0, std::ptr::null()) };
86 if handle.is_null() {
87 panic!("CreateEventW failed: {}", std::io::Error::last_os_error());
88 }
89 Self {
90 handle,
91 async_wake: tokio::sync::Notify::new(),
92 }
93 }
94
95 #[cfg(unix)]
100 pub fn wake(&self) {
101 unsafe {
104 libc::write(self.write_fd.as_raw_fd(), [1u8].as_ptr().cast(), 1);
105 }
106 }
107
108 #[cfg(windows)]
110 pub fn wake(&self) {
111 unsafe {
112 SetEvent(self.handle);
113 }
114 self.async_wake.notify_one();
117 }
118
119 #[cfg(windows)]
127 pub async fn notified(&self) {
128 self.async_wake.notified().await;
129 }
130
131 #[cfg(unix)]
134 pub fn drain(&self) {
135 let mut buf = [0u8; 512];
136 loop {
137 let n =
139 unsafe { libc::read(self.read_fd.as_raw_fd(), buf.as_mut_ptr().cast(), buf.len()) };
140 if n <= 0 {
141 break;
142 }
143 }
144 }
145
146 #[cfg(windows)]
148 pub fn drain(&self) {
149 unsafe {
150 ResetEvent(self.handle);
151 }
152 }
153
154 pub fn wait_timeout(&self, timeout: Duration) -> bool {
158 wait_timeout(self, timeout)
159 }
160
161 #[cfg(unix)]
165 pub fn as_raw_fd(&self) -> RawFd {
166 self.read_fd.as_raw_fd()
167 }
168
169 #[cfg(unix)]
171 pub fn try_clone_read_fd(&self) -> std::io::Result<OwnedFd> {
172 self.read_fd.try_clone()
173 }
174
175 #[cfg(windows)]
177 pub fn as_raw_handle(&self) -> RawHandle {
178 self.handle as RawHandle
179 }
180}
181
182impl Default for WakePipe {
187 fn default() -> Self {
188 Self::new()
189 }
190}
191
192#[cfg(windows)]
193unsafe impl Send for WakePipe {}
194
195#[cfg(windows)]
196unsafe impl Sync for WakePipe {}
197
198#[cfg(windows)]
199impl Drop for WakePipe {
200 fn drop(&mut self) {
201 unsafe {
202 CloseHandle(self.handle);
203 }
204 }
205}
206
207#[cfg(unix)]
217unsafe fn set_nonblock_cloexec(fd: RawFd) {
218 unsafe {
219 let flags = libc::fcntl(fd, libc::F_GETFL);
221 assert!(
222 flags >= 0,
223 "fcntl(F_GETFL) failed: {}",
224 std::io::Error::last_os_error()
225 );
226 let ret = libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK);
227 assert!(
228 ret >= 0,
229 "fcntl(F_SETFL) failed: {}",
230 std::io::Error::last_os_error()
231 );
232
233 let flags = libc::fcntl(fd, libc::F_GETFD);
235 assert!(
236 flags >= 0,
237 "fcntl(F_GETFD) failed: {}",
238 std::io::Error::last_os_error()
239 );
240 let ret = libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC);
241 assert!(
242 ret >= 0,
243 "fcntl(F_SETFD) failed: {}",
244 std::io::Error::last_os_error()
245 );
246 }
247}
248
249#[cfg(unix)]
250fn wait_timeout(pipe: &WakePipe, timeout: Duration) -> bool {
251 let timeout_ms = timeout.as_millis().min(i32::MAX as u128) as i32;
252 poll_fd_readable_timeout(pipe.as_raw_fd(), timeout_ms)
253}
254
255#[cfg(unix)]
256fn poll_fd_readable_timeout(fd: RawFd, timeout_ms: i32) -> bool {
257 loop {
258 let mut pfd = libc::pollfd {
259 fd,
260 events: libc::POLLIN,
261 revents: 0,
262 };
263 let ret = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
265 if ret > 0 {
266 return true;
267 }
268 if ret == 0 {
269 return false;
270 }
271
272 let errno = std::io::Error::last_os_error();
273 if errno.raw_os_error() != Some(libc::EINTR) {
274 return false;
275 }
276 }
277}
278
279#[cfg(windows)]
280fn wait_timeout(pipe: &WakePipe, timeout: Duration) -> bool {
281 let timeout_ms = timeout.as_millis().min(u32::MAX as u128) as u32;
282 let result = unsafe { WaitForSingleObject(pipe.handle, timeout_ms) };
283 match result {
284 WAIT_OBJECT_0 => true,
285 WAIT_TIMEOUT | WAIT_FAILED => false,
286 _ => false,
287 }
288}
289
290#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[cfg(windows)]
299 #[test]
300 fn async_wake_survives_cancellation_and_native_drain() {
301 use std::future::Future;
302 use std::sync::Arc;
303 use std::sync::atomic::{AtomicBool, Ordering};
304 use std::task::{Context, Poll, Wake, Waker};
305
306 struct Flag(AtomicBool);
307 impl Wake for Flag {
308 fn wake(self: Arc<Self>) {
309 self.0.store(true, Ordering::SeqCst);
310 }
311 }
312
313 let flag = Arc::new(Flag(AtomicBool::new(false)));
314 let waker = Waker::from(flag.clone());
315 let mut context = Context::from_waker(&waker);
316 let pipe = WakePipe::new();
317
318 pipe.wake();
321 pipe.wake();
322 pipe.drain();
323 assert_eq!(
324 Box::pin(pipe.notified()).as_mut().poll(&mut context),
325 Poll::Ready(())
326 );
327 let mut cancelled = Box::pin(pipe.notified());
328 assert!(cancelled.as_mut().poll(&mut context).is_pending());
329 drop(cancelled);
330
331 let mut waiting = Box::pin(pipe.notified());
332 assert!(waiting.as_mut().poll(&mut context).is_pending());
333 pipe.wake();
334 assert!(flag.0.load(Ordering::SeqCst));
335 drop(waiting);
337 assert_eq!(
338 Box::pin(pipe.notified()).as_mut().poll(&mut context),
339 Poll::Ready(())
340 );
341 assert!(pipe.wait_timeout(Duration::ZERO));
342 pipe.drain();
343 assert!(
344 Box::pin(pipe.notified())
345 .as_mut()
346 .poll(&mut context)
347 .is_pending()
348 );
349
350 for _ in 0..100 {
352 let mut waiting = Box::pin(pipe.notified());
353 assert!(waiting.as_mut().poll(&mut context).is_pending());
354 pipe.wake();
355 assert!(waiting.as_mut().poll(&mut context).is_ready());
356 pipe.drain();
357 }
358 }
359
360 #[test]
361 fn wake_and_drain() {
362 let pipe = WakePipe::new();
363 pipe.drain();
365
366 pipe.wake();
368 pipe.wake();
369 pipe.drain();
370
371 pipe.wake();
373 pipe.drain();
374 }
375
376 #[cfg(unix)]
377 #[test]
378 fn fd_is_valid() {
379 let pipe = WakePipe::new();
380 let fd = pipe.as_raw_fd();
381 assert!(fd >= 0);
382 }
383
384 #[test]
385 fn nonblocking_read() {
386 let pipe = WakePipe::new();
387 pipe.drain();
389 }
390
391 #[test]
392 fn wait_timeout_observes_wake() {
393 let pipe = WakePipe::new();
394
395 assert!(!pipe.wait_timeout(Duration::from_millis(1)));
396 pipe.wake();
397 assert!(pipe.wait_timeout(Duration::from_secs(1)));
398 pipe.drain();
399 assert!(!pipe.wait_timeout(Duration::from_millis(1)));
400 }
401}