Compare commits

...
Author SHA1 Message Date
Alice Ryhl 1df874ead4 chore: prepare Tokio v1.27.0 (#5584) 2023-03-27 23:55:48 +02:00
Alice Ryhl 614fe357fc chore: prepare tokio-macros v2.0.0 (#5580) 2023-03-27 22:45:54 +02:00
sgasseandSimon B. Gasse 68b02db154 time: fix wake-up with interval on Ready (#5553)
When `tokio::time::Interval::poll_tick()` returns `Poll::Pending`, it
schedules itself for being woken up again through the waker of the
passed context, which is correct behavior.

However when `Poll::Ready(_)` is returned, the interval timer should be
reset but not scheduled to be woken up again as this is up to the
caller.

This commit fixes the bug by introducing a `reset_without_reregister`
method on `TimerEntry` which is called by `Intervall::poll_tick(cx)` in
case the delay poll returns `Poll::Ready(_)`.

Co-authored-by: Simon B. Gasse <[email protected]>
2023-03-27 17:39:53 +02:00
Alice Ryhl 822af18cf5 sync: fix Semaphore::MAX_PERMITS test (#5582) 2023-03-25 18:09:05 +00:00
David Pedersen 92d33b7181 macros: update syn (#5572) 2023-03-23 23:38:59 +01:00
Marcelo Diop-Gonzalez 1cb7bf11b3 time: clean up redundant check in Wheel::poll() (#5574)
The condition checked in the and_then() call is the same as is checked
in the match below, so we can clean it up by just matching on
next_expiration() directly.
2023-03-23 09:53:54 -07:00
João Marcos 768ede65c1 io: refer to ReaderStream and StreamReader in module docs (#5576) 2023-03-23 15:31:40 +00:00
João Marcos 54a394696f io: add details to docs of tokio::io::copy[_buf] (#5575) 2023-03-23 15:19:51 +00:00
Hayden Stainsby 35dd635630 tracing: fix spawn_blocking location fields (#5573)
In a previous PR (#4128), the `spawn.location` field on task spans was
structured into 3 separate fields for the `file`, `line`, and `col`.
There is a separately created span for blocking tasks which was missed.

This caused tasks created with `spawn_blocking` to appear in
`tokio-console` without a location, but with an additional "free form"
field containing the formatted source code location.

This change modifies this span to use the same format. The span creation
needs to be separate from the other task spans because it records the
function name. This information is useful in the `spawn_blocking` case,
but can be "catastrophically long" in the `async fn` case and was
removed in #3074.
2023-03-22 22:08:01 +01:00
Timmy Xiao a7bb054414 tokio: update stream, util, test to 2021 edition (#5571) 2023-03-21 17:36:40 +00:00
Alice Ryhl 0c8e8248f8 tokio: bump MSRV to 1.56 (#5559) 2023-03-21 18:06:47 +01:00
Alice Ryhl 2dfe4e8885 time: fix repeatedly_reset_entry_inserted_as_expired test (#5570) 2023-03-21 18:06:22 +01:00
Alice Ryhl b489acb46c sync: try to lock the parent first in CancellationToken (#5561) 2023-03-21 14:20:33 +01:00
Ivan Zderadicka d46c844bb9 examples: fix panic in tinyhttp example (#5328)
As method in httparse result is no longer part of input buffer it needs
to be handled separately.
2023-03-20 18:52:38 +01:00
devensiv 4cd4b02389 io: fix wait operation on mock (#5554) 2023-03-19 12:35:39 +00:00
daxpedda 17cc283f58 macros: accept path as crate rename (#5557) 2023-03-19 12:28:43 +01:00
Alice Ryhl 0a93ed7e7a io: use memchr from libc (#5558) 2023-03-19 10:49:19 +00:00
Alice Ryhl cef98e25e7 macros: define cancellation safety (#5525) 2023-03-18 21:25:23 +00:00
Tymoteusz Wiśniewski e7bd754231 fs: fuse std iterator in ReadDir (#5555)
Implementation of Tokio's ReadDir assumes that ReadDir from std is
fused, but that's not the case on Windows. This change wraps the std
iterator in std::iter::Fuse to make its usage correct.
2023-03-18 14:07:40 +01:00
Austin Bonander d459a93453 net: add UdpSocket::peek_sender() (#5520) 2023-03-17 10:22:42 +01:00
Shaye Garg f177aad6e4 task: add JoinHandle::abort_handle (#5543) 2023-03-16 14:02:48 +01:00
Tymoteusz Wiśniewski 4ea632005d tokio: make all windows docs visible in unix builds (#5530) 2023-03-14 23:33:31 +01:00
Matilda Smeds bfc43795f9 doc: explain testing in contributing guide (#5537)
* Add links to fundamental testing concepts in Rust
* Add information about conditional compilation attributes
  and how to use them to run tests with cargo
2023-03-12 21:01:58 +01:00
Tymoteusz Wiśniewski 89329cd07f io: add missing AsFd/AsHandle impls (#5540)
* io: add AsFd/AsHandle impls for stdio

* io: add AsFd impl for AsyncFd

* net: add AsHandle impl for named pipe types
2023-03-12 20:01:19 +01:00
Steven Fackler e34978233b io: add implementations of AsFd/AsHandle/AsSocket (#5514) 2023-03-11 17:04:01 +01:00
Andrew Halle 8eb94a33c0 time: fix a typo in timeout docs (#5538)
Replace instances of "immediatelly" with "immediately".
2023-03-11 10:40:19 +01:00
amab8901 2b7b1a0494 io: add async_io helper method to sockets (#5512) 2023-03-09 10:12:55 +00:00
Filipe Rodrigues 002f4a28c8 sync: add RwLockWriteGuard::{downgrade_map, try_downgrade_map} (#5527) 2023-03-08 17:06:08 +01:00
Tymoteusz Wiśniewski ff2f286c12 fs: fix File::from_raw_fd test (#5528) 2023-03-04 11:45:42 +01:00
Aaron Chen bd4ce68864 process: change "with regards" to "with regard to" in the docs (#5529) 2023-03-04 09:48:29 +00:00
Tymoteusz Wiśniewski abd92fb27f net: document usage of ready with stream halves (#5515) 2023-03-03 12:21:29 +01:00
Noah Kennedy 9931901d5c chore: list 1.25.x as LTS release (#5524) 2023-03-01 23:42:18 +00:00
Noah Kennedy a377240bbf chore: prepare for Tokio v1.26.0 release (#5521)
# 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
2023-03-01 22:09:48 +00:00
Noah Kennedy 52da177dea metrics: add a new metric for budget exhaustion yields (#5517) 2023-03-01 21:25:35 +01:00
Carl Lerche ee1c940709 time: Improve Instant::now() perf with test-util (#5513)
The test-util feature flag is only intended to be used with tests.
However, it is possible to enable it in release mode accidentally. This
patch reduces the overhead of `Instant::now()` when the `test-util`
feature flag is enabled but `time::pause()` is not called.

The optimization is implemented by adding a static atomic flag that
tracks if `time::pause()` has ever been called. In `Instant::now()`, the
atomic flag is first checked before the thread-local and mutex are
accessed.
2023-02-27 10:21:42 -08:00
Grachev Mikhail 815d89a407 runtime: remove extra period in docs (#5511) 2023-02-27 15:41:17 +01:00
Daria Sukhonina 54aaf3d0e3 time: document immediate completion guarantee for timeouts (#5509) 2023-02-27 14:17:31 +00:00
Tymoteusz Wiśniewski 5a3abe56ee net: add types for named unix pipes (#5351) 2023-02-27 09:39:07 +01:00
Alice Ryhl d44b1ca9c8 io: ignore SplitByUtf8BoundaryIfWindows test on miri (#5507)
These tests take a very long time under miri, but the code they're
testing isn't unsafe, so there isn't any reason to run them under miri.
2023-02-26 21:44:44 +00:00
Eric McBride e23c6f3935 signal: updated Documentation for Signals (#5459) 2023-02-26 19:43:41 +00:00
Alice Ryhl 0a50cb3baa net: fix test compilation failure (#5506) 2023-02-26 19:06:08 +00:00
Christopher Hunt 2298679af4 runtime: document the nature of the main future (#5494) 2023-02-26 16:48:56 +01:00
Chris Brody cadcd5da5e fs: add more tests for filesystem functionality (#5493) 2023-02-26 16:48:09 +01:00
Adrian Heine né Lang ca9f7ee9f4 macros: fix empty join! and try_join! (#5504)
Fixes: #5502
2023-02-25 18:06:17 +01:00
Hayden Stainsby c89406965f sync: document drop behavior for channels (#5497)
Some users mentioned that the behavior of a channel when the receivers
and/or senders are dropped isn't explicitly documented.

This change adds wording to the documentation for each channel in the
sync module, explaining under which conditions messages in a channel are
dropped with respect to dropping the senders and the receivers.

Refs: #5490
2023-02-23 11:04:12 +01:00
Predrag Gruevski cf486361d0 ci: remove cargo-semver-checks flags that are no longer necessary (#5496)
These flags were previously only needed due to a bug in the `cargo-semver-checks` CLI logic.

The correct behavior (available as of v0.18.3) for `cargo-semver-checks` is to ignore `publish = false` crates when scanning a workspace, *unless* those crates are specifically selected for checking.

All the crates being excluded here are `publish = false` so they are already excluded by the default behavior, so all `--exclude` flags are no-ops.
2023-02-22 18:54:17 +00:00
Alice Ryhl d7b7c61317 tokio: document supported platforms (#5483) 2023-02-21 19:57:19 +01:00
Kevin (Kun) "Kassimo" Qian 12f81ffa61 fs: add fs::try_exists (#4299) 2023-02-21 14:15:54 +01:00
Alice Ryhl 3ea5cc5a82 stream: fix changelog for 0.1.12 (#5488) 2023-02-20 16:19:56 +00:00
Konrad Borowski fa31cd9990 io: use poll_fn in copy_bidirectional (#5486) 2023-02-20 15:38:40 +01:00
Alice Ryhl 46f974d8cf chore: prepare tokio-stream v0.1.12 (#5484) 2023-02-20 10:18:18 +01:00
Alice Ryhl 018d0450c7 io: improve AsyncFd example (#5481) 2023-02-19 23:40:56 +01:00
Alice Ryhl ee09e04c31 sync: drop wakers after unlocking the mutex in Notify (#5471) 2023-02-19 22:16:24 +01:00
Chris Brody d07027f5bc sync: add WatchStream::from_changes (#5432) 2023-02-19 16:16:12 +01:00
Alice Ryhl 2e0372be6f sync: add MappedOwnedMutexGuard (#5474) 2023-02-19 14:12:32 +01:00
Tymoteusz Wiśniewski eca24068f7 sync: fix docs for Send/Sync bounds in broadcast (#5480) 2023-02-19 14:11:42 +01:00
Tymoteusz Wiśniewski 795754a846 sync: make notify_waiters calls atomic (#5458) 2023-02-19 14:10:38 +01:00
Alice Ryhl 0f17d69303 ci: remove PROPTEST_CASES from miri (#5478)
This option doesn't do anything anymore.
2023-02-19 11:11:22 +00:00
Maximilian Hils 2e7f996f17 net: refactor named pipe builders to not use bitfields (#5477) 2023-02-19 11:59:28 +01:00
amab8901 901f6d26c6 sync: drop wakers outside lock in semaphore (#5475) 2023-02-19 11:16:59 +01:00
Maximilian Hils a8fda87058 net: use Message Read Mode for named pipes (#5350) 2023-02-18 20:03:16 +01:00
tijsvd d7abdbb315 benches: mutex contention in watch::Receiver bench (#5472) 2023-02-18 14:51:08 +00:00
Alice Ryhl 24aac0add3 sync: don't leak tracing spans in mutex guards (#5469) 2023-02-18 10:39:25 +01:00
Alice Ryhl b921fe45ac sync: reduce contention in watch channel (#5464) 2023-02-17 23:56:56 +01:00
Alice Ryhl 0dc1b71e6e time: remove cache padding in timer entries (#5468) 2023-02-17 22:49:45 +01:00
Alice Ryhl d19f2f2d39 sync: add doc aliases for blocking_* methods (#5448) 2023-02-17 16:23:57 +01:00
Christopher Hunt e106c4d32b benches: benchmark for things in block_on (#5440)
This additional benchmark exercises a common request/reply pattern using an MPSC for requests along with a oneshot payload as a reply mechanism. When used in a current threaded scenario, the bench is 17 times faster on my machine than when using the multi-threaded runtime and one worker thread. Not only that, but if I increase the number of worker threads to 6, performance degrades further.

Does this suggest a scheduling problem with the multi-threaded runtime?

No matter what, hopefully the benchmarks are a useful addition.
2023-02-14 23:05:10 +00:00
Tim de Jager 28d6f4d509 task: fix wording with 'unsend' (#5452) 2023-02-14 12:44:05 +00:00
Finomnis d1da6c20d8 ci: always assume minor release in semver check (#5455) 2023-02-14 10:09:59 +01:00
147 changed files with 6631 additions and 1341 deletions
+1 -1
View File
@@ -1 +1 @@
msrv = "1.49"
msrv = "1.56"
+2 -8
View File
@@ -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
View File
@@ -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
+18 -4
View File
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.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
+18
View File
@@ -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
+53
View File
@@ -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);
+64
View File
@@ -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);
+25
View File
@@ -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);
+6 -3
View File
@@ -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
+15
View File
@@ -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])
+3 -3
View File
@@ -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
View File
@@ -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 {
+7
View File
@@ -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
+3 -4
View File
@@ -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);
}
}
+10
View File
@@ -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])
+3 -3
View File
@@ -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"
+1 -1
View File
@@ -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,
{
+32 -2
View File
@@ -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> {
+29 -1
View File
@@ -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");
}
+2 -2
View File
@@ -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"
+2
View File
@@ -310,6 +310,8 @@ impl Inner {
if now < until {
break;
} else {
self.waiting = None;
}
} else {
self.waiting = Some(Instant::now() + *dur);
+63
View File
@@ -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()
);
}
+2 -2
View File
@@ -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);
}
}
+8
View File
@@ -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();
+132
View File
@@ -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
View File
@@ -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
View File
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+6 -7
View File
@@ -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.
+6 -4
View File
@@ -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();
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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();
+34
View File
@@ -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),
}
}
+45 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()) }
}
}
}
+2
View File
@@ -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
View File
@@ -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()) }
}
}
}
+9 -1
View File
@@ -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
///
+14 -43
View File
@@ -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
}
+7 -1
View File
@@ -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
View File
@@ -1,4 +1,5 @@
use crate::io::AsyncBufRead;
use crate::util::memchr;
use pin_project_lite::pin_project;
use std::future::Future;
+27 -1
View File
@@ -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:
-31
View File
@@ -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())
}
)*}
};
}
+12
View File
@@ -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)*) => {
+3 -1
View File
@@ -158,7 +158,9 @@ macro_rules! join {
// ===== Entry point =====
( $($e:expr),* $(,)?) => {
( $($e:expr),+ $(,)?) => {
$crate::join!(@{ () (0) } $($e,)*)
};
() => { async {}.await }
}
+7
View File
@@ -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
+3 -1
View File
@@ -210,7 +210,9 @@ macro_rules! try_join {
// ===== Entry point =====
( $($e:expr),* $(,)?) => {
( $($e:expr),+ $(,)?) => {
$crate::try_join!(@{ () (0) } $($e,)*)
};
() => { async { Ok(()) }.await }
}
+25 -5
View File
@@ -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
View File
@@ -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 }
}
}
}
+12 -6
View File
@@ -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
+12 -6
View File
@@ -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
+61 -5
View File
@@ -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
View File
@@ -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()) }
}
}
}
+45 -1
View File
@@ -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()) }
}
}
+9 -1
View File
@@ -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()) }
}
}
+3 -1
View File
@@ -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
+22 -6
View File
@@ -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
+16 -6
View File
@@ -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
+45 -1
View File
@@ -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()) }
}
}
+185 -91
View File
@@ -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
View File
@@ -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)))]
+16
View File
@@ -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>,
+3 -1
View File
@@ -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)
};
+1 -1
View File
@@ -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))
}
+42 -5
View File
@@ -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 }
}
}
+1 -1
View File
@@ -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
View File
@@ -1,6 +1,5 @@
use crate::runtime::WorkerMetrics;
use std::convert::TryFrom;
use std::sync::atomic::Ordering::Relaxed;
use std::time::Instant;
+15
View File
@@ -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
+7
View File
@@ -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);
}
}
+7
View File
@@ -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
+54 -21
View File
@@ -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
View File
@@ -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);
-2
View File
@@ -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 {
+1 -1
View File
@@ -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| {
+3 -13
View File
@@ -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
+9 -5
View File
@@ -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
View File
@@ -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
View File
@@ -1,4 +1,3 @@
use std::convert::TryFrom;
use std::io;
use std::sync::Once;
+4 -1
View File
@@ -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
}
+5 -1
View File
@@ -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
+1 -1
View File
@@ -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;
+2
View File
@@ -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))
}
+2 -1
View File
@@ -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
//!
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
+5
View File
@@ -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
View File
@@ -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.
+34 -23
View File
@@ -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(|| {
+205 -45
View File
@@ -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