mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
69
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1df874ead4 | ||
|
|
614fe357fc | ||
|
|
68b02db154 | ||
|
|
822af18cf5 | ||
|
|
92d33b7181 | ||
|
|
1cb7bf11b3 | ||
|
|
768ede65c1 | ||
|
|
54a394696f | ||
|
|
35dd635630 | ||
|
|
a7bb054414 | ||
|
|
0c8e8248f8 | ||
|
|
2dfe4e8885 | ||
|
|
b489acb46c | ||
|
|
d46c844bb9 | ||
|
|
4cd4b02389 | ||
|
|
17cc283f58 | ||
|
|
0a93ed7e7a | ||
|
|
cef98e25e7 | ||
|
|
e7bd754231 | ||
|
|
d459a93453 | ||
|
|
f177aad6e4 | ||
|
|
4ea632005d | ||
|
|
bfc43795f9 | ||
|
|
89329cd07f | ||
|
|
e34978233b | ||
|
|
8eb94a33c0 | ||
|
|
2b7b1a0494 | ||
|
|
002f4a28c8 | ||
|
|
ff2f286c12 | ||
|
|
bd4ce68864 | ||
|
|
abd92fb27f | ||
|
|
9931901d5c | ||
|
|
a377240bbf | ||
|
|
52da177dea | ||
|
|
ee1c940709 | ||
|
|
815d89a407 | ||
|
|
54aaf3d0e3 | ||
|
|
5a3abe56ee | ||
|
|
d44b1ca9c8 | ||
|
|
e23c6f3935 | ||
|
|
0a50cb3baa | ||
|
|
2298679af4 | ||
|
|
cadcd5da5e | ||
|
|
ca9f7ee9f4 | ||
|
|
c89406965f | ||
|
|
cf486361d0 | ||
|
|
d7b7c61317 | ||
|
|
12f81ffa61 | ||
|
|
3ea5cc5a82 | ||
|
|
fa31cd9990 | ||
|
|
46f974d8cf | ||
|
|
018d0450c7 | ||
|
|
ee09e04c31 | ||
|
|
d07027f5bc | ||
|
|
2e0372be6f | ||
|
|
eca24068f7 | ||
|
|
795754a846 | ||
|
|
0f17d69303 | ||
|
|
2e7f996f17 | ||
|
|
901f6d26c6 | ||
|
|
a8fda87058 | ||
|
|
d7abdbb315 | ||
|
|
24aac0add3 | ||
|
|
b921fe45ac | ||
|
|
0dc1b71e6e | ||
|
|
d19f2f2d39 | ||
|
|
e106c4d32b | ||
|
|
28d6f4d509 | ||
|
|
d1da6c20d8 |
+1
-1
@@ -1 +1 @@
|
||||
msrv = "1.49"
|
||||
msrv = "1.56"
|
||||
|
||||
@@ -21,7 +21,7 @@ env:
|
||||
# - tokio-util/Cargo.toml
|
||||
# - tokio-test/Cargo.toml
|
||||
# - tokio-stream/Cargo.toml
|
||||
rust_min: 1.49.0
|
||||
rust_min: 1.56.0
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -209,7 +209,6 @@ jobs:
|
||||
working-directory: tokio
|
||||
env:
|
||||
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields
|
||||
PROPTEST_CASES: 10
|
||||
|
||||
asan:
|
||||
name: asan
|
||||
@@ -246,12 +245,7 @@ jobs:
|
||||
tool: cargo-semver-checks
|
||||
- name: Check semver compatibility
|
||||
run: |
|
||||
cargo semver-checks check-release \
|
||||
--exclude benches \
|
||||
--exclude examples \
|
||||
--exclude stress-test \
|
||||
--exclude tests-build \
|
||||
--exclude tests-integration
|
||||
cargo semver-checks check-release --release-type minor
|
||||
|
||||
cross-check:
|
||||
name: cross-check
|
||||
|
||||
+20
-2
@@ -197,8 +197,22 @@ If the change being proposed alters code (as opposed to only documentation for
|
||||
example), it is either adding new functionality to Tokio or it is fixing
|
||||
existing, broken functionality. In both of these cases, the pull request should
|
||||
include one or more tests to ensure that Tokio does not regress in the future.
|
||||
There are two ways to write tests: integration tests and documentation tests
|
||||
(Tokio avoids unit tests as much as possible).
|
||||
There are two ways to write tests: [integration tests][integration-tests]
|
||||
and [documentation tests][documentation-tests].
|
||||
(Tokio avoids [unit tests][unit-tests] as much as possible).
|
||||
|
||||
Tokio uses [conditional compilation attributes][conditional-compilation]
|
||||
throughout the codebase, to modify rustc's behavior. Code marked with such
|
||||
attributes can be enabled using RUSTFLAGS and RUSTDOCFLAGS environment
|
||||
variables. One of the most prevalent flags passed in these variables is
|
||||
the `--cfg` option. To run tests in a particular file, check first what
|
||||
options #![cfg] declaration defines for that file.
|
||||
|
||||
For instance, to run a test marked with the 'tokio_unstable' cfg option,
|
||||
you must pass this flag to the compiler when running the test.
|
||||
```
|
||||
$ RUSTFLAGS="--cfg tokio_unstable" cargo test -p tokio --all-features --test rt_metrics
|
||||
```
|
||||
|
||||
#### Integration tests
|
||||
|
||||
@@ -658,3 +672,7 @@ When releasing a new version of a crate, follow these steps:
|
||||
entry for that release version into your editor and close the window.
|
||||
|
||||
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog/blob/master/CHANGELOG.md
|
||||
[unit-tests]: https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html
|
||||
[integration-tests]: https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html
|
||||
[documentation-tests]: https://doc.rust-lang.org/rust-by-example/testing/doc_testing.html
|
||||
[conditional-compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
|
||||
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.25.0", features = ["full"] }
|
||||
tokio = { version = "1.27.0", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
@@ -187,7 +187,20 @@ When updating this, also update:
|
||||
|
||||
Tokio will keep a rolling MSRV (minimum supported rust version) policy of **at
|
||||
least** 6 months. When increasing the MSRV, the new Rust version must have been
|
||||
released at least six months ago. The current MSRV is 1.49.0.
|
||||
released at least six months ago. The current MSRV is 1.56.0.
|
||||
|
||||
Note that the MSRV is not increased automatically, and only as part of a minor
|
||||
release. The MSRV history for past minor releases can be found below:
|
||||
|
||||
* 1.27 to now - Rust 1.56
|
||||
* 1.17 to 1.26 - Rust 1.49
|
||||
* 1.15 to 1.16 - Rust 1.46
|
||||
* 1.0 to 1.14 - Rust 1.45
|
||||
|
||||
Note that although we try to avoid the situation where a dependency transitively
|
||||
increases the MSRV of Tokio, we do not guarantee that this does not happen.
|
||||
However, every minor release will have some set of versions of dependencies that
|
||||
works with the MSRV of that minor release.
|
||||
|
||||
## Release schedule
|
||||
|
||||
@@ -202,8 +215,9 @@ warrants a patch release with a fix for the bug, it will be backported and
|
||||
released as a new patch release for each LTS minor version. Our current LTS
|
||||
releases are:
|
||||
|
||||
* `1.18.x` - LTS release until June 2023
|
||||
* `1.20.x` - LTS release until September 2023.
|
||||
* `1.18.x` - LTS release until June 2023. (MSRV 1.49)
|
||||
* `1.20.x` - LTS release until September 2023. (MSRV 1.49)
|
||||
* `1.25.x` - LTS release until March 2024. (MSRV 1.49)
|
||||
|
||||
Each LTS release will continue to receive backported fixes for at least a year.
|
||||
If you wish to use a fixed minor release in your project, we recommend that you
|
||||
|
||||
@@ -4,6 +4,9 @@ version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
test-util = ["tokio/test-util"]
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.5.0", path = "../tokio", features = ["full"] }
|
||||
bencher = "0.1.5"
|
||||
@@ -27,6 +30,16 @@ name = "sync_mpsc"
|
||||
path = "sync_mpsc.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "sync_mpsc_oneshot"
|
||||
path = "sync_mpsc_oneshot.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "sync_watch"
|
||||
path = "sync_watch.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "rt_multi_threaded"
|
||||
path = "rt_multi_threaded.rs"
|
||||
@@ -57,3 +70,8 @@ harness = false
|
||||
name = "copy"
|
||||
path = "copy.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "time_now"
|
||||
path = "time_now.rs"
|
||||
harness = false
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
||||
use tokio::{
|
||||
runtime::Runtime,
|
||||
sync::{mpsc, oneshot},
|
||||
};
|
||||
|
||||
fn request_reply_current_thread(b: &mut Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
request_reply(b, rt);
|
||||
}
|
||||
|
||||
fn request_reply_multi_threaded(b: &mut Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(1)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
request_reply(b, rt);
|
||||
}
|
||||
|
||||
fn request_reply(b: &mut Bencher, rt: Runtime) {
|
||||
let tx = rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<oneshot::Sender<()>>(10);
|
||||
tokio::spawn(async move {
|
||||
while let Some(reply) = rx.recv().await {
|
||||
reply.send(()).unwrap();
|
||||
}
|
||||
});
|
||||
tx
|
||||
});
|
||||
|
||||
b.iter(|| {
|
||||
let task_tx = tx.clone();
|
||||
rt.block_on(async move {
|
||||
for _ in 0..1_000 {
|
||||
let (o_tx, o_rx) = oneshot::channel();
|
||||
task_tx.send(o_tx).await.unwrap();
|
||||
let _ = o_rx.await;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
benchmark_group!(
|
||||
sync_mpsc_oneshot_group,
|
||||
request_reply_current_thread,
|
||||
request_reply_multi_threaded,
|
||||
);
|
||||
|
||||
benchmark_main!(sync_mpsc_oneshot_group);
|
||||
@@ -0,0 +1,64 @@
|
||||
use bencher::{black_box, Bencher};
|
||||
use rand::prelude::*;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{watch, Notify};
|
||||
|
||||
fn rt() -> tokio::runtime::Runtime {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(6)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn do_work(rng: &mut impl RngCore) -> u32 {
|
||||
use std::fmt::Write;
|
||||
let mut message = String::new();
|
||||
for i in 1..=10 {
|
||||
let _ = write!(&mut message, " {i}={}", rng.gen::<f64>());
|
||||
}
|
||||
message
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.map(|&c| c as u32)
|
||||
.fold(0, u32::wrapping_add)
|
||||
}
|
||||
|
||||
fn contention_resubscribe(b: &mut Bencher) {
|
||||
const NTASK: u64 = 1000;
|
||||
|
||||
let rt = rt();
|
||||
let (snd, rcv) = watch::channel(0i32);
|
||||
let wg = Arc::new((AtomicU64::new(0), Notify::new()));
|
||||
for n in 0..NTASK {
|
||||
let mut rcv = rcv.clone();
|
||||
let wg = wg.clone();
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(n);
|
||||
rt.spawn(async move {
|
||||
while rcv.changed().await.is_ok() {
|
||||
let _ = *rcv.borrow(); // contend on rwlock
|
||||
let r = do_work(&mut rng);
|
||||
let _ = black_box(r);
|
||||
if wg.0.fetch_sub(1, Ordering::Release) == 1 {
|
||||
wg.1.notify_one();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async {
|
||||
for _ in 0..100 {
|
||||
assert_eq!(wg.0.fetch_add(NTASK, Ordering::Relaxed), 0);
|
||||
let _ = snd.send(black_box(42));
|
||||
while wg.0.load(Ordering::Acquire) > 0 {
|
||||
wg.1.notified().await;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(contention, contention_resubscribe);
|
||||
|
||||
bencher::benchmark_main!(contention);
|
||||
@@ -0,0 +1,25 @@
|
||||
//! Benchmark spawning a task onto the basic and threaded Tokio executors.
|
||||
//! This essentially measure the time to enqueue a task in the local and remote
|
||||
//! case.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bencher;
|
||||
|
||||
use bencher::{black_box, Bencher};
|
||||
|
||||
fn time_now_current_thread(bench: &mut Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_time()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
rt.block_on(async {
|
||||
black_box(tokio::time::Instant::now());
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(time_now, time_now_current_thread,);
|
||||
|
||||
bencher::benchmark_main!(time_now);
|
||||
@@ -18,7 +18,7 @@ use futures::SinkExt;
|
||||
use http::{header::HeaderValue, Request, Response, StatusCode};
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
use std::{env, error::Error, fmt, io};
|
||||
use std::{convert::TryFrom, env, error::Error, fmt, io};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_stream::StreamExt;
|
||||
use tokio_util::codec::{Decoder, Encoder, Framed};
|
||||
@@ -180,8 +180,11 @@ impl Decoder for Http {
|
||||
headers[i] = Some((k, v));
|
||||
}
|
||||
|
||||
let method = http::Method::try_from(r.method.unwrap())
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
(
|
||||
toslice(r.method.unwrap().as_bytes()),
|
||||
method,
|
||||
toslice(r.path.unwrap().as_bytes()),
|
||||
r.version.unwrap(),
|
||||
amt,
|
||||
@@ -195,7 +198,7 @@ impl Decoder for Http {
|
||||
}
|
||||
let data = src.split_to(amt).freeze();
|
||||
let mut ret = Request::builder();
|
||||
ret = ret.method(&data[method.0..method.1]);
|
||||
ret = ret.method(method);
|
||||
let s = data.slice(path.0..path.1);
|
||||
let s = unsafe { String::from_utf8_unchecked(Vec::from(s.as_ref())) };
|
||||
ret = ret.uri(s);
|
||||
|
||||
@@ -36,13 +36,10 @@ async fn test_worker_threads_not_int() {}
|
||||
async fn test_worker_threads_and_current_thread() {}
|
||||
|
||||
#[tokio::test(crate = 456)]
|
||||
async fn test_crate_not_ident_int() {}
|
||||
async fn test_crate_not_path_int() {}
|
||||
|
||||
#[tokio::test(crate = "456")]
|
||||
async fn test_crate_not_ident_invalid() {}
|
||||
|
||||
#[tokio::test(crate = "abc::edf")]
|
||||
async fn test_crate_not_ident_path() {}
|
||||
async fn test_crate_not_path_invalid() {}
|
||||
|
||||
#[tokio::test]
|
||||
#[test]
|
||||
|
||||
@@ -64,34 +64,28 @@ error: The `worker_threads` option requires the `multi_thread` runtime flavor. U
|
||||
35 | #[tokio::test(flavor = "current_thread", worker_threads = 4)]
|
||||
| ^
|
||||
|
||||
error: Failed to parse value of `crate` as ident.
|
||||
error: Failed to parse value of `crate` as path.
|
||||
--> $DIR/macros_invalid_input.rs:38:23
|
||||
|
|
||||
38 | #[tokio::test(crate = 456)]
|
||||
| ^^^
|
||||
|
||||
error: Failed to parse value of `crate` as ident: "456"
|
||||
error: Failed to parse value of `crate` as path: "456"
|
||||
--> $DIR/macros_invalid_input.rs:41:23
|
||||
|
|
||||
41 | #[tokio::test(crate = "456")]
|
||||
| ^^^^^
|
||||
|
||||
error: Failed to parse value of `crate` as ident: "abc::edf"
|
||||
--> $DIR/macros_invalid_input.rs:44:23
|
||||
|
|
||||
44 | #[tokio::test(crate = "abc::edf")]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: second test attribute is supplied
|
||||
--> $DIR/macros_invalid_input.rs:48:1
|
||||
--> $DIR/macros_invalid_input.rs:45:1
|
||||
|
|
||||
48 | #[test]
|
||||
45 | #[test]
|
||||
| ^^^^^^^
|
||||
|
||||
error: duplicated attribute
|
||||
--> $DIR/macros_invalid_input.rs:48:1
|
||||
--> $DIR/macros_invalid_input.rs:45:1
|
||||
|
|
||||
48 | #[test]
|
||||
45 | #[test]
|
||||
| ^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
# 2.0.0 (March 24th, 2023)
|
||||
|
||||
This major release updates the dependency on the syn crate to 2.0.0, and
|
||||
increases the MSRV to 1.56.
|
||||
|
||||
As part of this release, we are adopting a policy of depending on a specific minor
|
||||
release of tokio-macros. This prevents Tokio from being able to pull in many different
|
||||
versions of tokio-macros.
|
||||
|
||||
- macros: update `syn` ([#5572])
|
||||
- macros: accept path as crate rename ([#5557])
|
||||
|
||||
[#5572]: https://github.com/tokio-rs/tokio/pull/5572
|
||||
[#5557]: https://github.com/tokio-rs/tokio/pull/5557
|
||||
|
||||
# 1.8.2 (November 30th, 2022)
|
||||
|
||||
- fix a regression introduced in 1.8.1 ([#5244])
|
||||
|
||||
@@ -4,9 +4,9 @@ name = "tokio-macros"
|
||||
# - Remove path dependencies
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-macros-1.x.y" git tag.
|
||||
version = "1.8.2"
|
||||
version = "2.0.0"
|
||||
edition = "2018"
|
||||
rust-version = "1.49"
|
||||
rust-version = "1.56"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
@@ -24,7 +24,7 @@ proc-macro = true
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.7"
|
||||
quote = "1"
|
||||
syn = { version = "1.0.56", features = ["full"] }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
|
||||
|
||||
+33
-37
@@ -1,10 +1,10 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::parse::Parser;
|
||||
use syn::{parse::Parser, Ident, Path};
|
||||
|
||||
// syn::AttributeArgs does not implement syn::Parse
|
||||
type AttributeArgs = syn::punctuated::Punctuated<syn::NestedMeta, syn::Token![,]>;
|
||||
type AttributeArgs = syn::punctuated::Punctuated<syn::Meta, syn::Token![,]>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum RuntimeFlavor {
|
||||
@@ -29,7 +29,7 @@ struct FinalConfig {
|
||||
flavor: RuntimeFlavor,
|
||||
worker_threads: Option<usize>,
|
||||
start_paused: Option<bool>,
|
||||
crate_name: Option<String>,
|
||||
crate_name: Option<Path>,
|
||||
}
|
||||
|
||||
/// Config used in case of the attribute not being able to build a valid config
|
||||
@@ -47,7 +47,7 @@ struct Configuration {
|
||||
worker_threads: Option<(usize, Span)>,
|
||||
start_paused: Option<(bool, Span)>,
|
||||
is_test: bool,
|
||||
crate_name: Option<String>,
|
||||
crate_name: Option<Path>,
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
@@ -112,8 +112,8 @@ impl Configuration {
|
||||
if self.crate_name.is_some() {
|
||||
return Err(syn::Error::new(span, "`crate` set multiple times."));
|
||||
}
|
||||
let name_ident = parse_ident(name, span, "crate")?;
|
||||
self.crate_name = Some(name_ident.to_string());
|
||||
let name_path = parse_path(name, span, "crate")?;
|
||||
self.crate_name = Some(name_path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -199,23 +199,22 @@ fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result<String, syn::E
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_ident(lit: syn::Lit, span: Span, field: &str) -> Result<Ident, syn::Error> {
|
||||
fn parse_path(lit: syn::Lit, span: Span, field: &str) -> Result<Path, syn::Error> {
|
||||
match lit {
|
||||
syn::Lit::Str(s) => {
|
||||
let err = syn::Error::new(
|
||||
span,
|
||||
format!(
|
||||
"Failed to parse value of `{}` as ident: \"{}\"",
|
||||
"Failed to parse value of `{}` as path: \"{}\"",
|
||||
field,
|
||||
s.value()
|
||||
),
|
||||
);
|
||||
let path = s.parse::<syn::Path>().map_err(|_| err.clone())?;
|
||||
path.get_ident().cloned().ok_or(err)
|
||||
s.parse::<syn::Path>().map_err(|_| err.clone())
|
||||
}
|
||||
_ => Err(syn::Error::new(
|
||||
span,
|
||||
format!("Failed to parse value of `{}` as ident.", field),
|
||||
format!("Failed to parse value of `{}` as path.", field),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -246,7 +245,7 @@ fn build_config(
|
||||
|
||||
for arg in args {
|
||||
match arg {
|
||||
syn::NestedMeta::Meta(syn::Meta::NameValue(namevalue)) => {
|
||||
syn::Meta::NameValue(namevalue) => {
|
||||
let ident = namevalue
|
||||
.path
|
||||
.get_ident()
|
||||
@@ -255,34 +254,26 @@ fn build_config(
|
||||
})?
|
||||
.to_string()
|
||||
.to_lowercase();
|
||||
let lit = match &namevalue.value {
|
||||
syn::Expr::Lit(syn::ExprLit { lit, .. }) => lit,
|
||||
expr => return Err(syn::Error::new_spanned(expr, "Must be a literal")),
|
||||
};
|
||||
match ident.as_str() {
|
||||
"worker_threads" => {
|
||||
config.set_worker_threads(
|
||||
namevalue.lit.clone(),
|
||||
syn::spanned::Spanned::span(&namevalue.lit),
|
||||
)?;
|
||||
config.set_worker_threads(lit.clone(), syn::spanned::Spanned::span(lit))?;
|
||||
}
|
||||
"flavor" => {
|
||||
config.set_flavor(
|
||||
namevalue.lit.clone(),
|
||||
syn::spanned::Spanned::span(&namevalue.lit),
|
||||
)?;
|
||||
config.set_flavor(lit.clone(), syn::spanned::Spanned::span(lit))?;
|
||||
}
|
||||
"start_paused" => {
|
||||
config.set_start_paused(
|
||||
namevalue.lit.clone(),
|
||||
syn::spanned::Spanned::span(&namevalue.lit),
|
||||
)?;
|
||||
config.set_start_paused(lit.clone(), syn::spanned::Spanned::span(lit))?;
|
||||
}
|
||||
"core_threads" => {
|
||||
let msg = "Attribute `core_threads` is renamed to `worker_threads`";
|
||||
return Err(syn::Error::new_spanned(namevalue, msg));
|
||||
}
|
||||
"crate" => {
|
||||
config.set_crate_name(
|
||||
namevalue.lit.clone(),
|
||||
syn::spanned::Spanned::span(&namevalue.lit),
|
||||
)?;
|
||||
config.set_crate_name(lit.clone(), syn::spanned::Spanned::span(lit))?;
|
||||
}
|
||||
name => {
|
||||
let msg = format!(
|
||||
@@ -293,7 +284,7 @@ fn build_config(
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
|
||||
syn::Meta::Path(path) => {
|
||||
let name = path
|
||||
.get_ident()
|
||||
.ok_or_else(|| syn::Error::new_spanned(&path, "Must have specified ident"))?
|
||||
@@ -354,16 +345,17 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
|
||||
(start, end)
|
||||
};
|
||||
|
||||
let crate_name = config.crate_name.as_deref().unwrap_or("tokio");
|
||||
|
||||
let crate_ident = Ident::new(crate_name, last_stmt_start_span);
|
||||
let crate_path = config
|
||||
.crate_name
|
||||
.map(ToTokens::into_token_stream)
|
||||
.unwrap_or_else(|| Ident::new("tokio", last_stmt_start_span).into_token_stream());
|
||||
|
||||
let mut rt = match config.flavor {
|
||||
RuntimeFlavor::CurrentThread => quote_spanned! {last_stmt_start_span=>
|
||||
#crate_ident::runtime::Builder::new_current_thread()
|
||||
#crate_path::runtime::Builder::new_current_thread()
|
||||
},
|
||||
RuntimeFlavor::Threaded => quote_spanned! {last_stmt_start_span=>
|
||||
#crate_ident::runtime::Builder::new_multi_thread()
|
||||
#crate_path::runtime::Builder::new_multi_thread()
|
||||
},
|
||||
};
|
||||
if let Some(v) = config.worker_threads {
|
||||
@@ -414,7 +406,7 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
|
||||
};
|
||||
quote! {
|
||||
let body = async #body;
|
||||
#crate_ident::pin!(body);
|
||||
#crate_path::pin!(body);
|
||||
let body: ::std::pin::Pin<&mut dyn ::std::future::Future<Output = #output_type>> = body;
|
||||
}
|
||||
} else {
|
||||
@@ -478,7 +470,11 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
|
||||
Ok(it) => it,
|
||||
Err(e) => return token_stream_with_error(item, e),
|
||||
};
|
||||
let config = if let Some(attr) = input.attrs.iter().find(|attr| attr.path.is_ident("test")) {
|
||||
let config = if let Some(attr) = input
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|attr| attr.meta.path().is_ident("test"))
|
||||
{
|
||||
let msg = "second test attribute is supplied";
|
||||
Err(syn::Error::new_spanned(attr, msg))
|
||||
} else {
|
||||
|
||||
@@ -39,6 +39,13 @@ use proc_macro::TokenStream;
|
||||
/// function is called often, it is preferable to create the runtime using the
|
||||
/// runtime builder so the runtime can be reused across calls.
|
||||
///
|
||||
/// # Non-worker async function
|
||||
///
|
||||
/// Note that the async function marked with this macro does not run as a
|
||||
/// worker. The expectation is that other tasks are spawned by the function here.
|
||||
/// Awaiting on other futures from the function provided here will not
|
||||
/// perform as fast as those spawned as workers.
|
||||
///
|
||||
/// # Multi-threaded runtime
|
||||
///
|
||||
/// To use the multi-threaded runtime, the macro can be configured using
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use proc_macro::{TokenStream, TokenTree};
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
use syn::{parse::Parser, Ident};
|
||||
|
||||
pub(crate) fn declare_output_enum(input: TokenStream) -> TokenStream {
|
||||
// passed in is: `(_ _ _)` with one `_` per branch
|
||||
@@ -46,7 +46,7 @@ pub(crate) fn clean_pattern_macro(input: TokenStream) -> TokenStream {
|
||||
// If this isn't a pattern, we return the token stream as-is. The select!
|
||||
// macro is using it in a location requiring a pattern, so an error will be
|
||||
// emitted there.
|
||||
let mut input: syn::Pat = match syn::parse(input.clone()) {
|
||||
let mut input: syn::Pat = match syn::Pat::parse_single.parse(input.clone()) {
|
||||
Ok(it) => it,
|
||||
Err(_) => return input,
|
||||
};
|
||||
@@ -58,7 +58,6 @@ pub(crate) fn clean_pattern_macro(input: TokenStream) -> TokenStream {
|
||||
// Removes any occurrences of ref or mut in the provided pattern.
|
||||
fn clean_pattern(pat: &mut syn::Pat) {
|
||||
match pat {
|
||||
syn::Pat::Box(_box) => {}
|
||||
syn::Pat::Lit(_literal) => {}
|
||||
syn::Pat::Macro(_macro) => {}
|
||||
syn::Pat::Path(_path) => {}
|
||||
@@ -94,7 +93,7 @@ fn clean_pattern(pat: &mut syn::Pat) {
|
||||
}
|
||||
}
|
||||
syn::Pat::TupleStruct(tuple) => {
|
||||
for elem in tuple.pat.elems.iter_mut() {
|
||||
for elem in tuple.elems.iter_mut() {
|
||||
clean_pattern(elem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# 0.1.12 (January 20, 2023)
|
||||
|
||||
- time: remove `Unpin` bound on `Throttle` methods ([#5105])
|
||||
- time: document that `throttle` operates on ms granularity ([#5101])
|
||||
- sync: add `WatchStream::from_changes` ([#5432])
|
||||
|
||||
[#5105]: https://github.com/tokio-rs/tokio/pull/5105
|
||||
[#5101]: https://github.com/tokio-rs/tokio/pull/5101
|
||||
[#5432]: https://github.com/tokio-rs/tokio/pull/5432
|
||||
|
||||
# 0.1.11 (October 11, 2022)
|
||||
|
||||
- time: allow `StreamExt::chunks_timeout` outside of a runtime ([#5036])
|
||||
|
||||
@@ -4,9 +4,9 @@ name = "tokio-stream"
|
||||
# - Remove path dependencies
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-stream-0.1.x" git tag.
|
||||
version = "0.1.11"
|
||||
edition = "2018"
|
||||
rust-version = "1.49"
|
||||
version = "0.1.12"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
|
||||
@@ -568,7 +568,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> std::iter::FromIterator<(K, V)> for StreamMap<K, V>
|
||||
impl<K, V> FromIterator<(K, V)> for StreamMap<K, V>
|
||||
where
|
||||
K: Hash + Eq,
|
||||
{
|
||||
|
||||
@@ -10,8 +10,9 @@ use tokio::sync::watch::error::RecvError;
|
||||
|
||||
/// A wrapper around [`tokio::sync::watch::Receiver`] that implements [`Stream`].
|
||||
///
|
||||
/// This stream will always start by yielding the current value when the WatchStream is polled,
|
||||
/// regardless of whether it was the initial value or sent afterwards.
|
||||
/// This stream will start by yielding the current value when the WatchStream is polled,
|
||||
/// regardless of whether it was the initial value or sent afterwards,
|
||||
/// unless you use [`WatchStream<T>::from_changes`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -40,6 +41,28 @@ use tokio::sync::watch::error::RecvError;
|
||||
/// let (tx, rx) = watch::channel("hello");
|
||||
/// let mut rx = WatchStream::new(rx);
|
||||
///
|
||||
/// // existing rx output with "hello" is ignored here
|
||||
///
|
||||
/// tx.send("goodbye").unwrap();
|
||||
/// assert_eq!(rx.next().await, Some("goodbye"));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Example with [`WatchStream<T>::from_changes`]:
|
||||
///
|
||||
/// ```
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// use futures::future::FutureExt;
|
||||
/// use tokio::sync::watch;
|
||||
/// use tokio_stream::{StreamExt, wrappers::WatchStream};
|
||||
///
|
||||
/// let (tx, rx) = watch::channel("hello");
|
||||
/// let mut rx = WatchStream::from_changes(rx);
|
||||
///
|
||||
/// // no output from rx is available at this point - let's check this:
|
||||
/// assert!(rx.next().now_or_never().is_none());
|
||||
///
|
||||
/// tx.send("goodbye").unwrap();
|
||||
/// assert_eq!(rx.next().await, Some("goodbye"));
|
||||
/// # }
|
||||
@@ -66,6 +89,13 @@ impl<T: 'static + Clone + Send + Sync> WatchStream<T> {
|
||||
inner: ReusableBoxFuture::new(async move { (Ok(()), rx) }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `WatchStream` that waits for the value to be changed.
|
||||
pub fn from_changes(rx: Receiver<T>) -> Self {
|
||||
Self {
|
||||
inner: ReusableBoxFuture::new(make_future(rx)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + 'static + Send + Sync> Stream for WatchStream<T> {
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
use tokio::sync::watch;
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tokio_stream::StreamExt;
|
||||
use tokio_test::assert_pending;
|
||||
use tokio_test::task::spawn;
|
||||
|
||||
#[tokio::test]
|
||||
async fn message_not_twice() {
|
||||
async fn watch_stream_message_not_twice() {
|
||||
let (tx, rx) = watch::channel("hello");
|
||||
|
||||
let mut counter = 0;
|
||||
@@ -27,3 +29,29 @@ async fn message_not_twice() {
|
||||
drop(tx);
|
||||
task.await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn watch_stream_from_rx() {
|
||||
let (tx, rx) = watch::channel("hello");
|
||||
|
||||
let mut stream = WatchStream::from(rx);
|
||||
|
||||
assert_eq!(stream.next().await.unwrap(), "hello");
|
||||
|
||||
tx.send("bye").unwrap();
|
||||
|
||||
assert_eq!(stream.next().await.unwrap(), "bye");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn watch_stream_from_changes() {
|
||||
let (tx, rx) = watch::channel("hello");
|
||||
|
||||
let mut stream = WatchStream::from_changes(rx);
|
||||
|
||||
assert_pending!(spawn(&mut stream).poll_next());
|
||||
|
||||
tx.send("bye").unwrap();
|
||||
|
||||
assert_eq!(stream.next().await.unwrap(), "bye");
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ name = "tokio-test"
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-test-0.4.x" git tag.
|
||||
version = "0.4.2"
|
||||
edition = "2018"
|
||||
rust-version = "1.49"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
|
||||
@@ -310,6 +310,8 @@ impl Inner {
|
||||
|
||||
if now < until {
|
||||
break;
|
||||
} else {
|
||||
self.waiting = None;
|
||||
}
|
||||
} else {
|
||||
self.waiting = Some(Instant::now() + *dur);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use std::io;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::time::{Duration, Instant};
|
||||
use tokio_test::io::Builder;
|
||||
|
||||
#[tokio::test]
|
||||
@@ -84,3 +85,65 @@ async fn mock_panics_write_data_left() {
|
||||
use tokio_test::io::Builder;
|
||||
Builder::new().write(b"write").build();
|
||||
}
|
||||
|
||||
#[tokio::test(start_paused = true)]
|
||||
async fn wait() {
|
||||
const FIRST_WAIT: Duration = Duration::from_secs(1);
|
||||
|
||||
let mut mock = Builder::new()
|
||||
.wait(FIRST_WAIT)
|
||||
.read(b"hello ")
|
||||
.read(b"world!")
|
||||
.build();
|
||||
|
||||
let mut buf = [0; 256];
|
||||
|
||||
let start = Instant::now(); // record the time the read call takes
|
||||
//
|
||||
let n = mock.read(&mut buf).await.expect("read 1");
|
||||
assert_eq!(&buf[..n], b"hello ");
|
||||
println!("time elapsed after first read {:?}", start.elapsed());
|
||||
|
||||
let n = mock.read(&mut buf).await.expect("read 2");
|
||||
assert_eq!(&buf[..n], b"world!");
|
||||
println!("time elapsed after second read {:?}", start.elapsed());
|
||||
|
||||
// make sure the .wait() instruction worked
|
||||
assert!(
|
||||
start.elapsed() >= FIRST_WAIT,
|
||||
"consuming the whole mock only took {}ms",
|
||||
start.elapsed().as_millis()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(start_paused = true)]
|
||||
async fn multiple_wait() {
|
||||
const FIRST_WAIT: Duration = Duration::from_secs(1);
|
||||
const SECOND_WAIT: Duration = Duration::from_secs(1);
|
||||
|
||||
let mut mock = Builder::new()
|
||||
.wait(FIRST_WAIT)
|
||||
.read(b"hello ")
|
||||
.wait(SECOND_WAIT)
|
||||
.read(b"world!")
|
||||
.build();
|
||||
|
||||
let mut buf = [0; 256];
|
||||
|
||||
let start = Instant::now(); // record the time it takes to consume the mock
|
||||
|
||||
let n = mock.read(&mut buf).await.expect("read 1");
|
||||
assert_eq!(&buf[..n], b"hello ");
|
||||
println!("time elapsed after first read {:?}", start.elapsed());
|
||||
|
||||
let n = mock.read(&mut buf).await.expect("read 2");
|
||||
assert_eq!(&buf[..n], b"world!");
|
||||
println!("time elapsed after second read {:?}", start.elapsed());
|
||||
|
||||
// make sure the .wait() instruction worked
|
||||
assert!(
|
||||
start.elapsed() >= FIRST_WAIT + SECOND_WAIT,
|
||||
"consuming the whole mock only took {}ms",
|
||||
start.elapsed().as_millis()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ name = "tokio-util"
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-util-0.7.x" git tag.
|
||||
version = "0.7.7"
|
||||
edition = "2018"
|
||||
rust-version = "1.49"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
|
||||
@@ -151,47 +151,43 @@ fn with_locked_node_and_parent<F, Ret>(node: &Arc<TreeNode>, func: F) -> Ret
|
||||
where
|
||||
F: FnOnce(MutexGuard<'_, Inner>, Option<MutexGuard<'_, Inner>>) -> Ret,
|
||||
{
|
||||
let mut potential_parent = {
|
||||
let locked_node = node.inner.lock().unwrap();
|
||||
match locked_node.parent.clone() {
|
||||
Some(parent) => parent,
|
||||
// If we locked the node and its parent is `None`, we are in a valid state
|
||||
// and can return.
|
||||
None => return func(locked_node, None),
|
||||
}
|
||||
};
|
||||
use std::sync::TryLockError;
|
||||
|
||||
let mut locked_node = node.inner.lock().unwrap();
|
||||
|
||||
// Every time this fails, the number of ancestors of the node decreases,
|
||||
// so the loop must succeed after a finite number of iterations.
|
||||
loop {
|
||||
// Deadlock safety:
|
||||
//
|
||||
// Due to invariant #2, we know that we have to lock the parent first, and then the child.
|
||||
// This is true even if the potential_parent is no longer the current parent or even its
|
||||
// sibling, as the invariant still holds.
|
||||
let locked_parent = potential_parent.inner.lock().unwrap();
|
||||
let locked_node = node.inner.lock().unwrap();
|
||||
|
||||
let actual_parent = match locked_node.parent.clone() {
|
||||
Some(parent) => parent,
|
||||
// If we locked the node and its parent is `None`, we are in a valid state
|
||||
// and can return.
|
||||
None => {
|
||||
// Was the wrong parent, so unlock it before calling `func`
|
||||
drop(locked_parent);
|
||||
return func(locked_node, None);
|
||||
}
|
||||
// Look up the parent of the currently locked node.
|
||||
let potential_parent = match locked_node.parent.as_ref() {
|
||||
Some(potential_parent) => potential_parent.clone(),
|
||||
None => return func(locked_node, None),
|
||||
};
|
||||
|
||||
// Loop until we managed to lock both the node and its parent
|
||||
if Arc::ptr_eq(&actual_parent, &potential_parent) {
|
||||
return func(locked_node, Some(locked_parent));
|
||||
// Lock the parent. This may require unlocking the child first.
|
||||
let locked_parent = match potential_parent.inner.try_lock() {
|
||||
Ok(locked_parent) => locked_parent,
|
||||
Err(TryLockError::WouldBlock) => {
|
||||
drop(locked_node);
|
||||
// Deadlock safety:
|
||||
//
|
||||
// Due to invariant #2, the potential parent must come before
|
||||
// the child in the creation order. Therefore, we can safely
|
||||
// lock the child while holding the parent lock.
|
||||
let locked_parent = potential_parent.inner.lock().unwrap();
|
||||
locked_node = node.inner.lock().unwrap();
|
||||
locked_parent
|
||||
}
|
||||
Err(TryLockError::Poisoned(err)) => Err(err).unwrap(),
|
||||
};
|
||||
|
||||
// If we unlocked the child, then the parent may have changed. Check
|
||||
// that we still have the right parent.
|
||||
if let Some(actual_parent) = locked_node.parent.as_ref() {
|
||||
if Arc::ptr_eq(actual_parent, &potential_parent) {
|
||||
return func(locked_node, Some(locked_parent));
|
||||
}
|
||||
}
|
||||
|
||||
// Drop locked_parent before reassigning to potential_parent,
|
||||
// as potential_parent is borrowed in it
|
||||
drop(locked_node);
|
||||
drop(locked_parent);
|
||||
|
||||
potential_parent = actual_parent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,11 +239,7 @@ fn remove_child(parent: &mut Inner, mut node: MutexGuard<'_, Inner>) {
|
||||
|
||||
let len = parent.children.len();
|
||||
if 4 * len <= parent.children.capacity() {
|
||||
// equal to:
|
||||
// parent.children.shrink_to(2 * len);
|
||||
// but shrink_to was not yet stabilized in our minimal compatible version
|
||||
let old_children = std::mem::replace(&mut parent.children, Vec::with_capacity(2 * len));
|
||||
parent.children.extend(old_children);
|
||||
parent.children.shrink_to(2 * len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -257,6 +257,10 @@ async fn reset_twice() {
|
||||
#[tokio::test]
|
||||
async fn repeatedly_reset_entry_inserted_as_expired() {
|
||||
time::pause();
|
||||
|
||||
// Instants before the start of the test seem to break in wasm.
|
||||
time::sleep(ms(1000)).await;
|
||||
|
||||
let mut queue = task::spawn(DelayQueue::new());
|
||||
let now = Instant::now();
|
||||
|
||||
@@ -556,6 +560,10 @@ async fn reset_later_after_slot_starts() {
|
||||
#[tokio::test]
|
||||
async fn reset_inserted_expired() {
|
||||
time::pause();
|
||||
|
||||
// Instants before the start of the test seem to break in wasm.
|
||||
time::sleep(ms(1000)).await;
|
||||
|
||||
let mut queue = task::spawn(DelayQueue::new());
|
||||
let now = Instant::now();
|
||||
|
||||
|
||||
@@ -1,3 +1,135 @@
|
||||
# 1.27.0 (March 27th, 2023)
|
||||
|
||||
This release bumps the MSRV of Tokio to 1.56. ([#5559])
|
||||
|
||||
### Added
|
||||
|
||||
- io: add `async_io` helper method to sockets ([#5512])
|
||||
- io: add implementations of `AsFd`/`AsHandle`/`AsSocket` ([#5514], [#5540])
|
||||
- net: add `UdpSocket::peek_sender()` ([#5520])
|
||||
- sync: add `RwLockWriteGuard::{downgrade_map, try_downgrade_map}` ([#5527])
|
||||
- task: add `JoinHandle::abort_handle` ([#5543])
|
||||
|
||||
### Changed
|
||||
|
||||
- io: use `memchr` from `libc` ([#5558])
|
||||
- macros: accept path as crate rename in `#[tokio::main]` ([#5557])
|
||||
- macros: update to syn 2.0.0 ([#5572])
|
||||
- time: don't register for a wakeup when `Interval` returns `Ready` ([#5553])
|
||||
|
||||
### Fixed
|
||||
|
||||
- fs: fuse std iterator in `ReadDir` ([#5555])
|
||||
- tracing: fix `spawn_blocking` location fields ([#5573])
|
||||
- time: clean up redundant check in `Wheel::poll()` ([#5574])
|
||||
|
||||
### Documented
|
||||
|
||||
- macros: define cancellation safety ([#5525])
|
||||
- io: add details to docs of `tokio::io::copy[_buf]` ([#5575])
|
||||
- io: refer to `ReaderStream` and `StreamReader` in module docs ([#5576])
|
||||
|
||||
[#5512]: https://github.com/tokio-rs/tokio/pull/5512
|
||||
[#5514]: https://github.com/tokio-rs/tokio/pull/5514
|
||||
[#5520]: https://github.com/tokio-rs/tokio/pull/5520
|
||||
[#5525]: https://github.com/tokio-rs/tokio/pull/5525
|
||||
[#5527]: https://github.com/tokio-rs/tokio/pull/5527
|
||||
[#5540]: https://github.com/tokio-rs/tokio/pull/5540
|
||||
[#5543]: https://github.com/tokio-rs/tokio/pull/5543
|
||||
[#5553]: https://github.com/tokio-rs/tokio/pull/5553
|
||||
[#5555]: https://github.com/tokio-rs/tokio/pull/5555
|
||||
[#5557]: https://github.com/tokio-rs/tokio/pull/5557
|
||||
[#5558]: https://github.com/tokio-rs/tokio/pull/5558
|
||||
[#5559]: https://github.com/tokio-rs/tokio/pull/5559
|
||||
[#5572]: https://github.com/tokio-rs/tokio/pull/5572
|
||||
[#5573]: https://github.com/tokio-rs/tokio/pull/5573
|
||||
[#5574]: https://github.com/tokio-rs/tokio/pull/5574
|
||||
[#5575]: https://github.com/tokio-rs/tokio/pull/5575
|
||||
[#5576]: https://github.com/tokio-rs/tokio/pull/5576
|
||||
|
||||
# 1.26.0 (March 1st, 2023)
|
||||
|
||||
### Fixed
|
||||
|
||||
- macros: fix empty `join!` and `try_join!` ([#5504])
|
||||
- sync: don't leak tracing spans in mutex guards ([#5469])
|
||||
- sync: drop wakers after unlocking the mutex in Notify ([#5471])
|
||||
- sync: drop wakers outside lock in semaphore ([#5475])
|
||||
|
||||
### Added
|
||||
|
||||
- fs: add `fs::try_exists` ([#4299])
|
||||
- net: add types for named unix pipes ([#5351])
|
||||
- sync: add `MappedOwnedMutexGuard` ([#5474])
|
||||
|
||||
### Changed
|
||||
|
||||
- chore: update windows-sys to 0.45 ([#5386])
|
||||
- net: use Message Read Mode for named pipes ([#5350])
|
||||
- sync: mark lock guards with `#[clippy::has_significant_drop]` ([#5422])
|
||||
- sync: reduce contention in watch channel ([#5464])
|
||||
- time: remove cache padding in timer entries ([#5468])
|
||||
- time: Improve `Instant::now()` perf with test-util ([#5513])
|
||||
|
||||
### Internal Changes
|
||||
|
||||
- io: use `poll_fn` in `copy_bidirectional` ([#5486])
|
||||
- net: refactor named pipe builders to not use bitfields ([#5477])
|
||||
- rt: remove Arc from Clock ([#5434])
|
||||
- sync: make `notify_waiters` calls atomic ([#5458])
|
||||
- time: don't store deadline twice in sleep entries ([#5410])
|
||||
|
||||
### Unstable
|
||||
|
||||
- metrics: add a new metric for budget exhaustion yields ([#5517])
|
||||
|
||||
### Documented
|
||||
|
||||
- io: improve AsyncFd example ([#5481])
|
||||
- runtime: document the nature of the main future ([#5494])
|
||||
- runtime: remove extra period in docs ([#5511])
|
||||
- signal: updated Documentation for Signals ([#5459])
|
||||
- sync: add doc aliases for `blocking_*` methods ([#5448])
|
||||
- sync: fix docs for Send/Sync bounds in broadcast ([#5480])
|
||||
- sync: document drop behavior for channels ([#5497])
|
||||
- task: clarify what happens to spawned work during runtime shutdown ([#5394])
|
||||
- task: clarify `process::Command` docs ([#5413])
|
||||
- task: fix wording with 'unsend' ([#5452])
|
||||
- time: document immediate completion guarantee for timeouts ([#5509])
|
||||
- tokio: document supported platforms ([#5483])
|
||||
|
||||
[#4299]: https://github.com/tokio-rs/tokio/pull/4299
|
||||
[#5350]: https://github.com/tokio-rs/tokio/pull/5350
|
||||
[#5351]: https://github.com/tokio-rs/tokio/pull/5351
|
||||
[#5386]: https://github.com/tokio-rs/tokio/pull/5386
|
||||
[#5394]: https://github.com/tokio-rs/tokio/pull/5394
|
||||
[#5410]: https://github.com/tokio-rs/tokio/pull/5410
|
||||
[#5413]: https://github.com/tokio-rs/tokio/pull/5413
|
||||
[#5422]: https://github.com/tokio-rs/tokio/pull/5422
|
||||
[#5434]: https://github.com/tokio-rs/tokio/pull/5434
|
||||
[#5448]: https://github.com/tokio-rs/tokio/pull/5448
|
||||
[#5452]: https://github.com/tokio-rs/tokio/pull/5452
|
||||
[#5458]: https://github.com/tokio-rs/tokio/pull/5458
|
||||
[#5459]: https://github.com/tokio-rs/tokio/pull/5459
|
||||
[#5464]: https://github.com/tokio-rs/tokio/pull/5464
|
||||
[#5468]: https://github.com/tokio-rs/tokio/pull/5468
|
||||
[#5469]: https://github.com/tokio-rs/tokio/pull/5469
|
||||
[#5471]: https://github.com/tokio-rs/tokio/pull/5471
|
||||
[#5474]: https://github.com/tokio-rs/tokio/pull/5474
|
||||
[#5475]: https://github.com/tokio-rs/tokio/pull/5475
|
||||
[#5477]: https://github.com/tokio-rs/tokio/pull/5477
|
||||
[#5480]: https://github.com/tokio-rs/tokio/pull/5480
|
||||
[#5481]: https://github.com/tokio-rs/tokio/pull/5481
|
||||
[#5483]: https://github.com/tokio-rs/tokio/pull/5483
|
||||
[#5486]: https://github.com/tokio-rs/tokio/pull/5486
|
||||
[#5494]: https://github.com/tokio-rs/tokio/pull/5494
|
||||
[#5497]: https://github.com/tokio-rs/tokio/pull/5497
|
||||
[#5504]: https://github.com/tokio-rs/tokio/pull/5504
|
||||
[#5509]: https://github.com/tokio-rs/tokio/pull/5509
|
||||
[#5511]: https://github.com/tokio-rs/tokio/pull/5511
|
||||
[#5513]: https://github.com/tokio-rs/tokio/pull/5513
|
||||
[#5517]: https://github.com/tokio-rs/tokio/pull/5517
|
||||
|
||||
# 1.25.0 (January 28, 2023)
|
||||
|
||||
### Fixed
|
||||
|
||||
+8
-9
@@ -6,9 +6,9 @@ name = "tokio"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v1.x.y" git tag.
|
||||
version = "1.25.0"
|
||||
edition = "2018"
|
||||
rust-version = "1.49"
|
||||
version = "1.27.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
@@ -42,7 +42,7 @@ full = [
|
||||
]
|
||||
|
||||
fs = []
|
||||
io-util = ["memchr", "bytes"]
|
||||
io-util = ["bytes"]
|
||||
# stdin, stdout, stderr
|
||||
io-std = []
|
||||
macros = ["tokio-macros"]
|
||||
@@ -97,19 +97,18 @@ stats = []
|
||||
autocfg = "1.1"
|
||||
|
||||
[dependencies]
|
||||
tokio-macros = { version = "1.7.0", path = "../tokio-macros", optional = true }
|
||||
tokio-macros = { version = "~2.0.0", path = "../tokio-macros", optional = true }
|
||||
|
||||
pin-project-lite = "0.2.0"
|
||||
|
||||
# Everything else is optional...
|
||||
bytes = { version = "1.0.0", optional = true }
|
||||
memchr = { version = "2.2", optional = true }
|
||||
mio = { version = "0.8.4", optional = true }
|
||||
num_cpus = { version = "1.8.0", optional = true }
|
||||
parking_lot = { version = "0.12.0", optional = true }
|
||||
|
||||
[target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies]
|
||||
socket2 = { version = "0.4.4", optional = true, features = [ "all" ] }
|
||||
socket2 = { version = "0.4.9", optional = true, features = [ "all" ] }
|
||||
|
||||
# Currently unstable. The API exposed by these features may be broken at any time.
|
||||
# Requires `--cfg tokio_unstable` to enable.
|
||||
@@ -143,11 +142,11 @@ tokio-test = { version = "0.4.0", path = "../tokio-test" }
|
||||
tokio-stream = { version = "0.1", path = "../tokio-stream" }
|
||||
futures = { version = "0.3.0", features = ["async-await"] }
|
||||
mockall = "0.11.1"
|
||||
tempfile = "3.1.0"
|
||||
async-stream = "0.3"
|
||||
|
||||
[target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dev-dependencies]
|
||||
socket2 = "0.4"
|
||||
socket2 = "0.4.9"
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[target.'cfg(not(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown")))'.dev-dependencies]
|
||||
rand = "0.8.0"
|
||||
|
||||
+18
-4
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.25.0", features = ["full"] }
|
||||
tokio = { version = "1.27.0", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
@@ -187,7 +187,20 @@ When updating this, also update:
|
||||
|
||||
Tokio will keep a rolling MSRV (minimum supported rust version) policy of **at
|
||||
least** 6 months. When increasing the MSRV, the new Rust version must have been
|
||||
released at least six months ago. The current MSRV is 1.49.0.
|
||||
released at least six months ago. The current MSRV is 1.56.0.
|
||||
|
||||
Note that the MSRV is not increased automatically, and only as part of a minor
|
||||
release. The MSRV history for past minor releases can be found below:
|
||||
|
||||
* 1.27 to now - Rust 1.56
|
||||
* 1.17 to 1.26 - Rust 1.49
|
||||
* 1.15 to 1.16 - Rust 1.46
|
||||
* 1.0 to 1.14 - Rust 1.45
|
||||
|
||||
Note that although we try to avoid the situation where a dependency transitively
|
||||
increases the MSRV of Tokio, we do not guarantee that this does not happen.
|
||||
However, every minor release will have some set of versions of dependencies that
|
||||
works with the MSRV of that minor release.
|
||||
|
||||
## Release schedule
|
||||
|
||||
@@ -202,8 +215,9 @@ warrants a patch release with a fix for the bug, it will be backported and
|
||||
released as a new patch release for each LTS minor version. Our current LTS
|
||||
releases are:
|
||||
|
||||
* `1.18.x` - LTS release until June 2023
|
||||
* `1.20.x` - LTS release until September 2023.
|
||||
* `1.18.x` - LTS release until June 2023. (MSRV 1.49)
|
||||
* `1.20.x` - LTS release until September 2023. (MSRV 1.49)
|
||||
* `1.25.x` - LTS release until March 2024. (MSRV 1.49)
|
||||
|
||||
Each LTS release will continue to receive backported fixes for at least a year.
|
||||
If you wish to use a fixed minor release in your project, we recommend that you
|
||||
|
||||
+37
-31
@@ -10,13 +10,6 @@ const CONST_THREAD_LOCAL_PROBE: &str = r#"
|
||||
}
|
||||
"#;
|
||||
|
||||
const ADDR_OF_PROBE: &str = r#"
|
||||
{
|
||||
let my_var = 10;
|
||||
::std::ptr::addr_of!(my_var)
|
||||
}
|
||||
"#;
|
||||
|
||||
const CONST_MUTEX_NEW_PROBE: &str = r#"
|
||||
{
|
||||
static MY_MUTEX: ::std::sync::Mutex<i32> = ::std::sync::Mutex::new(1);
|
||||
@@ -24,6 +17,19 @@ const CONST_MUTEX_NEW_PROBE: &str = r#"
|
||||
}
|
||||
"#;
|
||||
|
||||
const AS_FD_PROBE: &str = r#"
|
||||
{
|
||||
#![allow(unused_imports)]
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::prelude::AsFd as _;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::prelude::AsSocket as _;
|
||||
#[cfg(target = "wasm32-wasi")]
|
||||
use std::os::wasi::prelude::AsFd as _;
|
||||
}
|
||||
"#;
|
||||
|
||||
const TARGET_HAS_ATOMIC_PROBE: &str = r#"
|
||||
{
|
||||
#[cfg(target_has_atomic = "ptr")]
|
||||
@@ -40,9 +46,9 @@ const TARGET_ATOMIC_U64_PROBE: &str = r#"
|
||||
|
||||
fn main() {
|
||||
let mut enable_const_thread_local = false;
|
||||
let mut enable_addr_of = false;
|
||||
let mut enable_target_has_atomic = false;
|
||||
let mut enable_const_mutex_new = false;
|
||||
let mut enable_as_fd = false;
|
||||
let mut target_needs_atomic_u64_fallback = false;
|
||||
|
||||
match AutoCfg::new() {
|
||||
@@ -67,21 +73,6 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// The `addr_of` and `addr_of_mut` macros were stabilized in 1.51.
|
||||
if ac.probe_rustc_version(1, 52) {
|
||||
enable_addr_of = true;
|
||||
} else if ac.probe_rustc_version(1, 51) {
|
||||
// This compiler claims to be 1.51, but there are some nightly
|
||||
// compilers that claim to be 1.51 without supporting the
|
||||
// feature. Explicitly probe to check if code using them
|
||||
// compiles.
|
||||
//
|
||||
// The oldest nightly that supports the feature is 2021-01-31.
|
||||
if ac.probe_expression(ADDR_OF_PROBE) {
|
||||
enable_addr_of = true;
|
||||
}
|
||||
}
|
||||
|
||||
// The `target_has_atomic` cfg was stabilized in 1.60.
|
||||
if ac.probe_rustc_version(1, 61) {
|
||||
enable_target_has_atomic = true;
|
||||
@@ -117,6 +108,21 @@ fn main() {
|
||||
enable_const_mutex_new = true;
|
||||
}
|
||||
}
|
||||
|
||||
// The `AsFd` family of traits were made stable in 1.63.
|
||||
if ac.probe_rustc_version(1, 64) {
|
||||
enable_as_fd = true;
|
||||
} else if ac.probe_rustc_version(1, 63) {
|
||||
// This compiler claims to be 1.63, but there are some nightly
|
||||
// compilers that claim to be 1.63 without supporting the
|
||||
// feature. Explicitly probe to check if code using them
|
||||
// compiles.
|
||||
//
|
||||
// The oldest nightly that supports the feature is 2022-06-16.
|
||||
if ac.probe_expression(AS_FD_PROBE) {
|
||||
enable_as_fd = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
@@ -138,14 +144,6 @@ fn main() {
|
||||
autocfg::emit("tokio_no_const_thread_local")
|
||||
}
|
||||
|
||||
if !enable_addr_of {
|
||||
// To disable this feature on compilers that support it, you can
|
||||
// explicitly pass this flag with the following environment variable:
|
||||
//
|
||||
// RUSTFLAGS="--cfg tokio_no_addr_of"
|
||||
autocfg::emit("tokio_no_addr_of")
|
||||
}
|
||||
|
||||
if !enable_target_has_atomic {
|
||||
// To disable this feature on compilers that support it, you can
|
||||
// explicitly pass this flag with the following environment variable:
|
||||
@@ -162,6 +160,14 @@ fn main() {
|
||||
autocfg::emit("tokio_no_const_mutex_new")
|
||||
}
|
||||
|
||||
if !enable_as_fd {
|
||||
// To disable this feature on compilers that support it, you can
|
||||
// explicitly pass this flag with the following environment variable:
|
||||
//
|
||||
// RUSTFLAGS="--cfg tokio_no_as_fd"
|
||||
autocfg::emit("tokio_no_as_fd");
|
||||
}
|
||||
|
||||
if target_needs_atomic_u64_fallback {
|
||||
// To disable this feature on compilers that support it, you can
|
||||
// explicitly pass this flag with the following environment variable:
|
||||
|
||||
+40
-1
@@ -13,7 +13,7 @@ pub mod windows {
|
||||
|
||||
/// See [std::os::windows::io::AsRawHandle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html)
|
||||
pub trait AsRawHandle {
|
||||
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html#tymethod.as_raw_handle)
|
||||
/// See [std::os::windows::io::AsRawHandle::as_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html#tymethod.as_raw_handle)
|
||||
fn as_raw_handle(&self) -> RawHandle;
|
||||
}
|
||||
|
||||
@@ -22,5 +22,44 @@ pub mod windows {
|
||||
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html#tymethod.from_raw_handle)
|
||||
unsafe fn from_raw_handle(handle: RawHandle) -> Self;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::RawSocket](https://doc.rust-lang.org/std/os/windows/io/type.RawSocket.html)
|
||||
pub type RawSocket = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [std::os::windows::io::AsRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html)
|
||||
pub trait AsRawSocket {
|
||||
/// See [std::os::windows::io::AsRawSocket::as_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html#tymethod.as_raw_socket)
|
||||
fn as_raw_socket(&self) -> RawSocket;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::FromRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawSocket.html)
|
||||
pub trait FromRawSocket {
|
||||
/// See [std::os::windows::io::FromRawSocket::from_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawSocket.html#tymethod.from_raw_socket)
|
||||
unsafe fn from_raw_socket(sock: RawSocket) -> Self;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::IntoRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawSocket.html)
|
||||
pub trait IntoRawSocket {
|
||||
/// See [std::os::windows::io::IntoRawSocket::into_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawSocket.html#tymethod.into_raw_socket)
|
||||
fn into_raw_socket(self) -> RawSocket;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::BorrowedHandle](https://doc.rust-lang.org/std/os/windows/io/struct.BorrowedHandle.html)
|
||||
pub type BorrowedHandle<'handle> = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [std::os::windows::io::AsHandle](https://doc.rust-lang.org/std/os/windows/io/trait.AsHandle.html)
|
||||
pub trait AsHandle {
|
||||
/// See [std::os::windows::io::AsHandle::as_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsHandle.html#tymethod.as_handle)
|
||||
fn as_handle(&self) -> BorrowedHandle<'_>;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::BorrowedSocket](https://doc.rust-lang.org/std/os/windows/io/struct.BorrowedSocket.html)
|
||||
pub type BorrowedSocket<'socket> = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [std::os::windows::io::AsSocket](https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html)
|
||||
pub trait AsSocket {
|
||||
/// See [std::os::windows::io::AsSocket::as_socket](https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html#tymethod.as_socket)
|
||||
fn as_socket(&self) -> BorrowedSocket<'_>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
-10
@@ -725,6 +725,15 @@ impl std::os::unix::io::AsRawFd for File {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(tokio_no_as_fd)))]
|
||||
impl std::os::unix::io::AsFd for File {
|
||||
fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> {
|
||||
unsafe {
|
||||
std::os::unix::io::BorrowedFd::borrow_raw(std::os::unix::io::AsRawFd::as_raw_fd(self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::FromRawFd for File {
|
||||
unsafe fn from_raw_fd(fd: std::os::unix::io::RawFd) -> Self {
|
||||
@@ -732,17 +741,32 @@ impl std::os::unix::io::FromRawFd for File {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for File {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
|
||||
self.std.as_raw_handle()
|
||||
}
|
||||
}
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::FromRawHandle for File {
|
||||
unsafe fn from_raw_handle(handle: std::os::windows::io::RawHandle) -> Self {
|
||||
StdFile::from_raw_handle(handle).into()
|
||||
impl AsRawHandle for File {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
self.std.as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for File {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe {
|
||||
BorrowedHandle::borrow_raw(
|
||||
AsRawHandle::as_raw_handle(self),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawHandle for File {
|
||||
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
|
||||
StdFile::from_raw_handle(handle).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -102,6 +102,9 @@ pub use self::write::write;
|
||||
mod copy;
|
||||
pub use self::copy::copy;
|
||||
|
||||
mod try_exists;
|
||||
pub use self::try_exists::try_exists;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mocks;
|
||||
|
||||
@@ -112,9 +115,7 @@ feature! {
|
||||
pub use self::symlink::symlink;
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![windows]
|
||||
|
||||
cfg_windows! {
|
||||
mod symlink_dir;
|
||||
pub use self::symlink_dir::symlink_dir;
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ use mock_open_options::MockOpenOptions as StdOpenOptions;
|
||||
#[cfg(not(test))]
|
||||
use std::fs::OpenOptions as StdOpenOptions;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
|
||||
/// Options and flags which can be used to configure how a file is opened.
|
||||
///
|
||||
/// This builder exposes the ability to configure how a [`File`] is opened and
|
||||
@@ -399,8 +404,6 @@ impl OpenOptions {
|
||||
feature! {
|
||||
#![unix]
|
||||
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
impl OpenOptions {
|
||||
/// Sets the mode bits that a new file will be created with.
|
||||
///
|
||||
@@ -464,11 +467,7 @@ feature! {
|
||||
}
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![windows]
|
||||
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
|
||||
cfg_windows! {
|
||||
impl OpenOptions {
|
||||
/// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
|
||||
/// with the specified value.
|
||||
|
||||
@@ -33,7 +33,7 @@ const CHUNK_SIZE: usize = 32;
|
||||
pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
|
||||
let path = path.as_ref().to_owned();
|
||||
asyncify(|| -> io::Result<ReadDir> {
|
||||
let mut std = std::fs::read_dir(path)?;
|
||||
let mut std = std::fs::read_dir(path)?.fuse();
|
||||
let mut buf = VecDeque::with_capacity(CHUNK_SIZE);
|
||||
ReadDir::next_chunk(&mut buf, &mut std);
|
||||
|
||||
@@ -64,10 +64,12 @@ pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
pub struct ReadDir(State);
|
||||
|
||||
type StdReadDir = std::iter::Fuse<std::fs::ReadDir>;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
Idle(Option<(VecDeque<io::Result<DirEntry>>, std::fs::ReadDir)>),
|
||||
Pending(JoinHandle<(VecDeque<io::Result<DirEntry>>, std::fs::ReadDir)>),
|
||||
Idle(Option<(VecDeque<io::Result<DirEntry>>, StdReadDir)>),
|
||||
Pending(JoinHandle<(VecDeque<io::Result<DirEntry>>, StdReadDir)>),
|
||||
}
|
||||
|
||||
impl ReadDir {
|
||||
@@ -133,7 +135,7 @@ impl ReadDir {
|
||||
}
|
||||
}
|
||||
|
||||
fn next_chunk(buf: &mut VecDeque<io::Result<DirEntry>>, std: &mut std::fs::ReadDir) {
|
||||
fn next_chunk(buf: &mut VecDeque<io::Result<DirEntry>>, std: &mut StdReadDir) {
|
||||
for ret in std.by_ref().take(CHUNK_SIZE) {
|
||||
let success = ret.is_ok();
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::os::windows::fs::symlink_dir`][std]
|
||||
///
|
||||
/// [std]: std::os::windows::fs::symlink_dir
|
||||
/// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
|
||||
pub async fn symlink_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
let src = src.as_ref().to_owned();
|
||||
let dst = dst.as_ref().to_owned();
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::os::windows::fs::symlink_file`][std]
|
||||
///
|
||||
/// [std]: std::os::windows::fs::symlink_file
|
||||
/// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
|
||||
pub async fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
let src = src.as_ref().to_owned();
|
||||
let dst = dst.as_ref().to_owned();
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
use crate::fs::asyncify;
|
||||
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Returns `Ok(true)` if the path points at an existing entity.
|
||||
///
|
||||
/// This function will traverse symbolic links to query information about the
|
||||
/// destination file. In case of broken symbolic links this will return `Ok(false)`.
|
||||
///
|
||||
/// This is the async equivalent of [`std::path::Path::try_exists`][std].
|
||||
///
|
||||
/// [std]: fn@std::path::Path::try_exists
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs;
|
||||
///
|
||||
/// # async fn dox() -> std::io::Result<()> {
|
||||
/// fs::try_exists("foo.txt").await?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn try_exists(path: impl AsRef<Path>) -> io::Result<bool> {
|
||||
let path = path.as_ref().to_owned();
|
||||
// std's Path::try_exists is not available for current Rust min supported version.
|
||||
// Current implementation is based on its internal implementation instead.
|
||||
match asyncify(move || std::fs::metadata(path)).await {
|
||||
Ok(_) => Ok(true),
|
||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
@@ -65,8 +65,8 @@ use std::{task::Context, task::Poll};
|
||||
/// # Examples
|
||||
///
|
||||
/// This example shows how to turn [`std::net::TcpStream`] asynchronous using
|
||||
/// `AsyncFd`. It implements `read` as an async fn, and `AsyncWrite` as a trait
|
||||
/// to show how to implement both approaches.
|
||||
/// `AsyncFd`. It implements the read/write operations both as an `async fn`
|
||||
/// and using the IO traits [`AsyncRead`] and [`AsyncWrite`].
|
||||
///
|
||||
/// ```no_run
|
||||
/// use futures::ready;
|
||||
@@ -74,7 +74,7 @@ use std::{task::Context, task::Poll};
|
||||
/// use std::net::TcpStream;
|
||||
/// use std::pin::Pin;
|
||||
/// use std::task::{Context, Poll};
|
||||
/// use tokio::io::AsyncWrite;
|
||||
/// use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
/// use tokio::io::unix::AsyncFd;
|
||||
///
|
||||
/// pub struct AsyncTcpStream {
|
||||
@@ -99,6 +99,39 @@ use std::{task::Context, task::Poll};
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// pub async fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
/// loop {
|
||||
/// let mut guard = self.inner.writable().await?;
|
||||
///
|
||||
/// match guard.try_io(|inner| inner.get_ref().write(buf)) {
|
||||
/// Ok(result) => return result,
|
||||
/// Err(_would_block) => continue,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl AsyncRead for AsyncTcpStream {
|
||||
/// fn poll_read(
|
||||
/// self: Pin<&mut Self>,
|
||||
/// cx: &mut Context<'_>,
|
||||
/// buf: &mut ReadBuf<'_>
|
||||
/// ) -> Poll<io::Result<()>> {
|
||||
/// loop {
|
||||
/// let mut guard = ready!(self.inner.poll_read_ready(cx))?;
|
||||
///
|
||||
/// let unfilled = buf.initialize_unfilled();
|
||||
/// match guard.try_io(|inner| inner.get_ref().read(unfilled)) {
|
||||
/// Ok(Ok(len)) => {
|
||||
/// buf.advance(len);
|
||||
/// return Poll::Ready(Ok(()));
|
||||
/// },
|
||||
/// Ok(Err(err)) => return Poll::Ready(Err(err)),
|
||||
/// Err(_would_block) => continue,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl AsyncWrite for AsyncTcpStream {
|
||||
@@ -139,6 +172,8 @@ use std::{task::Context, task::Poll};
|
||||
/// [`writable`]: method@Self::writable
|
||||
/// [`AsyncFdReadyGuard`]: struct@self::AsyncFdReadyGuard
|
||||
/// [`TcpStream::poll_read_ready`]: struct@crate::net::TcpStream
|
||||
/// [`AsyncRead`]: trait@crate::io::AsyncRead
|
||||
/// [`AsyncWrite`]: trait@crate::io::AsyncWrite
|
||||
pub struct AsyncFd<T: AsRawFd> {
|
||||
registration: Registration,
|
||||
inner: Option<T>,
|
||||
@@ -481,6 +516,13 @@ impl<T: AsRawFd> AsRawFd for AsyncFd<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl<T: AsRawFd> std::os::unix::io::AsFd for AsyncFd<T> {
|
||||
fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> {
|
||||
unsafe { std::os::unix::io::BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug + AsRawFd> std::fmt::Debug for AsyncFd<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AsyncFd")
|
||||
|
||||
+20
-14
@@ -130,19 +130,23 @@
|
||||
//! other words, these types must never block the thread, and instead the
|
||||
//! current task is notified when the I/O resource is ready.
|
||||
//!
|
||||
//! ## Conversion to and from Sink/Stream
|
||||
//! ## Conversion to and from Stream/Sink
|
||||
//!
|
||||
//! It is often convenient to encapsulate the reading and writing of
|
||||
//! bytes and instead work with a [`Sink`] or [`Stream`] of some data
|
||||
//! type that is encoded as bytes and/or decoded from bytes. Tokio
|
||||
//! provides some utility traits in the [tokio-util] crate that
|
||||
//! abstract the asynchronous buffering that is required and allows
|
||||
//! you to write [`Encoder`] and [`Decoder`] functions working with a
|
||||
//! buffer of bytes, and then use that ["codec"] to transform anything
|
||||
//! that implements [`AsyncRead`] and [`AsyncWrite`] into a `Sink`/`Stream` of
|
||||
//! your structured data.
|
||||
//! It is often convenient to encapsulate the reading and writing of bytes in a
|
||||
//! [`Stream`] or [`Sink`] of data.
|
||||
//!
|
||||
//! [tokio-util]: https://docs.rs/tokio-util/0.6/tokio_util/codec/index.html
|
||||
//! Tokio provides simple wrappers for converting [`AsyncRead`] to [`Stream`]
|
||||
//! and vice-versa in the [tokio-util] crate, see [`ReaderStream`] and
|
||||
//! [`StreamReader`].
|
||||
//!
|
||||
//! There are also utility traits that abstract the asynchronous buffering
|
||||
//! necessary to write your own adaptors for encoding and decoding bytes to/from
|
||||
//! your structured data, allowing to transform something that implements
|
||||
//! [`AsyncRead`]/[`AsyncWrite`] into a [`Stream`]/[`Sink`], see [`Decoder`] and
|
||||
//! [`Encoder`] in the [tokio-util::codec] module.
|
||||
//!
|
||||
//! [tokio-util]: https://docs.rs/tokio-util
|
||||
//! [tokio-util::codec]: https://docs.rs/tokio-util/latest/tokio_util/codec/index.html
|
||||
//!
|
||||
//! # Standard input and output
|
||||
//!
|
||||
@@ -167,9 +171,11 @@
|
||||
//! [`AsyncWrite`]: trait@AsyncWrite
|
||||
//! [`AsyncReadExt`]: trait@AsyncReadExt
|
||||
//! [`AsyncWriteExt`]: trait@AsyncWriteExt
|
||||
//! ["codec"]: https://docs.rs/tokio-util/0.6/tokio_util/codec/index.html
|
||||
//! [`Encoder`]: https://docs.rs/tokio-util/0.6/tokio_util/codec/trait.Encoder.html
|
||||
//! [`Decoder`]: https://docs.rs/tokio-util/0.6/tokio_util/codec/trait.Decoder.html
|
||||
//! ["codec"]: https://docs.rs/tokio-util/latest/tokio_util/codec/index.html
|
||||
//! [`Encoder`]: https://docs.rs/tokio-util/latest/tokio_util/codec/trait.Encoder.html
|
||||
//! [`Decoder`]: https://docs.rs/tokio-util/latest/tokio_util/codec/trait.Decoder.html
|
||||
//! [`ReaderStream`]: https://docs.rs/tokio-util/latest/tokio_util/io/struct.ReaderStream.html
|
||||
//! [`StreamReader`]: https://docs.rs/tokio-util/latest/tokio_util/io/struct.StreamReader.html
|
||||
//! [`Error`]: struct@Error
|
||||
//! [`ErrorKind`]: enum@ErrorKind
|
||||
//! [`Result`]: type@Result
|
||||
|
||||
+34
-7
@@ -74,16 +74,43 @@ cfg_io_std! {
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::AsRawFd for Stderr {
|
||||
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
|
||||
std::io::stderr().as_raw_fd()
|
||||
mod sys {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use super::Stderr;
|
||||
|
||||
impl AsRawFd for Stderr {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
std::io::stderr().as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for Stderr {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for Stderr {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
|
||||
std::io::stderr().as_raw_handle()
|
||||
cfg_windows! {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
use crate::os::windows::io::{AsRawHandle, RawHandle};
|
||||
|
||||
impl AsRawHandle for Stderr {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
std::io::stderr().as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for Stderr {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+34
-7
@@ -49,16 +49,43 @@ cfg_io_std! {
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::AsRawFd for Stdin {
|
||||
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
|
||||
std::io::stdin().as_raw_fd()
|
||||
mod sys {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use super::Stdin;
|
||||
|
||||
impl AsRawFd for Stdin {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
std::io::stdin().as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for Stdin {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for Stdin {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
|
||||
std::io::stdin().as_raw_handle()
|
||||
cfg_windows! {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
use crate::os::windows::io::{AsRawHandle, RawHandle};
|
||||
|
||||
impl AsRawHandle for Stdin {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
std::io::stdin().as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for Stdin {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn test_splitter() {
|
||||
let data = str::repeat("█", MAX_BUF);
|
||||
let mut wr = super::SplitByUtf8BoundaryIfWindows::new(TextMockWriter);
|
||||
@@ -189,6 +190,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn test_pseudo_text() {
|
||||
// In this test we write a piece of binary data, whose beginning is
|
||||
// text though. We then validate that even in this corner case buffer
|
||||
|
||||
+34
-7
@@ -73,16 +73,43 @@ cfg_io_std! {
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::AsRawFd for Stdout {
|
||||
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
|
||||
std::io::stdout().as_raw_fd()
|
||||
mod sys {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use super::Stdout;
|
||||
|
||||
impl AsRawFd for Stdout {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
std::io::stdout().as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for Stdout {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for Stdout {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
|
||||
std::io::stdout().as_raw_handle()
|
||||
cfg_windows! {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
use crate::os::windows::io::{AsRawHandle, RawHandle};
|
||||
|
||||
impl AsRawHandle for Stdout {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
std::io::stdout().as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for Stdout {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,14 +153,22 @@ cfg_io_util! {
|
||||
///
|
||||
/// This function returns a future that will continuously read data from
|
||||
/// `reader` and then write it into `writer` in a streaming fashion until
|
||||
/// `reader` returns EOF.
|
||||
/// `reader` returns EOF or fails.
|
||||
///
|
||||
/// On success, the total number of bytes that were copied from `reader` to
|
||||
/// `writer` is returned.
|
||||
///
|
||||
/// This is an asynchronous version of [`std::io::copy`][std].
|
||||
///
|
||||
/// A heap-allocated copy buffer with 8 KB is created to take data from the
|
||||
/// reader to the writer, check [`copy_buf`] if you want an alternative for
|
||||
/// [`AsyncBufRead`]. You can use `copy_buf` with [`BufReader`] to change the
|
||||
/// buffer capacity.
|
||||
///
|
||||
/// [std]: std::io::copy
|
||||
/// [`copy_buf`]: crate::io::copy_buf
|
||||
/// [`AsyncBufRead`]: crate::io::AsyncBufRead
|
||||
/// [`BufReader`]: crate::io::BufReader
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::copy::CopyBuffer;
|
||||
|
||||
use crate::future::poll_fn;
|
||||
use crate::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
@@ -13,13 +13,6 @@ enum TransferState {
|
||||
Done(u64),
|
||||
}
|
||||
|
||||
struct CopyBidirectional<'a, A: ?Sized, B: ?Sized> {
|
||||
a: &'a mut A,
|
||||
b: &'a mut B,
|
||||
a_to_b: TransferState,
|
||||
b_to_a: TransferState,
|
||||
}
|
||||
|
||||
fn transfer_one_direction<A, B>(
|
||||
cx: &mut Context<'_>,
|
||||
state: &mut TransferState,
|
||||
@@ -48,35 +41,6 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A, B> Future for CopyBidirectional<'a, A, B>
|
||||
where
|
||||
A: AsyncRead + AsyncWrite + Unpin + ?Sized,
|
||||
B: AsyncRead + AsyncWrite + Unpin + ?Sized,
|
||||
{
|
||||
type Output = io::Result<(u64, u64)>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// Unpack self into mut refs to each field to avoid borrow check issues.
|
||||
let CopyBidirectional {
|
||||
a,
|
||||
b,
|
||||
a_to_b,
|
||||
b_to_a,
|
||||
} = &mut *self;
|
||||
|
||||
let a_to_b = transfer_one_direction(cx, a_to_b, &mut *a, &mut *b)?;
|
||||
let b_to_a = transfer_one_direction(cx, b_to_a, &mut *b, &mut *a)?;
|
||||
|
||||
// It is not a problem if ready! returns early because transfer_one_direction for the
|
||||
// other direction will keep returning TransferState::Done(count) in future calls to poll
|
||||
let a_to_b = ready!(a_to_b);
|
||||
let b_to_a = ready!(b_to_a);
|
||||
|
||||
Poll::Ready(Ok((a_to_b, b_to_a)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies data in both directions between `a` and `b`.
|
||||
///
|
||||
/// This function returns a future that will read from both streams,
|
||||
@@ -110,11 +74,18 @@ where
|
||||
A: AsyncRead + AsyncWrite + Unpin + ?Sized,
|
||||
B: AsyncRead + AsyncWrite + Unpin + ?Sized,
|
||||
{
|
||||
CopyBidirectional {
|
||||
a,
|
||||
b,
|
||||
a_to_b: TransferState::Running(CopyBuffer::new()),
|
||||
b_to_a: TransferState::Running(CopyBuffer::new()),
|
||||
}
|
||||
let mut a_to_b = TransferState::Running(CopyBuffer::new());
|
||||
let mut b_to_a = TransferState::Running(CopyBuffer::new());
|
||||
poll_fn(|cx| {
|
||||
let a_to_b = transfer_one_direction(cx, &mut a_to_b, a, b)?;
|
||||
let b_to_a = transfer_one_direction(cx, &mut b_to_a, b, a)?;
|
||||
|
||||
// It is not a problem if ready! returns early because transfer_one_direction for the
|
||||
// other direction will keep returning TransferState::Done(count) in future calls to poll
|
||||
let a_to_b = ready!(a_to_b);
|
||||
let b_to_a = ready!(b_to_a);
|
||||
|
||||
Poll::Ready(Ok((a_to_b, b_to_a)))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -24,11 +24,17 @@ cfg_io_util! {
|
||||
///
|
||||
/// This function returns a future that will continuously read data from
|
||||
/// `reader` and then write it into `writer` in a streaming fashion until
|
||||
/// `reader` returns EOF.
|
||||
/// `reader` returns EOF or fails.
|
||||
///
|
||||
/// On success, the total number of bytes that were copied from `reader` to
|
||||
/// `writer` is returned.
|
||||
///
|
||||
/// This is a [`tokio::io::copy`] alternative for [`AsyncBufRead`] readers
|
||||
/// with no extra buffer allocation, since [`AsyncBufRead`] allow access
|
||||
/// to the reader's inner buffer.
|
||||
///
|
||||
/// [`tokio::io::copy`]: crate::io::copy
|
||||
/// [`AsyncBufRead`]: crate::io::AsyncBufRead
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::io::AsyncBufRead;
|
||||
use crate::util::memchr;
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::future::Future;
|
||||
|
||||
+27
-1
@@ -384,7 +384,33 @@
|
||||
//! [unstable features]: https://internals.rust-lang.org/t/feature-request-unstable-opt-in-non-transitive-crate-features/16193#why-not-a-crate-feature-2
|
||||
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
|
||||
//!
|
||||
//! ## WASM support
|
||||
//! ## Supported platforms
|
||||
//!
|
||||
//! Tokio currently guarantees support for the following platforms:
|
||||
//!
|
||||
//! * Linux
|
||||
//! * Windows
|
||||
//! * Android (API level 21)
|
||||
//! * macOS
|
||||
//! * iOS
|
||||
//! * FreeBSD
|
||||
//!
|
||||
//! Tokio will continue to support these platforms in the future. However,
|
||||
//! future releases may change requirements such as the minimum required libc
|
||||
//! version on Linux, the API level on Android, or the supported FreeBSD
|
||||
//! release.
|
||||
//!
|
||||
//! Beyond the above platforms, Tokio is intended to work on all platforms
|
||||
//! supported by the mio crate. You can find a longer list [in mio's
|
||||
//! documentation][mio-supported]. However, these additional platforms may
|
||||
//! become unsupported in the future.
|
||||
//!
|
||||
//! Note that Wine is considered to be a different platform from Windows. See
|
||||
//! mio's documentation for more information on Wine support.
|
||||
//!
|
||||
//! [mio-supported]: https://crates.io/crates/mio#platforms
|
||||
//!
|
||||
//! ### WASM support
|
||||
//!
|
||||
//! Tokio has some limited support for the WASM platform. Without the
|
||||
//! `tokio_unstable` flag, the following features are supported:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! This module defines a macro that lets you go from a raw pointer to a struct
|
||||
//! to a raw pointer to a field of the struct.
|
||||
|
||||
#[cfg(not(tokio_no_addr_of))]
|
||||
macro_rules! generate_addr_of_methods {
|
||||
(
|
||||
impl<$($gen:ident)*> $struct_name:ty {$(
|
||||
@@ -21,33 +20,3 @@ macro_rules! generate_addr_of_methods {
|
||||
)*}
|
||||
};
|
||||
}
|
||||
|
||||
// The `addr_of_mut!` macro is only available for MSRV at least 1.51.0. This
|
||||
// version of the macro uses a workaround for older versions of rustc.
|
||||
#[cfg(tokio_no_addr_of)]
|
||||
macro_rules! generate_addr_of_methods {
|
||||
(
|
||||
impl<$($gen:ident)*> $struct_name:ty {$(
|
||||
$(#[$attrs:meta])*
|
||||
$vis:vis unsafe fn $fn_name:ident(self: NonNull<Self>) -> NonNull<$field_type:ty> {
|
||||
&self$(.$field_name:tt)+
|
||||
}
|
||||
)*}
|
||||
) => {
|
||||
impl<$($gen)*> $struct_name {$(
|
||||
$(#[$attrs])*
|
||||
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
|
||||
let me = me.as_ptr();
|
||||
let me_u8 = me as *mut u8;
|
||||
|
||||
let field_offset = {
|
||||
let me_ref = &*me;
|
||||
let field_ref_u8 = (&me_ref $(.$field_name)+ ) as *const $field_type as *const u8;
|
||||
field_ref_u8.offset_from(me_u8)
|
||||
};
|
||||
|
||||
::core::ptr::NonNull::new_unchecked(me_u8.offset(field_offset).cast())
|
||||
}
|
||||
)*}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,6 +13,18 @@ macro_rules! feature {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables Windows-specific code.
|
||||
/// Use this macro instead of `cfg(windows)` to generate docs properly.
|
||||
macro_rules! cfg_windows {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(any(all(doc, docsrs), windows))]
|
||||
#[cfg_attr(docsrs, doc(cfg(windows)))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables enter::block_on.
|
||||
macro_rules! cfg_block_on {
|
||||
($($item:item)*) => {
|
||||
|
||||
@@ -158,7 +158,9 @@ macro_rules! join {
|
||||
|
||||
// ===== Entry point =====
|
||||
|
||||
( $($e:expr),* $(,)?) => {
|
||||
( $($e:expr),+ $(,)?) => {
|
||||
$crate::join!(@{ () (0) } $($e,)*)
|
||||
};
|
||||
|
||||
() => { async {}.await }
|
||||
}
|
||||
|
||||
@@ -131,6 +131,13 @@
|
||||
/// correctly even if it is restarted while waiting at an `.await`, then it is
|
||||
/// cancellation safe.
|
||||
///
|
||||
/// Cancellation safety can be defined in the following way: If you have a
|
||||
/// future that has not yet completed, then it must be a no-op to drop that
|
||||
/// future and recreate it. This definition is motivated by the situation where
|
||||
/// a `select!` is used in a loop. Without this guarantee, you would lose your
|
||||
/// progress when another branch completes and you restart the `select!` by
|
||||
/// going around the loop.
|
||||
///
|
||||
/// Be aware that cancelling something that is not cancellation safe is not
|
||||
/// necessarily wrong. For example, if you are cancelling a task because the
|
||||
/// application is shutting down, then you probably don't care that partially
|
||||
|
||||
@@ -210,7 +210,9 @@ macro_rules! try_join {
|
||||
|
||||
// ===== Entry point =====
|
||||
|
||||
( $($e:expr),* $(,)?) => {
|
||||
( $($e:expr),+ $(,)?) => {
|
||||
$crate::try_join!(@{ () (0) } $($e,)*)
|
||||
};
|
||||
|
||||
() => { async { Ok(()) }.await }
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ cfg_not_wasi! {
|
||||
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
||||
}
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::{self, SocketAddr};
|
||||
@@ -407,6 +406,13 @@ mod sys {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for TcpListener {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg_unstable! {
|
||||
@@ -420,17 +426,31 @@ cfg_unstable! {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for TcpListener {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod sys {
|
||||
use super::TcpListener;
|
||||
use std::os::windows::prelude::*;
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawSocket, RawSocket};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsSocket, BorrowedSocket};
|
||||
|
||||
impl AsRawSocket for TcpListener {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.io.as_raw_socket()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsSocket for TcpListener {
|
||||
fn as_socket(&self) -> BorrowedSocket<'_> {
|
||||
unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+42
-23
@@ -4,12 +4,18 @@ use std::fmt;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[cfg(all(unix, not(tokio_no_as_fd)))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
#[cfg(unix)]
|
||||
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_windows! {
|
||||
use crate::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsSocket, BorrowedSocket};
|
||||
}
|
||||
|
||||
cfg_net! {
|
||||
/// A TCP socket that has not yet been converted to a `TcpStream` or
|
||||
/// `TcpListener`.
|
||||
@@ -737,6 +743,13 @@ impl AsRawFd for TcpSocket {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(tokio_no_as_fd)))]
|
||||
impl AsFd for TcpSocket {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl FromRawFd for TcpSocket {
|
||||
/// Converts a `RawFd` to a `TcpSocket`.
|
||||
@@ -758,30 +771,36 @@ impl IntoRawFd for TcpSocket {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl IntoRawSocket for TcpSocket {
|
||||
fn into_raw_socket(self) -> RawSocket {
|
||||
self.inner.into_raw_socket()
|
||||
cfg_windows! {
|
||||
impl IntoRawSocket for TcpSocket {
|
||||
fn into_raw_socket(self) -> RawSocket {
|
||||
self.inner.into_raw_socket()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl AsRawSocket for TcpSocket {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.inner.as_raw_socket()
|
||||
impl AsRawSocket for TcpSocket {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.inner.as_raw_socket()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl FromRawSocket for TcpSocket {
|
||||
/// Converts a `RawSocket` to a `TcpStream`.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the socket is in
|
||||
/// non-blocking mode.
|
||||
unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
|
||||
let inner = socket2::Socket::from_raw_socket(socket);
|
||||
TcpSocket { inner }
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsSocket for TcpSocket {
|
||||
fn as_socket(&self) -> BorrowedSocket<'_> {
|
||||
unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawSocket for TcpSocket {
|
||||
/// Converts a `RawSocket` to a `TcpStream`.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The caller is responsible for ensuring that the socket is in
|
||||
/// non-blocking mode.
|
||||
unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
|
||||
let inner = socket2::Socket::from_raw_socket(socket);
|
||||
TcpSocket { inner }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,9 +141,9 @@ impl ReadHalf<'_> {
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_read()`]. It can be used instead
|
||||
/// of [`readable()`] to check the returned ready set for [`Ready::READABLE`]
|
||||
/// and [`Ready::READ_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
@@ -153,6 +153,9 @@ impl ReadHalf<'_> {
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read
|
||||
/// [`readable()`]: Self::readable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
@@ -275,9 +278,9 @@ impl ReadHalf<'_> {
|
||||
impl WriteHalf<'_> {
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_write()`]. It can be used instead
|
||||
/// of [`writable()`] to check the returned ready set for [`Ready::WRITABLE`]
|
||||
/// and [`Ready::WRITE_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
@@ -287,6 +290,9 @@ impl WriteHalf<'_> {
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write
|
||||
/// [`writable()`]: Self::writable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
||||
@@ -196,9 +196,9 @@ impl OwnedReadHalf {
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_read()`]. It can be used instead
|
||||
/// of [`readable()`] to check the returned ready set for [`Ready::READABLE`]
|
||||
/// and [`Ready::READ_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
@@ -208,6 +208,9 @@ impl OwnedReadHalf {
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read
|
||||
/// [`readable()`]: Self::readable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
@@ -357,9 +360,9 @@ impl OwnedWriteHalf {
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_write()`]. It can be used instead
|
||||
/// of [`writable()`] to check the returned ready set for [`Ready::WRITABLE`]
|
||||
/// and [`Ready::WRITE_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
@@ -369,6 +372,9 @@ impl OwnedWriteHalf {
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write
|
||||
/// [`writable()`]: Self::writable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready};
|
||||
use crate::net::tcp::split::{split, ReadHalf, WriteHalf};
|
||||
use crate::net::tcp::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
@@ -1016,6 +1015,42 @@ impl TcpStream {
|
||||
.try_io(interest, || self.io.try_io(f))
|
||||
}
|
||||
|
||||
/// Reads or writes from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// The readiness of the socket is awaited and when the socket is ready,
|
||||
/// the provided closure is called. The closure should attempt to perform
|
||||
/// IO operation on the socket by manually calling the appropriate syscall.
|
||||
/// If the operation fails because the socket is not actually ready,
|
||||
/// then the closure should return a `WouldBlock` error. In such case the
|
||||
/// readiness flag is cleared and the socket readiness is awaited again.
|
||||
/// This loop is repeated until the closure returns an `Ok` or an error
|
||||
/// other than `WouldBlock`.
|
||||
///
|
||||
/// The closure should only return a `WouldBlock` error if it has performed
|
||||
/// an IO operation on the socket that failed due to the socket not being
|
||||
/// ready. Returning a `WouldBlock` error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the socket to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio `TcpStream` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
mut f: impl FnMut() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io
|
||||
.registration()
|
||||
.async_io(interest, || self.io.try_io(&mut f))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Receives data on the socket from the remote address to which it is
|
||||
/// connected, without removing that data from the queue. On success,
|
||||
/// returns the number of bytes peeked.
|
||||
@@ -1342,18 +1377,32 @@ mod sys {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for TcpStream {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod sys {
|
||||
use super::TcpStream;
|
||||
use std::os::windows::prelude::*;
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawSocket, RawSocket};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsSocket, BorrowedSocket};
|
||||
|
||||
impl AsRawSocket for TcpStream {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.io.as_raw_socket()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsSocket for TcpStream {
|
||||
fn as_socket(&self) -> BorrowedSocket<'_> {
|
||||
unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(tokio_unstable, tokio_wasi))]
|
||||
@@ -1366,4 +1415,11 @@ mod sys {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for TcpStream {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+232
-7
@@ -1,7 +1,6 @@
|
||||
use crate::io::{Interest, PollEvented, ReadBuf, Ready};
|
||||
use crate::net::{to_socket_addrs, ToSocketAddrs};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
@@ -954,6 +953,15 @@ impl UdpSocket {
|
||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||
/// returned. This function is usually paired with `readable()`.
|
||||
///
|
||||
/// # Notes
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@@ -1177,6 +1185,15 @@ impl UdpSocket {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Notes
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.io
|
||||
.registration()
|
||||
@@ -1201,6 +1218,15 @@ impl UdpSocket {
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// # Notes
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub fn poll_recv_from(
|
||||
&self,
|
||||
cx: &mut Context<'_>,
|
||||
@@ -1233,6 +1259,16 @@ impl UdpSocket {
|
||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||
/// returned. This function is usually paired with `readable()`.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@@ -1319,6 +1355,42 @@ impl UdpSocket {
|
||||
.try_io(interest, || self.io.try_io(f))
|
||||
}
|
||||
|
||||
/// Reads or writes from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// The readiness of the socket is awaited and when the socket is ready,
|
||||
/// the provided closure is called. The closure should attempt to perform
|
||||
/// IO operation on the socket by manually calling the appropriate syscall.
|
||||
/// If the operation fails because the socket is not actually ready,
|
||||
/// then the closure should return a `WouldBlock` error. In such case the
|
||||
/// readiness flag is cleared and the socket readiness is awaited again.
|
||||
/// This loop is repeated until the closure returns an `Ok` or an error
|
||||
/// other than `WouldBlock`.
|
||||
///
|
||||
/// The closure should only return a `WouldBlock` error if it has performed
|
||||
/// an IO operation on the socket that failed due to the socket not being
|
||||
/// ready. Returning a `WouldBlock` error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the socket to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio `UdpSocket` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
mut f: impl FnMut() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io
|
||||
.registration()
|
||||
.async_io(interest, || self.io.try_io(&mut f))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Receives data from the socket, without removing it from the input queue.
|
||||
/// On success, returns the number of bytes read and the address from whence
|
||||
/// the data came.
|
||||
@@ -1331,6 +1403,17 @@ impl UdpSocket {
|
||||
/// Make sure to always use a sufficiently large buffer to hold the
|
||||
/// maximum UDP packet size, which can be up to 65536 bytes in size.
|
||||
///
|
||||
/// MacOS will return an error if you pass a zero-sized buffer.
|
||||
///
|
||||
/// If you're merely interested in learning the sender of the data at the head of the queue,
|
||||
/// try [`peek_sender`].
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@@ -1349,6 +1432,9 @@ impl UdpSocket {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`peek_sender`]: method@Self::peek_sender
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.io
|
||||
.registration()
|
||||
@@ -1357,7 +1443,7 @@ impl UdpSocket {
|
||||
}
|
||||
|
||||
/// Receives data from the socket, without removing it from the input queue.
|
||||
/// On success, returns the number of bytes read.
|
||||
/// On success, returns the sending address of the datagram.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
@@ -1371,6 +1457,17 @@ impl UdpSocket {
|
||||
/// Make sure to always use a sufficiently large buffer to hold the
|
||||
/// maximum UDP packet size, which can be up to 65536 bytes in size.
|
||||
///
|
||||
/// MacOS will return an error if you pass a zero-sized buffer.
|
||||
///
|
||||
/// If you're merely interested in learning the sender of the data at the head of the queue,
|
||||
/// try [`poll_peek_sender`].
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The function returns:
|
||||
@@ -1382,6 +1479,9 @@ impl UdpSocket {
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// [`poll_peek_sender`]: method@Self::poll_peek_sender
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub fn poll_peek_from(
|
||||
&self,
|
||||
cx: &mut Context<'_>,
|
||||
@@ -1404,6 +1504,117 @@ impl UdpSocket {
|
||||
Poll::Ready(Ok(addr))
|
||||
}
|
||||
|
||||
/// Tries to receive data on the socket without removing it from the input queue.
|
||||
/// On success, returns the number of bytes read and the sending address of the
|
||||
/// datagram.
|
||||
///
|
||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||
/// returned. This function is usually paired with `readable()`.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// On Windows, if the data is larger than the buffer specified, the buffer
|
||||
/// is filled with the first part of the data, and peek returns the error
|
||||
/// WSAEMSGSIZE(10040). The excess data is lost.
|
||||
/// Make sure to always use a sufficiently large buffer to hold the
|
||||
/// maximum UDP packet size, which can be up to 65536 bytes in size.
|
||||
///
|
||||
/// MacOS will return an error if you pass a zero-sized buffer.
|
||||
///
|
||||
/// If you're merely interested in learning the sender of the data at the head of the queue,
|
||||
/// try [`try_peek_sender`].
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [`try_peek_sender`]: method@Self::try_peek_sender
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub fn try_peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.io
|
||||
.registration()
|
||||
.try_io(Interest::READABLE, || self.io.peek_from(buf))
|
||||
}
|
||||
|
||||
/// Retrieve the sender of the data at the head of the input queue, waiting if empty.
|
||||
///
|
||||
/// This is equivalent to calling [`peek_from`] with a zero-sized buffer,
|
||||
/// but suppresses the `WSAEMSGSIZE` error on Windows and the "invalid argument" error on macOS.
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [`peek_from`]: method@Self::peek_from
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub async fn peek_sender(&self) -> io::Result<SocketAddr> {
|
||||
self.io
|
||||
.registration()
|
||||
.async_io(Interest::READABLE, || self.peek_sender_inner())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Retrieve the sender of the data at the head of the input queue,
|
||||
/// scheduling a wakeup if empty.
|
||||
///
|
||||
/// This is equivalent to calling [`poll_peek_from`] with a zero-sized buffer,
|
||||
/// but suppresses the `WSAEMSGSIZE` error on Windows and the "invalid argument" error on macOS.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Note that on multiple calls to a `poll_*` method in the recv direction, only the
|
||||
/// `Waker` from the `Context` passed to the most recent call will be scheduled to
|
||||
/// receive a wakeup.
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [`poll_peek_from`]: method@Self::poll_peek_from
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub fn poll_peek_sender(&self, cx: &mut Context<'_>) -> Poll<io::Result<SocketAddr>> {
|
||||
self.io
|
||||
.registration()
|
||||
.poll_read_io(cx, || self.peek_sender_inner())
|
||||
}
|
||||
|
||||
/// Try to retrieve the sender of the data at the head of the input queue.
|
||||
///
|
||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||
/// returned. This function is usually paired with `readable()`.
|
||||
///
|
||||
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||
/// It is important to be aware of this when designing your application-level protocol.
|
||||
///
|
||||
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||
pub fn try_peek_sender(&self) -> io::Result<SocketAddr> {
|
||||
self.io
|
||||
.registration()
|
||||
.try_io(Interest::READABLE, || self.peek_sender_inner())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn peek_sender_inner(&self) -> io::Result<SocketAddr> {
|
||||
self.io.try_io(|| {
|
||||
self.as_socket()
|
||||
.peek_sender()?
|
||||
// May be `None` if the platform doesn't populate the sender for some reason.
|
||||
// In testing, that only occurred on macOS if you pass a zero-sized buffer,
|
||||
// but the implementation of `Socket::peek_sender()` covers that.
|
||||
.as_socket()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "sender not available"))
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the value of the `SO_BROADCAST` option for this socket.
|
||||
///
|
||||
/// For more information about this option, see [`set_broadcast`].
|
||||
@@ -1691,7 +1902,7 @@ impl fmt::Debug for UdpSocket {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix))]
|
||||
#[cfg(unix)]
|
||||
mod sys {
|
||||
use super::UdpSocket;
|
||||
use std::os::unix::prelude::*;
|
||||
@@ -1701,16 +1912,30 @@ mod sys {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for UdpSocket {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod sys {
|
||||
use super::UdpSocket;
|
||||
use std::os::windows::prelude::*;
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawSocket, RawSocket};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsSocket, BorrowedSocket};
|
||||
|
||||
impl AsRawSocket for UdpSocket {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.io.as_raw_socket()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsSocket for UdpSocket {
|
||||
fn as_socket(&self) -> BorrowedSocket<'_> {
|
||||
unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use crate::io::{Interest, PollEvented, ReadBuf, Ready};
|
||||
use crate::net::unix::SocketAddr;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::Shutdown;
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::os::unix::net;
|
||||
use std::path::Path;
|
||||
@@ -1260,6 +1261,42 @@ impl UnixDatagram {
|
||||
.try_io(interest, || self.io.try_io(f))
|
||||
}
|
||||
|
||||
/// Reads or writes from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// The readiness of the socket is awaited and when the socket is ready,
|
||||
/// the provided closure is called. The closure should attempt to perform
|
||||
/// IO operation on the socket by manually calling the appropriate syscall.
|
||||
/// If the operation fails because the socket is not actually ready,
|
||||
/// then the closure should return a `WouldBlock` error. In such case the
|
||||
/// readiness flag is cleared and the socket readiness is awaited again.
|
||||
/// This loop is repeated until the closure returns an `Ok` or an error
|
||||
/// other than `WouldBlock`.
|
||||
///
|
||||
/// The closure should only return a `WouldBlock` error if it has performed
|
||||
/// an IO operation on the socket that failed due to the socket not being
|
||||
/// ready. Returning a `WouldBlock` error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the socket to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio `UnixDatagram` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
mut f: impl FnMut() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io
|
||||
.registration()
|
||||
.async_io(interest, || self.io.try_io(&mut f))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns the local address that this socket is bound to.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -1436,3 +1473,10 @@ impl AsRawFd for UnixDatagram {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for UnixDatagram {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use crate::io::{Interest, PollEvented};
|
||||
use crate::net::unix::{SocketAddr, UnixStream};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::os::unix::net;
|
||||
use std::path::Path;
|
||||
@@ -208,3 +209,10 @@ impl AsRawFd for UnixListener {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for UnixListener {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Unix domain socket utility types.
|
||||
//! Unix specific network types.
|
||||
// This module does not currently provide any public API, but it was
|
||||
// unintentionally defined as a public module. Hide it from the documentation
|
||||
// instead of changing it to a private module to avoid breakage.
|
||||
@@ -22,6 +22,8 @@ pub(crate) use stream::UnixStream;
|
||||
mod ucred;
|
||||
pub use ucred::UCred;
|
||||
|
||||
pub mod pipe;
|
||||
|
||||
/// A type representing process and process group IDs.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type uid_t = u32;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,9 +55,20 @@ pub(crate) fn split(stream: &mut UnixStream) -> (ReadHalf<'_>, WriteHalf<'_>) {
|
||||
impl ReadHalf<'_> {
|
||||
/// Wait for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_read()`]. It can be used instead
|
||||
/// of [`readable()`] to check the returned ready set for [`Ready::READABLE`]
|
||||
/// and [`Ready::READ_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`UnixStream::ready`].
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read
|
||||
/// [`readable()`]: Self::readable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
@@ -178,9 +189,9 @@ impl ReadHalf<'_> {
|
||||
impl WriteHalf<'_> {
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_write()`]. It can be used instead
|
||||
/// of [`writable()`] to check the returned ready set for [`Ready::WRITABLE`]
|
||||
/// and [`Ready::WRITE_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
@@ -188,6 +199,11 @@ impl WriteHalf<'_> {
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`UnixStream::ready`].
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write
|
||||
/// [`writable()`]: Self::writable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
||||
@@ -110,9 +110,9 @@ impl OwnedReadHalf {
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_read()`]. It can be used instead
|
||||
/// of [`readable()`] to check the returned ready set for [`Ready::READABLE`]
|
||||
/// and [`Ready::READ_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
@@ -120,6 +120,11 @@ impl OwnedReadHalf {
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`UnixStream::ready`].
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read
|
||||
/// [`readable()`]: Self::readable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
@@ -267,9 +272,9 @@ impl OwnedWriteHalf {
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
/// This function is usually paired with [`try_write()`]. It can be used instead
|
||||
/// of [`writable()`] to check the returned ready set for [`Ready::WRITABLE`]
|
||||
/// and [`Ready::WRITE_CLOSED`] events.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
@@ -277,6 +282,11 @@ impl OwnedWriteHalf {
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`UnixStream::ready`].
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write
|
||||
/// [`writable()`]: Self::writable
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
||||
@@ -5,10 +5,11 @@ use crate::net::unix::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
|
||||
use crate::net::unix::ucred::{self, UCred};
|
||||
use crate::net::unix::SocketAddr;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::Shutdown;
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::os::unix::net;
|
||||
use std::path::Path;
|
||||
@@ -706,6 +707,42 @@ impl UnixStream {
|
||||
.try_io(interest, || self.io.try_io(f))
|
||||
}
|
||||
|
||||
/// Reads or writes from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// The readiness of the socket is awaited and when the socket is ready,
|
||||
/// the provided closure is called. The closure should attempt to perform
|
||||
/// IO operation on the socket by manually calling the appropriate syscall.
|
||||
/// If the operation fails because the socket is not actually ready,
|
||||
/// then the closure should return a `WouldBlock` error. In such case the
|
||||
/// readiness flag is cleared and the socket readiness is awaited again.
|
||||
/// This loop is repeated until the closure returns an `Ok` or an error
|
||||
/// other than `WouldBlock`.
|
||||
///
|
||||
/// The closure should only return a `WouldBlock` error if it has performed
|
||||
/// an IO operation on the socket that failed due to the socket not being
|
||||
/// ready. Returning a `WouldBlock` error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the socket to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio `UnixStream` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
mut f: impl FnMut() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io
|
||||
.registration()
|
||||
.async_io(interest, || self.io.try_io(&mut f))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Creates new `UnixStream` from a `std::os::unix::net::UnixStream`.
|
||||
///
|
||||
/// This function is intended to be used to wrap a UnixStream from the
|
||||
@@ -1000,3 +1037,10 @@ impl AsRawFd for UnixStream {
|
||||
self.io.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for UnixStream {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ use std::ptr;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use crate::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle};
|
||||
|
||||
cfg_io_util! {
|
||||
@@ -851,6 +853,39 @@ impl NamedPipeServer {
|
||||
) -> io::Result<R> {
|
||||
self.io.registration().try_io(interest, f)
|
||||
}
|
||||
|
||||
/// Reads or writes from the pipe using a user-provided IO operation.
|
||||
///
|
||||
/// The readiness of the pipe is awaited and when the pipe is ready,
|
||||
/// the provided closure is called. The closure should attempt to perform
|
||||
/// IO operation on the pipe by manually calling the appropriate syscall.
|
||||
/// If the operation fails because the pipe is not actually ready,
|
||||
/// then the closure should return a `WouldBlock` error. In such case the
|
||||
/// readiness flag is cleared and the pipe readiness is awaited again.
|
||||
/// This loop is repeated until the closure returns an `Ok` or an error
|
||||
/// other than `WouldBlock`.
|
||||
///
|
||||
/// The closure should only return a `WouldBlock` error if it has performed
|
||||
/// an IO operation on the pipe that failed due to the pipe not being
|
||||
/// ready. Returning a `WouldBlock` error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the pipe to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio `NamedPipeServer` type, as this will mess with the
|
||||
/// readiness flag and can cause the pipe to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
f: impl FnMut() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io.registration().async_io(interest, f).await
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for NamedPipeServer {
|
||||
@@ -895,6 +930,13 @@ impl AsRawHandle for NamedPipeServer {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for NamedPipeServer {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A [Windows named pipe] client.
|
||||
///
|
||||
/// Constructed using [`ClientOptions::open`].
|
||||
@@ -1601,6 +1643,39 @@ impl NamedPipeClient {
|
||||
) -> io::Result<R> {
|
||||
self.io.registration().try_io(interest, f)
|
||||
}
|
||||
|
||||
/// Reads or writes from the pipe using a user-provided IO operation.
|
||||
///
|
||||
/// The readiness of the pipe is awaited and when the pipe is ready,
|
||||
/// the provided closure is called. The closure should attempt to perform
|
||||
/// IO operation on the pipe by manually calling the appropriate syscall.
|
||||
/// If the operation fails because the pipe is not actually ready,
|
||||
/// then the closure should return a `WouldBlock` error. In such case the
|
||||
/// readiness flag is cleared and the pipe readiness is awaited again.
|
||||
/// This loop is repeated until the closure returns an `Ok` or an error
|
||||
/// other than `WouldBlock`.
|
||||
///
|
||||
/// The closure should only return a `WouldBlock` error if it has performed
|
||||
/// an IO operation on the pipe that failed due to the pipe not being
|
||||
/// ready. Returning a `WouldBlock` error in any other situation will
|
||||
/// incorrectly clear the readiness flag, which can cause the pipe to
|
||||
/// behave incorrectly.
|
||||
///
|
||||
/// The closure should not perform the IO operation using any of the methods
|
||||
/// defined on the Tokio `NamedPipeClient` type, as this will mess with the
|
||||
/// readiness flag and can cause the pipe to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
pub async fn async_io<R>(
|
||||
&self,
|
||||
interest: Interest,
|
||||
f: impl FnMut() -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
self.io.registration().async_io(interest, f).await
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for NamedPipeClient {
|
||||
@@ -1645,17 +1720,11 @@ impl AsRawHandle for NamedPipeClient {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to set a boolean flag as a bitfield.
|
||||
macro_rules! bool_flag {
|
||||
($f:expr, $t:expr, $flag:expr) => {{
|
||||
let current = $f;
|
||||
|
||||
if $t {
|
||||
$f = current | $flag;
|
||||
} else {
|
||||
$f = current & !$flag;
|
||||
};
|
||||
}};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for NamedPipeClient {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder structure for construct a named pipe with named pipe-specific
|
||||
@@ -1665,8 +1734,17 @@ macro_rules! bool_flag {
|
||||
/// See [`ServerOptions::create`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServerOptions {
|
||||
open_mode: u32,
|
||||
pipe_mode: u32,
|
||||
// dwOpenMode
|
||||
access_inbound: bool,
|
||||
access_outbound: bool,
|
||||
first_pipe_instance: bool,
|
||||
write_dac: bool,
|
||||
write_owner: bool,
|
||||
access_system_security: bool,
|
||||
// dwPipeMode
|
||||
pipe_mode: PipeMode,
|
||||
reject_remote_clients: bool,
|
||||
// other options
|
||||
max_instances: u32,
|
||||
out_buffer_size: u32,
|
||||
in_buffer_size: u32,
|
||||
@@ -1687,8 +1765,14 @@ impl ServerOptions {
|
||||
/// ```
|
||||
pub fn new() -> ServerOptions {
|
||||
ServerOptions {
|
||||
open_mode: windows_sys::PIPE_ACCESS_DUPLEX | windows_sys::FILE_FLAG_OVERLAPPED,
|
||||
pipe_mode: windows_sys::PIPE_TYPE_BYTE | windows_sys::PIPE_REJECT_REMOTE_CLIENTS,
|
||||
access_inbound: true,
|
||||
access_outbound: true,
|
||||
first_pipe_instance: false,
|
||||
write_dac: false,
|
||||
write_owner: false,
|
||||
access_system_security: false,
|
||||
pipe_mode: PipeMode::Byte,
|
||||
reject_remote_clients: true,
|
||||
max_instances: windows_sys::PIPE_UNLIMITED_INSTANCES,
|
||||
out_buffer_size: 65536,
|
||||
in_buffer_size: 65536,
|
||||
@@ -1701,14 +1785,11 @@ impl ServerOptions {
|
||||
/// The default pipe mode is [`PipeMode::Byte`]. See [`PipeMode`] for
|
||||
/// documentation of what each mode means.
|
||||
///
|
||||
/// This corresponding to specifying [`dwPipeMode`].
|
||||
/// This corresponds to specifying `PIPE_TYPE_` and `PIPE_READMODE_` in [`dwPipeMode`].
|
||||
///
|
||||
/// [`dwPipeMode`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
||||
pub fn pipe_mode(&mut self, pipe_mode: PipeMode) -> &mut Self {
|
||||
let is_msg = matches!(pipe_mode, PipeMode::Message);
|
||||
// Pipe mode is implemented as a bit flag 0x4. Set is message and unset
|
||||
// is byte.
|
||||
bool_flag!(self.pipe_mode, is_msg, windows_sys::PIPE_TYPE_MESSAGE);
|
||||
self.pipe_mode = pipe_mode;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1804,7 +1885,7 @@ impl ServerOptions {
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
pub fn access_inbound(&mut self, allowed: bool) -> &mut Self {
|
||||
bool_flag!(self.open_mode, allowed, windows_sys::PIPE_ACCESS_INBOUND);
|
||||
self.access_inbound = allowed;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1902,7 +1983,7 @@ impl ServerOptions {
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
pub fn access_outbound(&mut self, allowed: bool) -> &mut Self {
|
||||
bool_flag!(self.open_mode, allowed, windows_sys::PIPE_ACCESS_OUTBOUND);
|
||||
self.access_outbound = allowed;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1970,11 +2051,7 @@ impl ServerOptions {
|
||||
/// [`create`]: ServerOptions::create
|
||||
/// [`FILE_FLAG_FIRST_PIPE_INSTANCE`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea#pipe_first_pipe_instance
|
||||
pub fn first_pipe_instance(&mut self, first: bool) -> &mut Self {
|
||||
bool_flag!(
|
||||
self.open_mode,
|
||||
first,
|
||||
windows_sys::FILE_FLAG_FIRST_PIPE_INSTANCE
|
||||
);
|
||||
self.first_pipe_instance = first;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2056,7 +2133,7 @@ impl ServerOptions {
|
||||
///
|
||||
/// [`WRITE_DAC`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
||||
pub fn write_dac(&mut self, requested: bool) -> &mut Self {
|
||||
bool_flag!(self.open_mode, requested, windows_sys::WRITE_DAC);
|
||||
self.write_dac = requested;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2066,7 +2143,7 @@ impl ServerOptions {
|
||||
///
|
||||
/// [`WRITE_OWNER`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
||||
pub fn write_owner(&mut self, requested: bool) -> &mut Self {
|
||||
bool_flag!(self.open_mode, requested, windows_sys::WRITE_OWNER);
|
||||
self.write_owner = requested;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2076,11 +2153,7 @@ impl ServerOptions {
|
||||
///
|
||||
/// [`ACCESS_SYSTEM_SECURITY`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
||||
pub fn access_system_security(&mut self, requested: bool) -> &mut Self {
|
||||
bool_flag!(
|
||||
self.open_mode,
|
||||
requested,
|
||||
windows_sys::ACCESS_SYSTEM_SECURITY
|
||||
);
|
||||
self.access_system_security = requested;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2091,11 +2164,7 @@ impl ServerOptions {
|
||||
///
|
||||
/// [`PIPE_REJECT_REMOTE_CLIENTS`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea#pipe_reject_remote_clients
|
||||
pub fn reject_remote_clients(&mut self, reject: bool) -> &mut Self {
|
||||
bool_flag!(
|
||||
self.pipe_mode,
|
||||
reject,
|
||||
windows_sys::PIPE_REJECT_REMOTE_CLIENTS
|
||||
);
|
||||
self.reject_remote_clients = reject;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2241,10 +2310,46 @@ impl ServerOptions {
|
||||
) -> io::Result<NamedPipeServer> {
|
||||
let addr = encode_addr(addr);
|
||||
|
||||
let pipe_mode = {
|
||||
let mut mode = if matches!(self.pipe_mode, PipeMode::Message) {
|
||||
windows_sys::PIPE_TYPE_MESSAGE | windows_sys::PIPE_READMODE_MESSAGE
|
||||
} else {
|
||||
windows_sys::PIPE_TYPE_BYTE | windows_sys::PIPE_READMODE_BYTE
|
||||
};
|
||||
if self.reject_remote_clients {
|
||||
mode |= windows_sys::PIPE_REJECT_REMOTE_CLIENTS;
|
||||
} else {
|
||||
mode |= windows_sys::PIPE_ACCEPT_REMOTE_CLIENTS;
|
||||
}
|
||||
mode
|
||||
};
|
||||
let open_mode = {
|
||||
let mut mode = windows_sys::FILE_FLAG_OVERLAPPED;
|
||||
if self.access_inbound {
|
||||
mode |= windows_sys::PIPE_ACCESS_INBOUND;
|
||||
}
|
||||
if self.access_outbound {
|
||||
mode |= windows_sys::PIPE_ACCESS_OUTBOUND;
|
||||
}
|
||||
if self.first_pipe_instance {
|
||||
mode |= windows_sys::FILE_FLAG_FIRST_PIPE_INSTANCE;
|
||||
}
|
||||
if self.write_dac {
|
||||
mode |= windows_sys::WRITE_DAC;
|
||||
}
|
||||
if self.write_owner {
|
||||
mode |= windows_sys::WRITE_OWNER;
|
||||
}
|
||||
if self.access_system_security {
|
||||
mode |= windows_sys::ACCESS_SYSTEM_SECURITY;
|
||||
}
|
||||
mode
|
||||
};
|
||||
|
||||
let h = windows_sys::CreateNamedPipeW(
|
||||
addr.as_ptr(),
|
||||
self.open_mode,
|
||||
self.pipe_mode,
|
||||
open_mode,
|
||||
pipe_mode,
|
||||
self.max_instances,
|
||||
self.out_buffer_size,
|
||||
self.in_buffer_size,
|
||||
@@ -2266,8 +2371,10 @@ impl ServerOptions {
|
||||
/// See [`ClientOptions::open`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientOptions {
|
||||
desired_access: u32,
|
||||
generic_read: bool,
|
||||
generic_write: bool,
|
||||
security_qos_flags: u32,
|
||||
pipe_mode: PipeMode,
|
||||
}
|
||||
|
||||
impl ClientOptions {
|
||||
@@ -2286,9 +2393,11 @@ impl ClientOptions {
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
desired_access: windows_sys::GENERIC_READ | windows_sys::GENERIC_WRITE,
|
||||
generic_read: true,
|
||||
generic_write: true,
|
||||
security_qos_flags: windows_sys::SECURITY_IDENTIFICATION
|
||||
| windows_sys::SECURITY_SQOS_PRESENT,
|
||||
pipe_mode: PipeMode::Byte,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2299,7 +2408,7 @@ impl ClientOptions {
|
||||
/// [`GENERIC_READ`]: https://docs.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
|
||||
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
||||
pub fn read(&mut self, allowed: bool) -> &mut Self {
|
||||
bool_flag!(self.desired_access, allowed, windows_sys::GENERIC_READ);
|
||||
self.generic_read = allowed;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2310,7 +2419,7 @@ impl ClientOptions {
|
||||
/// [`GENERIC_WRITE`]: https://docs.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
|
||||
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
||||
pub fn write(&mut self, allowed: bool) -> &mut Self {
|
||||
bool_flag!(self.desired_access, allowed, windows_sys::GENERIC_WRITE);
|
||||
self.generic_write = allowed;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2341,6 +2450,15 @@ impl ClientOptions {
|
||||
self
|
||||
}
|
||||
|
||||
/// The pipe mode.
|
||||
///
|
||||
/// The default pipe mode is [`PipeMode::Byte`]. See [`PipeMode`] for
|
||||
/// documentation of what each mode means.
|
||||
pub fn pipe_mode(&mut self, pipe_mode: PipeMode) -> &mut Self {
|
||||
self.pipe_mode = pipe_mode;
|
||||
self
|
||||
}
|
||||
|
||||
/// Opens the named pipe identified by `addr`.
|
||||
///
|
||||
/// This opens the client using [`CreateFile`] with the
|
||||
@@ -2419,13 +2537,24 @@ impl ClientOptions {
|
||||
) -> io::Result<NamedPipeClient> {
|
||||
let addr = encode_addr(addr);
|
||||
|
||||
let desired_access = {
|
||||
let mut access = 0;
|
||||
if self.generic_read {
|
||||
access |= windows_sys::GENERIC_READ;
|
||||
}
|
||||
if self.generic_write {
|
||||
access |= windows_sys::GENERIC_WRITE;
|
||||
}
|
||||
access
|
||||
};
|
||||
|
||||
// NB: We could use a platform specialized `OpenOptions` here, but since
|
||||
// we have access to windows_sys it ultimately doesn't hurt to use
|
||||
// `CreateFile` explicitly since it allows the use of our already
|
||||
// well-structured wide `addr` to pass into CreateFileW.
|
||||
let h = windows_sys::CreateFileW(
|
||||
addr.as_ptr(),
|
||||
self.desired_access,
|
||||
desired_access,
|
||||
0,
|
||||
attrs as *mut _,
|
||||
windows_sys::OPEN_EXISTING,
|
||||
@@ -2437,6 +2566,16 @@ impl ClientOptions {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
if matches!(self.pipe_mode, PipeMode::Message) {
|
||||
let mode = windows_sys::PIPE_READMODE_MESSAGE;
|
||||
let result =
|
||||
windows_sys::SetNamedPipeHandleState(h, &mode, ptr::null_mut(), ptr::null_mut());
|
||||
|
||||
if result == 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
NamedPipeClient::from_raw_handle(h as _)
|
||||
}
|
||||
|
||||
@@ -2553,48 +2692,3 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result<PipeInfo> {
|
||||
max_instances,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use self::windows_sys::{PIPE_REJECT_REMOTE_CLIENTS, PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE};
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn opts_default_pipe_mode() {
|
||||
let opts = ServerOptions::new();
|
||||
assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opts_unset_reject_remote() {
|
||||
let mut opts = ServerOptions::new();
|
||||
opts.reject_remote_clients(false);
|
||||
assert_eq!(opts.pipe_mode & PIPE_REJECT_REMOTE_CLIENTS, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opts_set_pipe_mode_maintains_reject_remote_clients() {
|
||||
let mut opts = ServerOptions::new();
|
||||
opts.pipe_mode(PipeMode::Byte);
|
||||
assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS);
|
||||
|
||||
opts.reject_remote_clients(false);
|
||||
opts.pipe_mode(PipeMode::Byte);
|
||||
assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE);
|
||||
|
||||
opts.reject_remote_clients(true);
|
||||
opts.pipe_mode(PipeMode::Byte);
|
||||
assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS);
|
||||
|
||||
opts.reject_remote_clients(false);
|
||||
opts.pipe_mode(PipeMode::Message);
|
||||
assert_eq!(opts.pipe_mode, PIPE_TYPE_MESSAGE);
|
||||
|
||||
opts.reject_remote_clients(true);
|
||||
opts.pipe_mode(PipeMode::Message);
|
||||
assert_eq!(
|
||||
opts.pipe_mode,
|
||||
PIPE_TYPE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+78
-35
@@ -156,7 +156,6 @@
|
||||
//! ```no_run
|
||||
//! use tokio::join;
|
||||
//! use tokio::process::Command;
|
||||
//! use std::convert::TryInto;
|
||||
//! use std::process::Stdio;
|
||||
//!
|
||||
//! #[tokio::main]
|
||||
@@ -217,7 +216,7 @@
|
||||
//! from being spawned.
|
||||
//!
|
||||
//! The tokio runtime will, on a best-effort basis, attempt to reap and clean up
|
||||
//! any process which it has spawned. No additional guarantees are made with regards
|
||||
//! any process which it has spawned. No additional guarantees are made with regard to
|
||||
//! how quickly or how often this procedure will take place.
|
||||
//!
|
||||
//! It is recommended to avoid dropping a [`Child`] process handle before it has been
|
||||
@@ -245,22 +244,26 @@ mod kill;
|
||||
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use crate::process::kill::Kill;
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::OsStr;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::process::CommandExt;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::{AsRawHandle, RawHandle};
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::path::Path;
|
||||
use std::pin::Pin;
|
||||
use std::process::{Command as StdCommand, ExitStatus, Output, Stdio};
|
||||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::process::CommandExt;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawHandle, RawHandle};
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
}
|
||||
|
||||
/// This structure mimics the API of [`std::process::Command`] found in the standard library, but
|
||||
/// replaces functions that create a process with an asynchronous variant. The main provided
|
||||
/// asynchronous functions are [spawn](Command::spawn), [status](Command::status), and
|
||||
@@ -631,7 +634,7 @@ impl Command {
|
||||
/// operation, the resulting zombie process cannot be `.await`ed inside of the
|
||||
/// destructor to avoid blocking other tasks. The tokio runtime will, on a
|
||||
/// best-effort basis, attempt to reap and clean up such processes in the
|
||||
/// background, but makes no additional guarantees are made with regards
|
||||
/// background, but no additional guarantees are made with regard to
|
||||
/// how quickly or how often this procedure will take place.
|
||||
///
|
||||
/// If stronger guarantees are required, it is recommended to avoid dropping
|
||||
@@ -642,16 +645,16 @@ impl Command {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the [process creation flags][1] to be passed to `CreateProcess`.
|
||||
///
|
||||
/// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`.
|
||||
///
|
||||
/// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
|
||||
#[cfg(windows)]
|
||||
#[cfg_attr(docsrs, doc(cfg(windows)))]
|
||||
pub fn creation_flags(&mut self, flags: u32) -> &mut Command {
|
||||
self.std.creation_flags(flags);
|
||||
self
|
||||
cfg_windows! {
|
||||
/// Sets the [process creation flags][1] to be passed to `CreateProcess`.
|
||||
///
|
||||
/// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`.
|
||||
///
|
||||
/// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
|
||||
pub fn creation_flags(&mut self, flags: u32) -> &mut Command {
|
||||
self.std.creation_flags(flags);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the child process's user ID. This translates to a
|
||||
@@ -811,7 +814,7 @@ impl Command {
|
||||
/// from being spawned.
|
||||
///
|
||||
/// The tokio runtime will, on a best-effort basis, attempt to reap and clean up
|
||||
/// any process which it has spawned. No additional guarantees are made with regards
|
||||
/// any process which it has spawned. No additional guarantees are made with regard to
|
||||
/// how quickly or how often this procedure will take place.
|
||||
///
|
||||
/// It is recommended to avoid dropping a [`Child`] process handle before it has been
|
||||
@@ -1082,13 +1085,14 @@ impl Child {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the raw handle of the process associated with this child while
|
||||
/// it is still running. Returns `None` if the child has exited.
|
||||
#[cfg(windows)]
|
||||
pub fn raw_handle(&self) -> Option<RawHandle> {
|
||||
match &self.child {
|
||||
FusedChild::Child(c) => Some(c.inner.as_raw_handle()),
|
||||
FusedChild::Done(_) => None,
|
||||
cfg_windows! {
|
||||
/// Extracts the raw handle of the process associated with this child while
|
||||
/// it is still running. Returns `None` if the child has exited.
|
||||
pub fn raw_handle(&self) -> Option<RawHandle> {
|
||||
match &self.child {
|
||||
FusedChild::Child(c) => Some(c.inner.as_raw_handle()),
|
||||
FusedChild::Done(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1323,7 +1327,7 @@ impl ChildStdin {
|
||||
}
|
||||
|
||||
impl ChildStdout {
|
||||
/// Creates an asynchronous `ChildStderr` from a synchronous one.
|
||||
/// Creates an asynchronous `ChildStdout` from a synchronous one.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@@ -1428,6 +1432,8 @@ impl TryInto<Stdio> for ChildStderr {
|
||||
|
||||
#[cfg(unix)]
|
||||
mod sys {
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use super::{ChildStderr, ChildStdin, ChildStdout};
|
||||
@@ -1438,42 +1444,79 @@ mod sys {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for ChildStdin {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for ChildStdout {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.inner.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for ChildStdout {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for ChildStderr {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.inner.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for ChildStderr {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod sys {
|
||||
use std::os::windows::io::{AsRawHandle, RawHandle};
|
||||
|
||||
use super::{ChildStderr, ChildStdin, ChildStdout};
|
||||
|
||||
cfg_windows! {
|
||||
impl AsRawHandle for ChildStdin {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
self.inner.as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for ChildStdin {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawHandle for ChildStdout {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
self.inner.as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for ChildStdout {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawHandle for ChildStderr {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
self.inner.as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsHandle for ChildStderr {
|
||||
fn as_handle(&self) -> BorrowedHandle<'_> {
|
||||
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, not(loom)))]
|
||||
|
||||
@@ -39,6 +39,8 @@ use std::fmt;
|
||||
use std::fs::File;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::pin::Pin;
|
||||
use std::process::{Child as StdChild, ExitStatus, Stdio};
|
||||
@@ -194,6 +196,13 @@ impl AsRawFd for Pipe {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for Pipe {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn convert_to_stdio(io: ChildStdio) -> io::Result<Stdio> {
|
||||
let mut fd = io.inner.into_inner()?.fd;
|
||||
|
||||
@@ -246,6 +255,13 @@ impl AsRawFd for ChildStdio {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_no_as_fd))]
|
||||
impl AsFd for ChildStdio {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for ChildStdio {
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
|
||||
@@ -371,7 +371,9 @@ impl Spawner {
|
||||
task.name = %name.unwrap_or_default(),
|
||||
task.id = id.as_u64(),
|
||||
"fn" = %std::any::type_name::<F>(),
|
||||
spawn.location = %format_args!("{}:{}:{}", location.file(), location.line(), location.column()),
|
||||
loc.file = location.file(),
|
||||
loc.line = location.line(),
|
||||
loc.col = location.column(),
|
||||
);
|
||||
fut.instrument(span)
|
||||
};
|
||||
|
||||
@@ -79,7 +79,7 @@ tokio_thread_local! {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg(any(feature = "macros", all(feature = "sync", feature = "rt")))]
|
||||
pub(crate) fn thread_rng_n(n: u32) -> u32 {
|
||||
CONTEXT.with(|ctx| ctx.rng.fastrand_n(n))
|
||||
}
|
||||
|
||||
@@ -36,6 +36,11 @@ use crate::runtime::context;
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct Budget(Option<u8>);
|
||||
|
||||
pub(crate) struct BudgetDecrement {
|
||||
success: bool,
|
||||
hit_zero: bool,
|
||||
}
|
||||
|
||||
impl Budget {
|
||||
/// Budget assigned to a task on each poll.
|
||||
///
|
||||
@@ -172,9 +177,17 @@ cfg_coop! {
|
||||
context::budget(|cell| {
|
||||
let mut budget = cell.get();
|
||||
|
||||
if budget.decrement() {
|
||||
let decrement = budget.decrement();
|
||||
|
||||
if decrement.success {
|
||||
let restore = RestoreOnPending(Cell::new(cell.get()));
|
||||
cell.set(budget);
|
||||
|
||||
// avoid double counting
|
||||
if decrement.hit_zero {
|
||||
inc_budget_forced_yield_count();
|
||||
}
|
||||
|
||||
Poll::Ready(restore)
|
||||
} else {
|
||||
cx.waker().wake_by_ref();
|
||||
@@ -183,19 +196,43 @@ cfg_coop! {
|
||||
}).unwrap_or(Poll::Ready(RestoreOnPending(Cell::new(Budget::unconstrained()))))
|
||||
}
|
||||
|
||||
cfg_rt! {
|
||||
cfg_metrics! {
|
||||
#[inline(always)]
|
||||
fn inc_budget_forced_yield_count() {
|
||||
if let Ok(handle) = context::try_current() {
|
||||
handle.scheduler_metrics().inc_budget_forced_yield_count();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg_not_metrics! {
|
||||
#[inline(always)]
|
||||
fn inc_budget_forced_yield_count() {}
|
||||
}
|
||||
}
|
||||
|
||||
cfg_not_rt! {
|
||||
#[inline(always)]
|
||||
fn inc_budget_forced_yield_count() {}
|
||||
}
|
||||
|
||||
impl Budget {
|
||||
/// Decrements the budget. Returns `true` if successful. Decrementing fails
|
||||
/// when there is not enough remaining budget.
|
||||
fn decrement(&mut self) -> bool {
|
||||
fn decrement(&mut self) -> BudgetDecrement {
|
||||
if let Some(num) = &mut self.0 {
|
||||
if *num > 0 {
|
||||
*num -= 1;
|
||||
true
|
||||
|
||||
let hit_zero = *num == 0;
|
||||
|
||||
BudgetDecrement { success: true, hit_zero }
|
||||
} else {
|
||||
false
|
||||
BudgetDecrement { success: false, hit_zero: false }
|
||||
}
|
||||
} else {
|
||||
true
|
||||
BudgetDecrement { success: true, hit_zero: false }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ impl Handle {
|
||||
self.spawn_named(future, None)
|
||||
}
|
||||
|
||||
/// Runs the provided function on an executor dedicated to blocking.
|
||||
/// Runs the provided function on an executor dedicated to blocking
|
||||
/// operations.
|
||||
///
|
||||
/// # Examples
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::runtime::WorkerMetrics;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::time::Instant;
|
||||
|
||||
|
||||
@@ -124,6 +124,21 @@ impl RuntimeMetrics {
|
||||
.load(Relaxed)
|
||||
}
|
||||
|
||||
/// Returns the number of times that tasks have been forced to yield back to the scheduler
|
||||
/// after exhausting their task budgets.
|
||||
///
|
||||
/// This count starts at zero when the runtime is created and increases by one each time a task yields due to exhausting its budget.
|
||||
///
|
||||
/// The counter is monotonically increasing. It is never decremented or
|
||||
/// reset to zero.
|
||||
pub fn budget_forced_yield_count(&self) -> u64 {
|
||||
self.handle
|
||||
.inner
|
||||
.scheduler_metrics()
|
||||
.budget_forced_yield_count
|
||||
.load(Relaxed)
|
||||
}
|
||||
|
||||
/// Returns the total number of times the given worker thread has parked.
|
||||
///
|
||||
/// The worker park count starts at zero when the runtime is created and
|
||||
|
||||
@@ -11,12 +11,14 @@ use crate::loom::sync::atomic::{AtomicU64, Ordering::Relaxed};
|
||||
pub(crate) struct SchedulerMetrics {
|
||||
/// Number of tasks that are scheduled from outside the runtime.
|
||||
pub(super) remote_schedule_count: AtomicU64,
|
||||
pub(super) budget_forced_yield_count: AtomicU64,
|
||||
}
|
||||
|
||||
impl SchedulerMetrics {
|
||||
pub(crate) fn new() -> SchedulerMetrics {
|
||||
SchedulerMetrics {
|
||||
remote_schedule_count: AtomicU64::new(0),
|
||||
budget_forced_yield_count: AtomicU64::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,4 +26,9 @@ impl SchedulerMetrics {
|
||||
pub(crate) fn inc_remote_schedule_count(&self) {
|
||||
self.remote_schedule_count.fetch_add(1, Relaxed);
|
||||
}
|
||||
|
||||
/// Increment the number of tasks forced to yield due to budget exhaustion
|
||||
pub(crate) fn inc_budget_forced_yield_count(&self) {
|
||||
self.budget_forced_yield_count.fetch_add(1, Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +240,13 @@ impl Runtime {
|
||||
/// complete, and yielding its resolved result. Any tasks or timers
|
||||
/// which the future spawns internally will be executed on the runtime.
|
||||
///
|
||||
/// # Non-worker future
|
||||
///
|
||||
/// Note that the future required by this function does not run as a
|
||||
/// worker. The expectation is that other tasks are spawned by the future here.
|
||||
/// Awaiting on other futures from the future provided here will not
|
||||
/// perform as fast as those spawned as workers.
|
||||
///
|
||||
/// # Multi thread scheduler
|
||||
///
|
||||
/// When the multi thread scheduler is used this will allow futures
|
||||
|
||||
@@ -182,28 +182,28 @@ impl<T> JoinHandle<T> {
|
||||
/// ```rust
|
||||
/// use tokio::time;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let mut handles = Vec::new();
|
||||
/// # #[tokio::main(flavor = "current_thread", start_paused = true)]
|
||||
/// # async fn main() {
|
||||
/// let mut handles = Vec::new();
|
||||
///
|
||||
/// handles.push(tokio::spawn(async {
|
||||
/// time::sleep(time::Duration::from_secs(10)).await;
|
||||
/// true
|
||||
/// }));
|
||||
/// handles.push(tokio::spawn(async {
|
||||
/// time::sleep(time::Duration::from_secs(10)).await;
|
||||
/// true
|
||||
/// }));
|
||||
///
|
||||
/// handles.push(tokio::spawn(async {
|
||||
/// time::sleep(time::Duration::from_secs(10)).await;
|
||||
/// false
|
||||
/// }));
|
||||
/// handles.push(tokio::spawn(async {
|
||||
/// time::sleep(time::Duration::from_secs(10)).await;
|
||||
/// false
|
||||
/// }));
|
||||
///
|
||||
/// for handle in &handles {
|
||||
/// handle.abort();
|
||||
/// }
|
||||
///
|
||||
/// for handle in handles {
|
||||
/// assert!(handle.await.unwrap_err().is_cancelled());
|
||||
/// }
|
||||
/// for handle in &handles {
|
||||
/// handle.abort();
|
||||
/// }
|
||||
///
|
||||
/// for handle in handles {
|
||||
/// assert!(handle.await.unwrap_err().is_cancelled());
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
/// [cancelled]: method@super::error::JoinError::is_cancelled
|
||||
pub fn abort(&self) {
|
||||
@@ -220,9 +220,8 @@ impl<T> JoinHandle<T> {
|
||||
/// ```rust
|
||||
/// use tokio::time;
|
||||
///
|
||||
/// # #[tokio::main(flavor = "current_thread")]
|
||||
/// # #[tokio::main(flavor = "current_thread", start_paused = true)]
|
||||
/// # async fn main() {
|
||||
/// # time::pause();
|
||||
/// let handle1 = tokio::spawn(async {
|
||||
/// // do some stuff here
|
||||
/// });
|
||||
@@ -252,7 +251,41 @@ impl<T> JoinHandle<T> {
|
||||
}
|
||||
|
||||
/// Returns a new `AbortHandle` that can be used to remotely abort this task.
|
||||
pub(crate) fn abort_handle(&self) -> super::AbortHandle {
|
||||
///
|
||||
/// Awaiting a task cancelled by the `AbortHandle` might complete as usual if the task was
|
||||
/// already completed at the time it was cancelled, but most likely it
|
||||
/// will fail with a [cancelled] `JoinError`.
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::{time, task};
|
||||
///
|
||||
/// # #[tokio::main(flavor = "current_thread", start_paused = true)]
|
||||
/// # async fn main() {
|
||||
/// let mut handles = Vec::new();
|
||||
///
|
||||
/// handles.push(tokio::spawn(async {
|
||||
/// time::sleep(time::Duration::from_secs(10)).await;
|
||||
/// true
|
||||
/// }));
|
||||
///
|
||||
/// handles.push(tokio::spawn(async {
|
||||
/// time::sleep(time::Duration::from_secs(10)).await;
|
||||
/// false
|
||||
/// }));
|
||||
///
|
||||
/// let abort_handles: Vec<task::AbortHandle> = handles.iter().map(|h| h.abort_handle()).collect();
|
||||
///
|
||||
/// for handle in abort_handles {
|
||||
/// handle.abort();
|
||||
/// }
|
||||
///
|
||||
/// for handle in handles {
|
||||
/// assert!(handle.await.unwrap_err().is_cancelled());
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
/// [cancelled]: method@super::error::JoinError::is_cancelled
|
||||
pub fn abort_handle(&self) -> super::AbortHandle {
|
||||
self.raw.ref_inc();
|
||||
super::AbortHandle::new(self.raw)
|
||||
}
|
||||
|
||||
+46
-139
@@ -94,7 +94,7 @@ pub(super) struct StateCell {
|
||||
/// without holding the driver lock is undefined behavior.
|
||||
result: UnsafeCell<TimerResult>,
|
||||
/// The currently-registered waker
|
||||
waker: CachePadded<AtomicWaker>,
|
||||
waker: AtomicWaker,
|
||||
}
|
||||
|
||||
impl Default for StateCell {
|
||||
@@ -114,7 +114,7 @@ impl StateCell {
|
||||
Self {
|
||||
state: AtomicU64::new(STATE_DEREGISTERED),
|
||||
result: UnsafeCell::new(Ok(())),
|
||||
waker: CachePadded(AtomicWaker::new()),
|
||||
waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ impl StateCell {
|
||||
// We must register first. This ensures that either `fire` will
|
||||
// observe the new waker, or we will observe a racing fire to have set
|
||||
// the state, or both.
|
||||
self.waker.0.register_by_ref(waker);
|
||||
self.waker.register_by_ref(waker);
|
||||
|
||||
self.read_state()
|
||||
}
|
||||
@@ -227,7 +227,7 @@ impl StateCell {
|
||||
|
||||
self.state.store(STATE_DEREGISTERED, Ordering::Release);
|
||||
|
||||
self.waker.0.take_waker()
|
||||
self.waker.take_waker()
|
||||
}
|
||||
|
||||
/// Marks the timer as registered (poll will return None) and sets the
|
||||
@@ -331,11 +331,20 @@ pub(super) type EntryList = crate::util::linked_list::LinkedList<TimerShared, Ti
|
||||
/// frontend (`Entry`) and driver backend.
|
||||
///
|
||||
/// Note that this structure is located inside the `TimerEntry` structure.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct TimerShared {
|
||||
/// Data manipulated by the driver thread itself, only.
|
||||
driver_state: CachePadded<TimerSharedPadded>,
|
||||
/// A link within the doubly-linked list of timers on a particular level and
|
||||
/// slot. Valid only if state is equal to Registered.
|
||||
///
|
||||
/// Only accessed under the entry lock.
|
||||
pointers: linked_list::Pointers<TimerShared>,
|
||||
|
||||
/// The expiration time for which this entry is currently registered.
|
||||
/// Generally owned by the driver, but is accessed by the entry when not
|
||||
/// registered.
|
||||
cached_when: AtomicU64,
|
||||
|
||||
/// The true expiration time. Set by the timer future, read by the driver.
|
||||
true_when: AtomicU64,
|
||||
|
||||
/// Current state. This records whether the timer entry is currently under
|
||||
/// the ownership of the driver, and if not, its current state (not
|
||||
@@ -345,10 +354,23 @@ pub(crate) struct TimerShared {
|
||||
_p: PhantomPinned,
|
||||
}
|
||||
|
||||
unsafe impl Send for TimerShared {}
|
||||
unsafe impl Sync for TimerShared {}
|
||||
|
||||
impl std::fmt::Debug for TimerShared {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TimerShared")
|
||||
.field("when", &self.true_when.load(Ordering::Relaxed))
|
||||
.field("cached_when", &self.cached_when.load(Ordering::Relaxed))
|
||||
.field("state", &self.state)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
generate_addr_of_methods! {
|
||||
impl<> TimerShared {
|
||||
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<TimerShared>> {
|
||||
&self.driver_state.0.pointers
|
||||
&self.pointers
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,8 +378,10 @@ generate_addr_of_methods! {
|
||||
impl TimerShared {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
cached_when: AtomicU64::new(0),
|
||||
true_when: AtomicU64::new(0),
|
||||
pointers: linked_list::Pointers::new(),
|
||||
state: StateCell::default(),
|
||||
driver_state: CachePadded(TimerSharedPadded::new()),
|
||||
_p: PhantomPinned,
|
||||
}
|
||||
}
|
||||
@@ -365,7 +389,7 @@ impl TimerShared {
|
||||
/// Gets the cached time-of-expiration value.
|
||||
pub(super) fn cached_when(&self) -> u64 {
|
||||
// Cached-when is only accessed under the driver lock, so we can use relaxed
|
||||
self.driver_state.0.cached_when.load(Ordering::Relaxed)
|
||||
self.cached_when.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Gets the true time-of-expiration value, and copies it into the cached
|
||||
@@ -376,10 +400,7 @@ impl TimerShared {
|
||||
pub(super) unsafe fn sync_when(&self) -> u64 {
|
||||
let true_when = self.true_when();
|
||||
|
||||
self.driver_state
|
||||
.0
|
||||
.cached_when
|
||||
.store(true_when, Ordering::Relaxed);
|
||||
self.cached_when.store(true_when, Ordering::Relaxed);
|
||||
|
||||
true_when
|
||||
}
|
||||
@@ -389,10 +410,7 @@ impl TimerShared {
|
||||
/// SAFETY: Must be called with the driver lock held, and when this entry is
|
||||
/// not in any timer wheel lists.
|
||||
unsafe fn set_cached_when(&self, when: u64) {
|
||||
self.driver_state
|
||||
.0
|
||||
.cached_when
|
||||
.store(when, Ordering::Relaxed);
|
||||
self.cached_when.store(when, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Returns the true time-of-expiration value, with relaxed memory ordering.
|
||||
@@ -407,7 +425,7 @@ impl TimerShared {
|
||||
/// in the timer wheel.
|
||||
pub(super) unsafe fn set_expiration(&self, t: u64) {
|
||||
self.state.set_expiration(t);
|
||||
self.driver_state.0.cached_when.store(t, Ordering::Relaxed);
|
||||
self.cached_when.store(t, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Sets the true time-of-expiration only if it is after the current.
|
||||
@@ -431,48 +449,6 @@ impl TimerShared {
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional shared state between the driver and the timer which is cache
|
||||
/// padded. This contains the information that the driver thread accesses most
|
||||
/// frequently to minimize contention. In particular, we move it away from the
|
||||
/// waker, as the waker is updated on every poll.
|
||||
struct TimerSharedPadded {
|
||||
/// A link within the doubly-linked list of timers on a particular level and
|
||||
/// slot. Valid only if state is equal to Registered.
|
||||
///
|
||||
/// Only accessed under the entry lock.
|
||||
pointers: linked_list::Pointers<TimerShared>,
|
||||
|
||||
/// The expiration time for which this entry is currently registered.
|
||||
/// Generally owned by the driver, but is accessed by the entry when not
|
||||
/// registered.
|
||||
cached_when: AtomicU64,
|
||||
|
||||
/// The true expiration time. Set by the timer future, read by the driver.
|
||||
true_when: AtomicU64,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for TimerSharedPadded {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TimerSharedPadded")
|
||||
.field("when", &self.true_when.load(Ordering::Relaxed))
|
||||
.field("cached_when", &self.cached_when.load(Ordering::Relaxed))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl TimerSharedPadded {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
cached_when: AtomicU64::new(0),
|
||||
true_when: AtomicU64::new(0),
|
||||
pointers: linked_list::Pointers::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for TimerShared {}
|
||||
unsafe impl Sync for TimerShared {}
|
||||
|
||||
unsafe impl linked_list::Link for TimerShared {
|
||||
type Handle = TimerHandle;
|
||||
|
||||
@@ -551,9 +527,9 @@ impl TimerEntry {
|
||||
unsafe { self.driver().clear_entry(NonNull::from(self.inner())) };
|
||||
}
|
||||
|
||||
pub(crate) fn reset(mut self: Pin<&mut Self>, new_time: Instant) {
|
||||
pub(crate) fn reset(mut self: Pin<&mut Self>, new_time: Instant, reregister: bool) {
|
||||
unsafe { self.as_mut().get_unchecked_mut() }.deadline = new_time;
|
||||
unsafe { self.as_mut().get_unchecked_mut() }.registered = true;
|
||||
unsafe { self.as_mut().get_unchecked_mut() }.registered = reregister;
|
||||
|
||||
let tick = self.driver().time_source().deadline_to_tick(new_time);
|
||||
|
||||
@@ -561,9 +537,11 @@ impl TimerEntry {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.driver()
|
||||
.reregister(&self.driver.driver().io, tick, self.inner().into());
|
||||
if reregister {
|
||||
unsafe {
|
||||
self.driver()
|
||||
.reregister(&self.driver.driver().io, tick, self.inner().into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,7 +555,7 @@ impl TimerEntry {
|
||||
|
||||
if !self.registered {
|
||||
let deadline = self.deadline;
|
||||
self.as_mut().reset(deadline);
|
||||
self.as_mut().reset(deadline, true);
|
||||
}
|
||||
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
@@ -660,74 +638,3 @@ impl Drop for TimerEntry {
|
||||
unsafe { Pin::new_unchecked(self) }.as_mut().cancel()
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from [crossbeam/cache_padded](https://github.com/crossbeam-rs/crossbeam/blob/fa35346b7c789bba045ad789e894c68c466d1779/crossbeam-utils/src/cache_padded.rs#L62-L127)
|
||||
//
|
||||
// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache
|
||||
// lines at a time, so we have to align to 128 bytes rather than 64.
|
||||
//
|
||||
// Sources:
|
||||
// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
|
||||
// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107
|
||||
//
|
||||
// ARM's big.LITTLE architecture has asymmetric cores and "big" cores have 128-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://www.mono-project.com/news/2016/09/12/arm64-icache/
|
||||
//
|
||||
// powerpc64 has 128-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9
|
||||
#[cfg_attr(
|
||||
any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "powerpc64",
|
||||
),
|
||||
repr(align(128))
|
||||
)]
|
||||
// arm, mips, mips64, and riscv64 have 32-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_riscv64.go#L7
|
||||
#[cfg_attr(
|
||||
any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
target_arch = "riscv64",
|
||||
),
|
||||
repr(align(32))
|
||||
)]
|
||||
// s390x has 256-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7
|
||||
#[cfg_attr(target_arch = "s390x", repr(align(256)))]
|
||||
// x86 and wasm have 64-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7
|
||||
//
|
||||
// All others are assumed to have 64-byte cache line size.
|
||||
#[cfg_attr(
|
||||
not(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "s390x",
|
||||
)),
|
||||
repr(align(64))
|
||||
)]
|
||||
#[derive(Debug, Default)]
|
||||
struct CachePadded<T>(T);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use crate::time::{Clock, Duration, Instant};
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
/// A structure which handles conversion from Instants to u64 timestamps.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct TimeSource {
|
||||
|
||||
@@ -164,7 +164,7 @@ fn reset_future() {
|
||||
.as_mut()
|
||||
.poll_elapsed(&mut Context::from_waker(futures::task::noop_waker_ref()));
|
||||
|
||||
entry.as_mut().reset(start + Duration::from_secs(2));
|
||||
entry.as_mut().reset(start + Duration::from_secs(2), true);
|
||||
|
||||
// shouldn't complete before 2s
|
||||
block_on(futures::future::poll_fn(|cx| {
|
||||
|
||||
@@ -148,23 +148,13 @@ impl Wheel {
|
||||
return Some(handle);
|
||||
}
|
||||
|
||||
// under what circumstances is poll.expiration Some vs. None?
|
||||
let expiration = self.next_expiration().and_then(|expiration| {
|
||||
if expiration.deadline > now {
|
||||
None
|
||||
} else {
|
||||
Some(expiration)
|
||||
}
|
||||
});
|
||||
|
||||
match expiration {
|
||||
Some(ref expiration) if expiration.deadline > now => return None,
|
||||
Some(ref expiration) => {
|
||||
match self.next_expiration() {
|
||||
Some(ref expiration) if expiration.deadline <= now => {
|
||||
self.process_expiration(expiration);
|
||||
|
||||
self.set_elapsed(expiration.deadline);
|
||||
}
|
||||
None => {
|
||||
_ => {
|
||||
// in this case the poll did not indicate an expiration
|
||||
// _and_ we were not able to find a next expiration in
|
||||
// the current list of timers. advance to the poll's
|
||||
|
||||
@@ -292,7 +292,11 @@ fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A stream of events for receiving a particular type of OS signal.
|
||||
/// An listener for receiving a particular type of OS signal.
|
||||
///
|
||||
/// The listener can be turned into a `Stream` using [`SignalStream`].
|
||||
///
|
||||
/// [`SignalStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.SignalStream.html
|
||||
///
|
||||
/// In general signal handling on Unix is a pretty tricky topic, and this
|
||||
/// structure is no exception! There are some important limitations to keep in
|
||||
@@ -307,7 +311,7 @@ fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> {
|
||||
/// Once `poll` has been called, however, a further signal is guaranteed to
|
||||
/// be yielded as an item.
|
||||
///
|
||||
/// Put another way, any element pulled off the returned stream corresponds to
|
||||
/// Put another way, any element pulled off the returned listener corresponds to
|
||||
/// *at least one* signal, but possibly more.
|
||||
///
|
||||
/// * Signal handling in general is relatively inefficient. Although some
|
||||
@@ -345,11 +349,11 @@ fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> {
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of hangup signals.
|
||||
/// let mut stream = signal(SignalKind::hangup())?;
|
||||
/// let mut sig = signal(SignalKind::hangup())?;
|
||||
///
|
||||
/// // Print whenever a HUP signal is received
|
||||
/// loop {
|
||||
/// stream.recv().await;
|
||||
/// sig.recv().await;
|
||||
/// println!("got signal HUP");
|
||||
/// }
|
||||
/// }
|
||||
@@ -360,7 +364,7 @@ pub struct Signal {
|
||||
inner: RxFuture,
|
||||
}
|
||||
|
||||
/// Creates a new stream which will receive notifications when the current
|
||||
/// Creates a new listener which will receive notifications when the current
|
||||
/// process receives the specified signal `kind`.
|
||||
///
|
||||
/// This function will create a new stream which binds to the default reactor.
|
||||
|
||||
+72
-65
@@ -22,7 +22,7 @@ pub(crate) use self::imp::{OsExtraData, OsStorage};
|
||||
#[path = "windows/stub.rs"]
|
||||
mod imp;
|
||||
|
||||
/// Creates a new stream which receives "ctrl-c" notifications sent to the
|
||||
/// Creates a new listener which receives "ctrl-c" notifications sent to the
|
||||
/// process.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -32,12 +32,12 @@ mod imp;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of CTRL-C events.
|
||||
/// let mut stream = ctrl_c()?;
|
||||
/// // A listener of CTRL-C events.
|
||||
/// let mut signal = ctrl_c()?;
|
||||
///
|
||||
/// // Print whenever a CTRL-C event is received.
|
||||
/// for countdown in (0..3).rev() {
|
||||
/// stream.recv().await;
|
||||
/// signal.recv().await;
|
||||
/// println!("got CTRL-C. {} more to exit", countdown);
|
||||
/// }
|
||||
///
|
||||
@@ -50,14 +50,18 @@ pub fn ctrl_c() -> io::Result<CtrlC> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Represents a stream which receives "ctrl-c" notifications sent to the process
|
||||
/// Represents a listener which receives "ctrl-c" notifications sent to the process
|
||||
/// via `SetConsoleCtrlHandler`.
|
||||
///
|
||||
/// A notification to this process notifies *all* streams listening for
|
||||
/// This event can be turned into a `Stream` using [`CtrlCStream`].
|
||||
///
|
||||
/// [`CtrlCStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.CtrlCStream.html
|
||||
///
|
||||
/// A notification to this process notifies *all* receivers for
|
||||
/// this event. Moreover, the notifications **are coalesced** if they aren't processed
|
||||
/// quickly enough. This means that if two notifications are received back-to-back,
|
||||
/// then the stream may only receive one item about the two notifications.
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
/// then the listener may only receive one item about the two notifications.
|
||||
#[must_use = "listeners do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct CtrlC {
|
||||
inner: RxFuture,
|
||||
@@ -66,7 +70,7 @@ pub struct CtrlC {
|
||||
impl CtrlC {
|
||||
/// Receives the next signal notification event.
|
||||
///
|
||||
/// `None` is returned if no more events can be received by this stream.
|
||||
/// `None` is returned if no more events can be received by the listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -75,12 +79,11 @@ impl CtrlC {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of CTRL-C events.
|
||||
/// let mut stream = ctrl_c()?;
|
||||
/// let mut signal = ctrl_c()?;
|
||||
///
|
||||
/// // Print whenever a CTRL-C event is received.
|
||||
/// for countdown in (0..3).rev() {
|
||||
/// stream.recv().await;
|
||||
/// signal.recv().await;
|
||||
/// println!("got CTRL-C. {} more to exit", countdown);
|
||||
/// }
|
||||
///
|
||||
@@ -94,7 +97,7 @@ impl CtrlC {
|
||||
/// Polls to receive the next signal notification event, outside of an
|
||||
/// `async` context.
|
||||
///
|
||||
/// `None` is returned if no more events can be received by this stream.
|
||||
/// `None` is returned if no more events can be received.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -124,14 +127,18 @@ impl CtrlC {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a stream which receives "ctrl-break" notifications sent to the process
|
||||
/// Represents a listener which receives "ctrl-break" notifications sent to the process
|
||||
/// via `SetConsoleCtrlHandler`.
|
||||
///
|
||||
/// A notification to this process notifies *all* streams listening for
|
||||
/// This listener can be turned into a `Stream` using [`CtrlBreakStream`].
|
||||
///
|
||||
/// [`CtrlBreakStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.CtrlBreakStream.html
|
||||
///
|
||||
/// A notification to this process notifies *all* receivers for
|
||||
/// this event. Moreover, the notifications **are coalesced** if they aren't processed
|
||||
/// quickly enough. This means that if two notifications are received back-to-back,
|
||||
/// then the stream may only receive one item about the two notifications.
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
/// then the listener may only receive one item about the two notifications.
|
||||
#[must_use = "listeners do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct CtrlBreak {
|
||||
inner: RxFuture,
|
||||
@@ -140,7 +147,7 @@ pub struct CtrlBreak {
|
||||
impl CtrlBreak {
|
||||
/// Receives the next signal notification event.
|
||||
///
|
||||
/// `None` is returned if no more events can be received by this stream.
|
||||
/// `None` is returned if no more events can be received by this listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -149,12 +156,12 @@ impl CtrlBreak {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of CTRL-BREAK events.
|
||||
/// let mut stream = ctrl_break()?;
|
||||
/// // A listener of CTRL-BREAK events.
|
||||
/// let mut signal = ctrl_break()?;
|
||||
///
|
||||
/// // Print whenever a CTRL-BREAK event is received.
|
||||
/// loop {
|
||||
/// stream.recv().await;
|
||||
/// signal.recv().await;
|
||||
/// println!("got signal CTRL-BREAK");
|
||||
/// }
|
||||
/// }
|
||||
@@ -166,7 +173,7 @@ impl CtrlBreak {
|
||||
/// Polls to receive the next signal notification event, outside of an
|
||||
/// `async` context.
|
||||
///
|
||||
/// `None` is returned if no more events can be received by this stream.
|
||||
/// `None` is returned if no more events can be received by this listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -196,7 +203,7 @@ impl CtrlBreak {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new stream which receives "ctrl-break" notifications sent to the
|
||||
/// Creates a new listener which receives "ctrl-break" notifications sent to the
|
||||
/// process.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -206,12 +213,12 @@ impl CtrlBreak {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of CTRL-BREAK events.
|
||||
/// let mut stream = ctrl_break()?;
|
||||
/// // A listener of CTRL-BREAK events.
|
||||
/// let mut signal = ctrl_break()?;
|
||||
///
|
||||
/// // Print whenever a CTRL-BREAK event is received.
|
||||
/// loop {
|
||||
/// stream.recv().await;
|
||||
/// signal.recv().await;
|
||||
/// println!("got signal CTRL-BREAK");
|
||||
/// }
|
||||
/// }
|
||||
@@ -222,7 +229,7 @@ pub fn ctrl_break() -> io::Result<CtrlBreak> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new stream which receives "ctrl-close" notifications sent to the
|
||||
/// Creates a new listener which receives "ctrl-close" notifications sent to the
|
||||
/// process.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -232,12 +239,12 @@ pub fn ctrl_break() -> io::Result<CtrlBreak> {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of CTRL-CLOSE events.
|
||||
/// let mut stream = ctrl_close()?;
|
||||
/// // A listener of CTRL-CLOSE events.
|
||||
/// let mut signal = ctrl_close()?;
|
||||
///
|
||||
/// // Print whenever a CTRL-CLOSE event is received.
|
||||
/// for countdown in (0..3).rev() {
|
||||
/// stream.recv().await;
|
||||
/// signal.recv().await;
|
||||
/// println!("got CTRL-CLOSE. {} more to exit", countdown);
|
||||
/// }
|
||||
///
|
||||
@@ -250,14 +257,14 @@ pub fn ctrl_close() -> io::Result<CtrlClose> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Represents a stream which receives "ctrl-close" notitifications sent to the process
|
||||
/// Represents a listener which receives "ctrl-close" notitifications sent to the process
|
||||
/// via 'SetConsoleCtrlHandler'.
|
||||
///
|
||||
/// A notification to this process notifies *all* streams listening for
|
||||
/// A notification to this process notifies *all* listeners listening for
|
||||
/// this event. Moreover, the notifications **are coalesced** if they aren't processed
|
||||
/// quickly enough. This means that if two notifications are received back-to-back,
|
||||
/// then the stream may only receive one item about the two notifications.
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
/// then the listener may only receive one item about the two notifications.
|
||||
#[must_use = "listeners do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct CtrlClose {
|
||||
inner: RxFuture,
|
||||
@@ -266,7 +273,7 @@ pub struct CtrlClose {
|
||||
impl CtrlClose {
|
||||
/// Receives the next signal notification event.
|
||||
///
|
||||
/// `None` is returned if no more events can be received by this stream.
|
||||
/// `None` is returned if no more events can be received by this listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -275,11 +282,11 @@ impl CtrlClose {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of CTRL-CLOSE events.
|
||||
/// let mut stream = ctrl_close()?;
|
||||
/// // A listener of CTRL-CLOSE events.
|
||||
/// let mut signal = ctrl_close()?;
|
||||
///
|
||||
/// // Print whenever a CTRL-CLOSE event is received.
|
||||
/// stream.recv().await;
|
||||
/// signal.recv().await;
|
||||
/// println!("got CTRL-CLOSE. Cleaning up before exiting");
|
||||
///
|
||||
/// Ok(())
|
||||
@@ -292,7 +299,7 @@ impl CtrlClose {
|
||||
/// Polls to receive the next signal notification event, outside of an
|
||||
/// `async` context.
|
||||
///
|
||||
/// `None` is returned if no more events can be received by this stream.
|
||||
/// `None` is returned if no more events can be received by this listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -322,7 +329,7 @@ impl CtrlClose {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new stream which receives "ctrl-shutdown" notifications sent to the
|
||||
/// Creates a new listener which receives "ctrl-shutdown" notifications sent to the
|
||||
/// process.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -332,10 +339,10 @@ impl CtrlClose {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of CTRL-SHUTDOWN events.
|
||||
/// let mut stream = ctrl_shutdown()?;
|
||||
/// // A listener of CTRL-SHUTDOWN events.
|
||||
/// let mut signal = ctrl_shutdown()?;
|
||||
///
|
||||
/// stream.recv().await;
|
||||
/// signal.recv().await;
|
||||
/// println!("got CTRL-SHUTDOWN. Cleaning up before exiting");
|
||||
///
|
||||
/// Ok(())
|
||||
@@ -347,14 +354,14 @@ pub fn ctrl_shutdown() -> io::Result<CtrlShutdown> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Represents a stream which receives "ctrl-shutdown" notitifications sent to the process
|
||||
/// Represents a listener which receives "ctrl-shutdown" notitifications sent to the process
|
||||
/// via 'SetConsoleCtrlHandler'.
|
||||
///
|
||||
/// A notification to this process notifies *all* streams listening for
|
||||
/// A notification to this process notifies *all* listeners listening for
|
||||
/// this event. Moreover, the notifications **are coalesced** if they aren't processed
|
||||
/// quickly enough. This means that if two notifications are received back-to-back,
|
||||
/// then the stream may only receive one item about the two notifications.
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
/// then the listener may only receive one item about the two notifications.
|
||||
#[must_use = "listeners do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct CtrlShutdown {
|
||||
inner: RxFuture,
|
||||
@@ -363,7 +370,7 @@ pub struct CtrlShutdown {
|
||||
impl CtrlShutdown {
|
||||
/// Receives the next signal notification event.
|
||||
///
|
||||
/// `None` is returned if no more events can be received by this stream.
|
||||
/// `None` is returned if no more events can be received by this listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -372,11 +379,11 @@ impl CtrlShutdown {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of CTRL-SHUTDOWN events.
|
||||
/// let mut stream = ctrl_shutdown()?;
|
||||
/// // A listener of CTRL-SHUTDOWN events.
|
||||
/// let mut signal = ctrl_shutdown()?;
|
||||
///
|
||||
/// // Print whenever a CTRL-SHUTDOWN event is received.
|
||||
/// stream.recv().await;
|
||||
/// signal.recv().await;
|
||||
/// println!("got CTRL-SHUTDOWN. Cleaning up before exiting");
|
||||
///
|
||||
/// Ok(())
|
||||
@@ -389,7 +396,7 @@ impl CtrlShutdown {
|
||||
/// Polls to receive the next signal notification event, outside of an
|
||||
/// `async` context.
|
||||
///
|
||||
/// `None` is returned if no more events can be received by this stream.
|
||||
/// `None` is returned if no more events can be received by this listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -419,7 +426,7 @@ impl CtrlShutdown {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new stream which receives "ctrl-logoff" notifications sent to the
|
||||
/// Creates a new listener which receives "ctrl-logoff" notifications sent to the
|
||||
/// process.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -429,10 +436,10 @@ impl CtrlShutdown {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of CTRL-LOGOFF events.
|
||||
/// let mut stream = ctrl_logoff()?;
|
||||
/// // A listener of CTRL-LOGOFF events.
|
||||
/// let mut signal = ctrl_logoff()?;
|
||||
///
|
||||
/// stream.recv().await;
|
||||
/// signal.recv().await;
|
||||
/// println!("got CTRL-LOGOFF. Cleaning up before exiting");
|
||||
///
|
||||
/// Ok(())
|
||||
@@ -444,14 +451,14 @@ pub fn ctrl_logoff() -> io::Result<CtrlLogoff> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Represents a stream which receives "ctrl-logoff" notitifications sent to the process
|
||||
/// Represents a listener which receives "ctrl-logoff" notitifications sent to the process
|
||||
/// via 'SetConsoleCtrlHandler'.
|
||||
///
|
||||
/// A notification to this process notifies *all* streams listening for
|
||||
/// A notification to this process notifies *all* listeners listening for
|
||||
/// this event. Moreover, the notifications **are coalesced** if they aren't processed
|
||||
/// quickly enough. This means that if two notifications are received back-to-back,
|
||||
/// then the stream may only receive one item about the two notifications.
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
/// then the listener may only receive one item about the two notifications.
|
||||
#[must_use = "listeners do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct CtrlLogoff {
|
||||
inner: RxFuture,
|
||||
@@ -460,7 +467,7 @@ pub struct CtrlLogoff {
|
||||
impl CtrlLogoff {
|
||||
/// Receives the next signal notification event.
|
||||
///
|
||||
/// `None` is returned if no more events can be received by this stream.
|
||||
/// `None` is returned if no more events can be received by this listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -469,11 +476,11 @@ impl CtrlLogoff {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // An infinite stream of CTRL-LOGOFF events.
|
||||
/// let mut stream = ctrl_logoff()?;
|
||||
/// // An listener of CTRL-LOGOFF events.
|
||||
/// let mut signal = ctrl_logoff()?;
|
||||
///
|
||||
/// // Print whenever a CTRL-LOGOFF event is received.
|
||||
/// stream.recv().await;
|
||||
/// signal.recv().await;
|
||||
/// println!("got CTRL-LOGOFF. Cleaning up before exiting");
|
||||
///
|
||||
/// Ok(())
|
||||
@@ -486,7 +493,7 @@ impl CtrlLogoff {
|
||||
/// Polls to receive the next signal notification event, outside of an
|
||||
/// `async` context.
|
||||
///
|
||||
/// `None` is returned if no more events can be received by this stream.
|
||||
/// `None` is returned if no more events can be received by this listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::io;
|
||||
use std::sync::Once;
|
||||
|
||||
|
||||
@@ -444,6 +444,7 @@ impl Semaphore {
|
||||
}
|
||||
|
||||
assert_eq!(acquired, 0);
|
||||
let mut old_waker = None;
|
||||
|
||||
// Otherwise, register the waker & enqueue the node.
|
||||
node.waker.with_mut(|waker| {
|
||||
@@ -455,7 +456,7 @@ impl Semaphore {
|
||||
.map(|waker| !waker.will_wake(cx.waker()))
|
||||
.unwrap_or(true)
|
||||
{
|
||||
*waker = Some(cx.waker().clone());
|
||||
old_waker = std::mem::replace(waker, Some(cx.waker().clone()));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -468,6 +469,8 @@ impl Semaphore {
|
||||
|
||||
waiters.queue.push_front(node);
|
||||
}
|
||||
drop(waiters);
|
||||
drop(old_waker);
|
||||
|
||||
Pending
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! A [`Sender`] is used to broadcast values to **all** connected [`Receiver`]
|
||||
//! values. [`Sender`] handles are clone-able, allowing concurrent send and
|
||||
//! receive actions. [`Sender`] and [`Receiver`] are both `Send` and `Sync` as
|
||||
//! long as `T` is also `Send` or `Sync` respectively.
|
||||
//! long as `T` is `Send`.
|
||||
//!
|
||||
//! When a value is sent, **all** [`Receiver`] handles are notified and will
|
||||
//! receive the value. The value is stored once inside the channel and cloned on
|
||||
@@ -54,6 +54,10 @@
|
||||
//! all values retained by the channel, the next call to [`recv`] will return
|
||||
//! with [`RecvError::Closed`].
|
||||
//!
|
||||
//! When a [`Receiver`] handle is dropped, any messages not read by the receiver
|
||||
//! will be marked as read. If this receiver was the only one not to have read
|
||||
//! that message, the message will be dropped at this point.
|
||||
//!
|
||||
//! [`Sender`]: crate::sync::broadcast::Sender
|
||||
//! [`Sender::subscribe`]: crate::sync::broadcast::Sender::subscribe
|
||||
//! [`Receiver`]: crate::sync::broadcast::Receiver
|
||||
|
||||
@@ -449,7 +449,7 @@ cfg_sync! {
|
||||
pub mod mpsc;
|
||||
|
||||
mod mutex;
|
||||
pub use mutex::{Mutex, MutexGuard, TryLockError, OwnedMutexGuard, MappedMutexGuard};
|
||||
pub use mutex::{Mutex, MutexGuard, TryLockError, OwnedMutexGuard, MappedMutexGuard, OwnedMappedMutexGuard};
|
||||
|
||||
pub(crate) mod notify;
|
||||
pub use notify::Notify;
|
||||
|
||||
@@ -326,6 +326,7 @@ impl<T> Receiver<T> {
|
||||
/// ```
|
||||
#[track_caller]
|
||||
#[cfg(feature = "sync")]
|
||||
#[cfg_attr(docsrs, doc(alias = "recv_blocking"))]
|
||||
pub fn blocking_recv(&mut self) -> Option<T> {
|
||||
crate::future::block_on(self.recv())
|
||||
}
|
||||
@@ -696,6 +697,7 @@ impl<T> Sender<T> {
|
||||
/// ```
|
||||
#[track_caller]
|
||||
#[cfg(feature = "sync")]
|
||||
#[cfg_attr(docsrs, doc(alias = "send_blocking"))]
|
||||
pub fn blocking_send(&self, value: T) -> Result<(), SendError<T>> {
|
||||
crate::future::block_on(self.send(value))
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
//!
|
||||
//! If the [`Receiver`] handle is dropped, then messages can no longer
|
||||
//! be read out of the channel. In this case, all further attempts to send will
|
||||
//! result in an error.
|
||||
//! result in an error. Additionally, all unread messages will be drained from the
|
||||
//! channel and dropped.
|
||||
//!
|
||||
//! # Clean Shutdown
|
||||
//!
|
||||
|
||||
@@ -243,6 +243,7 @@ impl<T> UnboundedReceiver<T> {
|
||||
/// ```
|
||||
#[track_caller]
|
||||
#[cfg(feature = "sync")]
|
||||
#[cfg_attr(docsrs, doc(alias = "recv_blocking"))]
|
||||
pub fn blocking_recv(&mut self) -> Option<T> {
|
||||
crate::future::block_on(self.recv())
|
||||
}
|
||||
|
||||
+405
-64
@@ -6,9 +6,10 @@ use crate::util::trace;
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::error::Error;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, marker, mem};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
/// An asynchronous `Mutex`-like type.
|
||||
///
|
||||
@@ -144,6 +145,8 @@ pub struct Mutex<T: ?Sized> {
|
||||
#[clippy::has_significant_drop]
|
||||
#[must_use = "if unused the Mutex will immediately unlock"]
|
||||
pub struct MutexGuard<'a, T: ?Sized> {
|
||||
// When changing the fields in this struct, make sure to update the
|
||||
// `skip_drop` method.
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
lock: &'a Mutex<T>,
|
||||
@@ -166,6 +169,8 @@ pub struct MutexGuard<'a, T: ?Sized> {
|
||||
/// [`Arc`]: std::sync::Arc
|
||||
#[clippy::has_significant_drop]
|
||||
pub struct OwnedMutexGuard<T: ?Sized> {
|
||||
// When changing the fields in this struct, make sure to update the
|
||||
// `skip_drop` method.
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
lock: Arc<Mutex<T>>,
|
||||
@@ -179,10 +184,68 @@ pub struct OwnedMutexGuard<T: ?Sized> {
|
||||
#[clippy::has_significant_drop]
|
||||
#[must_use = "if unused the Mutex will immediately unlock"]
|
||||
pub struct MappedMutexGuard<'a, T: ?Sized> {
|
||||
// When changing the fields in this struct, make sure to update the
|
||||
// `skip_drop` method.
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
s: &'a semaphore::Semaphore,
|
||||
data: *mut T,
|
||||
// Needed to tell the borrow checker that we are holding a `&mut T`
|
||||
marker: marker::PhantomData<&'a mut T>,
|
||||
marker: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
/// A owned handle to a held `Mutex` that has had a function applied to it via
|
||||
/// [`OwnedMutexGuard::map`].
|
||||
///
|
||||
/// This can be used to hold a subfield of the protected data.
|
||||
///
|
||||
/// [`OwnedMutexGuard::map`]: method@OwnedMutexGuard::map
|
||||
#[clippy::has_significant_drop]
|
||||
#[must_use = "if unused the Mutex will immediately unlock"]
|
||||
pub struct OwnedMappedMutexGuard<T: ?Sized, U: ?Sized = T> {
|
||||
// When changing the fields in this struct, make sure to update the
|
||||
// `skip_drop` method.
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
data: *mut U,
|
||||
lock: Arc<Mutex<T>>,
|
||||
}
|
||||
|
||||
/// A helper type used when taking apart a `MutexGuard` without running its
|
||||
/// Drop implementation.
|
||||
#[allow(dead_code)] // Unused fields are still used in Drop.
|
||||
struct MutexGuardInner<'a, T: ?Sized> {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
lock: &'a Mutex<T>,
|
||||
}
|
||||
|
||||
/// A helper type used when taking apart a `OwnedMutexGuard` without running
|
||||
/// its Drop implementation.
|
||||
struct OwnedMutexGuardInner<T: ?Sized> {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
lock: Arc<Mutex<T>>,
|
||||
}
|
||||
|
||||
/// A helper type used when taking apart a `MappedMutexGuard` without running
|
||||
/// its Drop implementation.
|
||||
#[allow(dead_code)] // Unused fields are still used in Drop.
|
||||
struct MappedMutexGuardInner<'a, T: ?Sized> {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
s: &'a semaphore::Semaphore,
|
||||
data: *mut T,
|
||||
}
|
||||
|
||||
/// A helper type used when taking apart a `OwnedMappedMutexGuard` without running
|
||||
/// its Drop implementation.
|
||||
#[allow(dead_code)] // Unused fields are still used in Drop.
|
||||
struct OwnedMappedMutexGuardInner<T: ?Sized, U: ?Sized> {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
data: *mut U,
|
||||
lock: Arc<Mutex<T>>,
|
||||
}
|
||||
|
||||
// As long as T: Send, it's fine to send and share Mutex<T> between threads.
|
||||
@@ -195,6 +258,19 @@ unsafe impl<T> Sync for OwnedMutexGuard<T> where T: ?Sized + Send + Sync {}
|
||||
unsafe impl<'a, T> Sync for MappedMutexGuard<'a, T> where T: ?Sized + Sync + 'a {}
|
||||
unsafe impl<'a, T> Send for MappedMutexGuard<'a, T> where T: ?Sized + Send + 'a {}
|
||||
|
||||
unsafe impl<T, U> Sync for OwnedMappedMutexGuard<T, U>
|
||||
where
|
||||
T: ?Sized + Send + Sync,
|
||||
U: ?Sized + Send + Sync,
|
||||
{
|
||||
}
|
||||
unsafe impl<T, U> Send for OwnedMappedMutexGuard<T, U>
|
||||
where
|
||||
T: ?Sized + Send,
|
||||
U: ?Sized + Send,
|
||||
{
|
||||
}
|
||||
|
||||
/// Error returned from the [`Mutex::try_lock`], [`RwLock::try_read`] and
|
||||
/// [`RwLock::try_write`] functions.
|
||||
///
|
||||
@@ -340,15 +416,27 @@ impl<T: ?Sized> Mutex<T> {
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn lock(&self) -> MutexGuard<'_, T> {
|
||||
let acquire_fut = async {
|
||||
self.acquire().await;
|
||||
|
||||
MutexGuard {
|
||||
lock: self,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
trace::async_op(
|
||||
|| self.acquire(),
|
||||
let acquire_fut = trace::async_op(
|
||||
move || acquire_fut,
|
||||
self.resource_span.clone(),
|
||||
"Mutex::lock",
|
||||
"poll",
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
);
|
||||
|
||||
#[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
|
||||
let guard = acquire_fut.await;
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
@@ -358,14 +446,7 @@ impl<T: ?Sized> Mutex<T> {
|
||||
);
|
||||
});
|
||||
|
||||
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
|
||||
self.acquire().await;
|
||||
|
||||
MutexGuard {
|
||||
lock: self,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
}
|
||||
guard
|
||||
}
|
||||
|
||||
/// Blockingly locks this `Mutex`. When the lock has been acquired, function returns a
|
||||
@@ -417,6 +498,7 @@ impl<T: ?Sized> Mutex<T> {
|
||||
/// ```
|
||||
#[track_caller]
|
||||
#[cfg(feature = "sync")]
|
||||
#[cfg_attr(docsrs, doc(alias = "lock_blocking"))]
|
||||
pub fn blocking_lock(&self) -> MutexGuard<'_, T> {
|
||||
crate::future::block_on(self.lock())
|
||||
}
|
||||
@@ -511,34 +593,39 @@ 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(),
|
||||
let resource_span = self.resource_span.clone();
|
||||
|
||||
let acquire_fut = async {
|
||||
self.acquire().await;
|
||||
|
||||
OwnedMutexGuard {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
lock: self,
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
let acquire_fut = trace::async_op(
|
||||
move || acquire_fut,
|
||||
resource_span,
|
||||
"Mutex::lock_owned",
|
||||
"poll",
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
);
|
||||
|
||||
#[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
|
||||
let guard = acquire_fut.await;
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
guard.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,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span,
|
||||
}
|
||||
guard
|
||||
}
|
||||
|
||||
async fn acquire(&self) {
|
||||
@@ -569,6 +656,12 @@ impl<T: ?Sized> Mutex<T> {
|
||||
pub fn try_lock(&self) -> Result<MutexGuard<'_, T>, TryLockError> {
|
||||
match self.s.try_acquire(1) {
|
||||
Ok(_) => {
|
||||
let guard = MutexGuard {
|
||||
lock: self,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
@@ -577,11 +670,7 @@ impl<T: ?Sized> Mutex<T> {
|
||||
);
|
||||
});
|
||||
|
||||
Ok(MutexGuard {
|
||||
lock: self,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
})
|
||||
Ok(guard)
|
||||
}
|
||||
Err(_) => Err(TryLockError(())),
|
||||
}
|
||||
@@ -638,22 +727,21 @@ impl<T: ?Sized> Mutex<T> {
|
||||
pub fn try_lock_owned(self: Arc<Self>) -> Result<OwnedMutexGuard<T>, TryLockError> {
|
||||
match self.s.try_acquire(1) {
|
||||
Ok(_) => {
|
||||
let guard = OwnedMutexGuard {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
lock: self,
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
guard.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,
|
||||
})
|
||||
Ok(guard)
|
||||
}
|
||||
Err(_) => Err(TryLockError(())),
|
||||
}
|
||||
@@ -713,6 +801,17 @@ where
|
||||
// === impl MutexGuard ===
|
||||
|
||||
impl<'a, T: ?Sized> MutexGuard<'a, T> {
|
||||
fn skip_drop(self) -> MutexGuardInner<'a, T> {
|
||||
let me = mem::ManuallyDrop::new(self);
|
||||
// SAFETY: This duplicates the `resource_span` and then forgets the
|
||||
// original. In the end, we have not duplicated or forgotten any values.
|
||||
MutexGuardInner {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: unsafe { std::ptr::read(&me.resource_span) },
|
||||
lock: me.lock,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new [`MappedMutexGuard`] for a component of the locked data.
|
||||
///
|
||||
/// This operation cannot fail as the [`MutexGuard`] passed in already locked the mutex.
|
||||
@@ -749,12 +848,13 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
|
||||
F: FnOnce(&mut T) -> &mut U,
|
||||
{
|
||||
let data = f(&mut *this) as *mut U;
|
||||
let s = &this.lock.s;
|
||||
mem::forget(this);
|
||||
let inner = this.skip_drop();
|
||||
MappedMutexGuard {
|
||||
s,
|
||||
s: &inner.lock.s,
|
||||
data,
|
||||
marker: marker::PhantomData,
|
||||
marker: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: inner.resource_span,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -799,12 +899,13 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
|
||||
Some(data) => data as *mut U,
|
||||
None => return Err(this),
|
||||
};
|
||||
let s = &this.lock.s;
|
||||
mem::forget(this);
|
||||
let inner = this.skip_drop();
|
||||
Ok(MappedMutexGuard {
|
||||
s,
|
||||
s: &inner.lock.s,
|
||||
data,
|
||||
marker: marker::PhantomData,
|
||||
marker: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: inner.resource_span,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -837,6 +938,8 @@ 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!(
|
||||
@@ -844,7 +947,6 @@ impl<T: ?Sized> Drop for MutexGuard<'_, T> {
|
||||
locked = false,
|
||||
);
|
||||
});
|
||||
self.lock.s.release(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -876,6 +978,116 @@ impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> {
|
||||
// === impl OwnedMutexGuard ===
|
||||
|
||||
impl<T: ?Sized> OwnedMutexGuard<T> {
|
||||
fn skip_drop(self) -> OwnedMutexGuardInner<T> {
|
||||
let me = mem::ManuallyDrop::new(self);
|
||||
// SAFETY: This duplicates the values in every field of the guard, then
|
||||
// forgets the originals, so in the end no value is duplicated.
|
||||
unsafe {
|
||||
OwnedMutexGuardInner {
|
||||
lock: ptr::read(&me.lock),
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: ptr::read(&me.resource_span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new [`OwnedMappedMutexGuard`] for a component of the locked data.
|
||||
///
|
||||
/// This operation cannot fail as the [`OwnedMutexGuard`] passed in already locked the mutex.
|
||||
///
|
||||
/// This is an associated function that needs to be used as `OwnedMutexGuard::map(...)`. A method
|
||||
/// would interfere with methods of the same name on the contents of the locked data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::sync::{Mutex, OwnedMutexGuard};
|
||||
/// use std::sync::Arc;
|
||||
///
|
||||
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
/// struct Foo(u32);
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// let foo = Arc::new(Mutex::new(Foo(1)));
|
||||
///
|
||||
/// {
|
||||
/// let mut mapped = OwnedMutexGuard::map(foo.clone().lock_owned().await, |f| &mut f.0);
|
||||
/// *mapped = 2;
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(Foo(2), *foo.lock().await);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`OwnedMutexGuard`]: struct@OwnedMutexGuard
|
||||
/// [`OwnedMappedMutexGuard`]: struct@OwnedMappedMutexGuard
|
||||
#[inline]
|
||||
pub fn map<U, F>(mut this: Self, f: F) -> OwnedMappedMutexGuard<T, U>
|
||||
where
|
||||
F: FnOnce(&mut T) -> &mut U,
|
||||
{
|
||||
let data = f(&mut *this) as *mut U;
|
||||
let inner = this.skip_drop();
|
||||
OwnedMappedMutexGuard {
|
||||
data,
|
||||
lock: inner.lock,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: inner.resource_span,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to make a new [`OwnedMappedMutexGuard`] for a component of the locked data. The
|
||||
/// original guard is returned if the closure returns `None`.
|
||||
///
|
||||
/// This operation cannot fail as the [`OwnedMutexGuard`] passed in already locked the mutex.
|
||||
///
|
||||
/// This is an associated function that needs to be used as `OwnedMutexGuard::try_map(...)`. A
|
||||
/// method would interfere with methods of the same name on the contents of the locked data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::sync::{Mutex, OwnedMutexGuard};
|
||||
/// use std::sync::Arc;
|
||||
///
|
||||
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
/// struct Foo(u32);
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// let foo = Arc::new(Mutex::new(Foo(1)));
|
||||
///
|
||||
/// {
|
||||
/// let mut mapped = OwnedMutexGuard::try_map(foo.clone().lock_owned().await, |f| Some(&mut f.0))
|
||||
/// .expect("should not fail");
|
||||
/// *mapped = 2;
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(Foo(2), *foo.lock().await);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`OwnedMutexGuard`]: struct@OwnedMutexGuard
|
||||
/// [`OwnedMappedMutexGuard`]: struct@OwnedMappedMutexGuard
|
||||
#[inline]
|
||||
pub fn try_map<U, F>(mut this: Self, f: F) -> Result<OwnedMappedMutexGuard<T, U>, Self>
|
||||
where
|
||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||
{
|
||||
let data = match f(&mut *this) {
|
||||
Some(data) => data as *mut U,
|
||||
None => return Err(this),
|
||||
};
|
||||
let inner = this.skip_drop();
|
||||
Ok(OwnedMappedMutexGuard {
|
||||
data,
|
||||
lock: inner.lock,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: inner.resource_span,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a reference to the original `Arc<Mutex>`.
|
||||
///
|
||||
/// ```
|
||||
@@ -906,6 +1118,8 @@ impl<T: ?Sized> OwnedMutexGuard<T> {
|
||||
|
||||
impl<T: ?Sized> Drop for OwnedMutexGuard<T> {
|
||||
fn drop(&mut self) {
|
||||
self.lock.s.release(1);
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
@@ -913,7 +1127,6 @@ impl<T: ?Sized> Drop for OwnedMutexGuard<T> {
|
||||
locked = false,
|
||||
);
|
||||
});
|
||||
self.lock.s.release(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,6 +1158,16 @@ impl<T: ?Sized + fmt::Display> fmt::Display for OwnedMutexGuard<T> {
|
||||
// === impl MappedMutexGuard ===
|
||||
|
||||
impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
|
||||
fn skip_drop(self) -> MappedMutexGuardInner<'a, T> {
|
||||
let me = mem::ManuallyDrop::new(self);
|
||||
MappedMutexGuardInner {
|
||||
s: me.s,
|
||||
data: me.data,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: unsafe { std::ptr::read(&me.resource_span) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new [`MappedMutexGuard`] for a component of the locked data.
|
||||
///
|
||||
/// This operation cannot fail as the [`MappedMutexGuard`] passed in already locked the mutex.
|
||||
@@ -959,12 +1182,13 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
|
||||
F: FnOnce(&mut T) -> &mut U,
|
||||
{
|
||||
let data = f(&mut *this) as *mut U;
|
||||
let s = this.s;
|
||||
mem::forget(this);
|
||||
let inner = this.skip_drop();
|
||||
MappedMutexGuard {
|
||||
s,
|
||||
s: inner.s,
|
||||
data,
|
||||
marker: marker::PhantomData,
|
||||
marker: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: inner.resource_span,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -986,19 +1210,28 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
|
||||
Some(data) => data as *mut U,
|
||||
None => return Err(this),
|
||||
};
|
||||
let s = this.s;
|
||||
mem::forget(this);
|
||||
let inner = this.skip_drop();
|
||||
Ok(MappedMutexGuard {
|
||||
s,
|
||||
s: inner.s,
|
||||
data,
|
||||
marker: marker::PhantomData,
|
||||
marker: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: inner.resource_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> Drop for MappedMutexGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
self.s.release(1)
|
||||
self.s.release(1);
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
locked = false,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1026,3 +1259,111 @@ impl<'a, T: ?Sized + fmt::Display> fmt::Display for MappedMutexGuard<'a, T> {
|
||||
fmt::Display::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
// === impl OwnedMappedMutexGuard ===
|
||||
|
||||
impl<T: ?Sized, U: ?Sized> OwnedMappedMutexGuard<T, U> {
|
||||
fn skip_drop(self) -> OwnedMappedMutexGuardInner<T, U> {
|
||||
let me = mem::ManuallyDrop::new(self);
|
||||
// SAFETY: This duplicates the values in every field of the guard, then
|
||||
// forgets the originals, so in the end no value is duplicated.
|
||||
unsafe {
|
||||
OwnedMappedMutexGuardInner {
|
||||
data: me.data,
|
||||
lock: ptr::read(&me.lock),
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: ptr::read(&me.resource_span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new [`OwnedMappedMutexGuard`] for a component of the locked data.
|
||||
///
|
||||
/// This operation cannot fail as the [`OwnedMappedMutexGuard`] passed in already locked the mutex.
|
||||
///
|
||||
/// This is an associated function that needs to be used as `OwnedMappedMutexGuard::map(...)`. A method
|
||||
/// would interfere with methods of the same name on the contents of the locked data.
|
||||
///
|
||||
/// [`OwnedMappedMutexGuard`]: struct@OwnedMappedMutexGuard
|
||||
#[inline]
|
||||
pub fn map<S, F>(mut this: Self, f: F) -> OwnedMappedMutexGuard<T, S>
|
||||
where
|
||||
F: FnOnce(&mut U) -> &mut S,
|
||||
{
|
||||
let data = f(&mut *this) as *mut S;
|
||||
let inner = this.skip_drop();
|
||||
OwnedMappedMutexGuard {
|
||||
data,
|
||||
lock: inner.lock,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: inner.resource_span,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to make a new [`OwnedMappedMutexGuard`] for a component of the locked data. The
|
||||
/// original guard is returned if the closure returns `None`.
|
||||
///
|
||||
/// This operation cannot fail as the [`OwnedMutexGuard`] passed in already locked the mutex.
|
||||
///
|
||||
/// This is an associated function that needs to be used as `OwnedMutexGuard::try_map(...)`. A
|
||||
/// method would interfere with methods of the same name on the contents of the locked data.
|
||||
///
|
||||
/// [`OwnedMutexGuard`]: struct@OwnedMutexGuard
|
||||
/// [`OwnedMappedMutexGuard`]: struct@OwnedMappedMutexGuard
|
||||
#[inline]
|
||||
pub fn try_map<S, F>(mut this: Self, f: F) -> Result<OwnedMappedMutexGuard<T, S>, Self>
|
||||
where
|
||||
F: FnOnce(&mut U) -> Option<&mut S>,
|
||||
{
|
||||
let data = match f(&mut *this) {
|
||||
Some(data) => data as *mut S,
|
||||
None => return Err(this),
|
||||
};
|
||||
let inner = this.skip_drop();
|
||||
Ok(OwnedMappedMutexGuard {
|
||||
data,
|
||||
lock: inner.lock,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: inner.resource_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, U: ?Sized> Drop for OwnedMappedMutexGuard<T, U> {
|
||||
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,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, U: ?Sized> Deref for OwnedMappedMutexGuard<T, U> {
|
||||
type Target = U;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, U: ?Sized> DerefMut for OwnedMappedMutexGuard<T, U> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, U: ?Sized + fmt::Debug> fmt::Debug for OwnedMappedMutexGuard<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, U: ?Sized + fmt::Display> fmt::Display for OwnedMappedMutexGuard<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
+165
-44
@@ -7,7 +7,7 @@
|
||||
|
||||
use crate::loom::sync::atomic::AtomicUsize;
|
||||
use crate::loom::sync::Mutex;
|
||||
use crate::util::linked_list::{self, LinkedList};
|
||||
use crate::util::linked_list::{self, GuardedLinkedList, LinkedList};
|
||||
use crate::util::WakeList;
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
@@ -20,6 +20,7 @@ use std::sync::atomic::Ordering::SeqCst;
|
||||
use std::task::{Context, Poll, Waker};
|
||||
|
||||
type WaitList = LinkedList<Waiter, <Waiter as linked_list::Link>::Target>;
|
||||
type GuardedWaitList = GuardedLinkedList<Waiter, <Waiter as linked_list::Link>::Target>;
|
||||
|
||||
/// Notifies a single task to wake up.
|
||||
///
|
||||
@@ -198,10 +199,16 @@ type WaitList = LinkedList<Waiter, <Waiter as linked_list::Link>::Target>;
|
||||
/// [`Semaphore`]: crate::sync::Semaphore
|
||||
#[derive(Debug)]
|
||||
pub struct Notify {
|
||||
// This uses 2 bits to store one of `EMPTY`,
|
||||
// `state` uses 2 bits to store one of `EMPTY`,
|
||||
// `WAITING` or `NOTIFIED`. The rest of the bits
|
||||
// are used to store the number of times `notify_waiters`
|
||||
// was called.
|
||||
//
|
||||
// Throughout the code there are two assumptions:
|
||||
// - state can be transitioned *from* `WAITING` only if
|
||||
// `waiters` lock is held
|
||||
// - number of times `notify_waiters` was called can
|
||||
// be modified only if `waiters` lock is held
|
||||
state: AtomicUsize,
|
||||
waiters: Mutex<WaitList>,
|
||||
}
|
||||
@@ -229,6 +236,17 @@ struct Waiter {
|
||||
_p: PhantomPinned,
|
||||
}
|
||||
|
||||
impl Waiter {
|
||||
fn new() -> Waiter {
|
||||
Waiter {
|
||||
pointers: linked_list::Pointers::new(),
|
||||
waker: None,
|
||||
notified: None,
|
||||
_p: PhantomPinned,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_addr_of_methods! {
|
||||
impl<> Waiter {
|
||||
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||
@@ -237,6 +255,59 @@ generate_addr_of_methods! {
|
||||
}
|
||||
}
|
||||
|
||||
/// List used in `Notify::notify_waiters`. It wraps a guarded linked list
|
||||
/// and gates the access to it on `notify.waiters` mutex. It also empties
|
||||
/// the list on drop.
|
||||
struct NotifyWaitersList<'a> {
|
||||
list: GuardedWaitList,
|
||||
is_empty: bool,
|
||||
notify: &'a Notify,
|
||||
}
|
||||
|
||||
impl<'a> NotifyWaitersList<'a> {
|
||||
fn new(
|
||||
unguarded_list: WaitList,
|
||||
guard: Pin<&'a mut UnsafeCell<Waiter>>,
|
||||
notify: &'a Notify,
|
||||
) -> NotifyWaitersList<'a> {
|
||||
// Safety: pointer to the guarding waiter is not null.
|
||||
let guard_ptr = unsafe { NonNull::new_unchecked(guard.get()) };
|
||||
let list = unguarded_list.into_guarded(guard_ptr);
|
||||
NotifyWaitersList {
|
||||
list,
|
||||
is_empty: false,
|
||||
notify,
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the last element from the guarded list. Modifying this list
|
||||
/// requires an exclusive access to the main list in `Notify`.
|
||||
fn pop_back_locked(&mut self, _waiters: &mut WaitList) -> Option<NonNull<Waiter>> {
|
||||
let result = self.list.pop_back();
|
||||
if result.is_none() {
|
||||
// Save information about emptiness to avoid waiting for lock
|
||||
// in the destructor.
|
||||
self.is_empty = true;
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NotifyWaitersList<'_> {
|
||||
fn drop(&mut self) {
|
||||
// If the list is not empty, we unlink all waiters from it.
|
||||
// We do not wake the waiters to avoid double panics.
|
||||
if !self.is_empty {
|
||||
let _lock_guard = self.notify.waiters.lock();
|
||||
while let Some(mut waiter) = self.list.pop_back() {
|
||||
// Safety: we hold the lock.
|
||||
let waiter = unsafe { waiter.as_mut() };
|
||||
waiter.notified = Some(NotificationType::AllWaiters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Future returned from [`Notify::notified()`].
|
||||
///
|
||||
/// This future is fused, so once it has completed, any future calls to poll
|
||||
@@ -249,6 +320,9 @@ pub struct Notified<'a> {
|
||||
/// The current state of the receiving process.
|
||||
state: State,
|
||||
|
||||
/// Number of calls to `notify_waiters` at the time of creation.
|
||||
notify_waiters_calls: usize,
|
||||
|
||||
/// Entry in the waiter `LinkedList`.
|
||||
waiter: UnsafeCell<Waiter>,
|
||||
}
|
||||
@@ -258,7 +332,7 @@ unsafe impl<'a> Sync for Notified<'a> {}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
Init(usize),
|
||||
Init,
|
||||
Waiting,
|
||||
Done,
|
||||
}
|
||||
@@ -383,17 +457,13 @@ impl Notify {
|
||||
/// ```
|
||||
pub fn notified(&self) -> Notified<'_> {
|
||||
// we load the number of times notify_waiters
|
||||
// was called and store that in our initial state
|
||||
// was called and store that in the future.
|
||||
let state = self.state.load(SeqCst);
|
||||
Notified {
|
||||
notify: self,
|
||||
state: State::Init(state >> NOTIFY_WAITERS_SHIFT),
|
||||
waiter: UnsafeCell::new(Waiter {
|
||||
pointers: linked_list::Pointers::new(),
|
||||
waker: None,
|
||||
notified: None,
|
||||
_p: PhantomPinned,
|
||||
}),
|
||||
state: State::Init,
|
||||
notify_waiters_calls: get_num_notify_waiters_calls(state),
|
||||
waiter: UnsafeCell::new(Waiter::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,12 +570,9 @@ impl Notify {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn notify_waiters(&self) {
|
||||
let mut wakers = WakeList::new();
|
||||
|
||||
// There are waiters, the lock must be acquired to notify.
|
||||
let mut waiters = self.waiters.lock();
|
||||
|
||||
// The state must be reloaded while the lock is held. The state may only
|
||||
// The state must be loaded while the lock is held. The state may only
|
||||
// transition out of WAITING while the lock is held.
|
||||
let curr = self.state.load(SeqCst);
|
||||
|
||||
@@ -516,12 +583,30 @@ impl Notify {
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point, it is guaranteed that the state will not
|
||||
// concurrently change, as holding the lock is required to
|
||||
// transition **out** of `WAITING`.
|
||||
// Increment the number of times this method was called
|
||||
// and transition to empty.
|
||||
let new_state = set_state(inc_num_notify_waiters_calls(curr), EMPTY);
|
||||
self.state.store(new_state, SeqCst);
|
||||
|
||||
// It is critical for `GuardedLinkedList` safety that the guard node is
|
||||
// pinned in memory and is not dropped until the guarded list is dropped.
|
||||
let guard = UnsafeCell::new(Waiter::new());
|
||||
pin!(guard);
|
||||
|
||||
// We move all waiters to a secondary list. It uses a `GuardedLinkedList`
|
||||
// underneath to allow every waiter to safely remove itself from it.
|
||||
//
|
||||
// * This list will be still guarded by the `waiters` lock.
|
||||
// `NotifyWaitersList` wrapper makes sure we hold the lock to modify it.
|
||||
// * This wrapper will empty the list on drop. It is critical for safety
|
||||
// that we will not leave any list entry with a pointer to the local
|
||||
// guard node after this function returns / panics.
|
||||
let mut list = NotifyWaitersList::new(std::mem::take(&mut *waiters), guard, self);
|
||||
|
||||
let mut wakers = WakeList::new();
|
||||
'outer: loop {
|
||||
while wakers.can_push() {
|
||||
match waiters.pop_back() {
|
||||
match list.pop_back_locked(&mut waiters) {
|
||||
Some(mut waiter) => {
|
||||
// Safety: `waiters` lock is still held.
|
||||
let waiter = unsafe { waiter.as_mut() };
|
||||
@@ -540,20 +625,17 @@ impl Notify {
|
||||
}
|
||||
}
|
||||
|
||||
// Release the lock before notifying.
|
||||
drop(waiters);
|
||||
|
||||
// One of the wakers may panic, but the remaining waiters will still
|
||||
// be unlinked from the list in `NotifyWaitersList` destructor.
|
||||
wakers.wake_all();
|
||||
|
||||
// Acquire the lock again.
|
||||
waiters = self.waiters.lock();
|
||||
}
|
||||
|
||||
// All waiters will be notified, the state must be transitioned to
|
||||
// `EMPTY`. As transitioning **from** `WAITING` requires the lock to be
|
||||
// held, a `store` is sufficient.
|
||||
let new = set_state(inc_num_notify_waiters_calls(curr), EMPTY);
|
||||
self.state.store(new, SeqCst);
|
||||
|
||||
// Release the lock before notifying
|
||||
drop(waiters);
|
||||
|
||||
@@ -730,26 +812,32 @@ impl Notified<'_> {
|
||||
|
||||
/// A custom `project` implementation is used in place of `pin-project-lite`
|
||||
/// as a custom drop implementation is needed.
|
||||
fn project(self: Pin<&mut Self>) -> (&Notify, &mut State, &UnsafeCell<Waiter>) {
|
||||
fn project(self: Pin<&mut Self>) -> (&Notify, &mut State, &usize, &UnsafeCell<Waiter>) {
|
||||
unsafe {
|
||||
// Safety: both `notify` and `state` are `Unpin`.
|
||||
// Safety: `notify`, `state` and `notify_waiters_calls` are `Unpin`.
|
||||
|
||||
is_unpin::<&Notify>();
|
||||
is_unpin::<AtomicUsize>();
|
||||
is_unpin::<usize>();
|
||||
|
||||
let me = self.get_unchecked_mut();
|
||||
(me.notify, &mut me.state, &me.waiter)
|
||||
(
|
||||
me.notify,
|
||||
&mut me.state,
|
||||
&me.notify_waiters_calls,
|
||||
&me.waiter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_notified(self: Pin<&mut Self>, waker: Option<&Waker>) -> Poll<()> {
|
||||
use State::*;
|
||||
|
||||
let (notify, state, waiter) = self.project();
|
||||
let (notify, state, notify_waiters_calls, waiter) = self.project();
|
||||
|
||||
loop {
|
||||
match *state {
|
||||
Init(initial_notify_waiters_calls) => {
|
||||
Init => {
|
||||
let curr = notify.state.load(SeqCst);
|
||||
|
||||
// Optimistically try acquiring a pending notification
|
||||
@@ -779,7 +867,7 @@ impl Notified<'_> {
|
||||
|
||||
// if notify_waiters has been called after the future
|
||||
// was created, then we are done
|
||||
if get_num_notify_waiters_calls(curr) != initial_notify_waiters_calls {
|
||||
if get_num_notify_waiters_calls(curr) != *notify_waiters_calls {
|
||||
*state = Done;
|
||||
return Poll::Ready(());
|
||||
}
|
||||
@@ -829,10 +917,14 @@ impl Notified<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut old_waker = None;
|
||||
if waker.is_some() {
|
||||
// Safety: called while locked.
|
||||
//
|
||||
// The use of `old_waiter` here is not necessary, as the field is always
|
||||
// None when we reach this line.
|
||||
unsafe {
|
||||
(*waiter.get()).waker = waker;
|
||||
old_waker = std::mem::replace(&mut (*waiter.get()).waker, waker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -843,24 +935,44 @@ impl Notified<'_> {
|
||||
|
||||
*state = Waiting;
|
||||
|
||||
drop(waiters);
|
||||
drop(old_waker);
|
||||
|
||||
return Poll::Pending;
|
||||
}
|
||||
Waiting => {
|
||||
// Currently in the "Waiting" state, implying the caller has
|
||||
// a waiter stored in the waiter list (guarded by
|
||||
// `notify.waiters`). In order to access the waker fields,
|
||||
// we must hold the lock.
|
||||
// Currently in the "Waiting" state, implying the caller has a waiter stored in
|
||||
// a waiter list (guarded by `notify.waiters`). In order to access the waker
|
||||
// fields, we must acquire the lock.
|
||||
|
||||
let waiters = notify.waiters.lock();
|
||||
let mut waiters = notify.waiters.lock();
|
||||
|
||||
// Load the state with the lock held.
|
||||
let curr = notify.state.load(SeqCst);
|
||||
|
||||
// Safety: called while locked
|
||||
let w = unsafe { &mut *waiter.get() };
|
||||
let mut old_waker = None;
|
||||
|
||||
if w.notified.is_some() {
|
||||
// Our waker has been notified. Reset the fields and
|
||||
// remove it from the list.
|
||||
w.waker = None;
|
||||
// Our waker has been notified and our waiter is already removed from
|
||||
// the list. Reset the notification and convert to `Done`.
|
||||
old_waker = std::mem::take(&mut w.waker);
|
||||
w.notified = None;
|
||||
*state = Done;
|
||||
} else if get_num_notify_waiters_calls(curr) != *notify_waiters_calls {
|
||||
// Before we add a waiter to the list we check if these numbers are
|
||||
// different while holding the lock. If these numbers are different now,
|
||||
// it means that there is a call to `notify_waiters` in progress and this
|
||||
// waiter must be contained by a guarded list used in `notify_waiters`.
|
||||
// We can treat the waiter as notified and remove it from the list, as
|
||||
// it would have been notified in the `notify_waiters` call anyways.
|
||||
|
||||
old_waker = std::mem::take(&mut w.waker);
|
||||
|
||||
// Safety: we hold the lock, so we have an exclusive access to the list.
|
||||
// The list is used in `notify_waiters`, so it must be guarded.
|
||||
unsafe { waiters.remove(NonNull::new_unchecked(w)) };
|
||||
|
||||
*state = Done;
|
||||
} else {
|
||||
@@ -871,10 +983,14 @@ impl Notified<'_> {
|
||||
None => true,
|
||||
};
|
||||
if should_update {
|
||||
w.waker = Some(waker.clone());
|
||||
old_waker = std::mem::replace(&mut w.waker, Some(waker.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the old waker after releasing the lock.
|
||||
drop(waiters);
|
||||
drop(old_waker);
|
||||
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
@@ -884,6 +1000,9 @@ impl Notified<'_> {
|
||||
// is helpful to visualize the scope of the critical
|
||||
// section.
|
||||
drop(waiters);
|
||||
|
||||
// Drop the old waker after releasing the lock.
|
||||
drop(old_waker);
|
||||
}
|
||||
Done => {
|
||||
return Poll::Ready(());
|
||||
@@ -906,7 +1025,7 @@ impl Drop for Notified<'_> {
|
||||
use State::*;
|
||||
|
||||
// Safety: The type only transitions to a "Waiting" state when pinned.
|
||||
let (notify, state, waiter) = unsafe { Pin::new_unchecked(self).project() };
|
||||
let (notify, state, _, waiter) = unsafe { Pin::new_unchecked(self).project() };
|
||||
|
||||
// This is where we ensure safety. The `Notified` value is being
|
||||
// dropped, which means we must ensure that the waiter entry is no
|
||||
@@ -917,8 +1036,10 @@ impl Drop for Notified<'_> {
|
||||
|
||||
// remove the entry from the list (if not already removed)
|
||||
//
|
||||
// safety: the waiter is only added to `waiters` by virtue of it
|
||||
// being the only `LinkedList` available to the type.
|
||||
// Safety: we hold the lock, so we have an exclusive access to every list the
|
||||
// waiter may be contained in. If the node is not contained in the `waiters`
|
||||
// list, then it is contained by a guarded list used by `notify_waiters` and
|
||||
// in such case it must be a middle node.
|
||||
unsafe { waiters.remove(NonNull::new_unchecked(waiter.get())) };
|
||||
|
||||
if waiters.is_empty() && get_state(notify_state) == WAITING {
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
//! 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.
|
||||
//!
|
||||
//! If the [`Receiver`] is closed before receiving a message which has already
|
||||
//! been sent, the message will remain in the channel until the receiver is
|
||||
//! dropped, at which point the message will be dropped immediately.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
@@ -1056,6 +1060,7 @@ impl<T> Receiver<T> {
|
||||
/// ```
|
||||
#[track_caller]
|
||||
#[cfg(feature = "sync")]
|
||||
#[cfg_attr(docsrs, doc(alias = "recv_blocking"))]
|
||||
pub fn blocking_recv(self) -> Result<T, RecvError> {
|
||||
crate::future::block_on(self)
|
||||
}
|
||||
|
||||
+136
-119
@@ -5,7 +5,6 @@ use crate::util::trace;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::marker;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) mod owned_read_guard;
|
||||
@@ -423,23 +422,33 @@ impl<T: ?Sized> RwLock<T> {
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn read(&self) -> RwLockReadGuard<'_, T> {
|
||||
let acquire_fut = async {
|
||||
self.s.acquire(1).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!()
|
||||
});
|
||||
|
||||
RwLockReadGuard {
|
||||
s: &self.s,
|
||||
data: self.c.get(),
|
||||
marker: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
let inner = trace::async_op(
|
||||
|| self.s.acquire(1),
|
||||
let acquire_fut = trace::async_op(
|
||||
move || acquire_fut,
|
||||
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!()
|
||||
});
|
||||
#[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
|
||||
let guard = acquire_fut.await;
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
@@ -450,13 +459,7 @@ impl<T: ?Sized> RwLock<T> {
|
||||
)
|
||||
});
|
||||
|
||||
RwLockReadGuard {
|
||||
s: &self.s,
|
||||
data: self.c.get(),
|
||||
marker: marker::PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
}
|
||||
guard
|
||||
}
|
||||
|
||||
/// Blockingly locks this `RwLock` with shared read access.
|
||||
@@ -565,25 +568,38 @@ impl<T: ?Sized> RwLock<T> {
|
||||
/// ```
|
||||
pub async fn read_owned(self: Arc<Self>) -> OwnedRwLockReadGuard<T> {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
let inner = trace::async_op(
|
||||
|| self.s.acquire(1),
|
||||
self.resource_span.clone(),
|
||||
let resource_span = self.resource_span.clone();
|
||||
|
||||
let acquire_fut = async {
|
||||
self.s.acquire(1).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!()
|
||||
});
|
||||
|
||||
OwnedRwLockReadGuard {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
data: self.c.get(),
|
||||
lock: self,
|
||||
_p: PhantomData,
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
let acquire_fut = trace::async_op(
|
||||
move || acquire_fut,
|
||||
resource_span,
|
||||
"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!()
|
||||
});
|
||||
#[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
|
||||
let guard = acquire_fut.await;
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
guard.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
current_readers = 1,
|
||||
@@ -591,16 +607,7 @@ impl<T: ?Sized> RwLock<T> {
|
||||
)
|
||||
});
|
||||
|
||||
#[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,
|
||||
}
|
||||
guard
|
||||
}
|
||||
|
||||
/// Attempts to acquire this `RwLock` with shared read access.
|
||||
@@ -642,6 +649,14 @@ impl<T: ?Sized> RwLock<T> {
|
||||
Err(TryAcquireError::Closed) => unreachable!(),
|
||||
}
|
||||
|
||||
let guard = RwLockReadGuard {
|
||||
s: &self.s,
|
||||
data: self.c.get(),
|
||||
marker: marker::PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
@@ -651,13 +666,7 @@ impl<T: ?Sized> RwLock<T> {
|
||||
)
|
||||
});
|
||||
|
||||
Ok(RwLockReadGuard {
|
||||
s: &self.s,
|
||||
data: self.c.get(),
|
||||
marker: marker::PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
})
|
||||
Ok(guard)
|
||||
}
|
||||
|
||||
/// Attempts to acquire this `RwLock` with shared read access.
|
||||
@@ -705,8 +714,16 @@ impl<T: ?Sized> RwLock<T> {
|
||||
Err(TryAcquireError::Closed) => unreachable!(),
|
||||
}
|
||||
|
||||
let guard = OwnedRwLockReadGuard {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
data: self.c.get(),
|
||||
lock: self,
|
||||
_p: PhantomData,
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
guard.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
current_readers = 1,
|
||||
@@ -714,16 +731,7 @@ impl<T: ?Sized> RwLock<T> {
|
||||
)
|
||||
});
|
||||
|
||||
#[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,
|
||||
})
|
||||
Ok(guard)
|
||||
}
|
||||
|
||||
/// Locks this `RwLock` with exclusive write access, causing the current
|
||||
@@ -755,23 +763,34 @@ impl<T: ?Sized> RwLock<T> {
|
||||
///}
|
||||
/// ```
|
||||
pub async fn write(&self) -> RwLockWriteGuard<'_, T> {
|
||||
let acquire_fut = async {
|
||||
self.s.acquire(self.mr).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!()
|
||||
});
|
||||
|
||||
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(),
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
let inner = trace::async_op(
|
||||
|| self.s.acquire(self.mr),
|
||||
let acquire_fut = trace::async_op(
|
||||
move || acquire_fut,
|
||||
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!()
|
||||
});
|
||||
#[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
|
||||
let guard = acquire_fut.await;
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
@@ -782,14 +801,7 @@ impl<T: ?Sized> RwLock<T> {
|
||||
)
|
||||
});
|
||||
|
||||
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(),
|
||||
}
|
||||
guard
|
||||
}
|
||||
|
||||
/// Blockingly locks this `RwLock` with exclusive write access.
|
||||
@@ -884,25 +896,39 @@ impl<T: ?Sized> RwLock<T> {
|
||||
/// ```
|
||||
pub async fn write_owned(self: Arc<Self>) -> OwnedRwLockWriteGuard<T> {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
let inner = trace::async_op(
|
||||
|| self.s.acquire(self.mr),
|
||||
self.resource_span.clone(),
|
||||
let resource_span = self.resource_span.clone();
|
||||
|
||||
let acquire_fut = async {
|
||||
self.s.acquire(self.mr).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!()
|
||||
});
|
||||
|
||||
OwnedRwLockWriteGuard {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
permits_acquired: self.mr,
|
||||
data: self.c.get(),
|
||||
lock: self,
|
||||
_p: PhantomData,
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
let acquire_fut = trace::async_op(
|
||||
move || acquire_fut,
|
||||
resource_span,
|
||||
"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!()
|
||||
});
|
||||
#[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
|
||||
let guard = acquire_fut.await;
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
guard.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
write_locked = true,
|
||||
@@ -910,17 +936,7 @@ impl<T: ?Sized> RwLock<T> {
|
||||
)
|
||||
});
|
||||
|
||||
#[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,
|
||||
}
|
||||
guard
|
||||
}
|
||||
|
||||
/// Attempts to acquire this `RwLock` with exclusive write access.
|
||||
@@ -953,6 +969,15 @@ impl<T: ?Sized> RwLock<T> {
|
||||
Err(TryAcquireError::Closed) => unreachable!(),
|
||||
}
|
||||
|
||||
let guard = 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(),
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
@@ -962,14 +987,7 @@ impl<T: ?Sized> RwLock<T> {
|
||||
)
|
||||
});
|
||||
|
||||
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(),
|
||||
})
|
||||
Ok(guard)
|
||||
}
|
||||
|
||||
/// Attempts to acquire this `RwLock` with exclusive write access.
|
||||
@@ -1009,8 +1027,17 @@ impl<T: ?Sized> RwLock<T> {
|
||||
Err(TryAcquireError::Closed) => unreachable!(),
|
||||
}
|
||||
|
||||
let guard = OwnedRwLockWriteGuard {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: self.resource_span.clone(),
|
||||
permits_acquired: self.mr,
|
||||
data: self.c.get(),
|
||||
lock: self,
|
||||
_p: PhantomData,
|
||||
};
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
guard.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
write_locked = true,
|
||||
@@ -1018,17 +1045,7 @@ impl<T: ?Sized> RwLock<T> {
|
||||
)
|
||||
});
|
||||
|
||||
#[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,
|
||||
})
|
||||
Ok(guard)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying data.
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use crate::sync::rwlock::RwLock;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem, ops, ptr};
|
||||
|
||||
/// Owned RAII structure used to release the shared read access of a lock when
|
||||
/// dropped.
|
||||
@@ -16,15 +13,38 @@ use std::sync::Arc;
|
||||
/// [`RwLock`]: struct@crate::sync::RwLock
|
||||
#[clippy::has_significant_drop]
|
||||
pub struct OwnedRwLockReadGuard<T: ?Sized, U: ?Sized = T> {
|
||||
// When changing the fields in this struct, make sure to update the
|
||||
// `skip_drop` method.
|
||||
#[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) lock: Arc<RwLock<T>>,
|
||||
pub(super) data: *const U,
|
||||
pub(super) _p: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // Unused fields are still used in Drop.
|
||||
struct Inner<T: ?Sized, U: ?Sized> {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
lock: Arc<RwLock<T>>,
|
||||
data: *const U,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, U: ?Sized> OwnedRwLockReadGuard<T, U> {
|
||||
fn skip_drop(self) -> Inner<T, U> {
|
||||
let me = mem::ManuallyDrop::new(self);
|
||||
// SAFETY: This duplicates the values in every field of the guard, then
|
||||
// forgets the originals, so in the end no value is duplicated.
|
||||
unsafe {
|
||||
Inner {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: ptr::read(&me.resource_span),
|
||||
lock: ptr::read(&me.lock),
|
||||
data: me.data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `OwnedRwLockReadGuard` for a component of the locked data.
|
||||
/// This operation cannot fail as the `OwnedRwLockReadGuard` passed in
|
||||
/// already locked the data.
|
||||
@@ -53,23 +73,19 @@ impl<T: ?Sized, U: ?Sized> OwnedRwLockReadGuard<T, U> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn map<F, V: ?Sized>(mut this: Self, f: F) -> OwnedRwLockReadGuard<T, V>
|
||||
pub fn map<F, V: ?Sized>(this: Self, f: F) -> OwnedRwLockReadGuard<T, V>
|
||||
where
|
||||
F: FnOnce(&U) -> &V,
|
||||
{
|
||||
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);
|
||||
let this = this.skip_drop();
|
||||
|
||||
OwnedRwLockReadGuard {
|
||||
lock: ManuallyDrop::new(lock),
|
||||
lock: this.lock,
|
||||
data,
|
||||
_p: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span,
|
||||
resource_span: this.resource_span,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +120,7 @@ impl<T: ?Sized, U: ?Sized> OwnedRwLockReadGuard<T, U> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn try_map<F, V: ?Sized>(mut this: Self, f: F) -> Result<OwnedRwLockReadGuard<T, V>, Self>
|
||||
pub fn try_map<F, V: ?Sized>(this: Self, f: F) -> Result<OwnedRwLockReadGuard<T, V>, Self>
|
||||
where
|
||||
F: FnOnce(&U) -> Option<&V>,
|
||||
{
|
||||
@@ -112,18 +128,14 @@ impl<T: ?Sized, U: ?Sized> OwnedRwLockReadGuard<T, U> {
|
||||
Some(data) => data as *const V,
|
||||
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);
|
||||
let this = this.skip_drop();
|
||||
|
||||
Ok(OwnedRwLockReadGuard {
|
||||
lock: ManuallyDrop::new(lock),
|
||||
lock: this.lock,
|
||||
data,
|
||||
_p: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span,
|
||||
resource_span: this.resource_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -157,7 +169,6 @@ where
|
||||
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(|| {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use crate::sync::rwlock::owned_read_guard::OwnedRwLockReadGuard;
|
||||
use crate::sync::rwlock::owned_write_guard_mapped::OwnedRwLockMappedWriteGuard;
|
||||
use crate::sync::rwlock::RwLock;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
use std::ops;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem, ops, ptr};
|
||||
|
||||
/// Owned RAII structure used to release the exclusive write access of a lock when
|
||||
/// dropped.
|
||||
@@ -17,16 +15,41 @@ use std::sync::Arc;
|
||||
/// [`RwLock`]: struct@crate::sync::RwLock
|
||||
#[clippy::has_significant_drop]
|
||||
pub struct OwnedRwLockWriteGuard<T: ?Sized> {
|
||||
// When changing the fields in this struct, make sure to update the
|
||||
// `skip_drop` method.
|
||||
#[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>>>,
|
||||
pub(super) lock: Arc<RwLock<T>>,
|
||||
pub(super) data: *mut T,
|
||||
pub(super) _p: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // Unused fields are still used in Drop.
|
||||
struct Inner<T: ?Sized> {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
permits_acquired: u32,
|
||||
lock: Arc<RwLock<T>>,
|
||||
data: *const T,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
|
||||
fn skip_drop(self) -> Inner<T> {
|
||||
let me = mem::ManuallyDrop::new(self);
|
||||
// SAFETY: This duplicates the values in every field of the guard, then
|
||||
// forgets the originals, so in the end no value is duplicated.
|
||||
unsafe {
|
||||
Inner {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: ptr::read(&me.resource_span),
|
||||
permits_acquired: me.permits_acquired,
|
||||
lock: ptr::read(&me.lock),
|
||||
data: me.data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new [`OwnedRwLockMappedWriteGuard`] for a component of the locked
|
||||
/// data.
|
||||
///
|
||||
@@ -65,24 +88,90 @@ impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
|
||||
F: FnOnce(&mut T) -> &mut U,
|
||||
{
|
||||
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);
|
||||
let this = this.skip_drop();
|
||||
|
||||
OwnedRwLockMappedWriteGuard {
|
||||
permits_acquired,
|
||||
lock: ManuallyDrop::new(lock),
|
||||
permits_acquired: this.permits_acquired,
|
||||
lock: this.lock,
|
||||
data,
|
||||
_p: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span,
|
||||
resource_span: this.resource_span,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to make a new [`OwnedRwLockMappedWriteGuard`] for a component
|
||||
/// Makes a new [`OwnedRwLockReadGuard`] for a component of the locked data.
|
||||
///
|
||||
/// This operation cannot fail as the `OwnedRwLockWriteGuard` passed in already
|
||||
/// locked the data.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `OwnedRwLockWriteGuard::downgrade_map(..)`. A method would interfere with methods of
|
||||
/// the same name on the contents of the locked data.
|
||||
///
|
||||
/// Inside of `f`, you retain exclusive access to the data, despite only being given a `&T`. Handing out a
|
||||
/// `&mut T` would result in unsoundness, as you could use interior mutability.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Arc;
|
||||
/// use tokio::sync::{RwLock, OwnedRwLockWriteGuard};
|
||||
///
|
||||
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
/// struct Foo(u32);
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// let lock = Arc::new(RwLock::new(Foo(1)));
|
||||
///
|
||||
/// let guard = Arc::clone(&lock).write_owned().await;
|
||||
/// let mapped = OwnedRwLockWriteGuard::downgrade_map(guard, |f| &f.0);
|
||||
/// let foo = lock.read_owned().await;
|
||||
/// assert_eq!(foo.0, *mapped);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn downgrade_map<F, U: ?Sized>(this: Self, f: F) -> OwnedRwLockReadGuard<T, U>
|
||||
where
|
||||
F: FnOnce(&T) -> &U,
|
||||
{
|
||||
let data = f(&*this) as *const U;
|
||||
let this = this.skip_drop();
|
||||
let guard = OwnedRwLockReadGuard {
|
||||
lock: this.lock,
|
||||
data,
|
||||
_p: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: this.resource_span,
|
||||
};
|
||||
|
||||
// Release all but one of the permits held by the write guard
|
||||
let to_release = (this.permits_acquired - 1) as usize;
|
||||
guard.lock.s.release(to_release);
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
guard.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
write_locked = false,
|
||||
write_locked.op = "override",
|
||||
)
|
||||
});
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
guard.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
current_readers = 1,
|
||||
current_readers.op = "add",
|
||||
)
|
||||
});
|
||||
|
||||
guard
|
||||
}
|
||||
|
||||
/// Attempts to make a new [`OwnedRwLockMappedWriteGuard`] for a component
|
||||
/// of the locked data. The original guard is returned if the closure
|
||||
/// returns `None`.
|
||||
///
|
||||
@@ -129,24 +218,99 @@ impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
|
||||
Some(data) => data as *mut U,
|
||||
None => return Err(this),
|
||||
};
|
||||
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);
|
||||
let this = this.skip_drop();
|
||||
|
||||
Ok(OwnedRwLockMappedWriteGuard {
|
||||
permits_acquired,
|
||||
lock: ManuallyDrop::new(lock),
|
||||
permits_acquired: this.permits_acquired,
|
||||
lock: this.lock,
|
||||
data,
|
||||
_p: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span,
|
||||
resource_span: this.resource_span,
|
||||
})
|
||||
}
|
||||
|
||||
/// Attempts to make a new [`OwnedRwLockReadGuard`] for a component of
|
||||
/// the locked data. The original guard is returned if the closure returns
|
||||
/// `None`.
|
||||
///
|
||||
/// This operation cannot fail as the `OwnedRwLockWriteGuard` passed in already
|
||||
/// locked the data.
|
||||
///
|
||||
/// This is an associated function that needs to be
|
||||
/// used as `OwnedRwLockWriteGuard::try_downgrade_map(...)`. A method would interfere with
|
||||
/// methods of the same name on the contents of the locked data.
|
||||
///
|
||||
/// Inside of `f`, you retain exclusive access to the data, despite only being given a `&T`. Handing out a
|
||||
/// `&mut T` would result in unsoundness, as you could use interior mutability.
|
||||
///
|
||||
/// If this function returns `Err(...)`, the lock is never unlocked nor downgraded.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Arc;
|
||||
/// use tokio::sync::{RwLock, OwnedRwLockWriteGuard};
|
||||
///
|
||||
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
/// struct Foo(u32);
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// let lock = Arc::new(RwLock::new(Foo(1)));
|
||||
///
|
||||
/// let guard = Arc::clone(&lock).write_owned().await;
|
||||
/// let guard = OwnedRwLockWriteGuard::try_downgrade_map(guard, |f| Some(&f.0)).expect("should not fail");
|
||||
/// let foo = lock.read_owned().await;
|
||||
/// assert_eq!(foo.0, *guard);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn try_downgrade_map<F, U: ?Sized>(
|
||||
this: Self,
|
||||
f: F,
|
||||
) -> Result<OwnedRwLockReadGuard<T, U>, Self>
|
||||
where
|
||||
F: FnOnce(&T) -> Option<&U>,
|
||||
{
|
||||
let data = match f(&*this) {
|
||||
Some(data) => data as *const U,
|
||||
None => return Err(this),
|
||||
};
|
||||
let this = this.skip_drop();
|
||||
let guard = OwnedRwLockReadGuard {
|
||||
lock: this.lock,
|
||||
data,
|
||||
_p: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: this.resource_span,
|
||||
};
|
||||
|
||||
// Release all but one of the permits held by the write guard
|
||||
let to_release = (this.permits_acquired - 1) as usize;
|
||||
guard.lock.s.release(to_release);
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
guard.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
write_locked = false,
|
||||
write_locked.op = "override",
|
||||
)
|
||||
});
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
guard.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
current_readers = 1,
|
||||
current_readers.op = "add",
|
||||
)
|
||||
});
|
||||
|
||||
Ok(guard)
|
||||
}
|
||||
|
||||
/// Converts this `OwnedRwLockWriteGuard` into an
|
||||
/// `OwnedRwLockMappedWriteGuard`. This method can be used to store a
|
||||
/// non-mapped guard in a struct field that expects a mapped guard.
|
||||
@@ -192,15 +356,22 @@ impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
|
||||
/// assert_eq!(*lock.read().await, 2, "second writer obtained write lock");
|
||||
/// # }
|
||||
/// ```
|
||||
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;
|
||||
pub fn downgrade(self) -> OwnedRwLockReadGuard<T> {
|
||||
let this = self.skip_drop();
|
||||
let guard = OwnedRwLockReadGuard {
|
||||
lock: this.lock,
|
||||
data: this.data,
|
||||
_p: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: this.resource_span,
|
||||
};
|
||||
|
||||
// Release all but one of the permits held by the write guard
|
||||
lock.s.release(to_release);
|
||||
let to_release = (this.permits_acquired - 1) as usize;
|
||||
guard.lock.s.release(to_release);
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
guard.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
write_locked = false,
|
||||
@@ -209,7 +380,7 @@ impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
|
||||
});
|
||||
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
self.resource_span.in_scope(|| {
|
||||
guard.resource_span.in_scope(|| {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::state_update",
|
||||
current_readers = 1,
|
||||
@@ -217,18 +388,7 @@ impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
|
||||
)
|
||||
});
|
||||
|
||||
#[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,
|
||||
}
|
||||
guard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,6 +427,7 @@ 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!(
|
||||
@@ -275,6 +436,5 @@ impl<T: ?Sized> Drop for OwnedRwLockWriteGuard<T> {
|
||||
write_locked.op = "override",
|
||||
)
|
||||
});
|
||||
unsafe { ManuallyDrop::drop(&mut self.lock) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use crate::sync::rwlock::RwLock;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
use std::ops;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem, ops, ptr};
|
||||
|
||||
/// Owned RAII structure used to release the exclusive write access of a lock when
|
||||
/// dropped.
|
||||
@@ -16,16 +14,41 @@ use std::sync::Arc;
|
||||
/// [`OwnedRwLockWriteGuard`]: struct@crate::sync::OwnedRwLockWriteGuard
|
||||
#[clippy::has_significant_drop]
|
||||
pub struct OwnedRwLockMappedWriteGuard<T: ?Sized, U: ?Sized = T> {
|
||||
// When changing the fields in this struct, make sure to update the
|
||||
// `skip_drop` method.
|
||||
#[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>>>,
|
||||
pub(super) lock: Arc<RwLock<T>>,
|
||||
pub(super) data: *mut U,
|
||||
pub(super) _p: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // Unused fields are still used in Drop.
|
||||
struct Inner<T: ?Sized, U: ?Sized> {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: tracing::Span,
|
||||
permits_acquired: u32,
|
||||
lock: Arc<RwLock<T>>,
|
||||
data: *const U,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, U: ?Sized> OwnedRwLockMappedWriteGuard<T, U> {
|
||||
fn skip_drop(self) -> Inner<T, U> {
|
||||
let me = mem::ManuallyDrop::new(self);
|
||||
// SAFETY: This duplicates the values in every field of the guard, then
|
||||
// forgets the originals, so in the end no value is duplicated.
|
||||
unsafe {
|
||||
Inner {
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span: ptr::read(&me.resource_span),
|
||||
permits_acquired: me.permits_acquired,
|
||||
lock: ptr::read(&me.lock),
|
||||
data: me.data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `OwnedRwLockMappedWriteGuard` for a component of the locked
|
||||
/// data.
|
||||
///
|
||||
@@ -64,20 +87,15 @@ impl<T: ?Sized, U: ?Sized> OwnedRwLockMappedWriteGuard<T, U> {
|
||||
F: FnOnce(&mut U) -> &mut V,
|
||||
{
|
||||
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);
|
||||
let this = this.skip_drop();
|
||||
|
||||
OwnedRwLockMappedWriteGuard {
|
||||
permits_acquired,
|
||||
lock: ManuallyDrop::new(lock),
|
||||
permits_acquired: this.permits_acquired,
|
||||
lock: this.lock,
|
||||
data,
|
||||
_p: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span,
|
||||
resource_span: this.resource_span,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,20 +144,15 @@ impl<T: ?Sized, U: ?Sized> OwnedRwLockMappedWriteGuard<T, U> {
|
||||
Some(data) => data as *mut V,
|
||||
None => return Err(this),
|
||||
};
|
||||
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);
|
||||
let this = this.skip_drop();
|
||||
|
||||
Ok(OwnedRwLockMappedWriteGuard {
|
||||
permits_acquired,
|
||||
lock: ManuallyDrop::new(lock),
|
||||
permits_acquired: this.permits_acquired,
|
||||
lock: this.lock,
|
||||
data,
|
||||
_p: PhantomData,
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
resource_span,
|
||||
resource_span: this.resource_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -179,6 +192,7 @@ 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!(
|
||||
@@ -187,6 +201,5 @@ impl<T: ?Sized, U: ?Sized> Drop for OwnedRwLockMappedWriteGuard<T, U> {
|
||||
write_locked.op = "override",
|
||||
)
|
||||
});
|
||||
unsafe { ManuallyDrop::drop(&mut self.lock) };
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user