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
rt: Runtime refinements #2720
Comments
Couldn't we just only expose |
Can we still provide a single threaded macro too? Otherwise this looks good.
I am curious what the use-case here would be with no io driver or timer? |
IMO, yeah, it seems pretty reasonable to require users who want the shell runtime to have to actually go through the builder API...it's not a common configuration or what most folks are going to want out of the box. I'm open to being convinced otherwise, of course. |
We also likely want to remove |
Some thoughts: (partially from #2876 (comment)) core_threads I think the suggested behavior with #[tokio::main] We could still provide a time and io drivers I think it makes perfect sense to provide the runtime without them. As for whether this counts as a change of behavior based on feature flags, any code that tries to use the drivers would fail to compile without the feature, so I think it's ok in this case. A question Should |
What about |
As for the shell runtime, I think we should consider removing it entirely from the public API. |
@Darksonn I generally agree w/ the sentiment. We should avoid changing behavior w/ feature flags. One problem w/ removing Or, we change the API completely and add a "runtime flavor" option defined as an enum Flavor {
InPlace,
SingleThreaded,
WorkStealing(num_workers),
} Another option: enum Flavor {
Isolated(num_workers),
WorkStealing(num_workers),
} We could then enable the variants using the feature flags. We could also consider renaming We could also pass the flavor to the builder |
@Darksonn I think I am OK w/ removing the shell runtime entirely. |
Note that the enum would have to be marked |
Yeah, I think some way to enforce this stuff at compile time would be really nice. I think its extremely rare someone would want this runtime selection to be done implicitly like it currently does. |
Here's another option: impl Builder {
fn new_in_place() -> Builder;
fn new_single_threaded() -> Builder;
// Defaults to num-cpus threads
#[cfg(feature = "rt-threaded")]
fn new_work_stealing() -> Builder;
#[cfg(feature = "rt-threaded")]
fn core_threads(&mut self, num: usize) -> &mut Builder;
} ± naming |
Edit: I updated this to factor in @Darksonn's feedback below. In an effort to keep things simpler (hopefully), I propose we go roughly w/ what @Darksonn proposed: impl Builder {
fn new_in_place() -> Builder;
fn new_single_threaded() -> Builder;
// Defaults to num-cpus threads
#[cfg(feature = "rt-work-stealing")]
fn new_work_stealing(num_workers: usize) -> Builder;
} We rename the Then, for the main macro, we just keep a flat space: #[tokio::main(flavor = "work_stealing", num_workers = 4)]
#[tokio::main(flavor = "in_place")] We leave off |
It looks good to me. My only concern is the naming of single threaded vs in place, since it seems like people who really wanted |
One option is to only provide |
Why work-stealing over threaded? I personally like threaded since it feels easier to digest? |
I have no strong feelings either way. I assume you're talking about the name of the Cargo feature. |
This PR updates the runtime module docs to the changes made in `0.3` release of tokio. Closes #2720
Summary
Based on experience gained in 0.2, there are a few aspects of the Runtime that can use improvements.
basic_scheduler
andthreaded_scheduler
variants from the public API.basic_scheduler
when core_threads is set to 0, or 1.core_threads
is set to 1, usebasic_scheduler
and spawn it on a background thread.core_threads
is set to 0, usebasic_scheduler
on the current thread.Runtime::block_on
to take&self
and removetokio::runtime::Handle
.#[tokio::main]
always uses the threaded scheduler and requires thert-threaded
scheduler.#[tokio::main]
panics withoutrt-threaded
.#[tokio::main(core_threads = 1)]
results in a build failure.Remaining
Runtime::block_on
to take&self
#2782block_on
. Refactorbasic_scheduler
/shell
to embed theblock_on
implementation within their own structs rather than withinRuntime::block_on
. This means making thoseblock_on
's take &self not&mut self
. Improve stealing the root parker in situations where it gets reset back into the mutex and another thread needs to now drive it.runtime::Builder
to removethreaded_scheduler
/basic_scheduler
and refactor core_threads to do the behavior described in rt: Runtime refinements #2720.Fornew_single_thread
should spawn basic on a bg thread#[tokio::main]
as described in rt: Runtime refinements #2720.Details
Removing
basic_scheduler
andthreaded_scheduler
The details of which scheduler to pick isn't really what matters to the end-user. We already have
core_threads
to configure how many threads should be used to schedule tasks. The scheduler implementation can be selected internally based on the number of threads used.Currently,
basic_scheduler
does not spawn a dedicated runtime thread. Instead, the current thread is used to schedule tasks. Whenblock_on
is called, the runtime starts running tasks and whenblock_on
completes, the runtime is paused. This behavior can be gained again by settingcore_threads = 0
.Using
core_threads = 0
to signify running on the current thread might be a bit confusing. However, running the scheduler on the current thread is, in effect, requesting no runtime threads. This is also a somewhat specialized use case. Most users will use the threaded scheduler with default configuration settings.Switch
Runtime::block_on
to take&self
The reason
Runtime::block_on
takes&mut self
is to support thecore_threads = 0
use case. When there are no runtime threads, the runtime is executed "in-place". The&mut self
argument enforces that a single caller may "enter" the runtime so it is clear which thread executes the runtime.However, there are valid cases where multiple threads may want to potentially share the responsibility of executing the runtime. In this case, the user is responsible for managing synchronization. Instead, Tokio can provide this functionality out of the box. When running with
core_threads = 0
and callingblock_on
concurrently from multiple threads, the first thread that enters the runtime becomes responsible for executing the runtime. Other threads just block on the supplied task.Making this change also has the property of removing the need for having a separate
runtime::Handle
. TheHandle
type was added becauseRuntime
is!Sync
. This change would makeRuntime
sync and threads could just use functions onRuntime
instead ofHandle
.Additional consideration must be taken to support concurrent calls to
block_on
whencore_threads == 0
. Consider the following:block_on
and takes the responsibility of driving the runtimeblock_on
and uses a regular thread parker.At this point, thread B is waiting for its future to complete, but nothing is driving the runtime anymore, so the future will never unblock.
To handle this, when the thread that is driving the runtime completes the call to
block_on
, another blocked thread must be notified to start driving the runtime.This can probably be implemented using a
SyncParker<T: Park>
utility that handles the hand off and implementsPark for &Self
.#[tokio::main]
always uses the threaded scheduler and requires thert-threaded
scheduler.The behavior of
#[tokio::main]
changes based on feature flags that are enabled. Whenrt-threaded
is not included,#[tokio::main]
uses the basic scheduler. When it is enabled, the threaded scheduler is used. In general, changing behavior based on feature flags is not great. Feature flags are intended to be additive.Additionally,
Runtime::new()
is only defined withrt-threaded
.Instead,
#[tokio::main]
will requirert-threaded
and always have the same behavior. Ifrt-threaded
is not the program will fail to build. The main macro can stll be used without thert-threaded
feature flag enabled, butcore_threads
will need to be set to 0 or 1. In this case, the basic scheduler is used.The goal is to ensure consistent behavior regardless of which feature flags are selected.
Open question
Should
#[tokio::main]
/Runtime::new()
be available ifio-driver
andtime
feature flags are not enabled? I.e. should the default functions be available when all feature flags are not enabled? I'm leaning towards allowing#[tokio::main]
being available w/ onlyrt-threaded
and not require 'io-driver/
time`.Refs: #2698
The text was updated successfully, but these errors were encountered: