diff --git a/src/runtime2/poll/mod.rs b/src/runtime2/poll/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..e9240695432169e620a27f5b5a3f36e0efbbf397 --- /dev/null +++ b/src/runtime2/poll/mod.rs @@ -0,0 +1,117 @@ +use libc::{self, c_int}; + +use std::{io, ptr, time}; + +pub(crate) type FileDescriptor = c_int; + +pub(crate) trait AsFileDescriptor { + fn as_file_descriptor(&self) -> FileDescriptor; + +} +pub(crate) struct UserData(u64); + +#[inline] +pub(crate) fn register_polling( + poller: &Poller, entity: F, user: UserData, read: bool, write: bool +) -> io::Result<()> { + let file_descriptor = entity.as_file_descriptor(); + return poller.register(file_descriptor, user, read, write); +} + +#[inline] +pub(crate) fn unregister_polling( + poller: &Poller, entity: F +) -> io::Result<()> { + let file_descriptor = entity.as_file_descriptor(); + return poller.unregister(file_descriptor); +} + +#[cfg(unix)] +pub(crate) struct Poller { + handle: c_int, + events: Vec +} + +// All of this is gleaned from the `mio` crate. +#[cfg(unix)] +impl Poller { + pub fn new(event_capacity: usize) -> io::Result { + assert!(event_capacity < i32::MAX as usize); // because of argument to `epoll_wait`. + let handle = syscall_result(unsafe{ libc::epoll_create1(libc::EPOLL_CLOEXEC) })?; + + return Ok(Self{ + handle, + events: Vec::with_capacity(event_capacity), + }) + } + + fn register(&self, fd: FileDescriptor, user: UserData, read: bool, write: bool) -> io::Result<()> { + let mut event = libc::epoll_event{ + events: Self::events_from_rw_flags(read, write), + u64: user.0, + }; + syscall_result(unsafe{ + libc::epoll_ctl(self.handle, libc::EPOLL_CTL_ADD, fd, &mut event) + })?; + + return Ok(()); + } + + fn unregister(&self, fd: FileDescriptor) -> io::Result<()> { + syscall_result(unsafe{ + libc::epoll_ctl(self.handle, libc::EPOLL_CTL_DEL, fd, ptr::null_mut()) + })?; + + return Ok(()); + } + + pub fn wait(&mut self, timeout: time::Duration) -> io::Result<()> { + // See `mio` for the reason. Works around a linux bug + #[cfg(target_pointer_width = "32")] + const MAX_TIMEOUT: u128 = 1789569; + #[cfg(not(target_pointer_width = "32"))] + const MAX_TIMEOUT: u128 = c_int::MAX as u128; + + let mut timeout_millis = timeout.as_millis(); + if timeout_millis > MAX_TIMEOUT { + timeout_millis = -1; // effectively infinite + } + + let num_events = syscall_result(unsafe{ + libc::epoll_wait(self.handle, self.events.as_mut(), self.events.capacity() as i32, timeout_millis) + })?; + + unsafe{ + debug_assert!(num_events >= 0); + self.events.set_len(num_events as usize); + } + + return Ok(()); + } + + fn events_from_rw_flags(read: bool, write: bool) -> u32 { + let mut events = libc::EPOLLET; + if read { + events |= libc::EPOLLIN | libc::EPOLLRDHUP; + } + if write { + events |= libc::EPOLLOUT; + } + + return events as u32; + } +} + +#[inline] +fn syscall_result(result: c_int) -> io::Result { + if result < 0 { + return Err(io::Error::last_os_error()); + } else { + return Ok(result); + } +} + +#[cfg(not(unix))] +struct Poller { + +} \ No newline at end of file