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

runtime: merge multi & single threaded runtimes #1716

Merged
merged 14 commits into from Nov 1, 2019
Merged

Conversation

carllerche
Copy link
Member

Simplify Tokio's runtime construct by combining both Runtime variants
into a single type. The execution style can be controlled by a
configuration setting on Builder.

The implication of this change is that there is no longer any way to
spawn !Send futures. This, however, is a temporary limitation. A
different strategy will be employed for supporting !Send futures.

Included in this patch is a rework of task::JoinHandle to support
using this type from both the thread-pool and current-thread executors.

Simplify Tokio's runtime construct by combining both Runtime variants
into a single type. The execution style can be controlled by a
configuration setting on `Builder`.

The implication of this change is that there is no longer any way to
spawn `!Send` futures. This, however, is a temporary limitation. A
different strategy will be employed for supporting `!Send` futures.

Included in this patch is a rework of `task::JoinHandle` to support
using this type from both the thread-pool and current-thread executors.
@@ -103,7 +106,7 @@ impl<T: Future> Core<T> {
self.stage = Stage::Consumed
}

pub(super) fn poll<S>(&mut self, header: &Header<S>) -> Poll<T::Output>
pub(super) fn poll<S>(&mut self, header: &Header) -> Poll<T::Output>
Copy link
Sponsor Contributor

Choose a reason for hiding this comment

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

This change worries me, almost to the point that it should be marked as unsafe. Now, this basically allows doing an arbitrary pointer cast in safe code.

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 don't think it is any more unsafe than it was before, but I can mark it as such.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you have a change in mind?

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

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

Before you had to have a Header<S> to go with the poll. Now, you no longer need to have a Header<S> to "prove" that you can use that S (before the proof was also arguably pretty weak). It's tricky, because I also agree with you argument from Discord that unsafe fn makes the whole function body an unsafe block, which isn't great. I think I'd be okay with this being discussed in a doc block for the fn instead.

let around_reactor_handles = reactor_handles.clone();
let around_timer_handles = timer_handles.clone();
// Blocking pool
let blocking_pool = PoolWaiter::from(Pool::default());
Copy link
Sponsor Contributor

Choose a reason for hiding this comment

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

This doesn't seem right. The ThreadPool builder already creates a blocking pool, and stores a PoolWaiter internally. With this you'll end up with two pools.

Copy link
Member Author

Choose a reason for hiding this comment

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

You are correct. This was done intentionally as a transitional step. Fixing this "right" will take some restructuring of thread_pool that i want to punt to a follow up PR.

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

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

Okay, seems fine as long as it'll be fixed before the next release.

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

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

Maybe leave a TODO:?

Copy link
Sponsor Contributor

@jonhoo jonhoo left a comment

Choose a reason for hiding this comment

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

Overall this looks good, though I left some notes about unsafety that I think should be documented better even if we're going to move things around later, and about what I believe to be a bug where a threadpool runtime ends up with two blocking pools.

@carllerche carllerche merged commit d70c928 into master Nov 1, 2019
@carllerche carllerche deleted the simplify-rt branch November 1, 2019 21:06
@hawkw
Copy link
Member

hawkw commented Nov 4, 2019

The implication of this change is that there is no longer any way to
spawn !Send futures. This, however, is a temporary limitation. A
different strategy will be employed for supporting !Send futures.

@carllerche can you share details on the "different strategy for supporting !Send futures"? I'd like to update tokio-compat to use the latest master, but in order to provide drop-in compatibility for the 0.1 current_thread runtime, we need a mechanism for spawning !Send futures. If you have a plan for the new approach, I'd love to help get support for spawning !Send futures back in.

@carllerche
Copy link
Member Author

We can write something like LocalTaskSet that accepts !Send futures. This type can provide a spawn_local fn. Then, you would use it like:

let mut rt = Runtime::new();
let local_tasks = LocalTaskSet::new();
local_tasks.spawn(async { ... });

rt.block_on(local_tasks);

Not exactly that, but something like that should work.

@s97712
Copy link

s97712 commented Nov 5, 2019

We can write something like LocalTaskSet that accepts !Send futures. This type can provide a spawn_local fn. Then, you would use it like:

let mut rt = Runtime::new();
let local_tasks = LocalTaskSet::new();
local_tasks.spawn(async { ... });

rt.block_on(local_tasks);

Not exactly that, but something like that should work.

Maybe we can provide a spawn_local method for runtime, spawn_local will spawn task on current thread. If the runtime has only one thread , then spawn and spwan_local have the same effect.

hawkw added a commit that referenced this pull request Nov 27, 2019
## Motivation

In earlier versions of `tokio`, the `current_thread::Runtime` type could
be used to run `!Send` futures. However, PR #1716 merged the
current-thread and threadpool runtimes into a single type, which can no
longer run `!Send` futures. There is still a need in some cases to
support futures that don't implement `Send`, and the `tokio-compat`
crate requires this in order to provide APIs that existed in `tokio`
0.1.

## Solution

This branch implements the API described by @carllerche in
#1716 (comment). It
adds a new `LocalSet` type and `spawn_local` function to `tokio::task`.
The `LocalSet` type is used to group together a set of tasks which must
run on the same thread and don't implement `Send`. These are available
when a new "rt-util" feature flag is enabled.

Currently, the local task set is run by passing it a reference to a
`Runtime` and a future to `block_on`. In the future, we may also want
to investigate allowing spawned futures to construct their own local
task sets, which would be executed on the worker that the future is
executing on. 

In order to implement the new API, I've made some internal changes to
the `task` module and `Schedule` trait to support scheduling both `Send`
and `!Send` futures.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
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

4 participants