Remove mio types from the Tokio public API. In practice, this means removing Registration and PollEvented. Some users currently depend on these types in order to integrate custom resources with Tokio. Instead of providing hook points based on mio, we would provide platform-specific "low level" resource types. For example, on *nix systems, we could provide an AsyncFd type that provides access to readiness notifications.
Tokio cannot include public API dependencies on third party crates that do not match Tokio's stability guarantees. The Mio crate is not yet at a 1.0 release. We would like to be able to release Tokio 1.0 without blocking on Mio for 1.0. Additionally, we may want to change the low-level implementation details. For example, we may wish to switch the underlying implementation to an io_uring based backend or we may wish to provide windows integration directly instead of using the Mio compatibility layer.
Given the open questions, the safest path forward is to decouple Tokio from Mio at this point in time.
The PollEvented and Registration types are made private. In order to provide the ability to integrate with Tokio, platform-specific low-level resource types are provided. On *nix platforms, an AsyncFd type is provided.
struct AsyncFd { ... }
impl AsyncFd {
// Create a new `AsyncFd`. This registers the FD w/ the io-driver with the
// given `Interest`.
pub fn new(fd: RawFd, interest: Interest) -> AsyncFd;
// Awaits for a readiness event covering any of the given `Interest`. The
// `Interest` provided here must be a subset of the `Interest` supplied to
// `new`.
pub async fn readiness(&self, interest: Interest) -> io::Result<Readiness>;
}
struct Interest(mio::Interest);
struct Readiness(mio::Readiness);
Usage would look something like this:
let interest = Interest::READABLE
.add(Interest::WRITEABLE);
let my_fd = AsyncFd::new(fd, interest);
// Await only *read* interest
my_fd.readiness(Interest::READABLE).await;
This would only provide extensibility on *nix platforms. Extending Windows platforms would require a completely different API. As of now, Mio does not have a strategy for extending windows platforms. We would need to wait until one is developed.
Another option would be to move those types into a separate crate tokio_mio_interop or something. (Export them from tokio 1.0 under a hidden module, and then have that crate re-export them publically).
I believe you are referring to Interest and Readiness here. This is true and worth considering.
The biggest impact of this proposal would be that one would no longer be able to take a T: mio::Evented and make it work with Tokio. This means that any existing Mio based "resource" type could not be made to work with Tokio.
I believe that this will cause a bit of short/medium term turmoil but the AsyncFd is a better long term strategy.
I really would like "proper" windows async, that is some form of overlapped file IO, rather than threadpooled sync IO. IPC with named pipes in particular basically requires overlapped file IO, and I found it very hard to wrap manually wrapped file APIs so that they could be integrated with other tokio async APIs - my current code feels incredibly fragile.
Not sure what that would look like if you really want to be extensible in an AsyncFd style, maybe - AsyncRawHandle that is an fd on unix, and a IOCP-compatible HANDLE on windows?
I've started work on this issue. A couple thoughts:
AsyncFd::new() be unsafe? While it's not immediately obvious whether there are memory safety issues here, passing an unowned file descriptor to AsyncFd, closing it, and opening a new fd with the same file descriptor number could result in behavior that is undefined, with respect to tokio's API contract (for analogous reasons, std::os::unix::io::FromRawFd::from_raw_fd is unsafe)AsRawFd to allow for safer RAII patterns here? That is, this would discourage (but not entirely prevent) problems like closing the fd while it is registered or multiple registrations of the same fd.I don't think it needs to be unsafe. I don't believe there is any actual unsafety w.r.t working with an FD. I believe it is left over from early rust days when "unsafe" was used for things that were not strictly memory unsafe.
I don't think AsyncFd should close the FD or anything like that on drop. I would lean towards just taking a RawFd but don't have a strong opinion there.
Relevant to the unsafety of FromRawFd:
I don't think AsyncFd should close the FD or anything like that on drop. I would lean towards just taking a RawFd but don't have a strong opinion there.
Curious around the motivation against having AsyncFd take ownership and close the fd on drop? I think taking this approach will lead to awkward juggling between an AsyncFd instance and its actual owner and making sure they are dropped in the correct order.
If the contract is such that ownership of the fd is taken, it will largely do the right thing by default when dropped. We already have the IntoRawFd trait for taking ownership back in case the caller does not want to close the fd quite yet
cc @bdonlan since you're working on this :)
A PR for AsyncFd has been submitted. Merging this PR is not critical for the initial 0.3 release. We can merge it in after 0.3.
To close this issue, we need to audit tokio and ensure mio types have been removed from all public APIs.
I scanned and it looks like this is done.