Compare commits

...
Author SHA1 Message Date
Alice Ryhl 92a3455c66 chore: prepare Tokio v1.35.0 (#6197) 2023-12-08 23:35:27 +01:00
Alice Ryhl 1968565825 chore: use relaxed load for unsync_load (#6203) 2023-12-08 22:28:40 +00:00
Alice Ryhl c9273f1aee sync: improve safety comments for WakeList (#6200) 2023-12-08 18:57:01 +01:00
Alice Ryhl e05d0f8c2c changelog: fix missing link for 1.8.2 (#6199) 2023-12-08 17:51:46 +00:00
Alice Ryhl debcb2254a Revert "net: add SocketAddr::as_abstract_namespace (#6144)" (#6198)
This reverts commit 2400769b54.
2023-12-08 18:44:32 +01:00
tijsvd 83b7397e44 io: drop the Sized requirements from AsyncReadExt.read_buf (#6169) 2023-12-08 09:51:08 +01:00
NAHO 3991f9f9a4 docs: fix typo in 'tokio/src/sync/broadcast.rs' (#6182) 2023-12-08 09:48:36 +01:00
Alice Ryhl 48c0e6283f chore: use relaxed load for unsync_load on miri (#6179) 2023-12-08 09:48:15 +01:00
Jack Wrenn d561b5850a taskdump: skip notified tasks during taskdumps (#6194)
Fixes #6051
2023-12-08 09:47:46 +01:00
Weijia Jiang 3a4aef17b2 runtime: reduce the lock contention in task spawn (#6001) 2023-12-07 10:45:07 +00:00
Andrew Walbran a0a58d7edd tokio: update nix dependency to 0.27.1 (#6190) 2023-12-04 16:09:58 +01:00
fzyzcjy ed4f766c98 runtime: make Runtime unwind safe (#6189) 2023-12-04 13:07:31 +01:00
zhiqiangxu 3ac4cfb68a runtime: move comment to the right place (#6184) 2023-12-03 11:47:40 +01:00
Kamila Borowska 7232424a81 chore: remove uses of unsafe from maybe_done.rs (#6180) 2023-12-01 12:51:42 +01:00
simlay 4c33ed33f6 net: add Apple watchOS support (#6176) 2023-11-29 21:16:48 +01:00
Alice Ryhl 3468b4b72f runtime: document fairness guarantees and current behavior (#6145) 2023-11-29 12:32:58 +01:00
Hans Krutzer 2400769b54 net: add SocketAddr::as_abstract_namespace (#6144) 2023-11-25 14:44:34 +00:00
aliu a8e8fa6681 time: add DelayQueue::deadline (#6163) 2023-11-23 18:20:26 +01:00
Alice Ryhl 498288cd31 chore: fix docsrs without net feature (#6166) 2023-11-23 15:20:46 +01:00
Hayden Stainsby 7a30504fd4 task: make task span explicit root (#6158)
In Tokio, tasks are optionally instrumented with tracing spans to allow
analysis of the runtime behavior to be performed with tools like
tokio-console.

The span that is created for each task gets currently follows the
default tracing behavior and has a contextual parent attached to it
based on the span that is actual when `tokio::spawn` or similar is
called.

However, in tracing, a span will remain "alive" until all its children
spans are closed. This doesn't match how spawned tasks work. A task may
outlive the context in which is was spawned (and frequently does). This
causes tasks which spawn other - longer living - tasks to appear in
`tokio-console` as having lost their waker when instead they should be
shown as completed (tokio-rs/console#345). It can also cause undesired
behavior for unrelated tracing spans if a subscriber is receiving both
the other spans as well as Tokio's instrumentation.

To fix this mismatch in behavior, the task span has `parent: None` set
on it, making it an explicit root - it has no parent. The same was
already done for all spans representing resources in #6107. This change
is made within the scope of #5792.

Due to a defect in the currently available `tracing-mock` crate, it is
not possible to test this change at a tracing level
(tokio-rs/tracing#2440). Instead, a test for the `console-subscriber`
has been written which shows that this change fixes the defect as
observed in `tokio-console` (tokio-rs/console#490).
2023-11-19 19:21:19 +01:00
Daniel Sedlak 340d4e5238 runtime: fix comment typos (#6143) 2023-11-19 14:01:29 +01:00
Marc-Andre GirouxandDavid Barsky 7b555185ff sync: avoid creating resource spans with curernt parent, use a None parent instead (#6107)
A child span stored on sync primitives can keep the parent span open,
unable to be closed by subscribers due to the sync resource referencing it.

Fixes: #6106

Co-authored-by: David Barsky <[email protected]>
2023-11-14 16:24:21 -05:00
Hayden Stainsby 135d7ca38e taskdump: fix taskdump cargo config example (#6150)
The documentation for the `Handle::dump()` method includes a description
of the Rust flags needed. However, the sample `.cargo/config.toml` file
is incorrect. It gives the Rust flags as:

```
rustflags = ["--cfg tokio_unstable", "--cfg tokio_taskdump"]
```

However, each command line argument needs to be a separate element in
the array:

```
rustflags = ["--cfg", "tokio_unstable", "--cfg", "tokio_taskdump"]
```

This change corrects that line.
2023-11-14 20:17:05 +01:00
Alice Ryhl e6720f985d metrics: fix hang in worker_overflow_count test (#6146)
Closes: #6054
2023-11-14 20:13:56 +01:00
Alice Ryhl d44e995bb0 task: document cancel safety of LocalSet::run_until (#6147)
Closes: #6122
2023-11-14 20:13:43 +01:00
Alice Ryhl 06660ef00a io: flush in AsyncWriteExt examples (#6149)
Closes: #6068
2023-11-14 20:13:00 +01:00
Alice Ryhl 2e5773a6fe runtime: handle missing context on wake (#6148) 2023-11-14 14:01:10 +01:00
Carl Lerche 49eb26f159 chore: prepare Tokio v1.34.0 release (#6138)
This also includes:
  - tokio-macros v2.2.0
2023-11-09 11:22:54 -08:00
Carl Lerche 19d96c0674 io: increase ScheduledIo tick resolution (#6135)
While this is no current evidence that increasing the tick resolution is
necessary, since we have available bits, we might as well use them.
2023-11-08 15:11:18 -08:00
Carl Lerche 30b2eb17c8 io: fix possible I/O resource hang (#6134)
Use a per-resource tick instead of a single global tick counter. This
prevents the hang issue described by #6133.

Fixes: #6133
2023-11-08 14:05:06 -08:00
aliu 8ec3e0d94d metrics: update stats when unparking in multi-thread (#6131) 2023-11-06 17:14:51 +01:00
Yotam Ofek 161ecec156 stream: fix typo in peekable docs (#6130) 2023-11-05 22:51:09 +01:00
Tymoteusz Wiśniewski 61fcc3bc0b time: remove cached elapsed value from driver state (#6097) 2023-11-05 15:00:05 +01:00
Alice Ryhl 944024e8eb chore: update rust-version to 1.63 in all crates (#6126) 2023-11-04 09:07:22 +01:00
ad hoc 65f861f478 stream: add StreamExt::peekable (#6095) 2023-11-01 17:32:35 +00:00
Alice Ryhl 4c8580152d ci: fix docs on latest nightly (#6120) 2023-11-01 16:33:32 +00:00
Hayden Stainsby ed32cd194c task: add tests for tracing instrumentation of tasks (#6112)
Tokio is instrumented with traces which can be used to analyze the
behavior of the runtime during execution or post-mortem. The
instrumentation is optional. This is where tokio-console collections
information.

There are currently no tests for the instrumentation.

In order to provide more stability to the instrumentation and prepare
for future changes, tests are added to verify the current behavior. The
tests are written using the `tracing-mock` crate. As this crate is still
unreleased, a separate test create has been added under `tokio/tests`
which is outside the workspace. This allows us to pull in both `tracing`
and `tracing-mock` from the tracing repository on GitHub without
affecting the rest of the tokio repository.

This change adds initial tests for the task instrumentation. Further
tests will be added in subsequent commits.

Once `tracing-mock` is published on crates.io (tokio-rs/tracing#539),
these tests can be moved in with the "normal" tokio integration tests.
The decision to add these tests now is due to the release of
`tracing-mock` taking a while, so it would be better to have tests while
we wait.
2023-10-31 10:02:00 +01:00
Fritz Rehde 593dbf55d1 docs: fix typos (#6118) 2023-10-30 15:17:46 +01:00
Satyam1Vishwakarma d8a4a5f24b tokio: gate some panicking tests with #[cfg(panic = "unwind")] (#6115) 2023-10-30 11:51:39 +01:00
Alice Ryhl cc86fef9c0 tokio: gate some panicking tests with #[cfg(panic = "unwind")] (#6110) 2023-10-26 12:28:12 +02:00
Nikolay Arhipov f3949cc56d tokio: added vita target support (#6094) 2023-10-25 14:44:41 +02:00
Alice Ryhl 503fad7908 chore: prepare tokio-util v0.7.10 (#6104) 2023-10-25 12:05:47 +02:00
Alice Ryhl 58acb56a17 ci: update nightly to 2023-10-21 (#6103) 2023-10-24 14:15:25 +00:00
Friedel Ziegelmayer d22c549d97 deps: update hashbrown to 0.14 (#6102) 2023-10-24 12:27:39 +02:00
xianhua zhouandXianhua Zhou bc48a6fa8d sync: fix broadcast::channel link (#6100)
Co-authored-by: Xianhua Zhou <[email protected]>
2023-10-23 15:40:17 +00:00
Alice Ryhl 70410836ae task: add tokio_util::sync::TaskTracker (#6033) 2023-10-22 14:35:19 +00:00
Aaron Schweiger 881b510a07 sync: add mpsc::Receiver::recv_many (#6010) 2023-10-17 11:01:41 +02:00
Rafael Bachmann 6871084629 chore: clippy and doc fixes (#6081) 2023-10-16 17:37:51 +02:00
Alice Ryhl 1b8ebfcffb readme: remove rdbc and add axum to related projects (#6077) 2023-10-16 10:25:40 +02:00
Alice Ryhl 654a3d5acf io: fix integer overflow in take (#6080) 2023-10-15 20:54:27 +02:00
Alice Ryhl a08ad926b1 changelog: fix typo in quadratic (#6079) 2023-10-15 20:30:46 +02:00
Alice Ryhl 723934242b time: reorder comment in sleep.rs (#6076) 2023-10-15 19:21:57 +02:00
Alice Ryhl 944f769cd5 chore: remove bin directory (#6078) 2023-10-15 19:21:12 +02:00
inkyu f3ad6cffd9 task: fix missing wakeup when using LocalSet::enter (#6016) 2023-10-15 19:18:06 +02:00
Andrea StedileandAlice Ryhl f1e41a4ad4 task: add JoinMap::keys (#6046)
Co-authored-by: Alice Ryhl <[email protected]>
2023-10-15 16:47:41 +02:00
icedrocket f9335b8186 fs: update cfg attr in fs::read_dir (#6075) 2023-10-15 13:50:33 +02:00
Alice Ryhl 1134cbb168 net: fix flaky doctest for TcpStream::into_std (#6074) 2023-10-14 15:09:07 +00:00
Alice Ryhl 339c78a680 tokio: remove #5973 from changelog (#6073) 2023-10-14 12:24:45 +00:00
Tymoteusz Wiśniewski c00861210b chore: move 1.20.x to previous LTS releases (#6069) 2023-10-12 18:18:03 +02:00
Alice Ryhl 0f296d2089 io: allow clear_readiness after io driver shutdown (#6067) 2023-10-12 08:18:22 -07:00
Tymoteusz Wiśniewski d420d528f9 chore: render taskdump docs in Netlify previews (#6060) 2023-10-09 11:57:36 +02:00
Tymoteusz Wiśniewski 0457690d01 chore: prepare Tokio v1.33.0 release (#6059) 2023-10-09 11:55:17 +02:00
David Yamnitsky 4557451257 io: implement Seek for SyncIoBridge (#6058) 2023-10-07 14:43:35 +02:00
Jack Wrenn 2bd43765d9 rt: do not trace tasks while locking OwnedTasks (#6036) 2023-10-06 21:48:47 +02:00
Dan Kov f306bd02c3 sync: fix unclosed code block in example (#6056) 2023-10-06 14:33:26 +02:00
Alice Ryhl 6b010ac80f docs: fix new doc warnings in 1.73.0 (#6055) 2023-10-06 09:49:44 +00:00
Alice Ryhl 8cd3383913 time: reduce iteration count in short_sleeps test (#6052) 2023-10-05 22:03:27 +00:00
Alice Ryhl d6ed00c292 sync: Semaphore doc final cleanup (#6050) 2023-10-05 12:43:49 +00:00
songmuhan 5d29136a83 docs: add semaphore example for running tests sequentially (#6038)
Signed-off-by: Muhan Song <[email protected]>
2023-10-05 13:48:19 +02:00
Luís Cruz 52b29b33bb net: add apple tvos support (#6045) 2023-10-04 18:48:53 +02:00
Alice Ryhl eaba9712e8 sync: document that broadcast capacity is a lower bound (#6042) 2023-10-03 07:58:34 +00:00
Alice Ryhl 310adf7ca6 rt: fix flaky test test_disable_lifo_slot (#6043) 2023-10-02 15:05:48 +02:00
Marek Kuskowski 0700d6a7cd io: mark Interest::add with #[must_use] (#6037) 2023-09-29 16:43:38 +02:00
Tymoteusz Wiśniewski ca89c5b2ec benches: move sender to a spawned task in watch benchmark (#6034) 2023-09-28 11:28:12 +02:00
Uwe Klotz 453c720709 sync: use Acquire/Release instead of SeqCst in watch (#6018) 2023-09-24 18:13:43 +02:00
Uwe Klotz e76c06ba38 sync: prevent lock poisoning in watch::Receiver::wait_for (#6021) 2023-09-24 17:26:35 +02:00
Alice Ryhl 02aacf5110 sync: make TokenBucket::close into a destructor in example (#6032) 2023-09-24 14:16:14 +02:00
Weijia Jiang 707fb4d0df tokio: remove wildcard in match patterns (#5970) 2023-09-23 22:05:44 +02:00
Alice Ryhl b161633b5f sync: reorder Semaphore examples (#6031) 2023-09-23 19:53:44 +02:00
M.Amin Rayej f5b8cf9dac sync: add token bucket example to Semaphore (#5978) 2023-09-23 17:45:30 +02:00
Rebekah Kim aa36807c02 sync: fix docs typo (#6030) 2023-09-23 12:33:41 +02:00
Alice Ryhl 74c7a87985 Merge 'tokio-1.32.x' into 'master' (#6028) 2023-09-22 20:00:09 +02:00
Alice Ryhl ccb37c4f39 Merge 'tokio-1.25.2' into 'tokio-1.32.x' (#6027) 2023-09-22 19:58:58 +02:00
Alice Ryhl 9ab4ca68ac chore: prepare Tokio v1.25.2 (#6026) 2023-09-22 18:32:04 +02:00
Alice Ryhl 60a0ca58fa Merge 'tokio-1.20.6' into 'tokio-1.25.x' (#6025) 2023-09-22 18:26:10 +02:00
Alice Ryhl 938c7eb023 chore: prepare Tokio v1.20.6 (#6024) 2023-09-22 13:44:00 +02:00
Alice Ryhl bfa9ea8d9b io: use memchr from libc (#5960) 2023-09-22 13:40:01 +02:00
Uwe Klotz 9bc782acfc sync: fix incorrect comment (#6020) 2023-09-21 10:44:27 +02:00
Chris Constantine 3f6165d82e chore: prepare tokio-util v0.7.9 (#6019) 2023-09-20 19:30:12 +02:00
Uwe Klotz ad7f988da3 sync: fix mark_changed when version overflows (#6017) 2023-09-19 15:44:08 +00:00
nicflower 9d51b76d01 sync: add watch::Sender::new (#5998) 2023-09-19 16:01:36 +02:00
Uwe Klotz 804511822b sync: rename watch::mark_unseen to watch::mark_changed (#6014) 2023-09-19 13:13:46 +00:00
Alexander Kirilin e6553c4ee3 sync: add Semaphore example using an Arc<Semaphore> (#5956) 2023-09-19 14:13:19 +02:00
M.Amin Rayej 98bb3be094 ci: fix ci on tokio-1.20.x (#5999) 2023-09-19 00:16:15 +02:00
Hayden Stainsby d247e7f5df sync: document that const_new() is not instrumented (#6002) 2023-09-13 13:49:02 +02:00
Victor Timofei 65e7715909 util: replace sync::reusable_box::Pending with std::future::Pending (#6000) 2023-09-12 08:53:05 +09:00
Victor Timofei 61042b4d90 sync: add watch::Receiver::mark_unseen (#5962) 2023-09-11 13:50:29 +02:00
Icenowy Zheng 1c428cc558 tokio: fix cache line size for RISC-V (#5994) 2023-09-11 07:22:14 +00:00
Alexandre Bléron 61f095fdc1 sync: add ?Sized bound to {MutexGuard,OwnedMutexGuard}::map (#5997) 2023-09-10 20:08:12 +02:00
Marek Kuskowski 65027b60bc io: add Interest::remove method (#5906) 2023-09-10 16:46:07 +02:00
M.Amin Rayej b046c0dcbb benches: use criterion instead of bencher (#5981) 2023-09-10 16:42:53 +02:00
Adam Chalmers 737dff40cb task: rename generic paramter for spawn (#5993) 2023-09-10 11:21:51 +02:00
Weijia Jiang a6be73eecb codec: document the line ending used by LinesCodec (#5982) 2023-09-08 15:59:48 +02:00
M.Amin Rayej 9fafe783d3 io: support vectored writes for DuplexStream (#5985) 2023-09-08 12:57:18 +02:00
Joan Antoni RE fb3ae0a254 docs: fix worker_overflow_count (#5988) 2023-09-07 14:16:52 -05:00
Hayden Stainsby aad1892ab5 task: fix spawn_local source location (#5984)
The location of a spawned task, as shown in tokio console, is taken from
the location set on the tracing span that instruments the task. For this
location to work, there must be unbroken chain of functions instrumented
with `#[track_caller]`.

For `task::spawn_local`, there was a break in this chain and so the
span contained the location of an internal function in tokio.

This change adds the missing `#[track_caller]` attribute. It has been
tested locally as automated tests would really need `tracing-mock` to be
published so we can use it in the tokio tests.
2023-09-06 10:55:47 +02:00
Hayden Stainsby 8ea303e027 chore: list 1.32.x as LTS release (#5980) 2023-09-05 10:02:50 +02:00
M.Amin Rayej 84ed35ef70 process: document that Child::wait is cancel safe (#5977) 2023-09-04 10:24:55 +02:00
Jack Wrenn 95fb599664 tokio: render taskdump documentation on docs.rs (#5972)
Modifies `package.metadata.docs.r` so that `--cfg tokio_taskdump`
is used by docs.rs when building documentation.
2023-09-02 11:43:31 +02:00
ComplexSpaces 8b312ee571 macros: use ::core qualified imports instead of ::std inside tokio::test macro (#5973) 2023-09-02 03:44:08 +09:00
Colin Walters fd7d0ad5e5 io: add SyncIOBridge::into_inner (#5971) 2023-09-01 13:01:22 +00:00
M.Amin Rayej 37bb47c4a2 fs: add vectored writes to tokio::fs::File (#5958) 2023-08-29 14:18:16 +02:00
Rain cb1e10b745 sync: improve docs for watch channels (#5954)
## Motivation

I found the watch docs as written to be somewhat confusing.

* It wasn't clear to me whether values are marked seen or not at
  creation/subscribe time.
* The example also confused me a bit, suggesting a while loop when a
  do-while loop is generally more correct.
* I noticed a potential race with `borrow` that is no longer an issue
  with `borrow_and_update`.

## Solution

Update the documentation for the watch module to try and make all this
clearer.
2023-08-28 16:13:07 -07:00
Alice Ryhl fb3028f3a2 test: fix testing category slug (#5953) 2023-08-26 17:09:49 +02:00
Alice Ryhl b45f5831cf tokio: remove stats feature (#5952) 2023-08-26 17:09:33 +02:00
Rain 0fe24fcffa sync: improve cancel-safety documentation for mpsc::Sender::send (#5947)
This specific issue (data loss because a send got cancelled) has bitten
our team a couple of times over the last few months. We've switched to
recommending this kind of reserve pattern instead.
2023-08-25 22:03:23 +00:00
Eliza Weisman d1dae25cd2 ci: drop MIPS targets from cross-check (#5951)
Currently, Tokio runs cross-compilation checks for the
`mips-unknown-linux-gnu` and `mipsel-unknown-linux-musl` target triples.
However, Rust has recently demoted these targets from Tier 2 support to
Tier 3 (see rust-lang/compiler-team#648). Therefore, MIPS toolchains may
not always be available, even in stable releases. This is currently
[breaking our CI builds][1], as Rust 1.72.0 does not contain a standard
library for `mips-unknown-linux-gnu`.

This branch removes these builds from the cross-compilation check's
build matrix. Tokio may still build successfully for MIPS targets, but
we can't easily guarantee support when the stable Rust release train may
or may not be able to build for MIPS targets.

[1]: https://github.com/tokio-rs/tokio/actions/runs/5970263562/job/16197657405?pr=5947#step:3:80
2023-08-25 14:40:32 -07:00
nicflower 59c9364689 io: pass through IO traits for StreamReader and SinkWriter (#5941) 2023-08-23 17:46:39 +00:00
Alice Ryhl 3b79be624d chore: prepare tokio-test v0.4.3 (#5943) 2023-08-23 12:38:11 +02:00
Jiahao XU 8955ed5f85 sync: add const fn OnceCell::from_value (#5903)
Signed-off-by: Jiahao XU <[email protected]>
2023-08-22 13:32:56 +02:00
Matilda Smeds bc26934e3b sync: add Semaphore example that limits open files (#5939) 2023-08-20 12:14:41 +02:00
Hamza Jadid 3d64a06600 docs: added trailing backticks (#5938) 2023-08-17 17:22:34 -05:00
Carl Lerche a7d52c2fed chore: prepare Tokio v1.32.0 release (#5937) 2023-08-16 14:11:30 -07:00
Marek Kuskowski f5f2b58b8d rt: improve docs for Builder::max_blocking_threads (#5793)
Closes #5777
2023-08-16 12:12:53 -07:00
mahkoh 718dcc8dff docs: BytesMut::with_capacity does not guarantee exact capacity (#5870) 2023-08-16 10:29:29 -07:00
Folkert de Vries 10e141d211 io: add Ready::ERROR and report error readiness (#5781)
Add `Ready::ERROR` enabling callers to specify interest in error readiness. Some platforms use error
readiness notifications to notify of other events. For example, Linux uses error to notify receipt of
messages on a UDP socket's error queue.

Using error readiness is platform specific.

Closes #5716
2023-08-16 10:28:19 -07:00
Carl Lerche 6e42c26c80 rt(alt): tweak some constants to improve scalability (#5935)
This patch aims to reduce the number of threads that get no-op wakeups.
2023-08-16 08:20:48 -07:00
Folkert de Vries 82bef00db4 io: minor tweaks to AsyncFd (#5932) 2023-08-14 19:13:14 +02:00
Alice Ryhl 40633fc678 readme: list previous LTS releases (#5931) 2023-08-14 19:10:24 +02:00
Aspen Smith 3dd5f7ae2e sync: move broadcast waiters into separate list before waking (#5925)
Within `notify_rx`, looping while re-locking and re-reading from
`Shared.tail` as long as there are still available wakers causes a
quadratic slowdown as receivers which are looping receiving from the
channel are added. Instead of continually re-reading from the original
list, this commit modifies `notify_rx` to move the waiters into a
separate list immediately similar to how `Notify::notify_waiters` works,
using a new `WaitersList` struct modified after NotifyWaitersList.

Fixes #5923
2023-08-14 19:09:56 +02:00
Félix Saparelli 2c92cad9db process: stabilize Command::raw_arg (#5930) 2023-08-13 10:03:11 +02:00
joe thomas 197757d440 streams: create StreamMock for testing Streams (#5915)
Introduce a new mock type to tests streams and eventually
sinks. Only includes next() and wait() for now. Fixes #4106
2023-08-12 19:18:23 +00:00
Carl Lerche 8b8005ebdd chore: prepare Tokio v1.31.0 release (#5928) 2023-08-12 09:37:49 -07:00
Carl Lerche 6cb106c353 rt: unstable EWMA poll time metric (#5927)
Because the runtime uses this value as a tuning heuristic, it can be
useful to get its value. This patch exposes the value as an unstable
metric.
2023-08-10 19:43:44 +00:00
Carl Lerche dd23f08c3a rt(alt): fix memory leak and increase max preemption when running Loom CI tests (#5911)
The memory leak was caused by a bug during shutdown where some state was leaked.
2023-08-10 09:18:10 -07:00
Jakub Kubík 5d29bdfb0c io: delegate WriteHalf::poll_write_vectored (#5914) 2023-08-10 10:11:00 +02:00
Brian Cardarella 4c220af777 chore: prepare Tokio v1.30.0 release (#5917) 2023-08-09 15:16:11 +00:00
Jiahao XU 0a631f88e8 process: add {ChildStd*}::into_owned_{fd, handle} (#5899) 2023-08-09 16:04:53 +02:00
Carl Lerche ee44dc98d8 ci: fix MIRI tests (#5919)
A change to parking lot or miri resulted in CI breaking.
2023-08-08 12:34:19 -07:00
Consoli 51cffbb74f time: mark Sleep as !Unpin in docs (#5916) 2023-08-06 13:34:08 +02:00
Carl Lerche 8832e936b1 rt(alt): fix a number of concurrency bugs (#5907)
Expands loom coverage and fixes a number of bugs.

Closes #5888
2023-08-04 11:59:38 -07:00
Victor Timofei dbda2045f1 time: implement extra reset variants for Interval (#5878) 2023-08-04 19:12:51 +02:00
Carl Lerche 7c54fdce3e rt: pop at least one task from inject queue (#5908)
When attempting to pull a batch of tasks from the injection queue,
ensure we set the cap to at least one.
2023-08-04 08:24:01 -07:00
wathen 38d1bcd9df sync: avoid false sharing in mpsc channel (#5829) 2023-08-03 15:54:19 +02:00
Alice Ryhl 52e6510215 runtime: fix flaky test wake_while_rt_is_dropping (#5905) 2023-08-02 22:30:34 +02:00
Taiki Endo e5e88551d2 Update CI config (#5893) 2023-08-02 01:04:11 +09:00
Jiahao XU efe3ab679a sync: make const_new methods always available (#5885)
Since MSRV is bumped to 1.63, `Mutex::new` is now usable in const context.

Also use `assert!` in const function to ensure correctness instead of
silently truncating the value and remove cfg `tokio_no_const_mutex_new`.

Signed-off-by: Jiahao XU <[email protected]>
2023-07-28 13:46:21 +02:00
Jiahao XU fb08591b43 tokio: removed unused tokio_* cfgs (#5890)
Signed-off-by: Jiahao XU <[email protected]>
2023-07-28 12:54:55 +02:00
kiron1 6aca07bee7 example: use copy_bidirectional in proxy.rs (#5856) 2023-07-28 12:06:44 +02:00
Jiahao XU 5128601898 ci: fix clippy warnings (#5891)
Signed-off-by: Jiahao XU <[email protected]>
2023-07-28 12:05:32 +02:00
Jiahao XU c445e467ce tokio: bump MSRV to 1.63 (#5887) 2023-07-27 10:57:19 +02:00
Carl Lerche a58beb3aca rt(alt): track which workers are idle. (#5886)
The scheduler uses this map to avoid trying to steal from idle workers.
2023-07-21 14:00:33 -07:00
Carl Lerche 4165601b1b rt: initial implementation of new threaded runtime (#5823)
This patch includes an initial implementation of a new multi-threaded
runtime. The new runtime aims to increase the scheduler throughput by
speeding up how it dispatches work to peer worker threads. This
implementation improves most benchmarks by about ~10% when the number of
threads is below 16. As threads increase, mutex contention deteriorates
performance.

Because the new scheduler is not yet ready to replace the old one, the
patch introduces it as an unstable runtime flavor with a warning that it
isn't production ready. Work to improve the scalability of the runtime
will most likely require more intrusive changes across Tokio, so I am
opting to merge with master to avoid larger conflicts.
2023-07-21 11:56:34 -07:00
Hayden Stainsby 63577cd8d3 rt: add runtime ID (#5864)
There are a number of cases in which being able to identify a runtime is
useful.

When instrumenting an application, this is particularly true. For
example, we would like to be able to add traces for runtimes so that
tasks can be differentiated (#5792). It would also allow a way to
differentiate runtimes which are have their tasks dumped.

Outside of instrumentation, it may be useful to check whether 2 runtime
handles are pointing to the same runtime.

This change adds an opaque `runtime::Id` struct which serves this
purpose, initially behind the `tokio_unstable` cfg flag. 

The inner value of the ID is taken from the `OwnedTasks` or
`LocalOwnedTasks` struct which every runtime and local set already
has. This will mean that any use of the ID will align with the task
dump feature.

The ID is added within the scope of working towards closing #5545.
2023-07-19 12:53:40 +02:00
Josh Guilfoyle 02544540f1 net: implement UCred for espidf (#5868) 2023-07-19 11:24:13 +02:00
Josh Guilfoyle d64c8e3ae0 poll: Do not clear readiness on short read/writes. (#5881)
The new mio_unsupported_force_poll_poll behaviour works the same as
Windows (using level-triggered APIs to mimic edge-triggered ones) and it
depends on intercepting an EAGAIN result to start polling the fd again.
2023-07-18 20:28:30 -05:00
Hayden StainsbyandAlice Ryhl f24b9824e6 rt: use optional non-zero value for task owner_id (#5876)
We switch to using a `NonZeroU64` for the `id` field for `OwnedTasks`
and `LocalOwnedTasks` lists. This allows the task header to contain an
`Option<NonZeroU64>` instead of a `u64` with a special meaning for 0.

The size in memory will be the same thanks to Rust's niche optimization,
but this solution is clearer in its intent.

Co-authored-by: Alice Ryhl <[email protected]>
2023-07-18 09:43:25 +02:00
Alice Ryhl 267a231581 runtime: use Arc::increment_strong_count instead of mem::forget (#5872) 2023-07-17 13:46:41 +02:00
Hyeonu Park 05feb2b0bb fs: add File::options() (#5869) 2023-07-16 19:09:22 +02:00
avdb 33d6d4f63c io: use vec in example for AsyncReadExt::read_exact (#5863) 2023-07-16 16:11:05 +02:00
Joris Kleiber 6166e9bcad process: fix raw_arg not showing up in docs (#5865) 2023-07-16 14:10:42 +02:00
João Marcos e52d56e807 sync: add broadcast::Sender::new (#5824) 2023-07-15 18:54:06 +02:00
dullbananas 304d140361 tokio: reduce LLVM code generation (#5859) 2023-07-15 10:44:23 +02:00
Alice Ryhl 91ad76c00c runtime: expand on sharing runtime docs (#5858) 2023-07-10 16:24:55 +02:00
Marek Kuskowski 74a5a458ea util: fix broken intra-doc link (#5849) 2023-07-06 10:43:35 +02:00
João Marcos 0d382faa4e sync: mention lagging in docs for broadcast::send (#5820) 2023-07-03 08:27:46 +00:00
wathen d8847cf891 sync: fix import style for std::error::Error (#5818) 2023-07-03 08:11:00 +00:00
pbrenna 918cf08a5f test: fetch actions from mock handle before write (#5814) 2023-07-03 08:03:33 +00:00
Jiahao XU fc69666f8a Speedup CI (#5691)
- Pass `--no-deps` to `cargo-clippy`
 - Use `dtolnay/rust-toolchain@stale` instead of
   `dtolnay/rust-toolchain@master`
 - Use dtolnay/rust-toolchain instead of `rustup` directly
 - Use `cargo-nextest` in job test to speedup testing
 - Use `cargo-nextest` in job test-unstable to speedup testing
 - Use `cargo-nextest` in job test-unstable-taskdump to speedup testing
 - Use `cargo-nextest` in job no-atomic-u64 to speedup testing
 - Use `cargo-nextest` in job check-unstable-mt-counters
 - Run `cargo check --benches` for `benches/` in job test
   Since the benchmark is not run
 - Run `cargo-check` instead of `cargo-build` in job test-parking_lot
   since no test is run
 - Run `cargo-check` instead of `cargo-build` in job no-atomic-u64
 - Run `Swatinem/rust-cache@v2` after `taiki-e/install-action@v2` to
   avoid caching pre-built artifacts downloaded by it.
 - Use `Swatinem/rust-cache@v2` in job no-atomic-u64
 - Add concurrenty group to cancel outdated CI
 - Use `taiki-e/setup-cross-toolchain-action@v1` in job cross-test

   instead of cross, so that we can use `cargo-nextest` to run tests in
   parallel.

   Also use `Swatinem/rust-cache@v2` to cache artifacts.
 - Use `Swatinem/rust-cache@v2` in job cross-check to speedup ci.
 - Fix job `cross-test`: Use `armv5te-unknown-linux-gnueabi` for no-atomic-u64

   testing instead of `arm-unknown-linux-gnueabihf`, which actually has
   atomic-u64
 - Rm use of `cross` in job `cross-check`

   Since it does not run any test, it does not need the `cross-rs`
   toolchain as tokio does not use any external C/C++ library that require
   `gcc`/`clang` to compile.
 - Add more recognizable name for steps in job cross-test
 - Split job `test` into `test-{tokio-full, workspace-all-features,
   integration-tests-per-feature}`
 - Split job `no-atomic-u64` into `no-atomic-u64-{test, check}`
 - Parallelize job `features` by using matrix
 - Split `cross-test` into `cross-test-{with, without}-parking_lot`
 - Speedup job `cross-test-*` and `no-atomic-u64-test` by running
   `cargo-test` with `-- --test-threads 1` since `qemu` userspace
   emulation has problems running binaries with many threads.
 - Speedup workflow `stress-test.yml` and job `valgrind` in workflow `ci.yml`
   by passing `--fair-sched=yes` to `valgrind`.
 - Speedup job `test-hyper`: Cache `./hyper/target`
   instead of caching `./target`, which is non-existent.
 - Set `RUST_TEST_THREADS=1` to make sure `libtest` only use one thread
   so that qemu will be happy with the tests.
   This is applied to `cross-test-with(out)-parking_lot, no-atomic-u64-test`.
 - Apply suggestions from code review

Signed-off-by: Jiahao XU <[email protected]>
2023-07-02 16:56:38 +09:00
Alice Ryhl bb4512eae0 ci: reenable semver check (#5845) 2023-07-01 16:54:38 +02:00
Carl Lerche 9dbf1879ee Merge branch 'tokio-1.29.x' into merge-1.29.x 2023-06-29 15:06:16 -07:00
Carl Lerche 1b1b9dc7e3 chore: prepare Tokio v1.29.1 release 2023-06-29 14:27:28 -07:00
Carl Lerche 012c848401 rt: fix nesting block_in_place with block_on (#5837)
This patch fixes a bug where nesting `block_in_place` with a `block_on`
between could lead to a panic. This happened because the nested
`block_in_place` would try to acquire a core on return when it should
not attempt to do so. The `block_on` between the two nested
`block_in_place` altered the thread-local state to lead to the incorrect
behavior.

The fix is for each call to `block_in_place` to track if it needs to try
to steal a core back.

Fixes #5239
2023-06-29 14:23:46 -07:00
Carl Lerche 6e990eb2c8 rt: fix nesting block_in_place with block_on (#5837)
This patch fixes a bug where nesting `block_in_place` with a `block_on`
between could lead to a panic. This happened because the nested
`block_in_place` would try to acquire a core on return when it should
not attempt to do so. The `block_on` between the two nested
`block_in_place` altered the thread-local state to lead to the incorrect
behavior.

The fix is for each call to `block_in_place` to track if it needs to try
to steal a core back.

Fixes #5239
2023-06-29 13:47:14 -07:00
Carl Lerche b573adc733 io: remove slab in favor of Arc and allocations (#5833)
This patch removes the custom slab in favor of regular allocations an
`Arc`. Originally, the slab was used to be able to pass indexes as
tokens to the I/O driver when registering I/O resources. However, this
has the downside of having a more expensive token lookup path. It also
pins a `ScheduledIo` to a specific I/O driver. Additionally, the slab is
approaching custom allocator territory.

We plan to explore migrating I/O resources between I/O drivers. As a
step towards that, we need to decouple `ScheduledIo` from the I/O
driver. To do this, the patch uses plain-old allocation to allocate the
`ScheduledIo` and we use the pointer as the token. To use the token, we
need to be very careful about releasing the `ScheduledIo`. We need to
make sure that the associated I/O handle is deregistered from the I/O
driver **and** there are no polls. The strategy in this PR is to let the
I/O driver do the final release between polls, but I expect this
strategy to evolve over time.
2023-06-29 13:46:45 -07:00
Carl Lerche 0c7d8d10fb ci: disable tuning tests for cross tests (#5836) 2023-06-29 10:23:13 -07:00
Carl Lerche ec1f52e1d3 io: fix safety of LinkedList drain_filter API (#5832)
The `drain_filter` method on the internal `LinkedList` type passes a
`&mut` reference to the node type. However, the `LinkedList` is intended
to be used with nodes that are shared in other ways. For example
`task::Header` is accessible concurrently from multiple threads.

Currently, the only usage of `drain_filter` is in a case where `&mut`
access is safe, so this change is to help prevent future bugs and
tighten up the safety of internal utilities.
2023-06-28 12:23:08 -07:00
Consoli 1bfe778acb sync: handle possibly dangling reference safely (#5812) 2023-06-28 09:10:18 +02:00
Carl Lerche ce23db6bc7 rt: reorganize I/O driver source (#5828)
Moves `Driver` into its own file and eliminates a bunch of code defined
in macros.
2023-06-27 16:24:36 -07:00
Carl Lerche 48c55768fd chore: prepare Tokio v1.29.0 release (#5826) 2023-06-27 13:37:10 -07:00
Diggory Blake 657fd883d2 task: add guarantee about when a spawned task may be polled (#5816) 2023-06-27 19:05:09 +02:00
Dustin J. Mitchell 6b076a2743 fs: wait for in-flight ops before cloning File (#5803)
If there is an ongoing operation on a file, wait for that to complete
before performing the clone in `File::try_clone`. This avoids a race
between the ongoing operation and any subsequent operations performed on
the clone.

Fixes: #5759
2023-06-27 10:19:03 +02:00
Dhruv Vats 910a1e2fcf io: fix futures_io::AsyncSeek implementaion for Compat (#5783) 2023-06-25 13:04:35 +02:00
icedrocket 6d25a00145 fs: update cfg attr in fs::read_dir (#5806) 2023-06-25 10:54:14 +02:00
wjjiang 78bf8a9e5e sync: replace Poll::Ready with Ready (#5815) 2023-06-25 10:40:49 +02:00
tim gretler b8af5aad16 task: add spawn_blocking methods to JoinMap (#5797) 2023-06-24 12:13:56 +02:00
Carl Lerche 2e62374e4a rt: pad the task struct to avoid false sharing (#5809)
This change pads the task struct to avoid false sharing. It is possible
for these structs to overlap cache lines without this alignment.
2023-06-21 09:39:14 -07:00
Jack Wrenn 56c4365584 tokio: improve taskdump documentation (#5805)
- Add example trace output.
- Add note on enabling unstable features.
- Add note on performance overhead.
2023-06-19 13:34:48 -04:00
Andrew Mackenzie fb0d305a7a ci: build tokio for redox-os (#5800) 2023-06-19 19:33:17 +02:00
盏一 848482d2bb rt(threaded): adjust transition_from_parked behavior after introducing disable_lifo_slot feature (#5753) 2023-06-14 12:42:31 +02:00
Andrew Mackenzie 00af6eff77 net: add support for Redox OS (#5790) 2023-06-13 12:42:40 +02:00
icedrocket b7290910f7 sync: fix typo in batch semaphore (#5789) 2023-06-12 15:43:12 +02:00
Taiki Endo af6c87a045 chore: upgrade remaining 2018 edition crates to 2021 edition (#5788) 2023-06-12 02:21:50 +09:00
Taiki Endo 6257712d68 ci: update cargo-check-external-types to 0.1.7 (#5786) 2023-06-11 19:02:12 +09:00
Taiki Endo c5d52c17ae chore: enable cargo v2 resolver to prevent dev-deps from enabling log feature of mio (#5787) 2023-06-11 17:34:22 +09:00
Erk 2a54ad01d0 time: do not overflow to signal value (#5710) 2023-06-10 14:24:19 +02:00
Jack Wrenn cb18b0a231 tokio: improve task dump documentation (#5778)
Adds depth to the taskdump example, and documentation to Handle::dump.
2023-06-10 13:30:08 +02:00
nvartolomei 7ccd3e0c6d task: add JoinSet::poll_join_next (#5721) 2023-06-10 13:19:07 +02:00
Bugen Zhao e63d0f10bf task: use pin-project for TaskLocalFuture (#5758)
Signed-off-by: Bugen Zhao <[email protected]>
2023-06-10 12:38:52 +02:00
Alice Ryhl a2941e48be ci: temporarily disable semver check (#5774) 2023-06-08 10:36:25 +02:00
Carl Lerche 1c8d22c18b rt: reduce code defined in macros (#5773)
Instead of defining code in macros, move code definition to sub modules
and use the cfg_macro to declare the module.
2023-06-07 08:48:27 -07:00
Carl Lerche cbb3c155dd rt: panic if EnterGuard dropped incorrect order (#5772)
Calling `Handle::enter()` returns a `EnterGuard` value, which resets the
thread-local context on drop. The drop implementation assumes that
guards from nested `enter()` calls are dropped in reverse order.
However, there is no static enforcement of this requirement.

This patch checks that the guards are dropped in reverse order and
panics otherwise. A future PR will deprecate `Handle::enter()` in favor
of a method that takes a closure, ensuring the guard is dropped
appropriately.
2023-06-07 08:47:58 -07:00
Jack Wrenn 038c4d9999 rt: implement task dumps for multi-thread runtime (#5717)
This patch implements task dumps on the multi-thread runtime. It
complements #5608, which implemented task dumps on the current-thread
runtime.
2023-06-06 13:55:37 -07:00
Folkert de Vries 7b24b22901 io: support PRIORITY epoll events (#5566)
Add support for epoll priority events. The commit adds `Interest::PRIORITY`, `ready`, and `ready_mut` functions to `AsyncFd`.

Closes #4885
2023-06-06 11:57:20 -07:00
Carl Lerche 779b9c19d5 ci: disable tuning test when runing ASAN (#5770)
The tuning test relies on a predictable execution environment. It
assumes that spawning a new task can complete reasonably fast. When
running tests with ASAN, the tuning test will spurriously fail. After
investigating, I believe this is due to running tests with ASAN enabled
and without `release` in a low resource environment (CI) results in an
execution environment that is too slow for the tuning test to succeed.
2023-06-06 09:35:19 -07:00
Carl Lerche 1204da7300 rt: split runtime::context into multiple files (#5768)
This PR restructures `runtime::context` into multiple files by component and feature flag. The goal is to reduce code defined in macros and make each context component more manageable.

There should be no behavior changes except tweaking how the RNG seed is set. Instead of putting it in `set_current`, we set it when entering the runtime. This aligns better with the feature's original intent, enabling users to make a runtime's RNG deterministic. The seed should not be changed by `Handle::enter()`, so there is no need to have the code in `context::set_current`.
2023-06-06 08:37:11 -07:00
Carl Lerche e75ca93d30 rt: EnterGuard should not be Send (#5766)
Removes `Send` from `EnterGuard` (returned by `Handle::enter()`. The
guard type changes a thread-local variable on drop. If the guard is
moved to a different thread, it would modify the wrong thread-local.

This is a **breaking change** but it fixes a bug and prevents incorrect
user behavior. If user code breaks because of this, it is because they
(most likely) have a bug in their code.
2023-06-05 14:09:43 -07:00
Alice Ryhl 15712018da rt: Scoped should not be Sync (#5765)
If the `Scoped` type is `Sync`, then you can call `set` from two threads in parallel. Since it accesses `inner` without synchronization, this is a data race.

This is a soundness issue for the `Scoped` type, but since this is an internal API and we don't use it incorrectly anywhere, no harm is done.
2023-06-05 09:36:48 -07:00
icedrocket 076d77c186 macros: fix diagnostics of last statement (#5762) 2023-06-04 15:10:59 +02:00
John-John Tedro e2853c1b49 rt: remove dead platform.rs file (#5761) 2023-06-04 13:20:38 +02:00
Carl Lerche 8f0103f6c5 rt: make CONTEXT const TLS (#5757)
This makes initializing `Context` const, which lets us use const
thread-locals. The next step will be to ensure `Context` does not have a
drop impl.
2023-06-03 14:16:45 -07:00
Carl Lerche fb4d43017d rt(threaded): move inject queue lock to worker (#5754)
This commit is a step towards the ongoing effort to unify the mutex in
the multi-threaded scheduler. The Inject queue is split into two
structs. `Shared` holds fields that are concurrently accessed, and
`Synced` holds fields that must be locked to access. The multi-threaded
scheduler is responsible for locking `Synced` and passing it in when
needed.

The commit also splits `inject` into multiple files to help reduce the
amount of code defined in macros.
2023-06-02 13:36:06 -07:00
Carl Lerche 1e14ef0093 ci: fix spurious CI failure (#5752)
PR #5720 introduced runtime self-tuning. It included a test that
attempts to verify self-tuning logic. The test is heavily reliant on
timing details. This patch attempts to make the test a bit more reliable
by not assuming tuning will converge within a set amount of time.
2023-06-01 17:15:48 -07:00
Carl Lerche a8b6353535 rt: move Inject to runtime::scheduler (#5748)
Previously, `Inject` was defined in `runtime::task`. This was because it
used some internal fns as part of the intrusive linked-list
implementation.

In the future, we want to remove the mutex from Inject and move it to
the scheduler proper (to reduce mutex ops). To set this up, this commit
moves `Inject` to `runtime::scheduler`. To make this work, we have to
`pub(crate)` `task::RawTask` and use that as the interface to access the
next / previous pointers.
2023-06-01 14:56:12 -07:00
Carl Lerche c748f4965e rt: move deferred task list to scheduler (#5741)
Previously, the deferred task list (list of tasks that yielded and are
waiting to be woken) was stored on the global runtime context. Because
the scheduler is responsible for waking these tasks, it took additional
TLS reads to perform the wake operation.

Instead, this commit moves the list of deferred tasks into the scheduler
context. This makes it easily accessible from the scheduler itself.
2023-06-01 11:36:28 -07:00
Carl Lerche a96dab1089 rt: start work to unify MT scheduler mutexes (#5747)
In order to reduce the number of mutex operations in the multi-threaded
scheduler hot path, we need to unify the various mutexes into a single
one. To start this work, this commit splits up `Idle` into `Idle` and
`Synced`. The `Synced` component is stored separately in the scheduler's
`Shared` structure.
2023-06-01 09:17:43 -07:00
Carl Lerche 79a7e78c0d rt(threaded): basic self-tuning of injection queue (#5720)
Each multi-threaded runtime worker prioritizes pulling tasks off of its
local queue. Every so often, it checks the injection (global) queue for
work submitted there. Previously, "every so often," was a constant
"number of tasks polled" value. Tokio sets a default of 61, but allows
users to configure this value.

If workers are under load with tasks that are slow to poll, the
injection queue can be starved. To prevent starvation in this case, this
commit implements some basic self-tuning. The multi-threaded scheduler
tracks the mean task poll time using an exponentially-weighted moving
average. It then uses this value to pick an interval at which to check
the injection queue.

This commit is a first pass at adding self-tuning to the scheduler.
There are other values in the scheduler that could benefit from
self-tuning (e.g. the maintenance interval). Additionally, the
current-thread scheduler could also benfit from self-tuning. However, we
have reached the point where we should start investigating ways to unify
logic in both schedulers. Adding self-tuning to the current-thread
scheduler will be punted until after this unification.
2023-06-01 08:13:24 -07:00
Chris Constantine 7c12e41d07 io: add AsyncRead/AsyncWrite passthrough for Inspect (#5739) 2023-06-01 15:35:03 +02:00
Alice Ryhl 7a99f87df2 taskdump: instrument the remaining leaf futures (#5708) 2023-05-31 18:27:40 +02:00
RaccoonSupremacy 0b2c9b8bab util: add reexport of bytes crate (#5725) 2023-05-28 19:12:40 +02:00
Carl Lerche 98c8c38e96 ci: speed up multi-threaded runtime loom tests. (#5723)
Increase max preemption back to 2 while running the tests in under 90 minutes.
2023-05-27 16:34:59 -07:00
Alice Ryhl 080d52902f Merge 'tokio-1.28.2' into 'master' (#5737) 2023-05-27 20:39:03 +02:00
Alice Ryhl e87ff8a83a chore: prepare Tokio v1.28.2 (#5736) 2023-05-27 20:38:02 +02:00
Alice Ryhl 1605279abf Merge 'tokio-1.25.1' into 'tokio-1.28.x' (#5735) 2023-05-27 20:36:34 +02:00
Alice Ryhl 25258d572a chore: prepare Tokio v1.25.1 (#5734) 2023-05-27 20:32:20 +02:00
Alice Ryhl 8ddb58bf6c Merge 'tokio-1.20.5' into 'tokio-1.25.x' (#5733) 2023-05-27 20:30:39 +02:00
Alice Ryhl 4b032a25a4 ci: use a fixed stable on 1.25.x (#5732)
This cherry-picks:
 * chore: remove ntapi dev-dependency
 * time: fix repeatedly_reset_entry_inserted_as_expired test
2023-05-27 17:20:38 +02:00
Alice Ryhl edd172cd32 chore: prepare Tokio v1.20.5 (#5731) 2023-05-27 15:41:02 +02:00
Alice Ryhl 9877fa2a97 Merge 'tokio-1.18.6' into 'tokio-1.20.x' (#5730) 2023-05-27 15:29:59 +02:00
Alice Ryhl 0f898a3148 chore: prepare Tokio v1.18.6 (#5729) 2023-05-27 15:26:45 +02:00
Alice Ryhl d6a9ef5333 tokio: disable default features for mio (#5728) 2023-05-27 15:18:15 +02:00
Alice Ryhl 2a180188c6 ci: fix CI for 1.18.x branch (#5728)
Some of these changes will be progressively reverted as we merge this
into newer branches.
2023-05-27 15:05:34 +02:00
Carl Lerche 9f9db7da63 rt: move scheduler ctxs to runtime::context (#5727)
This commit eliminates the current_thread::CURRENT and multi_thread::current
thread-local variables in favor of using `runtime::context`. This is another step
towards reducing the total number of thread-local variables used by Tokio.
2023-05-26 19:07:20 -07:00
Carl Lerche d274ef3748 rt: avoid cloning runtime::Handle in spawn (#5724)
This commit updates `tokio::spawn` to avoid having to clone
`runtime::Handle`.
2023-05-26 08:08:53 -07:00
Nano 5e6d4c7999 task: typo fix (#5726) 2023-05-26 15:43:04 +02:00
Carl Lerche 9eb3f5b556 rt(threaded): cap LIFO slot polls (#5712)
As an optimization to improve locality, the multi-threaded scheduler
maintains a single slot (LIFO slot). When a task is scheduled, it goes
into the LIFO slot. The scheduler will run tasks in the LIFO slot first
before checking the local queue.

Ping-ping style workloads where task A notifies task B, which
notifies task A again, can cause starvation as these two tasks 
repeatedly schedule the other in the LIFO slot. #5686, a first
attempt at solving this problem, consumes a unit of budget each time a
task is scheduled from the LIFO slot. However, at the time of this
commit, the scheduler allocates 128 units of budget for each chunk of
work. This is relatively high in situations where tasks do not perform many
async operations yet have meaningful poll times (even 5-10 microsecond
poll times can have an outsized impact on the scheduler).

In an ideal world, the scheduler would adapt to the workload it is
executing. However, as a stopgap, this commit limits the times
the LIFO slot is prioritized per scheduler tick.
2023-05-23 14:38:15 -07:00
Carl Lerche 3a94eb0893 rt: batch pop from injection queue when idle (#5705)
In the multi-threaded scheduler, when there are no tasks on the local
queue, a worker will attempt to pull tasks from the injection queue.
Previously, the worker would only attempt to poll one task from the
injection queue then continue trying to find work from other sources.
This can result in the injection queue backing up when there are many
tasks being scheduled from outside of the runtime.

This patch updates the worker to try to poll more than one task from the
injection queue when it has no more local work. Note that we also don't
want a single worker to poll **all** tasks on the injection queue as
that would result in work becoming unbalanced.
2023-05-23 08:16:41 -07:00
Carl Lerche 93bde0870f rt: use task::Inject with current_thread scheduler (#5702)
Previously, the current_thread scheduler used its own injection queue
instead of sharing the same one as the multi-threaded scheduler. This
patch updates the current_thread scheduler to use the same injection
queue as the multi-threaded one (`task::Inject`).

`task::Inject` includes an optimization where it does not need to
acquire the mutex if the queue is empty.
2023-05-21 00:08:00 +00:00
Carl Lerche ddd7250e62 ci: update nightly version (#5706) 2023-05-20 11:00:50 +02:00
Carl Lerche f64a1a3dbd chore: rm .cargo/config and include in .gitignore (#5707)
It was most likely included by accident.
2023-05-19 19:21:47 -07:00
Carl Lerche c88f9bc930 rt: small current_thread scheduler cleanup (#5701)
There should be no functional changes.
2023-05-19 08:15:31 -07:00
Joris Kleiber 29a6f468a6 process: add raw_arg method to Command (#5704) 2023-05-19 15:42:38 +02:00
Carl Lerche 8c076cb00d rt: add internal counters to threaded runtime. (#5700)
These counters are enabled using the `tokio_internal_mt_counters` and
are intended to help with debugging performance issues.

Whenever I work on the threaded runtime, I often find myself adding
these counters, then removing them before submitting a PR. I think
keeping them in will save time in the long run and shouldn't impact dev
much.
2023-05-18 20:28:47 +00:00
Carl Lerche c84d0a14b1 rt: instrument task poll times with a histogram (#5685)
Adds support for instrumenting the poll times of all spawned tasks. Data is tracked in a histogram. The user must specify the histogram scale and bucket ranges. Implementation-wise, the same strategy is used in the runtime where we are just using atomic counters. Because instrumenting each poll duration will result in frequent calls to `Instant::now()`, I think it should be an opt-in metric.
2023-05-15 15:20:41 -07:00
Alex Robinson a883fd4378 docs: link to latest version of tokio-util docs (#5694) 2023-05-15 21:07:08 +02:00
Carl Lerche 1014262d34 ci: skip miri tests when running loom (#5695)
Disables a recently added miri test when running loom tests.
2023-05-15 11:11:56 -07:00
Alice Ryhl f6313f4382 task: fix stacked borrows issue in JoinSet (#5693) 2023-05-15 17:55:52 +02:00
Alice Ryhl 70364b7079 runtime: fix possible starvation when using lifo slot (#5686) 2023-05-15 12:40:04 +00:00
Marek Kuskowski dd9471d13a sync: add broadcast::Receiver::blocking_recv (#5690) 2023-05-15 14:01:18 +02:00
Alice Ryhl 4e2ef63c4e ci: only check fuzz tests after basic tests (#5687) 2023-05-14 12:43:37 +02:00
Hootan Shadmehr dec390df1e ci: check that tokio-stream/fuzz compiles (#5682) 2023-05-10 20:17:50 +02:00
Alice Ryhl 89b73f39bf Merge 'tokio-1.28.x' into 'master' (#5680) 2023-05-10 10:46:39 +02:00
Alice Ryhl a26fc9c9f9 chore: prepare Tokio v1.28.1 (#5679) 2023-05-10 10:44:04 +02:00
Hootan Shadmehr 7fe88ce4ad fuzz: remove unused code from fuzz_steam_map.rs (#5675) 2023-05-10 10:04:36 +02:00
Dirkjan Ochtman f2d033e454 build: fix warnings in AS_FD_PROBE (#5677) 2023-05-09 21:57:35 +02:00
Daniel Bloom c999699f5e sync: remove 'static bound from PollSender (#5665) 2023-05-09 16:01:57 +00:00
Alice Ryhl 7430865d65 taskdump: instrument JoinHandle and tokio::fs (#5676) 2023-05-09 10:14:59 +00:00
Vidhan Bhatt 56239a9035 macros: fix typo in doc comment (#5671) 2023-05-08 17:50:49 +02:00
Matilda Smeds 1b4106a1ce net: add nodelay methods on TcpSocket (#5672) 2023-05-06 14:35:20 +02:00
Hootan Shadmehr 3abe877bf7 ci: check that tokio/fuzz compiles (#5670) 2023-05-03 22:00:06 +02:00
Gil Shoshan 61b68a8abc sync: implement more traits for channel errors (#5666) 2023-05-03 09:59:07 +02:00
icedrocket 52bc6b6f2d fs: reduce blocking ops in fs::read_dir (#5653) 2023-04-28 11:38:38 +02:00
Jack Wrenn f478ff4a24 tokio: add CountedLinkedList::for_each (#5660) 2023-04-27 22:24:44 +02:00
Jack Wrenn 660eac71f0 taskdump: implement task dumps for current-thread runtime (#5608)
Task dumps are snapshots of runtime state. Taskdumps are collected by
instrumenting Tokio's leaves to conditionally collect backtraces, which
are then coalesced per-task into execution tree traces.

This initial implementation only supports collecting taskdumps from
within the context of a current-thread runtime, and only `yield_now()`
is instrumented.
2023-04-27 12:59:20 +02:00
Matilda Smeds 1d785fd66f metrics: add metric for number of tasks (#5628) 2023-04-27 12:58:31 +02:00
Alice Ryhl 6a8f6f5a90 net: add uds doc alias for unix sockets (#5659) 2023-04-26 12:12:53 +02:00
Alice Ryhl 398dfda56d chore: prepare tokio-stream v0.1.14 (#5658) 2023-04-26 10:47:43 +02:00
Alice Ryhl 9bdc475539 stream: fix minimum Tokio dependency (#5657) 2023-04-26 10:13:35 +02:00
Alice Ryhl b5a5ddb4cf chore: prepare tokio-stream v0.1.13 (#5652) 2023-04-25 20:21:32 +02:00
Alice Ryhl 74c6e6c683 chore: prepare tokio-util v0.7.8 (#5651) 2023-04-25 20:21:20 +02:00
Alice Ryhl f21d596099 chore: prepare Tokio v1.28.0 (#5650) 2023-04-25 20:21:00 +02:00
Alice Ryhl 66c62a4b74 chore: prepare tokio-macros v2.1.0 (#5649) 2023-04-25 17:07:08 +02:00
Predrag Gruevski a86c052218 ci: use cargo-semver-checks GitHub Action (#5648) 2023-04-25 00:17:17 +02:00
Debadree Chatterjee c1778eda38 sync: add watch::Receiver::wait_for (#5611) 2023-04-24 15:48:07 +02:00
Fergus Mitchell 11b8807544 sync: improve Debug impl for RwLock (#5647) 2023-04-24 15:39:42 +02:00
jrray e789b61424 stream: add StreamExt::timeout_repeating (#5577) 2023-04-24 09:27:24 +02:00
Burkhard Mittelbach 2cd4f4ab46 stream: add "full" feature flag (#5639) 2023-04-23 13:59:49 +02:00
Taiki Endo b6bbe5f487 Revert "macros: hide internal constant in select! macro (#5617)" (#5637)
This reverts commit cf9a03c107.
2023-04-23 20:38:50 +09:00
Denis Kayshev 57ba4a4b10 sync: fix typo in Semaphore::MAX_PERMITS (#5645) 2023-04-22 18:52:07 +02:00
Alice Ryhl 5e6c6bdafd chore: fix compiler output changes in rustc 1.69 (#5643) 2023-04-21 07:10:55 +00:00
Alice Ryhl 77e3911806 chore: remove ntapi dev-dependency (#5642) 2023-04-21 08:38:46 +02:00
Alice Ryhl b9868b23aa rt: fix spurious yield_defers_until_park test (#5634) 2023-04-20 18:00:33 +02:00
Adam Chalmers 623483c81f docs: fix typo in #[tokio::test] docs (#5636)
Both current- and multi-thread runtime claimed to be the default, but only current thread actually is.
2023-04-20 14:12:43 +00:00
Sigurd 5cef6eba7b sync: improve CancellationToken doc on child tokens (#5632) 2023-04-19 17:42:32 +02:00
Tymoteusz Wiśniewski db543639e1 sync: reduce contention in Notify (#5503) 2023-04-19 13:07:10 +02:00
isabelleatkins 9f9e596eec time: fix panic in DelayQueue (#5630) 2023-04-17 20:51:42 +00:00
Jake Goulding f6cb6e084b task: add JoinSet::spawn_blocking (#5612) 2023-04-17 08:20:27 +00:00
Timmy Xiao 7aea597a8f io: make read_to_end not grow unnecessarily (#5610) 2023-04-16 20:23:53 +02:00
Aviram Hassan 9507f8b374 stream: add StreamNotifyClose (#4851) 2023-04-16 15:04:00 +00:00
Folkert de Vries 6037faeede io: add AsyncFd::async_io (#5542) 2023-04-16 16:27:58 +02:00
Tymoteusz Wiśniewski 8497f379b5 sync: avoid deadlocks in broadcast with custom wakers (#5578) 2023-04-16 16:22:38 +02:00
Hayden Stainsby 1b22cbfd33 readme: update clippy version in contrib guide (#5623)
In CI, we are using a newer version of Clippy than what is stated in the
contributions guide.

Additionally, it is no longer necessary to use Clippy from the MSRV. As
of Clippy 1.64, the `rust-version` field in Cargo.toml is respected.

The text and the command have been updated to reflect the current state
of CI and best practices.
2023-04-16 15:46:01 +02:00
Jens Reidel effead29d1 time: add DelayQueue::peek (#5569)
Signed-off-by: Jens Reidel <[email protected]>
2023-04-16 09:43:03 +02:00
Alexander van Ratingen 3b16564ce0 sync: add OwnedSemaphorePermit::semaphore (#5618) 2023-04-15 20:55:39 +02:00
John-John Tedro fc1e03f91b macros: make entrypoints more efficient (#5621) 2023-04-15 20:55:00 +02:00
John-John Tedro ea5d448ee8 ci: gate costly checks behind basic ones (#5622) 2023-04-15 20:16:42 +02:00
Iron(III) Oxide abc93f615e fuzz: fix fuzz warnings (#5614) 2023-04-13 16:07:12 +02:00
mTsBucy1 cf9a03c107 macros: hide internal constant in select! macro (#5617) 2023-04-12 21:32:38 +09:00
Alice Ryhl 3b45e8614d stream: update StreamMap fuzz test (#5600)
This fuzz test touches some quadratic time code paths. This changes the
test to limit the test size so that the test doesn't time out.
2023-04-11 10:40:10 +02:00
Alice Ryhl b02c550c52 ci: fix FreeBSD ci (#5613) 2023-04-10 15:26:59 +02:00
Oddbjørn Grødem 03912b9cf7 sync: add same_channel to broadcast channel (#5607) 2023-04-07 14:18:47 +02:00
Daniel Sedlak d4afbad6e5 tokio: fix typos (#5604) 2023-04-06 14:29:12 +02:00
Daniel Netzer b1ca0d8b12 ci: fix typo in ci.yml (#5599) 2023-04-05 09:35:47 +00:00
Iron(III) Oxide 16cdb109f4 io: impl BufMut for ReadBuf (#5590) 2023-04-04 22:00:41 +02:00
Taiki Endo 88445e762c deps: update windows-sys to 0.48 (#5591) 2023-04-02 09:31:27 +02:00
teor 3c403d6ee8 coop: fix typo in poll_proceed() doc comment (#5589) 2023-03-30 13:25:05 +02:00
Dmitry Rodionov d63d659078 sync: fix typo in tokio::sync::watch::Sender docs (#5587) 2023-03-29 18:05:24 +00:00
Flavio Bizzarri b31f1a4662 net: add recv_buf for UdpSocket and UnixDatagram (#5583) 2023-03-28 18:17:22 +00:00
Qiu Chaofan 663e56e983 net: support AIX get_peer_cred (#5065) 2023-03-28 11:25:12 +02:00
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
Alice Ryhl e629ad7c9a chore: prepare tokio-util v0.7.7 (#5451) 2023-02-12 12:42:38 +01:00
Alice Ryhl 36fdccc3bc util: Revert "remove Encoder bound on FramedParts constructor" (#5450)
This reverts commit ae69d11d1f.
2023-02-12 11:55:41 +01:00
Alice Ryhl 01bb1ecf4d chore: prepare tokio-util v0.7.6 (#5447) 2023-02-10 10:48:36 +01:00
Alice Ryhl 36d2233579 chore: fix dependency on Tokio (#5445) 2023-02-10 10:13:37 +01:00
Alice Ryhl 74fb9e387a chore: prepare tokio-util v0.7.5 (#5442) 2023-02-09 17:35:53 +01:00
Alice Ryhl 8b44077ebc sync: make CancellationToken UnwindSafe (#5438) 2023-02-09 13:07:52 +01:00
Caio d6dbefcdc0 sync: mark lock guards with #[clippy::has_significant_drop] (#5422) 2023-02-09 11:20:09 +01:00
Conrad Ludgate d96bbf0465 time: don't store deadline twice in sleep entries (#5410) 2023-02-09 11:19:02 +01:00
Taiki Endo 09b2653e71 chore: update windows-sys to 0.45 (#5386) 2023-02-09 11:16:12 +01:00
Valentin 061325ba7e task: clarify what happens to spawned work during runtime shutdown (#5394) 2023-02-09 11:14:01 +01:00
Nathaniel Brough d7d5d05333 tests: port proptest fuzz harnesses to use cargo-fuzz (#5392)
This change ports fuzz tests from the black-box fuzzing framework,
proptest-rs over to use the grey-box fuzzing framework cargo-fuzz.

Refs: #5391
2023-02-09 11:08:50 +01:00
Finomnis 1dcfe1cc9b ci: add semver checking to CI (#5437) 2023-02-08 16:45:02 +01:00
Dmitry Ivanov 5653b4583c io: remove erroneous wake call in SinkWriter (#5436) 2023-02-07 18:32:27 +00:00
Carl Lerche abf5d28f2c rt: remove Arc from Clock (#5434)
This patch removes `Arc` from Tokio's internal clock source. Instead of
cloning `Clock` when needed, a reference is passed into functions that
need to get the current instant.
2023-02-07 09:45:14 -08:00
Taiki Endo a7945b469d ci: update Cirrus CI config (#5428)
* Use image_family for FreeBSD image in Cirrus CI
* Do not trigger Cirrus CI on branches other than master and tokio-.*
2023-02-07 03:58:19 +09:00
mTsBucy1 80ec80165b task: clarify process::Command docs (#5406) (#5413) 2023-01-30 16:47:04 +01:00
Steven Fackler 88b1eb54fb chore: prepare Tokio v1.25.0 release (#5408) 2023-01-29 22:44:31 +01:00
Jonathan Schwender 1f50c57185 metrics: fix steal_count docs, add steal_operations (#5330) 2023-01-27 20:44:47 +01:00
Flavio Moreira a18b3645f3 chore: update year in LICENSE files (#5402) 2023-01-27 16:05:48 +01:00
jake fe2dcb9453 io: increase MAX_BUF from 16384 to 2MiB (#5397) 2023-01-27 13:50:55 +01:00
Chris Wailes c90757f07a tests: condition unwinding tests on cfg(panic = "unwind") (#5384) 2023-01-21 11:12:24 +01:00
Taiki Endo f3f8e4f17f chore: update nix to 0.26 (#5385) 2023-01-21 12:17:04 +09:00
Carl Lerche 42bec96189 Merge branch 'tokio-1.24.x' into master 2023-01-17 12:59:09 -08:00
Carl Lerche 4f6a95badc chore: prepare Tokio v1.24.2 release 2023-01-17 12:26:13 -08:00
Carl Lerche 3d33610ed2 Merge branch 'tokio-1.20.x' into tokio-1.24.x 2023-01-17 12:25:05 -08:00
Carl Lerche 38a9c6c1a5 Merge branch 'tokio-1.20.x' into master 2023-01-17 11:24:22 -08:00
Carl Lerche f3ce29a003 chore: prepare Tokio v1.20.4 release 2023-01-17 11:09:42 -08:00
Carl Lerche 0d8fe5fe75 Merge branch 'tokio-1.18.x' into tokio-1.20.x 2023-01-17 11:08:06 -08:00
Taiki Endo 171ce0ff8d chore: prepare Tokio v1.18.5 release 2023-01-17 23:00:38 +09:00
Taiki Endo d6ea7a742b Add T: Unpin bound to ReadHalf::unsplit 2023-01-17 21:53:54 +09:00
Alice Ryhl 06f1a601bb task: clarify doc about tasks starting immediately (#5364) 2023-01-14 11:11:45 -08:00
Taiki Endo 40782efb76 tokio: fix remaining issues about atomic_u64_static_once_cell.rs (#5374)
Fixes #5373
Closes #5358 

- Add check for no_atomic_u64 & no_const_mutex_new (condition to atomic_u64_static_once_cell.rs is compiled)
- Allow unused_imports in TARGET_ATOMIC_U64_PROBE. I also tested other *_PROBE and found no other errors triggered by -D warning.
- Fix cfg of util::once_cell module
2023-01-14 11:11:05 -08:00
Steven Fackler c390a62387 Add broadcast::Sender::len (#5343)
* Add broadcast::Sender::len

* Add a randomized test for broadcast::Sender::len

* fix wasm build

* less silly cfg

* review feedback

* grammar?
2023-01-12 13:53:31 -05:00
Tymoteusz Wiśniewski f9dbfa8251 net: improve from_std docs regarding non-blocking IO (#5332) 2023-01-11 12:56:27 +00:00
Alice Ryhl 31c7e82919 chore: prepare Tokio v1.24.1 (#5357) 2023-01-06 10:48:42 +00:00
dtolnay 8d8db27442 tokio: add load and compare_exchange_weak to loom StaticAtomicU64 (#5356) 2023-01-06 15:19:31 +09:00
Carl Lerche dfe252d1fa chore: prepare Tokio v1.24.0 release (#5353) 2023-01-05 11:20:35 -08:00
Paul Loyd 21b233fa9c test: bump version of async-stream (#5347) 2023-01-05 11:02:07 +01:00
Carl Lerche 72993044e6 Merge branch 'tokio-1.23.x' into master 2023-01-04 11:36:07 -08:00
Carl Lerche 1a997ffbd6 chore: prepare Tokio v1.23.1 release 2023-01-04 10:32:38 -08:00
Carl Lerche a8fe333cc4 Merge branch 'tokio-1.20.x' into tokio-1.23.x 2023-01-03 15:06:02 -08:00
Carl Lerche ba81945ffc chore: prepare Tokio 1.20.3 release 2023-01-03 13:28:44 -08:00
Carl Lerche 763bdc967e ci: run WASI tasks using latest Rust
This should let CI to pass.
2023-01-03 13:28:43 -08:00
Carl Lerche 9f98535877 Merge remote-tracking branch 'origin/tokio-1.18.x' into fix-named-pipes-1.20 2023-01-03 13:12:20 -08:00
Carl Lerche 9241c3eddf chore: prepare Tokio v1.18.4 release 2023-01-03 13:06:27 -08:00
Carl Lerche 699573d550 net: fix named pipes server configuration builder
The `pipe_mode` function would erase any previously set configuration
option that is specified using the pipe_mode fit field. This patch fixes
the builder to maintain the bit field when changing the pipe mode.
2023-01-03 13:06:27 -08:00
Carl Lerche c6552c5680 rt: use internal ThreadId implementation (#5329)
The version provided by `std` has limitations, including no way to try
to get a thread ID without panicking.
2022-12-30 15:17:35 -08:00
Carl Lerche 048049f888 rt: move task::Id into its own file (#5327)
This is a minor internal cleanup.
2022-12-30 10:49:45 -08:00
Taiki Endo 98d484e29c ci: update cargo-check-external-types to 0.1.6 (#5325) 2022-12-30 19:03:44 +09:00
Taiki Endo ef0224246b tests: fix SB violation in LeakedBuffers (#5322) 2022-12-29 11:46:08 +09:00
icedrocket 4a4f80ca70 fs: use chunks in fs::read_dir (#5309) 2022-12-28 12:06:46 +01:00
Hyeonu Park 9af2f5ee59 io: optimize shutdown check on I/O operations (#5300)
The global flag remains and is used to prevent duplicated shutdowns and
new io operations after shutdown.

On shutdown, the driver flips the shutdown flag of every pending io
operations and wake them to fail with a shutdown error.

Fixes: #5227
2022-12-27 10:40:41 -08:00
Taiki Endo b75dba6904 ci: update Swatinem/rust-cache action to v2 (#5320) 2022-12-28 02:20:12 +09:00
Lencerf 353e5cabb8 process: fix typo in process::imp::Pipe comment (#5314)
Signed-off-by: Changyuan Lyu <[email protected]>
2022-12-27 15:50:12 +00:00
Alice Ryhl 8d58dc85b5 sync: document that there is no spsc and spmc channel (#5306) 2022-12-27 16:24:50 +01:00
Taiki Endo 519afd4458 ci: remove uses of unmaintained actions-rs actions (#5316)
- Use dtolnay/rust-toolchain instead of actions-rs/toolchain
- Use cargo/cross directly instead of actions-rs/cargo
- Use rustsec/audit-check instead of actions-rs/audit-check
2022-12-28 00:06:56 +09:00
Pure White 682e93df93 rt: read environment variable for worker thread count (#4250) 2022-12-21 15:43:35 +01:00
Alice Ryhl b9ae7e6659 signal: remove redundant Pin around globals (#5303) 2022-12-18 09:42:59 +01:00
Abutalib Aghayev d9e0f66113 task: rename State::has_join_waker to State::is_join_waker_set (#5248) 2022-12-17 22:34:12 +01:00
Alice Ryhl 6b3727d580 metrics: make num_idle_blocking_threads test less flaky (#5302) 2022-12-17 19:06:30 +01:00
Jason Orendorff e14ca72e68 test-util: don't auto-advance time when a spawn_blocking task is running (#5115) 2022-12-17 13:02:18 +01:00
John Nunley 42db755ac1 tokio: improve detection of whether a target supports AtomicU64 (#5284) 2022-12-16 19:53:35 +01:00
John Nunley 81b50e946f sync: decrease stack usage in mpsc channel (#5294) 2022-12-15 22:48:00 +01:00
Carl Lerche 39766220f4 rt: implement task::Id using StaticAtomicU64 (#5282)
This patch simplifies the implementation of `task::Id` by moving
conditional compilation into the `AtomicU64` definition. To handle
platforms that do not include `const fn Mutex::new()`, `StaticAtomicU64`
is defined as always having a `const fn new()`. `StaticAtomicU64` is
implemented with `OnceCell` when needed.
2022-12-10 13:49:16 -08:00
Matt Fellenz ae69d11d1f util: remove Encoder bound on FramedParts constructor (#5280) 2022-12-09 11:12:42 +00:00
Carl Lerche c693ccd210 ci: test no const mutex new (#5257)
This adds CI coverage for a couple of code paths that are not currently
hit in CI:

* no `const fn Mutex::new`
* no `AtomicU64`

This is done by adding some new CFG flags used only for tests in order
to force those code paths.
2022-12-09 02:13:22 +09:00
Divy Srivastava 36039d0bb9 rt: allow configuring I/O events capacity (#5186)
Adds a method `Builder::max_io_events_per_tick()` to the runtime builder. This can be used to configure the capacity of events that may be processed per OS poll.
2022-12-07 12:15:03 -06:00
Carl Lerche 22cff80048 chore: update CI's clippy version to 1.65 (#5276) 2022-12-06 19:56:13 -08:00
Alan Somers 07da5e73ee ci: update CI environment to FreeBSD 12.4 (#5272)
12.3 will soon be EoL
2022-12-06 10:03:51 +01:00
Alan Somers c4ed16d1b4 ci: future-proof for FreeBSD 12 (#5260)
Raise the mio-aio dev dependency, which transitively brings in Nix, to
ensure that the tests will continue to compile if libc switches from a
FreeBSD 11 ABI to a FreeBSD 12 one.
2022-12-06 10:03:09 +01:00
Carl Lerche 3ce5a2681c chore: prepare Tokio v1.23 release (#5270)
### Fixed
 - net: fix Windows named pipe connect ([#5208])
 - io: support vectored writes for `ChildStdin` ([#5216])
 - io: fix `async fn ready()` false positive for OS-specific events ([#5231])

 ### Changed
 - runtime: `yield_now` defers task until after driver poll ([#5223])
 - runtime: reduce amount of codegen needed per spawned task ([#5213])
 - windows: replace `winapi` dependency with `windows-sys` ([#5204])

 [#5208]: https://github.com/tokio-rs/tokio/pull/5208
 [#5216]: https://github.com/tokio-rs/tokio/pull/5216
 [#5213]: https://github.com/tokio-rs/tokio/pull/5213
 [#5204]: https://github.com/tokio-rs/tokio/pull/5204
 [#5223]: https://github.com/tokio-rs/tokio/pull/5223
 [#5231]: https://github.com/tokio-rs/tokio/pull/5231
2022-12-05 15:22:43 -08:00
Tymoteusz Wiśniewski 644cb8207d rt: fix *_closed false positives (#5231)
Readiness futures inconsistently return the current readiness of an I/O resource if it is immediately available, or all readiness relevant for the given `Interest`, if a future needs to wait. In particular, it always returns `read_closed` for `Interest::READABLE` and `write_closed` for `Interest::WRITABLE`, which often is not true. Tokio should not tolerate false positives for `*_closed` events because they are considered final states and are not cleared internally.

In the case of an `io_resource.ready(Interest::READABLE | Interest::WRITABLE)` call, this behavior may also lead to false positives of other events.

## Solution

Follow the same strategy as `poll_ready` and return the current resource's readiness.

Closes: #5098
2022-12-05 14:42:49 -08:00
Jiahao XU a1316cd792 io: impl std::io::BufRead on SyncIoBridge<T> (#5265)
Signed-off-by: Jiahao XU <[email protected]>
2022-12-05 10:11:44 +01:00
sharnoff 86ffabe2af docs: add note about current-thread + Handle::block_on (#5264)
There's already an existing warning about this combo in the
documentation for `Handle::block_on`. This commit adds a summarized
version in `Runtime::handle`.
2022-12-05 00:14:05 -06:00
Vitalii Kryvenko 00bf5ee8a8 sync: improve watch docs (#5261) 2022-12-04 00:06:12 +00:00
Tilman 87510100ce Fix typo (#5255) 2022-12-03 10:49:55 +00:00
Carl Lerche 2be71ad746 chore: move conditional AtomicU64 impl to new file (#5256)
Keeping the implementation out of a macro lets rustfmt apply to it.
2022-12-02 11:47:11 -08:00
Carl Lerche d1b789f33a rt: fix new yield_now behavior with block_in_place (#5251)
PR #5223 changed the behavior of `yield_now()` to store yielded tasks
and notify them *after* polling the resource drivers. This PR fixes a
couple of bugs with this new behavior when combined with
`block_in_place()`.

First, we need to avoid freeing the deferred task queue when exiting a
runtime if it is *not* the root runtime. Because `block_in_place()`
allows a user to start a new runtime from within an existing task, this
check is necessary.

Second, when a worker core is stolen from a thread during a
`block_in_place()` call, we need to ensure that deferred tasks are
notified anyway.
2022-12-01 17:23:33 -08:00
Carl Lerche 22862739dd rt: yield_now defers task until after driver poll (#5223)
Previously, calling `task::yield_now().await` would yield the current
task to the scheduler, but the scheduler would poll it again before
polling the resource drivers. This behavior can result in starving the
resource drivers.

This patch creates a queue tracking yielded tasks. The scheduler
notifies those tasks **after** polling the resource drivers.

Refs: #5209
2022-11-30 14:21:08 -08:00
514 changed files with 32795 additions and 8034 deletions
-2
View File
@@ -1,2 +0,0 @@
# [build]
# rustflags = ["--cfg", "tokio_unstable"]
+7 -8
View File
@@ -1,8 +1,10 @@
only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'master' || $CIRRUS_BRANCH =~ 'tokio-.*')
auto_cancellation: $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH !=~ 'tokio-.*'
freebsd_instance:
image: freebsd-12-3-release-amd64
image_family: freebsd-13-1
env:
RUST_STABLE: stable
RUST_NIGHTLY: nightly-2022-10-25
RUST_NIGHTLY: nightly-2023-10-21
RUSTFLAGS: -D warnings
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
@@ -11,9 +13,8 @@ env:
# the system's binaries, so the environment shouldn't matter.
task:
name: FreeBSD 64-bit
auto_cancellation: $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH !=~ 'tokio-.*'
setup_script:
- pkg install -y bash curl
- pkg install -y bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_STABLE
- . $HOME/.cargo/env
@@ -26,12 +27,11 @@ task:
task:
name: FreeBSD docs
auto_cancellation: $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH !=~ 'tokio-.*'
env:
RUSTFLAGS: --cfg docsrs --cfg tokio_unstable
RUSTDOCFLAGS: --cfg docsrs --cfg tokio_unstable -Dwarnings
setup_script:
- pkg install -y bash curl
- pkg install -y bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_NIGHTLY
- . $HOME/.cargo/env
@@ -44,9 +44,8 @@ task:
task:
name: FreeBSD 32-bit
auto_cancellation: $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH !=~ 'tokio-.*'
setup_script:
- pkg install -y bash curl
- pkg install -y bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_STABLE
- . $HOME/.cargo/env
+1 -1
View File
@@ -1 +1 @@
msrv = "1.49"
msrv = "1.56"
+25 -5
View File
@@ -1,8 +1,28 @@
R-loom:
R-loom-sync:
- tokio/src/sync/*
- tokio/src/sync/**/*
- tokio-util/src/sync/*
- tokio-util/src/sync/**/*
- tokio/src/runtime/*
- tokio/src/runtime/**/*
R-loom-time-driver:
- tokio/src/runtime/time/*
- tokio/src/runtime/time/**/*
R-loom-current-thread:
- tokio/src/runtime/scheduler/*
- tokio/src/runtime/scheduler/current_thread/*
- tokio/src/runtime/task/*
- tokio/src/runtime/task/**
R-loom-multi-thread:
- tokio/src/runtime/scheduler/*
- tokio/src/runtime/scheduler/multi_thread/*
- tokio/src/runtime/scheduler/multi_thread/**
- tokio/src/runtime/task/*
- tokio/src/runtime/task/**
R-loom-multi-thread-alt:
- tokio/src/runtime/scheduler/*
- tokio/src/runtime/scheduler/multi_thread_alt/*
- tokio/src/runtime/scheduler/multi_thread_alt/**
- tokio/src/runtime/task/*
- tokio/src/runtime/task/**
+4 -3
View File
@@ -15,15 +15,16 @@ permissions:
jobs:
security-audit:
permissions:
checks: write # for actions-rs/audit-check to create check
checks: write # for rustsec/audit-check to create check
contents: read # for actions/checkout to fetch code
issues: write # for actions-rs/audit-check to create issues
issues: write # for rustsec/audit-check to create issues
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'ci skip')"
steps:
- uses: actions/checkout@v3
- name: Audit Check
uses: actions-rs/audit-check@v1
# https://github.com/rustsec/audit-check/issues/2
uses: rustsec/audit-check@master
with:
token: ${{ secrets.GITHUB_TOKEN }}
+536 -181
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -4,6 +4,10 @@ on:
# See .github/labeler.yml file
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
permissions:
contents: read
+93 -19
View File
@@ -7,8 +7,14 @@ on:
name: Loom
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
env:
RUSTFLAGS: -Dwarnings
RUSTFLAGS: -Dwarnings --cfg loom --cfg tokio_unstable -C debug_assertions
LOOM_MAX_PREEMPTIONS: 2
LOOM_MAX_BRANCHES: 10000
RUST_BACKTRACE: 1
# Change to specific Rust release to pin
rust_stable: stable
@@ -17,32 +23,100 @@ permissions:
contents: read
jobs:
loom:
name: loom
loom-sync:
name: loom tokio::sync
# base_ref is null when it's not a pull request
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom') || (github.base_ref == null))
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-sync') || (github.base_ref == null))
runs-on: ubuntu-latest
strategy:
matrix:
scope:
- --skip loom_pool
- loom_pool::group_a
- loom_pool::group_b
- loom_pool::group_c
- loom_pool::group_d
- time::driver
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
override: true
- uses: Swatinem/rust-cache@v1
- uses: Swatinem/rust-cache@v2
- name: run tests
run: cargo test --lib --release --features full -- --nocapture sync::tests
working-directory: tokio
loom-time-driver:
name: loom time driver
# base_ref is null when it's not a pull request
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-time-driver') || (github.base_ref == null))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
- name: run tests
run: cargo test --lib --release --features full -- --nocapture runtime::time::tests
working-directory: tokio
loom-current-thread:
name: loom current-thread scheduler
# base_ref is null when it's not a pull request
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-current-thread') || (github.base_ref == null))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
- name: run tests
run: cargo test --lib --release --features full -- --nocapture loom_current_thread
working-directory: tokio
loom-multi-thread:
name: loom multi-thread scheduler
# base_ref is null when it's not a pull request
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-multi-thread') || (github.base_ref == null))
runs-on: ubuntu-latest
strategy:
matrix:
include:
- scope: loom_multi_thread::group_a
- scope: loom_multi_thread::group_b
- scope: loom_multi_thread::group_c
- scope: loom_multi_thread::group_d
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
- name: loom ${{ matrix.scope }}
run: cargo test --lib --release --features full -- --nocapture $SCOPE
run: cargo test --lib --release --features full -- $SCOPE
working-directory: tokio
env:
SCOPE: ${{ matrix.scope }}
loom-multi-thread-alt:
name: loom ALT multi-thread scheduler
# base_ref is null when it's not a pull request
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-multi-thread-alt') || (github.base_ref == null))
runs-on: ubuntu-latest
strategy:
matrix:
include:
- scope: loom_multi_thread_alt::group_a
- scope: loom_multi_thread_alt::group_b
- scope: loom_multi_thread_alt::group_c
- scope: loom_multi_thread_alt::group_d
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
- name: loom ${{ matrix.scope }}
run: cargo test --lib --release --features full -- $SCOPE
working-directory: tokio
env:
RUSTFLAGS: --cfg loom --cfg tokio_unstable -Dwarnings
LOOM_MAX_PREEMPTIONS: 2
SCOPE: ${{ matrix.scope }}
+7 -10
View File
@@ -8,6 +8,10 @@ on:
paths:
- '**/Cargo.toml'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
permissions:
contents: read
@@ -19,17 +23,10 @@ jobs:
- uses: actions/checkout@v3
- name: Install cargo-audit
uses: actions-rs/cargo@v1
with:
command: install
args: cargo-audit
run: cargo install cargo-audit
- name: Generate lockfile
uses: actions-rs/cargo@v1
with:
command: generate-lockfile
run: cargo generate-lockfile
- name: Audit dependencies
uses: actions-rs/cargo@v1
with:
command: audit
run: cargo audit
+7 -4
View File
@@ -5,6 +5,10 @@ on:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
@@ -25,18 +29,17 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
override: true
- uses: Swatinem/rust-cache@v1
- name: Install Valgrind
uses: taiki-e/install-action@valgrind
- uses: Swatinem/rust-cache@v2
# Compiles each of the stress test examples.
- name: Compile stress test examples
run: cargo build -p stress-test --release --example ${{ matrix.stress-test }}
# Runs each of the examples using Valgrind. Detects leaks and displays them.
- name: Run valgrind
run: valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./target/release/examples/${{ matrix.stress-test }}
run: valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all --fair-sched=yes ./target/release/examples/${{ matrix.stress-test }}
+1
View File
@@ -2,3 +2,4 @@ target
Cargo.lock
.cargo/config.toml
.cargo/config
+54 -8
View File
@@ -131,8 +131,11 @@ cargo check --all-features
cargo test --all-features
```
Clippy must be run using the MSRV, so Tokio can avoid having to `#[allow]` new
lints whose fixes would be incompatible with the current MSRV:
Ideally, you should use the same version of clippy as the one used in CI
(defined by `env.rust_clippy` in [ci.yml][ci.yml]), because newer versions
might have new lints:
[ci.yml]: .github/workflows/ci.yml
<!--
When updating this, also update:
@@ -146,7 +149,7 @@ When updating this, also update:
-->
```
cargo +1.49.0 clippy --all --tests --all-features
cargo +1.65.0 clippy --all --tests --all-features
```
When building documentation normally, the markers that list the features
@@ -170,10 +173,10 @@ command below instead:
```
# Mac or Linux
rustfmt --check --edition 2018 $(git ls-files '*.rs')
rustfmt --check --edition 2021 $(git ls-files '*.rs')
# Powershell
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2018 $_.FullName }
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2021 $_.FullName }
```
The `--check` argument prints the things that need to be fixed. If you remove
it, `rustfmt` will update your files locally instead.
@@ -187,7 +190,7 @@ LOOM_MAX_PREEMPTIONS=1 RUSTFLAGS="--cfg loom" \
You can run miri tests with
```
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-tag-raw-pointers" PROPTEST_CASES=10 \
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-tag-raw-pointers" \
cargo +nightly miri test --features full --lib
```
@@ -197,8 +200,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
@@ -209,6 +226,31 @@ utilities available to use in tests, no matter the crate being tested.
The best strategy for writing a new integration test is to look at existing
integration tests in the crate and follow the style.
#### Fuzz tests
Some of our crates include a set of fuzz tests, this will be marked by a
directory `fuzz`. It is a good idea to run fuzz tests after each change.
To get started with fuzz testing you'll need to install
[cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz).
`cargo install cargo-fuzz`
To list the available fuzzing harnesses you can run;
```bash
$ cd tokio
$ cargo fuzz list
fuzz_linked_list
````
Running a fuzz test is as simple as;
`cargo fuzz run fuzz_linked_list`
**NOTE**: Keep in mind that by default when running a fuzz test the fuzz
harness will run forever and will only exit if you `ctrl-c` or it finds
a bug.
#### Documentation tests
Ideally, every API has at least one [documentation test] that demonstrates how to
@@ -633,3 +675,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
+1 -1
View File
@@ -1,5 +1,5 @@
[workspace]
resolver = "2"
members = [
"tokio",
"tokio-macros",
+1
View File
@@ -1,4 +1,5 @@
[build.env]
passthrough = [
"RUSTFLAGS",
"RUST_BACKTRACE",
]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2022 Tokio Contributors
Copyright (c) 2023 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+32 -12
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.22.0", features = ["full"] }
tokio = { version = "1.35.0", features = ["full"] }
```
Then, on your main.rs:
@@ -132,6 +132,8 @@ project.
In addition to the crates in this repository, the Tokio project also maintains
several other libraries, including:
* [`axum`]: A web application framework that focuses on ergonomics and modularity.
* [`hyper`]: A fast and correct HTTP/1.1 and HTTP/2 implementation for Rust.
* [`tonic`]: A gRPC over HTTP/2 implementation focused on high performance, interoperability, and flexibility.
@@ -142,21 +144,18 @@ several other libraries, including:
* [`tracing`] (formerly `tokio-trace`): A framework for application-level tracing and async-aware diagnostics.
* [`rdbc`]: A Rust database connectivity library for MySQL, Postgres and SQLite.
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers
`tokio`.
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers `tokio`.
* [`bytes`]: Utilities for working with bytes, including efficient byte buffers.
* [`loom`]: A testing tool for concurrent Rust code
* [`loom`]: A testing tool for concurrent Rust code.
[`axum`]: https://github.com/tokio-rs/axum
[`warp`]: https://github.com/seanmonstar/warp
[`hyper`]: https://github.com/hyperium/hyper
[`tonic`]: https://github.com/hyperium/tonic
[`tower`]: https://github.com/tower-rs/tower
[`loom`]: https://github.com/tokio-rs/loom
[`rdbc`]: https://github.com/tokio-rs/rdbc
[`tracing`]: https://github.com/tokio-rs/tracing
[`mio`]: https://github.com/tokio-rs/mio
[`bytes`]: https://github.com/tokio-rs/bytes
@@ -187,7 +186,21 @@ 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.63.
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.30 to now - Rust 1.63
* 1.27 to 1.29 - 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,20 +215,27 @@ 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.25.x` - LTS release until March 2024. (MSRV 1.49)
* `1.32.x` - LTS release until September 2024. (MSRV 1.63)
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
use an LTS release.
To use a fixed minor version, you can specify the version with a tilde. For
example, to specify that you wish to use the newest `1.18.x` patch release, you
example, to specify that you wish to use the newest `1.25.x` patch release, you
can use the following dependency specification:
```text
tokio = { version = "~1.18", features = [...] }
tokio = { version = "~1.25", features = [...] }
```
### Previous LTS releases
* `1.8.x` - LTS release until February 2022.
* `1.14.x` - LTS release until June 2022.
* `1.18.x` - LTS release until June 2023.
* `1.20.x` - LTS release until September 2023.
## License
This project is licensed under the [MIT license].
+30 -2
View File
@@ -2,13 +2,17 @@
name = "benches"
version = "0.0.0"
publish = false
edition = "2018"
edition = "2021"
[features]
test-util = ["tokio/test-util"]
[dependencies]
tokio = { version = "1.5.0", path = "../tokio", features = ["full"] }
bencher = "0.1.5"
criterion = "0.5.1"
rand = "0.8"
rand_chacha = "0.3"
num_cpus = "1.16.0"
[dev-dependencies]
tokio-util = { version = "0.7.0", path = "../tokio-util", features = ["full"] }
@@ -27,11 +31,30 @@ 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_current_thread"
path = "rt_current_thread.rs"
harness = false
[[bench]]
name = "rt_multi_threaded"
path = "rt_multi_threaded.rs"
harness = false
[[bench]]
name = "sync_notify"
path = "sync_notify.rs"
harness = false
[[bench]]
name = "sync_rwlock"
@@ -57,3 +80,8 @@ harness = false
name = "copy"
path = "copy.rs"
harness = false
[[bench]]
name = "time_now"
path = "time_now.rs"
harness = false
+51 -39
View File
@@ -1,4 +1,4 @@
use bencher::{benchmark_group, benchmark_main, Bencher};
use criterion::{criterion_group, criterion_main, Criterion};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;
@@ -174,65 +174,77 @@ fn rt() -> tokio::runtime::Runtime {
.unwrap()
}
fn copy_mem_to_mem(b: &mut Bencher) {
fn copy_mem_to_mem(c: &mut Criterion) {
let rt = rt();
b.iter(|| {
let task = || async {
let mut source = repeat(0).take(SOURCE_SIZE);
let mut dest = Vec::new();
copy(&mut source, &mut dest).await.unwrap();
};
c.bench_function("copy_mem_to_mem", |b| {
b.iter(|| {
let task = || async {
let mut source = repeat(0).take(SOURCE_SIZE);
let mut dest = Vec::new();
copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task());
})
rt.block_on(task());
})
});
}
fn copy_mem_to_slow_hdd(b: &mut Bencher) {
fn copy_mem_to_slow_hdd(c: &mut Criterion) {
let rt = rt();
b.iter(|| {
let task = || async {
let mut source = repeat(0).take(SOURCE_SIZE);
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
copy(&mut source, &mut dest).await.unwrap();
};
c.bench_function("copy_mem_to_slow_hdd", |b| {
b.iter(|| {
let task = || async {
let mut source = repeat(0).take(SOURCE_SIZE);
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task());
})
rt.block_on(task());
})
});
}
fn copy_chunk_to_mem(b: &mut Bencher) {
fn copy_chunk_to_mem(c: &mut Criterion) {
let rt = rt();
b.iter(|| {
let task = || async {
let mut source = ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
let mut dest = Vec::new();
copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task());
})
c.bench_function("copy_chunk_to_mem", |b| {
b.iter(|| {
let task = || async {
let mut source =
ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
let mut dest = Vec::new();
copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task());
})
});
}
fn copy_chunk_to_slow_hdd(b: &mut Bencher) {
fn copy_chunk_to_slow_hdd(c: &mut Criterion) {
let rt = rt();
b.iter(|| {
let task = || async {
let mut source = ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task());
})
c.bench_function("copy_chunk_to_slow_hdd", |b| {
b.iter(|| {
let task = || async {
let mut source =
ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task());
})
});
}
benchmark_group!(
criterion_group!(
copy_bench,
copy_mem_to_mem,
copy_mem_to_slow_hdd,
copy_chunk_to_mem,
copy_chunk_to_slow_hdd,
);
benchmark_main!(copy_bench);
criterion_main!(copy_bench);
+66 -57
View File
@@ -6,7 +6,7 @@ use tokio::fs::File;
use tokio::io::AsyncReadExt;
use tokio_util::codec::{BytesCodec, FramedRead /*FramedWrite*/};
use bencher::{benchmark_group, benchmark_main, Bencher};
use criterion::{criterion_group, criterion_main, Criterion};
use std::fs::File as StdFile;
use std::io::Read as StdRead;
@@ -23,81 +23,90 @@ const BLOCK_COUNT: usize = 1_000;
const BUFFER_SIZE: usize = 4096;
const DEV_ZERO: &str = "/dev/zero";
fn async_read_codec(b: &mut Bencher) {
fn async_read_codec(c: &mut Criterion) {
let rt = rt();
b.iter(|| {
let task = || async {
let file = File::open(DEV_ZERO).await.unwrap();
let mut input_stream = FramedRead::with_capacity(file, BytesCodec::new(), BUFFER_SIZE);
c.bench_function("async_read_codec", |b| {
b.iter(|| {
let task = || async {
let file = File::open(DEV_ZERO).await.unwrap();
let mut input_stream =
FramedRead::with_capacity(file, BytesCodec::new(), BUFFER_SIZE);
for _i in 0..BLOCK_COUNT {
let _bytes = input_stream.next().await.unwrap();
}
};
rt.block_on(task());
});
}
fn async_read_buf(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
let task = || async {
let mut file = File::open(DEV_ZERO).await.unwrap();
let mut buffer = [0u8; BUFFER_SIZE];
for _i in 0..BLOCK_COUNT {
let count = file.read(&mut buffer).await.unwrap();
if count == 0 {
break;
for _i in 0..BLOCK_COUNT {
let _bytes = input_stream.next().await.unwrap();
}
}
};
};
rt.block_on(task());
rt.block_on(task());
})
});
}
fn async_read_std_file(b: &mut Bencher) {
fn async_read_buf(c: &mut Criterion) {
let rt = rt();
let task = || async {
let mut file = tokio::task::block_in_place(|| Box::pin(StdFile::open(DEV_ZERO).unwrap()));
c.bench_function("async_read_buf", |b| {
b.iter(|| {
let task = || async {
let mut file = File::open(DEV_ZERO).await.unwrap();
let mut buffer = [0u8; BUFFER_SIZE];
for _i in 0..BLOCK_COUNT {
for _i in 0..BLOCK_COUNT {
let count = file.read(&mut buffer).await.unwrap();
if count == 0 {
break;
}
}
};
rt.block_on(task());
});
});
}
fn async_read_std_file(c: &mut Criterion) {
let rt = rt();
c.bench_function("async_read_std_file", |b| {
b.iter(|| {
let task = || async {
let mut file =
tokio::task::block_in_place(|| Box::pin(StdFile::open(DEV_ZERO).unwrap()));
for _i in 0..BLOCK_COUNT {
let mut buffer = [0u8; BUFFER_SIZE];
let mut file_ref = file.as_mut();
tokio::task::block_in_place(move || {
file_ref.read_exact(&mut buffer).unwrap();
});
}
};
rt.block_on(task());
});
});
}
fn sync_read(c: &mut Criterion) {
c.bench_function("sync_read", |b| {
b.iter(|| {
let mut file = StdFile::open(DEV_ZERO).unwrap();
let mut buffer = [0u8; BUFFER_SIZE];
let mut file_ref = file.as_mut();
tokio::task::block_in_place(move || {
file_ref.read_exact(&mut buffer).unwrap();
});
}
};
b.iter(|| {
rt.block_on(task());
for _i in 0..BLOCK_COUNT {
file.read_exact(&mut buffer).unwrap();
}
})
});
}
fn sync_read(b: &mut Bencher) {
b.iter(|| {
let mut file = StdFile::open(DEV_ZERO).unwrap();
let mut buffer = [0u8; BUFFER_SIZE];
for _i in 0..BLOCK_COUNT {
file.read_exact(&mut buffer).unwrap();
}
});
}
benchmark_group!(
criterion_group!(
file,
async_read_std_file,
async_read_buf,
async_read_codec,
sync_read
);
benchmark_main!(file);
criterion_main!(file);
+89
View File
@@ -0,0 +1,89 @@
//! Benchmark implementation details of the threaded scheduler. These benches are
//! intended to be used as a form of regression testing and not as a general
//! purpose benchmark demonstrating real-world performance.
use tokio::runtime::{self, Runtime};
use criterion::{criterion_group, criterion_main, Criterion};
const NUM_SPAWN: usize = 1_000;
fn spawn_many_local(c: &mut Criterion) {
let rt = rt();
let mut handles = Vec::with_capacity(NUM_SPAWN);
c.bench_function("spawn_many_local", |b| {
b.iter(|| {
rt.block_on(async {
for _ in 0..NUM_SPAWN {
handles.push(tokio::spawn(async move {}));
}
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
});
}
fn spawn_many_remote_idle(c: &mut Criterion) {
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
c.bench_function("spawn_many_remote_idle", |b| {
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt_handle.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
});
}
fn spawn_many_remote_busy(c: &mut Criterion) {
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
rt.spawn(async {
fn iter() {
tokio::spawn(async { iter() });
}
iter()
});
c.bench_function("spawn_many_remote_busy", |b| {
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt_handle.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
});
}
fn rt() -> Runtime {
runtime::Builder::new_current_thread().build().unwrap()
}
criterion_group!(
scheduler,
spawn_many_local,
spawn_many_remote_idle,
spawn_many_remote_busy
);
criterion_main!(scheduler);
+202 -77
View File
@@ -5,67 +5,173 @@
use tokio::runtime::{self, Runtime};
use tokio::sync::oneshot;
use bencher::{benchmark_group, benchmark_main, Bencher};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::{mpsc, Arc};
use std::time::{Duration, Instant};
fn spawn_many(b: &mut Bencher) {
const NUM_SPAWN: usize = 10_000;
use criterion::{criterion_group, criterion_main, Criterion};
const NUM_WORKERS: usize = 4;
const NUM_SPAWN: usize = 10_000;
const STALL_DUR: Duration = Duration::from_micros(10);
fn spawn_many_local(c: &mut Criterion) {
let rt = rt();
let (tx, rx) = mpsc::sync_channel(1000);
let rem = Arc::new(AtomicUsize::new(0));
b.iter(|| {
rem.store(NUM_SPAWN, Relaxed);
c.bench_function("spawn_many_local", |b| {
b.iter(|| {
rem.store(NUM_SPAWN, Relaxed);
rt.block_on(async {
for _ in 0..NUM_SPAWN {
let tx = tx.clone();
let rem = rem.clone();
rt.block_on(async {
for _ in 0..NUM_SPAWN {
let tx = tx.clone();
let rem = rem.clone();
tokio::spawn(async move {
if 1 == rem.fetch_sub(1, Relaxed) {
tx.send(()).unwrap();
}
});
}
tokio::spawn(async move {
if 1 == rem.fetch_sub(1, Relaxed) {
tx.send(()).unwrap();
}
});
}
let _ = rx.recv().unwrap();
});
let _ = rx.recv().unwrap();
});
})
});
}
fn yield_many(b: &mut Bencher) {
fn spawn_many_remote_idle(c: &mut Criterion) {
let rt = rt();
let mut handles = Vec::with_capacity(NUM_SPAWN);
c.bench_function("spawn_many_remote_idle", |b| {
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
});
}
// The runtime is busy with tasks that consume CPU time and yield. Yielding is a
// lower notification priority than spawning / regular notification.
fn spawn_many_remote_busy1(c: &mut Criterion) {
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
let flag = Arc::new(AtomicBool::new(true));
// Spawn some tasks to keep the runtimes busy
for _ in 0..(2 * NUM_WORKERS) {
let flag = flag.clone();
rt.spawn(async move {
while flag.load(Relaxed) {
tokio::task::yield_now().await;
stall();
}
});
}
c.bench_function("spawn_many_remote_busy1", |b| {
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt_handle.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
});
flag.store(false, Relaxed);
}
// The runtime is busy with tasks that consume CPU time and spawn new high-CPU
// tasks. Spawning goes via a higher notification priority than yielding.
fn spawn_many_remote_busy2(c: &mut Criterion) {
const NUM_SPAWN: usize = 1_000;
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
let flag = Arc::new(AtomicBool::new(true));
// Spawn some tasks to keep the runtimes busy
for _ in 0..(NUM_WORKERS) {
let flag = flag.clone();
fn iter(flag: Arc<AtomicBool>) {
tokio::spawn(async {
if flag.load(Relaxed) {
stall();
iter(flag);
}
});
}
rt.spawn(async {
iter(flag);
});
}
c.bench_function("spawn_many_remote_busy2", |b| {
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt_handle.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
});
flag.store(false, Relaxed);
}
fn yield_many(c: &mut Criterion) {
const NUM_YIELD: usize = 1_000;
const TASKS: usize = 200;
let rt = rt();
c.bench_function("yield_many", |b| {
let rt = rt();
let (tx, rx) = mpsc::sync_channel(TASKS);
let (tx, rx) = mpsc::sync_channel(TASKS);
b.iter(move || {
for _ in 0..TASKS {
let tx = tx.clone();
b.iter(move || {
for _ in 0..TASKS {
let tx = tx.clone();
rt.spawn(async move {
for _ in 0..NUM_YIELD {
tokio::task::yield_now().await;
}
rt.spawn(async move {
for _ in 0..NUM_YIELD {
tokio::task::yield_now().await;
}
tx.send(()).unwrap();
});
}
tx.send(()).unwrap();
});
}
for _ in 0..TASKS {
let _ = rx.recv().unwrap();
}
for _ in 0..TASKS {
let _ = rx.recv().unwrap();
}
})
});
}
fn ping_pong(b: &mut Bencher) {
fn ping_pong(c: &mut Criterion) {
const NUM_PINGS: usize = 1_000;
let rt = rt();
@@ -73,46 +179,46 @@ fn ping_pong(b: &mut Bencher) {
let (done_tx, done_rx) = mpsc::sync_channel(1000);
let rem = Arc::new(AtomicUsize::new(0));
b.iter(|| {
let done_tx = done_tx.clone();
let rem = rem.clone();
rem.store(NUM_PINGS, Relaxed);
c.bench_function("ping_pong", |b| {
b.iter(|| {
let done_tx = done_tx.clone();
let rem = rem.clone();
rem.store(NUM_PINGS, Relaxed);
rt.block_on(async {
tokio::spawn(async move {
for _ in 0..NUM_PINGS {
let rem = rem.clone();
let done_tx = done_tx.clone();
tokio::spawn(async move {
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
rt.block_on(async {
tokio::spawn(async move {
for _ in 0..NUM_PINGS {
let rem = rem.clone();
let done_tx = done_tx.clone();
tokio::spawn(async move {
rx1.await.unwrap();
tx2.send(()).unwrap();
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
tokio::spawn(async move {
rx1.await.unwrap();
tx2.send(()).unwrap();
});
tx1.send(()).unwrap();
rx2.await.unwrap();
if 1 == rem.fetch_sub(1, Relaxed) {
done_tx.send(()).unwrap();
}
});
}
});
tx1.send(()).unwrap();
rx2.await.unwrap();
if 1 == rem.fetch_sub(1, Relaxed) {
done_tx.send(()).unwrap();
}
});
}
done_rx.recv().unwrap();
});
done_rx.recv().unwrap();
});
})
});
}
fn chained_spawn(b: &mut Bencher) {
fn chained_spawn(c: &mut Criterion) {
const ITER: usize = 1_000;
let rt = rt();
fn iter(done_tx: mpsc::SyncSender<()>, n: usize) {
if n == 0 {
done_tx.send(()).unwrap();
@@ -123,29 +229,48 @@ fn chained_spawn(b: &mut Bencher) {
}
}
let (done_tx, done_rx) = mpsc::sync_channel(1000);
c.bench_function("chained_spawn", |b| {
let rt = rt();
let (done_tx, done_rx) = mpsc::sync_channel(1000);
b.iter(move || {
let done_tx = done_tx.clone();
b.iter(move || {
let done_tx = done_tx.clone();
rt.block_on(async {
tokio::spawn(async move {
iter(done_tx, ITER);
rt.block_on(async {
tokio::spawn(async move {
iter(done_tx, ITER);
});
done_rx.recv().unwrap();
});
done_rx.recv().unwrap();
});
})
});
}
fn rt() -> Runtime {
runtime::Builder::new_multi_thread()
.worker_threads(4)
.worker_threads(NUM_WORKERS)
.enable_all()
.build()
.unwrap()
}
benchmark_group!(scheduler, spawn_many, ping_pong, yield_many, chained_spawn,);
fn stall() {
let now = Instant::now();
while now.elapsed() < STALL_DUR {
std::thread::yield_now();
}
}
benchmark_main!(scheduler);
criterion_group!(
scheduler,
spawn_many_local,
spawn_many_remote_idle,
spawn_many_remote_busy1,
spawn_many_remote_busy2,
ping_pong,
yield_many,
chained_spawn,
);
criterion_main!(scheduler);
+17 -15
View File
@@ -1,7 +1,7 @@
//! Benchmark the delay in propagating OS signals to any listeners.
#![cfg(unix)]
use bencher::{benchmark_group, benchmark_main, Bencher};
use criterion::{criterion_group, criterion_main, Criterion};
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -41,7 +41,7 @@ pub fn send_signal(signal: libc::c_int) {
}
}
fn many_signals(bench: &mut Bencher) {
fn many_signals(c: &mut Criterion) {
let num_signals = 10;
let (tx, mut rx) = mpsc::channel(num_signals);
@@ -75,21 +75,23 @@ fn many_signals(bench: &mut Bencher) {
// tasks have been polled at least once
rt.block_on(Spinner::new());
bench.iter(|| {
rt.block_on(async {
send_signal(libc::SIGCHLD);
for _ in 0..num_signals {
rx.recv().await.expect("channel closed");
}
c.bench_function("many_signals", |b| {
b.iter(|| {
rt.block_on(async {
send_signal(libc::SIGCHLD);
for _ in 0..num_signals {
rx.recv().await.expect("channel closed");
}
send_signal(libc::SIGIO);
for _ in 0..num_signals {
rx.recv().await.expect("channel closed");
}
});
send_signal(libc::SIGIO);
for _ in 0..num_signals {
rx.recv().await.expect("channel closed");
}
});
})
});
}
benchmark_group!(signal_group, many_signals,);
criterion_group!(signal_group, many_signals);
benchmark_main!(signal_group);
criterion_main!(signal_group);
+47 -40
View File
@@ -2,10 +2,7 @@
//! 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};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
async fn work() -> usize {
let val = 1 + 1;
@@ -13,67 +10,77 @@ async fn work() -> usize {
black_box(val)
}
fn basic_scheduler_spawn(bench: &mut Bencher) {
fn basic_scheduler_spawn(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
bench.iter(|| {
runtime.block_on(async {
let h = tokio::spawn(work());
assert_eq!(h.await.unwrap(), 2);
});
c.bench_function("basic_scheduler_spawn", |b| {
b.iter(|| {
runtime.block_on(async {
let h = tokio::spawn(work());
assert_eq!(h.await.unwrap(), 2);
});
})
});
}
fn basic_scheduler_spawn10(bench: &mut Bencher) {
fn basic_scheduler_spawn10(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
bench.iter(|| {
runtime.block_on(async {
let mut handles = Vec::with_capacity(10);
for _ in 0..10 {
handles.push(tokio::spawn(work()));
}
for handle in handles {
assert_eq!(handle.await.unwrap(), 2);
}
});
c.bench_function("basic_scheduler_spawn10", |b| {
b.iter(|| {
runtime.block_on(async {
let mut handles = Vec::with_capacity(10);
for _ in 0..10 {
handles.push(tokio::spawn(work()));
}
for handle in handles {
assert_eq!(handle.await.unwrap(), 2);
}
});
})
});
}
fn threaded_scheduler_spawn(bench: &mut Bencher) {
fn threaded_scheduler_spawn(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.build()
.unwrap();
bench.iter(|| {
runtime.block_on(async {
let h = tokio::spawn(work());
assert_eq!(h.await.unwrap(), 2);
});
c.bench_function("threaded_scheduler_spawn", |b| {
b.iter(|| {
runtime.block_on(async {
let h = tokio::spawn(work());
assert_eq!(h.await.unwrap(), 2);
});
})
});
}
fn threaded_scheduler_spawn10(bench: &mut Bencher) {
fn threaded_scheduler_spawn10(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.build()
.unwrap();
bench.iter(|| {
runtime.block_on(async {
let mut handles = Vec::with_capacity(10);
for _ in 0..10 {
handles.push(tokio::spawn(work()));
}
for handle in handles {
assert_eq!(handle.await.unwrap(), 2);
}
});
c.bench_function("threaded_scheduler_spawn10", |b| {
b.iter(|| {
runtime.block_on(async {
let mut handles = Vec::with_capacity(10);
for _ in 0..10 {
handles.push(tokio::spawn(work()));
}
for handle in handles {
assert_eq!(handle.await.unwrap(), 2);
}
});
})
});
}
bencher::benchmark_group!(
criterion_group!(
spawn,
basic_scheduler_spawn,
basic_scheduler_spawn10,
@@ -81,4 +88,4 @@ bencher::benchmark_group!(
threaded_scheduler_spawn10,
);
bencher::benchmark_main!(spawn);
criterion_main!(spawn);
+282 -130
View File
@@ -1,8 +1,23 @@
use bencher::{black_box, Bencher};
use tokio::sync::mpsc;
type Medium = [usize; 64];
type Large = [Medium; 64];
use criterion::measurement::WallTime;
use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion};
#[derive(Debug, Copy, Clone)]
struct Medium([usize; 64]);
impl Default for Medium {
fn default() -> Self {
Medium([0; 64])
}
}
#[derive(Debug, Copy, Clone)]
struct Large([Medium; 64]);
impl Default for Large {
fn default() -> Self {
Large([Medium::default(); 64])
}
}
fn rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_multi_thread()
@@ -11,169 +26,306 @@ fn rt() -> tokio::runtime::Runtime {
.unwrap()
}
fn create_1_medium(b: &mut Bencher) {
b.iter(|| {
black_box(&mpsc::channel::<Medium>(1));
});
}
fn create_100_medium(b: &mut Bencher) {
b.iter(|| {
black_box(&mpsc::channel::<Medium>(100));
});
}
fn create_100_000_medium(b: &mut Bencher) {
b.iter(|| {
black_box(&mpsc::channel::<Medium>(100_000));
});
}
fn send_medium(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
let (tx, mut rx) = mpsc::channel::<Medium>(1000);
let _ = rt.block_on(tx.send([0; 64]));
rt.block_on(rx.recv()).unwrap();
});
}
fn send_large(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
let (tx, mut rx) = mpsc::channel::<Large>(1000);
let _ = rt.block_on(tx.send([[0; 64]; 64]));
rt.block_on(rx.recv()).unwrap();
});
}
fn contention_bounded(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
for _ in 0..1_000 * 5 {
let _ = rx.recv().await;
}
fn create_medium<const SIZE: usize>(g: &mut BenchmarkGroup<WallTime>) {
g.bench_function(SIZE.to_string(), |b| {
b.iter(|| {
black_box(&mpsc::channel::<Medium>(SIZE));
})
});
}
fn contention_bounded_full(b: &mut Bencher) {
fn send_data<T: Default, const SIZE: usize>(g: &mut BenchmarkGroup<WallTime>, prefix: &str) {
let rt = rt();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(100);
g.bench_function(format!("{}_{}", prefix, SIZE), |b| {
b.iter(|| {
let (tx, mut rx) = mpsc::channel::<T>(SIZE);
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
let _ = rt.block_on(tx.send(T::default()));
for _ in 0..1_000 * 5 {
let _ = rx.recv().await;
}
rt.block_on(rx.recv()).unwrap();
})
});
}
fn contention_unbounded(b: &mut Bencher) {
fn contention_bounded(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
g.bench_function("bounded", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).unwrap();
}
});
}
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
for _ in 0..1_000 * 5 {
let _ = rx.recv().await;
}
for _ in 0..1_000 * 5 {
let _ = rx.recv().await;
}
})
})
});
}
fn uncontented_bounded(b: &mut Bencher) {
fn contention_bounded_recv_many(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
g.bench_function("bounded_recv_many", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for i in 0..5000 {
tx.send(i).await.unwrap();
}
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
for _ in 0..5_000 {
let _ = rx.recv().await;
}
let mut buffer = Vec::<usize>::with_capacity(5_000);
let mut total = 0;
while total < 1_000 * 5 {
total += rx.recv_many(&mut buffer, 5_000).await;
}
})
})
});
}
fn uncontented_unbounded(b: &mut Bencher) {
fn contention_bounded_full(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
g.bench_function("bounded_full", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(100);
for i in 0..5000 {
tx.send(i).unwrap();
}
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
for _ in 0..5_000 {
let _ = rx.recv().await;
}
for _ in 0..1_000 * 5 {
let _ = rx.recv().await;
}
})
})
});
}
bencher::benchmark_group!(
create,
create_1_medium,
create_100_medium,
create_100_000_medium
);
fn contention_bounded_full_recv_many(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
bencher::benchmark_group!(send, send_medium, send_large);
g.bench_function("bounded_full_recv_many", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(100);
bencher::benchmark_group!(
contention,
contention_bounded,
contention_bounded_full,
contention_unbounded,
uncontented_bounded,
uncontented_unbounded
);
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
bencher::benchmark_main!(create, send, contention);
let mut buffer = Vec::<usize>::with_capacity(5_000);
let mut total = 0;
while total < 1_000 * 5 {
total += rx.recv_many(&mut buffer, 5_000).await;
}
})
})
});
}
fn contention_unbounded(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
g.bench_function("unbounded", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).unwrap();
}
});
}
for _ in 0..1_000 * 5 {
let _ = rx.recv().await;
}
})
})
});
}
fn contention_unbounded_recv_many(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
g.bench_function("unbounded_recv_many", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).unwrap();
}
});
}
let mut buffer = Vec::<usize>::with_capacity(5_000);
let mut total = 0;
while total < 1_000 * 5 {
total += rx.recv_many(&mut buffer, 5_000).await;
}
})
})
});
}
fn uncontented_bounded(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
g.bench_function("bounded", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for i in 0..5000 {
tx.send(i).await.unwrap();
}
for _ in 0..5_000 {
let _ = rx.recv().await;
}
})
})
});
}
fn uncontented_bounded_recv_many(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
g.bench_function("bounded_recv_many", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for i in 0..5000 {
tx.send(i).await.unwrap();
}
let mut buffer = Vec::<usize>::with_capacity(5_000);
let mut total = 0;
while total < 1_000 * 5 {
total += rx.recv_many(&mut buffer, 5_000).await;
}
})
})
});
}
fn uncontented_unbounded(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
g.bench_function("unbounded", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
for i in 0..5000 {
tx.send(i).unwrap();
}
for _ in 0..5_000 {
let _ = rx.recv().await;
}
})
})
});
}
fn uncontented_unbounded_recv_many(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
g.bench_function("unbounded_recv_many", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
for i in 0..5000 {
tx.send(i).unwrap();
}
let mut buffer = Vec::<usize>::with_capacity(5_000);
let mut total = 0;
while total < 1_000 * 5 {
total += rx.recv_many(&mut buffer, 5_000).await;
}
})
})
});
}
fn bench_create_medium(c: &mut Criterion) {
let mut group = c.benchmark_group("create_medium");
create_medium::<1>(&mut group);
create_medium::<100>(&mut group);
create_medium::<100_000>(&mut group);
group.finish();
}
fn bench_send(c: &mut Criterion) {
let mut group = c.benchmark_group("send");
send_data::<Medium, 1000>(&mut group, "medium");
send_data::<Large, 1000>(&mut group, "large");
group.finish();
}
fn bench_contention(c: &mut Criterion) {
let mut group = c.benchmark_group("contention");
contention_bounded(&mut group);
contention_bounded_recv_many(&mut group);
contention_bounded_full(&mut group);
contention_bounded_full_recv_many(&mut group);
contention_unbounded(&mut group);
contention_unbounded_recv_many(&mut group);
group.finish();
}
fn bench_uncontented(c: &mut Criterion) {
let mut group = c.benchmark_group("uncontented");
uncontented_bounded(&mut group);
uncontented_bounded_recv_many(&mut group);
uncontented_unbounded(&mut group);
uncontented_unbounded_recv_many(&mut group);
group.finish();
}
criterion_group!(create, bench_create_medium);
criterion_group!(send, bench_send);
criterion_group!(contention, bench_contention);
criterion_group!(uncontented, bench_uncontented);
criterion_main!(create, send, contention, uncontented);
+56
View File
@@ -0,0 +1,56 @@
use tokio::{
runtime::Runtime,
sync::{mpsc, oneshot},
};
use criterion::{criterion_group, criterion_main, Criterion};
fn request_reply_current_thread(c: &mut Criterion) {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
request_reply(c, rt);
}
fn request_reply_multi_threaded(c: &mut Criterion) {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.build()
.unwrap();
request_reply(c, rt);
}
fn request_reply(b: &mut Criterion, 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.bench_function("request_reply", |b| {
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;
}
})
})
});
}
criterion_group!(
sync_mpsc_oneshot_group,
request_reply_current_thread,
request_reply_multi_threaded,
);
criterion_main!(sync_mpsc_oneshot_group);
+104
View File
@@ -0,0 +1,104 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use tokio::sync::Notify;
use criterion::measurement::WallTime;
use criterion::{criterion_group, criterion_main, BenchmarkGroup, Criterion};
fn rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap()
}
fn notify_waiters<const N_WAITERS: usize>(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
let notify = Arc::new(Notify::new());
let counter = Arc::new(AtomicUsize::new(0));
for _ in 0..N_WAITERS {
rt.spawn({
let notify = notify.clone();
let counter = counter.clone();
async move {
loop {
notify.notified().await;
counter.fetch_add(1, Ordering::Relaxed);
}
}
});
}
const N_ITERS: usize = 500;
g.bench_function(N_WAITERS.to_string(), |b| {
b.iter(|| {
counter.store(0, Ordering::Relaxed);
loop {
notify.notify_waiters();
if counter.load(Ordering::Relaxed) >= N_ITERS {
break;
}
}
})
});
}
fn notify_one<const N_WAITERS: usize>(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
let notify = Arc::new(Notify::new());
let counter = Arc::new(AtomicUsize::new(0));
for _ in 0..N_WAITERS {
rt.spawn({
let notify = notify.clone();
let counter = counter.clone();
async move {
loop {
notify.notified().await;
counter.fetch_add(1, Ordering::Relaxed);
}
}
});
}
const N_ITERS: usize = 500;
g.bench_function(N_WAITERS.to_string(), |b| {
b.iter(|| {
counter.store(0, Ordering::Relaxed);
loop {
notify.notify_one();
if counter.load(Ordering::Relaxed) >= N_ITERS {
break;
}
}
})
});
}
fn bench_notify_one(c: &mut Criterion) {
let mut group = c.benchmark_group("notify_one");
notify_one::<10>(&mut group);
notify_one::<50>(&mut group);
notify_one::<100>(&mut group);
notify_one::<200>(&mut group);
notify_one::<500>(&mut group);
group.finish();
}
fn bench_notify_waiters(c: &mut Criterion) {
let mut group = c.benchmark_group("notify_waiters");
notify_waiters::<10>(&mut group);
notify_waiters::<50>(&mut group);
notify_waiters::<100>(&mut group);
notify_waiters::<200>(&mut group);
notify_waiters::<500>(&mut group);
group.finish();
}
criterion_group!(
notify_waiters_simple,
bench_notify_one,
bench_notify_waiters
);
criterion_main!(notify_waiters_simple);
+91 -70
View File
@@ -1,26 +1,30 @@
use bencher::{black_box, Bencher};
use std::sync::Arc;
use tokio::{sync::RwLock, task};
fn read_uncontended(b: &mut Bencher) {
use criterion::measurement::WallTime;
use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion};
fn read_uncontended(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap();
let lock = Arc::new(RwLock::new(()));
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
for _ in 0..6 {
let read = lock.read().await;
let _read = black_box(read);
}
g.bench_function("read", |b| {
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
for _ in 0..6 {
let read = lock.read().await;
let _read = black_box(read);
}
})
})
});
}
fn read_concurrent_uncontended_multi(b: &mut Bencher) {
fn read_concurrent_uncontended_multi(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
@@ -32,23 +36,25 @@ fn read_concurrent_uncontended_multi(b: &mut Bencher) {
}
let lock = Arc::new(RwLock::new(()));
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
let j = tokio::try_join! {
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone()))
};
j.unwrap();
g.bench_function("read_concurrent_multi", |b| {
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
let j = tokio::try_join! {
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone()))
};
j.unwrap();
})
})
});
}
fn read_concurrent_uncontended(b: &mut Bencher) {
fn read_concurrent_uncontended(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
@@ -59,22 +65,24 @@ fn read_concurrent_uncontended(b: &mut Bencher) {
}
let lock = Arc::new(RwLock::new(()));
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
tokio::join! {
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone())
};
g.bench_function("read_concurrent", |b| {
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
tokio::join! {
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone())
};
})
})
});
}
fn read_concurrent_contended_multi(b: &mut Bencher) {
fn read_concurrent_contended_multi(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
@@ -86,24 +94,26 @@ fn read_concurrent_contended_multi(b: &mut Bencher) {
}
let lock = Arc::new(RwLock::new(()));
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
let write = lock.write().await;
let j = tokio::try_join! {
async move { drop(write); Ok(()) },
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
};
j.unwrap();
g.bench_function("read_concurrent_multi", |b| {
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
let write = lock.write().await;
let j = tokio::try_join! {
async move { drop(write); Ok(()) },
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
};
j.unwrap();
})
})
});
}
fn read_concurrent_contended(b: &mut Bencher) {
fn read_concurrent_contended(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
@@ -114,29 +124,40 @@ fn read_concurrent_contended(b: &mut Bencher) {
}
let lock = Arc::new(RwLock::new(()));
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
let write = lock.write().await;
tokio::join! {
async move { drop(write) },
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
};
g.bench_function("read_concurrent", |b| {
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
let write = lock.write().await;
tokio::join! {
async move { drop(write) },
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
};
})
})
});
}
bencher::benchmark_group!(
sync_rwlock,
read_uncontended,
read_concurrent_uncontended,
read_concurrent_uncontended_multi,
read_concurrent_contended,
read_concurrent_contended_multi
);
fn bench_contention(c: &mut Criterion) {
let mut group = c.benchmark_group("contention");
read_concurrent_contended(&mut group);
read_concurrent_contended_multi(&mut group);
group.finish();
}
bencher::benchmark_main!(sync_rwlock);
fn bench_uncontented(c: &mut Criterion) {
let mut group = c.benchmark_group("uncontented");
read_uncontended(&mut group);
read_concurrent_uncontended(&mut group);
read_concurrent_uncontended_multi(&mut group);
group.finish();
}
criterion_group!(contention, bench_contention);
criterion_group!(uncontented, bench_uncontented);
criterion_main!(contention, uncontented);
+106 -84
View File
@@ -1,21 +1,36 @@
use bencher::Bencher;
use std::sync::Arc;
use tokio::runtime::Runtime;
use tokio::{sync::Semaphore, task};
fn uncontended(b: &mut Bencher) {
let rt = tokio::runtime::Builder::new_multi_thread()
use criterion::measurement::WallTime;
use criterion::{criterion_group, criterion_main, BenchmarkGroup, Criterion};
fn single_rt() -> Runtime {
tokio::runtime::Builder::new_current_thread()
.build()
.unwrap()
}
fn multi_rt() -> Runtime {
tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap();
.unwrap()
}
fn uncontended(g: &mut BenchmarkGroup<WallTime>) {
let rt = multi_rt();
let s = Arc::new(Semaphore::new(10));
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
for _ in 0..6 {
let permit = s.acquire().await;
drop(permit);
}
g.bench_function("multi", |b| {
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
for _ in 0..6 {
let permit = s.acquire().await;
drop(permit);
}
})
})
});
}
@@ -25,101 +40,108 @@ async fn task(s: Arc<Semaphore>) {
drop(permit);
}
fn uncontended_concurrent_multi(b: &mut Bencher) {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap();
fn uncontended_concurrent_multi(g: &mut BenchmarkGroup<WallTime>) {
let rt = multi_rt();
let s = Arc::new(Semaphore::new(10));
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
let j = tokio::try_join! {
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone()))
};
j.unwrap();
g.bench_function("concurrent_multi", |b| {
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
let j = tokio::try_join! {
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone()))
};
j.unwrap();
})
})
});
}
fn uncontended_concurrent_single(b: &mut Bencher) {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
fn uncontended_concurrent_single(g: &mut BenchmarkGroup<WallTime>) {
let rt = single_rt();
let s = Arc::new(Semaphore::new(10));
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
tokio::join! {
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone())
};
g.bench_function("concurrent_single", |b| {
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
tokio::join! {
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone())
};
})
})
});
}
fn contended_concurrent_multi(b: &mut Bencher) {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap();
fn contended_concurrent_multi(g: &mut BenchmarkGroup<WallTime>) {
let rt = multi_rt();
let s = Arc::new(Semaphore::new(5));
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
let j = tokio::try_join! {
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone()))
};
j.unwrap();
g.bench_function("concurrent_multi", |b| {
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
let j = tokio::try_join! {
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone()))
};
j.unwrap();
})
})
});
}
fn contended_concurrent_single(b: &mut Bencher) {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
fn contended_concurrent_single(g: &mut BenchmarkGroup<WallTime>) {
let rt = single_rt();
let s = Arc::new(Semaphore::new(5));
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
tokio::join! {
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone())
};
g.bench_function("concurrent_single", |b| {
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
tokio::join! {
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone())
};
})
})
});
}
bencher::benchmark_group!(
sync_semaphore,
uncontended,
uncontended_concurrent_multi,
uncontended_concurrent_single,
contended_concurrent_multi,
contended_concurrent_single
);
fn bench_contention(c: &mut Criterion) {
let mut group = c.benchmark_group("contention");
contended_concurrent_multi(&mut group);
contended_concurrent_single(&mut group);
group.finish();
}
bencher::benchmark_main!(sync_semaphore);
fn bench_uncontented(c: &mut Criterion) {
let mut group = c.benchmark_group("uncontented");
uncontended(&mut group);
uncontended_concurrent_multi(&mut group);
uncontended_concurrent_single(&mut group);
group.finish();
}
criterion_group!(contention, bench_contention);
criterion_group!(uncontented, bench_uncontented);
criterion_main!(contention, uncontented);
+85
View File
@@ -0,0 +1,85 @@
use rand::prelude::*;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use tokio::sync::{watch, Notify};
use criterion::measurement::WallTime;
use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion};
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<const N_TASKS: usize>(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
let (snd, _) = watch::channel(0i32);
let snd = Arc::new(snd);
let wg = Arc::new((AtomicU64::new(0), Notify::new()));
for n in 0..N_TASKS {
let mut rcv = snd.subscribe();
let wg = wg.clone();
let mut rng = rand::rngs::StdRng::seed_from_u64(n as u64);
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();
}
}
});
}
const N_ITERS: usize = 100;
g.bench_function(N_TASKS.to_string(), |b| {
b.iter(|| {
rt.block_on({
let snd = snd.clone();
let wg = wg.clone();
async move {
tokio::spawn(async move {
for _ in 0..N_ITERS {
assert_eq!(wg.0.fetch_add(N_TASKS as u64, Ordering::Relaxed), 0);
let _ = snd.send(black_box(42));
while wg.0.load(Ordering::Acquire) > 0 {
wg.1.notified().await;
}
}
})
.await
.unwrap();
}
});
})
});
}
fn bench_contention_resubscribe(c: &mut Criterion) {
let mut group = c.benchmark_group("contention_resubscribe");
contention_resubscribe::<10>(&mut group);
contention_resubscribe::<100>(&mut group);
contention_resubscribe::<500>(&mut group);
contention_resubscribe::<1000>(&mut group);
group.finish();
}
criterion_group!(contention, bench_contention_resubscribe);
criterion_main!(contention);
+24
View File
@@ -0,0 +1,24 @@
//! 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.
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn time_now_current_thread(c: &mut Criterion) {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_time()
.build()
.unwrap();
c.bench_function("time_now_current_thread", |b| {
b.iter(|| {
rt.block_on(async {
black_box(tokio::time::Instant::now());
})
})
});
}
criterion_group!(time_now, time_now_current_thread);
criterion_main!(time_now);
-121
View File
@@ -1,121 +0,0 @@
#!/usr/bin/env bash
set -e
USAGE="Publish a new release of a tokio crate
USAGE:
$(basename "$0") [OPTIONS] [CRATE] [VERSION]
OPTIONS:
-v, --verbose Use verbose Cargo output
-d, --dry-run Perform a dry run (do not publish or tag the release)
-h, --help Show this help text and exit"
DRY_RUN=""
VERBOSE=""
err() {
echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2;
}
status() {
WIDTH=12
printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2"
}
verify() {
status "Verifying" "if $CRATE v$VERSION can be released"
ACTUAL=$(cargo pkgid | sed -n 's/.*#\(.*\)/\1/p')
if [ "$ACTUAL" != "$VERSION" ]; then
err "expected to release version $VERSION, but Cargo.toml contained $ACTUAL"
exit 1
fi
if git tag -l | grep -Fxq "$TAG" ; then
err "git tag \`$TAG\` already exists"
exit 1
fi
PATH_DEPS=$(grep -F "path = \"" Cargo.toml | sed -e 's/^/ /')
if [ -n "$PATH_DEPS" ]; then
err "crate \`$CRATE\` contained path dependencies:\n$PATH_DEPS"
echo "path dependencies must be removed prior to release"
exit 1
fi
}
release() {
status "Releasing" "$CRATE v$VERSION"
cargo package $VERBOSE
cargo publish $VERBOSE $DRY_RUN
status "Tagging" "$TAG"
if [ -n "$DRY_RUN" ]; then
echo "# git tag $TAG && git push --tags"
else
git tag "$TAG" && git push --tags
fi
}
while [[ $# -gt 0 ]]
do
case "$1" in
-h|--help)
echo "$USAGE"
exit 0
;;
-v|--verbose)
VERBOSE="--verbose"
set +x
shift
;;
-d|--dry-run)
DRY_RUN="--dry-run"
shift
;;
-*)
err "unknown flag \"$1\""
echo "$USAGE"
exit 1
;;
*) # crate or version
if [ -z "$CRATE" ]; then
CRATE="$1"
elif [ -z "$VERSION" ]; then
VERSION="$1"
else
err "unknown positional argument \"$1\""
echo "$USAGE"
exit 1
fi
shift
;;
esac
done
# set -- "${POSITIONAL[@]}"
if [ -z "$VERSION" ]; then
err "no version specified!"
HELP=1
fi
if [ -n "$CRATE" ]; then
TAG="$CRATE-$VERSION"
else
err "no crate specified!"
HELP=1
fi
if [ -n "$HELP" ]; then
echo "$USAGE"
exit 1
fi
if [ -d "$CRATE" ]; then
(cd "$CRATE" && verify && release )
else
err "no such crate \"$CRATE\""
exit 1
fi
-118
View File
@@ -1,118 +0,0 @@
#!/usr/bin/env bash
set -e
USAGE="Update links to docs.rs in a tokio crate
USAGE:
$(basename "$0") [OPTIONS] [CRATE] [VERSION]
OPTIONS:
-d, --dry-run Perform a dry run (do not modify any file)
-h, --help Show this help text and exit"
err() {
echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2;
}
status() {
WIDTH=12
printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2"
}
c1grep() { grep "$@" || test $? = 1; }
update_versions_in_doc() {
# Print what is being/would be done
if [ -n "$DRY_RUN" ]; then
local MSG="Would change:"
else
local MSG="Updating:"
fi
git grep -lr "docs.rs/$CRATE/" \
| xargs sed --quiet \
-E "s|docs.rs/$CRATE/[0-9.]+|docs.rs/$CRATE/$VERSION|gp" \
| sed -e "s/^/$MSG /"
# Apply changes if not in dry run
if [ -z "$DRY_RUN" ]; then
git grep -lr "docs.rs/$CRATE/" \
| xargs sed -i \
-E "s|docs.rs/$CRATE/[0-9.]+|docs.rs/$CRATE/$VERSION|g"
fi
}
update() {
update_versions_in_doc
}
show_outdated() {
OUTDATED=$(git grep -rn "docs.rs/$CRATE/" \
| c1grep -v "$VERSION" \
| sed -e 's/^/ - /')
if [[ -n "$OUTDATED" ]]; then
echo "Found the following links to docs.rs with an outdated version:"
echo "$OUTDATED"
echo
else
echo "Nothing to do."
exit 1
fi
}
while [[ $# -gt 0 ]]
do
case "$1" in
-h|--help)
echo "$USAGE"
exit 0
;;
-d|--dry-run)
DRY_RUN="--dry-run"
shift
;;
-*)
err "unknown flag \"$1\""
echo "$USAGE"
exit 1
;;
*) # crate or version
if [ -z "$CRATE" ]; then
CRATE="$1"
elif [ -z "$VERSION" ]; then
VERSION="$1"
else
err "unknown positional argument \"$1\""
echo "$USAGE"
exit 1
fi
shift
;;
esac
done
# set -- "${POSITIONAL[@]}"
if [ -z "$VERSION" ]; then
err "no version specified!"
HELP=1
fi
if [ -n "$CRATE" ]; then
TAG="$CRATE-$VERSION"
else
err "no crate specified!"
HELP=1
fi
if [ -n "$HELP" ]; then
echo "$USAGE"
exit 1
fi
if [ -d "$CRATE" ]; then
# Does not cd in order to update everywhere
show_outdated && update
else
err "no such crate \"$CRATE\""
exit 1
fi
+6 -2
View File
@@ -2,7 +2,7 @@
name = "examples"
version = "0.0.0"
publish = false
edition = "2018"
edition = "2021"
# If you copy one of the examples into a new project, you should be using
# [dependencies] instead, and delete the **path**.
@@ -25,7 +25,7 @@ once_cell = "1.5.2"
rand = "0.8.3"
[target.'cfg(windows)'.dev-dependencies.windows-sys]
version = "0.42.0"
version = "0.48"
[[example]]
name = "chat"
@@ -90,3 +90,7 @@ path = "named-pipe-ready.rs"
[[example]]
name = "named-pipe-multi-client"
path = "named-pipe-multi-client.rs"
[[example]]
name = "dump"
path = "dump.rs"
+90
View File
@@ -0,0 +1,90 @@
//! This example demonstrates tokio's experimental task dumping functionality.
//! This application deadlocks. Input CTRL+C to display traces of each task, or
//! input CTRL+C twice within 1 second to quit.
#[cfg(all(
tokio_unstable,
tokio_taskdump,
target_os = "linux",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
))]
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
use std::sync::Arc;
use tokio::sync::Barrier;
#[inline(never)]
async fn a(barrier: Arc<Barrier>) {
b(barrier).await
}
#[inline(never)]
async fn b(barrier: Arc<Barrier>) {
c(barrier).await
}
#[inline(never)]
async fn c(barrier: Arc<Barrier>) {
barrier.wait().await;
}
// Prints a task dump upon receipt of CTRL+C, or returns if CTRL+C is
// inputted twice within a second.
async fn dump_or_quit() {
use tokio::time::{timeout, Duration, Instant};
let handle = tokio::runtime::Handle::current();
let mut last_signal: Option<Instant> = None;
// wait for CTRL+C
while let Ok(_) = tokio::signal::ctrl_c().await {
// exit if a CTRL+C is inputted twice within 1 second
if let Some(time_since_last_signal) = last_signal.map(|i| i.elapsed()) {
if time_since_last_signal < Duration::from_secs(1) {
return;
}
}
last_signal = Some(Instant::now());
// capture a dump, and print each trace
println!("{:-<80}", "");
if let Ok(dump) = timeout(Duration::from_secs(2), handle.dump()).await {
for (i, task) in dump.tasks().iter().enumerate() {
let trace = task.trace();
println!("TASK {i}:");
println!("{trace}\n");
}
} else {
println!("Task dumping timed out. Use a native debugger (like gdb) to debug the deadlock.");
}
println!("{:-<80}", "");
println!("Input CTRL+C twice within 1 second to exit.");
}
}
println!("This program has a deadlock.");
println!("Input CTRL+C to print a task dump.");
println!("Input CTRL+C twice within 1 second to exit.");
// oops! this barrier waits for one more task than will ever come.
let barrier = Arc::new(Barrier::new(3));
let task_1 = tokio::spawn(a(barrier.clone()));
let task_2 = tokio::spawn(a(barrier));
tokio::select!(
_ = dump_or_quit() => {},
_ = task_1 => {},
_ = task_2 => {},
);
Ok(())
}
#[cfg(not(all(
tokio_unstable,
tokio_taskdump,
target_os = "linux",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
)))]
fn main() {
println!("task dumps are not available")
}
+12 -30
View File
@@ -22,8 +22,7 @@
#![warn(rust_2018_idioms)]
use tokio::io;
use tokio::io::AsyncWriteExt;
use tokio::io::copy_bidirectional;
use tokio::net::{TcpListener, TcpStream};
use futures::FutureExt;
@@ -44,36 +43,19 @@ async fn main() -> Result<(), Box<dyn Error>> {
let listener = TcpListener::bind(listen_addr).await?;
while let Ok((inbound, _)) = listener.accept().await {
let transfer = transfer(inbound, server_addr.clone()).map(|r| {
if let Err(e) = r {
println!("Failed to transfer; error={}", e);
}
});
while let Ok((mut inbound, _)) = listener.accept().await {
let mut outbound = TcpStream::connect(server_addr.clone()).await?;
tokio::spawn(transfer);
tokio::spawn(async move {
copy_bidirectional(&mut inbound, &mut outbound)
.map(|r| {
if let Err(e) = r {
println!("Failed to transfer; error={}", e);
}
})
.await
});
}
Ok(())
}
async fn transfer(mut inbound: TcpStream, proxy_addr: String) -> Result<(), Box<dyn Error>> {
let mut outbound = TcpStream::connect(proxy_addr).await?;
let (mut ri, mut wi) = inbound.split();
let (mut ro, mut wo) = outbound.split();
let client_to_server = async {
io::copy(&mut ri, &mut wo).await?;
wo.shutdown().await
};
let server_to_client = async {
io::copy(&mut ro, &mut wi).await?;
wi.shutdown().await
};
tokio::try_join!(client_to_server, server_to_client)?;
Ok(())
}
+5 -2
View File
@@ -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);
+2 -1
View File
@@ -8,8 +8,9 @@
RUSTDOCFLAGS="""
--cfg docsrs \
--cfg tokio_unstable \
--cfg tokio_taskdump \
"""
RUSTFLAGS="--cfg tokio_unstable --cfg docsrs"
RUSTFLAGS="--cfg tokio_unstable --cfg tokio_taskdump --cfg docsrs"
[[redirects]]
from = "/"
+1 -1
View File
@@ -2,7 +2,7 @@
name = "stress-test"
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
edition = "2018"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -2,7 +2,7 @@
name = "tests-build"
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
edition = "2018"
edition = "2021"
publish = false
[features]
@@ -36,13 +36,10 @@ async fn test_worker_threads_not_int() {}
async fn test_worker_threads_and_current_thread() {}
#[tokio::test(crate = 456)]
async fn test_crate_not_ident_int() {}
async fn test_crate_not_path_int() {}
#[tokio::test(crate = "456")]
async fn test_crate_not_ident_invalid() {}
#[tokio::test(crate = "abc::edf")]
async fn test_crate_not_ident_path() {}
async fn test_crate_not_path_invalid() {}
#[tokio::test]
#[test]
@@ -64,34 +64,28 @@ error: The `worker_threads` option requires the `multi_thread` runtime flavor. U
35 | #[tokio::test(flavor = "current_thread", worker_threads = 4)]
| ^
error: Failed to parse value of `crate` as ident.
error: Failed to parse value of `crate` as path.
--> $DIR/macros_invalid_input.rs:38:23
|
38 | #[tokio::test(crate = 456)]
| ^^^
error: Failed to parse value of `crate` as ident: "456"
error: Failed to parse value of `crate` as path: "456"
--> $DIR/macros_invalid_input.rs:41:23
|
41 | #[tokio::test(crate = "456")]
| ^^^^^
error: Failed to parse value of `crate` as ident: "abc::edf"
--> $DIR/macros_invalid_input.rs:44:23
|
44 | #[tokio::test(crate = "abc::edf")]
| ^^^^^^^^^^
error: second test attribute is supplied
--> $DIR/macros_invalid_input.rs:48:1
--> $DIR/macros_invalid_input.rs:45:1
|
48 | #[test]
45 | #[test]
| ^^^^^^^
error: duplicated attribute
--> $DIR/macros_invalid_input.rs:48:1
--> $DIR/macros_invalid_input.rs:45:1
|
48 | #[test]
45 | #[test]
| ^^^^^^^
|
note: the lint level is defined here
@@ -1,33 +1,33 @@
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:5:5
--> tests/fail/macros_type_mismatch.rs:5:5
|
4 | async fn missing_semicolon_or_return_type() {
| - help: a return type might be missing here: `-> _`
5 | Ok(())
| ^^^^^^ expected `()`, found enum `Result`
| ^^^^^^ expected `()`, found `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:10:5
--> tests/fail/macros_type_mismatch.rs:10:5
|
9 | async fn missing_return_type() {
| - help: a return type might be missing here: `-> _`
10 | return Ok(());
| ^^^^^^^^^^^^^^ expected `()`, found enum `Result`
| ^^^^^^^^^^^^^^ expected `()`, found `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:23:5
--> tests/fail/macros_type_mismatch.rs:23:5
|
14 | async fn extra_semicolon() -> Result<(), ()> {
| -------------- expected `Result<(), ()>` because of return type
...
23 | Ok(());
| ^^^^^^^ expected enum `Result`, found `()`
| ^^^^^^^ expected `Result<(), ()>`, found `()`
|
= note: expected enum `Result<(), ()>`
found unit type `()`
@@ -38,7 +38,7 @@ help: try adding an expression at the end of the block
|
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:32:5
--> tests/fail/macros_type_mismatch.rs:32:5
|
30 | async fn issue_4635() {
| - help: try adding a return type: `-> i32`
+1 -1
View File
@@ -2,7 +2,7 @@
name = "tests-integration"
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
edition = "2018"
edition = "2021"
publish = false
[[bin]]
-1
View File
@@ -7,7 +7,6 @@ use tokio::process::{Child, Command};
use tokio_test::assert_ok;
use futures::future::{self, FutureExt};
use std::convert::TryInto;
use std::env;
use std::io;
use std::process::{ExitStatus, Stdio};
+31
View File
@@ -1,3 +1,34 @@
# 2.2.0 (November 19th, 2023)
### Changed
- use `::core` qualified imports instead of `::std` inside `tokio::test` macro ([#5973])
[#5973]: https://github.com/tokio-rs/tokio/pull/5973
# 2.1.0 (April 25th, 2023)
- macros: fix typo in `#[tokio::test]` docs ([#5636])
- macros: make entrypoints more efficient ([#5621])
[#5621]: https://github.com/tokio-rs/tokio/pull/5621
[#5636]: https://github.com/tokio-rs/tokio/pull/5636
# 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])
+5 -5
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"
edition = "2018"
rust-version = "1.49"
version = "2.2.0"
edition = "2021"
rust-version = "1.63"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -22,9 +22,9 @@ proc-macro = true
[features]
[dependencies]
proc-macro2 = "1.0.7"
proc-macro2 = "1.0.60"
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"] }
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2022 Tokio Contributors
Copyright (c) 2023 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+180 -83
View File
@@ -1,10 +1,10 @@
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use proc_macro2::{Span, TokenStream, TokenTree};
use quote::{quote, quote_spanned, ToTokens};
use syn::parse::Parser;
use syn::parse::{Parse, ParseStream, Parser};
use syn::{braced, Attribute, Ident, Path, Signature, Visibility};
// 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(())
}
@@ -126,22 +126,22 @@ impl Configuration {
}
fn build(&self) -> Result<FinalConfig, syn::Error> {
let flavor = self.flavor.unwrap_or(self.default_flavor);
use RuntimeFlavor::*;
use RuntimeFlavor as F;
let flavor = self.flavor.unwrap_or(self.default_flavor);
let worker_threads = match (flavor, self.worker_threads) {
(CurrentThread, Some((_, worker_threads_span))) => {
(F::CurrentThread, Some((_, worker_threads_span))) => {
let msg = format!(
"The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[{}(flavor = \"multi_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(worker_threads_span, msg));
}
(CurrentThread, None) => None,
(Threaded, worker_threads) if self.rt_multi_thread_available => {
(F::CurrentThread, None) => None,
(F::Threaded, worker_threads) if self.rt_multi_thread_available => {
worker_threads.map(|(val, _span)| val)
}
(Threaded, _) => {
(F::Threaded, _) => {
let msg = if self.flavor.is_none() {
"The default runtime flavor is `multi_thread`, but the `rt-multi-thread` feature is disabled."
} else {
@@ -152,14 +152,14 @@ impl Configuration {
};
let start_paused = match (flavor, self.start_paused) {
(Threaded, Some((_, start_paused_span))) => {
(F::Threaded, Some((_, start_paused_span))) => {
let msg = format!(
"The `start_paused` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(start_paused_span, msg));
}
(CurrentThread, Some((start_paused, _))) => Some(start_paused),
(F::CurrentThread, Some((start_paused, _))) => Some(start_paused),
(_, None) => None,
};
@@ -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),
)),
}
}
@@ -231,7 +230,7 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Erro
}
fn build_config(
input: syn::ItemFn,
input: &ItemFn,
args: AttributeArgs,
is_test: bool,
rt_multi_thread: bool,
@@ -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"))?
@@ -333,18 +324,13 @@ fn build_config(
config.build()
}
fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> TokenStream {
fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream {
input.sig.asyncness = None;
// If type mismatch occurs, the current rustc points to the last statement.
let (last_stmt_start_span, last_stmt_end_span) = {
let mut last_stmt = input
.block
.stmts
.last()
.map(ToTokens::into_token_stream)
.unwrap_or_default()
.into_iter();
let mut last_stmt = input.stmts.last().cloned().unwrap_or_default().into_iter();
// `Span` on stable Rust has a limitation that only points to the first
// token, not the whole tokens. We can work around this limitation by
// using the first/last span of the tokens like
@@ -354,23 +340,24 @@ 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 {
rt = quote! { #rt.worker_threads(#v) };
rt = quote_spanned! {last_stmt_start_span=> #rt.worker_threads(#v) };
}
if let Some(v) = config.start_paused {
rt = quote! { #rt.start_paused(#v) };
rt = quote_spanned! {last_stmt_start_span=> #rt.start_paused(#v) };
}
let header = if is_test {
@@ -381,10 +368,8 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
quote! {}
};
let body = &input.block;
let brace_token = input.block.brace_token;
let body_ident = quote! { body };
let block_expr = quote_spanned! {last_stmt_end_span=>
let last_block = quote_spanned! {last_stmt_end_span=>
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
{
return #rt
@@ -395,6 +380,8 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
}
};
let body = input.body();
// For test functions pin the body to the stack and use `Pin<&mut dyn
// Future>` to reduce the amount of `Runtime::block_on` (and related
// functions) copies we generate during compilation due to the generic
@@ -414,8 +401,8 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
};
quote! {
let body = async #body;
#crate_ident::pin!(body);
let body: ::std::pin::Pin<&mut dyn ::std::future::Future<Output = #output_type>> = body;
#crate_path::pin!(body);
let body: ::core::pin::Pin<&mut dyn ::core::future::Future<Output = #output_type>> = body;
}
} else {
quote! {
@@ -423,25 +410,11 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
}
};
input.block = syn::parse2(quote! {
{
#body
#block_expr
}
})
.expect("Parsing failure");
input.block.brace_token = brace_token;
let result = quote! {
#header
#input
};
result.into()
input.into_tokens(header, body, last_block)
}
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
tokens.extend(TokenStream::from(error.into_compile_error()));
tokens.extend(error.into_compile_error());
tokens
}
@@ -450,7 +423,7 @@ pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
// If any of the steps for this macro fail, we still want to expand to an item that is as close
// to the expected output as possible. This helps out IDEs such that completions and other
// related features keep working.
let input: syn::ItemFn = match syn::parse(item.clone()) {
let input: ItemFn = match syn::parse2(item.clone()) {
Ok(it) => it,
Err(e) => return token_stream_with_error(item, e),
};
@@ -460,8 +433,8 @@ pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
Err(syn::Error::new_spanned(&input.sig.ident, msg))
} else {
AttributeArgs::parse_terminated
.parse(args)
.and_then(|args| build_config(input.clone(), args, false, rt_multi_thread))
.parse2(args)
.and_then(|args| build_config(&input, args, false, rt_multi_thread))
};
match config {
@@ -474,17 +447,17 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
// If any of the steps for this macro fail, we still want to expand to an item that is as close
// to the expected output as possible. This helps out IDEs such that completions and other
// related features keep working.
let input: syn::ItemFn = match syn::parse(item.clone()) {
let input: ItemFn = match syn::parse2(item.clone()) {
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().find(|attr| attr.meta.path().is_ident("test")) {
let msg = "second test attribute is supplied";
Err(syn::Error::new_spanned(attr, msg))
} else {
AttributeArgs::parse_terminated
.parse(args)
.and_then(|args| build_config(input.clone(), args, true, rt_multi_thread))
.parse2(args)
.and_then(|args| build_config(&input, args, true, rt_multi_thread))
};
match config {
@@ -492,3 +465,127 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
Err(e) => token_stream_with_error(parse_knobs(input, true, DEFAULT_ERROR_CONFIG), e),
}
}
struct ItemFn {
outer_attrs: Vec<Attribute>,
vis: Visibility,
sig: Signature,
brace_token: syn::token::Brace,
inner_attrs: Vec<Attribute>,
stmts: Vec<proc_macro2::TokenStream>,
}
impl ItemFn {
/// Access all attributes of the function item.
fn attrs(&self) -> impl Iterator<Item = &Attribute> {
self.outer_attrs.iter().chain(self.inner_attrs.iter())
}
/// Get the body of the function item in a manner so that it can be
/// conveniently used with the `quote!` macro.
fn body(&self) -> Body<'_> {
Body {
brace_token: self.brace_token,
stmts: &self.stmts,
}
}
/// Convert our local function item into a token stream.
fn into_tokens(
self,
header: proc_macro2::TokenStream,
body: proc_macro2::TokenStream,
last_block: proc_macro2::TokenStream,
) -> TokenStream {
let mut tokens = proc_macro2::TokenStream::new();
header.to_tokens(&mut tokens);
// Outer attributes are simply streamed as-is.
for attr in self.outer_attrs {
attr.to_tokens(&mut tokens);
}
// Inner attributes require extra care, since they're not supported on
// blocks (which is what we're expanded into) we instead lift them
// outside of the function. This matches the behaviour of `syn`.
for mut attr in self.inner_attrs {
attr.style = syn::AttrStyle::Outer;
attr.to_tokens(&mut tokens);
}
self.vis.to_tokens(&mut tokens);
self.sig.to_tokens(&mut tokens);
self.brace_token.surround(&mut tokens, |tokens| {
body.to_tokens(tokens);
last_block.to_tokens(tokens);
});
tokens
}
}
impl Parse for ItemFn {
#[inline]
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
// This parse implementation has been largely lifted from `syn`, with
// the exception of:
// * We don't have access to the plumbing necessary to parse inner
// attributes in-place.
// * We do our own statements parsing to avoid recursively parsing
// entire statements and only look for the parts we're interested in.
let outer_attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let sig: Signature = input.parse()?;
let content;
let brace_token = braced!(content in input);
let inner_attrs = Attribute::parse_inner(&content)?;
let mut buf = proc_macro2::TokenStream::new();
let mut stmts = Vec::new();
while !content.is_empty() {
if let Some(semi) = content.parse::<Option<syn::Token![;]>>()? {
semi.to_tokens(&mut buf);
stmts.push(buf);
buf = proc_macro2::TokenStream::new();
continue;
}
// Parse a single token tree and extend our current buffer with it.
// This avoids parsing the entire content of the sub-tree.
buf.extend([content.parse::<TokenTree>()?]);
}
if !buf.is_empty() {
stmts.push(buf);
}
Ok(Self {
outer_attrs,
vis,
sig,
brace_token,
inner_attrs,
stmts,
})
}
}
struct Body<'a> {
brace_token: syn::token::Brace,
// Statements, with terminating `;`.
stmts: &'a [TokenStream],
}
impl ToTokens for Body<'_> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
self.brace_token.surround(tokens, |tokens| {
for stmt in self.stmts {
stmt.to_tokens(tokens);
}
});
}
}
+13 -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
@@ -197,12 +204,12 @@ use proc_macro::TokenStream;
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args, item, true)
entry::main(args.into(), item.into(), true).into()
}
/// Marks async function to be executed by selected runtime. This macro helps set up a `Runtime`
/// without requiring the user to use [Runtime](../tokio/runtime/struct.Runtime.html) or
/// [Builder](../tokio/runtime/struct.builder.html) directly.
/// [Builder](../tokio/runtime/struct.Builder.html) directly.
///
/// ## Function arguments:
///
@@ -262,7 +269,7 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args, item, false)
entry::main(args.into(), item.into(), false).into()
}
/// Marks async function to be executed by runtime, suitable to test environment.
@@ -288,8 +295,7 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// ```
///
/// The `worker_threads` option configures the number of worker threads, and
/// defaults to the number of cpus on the system. This is the default
/// flavor.
/// defaults to the number of cpus on the system.
///
/// Note: The multi-threaded runtime requires the `rt-multi-thread` feature
/// flag.
@@ -420,7 +426,7 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// ```
#[proc_macro_attribute]
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, true)
entry::test(args.into(), item.into(), true).into()
}
/// Marks async function to be executed by runtime, suitable to test environment
@@ -435,7 +441,7 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
/// ```
#[proc_macro_attribute]
pub fn test_rt(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, false)
entry::test(args.into(), item.into(), false).into()
}
/// Always fails with the error message below.
+9 -10
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) => {}
@@ -74,36 +73,36 @@ fn clean_pattern(pat: &mut syn::Pat) {
}
}
syn::Pat::Or(or) => {
for case in or.cases.iter_mut() {
for case in &mut or.cases {
clean_pattern(case);
}
}
syn::Pat::Slice(slice) => {
for elem in slice.elems.iter_mut() {
for elem in &mut slice.elems {
clean_pattern(elem);
}
}
syn::Pat::Struct(struct_pat) => {
for field in struct_pat.fields.iter_mut() {
for field in &mut struct_pat.fields {
clean_pattern(&mut field.pat);
}
}
syn::Pat::Tuple(tuple) => {
for elem in tuple.elems.iter_mut() {
for elem in &mut tuple.elems {
clean_pattern(elem);
}
}
syn::Pat::TupleStruct(tuple) => {
for elem in tuple.pat.elems.iter_mut() {
for elem in &mut tuple.elems {
clean_pattern(elem);
}
}
syn::Pat::Reference(reference) => {
reference.mutability = None;
clean_pattern(&mut *reference.pat);
clean_pattern(&mut reference.pat);
}
syn::Pat::Type(type_pat) => {
clean_pattern(&mut *type_pat.pat);
clean_pattern(&mut type_pat.pat);
}
_ => {}
}
+29
View File
@@ -1,3 +1,32 @@
# 0.1.14 (April 26th, 2023)
This bugfix release bumps the minimum version of Tokio to 1.15, which is
necessary for `timeout_repeating` to compile. ([#5657])
[#5657]: https://github.com/tokio-rs/tokio/pull/5657
# 0.1.13 (April 25th, 2023)
This release bumps the MSRV of tokio-stream to 1.56.
- stream: add "full" feature flag ([#5639])
- stream: add `StreamExt::timeout_repeating` ([#5577])
- stream: add `StreamNotifyClose` ([#4851])
[#4851]: https://github.com/tokio-rs/tokio/pull/4851
[#5577]: https://github.com/tokio-rs/tokio/pull/5577
[#5639]: https://github.com/tokio-rs/tokio/pull/5639
# 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])
+15 -8
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.14"
edition = "2021"
rust-version = "1.63"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -18,6 +18,16 @@ categories = ["asynchronous"]
[features]
default = ["time"]
full = [
"time",
"net",
"io-util",
"fs",
"sync",
"signal"
]
time = ["tokio/time"]
net = ["tokio/net"]
io-util = ["tokio/io-util"]
@@ -27,8 +37,8 @@ signal = ["tokio/signal"]
[dependencies]
futures-core = { version = "0.3.0" }
pin-project-lite = "0.2.0"
tokio = { version = "1.8.0", path = "../tokio", features = ["sync"] }
pin-project-lite = "0.2.11"
tokio = { version = "1.15.0", path = "../tokio", features = ["sync"] }
tokio-util = { version = "0.7.0", path = "../tokio-util", optional = true }
[dev-dependencies]
@@ -38,9 +48,6 @@ parking_lot = "0.12.0"
tokio-test = { path = "../tokio-test" }
futures = { version = "0.3", default-features = false }
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
proptest = "1"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2022 Tokio Contributors
Copyright (c) 2023 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+4
View File
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
+29
View File
@@ -0,0 +1,29 @@
[package]
name = "tokio-stream-fuzz"
version = "0.0.0"
publish = false
edition = "2021"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
tokio-test = { path = "../../tokio-test" }
[dependencies.tokio-stream]
path = ".."
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[profile.release]
debug = 1
[[bin]]
name = "fuzz_stream_map"
path = "fuzz_targets/fuzz_stream_map.rs"
test = false
doc = false
@@ -0,0 +1,72 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use std::pin::Pin;
use tokio_stream::{self as stream, Stream, StreamMap};
use tokio_test::{assert_pending, assert_ready, task};
macro_rules! assert_ready_none {
($($t:tt)*) => {
match assert_ready!($($t)*) {
None => {}
Some(v) => panic!("expected `None`, got `Some({:?})`", v),
}
};
}
fn pin_box<T: Stream<Item = U> + 'static, U>(s: T) -> Pin<Box<dyn Stream<Item = U>>> {
Box::pin(s)
}
fuzz_target!(|data: [bool; 64]| {
use std::task::{Context, Poll};
struct DidPoll<T> {
did_poll: bool,
inner: T,
}
impl<T: Stream + Unpin> Stream for DidPoll<T> {
type Item = T::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T::Item>> {
self.did_poll = true;
Pin::new(&mut self.inner).poll_next(cx)
}
}
// Try the test with each possible length.
for len in 0..data.len() {
let mut map = task::spawn(StreamMap::new());
let mut expect = 0;
for (i, is_empty) in data[..len].iter().copied().enumerate() {
let inner = if is_empty {
pin_box(stream::empty::<()>())
} else {
expect += 1;
pin_box(stream::pending::<()>())
};
let stream = DidPoll {
did_poll: false,
inner,
};
map.insert(i, stream);
}
if expect == 0 {
assert_ready_none!(map.poll_next());
} else {
assert_pending!(map.poll_next());
assert_eq!(expect, map.values().count());
for stream in map.values() {
assert!(stream.did_poll);
}
}
}
});
+9 -6
View File
@@ -63,12 +63,12 @@
//! [`tokio-util`] provides the [`StreamReader`] and [`ReaderStream`]
//! types when the io feature is enabled.
//!
//! [`tokio-util`]: https://docs.rs/tokio-util/0.4/tokio_util/codec/index.html
//! [`tokio::io`]: https://docs.rs/tokio/1.0/tokio/io/index.html
//! [`AsyncRead`]: https://docs.rs/tokio/1.0/tokio/io/trait.AsyncRead.html
//! [`AsyncWrite`]: https://docs.rs/tokio/1.0/tokio/io/trait.AsyncWrite.html
//! [`ReaderStream`]: https://docs.rs/tokio-util/0.4/tokio_util/io/struct.ReaderStream.html
//! [`StreamReader`]: https://docs.rs/tokio-util/0.4/tokio_util/io/struct.StreamReader.html
//! [`tokio-util`]: https://docs.rs/tokio-util/latest/tokio_util/codec/index.html
//! [`tokio::io`]: https://docs.rs/tokio/latest/tokio/io/index.html
//! [`AsyncRead`]: https://docs.rs/tokio/latest/tokio/io/trait.AsyncRead.html
//! [`AsyncWrite`]: https://docs.rs/tokio/latest/tokio/io/trait.AsyncWrite.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
#[macro_use]
mod macros;
@@ -96,5 +96,8 @@ pub use pending::{pending, Pending};
mod stream_map;
pub use stream_map::StreamMap;
mod stream_close;
pub use stream_close::StreamNotifyClose;
#[doc(no_inline)]
pub use futures_core::Stream;
+1 -1
View File
@@ -35,7 +35,7 @@ impl<I> Unpin for Once<I> {}
/// ```
pub fn once<T>(value: T) -> Once<T> {
Once {
iter: crate::iter(Some(value).into_iter()),
iter: crate::iter(Some(value)),
}
}
+93
View File
@@ -0,0 +1,93 @@
use crate::Stream;
use pin_project_lite::pin_project;
use std::pin::Pin;
use std::task::{Context, Poll};
pin_project! {
/// A `Stream` that wraps the values in an `Option`.
///
/// Whenever the wrapped stream yields an item, this stream yields that item
/// wrapped in `Some`. When the inner stream ends, then this stream first
/// yields a `None` item, and then this stream will also end.
///
/// # Example
///
/// Using `StreamNotifyClose` to handle closed streams with `StreamMap`.
///
/// ```
/// use tokio_stream::{StreamExt, StreamMap, StreamNotifyClose};
///
/// #[tokio::main]
/// async fn main() {
/// let mut map = StreamMap::new();
/// let stream = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// let stream2 = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// map.insert(0, stream);
/// map.insert(1, stream2);
/// while let Some((key, val)) = map.next().await {
/// match val {
/// Some(val) => println!("got {val:?} from stream {key:?}"),
/// None => println!("stream {key:?} closed"),
/// }
/// }
/// }
/// ```
#[must_use = "streams do nothing unless polled"]
pub struct StreamNotifyClose<S> {
#[pin]
inner: Option<S>,
}
}
impl<S> StreamNotifyClose<S> {
/// Create a new `StreamNotifyClose`.
pub fn new(stream: S) -> Self {
Self {
inner: Some(stream),
}
}
/// Get back the inner `Stream`.
///
/// Returns `None` if the stream has reached its end.
pub fn into_inner(self) -> Option<S> {
self.inner
}
}
impl<S> Stream for StreamNotifyClose<S>
where
S: Stream,
{
type Item = Option<S::Item>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
// We can't invoke poll_next after it ended, so we unset the inner stream as a marker.
match self
.as_mut()
.project()
.inner
.as_pin_mut()
.map(|stream| S::poll_next(stream, cx))
{
Some(Poll::Ready(Some(item))) => Poll::Ready(Some(Some(item))),
Some(Poll::Ready(None)) => {
self.project().inner.set(None);
Poll::Ready(Some(None))
}
Some(Poll::Pending) => Poll::Pending,
None => Poll::Ready(None),
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if let Some(inner) = &self.inner {
// We always return +1 because when there's stream there's atleast one more item.
let (l, u) = inner.size_hint();
(l.saturating_add(1), u.and_then(|u| u.checked_add(1)))
} else {
(0, Some(0))
}
}
}
+144 -6
View File
@@ -55,10 +55,15 @@ use then::Then;
mod try_next;
use try_next::TryNext;
mod peekable;
use peekable::Peekable;
cfg_time! {
pub(crate) mod timeout;
pub(crate) mod timeout_repeating;
use timeout::Timeout;
use tokio::time::Duration;
use timeout_repeating::TimeoutRepeating;
use tokio::time::{Duration, Interval};
mod throttle;
use throttle::{throttle, Throttle};
mod chunks_timeout;
@@ -846,8 +851,7 @@ pub trait StreamExt: Stream {
///
/// `collect` streams all values, awaiting as needed. Values are pushed into
/// a collection. A number of different target collection types are
/// supported, including [`Vec`](std::vec::Vec),
/// [`String`](std::string::String), and [`Bytes`].
/// supported, including [`Vec`], [`String`], and [`Bytes`].
///
/// [`Bytes`]: https://docs.rs/bytes/0.6.0/bytes/struct.Bytes.html
///
@@ -924,7 +928,9 @@ pub trait StreamExt: Stream {
/// If the wrapped stream yields a value before the deadline is reached, the
/// value is returned. Otherwise, an error is returned. The caller may decide
/// to continue consuming the stream and will eventually get the next source
/// stream value once it becomes available.
/// stream value once it becomes available. See
/// [`timeout_repeating`](StreamExt::timeout_repeating) for an alternative
/// where the timeouts will repeat.
///
/// # Notes
///
@@ -971,7 +977,26 @@ pub trait StreamExt: Stream {
/// assert_eq!(int_stream.try_next().await, Ok(None));
/// # }
/// ```
#[cfg(all(feature = "time"))]
///
/// Once a timeout error is received, no further events will be received
/// unless the wrapped stream yields a value (timeouts do not repeat).
///
/// ```
/// # #[tokio::main(flavor = "current_thread", start_paused = true)]
/// # async fn main() {
/// use tokio_stream::{StreamExt, wrappers::IntervalStream};
/// use std::time::Duration;
/// let interval_stream = IntervalStream::new(tokio::time::interval(Duration::from_millis(100)));
/// let timeout_stream = interval_stream.timeout(Duration::from_millis(10));
/// tokio::pin!(timeout_stream);
///
/// // Only one timeout will be received between values in the source stream.
/// assert!(timeout_stream.try_next().await.is_ok());
/// assert!(timeout_stream.try_next().await.is_err(), "expected one timeout");
/// assert!(timeout_stream.try_next().await.is_ok(), "expected no more timeouts");
/// # }
/// ```
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
fn timeout(self, duration: Duration) -> Timeout<Self>
where
@@ -980,6 +1005,94 @@ pub trait StreamExt: Stream {
Timeout::new(self, duration)
}
/// Applies a per-item timeout to the passed stream.
///
/// `timeout_repeating()` takes an [`Interval`] that controls the time each
/// element of the stream has to complete before timing out.
///
/// If the wrapped stream yields a value before the deadline is reached, the
/// value is returned. Otherwise, an error is returned. The caller may decide
/// to continue consuming the stream and will eventually get the next source
/// stream value once it becomes available. Unlike `timeout()`, if no value
/// becomes available before the deadline is reached, additional errors are
/// returned at the specified interval. See [`timeout`](StreamExt::timeout)
/// for an alternative where the timeouts do not repeat.
///
/// # Notes
///
/// This function consumes the stream passed into it and returns a
/// wrapped version of it.
///
/// Polling the returned stream will continue to poll the inner stream even
/// if one or more items time out.
///
/// # Examples
///
/// Suppose we have a stream `int_stream` that yields 3 numbers (1, 2, 3):
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
/// use std::time::Duration;
/// # let int_stream = stream::iter(1..=3);
///
/// let int_stream = int_stream.timeout_repeating(tokio::time::interval(Duration::from_secs(1)));
/// tokio::pin!(int_stream);
///
/// // When no items time out, we get the 3 elements in succession:
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(2)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(3)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
///
/// // If the second item times out, we get an error and continue polling the stream:
/// # let mut int_stream = stream::iter(vec![Ok(1), Err(()), Ok(2), Ok(3)]);
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert!(int_stream.try_next().await.is_err());
/// assert_eq!(int_stream.try_next().await, Ok(Some(2)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(3)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
///
/// // If we want to stop consuming the source stream the first time an
/// // element times out, we can use the `take_while` operator:
/// # let int_stream = stream::iter(vec![Ok(1), Err(()), Ok(2), Ok(3)]);
/// let mut int_stream = int_stream.take_while(Result::is_ok);
///
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
/// # }
/// ```
///
/// Timeout errors will be continuously produced at the specified interval
/// until the wrapped stream yields a value.
///
/// ```
/// # #[tokio::main(flavor = "current_thread", start_paused = true)]
/// # async fn main() {
/// use tokio_stream::{StreamExt, wrappers::IntervalStream};
/// use std::time::Duration;
/// let interval_stream = IntervalStream::new(tokio::time::interval(Duration::from_millis(23)));
/// let timeout_stream = interval_stream.timeout_repeating(tokio::time::interval(Duration::from_millis(9)));
/// tokio::pin!(timeout_stream);
///
/// // Multiple timeouts will be received between values in the source stream.
/// assert!(timeout_stream.try_next().await.is_ok());
/// assert!(timeout_stream.try_next().await.is_err(), "expected one timeout");
/// assert!(timeout_stream.try_next().await.is_err(), "expected a second timeout");
/// // Will eventually receive another value from the source stream...
/// assert!(timeout_stream.try_next().await.is_ok(), "expected non-timeout");
/// # }
/// ```
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
fn timeout_repeating(self, interval: Interval) -> TimeoutRepeating<Self>
where
Self: Sized,
{
TimeoutRepeating::new(self, interval)
}
/// Slows down a stream by enforcing a delay between items.
///
/// The underlying timer behind this utility has a granularity of one millisecond.
@@ -1001,7 +1114,7 @@ pub trait StreamExt: Stream {
/// }
/// # }
/// ```
#[cfg(all(feature = "time"))]
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
fn throttle(self, duration: Duration) -> Throttle<Self>
where
@@ -1066,6 +1179,31 @@ pub trait StreamExt: Stream {
assert!(max_size > 0, "`max_size` must be non-zero.");
ChunksTimeout::new(self, max_size, duration)
}
/// Turns the stream into a peekable stream, whose next element can be peeked at without being
/// consumed.
/// ```rust
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// # async fn _unused() {}
/// # #[tokio::main(flavor = "current_thread", start_paused = true)]
/// async fn main() {
/// let iter = vec![1, 2, 3, 4].into_iter();
/// let mut stream = stream::iter(iter).peekable();
///
/// assert_eq!(*stream.peek().await.unwrap(), 1);
/// assert_eq!(*stream.peek().await.unwrap(), 1);
/// assert_eq!(stream.next().await.unwrap(), 1);
/// assert_eq!(*stream.peek().await.unwrap(), 2);
/// }
/// ```
fn peekable(self) -> Peekable<Self>
where
Self: Sized,
{
Peekable::new(self)
}
}
impl<St: ?Sized> StreamExt for St where St: Stream {}
+1 -1
View File
@@ -26,7 +26,7 @@ pin_project! {
}
}
/// Convert from a [`Stream`](crate::Stream).
/// Convert from a [`Stream`].
///
/// This trait is not intended to be used directly. Instead, call
/// [`StreamExt::collect()`](super::StreamExt::collect).
+8 -10
View File
@@ -66,25 +66,23 @@ where
T: Stream,
U: Stream<Item = T::Item>,
{
use Poll::*;
let mut done = true;
match first.poll_next(cx) {
Ready(Some(val)) => return Ready(Some(val)),
Ready(None) => {}
Pending => done = false,
Poll::Ready(Some(val)) => return Poll::Ready(Some(val)),
Poll::Ready(None) => {}
Poll::Pending => done = false,
}
match second.poll_next(cx) {
Ready(Some(val)) => return Ready(Some(val)),
Ready(None) => {}
Pending => done = false,
Poll::Ready(Some(val)) => return Poll::Ready(Some(val)),
Poll::Ready(None) => {}
Poll::Pending => done = false,
}
if done {
Ready(None)
Poll::Ready(None)
} else {
Pending
Poll::Pending
}
}
+50
View File
@@ -0,0 +1,50 @@
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::Stream;
use pin_project_lite::pin_project;
use crate::stream_ext::Fuse;
use crate::StreamExt;
pin_project! {
/// Stream returned by the [`chain`](super::StreamExt::peekable) method.
pub struct Peekable<T: Stream> {
peek: Option<T::Item>,
#[pin]
stream: Fuse<T>,
}
}
impl<T: Stream> Peekable<T> {
pub(crate) fn new(stream: T) -> Self {
let stream = stream.fuse();
Self { peek: None, stream }
}
/// Peek at the next item in the stream.
pub async fn peek(&mut self) -> Option<&T::Item>
where
T: Unpin,
{
if let Some(ref it) = self.peek {
Some(it)
} else {
self.peek = self.next().await;
self.peek.as_ref()
}
}
}
impl<T: Stream> Stream for Peekable<T> {
type Item = T::Item;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.project();
if let Some(it) = this.peek.take() {
Poll::Ready(Some(it))
} else {
this.stream.poll_next(cx)
}
}
}
+3 -3
View File
@@ -64,11 +64,11 @@ where
let (lower, upper) = self.stream.size_hint();
let lower = cmp::min(lower, self.remaining as usize);
let lower = cmp::min(lower, self.remaining);
let upper = match upper {
Some(x) if x < self.remaining as usize => Some(x),
_ => Some(self.remaining as usize),
Some(x) if x < self.remaining => Some(x),
_ => Some(self.remaining),
};
(lower, upper)
+1 -1
View File
@@ -72,7 +72,7 @@ where
}
fn size_hint(&self) -> (usize, Option<usize>) {
let future_len = if self.future.is_some() { 1 } else { 0 };
let future_len = usize::from(self.future.is_some());
let (lower, upper) = self.stream.size_hint();
let lower = lower.saturating_add(future_len);
+1 -1
View File
@@ -23,7 +23,7 @@ pin_project! {
}
}
/// Error returned by `Timeout`.
/// Error returned by `Timeout` and `TimeoutRepeating`.
#[derive(Debug, PartialEq, Eq)]
pub struct Elapsed(());
@@ -0,0 +1,56 @@
use crate::stream_ext::Fuse;
use crate::{Elapsed, Stream};
use tokio::time::Interval;
use core::pin::Pin;
use core::task::{Context, Poll};
use pin_project_lite::pin_project;
pin_project! {
/// Stream returned by the [`timeout_repeating`](super::StreamExt::timeout_repeating) method.
#[must_use = "streams do nothing unless polled"]
#[derive(Debug)]
pub struct TimeoutRepeating<S> {
#[pin]
stream: Fuse<S>,
#[pin]
interval: Interval,
}
}
impl<S: Stream> TimeoutRepeating<S> {
pub(super) fn new(stream: S, interval: Interval) -> Self {
TimeoutRepeating {
stream: Fuse::new(stream),
interval,
}
}
}
impl<S: Stream> Stream for TimeoutRepeating<S> {
type Item = Result<S::Item, Elapsed>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut me = self.project();
match me.stream.poll_next(cx) {
Poll::Ready(v) => {
if v.is_some() {
me.interval.reset();
}
return Poll::Ready(v.map(Ok));
}
Poll::Pending => {}
};
ready!(me.interval.poll_tick(cx));
Poll::Ready(Some(Err(Elapsed::new())))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (lower, _) = self.stream.size_hint();
// The timeout stream may insert an error an infinite number of times.
(lower, None)
}
}
+36 -8
View File
@@ -42,10 +42,18 @@ use std::task::{Context, Poll};
/// to be merged, it may be advisable to use tasks sending values on a shared
/// [`mpsc`] channel.
///
/// # Notes
///
/// `StreamMap` removes finished streams automatically, without alerting the user.
/// In some scenarios, the caller would want to know on closed streams.
/// To do this, use [`StreamNotifyClose`] as a wrapper to your stream.
/// It will return None when the stream is closed.
///
/// [`StreamExt::merge`]: crate::StreamExt::merge
/// [`mpsc`]: https://docs.rs/tokio/1.0/tokio/sync/mpsc/index.html
/// [`pin!`]: https://docs.rs/tokio/1.0/tokio/macro.pin.html
/// [`Box::pin`]: std::boxed::Box::pin
/// [`StreamNotifyClose`]: crate::StreamNotifyClose
///
/// # Examples
///
@@ -170,6 +178,28 @@ use std::task::{Context, Poll};
/// }
/// }
/// ```
///
/// Using `StreamNotifyClose` to handle closed streams with `StreamMap`.
///
/// ```
/// use tokio_stream::{StreamExt, StreamMap, StreamNotifyClose};
///
/// #[tokio::main]
/// async fn main() {
/// let mut map = StreamMap::new();
/// let stream = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// let stream2 = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// map.insert(0, stream);
/// map.insert(1, stream2);
/// while let Some((key, val)) = map.next().await {
/// match val {
/// Some(val) => println!("got {val:?} from stream {key:?}"),
/// None => println!("stream {key:?} closed"),
/// }
/// }
/// }
/// ```
#[derive(Debug)]
pub struct StreamMap<K, V> {
/// Streams stored in the map
@@ -488,8 +518,6 @@ where
{
/// Polls the next value, includes the vec entry index
fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<Option<(usize, V::Item)>> {
use Poll::*;
let start = self::rand::thread_rng_n(self.entries.len() as u32) as usize;
let mut idx = start;
@@ -497,8 +525,8 @@ where
let (_, stream) = &mut self.entries[idx];
match Pin::new(stream).poll_next(cx) {
Ready(Some(val)) => return Ready(Some((idx, val))),
Ready(None) => {
Poll::Ready(Some(val)) => return Poll::Ready(Some((idx, val))),
Poll::Ready(None) => {
// Remove the entry
self.entries.swap_remove(idx);
@@ -512,7 +540,7 @@ where
idx = idx.wrapping_add(1) % self.entries.len();
}
}
Pending => {
Poll::Pending => {
idx = idx.wrapping_add(1) % self.entries.len();
}
}
@@ -520,9 +548,9 @@ where
// If the map is empty, then the stream is complete.
if self.entries.is_empty() {
Ready(None)
Poll::Ready(None)
} else {
Pending
Poll::Pending
}
}
}
@@ -568,7 +596,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,
{
+1 -1
View File
@@ -34,7 +34,7 @@ impl<T> ReceiverStream<T> {
///
/// [`Permit`]: struct@tokio::sync::mpsc::Permit
pub fn close(&mut self) {
self.inner.close()
self.inner.close();
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ impl<T> UnboundedReceiverStream<T> {
/// This prevents any further messages from being sent on the channel while
/// still enabling the receiver to drain messages that are buffered.
pub fn close(&mut self) {
self.inner.close()
self.inner.close();
}
}
+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> {
+11
View File
@@ -0,0 +1,11 @@
use tokio_stream::{StreamExt, StreamNotifyClose};
#[tokio::test]
async fn basic_usage() {
let mut stream = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
assert_eq!(stream.next().await, Some(Some(0)));
assert_eq!(stream.next().await, Some(Some(1)));
assert_eq!(stream.next().await, Some(None));
assert_eq!(stream.next().await, None);
}
-57
View File
@@ -325,63 +325,6 @@ fn one_ready_many_none() {
}
}
#[cfg(not(target_os = "wasi"))]
proptest::proptest! {
#[test]
fn fuzz_pending_complete_mix(kinds: Vec<bool>) {
use std::task::{Context, Poll};
struct DidPoll<T> {
did_poll: bool,
inner: T,
}
impl<T: Stream + Unpin> Stream for DidPoll<T> {
type Item = T::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>)
-> Poll<Option<T::Item>>
{
self.did_poll = true;
Pin::new(&mut self.inner).poll_next(cx)
}
}
for _ in 0..10 {
let mut map = task::spawn(StreamMap::new());
let mut expect = 0;
for (i, &is_empty) in kinds.iter().enumerate() {
let inner = if is_empty {
pin_box(stream::empty::<()>())
} else {
expect += 1;
pin_box(stream::pending::<()>())
};
let stream = DidPoll {
did_poll: false,
inner,
};
map.insert(i, stream);
}
if expect == 0 {
assert_ready_none!(map.poll_next());
} else {
assert_pending!(map.poll_next());
assert_eq!(expect, map.values().count());
for stream in map.values() {
assert!(stream.did_poll);
}
}
}
}
}
fn pin_box<T: Stream<Item = U> + 'static, U>(s: T) -> Pin<Box<dyn Stream<Item = U>>> {
Box::pin(s)
}
+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");
}
+14
View File
@@ -1,3 +1,17 @@
# 0.4.3 (August 23, 2023)
- deps: fix minimum required version of `async-stream` ([#5347])
- deps: fix minimum required version of `tokio-stream` ([#4376])
- docs: improve `tokio_test::task` docs ([#5132])
- io: fetch actions from mock handle before write ([#5814])
- io: fix wait operation on mock ([#5554])
[#4376]: https://github.com/tokio-rs/tokio/pull/4376
[#5132]: https://github.com/tokio-rs/tokio/pull/5132
[#5347]: https://github.com/tokio-rs/tokio/pull/5347
[#5554]: https://github.com/tokio-rs/tokio/pull/5554
[#5814]: https://github.com/tokio-rs/tokio/pull/5814
# 0.4.2 (May 14, 2021)
- test: add `assert_elapsed!` macro ([#3728])
+5 -5
View File
@@ -4,9 +4,9 @@ name = "tokio-test"
# - Remove path dependencies
# - Update CHANGELOG.md.
# - Create "tokio-test-0.4.x" git tag.
version = "0.4.2"
edition = "2018"
rust-version = "1.49"
version = "0.4.3"
edition = "2021"
rust-version = "1.63"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -14,12 +14,12 @@ homepage = "https://tokio.rs"
description = """
Testing utilities for Tokio- and futures-based code
"""
categories = ["asynchronous", "testing"]
categories = ["asynchronous", "development-tools::testing"]
[dependencies]
tokio = { version = "1.2.0", path = "../tokio", features = ["rt", "sync", "time", "test-util"] }
tokio-stream = { version = "0.1.1", path = "../tokio-stream" }
async-stream = "0.3"
async-stream = "0.3.3"
bytes = "1.0.0"
futures-core = "0.3.0"
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2022 Tokio Contributors
Copyright (c) 2023 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+18 -2
View File
@@ -74,7 +74,7 @@ struct Inner {
}
impl Builder {
/// Return a new, empty `Builder.
/// Return a new, empty `Builder`.
pub fn new() -> Self {
Self::default()
}
@@ -310,6 +310,8 @@ impl Inner {
if now < until {
break;
} else {
self.waiting = None;
}
} else {
self.waiting = Some(Instant::now() + *dur);
@@ -407,6 +409,20 @@ impl AsyncWrite for Mock {
// If a sleep is set, it has already fired
self.inner.sleep = None;
if self.inner.actions.is_empty() {
match self.inner.poll_action(cx) {
Poll::Pending => {
// do not propagate pending
}
Poll::Ready(Some(action)) => {
self.inner.actions.push_back(action);
}
Poll::Ready(None) => {
panic!("unexpected write");
}
}
}
match self.inner.write(buf) {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
if let Some(rem) = self.inner.remaining_wait() {
@@ -462,7 +478,7 @@ impl Drop for Mock {
Action::Read(data) => assert!(data.is_empty(), "There is still data left to read."),
Action::Write(data) => assert!(data.is_empty(), "There is still data left to write."),
_ => (),
})
});
}
}
/*
+1
View File
@@ -12,6 +12,7 @@
//! Tokio and Futures based testing utilities
pub mod io;
pub mod stream_mock;
mod macros;
pub mod task;
+12 -12
View File
@@ -22,17 +22,17 @@
#[macro_export]
macro_rules! assert_ready {
($e:expr) => {{
use core::task::Poll::*;
use core::task::Poll;
match $e {
Ready(v) => v,
Pending => panic!("pending"),
Poll::Ready(v) => v,
Poll::Pending => panic!("pending"),
}
}};
($e:expr, $($msg:tt)+) => {{
use core::task::Poll::*;
use core::task::Poll;
match $e {
Ready(v) => v,
Pending => {
Poll::Ready(v) => v,
Poll::Pending => {
panic!("pending; {}", format_args!($($msg)+))
}
}
@@ -127,17 +127,17 @@ macro_rules! assert_ready_err {
#[macro_export]
macro_rules! assert_pending {
($e:expr) => {{
use core::task::Poll::*;
use core::task::Poll;
match $e {
Pending => {}
Ready(v) => panic!("ready; value = {:?}", v),
Poll::Pending => {}
Poll::Ready(v) => panic!("ready; value = {:?}", v),
}
}};
($e:expr, $($msg:tt)+) => {{
use core::task::Poll::*;
use core::task::Poll;
match $e {
Pending => {}
Ready(v) => {
Poll::Pending => {}
Poll::Ready(v) => {
panic!("ready; value = {:?}; {}", v, format_args!($($msg)+))
}
}
+168
View File
@@ -0,0 +1,168 @@
#![cfg(not(loom))]
//! A mock stream implementing [`Stream`].
//!
//! # Overview
//! This crate provides a `StreamMock` that can be used to test code that interacts with streams.
//! It allows you to mock the behavior of a stream and control the items it yields and the waiting
//! intervals between items.
//!
//! # Usage
//! To use the `StreamMock`, you need to create a builder using[`StreamMockBuilder`]. The builder
//! allows you to enqueue actions such as returning items or waiting for a certain duration.
//!
//! # Example
//! ```rust
//!
//! use futures_util::StreamExt;
//! use std::time::Duration;
//! use tokio_test::stream_mock::StreamMockBuilder;
//!
//! async fn test_stream_mock_wait() {
//! let mut stream_mock = StreamMockBuilder::new()
//! .next(1)
//! .wait(Duration::from_millis(300))
//! .next(2)
//! .build();
//!
//! assert_eq!(stream_mock.next().await, Some(1));
//! let start = std::time::Instant::now();
//! assert_eq!(stream_mock.next().await, Some(2));
//! let elapsed = start.elapsed();
//! assert!(elapsed >= Duration::from_millis(300));
//! assert_eq!(stream_mock.next().await, None);
//! }
//! ```
use std::collections::VecDeque;
use std::pin::Pin;
use std::task::Poll;
use std::time::Duration;
use futures_core::{ready, Stream};
use std::future::Future;
use tokio::time::{sleep_until, Instant, Sleep};
#[derive(Debug, Clone)]
enum Action<T: Unpin> {
Next(T),
Wait(Duration),
}
/// A builder for [`StreamMock`]
#[derive(Debug, Clone)]
pub struct StreamMockBuilder<T: Unpin> {
actions: VecDeque<Action<T>>,
}
impl<T: Unpin> StreamMockBuilder<T> {
/// Create a new empty [`StreamMockBuilder`]
pub fn new() -> Self {
StreamMockBuilder::default()
}
/// Queue an item to be returned by the stream
pub fn next(mut self, value: T) -> Self {
self.actions.push_back(Action::Next(value));
self
}
// Queue an item to be consumed by the sink,
// commented out until Sink is implemented.
//
// pub fn consume(mut self, value: T) -> Self {
// self.actions.push_back(Action::Consume(value));
// self
// }
/// Queue the stream to wait for a duration
pub fn wait(mut self, duration: Duration) -> Self {
self.actions.push_back(Action::Wait(duration));
self
}
/// Build the [`StreamMock`]
pub fn build(self) -> StreamMock<T> {
StreamMock {
actions: self.actions,
sleep: None,
}
}
}
impl<T: Unpin> Default for StreamMockBuilder<T> {
fn default() -> Self {
StreamMockBuilder {
actions: VecDeque::new(),
}
}
}
/// A mock stream implementing [`Stream`]
///
/// See [`StreamMockBuilder`] for more information.
#[derive(Debug)]
pub struct StreamMock<T: Unpin> {
actions: VecDeque<Action<T>>,
sleep: Option<Pin<Box<Sleep>>>,
}
impl<T: Unpin> StreamMock<T> {
fn next_action(&mut self) -> Option<Action<T>> {
self.actions.pop_front()
}
}
impl<T: Unpin> Stream for StreamMock<T> {
type Item = T;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
// Try polling the sleep future first
if let Some(ref mut sleep) = self.sleep {
ready!(Pin::new(sleep).poll(cx));
// Since we're ready, discard the sleep future
self.sleep.take();
}
match self.next_action() {
Some(action) => match action {
Action::Next(item) => Poll::Ready(Some(item)),
Action::Wait(duration) => {
// Set up a sleep future and schedule this future to be polled again for it.
self.sleep = Some(Box::pin(sleep_until(Instant::now() + duration)));
cx.waker().wake_by_ref();
Poll::Pending
}
},
None => Poll::Ready(None),
}
}
}
impl<T: Unpin> Drop for StreamMock<T> {
fn drop(&mut self) {
// Avoid double panicking to make debugging easier.
if std::thread::panicking() {
return;
}
let undropped_count = self
.actions
.iter()
.filter(|action| match action {
Action::Next(_) => true,
Action::Wait(_) => false,
})
.count();
assert!(
undropped_count == 0,
"StreamMock was dropped before all actions were consumed, {} actions were not consumed",
undropped_count
);
}
}
+1 -1
View File
@@ -127,7 +127,7 @@ impl<T: Future> Spawn<T> {
}
impl<T: Stream> Spawn<T> {
/// If `T` is a [`Stream`] then poll_next it. This will handle pinning and the context
/// If `T` is a [`Stream`] then `poll_next` it. This will handle pinning and the context
/// type for the stream.
pub fn poll_next(&mut self) -> Poll<Option<T::Item>> {
let stream = self.future.as_mut();
+86
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]
@@ -50,6 +51,29 @@ async fn write() {
mock.write_all(b"world!").await.expect("write 2");
}
#[tokio::test]
async fn write_with_handle() {
let (mut mock, mut handle) = Builder::new().build_with_handle();
handle.write(b"hello ");
handle.write(b"world!");
mock.write_all(b"hello ").await.expect("write 1");
mock.write_all(b"world!").await.expect("write 2");
}
#[tokio::test]
async fn read_with_handle() {
let (mut mock, mut handle) = Builder::new().build_with_handle();
handle.read(b"hello ");
handle.read(b"world!");
let mut buf = vec![0; 6];
mock.read_exact(&mut buf).await.expect("read 1");
assert_eq!(&buf[..], b"hello ");
mock.read_exact(&mut buf).await.expect("read 2");
assert_eq!(&buf[..], b"world!");
}
#[tokio::test]
async fn write_error() {
let error = io::Error::new(io::ErrorKind::Other, "cruel");
@@ -84,3 +108,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()
);
}
+50
View File
@@ -0,0 +1,50 @@
use futures_util::StreamExt;
use std::time::Duration;
use tokio_test::stream_mock::StreamMockBuilder;
#[tokio::test]
async fn test_stream_mock_empty() {
let mut stream_mock = StreamMockBuilder::<u32>::new().build();
assert_eq!(stream_mock.next().await, None);
assert_eq!(stream_mock.next().await, None);
}
#[tokio::test]
async fn test_stream_mock_items() {
let mut stream_mock = StreamMockBuilder::new().next(1).next(2).build();
assert_eq!(stream_mock.next().await, Some(1));
assert_eq!(stream_mock.next().await, Some(2));
assert_eq!(stream_mock.next().await, None);
}
#[tokio::test]
async fn test_stream_mock_wait() {
let mut stream_mock = StreamMockBuilder::new()
.next(1)
.wait(Duration::from_millis(300))
.next(2)
.build();
assert_eq!(stream_mock.next().await, Some(1));
let start = std::time::Instant::now();
assert_eq!(stream_mock.next().await, Some(2));
let elapsed = start.elapsed();
assert!(elapsed >= Duration::from_millis(300));
assert_eq!(stream_mock.next().await, None);
}
#[tokio::test]
#[should_panic(expected = "StreamMock was dropped before all actions were consumed")]
async fn test_stream_mock_drop_without_consuming_all() {
let stream_mock = StreamMockBuilder::new().next(1).next(2).build();
drop(stream_mock);
}
#[tokio::test]
#[should_panic(expected = "test panic was not masked")]
async fn test_stream_mock_drop_during_panic_doesnt_mask_panic() {
let _stream_mock = StreamMockBuilder::new().next(1).next(2).build();
panic!("test panic was not masked");
}
+121
View File
@@ -1,3 +1,124 @@
# 0.7.10 (October 24th, 2023)
### Added
- task: add `TaskTracker` ([#6033])
- task: add `JoinMap::keys` ([#6046])
- io: implement `Seek` for `SyncIoBridge` ([#6058])
### Changed
- deps: update hashbrown to 0.14 ([#6102])
[#6033]: https://github.com/tokio-rs/tokio/pull/6033
[#6046]: https://github.com/tokio-rs/tokio/pull/6046
[#6058]: https://github.com/tokio-rs/tokio/pull/6058
[#6102]: https://github.com/tokio-rs/tokio/pull/6102
# 0.7.9 (September 20th, 2023)
### Added
- io: add passthrough `AsyncRead`/`AsyncWrite` to `InspectWriter`/`InspectReader` ([#5739])
- task: add spawn blocking methods to `JoinMap` ([#5797])
- io: pass through traits for `StreamReader` and `SinkWriter` ([#5941])
- io: add `SyncIoBridge::into_inner` ([#5971])
### Fixed
- sync: handle possibly dangling reference safely ([#5812])
- util: fix broken intra-doc link ([#5849])
- compat: fix clippy warnings ([#5891])
### Documented
- codec: Specify the line ending of `LinesCodec` ([#5982])
[#5739]: https://github.com/tokio-rs/tokio/pull/5739
[#5797]: https://github.com/tokio-rs/tokio/pull/5797
[#5941]: https://github.com/tokio-rs/tokio/pull/5941
[#5971]: https://github.com/tokio-rs/tokio/pull/5971
[#5812]: https://github.com/tokio-rs/tokio/pull/5812
[#5849]: https://github.com/tokio-rs/tokio/pull/5849
[#5891]: https://github.com/tokio-rs/tokio/pull/5891
[#5982]: https://github.com/tokio-rs/tokio/pull/5982
# 0.7.8 (April 25th, 2023)
This release bumps the MSRV of tokio-util to 1.56.
### Added
- time: add `DelayQueue::peek` ([#5569])
### Changed
This release contains one performance improvement:
- sync: try to lock the parent first in `CancellationToken` ([#5561])
### Fixed
- time: fix panic in `DelayQueue` ([#5630])
### Documented
- sync: improve `CancellationToken` doc on child tokens ([#5632])
[#5561]: https://github.com/tokio-rs/tokio/pull/5561
[#5569]: https://github.com/tokio-rs/tokio/pull/5569
[#5630]: https://github.com/tokio-rs/tokio/pull/5630
[#5632]: https://github.com/tokio-rs/tokio/pull/5632
# 0.7.7 (February 12, 2023)
This release reverts the removal of the `Encoder` bound on the `FramedParts`
constructor from [#5280] since it turned out to be a breaking change. ([#5450])
[#5450]: https://github.com/tokio-rs/tokio/pull/5450
# 0.7.6 (February 10, 2023)
This release fixes a compilation failure in 0.7.5 when it is used together with
Tokio version 1.21 and unstable features are enabled. ([#5445])
[#5445]: https://github.com/tokio-rs/tokio/pull/5445
# 0.7.5 (February 9, 2023)
This release fixes an accidental breaking change where `UnwindSafe` was
accidentally removed from `CancellationToken`.
### Added
- codec: add `Framed::backpressure_boundary` ([#5124])
- io: add `InspectReader` and `InspectWriter` ([#5033])
- io: add `tokio_util::io::{CopyToBytes, SinkWriter}` ([#5070], [#5436])
- io: impl `std::io::BufRead` on `SyncIoBridge` ([#5265])
- sync: add `PollSemaphore::poll_acquire_many` ([#5137])
- sync: add owned future for `CancellationToken` ([#5153])
- time: add `DelayQueue::try_remove` ([#5052])
### Fixed
- codec: fix `LengthDelimitedCodec` buffer over-reservation ([#4997])
- sync: impl `UnwindSafe` on `CancellationToken` ([#5438])
- util: remove `Encoder` bound on `FramedParts` constructor ([#5280])
### Documented
- io: add lines example for `StreamReader` ([#5145])
[#4997]: https://github.com/tokio-rs/tokio/pull/4997
[#5033]: https://github.com/tokio-rs/tokio/pull/5033
[#5052]: https://github.com/tokio-rs/tokio/pull/5052
[#5070]: https://github.com/tokio-rs/tokio/pull/5070
[#5124]: https://github.com/tokio-rs/tokio/pull/5124
[#5137]: https://github.com/tokio-rs/tokio/pull/5137
[#5145]: https://github.com/tokio-rs/tokio/pull/5145
[#5153]: https://github.com/tokio-rs/tokio/pull/5153
[#5265]: https://github.com/tokio-rs/tokio/pull/5265
[#5280]: https://github.com/tokio-rs/tokio/pull/5280
[#5436]: https://github.com/tokio-rs/tokio/pull/5436
[#5438]: https://github.com/tokio-rs/tokio/pull/5438
# 0.7.4 (September 8, 2022)
### Added
+7 -6
View File
@@ -4,9 +4,9 @@ name = "tokio-util"
# - Remove path dependencies
# - Update CHANGELOG.md.
# - Create "tokio-util-0.7.x" git tag.
version = "0.7.4"
edition = "2018"
rust-version = "1.49"
version = "0.7.10"
edition = "2021"
rust-version = "1.63"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -34,18 +34,18 @@ rt = ["tokio/rt", "tokio/sync", "futures-util", "hashbrown"]
__docs_rs = ["futures-util"]
[dependencies]
tokio = { version = "1.21.0", path = "../tokio", features = ["sync"] }
tokio = { version = "1.28.0", path = "../tokio", features = ["sync"] }
bytes = "1.0.0"
futures-core = "0.3.0"
futures-sink = "0.3.0"
futures-io = { version = "0.3.0", optional = true }
futures-util = { version = "0.3.0", optional = true }
pin-project-lite = "0.2.0"
pin-project-lite = "0.2.11"
slab = { version = "0.4.4", optional = true } # Backs `DelayQueue`
tracing = { version = "0.1.25", default-features = false, features = ["std"], optional = true }
[target.'cfg(tokio_unstable)'.dependencies]
hashbrown = { version = "0.12.0", optional = true }
hashbrown = { version = "0.14.0", optional = true }
[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
@@ -56,6 +56,7 @@ async-stream = "0.3.0"
futures = "0.3.0"
futures-test = "0.3.5"
parking_lot = "0.12.0"
tempfile = "3.1.0"
[package.metadata.docs.rs]
all-features = true
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2022 Tokio Contributors
Copyright (c) 2023 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+2
View File
@@ -6,6 +6,8 @@ use std::{cmp, fmt, io, str, usize};
/// A simple [`Decoder`] and [`Encoder`] implementation that splits up data into lines.
///
/// This uses the `\n` character as the line ending on all platforms.
///
/// [`Decoder`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+4 -2
View File
@@ -227,12 +227,14 @@ impl<T: tokio::io::AsyncSeek> futures_io::AsyncSeek for Compat<T> {
pos: io::SeekFrom,
) -> Poll<io::Result<u64>> {
if self.seek_pos != Some(pos) {
// Ensure previous seeks have finished before starting a new one
ready!(self.as_mut().project().inner.poll_complete(cx))?;
self.as_mut().project().inner.start_seek(pos)?;
*self.as_mut().project().seek_pos = Some(pos);
}
let res = ready!(self.as_mut().project().inner.poll_complete(cx));
*self.as_mut().project().seek_pos = None;
Poll::Ready(res.map(|p| p as u64))
Poll::Ready(res)
}
}
@@ -255,7 +257,7 @@ impl<T: futures_io::AsyncSeek> tokio::io::AsyncSeek for Compat<T> {
};
let res = ready!(self.as_mut().project().inner.poll_seek(cx, pos));
*self.as_mut().project().seek_pos = None;
Poll::Ready(res.map(|p| p as u64))
Poll::Ready(res)
}
}
+1 -1
View File
@@ -116,7 +116,7 @@ where
}
fn consume(self: Pin<&mut Self>, amt: usize) {
delegate_call!(self.consume(amt))
delegate_call!(self.consume(amt));
}
}
+8
View File
@@ -1,4 +1,5 @@
use bytes::Bytes;
use futures_core::stream::Stream;
use futures_sink::Sink;
use pin_project_lite::pin_project;
use std::pin::Pin;
@@ -66,3 +67,10 @@ where
self.project().inner.poll_close(cx)
}
}
impl<S: Stream> Stream for CopyToBytes<S> {
type Item = S::Item;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project().inner.poll_next(cx)
}
}
+46
View File
@@ -52,6 +52,42 @@ impl<R: AsyncRead, F: FnMut(&[u8])> AsyncRead for InspectReader<R, F> {
}
}
impl<R: AsyncWrite, F> AsyncWrite for InspectReader<R, F> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::result::Result<usize, std::io::Error>> {
self.project().reader.poll_write(cx, buf)
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::result::Result<(), std::io::Error>> {
self.project().reader.poll_flush(cx)
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::result::Result<(), std::io::Error>> {
self.project().reader.poll_shutdown(cx)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<Result<usize>> {
self.project().reader.poll_write_vectored(cx, bufs)
}
fn is_write_vectored(&self) -> bool {
self.reader.is_write_vectored()
}
}
pin_project! {
/// An adapter that lets you inspect the data that's being written.
///
@@ -132,3 +168,13 @@ impl<W: AsyncWrite, F: FnMut(&[u8])> AsyncWrite for InspectWriter<W, F> {
self.writer.is_write_vectored()
}
}
impl<W: AsyncRead, F> AsyncRead for InspectWriter<W, F> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
self.project().writer.poll_read(cx, buf)
}
}
+26 -15
View File
@@ -1,10 +1,12 @@
use futures_core::ready;
use futures_sink::Sink;
use futures_core::stream::Stream;
use pin_project_lite::pin_project;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::AsyncWrite;
use tokio::io::{AsyncRead, AsyncWrite};
pin_project! {
/// Convert a [`Sink`] of byte chunks into an [`AsyncWrite`].
@@ -58,7 +60,7 @@ pin_project! {
/// [`CopyToBytes`]: crate::io::CopyToBytes
/// [`Encoder`]: crate::codec::Encoder
/// [`Sink`]: futures_sink::Sink
/// [`codec`]: tokio_util::codec
/// [`codec`]: crate::codec
#[derive(Debug)]
pub struct SinkWriter<S> {
#[pin]
@@ -98,19 +100,11 @@ where
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
let mut this = self.project();
match this.inner.as_mut().poll_ready(cx) {
Poll::Ready(Ok(())) => {
if let Err(e) = this.inner.as_mut().start_send(buf) {
Poll::Ready(Err(e.into()))
} else {
Poll::Ready(Ok(buf.len()))
}
}
Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),
Poll::Pending => {
cx.waker().wake_by_ref();
Poll::Pending
}
ready!(this.inner.as_mut().poll_ready(cx).map_err(Into::into))?;
match this.inner.as_mut().start_send(buf) {
Ok(()) => Poll::Ready(Ok(buf.len())),
Err(e) => Poll::Ready(Err(e.into())),
}
}
@@ -122,3 +116,20 @@ where
self.project().inner.poll_close(cx).map_err(Into::into)
}
}
impl<S: Stream> Stream for SinkWriter<S> {
type Item = S::Item;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project().inner.poll_next(cx)
}
}
impl<S: AsyncRead> AsyncRead for SinkWriter<S> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<io::Result<()>> {
self.project().inner.poll_read(cx, buf)
}
}
+22 -2
View File
@@ -1,5 +1,6 @@
use bytes::Buf;
use futures_core::stream::Stream;
use futures_sink::Sink;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -165,7 +166,7 @@ where
B: Buf,
E: Into<std::io::Error>,
{
/// Convert a stream of byte chunks into an [`AsyncRead`](tokio::io::AsyncRead).
/// Convert a stream of byte chunks into an [`AsyncRead`].
///
/// The item should be a [`Result`] with the ok variant being something that
/// implements the [`Buf`] trait (e.g. `Vec<u8>` or `Bytes`). The error
@@ -301,7 +302,7 @@ where
}
// The code below is a manual expansion of the code that pin-project-lite would
// generate. This is done because pin-project-lite fails by hitting the recusion
// generate. This is done because pin-project-lite fails by hitting the recursion
// limit on this struct. (Every line of documentation is handled recursively by
// the macro.)
@@ -324,3 +325,22 @@ impl<S, B> StreamReader<S, B> {
}
}
}
impl<S: Sink<T, Error = E>, E, T> Sink<T> for StreamReader<S, E> {
type Error = E;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project().inner.poll_ready(cx)
}
fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> {
self.project().inner.start_send(item)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project().inner.poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project().inner.poll_close(cx)
}
}
+39 -2
View File
@@ -1,5 +1,8 @@
use std::io::{Read, Write};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use std::io::{BufRead, Read, Seek, Write};
use tokio::io::{
AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, AsyncWrite,
AsyncWriteExt,
};
/// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or
/// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`].
@@ -9,6 +12,28 @@ pub struct SyncIoBridge<T> {
rt: tokio::runtime::Handle,
}
impl<T: AsyncBufRead + Unpin> BufRead for SyncIoBridge<T> {
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
let src = &mut self.src;
self.rt.block_on(AsyncBufReadExt::fill_buf(src))
}
fn consume(&mut self, amt: usize) {
let src = &mut self.src;
AsyncBufReadExt::consume(src, amt)
}
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> std::io::Result<usize> {
let src = &mut self.src;
self.rt
.block_on(AsyncBufReadExt::read_until(src, byte, buf))
}
fn read_line(&mut self, buf: &mut String) -> std::io::Result<usize> {
let src = &mut self.src;
self.rt.block_on(AsyncBufReadExt::read_line(src, buf))
}
}
impl<T: AsyncRead + Unpin> Read for SyncIoBridge<T> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let src = &mut self.src;
@@ -55,6 +80,13 @@ impl<T: AsyncWrite + Unpin> Write for SyncIoBridge<T> {
}
}
impl<T: AsyncSeek + Unpin> Seek for SyncIoBridge<T> {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
let src = &mut self.src;
self.rt.block_on(AsyncSeekExt::seek(src, pos))
}
}
// Because https://doc.rust-lang.org/std/io/trait.Write.html#method.is_write_vectored is at the time
// of this writing still unstable, we expose this as part of a standalone method.
impl<T: AsyncWrite> SyncIoBridge<T> {
@@ -116,4 +148,9 @@ impl<T: Unpin> SyncIoBridge<T> {
pub fn new_with_handle(src: T, rt: tokio::runtime::Handle) -> Self {
Self { src, rt }
}
/// Consume this bridge, returning the underlying stream.
pub fn into_inner(self) -> T {
self.src
}
}
+2 -147
View File
@@ -55,151 +55,6 @@ pub mod sync;
pub mod either;
#[cfg(any(feature = "io", feature = "codec"))]
mod util {
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
pub use bytes;
use bytes::{Buf, BufMut};
use futures_core::ready;
use std::io::{self, IoSlice};
use std::mem::MaybeUninit;
use std::pin::Pin;
use std::task::{Context, Poll};
/// Try to read data from an `AsyncRead` into an implementer of the [`BufMut`] trait.
///
/// [`BufMut`]: bytes::Buf
///
/// # Example
///
/// ```
/// use bytes::{Bytes, BytesMut};
/// use tokio_stream as stream;
/// use tokio::io::Result;
/// use tokio_util::io::{StreamReader, poll_read_buf};
/// use futures::future::poll_fn;
/// use std::pin::Pin;
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
///
/// // Create a reader from an iterator. This particular reader will always be
/// // ready.
/// let mut read = StreamReader::new(stream::iter(vec![Result::Ok(Bytes::from_static(&[0, 1, 2, 3]))]));
///
/// let mut buf = BytesMut::new();
/// let mut reads = 0;
///
/// loop {
/// reads += 1;
/// let n = poll_fn(|cx| poll_read_buf(Pin::new(&mut read), cx, &mut buf)).await?;
///
/// if n == 0 {
/// break;
/// }
/// }
///
/// // one or more reads might be necessary.
/// assert!(reads >= 1);
/// assert_eq!(&buf[..], &[0, 1, 2, 3]);
/// # Ok(())
/// # }
/// ```
#[cfg_attr(not(feature = "io"), allow(unreachable_pub))]
pub fn poll_read_buf<T: AsyncRead, B: BufMut>(
io: Pin<&mut T>,
cx: &mut Context<'_>,
buf: &mut B,
) -> Poll<io::Result<usize>> {
if !buf.has_remaining_mut() {
return Poll::Ready(Ok(0));
}
let n = {
let dst = buf.chunk_mut();
// Safety: `chunk_mut()` returns a `&mut UninitSlice`, and `UninitSlice` is a
// transparent wrapper around `[MaybeUninit<u8>]`.
let dst = unsafe { &mut *(dst as *mut _ as *mut [MaybeUninit<u8>]) };
let mut buf = ReadBuf::uninit(dst);
let ptr = buf.filled().as_ptr();
ready!(io.poll_read(cx, &mut buf)?);
// Ensure the pointer does not change from under us
assert_eq!(ptr, buf.filled().as_ptr());
buf.filled().len()
};
// Safety: This is guaranteed to be the number of initialized (and read)
// bytes due to the invariants provided by `ReadBuf::filled`.
unsafe {
buf.advance_mut(n);
}
Poll::Ready(Ok(n))
}
/// Try to write data from an implementer of the [`Buf`] trait to an
/// [`AsyncWrite`], advancing the buffer's internal cursor.
///
/// This function will use [vectored writes] when the [`AsyncWrite`] supports
/// vectored writes.
///
/// # Examples
///
/// [`File`] implements [`AsyncWrite`] and [`Cursor<&[u8]>`] implements
/// [`Buf`]:
///
/// ```no_run
/// use tokio_util::io::poll_write_buf;
/// use tokio::io;
/// use tokio::fs::File;
///
/// use bytes::Buf;
/// use std::io::Cursor;
/// use std::pin::Pin;
/// use futures::future::poll_fn;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut file = File::create("foo.txt").await?;
/// let mut buf = Cursor::new(b"data to write");
///
/// // Loop until the entire contents of the buffer are written to
/// // the file.
/// while buf.has_remaining() {
/// poll_fn(|cx| poll_write_buf(Pin::new(&mut file), cx, &mut buf)).await?;
/// }
///
/// Ok(())
/// }
/// ```
///
/// [`Buf`]: bytes::Buf
/// [`AsyncWrite`]: tokio::io::AsyncWrite
/// [`File`]: tokio::fs::File
/// [vectored writes]: tokio::io::AsyncWrite::poll_write_vectored
#[cfg_attr(not(feature = "io"), allow(unreachable_pub))]
pub fn poll_write_buf<T: AsyncWrite, B: Buf>(
io: Pin<&mut T>,
cx: &mut Context<'_>,
buf: &mut B,
) -> Poll<io::Result<usize>> {
const MAX_BUFS: usize = 64;
if !buf.has_remaining() {
return Poll::Ready(Ok(0));
}
let n = if io.is_write_vectored() {
let mut slices = [IoSlice::new(&[]); MAX_BUFS];
let cnt = buf.chunks_vectored(&mut slices);
ready!(io.poll_write_vectored(cx, &slices[..cnt]))?
} else {
ready!(io.poll_write(cx, buf.chunk()))?
};
buf.advance(n);
Poll::Ready(Ok(n))
}
}
mod util;
+29 -9
View File
@@ -4,6 +4,7 @@ pub(crate) mod guard;
mod tree_node;
use crate::loom::sync::Arc;
use crate::util::MaybeDangling;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
@@ -55,6 +56,9 @@ pub struct CancellationToken {
inner: Arc<tree_node::TreeNode>,
}
impl std::panic::UnwindSafe for CancellationToken {}
impl std::panic::RefUnwindSafe for CancellationToken {}
pin_project! {
/// A Future that is resolved once the corresponding [`CancellationToken`]
/// is cancelled.
@@ -74,11 +78,23 @@ pin_project! {
/// [`CancellationToken`] by value instead of using a reference.
#[must_use = "futures do nothing unless polled"]
pub struct WaitForCancellationFutureOwned {
// Since `future` is the first field, it is dropped before the
// cancellation_token field. This ensures that the reference inside the
// `Notified` remains valid.
// This field internally has a reference to the cancellation token, but camouflages
// the relationship with `'static`. To avoid Undefined Behavior, we must ensure
// that the reference is only used while the cancellation token is still alive. To
// do that, we ensure that the future is the first field, so that it is dropped
// before the cancellation token.
//
// We use `MaybeDanglingFuture` here because without it, the compiler could assert
// the reference inside `future` to be valid even after the destructor of that
// field runs. (Specifically, when the `WaitForCancellationFutureOwned` is passed
// as an argument to a function, the reference can be asserted to be valid for the
// rest of that function.) To avoid that, we use `MaybeDangling` which tells the
// compiler that the reference stored inside it might not be valid.
//
// See <https://users.rust-lang.org/t/unsafe-code-review-semi-owning-weak-rwlock-t-guard/95706>
// for more info.
#[pin]
future: tokio::sync::futures::Notified<'static>,
future: MaybeDangling<tokio::sync::futures::Notified<'static>>,
cancellation_token: CancellationToken,
}
}
@@ -94,6 +110,8 @@ impl core::fmt::Debug for CancellationToken {
}
impl Clone for CancellationToken {
/// Creates a clone of the `CancellationToken` which will get cancelled
/// whenever the current token gets cancelled, and vice versa.
fn clone(&self) -> Self {
tree_node::increase_handle_refcount(&self.inner);
CancellationToken {
@@ -115,7 +133,7 @@ impl Default for CancellationToken {
}
impl CancellationToken {
/// Creates a new CancellationToken in the non-cancelled state.
/// Creates a new `CancellationToken` in the non-cancelled state.
pub fn new() -> CancellationToken {
CancellationToken {
inner: Arc::new(tree_node::TreeNode::new()),
@@ -123,7 +141,8 @@ impl CancellationToken {
}
/// Creates a `CancellationToken` which will get cancelled whenever the
/// current token gets cancelled.
/// current token gets cancelled. Unlike a cloned `CancellationToken`,
/// cancelling a child token does not cancel the parent token.
///
/// If the current token is already cancelled, the child token will get
/// returned in cancelled state.
@@ -273,7 +292,7 @@ impl WaitForCancellationFutureOwned {
// # Safety
//
// cancellation_token is dropped after future due to the field ordering.
future: unsafe { Self::new_future(&cancellation_token) },
future: MaybeDangling::new(unsafe { Self::new_future(&cancellation_token) }),
cancellation_token,
}
}
@@ -314,8 +333,9 @@ impl Future for WaitForCancellationFutureOwned {
// # Safety
//
// cancellation_token is dropped after future due to the field ordering.
this.future
.set(unsafe { Self::new_future(this.cancellation_token) });
this.future.set(MaybeDangling::new(unsafe {
Self::new_future(this.cancellation_token)
}));
}
}
}
@@ -1,12 +1,12 @@
//! This mod provides the logic for the inner tree structure of the CancellationToken.
//!
//! CancellationTokens are only light handles with references to TreeNode.
//! All the logic is actually implemented in the TreeNode.
//! CancellationTokens are only light handles with references to [`TreeNode`].
//! All the logic is actually implemented in the [`TreeNode`].
//!
//! A TreeNode is part of the cancellation tree and may have one parent and an arbitrary number of
//! A [`TreeNode`] is part of the cancellation tree and may have one parent and an arbitrary number of
//! children.
//!
//! A TreeNode can receive the request to perform a cancellation through a CancellationToken.
//! A [`TreeNode`] can receive the request to perform a cancellation through a CancellationToken.
//! This cancellation request will cancel the node and all of its descendants.
//!
//! As soon as a node cannot get cancelled any more (because it was already cancelled or it has no
@@ -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);
}
}
+50 -9
View File
@@ -44,7 +44,7 @@ enum State<T> {
pub struct PollSender<T> {
sender: Option<Sender<T>>,
state: State<T>,
acquire: ReusableBoxFuture<'static, Result<OwnedPermit<T>, PollSendError<T>>>,
acquire: PollSenderFuture<T>,
}
// Creates a future for acquiring a permit from the underlying channel. This is used to ensure
@@ -64,13 +64,56 @@ async fn make_acquire_future<T>(
}
}
impl<T: Send + 'static> PollSender<T> {
type InnerFuture<'a, T> = ReusableBoxFuture<'a, Result<OwnedPermit<T>, PollSendError<T>>>;
#[derive(Debug)]
// TODO: This should be replace with a type_alias_impl_trait to eliminate `'static` and all the transmutes
struct PollSenderFuture<T>(InnerFuture<'static, T>);
impl<T> PollSenderFuture<T> {
/// Create with an empty inner future with no `Send` bound.
fn empty() -> Self {
// We don't use `make_acquire_future` here because our relaxed bounds on `T` are not
// compatible with the transitive bounds required by `Sender<T>`.
Self(ReusableBoxFuture::new(async { unreachable!() }))
}
}
impl<T: Send> PollSenderFuture<T> {
/// Create with an empty inner future.
fn new() -> Self {
let v = InnerFuture::new(make_acquire_future(None));
// This is safe because `make_acquire_future(None)` is actually `'static`
Self(unsafe { mem::transmute::<InnerFuture<'_, T>, InnerFuture<'static, T>>(v) })
}
/// Poll the inner future.
fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Result<OwnedPermit<T>, PollSendError<T>>> {
self.0.poll(cx)
}
/// Replace the inner future.
fn set(&mut self, sender: Option<Sender<T>>) {
let inner: *mut InnerFuture<'static, T> = &mut self.0;
let inner: *mut InnerFuture<'_, T> = inner.cast();
// SAFETY: The `make_acquire_future(sender)` future must not exist after the type `T`
// becomes invalid, and this casts away the type-level lifetime check for that. However, the
// inner future is never moved out of this `PollSenderFuture<T>`, so the future will not
// live longer than the `PollSenderFuture<T>` lives. A `PollSenderFuture<T>` is guaranteed
// to not exist after the type `T` becomes invalid, because it is annotated with a `T`, so
// this is ok.
let inner = unsafe { &mut *inner };
inner.set(make_acquire_future(sender));
}
}
impl<T: Send> PollSender<T> {
/// Creates a new `PollSender`.
pub fn new(sender: Sender<T>) -> Self {
Self {
sender: Some(sender.clone()),
state: State::Idle(sender),
acquire: ReusableBoxFuture::new(make_acquire_future(None)),
acquire: PollSenderFuture::new(),
}
}
@@ -97,7 +140,7 @@ impl<T: Send + 'static> PollSender<T> {
State::Idle(sender) => {
// Start trying to acquire a permit to reserve a slot for our send, and
// immediately loop back around to poll it the first time.
self.acquire.set(make_acquire_future(Some(sender)));
self.acquire.set(Some(sender));
(None, State::Acquiring)
}
State::Acquiring => match self.acquire.poll(cx) {
@@ -194,7 +237,7 @@ impl<T: Send + 'static> PollSender<T> {
match self.state {
State::Idle(_) => self.state = State::Closed,
State::Acquiring => {
self.acquire.set(make_acquire_future(None));
self.acquire.set(None);
self.state = State::Closed;
}
_ => {}
@@ -215,7 +258,7 @@ impl<T: Send + 'static> PollSender<T> {
// We're currently trying to reserve a slot to send into.
State::Acquiring => {
// Replacing the future drops the in-flight one.
self.acquire.set(make_acquire_future(None));
self.acquire.set(None);
// If we haven't closed yet, we have to clone our stored sender since we have no way
// to get it back from the acquire future we just dropped.
@@ -255,9 +298,7 @@ impl<T> Clone for PollSender<T> {
Self {
sender,
state,
// We don't use `make_acquire_future` here because our relaxed bounds on `T` are not
// compatible with the transitive bounds required by `Sender<T>`.
acquire: ReusableBoxFuture::new(async { unreachable!() }),
acquire: PollSenderFuture::empty(),
}
}
}
+2 -2
View File
@@ -29,7 +29,7 @@ impl PollSemaphore {
/// Closes the semaphore.
pub fn close(&self) {
self.semaphore.close()
self.semaphore.close();
}
/// Obtain a clone of the inner semaphore.
@@ -166,6 +166,6 @@ impl fmt::Debug for PollSemaphore {
impl AsRef<Semaphore> for PollSemaphore {
fn as_ref(&self) -> &Semaphore {
&*self.semaphore
&self.semaphore
}
}

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