Compare commits

...
Author SHA1 Message Date
Alice Ryhl 48e35c11d9 chore: release Tokio v1.39.0 (#6711) 2024-07-23 15:30:11 +02:00
Alice Ryhl dd1d37167d macros: accept IntoFuture args for macros (#6710) 2024-07-23 12:52:25 +00:00
Alice Ryhl 6a1a7b1591 chore: prepare tokio-macros v2.4.0 (#6707) 2024-07-23 14:45:06 +02:00
Kenny Kerr 51b03f0334 deps: update to windows-sys v0.52 (#6154) 2024-07-23 14:43:23 +02:00
Thomas de Zeeuw 754a1fb03c deps: update to Mio v1 (#6635) 2024-07-23 14:26:07 +02:00
Sebastian Urban 90b23a9584 metrics: add worker thread id (#6695) 2024-07-23 12:41:33 +02:00
Sebastian Urban b69f16aa21 metrics: add worker_park_unpark_count (#6696) 2024-07-23 09:15:54 +02:00
Alex Butler 6e845b794d time: support IntoFuture with timeout (#6666) 2024-07-22 23:29:18 +02:00
Tim Vilgot Mikael Fredenberg feb742c58e chore: replace num_cpus with available_parallelism (#6709) 2024-07-22 23:15:23 +02:00
Alice Ryhl 15cd5146d4 chore: increase MSRV to 1.70 (#6645) 2024-07-22 18:01:27 +00:00
Alice Ryhl 56f4bc6543 chore: make 1.38 an LTS (#6706) 2024-07-21 18:28:12 +02:00
Motoyuki Kimura 3ad5b6df1a runtime: add cfg for loom specific code (#6694) 2024-07-21 17:26:36 +02:00
Russell Cohen 1be8a8e691 metrics: stabilize num_alive_tasks (#6619) 2024-07-18 22:08:29 +02:00
Motoyuki Kimura da17c61464 task: add size check for user-supplied future (#6692) 2024-07-18 11:54:37 +00:00
Ikko Eltociear Ashimine f71bded943 sync: fix typo in name of test (#6693) 2024-07-18 11:05:16 +02:00
Motoyuki Kimura fc058b9561 io: update tokio::io::stdout documentation (#6674) 2024-07-16 19:34:09 +02:00
wathenjiang c0280624f3 Merge 'tokio-1.38.1' into 'master' (#6689) 2024-07-17 00:02:27 +08:00
Evan Rittenhouse 1e286689ff runtime: fix yield_calls_park_before_scheduling_again test (#6679) 2024-07-16 17:29:19 +02:00
Weijia Jiang 14b9f71157 chore: release Tokio v1.38.1 (#6688) 2024-07-16 17:16:29 +02:00
Weijia Jiang 24344dfe4b time: fix race condition leading to lost timers (#6683) 2024-07-16 13:25:59 +00:00
Paolo Barbolini 15925efe43 macros: fix doc format issue (#6686) 2024-07-16 12:13:41 +02:00
Matthew Leach 4825c444eb test: fix tests when '-' is absent from kernel version (#6681)
On my machine, any test that calls `is_pidfd_available` fails as my
kernel string does not contain a '-'; the code expects there to be one.

Fix the test so that is works regardless on whether the kernel string
contains a '-'.
2024-07-14 13:38:23 +09:00
sharpened-nacho c8f3539bc1 stream: make stream adapters public (#6658) 2024-07-02 20:49:34 +00:00
Alice Ryhl b2e4c5fc9e io: hardcode platform list in short-read optimization (#6668) 2024-07-02 20:25:49 +00:00
teor 4d0d89fb70 task: document behavior of JoinSet::try_join_next when all tasks are running (#6671) 2024-07-02 17:54:59 +02:00
二手掉包工程师 fe7285d3d1 sync: add {Receiver,UnboundedReceiver}::{sender_strong_count,sender_weak_count} (#6661) 2024-07-02 23:41:15 +08:00
Michael MaciasandWeijia Jiang dff4ecd0e7 io: implement AsyncSeek for Empty (#6663)
* io: implement `AsyncSeek` for `Empty`

* io: add more tests for seeking `Empty`

This adds more tests seeking with other kinds of `SeekFrom`. It follows
the same structure as used in
rust-lang/rust@f1cd17961c.

See <https://github.com/tokio-rs/tokio/pull/6663#discussion_r1659491016>.

* io: add inline attribute to `AsyncSeek` implementations

This follows the style of the other trait implementations of `Empty`.

See <https://github.com/tokio-rs/tokio/pull/6663#discussion_r1658835942>.

---------

Co-authored-by: Weijia Jiang <[email protected]>
2024-07-01 09:50:49 +08:00
Alice Ryhl 68d0e3cb5f metrics: rename num_active_tasks to num_alive_tasks (#6667) 2024-06-30 15:02:29 +02:00
Tobias Nießen 65d0e08d39 runtime: fix typo in unhandled_panic (#6660) 2024-06-28 01:10:14 +09:00
Hai-Hsin 06582776a5 codec: fix length_delimited docs examples (#6638) 2024-06-23 13:52:51 +09:00
Eric Seppanen ed4ddf443d io: fix trait bounds on impl Sink for StreamReader (#6647)
This impl had a bound on `StreamReader<S, E>`; this is incorrect
because:
- The second generic parameter to `StreamReader` is not an error type;
  it's a buffer type.
- The `Stream` error type in `StreamReader` should not need to be the
  same as the `Sink` error type.

This "passthrough" `Sink` impl was effectively unusable because it
required the `Sink` error type be the same as the `StreamReader` buffer
type.

Resolve this by allowing the `StreamReader` buffer to be anything in
this impl.
2024-06-21 15:00:33 +02:00
Alice Ryhl 9a75d6f7f7 metrics: use MetricAtomic* for task counters (#6624) 2024-06-17 08:33:08 +00:00
Uwe Klotz 3bf4f93854 sync: add watch::Sender::same_channel (#6637) 2024-06-15 21:11:35 +02:00
FabijanC 39cf6bba00 macros: typo fix in join.rs and try_join.rs (#6641) 2024-06-15 21:10:47 +02:00
Weijia Jiang 8480a180e6 time: avoid traversing entries in the time wheel twice (#6584) 2024-06-14 11:03:47 +02:00
Timo 53ea44bfb9 sync: add CancellationToken::run_until_cancelled (#6618) 2024-06-13 10:58:45 +02:00
Weijia Jiang a865ca139a rt: relaxed trait bounds for LinkedList::into_guarded (#6630) 2024-06-13 08:50:28 +02:00
Niki C 479f736935 io: improve panic message of ReadBuf::put_slice() (#6629) 2024-06-13 01:09:59 +09:00
Marek Kuskowski 17555d71d9 sync: implement Default for watch::Sender (#6626) 2024-06-10 10:44:45 +02:00
Conrad Ludgate 341b5daa6e metrics: add spawned_tasks_count, rename active_tasks_count (#6114) 2024-06-09 12:25:54 +02:00
Rob Ede 53b586c5b9 task: stabilize consume_budget (#6622) 2024-06-08 22:17:06 +02:00
Hai-Hsin 833ee027d0 macros: allow unhandled_panic behavior for #[tokio::main] and #[tokio::test] (#6593) 2024-06-07 20:48:56 +09:00
Aaron Schweiger 126ce89bb4 task: implement Clone for AbortHandle (#6621) 2024-06-07 09:17:25 +02:00
Russell Cohen 8e15c234c6 metrics: add MetricAtomicUsize for usized-metrics (#6598) 2024-06-06 10:08:46 +02:00
John-John Tedro 16fccafb41 docs: fix docsrs builds with the fs feature enabled (#6585) 2024-06-05 08:20:27 +00:00
Armillus 3f397ccded io: read during write in copy_bidirectional and copy (#6532) 2024-06-05 00:29:28 +02:00
Emil Loer 49609d073f test: make Spawn forward size_hint (#6607) 2024-06-04 23:42:42 +02:00
Alan Somers a91d43823c ci: update FreeBSD CI environment (#6616)
Use the newly released FreeBSD 14.1.
2024-06-04 23:37:13 +02:00
Timo 8fca6f6dad process: add Command::as_std_mut (#6608) 2024-06-04 13:34:22 +02:00
Weijia Jiang 75c953bd63 time: fix big time panic issue (#6612) 2024-06-04 09:45:35 +02:00
Alice Ryhl 14c17fc096 chore: prepare Tokio v1.38.0 (#6601) 2024-05-30 13:24:58 -07:00
Alice Ryhl 65cbf730de chore: prepare tokio-macros v2.3.0 (#6600) 2024-05-30 13:21:51 -07:00
Alice Ryhl dbf93c7184 sync: fix incorrect is_empty on mpsc block boundaries (#6603) 2024-05-30 13:21:16 -07:00
Weijia Jiang 873cb8ae2f runtime: move task out of the lifo_slot in block_in_place (#6596) 2024-05-30 10:45:45 +02:00
SteveLauC 97bb47b480 task: fix a typo in doc of LocalSet::run_until (#6599) 2024-05-30 10:44:53 +02:00
Russell Cohen 86658bd87d metrics: stabilize RuntimeMetrics::worker_count (#6556) 2024-05-28 21:55:20 +02:00
Pau Freixes 9e00b266e0 sync: add Notify::notify_last (#6520) 2024-05-27 11:18:34 +00:00
Maxwell Borden 6c42d286b3 net: fix misleading NamedPipeServer example (#6590)
The previous NamedPipeServer doc example seemed to imply that the return
type of `NamedPipeServer::connect()` was a
`Future<Result<some_kind_of_client>>>`, however, `connect()` returns a
`Future<Result<()>>>`. The following line of code reopening the pipe
would shadow the newly connected pipe immediately, making the following
spawned task pointless. Hopefully these changes make it more clear what
should be happening in the example.
2024-05-26 12:25:19 +02:00
Josh McKinney 3a6fdc0568 license: fix formatting and remove year in licenses (#6451) 2024-05-26 09:29:27 +00:00
Conrad Ludgate 2890d0c3db metrics: fix blocking_threads count (#6551) 2024-05-25 21:49:17 +00:00
Maxwell Borden 0a85a9662d net: implement Clone for NamedPipeInfo (#6586) 2024-05-25 22:14:59 +02:00
Alan SomersandFrederick Mayle 12920cea45 tests: update nix and mio-aio dependencies (#6552)
nix 0.29.0 and mio-aio 0.9.0 use I/O Safety.

Co-authored-by: Frederick Mayle <[email protected]>
2024-05-25 17:30:36 +02:00
Russell Cohen cba86cf1b1 metrics: add MetricAtomicU64 and use in metrics (#6574) 2024-05-23 18:41:01 +02:00
Roy Wellington 16ef7b1fd5 docs: fix stdin documentation (#6581)
The code for `output` indicates that only sets `stdout` and `stderr`, 
yet the docs for `stdin` indicated that it too would be set. This seems 
like it was just a simple copy/paste typo, so correct `stdin` to note
that it just defaults to `inherit`.

Fixes #6577.
2024-05-23 13:52:57 +09:00
Weijia Jiang 1914e1e4b9 time: use sharding for timer implementation (#6534) 2024-05-22 11:54:26 +00:00
二手掉包工程师 e62c3e9269 task: add tokio::task::join_set::Builder::spawn_blocking (#6578)
Signed-off-by: hi-rustin <[email protected]>
2024-05-22 13:35:12 +02:00
M.Amin Rayej 0b651c070f fs: add File::create_new (#6573) 2024-05-22 13:24:48 +02:00
Alice Ryhl a02407171a task: clarify that you can't abort spawn_blocking tasks (#6571) 2024-05-19 16:09:22 +00:00
Michael Scholten daa89017da ci: fix new clippy warnings (#6569) 2024-05-18 10:09:37 +02:00
Alice Ryhl 35c6fd9924 ci: set RUSTUP_WINDOWS_PATH_ADD_BIN=1 (#6568) 2024-05-17 13:03:22 +02:00
Russell Cohen d221c500fa docs: gate taskdump compiler_error behind not(doc) (#6564) 2024-05-16 12:14:07 +02:00
John-John Tedro ced7739f69 tokio: use ptr::addr_of instead of pointer arithmetic in linked_list (#6561) 2024-05-16 10:30:48 +02:00
William Wen 18e048d764 sync: always drop message in destructor for oneshot receiver (#6558) 2024-05-15 13:57:35 +02:00
Weijia Jiang df77063485 time: remove the true_when field in TimerShared (#6563) 2024-05-15 09:32:39 +02:00
Alice Ryhl d085260ee0 metrics: fix flaky injection_queue_depth test (#6559) 2024-05-14 21:01:49 +00:00
Sebastian SchildtandAkhil Thankachan Thomas 227979f091 net: support QNX OS (#6421)
Co-authored-by: Akhil Thankachan Thomas <[email protected]>
2024-05-14 13:11:37 +02:00
Kezhu Wang 6fcd9c0217 macros: make #[tokio::test] append #[test] at the end of the attribute list (#6497) 2024-05-05 16:28:59 +00:00
Alice Ryhl 2a0df5fb05 ci: bump nightly to nightly-2024-05-05 (#6538) 2024-05-05 15:43:11 +00:00
Alice Ryhl b652a4e64c util: no default features for hashbrown (#6541) 2024-05-05 17:18:49 +02:00
Alice Ryhl 7f59a6ea85 tests: remove tests module from integration tests (#6540) 2024-05-05 17:18:37 +02:00
Alice Ryhl 75e5b3d96d runtime: ignore yield_calls_park_before_scheduling_again test (#6537) 2024-05-05 11:06:56 +02:00
Alice Ryhl cdf9d997dc chore: prepare tokio-util v0.7.11 (#6535) 2024-05-04 22:32:31 +02:00
Weijia Jiang f6eb1ee196 time: lazily init timers on first poll (#6512) 2024-05-03 15:37:52 +02:00
Matthijs Brobbel b7d4fba707 sync: add mpsc::Receiver::{capacity,max_capacity} (#6511) 2024-05-03 13:44:01 +02:00
Motoyuki Kimura 3c8d8e60ca chore: fix latest rust-1.78.0 warnings (#6528) 2024-05-03 11:23:25 +02:00
Paolo Barbolini e971a5e7d7 util: use FIFO ordering in WakeList (#6521) 2024-05-01 14:19:23 +00:00
Paolo Barbolini 28439e2269 time: clean up implementation (#6517) 2024-05-01 14:50:40 +02:00
João Oliveira d33fdd86a3 time: check for overflow in Interval::poll_tick (#6487) 2024-04-25 09:02:11 +02:00
Valentin 731dde21dc runtime: clarify misleading use of UnsafeCell::with_mut (#6513)
The code that we're removing calls UnsafeCell::with_mut with the
argument `std::mem::drop`. This is misleading because the use of `drop`
has no effect. `with_mut` takes an argument of type
`impl FnOnce(*mut T) -> R`. The argument to the argument function is a
pointer. Dropping a pointer has no effect.

The comment above the first instance of this pattern claims that this
releases some resource. This is false because the call has no effect.
The intention might have been to drop the value behind the pointer. If
this did happen, it would be a bug because the resource (`waker`) would
be dropped again at the end of the function when the containing object
is dropped.

I looked through the history of this code. This code originally called
`with_mut` with the argument `|_| ()`. Calling `with_mut` with an
argument function that does nothing has a side effect when testing with
loom. When testing with loom, the code uses loom's UnsafeCell type
instead of std's. The intention of the code was likely to make use of
that side effect because we expect to have exclusive access here as we
are going to drop the containing object. The side effect is that loom
checks that Rust's reference uniqueness properties are upheld.

To continue to check this, I have only removed the use of `drop` while
keeping `with_mut`. It would be even better to have loom check this
implicitly when UnsafeCell is dropped. I created an issue about this in
loom [1].

Links: https://github.com/tokio-rs/loom/issues/349 [1]
2024-04-25 09:01:15 +02:00
LongYinan 9ed595767d wasm: support rt-multi-thread with wasm32-wasi-preview1-threads (#6510) 2024-04-24 09:45:54 +02:00
avtrujillo a73d6bf33a macros: #[cfg(not(test))] is no longer necessary for main macros (#6508) 2024-04-23 14:05:14 +02:00
Alex Yusiuk 8093712604 io: add copy_bidirectional_with_sizes (#6500) 2024-04-22 11:03:33 +02:00
Jainil Patel 19618905de time: fix test-util requirement for pause and resume in docs (#6503) 2024-04-21 14:12:36 +02:00
Motoyuki Kimura 2438b43671 sync: instrument Semaphore for task dumps (#6499) 2024-04-20 12:35:11 +02:00
Kevin Reid 125a185749 io: fix obsolete reference in ReadHalf::unsplit() documentation (#6498) 2024-04-20 14:21:31 +09:00
Kenny f3bfed30eb metrics: document Runtime::metrics (#6496) 2024-04-19 10:07:55 +02:00
Alice Ryhl 5ba12a7145 sync: document that Barrier::wait is not cancel safe (#6494)
Signed-off-by: Alice Ryhl <[email protected]>
2024-04-18 17:21:34 +02:00
Alice Ryhl 9c6ff3b7e5 sync: explain relation between watch::Sender::{subscribe,closed} (#6490)
Signed-off-by: Alice Ryhl <[email protected]>
2024-04-17 16:16:14 +02:00
Alice Ryhl 1f6fc55917 net: add missing types to module docs (#6482)
Signed-off-by: Alice Ryhl <[email protected]>
2024-04-13 17:22:11 +02:00
Josh McKinney a5bd0deaa5 docs: add --locked to cargo install commands (#6479) 2024-04-13 11:44:33 +02:00
Motoyuki Kimura ee68c1a8c2 sync: add examples for SemaphorePermit, OwnedSemaphorePermit (#6477) 2024-04-12 13:14:08 +02:00
Alice Ryhl 90e20bcad3 sync: use usize instead of u32 for SemaphorePermit::split (#6478)
Signed-off-by: Alice Ryhl <[email protected]>
2024-04-11 17:21:55 +00:00
Alice Ryhl bdf4c142c9 fs: rewrite file system docs (#6467) 2024-04-11 10:33:51 +02:00
dtolnay 43de364cd9 readme: mention cargo-docs-rs in CONTRIBUTING.md (#6475) 2024-04-11 10:33:01 +02:00
vvvviiv 224fea4f3c sync: add split method to the semaphore permit (#6472) 2024-04-10 15:55:44 +02:00
Pierre Fenoll ccee1d4493 task: make LocalKey::get work with Clone types (#6433)
Signed-off-by: Pierre Fenoll <[email protected]>
2024-04-10 11:41:58 +02:00
Rafael Bachmann be9328da75 chore: fix clippy warnings (#6466) 2024-04-08 20:44:18 +09:00
Aoi Kurokawa b6d74ac4eb runtime: add links in docs for issues and crates (#6473) 2024-04-08 10:47:57 +02:00
Alice Ryhl 5712aaf995 metrics: ignore worker_steal_count test (#6471) 2024-04-07 16:30:33 +02:00
dtolnay 431b7c5263 macros: render more comprehensible documentation for select! (#6468) 2024-04-06 22:57:29 +00:00
Quentin Perez 01ed7b55f7 net: add Apple visionOS support (#6465) 2024-04-06 18:09:43 +02:00
Taiki Endo 035a968bdd chore: fix typo (#6464) 2024-04-06 00:43:13 +09:00
Aoi Kurokawa a1acfd8c20 readme: add readme for tokio-stream (#6456) 2024-04-03 15:36:08 +09:00
Motoyuki Kimura 328a02c1ce runtime: improvements for global_queue_interval (#6445) 2024-04-03 14:10:13 +09:00
Bharath Vedartham e9ae5d4ce9 io: implement AsyncBufRead for Join (#6449)
Fixes: #6446
2024-04-02 13:00:44 +02:00
Jens Reidel d298049299 codec: make tracing feature optional for codecs (#6434)
Signed-off-by: Jens Reidel <[email protected]>
2024-03-30 18:20:05 +01:00
Timo 1fcb77db34 io: add T: ?Sized to tokio_util::io::poll_read_buf (#6441) 2024-03-30 12:13:06 +00:00
Siech0 3936ebdfe4 chore: update CI to clippy 1.77 (#6443) 2024-03-30 15:49:42 +09:00
Alice Ryhl 9c337ca1a3 chore: prepare Tokio v1.37.0 (#6435) 2024-03-28 17:34:42 +01:00
M.Amin Rayej e542501439 io: document cancel safety of AsyncBufReadExt::fill_buf (#6431) 2024-03-26 20:45:15 +03:30
M.Amin Rayej 4601c84718 stream: add next_many and poll_next_many to StreamMap (#6409) 2024-03-26 18:38:53 +03:30
M.Amin Rayej deff2524c3 util: document cancel safety of SinkExt::send and StreamExt::next (#6417) 2024-03-25 01:53:01 +03:30
Matthieu Le brazidec (r3v2d0g) 4565b81097 sync: add a rwlock() method to owned RwLock guards (#6418) 2024-03-25 00:37:05 +09:00
Ilson Balliego 3ce4720a45 sync: add is_closed, is_empty, and len to mpsc receivers (#6348)
Fixes: #4638
2024-03-24 14:46:02 +01:00
M.Amin Rayej 8342e4b524 util: assert compatibility between LengthDelimitedCodec options (#6414) 2024-03-24 01:12:24 +03:30
Motoyuki Kimura 4c453e9790 readme: add description about benchmarks (#6425) 2024-03-23 18:11:41 +03:30
M.Amin Rayej 1846483f19 sync: expose strong and weak counts of mpsc sender handles (#6405) 2024-03-22 23:25:33 +03:30
Christopher Acosta baad270b98 sync: add Semaphore example for limiting the number of outgoing requests (#6419) 2024-03-22 21:33:28 +03:30
Motoyuki Kimura f9d78fbe92 fs: add set_max_buf_size to tokio::fs::File (#6411) 2024-03-22 17:28:29 +03:30
M.Amin Rayej bb25a06f34 chore: fix dead code warnings (#6423) 2024-03-22 13:44:18 +03:30
M.Amin Rayej d51f16855b runtime: panic if unhandled_panic is enabled when not supported (#6410) 2024-03-20 09:48:09 +01:00
M.Amin Rayej 5baa8d58ec sync: remove stale comment (#6406) 2024-03-17 19:29:10 +03:30
M.Amin Rayej bd51feac47 readme: fix running loom tests guide (#6408) 2024-03-17 14:57:31 +03:30
M.Amin Rayej b2896feb5b io: use Mutex instead of spinlock (#6403) 2024-03-16 12:36:20 +03:30
Alice Ryhl 3d0d0fd2af chore: prepare tokio-test v0.4.4 (#6400) 2024-03-14 22:10:15 +01:00
Alice Ryhl 7cfb100796 chore: prepare tokio-stream v0.1.15 (#6401) 2024-03-14 22:10:05 +01:00
M.Amin Rayej e37bd63854 io: implement try_new and try_with_interest for AsyncFd (#6345) 2024-03-14 22:48:26 +09:00
Vincent Palancher c9e75785c8 sync: remove 'static bound on impl Sink for PollSender (#6397)
In PR #5665, the `'static` bound has been removed on values sent into
`PollSender`. One of this bound was remaining on the `PollSender`
implementation of `Sink`. This patch removes it and adds some tests on
the `Sink` interface for `PollSender`.
2024-03-14 10:23:59 +01:00
kim / Motoyuki Kimura 3141ed6228 sync: update watch channel docs (#6395) 2024-03-13 17:24:19 +01:00
Alice Ryhl ea1cfbdb97 sync: reorder const_new before new_with (#6392) 2024-03-12 14:22:25 +00:00
Alice Ryhl fb2dc97468 readme: update commit message guidelines (#6393) 2024-03-12 14:21:08 +00:00
kim / Motoyuki Kimura a3d2548789 sync: implement Clone for watch::Sender (#6388) 2024-03-12 10:25:43 +01:00
Yotam Ofek b4ab6472de signal: fix typo in argument name (#6389) 2024-03-10 16:24:07 +01:00
M.Amin Rayej 3133af42e1 runtime: make the enter example deterministic (#6351) 2024-03-10 11:37:18 +01:00
Patrick McGleenon 1f924f95f1 chore: fix deprecated circleci image (#6379) 2024-03-06 00:23:38 +09:00
kim / Motoyuki Kimura f6d061919f test: mark Spawn as #[must_use] (#6371) 2024-03-04 16:56:40 +01:00
Lev Kokotov f5ca423bf1 sync: fix missing period in broadcast docs (#6377) 2024-03-04 08:40:03 +00:00
Alice Ryhl e0d33c4a20 tokio: mark 1.36 as an LTS release (#6375) 2024-03-03 20:44:38 +00:00
Alice Ryhl 3125279697 ci: update FreeBSD image to 14 (#6376) 2024-03-03 20:12:58 +00:00
Kevin Reid 5658d7c503 runtime: add doc link from Runtime to #[tokio::main] (#6366) 2024-02-25 22:17:26 +01:00
Patrick McGleenon a2096049ee ci: downgrade to QEMU 7.2 for cross-compile builds (#6361) (#6363) 2024-02-23 12:08:57 +09:00
kim / Motoyuki Kimura 94db07b379 task: add TaskLocalFuture::take_value (#6340) 2024-02-21 11:23:54 +01:00
qj 099ee23b65 docs: fix docsrs build without net feature (#6360) 2024-02-19 11:37:09 +00:00
M.Amin Rayej b32826bc93 runtime: fix leaking registration entries when os registration fails (#6329) 2024-02-15 03:13:08 +03:30
Gil Shoshan 2ce1cee0f9 macros: allow select with only else branch (#6339) 2024-02-13 09:28:16 +01:00
M.Amin Rayej 84e41d4aff io: document cancel safety of AsyncReadExt's primitive read functions (#6337) 2024-02-11 20:45:13 +03:30
Val Lorentz db6929ad62 task: fix documentation links (#6336) 2024-02-11 15:08:01 +01:00
M.Amin Rayej e25d661e08 util: add examples for FramedRead and FramedWrite (#6310) 2024-02-11 02:38:44 +03:30
Patrick McGleenonandRafael Bachmann e392c4ff1e chore: update CI to clippy 1.76 (#6334)
Co-authored-by: Rafael Bachmann <[email protected]>
2024-02-10 10:45:40 +01:00
kim / Motoyuki Kimura 0fbde0e94b sync: add forget_permits method to semaphore (#6331) 2024-02-08 17:40:55 +03:30
Jack Wrenn 10c9eeb6c2 runtime: include task Id in taskdumps (#6328)
Task `Id`s provide a semi-stable identifier for monitoring task
state across task dumps.

Fixes #6313
2024-02-05 22:53:20 +01:00
Alice Ryhl 47a5fe3a12 metrics: fix worker_steal_count test hanging (#6327) 2024-02-05 15:59:04 +00:00
Alice Ryhl fbdf539ac2 sync: make downgrade must_use (#6326) 2024-02-05 08:46:04 +01:00
Alice Ryhl 63caced26f tokio: update CHANGELOG.md (#6324) 2024-02-03 21:57:58 +01:00
erikdesjardins 0b31b2a195 task: implement FromIterator for JoinSet (#6300) 2024-02-02 13:30:49 +01:00
Augusto Hack cb197def68 docs: transition_to_idle doesn't return boolean (#6320) 2024-02-02 13:29:35 +01:00
M.Amin Rayej eaf81ed324 chore: prepare Tokio v1.36.0 (#6312) 2024-02-02 11:13:46 +00:00
M.Amin Rayej 53f9e5a357 ci: make sure dictionary words are sorted and unique (#6316) 2024-01-31 16:11:54 +03:30
M.Amin Rayej 9077762545 net: expose keepalive option on TcpSocket (#6311) 2024-01-30 21:46:37 +03:30
Owen Leung 131e7b4e49 ci: add spellchecking (#6297) 2024-01-29 10:53:43 +01:00
M.Amin Rayej e53b92a993 io: clarify clear_ready docs (#6304) 2024-01-27 20:30:15 +00:00
Sergei Fomin 7536132065 sync: use AtomicBool in broadcast channel future (#6298) 2024-01-27 18:52:55 +00:00
JungChul Shin b6d0c9091d macros: fix trait_method breaking change detection (#6308) 2024-01-26 14:51:44 +01:00
kim / Motoyuki Kimura 4846959e8a runtime: remove obsolete comment (#6303) 2024-01-24 16:22:28 +01:00
M.Amin Rayej ec3038357f net: add UnixSocket (#6290) 2024-01-22 11:35:02 +01:00
Daniel Sedlak f80bbec28f io: simplify check for empty slice (#6293) 2024-01-18 10:59:22 +00:00
M.Amin Rayej eab26a662c net: document that *Fd traits on TcpSocket are unix-only (#6294) 2024-01-18 10:35:46 +01:00
M.Amin Rayej 58edfc61ab ci: verify that tests work with panic=abort (#6283) 2024-01-16 16:10:23 +01:00
Evan Rittenhouse bfd7b08067 time: add FutureExt::timeout (#6276) 2024-01-16 09:51:38 +01:00
M.Amin Rayej 12ce924fb9 task: add JoinSet::try_join_next (#6280) 2024-01-14 10:34:30 +00:00
Jiahao XU e4f9bcb577 process: use pidfd on Linux when available (#6152)
Signed-off-by: Jiahao XU <[email protected]>
2024-01-11 13:58:23 +01:00
zixuan zhao 8463af92af sync: document FIFO behavior of tokio::sync::Mutex (#6279)
Signed-off-by: azuredream <[email protected]>
2024-01-11 11:15:57 +01:00
Taiki Endo 84c5674c60 ci: update clippy version to 1.75 (#6273) 2024-01-07 02:33:37 +09:00
Rustin 3275cfb638 io: make copy cooperative (#6265) 2024-01-06 17:22:26 +01:00
Taiki Endo 9780bf491f ci: update cargo-check-external-types to 0.1.10 (#6274) 2024-01-07 00:53:08 +09:00
Taiki Endo d6ba535ceb ci: update actions/checkout action to v4 (#6272) 2024-01-06 20:30:28 +09:00
Alan Somers e929d0e8b9 tests: update mio-aio to 0.8 (#6269)
This is a test-only dependency.  The main reason for the update is to
avoid transitively depending on a Nix version with a CVE.

mio-aio 0.8.0 has a substantially different API than 0.7.0.  Notably, it
no longer includes any lio_listio functionality.  So to test Tokio's
handling of EVFILT_LIO events we must go low-level and call
libc::lio_listio directly.
2024-01-06 11:14:32 +00:00
Alan Somers 2f730d4d5a ci: update FreeBSD CI environment to 13.2 (#6270)
Because 13.1 is EoL
2024-01-06 11:59:04 +01:00
Alice Ryhl c7e7f203ee chore: typographic improvements (#6262) 2024-01-03 18:01:27 +00:00
Théodore Prévot 7c606ab44a sync: add Sender::{try_,}reserve_many (#6205) 2024-01-02 16:34:56 +00:00
Alice Ryhl 2d2faf6014 rt: improve robustness of wake_in_drop_after_panic test (#6238) 2024-01-02 17:25:51 +01:00
Jesse Schalken c029771247 task: fix typo (#6261) 2024-01-02 12:08:11 +01:00
Owen Leung 7341004535 sync: add {Receiver,UnboundedReceiver}::poll_recv_many (#6236) 2024-01-01 14:26:58 +01:00
Paul Olteanu 02b779e315 sync: add watch::Receiver::mark_unchanged (#6252) 2023-12-30 20:05:37 +00:00
Tymoteusz Wiśniewski 48345d6e48 net: add support for anonymous unix pipes (#6127) 2023-12-30 15:37:47 +00:00
kim / Motoyuki Kimura 581cd41d79 io: make repeat and sink cooperative (#6254) 2023-12-30 15:26:16 +01:00
kim / Motoyuki Kimura 5f7fe8fd0d ci: fix new warnings on 1.75.0 (#6255) 2023-12-29 17:22:54 +01:00
Alice Ryhl 52f28dcb4f benches: fix benchmarking conflicts for real this time (#6246) 2023-12-28 09:52:18 +00:00
Alice Ryhl d51308b035 ci: fix configured clippy msrv (#6244) 2023-12-23 00:28:14 +00:00
Alice Ryhl 7cae89af47 benches: fix benchmarking conflicts (#6243) 2023-12-22 22:43:05 +00:00
Eddy Oyieko 53ad102cf0 util: implement Sink for Either (#6239) 2023-12-22 18:23:04 +01:00
Miccah e7214e3670 io: implement AsyncWrite for Empty (#6235) 2023-12-20 00:05:53 +00:00
Alice Ryhl 433545782e tests: fix name of coop_budget.rs (#6234) 2023-12-19 19:02:02 +00:00
snek bd15bace35 io: add tokio::io::Join (#6220) 2023-12-19 19:51:48 +01:00
Alice Ryhl 410660fd54 Merge 'tokio-1.35.1' into 'master' (#6231) 2023-12-19 18:15:46 +01:00
Alice Ryhl 46ff36386d chore: prepare Tokio v1.35.1 (#6230) 2023-12-19 18:14:34 +01:00
Alice Ryhl 0a1695ebfb Merge 'tokio-1.32.1' into 'tokio-1.35.x' (#6229) 2023-12-19 18:11:16 +01:00
Alice Ryhl c4f0178b74 chore: prepare Tokio v1.32.1 (#6228) 2023-12-19 18:07:05 +01:00
Alice Ryhl 22b3a65934 Merge 'tokio-1.25.3' into 'tokio-1.32.x' (#6227) 2023-12-19 18:00:41 +01:00
Noah Kennedy 0d362339d5 chore: release v1.25.3 (#6223)
# 1.25.3 (December 17thm 2023)

### Fixed
- io: add budgeting to `tokio::runtime::io::registration::async_io` ([#6221])

[#6221]: https://github.com/tokio-rs/tokio/pull/6221
2023-12-19 17:09:44 +01:00
Noah Kennedy ab7313ff6b io: add budgeting to tokio::runtime::io::registration::async_io (#6221)
Fixes #5946.
Fixes #4782.

This change adds budgeting to most of the remaining unbudgeted IO operations which can complete instantly, including datagram send/recv operations and listener socket accepts.

This is particularly significant for scenarios in which resource limits are hit, as it can be common for things like listener tasks to spin when receiving errors and just log them, busy looping worker threads which might otherwise be handling existing connections and closing them.

This can also sometimes lead to complex failure scenarios within datagram systems experiencing resource exhaustion.
2023-12-17 22:34:51 +00:00
oliverandkwfn 4aa7bbff4c chore: typo fixes (#6213)
Co-authored-by: kwfn <[email protected]>
2023-12-11 19:22:55 +00:00
Bryan Baron c06a55088b coop: fix typo in docs for task::unconstrained (#6212) 2023-12-11 17:50:24 +01:00
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
419 changed files with 18262 additions and 6189 deletions
-8
View File
@@ -1,8 +0,0 @@
# See https://github.com/rustsec/rustsec/blob/59e1d2ad0b9cbc6892c26de233d4925074b4b97b/cargo-audit/audit.toml.example for example.
[advisories]
ignore = [
# We depend on nix 0.22 only via mio-aio, a dev-dependency.
# https://github.com/tokio-rs/tokio/pull/4255#issuecomment-974786349
"RUSTSEC-2021-0119",
]
+1 -1
View File
@@ -2,7 +2,7 @@ version: 2.1
jobs:
test-arm:
machine:
image: ubuntu-2004:202101-01
image: default
resource_class: arm.medium
environment:
# Change to pin rust version
+2 -2
View File
@@ -1,10 +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_family: freebsd-13-1
image_family: freebsd-14-1
env:
RUST_STABLE: stable
RUST_NIGHTLY: nightly-2022-10-25
RUST_NIGHTLY: nightly-2024-05-05
RUSTFLAGS: -D warnings
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
-1
View File
@@ -1 +0,0 @@
msrv = "1.56"
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'ci skip')"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Audit Check
# https://github.com/rustsec/audit-check/issues/2
+205 -54
View File
@@ -13,10 +13,11 @@ concurrency:
env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
RUSTUP_WINDOWS_PATH_ADD_BIN: 1
# Change to specific Rust release to pin
rust_stable: stable
rust_nightly: nightly-2023-05-18
rust_clippy: 1.65.0
rust_nightly: nightly-2024-05-05
rust_clippy: '1.77'
# When updating this, also update:
# - README.md
# - tokio/README.md
@@ -25,7 +26,7 @@ env:
# - tokio-util/Cargo.toml
# - tokio-test/Cargo.toml
# - tokio-stream/Cargo.toml
rust_min: 1.63.0
rust_min: '1.70'
defaults:
run:
@@ -44,11 +45,13 @@ jobs:
- test-workspace-all-features
- test-integration-tests-per-feature
- test-parking_lot
- test-tracing-instrumentation
- valgrind
- test-unstable
- miri
- asan
- cross-check
- cross-check-tier3
- cross-test-with-parking_lot
- cross-test-without-parking_lot
- no-atomic-u64-test
@@ -65,10 +68,11 @@ jobs:
- x86_64-fortanix-unknown-sgx
- check-redox
- wasm32-unknown-unknown
- wasm32-wasi
- wasm32-wasip1
- check-external-types
- check-fuzzing
- check-unstable-mt-counters
- check-spelling
steps:
- run: exit 0
@@ -95,7 +99,7 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -127,7 +131,7 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -146,6 +150,34 @@ jobs:
cargo nextest run --workspace --all-features
cargo test --doc --workspace --all-features
test-workspace-all-features-panic-abort:
needs: basics
name: test all crates in the workspace with all features and panic=abort
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- windows-latest
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.rust_nightly }}
- name: Install cargo-nextest
uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- uses: Swatinem/rust-cache@v2
- name: test all --all-features panic=abort
run: |
set -euxo pipefail
RUSTFLAGS="$RUSTFLAGS -C panic=abort -Zpanic-abort-tests" cargo nextest run --workspace --exclude tokio-macros --exclude tests-build --all-features --tests
test-integration-tests-per-feature:
needs: basics
name: Run integration tests for each feature
@@ -157,7 +189,7 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -179,11 +211,10 @@ jobs:
run: cargo hack test --each-feature
working-directory: tests-build
# Check benchmarks. Run of benchmarks is done by bench.yml workflow.
# Check benchmarks.
- name: Check benches
run: cargo check --benches
working-directory: benches
# bench.yml workflow runs benchmarks only on linux.
if: startsWith(matrix.os, 'ubuntu')
test-parking_lot:
@@ -198,7 +229,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -212,12 +243,40 @@ jobs:
- name: Check tests with all features enabled
run: cargo check --workspace --all-features --tests
test-tracing-instrumentation:
# These tests use the as-yet unpublished `tracing-mock` crate to test the
# tracing instrumentation present in Tokio. As such they are placed in
# their own test crate outside of the workspace.
needs: basics
name: test tokio instrumentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.rust_stable }}
- name: Install cargo-nextest
uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- uses: Swatinem/rust-cache@v2
- name: test tracing-instrumentation
run: |
set -euxo pipefail
cargo nextest run
working-directory: tokio/tests/tracing-instrumentation
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
valgrind:
name: valgrind
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -256,7 +315,7 @@ jobs:
- os: ubuntu-latest
- os: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -290,7 +349,7 @@ jobs:
include:
- os: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -324,7 +383,7 @@ jobs:
include:
- os: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -352,7 +411,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -373,7 +432,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install llvm
# Required to resolve symbols in sanitizer output
run: sudo apt-get install -y llvm
@@ -395,7 +454,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Check semver
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
@@ -411,11 +470,9 @@ jobs:
target:
- powerpc-unknown-linux-gnu
- powerpc64-unknown-linux-gnu
- mips-unknown-linux-gnu
- arm-linux-androideabi
- mipsel-unknown-linux-musl
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -427,6 +484,31 @@ jobs:
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
cross-check-tier3:
name: cross-check-tier3
needs: basics
runs-on: ubuntu-latest
strategy:
matrix:
target:
- name: armv7-sony-vita-newlibeabihf
exclude_features: "process,signal,rt-process-signal,full"
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@nightly
with:
toolchain: ${{ env.rust_nightly }}
components: rust-src
- name: Install cargo-hack
uses: taiki-e/install-action@v2
with:
tool: cargo-hack
- uses: Swatinem/rust-cache@v2
- run: cargo hack check -Zbuild-std --workspace --each-feature --target ${{ matrix.target.name }} --exclude-features "${{ matrix.target.exclude_features }}"
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
cross-test-with-parking_lot:
needs: basics
runs-on: ubuntu-latest
@@ -440,7 +522,7 @@ jobs:
- target: aarch64-unknown-linux-gnu
rustflags: --cfg tokio_taskdump
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
@@ -455,6 +537,7 @@ jobs:
- uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: ${{ matrix.target }}
qemu: '7.2'
- uses: Swatinem/rust-cache@v2
- name: Tests run with all features (including parking_lot)
@@ -479,7 +562,7 @@ jobs:
- target: aarch64-unknown-linux-gnu
rustflags: --cfg tokio_taskdump
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
@@ -494,6 +577,7 @@ jobs:
- uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: ${{ matrix.target }}
qemu: '7.2'
- name: Remove `parking_lot` from `full` feature
run: sed -i '0,/parking_lot/{/parking_lot/d;}' tokio/Cargo.toml
@@ -515,7 +599,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -530,6 +614,7 @@ jobs:
- uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: i686-unknown-linux-gnu
qemu: '7.2'
- uses: Swatinem/rust-cache@v2
- name: test tokio --all-features
@@ -545,7 +630,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -578,7 +663,7 @@ jobs:
# Try with unstable and taskdump feature flags
- { name: "--unstable --taskdump", rustflags: "--cfg tokio_unstable -Dwarnings --cfg tokio_taskdump" }
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -597,7 +682,7 @@ jobs:
name: minrust
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_min }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -613,7 +698,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -645,7 +730,7 @@ jobs:
name: fmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -665,7 +750,7 @@ jobs:
name: clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_clippy }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -680,7 +765,7 @@ jobs:
name: docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -698,7 +783,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -714,7 +799,7 @@ jobs:
name: Check README
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Verify that both READMEs are identical
run: diff README.md tokio/README.md
@@ -733,7 +818,7 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -779,7 +864,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -796,7 +881,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@master
with:
@@ -811,7 +896,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -824,17 +909,22 @@ jobs:
run: wasm-pack test --node -- --features "macros sync"
working-directory: tokio
wasm32-wasi:
name: wasm32-wasi
wasm32-wasip1:
name: ${{ matrix.target }}
needs: basics
runs-on: ubuntu-latest
strategy:
matrix:
target:
- wasm32-wasip1
- wasm32-wasip1-threads
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.rust_stable }}
targets: wasm32-wasi
targets: ${{ matrix.target }}
# Install dependencies
- name: Install cargo-hack, wasmtime, and cargo-wasi
@@ -844,28 +934,40 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: WASI test tokio full
run: cargo test -p tokio --target wasm32-wasi --features full
run: cargo test -p tokio --target ${{ matrix.target }} --features full
env:
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -S threads=y --"
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
- name: WASI test tokio-util full
run: cargo test -p tokio-util --target wasm32-wasi --features full
run: cargo test -p tokio-util --target ${{ matrix.target }} --features full
env:
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -S threads=y --"
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
- name: WASI test tokio-stream
run: cargo test -p tokio-stream --target wasm32-wasi --features time,net,io-util,sync
run: cargo test -p tokio-stream --target ${{ matrix.target }} --features time,net,io-util,sync
env:
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -S threads=y --"
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
- name: test tests-integration --features wasi-rt
# TODO: this should become: `cargo hack wasi test --each-feature`
run: cargo wasi test --test rt_yield --features wasi-rt
if: matrix.target == 'wasm32-wasip1'
working-directory: tests-integration
- name: test tests-integration --features wasi-threads-rt
run: cargo test --target ${{ matrix.target }} --features wasi-threads-rt
if: matrix.target == 'wasm32-wasip1-threads'
working-directory: tests-integration
env:
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -S threads=y --"
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
check-external-types:
name: check-external-types (${{ matrix.os }})
needs: basics
@@ -878,9 +980,9 @@ jobs:
rust:
# `check-external-types` requires a specific Rust nightly version. See
# the README for details: https://github.com/awslabs/cargo-check-external-types
- nightly-2023-05-31
- nightly-2023-10-21
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ matrix.rust }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -889,17 +991,34 @@ jobs:
- name: Install cargo-check-external-types
uses: taiki-e/cache-cargo-install-action@v1
with:
tool: [email protected].7
tool: [email protected].10
- name: check-external-types
run: cargo check-external-types --all-features --config external-types.toml
run: cargo check-external-types --all-features
working-directory: tokio
check-unexpected-lints-cfgs:
name: check unexpected lints and cfgs
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_nightly }}
- name: don't allow warnings
run: sed -i '/#!\[allow(unknown_lints, unexpected_cfgs)\]/d' */src/lib.rs */tests/*.rs
- name: check for unknown lints and cfgs
run: cargo check --all-features --tests
env:
RUSTFLAGS: -Dwarnings --check-cfg=cfg(loom,tokio_unstable,tokio_taskdump,fuzzing,mio_unsupported_force_poll_poll,tokio_internal_mt_counters,fs,tokio_no_parking_lot,tokio_no_tuning_tests) -Funexpected_cfgs -Funknown_lints
check-fuzzing:
name: check-fuzzing
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -913,3 +1032,35 @@ jobs:
- name: Check /tokio-stream/
run: cargo fuzz check --all-features
working-directory: tokio-stream
check-spelling:
name: check-spelling
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.rust_stable }}
- name: Install cargo-spellcheck
uses: taiki-e/install-action@v2
with:
tool: cargo-spellcheck
- uses: actions/checkout@v4
- name: Make sure dictionary words are sorted and unique
run: |
# `sed` removes the first line (number of words) and
# the last line (new line).
#
# `sort` makes sure everything in between is sorted
# and contains no duplicates.
#
# Since `sort` is sensitive to locale, we set it
# using LC_ALL to en_US.UTF8 to be consistent in different
# environments.
sed '1d; $d' spellcheck.dic | LC_ALL=en_US.UTF8 sort -uc
- name: Run cargo-spellcheck
run: cargo spellcheck --code 1
+5 -5
View File
@@ -29,7 +29,7 @@ jobs:
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-sync') || (github.base_ref == null))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -45,7 +45,7 @@ jobs:
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
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -61,7 +61,7 @@ jobs:
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
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -84,7 +84,7 @@ jobs:
- scope: loom_multi_thread::group_c
- scope: loom_multi_thread::group_d
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -109,7 +109,7 @@ jobs:
- scope: loom_multi_thread_alt::group_c
- scope: loom_multi_thread_alt::group_d
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'ci skip')"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install cargo-audit
run: cargo install cargo-audit
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
stress-test:
- simple_echo_tcp
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
+70 -17
View File
@@ -149,23 +149,31 @@ When updating this, also update:
-->
```
cargo +1.65.0 clippy --all --tests --all-features
cargo +1.77 clippy --all --tests --all-features
```
When building documentation normally, the markers that list the features
required for various parts of Tokio are missing. To build the documentation
correctly, use this command:
When building documentation, a simple `cargo doc` is not sufficient. To produce
documentation equivalent to what will be produced in docs.rs's builds of Tokio's
docs, please use:
```
RUSTDOCFLAGS="--cfg docsrs" RUSTFLAGS="--cfg docsrs" cargo +nightly doc --all-features
RUSTDOCFLAGS="--cfg docsrs --cfg tokio_unstable" RUSTFLAGS="--cfg docsrs --cfg tokio_unstable" cargo +nightly doc --all-features [--open]
```
To build documentation including Tokio's unstable features, it is necessary to
pass `--cfg tokio_unstable` to both RustDoc *and* rustc. To build the
documentation for unstable features, use this command:
This turns on indicators to display the Cargo features required for
conditionally compiled APIs in Tokio, and it enables documentation of unstable
Tokio features. Notice that it is necessary to pass cfg flags to both RustDoc
*and* rustc.
There is a more concise way to build docs.rs-equivalent docs by using [`cargo
docs-rs`], which reads the above documentation flags out of Tokio's Cargo.toml
as docs.rs itself does.
[`cargo docs-rs`]: https://github.com/dtolnay/cargo-docs-rs
```
RUSTDOCFLAGS="--cfg docsrs --cfg tokio_unstable" RUSTFLAGS="--cfg docsrs --cfg tokio_unstable" cargo +nightly doc --all-features
cargo install --locked cargo-docs-rs
cargo +nightly docs-rs [--open]
```
The `cargo fmt` command does not work on the Tokio codebase. You can use the
@@ -184,9 +192,11 @@ it, `rustfmt` will update your files locally instead.
You can run loom tests with
```
cd tokio # tokio crate in workspace
LOOM_MAX_PREEMPTIONS=1 RUSTFLAGS="--cfg loom" \
LOOM_MAX_PREEMPTIONS=1 LOOM_MAX_BRANCHES=10000 RUSTFLAGS="--cfg loom -C debug_assertions" \
cargo test --lib --release --features full -- --test-threads=1 --nocapture
```
Additionally, you can also add `--cfg tokio_unstable` to the `RUSTFLAGS` environment variable to
run loom tests that test unstable features.
You can run miri tests with
```
@@ -194,6 +204,24 @@ MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-tag-raw-pointers" \
cargo +nightly miri test --features full --lib
```
### Performing spellcheck on tokio codebase
You can perform spell-check on tokio codebase. For details of how to use the spellcheck tool, feel free to visit
https://github.com/drahnr/cargo-spellcheck
```
# First install the spell-check plugin
cargo install --locked cargo-spellcheck
# Then run the cargo spell check command
cargo spellcheck check
```
if the command rejects a word, you should backtick the rejected word if it's code related. If not, the
rejected word should be put into `spellcheck.dic` file.
Note that when you add a word into the file, you should also update the first line which tells the spellcheck tool
the total number of words included in the file
### Tests
If the change being proposed alters code (as opposed to only documentation for
@@ -233,7 +261,7 @@ 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`
`cargo install --locked cargo-fuzz`
To list the available fuzzing harnesses you can run;
@@ -327,6 +355,29 @@ example would explicitly use `Timeout::new`. For example:
/// # }
```
### Benchmarks
You can run benchmarks locally for the changes you've made to the tokio codebase.
Tokio currently uses [Criterion](https://github.com/bheisler/criterion.rs) as its benchmarking tool. To run a benchmark
against the changes you have made, for example, you can run;
```bash
cd benches
# Run all benchmarks.
cargo bench
# Run all tests in the `benches/fs.rs` file
cargo bench --bench fs
# Run the `async_read_buf` benchmark in `benches/fs.rs` specifically.
cargo bench async_read_buf
# After running benches, you can check the statistics under `tokio/target/criterion/`
```
You can also refer to Criterion docs for additional options and details.
### Commits
It is a recommended best practice to keep your changes as logically grouped as
@@ -350,14 +401,16 @@ A good commit message should describe what changed and why.
and no more than 72 characters)
* be entirely in lowercase with the exception of proper nouns, acronyms, and
the words that refer to code, like function/variable names
* be prefixed with the name of the sub crate being changed (without the `tokio-`
prefix) and start with an imperative verb. If modifying `tokio` proper,
omit the crate prefix.
* start with an imperative verb
* not have a period at the end
* be prefixed with the name of the module being changed; usually this is the
same as the M-* label on the PR
Examples:
* timer: introduce `Timeout` and deprecate `Deadline`
* export `Encoder`, `Decoder`, `Framed*` from tokio_codec
* time: introduce `Timeout` and deprecate `Deadline`
* codec: export `Encoder`, `Decoder`, `Framed*`
* ci: fix the FreeBSD ci configuration
2. Keep the second line blank.
3. Wrap all other lines at 72 columns (except for long URLs).
@@ -374,7 +427,7 @@ A good commit message should describe what changed and why.
Sample complete commit message:
```txt
subcrate: explain the commit in one line
module: explain the commit in one line
Body of commit message is a few lines of text, explaining things
in more detail, possibly giving some background about the issue
+3
View File
@@ -14,3 +14,6 @@ members = [
"tests-build",
"tests-integration",
]
[workspace.metadata.spellcheck]
config = "spellcheck.toml"
+18 -22
View File
@@ -1,25 +1,21 @@
Copyright (c) 2023 Tokio Contributors
MIT License
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
Copyright (c) Tokio Contributors
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+16 -13
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.32.0", features = ["full"] }
tokio = { version = "1.39.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,12 +186,13 @@ 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.63.
released at least six months ago. The current MSRV is 1.70.
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.39 to now - Rust 1.70
* 1.30 to 1.38 - 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
@@ -216,18 +216,19 @@ 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.20.x` - LTS release until September 2023. (MSRV 1.49)
* `1.25.x` - LTS release until March 2024. (MSRV 1.49)
* `1.32.x` - LTS release until September 2024. (MSRV 1.63)
* `1.36.x` - LTS release until March 2025. (MSRV 1.63)
* `1.38.x` - LTS release until July 2025. (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.32.x` patch release, you
can use the following dependency specification:
```text
tokio = { version = "~1.18", features = [...] }
tokio = { version = "~1.32", features = [...] }
```
### Previous LTS releases
@@ -235,6 +236,8 @@ tokio = { version = "~1.18", features = [...] }
* `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.
* `1.25.x` - LTS release until March 2024.
## License
+11 -1
View File
@@ -9,7 +9,7 @@ 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"
@@ -25,6 +25,11 @@ name = "spawn"
path = "spawn.rs"
harness = false
[[bench]]
name = "sync_broadcast"
path = "sync_broadcast.rs"
harness = false
[[bench]]
name = "sync_mpsc"
path = "sync_mpsc.rs"
@@ -84,3 +89,8 @@ harness = false
name = "time_now"
path = "time_now.rs"
harness = false
[[bench]]
name = "time_timeout"
path = "time_timeout.rs"
harness = false
+54 -42
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;
@@ -29,7 +29,7 @@ const WRITE_SERVICE_PERIOD: Duration = Duration::from_millis(20);
// because another writer claimed the buffer space
const PROBABILITY_FLUSH_WAIT: f64 = 0.1;
/// A slow writer that aims to simulate HDD behaviour under heavy load.
/// A slow writer that aims to simulate HDD behavior under heavy load.
///
/// There is a limited buffer, which is fully drained on the next write after
/// a time limit is reached. Flush waits for the time limit to be reached
@@ -64,7 +64,7 @@ impl SlowHddWriter {
) -> std::task::Poll<Result<(), std::io::Error>> {
// If we hit a service interval, the buffer can be cleared
let res = self.service_intervals.poll_tick(cx).map(|_| Ok(()));
if let Poll::Ready(_) = res {
if res.is_ready() {
self.buffer_used = 0;
}
res
@@ -123,7 +123,7 @@ impl AsyncWrite for SlowHddWriter {
cx: &mut std::task::Context<'_>,
bufs: &[std::io::IoSlice<'_>],
) -> std::task::Poll<Result<usize, std::io::Error>> {
let writeable = bufs.into_iter().fold(0, |acc, buf| acc + buf.len());
let writeable = bufs.iter().fold(0, |acc, buf| acc + buf.len());
self.write_bytes(cx, writeable)
}
@@ -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);
+43 -37
View File
@@ -4,46 +4,50 @@
use tokio::runtime::{self, Runtime};
use bencher::{benchmark_group, benchmark_main, Bencher};
use criterion::{criterion_group, criterion_main, Criterion};
const NUM_SPAWN: usize = 1_000;
fn spawn_many_local(b: &mut Bencher) {
fn rt_curr_spawn_many_local(c: &mut Criterion) {
let rt = rt();
let mut handles = Vec::with_capacity(NUM_SPAWN);
b.iter(|| {
rt.block_on(async {
for _ in 0..NUM_SPAWN {
handles.push(tokio::spawn(async move {}));
}
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();
}
});
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
});
}
fn spawn_many_remote_idle(b: &mut Bencher) {
fn rt_curr_spawn_many_remote_idle(c: &mut Criterion) {
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
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();
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(b: &mut Bencher) {
fn rt_curr_spawn_many_remote_busy(c: &mut Criterion) {
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
@@ -56,16 +60,18 @@ fn spawn_many_remote_busy(b: &mut Bencher) {
iter()
});
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();
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();
}
});
})
});
}
@@ -73,11 +79,11 @@ fn rt() -> Runtime {
runtime::Builder::new_current_thread().build().unwrap()
}
benchmark_group!(
scheduler,
spawn_many_local,
spawn_many_remote_idle,
spawn_many_remote_busy
criterion_group!(
rt_curr_scheduler,
rt_curr_spawn_many_local,
rt_curr_spawn_many_remote_idle,
rt_curr_spawn_many_remote_busy
);
benchmark_main!(scheduler);
criterion_main!(rt_curr_scheduler);
+125 -112
View File
@@ -5,63 +5,68 @@
use tokio::runtime::{self, Runtime};
use tokio::sync::oneshot;
use bencher::{benchmark_group, benchmark_main, Bencher};
use std::sync::atomic::Ordering::Relaxed;
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::{mpsc, Arc};
use std::time::{Duration, Instant};
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(b: &mut Bencher) {
fn rt_multi_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();
});
rx.recv().unwrap();
});
})
});
}
fn spawn_many_remote_idle(b: &mut Bencher) {
fn rt_multi_spawn_many_remote_idle(c: &mut Criterion) {
let rt = rt();
let mut handles = Vec::with_capacity(NUM_SPAWN);
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
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(b: &mut Bencher) {
fn rt_multi_spawn_many_remote_busy1(c: &mut Criterion) {
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
@@ -78,16 +83,18 @@ fn spawn_many_remote_busy1(b: &mut Bencher) {
});
}
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();
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);
@@ -95,7 +102,7 @@ fn spawn_many_remote_busy1(b: &mut Bencher) {
// 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(b: &mut Bencher) {
fn rt_multi_spawn_many_remote_busy2(c: &mut Criterion) {
const NUM_SPAWN: usize = 1_000;
let rt = rt();
@@ -119,49 +126,52 @@ fn spawn_many_remote_busy2(b: &mut Bencher) {
});
}
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();
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(b: &mut Bencher) {
fn rt_multi_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 {
rx.recv().unwrap();
}
})
});
}
fn ping_pong(b: &mut Bencher) {
fn rt_multi_ping_pong(c: &mut Criterion) {
const NUM_PINGS: usize = 1_000;
let rt = rt();
@@ -169,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 rt_multi_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();
@@ -219,18 +229,21 @@ 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();
});
})
});
}
@@ -249,15 +262,15 @@ fn stall() {
}
}
benchmark_group!(
scheduler,
spawn_many_local,
spawn_many_remote_idle,
spawn_many_remote_busy1,
spawn_many_remote_busy2,
ping_pong,
yield_many,
chained_spawn,
criterion_group!(
rt_multi_scheduler,
rt_multi_spawn_many_local,
rt_multi_spawn_many_remote_idle,
rt_multi_spawn_many_remote_busy1,
rt_multi_spawn_many_remote_busy2,
rt_multi_ping_pong,
rt_multi_yield_many,
rt_multi_chained_spawn,
);
benchmark_main!(scheduler);
criterion_main!(rt_multi_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);
+82
View File
@@ -0,0 +1,82 @@
use rand::{Rng, RngCore, SeedableRng};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use tokio::sync::{broadcast, 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_impl<const N_TASKS: usize>(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
let (tx, _rx) = broadcast::channel::<usize>(1000);
let wg = Arc::new((AtomicUsize::new(0), Notify::new()));
for n in 0..N_TASKS {
let wg = wg.clone();
let mut rx = tx.subscribe();
let mut rng = rand::rngs::StdRng::seed_from_u64(n as u64);
rt.spawn(async move {
while (rx.recv().await).is_ok() {
let r = do_work(&mut rng);
let _ = black_box(r);
if wg.0.fetch_sub(1, Ordering::Relaxed) == 1 {
wg.1.notify_one();
}
}
});
}
const N_ITERS: usize = 100;
g.bench_function(N_TASKS.to_string(), |b| {
b.iter(|| {
rt.block_on({
let wg = wg.clone();
let tx = tx.clone();
async move {
for i in 0..N_ITERS {
assert_eq!(wg.0.fetch_add(N_TASKS, Ordering::Relaxed), 0);
tx.send(i).unwrap();
while wg.0.load(Ordering::Relaxed) > 0 {
wg.1.notified().await;
}
}
}
})
})
});
}
fn bench_contention(c: &mut Criterion) {
let mut group = c.benchmark_group("contention");
contention_impl::<10>(&mut group);
contention_impl::<100>(&mut group);
contention_impl::<500>(&mut group);
contention_impl::<1000>(&mut group);
group.finish();
}
criterion_group!(contention, bench_contention);
criterion_main!(contention);
+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(#[allow(dead_code)] [usize; 64]);
impl Default for Medium {
fn default() -> Self {
Medium([0; 64])
}
}
#[derive(Debug, Copy, Clone)]
struct Large(#[allow(dead_code)] [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);
+19 -16
View File
@@ -1,27 +1,28 @@
use bencher::{benchmark_group, benchmark_main, Bencher};
use tokio::{
runtime::Runtime,
sync::{mpsc, oneshot},
};
fn request_reply_current_thread(b: &mut Bencher) {
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(b, rt);
request_reply(c, rt);
}
fn request_reply_multi_threaded(b: &mut Bencher) {
fn request_reply_multi_threaded(c: &mut Criterion) {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.build()
.unwrap();
request_reply(b, rt);
request_reply(c, rt);
}
fn request_reply(b: &mut Bencher, rt: Runtime) {
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 {
@@ -32,22 +33,24 @@ fn request_reply(b: &mut Bencher, rt: Runtime) {
tx
});
b.iter(|| {
let task_tx = tx.clone();
rt.block_on(async move {
for _ in 0..1_000 {
let (o_tx, o_rx) = oneshot::channel();
task_tx.send(o_tx).await.unwrap();
let _ = o_rx.await;
}
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;
}
})
})
});
}
benchmark_group!(
criterion_group!(
sync_mpsc_oneshot_group,
request_reply_current_thread,
request_reply_multi_threaded,
);
benchmark_main!(sync_mpsc_oneshot_group);
criterion_main!(sync_mpsc_oneshot_group);
+47 -33
View File
@@ -1,9 +1,11 @@
use bencher::Bencher;
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)
@@ -11,7 +13,7 @@ fn rt() -> tokio::runtime::Runtime {
.unwrap()
}
fn notify_waiters<const N_WAITERS: usize>(b: &mut Bencher) {
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));
@@ -29,18 +31,20 @@ fn notify_waiters<const N_WAITERS: usize>(b: &mut Bencher) {
}
const N_ITERS: usize = 500;
b.iter(|| {
counter.store(0, Ordering::Relaxed);
loop {
notify.notify_waiters();
if counter.load(Ordering::Relaxed) >= N_ITERS {
break;
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>(b: &mut Bencher) {
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));
@@ -58,33 +62,43 @@ fn notify_one<const N_WAITERS: usize>(b: &mut Bencher) {
}
const N_ITERS: usize = 500;
b.iter(|| {
counter.store(0, Ordering::Relaxed);
loop {
notify.notify_one();
if counter.load(Ordering::Relaxed) >= N_ITERS {
break;
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;
}
}
}
})
});
}
bencher::benchmark_group!(
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,
notify_waiters::<10>,
notify_waiters::<50>,
notify_waiters::<100>,
notify_waiters::<200>,
notify_waiters::<500>
bench_notify_one,
bench_notify_waiters
);
bencher::benchmark_group!(
notify_one_simple,
notify_one::<10>,
notify_one::<50>,
notify_one::<100>,
notify_one::<200>,
notify_one::<500>
);
bencher::benchmark_main!(notify_waiters_simple, notify_one_simple);
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);
+40 -19
View File
@@ -1,9 +1,11 @@
use bencher::{black_box, Bencher};
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)
@@ -24,16 +26,15 @@ fn do_work(rng: &mut impl RngCore) -> u32 {
.fold(0, u32::wrapping_add)
}
fn contention_resubscribe(b: &mut Bencher) {
const NTASK: u64 = 1000;
fn contention_resubscribe<const N_TASKS: usize>(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
let (snd, rcv) = watch::channel(0i32);
let (snd, _) = watch::channel(0i32);
let snd = Arc::new(snd);
let wg = Arc::new((AtomicU64::new(0), Notify::new()));
for n in 0..NTASK {
let mut rcv = rcv.clone();
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);
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
@@ -46,19 +47,39 @@ fn contention_resubscribe(b: &mut Bencher) {
});
}
b.iter(|| {
rt.block_on(async {
for _ in 0..100 {
assert_eq!(wg.0.fetch_add(NTASK, Ordering::Relaxed), 0);
let _ = snd.send(black_box(42));
while wg.0.load(Ordering::Acquire) > 0 {
wg.1.notified().await;
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();
}
}
});
});
})
});
}
bencher::benchmark_group!(contention, contention_resubscribe);
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();
}
bencher::benchmark_main!(contention);
criterion_group!(contention, bench_contention_resubscribe);
criterion_main!(contention);
+10 -11
View File
@@ -2,24 +2,23 @@
//! This essentially measure the time to enqueue a task in the local and remote
//! case.
#[macro_use]
extern crate bencher;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use bencher::{black_box, Bencher};
fn time_now_current_thread(bench: &mut Bencher) {
fn time_now_current_thread(c: &mut Criterion) {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_time()
.build()
.unwrap();
bench.iter(|| {
rt.block_on(async {
black_box(tokio::time::Instant::now());
c.bench_function("time_now_current_thread", |b| {
b.iter(|| {
rt.block_on(async {
black_box(tokio::time::Instant::now());
})
})
})
});
}
bencher::benchmark_group!(time_now, time_now_current_thread,);
criterion_group!(time_now, time_now_current_thread);
bencher::benchmark_main!(time_now);
criterion_main!(time_now);
+109
View File
@@ -0,0 +1,109 @@
use std::time::{Duration, Instant};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use tokio::{
runtime::Runtime,
time::{sleep, timeout},
};
// a very quick async task, but might timeout
async fn quick_job() -> usize {
1
}
fn build_run_time(workers: usize) -> Runtime {
if workers == 1 {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
} else {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.worker_threads(workers)
.build()
.unwrap()
}
}
fn single_thread_scheduler_timeout(c: &mut Criterion) {
do_timeout_test(c, 1, "single_thread_timeout");
}
fn multi_thread_scheduler_timeout(c: &mut Criterion) {
do_timeout_test(c, 8, "multi_thread_timeout-8");
}
fn do_timeout_test(c: &mut Criterion, workers: usize, name: &str) {
let runtime = build_run_time(workers);
c.bench_function(name, |b| {
b.iter_custom(|iters| {
let start = Instant::now();
runtime.block_on(async {
black_box(spawn_timeout_job(iters as usize, workers)).await;
});
start.elapsed()
})
});
}
async fn spawn_timeout_job(iters: usize, procs: usize) {
let mut handles = Vec::with_capacity(procs);
for _ in 0..procs {
handles.push(tokio::spawn(async move {
for _ in 0..iters / procs {
let h = timeout(Duration::from_secs(1), quick_job());
assert_eq!(black_box(h.await.unwrap()), 1);
}
}));
}
for handle in handles {
handle.await.unwrap();
}
}
fn single_thread_scheduler_sleep(c: &mut Criterion) {
do_sleep_test(c, 1, "single_thread_sleep");
}
fn multi_thread_scheduler_sleep(c: &mut Criterion) {
do_sleep_test(c, 8, "multi_thread_sleep-8");
}
fn do_sleep_test(c: &mut Criterion, workers: usize, name: &str) {
let runtime = build_run_time(workers);
c.bench_function(name, |b| {
b.iter_custom(|iters| {
let start = Instant::now();
runtime.block_on(async {
black_box(spawn_sleep_job(iters as usize, workers)).await;
});
start.elapsed()
})
});
}
async fn spawn_sleep_job(iters: usize, procs: usize) {
let mut handles = Vec::with_capacity(procs);
for _ in 0..procs {
handles.push(tokio::spawn(async move {
for _ in 0..iters / procs {
let _h = black_box(sleep(Duration::from_secs(1)));
}
}));
}
for handle in handles {
handle.await.unwrap();
}
}
criterion_group!(
timeout_benchmark,
single_thread_scheduler_timeout,
multi_thread_scheduler_timeout,
single_thread_scheduler_sleep,
multi_thread_scheduler_sleep
);
criterion_main!(timeout_benchmark);
-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
+1 -1
View File
@@ -25,7 +25,7 @@ once_cell = "1.5.2"
rand = "0.8.3"
[target.'cfg(windows)'.dev-dependencies.windows-sys]
version = "0.48"
version = "0.52"
[[example]]
name = "chat"
+1 -1
View File
@@ -23,7 +23,7 @@ fn main() {
// Without the `HandleExt.wrap()` there would be a panic because there is
// no timer running, since it would be referencing runtime r1.
let _ = rt1.block_on(rt2.wrap(async move {
rt1.block_on(rt2.wrap(async move {
let listener = TcpListener::bind("0.0.0.0:0").await.unwrap();
println!("addr: {:?}", listener.local_addr());
tx.send(()).unwrap();
+3 -2
View File
@@ -47,9 +47,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 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() {
for task in dump.tasks().iter() {
let id = task.id();
let trace = task.trace();
println!("TASK {i}:");
println!("TASK {id}:");
println!("{trace}\n");
}
} else {
+2 -2
View File
@@ -259,11 +259,11 @@ mod date {
unix_date: u64,
}
thread_local!(static LAST: RefCell<LastRenderedNow> = RefCell::new(LastRenderedNow {
thread_local!(static LAST: RefCell<LastRenderedNow> = const { RefCell::new(LastRenderedNow {
bytes: [0; 128],
amt: 0,
unix_date: 0,
}));
}) });
impl fmt::Display for Now {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+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 = "/"
+285
View File
@@ -0,0 +1,285 @@
285
&
+
<
=
>
\
~
0o777
0s
0xA
0xD
100ms
10ms
~12
±1m
±1ms
1ms
1s
250ms
2x
~4
443
450ms
50ms
8MB
adaptor
adaptors
Adaptors
AIO
ambiant
amongst
api
APIs
async
atomics
awaitable
backend
backpressure
backtrace
backtraces
backtracing
binded
bitfield
bitfields
Blockingly
boolean
broadcasted
cancelled
cancelling
Cancelling
CLI
cloneable
codebase
codec
codecs
combinator
combinators
config
Config
connectionless
coroutines
cpu
cpus
Customizable
datagram
Datagram
datagrams
deallocate
deallocated
Deallocates
decrementing
dequeued
deregister
deregistered
deregistering
deregisters
Deregisters
deregistration
descriptor's
destructor
destructors
destructure
Destructures
Dev
dns
DNS
DoS
dwOpenMode
endian
enqueue
enqueued
EntryInner
enum
eof
Erlang's
errored
EWMA
expirations
fcntl
fd's
FIFOs
filename
filesystem
filesystems
fn
fns
FreeBSD
frontend
fs
functionalities
getters
GID
goroutines
Growable
gzip
hashmaps
HashMaps
hashsets
ie
Illumos
impl
implementers
implementor
implementors
incrementing
interoperate
invariants
Invariants
io
IOCP
iOS
IOs
io_uring
IP
IPv4
IPv6
iteratively
Kotlin's
latencies
Lauck
libc
lifecycle
lifo
lookups
macOS
MacOS
Marsaglia's
metadata
mio
Mio
mio's
misconfigured
mock's
mpmc
mpsc
multi
Multi
multicast
Multithreaded
mut
mutex
Mutex
Nagle
nonblocking
nondecreasing
noop
ntasks
ok
oneshot
ORed
os
overweighing
parker
parsers
peekable
PGID
PID
plaintext
poller
POSIX
proxied
qos
RAII
reallocations
recv's
refactors
refcount
refcounting
repo
repr
representable
reqwest
reregistering
resize
resized
RMW
runtime
runtime's
runtimes
rwlock
rx
scalability
scheduler's
semver
setpgid
sharded
signalled
signalling
SmallCrush
Solaris
spawner
Splitter
spmc
spsc
src
stabilised
startup
stateful
stderr
stdin
stdout
stealable
stealer
stealers
struct
structs
subfield
suboptimal
subprocess
superset
symlink
symlinks
sys
syscall
syscalls
tcp
TCP
TestU01
threadpool
timestamp
timestamps
TLS
TOCTOU
TODO
tokio
Tokio
tokio's
Tokio's
tuple
Tuple
tx
udp
UDP
UID
unhandled
unix
unlink
unpark
Unpark
unparked
unparking
unparks
Unparks
unreceived
unsafety
Unsets
unsynchronized
untrusted
usecases
Valgrind
Varghese
vec
versa
versioned
versioning
vtable
waker
wakers
Wakers
wakeup
wakeups
workstealing
+13
View File
@@ -0,0 +1,13 @@
dev_comments = false
skip_readme = false
[Hunspell]
lang = "en_US"
search_dirs = ["."]
extra_dictionaries = ["spellcheck.dic"]
skip_os_lookups = true
use_builtin = true
[Hunspell.quirks]
allow_concatenation = true
+1 -1
View File
@@ -42,7 +42,7 @@ fn main() {
.write_all(one_mega_random_bytes.as_slice())
.await
.unwrap();
stream.read(&mut buff).await.unwrap();
let _ = stream.read(&mut buff).await.unwrap();
}
tx.send(()).unwrap();
});
+9
View File
@@ -0,0 +1,9 @@
This is used for the `no-atomic-u64-test` ci check that verifies that Tokio
works even if the `AtomicU64` type is missing.
When increasing the nightly compiler version, you may need to regenerate this
target using the following command:
```
rustc +nightly -Z unstable-options --print target-spec-json --target i686-unknown-linux-gnu | grep -v 'is-builtin' | sed 's/"max-atomic-width": 64/"max-atomic-width": 32/' > target-specs/i686-unknown-linux-gnu.json
```
+14 -8
View File
@@ -1,29 +1,35 @@
{
"arch": "x86",
"cpu": "pentium4",
"crt-objects-fallback": "false",
"crt-static-respected": true,
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128",
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
"dynamic-linking": true,
"env": "gnu",
"has-rpath": true,
"has-thread-local": true,
"linker-flavor": "gnu-cc",
"llvm-target": "i686-unknown-linux-gnu",
"max-atomic-width": 32,
"metadata": {
"description": null,
"host_tools": null,
"std": null,
"tier": null
},
"os": "linux",
"position-independent-executables": true,
"pre-link-args": {
"gcc": [
"gnu-cc": [
"-m32"
],
"gnu-lld-cc": [
"-m32"
]
},
"relro-level": "full",
"stack-probes": {
"kind": "inline-or-call",
"min-llvm-version-for-inline": [
16,
0,
0
]
"kind": "inline"
},
"supported-sanitizers": [
"address"
@@ -41,8 +41,31 @@ async fn test_crate_not_path_int() {}
#[tokio::test(crate = "456")]
async fn test_crate_not_path_invalid() {}
#[tokio::test(flavor = "multi_thread", unhandled_panic = "shutdown_runtime")]
async fn test_multi_thread_with_unhandled_panic() {}
#[tokio::test]
#[test]
async fn test_has_second_test_attr() {}
#[tokio::test]
#[::core::prelude::v1::test]
async fn test_has_second_test_attr_v1() {}
#[tokio::test]
#[core::prelude::rust_2015::test]
async fn test_has_second_test_attr_rust_2015() {}
#[tokio::test]
#[::std::prelude::rust_2018::test]
async fn test_has_second_test_attr_rust_2018() {}
#[tokio::test]
#[std::prelude::rust_2021::test]
async fn test_has_second_test_attr_rust_2021() {}
#[tokio::test]
#[tokio::test]
async fn test_has_generated_second_test_attr() {}
fn main() {}
@@ -1,95 +1,121 @@
error: the `async` keyword is missing from the function declaration
--> $DIR/macros_invalid_input.rs:6:1
--> tests/fail/macros_invalid_input.rs:6:1
|
6 | fn main_is_not_async() {}
| ^^
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`
--> $DIR/macros_invalid_input.rs:8:15
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.
--> tests/fail/macros_invalid_input.rs:8:15
|
8 | #[tokio::main(foo)]
| ^^^
error: Must have specified ident
--> $DIR/macros_invalid_input.rs:11:15
--> tests/fail/macros_invalid_input.rs:11:15
|
11 | #[tokio::main(threadpool::bar)]
| ^^^^^^^^^^^^^^^
error: the `async` keyword is missing from the function declaration
--> $DIR/macros_invalid_input.rs:15:1
--> tests/fail/macros_invalid_input.rs:15:1
|
15 | fn test_is_not_async() {}
| ^^
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`
--> $DIR/macros_invalid_input.rs:17:15
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.
--> tests/fail/macros_invalid_input.rs:17:15
|
17 | #[tokio::test(foo)]
| ^^^
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`
--> $DIR/macros_invalid_input.rs:20:15
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`
--> tests/fail/macros_invalid_input.rs:20:15
|
20 | #[tokio::test(foo = 123)]
| ^^^^^^^^^
error: Failed to parse value of `flavor` as string.
--> $DIR/macros_invalid_input.rs:23:24
--> tests/fail/macros_invalid_input.rs:23:24
|
23 | #[tokio::test(flavor = 123)]
| ^^^
error: No such runtime flavor `foo`. The runtime flavors are `current_thread` and `multi_thread`.
--> $DIR/macros_invalid_input.rs:26:24
--> tests/fail/macros_invalid_input.rs:26:24
|
26 | #[tokio::test(flavor = "foo")]
| ^^^^^
error: The `start_paused` option requires the `current_thread` runtime flavor. Use `#[tokio::test(flavor = "current_thread")]`
--> $DIR/macros_invalid_input.rs:29:55
--> tests/fail/macros_invalid_input.rs:29:55
|
29 | #[tokio::test(flavor = "multi_thread", start_paused = false)]
| ^^^^^
error: Failed to parse value of `worker_threads` as integer.
--> $DIR/macros_invalid_input.rs:32:57
--> tests/fail/macros_invalid_input.rs:32:57
|
32 | #[tokio::test(flavor = "multi_thread", worker_threads = "foo")]
| ^^^^^
error: The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[tokio::test(flavor = "multi_thread")]`
--> $DIR/macros_invalid_input.rs:35:59
--> tests/fail/macros_invalid_input.rs:35:59
|
35 | #[tokio::test(flavor = "current_thread", worker_threads = 4)]
| ^
error: Failed to parse value of `crate` as path.
--> $DIR/macros_invalid_input.rs:38:23
--> tests/fail/macros_invalid_input.rs:38:23
|
38 | #[tokio::test(crate = 456)]
| ^^^
error: Failed to parse value of `crate` as path: "456"
--> $DIR/macros_invalid_input.rs:41:23
--> tests/fail/macros_invalid_input.rs:41:23
|
41 | #[tokio::test(crate = "456")]
| ^^^^^
error: second test attribute is supplied
--> $DIR/macros_invalid_input.rs:45:1
error: The `unhandled_panic` option requires the `current_thread` runtime flavor. Use `#[tokio::test(flavor = "current_thread")]`
--> tests/fail/macros_invalid_input.rs:44:58
|
45 | #[test]
44 | #[tokio::test(flavor = "multi_thread", unhandled_panic = "shutdown_runtime")]
| ^^^^^^^^^^^^^^^^^^
error: second test attribute is supplied, consider removing or changing the order of your test attributes
--> tests/fail/macros_invalid_input.rs:48:1
|
48 | #[test]
| ^^^^^^^
error: duplicated attribute
--> $DIR/macros_invalid_input.rs:45:1
error: second test attribute is supplied, consider removing or changing the order of your test attributes
--> tests/fail/macros_invalid_input.rs:52:1
|
45 | #[test]
| ^^^^^^^
52 | #[::core::prelude::v1::test]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: second test attribute is supplied, consider removing or changing the order of your test attributes
--> tests/fail/macros_invalid_input.rs:56:1
|
note: the lint level is defined here
--> $DIR/macros_invalid_input.rs:1:9
56 | #[core::prelude::rust_2015::test]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: second test attribute is supplied, consider removing or changing the order of your test attributes
--> tests/fail/macros_invalid_input.rs:60:1
|
1 | #![deny(duplicate_macro_attributes)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
60 | #[::std::prelude::rust_2018::test]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: second test attribute is supplied, consider removing or changing the order of your test attributes
--> tests/fail/macros_invalid_input.rs:64:1
|
64 | #[std::prelude::rust_2021::test]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: second test attribute is supplied, consider removing or changing the order of your test attributes
--> tests/fail/macros_invalid_input.rs:67:1
|
67 | #[tokio::test]
| ^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `tokio::test` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -1,24 +1,36 @@
error[E0308]: mismatched types
--> 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 `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
help: a return type might be missing here
|
4 | async fn missing_semicolon_or_return_type() -> _ {
| ++++
help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panicking if the value is a `Result::Err`
|
5 | Ok(()).expect("REASON")
| +++++++++++++++++
error[E0308]: mismatched types
--> 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 `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
help: a return type might be missing here
|
9 | async fn missing_return_type() -> _ {
| ++++
help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panicking if the value is a `Result::Err`
|
10 | return Ok(());.expect("REASON")
| +++++++++++++++++
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:23:5
@@ -41,7 +53,7 @@ error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:32:5
|
30 | async fn issue_4635() {
| - help: try adding a return type: `-> i32`
| - help: try adding a return type: `-> i32`
31 | return 1;
32 | ;
| ^ expected `()`, found integer
+1
View File
@@ -39,6 +39,7 @@ rt-process-signal = ["rt-net", "tokio/process", "tokio/signal"]
# This is an explicit feature so we can use `cargo hack` testing single features
# instead of all possible permutations.
wasi-rt = ["rt", "macros", "sync"]
wasi-threads-rt = ["wasi-rt", "rt-multi-thread"]
full = [
"macros",
+1 -5
View File
@@ -1,8 +1,4 @@
#![cfg(all(
feature = "macros",
feature = "rt-multi-thread",
not(target_os = "wasi")
))]
#![cfg(all(feature = "macros", feature = "rt-multi-thread"))]
#[tokio::main]
async fn basic_main() -> usize {
+4 -1
View File
@@ -4,7 +4,10 @@ use futures::channel::oneshot;
use futures::executor::block_on;
use std::thread;
#[cfg_attr(target_os = "wasi", ignore = "WASI: std::thread::spawn not supported")]
#[cfg_attr(
not(feature = "rt-multi-thread"),
ignore = "WASI: std::thread::spawn not supported"
)]
#[test]
fn join_with_select() {
block_on(async {
+22
View File
@@ -1,3 +1,25 @@
# 2.4.0 (July 22nd, 2024)
- msrv: increase MSRV to 1.70 ([#6645])
- macros: allow `unhandled_panic` behavior for `#[tokio::main]` and `#[tokio::test]` ([#6593])
[#6593]: https://github.com/tokio-rs/tokio/pull/6593
[#6645]: https://github.com/tokio-rs/tokio/pull/6645
# 2.3.0 (May 30th, 2024)
- macros: make `#[tokio::test]` append `#[test]` at the end of the attribute list ([#6497])
[#6497]: https://github.com/tokio-rs/tokio/pull/6497
# 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])
+3 -3
View File
@@ -4,9 +4,9 @@ name = "tokio-macros"
# - Remove path dependencies
# - Update CHANGELOG.md.
# - Create "tokio-macros-1.x.y" git tag.
version = "2.1.0"
version = "2.4.0"
edition = "2021"
rust-version = "1.56"
rust-version = "1.70"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -22,7 +22,7 @@ proc-macro = true
[features]
[dependencies]
proc-macro2 = "1.0.7"
proc-macro2 = "1.0.60"
quote = "1"
syn = { version = "2.0", features = ["full"] }
+2 -27
View File
@@ -1,32 +1,7 @@
Copyright (c) 2023 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
The MIT License (MIT)
MIT License
Copyright (c) 2019 Yoshua Wuyts
Copyright (c) Tokio Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+120 -22
View File
@@ -25,11 +25,37 @@ impl RuntimeFlavor {
}
}
#[derive(Clone, Copy, PartialEq)]
enum UnhandledPanic {
Ignore,
ShutdownRuntime,
}
impl UnhandledPanic {
fn from_str(s: &str) -> Result<UnhandledPanic, String> {
match s {
"ignore" => Ok(UnhandledPanic::Ignore),
"shutdown_runtime" => Ok(UnhandledPanic::ShutdownRuntime),
_ => Err(format!("No such unhandled panic behavior `{}`. The unhandled panic behaviors are `ignore` and `shutdown_runtime`.", s)),
}
}
fn into_tokens(self, crate_path: &TokenStream) -> TokenStream {
match self {
UnhandledPanic::Ignore => quote! { #crate_path::runtime::UnhandledPanic::Ignore },
UnhandledPanic::ShutdownRuntime => {
quote! { #crate_path::runtime::UnhandledPanic::ShutdownRuntime }
}
}
}
}
struct FinalConfig {
flavor: RuntimeFlavor,
worker_threads: Option<usize>,
start_paused: Option<bool>,
crate_name: Option<Path>,
unhandled_panic: Option<UnhandledPanic>,
}
/// Config used in case of the attribute not being able to build a valid config
@@ -38,6 +64,7 @@ const DEFAULT_ERROR_CONFIG: FinalConfig = FinalConfig {
worker_threads: None,
start_paused: None,
crate_name: None,
unhandled_panic: None,
};
struct Configuration {
@@ -48,6 +75,7 @@ struct Configuration {
start_paused: Option<(bool, Span)>,
is_test: bool,
crate_name: Option<Path>,
unhandled_panic: Option<(UnhandledPanic, Span)>,
}
impl Configuration {
@@ -63,6 +91,7 @@ impl Configuration {
start_paused: None,
is_test,
crate_name: None,
unhandled_panic: None,
}
}
@@ -117,6 +146,25 @@ impl Configuration {
Ok(())
}
fn set_unhandled_panic(
&mut self,
unhandled_panic: syn::Lit,
span: Span,
) -> Result<(), syn::Error> {
if self.unhandled_panic.is_some() {
return Err(syn::Error::new(
span,
"`unhandled_panic` set multiple times.",
));
}
let unhandled_panic = parse_string(unhandled_panic, span, "unhandled_panic")?;
let unhandled_panic =
UnhandledPanic::from_str(&unhandled_panic).map_err(|err| syn::Error::new(span, err))?;
self.unhandled_panic = Some((unhandled_panic, span));
Ok(())
}
fn macro_name(&self) -> &'static str {
if self.is_test {
"tokio::test"
@@ -126,22 +174,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 +200,26 @@ 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,
};
let unhandled_panic = match (flavor, self.unhandled_panic) {
(F::Threaded, Some((_, unhandled_panic_span))) => {
let msg = format!(
"The `unhandled_panic` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(unhandled_panic_span, msg));
}
(F::CurrentThread, Some((unhandled_panic, _))) => Some(unhandled_panic),
(_, None) => None,
};
@@ -168,6 +228,7 @@ impl Configuration {
flavor,
worker_threads,
start_paused,
unhandled_panic,
})
}
}
@@ -275,9 +336,13 @@ fn build_config(
"crate" => {
config.set_crate_name(lit.clone(), syn::spanned::Spanned::span(lit))?;
}
"unhandled_panic" => {
config
.set_unhandled_panic(lit.clone(), syn::spanned::Spanned::span(lit))?;
}
name => {
let msg = format!(
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`",
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`",
name,
);
return Err(syn::Error::new_spanned(namevalue, msg));
@@ -303,11 +368,11 @@ fn build_config(
macro_name
)
}
"flavor" | "worker_threads" | "start_paused" => {
"flavor" | "worker_threads" | "start_paused" | "crate" | "unhandled_panic" => {
format!("The `{}` attribute requires an argument.", name)
}
name => {
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`", name)
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.", name)
}
};
return Err(syn::Error::new_spanned(path, msg));
@@ -359,8 +424,12 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
if let Some(v) = config.start_paused {
rt = quote_spanned! {last_stmt_start_span=> #rt.start_paused(#v) };
}
if let Some(v) = config.unhandled_panic {
let unhandled_panic = v.into_tokens(&crate_path);
rt = quote_spanned! {last_stmt_start_span=> #rt.unhandled_panic(#unhandled_panic) };
}
let header = if is_test {
let generated_attrs = if is_test {
quote! {
#[::core::prelude::v1::test]
}
@@ -402,7 +471,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
quote! {
let body = async #body;
#crate_path::pin!(body);
let body: ::std::pin::Pin<&mut dyn ::std::future::Future<Output = #output_type>> = body;
let body: ::core::pin::Pin<&mut dyn ::core::future::Future<Output = #output_type>> = body;
}
} else {
quote! {
@@ -410,7 +479,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
}
};
input.into_tokens(header, body, last_block)
input.into_tokens(generated_attrs, body, last_block)
}
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
@@ -418,7 +487,6 @@ fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenS
tokens
}
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
// 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
@@ -443,6 +511,35 @@ pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
}
}
// Check whether given attribute is a test attribute of forms:
// * `#[test]`
// * `#[core::prelude::*::test]` or `#[::core::prelude::*::test]`
// * `#[std::prelude::*::test]` or `#[::std::prelude::*::test]`
fn is_test_attribute(attr: &Attribute) -> bool {
let path = match &attr.meta {
syn::Meta::Path(path) => path,
_ => return false,
};
let candidates = [
["core", "prelude", "*", "test"],
["std", "prelude", "*", "test"],
];
if path.leading_colon.is_none()
&& path.segments.len() == 1
&& path.segments[0].arguments.is_none()
&& path.segments[0].ident == "test"
{
return true;
} else if path.segments.len() != candidates[0].len() {
return false;
}
candidates.into_iter().any(|segments| {
path.segments.iter().zip(segments).all(|(segment, path)| {
segment.arguments.is_none() && (path == "*" || segment.ident == path)
})
})
}
pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
// 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
@@ -451,8 +548,8 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
Ok(it) => it,
Err(e) => return token_stream_with_error(item, e),
};
let config = if let Some(attr) = input.attrs().find(|attr| attr.meta.path().is_ident("test")) {
let msg = "second test attribute is supplied";
let config = if let Some(attr) = input.attrs().find(|attr| is_test_attribute(attr)) {
let msg = "second test attribute is supplied, consider removing or changing the order of your test attributes";
Err(syn::Error::new_spanned(attr, msg))
} else {
AttributeArgs::parse_terminated
@@ -493,13 +590,11 @@ impl ItemFn {
/// Convert our local function item into a token stream.
fn into_tokens(
self,
header: proc_macro2::TokenStream,
generated_attrs: 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);
@@ -507,12 +602,15 @@ impl ItemFn {
// 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`.
// outside of the function. This matches the behavior of `syn`.
for mut attr in self.inner_attrs {
attr.style = syn::AttrStyle::Outer;
attr.to_tokens(&mut tokens);
}
// Add generated macros at the end, so macros processed later are aware of them.
generated_attrs.to_tokens(&mut tokens);
self.vis.to_tokens(&mut tokens);
self.sig.to_tokens(&mut tokens);
@@ -586,6 +684,6 @@ impl ToTokens for Body<'_> {
for stmt in self.stmts {
stmt.to_tokens(tokens);
}
})
});
}
}
+95 -3
View File
@@ -1,3 +1,4 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -201,8 +202,53 @@ use proc_macro::TokenStream;
/// })
/// }
/// ```
///
/// ### Configure unhandled panic behavior
///
/// Available options are `shutdown_runtime` and `ignore`. For more details, see
/// [`Builder::unhandled_panic`].
///
/// This option is only compatible with the `current_thread` runtime.
///
/// ```no_run
/// #[cfg(tokio_unstable)]
/// #[tokio::main(flavor = "current_thread", unhandled_panic = "shutdown_runtime")]
/// async fn main() {
/// let _ = tokio::spawn(async {
/// panic!("This panic will shutdown the runtime.");
/// }).await;
/// }
/// # #[cfg(not(tokio_unstable))]
/// # fn main() { }
/// ```
///
/// Equivalent code not using `#[tokio::main]`
///
/// ```no_run
/// #[cfg(tokio_unstable)]
/// fn main() {
/// tokio::runtime::Builder::new_current_thread()
/// .enable_all()
/// .unhandled_panic(UnhandledPanic::ShutdownRuntime)
/// .build()
/// .unwrap()
/// .block_on(async {
/// let _ = tokio::spawn(async {
/// panic!("This panic will shutdown the runtime.");
/// }).await;
/// })
/// }
/// # #[cfg(not(tokio_unstable))]
/// # fn main() { }
/// ```
///
/// **Note**: This option depends on Tokio's [unstable API][unstable]. See [the
/// documentation on unstable features][unstable] for details on how to enable
/// Tokio's unstable features.
///
/// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic
/// [unstable]: ../tokio/index.html#unstable-features
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args.into(), item.into(), true).into()
}
@@ -267,7 +313,6 @@ 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.into(), item.into(), false).into()
}
@@ -365,7 +410,7 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// ### Set number of worker threads
///
/// ```no_run
/// #[tokio::test(flavor ="multi_thread", worker_threads = 2)]
/// #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
/// async fn my_test() {
/// assert!(true);
/// }
@@ -424,6 +469,53 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// println!("Hello world");
/// }
/// ```
///
/// ### Configure unhandled panic behavior
///
/// Available options are `shutdown_runtime` and `ignore`. For more details, see
/// [`Builder::unhandled_panic`].
///
/// This option is only compatible with the `current_thread` runtime.
///
/// ```no_run
/// #[cfg(tokio_unstable)]
/// #[tokio::test(flavor = "current_thread", unhandled_panic = "shutdown_runtime")]
/// async fn my_test() {
/// let _ = tokio::spawn(async {
/// panic!("This panic will shutdown the runtime.");
/// }).await;
/// }
/// # #[cfg(not(tokio_unstable))]
/// # fn main() { }
/// ```
///
/// Equivalent code not using `#[tokio::test]`
///
/// ```no_run
/// #[cfg(tokio_unstable)]
/// #[test]
/// fn my_test() {
/// tokio::runtime::Builder::new_current_thread()
/// .enable_all()
/// .unhandled_panic(UnhandledPanic::ShutdownRuntime)
/// .build()
/// .unwrap()
/// .block_on(async {
/// let _ = tokio::spawn(async {
/// panic!("This panic will shutdown the runtime.");
/// }).await;
/// })
/// }
/// # #[cfg(not(tokio_unstable))]
/// # fn main() { }
/// ```
///
/// **Note**: This option depends on Tokio's [unstable API][unstable]. See [the
/// documentation on unstable features][unstable] for details on how to enable
/// Tokio's unstable features.
///
/// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic
/// [unstable]: ../tokio/index.html#unstable-features
#[proc_macro_attribute]
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args.into(), item.into(), true).into()
+5 -5
View File
@@ -73,27 +73,27 @@ 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.elems.iter_mut() {
for elem in &mut tuple.elems {
clean_pattern(elem);
}
}
+16
View File
@@ -1,3 +1,19 @@
# 0.1.15 (March 14th, 2024)
This release bumps the MSRV of tokio-stream to 1.63.
- docs: fix typo in argument name ([#6389])
- docs: fix typo in peekable docs ([#6130])
- docs: link to latest version of tokio-util docs ([#5694])
- docs: typographic improvements ([#6262])
- stream: add `StreamExt::peekable` ([#6095])
[#5694]: https://github.com/tokio-rs/tokio/pull/5694
[#6095]: https://github.com/tokio-rs/tokio/pull/6095
[#6130]: https://github.com/tokio-rs/tokio/pull/6130
[#6262]: https://github.com/tokio-rs/tokio/pull/6262
[#6389]: https://github.com/tokio-rs/tokio/pull/6389
# 0.1.14 (April 26th, 2023)
This bugfix release bumps the minimum version of Tokio to 1.15, which is
+2 -2
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.14"
version = "0.1.15"
edition = "2021"
rust-version = "1.56"
rust-version = "1.70"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
+18 -22
View File
@@ -1,25 +1,21 @@
Copyright (c) 2023 Tokio Contributors
MIT License
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
Copyright (c) Tokio Contributors
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+13
View File
@@ -0,0 +1,13 @@
# tokio-stream
Utilities to work with `Stream` and `tokio`.
## License
This project is licensed under the [MIT license](LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
+20 -2
View File
@@ -1,3 +1,4 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![allow(
clippy::cognitive_complexity,
clippy::large_enum_variant,
@@ -54,7 +55,7 @@
//!
//! [async-stream]: https://docs.rs/async-stream
//!
//! # Conversion to and from AsyncRead/AsyncWrite
//! # Conversion to and from `AsyncRead`/`AsyncWrite`
//!
//! It is often desirable to convert a `Stream` into an [`AsyncRead`],
//! especially when dealing with plaintext formats streamed over the network.
@@ -73,12 +74,29 @@
#[macro_use]
mod macros;
mod poll_fn;
pub(crate) use poll_fn::poll_fn;
pub mod wrappers;
mod stream_ext;
pub use stream_ext::{collect::FromStream, StreamExt};
/// Adapters for [`Stream`]s created by methods in [`StreamExt`].
pub mod adapters {
pub use crate::stream_ext::{
Chain, Filter, FilterMap, Fuse, Map, MapWhile, Merge, Peekable, Skip, SkipWhile, Take,
TakeWhile, Then,
};
cfg_time! {
pub use crate::stream_ext::{ChunksTimeout, Timeout, TimeoutRepeating};
}
}
cfg_time! {
pub use stream_ext::timeout::{Elapsed, Timeout};
#[deprecated = "Import those symbols from adapters instead"]
#[doc(hidden)]
pub use stream_ext::timeout::Timeout;
pub use stream_ext::timeout::Elapsed;
}
mod empty;
+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)),
}
}
+35
View File
@@ -0,0 +1,35 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
pub(crate) struct PollFn<F> {
f: F,
}
pub(crate) fn poll_fn<T, F>(f: F) -> PollFn<F>
where
F: FnMut(&mut Context<'_>) -> Poll<T>,
{
PollFn { f }
}
impl<T, F> Future for PollFn<F>
where
F: FnMut(&mut Context<'_>) -> Poll<T>,
{
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
// Safety: We never construct a `Pin<&mut F>` anywhere, so accessing `f`
// mutably in an unpinned way is sound.
//
// This use of unsafe cannot be replaced with the pin-project macro
// because:
// * If we put `#[pin]` on the field, then it gives us a `Pin<&mut F>`,
// which we can't use to call the closure.
// * If we don't put `#[pin]` on the field, then it makes `PollFn` be
// unconditionally `Unpin`, which we also don't want.
let me = unsafe { Pin::into_inner_unchecked(self) };
(me.f)(cx)
}
}
+46 -20
View File
@@ -8,63 +8,66 @@ mod any;
use any::AnyFuture;
mod chain;
use chain::Chain;
pub use chain::Chain;
pub(crate) mod collect;
use collect::{Collect, FromStream};
mod filter;
use filter::Filter;
pub use filter::Filter;
mod filter_map;
use filter_map::FilterMap;
pub use filter_map::FilterMap;
mod fold;
use fold::FoldFuture;
mod fuse;
use fuse::Fuse;
pub use fuse::Fuse;
mod map;
use map::Map;
pub use map::Map;
mod map_while;
use map_while::MapWhile;
pub use map_while::MapWhile;
mod merge;
use merge::Merge;
pub use merge::Merge;
mod next;
use next::Next;
mod skip;
use skip::Skip;
pub use skip::Skip;
mod skip_while;
use skip_while::SkipWhile;
pub use skip_while::SkipWhile;
mod take;
use take::Take;
pub use take::Take;
mod take_while;
use take_while::TakeWhile;
pub use take_while::TakeWhile;
mod then;
use then::Then;
pub use then::Then;
mod try_next;
use try_next::TryNext;
mod peekable;
pub use peekable::Peekable;
cfg_time! {
pub(crate) mod timeout;
pub(crate) mod timeout_repeating;
use timeout::Timeout;
use timeout_repeating::TimeoutRepeating;
pub use timeout::Timeout;
pub use timeout_repeating::TimeoutRepeating;
use tokio::time::{Duration, Interval};
mod throttle;
use throttle::{throttle, Throttle};
mod chunks_timeout;
use chunks_timeout::ChunksTimeout;
pub use chunks_timeout::ChunksTimeout;
}
/// An extension trait for the [`Stream`] trait that provides a variety of
@@ -848,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
///
@@ -1005,9 +1007,8 @@ pub trait StreamExt: Stream {
/// Applies a per-item timeout to the passed stream.
///
/// `timeout_repeating()` takes an [`Interval`](tokio::time::Interval) that
/// controls the time each element of the stream has to complete before
/// timing out.
/// `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
@@ -1178,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)
}
}
}
+121 -19
View File
@@ -1,4 +1,4 @@
use crate::Stream;
use crate::{poll_fn, Stream};
use std::borrow::Borrow;
use std::hash::Hash;
@@ -209,7 +209,7 @@ pub struct StreamMap<K, V> {
impl<K, V> StreamMap<K, V> {
/// An iterator visiting all key-value pairs in arbitrary order.
///
/// The iterator element type is &'a (K, V).
/// The iterator element type is `&'a (K, V)`.
///
/// # Examples
///
@@ -232,7 +232,7 @@ impl<K, V> StreamMap<K, V> {
/// An iterator visiting all key-value pairs mutably in arbitrary order.
///
/// The iterator element type is &'a mut (K, V).
/// The iterator element type is `&'a mut (K, V)`.
///
/// # Examples
///
@@ -289,7 +289,7 @@ impl<K, V> StreamMap<K, V> {
/// Returns an iterator visiting all keys in arbitrary order.
///
/// The iterator element type is &'a K.
/// The iterator element type is `&'a K`.
///
/// # Examples
///
@@ -312,7 +312,7 @@ impl<K, V> StreamMap<K, V> {
/// An iterator visiting all values in arbitrary order.
///
/// The iterator element type is &'a V.
/// The iterator element type is `&'a V`.
///
/// # Examples
///
@@ -335,7 +335,7 @@ impl<K, V> StreamMap<K, V> {
/// An iterator visiting all values mutably in arbitrary order.
///
/// The iterator element type is &'a mut V.
/// The iterator element type is `&'a mut V`.
///
/// # Examples
///
@@ -467,10 +467,10 @@ impl<K, V> StreamMap<K, V> {
/// assert!(map.remove(&1).is_some());
/// assert!(map.remove(&1).is_none());
/// ```
pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
pub fn remove<Q>(&mut self, k: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq,
Q: Hash + Eq + ?Sized,
{
for i in 0..self.entries.len() {
if self.entries[i].0.borrow() == k {
@@ -496,10 +496,10 @@ impl<K, V> StreamMap<K, V> {
/// assert_eq!(map.contains_key(&1), true);
/// assert_eq!(map.contains_key(&2), false);
/// ```
pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool
pub fn contains_key<Q>(&self, k: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq,
Q: Hash + Eq + ?Sized,
{
for i in 0..self.entries.len() {
if self.entries[i].0.borrow() == k {
@@ -518,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;
@@ -527,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);
@@ -542,7 +540,7 @@ where
idx = idx.wrapping_add(1) % self.entries.len();
}
}
Pending => {
Poll::Pending => {
idx = idx.wrapping_add(1) % self.entries.len();
}
}
@@ -550,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
}
}
}
@@ -563,6 +561,110 @@ impl<K, V> Default for StreamMap<K, V> {
}
}
impl<K, V> StreamMap<K, V>
where
K: Clone + Unpin,
V: Stream + Unpin,
{
/// Receives multiple items on this [`StreamMap`], extending the provided `buffer`.
///
/// This method returns the number of items that is appended to the `buffer`.
///
/// Note that this method does not guarantee that exactly `limit` items
/// are received. Rather, if at least one item is available, it returns
/// as many items as it can up to the given limit. This method returns
/// zero only if the `StreamMap` is empty (or if `limit` is zero).
///
/// # Cancel safety
///
/// This method is cancel safe. If `next_many` is used as the event in a
/// [`tokio::select!`](tokio::select) statement and some other branch
/// completes first, it is guaranteed that no items were received on any of
/// the underlying streams.
pub async fn next_many(&mut self, buffer: &mut Vec<(K, V::Item)>, limit: usize) -> usize {
poll_fn(|cx| self.poll_next_many(cx, buffer, limit)).await
}
/// Polls to receive multiple items on this `StreamMap`, extending the provided `buffer`.
///
/// This method returns:
/// * `Poll::Pending` if no items are available but the `StreamMap` is not empty.
/// * `Poll::Ready(count)` where `count` is the number of items successfully received and
/// stored in `buffer`. This can be less than, or equal to, `limit`.
/// * `Poll::Ready(0)` if `limit` is set to zero or when the `StreamMap` is empty.
///
/// Note that this method does not guarantee that exactly `limit` items
/// are received. Rather, if at least one item is available, it returns
/// as many items as it can up to the given limit. This method returns
/// zero only if the `StreamMap` is empty (or if `limit` is zero).
pub fn poll_next_many(
&mut self,
cx: &mut Context<'_>,
buffer: &mut Vec<(K, V::Item)>,
limit: usize,
) -> Poll<usize> {
if limit == 0 || self.entries.is_empty() {
return Poll::Ready(0);
}
let mut added = 0;
let start = self::rand::thread_rng_n(self.entries.len() as u32) as usize;
let mut idx = start;
while added < limit {
// Indicates whether at least one stream returned a value when polled or not
let mut should_loop = false;
for _ in 0..self.entries.len() {
let (_, stream) = &mut self.entries[idx];
match Pin::new(stream).poll_next(cx) {
Poll::Ready(Some(val)) => {
added += 1;
let key = self.entries[idx].0.clone();
buffer.push((key, val));
should_loop = true;
idx = idx.wrapping_add(1) % self.entries.len();
}
Poll::Ready(None) => {
// Remove the entry
self.entries.swap_remove(idx);
// Check if this was the last entry, if so the cursor needs
// to wrap
if idx == self.entries.len() {
idx = 0;
} else if idx < start && start <= self.entries.len() {
// The stream being swapped into the current index has
// already been polled, so skip it.
idx = idx.wrapping_add(1) % self.entries.len();
}
}
Poll::Pending => {
idx = idx.wrapping_add(1) % self.entries.len();
}
}
}
if !should_loop {
break;
}
}
if added > 0 {
Poll::Ready(added)
} else if self.entries.is_empty() {
Poll::Ready(0)
} else {
Poll::Pending
}
}
}
impl<K, V> Stream for StreamMap<K, V>
where
K: Clone + Unpin,
@@ -660,9 +762,9 @@ mod rand {
/// Fast random number generate
///
/// Implement xorshift64+: 2 32-bit xorshift sequences added together.
/// Implement `xorshift64+`: 2 32-bit `xorshift` sequences added together.
/// Shift triplet `[17,7,16]` was calculated as indicated in Marsaglia's
/// Xorshift paper: <https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf>
/// `Xorshift` paper: <https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf>
/// This generator passes the SmallCrush suite, part of TestU01 framework:
/// <http://simul.iro.umontreal.ca/testu01/tu01.html>
#[derive(Debug)]
+1 -1
View File
@@ -33,7 +33,7 @@ impl Stream for IntervalStream {
}
fn size_hint(&self) -> (usize, Option<usize>) {
(std::usize::MAX, None)
(usize::MAX, None)
}
}
-1
View File
@@ -30,7 +30,6 @@ impl<R> LinesStream<R> {
}
/// Obtain a pinned reference to the inner `Lines<R>`.
#[allow(clippy::wrong_self_convention)] // https://github.com/rust-lang/rust-clippy/issues/4546
pub fn as_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Lines<R>> {
self.project().inner
}
+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();
}
}
+2 -2
View File
@@ -15,8 +15,8 @@ pub struct SignalStream {
impl SignalStream {
/// Create a new `SignalStream`.
pub fn new(interval: Signal) -> Self {
Self { inner: interval }
pub fn new(signal: Signal) -> Self {
Self { inner: signal }
}
/// Get back the inner `Signal`.
-1
View File
@@ -30,7 +30,6 @@ impl<R> SplitStream<R> {
}
/// Obtain a pinned reference to the inner `Split<R>`.
#[allow(clippy::wrong_self_convention)] // https://github.com/rust-lang/rust-clippy/issues/4546
pub fn as_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Split<R>> {
self.project().inner
}
+1 -1
View File
@@ -10,7 +10,7 @@ use tokio::sync::watch::error::RecvError;
/// A wrapper around [`tokio::sync::watch::Receiver`] that implements [`Stream`].
///
/// This stream will start by yielding the current value when the WatchStream is polled,
/// 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`].
///
+3
View File
@@ -15,18 +15,21 @@ fn require_unpin<T: Unpin>(_t: &T) {}
#[allow(dead_code)]
struct Invalid;
#[allow(unused)]
trait AmbiguousIfSend<A> {
fn some_item(&self) {}
}
impl<T: ?Sized> AmbiguousIfSend<()> for T {}
impl<T: ?Sized + Send> AmbiguousIfSend<Invalid> for T {}
#[allow(unused)]
trait AmbiguousIfSync<A> {
fn some_item(&self) {}
}
impl<T: ?Sized> AmbiguousIfSync<()> for T {}
impl<T: ?Sized + Sync> AmbiguousIfSync<Invalid> for T {}
#[allow(unused)]
trait AmbiguousIfUnpin<A> {
fn some_item(&self) {}
}
+10 -1
View File
@@ -6,13 +6,14 @@ mod support {
}
use support::mpsc;
use tokio_stream::adapters::Chain;
#[tokio::test]
async fn basic_usage() {
let one = stream::iter(vec![1, 2, 3]);
let two = stream::iter(vec![4, 5, 6]);
let mut stream = one.chain(two);
let mut stream = visibility_test(one, two);
assert_eq!(stream.size_hint(), (6, Some(6)));
assert_eq!(stream.next().await, Some(1));
@@ -39,6 +40,14 @@ async fn basic_usage() {
assert_eq!(stream.next().await, None);
}
fn visibility_test<I, S1, S2>(s1: S1, s2: S2) -> Chain<S1, S2>
where
S1: Stream<Item = I>,
S2: Stream<Item = I>,
{
s1.chain(s2)
}
#[tokio::test]
async fn pending_first() {
let (tx1, rx1) = mpsc::unbounded_channel_stream();
+1
View File
@@ -1,5 +1,6 @@
#![warn(rust_2018_idioms)]
#![cfg(all(feature = "time", not(target_os = "wasi")))] // Wasi does not support panic recovery
#![cfg(panic = "unwind")]
use parking_lot::{const_mutex, Mutex};
use std::error::Error;
+235 -2
View File
@@ -1,14 +1,17 @@
use futures::stream::iter;
use tokio_stream::{self as stream, pending, Stream, StreamExt, StreamMap};
use tokio_test::{assert_ok, assert_pending, assert_ready, task};
use std::future::{poll_fn, Future};
use std::pin::{pin, Pin};
use std::task::Poll;
mod support {
pub(crate) mod mpsc;
}
use support::mpsc;
use std::pin::Pin;
macro_rules! assert_ready_some {
($($t:tt)*) => {
match assert_ready!($($t)*) {
@@ -328,3 +331,233 @@ fn one_ready_many_none() {
fn pin_box<T: Stream<Item = U> + 'static, U>(s: T) -> Pin<Box<dyn Stream<Item = U>>> {
Box::pin(s)
}
type UsizeStream = Pin<Box<dyn Stream<Item = usize> + Send>>;
#[tokio::test]
async fn poll_next_many_zero() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(pending()) as UsizeStream);
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut vec![], 0)).await;
assert_eq!(n, 0);
}
#[tokio::test]
async fn poll_next_many_empty() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut vec![], 1)).await;
assert_eq!(n, 0);
}
#[tokio::test]
async fn poll_next_many_pending() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(pending()) as UsizeStream);
let mut is_pending = false;
poll_fn(|cx| {
let poll = stream_map.poll_next_many(cx, &mut vec![], 1);
is_pending = poll.is_pending();
Poll::Ready(())
})
.await;
assert!(is_pending);
}
#[tokio::test]
async fn poll_next_many_not_enough() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(iter([0usize].into_iter())) as UsizeStream);
stream_map.insert(1, Box::pin(iter([1usize].into_iter())) as UsizeStream);
let mut buffer = vec![];
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut buffer, 3)).await;
assert_eq!(n, 2);
assert_eq!(buffer.len(), 2);
assert!(buffer.contains(&(0, 0)));
assert!(buffer.contains(&(1, 1)));
}
#[tokio::test]
async fn poll_next_many_enough() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(iter([0usize].into_iter())) as UsizeStream);
stream_map.insert(1, Box::pin(iter([1usize].into_iter())) as UsizeStream);
let mut buffer = vec![];
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut buffer, 2)).await;
assert_eq!(n, 2);
assert_eq!(buffer.len(), 2);
assert!(buffer.contains(&(0, 0)));
assert!(buffer.contains(&(1, 1)));
}
#[tokio::test]
async fn poll_next_many_correctly_loops_around() {
for _ in 0..10 {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(iter([0usize].into_iter())) as UsizeStream);
stream_map.insert(1, Box::pin(iter([0usize, 1].into_iter())) as UsizeStream);
stream_map.insert(2, Box::pin(iter([0usize, 1, 2].into_iter())) as UsizeStream);
let mut buffer = vec![];
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut buffer, 3)).await;
assert_eq!(n, 3);
assert_eq!(
std::mem::take(&mut buffer)
.into_iter()
.map(|(_, v)| v)
.collect::<Vec<_>>(),
vec![0, 0, 0]
);
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut buffer, 2)).await;
assert_eq!(n, 2);
assert_eq!(
std::mem::take(&mut buffer)
.into_iter()
.map(|(_, v)| v)
.collect::<Vec<_>>(),
vec![1, 1]
);
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut buffer, 1)).await;
assert_eq!(n, 1);
assert_eq!(
std::mem::take(&mut buffer)
.into_iter()
.map(|(_, v)| v)
.collect::<Vec<_>>(),
vec![2]
);
}
}
#[tokio::test]
async fn next_many_zero() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(pending()) as UsizeStream);
let n = poll_fn(|cx| pin!(stream_map.next_many(&mut vec![], 0)).poll(cx)).await;
assert_eq!(n, 0);
}
#[tokio::test]
async fn next_many_empty() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
let n = stream_map.next_many(&mut vec![], 1).await;
assert_eq!(n, 0);
}
#[tokio::test]
async fn next_many_pending() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(pending()) as UsizeStream);
let mut is_pending = false;
poll_fn(|cx| {
let poll = pin!(stream_map.next_many(&mut vec![], 1)).poll(cx);
is_pending = poll.is_pending();
Poll::Ready(())
})
.await;
assert!(is_pending);
}
#[tokio::test]
async fn next_many_not_enough() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(iter([0usize].into_iter())) as UsizeStream);
stream_map.insert(1, Box::pin(iter([1usize].into_iter())) as UsizeStream);
let mut buffer = vec![];
let n = poll_fn(|cx| pin!(stream_map.next_many(&mut buffer, 3)).poll(cx)).await;
assert_eq!(n, 2);
assert_eq!(buffer.len(), 2);
assert!(buffer.contains(&(0, 0)));
assert!(buffer.contains(&(1, 1)));
}
#[tokio::test]
async fn next_many_enough() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(iter([0usize].into_iter())) as UsizeStream);
stream_map.insert(1, Box::pin(iter([1usize].into_iter())) as UsizeStream);
let mut buffer = vec![];
let n = poll_fn(|cx| pin!(stream_map.next_many(&mut buffer, 2)).poll(cx)).await;
assert_eq!(n, 2);
assert_eq!(buffer.len(), 2);
assert!(buffer.contains(&(0, 0)));
assert!(buffer.contains(&(1, 1)));
}
#[tokio::test]
async fn next_many_correctly_loops_around() {
for _ in 0..10 {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(iter([0usize].into_iter())) as UsizeStream);
stream_map.insert(1, Box::pin(iter([0usize, 1].into_iter())) as UsizeStream);
stream_map.insert(2, Box::pin(iter([0usize, 1, 2].into_iter())) as UsizeStream);
let mut buffer = vec![];
let n = poll_fn(|cx| pin!(stream_map.next_many(&mut buffer, 3)).poll(cx)).await;
assert_eq!(n, 3);
assert_eq!(
std::mem::take(&mut buffer)
.into_iter()
.map(|(_, v)| v)
.collect::<Vec<_>>(),
vec![0, 0, 0]
);
let n = poll_fn(|cx| pin!(stream_map.next_many(&mut buffer, 2)).poll(cx)).await;
assert_eq!(n, 2);
assert_eq!(
std::mem::take(&mut buffer)
.into_iter()
.map(|(_, v)| v)
.collect::<Vec<_>>(),
vec![1, 1]
);
let n = poll_fn(|cx| pin!(stream_map.next_many(&mut buffer, 1)).poll(cx)).await;
assert_eq!(n, 1);
assert_eq!(
std::mem::take(&mut buffer)
.into_iter()
.map(|(_, v)| v)
.collect::<Vec<_>>(),
vec![2]
);
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
#![cfg(all(feature = "time", feature = "sync", feature = "io-util"))]
use tokio::time::{self, sleep, Duration};
use tokio_stream::{self, StreamExt};
use tokio_stream::StreamExt;
use tokio_test::*;
use futures::stream;
+24
View File
@@ -1,3 +1,27 @@
# 0.4.4 (March 14, 2024)
- task: mark `Spawn` as `#[must_use]` ([#6371])
- test: increase MSRV to 1.63 ([#6126])
- test: update category slug ([#5953])
[#5953]: https://github.com/tokio-rs/tokio/pull/5953
[#6126]: https://github.com/tokio-rs/tokio/pull/6126
[#6371]: https://github.com/tokio-rs/tokio/pull/6371
# 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])
+3 -3
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"
version = "0.4.4"
edition = "2021"
rust-version = "1.56"
rust-version = "1.70"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -14,7 +14,7 @@ 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"] }
+18 -22
View File
@@ -1,25 +1,21 @@
Copyright (c) 2023 Tokio Contributors
MIT License
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
Copyright (c) Tokio Contributors
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+2 -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()
}
@@ -478,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
@@ -1,3 +1,4 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![warn(
missing_debug_implementations,
missing_docs,
+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)+))
}
}
+1 -1
View File
@@ -8,7 +8,7 @@
//! intervals between items.
//!
//! # Usage
//! To use the `StreamMock`, you need to create a builder using[`StreamMockBuilder`]. The builder
//! 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
+6 -3
View File
@@ -25,8 +25,6 @@
//! assert!(task.poll().is_ready(), "Task was not ready!");
//! ```
#![allow(clippy::mutex_atomic)]
use std::future::Future;
use std::mem;
use std::ops;
@@ -51,6 +49,7 @@ pub fn spawn<T>(task: T) -> Spawn<T> {
/// Future spawned on a mock task that can be used to poll the future or stream
/// without needing pinning or context types.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Spawn<T> {
task: MockTask,
future: Pin<Box<T>>,
@@ -127,7 +126,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();
@@ -149,6 +148,10 @@ impl<T: Stream> Stream for Spawn<T> {
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.future.as_mut().poll_next(cx)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.future.size_hint()
}
}
impl MockTask {
+25
View File
@@ -0,0 +1,25 @@
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio_stream::Stream;
use tokio_test::task;
/// A [`Stream`] that has a stub size hint.
struct SizedStream;
impl Stream for SizedStream {
type Item = ();
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Poll::Pending
}
fn size_hint(&self) -> (usize, Option<usize>) {
(100, Some(200))
}
}
#[test]
fn test_spawn_stream_size_hint() {
let spawn = task::spawn(SizedStream);
assert_eq!(spawn.size_hint(), (100, Some(200)));
}
+78
View File
@@ -1,3 +1,81 @@
# 0.7.11 (May 4th, 2024)
This release updates the MSRV to 1.63. ([#6126])
### Added
- either: implement `Sink` for `Either` ([#6239])
- time: add `DelayQueue::deadline` ([#6163])
- time: add `FutureExt::timeout` ([#6276])
### Changed
- codec: assert compatibility between `LengthDelimitedCodec` options ([#6414])
- codec: make tracing feature optional for codecs ([#6434])
- io: add `T: ?Sized` to `tokio_util::io::poll_read_buf` ([#6441])
- sync: remove `'static` bound on `impl Sink for PollSender` ([#6397])
### Documented
- codec: add examples for `FramedRead` and `FramedWrite` ([#6310])
- codec: document cancel safety of `SinkExt::send` and `StreamExt::next` ([#6417])
[#6126]: https://github.com/tokio-rs/tokio/pull/6126
[#6163]: https://github.com/tokio-rs/tokio/pull/6163
[#6239]: https://github.com/tokio-rs/tokio/pull/6239
[#6276]: https://github.com/tokio-rs/tokio/pull/6276
[#6310]: https://github.com/tokio-rs/tokio/pull/6310
[#6397]: https://github.com/tokio-rs/tokio/pull/6397
[#6414]: https://github.com/tokio-rs/tokio/pull/6414
[#6417]: https://github.com/tokio-rs/tokio/pull/6417
[#6434]: https://github.com/tokio-rs/tokio/pull/6434
[#6441]: https://github.com/tokio-rs/tokio/pull/6441
# 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.
+4 -4
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.8"
version = "0.7.11"
edition = "2021"
rust-version = "1.56"
rust-version = "1.70"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -25,7 +25,7 @@ full = ["codec", "compat", "io-util", "time", "net", "rt"]
net = ["tokio/net"]
compat = ["futures-io",]
codec = ["tracing"]
codec = []
time = ["tokio/time","slab"]
io = []
io-util = ["io", "tokio/rt", "tokio/io-util"]
@@ -45,7 +45,7 @@ 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", default-features = false, optional = true }
[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
+18 -22
View File
@@ -1,25 +1,21 @@
Copyright (c) 2023 Tokio Contributors
MIT License
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
Copyright (c) Tokio Contributors
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1 -1
View File
@@ -2,7 +2,7 @@ use crate::codec::decoder::Decoder;
use crate::codec::encoder::Encoder;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::{cmp, fmt, io, str, usize};
use std::{cmp, fmt, io, str};
const DEFAULT_SEEK_DELIMITERS: &[u8] = b",;\n\r";
const DEFAULT_SEQUENCE_WRITER: &[u8] = b",";
+12 -1
View File
@@ -20,10 +20,21 @@ pin_project! {
/// You can create a `Framed` instance by using the [`Decoder::framed`] adapter, or
/// by using the `new` function seen below.
///
/// # Cancellation safety
///
/// * [`futures_util::sink::SinkExt::send`]: if send is used as the event in a
/// `tokio::select!` statement and some other branch completes first, then it is
/// guaranteed that the message was not sent, but the message itself is lost.
/// * [`tokio_stream::StreamExt::next`]: This method is cancel safe. The returned
/// future only holds onto a reference to the underlying stream, so dropping it will
/// never lose a value.
///
/// [`Stream`]: futures_core::Stream
/// [`Sink`]: futures_sink::Sink
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`Decoder::framed`]: crate::codec::Decoder::framed()
/// [`futures_util::sink::SinkExt::send`]: futures_util::sink::SinkExt::send
/// [`tokio_stream::StreamExt::next`]: https://docs.rs/tokio-stream/latest/tokio_stream/trait.StreamExt.html#method.next
pub struct Framed<T, U> {
#[pin]
inner: FramedImpl<T, U, RWFrames>
@@ -130,7 +141,7 @@ impl<T, U> Framed<T, U> {
/// things like gzip or TLS, which require both read and write access to the
/// underlying object.
///
/// This objects takes a stream and a readbuffer and a writebuffer. These field
/// This objects takes a stream and a `readbuffer` and a `writebuffer`. These field
/// can be obtained from an existing `Framed` with the [`into_parts`] method.
///
/// If you want to work more directly with the streams and sink, consider
+1 -1
View File
@@ -12,7 +12,6 @@ use std::borrow::{Borrow, BorrowMut};
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tracing::trace;
pin_project! {
#[derive(Debug)]
@@ -218,6 +217,7 @@ where
// Make sure we've got room for at least one byte to read to ensure
// that we don't get a spurious 0 that looks like EOF.
state.buffer.reserve(1);
#[allow(clippy::blocks_in_conditions)]
let bytect = match poll_read_buf(pinned.inner.as_mut(), cx, &mut state.buffer).map_err(
|err| {
trace!("Got an error, going to errored state");
+10
View File
@@ -14,8 +14,18 @@ use std::task::{Context, Poll};
pin_project! {
/// A [`Stream`] of messages decoded from an [`AsyncRead`].
///
/// For examples of how to use `FramedRead` with a codec, see the
/// examples on the [`codec`] module.
///
/// # Cancellation safety
/// * [`tokio_stream::StreamExt::next`]: This method is cancel safe. The returned
/// future only holds onto a reference to the underlying stream, so dropping it will
/// never lose a value.
///
/// [`Stream`]: futures_core::Stream
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`codec`]: crate::codec
/// [`tokio_stream::StreamExt::next`]: https://docs.rs/tokio-stream/latest/tokio_stream/trait.StreamExt.html#method.next
pub struct FramedRead<T, D> {
#[pin]
inner: FramedImpl<T, D, ReadFrame>,
+11
View File
@@ -15,7 +15,18 @@ use std::task::{Context, Poll};
pin_project! {
/// A [`Sink`] of frames encoded to an `AsyncWrite`.
///
/// For examples of how to use `FramedWrite` with a codec, see the
/// examples on the [`codec`] module.
///
/// # Cancellation safety
///
/// * [`futures_util::sink::SinkExt::send`]: if send is used as the event in a
/// `tokio::select!` statement and some other branch completes first, then it is
/// guaranteed that the message was not sent, but the message itself is lost.
///
/// [`Sink`]: futures_sink::Sink
/// [`codec`]: crate::codec
/// [`futures_util::sink::SinkExt::send`]: futures_util::sink::SinkExt::send
pub struct FramedWrite<T, E> {
#[pin]
inner: FramedImpl<T, E, WriteFrame>,
+127 -69
View File
@@ -75,55 +75,23 @@
//!
//! ## Example 1
//!
//! The following will parse a `u16` length field at offset 0, including the
//! frame head in the yielded `BytesMut`.
//!
//! ```
//! # use tokio::io::AsyncRead;
//! # use tokio_util::codec::LengthDelimitedCodec;
//! # fn bind_read<T: AsyncRead>(io: T) {
//! LengthDelimitedCodec::builder()
//! .length_field_offset(0) // default value
//! .length_field_type::<u16>()
//! .length_adjustment(0) // default value
//! .num_skip(0) // Do not strip frame header
//! .new_read(io);
//! # }
//! # pub fn main() {}
//! ```
//!
//! The following frame will be decoded as such:
//!
//! ```text
//! INPUT DECODED
//! +-- len ---+--- Payload ---+ +-- len ---+--- Payload ---+
//! | \x00\x0B | Hello world | --> | \x00\x0B | Hello world |
//! +----------+---------------+ +----------+---------------+
//! ```
//!
//! The value of the length field is 11 (`\x0B`) which represents the length
//! of the payload, `hello world`. By default, [`FramedRead`] assumes that
//! the length field represents the number of bytes that **follows** the
//! length field. Thus, the entire frame has a length of 13: 2 bytes for the
//! frame head + 11 bytes for the payload.
//!
//! ## Example 2
//!
//! The following will parse a `u16` length field at offset 0, omitting the
//! frame head in the yielded `BytesMut`.
//!
//! ```
//! # use tokio::io::AsyncRead;
//! # use tokio_stream::StreamExt;
//! # use tokio_util::codec::LengthDelimitedCodec;
//! # fn bind_read<T: AsyncRead>(io: T) {
//! LengthDelimitedCodec::builder()
//! # #[tokio::main]
//! # async fn main() {
//! # let io: &[u8] = b"\x00\x0BHello world";
//! let mut reader = LengthDelimitedCodec::builder()
//! .length_field_offset(0) // default value
//! .length_field_type::<u16>()
//! .length_adjustment(0) // default value
//! // `num_skip` is not needed, the default is to skip
//! .new_read(io);
//! # let res = reader.next().await.unwrap().unwrap().to_vec();
//! # assert_eq!(res, b"Hello world");
//! # }
//! # pub fn main() {}
//! ```
//!
//! The following frame will be decoded as such:
@@ -135,27 +103,32 @@
//! +----------+---------------+ +---------------+
//! ```
//!
//! This is similar to the first example, the only difference is that the
//! frame head is **not** included in the yielded `BytesMut` value.
//! The value of the length field is 11 (`\x0B`) which represents the length
//! of the payload, `hello world`. By default, [`FramedRead`] assumes that
//! the length field represents the number of bytes that **follows** the
//! length field. Thus, the entire frame has a length of 13: 2 bytes for the
//! frame head + 11 bytes for the payload.
//!
//! ## Example 3
//! ## Example 2
//!
//! The following will parse a `u16` length field at offset 0, including the
//! frame head in the yielded `BytesMut`. In this case, the length field
//! **includes** the frame head length.
//! frame head in the yielded `BytesMut`.
//!
//! ```
//! # use tokio::io::AsyncRead;
//! # use tokio_stream::StreamExt;
//! # use tokio_util::codec::LengthDelimitedCodec;
//! # fn bind_read<T: AsyncRead>(io: T) {
//! LengthDelimitedCodec::builder()
//! # #[tokio::main]
//! # async fn main() {
//! # let io: &[u8] = b"\x00\x0BHello world";
//! let mut reader = LengthDelimitedCodec::builder()
//! .length_field_offset(0) // default value
//! .length_field_type::<u16>()
//! .length_adjustment(-2) // size of head
//! .num_skip(0)
//! .length_adjustment(2) // Add head size to length
//! .num_skip(0) // Do NOT skip the head
//! .new_read(io);
//! # let res = reader.next().await.unwrap().unwrap().to_vec();
//! # assert_eq!(res, b"\x00\x0BHello world");
//! # }
//! # pub fn main() {}
//! ```
//!
//! The following frame will be decoded as such:
@@ -163,10 +136,46 @@
//! ```text
//! INPUT DECODED
//! +-- len ---+--- Payload ---+ +-- len ---+--- Payload ---+
//! | \x00\x0D | Hello world | --> | \x00\x0D | Hello world |
//! | \x00\x0B | Hello world | --> | \x00\x0B | Hello world |
//! +----------+---------------+ +----------+---------------+
//! ```
//!
//! This is similar to the first example, the only difference is that the
//! frame head is **included** in the yielded `BytesMut` value. To achieve
//! this, we need to add the header size to the length with `length_adjustment`,
//! and set `num_skip` to `0` to prevent skipping the head.
//!
//! ## Example 3
//!
//! The following will parse a `u16` length field at offset 0, omitting the
//! frame head in the yielded `BytesMut`. In this case, the length field
//! **includes** the frame head length.
//!
//! ```
//! # use tokio_stream::StreamExt;
//! # use tokio_util::codec::LengthDelimitedCodec;
//! # #[tokio::main]
//! # async fn main() {
//! # let io: &[u8] = b"\x00\x0DHello world";
//! let mut reader = LengthDelimitedCodec::builder()
//! .length_field_offset(0) // default value
//! .length_field_type::<u16>()
//! .length_adjustment(-2) // size of head
//! .new_read(io);
//! # let res = reader.next().await.unwrap().unwrap().to_vec();
//! # assert_eq!(res, b"Hello world");
//! # }
//! ```
//!
//! The following frame will be decoded as such:
//!
//! ```text
//! INPUT DECODED
//! +-- len ---+--- Payload ---+ +--- Payload ---+
//! | \x00\x0D | Hello world | --> | Hello world |
//! +----------+---------------+ +---------------+
//! ```
//!
//! In most cases, the length field represents the length of the payload
//! only, as shown in the previous examples. However, in some protocols the
//! length field represents the length of the whole frame, including the
@@ -179,17 +188,20 @@
//! frame head, including the frame head in the yielded `BytesMut`.
//!
//! ```
//! # use tokio::io::AsyncRead;
//! # use tokio_stream::StreamExt;
//! # use tokio_util::codec::LengthDelimitedCodec;
//! # fn bind_read<T: AsyncRead>(io: T) {
//! LengthDelimitedCodec::builder()
//! # #[tokio::main]
//! # async fn main() {
//! # let io: &[u8] = b"\x00\x00\x0B\xCA\xFEHello world";
//! let mut reader = LengthDelimitedCodec::builder()
//! .length_field_offset(0) // default value
//! .length_field_length(3)
//! .length_adjustment(2) // remaining head
//! .length_adjustment(3 + 2) // len field and remaining head
//! .num_skip(0)
//! .new_read(io);
//! # let res = reader.next().await.unwrap().unwrap().to_vec();
//! # assert_eq!(res, b"\x00\x00\x0B\xCA\xFEHello world");
//! # }
//! # pub fn main() {}
//! ```
//!
//! The following frame will be decoded as such:
@@ -223,17 +235,20 @@
//! included.
//!
//! ```
//! # use tokio::io::AsyncRead;
//! # use tokio_stream::StreamExt;
//! # use tokio_util::codec::LengthDelimitedCodec;
//! # fn bind_read<T: AsyncRead>(io: T) {
//! LengthDelimitedCodec::builder()
//! # #[tokio::main]
//! # async fn main() {
//! # let io: &[u8] = b"\xCA\x00\x0B\xFEHello world";
//! let mut reader = LengthDelimitedCodec::builder()
//! .length_field_offset(1) // length of hdr1
//! .length_field_type::<u16>()
//! .length_adjustment(1) // length of hdr2
//! .num_skip(3) // length of hdr1 + LEN
//! .new_read(io);
//! # let res = reader.next().await.unwrap().unwrap().to_vec();
//! # assert_eq!(res, b"\xFEHello world");
//! # }
//! # pub fn main() {}
//! ```
//!
//! The following frame will be decoded as such:
@@ -269,15 +284,19 @@
//! length.
//!
//! ```
//! # use tokio::io::AsyncRead;
//! # use tokio_stream::StreamExt;
//! # use tokio_util::codec::LengthDelimitedCodec;
//! # fn bind_read<T: AsyncRead>(io: T) {
//! LengthDelimitedCodec::builder()
//! # #[tokio::main]
//! # async fn main() {
//! # let io: &[u8] = b"\xCA\x00\x0F\xFEHello world";
//! let mut reader = LengthDelimitedCodec::builder()
//! .length_field_offset(1) // length of hdr1
//! .length_field_type::<u16>()
//! .length_adjustment(-3) // length of hdr1 + LEN, negative
//! .num_skip(3)
//! .new_read(io);
//! # let res = reader.next().await.unwrap().unwrap().to_vec();
//! # assert_eq!(res, b"\xFEHello world");
//! # }
//! ```
//!
@@ -308,17 +327,20 @@
//! frame head, excluding the 4th byte from the yielded `BytesMut`.
//!
//! ```
//! # use tokio::io::AsyncRead;
//! # use tokio_stream::StreamExt;
//! # use tokio_util::codec::LengthDelimitedCodec;
//! # fn bind_read<T: AsyncRead>(io: T) {
//! LengthDelimitedCodec::builder()
//! # #[tokio::main]
//! # async fn main() {
//! # let io: &[u8] = b"\x00\x00\x0B\xFFHello world";
//! let mut reader = LengthDelimitedCodec::builder()
//! .length_field_offset(0) // default value
//! .length_field_length(3)
//! .length_adjustment(0) // default value
//! .num_skip(4) // skip the first 4 bytes
//! .new_read(io);
//! # let res = reader.next().await.unwrap().unwrap().to_vec();
//! # assert_eq!(res, b"Hello world");
//! # }
//! # pub fn main() {}
//! ```
//!
//! The following frame will be decoded as such:
@@ -386,6 +408,10 @@ use std::{cmp, fmt, mem};
/// `Builder` enables constructing configured length delimited codecs. Note
/// that not all configuration settings apply to both encoding and decoding. See
/// the documentation for specific methods for more detail.
///
/// Note that the if the value of [`Builder::max_frame_length`] becomes larger than
/// what can actually fit in [`Builder::length_field_length`], it will be clipped to
/// the maximum value that can fit.
#[derive(Debug, Clone, Copy)]
pub struct Builder {
// Maximum frame length
@@ -635,7 +661,6 @@ mod builder {
impl LengthFieldType for u64 {}
#[cfg(any(
target_pointer_width = "8",
target_pointer_width = "16",
target_pointer_width = "32",
target_pointer_width = "64",
@@ -935,8 +960,12 @@ impl Builder {
/// # }
/// ```
pub fn new_codec(&self) -> LengthDelimitedCodec {
let mut builder = *self;
builder.adjust_max_frame_len();
LengthDelimitedCodec {
builder: *self,
builder,
state: DecodeState::Head,
}
}
@@ -1018,6 +1047,35 @@ impl Builder {
self.num_skip
.unwrap_or(self.length_field_offset + self.length_field_len)
}
fn adjust_max_frame_len(&mut self) {
// This function is basically `std::u64::saturating_add_signed`. Since it
// requires MSRV 1.66, its implementation is copied here.
//
// TODO: use the method from std when MSRV becomes >= 1.66
fn saturating_add_signed(num: u64, rhs: i64) -> u64 {
let (res, overflow) = num.overflowing_add(rhs as u64);
if overflow == (rhs < 0) {
res
} else if overflow {
u64::MAX
} else {
0
}
}
// Calculate the maximum number that can be represented using `length_field_len` bytes.
let max_number = match 1u64.checked_shl((8 * self.length_field_len) as u32) {
Some(shl) => shl - 1,
None => u64::MAX,
};
let max_allowed_len = saturating_add_signed(max_number, self.length_adjustment as i64);
if self.max_frame_len as u64 > max_allowed_len {
self.max_frame_len = usize::try_from(max_allowed_len).unwrap_or(usize::MAX);
}
}
}
impl Default for Builder {
+3 -1
View File
@@ -2,10 +2,12 @@ use crate::codec::decoder::Decoder;
use crate::codec::encoder::Encoder;
use bytes::{Buf, BufMut, BytesMut};
use std::{cmp, fmt, io, str, usize};
use std::{cmp, fmt, io, str};
/// 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)]
+67 -1
View File
@@ -1,4 +1,4 @@
//! Adaptors from AsyncRead/AsyncWrite to Stream/Sink
//! Adaptors from `AsyncRead`/`AsyncWrite` to Stream/Sink
//!
//! Raw I/O objects work with byte sequences, but higher-level code usually
//! wants to batch these into meaningful chunks, called "frames".
@@ -7,6 +7,71 @@
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
//! Framed streams are also known as transports.
//!
//! # Example encoding using `LinesCodec`
//!
//! The following example demonstrates how to use a codec such as [`LinesCodec`] to
//! write framed data. [`FramedWrite`] can be used to achieve this. Data sent to
//! [`FramedWrite`] are first framed according to a specific codec, and then sent to
//! an implementor of [`AsyncWrite`].
//!
//! ```
//! use futures::sink::SinkExt;
//! use tokio_util::codec::LinesCodec;
//! use tokio_util::codec::FramedWrite;
//!
//! #[tokio::main]
//! async fn main() {
//! let buffer = Vec::new();
//! let messages = vec!["Hello", "World"];
//! let encoder = LinesCodec::new();
//!
//! // FramedWrite is a sink which means you can send values into it
//! // asynchronously.
//! let mut writer = FramedWrite::new(buffer, encoder);
//!
//! // To be able to send values into a FramedWrite, you need to bring the
//! // `SinkExt` trait into scope.
//! writer.send(messages[0]).await.unwrap();
//! writer.send(messages[1]).await.unwrap();
//!
//! let buffer = writer.get_ref();
//!
//! assert_eq!(buffer.as_slice(), "Hello\nWorld\n".as_bytes());
//! }
//!```
//!
//! # Example decoding using `LinesCodec`
//! The following example demonstrates how to use a codec such as [`LinesCodec`] to
//! read a stream of framed data. [`FramedRead`] can be used to achieve this. [`FramedRead`]
//! will keep reading from an [`AsyncRead`] implementor until a whole frame, according to a codec,
//! can be parsed.
//!
//!```
//! use tokio_stream::StreamExt;
//! use tokio_util::codec::LinesCodec;
//! use tokio_util::codec::FramedRead;
//!
//! #[tokio::main]
//! async fn main() {
//! let message = "Hello\nWorld".as_bytes();
//! let decoder = LinesCodec::new();
//!
//! // FramedRead can be used to read a stream of values that are framed according to
//! // a codec. FramedRead will read from its input (here `buffer`) until a whole frame
//! // can be parsed.
//! let mut reader = FramedRead::new(message, decoder);
//!
//! // To read values from a FramedRead, you need to bring the
//! // `StreamExt` trait into scope.
//! let frame1 = reader.next().await.unwrap().unwrap();
//! let frame2 = reader.next().await.unwrap().unwrap();
//!
//! assert!(reader.next().await.is_none());
//! assert_eq!(frame1, "Hello");
//! assert_eq!(frame2, "World");
//! }
//! ```
//!
//! # The Decoder trait
//!
//! A [`Decoder`] is used together with [`FramedRead`] or [`Framed`] to turn an
@@ -248,6 +313,7 @@
//! [`AsyncWrite`]: tokio::io::AsyncWrite
//! [`Stream`]: futures_core::Stream
//! [`Sink`]: futures_sink::Sink
//! [`SinkExt`]: futures::sink::SinkExt
//! [`SinkExt::close`]: https://docs.rs/futures/0.3/futures/sink/trait.SinkExt.html#method.close
//! [`FramedRead`]: struct@crate::codec::FramedRead
//! [`FramedWrite`]: struct@crate::codec::FramedWrite
+34 -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));
}
}
@@ -164,6 +164,39 @@ where
}
}
impl<L, R, Item, Error> futures_sink::Sink<Item> for Either<L, R>
where
L: futures_sink::Sink<Item, Error = Error>,
R: futures_sink::Sink<Item, Error = Error>,
{
type Error = Error;
fn poll_ready(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::result::Result<(), Self::Error>> {
delegate_call!(self.poll_ready(cx))
}
fn start_send(self: Pin<&mut Self>, item: Item) -> std::result::Result<(), Self::Error> {
delegate_call!(self.start_send(item))
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::result::Result<(), Self::Error>> {
delegate_call!(self.poll_flush(cx))
}
fn poll_close(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::result::Result<(), Self::Error>> {
delegate_call!(self.poll_close(cx))
}
}
#[cfg(test)]
mod tests {
use super::*;
+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)
}
}
+2 -2
View File
@@ -18,7 +18,7 @@ pin_project! {
}
impl<R, F> InspectReader<R, F> {
/// Create a new InspectReader, wrapping `reader` and calling `f` for the
/// Create a new `InspectReader`, wrapping `reader` and calling `f` for the
/// new data supplied by each read call.
///
/// The closure will only be called with an empty slice if the inner reader
@@ -100,7 +100,7 @@ pin_project! {
}
impl<W, F> InspectWriter<W, F> {
/// Create a new InspectWriter, wrapping `write` and calling `f` for the
/// Create a new `InspectWriter`, wrapping `write` and calling `f` for the
/// data successfully written by each write call.
///
/// The closure `f` will never be called with an empty slice. A vectored

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