-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AsyncFd doesn't allow to decide read/write interest making kqueue's fd fail in tokio 0.3 #3072
Comments
I don't see you creating the AsyncFd in your snippet. Can you include that? My guess is that we must filter the interest for some FDs. Can you show me how you were using PollEvented? |
Note that I'm using the same code for both timer_fd and kqueue timers. It only fails on mac during creation of The way I use it: impl<T: TimerFd> Future for AsyncTokioTimer<T> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Self::Output> {
if let State::Init(ref timeout) = &self.state {
let mut fd = AsyncFd::new(T::new()).expect("To create AsyncFd");
fd.get_mut().set(*timeout);
self.state = State::Running(fd, false)
};
if let State::Running(ref mut fd, ref mut state) = &mut self.state {
if *state {
return task::Poll::Ready(());
}
let fd = Pin::new(fd);
match fd.poll_read_ready(ctx) {
task::Poll::Pending => return task::Poll::Pending,
task::Poll::Ready(ready) => {
let mut ready = ready.expect("Unable to read async timer's fd");
//technically we should read first, but we cannot borrow as mut then
ready.clear_ready();
match fd.get_mut().get_mut().read() {
0 => {
*state = false;
return task::Poll::Pending
},
_ => {
*state = true;
return task::Poll::Ready(())
}
}
}
}
} else {
unreach!();
}
}
} It is pretty close to what I had with PollEvented (shorter snippet) let fd = Pin::new(fd);
match fd.poll_read_ready(ctx, mio::Ready::readable()) {
task::Poll::Pending => return task::Poll::Pending,
task::Poll::Ready(ready) => match ready.map(|ready| ready.is_readable()).expect("kqueue cannot be ready") {
true => {
let _ = fd.clear_read_ready(ctx, mio::Ready::readable());
match fd.get_mut().get_mut().read() {
0 => return task::Poll::Pending,
_ => return task::Poll::Ready(()),
}
}
false => return task::Poll::Pending,
},
} |
How did you create the |
It is similar Here full definition of wrapper struct RawTimer(c_int);
impl RawTimer {
fn new() -> Self {
let fd = nix::sys::event::kqueue().unwrap_or(-1);
//If you hit this, then most likely you run into OS imposed limit on file descriptor number
os_assert!(fd != -1);
Self(fd)
}
fn set(&self, time: time::Duration) {
use nix::sys::event::*;
let flags = EventFlag::EV_ADD | EventFlag::EV_ENABLE | EventFlag::EV_ONESHOT;
let mut time = time.as_nanos();
let mut unit = FilterFlag::NOTE_NSECONDS;
if time > isize::max_value() as u128 {
unit = FilterFlag::NOTE_USECONDS;
time /= 1_000;
}
if time > isize::max_value() as u128 {
unit = FilterFlag::empty(); // default is milliseconds
time /= 1_000;
}
if time > isize::max_value() as u128 {
unit = FilterFlag::NOTE_SECONDS;
time /= 1_000;
}
let time = time as isize;
kevent(self.0, &[KEvent::new(1, EventFilter::EVFILT_TIMER, flags, unit, time, 0)], &mut [], 0).expect("To arm timer");
}
fn unset(&self) {
use nix::sys::event::*;
let flags = EventFlag::EV_DELETE;
kevent(self.0, &[KEvent::new(1, EventFilter::EVFILT_TIMER, flags, FilterFlag::empty(), 0, 0)], &mut [], 0).expect("To disarm timer");
}
fn read(&self) -> usize {
use nix::sys::event::*;
let mut ev = [KEvent::new(0, EventFilter::EVFILT_TIMER, EventFlag::empty(), FilterFlag::empty(), 0, 0)];
kevent(self.0, &[], &mut ev[..], 0).expect("To execute kevent")
}
}
impl mio::Evented for RawTimer {
fn register(&self, poll: &mio::Poll, token: mio::Token, mut interest: mio::Ready, opts: mio::PollOpt) -> io::Result<()> {
interest.remove(mio::Ready::writable());
mio::unix::EventedFd(&self.0).register(poll, token, interest, opts)
}
fn reregister(&self, poll: &mio::Poll, token: mio::Token, mut interest: mio::Ready, opts: mio::PollOpt) -> io::Result<()> {
interest.remove(mio::Ready::writable());
mio::unix::EventedFd(&self.0).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
mio::unix::EventedFd(&self.0).deregister(poll)
}
} |
Once #3130 lands, we can add |
Adds function to await for readiness on the TcpStream and non-blocking read/write functions. `async fn TcpStream::ready(Interest)` waits for socket readiness satisfying **any** of the specified interest. There are also two shorthand functions, `readable()` and `writable()`. Once the stream is in a ready state, the caller may perform non-blocking operations on it using `try_read()` and `try_write()`. These function return `WouldBlock` if the stream is not, in fact, ready. The await readiness function are similar to `AsyncFd`, but do not require a guard. The guard in `AsyncFd` protect against a potential race between receiving the readiness notification and clearing it. The guard is needed as Tokio does not control the operations. With `TcpStream`, the `try_read()` and `try_write()` function handle clearing stream readiness as needed. This also exposes `Interest` and `Ready`, both defined in Tokio as wrappers for Mio types. These types will also be useful for fixing #3072 . Other I/O types, such as `TcpListener`, `UdpSocket`, `Unix*` should get similar functions, but this is left for later PRs. Refs: #3130
Version 0.3.2
Platform macOS 10.15
Description
Creating AsyncFd for such file description fails with:
Os { code: 22, kind: InvalidInput, message: "Invalid argument" }
The problem is probably in underlying mio, but I'm not sure how much changed since 0.6
The thing is PollEvented + Mio worked just fine, hence it can be considered a regression?
Just to elaborate: it is the way to create kqueue based timer, similar to linux's timer_fd
I was using
nix
to get simplified API over kqueue, but it works with 0.2's PollEventedI use my own wrapper for timer and implement AsRawFd for it.
The text was updated successfully, but these errors were encountered: