Instead of each scheduler flavor holding a reference to the scheduler
handle, the scheduler handle is passed in as needed. This removes a
duplicate handle reference in the `Runtime` struct and lays the
groundwork for further handle struct tweaks.
Publish the blocking thread pool metrics as thread-safe values, written
under the blocking thread pool's lock and able to be read in a lock-free
fashion by any reader.
Fixes#5156
This is a first step towards unifying the concepts of "entering a
runtime" and setting `Handle::current`.
Previously, these two operations were performed separately at each call
site (runtime block_on, ...). This is error-prone and also requires
multiple accesses to the thread-local variable. Additionally, "entering
the runtime" conflated the concept of entering a blocking region. For
example, calling `mpsc::Receiver::recv_blocking` performed the "enter
the runtime" step. This was done to prevent blocking a runtime, as the
operation will panic when called from an existing runtime.
To untangle these concepts, the patch splits out each logical operation
into functions. In total, there are three "enter" operations:
* `set_current_handle`
* `enter_runtime`
* `enter_blocking_region`
There are some behavior changes with each function, but they
should not translate to public behavior changes. The most significant is
`enter_blocking_region` does not change the value of the thread-local
variable, which means the function can be re-entered. Since
`enter_blocking_region` is an internal-only function and we do not
re-enter, this has no public-facing impact.
Because `enter_runtime` takes a `&Handle` to combine the
`set_current_handle` operation with entering a runtime, the patch
exposes an annoyance with the current `scheduler::Handle` struct layout.
A new instance of `scheduler::Handle` must be constructed at each call
to `enter_runtime`. We can explore cleaning this up later.
This patch also does not combine the "entered runtime" thread-local
variable with the "context" thread-local variable. To keep the patch
smaller, this has been punted to a follow-up change.
This patch consolidates the budget thread-local state with the
`runtime::context` thread local. This reduces the number of thread-local
variables used by Tokio by one.
The `LocalSet::run_until` future is just a "plain" future that should
run on a runtime that already has a coop budget. In other words, the
`run_until` future should not get its own budget but should inherit the
calling task's budget. Getting this behavior is done by removing the
call to `budget` in `run_until`.
This is a step towards unifying thread-local variables. In the future,
`coop` will be updated to use the runtime context thread-local to store
its state.
This patch does some internal renames to remove some confusion.
* `allow_blocking` is renamed to `allow_block_in_place` to indicate that
the variable only impacts the `block_in_place()` function.
* `context::try_enter` is renamed to `context::try_set_current` to
disambiguate between the various "enter" functions. This function only
sets the runtime handle used by Tokio's public APIs. Entering a runtime
is a separate operation. # Please enter the commit message for your
changes.
* `scheduler::Handle::enter()` is removed to consolidate methods that
set the current context.
This patch is the first step towards unifying all the thread-local
variables spread out across Tokio. A new `Context` struct is added which
will be used to replace the various thread-locals that exist today.
Initially, `Context` only holds the current runtime handle and the
random number generator. Further PRs will add other thread-local state.
A previous PR removed `runtime::context`. At that time,
`runtime::context` was used as an extra layer to access the various
runtime driver handles. This version of `runtime::context` serves a
different purpose (unifying all the thread-locals).
While setting the random number generator seed with the multi-threaded
scheduler does result in deterministic behavior related to the random
number generator, threads still introduce non-determinism, making it
hard (impossible?) to test this. There also is little value in doing so.
This patch also updates the docs to remove mention of work stealing.
The signal driver still uses an `Arc` internally to track if the driver
is still running, however, using the `scheduler::Handle` to access the
signal driver handle lets us delete some code.
The improvement to the `rng_seed` tests added in #5075 missed a case in
the `rt_threaded` tests which was still checking for a specific value.
As described in that PR, this makes the tests fragile and changing tokio
internals may require updating the test.
This change fixes that half-implemented improvement so that the tests no
longer depend on the exact internal ordering, but rather compare two
runs of separate runtimes built with the same seed to check that the
results are the same.
This is the start of applying a similar treatment to the I/O driver as
the time driver. The I/O driver will no longer hold its own reference to
the I/O handle. Instead, the handle is passed in when needed.
This patch also moves the process driver to the `runtime` module.
The signal driver uses a `UnixStream` to receive signal events.
Previously, the signal driver used `PollEvented` internally to receive
events on the `UnixStream`. However, using `PollEvented` from
within a runtime driver created a circular link between the runtime and
the `PollEvented` instance.
This patch replaces `PollEvented` usage in favor of accessing the I/O
driver directly. The I/O driver now reserves a token for signal-related
events and tracks signal readiness internally. The signal driver queries
the I/O driver to check for signal-related readiness.
The signal feature only requires a driver with unix platforms. The
unix signal driver uses the I/O driver. A future refactor would like to
make the signal driver use internal APIs of the I/O driver. To do this,
the signal driver must be moved to the runtime module.
This patch removes a handle to the internal runtime driver handle held
by the runtime. This is another step towards reducing the number of Arc
refs across the runtime internals. Specifically, this change is part of
an effort to remove an Arc in the time driver itself.
This excludes the initial let statement of the proc-macro expansion
from receiving the last statement spans to aid completions in rust-analyzer.
The current span confuses rust-analyzer as it will map the tail expression
tokens to the let keyword (as this is the first token it finds with the
same span) which currently breaks completions. This commit should not
degrade the initial intent of the span reusages, as the type mismatch
parts are still spanned appropriately.
The `LocalSet` implementation includes a conditional compilation clause
that removes the `const` statement from the `thread_local` definition.
However, there already is an internal macro that does this:
`tokio_thread_local`.
This patch removes the conditional compilation in favor of using the
`tokio_thread_local` macro. This also fixes a conditional compilation
issue with an internal utility (`RcCell`).
Tokio maintains an internal thread_local macro that abstracts some
conditional build logic. Before this patch, the macro was named the same
as the `std` macro (`thread_local`). This resulted in confusion as to
whether or not the internal macro or the std macro was being called.
This patch renames the internal macro to `tokio_thread_local` making it
more obvious.
Motivation
Currently, when a task spawned on a `LocalSet` is woken by an I/O driver
or time driver running on the same thread as the `LocalSet`, the task is
pushed to the `LocalSet`'s locked remote run queue rather than to its
unsynchronized local run queue. This is unfortunate, as it
negates some of the performance benefits of having an unsynchronized
local run queue. Instead, tasks are only woken to the local queue when
they are woken by other tasks also running on the local set.
This occurs because the local queue is only used when the `CONTEXT`
thread-local contains a Context that's the same as the task's
`Schedule` instance (an `Arc<Shared>`)'s Context. When the `LocalSet`
is not being polled, the thread-local is unset, and the local run queue
cannot be accessed by the `Schedule` implementation for `Arc<Shared>`.
Solution
This branch fixes this by moving the local run queue into Shared along
with the remote run queue. When an `Arc<Shared>`'s Schedule impl wakes
a task and the `CONTEXT` thread-local is None (indicating we are not
currently polling the LocalSet on this thread), we now check if the
current thread's `ThreadId` matches that of the thread the `LocalSet`
was created on, and push the woken task to the local queue if it was.
Moving the local run queue into `Shared` is somewhat unfortunate, as it
means we now have a single field on the `Shared` type, which must not be
accessed from other threads and must add an unsafe impl `Sync` for `Shared`.
However, it's the only viable way to wake to the local queue
from the Schedule impl for `Arc<Shared>`, so I figured it was worth
the additional unsafe code. I added a debug assertion to check that the
local queue is only accessed from the thread that owns the `LocalSet`.