#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EPollFileDescriptor(RawFd);
impl Drop for EPollFileDescriptor
{
#[inline(always)]
fn drop(&mut self)
{
self.0.close()
}
}
impl AsRawFd for EPollFileDescriptor
{
#[inline(always)]
fn as_raw_fd(&self) -> RawFd
{
self.0
}
}
impl IntoRawFd for EPollFileDescriptor
{
#[inline(always)]
fn into_raw_fd(self) -> RawFd
{
self.0
}
}
impl FromRawFd for EPollFileDescriptor
{
#[inline(always)]
unsafe fn from_raw_fd(fd: RawFd) -> Self
{
Self(fd)
}
}
impl FileDescriptor for EPollFileDescriptor
{
}
impl EPollFileDescriptor
{
#[inline(always)]
pub fn new() -> Result<Self, CreationError>
{
use self::CreationError::*;
let result = unsafe { epoll_create1(EPOLL_CLOEXEC) };
if likely!(result >= 0)
{
Ok(EPollFileDescriptor(result))
}
else if likely!(result == -1)
{
Err
(
match errno().0
{
EMFILE => PerProcessLimitOnNumberOfFileDescriptorsWouldBeExceeded,
ENFILE => SystemWideLimitOnTotalNumberOfFileDescriptorsWouldBeExceeded,
ENOMEM => KernelWouldBeOutOfMemory,
EINVAL => panic!("Invalid value specified in flags"),
_ => unreachable_code(format_args!("")),
}
)
}
else
{
unreachable_code(format_args!(""))
}
}
#[inline(always)]
pub fn wait<'a>(&self, events: &'a mut [epoll_event], time_out: EPollTimeOut) -> Result<&'a [epoll_event], EPollWaitError>
{
let length = events.len();
debug_assert_ne!(length, 0, "events.len() can not be zero");
debug_assert!(length <= i32::MAX as usize, "events.len() can not exceed i32::MAX");
let result = unsafe { epoll_wait(self.0, events.as_mut_ptr(), length as i32, time_out.into()) };
if likely!(result >= 0)
{
Ok(&events[0 .. result as usize])
}
else if likely!(result == -1)
{
match errno().0
{
EINTR => Err(EPollWaitError::Interrupted),
EBADF => panic!("`epfd` is not a valid file descriptor"),
EFAULT => panic!("Memory for events was not writable"),
EINVAL => panic!("`epfd` is not an epoll file descriptor"),
_ => unreachable_code(format_args!("")),
}
}
else
{
unreachable_code(format_args!(""));
}
}
#[inline(always)]
pub fn wait_signalled<'a>(&self, events: &'a mut [epoll_event], time_out: EPollTimeOut, signal_mask: &sigset_t) -> Result<&'a [epoll_event], EPollWaitError>
{
let length = events.len();
debug_assert_ne!(length, 0, "events.len() can not be zero");
debug_assert!(length <= i32::MAX as usize, "events.len() can not exceed i32::MAX");
let result = unsafe { epoll_pwait(self.0, events.as_mut_ptr(), length as i32, time_out.into(), signal_mask) };
if likely!(result >= 0)
{
Ok(&events[0 .. result as usize])
}
else if likely!(result == -1)
{
match errno().0
{
EINTR => Err(EPollWaitError::Interrupted),
EBADF => panic!("`epfd` is not a valid file descriptor"),
EFAULT => panic!("Memory for events was not writable"),
EINVAL => panic!("`epfd` is not an epoll file descriptor"),
_ => unreachable_code(format_args!("")),
}
}
else
{
unreachable_code(format_args!(""));
}
}
#[inline(always)]
pub fn add(&self, fd: RawFd, flags: EPollAddFlags, token: u64) -> Result<(), EPollAddError>
{
let mut event = epoll_event
{
events: flags.bits,
data: epoll_data_t
{
u64: token,
},
};
use self::EPollAddError::*;
let result = unsafe { epoll_ctl(self.0, EPOLL_CTL_ADD, fd, &mut event) };
if likely!(result == 0)
{
Ok(())
}
else if likely!(result == -1)
{
Err
(
match errno().0
{
ENOMEM => ThereWasInsufficientKernelMemory,
ENOSPC => LimitOnWatchesWouldBeExceeded,
EBADF => panic!("The supplied file descriptor was not a valid file descriptor"),
EEXIST => panic!("The supplied file descriptor was already registered with this epoll instance"),
EINVAL => panic!("Can not add epoll file descriptor to its self, or can not make wait on an epoll file descriptor `EPOLLEXCLUSIVE`"),
ELOOP => panic!("The supplied file descriptor is for an epoll instance and this operation would result in a circular loop of epoll instances monitoring one another"),
EPERM => panic!("The supplied file descriptor does not support epoll (perhaps it is an open regular file or the like)"),
_ => unreachable_code(format_args!("")),
}
)
}
else
{
unreachable_code(format_args!(""))
}
}
#[inline(always)]
pub fn modify(&self, fd: RawFd, flags: EPollModifyFlags, token: u64) -> Result<(), EPollModifyError>
{
let mut event = epoll_event
{
events: flags.bits,
data: epoll_data_t
{
u64: token,
},
};
let result = unsafe { epoll_ctl(self.0, EPOLL_CTL_MOD, fd, &mut event) };
if likely!(result == 0)
{
Ok(())
}
else if likely!(result == -1)
{
match errno().0
{
ENOMEM => Err(EPollModifyError::ThereWasInsufficientKernelMemory),
EBADF => panic!("The supplied file descriptor was not a valid file descriptor"),
EINVAL => panic!("Supplied file descriptor was not usable or there was the presence or absence of `Exclusive` when required"),
ENOENT => panic!("The supplied file descriptor is not registered with this epoll instance"),
EPERM => panic!("The supplied file descriptor does not support epoll (perhaps it is an open regular file or the like)"),
_ => unreachable_code(format_args!("")),
}
}
else
{
unreachable_code(format_args!(""))
}
}
#[inline(always)]
pub fn delete(&self, fd: RawFd)
{
let result = unsafe { epoll_ctl(self.0, EPOLL_CTL_DEL, fd, null_mut()) };
if likely!(result == 0)
{
}
else if likely!(result == -1)
{
match errno().0
{
ENOMEM => panic!("Examination of the Linux source code fs/eventpoll.c suggests `ENOMEM` should not occur for `EPOLL_CTL_DEL`"),
EBADF => panic!("The supplied file descriptor was not a valid file descriptor"),
EINVAL => panic!("Supplied file descriptor was not usable"),
ENOENT => panic!("The supplied file descriptor is not registered with this epoll instance"),
EPERM => panic!("The supplied file descriptor does not support epoll (perhaps it is an open regular file or the like)"),
_ => unreachable_code(format_args!("")),
}
}
else
{
unreachable_code(format_args!(""))
}
}
pub fn information(&self) -> io::Result<(FileDescriptorInformationHeader, impl Iterator<Item=EPollInformationItem>)>
{
struct InformationIterator
{
buffered_reader: Option<BufReader<File>>,
bytes_read: ArrayVec<u8, MaximumBytesPerLine>,
}
impl Iterator for InformationIterator
{
type Item = EPollInformationItem;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item>
{
if self.buffered_reader.is_none()
{
return None
}
let result =
{
let buffered_reader = self.buffered_reader.as_mut().unwrap();
self.bytes_read.read_until_line_feed(buffered_reader)
};
match result
{
Err(_) =>
{
self.buffered_reader = None;
None
}
Ok(0) =>
{
self.buffered_reader = None;
None
}
Ok(_) => match self.parse_line()
{
Err(_) =>
{
self.buffered_reader = None;
None
}
Ok(epoll_information_item) =>
{
self.bytes_read.clear();
Some(epoll_information_item)
}
}
}
}
}
impl InformationIterator
{
fn parse_line(&mut self) -> io::Result<EPollInformationItem>
{
let remaining_bytes = &self.bytes_read[..];
let (target_file_descriptor, remaining_bytes) = extract_fixed_width_value_from_slice(remaining_bytes, b"tfd: ", 8, |string| RawFd::from_str_radix(string.trim_start(), 10))?;
let (event_flags, remaining_bytes) = extract_fixed_width_value_from_slice(remaining_bytes, b" events: ", 8, |string| u32::from_str_radix(string.trim_start(), 16))?;
let (token, remaining_bytes) = extract_fixed_width_value_from_slice(remaining_bytes, b" data: ", 16, |string| u64::from_str_radix(string.trim_start(), 16))?;
let (position, remaining_bytes) = extract_space_terminated_value_from_slice(remaining_bytes, b" pos:", |string| i64::from_str_radix(string, 10))?;
let (inode, remaining_bytes) = extract_space_terminated_value_from_slice(remaining_bytes, b"ino:", |string| isize::from_str_radix(string, 16))?;
let (sdevice, remaining_bytes) = extract_space_terminated_value_from_slice(remaining_bytes, b"sdev:", |string| u32::from_str_radix(string, 16))?;
if unlikely!(remaining_bytes.len() != 0)
{
return Err(invalid_data())
}
Ok
(
EPollInformationItem
{
target_file_descriptor,
event_flags,
token,
position,
inode,
sdevice,
}
)
}
}
let process_identifier = unsafe { getpid() };
let mut buffered_reader = BufReader::new(File::open(&format!("/proc/{}/fdinfo/{}", process_identifier as u32, self.0 as u32))?);
const MaximumBytesPerLine: usize = 256;
let mut bytes_read: ArrayVec<u8, MaximumBytesPerLine> = ArrayVec::new();
let header = FileDescriptorInformationHeader
{
file_offset: bytes_read.parse_header_line(&mut buffered_reader, b"pos:\t", |string| isize::from_str_radix(string, 10))?,
file_flags: bytes_read.parse_header_line(&mut buffered_reader, b"flags:\t", |string| u32::from_str_radix(string, 8))?,
mount_identifier: bytes_read.parse_header_line(&mut buffered_reader, b"mnt_id:\t", |string| usize::from_str_radix(string, 16))?,
};
Ok((header, InformationIterator { buffered_reader: Some(buffered_reader), bytes_read }))
}
}