1 // Copyright 2023 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::mem; 6 use std::os::unix::io::AsFd; 7 use std::os::unix::io::AsRawFd; 8 use std::os::unix::io::FromRawFd; 9 use std::os::unix::io::OwnedFd; 10 11 use libc::c_int; 12 use nix::errno::Errno; 13 use nix::sys::epoll::EpollCreateFlags; 14 use nix::sys::epoll::EpollFlags; 15 use nix::sys::epoll::EpollOp; 16 use nix::Result; 17 18 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 19 #[repr(transparent)] 20 pub struct EpollEvent { 21 event: libc::epoll_event, 22 } 23 24 impl EpollEvent { new(events: EpollFlags, data: u64) -> Self25 pub fn new(events: EpollFlags, data: u64) -> Self { 26 EpollEvent { 27 event: libc::epoll_event { 28 events: events.bits() as u32, 29 u64: data, 30 }, 31 } 32 } 33 empty() -> Self34 pub fn empty() -> Self { 35 // SAFETY: trivially safe 36 unsafe { mem::zeroed::<EpollEvent>() } 37 } 38 events(&self) -> EpollFlags39 pub fn events(&self) -> EpollFlags { 40 EpollFlags::from_bits(self.event.events as c_int).unwrap() 41 } 42 data(&self) -> u6443 pub fn data(&self) -> u64 { 44 self.event.u64 45 } 46 } 47 48 // This is a function is unreleased nix 0.27 -- when it is released, we can delete this. 49 #[derive(Debug)] 50 pub struct Epoll(pub OwnedFd); 51 impl Epoll { 52 /// Creates a new epoll instance and returns a file descriptor referring to that instance. 53 /// 54 /// [`epoll_create1`](https://man7.org/linux/man-pages/man2/epoll_create1.2.html). new(flags: EpollCreateFlags) -> Result<Self>55 pub fn new(flags: EpollCreateFlags) -> Result<Self> { 56 // TODO(b/315870313): Add safety comment 57 #[allow(clippy::undocumented_unsafe_blocks)] 58 let res = unsafe { libc::epoll_create1(flags.bits()) }; 59 let fd = Errno::result(res)?; 60 // TODO(b/315870313): Add safety comment 61 #[allow(clippy::undocumented_unsafe_blocks)] 62 let owned_fd = unsafe { OwnedFd::from_raw_fd(fd) }; 63 Ok(Self(owned_fd)) 64 } 65 /// Add an entry to the interest list of the epoll file descriptor for 66 /// specified in events. 67 /// 68 /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_ADD`. add<Fd: AsFd>(&self, fd: Fd, mut event: EpollEvent) -> Result<()>69 pub fn add<Fd: AsFd>(&self, fd: Fd, mut event: EpollEvent) -> Result<()> { 70 self.epoll_ctl(EpollOp::EpollCtlAdd, fd, &mut event) 71 } 72 /// Remove (deregister) the target file descriptor `fd` from the interest list. 73 /// 74 /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_DEL` . delete<Fd: AsFd>(&self, fd: Fd) -> Result<()>75 pub fn delete<Fd: AsFd>(&self, fd: Fd) -> Result<()> { 76 self.epoll_ctl(EpollOp::EpollCtlDel, fd, None) 77 } 78 /// Change the settings associated with `fd` in the interest list to the new settings specified 79 /// in `event`. 80 /// 81 /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_MOD`. 82 #[allow(dead_code)] modify<Fd: AsFd>(&self, fd: Fd, event: &mut EpollEvent) -> Result<()>83 pub fn modify<Fd: AsFd>(&self, fd: Fd, event: &mut EpollEvent) -> Result<()> { 84 self.epoll_ctl(EpollOp::EpollCtlMod, fd, event) 85 } 86 /// Waits for I/O events, blocking the calling thread if no events are currently available. 87 /// (This can be thought of as fetching items from the ready list of the epoll instance.) 88 /// 89 /// [`epoll_wait`](https://man7.org/linux/man-pages/man2/epoll_wait.2.html) wait(&self, events: &mut [EpollEvent], timeout: isize) -> Result<usize>90 pub fn wait(&self, events: &mut [EpollEvent], timeout: isize) -> Result<usize> { 91 // TODO(b/315870313): Add safety comment 92 #[allow(clippy::undocumented_unsafe_blocks)] 93 let res = unsafe { 94 libc::epoll_wait( 95 self.0.as_raw_fd(), 96 events.as_mut_ptr() as *mut libc::epoll_event, 97 events.len() as c_int, 98 timeout as c_int, 99 ) 100 }; 101 102 Errno::result(res).map(|r| r as usize) 103 } 104 /// This system call is used to add, modify, or remove entries in the interest list of the epoll 105 /// instance referred to by `self`. It requests that the operation `op` be performed for the 106 /// target file descriptor, `fd`. 107 /// 108 /// When possible prefer [`Epoll::add`], [`Epoll::delete`] and [`Epoll::modify`]. 109 /// 110 /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) epoll_ctl<'a, Fd: AsFd, T>(&self, op: EpollOp, fd: Fd, event: T) -> Result<()> where T: Into<Option<&'a mut EpollEvent>>,111 fn epoll_ctl<'a, Fd: AsFd, T>(&self, op: EpollOp, fd: Fd, event: T) -> Result<()> 112 where 113 T: Into<Option<&'a mut EpollEvent>>, 114 { 115 let event: Option<&mut EpollEvent> = event.into(); 116 let ptr = event 117 .map(|x| &mut x.event as *mut libc::epoll_event) 118 .unwrap_or(std::ptr::null_mut()); 119 // TODO(b/315870313): Add safety comment 120 #[allow(clippy::undocumented_unsafe_blocks)] 121 unsafe { 122 Errno::result(libc::epoll_ctl( 123 self.0.as_raw_fd(), 124 op as c_int, 125 fd.as_fd().as_raw_fd(), 126 ptr, 127 )) 128 .map(drop) 129 } 130 } 131 } 132