1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-21 03:39:16 +03:00

emulate poll(2) on macos using select(2)

Refs: https://github.com/wez/wezterm/issues/31
This commit is contained in:
Wez Furlong 2019-06-02 16:15:17 -07:00
parent 090c24554f
commit d000938fef
2 changed files with 116 additions and 4 deletions

View File

@ -1,6 +1,9 @@
// macOS has a broken poll(2) implementation, so we introduce a layer to deal with that here
use libc::pollfd;
use std::time::Duration;
#[cfg(not(target_os = "macos"))]
pub fn poll(pfd: &mut [pollfd], duration: Option<Duration>) -> Result<usize, std::io::Error> {
let poll_result = unsafe {
libc::poll(
@ -17,3 +20,115 @@ pub fn poll(pfd: &mut [pollfd], duration: Option<Duration>) -> Result<usize, std
Ok(poll_result as usize)
}
}
#[cfg(target_os = "macos")]
mod macos {
use super::*;
use libc::{fd_set, timeval, FD_ISSET, FD_SET, FD_SETSIZE, FD_ZERO, POLLERR, POLLIN, POLLOUT};
use std::os::unix::io::RawFd;
struct FdSet {
set: fd_set,
}
fn check_fd(fd: RawFd) {
assert!(fd >= 0);
assert!((fd as usize) < FD_SETSIZE);
}
impl FdSet {
pub fn new() -> Self {
unsafe {
let mut set = std::mem::uninitialized();
FD_ZERO(&mut set);
Self { set }
}
}
pub fn add(&mut self, fd: RawFd) {
check_fd(fd);
unsafe {
FD_SET(fd, &mut self.set);
}
}
pub fn contains(&mut self, fd: RawFd) -> bool {
check_fd(fd);
unsafe { FD_ISSET(fd, &mut self.set) }
}
}
fn materialize<'a>(set: &'a mut Option<FdSet>) -> &'a mut FdSet {
set.get_or_insert_with(FdSet::new)
}
fn set_ptr(set: &mut Option<FdSet>) -> *mut fd_set {
set.as_mut()
.map(|s| &mut s.set as *mut _)
.unwrap_or_else(std::ptr::null_mut)
}
fn is_set(set: &mut Option<FdSet>, fd: RawFd) -> bool {
set.as_mut().map(|s| s.contains(fd)).unwrap_or(false)
}
pub fn poll(pfd: &mut [pollfd], duration: Option<Duration>) -> Result<usize, std::io::Error> {
let mut read_set = None;
let mut write_set = None;
let mut exception_set = None;
let mut nfds = 0;
for item in pfd.iter_mut() {
item.revents = 0;
nfds = nfds.max(item.fd);
if item.events & POLLIN != 0 {
materialize(&mut read_set).add(item.fd);
}
if item.events & POLLOUT != 0 {
materialize(&mut write_set).add(item.fd);
}
materialize(&mut exception_set).add(item.fd);
}
let mut timeout = duration.map(|d| timeval {
tv_sec: d.as_secs() as _,
tv_usec: d.as_micros() as _,
});
let res = unsafe {
libc::select(
nfds + 1,
set_ptr(&mut read_set),
set_ptr(&mut write_set),
set_ptr(&mut exception_set),
timeout
.as_mut()
.map(|t| t as *mut _)
.unwrap_or_else(std::ptr::null_mut),
)
};
if res < 0 {
Err(std::io::Error::last_os_error())
} else {
for item in pfd.iter_mut() {
if is_set(&mut read_set, item.fd) {
item.revents |= POLLIN;
}
if is_set(&mut write_set, item.fd) {
item.revents |= POLLOUT;
}
if is_set(&mut exception_set, item.fd) {
item.revents |= POLLERR;
}
}
Ok(res as usize)
}
}
}
#[cfg(target_os = "macos")]
pub use macos::poll;

View File

@ -1,6 +1,6 @@
use failure::{bail, format_err, Error, Fallible};
use filedescriptor::FileDescriptor;
use libc::{self, pollfd, winsize, POLLIN, POLLNVAL};
use libc::{self, pollfd, winsize, POLLIN};
use signal_hook::{self, SigId};
use std::collections::VecDeque;
use std::fs::OpenOptions;
@ -438,9 +438,6 @@ impl Terminal for UnixTerminal {
if pfd[1].revents != 0 {
let mut buf = [0u8; 64];
if pfd[1].revents & POLLNVAL != 0 {
bail!("poll reports POLLNVAL for tty input");
}
match self.read.read(&mut buf) {
Ok(n) => {
let input_queue = &mut self.input_queue;