Skip to content
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

I/O resources lazily bind to reactor. #160

Merged
merged 1 commit into from
Feb 28, 2018

Conversation

carllerche
Copy link
Member

This patch makes a significant change to how I/O resources bind to a
reactor. Currently, an I/O resource (TCP, UDP, PollEvented) will bind
itself with a reactor upon creation.

First, some history.

Originally, tokio-core required that I/O resources be explicitly
associated with a reactor upon creation by passing in a &Handle. Tokio
reform introduced a default reactor. If I/O resources do not specify a
reactor upon creation, then the default reactor is used.

However, futures tend to favor being lazy. Creating a future should do
no work, instead it is defining a computation to be performed once the
future is executed. Binding an I/O resource with a reactor on creation
goes against this pattern.

This patch fixes this by allowing I/O resources to lazily bind to a
reactor. An explicit &Handle can still be used on creation, but if no
reactor is specified, then the default reactor is used. However, this
binding happens during execution time (read / write) and not creation.

This patch makes a significant change to how I/O resources bind to a
reactor. Currently, an I/O resource (TCP, UDP, PollEvented) will bind
itself with a reactor upon creation.

First, some history.

Originally, tokio-core required that I/O resources be explicitly
associated with a reactor upon creation by passing in a `&Handle`. Tokio
reform introduced a default reactor. If I/O resources do not specify a
reactor upon creation, then the default reactor is used.

However, futures tend to favor being lazy. Creating a future should do
no work, instead it is defining a computation to be performed once the
future is executed. Binding an I/O resource with a reactor on creation
goes against this pattern.

This patch fixes this by allowing I/O resources to lazily bind to a
reactor. An explicit `&Handle` can still be used on creation, but if no
reactor is specified, then the default reactor is used. However, this
binding happens during execution time (read / write) and not creation.
/// idea. Concurrent calls to `register` will attempt to register different
/// tasks to be notified. One of the callers will win and have its task set,
/// but there is no guarantee as to which caller will succeed.
pub fn register_task(&self, task: Task) {
Copy link
Member Author

Choose a reason for hiding this comment

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

This file was copied from the futures crate in order to get this fn.

A PR has been submitted.

@@ -117,6 +118,8 @@ impl Drop for Background {
None => return,
};

inner.shutdown_now();
Copy link
Member Author

Choose a reason for hiding this comment

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

This fixes an unrelated bug found while testing this change.

/// Try to get a handle to the current reactor.
///
/// Returns `Err` if no handle is found.
pub(crate) fn try_current() -> io::Result<Handle> {
Copy link
Member Author

Choose a reason for hiding this comment

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

It might be useful to expose this as a public API, but for now it is kept private.

/// Essentially a good rule of thumb is that if you're using the `poll_ready`
/// method you want to also use `need_read` to signal blocking and you should
/// otherwise probably avoid using two tasks on the same `PollEvented`.
pub struct PollEvented<E> {
Copy link
Member Author

Choose a reason for hiding this comment

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

The existing PollEvented type is not compatible with lazy binding due to how the trait bounds are setup. Because of this, a new type must be introduced and the existing PollEvented deprecated.

This also reverts some aspects of PollEvented back to what they were originally in tokio-core. Adding lazy binding reduced some of the benefits of limiting fns to &mut self.

/// The difficulty is due to the fact that a single registration drives two
/// separate tasks -- A read half and a write half.
#[derive(Debug)]
pub struct Registration {
Copy link
Member Author

Choose a reason for hiding this comment

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

Registration is introduced as the most minimal API that is required to integrate with the reactor. It is only pair of readiness streams (one for read, one for write).

@@ -530,7 +552,7 @@ impl Inner {
Direction::Write => (&sched.writer, mio::Ready::writable()),
};

task.register();
task.register_task(t);
Copy link
Member

Choose a reason for hiding this comment

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

task is an AtomicTask? Not a big deal, just reads a little funny.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree, but I also didn't want to have to re-run CI :P I'll try to bundle this up w/ another PR.

@carllerche carllerche merged commit 2eabc37 into tokio-rs:master Feb 28, 2018
@carllerche carllerche deleted the lazy-reactor-binding branch March 7, 2018 05:45
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.

None yet

2 participants