Compare commits

...
Author SHA1 Message Date
Lucio Franco 6f840c5232 Add initial io driver stats 2022-01-07 17:08:51 -05:00
Carl Lerche cc8ad367a0 rt: refactor current-thread scheduler (#4377)
This patch does some refactoring to the current-thread scheduler bringing it closer to the
structure of the multi-threaded scheduler. More specifically, the core scheduler data is stored
in a Core struct and that struct is passed around as a "token" indicating permission to do
work. The Core structure is also stored in the thread-local context.

This refactor is intended to support #4373, making it easier to track counters in more locations
in the current-thread scheduler.
2022-01-06 17:19:26 -08:00
Rob Ede 25e5141c36 test: fix version requirement of tokio-stream (#4376) 2022-01-04 22:01:12 +01:00
Tom Dohrmann 4a12163d7c util: add mutable reference getters for codecs to pinned Framed (#4372) 2022-01-03 22:21:43 +01:00
Elichai Turkel 12dd06336d sync: add a has_changed method to watch::Receiver (#4342) 2021-12-31 16:23:29 +01:00
Alice Ryhl c301f6d83a sync: don't inherit Send from parking_lot::*Guard (#4359) 2021-12-31 15:57:56 +01:00
Braulio Valdivielso Martínez fb35c83944 tokio-stream: add StreamExt::map_while (#4351)
Fixes #4337

Rust 1.57 stabilized the `Iterator::map_while` API. This PR adds the
same functionality to the `StreamExt` trait, to keep parity.
2021-12-31 22:53:09 +09:00
Taiki Endo 43cdb2cb50 net: add tos and set_tos methods to TCP and UDP sockets (#4366) 2021-12-31 21:19:14 +09:00
Taiki Endo 49a9dc6743 net: add buffer size methods to UdpSocket (#4363)
This adds the following methods:

- UdpSocket::set_send_buffer_size
- UdpSocket::send_buffer_size
- UdpSocket::set_recv_buffer_size
- UdpSocket::recv_buffer_size
2021-12-31 20:47:34 +09:00
Taiki Endo 96370ba4ce net: add TcpSocket::take_error (#4364) 2021-12-31 11:25:50 +01:00
Taiki Endo a9d9bde068 net: add UdpSocket::peer_addr (#4362) 2021-12-31 11:23:04 +01:00
Taiki Endo 0190831ec1 net: fix build error on master (#4361) 2021-12-31 11:21:23 +01:00
Taiki Endo ee0e811a36 Update mio to 0.8 (#4270) 2021-12-31 12:28:14 +09:00
Alice Ryhl 47feaa7a89 io: fix clippy lint in write_all (#4358) 2021-12-30 15:31:11 +01:00
Alice Ryhl dda8da75d0 stream: add StreamExt::then (#4355) 2021-12-30 15:28:13 +01:00
David Kleingeld dc1894105b codec: improve Builder::max_frame_length docs (#4352) 2021-12-28 15:08:37 +01:00
Eliza Weisman 78e0f0b42a docs: improve RustDoc for unstable features (#4331)
Currently, the docs.rs documentation for tokio is built without
--cfg tokio_unstable set. This means that unstable features are not shown in
the API docs, making them difficutl to discover. Clearly, we do want to
document the existence of unstable APIs, given that there's a section in
the lib.rs documentation listing them, so it would be better if it was
also possible to determine what APIs an unstable feature enables when
reading the RustDoc documentation.

This branch changes the docs.rs metadata to also pass --cfg tokio_unstable
when building the documentation. It turns out that it's
necessary to separately pass the cfg flag to both RustDoc and rustc,
or else the tracing dependency, which is only enabled in
target.cfg(tokio_unstable).dependencies, will be missing and the build
will fail.

In addition, I made some minor improvements to the docs for unstable
features. Some links in the task::Builder docs were broken, and the
required tokio_unstable cfg was missing from the doc(cfg(...))
attributes. Furthermore, I added a note in the top-level docs for
unstable APIs, stating that they are unstable and linking back to the
section in the crate-level docs that explains how to enable unstable
features.

Fixes #4328
2021-12-21 11:11:48 -08:00
Jinhua Tan e55f3d4398 examples: make the introduction in examples/Cargo.toml more clear (#4333) 2021-12-21 14:02:18 +01:00
Alice Ryhl 8582363b4e stats: mark stats feature unstable in lib.rs (#4327) 2021-12-18 13:40:24 +01:00
Cyborus04 c3fbaba1f9 io: replace use of transmute with pointer manipulations (#4307) 2021-12-17 20:00:24 +01:00
Fabien GaudandFabien Gaud 22e6aef6e7 net: allow to set linger on TcpSocket (#4324)
For now, this is only allowed on TcpStream. This is a problem when one
want to disable lingering (i.e. set it to Duration(0, 0)). Without being
able to set it prior to the connect call, if the connect future is
dropped it would leave sockets in a TIME_WAIT state.

Co-authored-by: Fabien Gaud <[email protected]>
2021-12-16 20:34:00 +01:00
Carl Lerche f64673580d chore: prepare Tokio v1.15.0 release (#4320)
Includes `tokio-macros` v1.7.0
2021-12-15 10:36:09 -08:00
Braulio Valdivielso Martínez 54e6693dff time: make timeout robust against budget-depleting tasks (#4314) 2021-12-15 11:59:21 +01:00
Zahari Dichev 4e3268d222 tracing: instrument more resources (#4302)
This PR adds instrumentation to more resources from the sync package. The new
instrumentation requires the `tokio_unstable` feature flag to enable.
2021-12-14 14:04:19 -08:00
Toby Lawrence 4b6bb1d9a7 chore(util): start v0.7 release cycle (#4313)
* chore(util): start v0.7 release cycle

Signed-off-by: Toby Lawrence <[email protected]>
2021-12-10 13:16:17 -05:00
Braulio Valdivielso Martínez eb1af7f29c io: make tokio::io::empty cooperative (#4300)
Reads and buffered reads from a `tokio::io::empty` were always marked
as ready. That makes sense, given that there is nothing to wait for.
However, doing repeated reads on the `empty` could stall the event
loop and prevent other tasks from making progress.

This change uses tokio's coop system to yield control back to the
executor when appropriate.

Note that the issue that originally triggered this PR is not fixed
yet, because the `timeout` function will not poll the timer after
empty::read runs out of budget. A different change will be needed to
address that.

Refs: #4291
2021-12-10 11:08:49 +01:00
Toby Lawrence 0bc9160e25 chore: update labeler.yml to drop filepath prefixes (#4308)
- update labeler.yml to drop filepath prefixes
- make sure labeler enforces labels over lifetime of PR
2021-12-09 10:40:27 -05:00
Shin Seunghun 4c571b55b1 runtime: fix typo in the task modules (#4306) 2021-12-08 13:30:52 +01:00
Naruto210 60ba634d60 time: fix typo in tokio-time document (#4304) 2021-12-07 15:49:45 +01:00
Shin Seunghun f73ed1fdba runtime: fix typo (#4303) 2021-12-07 11:07:00 +01:00
Ivan Petkov ee4b2ede83 process: add as_std() method to Command (#4295) 2021-12-03 08:51:27 +01:00
Shin Seunghun 64da914d17 time: add doc links in entry doc (#4293) 2021-12-02 15:27:46 +01:00
kenmasu d764ba5816 io: call tcp.set_nonblocking(true) in AsyncFd example. (#4292) 2021-12-02 11:01:23 +01:00
Alice Ryhl 65fb0210d5 tokio: add 1.14.x to LTS releases (#4273) 2021-11-24 09:18:50 +01:00
Axel Forsman a77b2fbab2 io: extend AsyncFdReadyGuard method lifetimes (#4267)
The implicit elided lifetimes of the `AsyncFd` references in return
types of methods on `AsyncFdReadyGuard` resolved to that of `&self`.
However that lifetime is smaller than `'a` since `self` contains an `&'a
AsyncFd` reference. This will not change so the change also does not
lessen future proofing.
2021-11-23 13:56:31 +01:00
oblique 347c0cdaba time: add Interval::reset method (#4248) 2021-11-23 13:51:07 +01:00
Shin Seunghun 2c0e5c9704 time: document missing timer panics (#4247) 2021-11-23 12:14:08 +01:00
omjadas 2a614fba0d docs: document that parking_lot is enabled by full (#4269) 2021-11-23 12:11:42 +01:00
David Pedersen 3b339024f0 stream: impl Extend for StreamMap (#4272)
## Motivation

This allows `StreamMap` to be used with [`futures::stream::StreamExt::collect`][collect].

My use case is something like this:

```rust
let stream_map: StreamMap<_, _> = things
    .into_iter()
    .map(|thing| make_stream(thing)) // iterator of futures
    .collect::<FuturesUnordered<_>>() // stream of streams
    .collect::<StreamMap<_, _>>() // combine all the inner streams into one
    .await;

async fn make_stream(thing: Thing) -> impl Stream { ... }
```

[collect]: https://docs.rs/futures/0.3.17/futures/stream/trait.StreamExt.html#method.collect

## Solution

Add `Extend` impl that delegates to the inner `Vec`.
2021-11-23 11:54:06 +01:00
Taiki Endo 1a423b3322 chore: remove doc URL from Cargo.toml (#4251)
https://doc.rust-lang.org/cargo/reference/manifest.html#the-documentation-field

> If no URL is specified in the manifest file, crates.io will
> automatically link your crate to the corresponding docs.rs page.
2021-11-23 11:53:32 +01:00
Taiki Endo a8b662f643 ci: upgrade to new nightly (#4268) 2021-11-23 19:29:57 +09:00
Taiki Endo cf3206842c chore: bump MSRV to 1.46 (#4254) 2021-11-23 12:09:24 +09:00
Taiki Endo 8943e8aeef macros: address remainging clippy::semicolon_if_nothing_returned warning (#4252) 2021-11-22 18:41:17 +09:00
Taiki Endo fe770dc509 chore: fix newly added warnings (#4253) 2021-11-22 18:40:57 +09:00
Taiki Endo 8b6542fc3e chore: update nix to 0.23 (#4255) 2021-11-22 06:13:14 +09:00
Alice Ryhl b1afd95994 sync: elaborate on cross-runtime message passing (#4240) 2021-11-17 16:02:07 +01:00
Alice Ryhl 095b5dcf93 net: add connect_std -> TcpSocket doc alias (#4219) 2021-11-16 22:22:47 +01:00
Alice Ryhl 623c09c52c chore: prepare tokio-macros 1.6.0 (#4239) 2021-11-16 09:54:07 +01:00
101 changed files with 2773 additions and 611 deletions
+3
View File
@@ -4,4 +4,7 @@
ignore = [
# https://github.com/tokio-rs/tokio/issues/4177
"RUSTSEC-2020-0159",
# We depend on nix 0.22 only via mio-aio, a dev-dependency.
# https://github.com/tokio-rs/tokio/pull/4255#issuecomment-974786349
"RUSTSEC-2021-0119",
]
+1 -1
View File
@@ -29,7 +29,7 @@ task:
setup_script:
- pkg install -y bash curl
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain nightly-2021-10-25
- sh rustup.sh -y --profile minimal --default-toolchain nightly-2021-11-23
- . $HOME/.cargo/env
- |
echo "~~~~ rustc --version ~~~~"
+1 -1
View File
@@ -1 +1 @@
msrv = "1.45"
msrv = "1.46"
+6 -7
View File
@@ -1,9 +1,8 @@
R-loom:
- ./tokio/src/sync/*
- ./tokio/src/sync/**/*
- ./tokio-util/src/sync/*
- ./tokio-util/src/sync/**/*
- ./tokio/src/runtime/*
- ./tokio/src/runtime/**/*
- tokio/src/sync/*
- tokio/src/sync/**/*
- tokio-util/src/sync/*
- tokio-util/src/sync/**/*
- tokio/src/runtime/*
- tokio/src/runtime/**/*
+25 -5
View File
@@ -9,8 +9,8 @@ name: CI
env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
nightly: nightly-2021-10-25
minrust: 1.45.2
nightly: nightly-2021-11-23
minrust: 1.46
jobs:
# Depends on all action sthat are required for a "successful" CI run.
@@ -20,6 +20,7 @@ jobs:
needs:
- test
- test-unstable
- test-parking_lot
- miri
- cross
- features
@@ -77,6 +78,27 @@ jobs:
# bench.yml workflow runs benchmarks only on linux.
if: startsWith(matrix.os, 'ubuntu')
test-parking_lot:
# The parking_lot crate has a feature called send_guard which changes when
# some of its types are Send. Tokio has some measures in place to prevent
# this from affecting when Tokio types are Send, and this test exists to
# ensure that those measures are working.
#
# This relies on the potentially affected Tokio type being listed in
# `tokio/tokio/tests/async_send_sync.rs`.
name: compile tests with parking lot send guards
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update stable
- uses: Swatinem/rust-cache@v1
- name: Enable parking_lot send_guard feature
# Inserts the line "plsend = ["parking_lot/send_guard"]" right after [features]
run: sed -i '/\[features\]/a plsend = ["parking_lot/send_guard"]' tokio/Cargo.toml
- name: Compile tests with all features enabled
run: cargo build --workspace --all-features --tests
valgrind:
name: valgrind
runs-on: ubuntu-latest
@@ -267,8 +289,6 @@ jobs:
- name: Install Rust
run: rustup update stable
- uses: Swatinem/rust-cache@v1
- name: Install rustfmt
run: rustup component add rustfmt
# Check fmt
- name: "rustfmt --check"
@@ -285,7 +305,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update 1.52.1 && rustup default 1.52.1
run: rustup update 1.57 && rustup default 1.57
- uses: Swatinem/rust-cache@v1
- name: Install clippy
run: rustup component add clippy
+1
View File
@@ -11,3 +11,4 @@ jobs:
- uses: actions/labeler@v3
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true
+8
View File
@@ -139,6 +139,14 @@ correctly, use this command:
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
```
To build documentation including Tokio's unstable features, it is necessary to
pass `--cfg tokio_unstable` to both RustDoc *and* rustc. To build the
documentation for unstable features, use this command:
```
RUSTDOCFLAGS="--cfg docsrs --cfg tokio_unstable" RUSTFLAGS="--cfg tokio_unstable" cargo +nightly doc --all-features
```
There is currently a [bug in cargo] that means documentation cannot be built
from the root of the workspace. If you `cd` into the `tokio` subdirectory the
command shown above will work.
+3 -2
View File
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.14.0", features = ["full"] }
tokio = { version = "1.15.0", features = ["full"] }
```
Then, on your main.rs:
@@ -164,7 +164,7 @@ several other libraries, including:
## Supported Rust Versions
Tokio is built against the latest stable release. The minimum supported version
is 1.45. The current Tokio version is not guaranteed to build on Rust versions
is 1.46. The current Tokio version is not guaranteed to build on Rust versions
earlier than the minimum supported version.
## Release schedule
@@ -181,6 +181,7 @@ released as a new patch release for each LTS minor version. Our current LTS
releases are:
* `1.8.x` - LTS release until February 2022.
* `1.14.x` - LTS release until June 2022.
Each LTS release will continue to receive backported fixes for at least half a
year. If you wish to use a fixed minor release in your project, we recommend
+1 -1
View File
@@ -9,7 +9,7 @@ tokio = { version = "1.5.0", path = "../tokio", features = ["full"] }
bencher = "0.1.5"
[dev-dependencies]
tokio-util = { version = "0.6.6", path = "../tokio-util", features = ["full"] }
tokio-util = { version = "0.7.0", path = "../tokio-util", features = ["full"] }
tokio-stream = { path = "../tokio-stream" }
[target.'cfg(unix)'.dependencies]
+1 -1
View File
@@ -21,7 +21,7 @@ fn rt() -> tokio::runtime::Runtime {
const BLOCK_COUNT: usize = 1_000;
const BUFFER_SIZE: usize = 4096;
const DEV_ZERO: &'static str = "/dev/zero";
const DEV_ZERO: &str = "/dev/zero";
fn async_read_codec(b: &mut Bencher) {
let rt = rt();
+3 -3
View File
@@ -5,10 +5,10 @@ publish = false
edition = "2018"
# If you copy one of the examples into a new project, you should be using
# [dependencies] instead.
# [dependencies] instead, and delete the **path**.
[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio",features = ["full", "tracing"] }
tokio-util = { version = "0.6.3", path = "../tokio-util",features = ["full"] }
tokio = { version = "1.0.0", path = "../tokio", features = ["full", "tracing"] }
tokio-util = { version = "0.7.0", path = "../tokio-util", features = ["full"] }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
tracing = "0.1"
+1 -1
View File
@@ -149,7 +149,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
}
fn handle_request(line: &str, db: &Arc<Database>) -> Response {
let request = match Request::parse(&line) {
let request = match Request::parse(line) {
Ok(req) => req,
Err(e) => return Response::Error { msg: e },
};
+12
View File
@@ -1,3 +1,15 @@
# 1.7.0 (December 15th, 2021)
- macros: address remainging clippy::semicolon_if_nothing_returned warning ([#4252])
[#4252]: https://github.com/tokio-rs/tokio/pull/4252
# 1.6.0 (November 16th, 2021)
- macros: fix mut patterns in `select!` macro ([#4211])
[#4211]: https://github.com/tokio-rs/tokio/pull/4211
# 1.5.1 (October 29th, 2021)
- macros: fix type resolution error in `#[tokio::main]` ([#4176])
+2 -4
View File
@@ -2,17 +2,15 @@
name = "tokio-macros"
# When releasing to crates.io:
# - Remove path dependencies
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-macros-1.0.x" git tag.
version = "1.5.1"
version = "1.7.0"
edition = "2018"
rust-version = "1.46"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-macros/1.5.1/tokio_macros"
description = """
Tokio's proc macros.
"""
+5 -5
View File
@@ -339,17 +339,17 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
let body = &input.block;
let brace_token = input.block.brace_token;
let (tail_return, tail_semicolon) = match body.stmts.last() {
Some(syn::Stmt::Semi(expr, _)) => match expr {
syn::Expr::Return(_) => (quote! { return }, quote! { ; }),
_ => match &input.sig.output {
Some(syn::Stmt::Semi(syn::Expr::Return(_), _)) => (quote! { return }, quote! { ; }),
Some(syn::Stmt::Semi(..)) | Some(syn::Stmt::Local(..)) | None => {
match &input.sig.output {
syn::ReturnType::Type(_, ty) if matches!(&**ty, syn::Type::Tuple(ty) if ty.elems.is_empty()) =>
{
(quote! {}, quote! { ; }) // unit
}
syn::ReturnType::Default => (quote! {}, quote! { ; }), // unit
syn::ReturnType::Type(..) => (quote! {}, quote! {}), // ! or another
},
},
}
}
_ => (quote! {}, quote! {}),
};
input.block = syn::parse2(quote_spanned! {last_stmt_end_span=>
-1
View File
@@ -5,7 +5,6 @@
rust_2018_idioms,
unreachable_pub
)]
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
+2 -4
View File
@@ -2,17 +2,15 @@
name = "tokio-stream"
# When releasing to crates.io:
# - Remove path dependencies
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-stream-0.1.x" git tag.
version = "0.1.8"
edition = "2018"
rust-version = "1.46"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-stream/0.1.8/tokio_stream"
description = """
Utilities to work with `Stream` and `tokio`.
"""
@@ -31,7 +29,7 @@ signal = ["tokio/signal"]
futures-core = { version = "0.3.0" }
pin-project-lite = "0.2.0"
tokio = { version = "1.8.0", path = "../tokio", features = ["sync"] }
tokio-util = { version = "0.6.3", path = "../tokio-util", optional = true }
tokio-util = { version = "0.7.0", path = "../tokio-util", optional = true }
[dev-dependencies]
tokio = { version = "1.2.0", path = "../tokio", features = ["full", "test-util"] }
-1
View File
@@ -10,7 +10,6 @@
unreachable_pub
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
+97 -3
View File
@@ -1,3 +1,4 @@
use core::future::Future;
use futures_core::Stream;
mod all;
@@ -27,6 +28,9 @@ use fuse::Fuse;
mod map;
use map::Map;
mod map_while;
use map_while::MapWhile;
mod merge;
use merge::Merge;
@@ -39,15 +43,18 @@ use skip::Skip;
mod skip_while;
use skip_while::SkipWhile;
mod try_next;
use try_next::TryNext;
mod take;
use take::Take;
mod take_while;
use take_while::TakeWhile;
mod then;
use then::Then;
mod try_next;
use try_next::TryNext;
cfg_time! {
mod timeout;
use timeout::Timeout;
@@ -197,6 +204,93 @@ pub trait StreamExt: Stream {
Map::new(self, f)
}
/// Map this stream's items to a different type for as long as determined by
/// the provided closure. A stream of the target type will be returned,
/// which will yield elements until the closure returns `None`.
///
/// The provided closure is executed over all elements of this stream as
/// they are made available, until it returns `None`. It is executed inline
/// with calls to [`poll_next`](Stream::poll_next). Once `None` is returned,
/// the underlying stream will not be polled again.
///
/// Note that this function consumes the stream passed into it and returns a
/// wrapped version of it, similar to the [`Iterator::map_while`] method in the
/// standard library.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let stream = stream::iter(1..=10);
/// let mut stream = stream.map_while(|x| {
/// if x < 4 {
/// Some(x + 3)
/// } else {
/// None
/// }
/// });
/// assert_eq!(stream.next().await, Some(4));
/// assert_eq!(stream.next().await, Some(5));
/// assert_eq!(stream.next().await, Some(6));
/// assert_eq!(stream.next().await, None);
/// # }
/// ```
fn map_while<T, F>(self, f: F) -> MapWhile<Self, F>
where
F: FnMut(Self::Item) -> Option<T>,
Self: Sized,
{
MapWhile::new(self, f)
}
/// Maps this stream's items asynchronously to a different type, returning a
/// new stream of the resulting type.
///
/// The provided closure is executed over all elements of this stream as
/// they are made available, and the returned future is executed. Only one
/// future is executed at the time.
///
/// Note that this function consumes the stream passed into it and returns a
/// wrapped version of it, similar to the existing `then` methods in the
/// standard library.
///
/// Be aware that if the future is not `Unpin`, then neither is the `Stream`
/// returned by this method. To handle this, you can use `tokio::pin!` as in
/// the example below or put the stream in a `Box` with `Box::pin(stream)`.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// async fn do_async_work(value: i32) -> i32 {
/// value + 3
/// }
///
/// let stream = stream::iter(1..=3);
/// let stream = stream.then(do_async_work);
///
/// tokio::pin!(stream);
///
/// assert_eq!(stream.next().await, Some(4));
/// assert_eq!(stream.next().await, Some(5));
/// assert_eq!(stream.next().await, Some(6));
/// # }
/// ```
fn then<F, Fut>(self, f: F) -> Then<Self, Fut, F>
where
F: FnMut(Self::Item) -> Fut,
Fut: Future,
Self: Sized,
{
Then::new(self, f)
}
/// Combine two streams into one by interleaving the output of both as it
/// is produced.
///
+4 -4
View File
@@ -66,17 +66,17 @@ where
use Poll::Ready;
loop {
let mut me = self.as_mut().project();
let me = self.as_mut().project();
let item = match ready!(me.stream.poll_next(cx)) {
Some(item) => item,
None => {
return Ready(U::finalize(sealed::Internal, &mut me.collection));
return Ready(U::finalize(sealed::Internal, me.collection));
}
};
if !U::extend(sealed::Internal, &mut me.collection, item) {
return Ready(U::finalize(sealed::Internal, &mut me.collection));
if !U::extend(sealed::Internal, me.collection, item) {
return Ready(U::finalize(sealed::Internal, me.collection));
}
}
}
+52
View File
@@ -0,0 +1,52 @@
use crate::Stream;
use core::fmt;
use core::pin::Pin;
use core::task::{Context, Poll};
use pin_project_lite::pin_project;
pin_project! {
/// Stream for the [`map_while`](super::StreamExt::map_while) method.
#[must_use = "streams do nothing unless polled"]
pub struct MapWhile<St, F> {
#[pin]
stream: St,
f: F,
}
}
impl<St, F> fmt::Debug for MapWhile<St, F>
where
St: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MapWhile")
.field("stream", &self.stream)
.finish()
}
}
impl<St, F> MapWhile<St, F> {
pub(super) fn new(stream: St, f: F) -> Self {
MapWhile { stream, f }
}
}
impl<St, F, T> Stream for MapWhile<St, F>
where
St: Stream,
F: FnMut(St::Item) -> Option<T>,
{
type Item = T;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
let me = self.project();
let f = me.f;
me.stream.poll_next(cx).map(|opt| opt.and_then(f))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, upper) = self.stream.size_hint();
(0, upper)
}
}
+83
View File
@@ -0,0 +1,83 @@
use crate::Stream;
use core::fmt;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use pin_project_lite::pin_project;
pin_project! {
/// Stream for the [`then`](super::StreamExt::then) method.
#[must_use = "streams do nothing unless polled"]
pub struct Then<St, Fut, F> {
#[pin]
stream: St,
#[pin]
future: Option<Fut>,
f: F,
}
}
impl<St, Fut, F> fmt::Debug for Then<St, Fut, F>
where
St: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Then")
.field("stream", &self.stream)
.finish()
}
}
impl<St, Fut, F> Then<St, Fut, F> {
pub(super) fn new(stream: St, f: F) -> Self {
Then {
stream,
future: None,
f,
}
}
}
impl<St, F, Fut> Stream for Then<St, Fut, F>
where
St: Stream,
Fut: Future,
F: FnMut(St::Item) -> Fut,
{
type Item = Fut::Output;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Fut::Output>> {
let mut me = self.project();
loop {
if let Some(future) = me.future.as_mut().as_pin_mut() {
match future.poll(cx) {
Poll::Ready(item) => {
me.future.set(None);
return Poll::Ready(Some(item));
}
Poll::Pending => return Poll::Pending,
}
}
match me.stream.as_mut().poll_next(cx) {
Poll::Ready(Some(item)) => {
me.future.set(Some((me.f)(item)));
}
Poll::Ready(None) => return Poll::Ready(None),
Poll::Pending => return Poll::Pending,
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let future_len = if self.future.is_some() { 1 } else { 0 };
let (lower, upper) = self.stream.size_hint();
let lower = lower.saturating_add(future_len);
let upper = upper.and_then(|upper| upper.checked_add(future_len));
(lower, upper)
}
}
+9
View File
@@ -585,6 +585,15 @@ where
}
}
impl<K, V> Extend<(K, V)> for StreamMap<K, V> {
fn extend<T>(&mut self, iter: T)
where
T: IntoIterator<Item = (K, V)>,
{
self.entries.extend(iter);
}
}
mod rand {
use std::cell::Cell;
+2 -4
View File
@@ -2,17 +2,15 @@
name = "tokio-test"
# When releasing to crates.io:
# - Remove path dependencies
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-test-0.4.x" git tag.
version = "0.4.2"
edition = "2018"
rust-version = "1.46"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-test/0.4.2/tokio_test"
description = """
Testing utilities for Tokio- and futures-based code
"""
@@ -20,7 +18,7 @@ categories = ["asynchronous", "testing"]
[dependencies]
tokio = { version = "1.2.0", path = "../tokio", features = ["rt", "sync", "time", "test-util"] }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
tokio-stream = { version = "0.1.1", path = "../tokio-stream" }
async-stream = "0.3"
bytes = "1.0.0"
-1
View File
@@ -4,7 +4,6 @@
rust_2018_idioms,
unreachable_pub
)]
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
+4 -5
View File
@@ -2,21 +2,20 @@
name = "tokio-util"
# When releasing to crates.io:
# - Remove path dependencies
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-util-0.6.x" git tag.
version = "0.6.9"
# - Create "tokio-util-0.7.x" git tag.
version = "0.7.0"
edition = "2018"
rust-version = "1.46"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-util/0.6.9/tokio_util"
description = """
Additional utilities for working with Tokio.
"""
categories = ["asynchronous"]
publish = false
[features]
# No features on by default
+9
View File
@@ -204,6 +204,15 @@ impl<T, U> Framed<T, U> {
&mut self.inner.codec
}
/// Returns a mutable reference to the underlying codec wrapped by
/// `Framed`.
///
/// Note that care should be taken to not tamper with the underlying codec
/// as it may corrupt the stream of frames otherwise being worked with.
pub fn codec_pin_mut(self: Pin<&mut Self>) -> &mut U {
self.project().inner.project().codec
}
/// Returns a reference to the read buffer.
pub fn read_buffer(&self) -> &BytesMut {
&self.inner.state.read.buffer
+5
View File
@@ -108,6 +108,11 @@ impl<T, D> FramedRead<T, D> {
&mut self.inner.codec
}
/// Returns a mutable reference to the underlying decoder.
pub fn decoder_pin_mut(self: Pin<&mut Self>) -> &mut D {
self.project().inner.project().codec
}
/// Returns a reference to the read buffer.
pub fn read_buffer(&self) -> &BytesMut {
&self.inner.state.buffer
+5
View File
@@ -88,6 +88,11 @@ impl<T, E> FramedWrite<T, E> {
&mut self.inner.codec
}
/// Returns a mutable reference to the underlying encoder.
pub fn encoder_pin_mut(self: Pin<&mut Self>) -> &mut E {
self.project().inner.project().codec
}
/// Returns a reference to the write buffer.
pub fn write_buffer(&self) -> &BytesMut {
&self.inner.state.buffer
+2 -2
View File
@@ -746,7 +746,7 @@ impl Builder {
}
}
/// Sets the max frame length
/// Sets the max frame length in bytes
///
/// This configuration option applies to both encoding and decoding. The
/// default value is 8MB.
@@ -767,7 +767,7 @@ impl Builder {
///
/// # fn bind_read<T: AsyncRead>(io: T) {
/// LengthDelimitedCodec::builder()
/// .max_frame_length(8 * 1024)
/// .max_frame_length(8 * 1024 * 1024)
/// .new_read(io);
/// # }
/// # pub fn main() {}
-1
View File
@@ -5,7 +5,6 @@
rust_2018_idioms,
unreachable_pub
)]
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
+28
View File
@@ -1,3 +1,31 @@
# 1.15.0 (December 15, 2021)
### Fixed
- io: add cooperative yielding support to `io::empty()` ([#4300])
- time: make timeout robust against budget-depleting tasks ([#4314])
### Changed
- update minimum supported Rust version to 1.46.
### Added
- time: add `Interval::reset()` ([#4248])
- io: add explicit lifetimes to `AsyncFdReadyGuard` ([#4267])
- process: add `Command::as_std()` ([#4295])
### Added (unstable)
- tracing: instrument `tokio::sync` types ([#4302])
[#4302]: https://github.com/tokio-rs/tokio/pull/4302
[#4300]: https://github.com/tokio-rs/tokio/pull/4300
[#4295]: https://github.com/tokio-rs/tokio/pull/4295
[#4267]: https://github.com/tokio-rs/tokio/pull/4267
[#4248]: https://github.com/tokio-rs/tokio/pull/4248
[#4314]: https://github.com/tokio-rs/tokio/pull/4314
# 1.14.0 (November 15, 2021)
### Fixed
+18 -19
View File
@@ -3,16 +3,15 @@ name = "tokio"
# When releasing to crates.io:
# - Remove path dependencies
# - Update doc url
# - Cargo.toml
# - README.md
# - Update CHANGELOG.md.
# - Create "v1.0.x" git tag.
version = "1.14.0"
version = "1.15.0"
edition = "2018"
rust-version = "1.46"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
readme = "README.md"
documentation = "https://docs.rs/tokio/1.14.0/tokio/"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
description = """
@@ -50,20 +49,19 @@ macros = ["tokio-macros"]
stats = []
net = [
"libc",
"mio/net",
"mio/os-ext",
"mio/os-poll",
"mio/os-util",
"mio/tcp",
"mio/udp",
"mio/uds",
"socket2/all",
"winapi/namedpipeapi",
]
process = [
"bytes",
"once_cell",
"libc",
"mio/net",
"mio/os-ext",
"mio/os-poll",
"mio/os-util",
"mio/uds",
"signal-hook-registry",
"winapi/threadpoollegacyapiset",
]
@@ -76,9 +74,9 @@ rt-multi-thread = [
signal = [
"once_cell",
"libc",
"mio/net",
"mio/os-ext",
"mio/os-poll",
"mio/uds",
"mio/os-util",
"signal-hook-registry",
"winapi/consoleapi",
]
@@ -87,7 +85,7 @@ test-util = ["rt", "sync", "time"]
time = []
[dependencies]
tokio-macros = { path = "../tokio-macros", optional = true }
tokio-macros = { version = "1.7.0", path = "../tokio-macros", optional = true }
pin-project-lite = "0.2.0"
@@ -95,9 +93,10 @@ pin-project-lite = "0.2.0"
bytes = { version = "1.0.0", optional = true }
once_cell = { version = "1.5.2", optional = true }
memchr = { version = "2.2", optional = true }
mio = { version = "0.7.6", optional = true }
mio = { version = "0.8.0", optional = true }
num_cpus = { version = "1.8.0", optional = true }
parking_lot = { version = "0.11.0", optional = true }
socket2 = { version = "0.4.2", optional = true }
# Currently unstable. The API exposed by these features may be broken at any time.
# Requires `--cfg tokio_unstable` to enable.
@@ -110,7 +109,7 @@ signal-hook-registry = { version = "1.1.1", optional = true }
[target.'cfg(unix)'.dev-dependencies]
libc = { version = "0.2.42" }
nix = { version = "0.22.0" }
nix = { version = "0.23" }
[target.'cfg(windows)'.dependencies.winapi]
version = "0.3.8"
@@ -129,7 +128,6 @@ proptest = "1"
rand = "0.8.0"
tempfile = "3.1.0"
async-stream = "0.3"
socket2 = "0.4"
[target.'cfg(target_os = "freebsd")'.dev-dependencies]
mio-aio = { version = "0.6.0", features = ["tokio"] }
@@ -137,12 +135,13 @@ mio-aio = { version = "0.6.0", features = ["tokio"] }
[target.'cfg(loom)'.dev-dependencies]
loom = { version = "0.5", features = ["futures", "checkpoint"] }
[build-dependencies]
autocfg = "1" # Needed for conditionally enabling `track-caller`
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
# enable unstable features in the documentation
rustdoc-args = ["--cfg", "docsrs", "--cfg", "tokio_unstable"]
# it's necessary to _also_ pass `--cfg tokio_unstable` to rustc, or else
# dependencies will not be enabled, and the docs build will fail.
rustc-args = ["--cfg", "tokio_unstable"]
[package.metadata.playground]
features = ["full", "test-util"]
+3 -2
View File
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.14.0", features = ["full"] }
tokio = { version = "1.15.0", features = ["full"] }
```
Then, on your main.rs:
@@ -164,7 +164,7 @@ several other libraries, including:
## Supported Rust Versions
Tokio is built against the latest stable release. The minimum supported version
is 1.45. The current Tokio version is not guaranteed to build on Rust versions
is 1.46. The current Tokio version is not guaranteed to build on Rust versions
earlier than the minimum supported version.
## Release schedule
@@ -181,6 +181,7 @@ released as a new patch release for each LTS minor version. Our current LTS
releases are:
* `1.8.x` - LTS release until February 2022.
* `1.14.x` - LTS release until June 2022.
Each LTS release will continue to receive backported fixes for at least half a
year. If you wish to use a fixed minor release in your project, we recommend
-22
View File
@@ -1,22 +0,0 @@
use autocfg::AutoCfg;
fn main() {
match AutoCfg::new() {
Ok(ac) => {
// The #[track_caller] attribute was stabilized in rustc 1.46.0.
if ac.probe_rustc_version(1, 46) {
autocfg::emit("tokio_track_caller")
}
}
Err(e) => {
// If we couldn't detect the compiler version and features, just
// print a warning. This isn't a fatal error: we can still build
// Tokio, we just can't enable cfgs automatically.
println!(
"cargo:warning=tokio: failed to detect compiler features: {}",
e
);
}
}
}
+7 -11
View File
@@ -59,13 +59,9 @@ impl Budget {
const fn unconstrained() -> Budget {
Budget(None)
}
}
cfg_rt_multi_thread! {
impl Budget {
fn has_remaining(self) -> bool {
self.0.map(|budget| budget > 0).unwrap_or(true)
}
fn has_remaining(self) -> bool {
self.0.map(|budget| budget > 0).unwrap_or(true)
}
}
@@ -107,16 +103,16 @@ fn with_budget<R>(budget: Budget, f: impl FnOnce() -> R) -> R {
})
}
#[inline(always)]
pub(crate) fn has_budget_remaining() -> bool {
CURRENT.with(|cell| cell.get().has_remaining())
}
cfg_rt_multi_thread! {
/// Sets the current task's budget.
pub(crate) fn set(budget: Budget) {
CURRENT.with(|cell| cell.set(budget))
}
#[inline(always)]
pub(crate) fn has_budget_remaining() -> bool {
CURRENT.with(|cell| cell.get().has_remaining())
}
}
cfg_rt! {
+5 -4
View File
@@ -81,6 +81,7 @@ use std::{task::Context, task::Poll};
///
/// impl AsyncTcpStream {
/// pub fn new(tcp: TcpStream) -> io::Result<Self> {
/// tcp.set_nonblocking(true)?;
/// Ok(Self {
/// inner: AsyncFd::new(tcp)?,
/// })
@@ -525,7 +526,7 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
#[cfg_attr(docsrs, doc(alias = "with_io"))]
pub fn try_io<R>(
&mut self,
f: impl FnOnce(&AsyncFd<Inner>) -> io::Result<R>,
f: impl FnOnce(&'a AsyncFd<Inner>) -> io::Result<R>,
) -> Result<io::Result<R>, TryIoError> {
let result = f(self.async_fd);
@@ -542,12 +543,12 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
}
/// Returns a shared reference to the inner [`AsyncFd`].
pub fn get_ref(&self) -> &AsyncFd<Inner> {
pub fn get_ref(&self) -> &'a AsyncFd<Inner> {
self.async_fd
}
/// Returns a shared reference to the backing object of the inner [`AsyncFd`].
pub fn get_inner(&self) -> &Inner {
pub fn get_inner(&self) -> &'a Inner {
self.get_ref().get_ref()
}
}
@@ -598,7 +599,7 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
&mut self,
f: impl FnOnce(&mut AsyncFd<Inner>) -> io::Result<R>,
) -> Result<io::Result<R>, TryIoError> {
let result = f(&mut self.async_fd);
let result = f(self.async_fd);
if let Err(e) = result.as_ref() {
if e.kind() == io::ErrorKind::WouldBlock {
+22 -3
View File
@@ -15,6 +15,7 @@ mod scheduled_io;
use scheduled_io::ScheduledIo;
use crate::park::{Park, Unpark};
use crate::runtime::stats::IoDriverStats;
use crate::util::slab::{self, Slab};
use crate::{loom::sync::Mutex, util::bit};
@@ -74,6 +75,8 @@ pub(super) struct Inner {
/// Used to wake up the reactor from a call to `turn`.
waker: mio::Waker,
stats: IoDriverStats,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
@@ -112,7 +115,7 @@ fn _assert_kinds() {
impl Driver {
/// Creates a new event loop, returning any error that happened during the
/// creation.
pub(crate) fn new() -> io::Result<Driver> {
pub(crate) fn new(stats: IoDriverStats) -> io::Result<Driver> {
let poll = mio::Poll::new()?;
let waker = mio::Waker::new(poll.registry(), TOKEN_WAKEUP)?;
let registry = poll.registry().try_clone()?;
@@ -130,6 +133,7 @@ impl Driver {
registry,
io_dispatch: allocator,
waker,
stats,
}),
})
}
@@ -153,7 +157,8 @@ impl Driver {
self.tick = self.tick.wrapping_add(1);
if self.tick == COMPACT_INTERVAL {
self.resources.as_mut().unwrap().compact()
self.resources.as_mut().unwrap().compact();
self.inner.stats.incr_compact_count();
}
let mut events = self.events.take().expect("i/o driver event store missing");
@@ -192,6 +197,14 @@ impl Driver {
let res = io.set_readiness(Some(token.0), Tick::Set(self.tick), |curr| curr | ready);
if ready.is_readable() {
self.inner.stats.incr_read_ready_count();
}
if ready.is_writable() {
self.inner.stats.incr_write_ready_count();
}
if res.is_err() {
// token no longer valid!
return;
@@ -335,12 +348,18 @@ impl Inner {
self.registry
.register(source, mio::Token(token), interest.to_mio())?;
self.stats.incr_fd_count();
Ok(shared)
}
/// Deregisters an I/O resource from the reactor.
pub(super) fn deregister_source(&self, source: &mut impl mio::event::Source) -> io::Result<()> {
self.registry.deregister(source)
self.registry.deregister(source)?;
self.stats.dec_fd_count();
Ok(())
}
}
+22 -16
View File
@@ -1,9 +1,5 @@
// This lint claims ugly casting is somehow safer than transmute, but there's
// no evidence that is the case. Shush.
#![allow(clippy::transmute_ptr_to_ptr)]
use std::fmt;
use std::mem::{self, MaybeUninit};
use std::mem::MaybeUninit;
/// A wrapper around a byte buffer that is incrementally filled and initialized.
///
@@ -35,7 +31,7 @@ impl<'a> ReadBuf<'a> {
#[inline]
pub fn new(buf: &'a mut [u8]) -> ReadBuf<'a> {
let initialized = buf.len();
let buf = unsafe { mem::transmute::<&mut [u8], &mut [MaybeUninit<u8>]>(buf) };
let buf = unsafe { slice_to_uninit_mut(buf) };
ReadBuf {
buf,
filled: 0,
@@ -67,8 +63,7 @@ impl<'a> ReadBuf<'a> {
let slice = &self.buf[..self.filled];
// safety: filled describes how far into the buffer that the
// user has filled with bytes, so it's been initialized.
// TODO: This could use `MaybeUninit::slice_get_ref` when it is stable.
unsafe { mem::transmute::<&[MaybeUninit<u8>], &[u8]>(slice) }
unsafe { slice_assume_init(slice) }
}
/// Returns a mutable reference to the filled portion of the buffer.
@@ -77,8 +72,7 @@ impl<'a> ReadBuf<'a> {
let slice = &mut self.buf[..self.filled];
// safety: filled describes how far into the buffer that the
// user has filled with bytes, so it's been initialized.
// TODO: This could use `MaybeUninit::slice_get_mut` when it is stable.
unsafe { mem::transmute::<&mut [MaybeUninit<u8>], &mut [u8]>(slice) }
unsafe { slice_assume_init_mut(slice) }
}
/// Returns a new `ReadBuf` comprised of the unfilled section up to `n`.
@@ -97,8 +91,7 @@ impl<'a> ReadBuf<'a> {
let slice = &self.buf[..self.initialized];
// safety: initialized describes how far into the buffer that the
// user has at some point initialized with bytes.
// TODO: This could use `MaybeUninit::slice_get_ref` when it is stable.
unsafe { mem::transmute::<&[MaybeUninit<u8>], &[u8]>(slice) }
unsafe { slice_assume_init(slice) }
}
/// Returns a mutable reference to the initialized portion of the buffer.
@@ -109,15 +102,14 @@ impl<'a> ReadBuf<'a> {
let slice = &mut self.buf[..self.initialized];
// safety: initialized describes how far into the buffer that the
// user has at some point initialized with bytes.
// TODO: This could use `MaybeUninit::slice_get_mut` when it is stable.
unsafe { mem::transmute::<&mut [MaybeUninit<u8>], &mut [u8]>(slice) }
unsafe { slice_assume_init_mut(slice) }
}
/// Returns a mutable reference to the entire buffer, without ensuring that it has been fully
/// initialized.
///
/// The elements between 0 and `self.filled().len()` are filled, and those between 0 and
/// `self.initialized().len()` are initialized (and so can be transmuted to a `&mut [u8]`).
/// `self.initialized().len()` are initialized (and so can be converted to a `&mut [u8]`).
///
/// The caller of this method must ensure that these invariants are upheld. For example, if the
/// caller initializes some of the uninitialized section of the buffer, it must call
@@ -178,7 +170,7 @@ impl<'a> ReadBuf<'a> {
let slice = &mut self.buf[self.filled..end];
// safety: just above, we checked that the end of the buf has
// been initialized to some value.
unsafe { mem::transmute::<&mut [MaybeUninit<u8>], &mut [u8]>(slice) }
unsafe { slice_assume_init_mut(slice) }
}
/// Returns the number of bytes at the end of the slice that have not yet been filled.
@@ -283,3 +275,17 @@ impl fmt::Debug for ReadBuf<'_> {
.finish()
}
}
unsafe fn slice_to_uninit_mut(slice: &mut [u8]) -> &mut [MaybeUninit<u8>] {
&mut *(slice as *mut [u8] as *mut [MaybeUninit<u8>])
}
// TODO: This could use `MaybeUninit::slice_assume_init` when it is stable.
unsafe fn slice_assume_init(slice: &[MaybeUninit<u8>]) -> &[u8] {
&*(slice as *const [MaybeUninit<u8>] as *const [u8])
}
// TODO: This could use `MaybeUninit::slice_assume_init_mut` when it is stable.
unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit<u8>]) -> &mut [u8] {
&mut *(slice as *mut [MaybeUninit<u8>] as *mut [u8])
}
+1 -2
View File
@@ -204,7 +204,6 @@ impl<R: AsyncRead + AsyncSeek> AsyncSeek for BufReader<R> {
self.as_mut()
.get_pin_mut()
.start_seek(SeekFrom::Current(offset))?;
self.as_mut().get_pin_mut().poll_complete(cx)?
} else {
// seek backwards by our remainder, and then by the offset
self.as_mut()
@@ -221,8 +220,8 @@ impl<R: AsyncRead + AsyncSeek> AsyncSeek for BufReader<R> {
self.as_mut()
.get_pin_mut()
.start_seek(SeekFrom::Current(n))?;
self.as_mut().get_pin_mut().poll_complete(cx)?
}
self.as_mut().get_pin_mut().poll_complete(cx)?
}
SeekState::PendingOverflowed(n) => {
if self.as_mut().get_pin_mut().poll_complete(cx)?.is_pending() {
+18 -2
View File
@@ -50,16 +50,18 @@ impl AsyncRead for Empty {
#[inline]
fn poll_read(
self: Pin<&mut Self>,
_: &mut Context<'_>,
cx: &mut Context<'_>,
_: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(()))
}
}
impl AsyncBufRead for Empty {
#[inline]
fn poll_fill_buf(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(&[]))
}
@@ -73,6 +75,20 @@ impl fmt::Debug for Empty {
}
}
cfg_coop! {
fn poll_proceed_and_make_progress(cx: &mut Context<'_>) -> Poll<()> {
let coop = ready!(crate::coop::poll_proceed(cx));
coop.made_progress();
Poll::Ready(())
}
}
cfg_not_coop! {
fn poll_proceed_and_make_progress(_: &mut Context<'_>) -> Poll<()> {
Poll::Ready(())
}
}
#[cfg(test)]
mod tests {
use super::*;
+2 -2
View File
@@ -51,13 +51,13 @@ where
type Output = io::Result<usize>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
let mut me = self.project();
let me = self.project();
loop {
// if our buffer is empty, then we need to read some data to continue.
let rem = me.buf.remaining();
if rem != 0 {
ready!(Pin::new(&mut *me.reader).poll_read(cx, &mut me.buf))?;
ready!(Pin::new(&mut *me.reader).poll_read(cx, me.buf))?;
if me.buf.remaining() == rem {
return Err(eof()).into();
}
+1 -1
View File
@@ -42,7 +42,7 @@ where
while !me.buf.is_empty() {
let n = ready!(Pin::new(&mut *me.writer).poll_write(cx, me.buf))?;
{
let (_, rest) = mem::replace(&mut *me.buf, &[]).split_at(n);
let (_, rest) = mem::take(&mut *me.buf).split_at(n);
*me.buf = rest;
}
if n == 0 {
+2 -6
View File
@@ -10,16 +10,11 @@
unreachable_pub
)]
#![deny(unused_must_use)]
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg_hide))]
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
#![cfg_attr(docsrs, doc(cfg_hide(loom)))]
#![cfg_attr(docsrs, doc(cfg_hide(not(loom))))]
#![cfg_attr(docsrs, allow(unused_attributes))]
//! A runtime for writing reliable network applications without compromising speed.
@@ -312,7 +307,7 @@
//! Beware though that this will pull in many extra dependencies that you may not
//! need.
//!
//! - `full`: Enables all Tokio public API features listed below except `test-util`.
//! - `full`: Enables all features listed below except `test-util` and `tracing`.
//! - `rt`: Enables `tokio::spawn`, the basic (current thread) scheduler,
//! and non-scheduler utilities.
//! - `rt-multi-thread`: Enables the heavier, multi-threaded, work-stealing scheduler.
@@ -351,6 +346,7 @@
//! `RUSTFLAGS="--cfg tokio_unstable"`.
//!
//! - `tracing`: Enables tracing events.
//! - `stats`: Enables runtime stats collection. ([RFC](https://github.com/tokio-rs/tokio/pull/3845))
//!
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
+99 -21
View File
@@ -3,83 +3,143 @@
//!
//! This can be extended to additional types/methods as required.
use std::fmt;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::sync::LockResult;
use std::time::Duration;
// All types in this file are marked with PhantomData to ensure that
// parking_lot's send_guard feature does not leak through and affect when Tokio
// types are Send.
//
// See <https://github.com/tokio-rs/tokio/pull/4359> for more info.
// Types that do not need wrapping
pub(crate) use parking_lot::{MutexGuard, RwLockReadGuard, RwLockWriteGuard, WaitTimeoutResult};
/// Adapter for `parking_lot::Mutex` to the `std::sync::Mutex` interface.
#[derive(Debug)]
pub(crate) struct Mutex<T: ?Sized>(parking_lot::Mutex<T>);
pub(crate) use parking_lot::WaitTimeoutResult;
#[derive(Debug)]
pub(crate) struct RwLock<T>(parking_lot::RwLock<T>);
pub(crate) struct Mutex<T: ?Sized>(PhantomData<std::sync::Mutex<T>>, parking_lot::Mutex<T>);
/// Adapter for `parking_lot::Condvar` to the `std::sync::Condvar` interface.
#[derive(Debug)]
pub(crate) struct Condvar(parking_lot::Condvar);
pub(crate) struct RwLock<T>(PhantomData<std::sync::RwLock<T>>, parking_lot::RwLock<T>);
#[derive(Debug)]
pub(crate) struct Condvar(PhantomData<std::sync::Condvar>, parking_lot::Condvar);
#[derive(Debug)]
pub(crate) struct MutexGuard<'a, T: ?Sized>(
PhantomData<std::sync::MutexGuard<'a, T>>,
parking_lot::MutexGuard<'a, T>,
);
#[derive(Debug)]
pub(crate) struct RwLockReadGuard<'a, T: ?Sized>(
PhantomData<std::sync::RwLockReadGuard<'a, T>>,
parking_lot::RwLockReadGuard<'a, T>,
);
#[derive(Debug)]
pub(crate) struct RwLockWriteGuard<'a, T: ?Sized>(
PhantomData<std::sync::RwLockWriteGuard<'a, T>>,
parking_lot::RwLockWriteGuard<'a, T>,
);
impl<T> Mutex<T> {
#[inline]
pub(crate) fn new(t: T) -> Mutex<T> {
Mutex(parking_lot::Mutex::new(t))
Mutex(PhantomData, parking_lot::Mutex::new(t))
}
#[inline]
#[cfg(all(feature = "parking_lot", not(all(loom, test)),))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "parking_lot",))))]
pub(crate) const fn const_new(t: T) -> Mutex<T> {
Mutex(parking_lot::const_mutex(t))
Mutex(PhantomData, parking_lot::const_mutex(t))
}
#[inline]
pub(crate) fn lock(&self) -> MutexGuard<'_, T> {
self.0.lock()
MutexGuard(PhantomData, self.1.lock())
}
#[inline]
pub(crate) fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
self.0.try_lock()
self.1
.try_lock()
.map(|guard| MutexGuard(PhantomData, guard))
}
#[inline]
pub(crate) fn get_mut(&mut self) -> &mut T {
self.0.get_mut()
self.1.get_mut()
}
// Note: Additional methods `is_poisoned` and `into_inner`, can be
// provided here as needed.
}
impl<'a, T: ?Sized> Deref for MutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.1.deref()
}
}
impl<'a, T: ?Sized> DerefMut for MutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
self.1.deref_mut()
}
}
impl<T> RwLock<T> {
pub(crate) fn new(t: T) -> RwLock<T> {
RwLock(parking_lot::RwLock::new(t))
RwLock(PhantomData, parking_lot::RwLock::new(t))
}
pub(crate) fn read(&self) -> LockResult<RwLockReadGuard<'_, T>> {
Ok(self.0.read())
Ok(RwLockReadGuard(PhantomData, self.1.read()))
}
pub(crate) fn write(&self) -> LockResult<RwLockWriteGuard<'_, T>> {
Ok(self.0.write())
Ok(RwLockWriteGuard(PhantomData, self.1.write()))
}
}
impl<'a, T: ?Sized> Deref for RwLockReadGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.1.deref()
}
}
impl<'a, T: ?Sized> Deref for RwLockWriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.1.deref()
}
}
impl<'a, T: ?Sized> DerefMut for RwLockWriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
self.1.deref_mut()
}
}
impl Condvar {
#[inline]
pub(crate) fn new() -> Condvar {
Condvar(parking_lot::Condvar::new())
Condvar(PhantomData, parking_lot::Condvar::new())
}
#[inline]
pub(crate) fn notify_one(&self) {
self.0.notify_one();
self.1.notify_one();
}
#[inline]
pub(crate) fn notify_all(&self) {
self.0.notify_all();
self.1.notify_all();
}
#[inline]
@@ -87,7 +147,7 @@ impl Condvar {
&self,
mut guard: MutexGuard<'a, T>,
) -> LockResult<MutexGuard<'a, T>> {
self.0.wait(&mut guard);
self.1.wait(&mut guard.1);
Ok(guard)
}
@@ -97,10 +157,28 @@ impl Condvar {
mut guard: MutexGuard<'a, T>,
timeout: Duration,
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> {
let wtr = self.0.wait_for(&mut guard, timeout);
let wtr = self.1.wait_for(&mut guard.1, timeout);
Ok((guard, wtr))
}
// Note: Additional methods `wait_timeout_ms`, `wait_timeout_until`,
// `wait_until` can be provided here as needed.
}
impl<'a, T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.1, f)
}
}
impl<'a, T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.1, f)
}
}
impl<'a, T: ?Sized + fmt::Display> fmt::Display for RwLockWriteGuard<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.1, f)
}
}
+3 -4
View File
@@ -99,7 +99,6 @@ macro_rules! cfg_io_driver_impl {
feature = "process",
all(unix, feature = "signal"),
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
$item
)*
}
@@ -179,7 +178,7 @@ macro_rules! cfg_stats {
($($item:item)*) => {
$(
#[cfg(all(tokio_unstable, feature = "stats"))]
#[cfg_attr(docsrs, doc(cfg(feature = "stats")))]
#[cfg_attr(docsrs, doc(cfg(all(tokio_unstable, feature = "stats"))))]
$item
)*
}
@@ -366,10 +365,10 @@ macro_rules! cfg_trace {
($($item:item)*) => {
$(
#[cfg(all(tokio_unstable, feature = "tracing"))]
#[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
#[cfg_attr(docsrs, doc(cfg(all(tokio_unstable, feature = "tracing"))))]
$item
)*
}
};
}
macro_rules! cfg_not_trace {
+4 -5
View File
@@ -1,9 +1,8 @@
cfg_trace! {
macro_rules! trace_op {
($name:literal, $readiness:literal, $parent:expr) => {
($name:expr, $readiness:literal) => {
tracing::trace!(
target: "runtime::resource::poll_op",
parent: $parent,
op_name = $name,
is_ready = $readiness
);
@@ -11,14 +10,14 @@ cfg_trace! {
}
macro_rules! trace_poll_op {
($name:literal, $poll:expr, $parent:expr $(,)*) => {
($name:expr, $poll:expr $(,)*) => {
match $poll {
std::task::Poll::Ready(t) => {
trace_op!($name, true, $parent);
trace_op!($name, true);
std::task::Poll::Ready(t)
}
std::task::Poll::Pending => {
trace_op!($name, false, $parent);
trace_op!($name, false);
return std::task::Poll::Pending;
}
}
+126 -22
View File
@@ -1,5 +1,6 @@
use crate::net::{TcpListener, TcpStream};
use std::convert::TryInto;
use std::fmt;
use std::io;
use std::net::SocketAddr;
@@ -8,6 +9,7 @@ use std::net::SocketAddr;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
use std::time::Duration;
cfg_net! {
/// A TCP socket that has not yet been converted to a `TcpStream` or
@@ -81,8 +83,9 @@ cfg_net! {
/// [`AsRawFd`]: https://doc.rust-lang.org/std/os/unix/io/trait.AsRawFd.html
/// [`AsRawSocket`]: https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html
/// [`socket2`]: https://docs.rs/socket2/
#[cfg_attr(docsrs, doc(alias = "connect_std"))]
pub struct TcpSocket {
inner: mio::net::TcpSocket,
inner: socket2::Socket,
}
}
@@ -117,7 +120,11 @@ impl TcpSocket {
/// }
/// ```
pub fn new_v4() -> io::Result<TcpSocket> {
let inner = mio::net::TcpSocket::new_v4()?;
let inner = socket2::Socket::new(
socket2::Domain::IPV4,
socket2::Type::STREAM,
Some(socket2::Protocol::TCP),
)?;
Ok(TcpSocket { inner })
}
@@ -151,7 +158,11 @@ impl TcpSocket {
/// }
/// ```
pub fn new_v6() -> io::Result<TcpSocket> {
let inner = mio::net::TcpSocket::new_v6()?;
let inner = socket2::Socket::new(
socket2::Domain::IPV6,
socket2::Type::STREAM,
Some(socket2::Protocol::TCP),
)?;
Ok(TcpSocket { inner })
}
@@ -182,7 +193,7 @@ impl TcpSocket {
/// }
/// ```
pub fn set_reuseaddr(&self, reuseaddr: bool) -> io::Result<()> {
self.inner.set_reuseaddr(reuseaddr)
self.inner.set_reuse_address(reuseaddr)
}
/// Retrieves the value set for `SO_REUSEADDR` on this socket.
@@ -208,7 +219,7 @@ impl TcpSocket {
/// }
/// ```
pub fn reuseaddr(&self) -> io::Result<bool> {
self.inner.get_reuseaddr()
self.inner.reuse_address()
}
/// Allows the socket to bind to an in-use port. Only available for unix systems
@@ -242,7 +253,7 @@ impl TcpSocket {
doc(cfg(all(unix, not(target_os = "solaris"), not(target_os = "illumos"))))
)]
pub fn set_reuseport(&self, reuseport: bool) -> io::Result<()> {
self.inner.set_reuseport(reuseport)
self.inner.set_reuse_port(reuseport)
}
/// Allows the socket to bind to an in-use port. Only available for unix systems
@@ -277,14 +288,14 @@ impl TcpSocket {
doc(cfg(all(unix, not(target_os = "solaris"), not(target_os = "illumos"))))
)]
pub fn reuseport(&self) -> io::Result<bool> {
self.inner.get_reuseport()
self.inner.reuse_port()
}
/// Sets the size of the TCP send buffer on this socket.
///
/// On most operating systems, this sets the `SO_SNDBUF` socket option.
pub fn set_send_buffer_size(&self, size: u32) -> io::Result<()> {
self.inner.set_send_buffer_size(size)
self.inner.set_send_buffer_size(size as usize)
}
/// Returns the size of the TCP send buffer for this socket.
@@ -311,14 +322,14 @@ impl TcpSocket {
///
/// [`set_send_buffer_size`]: #method.set_send_buffer_size
pub fn send_buffer_size(&self) -> io::Result<u32> {
self.inner.get_send_buffer_size()
self.inner.send_buffer_size().map(|n| n as u32)
}
/// Sets the size of the TCP receive buffer on this socket.
///
/// On most operating systems, this sets the `SO_RCVBUF` socket option.
pub fn set_recv_buffer_size(&self, size: u32) -> io::Result<()> {
self.inner.set_recv_buffer_size(size)
self.inner.set_recv_buffer_size(size as usize)
}
/// Returns the size of the TCP receive buffer for this socket.
@@ -345,7 +356,84 @@ impl TcpSocket {
///
/// [`set_recv_buffer_size`]: #method.set_recv_buffer_size
pub fn recv_buffer_size(&self) -> io::Result<u32> {
self.inner.get_recv_buffer_size()
self.inner.recv_buffer_size().map(|n| n as u32)
}
/// Sets the linger duration of this socket by setting the SO_LINGER option.
///
/// This option controls the action taken when a stream has unsent messages and the stream is
/// closed. If SO_LINGER is set, the system shall block the process until it can transmit the
/// data or until the time expires.
///
/// If SO_LINGER is not specified, and the socket is closed, the system handles the call in a
/// way that allows the process to continue as quickly as possible.
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
self.inner.set_linger(dur)
}
/// Reads the linger duration for this socket by getting the `SO_LINGER`
/// option.
///
/// For more information about this option, see [`set_linger`].
///
/// [`set_linger`]: TcpSocket::set_linger
pub fn linger(&self) -> io::Result<Option<Duration>> {
self.inner.linger()
}
/// Gets the value of the `IP_TOS` option for this socket.
///
/// For more information about this option, see [`set_tos`].
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
///
/// [`set_tos`]: Self::set_tos
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
))))
)]
pub fn tos(&self) -> io::Result<u32> {
self.inner.tos()
}
/// Sets the value for the `IP_TOS` option on this socket.
///
/// This value sets the time-to-live field that is used in every packet sent
/// from this socket.
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
))))
)]
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
self.inner.set_tos(tos)
}
/// Gets the local address of this socket.
@@ -371,7 +459,14 @@ impl TcpSocket {
/// }
/// ```
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.inner.get_localaddr()
self.inner
.local_addr()
.map(|addr| addr.as_socket().unwrap())
}
/// Returns the value of the `SO_ERROR` option.
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.inner.take_error()
}
/// Binds the socket to the given address.
@@ -403,7 +498,7 @@ impl TcpSocket {
/// }
/// ```
pub fn bind(&self, addr: SocketAddr) -> io::Result<()> {
self.inner.bind(addr)
self.inner.bind(&addr.into())
}
/// Establishes a TCP connection with a peer at the specified socket address.
@@ -439,7 +534,13 @@ impl TcpSocket {
/// }
/// ```
pub async fn connect(self, addr: SocketAddr) -> io::Result<TcpStream> {
let mio = self.inner.connect(addr)?;
self.inner.connect(&addr.into())?;
#[cfg(windows)]
let mio = unsafe { mio::net::TcpStream::from_raw_socket(self.inner.into_raw_socket()) };
#[cfg(unix)]
let mio = unsafe { mio::net::TcpStream::from_raw_fd(self.inner.into_raw_fd()) };
TcpStream::connect_mio(mio).await
}
@@ -479,7 +580,14 @@ impl TcpSocket {
/// }
/// ```
pub fn listen(self, backlog: u32) -> io::Result<TcpListener> {
let mio = self.inner.listen(backlog)?;
let backlog = backlog.try_into().unwrap_or(i32::MAX);
self.inner.listen(backlog)?;
#[cfg(windows)]
let mio = unsafe { mio::net::TcpListener::from_raw_socket(self.inner.into_raw_socket()) };
#[cfg(unix)]
let mio = unsafe { mio::net::TcpListener::from_raw_fd(self.inner.into_raw_fd()) };
TcpListener::new(mio)
}
@@ -499,7 +607,7 @@ impl TcpSocket {
///
/// #[tokio::main]
/// async fn main() -> std::io::Result<()> {
///
///
/// let socket2_socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
///
/// let socket = TcpSocket::from_std_stream(socket2_socket.into());
@@ -510,16 +618,12 @@ impl TcpSocket {
pub fn from_std_stream(std_stream: std::net::TcpStream) -> TcpSocket {
#[cfg(unix)]
{
use std::os::unix::io::{FromRawFd, IntoRawFd};
let raw_fd = std_stream.into_raw_fd();
unsafe { TcpSocket::from_raw_fd(raw_fd) }
}
#[cfg(windows)]
{
use std::os::windows::io::{FromRawSocket, IntoRawSocket};
let raw_socket = std_stream.into_raw_socket();
unsafe { TcpSocket::from_raw_socket(raw_socket) }
}
@@ -548,7 +652,7 @@ impl FromRawFd for TcpSocket {
/// The caller is responsible for ensuring that the socket is in
/// non-blocking mode.
unsafe fn from_raw_fd(fd: RawFd) -> TcpSocket {
let inner = mio::net::TcpSocket::from_raw_fd(fd);
let inner = socket2::Socket::from_raw_fd(fd);
TcpSocket { inner }
}
}
@@ -583,7 +687,7 @@ impl FromRawSocket for TcpSocket {
/// The caller is responsible for ensuring that the socket is in
/// non-blocking mode.
unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
let inner = mio::net::TcpSocket::from_raw_socket(socket);
let inner = socket2::Socket::from_raw_socket(socket);
TcpSocket { inner }
}
}
+7 -19
View File
@@ -387,7 +387,7 @@ impl TcpStream {
/// // if the readiness event is a false positive.
/// match stream.try_read(&mut data) {
/// Ok(n) => {
/// println!("read {} bytes", n);
/// println!("read {} bytes", n);
/// }
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
/// continue;
@@ -1090,9 +1090,8 @@ impl TcpStream {
/// # }
/// ```
pub fn linger(&self) -> io::Result<Option<Duration>> {
let mio_socket = std::mem::ManuallyDrop::new(self.to_mio());
mio_socket.get_linger()
let socket = self.as_socket();
socket.linger()
}
/// Sets the linger duration of this socket by setting the SO_LINGER option.
@@ -1117,23 +1116,12 @@ impl TcpStream {
/// # }
/// ```
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
let mio_socket = std::mem::ManuallyDrop::new(self.to_mio());
mio_socket.set_linger(dur)
let socket = self.as_socket();
socket.set_linger(dur)
}
fn to_mio(&self) -> mio::net::TcpSocket {
#[cfg(windows)]
{
use std::os::windows::io::{AsRawSocket, FromRawSocket};
unsafe { mio::net::TcpSocket::from_raw_socket(self.as_raw_socket()) }
}
#[cfg(unix)]
{
use std::os::unix::io::{AsRawFd, FromRawFd};
unsafe { mio::net::TcpSocket::from_raw_fd(self.as_raw_fd()) }
}
fn as_socket(&self) -> socket2::SockRef<'_> {
socket2::SockRef::from(self)
}
/// Gets the value of the `IP_TTL` option for this socket.
+150
View File
@@ -253,6 +253,78 @@ impl UdpSocket {
}
}
/// Sets the size of the UDP send buffer on this socket.
///
/// On most operating systems, this sets the `SO_SNDBUF` socket option.
pub fn set_send_buffer_size(&self, size: u32) -> io::Result<()> {
self.as_socket().set_send_buffer_size(size as usize)
}
/// Returns the size of the UDP send buffer for this socket.
///
/// On most operating systems, this is the value of the `SO_SNDBUF` socket
/// option.
///
/// Note that if [`set_send_buffer_size`] has been called on this socket
/// previously, the value returned by this function may not be the same as
/// the argument provided to `set_send_buffer_size`. This is for the
/// following reasons:
///
/// * Most operating systems have minimum and maximum allowed sizes for the
/// send buffer, and will clamp the provided value if it is below the
/// minimum or above the maximum. The minimum and maximum buffer sizes are
/// OS-dependent.
/// * Linux will double the buffer size to account for internal bookkeeping
/// data, and returns the doubled value from `getsockopt(2)`. As per `man
/// 7 socket`:
/// > Sets or gets the maximum socket send buffer in bytes. The
/// > kernel doubles this value (to allow space for bookkeeping
/// > overhead) when it is set using `setsockopt(2)`, and this doubled
/// > value is returned by `getsockopt(2)`.
///
/// [`set_send_buffer_size`]: Self::set_send_buffer_size
pub fn send_buffer_size(&self) -> io::Result<u32> {
self.as_socket().send_buffer_size().map(|n| n as u32)
}
/// Sets the size of the UDP receive buffer on this socket.
///
/// On most operating systems, this sets the `SO_RCVBUF` socket option.
pub fn set_recv_buffer_size(&self, size: u32) -> io::Result<()> {
self.as_socket().set_recv_buffer_size(size as usize)
}
/// Returns the size of the UDP receive buffer for this socket.
///
/// On most operating systems, this is the value of the `SO_RCVBUF` socket
/// option.
///
/// Note that if [`set_recv_buffer_size`] has been called on this socket
/// previously, the value returned by this function may not be the same as
/// the argument provided to `set_send_buffer_size`. This is for the
/// following reasons:
///
/// * Most operating systems have minimum and maximum allowed sizes for the
/// receive buffer, and will clamp the provided value if it is below the
/// minimum or above the maximum. The minimum and maximum buffer sizes are
/// OS-dependent.
/// * Linux will double the buffer size to account for internal bookkeeping
/// data, and returns the doubled value from `getsockopt(2)`. As per `man
/// 7 socket`:
/// > Sets or gets the maximum socket send buffer in bytes. The
/// > kernel doubles this value (to allow space for bookkeeping
/// > overhead) when it is set using `setsockopt(2)`, and this doubled
/// > value is returned by `getsockopt(2)`.
///
/// [`set_recv_buffer_size`]: Self::set_recv_buffer_size
pub fn recv_buffer_size(&self) -> io::Result<u32> {
self.as_socket().recv_buffer_size().map(|n| n as u32)
}
fn as_socket(&self) -> socket2::SockRef<'_> {
socket2::SockRef::from(self)
}
/// Returns the local address that this socket is bound to.
///
/// # Example
@@ -274,6 +346,29 @@ impl UdpSocket {
self.io.local_addr()
}
/// Returns the socket address of the remote peer this socket was connected
/// to.
///
/// # Example
///
/// ```
/// use tokio::net::UdpSocket;
/// # use std::{io, net::SocketAddr};
///
/// # #[tokio::main]
/// # async fn main() -> io::Result<()> {
/// let addr = "127.0.0.1:0".parse::<SocketAddr>().unwrap();
/// let peer_addr = "127.0.0.1:11100".parse::<SocketAddr>().unwrap();
/// let sock = UdpSocket::bind(addr).await?;
/// sock.connect(peer_addr).await?;
/// assert_eq!(sock.peer_addr()?.ip(), peer_addr.ip());
/// # Ok(())
/// # }
/// ```
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.io.peer_addr()
}
/// Connects the UDP socket setting the default destination for send() and
/// limiting packets that are read via recv from the address specified in
/// `addr`.
@@ -1480,6 +1575,61 @@ impl UdpSocket {
self.io.set_ttl(ttl)
}
/// Gets the value of the `IP_TOS` option for this socket.
///
/// For more information about this option, see [`set_tos`].
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
///
/// [`set_tos`]: Self::set_tos
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
))))
)]
pub fn tos(&self) -> io::Result<u32> {
self.as_socket().tos()
}
/// Sets the value for the `IP_TOS` option on this socket.
///
/// This value sets the time-to-live field that is used in every packet sent
/// from this socket.
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
))))
)]
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
self.as_socket().set_tos(tos)
}
/// Executes an operation of the `IP_ADD_MEMBERSHIP` type.
///
/// This function specifies a new multicast group for this socket to join.
+6
View File
@@ -264,6 +264,12 @@ impl Command {
Self::from(StdCommand::new(program))
}
/// Cheaply convert to a `&std::process::Command` for places where the type from the standard
/// library is expected.
pub fn as_std(&self) -> &StdCommand {
&self.std
}
/// Adds an argument to pass to the program.
///
/// Only one argument can be passed per use. So instead of:
+228 -201
View File
@@ -3,10 +3,12 @@ use crate::loom::sync::atomic::AtomicBool;
use crate::loom::sync::Mutex;
use crate::park::{Park, Unpark};
use crate::runtime::context::EnterGuard;
use crate::runtime::driver::Driver;
use crate::runtime::stats::{RuntimeStats, WorkerStatsBatcher};
use crate::runtime::task::{self, JoinHandle, OwnedTasks, Schedule, Task};
use crate::runtime::Callback;
use crate::sync::notify::Notify;
use crate::util::atomic_cell::AtomicCell;
use crate::util::{waker_ref, Wake, WakerRef};
use std::cell::RefCell;
@@ -19,13 +21,12 @@ use std::task::Poll::{Pending, Ready};
use std::time::Duration;
/// Executes tasks on the current thread
pub(crate) struct BasicScheduler<P: Park> {
/// Inner state guarded by a mutex that is shared
/// between all `block_on` calls.
inner: Mutex<Option<Inner<P>>>,
pub(crate) struct BasicScheduler {
/// Core scheduler data is acquired by a thread entering `block_on`.
core: AtomicCell<Core>,
/// Notifier for waking up other threads to steal the
/// parker.
/// driver.
notify: Notify,
/// Sendable task spawner
@@ -38,15 +39,11 @@ pub(crate) struct BasicScheduler<P: Park> {
context_guard: Option<EnterGuard>,
}
/// The inner scheduler that owns the task queue and the main parker P.
struct Inner<P: Park> {
/// Data required for executing the scheduler. The struct is passed around to
/// a function that will perform the scheduling work and acts as a capability token.
struct Core {
/// Scheduler run queue
///
/// When the scheduler is executed, the queue is removed from `self` and
/// moved into `Context`.
///
/// This indirection is to allow `BasicScheduler` to be `Send`.
tasks: Option<Tasks>,
tasks: VecDeque<task::Notified<Arc<Shared>>>,
/// Sendable task spawner
spawner: Spawner,
@@ -54,13 +51,10 @@ struct Inner<P: Park> {
/// Current tick
tick: u8,
/// Thread park handle
park: P,
/// Callback for a worker parking itself
before_park: Option<Callback>,
/// Callback for a worker unparking itself
after_unpark: Option<Callback>,
/// Runtime driver
///
/// The driver is removed before starting to park the thread
driver: Option<Driver>,
/// Stats batcher
stats: WorkerStatsBatcher,
@@ -71,13 +65,6 @@ pub(crate) struct Spawner {
shared: Arc<Shared>,
}
struct Tasks {
/// Local run queue.
///
/// Tasks notified from the current thread are pushed into this queue.
queue: VecDeque<task::Notified<Arc<Shared>>>,
}
/// A remote scheduler entry.
///
/// These are filled in by remote threads sending instructions to the scheduler.
@@ -100,22 +87,29 @@ struct Shared {
owned: OwnedTasks<Arc<Shared>>,
/// Unpark the blocked thread.
unpark: Box<dyn Unpark>,
unpark: <Driver as Park>::Unpark,
/// Indicates whether the blocked on thread was woken.
woken: AtomicBool,
/// Callback for a worker parking itself
before_park: Option<Callback>,
/// Callback for a worker unparking itself
after_unpark: Option<Callback>,
/// Keeps track of various runtime stats.
stats: RuntimeStats,
}
/// Thread-local context.
struct Context {
/// Shared scheduler state
shared: Arc<Shared>,
/// Handle to the spawner
spawner: Spawner,
/// Local queue
tasks: RefCell<Tasks>,
/// Scheduler core, enabling the holder of `Context` to execute the
/// scheduler.
core: RefCell<Option<Box<Core>>>,
}
/// Initial queue capacity.
@@ -133,38 +127,36 @@ const REMOTE_FIRST_INTERVAL: u8 = 31;
// Tracks the current BasicScheduler.
scoped_thread_local!(static CURRENT: Context);
impl<P: Park> BasicScheduler<P> {
impl BasicScheduler {
pub(crate) fn new(
park: P,
driver: Driver,
before_park: Option<Callback>,
after_unpark: Option<Callback>,
) -> BasicScheduler<P> {
let unpark = Box::new(park.unpark());
) -> BasicScheduler {
let unpark = driver.unpark();
let spawner = Spawner {
shared: Arc::new(Shared {
queue: Mutex::new(Some(VecDeque::with_capacity(INITIAL_CAPACITY))),
owned: OwnedTasks::new(),
unpark: unpark as Box<dyn Unpark>,
unpark,
woken: AtomicBool::new(false),
before_park,
after_unpark,
stats: RuntimeStats::new(1),
}),
};
let inner = Mutex::new(Some(Inner {
tasks: Some(Tasks {
queue: VecDeque::with_capacity(INITIAL_CAPACITY),
}),
let core = AtomicCell::new(Some(Box::new(Core {
tasks: VecDeque::with_capacity(INITIAL_CAPACITY),
spawner: spawner.clone(),
tick: 0,
park,
before_park,
after_unpark,
driver: Some(driver),
stats: WorkerStatsBatcher::new(0),
}));
})));
BasicScheduler {
inner,
core,
notify: Notify::new(),
spawner,
context_guard: None,
@@ -178,12 +170,12 @@ impl<P: Park> BasicScheduler<P> {
pub(crate) fn block_on<F: Future>(&self, future: F) -> F::Output {
pin!(future);
// Attempt to steal the dedicated parker and block_on the future if we can there,
// otherwise, lets select on a notification that the parker is available
// or the future is complete.
// Attempt to steal the scheduler core and block_on the future if we can
// there, otherwise, lets select on a notification that the core is
// available or the future is complete.
loop {
if let Some(inner) = &mut self.take_inner() {
return inner.block_on(future);
if let Some(core) = self.take_core() {
return core.block_on(future);
} else {
let mut enter = crate::runtime::enter(false);
@@ -210,11 +202,14 @@ impl<P: Park> BasicScheduler<P> {
}
}
fn take_inner(&self) -> Option<InnerGuard<'_, P>> {
let inner = self.inner.lock().take()?;
fn take_core(&self) -> Option<CoreGuard<'_>> {
let core = self.core.take()?;
Some(InnerGuard {
inner: Some(inner),
Some(CoreGuard {
context: Context {
spawner: self.spawner.clone(),
core: RefCell::new(Some(core)),
},
basic_scheduler: self,
})
}
@@ -224,156 +219,109 @@ impl<P: Park> BasicScheduler<P> {
}
}
impl<P: Park> Inner<P> {
/// Blocks on the provided future and drives the runtime's driver.
fn block_on<F: Future>(&mut self, future: F) -> F::Output {
enter(self, |scheduler, context| {
let _enter = crate::runtime::enter(false);
let waker = scheduler.spawner.waker_ref();
let mut cx = std::task::Context::from_waker(&waker);
pin!(future);
'outer: loop {
if scheduler.spawner.reset_woken() {
scheduler.stats.incr_poll_count();
if let Ready(v) = crate::coop::budget(|| future.as_mut().poll(&mut cx)) {
return v;
}
}
for _ in 0..MAX_TASKS_PER_TICK {
// Get and increment the current tick
let tick = scheduler.tick;
scheduler.tick = scheduler.tick.wrapping_add(1);
let entry = if tick % REMOTE_FIRST_INTERVAL == 0 {
scheduler.spawner.pop().or_else(|| {
context
.tasks
.borrow_mut()
.queue
.pop_front()
.map(RemoteMsg::Schedule)
})
} else {
context
.tasks
.borrow_mut()
.queue
.pop_front()
.map(RemoteMsg::Schedule)
.or_else(|| scheduler.spawner.pop())
};
let entry = match entry {
Some(entry) => entry,
None => {
if let Some(f) = &scheduler.before_park {
f();
}
// This check will fail if `before_park` spawns a task for us to run
// instead of parking the thread
if context.tasks.borrow_mut().queue.is_empty() {
// Park until the thread is signaled
scheduler.stats.about_to_park();
scheduler.stats.submit(&scheduler.spawner.shared.stats);
scheduler.park.park().expect("failed to park");
scheduler.stats.returned_from_park();
}
if let Some(f) = &scheduler.after_unpark {
f();
}
// Try polling the `block_on` future next
continue 'outer;
}
};
match entry {
RemoteMsg::Schedule(task) => {
scheduler.stats.incr_poll_count();
let task = context.shared.owned.assert_owner(task);
crate::coop::budget(|| task.run())
}
}
}
// Yield to the park, this drives the timer and pulls any pending
// I/O events.
scheduler.stats.submit(&scheduler.spawner.shared.stats);
scheduler
.park
.park_timeout(Duration::from_millis(0))
.expect("failed to park");
}
})
}
}
/// Enters the scheduler context. This sets the queue and other necessary
/// scheduler state in the thread-local.
fn enter<F, R, P>(scheduler: &mut Inner<P>, f: F) -> R
where
F: FnOnce(&mut Inner<P>, &Context) -> R,
P: Park,
{
// Ensures the run queue is placed back in the `BasicScheduler` instance
// once `block_on` returns.`
struct Guard<'a, P: Park> {
context: Option<Context>,
scheduler: &'a mut Inner<P>,
impl Context {
/// Execute the closure with the given scheduler core stored in the
/// thread-local context.
fn run_task<R>(&self, mut core: Box<Core>, f: impl FnOnce() -> R) -> (Box<Core>, R) {
core.stats.incr_poll_count();
self.enter(core, || crate::coop::budget(f))
}
impl<P: Park> Drop for Guard<'_, P> {
fn drop(&mut self) {
let Context { tasks, .. } = self.context.take().expect("context missing");
self.scheduler.tasks = Some(tasks.into_inner());
/// Blocks the current thread until an event is received by the driver,
/// including I/O events, timer events, ...
fn park(&self, mut core: Box<Core>) -> Box<Core> {
let mut driver = core.driver.take().expect("driver missing");
if let Some(f) = &self.spawner.shared.before_park {
// Incorrect lint, the closures are actually different types so `f`
// cannot be passed as an argument to `enter`.
#[allow(clippy::redundant_closure)]
let (c, _) = self.enter(core, || f());
core = c;
}
// This check will fail if `before_park` spawns a task for us to run
// instead of parking the thread
if core.tasks.is_empty() {
// Park until the thread is signaled
core.stats.about_to_park();
core.stats.submit(&core.spawner.shared.stats);
let (c, _) = self.enter(core, || {
driver.park().expect("failed to park");
});
core = c;
core.stats.returned_from_park();
}
if let Some(f) = &self.spawner.shared.after_unpark {
// Incorrect lint, the closures are actually different types so `f`
// cannot be passed as an argument to `enter`.
#[allow(clippy::redundant_closure)]
let (c, _) = self.enter(core, || f());
core = c;
}
core.driver = Some(driver);
core
}
// Remove `tasks` from `self` and place it in a `Context`.
let tasks = scheduler.tasks.take().expect("invalid state");
/// Checks the driver for new events without blocking the thread.
fn park_yield(&self, mut core: Box<Core>) -> Box<Core> {
let mut driver = core.driver.take().expect("driver missing");
let guard = Guard {
context: Some(Context {
shared: scheduler.spawner.shared.clone(),
tasks: RefCell::new(tasks),
}),
scheduler,
};
core.stats.submit(&core.spawner.shared.stats);
let (mut core, _) = self.enter(core, || {
driver
.park_timeout(Duration::from_millis(0))
.expect("failed to park");
});
let context = guard.context.as_ref().unwrap();
let scheduler = &mut *guard.scheduler;
core.driver = Some(driver);
core
}
CURRENT.set(context, || f(scheduler, context))
fn enter<R>(&self, core: Box<Core>, f: impl FnOnce() -> R) -> (Box<Core>, R) {
// Store the scheduler core in the thread-local context
//
// A drop-guard is employed at a higher level.
*self.core.borrow_mut() = Some(core);
// Execute the closure while tracking the execution budget
let ret = f();
// Take the scheduler core back
let core = self.core.borrow_mut().take().expect("core missing");
(core, ret)
}
}
impl<P: Park> Drop for BasicScheduler<P> {
impl Drop for BasicScheduler {
fn drop(&mut self) {
// Avoid a double panic if we are currently panicking and
// the lock may be poisoned.
let mut inner = match self.inner.lock().take() {
Some(inner) => inner,
let core = match self.take_core() {
Some(core) => core,
None if std::thread::panicking() => return,
None => panic!("Oh no! We never placed the Inner state back, this is a bug!"),
None => panic!("Oh no! We never placed the Core back, this is a bug!"),
};
enter(&mut inner, |scheduler, context| {
core.enter(|mut core, context| {
// Drain the OwnedTasks collection. This call also closes the
// collection, ensuring that no tasks are ever pushed after this
// call returns.
context.shared.owned.close_and_shutdown_all();
context.spawner.shared.owned.close_and_shutdown_all();
// Drain local queue
// We already shut down every task, so we just need to drop the task.
for task in context.tasks.borrow_mut().queue.drain(..) {
while let Some(task) = core.tasks.pop_front() {
drop(task);
}
// Drain remote queue and set it to None
let remote_queue = scheduler.spawner.shared.queue.lock().take();
let remote_queue = core.spawner.shared.queue.lock().take();
// Using `Option::take` to replace the shared queue with `None`.
// We already shut down every task, so we just need to drop the task.
@@ -387,12 +335,14 @@ impl<P: Park> Drop for BasicScheduler<P> {
}
}
assert!(context.shared.owned.is_empty());
assert!(context.spawner.shared.owned.is_empty());
(core, ())
});
}
}
impl<P: Park> fmt::Debug for BasicScheduler<P> {
impl fmt::Debug for BasicScheduler {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("BasicScheduler").finish()
}
@@ -455,8 +405,13 @@ impl Schedule for Arc<Shared> {
fn schedule(&self, task: task::Notified<Self>) {
CURRENT.with(|maybe_cx| match maybe_cx {
Some(cx) if Arc::ptr_eq(self, &cx.shared) => {
cx.tasks.borrow_mut().queue.push_back(task);
Some(cx) if Arc::ptr_eq(self, &cx.spawner.shared) => {
cx.core
.borrow_mut()
.as_mut()
.expect("core missing")
.tasks
.push_back(task);
}
_ => {
// If the queue is None, then the runtime has shut down. We
@@ -484,35 +439,107 @@ impl Wake for Shared {
}
}
// ===== InnerGuard =====
// ===== CoreGuard =====
/// Used to ensure we always place the Inner value
/// back into its slot in `BasicScheduler`, even if the
/// future panics.
struct InnerGuard<'a, P: Park> {
inner: Option<Inner<P>>,
basic_scheduler: &'a BasicScheduler<P>,
/// Used to ensure we always place the `Core` value back into its slot in
/// `BasicScheduler`, even if the future panics.
struct CoreGuard<'a> {
context: Context,
basic_scheduler: &'a BasicScheduler,
}
impl<P: Park> InnerGuard<'_, P> {
fn block_on<F: Future>(&mut self, future: F) -> F::Output {
// The only time inner gets set to `None` is if we have dropped
// already so this unwrap is safe.
self.inner.as_mut().unwrap().block_on(future)
impl CoreGuard<'_> {
fn block_on<F: Future>(self, future: F) -> F::Output {
self.enter(|mut core, context| {
let _enter = crate::runtime::enter(false);
let waker = context.spawner.waker_ref();
let mut cx = std::task::Context::from_waker(&waker);
pin!(future);
'outer: loop {
if core.spawner.reset_woken() {
let (c, res) = context.run_task(core, || future.as_mut().poll(&mut cx));
core = c;
if let Ready(v) = res {
return (core, v);
}
}
for _ in 0..MAX_TASKS_PER_TICK {
// Get and increment the current tick
let tick = core.tick;
core.tick = core.tick.wrapping_add(1);
let entry = if tick % REMOTE_FIRST_INTERVAL == 0 {
core.spawner
.pop()
.or_else(|| core.tasks.pop_front().map(RemoteMsg::Schedule))
} else {
core.tasks
.pop_front()
.map(RemoteMsg::Schedule)
.or_else(|| core.spawner.pop())
};
let entry = match entry {
Some(entry) => entry,
None => {
core = context.park(core);
// Try polling the `block_on` future next
continue 'outer;
}
};
match entry {
RemoteMsg::Schedule(task) => {
let task = context.spawner.shared.owned.assert_owner(task);
let (c, _) = context.run_task(core, || {
task.run();
});
core = c;
}
}
}
// Yield to the driver, this drives the timer and pulls any
// pending I/O events.
core = context.park_yield(core);
}
})
}
/// Enters the scheduler context. This sets the queue and other necessary
/// scheduler state in the thread-local.
fn enter<F, R>(self, f: F) -> R
where
F: FnOnce(Box<Core>, &Context) -> (Box<Core>, R),
{
// Remove `core` from `context` to pass into the closure.
let core = self.context.core.borrow_mut().take().expect("core missing");
// Call the closure and place `core` back
let (core, ret) = CURRENT.set(&self.context, || f(core, &self.context));
*self.context.core.borrow_mut() = Some(core);
ret
}
}
impl<P: Park> Drop for InnerGuard<'_, P> {
impl Drop for CoreGuard<'_> {
fn drop(&mut self) {
if let Some(scheduler) = self.inner.take() {
let mut lock = self.basic_scheduler.inner.lock();
if let Some(core) = self.context.core.borrow_mut().take() {
// Replace old scheduler back into the state to allow
// other threads to pick it up and drive it.
lock.replace(scheduler);
self.basic_scheduler.core.set(core);
// Wake up other possible threads that could steal
// the dedicated parker P.
// Wake up other possible threads that could steal the driver.
self.basic_scheduler.notify.notify_one()
}
}
+7 -5
View File
@@ -5,6 +5,8 @@ use crate::park::Park;
use std::io;
use std::time::Duration;
use super::stats::IoDriverStats;
// ===== io driver =====
cfg_io_driver! {
@@ -12,14 +14,14 @@ cfg_io_driver! {
type IoStack = crate::park::either::Either<ProcessDriver, ParkThread>;
pub(crate) type IoHandle = Option<crate::io::driver::Handle>;
fn create_io_stack(enabled: bool) -> io::Result<(IoStack, IoHandle, SignalHandle)> {
fn create_io_stack(enabled: bool, stats: IoDriverStats) -> io::Result<(IoStack, IoHandle, SignalHandle)> {
use crate::park::either::Either;
#[cfg(loom)]
assert!(!enabled);
let ret = if enabled {
let io_driver = crate::io::driver::Driver::new()?;
let io_driver = crate::io::driver::Driver::new(stats)?;
let io_handle = io_driver.handle();
let (signal_driver, signal_handle) = create_signal_driver(io_driver)?;
@@ -38,7 +40,7 @@ cfg_not_io_driver! {
pub(crate) type IoHandle = ();
type IoStack = ParkThread;
fn create_io_stack(_enabled: bool) -> io::Result<(IoStack, IoHandle, SignalHandle)> {
fn create_io_stack(_enabled: bool, _stats: IoDriverStats) -> io::Result<(IoStack, IoHandle, SignalHandle)> {
Ok((ParkThread::new(), Default::default(), Default::default()))
}
}
@@ -166,8 +168,8 @@ pub(crate) struct Cfg {
}
impl Driver {
pub(crate) fn new(cfg: Cfg) -> io::Result<(Self, Resources)> {
let (io_stack, io_handle, signal_handle) = create_io_stack(cfg.enable_io)?;
pub(crate) fn new(cfg: Cfg, stats: IoDriverStats) -> io::Result<(Self, Resources)> {
let (io_stack, io_handle, signal_handle) = create_io_stack(cfg.enable_io, stats)?;
let clock = create_clock(cfg.enable_pause_time, cfg.start_paused);
+9 -15
View File
@@ -26,7 +26,11 @@ pub struct Handle {
/// Handles to the signal drivers
#[cfg_attr(
not(any(feature = "signal", all(unix, feature = "process"))),
any(
loom,
not(all(unix, feature = "signal")),
not(all(unix, feature = "process")),
),
allow(dead_code)
)]
pub(super) signal_handle: driver::SignalHandle,
@@ -157,7 +161,7 @@ impl Handle {
/// });
/// # }
/// ```
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
@@ -187,7 +191,7 @@ impl Handle {
/// println!("now running on a worker thread");
/// });
/// # }
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn_blocking<F, R>(&self, func: F) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
@@ -200,7 +204,7 @@ impl Handle {
}
}
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub(crate) fn spawn_blocking_inner<F, R>(&self, func: F, name: Option<&str>) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
@@ -211,9 +215,7 @@ impl Handle {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let fut = {
use tracing::Instrument;
#[cfg(tokio_track_caller)]
let location = std::panic::Location::caller();
#[cfg(tokio_track_caller)]
let span = tracing::trace_span!(
target: "tokio::task::blocking",
"runtime.spawn",
@@ -222,14 +224,6 @@ impl Handle {
"fn" = %std::any::type_name::<F>(),
spawn.location = %format_args!("{}:{}:{}", location.file(), location.line(), location.column()),
);
#[cfg(not(tokio_track_caller))]
let span = tracing::trace_span!(
target: "tokio::task::blocking",
"runtime.spawn",
kind = %"blocking",
task.name = %name.unwrap_or_default(),
"fn" = %std::any::type_name::<F>(),
);
fut.instrument(span)
};
@@ -311,7 +305,7 @@ impl Handle {
/// [`tokio::fs`]: crate::fs
/// [`tokio::net`]: crate::net
/// [`tokio::time`]: crate::time
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn block_on<F: Future>(&self, future: F) -> F::Output {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let future = crate::util::trace::task(future, "block_on", None);
+5 -5
View File
@@ -283,7 +283,7 @@ cfg_rt! {
#[derive(Debug)]
enum Kind {
/// Execute all tasks on the current-thread.
CurrentThread(BasicScheduler<driver::Driver>),
CurrentThread(BasicScheduler),
/// Execute tasks across multiple threads.
#[cfg(feature = "rt-multi-thread")]
@@ -375,7 +375,7 @@ cfg_rt! {
/// });
/// # }
/// ```
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
@@ -400,7 +400,7 @@ cfg_rt! {
/// println!("now running on a worker thread");
/// });
/// # }
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn_blocking<F, R>(&self, func: F) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
@@ -450,7 +450,7 @@ cfg_rt! {
/// ```
///
/// [handle]: fn@Handle::block_on
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn block_on<F: Future>(&self, future: F) -> F::Output {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let future = crate::util::trace::task(future, "block_on", None);
@@ -582,7 +582,7 @@ cfg_rt! {
match self::context::try_enter(self.handle.clone()) {
Some(guard) => basic.set_context_guard(guard),
None => {
// The context thread-local has alread been destroyed.
// The context thread-local has already been destroyed.
//
// We don't set the guard in this case. Calls to tokio::spawn in task
// destructors would fail regardless if this happens.
+7 -1
View File
@@ -1,12 +1,18 @@
//! This module contains information need to view information about how the
//! runtime is performing.
//!
//! **Note**: This is an [unstable API][unstable]. The public API of types in
//! this module may break in 1.x releases. See [the documentation on unstable
//! features][unstable] for details.
//!
//! [unstable]: crate#unstable-features
#![allow(clippy::module_inception)]
cfg_stats! {
mod stats;
pub use self::stats::{RuntimeStats, WorkerStats};
pub(crate) use self::stats::WorkerStatsBatcher;
pub(crate) use self::stats::{WorkerStatsBatcher, IoDriverStats};
}
cfg_not_stats! {
+51
View File
@@ -2,15 +2,29 @@
use crate::loom::sync::atomic::{AtomicU64, Ordering::Relaxed};
use std::convert::TryFrom;
use std::sync::Arc;
use std::time::{Duration, Instant};
/// This type contains methods to retrieve stats from a Tokio runtime.
///
/// **Note**: This is an [unstable API][unstable]. The public API of this type
/// may break in 1.x releases. See [the documentation on unstable
/// features][unstable] for details.
///
/// [unstable]: crate#unstable-features
#[derive(Debug)]
pub struct RuntimeStats {
workers: Box<[WorkerStats]>,
driver: IoDriverStats,
}
/// This type contains methods to retrieve stats from a worker thread on a Tokio runtime.
///
/// **Note**: This is an [unstable API][unstable]. The public API of this type
/// may break in 1.x releases. See [the documentation on unstable
/// features][unstable] for details.
///
/// [unstable]: crate#unstable-features
#[derive(Debug)]
#[repr(align(128))]
pub struct WorkerStats {
@@ -34,6 +48,7 @@ impl RuntimeStats {
Self {
workers: workers.into_boxed_slice(),
driver: IoDriverStats::default(),
}
}
@@ -120,3 +135,39 @@ impl WorkerStatsBatcher {
self.poll_count += 1;
}
}
#[derive(Debug, Default, Clone)]
pub(crate) struct IoDriverStats {
inner: Arc<IoDriverStatsInner>,
}
#[derive(Debug, Default)]
#[repr(align(128))]
struct IoDriverStatsInner {
read_ready_count: AtomicU64,
write_ready_count: AtomicU64,
fd_count: AtomicU64,
compact_count: AtomicU64,
}
impl IoDriverStats {
pub(crate) fn incr_read_ready_count(&self) {
self.inner.read_ready_count.fetch_add(1, Relaxed);
}
pub(crate) fn incr_write_ready_count(&self) {
self.inner.write_ready_count.fetch_add(1, Relaxed);
}
pub(crate) fn incr_fd_count(&self) {
self.inner.fd_count.fetch_add(1, Relaxed);
}
pub(crate) fn dec_fd_count(&self) {
self.inner.fd_count.fetch_sub(1, Relaxed);
}
pub(crate) fn incr_compact_count(&self) {
self.inner.compact_count.fetch_add(1, Relaxed);
}
}
+1 -1
View File
@@ -25,7 +25,7 @@
//!
//! The task uses a reference count to keep track of how many active references
//! exist. The Unowned reference type takes up two ref-counts. All other
//! reference types take pu a single ref-count.
//! reference types take up a single ref-count.
//!
//! Besides the waker type, each task has at most one of each reference type.
//!
@@ -34,20 +34,22 @@ fn assert_at_most_num_polls(rt: Arc<Runtime>, at_most_polls: usize) {
#[test]
fn block_on_num_polls() {
loom::model(|| {
// we expect at most 3 number of polls because there are
// three points at which we poll the future. At any of these
// points it can be ready:
// we expect at most 4 number of polls because there are three points at
// which we poll the future and an opportunity for a false-positive.. At
// any of these points it can be ready:
//
// - when we fail to steal the parker and we block on a
// notification that it is available.
// - when we fail to steal the parker and we block on a notification
// that it is available.
//
// - when we steal the parker and we schedule the future
//
// - when the future is woken up and we have ran the max
// number of tasks for the current tick or there are no
// more tasks to run.
// - when the future is woken up and we have ran the max number of tasks
// for the current tick or there are no more tasks to run.
//
let at_most = 3;
// - a thread is notified that the parker is available but a third
// thread acquires it before the notified thread can.
//
let at_most = 4;
let rt1 = Arc::new(Builder::new_current_thread().build().unwrap());
let rt2 = rt1.clone();
-3
View File
@@ -1,8 +1,5 @@
//! Threadpool
mod atomic_cell;
use atomic_cell::AtomicCell;
mod idle;
use self::idle::Idle;
+2 -1
View File
@@ -66,8 +66,9 @@ use crate::runtime::enter::EnterContext;
use crate::runtime::park::{Parker, Unparker};
use crate::runtime::stats::{RuntimeStats, WorkerStatsBatcher};
use crate::runtime::task::{Inject, JoinHandle, OwnedTasks};
use crate::runtime::thread_pool::{AtomicCell, Idle};
use crate::runtime::thread_pool::Idle;
use crate::runtime::{queue, task, Callback};
use crate::util::atomic_cell::AtomicCell;
use crate::util::FastRand;
use std::cell::RefCell;
+63
View File
@@ -1,5 +1,7 @@
use crate::loom::sync::Mutex;
use crate::sync::watch;
#[cfg(all(tokio_unstable, feature = "tracing"))]
use crate::util::trace;
/// A barrier enables multiple tasks to synchronize the beginning of some computation.
///
@@ -41,6 +43,8 @@ pub struct Barrier {
state: Mutex<BarrierState>,
wait: watch::Receiver<usize>,
n: usize,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
}
#[derive(Debug)]
@@ -55,6 +59,7 @@ impl Barrier {
///
/// A barrier will block `n`-1 tasks which call [`Barrier::wait`] and then wake up all
/// tasks at once when the `n`th task calls `wait`.
#[track_caller]
pub fn new(mut n: usize) -> Barrier {
let (waker, wait) = crate::sync::watch::channel(0);
@@ -65,6 +70,32 @@ impl Barrier {
n = 1;
}
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = {
let location = std::panic::Location::caller();
let resource_span = tracing::trace_span!(
"runtime.resource",
concrete_type = "Barrier",
kind = "Sync",
loc.file = location.file(),
loc.line = location.line(),
loc.col = location.column(),
);
resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
size = n,
);
tracing::trace!(
target: "runtime::resource::state_update",
arrived = 0,
)
});
resource_span
};
Barrier {
state: Mutex::new(BarrierState {
waker,
@@ -73,6 +104,8 @@ impl Barrier {
}),
n,
wait,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: resource_span,
}
}
@@ -85,6 +118,20 @@ impl Barrier {
/// [`BarrierWaitResult::is_leader`] when returning from this function, and all other tasks
/// will receive a result that will return `false` from `is_leader`.
pub async fn wait(&self) -> BarrierWaitResult {
#[cfg(all(tokio_unstable, feature = "tracing"))]
return trace::async_op(
|| self.wait_internal(),
self.resource_span.clone(),
"Barrier::wait",
"poll",
false,
)
.await;
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
return self.wait_internal().await;
}
async fn wait_internal(&self) -> BarrierWaitResult {
// NOTE: we are taking a _synchronous_ lock here.
// It is okay to do so because the critical section is fast and never yields, so it cannot
// deadlock even if another future is concurrently holding the lock.
@@ -96,7 +143,23 @@ impl Barrier {
let mut state = self.state.lock();
let generation = state.generation;
state.arrived += 1;
#[cfg(all(tokio_unstable, feature = "tracing"))]
tracing::trace!(
target: "runtime::resource::state_update",
arrived = 1,
arrived.op = "add",
);
#[cfg(all(tokio_unstable, feature = "tracing"))]
tracing::trace!(
target: "runtime::resource::async_op::state_update",
arrived = true,
);
if state.arrived == self.n {
#[cfg(all(tokio_unstable, feature = "tracing"))]
tracing::trace!(
target: "runtime::resource::async_op::state_update",
is_leader = true,
);
// we are the leader for this generation
// wake everyone, increment the generation, and return
state
+144 -8
View File
@@ -19,6 +19,8 @@ use crate::loom::cell::UnsafeCell;
use crate::loom::sync::atomic::AtomicUsize;
use crate::loom::sync::{Mutex, MutexGuard};
use crate::util::linked_list::{self, LinkedList};
#[cfg(all(tokio_unstable, feature = "tracing"))]
use crate::util::trace;
use crate::util::WakeList;
use std::future::Future;
@@ -35,6 +37,8 @@ pub(crate) struct Semaphore {
waiters: Mutex<Waitlist>,
/// The current number of available permits in the semaphore.
permits: AtomicUsize,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
}
struct Waitlist {
@@ -101,6 +105,9 @@ struct Waiter {
/// use `UnsafeCell` internally.
pointers: linked_list::Pointers<Waiter>,
#[cfg(all(tokio_unstable, feature = "tracing"))]
ctx: trace::AsyncOpTracingCtx,
/// Should not be `Unpin`.
_p: PhantomPinned,
}
@@ -129,12 +136,34 @@ impl Semaphore {
"a semaphore may not have more than MAX_PERMITS permits ({})",
Self::MAX_PERMITS
);
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = {
let resource_span = tracing::trace_span!(
"runtime.resource",
concrete_type = "Semaphore",
kind = "Sync",
is_internal = true
);
resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
permits = permits,
permits.op = "override",
)
});
resource_span
};
Self {
permits: AtomicUsize::new(permits << Self::PERMIT_SHIFT),
waiters: Mutex::new(Waitlist {
queue: LinkedList::new(),
closed: false,
}),
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -156,6 +185,8 @@ impl Semaphore {
queue: LinkedList::new(),
closed: false,
}),
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span::none(),
}
}
@@ -224,7 +255,10 @@ impl Semaphore {
let next = curr - num_permits;
match self.permits.compare_exchange(curr, next, AcqRel, Acquire) {
Ok(_) => return Ok(()),
Ok(_) => {
// TODO: Instrument once issue has been solved}
return Ok(());
}
Err(actual) => curr = actual,
}
}
@@ -283,6 +317,17 @@ impl Semaphore {
rem,
Self::MAX_PERMITS
);
// add remaining permits back
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
permits = rem,
permits.op = "add",
)
});
rem = 0;
}
@@ -347,6 +392,20 @@ impl Semaphore {
acquired += acq;
if remaining == 0 {
if !queued {
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
permits = acquired,
permits.op = "sub",
);
tracing::trace!(
target: "runtime::resource::async_op::state_update",
permits_obtained = acquired,
permits.op = "add",
)
});
return Ready(Ok(()));
} else if lock.is_none() {
break self.waiters.lock();
@@ -362,6 +421,15 @@ impl Semaphore {
return Ready(Err(AcquireError::closed()));
}
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
permits = acquired,
permits.op = "sub",
)
});
if node.assign_permits(&mut acquired) {
self.add_permits_locked(acquired, waiters);
return Ready(Ok(()));
@@ -406,11 +474,16 @@ impl fmt::Debug for Semaphore {
}
impl Waiter {
fn new(num_permits: u32) -> Self {
fn new(
num_permits: u32,
#[cfg(all(tokio_unstable, feature = "tracing"))] ctx: trace::AsyncOpTracingCtx,
) -> Self {
Waiter {
waker: UnsafeCell::new(None),
state: AtomicUsize::new(num_permits as usize),
pointers: linked_list::Pointers::new(),
#[cfg(all(tokio_unstable, feature = "tracing"))]
ctx,
_p: PhantomPinned,
}
}
@@ -426,6 +499,14 @@ impl Waiter {
match self.state.compare_exchange(curr, next, AcqRel, Acquire) {
Ok(_) => {
*n -= assign;
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.ctx.async_op_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::async_op::state_update",
permits_obtained = assign,
permits.op = "add",
);
});
return next == 0;
}
Err(actual) => curr = actual,
@@ -438,12 +519,26 @@ impl Future for Acquire<'_> {
type Output = Result<(), AcquireError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// First, ensure the current task has enough budget to proceed.
let coop = ready!(crate::coop::poll_proceed(cx));
#[cfg(all(tokio_unstable, feature = "tracing"))]
let _resource_span = self.node.ctx.resource_span.clone().entered();
#[cfg(all(tokio_unstable, feature = "tracing"))]
let _async_op_span = self.node.ctx.async_op_span.clone().entered();
#[cfg(all(tokio_unstable, feature = "tracing"))]
let _async_op_poll_span = self.node.ctx.async_op_poll_span.clone().entered();
let (node, semaphore, needed, queued) = self.project();
match semaphore.poll_acquire(cx, needed, node, *queued) {
// First, ensure the current task has enough budget to proceed.
#[cfg(all(tokio_unstable, feature = "tracing"))]
let coop = ready!(trace_poll_op!(
"poll_acquire",
crate::coop::poll_proceed(cx),
));
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let coop = ready!(crate::coop::poll_proceed(cx));
let result = match semaphore.poll_acquire(cx, needed, node, *queued) {
Pending => {
*queued = true;
Pending
@@ -454,18 +549,59 @@ impl Future for Acquire<'_> {
*queued = false;
Ready(Ok(()))
}
}
};
#[cfg(all(tokio_unstable, feature = "tracing"))]
return trace_poll_op!("poll_acquire", result);
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
return result;
}
}
impl<'a> Acquire<'a> {
fn new(semaphore: &'a Semaphore, num_permits: u32) -> Self {
Self {
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
return Self {
node: Waiter::new(num_permits),
semaphore,
num_permits,
queued: false,
}
};
#[cfg(all(tokio_unstable, feature = "tracing"))]
return semaphore.resource_span.in_scope(|| {
let async_op_span =
tracing::trace_span!("runtime.resource.async_op", source = "Acquire::new");
let async_op_poll_span = async_op_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::async_op::state_update",
permits_requested = num_permits,
permits.op = "override",
);
tracing::trace!(
target: "runtime::resource::async_op::state_update",
permits_obtained = 0 as usize,
permits.op = "override",
);
tracing::trace_span!("runtime.resource.async_op.poll")
});
let ctx = trace::AsyncOpTracingCtx {
async_op_span,
async_op_poll_span,
resource_span: semaphore.resource_span.clone(),
};
Self {
node: Waiter::new(num_permits, ctx),
semaphore,
num_permits,
queued: false,
}
});
}
fn project(self: Pin<&mut Self>) -> (Pin<&mut Waiter>, &Semaphore, u32, &mut bool) {
+5
View File
@@ -563,6 +563,11 @@ impl<T> Sender<T> {
/// [`close`]: Receiver::close
/// [`Receiver`]: Receiver
///
/// # Panics
///
/// This function panics if it is called outside the context of a Tokio
/// runtime [with time enabled](crate::runtime::Builder::enable_time).
///
/// # Examples
///
/// In the following example, each call to `send_timeout` will block until the
+2 -2
View File
@@ -19,7 +19,7 @@ impl<T: fmt::Debug> std::error::Error for SendError<T> {}
/// This enumeration is the list of the possible error outcomes for the
/// [try_send](super::Sender::try_send) method.
#[derive(Debug)]
#[derive(Debug, Eq, PartialEq)]
pub enum TrySendError<T> {
/// The data could not be sent on the channel because the channel is
/// currently full and sending would require blocking.
@@ -96,7 +96,7 @@ impl Error for RecvError {}
cfg_time! {
// ===== SendTimeoutError =====
#[derive(Debug)]
#[derive(Debug, Eq, PartialEq)]
/// Error returned by [`Sender::send_timeout`](super::Sender::send_timeout)].
pub enum SendTimeoutError<T> {
/// The data could not be sent on the channel because the channel is
+17
View File
@@ -58,6 +58,22 @@
//! [crossbeam][crossbeam-unbounded]. Similarly, for sending a message _from sync
//! to async_, you should use an unbounded Tokio `mpsc` channel.
//!
//! Please be aware that the above remarks were written with the `mpsc` channel
//! in mind, but they can also be generalized to other kinds of channels. In
//! general, any channel method that isn't marked async can be called anywhere,
//! including outside of the runtime. For example, sending a message on a
//! oneshot channel from outside the runtime is perfectly fine.
//!
//! # Multiple runtimes
//!
//! The mpsc channel does not care about which runtime you use it in, and can be
//! used to send messages from one runtime to another. It can also be used in
//! non-Tokio runtimes.
//!
//! There is one exception to the above: the [`send_timeout`] must be used from
//! within a Tokio runtime, however it is still not tied to one specific Tokio
//! runtime, and the sender may be moved from one Tokio runtime to another.
//!
//! [`Sender`]: crate::sync::mpsc::Sender
//! [`Receiver`]: crate::sync::mpsc::Receiver
//! [bounded-send]: crate::sync::mpsc::Sender::send()
@@ -69,6 +85,7 @@
//! [`Handle::block_on`]: crate::runtime::Handle::block_on()
//! [std-unbounded]: std::sync::mpsc::channel
//! [crossbeam-unbounded]: https://docs.rs/crossbeam/*/crossbeam/channel/fn.unbounded.html
//! [`send_timeout`]: crate::sync::mpsc::Sender::send_timeout
pub(super) mod block;
+141 -6
View File
@@ -1,6 +1,8 @@
#![cfg_attr(not(feature = "sync"), allow(unreachable_pub, dead_code))]
use crate::sync::batch_semaphore as semaphore;
#[cfg(all(tokio_unstable, feature = "tracing"))]
use crate::util::trace;
use std::cell::UnsafeCell;
use std::error::Error;
@@ -124,6 +126,8 @@ use std::{fmt, marker, mem};
/// [`Send`]: trait@std::marker::Send
/// [`lock`]: method@Mutex::lock
pub struct Mutex<T: ?Sized> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
s: semaphore::Semaphore,
c: UnsafeCell<T>,
}
@@ -138,6 +142,8 @@ pub struct Mutex<T: ?Sized> {
/// The lock is automatically released whenever the guard is dropped, at which
/// point `lock` will succeed yet again.
pub struct MutexGuard<'a, T: ?Sized> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
lock: &'a Mutex<T>,
}
@@ -157,6 +163,8 @@ pub struct MutexGuard<'a, T: ?Sized> {
///
/// [`Arc`]: std::sync::Arc
pub struct OwnedMutexGuard<T: ?Sized> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
lock: Arc<Mutex<T>>,
}
@@ -242,13 +250,42 @@ impl<T: ?Sized> Mutex<T> {
///
/// let lock = Mutex::new(5);
/// ```
#[track_caller]
pub fn new(t: T) -> Self
where
T: Sized,
{
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = {
let location = std::panic::Location::caller();
tracing::trace_span!(
"runtime.resource",
concrete_type = "Mutex",
kind = "Sync",
loc.file = location.file(),
loc.line = location.line(),
loc.col = location.column(),
)
};
#[cfg(all(tokio_unstable, feature = "tracing"))]
let s = resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
locked = false,
);
semaphore::Semaphore::new(1)
});
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
let s = semaphore::Semaphore::new(1);
Self {
c: UnsafeCell::new(t),
s: semaphore::Semaphore::new(1),
s,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -270,6 +307,8 @@ impl<T: ?Sized> Mutex<T> {
Self {
c: UnsafeCell::new(t),
s: semaphore::Semaphore::const_new(1),
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span::none(),
}
}
@@ -297,8 +336,32 @@ impl<T: ?Sized> Mutex<T> {
/// }
/// ```
pub async fn lock(&self) -> MutexGuard<'_, T> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
trace::async_op(
|| self.acquire(),
self.resource_span.clone(),
"Mutex::lock",
"poll",
false,
)
.await;
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
locked = true,
);
});
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
self.acquire().await;
MutexGuard { lock: self }
MutexGuard {
lock: self,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: self.resource_span.clone(),
}
}
/// Blocking lock this mutex. When the lock has been acquired, function returns a
@@ -368,8 +431,35 @@ impl<T: ?Sized> Mutex<T> {
///
/// [`Arc`]: std::sync::Arc
pub async fn lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
trace::async_op(
|| self.acquire(),
self.resource_span.clone(),
"Mutex::lock_owned",
"poll",
false,
)
.await;
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
locked = true,
);
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = self.resource_span.clone();
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
self.acquire().await;
OwnedMutexGuard { lock: self }
OwnedMutexGuard {
lock: self,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
async fn acquire(&self) {
@@ -399,7 +489,21 @@ impl<T: ?Sized> Mutex<T> {
/// ```
pub fn try_lock(&self) -> Result<MutexGuard<'_, T>, TryLockError> {
match self.s.try_acquire(1) {
Ok(_) => Ok(MutexGuard { lock: self }),
Ok(_) => {
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
locked = true,
);
});
Ok(MutexGuard {
lock: self,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: self.resource_span.clone(),
})
}
Err(_) => Err(TryLockError(())),
}
}
@@ -454,7 +558,24 @@ impl<T: ?Sized> Mutex<T> {
/// # }
pub fn try_lock_owned(self: Arc<Self>) -> Result<OwnedMutexGuard<T>, TryLockError> {
match self.s.try_acquire(1) {
Ok(_) => Ok(OwnedMutexGuard { lock: self }),
Ok(_) => {
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
locked = true,
);
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = self.resource_span.clone();
Ok(OwnedMutexGuard {
lock: self,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
})
}
Err(_) => Err(TryLockError(())),
}
}
@@ -637,7 +758,14 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
fn drop(&mut self) {
self.lock.s.release(1)
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
locked = false,
);
});
self.lock.s.release(1);
}
}
@@ -699,6 +827,13 @@ impl<T: ?Sized> OwnedMutexGuard<T> {
impl<T: ?Sized> Drop for OwnedMutexGuard<T> {
fn drop(&mut self) {
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
locked = false,
);
});
self.lock.s.release(1)
}
}
+154 -4
View File
@@ -9,6 +9,9 @@
//!
//! Each handle can be used on separate tasks.
//!
//! Since the `send` method is not async, it can be used anywhere. This includes
//! sending between two runtimes, and using it from non-async code.
//!
//! # Examples
//!
//! ```
@@ -119,6 +122,8 @@
use crate::loom::cell::UnsafeCell;
use crate::loom::sync::atomic::AtomicUsize;
use crate::loom::sync::Arc;
#[cfg(all(tokio_unstable, feature = "tracing"))]
use crate::util::trace;
use std::fmt;
use std::future::Future;
@@ -212,6 +217,8 @@ use std::task::{Context, Poll, Waker};
#[derive(Debug)]
pub struct Sender<T> {
inner: Option<Arc<Inner<T>>>,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
}
/// Receives a value from the associated [`Sender`].
@@ -302,6 +309,12 @@ pub struct Sender<T> {
#[derive(Debug)]
pub struct Receiver<T> {
inner: Option<Arc<Inner<T>>>,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
#[cfg(all(tokio_unstable, feature = "tracing"))]
async_op_span: tracing::Span,
#[cfg(all(tokio_unstable, feature = "tracing"))]
async_op_poll_span: tracing::Span,
}
pub mod error {
@@ -439,7 +452,56 @@ struct State(usize);
/// }
/// }
/// ```
#[track_caller]
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = {
let location = std::panic::Location::caller();
let resource_span = tracing::trace_span!(
"runtime.resource",
concrete_type = "Sender|Receiver",
kind = "Sync",
loc.file = location.file(),
loc.line = location.line(),
loc.col = location.column(),
);
resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
tx_dropped = false,
tx_dropped.op = "override",
)
});
resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
rx_dropped = false,
rx_dropped.op = "override",
)
});
resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
value_sent = false,
value_sent.op = "override",
)
});
resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
value_received = false,
value_received.op = "override",
)
});
resource_span
};
let inner = Arc::new(Inner {
state: AtomicUsize::new(State::new().as_usize()),
value: UnsafeCell::new(None),
@@ -449,8 +511,27 @@ pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
let tx = Sender {
inner: Some(inner.clone()),
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: resource_span.clone(),
};
#[cfg(all(tokio_unstable, feature = "tracing"))]
let async_op_span = resource_span
.in_scope(|| tracing::trace_span!("runtime.resource.async_op", source = "Receiver::await"));
#[cfg(all(tokio_unstable, feature = "tracing"))]
let async_op_poll_span =
async_op_span.in_scope(|| tracing::trace_span!("runtime.resource.async_op.poll"));
let rx = Receiver {
inner: Some(inner),
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: resource_span,
#[cfg(all(tokio_unstable, feature = "tracing"))]
async_op_span,
#[cfg(all(tokio_unstable, feature = "tracing"))]
async_op_poll_span,
};
let rx = Receiver { inner: Some(inner) };
(tx, rx)
}
@@ -522,6 +603,15 @@ impl<T> Sender<T> {
}
}
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
value_sent = true,
value_sent.op = "override",
)
});
Ok(())
}
@@ -595,7 +685,20 @@ impl<T> Sender<T> {
pub async fn closed(&mut self) {
use crate::future::poll_fn;
poll_fn(|cx| self.poll_closed(cx)).await
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = self.resource_span.clone();
#[cfg(all(tokio_unstable, feature = "tracing"))]
let closed = trace::async_op(
|| poll_fn(|cx| self.poll_closed(cx)),
resource_span,
"Sender::closed",
"poll_closed",
false,
);
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let closed = poll_fn(|cx| self.poll_closed(cx));
closed.await
}
/// Returns `true` if the associated [`Receiver`] handle has been dropped.
@@ -725,6 +828,14 @@ impl<T> Drop for Sender<T> {
fn drop(&mut self) {
if let Some(inner) = self.inner.as_ref() {
inner.complete();
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
tx_dropped = true,
tx_dropped.op = "override",
)
});
}
}
}
@@ -792,6 +903,14 @@ impl<T> Receiver<T> {
pub fn close(&mut self) {
if let Some(inner) = self.inner.as_ref() {
inner.close();
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
rx_dropped = true,
rx_dropped.op = "override",
)
});
}
}
@@ -869,7 +988,17 @@ impl<T> Receiver<T> {
// `UnsafeCell`. Therefore, it is now safe for us to access the
// cell.
match unsafe { inner.consume_value() } {
Some(value) => Ok(value),
Some(value) => {
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
value_received = true,
value_received.op = "override",
)
});
Ok(value)
}
None => Err(TryRecvError::Closed),
}
} else if state.is_closed() {
@@ -891,6 +1020,14 @@ impl<T> Drop for Receiver<T> {
fn drop(&mut self) {
if let Some(inner) = self.inner.as_ref() {
inner.close();
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
rx_dropped = true,
rx_dropped.op = "override",
)
});
}
}
}
@@ -900,8 +1037,21 @@ impl<T> Future for Receiver<T> {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// If `inner` is `None`, then `poll()` has already completed.
#[cfg(all(tokio_unstable, feature = "tracing"))]
let _res_span = self.resource_span.clone().entered();
#[cfg(all(tokio_unstable, feature = "tracing"))]
let _ao_span = self.async_op_span.clone().entered();
#[cfg(all(tokio_unstable, feature = "tracing"))]
let _ao_poll_span = self.async_op_poll_span.clone().entered();
let ret = if let Some(inner) = self.as_ref().get_ref().inner.as_ref() {
ready!(inner.poll_recv(cx))?
#[cfg(all(tokio_unstable, feature = "tracing"))]
let res = ready!(trace_poll_op!("poll_recv", inner.poll_recv(cx)))?;
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
let res = ready!(inner.poll_recv(cx))?;
res
} else {
panic!("called after complete");
};
+251 -6
View File
@@ -1,5 +1,7 @@
use crate::sync::batch_semaphore::{Semaphore, TryAcquireError};
use crate::sync::mutex::TryLockError;
#[cfg(all(tokio_unstable, feature = "tracing"))]
use crate::util::trace;
use std::cell::UnsafeCell;
use std::marker;
use std::marker::PhantomData;
@@ -86,6 +88,9 @@ const MAX_READS: u32 = 10;
/// [_write-preferring_]: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock#Priority_policies
#[derive(Debug)]
pub struct RwLock<T: ?Sized> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
// maximum number of concurrent readers
mr: u32,
@@ -197,14 +202,55 @@ impl<T: ?Sized> RwLock<T> {
///
/// let lock = RwLock::new(5);
/// ```
#[track_caller]
pub fn new(value: T) -> RwLock<T>
where
T: Sized,
{
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = {
let location = std::panic::Location::caller();
let resource_span = tracing::trace_span!(
"runtime.resource",
concrete_type = "RwLock",
kind = "Sync",
loc.file = location.file(),
loc.line = location.line(),
loc.col = location.column(),
);
resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
max_readers = MAX_READS,
);
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = false,
);
tracing::trace!(
target: "runtime::resource::state_update",
current_readers = 0,
);
});
resource_span
};
#[cfg(all(tokio_unstable, feature = "tracing"))]
let s = resource_span.in_scope(|| Semaphore::new(MAX_READS as usize));
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
let s = Semaphore::new(MAX_READS as usize);
RwLock {
mr: MAX_READS,
c: UnsafeCell::new(value),
s: Semaphore::new(MAX_READS as usize),
s,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -222,6 +268,7 @@ impl<T: ?Sized> RwLock<T> {
/// # Panics
///
/// Panics if `max_reads` is more than `u32::MAX >> 3`.
#[track_caller]
pub fn with_max_readers(value: T, max_reads: u32) -> RwLock<T>
where
T: Sized,
@@ -231,10 +278,52 @@ impl<T: ?Sized> RwLock<T> {
"a RwLock may not be created with more than {} readers",
MAX_READS
);
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = {
let location = std::panic::Location::caller();
let resource_span = tracing::trace_span!(
"runtime.resource",
concrete_type = "RwLock",
kind = "Sync",
loc.file = location.file(),
loc.line = location.line(),
loc.col = location.column(),
);
resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
max_readers = max_reads,
);
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = false,
);
tracing::trace!(
target: "runtime::resource::state_update",
current_readers = 0,
);
});
resource_span
};
#[cfg(all(tokio_unstable, feature = "tracing"))]
let s = resource_span.in_scope(|| Semaphore::new(max_reads as usize));
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
let s = Semaphore::new(max_reads as usize);
RwLock {
mr: max_reads,
c: UnsafeCell::new(value),
s: Semaphore::new(max_reads as usize),
s,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -257,6 +346,8 @@ impl<T: ?Sized> RwLock<T> {
mr: MAX_READS,
c: UnsafeCell::new(value),
s: Semaphore::const_new(MAX_READS as usize),
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span::none(),
}
}
@@ -281,6 +372,8 @@ impl<T: ?Sized> RwLock<T> {
mr: max_reads,
c: UnsafeCell::new(value),
s: Semaphore::const_new(max_reads as usize),
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span::none(),
}
}
@@ -330,15 +423,39 @@ impl<T: ?Sized> RwLock<T> {
///}
/// ```
pub async fn read(&self) -> RwLockReadGuard<'_, T> {
self.s.acquire(1).await.unwrap_or_else(|_| {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let inner = trace::async_op(
|| self.s.acquire(1),
self.resource_span.clone(),
"RwLock::read",
"poll",
false,
);
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let inner = self.s.acquire(1);
inner.await.unwrap_or_else(|_| {
// The semaphore was closed. but, we never explicitly close it, and we have a
// handle to it through the Arc, which means that this can never happen.
unreachable!()
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
current_readers = 1,
current_readers.op = "add",
)
});
RwLockReadGuard {
s: &self.s,
data: self.c.get(),
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: self.resource_span.clone(),
}
}
@@ -394,15 +511,42 @@ impl<T: ?Sized> RwLock<T> {
///}
/// ```
pub async fn read_owned(self: Arc<Self>) -> OwnedRwLockReadGuard<T> {
self.s.acquire(1).await.unwrap_or_else(|_| {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let inner = trace::async_op(
|| self.s.acquire(1),
self.resource_span.clone(),
"RwLock::read_owned",
"poll",
false,
);
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let inner = self.s.acquire(1);
inner.await.unwrap_or_else(|_| {
// The semaphore was closed. but, we never explicitly close it, and we have a
// handle to it through the Arc, which means that this can never happen.
unreachable!()
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
current_readers = 1,
current_readers.op = "add",
)
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = self.resource_span.clone();
OwnedRwLockReadGuard {
data: self.c.get(),
lock: ManuallyDrop::new(self),
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -445,10 +589,21 @@ impl<T: ?Sized> RwLock<T> {
Err(TryAcquireError::Closed) => unreachable!(),
}
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
current_readers = 1,
current_readers.op = "add",
)
});
Ok(RwLockReadGuard {
s: &self.s,
data: self.c.get(),
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: self.resource_span.clone(),
})
}
@@ -497,10 +652,24 @@ impl<T: ?Sized> RwLock<T> {
Err(TryAcquireError::Closed) => unreachable!(),
}
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
current_readers = 1,
current_readers.op = "add",
)
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = self.resource_span.clone();
Ok(OwnedRwLockReadGuard {
data: self.c.get(),
lock: ManuallyDrop::new(self),
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
})
}
@@ -533,16 +702,40 @@ impl<T: ?Sized> RwLock<T> {
///}
/// ```
pub async fn write(&self) -> RwLockWriteGuard<'_, T> {
self.s.acquire(self.mr).await.unwrap_or_else(|_| {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let inner = trace::async_op(
|| self.s.acquire(self.mr),
self.resource_span.clone(),
"RwLock::write",
"poll",
false,
);
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let inner = self.s.acquire(self.mr);
inner.await.unwrap_or_else(|_| {
// The semaphore was closed. but, we never explicitly close it, and we have a
// handle to it through the Arc, which means that this can never happen.
unreachable!()
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = true,
write_locked.op = "override",
)
});
RwLockWriteGuard {
permits_acquired: self.mr,
s: &self.s,
data: self.c.get(),
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: self.resource_span.clone(),
}
}
@@ -582,16 +775,43 @@ impl<T: ?Sized> RwLock<T> {
///}
/// ```
pub async fn write_owned(self: Arc<Self>) -> OwnedRwLockWriteGuard<T> {
self.s.acquire(self.mr).await.unwrap_or_else(|_| {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let inner = trace::async_op(
|| self.s.acquire(self.mr),
self.resource_span.clone(),
"RwLock::write_owned",
"poll",
false,
);
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let inner = self.s.acquire(self.mr);
inner.await.unwrap_or_else(|_| {
// The semaphore was closed. but, we never explicitly close it, and we have a
// handle to it through the Arc, which means that this can never happen.
unreachable!()
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = true,
write_locked.op = "override",
)
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = self.resource_span.clone();
OwnedRwLockWriteGuard {
permits_acquired: self.mr,
data: self.c.get(),
lock: ManuallyDrop::new(self),
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -625,11 +845,22 @@ impl<T: ?Sized> RwLock<T> {
Err(TryAcquireError::Closed) => unreachable!(),
}
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = true,
write_locked.op = "override",
)
});
Ok(RwLockWriteGuard {
permits_acquired: self.mr,
s: &self.s,
data: self.c.get(),
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: self.resource_span.clone(),
})
}
@@ -670,11 +901,25 @@ impl<T: ?Sized> RwLock<T> {
Err(TryAcquireError::Closed) => unreachable!(),
}
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = true,
write_locked.op = "override",
)
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = self.resource_span.clone();
Ok(OwnedRwLockWriteGuard {
permits_acquired: self.mr,
data: self.c.get(),
lock: ManuallyDrop::new(self),
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
})
}
+21
View File
@@ -15,6 +15,8 @@ use std::sync::Arc;
/// [`read_owned`]: method@crate::sync::RwLock::read_owned
/// [`RwLock`]: struct@crate::sync::RwLock
pub struct OwnedRwLockReadGuard<T: ?Sized, U: ?Sized = T> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
pub(super) resource_span: tracing::Span,
// ManuallyDrop allows us to destructure into this field without running the destructor.
pub(super) lock: ManuallyDrop<Arc<RwLock<T>>>,
pub(super) data: *const U,
@@ -56,12 +58,17 @@ impl<T: ?Sized, U: ?Sized> OwnedRwLockReadGuard<T, U> {
{
let data = f(&*this) as *const V;
let lock = unsafe { ManuallyDrop::take(&mut this.lock) };
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
OwnedRwLockReadGuard {
lock: ManuallyDrop::new(lock),
data,
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -105,12 +112,17 @@ impl<T: ?Sized, U: ?Sized> OwnedRwLockReadGuard<T, U> {
None => return Err(this),
};
let lock = unsafe { ManuallyDrop::take(&mut this.lock) };
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
Ok(OwnedRwLockReadGuard {
lock: ManuallyDrop::new(lock),
data,
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
})
}
}
@@ -145,5 +157,14 @@ impl<T: ?Sized, U: ?Sized> Drop for OwnedRwLockReadGuard<T, U> {
fn drop(&mut self) {
self.lock.s.release(1);
unsafe { ManuallyDrop::drop(&mut self.lock) };
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
current_readers = 1,
current_readers.op = "sub",
)
});
}
}
+46 -1
View File
@@ -16,6 +16,8 @@ use std::sync::Arc;
/// [`write_owned`]: method@crate::sync::RwLock::write_owned
/// [`RwLock`]: struct@crate::sync::RwLock
pub struct OwnedRwLockWriteGuard<T: ?Sized> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
pub(super) resource_span: tracing::Span,
pub(super) permits_acquired: u32,
// ManuallyDrop allows us to destructure into this field without running the destructor.
pub(super) lock: ManuallyDrop<Arc<RwLock<T>>>,
@@ -64,13 +66,18 @@ impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
let data = f(&mut *this) as *mut U;
let lock = unsafe { ManuallyDrop::take(&mut this.lock) };
let permits_acquired = this.permits_acquired;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
OwnedRwLockMappedWriteGuard {
permits_acquired,
lock: ManuallyDrop::new(lock),
data,
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -123,13 +130,19 @@ impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
};
let permits_acquired = this.permits_acquired;
let lock = unsafe { ManuallyDrop::take(&mut this.lock) };
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
Ok(OwnedRwLockMappedWriteGuard {
permits_acquired,
lock: ManuallyDrop::new(lock),
data,
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
})
}
@@ -181,15 +194,39 @@ impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
pub fn downgrade(mut self) -> OwnedRwLockReadGuard<T> {
let lock = unsafe { ManuallyDrop::take(&mut self.lock) };
let data = self.data;
let to_release = (self.permits_acquired - 1) as usize;
// Release all but one of the permits held by the write guard
lock.s.release((self.permits_acquired - 1) as usize);
lock.s.release(to_release);
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = false,
write_locked.op = "override",
)
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
current_readers = 1,
current_readers.op = "add",
)
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = self.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(self);
OwnedRwLockReadGuard {
lock: ManuallyDrop::new(lock),
data,
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
}
@@ -229,6 +266,14 @@ where
impl<T: ?Sized> Drop for OwnedRwLockWriteGuard<T> {
fn drop(&mut self) {
self.lock.s.release(self.permits_acquired as usize);
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = false,
write_locked.op = "override",
)
});
unsafe { ManuallyDrop::drop(&mut self.lock) };
}
}
@@ -15,6 +15,8 @@ use std::sync::Arc;
/// [mapping]: method@crate::sync::OwnedRwLockWriteGuard::map
/// [`OwnedRwLockWriteGuard`]: struct@crate::sync::OwnedRwLockWriteGuard
pub struct OwnedRwLockMappedWriteGuard<T: ?Sized, U: ?Sized = T> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
pub(super) resource_span: tracing::Span,
pub(super) permits_acquired: u32,
// ManuallyDrop allows us to destructure into this field without running the destructor.
pub(super) lock: ManuallyDrop<Arc<RwLock<T>>>,
@@ -63,13 +65,18 @@ impl<T: ?Sized, U: ?Sized> OwnedRwLockMappedWriteGuard<T, U> {
let data = f(&mut *this) as *mut V;
let lock = unsafe { ManuallyDrop::take(&mut this.lock) };
let permits_acquired = this.permits_acquired;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
OwnedRwLockMappedWriteGuard {
permits_acquired,
lock: ManuallyDrop::new(lock),
data,
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -120,13 +127,18 @@ impl<T: ?Sized, U: ?Sized> OwnedRwLockMappedWriteGuard<T, U> {
};
let lock = unsafe { ManuallyDrop::take(&mut this.lock) };
let permits_acquired = this.permits_acquired;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
Ok(OwnedRwLockMappedWriteGuard {
permits_acquired,
lock: ManuallyDrop::new(lock),
data,
_p: PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
})
}
}
@@ -166,6 +178,14 @@ where
impl<T: ?Sized, U: ?Sized> Drop for OwnedRwLockMappedWriteGuard<T, U> {
fn drop(&mut self) {
self.lock.s.release(self.permits_acquired as usize);
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = false,
write_locked.op = "override",
)
});
unsafe { ManuallyDrop::drop(&mut self.lock) };
}
}
+21
View File
@@ -13,6 +13,8 @@ use std::ops;
/// [`read`]: method@crate::sync::RwLock::read
/// [`RwLock`]: struct@crate::sync::RwLock
pub struct RwLockReadGuard<'a, T: ?Sized> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
pub(super) resource_span: tracing::Span,
pub(super) s: &'a Semaphore,
pub(super) data: *const T,
pub(super) marker: marker::PhantomData<&'a T>,
@@ -59,12 +61,17 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> {
{
let data = f(&*this) as *const U;
let s = this.s;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
RwLockReadGuard {
s,
data,
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -113,12 +120,17 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> {
None => return Err(this),
};
let s = this.s;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
Ok(RwLockReadGuard {
s,
data,
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
})
}
}
@@ -152,5 +164,14 @@ where
impl<'a, T: ?Sized> Drop for RwLockReadGuard<'a, T> {
fn drop(&mut self) {
self.s.release(1);
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
current_readers = 1,
current_readers.op = "sub",
)
});
}
}
+44 -2
View File
@@ -15,6 +15,8 @@ use std::ops;
/// [`write`]: method@crate::sync::RwLock::write
/// [`RwLock`]: struct@crate::sync::RwLock
pub struct RwLockWriteGuard<'a, T: ?Sized> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
pub(super) resource_span: tracing::Span,
pub(super) permits_acquired: u32,
pub(super) s: &'a Semaphore,
pub(super) data: *mut T,
@@ -66,6 +68,8 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
let data = f(&mut *this) as *mut U;
let s = this.s;
let permits_acquired = this.permits_acquired;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
RwLockMappedWriteGuard {
@@ -73,6 +77,8 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
s,
data,
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -129,6 +135,8 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
};
let s = this.s;
let permits_acquired = this.permits_acquired;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
Ok(RwLockMappedWriteGuard {
@@ -136,6 +144,8 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
s,
data,
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
})
}
@@ -188,15 +198,38 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
/// [`RwLock`]: struct@crate::sync::RwLock
pub fn downgrade(self) -> RwLockReadGuard<'a, T> {
let RwLockWriteGuard { s, data, .. } = self;
let to_release = (self.permits_acquired - 1) as usize;
// Release all but one of the permits held by the write guard
s.release((self.permits_acquired - 1) as usize);
s.release(to_release);
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = false,
write_locked.op = "override",
)
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
current_readers = 1,
current_readers.op = "add",
)
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = self.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(self);
RwLockReadGuard {
s,
data,
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
}
@@ -236,5 +269,14 @@ where
impl<'a, T: ?Sized> Drop for RwLockWriteGuard<'a, T> {
fn drop(&mut self) {
self.s.release(self.permits_acquired as usize);
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = false,
write_locked.op = "override",
)
});
}
}
@@ -14,6 +14,8 @@ use std::ops;
/// [mapping]: method@crate::sync::RwLockWriteGuard::map
/// [`RwLockWriteGuard`]: struct@crate::sync::RwLockWriteGuard
pub struct RwLockMappedWriteGuard<'a, T: ?Sized> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
pub(super) resource_span: tracing::Span,
pub(super) permits_acquired: u32,
pub(super) s: &'a Semaphore,
pub(super) data: *mut T,
@@ -64,13 +66,18 @@ impl<'a, T: ?Sized> RwLockMappedWriteGuard<'a, T> {
let data = f(&mut *this) as *mut U;
let s = this.s;
let permits_acquired = this.permits_acquired;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
RwLockMappedWriteGuard {
permits_acquired,
s,
data,
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -126,13 +133,18 @@ impl<'a, T: ?Sized> RwLockMappedWriteGuard<'a, T> {
};
let s = this.s;
let permits_acquired = this.permits_acquired;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = this.resource_span.clone();
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
Ok(RwLockMappedWriteGuard {
permits_acquired,
s,
data,
marker: marker::PhantomData,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
})
}
}
@@ -172,5 +184,14 @@ where
impl<'a, T: ?Sized> Drop for RwLockMappedWriteGuard<'a, T> {
fn drop(&mut self) {
self.s.release(self.permits_acquired as usize);
#[cfg(all(tokio_unstable, feature = "tracing"))]
self.resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
write_locked = false,
write_locked.op = "override",
)
});
}
}
+86 -6
View File
@@ -1,5 +1,7 @@
use super::batch_semaphore as ll; // low level implementation
use super::{AcquireError, TryAcquireError};
#[cfg(all(tokio_unstable, feature = "tracing"))]
use crate::util::trace;
use std::sync::Arc;
/// Counting semaphore performing asynchronous permit acquisition.
@@ -77,6 +79,8 @@ use std::sync::Arc;
pub struct Semaphore {
/// The low level semaphore
ll_sem: ll::Semaphore,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
}
/// A permit from the semaphore.
@@ -120,9 +124,33 @@ fn bounds() {
impl Semaphore {
/// Creates a new semaphore with the initial number of permits.
#[track_caller]
pub fn new(permits: usize) -> Self {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = {
let location = std::panic::Location::caller();
tracing::trace_span!(
"runtime.resource",
concrete_type = "Semaphore",
kind = "Sync",
loc.file = location.file(),
loc.line = location.line(),
loc.col = location.column(),
inherits_child_attrs = true,
)
};
#[cfg(all(tokio_unstable, feature = "tracing"))]
let ll_sem = resource_span.in_scope(|| ll::Semaphore::new(permits));
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
let ll_sem = ll::Semaphore::new(permits);
Self {
ll_sem: ll::Semaphore::new(permits),
ll_sem,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -139,9 +167,16 @@ impl Semaphore {
#[cfg(all(feature = "parking_lot", not(all(loom, test))))]
#[cfg_attr(docsrs, doc(cfg(feature = "parking_lot")))]
pub const fn const_new(permits: usize) -> Self {
Self {
#[cfg(all(tokio_unstable, feature = "tracing"))]
return Self {
ll_sem: ll::Semaphore::const_new(permits),
}
resource_span: tracing::Span::none(),
};
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
return Self {
ll_sem: ll::Semaphore::const_new(permits),
};
}
/// Returns the current number of available permits.
@@ -191,7 +226,18 @@ impl Semaphore {
/// [`AcquireError`]: crate::sync::AcquireError
/// [`SemaphorePermit`]: crate::sync::SemaphorePermit
pub async fn acquire(&self) -> Result<SemaphorePermit<'_>, AcquireError> {
self.ll_sem.acquire(1).await?;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let inner = trace::async_op(
|| self.ll_sem.acquire(1),
self.resource_span.clone(),
"Semaphore::acquire",
"poll",
true,
);
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let inner = self.ll_sem.acquire(1);
inner.await?;
Ok(SemaphorePermit {
sem: self,
permits: 1,
@@ -227,7 +273,19 @@ impl Semaphore {
/// [`AcquireError`]: crate::sync::AcquireError
/// [`SemaphorePermit`]: crate::sync::SemaphorePermit
pub async fn acquire_many(&self, n: u32) -> Result<SemaphorePermit<'_>, AcquireError> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
trace::async_op(
|| self.ll_sem.acquire(n),
self.resource_span.clone(),
"Semaphore::acquire_many",
"poll",
true,
)
.await?;
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
self.ll_sem.acquire(n).await?;
Ok(SemaphorePermit {
sem: self,
permits: n,
@@ -350,7 +408,18 @@ impl Semaphore {
/// [`AcquireError`]: crate::sync::AcquireError
/// [`OwnedSemaphorePermit`]: crate::sync::OwnedSemaphorePermit
pub async fn acquire_owned(self: Arc<Self>) -> Result<OwnedSemaphorePermit, AcquireError> {
self.ll_sem.acquire(1).await?;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let inner = trace::async_op(
|| self.ll_sem.acquire(1),
self.resource_span.clone(),
"Semaphore::acquire_owned",
"poll",
true,
);
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let inner = self.ll_sem.acquire(1);
inner.await?;
Ok(OwnedSemaphorePermit {
sem: self,
permits: 1,
@@ -403,7 +472,18 @@ impl Semaphore {
self: Arc<Self>,
n: u32,
) -> Result<OwnedSemaphorePermit, AcquireError> {
self.ll_sem.acquire(n).await?;
#[cfg(all(tokio_unstable, feature = "tracing"))]
let inner = trace::async_op(
|| self.ll_sem.acquire(n),
self.resource_span.clone(),
"Semaphore::acquire_many_owned",
"poll",
true,
);
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let inner = self.ll_sem.acquire(n);
inner.await?;
Ok(OwnedSemaphorePermit {
sem: self,
permits: n,
+42
View File
@@ -318,6 +318,48 @@ impl<T> Receiver<T> {
Ref { inner }
}
/// Checks if this channel contains a message that this receiver has not yet
/// seen. The new value is not marked as seen.
///
/// Although this method is called `has_changed`, it does not check new
/// messages for equality, so this call will return true even if the new
/// message is equal to the old message.
///
/// Returns an error if the channel has been closed.
/// # Examples
///
/// ```
/// use tokio::sync::watch;
///
/// #[tokio::main]
/// async fn main() {
/// let (tx, mut rx) = watch::channel("hello");
///
/// tx.send("goodbye").unwrap();
///
/// assert!(rx.has_changed().unwrap());
/// assert_eq!(*rx.borrow_and_update(), "goodbye");
///
/// // The value has been marked as seen
/// assert!(!rx.has_changed().unwrap());
///
/// drop(tx);
/// // The `tx` handle has been dropped
/// assert!(rx.has_changed().is_err());
/// }
/// ```
pub fn has_changed(&self) -> Result<bool, error::RecvError> {
// Load the version from the state
let state = self.shared.state.load();
if state.is_closed() {
// The sender has dropped.
return Err(error::RecvError(()));
}
let new_version = state.version();
Ok(self.version != new_version)
}
/// Waits for a change notification, then marks the newest value as seen.
///
/// If the newest value in the channel has not yet been marked seen when
+1 -1
View File
@@ -188,7 +188,7 @@ cfg_rt! {
/// worker.await.unwrap();
/// # }
/// ```
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
+13 -3
View File
@@ -4,6 +4,10 @@ use std::future::Future;
/// Factory which is used to configure the properties of a new task.
///
/// **Note**: This is an [unstable API][unstable]. The public API of this type
/// may break in 1.x releases. See [the documentation on unstable
/// features][unstable] for details.
///
/// Methods can be chained in order to configure it.
///
/// Currently, there is only one configuration option:
@@ -45,7 +49,13 @@ use std::future::Future;
/// }
/// }
/// ```
/// [unstable API]: crate#unstable-features
/// [`name`]: Builder::name
/// [`spawn_local`]: Builder::spawn_local
/// [`spawn`]: Builder::spawn
/// [`spawn_blocking`]: Builder::spawn_blocking
#[derive(Default, Debug)]
#[cfg_attr(docsrs, doc(cfg(all(tokio_unstable, feature = "tracing"))))]
pub struct Builder<'a> {
name: Option<&'a str>,
}
@@ -65,7 +75,7 @@ impl<'a> Builder<'a> {
///
/// See [`task::spawn`](crate::task::spawn) for
/// more details.
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn<Fut>(self, future: Fut) -> JoinHandle<Fut::Output>
where
Fut: Future + Send + 'static,
@@ -78,7 +88,7 @@ impl<'a> Builder<'a> {
///
/// See [`task::spawn_local`](crate::task::spawn_local)
/// for more details.
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn_local<Fut>(self, future: Fut) -> JoinHandle<Fut::Output>
where
Fut: Future + 'static,
@@ -91,7 +101,7 @@ impl<'a> Builder<'a> {
///
/// See [`task::spawn_blocking`](crate::task::spawn_blocking)
/// for more details.
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn_blocking<Function, Output>(self, function: Function) -> JoinHandle<Output>
where
Function: FnOnce() -> Output + Send + 'static,
+2 -2
View File
@@ -286,7 +286,7 @@ cfg_rt! {
/// }).await;
/// }
/// ```
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn_local<F>(future: F) -> JoinHandle<F::Output>
where
F: Future + 'static,
@@ -377,7 +377,7 @@ impl LocalSet {
/// }
/// ```
/// [`spawn_local`]: fn@spawn_local
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn_local<F>(&self, future: F) -> JoinHandle<F::Output>
where
F: Future + 'static,
+2 -2
View File
@@ -121,7 +121,7 @@ cfg_rt! {
/// ```text
/// error[E0391]: cycle detected when processing `main`
/// ```
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn spawn<T>(future: T) -> JoinHandle<T::Output>
where
T: Future + Send + 'static,
@@ -136,7 +136,7 @@ cfg_rt! {
}
}
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub(super) fn spawn_inner<T>(future: T, name: Option<&str>) -> JoinHandle<T::Output>
where
T: Future + Send + 'static,
+2 -2
View File
@@ -258,14 +258,14 @@ impl<T: 'static, F> TaskLocalFuture<T, F> {
}
}
let mut project = self.project();
let project = self.project();
let val = project.slot.take();
let prev = project.local.inner.with(|c| c.replace(val));
let _guard = Guard {
prev,
slot: &mut project.slot,
slot: project.slot,
local: *project.local,
};
-1
View File
@@ -33,7 +33,6 @@ use std::task::{Context, Poll};
/// which order the runtime polls your tasks in.
///
/// [`tokio::select!`]: macro@crate::select
#[must_use = "yield_now does nothing unless polled/`await`-ed"]
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
pub async fn yield_now() {
/// Yield implementation
+6 -4
View File
@@ -5,9 +5,9 @@
//!
//! # Ground rules
//!
//! The heart of the timer implementation here is the `TimerShared` structure,
//! shared between the `TimerEntry` and the driver. Generally, we permit access
//! to `TimerShared` ONLY via either 1) a mutable reference to `TimerEntry` or
//! The heart of the timer implementation here is the [`TimerShared`] structure,
//! shared between the [`TimerEntry`] and the driver. Generally, we permit access
//! to [`TimerShared`] ONLY via either 1) a mutable reference to [`TimerEntry`] or
//! 2) a held driver lock.
//!
//! It follows from this that any changes made while holding BOTH 1 and 2 will
@@ -49,8 +49,10 @@
//! There is of course a race condition between timer reset and timer
//! expiration. If the driver fails to observe the updated expiration time, it
//! could trigger expiration of the timer too early. However, because
//! `mark_pending` performs a compare-and-swap, it will identify this race and
//! [`mark_pending`][mark_pending] performs a compare-and-swap, it will identify this race and
//! refuse to mark the timer as pending.
//!
//! [mark_pending]: TimerHandle::mark_pending
use crate::loom::cell::UnsafeCell;
use crate::loom::sync::atomic::AtomicU64;
+17 -11
View File
@@ -40,16 +40,19 @@ cfg_rt! {
///
/// This function panics if there is no current timer set.
///
/// It can be triggered when `Builder::enable_time()` or
/// `Builder::enable_all()` are not included in the builder.
/// It can be triggered when [`Builder::enable_time`] or
/// [`Builder::enable_all`] are not included in the builder.
///
/// It can also panic whenever a timer is created outside of a
/// Tokio runtime. That is why `rt.block_on(delay_for(...))` will panic,
/// Tokio runtime. That is why `rt.block_on(sleep(...))` will panic,
/// since the function is executed outside of the runtime.
/// Whereas `rt.block_on(async {delay_for(...).await})` doesn't panic.
/// Whereas `rt.block_on(async {sleep(...).await})` doesn't panic.
/// And this is because wrapping the function on an async makes it lazy,
/// and so gets executed inside the runtime successfully without
/// panicking.
///
/// [`Builder::enable_time`]: crate::runtime::Builder::enable_time
/// [`Builder::enable_all`]: crate::runtime::Builder::enable_all
pub(crate) fn current() -> Self {
crate::runtime::context::time_handle()
.expect("A Tokio 1.x context was found, but timers are disabled. Call `enable_time` on the runtime builder to enable timers.")
@@ -65,16 +68,19 @@ cfg_not_rt! {
///
/// This function panics if there is no current timer set.
///
/// It can be triggered when `Builder::enable_time()` or
/// `Builder::enable_all()` are not included in the builder.
/// It can be triggered when [`Builder::enable_time`] or
/// [`Builder::enable_all`] are not included in the builder.
///
/// It can also panic whenever a timer is created outside of a Tokio
/// runtime. That is why `rt.block_on(delay_for(...))` will panic,
/// It can also panic whenever a timer is created outside of a
/// Tokio runtime. That is why `rt.block_on(sleep(...))` will panic,
/// since the function is executed outside of the runtime.
/// Whereas `rt.block_on(async {delay_for(...).await})` doesn't
/// panic. And this is because wrapping the function on an async makes it
/// lazy, and so outside executed inside the runtime successfully without
/// Whereas `rt.block_on(async {sleep(...).await})` doesn't panic.
/// And this is because wrapping the function on an async makes it lazy,
/// and so gets executed inside the runtime successfully without
/// panicking.
///
/// [`Builder::enable_time`]: crate::runtime::Builder::enable_time
/// [`Builder::enable_all`]: crate::runtime::Builder::enable_all
pub(crate) fn current() -> Self {
panic!("{}", crate::util::error::CONTEXT_MISSING_ERROR)
}
+94 -61
View File
@@ -1,3 +1,5 @@
#[cfg(all(tokio_unstable, feature = "tracing"))]
use crate::time::driver::ClockTime;
use crate::time::driver::{Handle, TimerEntry};
use crate::time::{error::Error, Duration, Instant};
use crate::util::trace;
@@ -8,10 +10,6 @@ use std::panic::Location;
use std::pin::Pin;
use std::task::{self, Poll};
cfg_trace! {
use crate::time::driver::ClockTime;
}
/// Waits until `deadline` is reached.
///
/// No work is performed while awaiting on the sleep future to complete. `Sleep`
@@ -41,11 +39,28 @@ cfg_trace! {
///
/// See the documentation for the [`Sleep`] type for more examples.
///
/// # Panics
///
/// This function panics if there is no current timer set.
///
/// It can be triggered when [`Builder::enable_time`] or
/// [`Builder::enable_all`] are not included in the builder.
///
/// It can also panic whenever a timer is created outside of a
/// Tokio runtime. That is why `rt.block_on(sleep(...))` will panic,
/// since the function is executed outside of the runtime.
/// Whereas `rt.block_on(async {sleep(...).await})` doesn't panic.
/// And this is because wrapping the function on an async makes it lazy,
/// and so gets executed inside the runtime successfully without
/// panicking.
///
/// [`Sleep`]: struct@crate::time::Sleep
/// [`interval`]: crate::time::interval()
/// [`Builder::enable_time`]: crate::runtime::Builder::enable_time
/// [`Builder::enable_all`]: crate::runtime::Builder::enable_all
// Alias for old name in 0.x
#[cfg_attr(docsrs, doc(alias = "delay_until"))]
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn sleep_until(deadline: Instant) -> Sleep {
return Sleep::new_timeout(deadline, trace::caller_location());
}
@@ -84,12 +99,29 @@ pub fn sleep_until(deadline: Instant) -> Sleep {
///
/// See the documentation for the [`Sleep`] type for more examples.
///
/// # Panics
///
/// This function panics if there is no current timer set.
///
/// It can be triggered when [`Builder::enable_time`] or
/// [`Builder::enable_all`] are not included in the builder.
///
/// It can also panic whenever a timer is created outside of a
/// Tokio runtime. That is why `rt.block_on(sleep(...))` will panic,
/// since the function is executed outside of the runtime.
/// Whereas `rt.block_on(async {sleep(...).await})` doesn't panic.
/// And this is because wrapping the function on an async makes it lazy,
/// and so gets executed inside the runtime successfully without
/// panicking.
///
/// [`Sleep`]: struct@crate::time::Sleep
/// [`interval`]: crate::time::interval()
/// [`Builder::enable_time`]: crate::runtime::Builder::enable_time
/// [`Builder::enable_all`]: crate::runtime::Builder::enable_all
// Alias for old name in 0.x
#[cfg_attr(docsrs, doc(alias = "delay_for"))]
#[cfg_attr(docsrs, doc(alias = "wait"))]
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub fn sleep(duration: Duration) -> Sleep {
let location = trace::caller_location();
@@ -204,8 +236,7 @@ cfg_trace! {
#[derive(Debug)]
struct Inner {
deadline: Instant,
resource_span: tracing::Span,
async_op_span: tracing::Span,
ctx: trace::AsyncOpTracingCtx,
time_source: ClockTime,
}
}
@@ -232,10 +263,7 @@ impl Sleep {
let deadline_tick = time_source.deadline_to_tick(deadline);
let duration = deadline_tick.checked_sub(time_source.now()).unwrap_or(0);
#[cfg(tokio_track_caller)]
let location = location.expect("should have location if tracking caller");
#[cfg(tokio_track_caller)]
let location = location.expect("should have location if tracing");
let resource_span = tracing::trace_span!(
"runtime.resource",
concrete_type = "Sleep",
@@ -245,25 +273,29 @@ impl Sleep {
loc.col = location.column(),
);
#[cfg(not(tokio_track_caller))]
let resource_span =
tracing::trace_span!("runtime.resource", concrete_type = "Sleep", kind = "timer");
let async_op_span = resource_span.in_scope(|| {
tracing::trace!(
target: "runtime::resource::state_update",
duration = duration,
duration.unit = "ms",
duration.op = "override",
);
let async_op_span =
tracing::trace_span!("runtime.resource.async_op", source = "Sleep::new_timeout");
tracing::trace_span!("runtime.resource.async_op", source = "Sleep::new_timeout")
});
tracing::trace!(
target: "runtime::resource::state_update",
parent: resource_span.id(),
duration = duration,
duration.unit = "ms",
duration.op = "override",
);
let async_op_poll_span =
async_op_span.in_scope(|| tracing::trace_span!("runtime.resource.async_op.poll"));
let ctx = trace::AsyncOpTracingCtx {
async_op_span,
async_op_poll_span,
resource_span,
};
Inner {
deadline,
resource_span,
async_op_span,
ctx,
time_source,
}
};
@@ -330,54 +362,52 @@ impl Sleep {
#[cfg(all(tokio_unstable, feature = "tracing"))]
{
me.inner.async_op_span =
let _resource_enter = me.inner.ctx.resource_span.enter();
me.inner.ctx.async_op_span =
tracing::trace_span!("runtime.resource.async_op", source = "Sleep::reset");
let _async_op_enter = me.inner.ctx.async_op_span.enter();
me.inner.ctx.async_op_poll_span =
tracing::trace_span!("runtime.resource.async_op.poll");
let duration = {
let now = me.inner.time_source.now();
let deadline_tick = me.inner.time_source.deadline_to_tick(deadline);
deadline_tick.checked_sub(now).unwrap_or(0)
};
tracing::trace!(
target: "runtime::resource::state_update",
parent: me.inner.resource_span.id(),
duration = {
let now = me.inner.time_source.now();
let deadline_tick = me.inner.time_source.deadline_to_tick(deadline);
deadline_tick.checked_sub(now).unwrap_or(0)
},
duration = duration,
duration.unit = "ms",
duration.op = "override",
);
}
}
cfg_not_trace! {
fn poll_elapsed(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Result<(), Error>> {
let me = self.project();
fn poll_elapsed(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Result<(), Error>> {
let me = self.project();
// Keep track of task budget
let coop = ready!(crate::coop::poll_proceed(cx));
// Keep track of task budget
#[cfg(all(tokio_unstable, feature = "tracing"))]
let coop = ready!(trace_poll_op!(
"poll_elapsed",
crate::coop::poll_proceed(cx),
));
me.entry.poll_elapsed(cx).map(move |r| {
coop.made_progress();
r
})
}
}
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
let coop = ready!(crate::coop::poll_proceed(cx));
cfg_trace! {
fn poll_elapsed(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Result<(), Error>> {
let me = self.project();
// Keep track of task budget
let coop = ready!(trace_poll_op!(
"poll_elapsed",
crate::coop::poll_proceed(cx),
me.inner.resource_span.id(),
));
let result = me.entry.poll_elapsed(cx).map(move |r| {
coop.made_progress();
r
});
let result = me.entry.poll_elapsed(cx).map(move |r| {
coop.made_progress();
r
});
#[cfg(all(tokio_unstable, feature = "tracing"))]
return trace_poll_op!("poll_elapsed", result);
trace_poll_op!("poll_elapsed", result, me.inner.resource_span.id())
}
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
return result;
}
}
@@ -395,8 +425,11 @@ impl Future for Sleep {
// really do much better if we passed the error onwards.
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let _span = self.inner.async_op_span.clone().entered();
let _res_span = self.inner.ctx.resource_span.clone().entered();
#[cfg(all(tokio_unstable, feature = "tracing"))]
let _ao_span = self.inner.ctx.async_op_span.clone().entered();
#[cfg(all(tokio_unstable, feature = "tracing"))]
let _ao_poll_span = self.inner.ctx.async_op_poll_span.clone().entered();
match ready!(self.as_mut().poll_elapsed(cx)) {
Ok(()) => Poll::Ready(()),
Err(e) => panic!("timer error: {}", e),
+1 -1
View File
@@ -53,7 +53,7 @@ impl Level {
// However, that is only supported for arrays of size
// 32 or fewer. So in our case we have to explicitly
// invoke the constructor for each array element.
let ctor = || EntryList::default();
let ctor = EntryList::default;
Level {
level,
+84 -5
View File
@@ -1,6 +1,8 @@
use crate::future::poll_fn;
use crate::time::{sleep_until, Duration, Instant, Sleep};
use crate::util::trace;
use std::panic::Location;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{convert::TryInto, future::Future};
@@ -68,10 +70,10 @@ use std::{convert::TryInto, future::Future};
///
/// [`sleep`]: crate::time::sleep()
/// [`.tick().await`]: Interval::tick
#[track_caller]
pub fn interval(period: Duration) -> Interval {
assert!(period > Duration::new(0, 0), "`period` must be non-zero.");
interval_at(Instant::now(), period)
internal_interval_at(Instant::now(), period, trace::caller_location())
}
/// Creates new [`Interval`] that yields with interval of `period` with the
@@ -103,13 +105,44 @@ pub fn interval(period: Duration) -> Interval {
/// // approximately 70ms have elapsed.
/// }
/// ```
#[track_caller]
pub fn interval_at(start: Instant, period: Duration) -> Interval {
assert!(period > Duration::new(0, 0), "`period` must be non-zero.");
internal_interval_at(start, period, trace::caller_location())
}
#[cfg_attr(not(all(tokio_unstable, feature = "tracing")), allow(unused_variables))]
fn internal_interval_at(
start: Instant,
period: Duration,
location: Option<&'static Location<'static>>,
) -> Interval {
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = {
let location = location.expect("should have location if tracing");
tracing::trace_span!(
"runtime.resource",
concrete_type = "Interval",
kind = "timer",
loc.file = location.file(),
loc.line = location.line(),
loc.col = location.column(),
)
};
#[cfg(all(tokio_unstable, feature = "tracing"))]
let delay = resource_span.in_scope(|| Box::pin(sleep_until(start)));
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let delay = Box::pin(sleep_until(start));
Interval {
delay: Box::pin(sleep_until(start)),
delay,
period,
missed_tick_behavior: Default::default(),
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span,
}
}
@@ -124,7 +157,7 @@ pub fn interval_at(start: Instant, period: Duration) -> Interval {
///
/// #[tokio::main]
/// async fn main() {
/// // ticks every 2 seconds
/// // ticks every 2 milliseconds
/// let mut interval = time::interval(Duration::from_millis(2));
/// for _ in 0..5 {
/// interval.tick().await;
@@ -362,6 +395,9 @@ pub struct Interval {
/// The strategy `Interval` should use when a tick is missed.
missed_tick_behavior: MissedTickBehavior,
#[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
}
impl Interval {
@@ -391,7 +427,20 @@ impl Interval {
/// }
/// ```
pub async fn tick(&mut self) -> Instant {
poll_fn(|cx| self.poll_tick(cx)).await
#[cfg(all(tokio_unstable, feature = "tracing"))]
let resource_span = self.resource_span.clone();
#[cfg(all(tokio_unstable, feature = "tracing"))]
let instant = trace::async_op(
|| poll_fn(|cx| self.poll_tick(cx)),
resource_span,
"Interval::tick",
"poll_tick",
false,
);
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let instant = poll_fn(|cx| self.poll_tick(cx));
instant.await
}
/// Polls for the next instant in the interval to be reached.
@@ -435,6 +484,36 @@ impl Interval {
Poll::Ready(timeout)
}
/// Resets the interval to complete one period after the current time.
///
/// This method ignores [`MissedTickBehavior`] strategy.
///
/// # Examples
///
/// ```
/// use tokio::time;
///
/// use std::time::Duration;
///
/// #[tokio::main]
/// async fn main() {
/// let mut interval = time::interval(Duration::from_millis(100));
///
/// interval.tick().await;
///
/// time::sleep(Duration::from_millis(50)).await;
/// interval.reset();
///
/// interval.tick().await;
/// interval.tick().await;
///
/// // approximately 250ms have elapsed.
/// }
/// ```
pub fn reset(&mut self) {
self.delay.as_mut().reset(Instant::now() + self.period);
}
/// Returns the [`MissedTickBehavior`] strategy currently being used.
pub fn missed_tick_behavior(&self) -> MissedTickBehavior {
self.missed_tick_behavior
+42 -5
View File
@@ -5,6 +5,7 @@
//! [`Timeout`]: struct@Timeout
use crate::{
coop,
time::{error::Elapsed, sleep_until, Duration, Instant, Sleep},
util::trace,
};
@@ -48,7 +49,25 @@ use std::task::{self, Poll};
/// }
/// # }
/// ```
#[cfg_attr(tokio_track_caller, track_caller)]
///
/// # Panics
///
/// This function panics if there is no current timer set.
///
/// It can be triggered when [`Builder::enable_time`] or
/// [`Builder::enable_all`] are not included in the builder.
///
/// It can also panic whenever a timer is created outside of a
/// Tokio runtime. That is why `rt.block_on(sleep(...))` will panic,
/// since the function is executed outside of the runtime.
/// Whereas `rt.block_on(async {sleep(...).await})` doesn't panic.
/// And this is because wrapping the function on an async makes it lazy,
/// and so gets executed inside the runtime successfully without
/// panicking.
///
/// [`Builder::enable_time`]: crate::runtime::Builder::enable_time
/// [`Builder::enable_all`]: crate::runtime::Builder::enable_all
#[track_caller]
pub fn timeout<T>(duration: Duration, future: T) -> Timeout<T>
where
T: Future,
@@ -151,15 +170,33 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let me = self.project();
let had_budget_before = coop::has_budget_remaining();
// First, try polling the future
if let Poll::Ready(v) = me.value.poll(cx) {
return Poll::Ready(Ok(v));
}
// Now check the timer
match me.delay.poll(cx) {
Poll::Ready(()) => Poll::Ready(Err(Elapsed::new())),
Poll::Pending => Poll::Pending,
let has_budget_now = coop::has_budget_remaining();
let delay = me.delay;
let poll_delay = || -> Poll<Self::Output> {
match delay.poll(cx) {
Poll::Ready(()) => Poll::Ready(Err(Elapsed::new())),
Poll::Pending => Poll::Pending,
}
};
if let (true, false) = (had_budget_before, has_budget_now) {
// if it is the underlying future that exhausted the budget, we poll
// the `delay` with an unconstrained one. This prevents pathological
// cases where the underlying future always exhausts the budget and
// we never get a chance to evaluate whether the timeout was hit or
// not.
coop::with_unconstrained(poll_delay)
} else {
poll_delay()
}
}
}
@@ -3,7 +3,7 @@ use crate::loom::sync::atomic::AtomicPtr;
use std::ptr;
use std::sync::atomic::Ordering::AcqRel;
pub(super) struct AtomicCell<T> {
pub(crate) struct AtomicCell<T> {
data: AtomicPtr<T>,
}
@@ -11,22 +11,22 @@ unsafe impl<T: Send> Send for AtomicCell<T> {}
unsafe impl<T: Send> Sync for AtomicCell<T> {}
impl<T> AtomicCell<T> {
pub(super) fn new(data: Option<Box<T>>) -> AtomicCell<T> {
pub(crate) fn new(data: Option<Box<T>>) -> AtomicCell<T> {
AtomicCell {
data: AtomicPtr::new(to_raw(data)),
}
}
pub(super) fn swap(&self, val: Option<Box<T>>) -> Option<Box<T>> {
pub(crate) fn swap(&self, val: Option<Box<T>>) -> Option<Box<T>> {
let old = self.data.swap(to_raw(val), AcqRel);
from_raw(old)
}
pub(super) fn set(&self, val: Box<T>) {
pub(crate) fn set(&self, val: Box<T>) {
let _ = self.swap(Some(val));
}
pub(super) fn take(&self) -> Option<Box<T>> {
pub(crate) fn take(&self) -> Option<Box<T>> {
self.swap(None)
}
}
+3
View File
@@ -3,6 +3,9 @@ cfg_io_driver! {
pub(crate) mod slab;
}
#[cfg(feature = "rt")]
pub(crate) mod atomic_cell;
#[cfg(any(
// io driver uses `WakeList` directly
feature = "net",
+62 -13
View File
@@ -1,14 +1,18 @@
cfg_trace! {
cfg_rt! {
use core::{
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use std::future::Future;
pub(crate) use tracing::instrument::Instrumented;
#[inline]
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub(crate) fn task<F>(task: F, kind: &'static str, name: Option<&str>) -> Instrumented<F> {
use tracing::instrument::Instrument;
#[cfg(tokio_track_caller)]
let location = std::panic::Location::caller();
#[cfg(tokio_track_caller)]
let span = tracing::trace_span!(
target: "tokio::task",
"runtime.spawn",
@@ -18,23 +22,68 @@ cfg_trace! {
loc.line = location.line(),
loc.col = location.column(),
);
#[cfg(not(tokio_track_caller))]
let span = tracing::trace_span!(
target: "tokio::task",
"runtime.spawn",
%kind,
task.name = %name.unwrap_or_default(),
);
task.instrument(span)
}
pub(crate) fn async_op<P,F>(inner: P, resource_span: tracing::Span, source: &str, poll_op_name: &'static str, inherits_child_attrs: bool) -> InstrumentedAsyncOp<F>
where P: FnOnce() -> F {
resource_span.in_scope(|| {
let async_op_span = tracing::trace_span!("runtime.resource.async_op", source = source, inherits_child_attrs = inherits_child_attrs);
let enter = async_op_span.enter();
let async_op_poll_span = tracing::trace_span!("runtime.resource.async_op.poll");
let inner = inner();
drop(enter);
let tracing_ctx = AsyncOpTracingCtx {
async_op_span,
async_op_poll_span,
resource_span: resource_span.clone(),
};
InstrumentedAsyncOp {
inner,
tracing_ctx,
poll_op_name,
}
})
}
#[derive(Debug, Clone)]
pub(crate) struct AsyncOpTracingCtx {
pub(crate) async_op_span: tracing::Span,
pub(crate) async_op_poll_span: tracing::Span,
pub(crate) resource_span: tracing::Span,
}
pin_project! {
#[derive(Debug, Clone)]
pub(crate) struct InstrumentedAsyncOp<F> {
#[pin]
pub(crate) inner: F,
pub(crate) tracing_ctx: AsyncOpTracingCtx,
pub(crate) poll_op_name: &'static str
}
}
impl<F: Future> Future for InstrumentedAsyncOp<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let poll_op_name = &*this.poll_op_name;
let _res_enter = this.tracing_ctx.resource_span.enter();
let _async_op_enter = this.tracing_ctx.async_op_span.enter();
let _async_op_poll_enter = this.tracing_ctx.async_op_poll_span.enter();
trace_poll_op!(poll_op_name, this.inner.poll(cx))
}
}
}
}
cfg_time! {
#[cfg_attr(tokio_track_caller, track_caller)]
#[track_caller]
pub(crate) fn caller_location() -> Option<&'static std::panic::Location<'static>> {
#[cfg(all(tokio_track_caller, tokio_unstable, feature = "tracing"))]
#[cfg(all(tokio_unstable, feature = "tracing"))]
return Some(std::panic::Location::caller());
#[cfg(not(all(tokio_track_caller, tokio_unstable, feature = "tracing")))]
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
None
}
}
+32
View File
@@ -0,0 +1,32 @@
#![cfg(feature = "full")]
use tokio::io::{AsyncBufReadExt, AsyncReadExt};
#[tokio::test]
async fn empty_read_is_cooperative() {
tokio::select! {
biased;
_ = async {
loop {
let mut buf = [0u8; 4096];
let _ = tokio::io::empty().read(&mut buf).await;
}
} => {},
_ = tokio::task::yield_now() => {}
}
}
#[tokio::test]
async fn empty_buf_reads_are_cooperative() {
tokio::select! {
biased;
_ = async {
loop {
let mut buf = String::new();
let _ = tokio::io::empty().read_line(&mut buf).await;
}
} => {},
_ = tokio::task::yield_now() => {}
}
}
+22
View File
@@ -46,3 +46,25 @@ pub async fn issue_4175_test() -> std::io::Result<()> {
return Ok(());
panic!();
}
// https://github.com/tokio-rs/tokio/issues/4175
pub mod clippy_semicolon_if_nothing_returned {
#![deny(clippy::semicolon_if_nothing_returned)]
#[tokio::main]
pub async fn local() {
let _x = ();
}
#[tokio::main]
pub async fn item() {
fn _f() {}
}
#[tokio::main]
pub async fn semi() {
panic!();
}
#[tokio::main]
pub async fn empty() {
// To trigger clippy::semicolon_if_nothing_returned lint, the block needs to contain newline.
}
}
+1 -1
View File
@@ -135,7 +135,7 @@ rt_test! {
let contents = Handle::current()
.block_on(fs::read_to_string("Cargo.toml"))
.unwrap();
assert!(contents.contains("Cargo.toml"));
assert!(contents.contains("https://tokio.rs"));
}
#[test]
+34
View File
@@ -597,3 +597,37 @@ fn try_recv_close_while_empty_unbounded() {
drop(tx);
assert_eq!(Err(TryRecvError::Disconnected), rx.try_recv());
}
#[tokio::test(start_paused = true)]
async fn recv_timeout() {
use tokio::sync::mpsc::error::SendTimeoutError::{Closed, Timeout};
use tokio::time::Duration;
let (tx, rx) = mpsc::channel(5);
assert_eq!(tx.send_timeout(10, Duration::from_secs(1)).await, Ok(()));
assert_eq!(tx.send_timeout(20, Duration::from_secs(1)).await, Ok(()));
assert_eq!(tx.send_timeout(30, Duration::from_secs(1)).await, Ok(()));
assert_eq!(tx.send_timeout(40, Duration::from_secs(1)).await, Ok(()));
assert_eq!(tx.send_timeout(50, Duration::from_secs(1)).await, Ok(()));
assert_eq!(
tx.send_timeout(60, Duration::from_secs(1)).await,
Err(Timeout(60))
);
drop(rx);
assert_eq!(
tx.send_timeout(70, Duration::from_secs(1)).await,
Err(Closed(70))
);
}
#[test]
#[should_panic = "there is no reactor running, must be called from the context of a Tokio 1.x runtime"]
fn recv_timeout_panic() {
use futures::future::FutureExt;
use tokio::time::Duration;
let (tx, _rx) = mpsc::channel(5);
tx.send_timeout(10, Duration::from_secs(1)).now_or_never();
}
+7
View File
@@ -174,17 +174,24 @@ fn poll_close() {
fn borrow_and_update() {
let (tx, mut rx) = watch::channel("one");
assert!(!rx.has_changed().unwrap());
tx.send("two").unwrap();
assert!(rx.has_changed().unwrap());
assert_ready!(spawn(rx.changed()).poll()).unwrap();
assert_pending!(spawn(rx.changed()).poll());
assert!(!rx.has_changed().unwrap());
tx.send("three").unwrap();
assert!(rx.has_changed().unwrap());
assert_eq!(*rx.borrow_and_update(), "three");
assert_pending!(spawn(rx.changed()).poll());
assert!(!rx.has_changed().unwrap());
drop(tx);
assert_eq!(*rx.borrow_and_update(), "three");
assert_ready!(spawn(rx.changed()).poll()).unwrap_err();
assert!(rx.has_changed().is_err());
}
#[test]
+14
View File
@@ -1,6 +1,7 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]
use std::time::Duration;
use tokio::net::TcpSocket;
use tokio_test::assert_ok;
@@ -58,3 +59,16 @@ async fn bind_before_connect() {
// Accept
let _ = assert_ok!(srv.accept().await);
}
#[tokio::test]
async fn basic_linger() {
// Create server
let addr = assert_ok!("127.0.0.1:0".parse());
let srv = assert_ok!(TcpSocket::new_v4());
assert_ok!(srv.bind(addr));
assert!(srv.linger().unwrap().is_none());
srv.set_linger(Some(Duration::new(0, 0))).unwrap();
assert_eq!(srv.linger().unwrap(), Some(Duration::new(0, 0)));
}
+36
View File
@@ -166,6 +166,42 @@ async fn skip() {
check_interval_poll!(i, start, 1800);
}
#[tokio::test(start_paused = true)]
async fn reset() {
let start = Instant::now();
// This is necessary because the timer is only so granular, and in order for
// all our ticks to resolve, the time needs to be 1ms ahead of what we
// expect, so that the runtime will see that it is time to resolve the timer
time::advance(ms(1)).await;
let mut i = task::spawn(time::interval_at(start, ms(300)));
check_interval_poll!(i, start, 0);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
time::advance(ms(200)).await;
check_interval_poll!(i, start, 300);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
i.reset();
time::advance(ms(250)).await;
check_interval_poll!(i, start);
time::advance(ms(50)).await;
// We add one because when using `reset` method, `Interval` adds the
// `period` from `Instant::now()`, which will always be off by one
check_interval_poll!(i, start, 701);
time::advance(ms(300)).await;
check_interval_poll!(i, start, 1001);
}
fn poll_next(interval: &mut task::Spawn<time::Interval>) -> Poll<Instant> {
interval.enter(|cx, mut interval| interval.poll_tick(cx))
}
+13
View File
@@ -135,3 +135,16 @@ async fn deadline_future_elapses() {
fn ms(n: u64) -> Duration {
Duration::from_millis(n)
}
#[tokio::test]
async fn timeout_is_not_exhausted_by_future() {
let fut = timeout(ms(1), async {
let mut buffer = [0u8; 1];
loop {
use tokio::io::AsyncReadExt;
let _ = tokio::io::empty().read(&mut buffer).await;
}
});
assert!(fut.await.is_err());
}

Some files were not shown because too many files have changed in this diff Show More