mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f840c5232 | ||
|
|
cc8ad367a0 | ||
|
|
25e5141c36 | ||
|
|
4a12163d7c | ||
|
|
12dd06336d | ||
|
|
c301f6d83a | ||
|
|
fb35c83944 | ||
|
|
43cdb2cb50 | ||
|
|
49a9dc6743 | ||
|
|
96370ba4ce | ||
|
|
a9d9bde068 | ||
|
|
0190831ec1 | ||
|
|
ee0e811a36 | ||
|
|
47feaa7a89 | ||
|
|
dda8da75d0 | ||
|
|
dc1894105b | ||
|
|
78e0f0b42a | ||
|
|
e55f3d4398 | ||
|
|
8582363b4e | ||
|
|
c3fbaba1f9 | ||
|
|
22e6aef6e7 | ||
|
|
f64673580d | ||
|
|
54e6693dff | ||
|
|
4e3268d222 | ||
|
|
4b6bb1d9a7 | ||
|
|
eb1af7f29c | ||
|
|
0bc9160e25 | ||
|
|
4c571b55b1 | ||
|
|
60ba634d60 | ||
|
|
f73ed1fdba | ||
|
|
ee4b2ede83 | ||
|
|
64da914d17 | ||
|
|
d764ba5816 | ||
|
|
65fb0210d5 | ||
|
|
a77b2fbab2 | ||
|
|
347c0cdaba | ||
|
|
2c0e5c9704 | ||
|
|
2a614fba0d | ||
|
|
3b339024f0 | ||
|
|
1a423b3322 | ||
|
|
a8b662f643 | ||
|
|
cf3206842c | ||
|
|
8943e8aeef | ||
|
|
fe770dc509 | ||
|
|
8b6542fc3e | ||
|
|
b1afd95994 | ||
|
|
095b5dcf93 | ||
|
|
623c09c52c |
@@ -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
@@ -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
@@ -1 +1 @@
|
||||
msrv = "1.45"
|
||||
msrv = "1.46"
|
||||
|
||||
+6
-7
@@ -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/**/*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,3 +11,4 @@ jobs:
|
||||
- uses: actions/labeler@v3
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
sync-labels: true
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 },
|
||||
};
|
||||
|
||||
@@ -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,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.
|
||||
"""
|
||||
|
||||
@@ -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=>
|
||||
|
||||
@@ -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,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"] }
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
#. 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.
|
||||
///
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,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"
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
@@ -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
@@ -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,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
@@ -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! {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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! {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
//! Threadpool
|
||||
|
||||
mod atomic_cell;
|
||||
use atomic_cell::AtomicCell;
|
||||
|
||||
mod idle;
|
||||
use self::idle::Idle;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() => {}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user