Skip to content

Conversation

hawkw
Copy link
Member

@hawkw hawkw commented Nov 5, 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.

@hawkw hawkw added the C-enhancement Category: A PR with an enhancement or bugfix. label Nov 5, 2019
@hawkw hawkw self-assigned this Nov 5, 2019
@hawkw
Copy link
Member Author

hawkw commented Nov 5, 2019

I would love any feedback on the API surface of the new local module — if there's anything we can do to make it more ergonomic to use, that would be great. In particular, I wasn't really sure if we would prefer the free function for spawning on the local task set to be used as tokio::spawn_local, or as tokio::local::spawn. I could go either way...

hawkw added 19 commits November 6, 2019 10:33
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
This adds a marker type to the `Task` struct and `Schedule` trait
indicating whether or not the task is `Send`, and changes `Task` to only
implement `Send` when the `Send` marker is present. Schedulers must
indicate whether they are capable of scheduling `Send` or `!Send` tasks.

This solution is singnificantly better than the previous one of using an
unsafe wrapper to make `!Send` tasks implement `Send`, which was
necessary due to the bounds on the `Task` struct. Although the previous
solution was correct, since the `!Send` tasks were only ever scheduled
locally, the compiler could no longer verify that this was the case.
Now, the typesystem actually _helps_ us ensure that schedulers that can
only schedule `Send` tasks will never schedule `!Send` tasks.

Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
These assertions asserted that the scheduler was current in all calls
to its methods. However, the `TaskSet::spawn_local` method allows
scheduling tasks to be run on the `TaskSet` when it is _not_ currently
running. Therefore, these assertions are wrong.

This test moves the assertion to the _right_ place: when the scheduler
actually _runs_ scheduled tasks.

Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
@hawkw hawkw force-pushed the eliza/local-spawn branch from df978a8 to 1c0cea1 Compare November 6, 2019 18:45
@hawkw
Copy link
Member Author

hawkw commented Nov 14, 2019

@carllerche BTW, the local task set now requires the "rt-full" feature flag. this is because it now requires the "sync" feature (due to the use of AtomicWaker), which I didn't think "rt-core" should require, and we didn't want to add a separate feature for local. I think that ideally, it would be possible to enable just the single-threaded runtime + LocalSet without also enabling the threadpool runtime, but I think that can wait until we give all the feature flags a pass.

Does that seem right to you?

}
}

fn with<F>(&self, f: impl FnOnce() -> F) -> F {
Copy link
Member

Choose a reason for hiding this comment

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

What happens when you nest with calls? Looks like bad things might happen? Either we want to allow it or disallow it via panic.

Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
@hawkw hawkw merged commit 38e602f into master Nov 27, 2019
@carllerche carllerche deleted the eliza/local-spawn branch November 28, 2019 04:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: A PR with an enhancement or bugfix.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants