Skip to content

Commit

Permalink
Allow registration of custom handles on Windows
Browse files Browse the repository at this point in the history
This commit intends to extend the functionality of mio on Windows to support
custom handles being registered with the internal IOCP object. This in turn
should unlock the ability to work with named pipes, filesystem changes, or any
other IOCP-enabled object on Windows. Named pipes are in particular quite
important as they're often a foundational IPC mechanism on Windows.

This support is provided by exporting two new types in a `windows` module. A
`Binding` serves as the ability to register with the actual IOCP port in an
`Evented` implementation. Internally the `Binding` keeps track of what it
was last associated with to implement IOCP semantics. This may one day be
possible to make a zero-sized-type.

The second type, `Overlapped`, is exported as a contract that all overlapped I/O
operations must be executed with this particular type. This ensures that after
an event is received from an IOCP object we know what to do with it. Essentially
this is just a `OVERLAPPED` with a function pointer after it.

Along the way this exposes the `winapi` crate as a public dependency of `mio`.
The `OVERLAPPED_ENTRY` and `OVERLAPPED` types in `winapi` are exposed through
the `Overlapped` type that mio itself exports.

I've implemented [bindings to named pipes][bindings] and I've also got a
proof-of-concept [process management library][tokio-process] using these
bindings. So far it seems that this support in mio is sufficient for building up
these applications, and it all appears to be working so far.

I personally see this as a much bigger committment on the mio side of things
than the Unix implementation. The `EventedFd` type on Unix is quite small and
minimal, but the `Overlapped` and `binding` types on Windows are just
pieces of a larger puzzle when dealing with overlapped operations. Questions
about ownership of I/O objects arise along with the method of handling
completion status notifications. For now this is essentially binding mio to
stick to at least the same strategy for handling IOCP for the 0.6 series. A
major version bump of mio could perhaps change these semantics, but it may be
difficult to do so.

It seems, though, that the Windows semantics are unlikely to change much in the
near future. The overhead seems to have essentially reached its limit ("bolting
readiness on completion") and otherwise the ownership management seems
negligible.

Closes #252
Closes #320
  • Loading branch information
alexcrichton committed Dec 14, 2016
1 parent 31c2298 commit ac8b7de
Show file tree
Hide file tree
Showing 7 changed files with 332 additions and 156 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ libc = "0.2.16"

[target.'cfg(windows)'.dependencies]
winapi = "0.2.1"
miow = "0.1.3"
miow = "0.1.4"
kernel32-sys = "0.2"

[dev-dependencies]
Expand Down
56 changes: 56 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,62 @@ pub mod unix {
};
}

/// Windows-only extensions to the mio crate.
///
/// Mio on windows is currently implemented with IOCP for a high-performance
/// implementation of asynchronous I/O. Mio then provides TCP and UDP as sample
/// bindings for the system to connect networking types to asynchronous I/O. On
/// Unix this scheme is then also extensible to all other file descriptors with
/// the `EventedFd` type, but on Windows no such analog is available. The
/// purpose of this module, however, is to similarly provide a mechanism for
/// foreign I/O types to get hooked up into the IOCP event loop.
///
/// This module provides two types for interfacing with a custom IOCP handle:
///
/// * `Binding` - this type is intended to govern binding with mio's `Poll`
/// type. Each I/O object should contain an instance of `Binding` that's
/// interfaced with for the implementation of the `Evented` trait. The
/// `register`, `reregister`, and `deregister` methods for the `Evented` trait
/// all have rough analogs with `Binding`.
///
/// Note that this type **does not handle readiness**. That is, this type does
/// not handle whether sockets are readable/writable/etc. It's intended that
/// IOCP types will internally manage this state with a `SetReadiness` type
/// from the `poll` module. The `SetReadiness` is typically lazily created on
/// the first time that `Evented::register` is called and then stored in the
/// I/O object.
///
/// Also note that for types which represent streams of bytes the mio
/// interface of *readiness* doesn't map directly to the Windows model of
/// *completion*. This means that types will have to perform internal
/// buffering to ensure that a readiness interface can be provided. For a
/// sample implementation see the TCP/UDP modules in mio itself.
///
/// * `Overlapped` - this type is intended to be used as the concreate instances
/// of the `OVERLAPPED` type that most win32 methods expect. It's crucial, for
/// safety, that all asynchronous operations are initiated with an instance of
/// `Overlapped` and not another instantiation of `OVERLAPPED`.
///
/// Mio's `Overlapped` type is created with a function pointer that receives
/// a `OVERLAPPED_ENTRY` type when called. This `OVERLAPPED_ENTRY` type is
/// defined in the `winapi` crate. Whenever a completion is posted to an IOCP
/// object the `OVERLAPPED` that was signaled will be interpreted as
/// `Overlapped` in the mio crate and this function pointer will be invoked.
/// Through this function pointer, and through the `OVERLAPPED` pointer,
/// implementations can handle management of I/O events.
///
/// When put together these two types enable custom Windows handles to be
/// registered with mio's event loops. The `Binding` type is used to associate
/// handles and the `Overlapped` type is used to execute I/O operations. When
/// the I/O operations are completed a custom function pointer is called which
/// typically modifies a `SetReadiness` set by `Evented` methods which will get
/// later hooked into the mio event loop.
#[cfg(windows)]
pub mod windows {

pub use sys::{Overlapped, Binding};
}

// Conversion utilities
mod convert {
use std::time::Duration;
Expand Down
2 changes: 2 additions & 0 deletions src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub use self::windows::{
TcpStream,
TcpListener,
UdpSocket,
Overlapped,
Binding,
};

#[cfg(windows)]
Expand Down
7 changes: 2 additions & 5 deletions src/sys/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,6 @@ use std::os::windows::prelude::*;
use kernel32;
use winapi;

use self::selector::Overlapped;

mod awakener;
#[macro_use]
mod selector;
Expand All @@ -153,7 +151,7 @@ mod from_raw_arc;
mod buffer_pool;

pub use self::awakener::Awakener;
pub use self::selector::{Events, Selector};
pub use self::selector::{Events, Selector, Overlapped, Binding};
pub use self::tcp::{TcpStream, TcpListener};
pub use self::udp::UdpSocket;

Expand All @@ -169,8 +167,7 @@ fn wouldblock() -> io::Error {
unsafe fn cancel(socket: &AsRawSocket,
overlapped: &Overlapped) -> io::Result<()> {
let handle = socket.as_raw_socket() as winapi::HANDLE;
let overlapped = overlapped.get_mut().raw();
let ret = kernel32::CancelIoEx(handle, overlapped);
let ret = kernel32::CancelIoEx(handle, overlapped.as_mut_ptr());
if ret == 0 {
Err(io::Error::last_os_error())
} else {
Expand Down
Loading

0 comments on commit ac8b7de

Please sign in to comment.