-
Notifications
You must be signed in to change notification settings - Fork 735
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
Add API to hook into Windows IOCP loop #1345
Conversation
a5b18bc
to
61f3672
Compare
An API similar to the one of mio 0.6 is now available so that custom objects can be added to the IOCP that is used to implement Poll.
This is a first test to play with the Windows IOCP handler API.
61f3672
to
bccc605
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm only looked at the surface level API, not at the implementation and added a few comment.
Overall I'm wondering if its possible to create something more closely resembling the SourceFd
API but for RawHandle
s. So far we exposed very little of the internals of Mio, e.g. no epoll
/kqueue
specific types but this exposed quite a lot of IOCP I'm not sure about that.
/// * `Binding` - this type is intended to govern binding with mio's `Registry` | ||
/// type. Each I/O object should contain an instance of `Binding` that's | ||
/// interfaced with for the implementation of the `Source` trait. The | ||
/// `register`, `reregister`, and `deregister` methods for the `Source` trait |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that Binding
only has register_handle
, how would I implement de/re-register?
Or this is that part simply not implemented yet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its not implemented yet because I'm unsure how that should/could be done as in Windows, there is no way of removing a handle from an IOCP except by closing that handle. So the only thing we should do here is to remove the handler from the list of IO handlers, which should be done by a Drop implementation of that type. reregistration would only support changing the token; I will add both functionalities.
/// I/O operations that are registered with mio's event loop. When the I/O | ||
/// operation associated with an `OVERLAPPED` pointer completes the event | ||
/// loop will invoke the object provided as `callback`. | ||
pub fn new<C: CompletionCallback+'static>(callback: C) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need a trait with a single method? Can't we useC: FnMut(&OVERLAPPED_ENTRY) + static
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also can't Mio provide that implementation? Or is there special handling per type of handle?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was torn on this too, but I've chosen to introduce a trait because it can be extended in a source-compatible way by adding more trait methods with default impls if we find more cases of hooks that could be costomized by mio drivers (e.g. a "pre-poll hook").
There is a blanket impl for FnMuts as well, so they can be used, see the provided sample test case, which is doing exactly that.
/// The callback will be called once the operation that it is bound to via a call to | ||
/// `Overlapped::new` has been completed and the IOCP is signalled. By returning a `Readiness` | ||
/// value, the callback may deliver a `mio::Event` to the user. | ||
fn complete_operation(&mut self, entry: &OVERLAPPED_ENTRY) -> Option<Readiness>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally not the biggest fan out exposing types from different crates, especially when those crates don't have a stable API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that this happens here: This method resembles the old fn(&OVERLAPPED_ENTRY) from mio 0.6, so except from the OVERLAPPED_ENTRY (which is a stable Windows API struct) we do not expose other types. But maybe I misunderstand your comment.
inner: UnsafeCell<miow::Overlapped>, | ||
#[cfg(debug_assertions)] | ||
canary: u32, | ||
callback: Box<dyn CompletionCallback>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe fn(&OVERLAPPED_ENTRY) -> Option<Readiness>
would be better, than we don't have to allocate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is correct. I've chosen to use a boxed trait here because I was looking at mio_named_pipes which does some pointer magic to get its state back inside the callback functions fore read, write and connect. By using dynamic dispatch, we can avoid that and have more safe code, but if resource usage beats that argument (plus the "future hooks" argument from above) I'd be willing to change this to a pure function pointer.
/// | ||
/// Won't actually do anything until `register_handle` has been called during a call to | ||
/// `Registry::register`. | ||
pub fn new(registry: &Registry, token: Token) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why you don't require an event::Source
on creation of Binding
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I just need the handle, not the whole source. I split this into two parts as I wanted to give the user a chance to store the Binding
instance before it starts firing events, so the usual sequence in a driver is:
- Initialize the
Option<Binding>
member of your custom struct withNone
- Inside
Source::register
, you then set it toSome(Binding::new(..))
- then* give it the handle to work with using
register_handle
/// Check whether the provided registry is the same as the one used during creation of the | ||
/// binding. | ||
/// | ||
/// IOCP driven sources cannot unregister from their completion port before they get closed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No event::Source
in Mio can be registered with another Poll
, so this is true for all platforms. See IoSource
's selector_id
field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see, so this is redundand and can be removed. I'll do that.
/// completed IO operation, this method can be used to mark the associated token as ready, e.g. | ||
/// in case it is necessary to signal an initial readiness before any IO operation has been | ||
/// scheduled. | ||
pub fn inject_event(&self, readiness: Readiness) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about exposing such a function. Is the case you describe in the docs real? I personally never needed that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While working on that PR, I used mio_named_pipes as a sample driver and forward ported it. There were cases (example: Establishing the first read after a successful connection) where in case of an error an immediate readiness flag was raised to be able to deliver the error. I also would love to get rid of that call as it requires a "side channel" for events but I couldn't think of a better solution yet.
/// | ||
/// This can be useful when only a shared borrow is held and the overlapped | ||
/// pointer needs to be passed down to winapi. | ||
pub fn as_mut_ptr(&self) -> *mut OVERLAPPED { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't really expose non-Mio types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack, will change that one.
@darkwisebear having though about this a bit more I think trying to |
The major problem I see when trying to mimic The only idea I have here is to provide premade All in all I'd say I agree with starting with a leaner API but I'm convinced that IOCP and |
@carllerche @Thomasdezeeuw Is this approach still relevant / desired in any form? |
@carllerche can you look at this? Seeing as an API akin to |
@darkwisebear do you plan on continuing this work? |
@Thomasdezeeuw Since there was no additional feedback I didn't pursue this PR any further but used an internal patched version of mio for my projects. Now there is tokio-rs/tokio#3760 which provides |
I'm afraid I'm not familiar enough with Windows to answer that. Also, sorry about the lack of feedback, I've been a bit busy over the last year or so, but I have a bit more time for Mio now. |
I need to know two things:
The core question now is: How/who can we answer those two questions? I would feel a lot more comfortable continuing to work if those were answered.
No worries, it was meant as an offer to the community in case this was needed, didn't have strong feelings about merging that. If there is still need, we can continue working on it. |
I can answer this one at least. I subscribed to this issue ages ago, because I was looking to integrate windows Filter Ports ( |
@roblabla could you maybe describe some (rough) requirement that you would need for Mio to work cleanly, and if this pr would (partially) implement those requirements. |
I'm in a similar situation to @roblabla here. |
I'm closing this due to inactivity. @darkwisebear if you, or anyone else, want to continue this please don't feel discouraged to do so. Thank you @darkwisebear for your efforts here. |
@Thomasdezeeuw I have a use case where I need to support some custom sockets on windows. I would be willing to contribute to getting an API like this in place for mio. |
Sounds good. I can't offer much help with the implementation as I'm no Windows expert, but some do sometimes visit our Mio discord channel so you could ask for help there. |
An API similar to the one of mio 0.6 is now available so that custom objects can be added to the IOCP that is used to implement Poll. Relates to #1047.
This change isn't complete yet but in a draft state as I wanted to gather some feedback on the suggested API.
Since sockets are polled by using a single connection to afd.sys, there is no 1:1 relationship between a user-provided token and the completion key of the IOCP. The implementation therefore uses a slab to keep track of the registered handlers and the mapping of the user-provided token to the reported events.
Other notable things about the change: