Skip to content

Conversation

@aturon
Copy link
Contributor

@aturon aturon commented Jul 1, 2016

Here's a first, quick attempt at mio reintegration.

It mostly matches what we discussed, in terms of using global lock-free token allocation, and a hashmap for dispatch.

The interesting new piece is trying to tease out the readiness vs completion story. I introduced a notion of "ReadinessFuture", which is one where basically poll is meaningless but schedule waits for readiness. Then there's a completion layer that lets you couple a readiness future with a custom poll which will attempt completion.

In principle, this should allow us to expose readiness futures at the lowest levels, and then pair them up with completions to give you various higher-level APIs (without being locked into those).

This should also be useful for things like writing a proxy server, where you might want to join a pair of readiness futures (one for reading from one socket, one for writing to another), and then complete with an action that uses both sockets.

Curious what you think of this factoring!

@aturon
Copy link
Contributor Author

aturon commented Jul 1, 2016

cc @carllerche

@aturon
Copy link
Contributor Author

aturon commented Jul 1, 2016

Oh, and I should say that if we do want the whole readiness/completion concepts, obviously it should move into the futures library proper.

}
pub type IoFuture<T> = Future<Item=T, Error=io::Error>;

#[derive(Clone)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the long run we probably don't want impl Clone for TcpListener I think, it seems like it could get into some uncomfortably weird situations if you can silently clone an I/O object. (the actual impl details here look good to me though)

@aturon
Copy link
Contributor Author

aturon commented Jul 1, 2016

So, thinking more, I don't think that readiness actually makes sense to expose directly as a Future. Once you start thinking about how you'd want to use the combinators to build something useful on top of a future exposing readiness, it goes off the rails a bit -- basically, you have to introduce some new, readiness-specific combinator to make it work. And many of the normal combinators (like and_then) just don't make sense for pure readiness.

I think we should instead consider exposing a first-class readiness abstraction, which supports schedule and provides at least two combinators: select and join. Given one of these readiness things (whatever we call it), you can construct a full-fledged future by pairing it with a poll implementation -- but you have to be careful to ensure that the events of interest are perfectly matched between the schedule and poll, to avoid lost wakeups.

Note that join is particularly useful for things like building a proxy, where you want a readiness signal when both a read and a write event are ready -- and then you try to complete. If you fail to complete, you want to loop back again, using the schedule from the read and write events.

Right now, the code sort of contains these abstractions, but they're awkwardly built on top of Futures. I'm increasingly feeling like the right thing to do is actually expose a readiness abstraction under Futures -- possibly via a Schedule super-trait?

@aturon
Copy link
Contributor Author

aturon commented Jul 1, 2016

OK, I pushed an update to this that cleans things up quite a bit.

As I was arguing above, I'm moving away from using Futures to directly signal readiness. But instead of trying to introduce a completely generic readiness abstraction right now, I just introduced MioEvent as an abstraction for readiness around leaf mio events -- i.e., Evented things.

Given a MioEvent, you can get a MioFuture by pairing it with a method that attempts to complete (basically like poll, but without any tokens).

This all works like a charm, and makes it possible to wrap the base readiness events in custom leaf futures (that expose various buffering strategies, etc).

The thing that's missing here is any notion of compositionality at the MioEvent level (or perhaps an even more general version of the abstraction). That might be something we want eventually, but we can cross that bridge later.

@aturon
Copy link
Contributor Author

aturon commented Jul 1, 2016

I want to explore using TLS to talk to the event loop fairly soon -- that's an essential enough piece, with enough unknowns, that I'd like to get it squared away early.


fn _await(&mut self, done: &mut FnMut() -> bool) {
while !done() {
pub fn run(&mut self) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be worth getting cargo test to work in this directory perhaps to see how this hooks up with driving a future

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Had limited time so wanted to get a draft in front of you first, but once you're OK with the general strategy here I'll wrap up the final bits.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah everything's looking good to me, what I expected!

@aturon
Copy link
Contributor Author

aturon commented Jul 10, 2016

Closing this out. Will open a fresh PR with the new version.

@aturon aturon closed this Jul 10, 2016
alexcrichton pushed a commit that referenced this pull request May 25, 2017
A nice way of working with tasks without allocations.
withoutboats pushed a commit that referenced this pull request Mar 25, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants