Compare commits

...
Author SHA1 Message Date
Daksh 3911cb8523 chore: prepare Tokio v1.47.0 (#7482) 2025-07-26 16:50:58 +02:00
Aria Andika d545aa2601 sync: add sync::Notify::notified_owned() (#7465) 2025-07-26 21:45:34 +08:00
Daksh 911ab21d70 sync: add SetOnce (#7418) 2025-07-25 10:29:14 +02:00
Conrad Ludgate 9e94fa7e15 task: remove raw-entry feature from hashbrown dep (#7252) 2025-07-22 15:52:54 +02:00
QiandTaiki Endo 0d234c3cf9 ci: unfreeze wasm-unknown-unknown from rustc 1.81 (#7471)
Signed-off-by: ADD-SP <[email protected]>
Co-authored-by: Taiki Endo <[email protected]>
2025-07-21 09:48:04 +08:00
Taiki Endo 3754e059b6 ci: use ubuntu-24.04-arm instead of ubuntu-22.04-arm (#7470) 2025-07-20 17:19:26 +09:00
Stepan TubanovandLuca BRUNO 6d868d96ce sync: fix CancellationToken failing to cancel the ready futures (#7462)
This patch fixes an issue where the `CancellationToken::run_until_cancelled` never cancels the `Future` that returns `Ready` at the first `poll`.

---------

Co-authored-by: Luca BRUNO <[email protected]>
2025-07-20 10:03:29 +08:00
Qi 0a3fe46086 sync: remove duplicated code in OnceCell tests (#7458)
Signed-off-by: ADD-SP <[email protected]>
2025-07-14 10:28:02 +02:00
Qi 154d7d5fe6 ci: cleanup legacy R-loom-multi-thread-alt label from the labeler (#7457)
Signed-off-by: ADD-SP <[email protected]>
2025-07-12 16:37:21 +02:00
Pepijn Van Eeckhoudt 611b793356 coop: add cooperative and poll_proceed (#7405) 2025-07-11 08:07:44 +00:00
Jess Izen 888ee60e41 metrics: properly annotate required features for 64-bit-only metrics (#7449) 2025-07-09 15:03:22 +02:00
Qi 7dd4d8a30e runtime: cleanup legacy tests of alt multi-threaded runtime (#7451)
Signed-off-by: ADD-SP <[email protected]>
2025-07-09 19:52:33 +08:00
Orson Peters 085e616c87 sync: use swap in AtomicWaker::wake (#7450) 2025-07-09 10:09:34 +00:00
Aaron Chen a7896d07f1 chore: update CI to clippy 1.88 (#7452) 2025-07-09 08:34:24 +02:00
Erich Gubler aff24dfbeb deps: upgrade windows-sys from 0.52 to 0.59 (#7117) 2025-07-07 17:49:12 +00:00
Thomas de Zeeuw 71cc9ab4c2 deps: update to socket2 v0.6 (#7443) 2025-07-07 13:45:30 +02:00
02cbe4591b runtime: improve safety comments of Readiness<'_> (#7415)
Signed-off-by: ADD-SP <[email protected]>
Co-authored-by: Eliza Weisman <[email protected]>
Co-authored-by: Alice Ryhl <[email protected]>
2025-07-07 19:04:08 +08:00
Motoyuki Kimura 0783797520 runtime: fix handling of cancelled io_uring Ops (#7436) 2025-07-05 10:41:37 +02:00
Eliza Weisman ab3ff69cf2 chore: prepare to release v1.46.1 (#7444)
# 1.46.1 (July 4th, 2025)

This release fixes incorrect spawn locations in runtime task hooks for tasks
spawned using `tokio::spawn` rather than `Runtime::spawn`. This issue only
effected the spawn location in `TaskMeta::spawned_at`, and did not effect task
locations in Tracing events.

## Unstable

- runtime: add `TaskMeta::spawn_location` tracking where a task was spawned
  ([#7440)])

[#7440]: https://github.com/tokio-rs/tokio/pull/7440
2025-07-04 19:27:35 +00:00
Eliza Weisman a0d5b8ab30 runtime(unstable): fix task hook spawn locations for tokio::spawn (#7440)
## Motivation

Unfortunately, due to an oversight on my part, the capturing of spawn
locations was only tested with the `Runtime::spawn` method, and *not*
with `tokio::spawn`/`tokio::task::spawn`, which is how most tasks are
spawned in Real Life. And, it turned out that because this was not
tested...well, it was broken. Agh. My bad.

## Solution

Although the whole call chain for spawning tasks using `tokio::spawn`
was correctly annotated with `#[track_caller]`, the location wasn't
propagated correctly because of the `context::with_current(|handle| {
... })` closure that accesses the current runtime. Because the call to
spawn the task occurs inside a closure, the *closure*'s location is
captured instead of the caller. This means any task spawned by
`tokio::spawn` records its location as being in
`tokio/src/task/spawn.rs`, which is not what we'd like. This commit
fixes that by capturing the spawn location outside the `with_current`
closure and passing it in explicitly.

I've updated the tests to also spawn a task with `tokio::spawn`, so that
we ensure this works correctly.
2025-07-04 09:25:40 -07:00
shangchenglumetro a1ee3ef218 chore: fix some minor typos in the comments (#7442)
Signed-off-by: shangchenglumetro <[email protected]>
2025-07-04 11:29:27 +02:00
Alice Ryhl 171cd148a3 changelog: fix typo in pipe::OpenOptions for 1.46.0 (#7439) 2025-07-02 09:27:11 +00:00
Eliza Weisman 3f1f268583 chore: prepare Tokio v1.46.0 (#7437) 2025-07-02 10:20:42 +02:00
Eliza Weisman 3e890cc017 rt(unstable): add spawn Location to TaskMeta (#7417)
As described in issue #7411, task spawning APIs are currently annotated
with `#[track_caller]`, allowing us to capture the location in the user
source code where the task was spawned. This is used for `tracing`
events used by `tokio-console` and friends. However, this information is
*not* exposed to the runtime `on_task_spawn`, `on_before_task_poll`,
`on_after_task_poll`, and `on_task_terminate` hooks, which is a shame,
as it would be useful there as well.

This branch adds the task's spawn location to the `TaskMeta` struct
provided to the runtime's task hooks. This is implemented by storing a
`&'static Location<'static>` in the task's `Core` alongside the
`task::Id`. In [this comment][1], @ADD-SP suggested storing the
`Location` in the task's `Trailer`.

I opted to store it in the `Core` instead, as the `Trailer` is intended
to store "cold" data that is only accessed when the task _completes_,
and not on every poll. Since the task meta is passed to the
`on_before_task_poll` and `on_after_task_poll` hooks, we would be
accessing the `Trailer` on polls if we stored the `Location` there.
Therefore, I put it in the `Core`, instead, which contains data that we
access every time the task is polled.

Closes #7411

[1]: https://github.com/tokio-rs/tokio/issues/7411#issuecomment-2993377045
2025-06-30 18:13:42 +00:00
xumaple 69290a6432 net: derive Clone for net::unix::SocketAddr (#7422) 2025-06-30 15:31:04 +02:00
Alice Ryhl e2b175848b fuzz: cfg fuzz tests under cfg(test) (#7428) 2025-06-30 10:24:10 +02:00
GarmashAlex b7a75b5be3 net: update AsRawFd doc link to current Rust stdlib location (#7429) 2025-06-27 14:08:31 +00:00
Marshall Pierce 6b705b3053 net: allow pipe::OpenOptions::read_write on Android (#7426) 2025-06-27 09:39:55 +02:00
VolodymyrBg 3636fd018a net: fix broken link of RawFd in TcpSocket docs (#7416) 2025-06-24 21:15:39 +08:00
Alice Ryhl 2506c9fa99 benches: revert "properly gate unix benches" (#7412)
This reverts commit 933fa498d0.
2025-06-21 14:52:04 +02:00
QiandAlice Ryhl b3a14483bf sync: improve docs of tokio_util::sync::CancellationToken (#7408)
Co-authored-by: Alice Ryhl <[email protected]>
2025-06-19 06:32:41 +08:00
Qi 013f323def docs: add a missing panic scenario of time::advance (#7394) 2025-06-18 20:25:12 +08:00
yanyuxing b926700065 sync: add DropGuardRef for CancellationToken (#7407) 2025-06-18 10:25:41 +02:00
Alice Ryhl 99a03a502e runtime: add thread_park_ok test (#7402) 2025-06-16 09:49:00 +02:00
Tim Vilgot Mikael Fredenberg 933fa498d0 benches: properly gate unix benches (#7392) 2025-06-11 10:07:34 +02:00
Motoyuki Kimura 9f848c9f54 rt: add check for io_uring availability at runtime (#7357) 2025-06-11 03:26:23 +09:00
Geoffry Song 912b862a05 task: add AbortOnDropHandle::detach (#7400) 2025-06-10 09:35:48 +02:00
Qi 714e5b571f runtime: move impl Schedule for Arc<Handle> (#7398) 2025-06-09 09:32:59 +02:00
Jess Izen 8e999e3806 macros: add biased mode to join! and try_join! (#7307) 2025-06-09 09:31:05 +02:00
Oliver E. Anderson 1d980145cb io: document cancellation safety of AsyncWriteExt::flush (#7364) 2025-06-08 20:57:36 +02:00
Alice Ryhl 8259133ca0 task: disallow blocking in LocalSet::{poll,drop} (#7372) 2025-06-08 20:56:55 +02:00
Yuyi Wang 38d88c6799 net: add cygwin support (#7393) 2025-06-08 09:55:43 +02:00
Austin Bonander c38de96b94 sync: add same_channel analogue to OwnedPermit (#7389) 2025-06-07 13:10:48 +02:00
tiif 2440d113ff ci: enable tests using fcntl in miri (#7382) 2025-06-04 09:55:27 +02:00
Maximilian Hubert ab8d7b82a1 readme: fix double period in reactor description (#7363) 2025-05-28 21:24:53 +02:00
Qi 9563707aaa time: cumulative minor improvements (#7358) 2025-05-28 14:01:31 +02:00
Jeff Vander Stoep 193c1574a1 examples: update rand crate to 0.9.1 (#7371) 2025-05-28 11:32:24 +00:00
Alice Ryhl 328bd049f6 io: clarify behavior of seeking when start_seek is not used (#7366) 2025-05-28 13:00:33 +02:00
Tim Vilgot Mikael Fredenberg 4380de9fe9 chore: replace manual vtable definitions with Wake (#7342) 2025-05-28 02:28:21 +09:00
Alice Ryhl 98f527f42d Merge tag 'tokio-1.45.1' 2025-05-24 07:32:44 -07:00
Alice Ryhl 3768696d92 chore: prepare Tokio v1.45.1 (#7359) 2025-05-24 14:27:50 +00:00
Alice Ryhl d7d4f7d08b sync: update broadcast docs on allocation failure (#7352) 2025-05-24 16:10:13 +02:00
Jason Gin 421a7b001c rt: do not track time-based metrics on wasm32-unknown-unknown (#7322) 2025-05-23 19:12:28 +00:00
Alice Ryhl b1bdb3c57b ci: update macros_type_mismatch for Rust 1.87.0 (#7339)
(cherry picked from commit a48e418dcb)
2025-05-23 10:39:10 -07:00
Qi 7ec77a0677 time: eliminate UnsafeCell around the TimerShared (#7329) 2025-05-23 19:24:29 +02:00
Qi 55e3ed2a39 runtime: eliminate unnecessary lfence while operating on queue::Local<T> (#7340) 2025-05-23 19:24:00 +02:00
Alice Ryhl 17d8c2b29d runtime: various minor LocalRuntime improvements (#7346) 2025-05-20 19:37:41 +02:00
Motoyuki Kimura 327bec2caf rt: add infrastructure code for io_uring (#7320) 2025-05-21 02:36:52 +09:00
Qi ea30a5ea5e time: rename cached_when to registered_when (#7333) 2025-05-20 14:55:28 +02:00
剑来 0cf95f0673 net: fix docs for recv_buffer_size method (#7336) 2025-05-17 09:51:31 +00:00
Alice Ryhl a48e418dcb ci: update macros_type_mismatch for Rust 1.87.0 (#7339) 2025-05-17 18:24:36 +09:00
Qi 4cbcb687f4 time: address style issues (#7328) 2025-05-12 23:32:23 +09:00
Qi 0715e6defc time: remove outdated explicitly drop call of Mutex (#7326)
This drop was firstly introduced by [#3289],
and the next line invokes `panic!`.

In [#5434], the original `panic!` was replaced
with `return Err`, so dropping it explicitly
is no longer necessary.

[#3289]: https://github.com/tokio-rs/tokio/pull/3289
[#5434]: https://github.com/tokio-rs/tokio/pull/5434
2025-05-12 22:18:43 +09:00
Alice Ryhl bdd64cc9d3 runtime: add doc note that on_*_task_poll is unstable (#7311) 2025-05-06 08:46:05 +00:00
soundofspace f0fdef80c4 net: ignore NotConnected in TcpStream::shutdown (#7290) 2025-05-06 17:27:40 +09:00
Carl Lerche 00754c8f9c chore: prepare Tokio v1.45.0 (#7308) 2025-05-06 08:43:25 +02:00
Carl Lerche 1ae9434e8e time: revert "use sharding for timer implementation" related changes (#7226)
The work on sharding the timer implementation has caused a measurable performance regression due to increased contention. This patch reverts the current work on sharding. The next step will be to work on a per-worker timer wheel.
2025-05-05 10:48:02 -07:00
Taiki Endo 8895bba448 ci: Test AArch64 Windows (#7288) 2025-05-05 11:10:20 +02:00
Till Rohrmann 48ca254d92 time: update sleep documentation to reflect maximum allowed duration (#7302) 2025-05-04 19:43:07 +03:30
Suryakant Soni a0af02a396 compat: add more documentation to tokio_util::compat (#7279) 2025-04-28 16:20:47 +02:00
Owen Leung 0ce3a1188a metrics: stabilize worker_park_count and worker_unpark_count (#7276) 2025-04-28 11:11:33 +02:00
Alice Ryhl 1ea9ce11d4 ci: fix cfg!(miri) declarations in tests (#7286) 2025-04-24 14:50:19 +02:00
Alice Ryhl 4d4d12613b chore: prepare tokio-util v0.7.15 (#7283) 2025-04-23 13:13:48 +02:00
Alan Somers 5490267a79 fs: update the mockall dev dependency to 0.13.0 (#7234) 2025-04-23 11:13:10 +02:00
Nicholas Skinsacos 1434b32b5a examples: improve echo example consistency (#7256) 2025-04-18 18:02:25 +03:30
Carl Lerche 159a3b2c85 rt(unstable): remove alt multi-threaded runtime (#7275)
The alternative multi-threaded runtime started as an experiment. We have been
unable to find real-world benefit. Work has halted on this effort, so lets get
rid of it.
2025-04-17 13:33:55 -07:00
M.Amin Rayej ce87dcfbf0 runtime: document the queue behavior of spawn_blocking (#7269) 2025-04-17 22:49:29 +03:30
Yichi Zhang d41d49d202 metrics: fix panic comment in max_error docs (#7273) 2025-04-18 00:40:59 +09:00
Paul Mabileau 7a6c424f6e process: add Command::spawn_with (#7249)
Signed-off-by: Paul Mabileau <[email protected]>
2025-04-16 14:07:36 +02:00
Conrad Ludgate c3037adac9 task: properly handle removed entries in JoinMap (#7264) 2025-04-15 16:33:20 +02:00
Nicholas Skinsacos 964fd06e0f benches: add helper functions for building runtimes (#7260) 2025-04-14 12:23:00 +03:30
Paolo Barbolini 817fa605ee fs: avoid some copies in tokio::fs::write (#7199) 2025-04-08 15:43:38 +02:00
Alex Bakon 77de684ed9 runtime: mark runtime::Handle unwind-safe (#7230) 2025-04-08 13:38:04 +02:00
Alice Ryhl 83d550e511 changelog: fix release date of v1.44.2 (#7248) 2025-04-08 10:15:58 +02:00
Alice Ryhl 1b3d3e7cd6 Merge 'tokio-1.43.1-fix-release-date' into 'master' (#7247) 2025-04-08 10:15:23 +02:00
Alice Ryhl 9e044e144b changelog: fix release date of v1.43.1 (#7246) 2025-04-08 10:04:13 +02:00
Alice Ryhl cb08fbc6c3 Merge 'tokio-1.42.1' into 'tokio-1.43.x' (#7245) 2025-04-08 10:02:57 +02:00
Alice Ryhl e59584a661 changelog: fix release date of v1.42.1 (#7244) 2025-04-08 09:51:07 +02:00
Alice Ryhl f7fb0bdc7a chore: prepare Tokio v1.42.1 2025-04-07 17:15:14 +02:00
Alice Ryhl 9faea740df Merge 'tokio-1.38.x' into 'tokio.1.42.x' 2025-04-07 16:33:23 +02:00
Alan Somers 2a8c551631 tokio: update mio-aio dev dependency to 1.0 (#7235)
This eliminates a duplicate dependency on mio
2025-04-06 12:15:37 +02:00
Carl Lerche 676630785b Merge branch 'tokio-1.44.x' into forward-port-1.44.x 2025-04-05 08:20:03 -07:00
Carl Lerche ec4b1d7215 chore: forward port 1.43.x 2025-04-04 16:13:58 -07:00
Carl Lerche e3c3a56718 Merge branch 'tokio-1.43.x' into forward-port-1.43.x 2025-04-04 16:11:53 -07:00
Carl Lerche a7b658c35b chore: prepare Tokio v1.43.1 release 2025-04-04 08:31:21 -07:00
Carl Lerche c1c8d1033d Merge remote-tracking branch 'origin/tokio-1.38.x' into forward-port-1.38.x 2025-04-04 08:18:13 -07:00
Carl Lerche aa303bc205 chore: prepare Tokio v1.38.2 release 2025-04-02 21:58:38 -07:00
Carl Lerche 7b6ccb515f chore: backport CI fixes 2025-04-02 14:34:28 -07:00
Carl Lerche 4b174ce2c9 sync: fix cloning value when receiving from broadcast channel
The broadcast channel does not require values to implement `Sync` yet it calls
the `.clone()` method without synchronizing. This is unsound logic. This patch
adds per-value synchronization on receive to handle this case. It is unlikely
any usage of the broadcast channel is currently at risk of the unsoundeness
issue as it requires accessing a `!Sync` type during `.clone()`, which would be
very unusual when using the broadcast channel.
2025-04-02 14:25:05 -07:00
jimmycathy 0ec4d0db4d docs: remove redundant words in comment (#7224) 2025-03-16 18:08:25 +09:00
Jamie d83ba30d8d task: explicitly state that TaskTracker does not abort tasks on Drop (#7223) 2025-03-14 14:48:54 +00:00
LongYinan f339587b27 deps: update hashbrown to 0.15 (#7219) 2025-03-14 09:47:18 +01:00
Alice Ryhl b663abe091 chore: update tokio-util version number (#7215) 2025-03-13 11:22:34 +01:00
Motoyuki Kimura 9a11efc262 chore: prepare tokio-util v0.7.14 (#7215) 2025-03-13 11:20:52 +01:00
Alice Ryhl d760b26666 Merge tokio-1.44.1 into master (#7218) 2025-03-13 09:44:03 +01:00
Alice Ryhl d413c9c02a chore: prepare Tokio v1.44.1 (#7217) 2025-03-13 09:13:25 +01:00
Carl Lerche addbfb9204 rt: skip defer queue in block_in_place context (#7216) 2025-03-13 08:18:13 +01:00
Motoyuki Kimura 5687043328 test: remove unused dependencies (#7214) 2025-03-13 01:28:01 +09:00
Vitaly Shukela 72c87a7724 test: add io::Builder::name for better panic messages (#7212)
Introduce tokio_test::io::Builder::name to configure
name of the mock object, to include in panic messages.

Also show number of remaining actions or action index
in some cases to help debugging failed tests.
2025-03-13 00:34:09 +09:00
Alphyr 8507e28f89 Remove an old custom OnceCell implementation in favor of std (#7208) 2025-03-11 08:15:26 +01:00
Alphyr 7efcab43c9 Do not require Unpin for some trait impls (#7204) 2025-03-11 08:14:22 +01:00
Ty Larrabee e4a39d2ef6 sync: add CancellationToken::run_until_cancelled_owned (#7081) 2025-03-10 14:24:25 +01:00
Owen Leung afd3678f89 metrics: stabilize worker_total_busy_duration (#6899) 2025-03-10 10:29:45 +01:00
Alice Ryhl 8182ecf262 chore: prepare Tokio v1.44.0 (#7202) 2025-03-07 21:11:03 +01:00
Motoyuki Kimura a258bff701 ci: enable printing in multi thread loom tests (#7200) 2025-03-07 13:33:16 +01:00
Stepan Koltsov e076d21f67 process: clarify Child::kill behavior (#7162) 2025-03-06 14:59:51 +03:30
Noah Kennedy 042433cdcc net: debug_assert on creating a tokio socket from a blocking one (#7166)
See #5595 and #7172.

This adds a debug assertion that checks that a supplied underlying std socket is set to nonblocking mode when constructing a tokio socket object from such an object.

This only works on unix.
2025-03-05 18:10:30 +00:00
M.Amin Rayej 0284d1b5c8 macros: make select! budget-aware (#7164) 2025-03-05 01:07:18 +03:30
Carl Lerche 710bc8071e rt: coop should yield using waker defer strategy (#7185) 2025-03-04 15:02:43 +01:00
Alice Ryhl a2b12bd579 readme: adjust release schedule to once per month (#7191) 2025-03-03 14:04:42 +03:30
Jonathan Hiles e7b593cbee process: fix grammar of the ChildStdin struct doc comment (#7192) 2025-03-03 13:01:35 +03:30
kilavvy 3aaf4a5377 coop: adjust grammar in tests/coop_budget.rs (#7173) 2025-03-03 09:59:03 +01:00
Alice Ryhl 8e741c1c0e tokio: mark 1.43 as LTS (#7189) 2025-03-03 09:54:26 +01:00
dlzht 47d46455bd util: optimize buffer reserve for AnyDelimiterCodec::encode (#7188) 2025-03-02 19:53:19 +03:30
Adriano Mourão 20c1fdc678 runtime: consistently use worker_threads instead of core_threads (#7186) 2025-02-28 10:19:24 +01:00
Josh Triplett 638ce93591 io: add read_exact_arc to safely read a new uninitialized Arc (#7165) 2025-02-27 14:07:49 +01:00
Kyle Cotton c853991b1e io: swap reader/writer in simplex doc test (#7176) 2025-02-25 14:49:19 +00:00
Josh Triplett 6d410f6c90 util: fix example of Buf implementor in StreamReader docs (#7167) 2025-02-21 09:22:16 +03:30
Finomnis a27575f284 signal: fix CTRL_CLOSE, CTRL_LOGOFF, CTRL_SHUTDOWN on windows (#7122) 2025-02-19 12:27:13 +01:00
Stepan Koltsov 13fbdace66 process: add test for Child::kill after Child::wait (#7163) 2025-02-19 00:52:50 +03:30
Timo 4380c3d821 sync: Added WeakSender to sync::broadcast::channel (#7100) 2025-02-17 21:24:29 +01:00
katelyn martin 383da87313 sync: implement oneshot::Receiver::is_empty() (#7153) 2025-02-17 02:01:11 +03:30
katelyn martin 17117b591e sync: implement oneshot::Receiver::is_terminated() (#7152) 2025-02-16 22:55:50 +03:30
Nathaniel Bajo aa70f6c5f0 io: add documentation for SyncIoBridge with examples and alternatives (#6815) 2025-02-16 19:01:52 +01:00
Dylan Laufenberg 67c343d9e9 docs: fix nesting of next sections under examples (#7159)
Promote the feature flags and supported platforms sections out from
under examples, as they are not examples. Adjust their subsections
accordingly. Expose these subsections via navigation sidebar.
2025-02-15 23:08:13 +01:00
Oleksandr Babak 34cdcc7d87 macros: docs about select! alternatives (#7110) 2025-02-15 22:49:23 +01:00
Stepan Koltsov 8e134172dd process: calling start_kill on exited child should not fail (#7160) 2025-02-15 13:48:22 +03:30
M.Amin Rayej 605ef578df coop: expose coop as a public module (#7116) 2025-02-14 18:56:12 +03:30
M.Amin Rayej 9b578f0c9d ci: bump freeBSD image version (#7158) 2025-02-14 17:37:16 +03:30
M.Amin Rayej 0a15768380 io: clean up buffer casts (#7142) 2025-02-10 19:57:25 +03:30
Motoyuki Kimura eb1a2ee990 net: rename the argument for send_to (#7146) 2025-02-08 15:05:05 +03:30
M.Amin Rayej 8713d39228 process: add example for reading Child stdout (#7141) 2025-02-08 13:31:02 +03:30
Florian Gäbler 7e27911911 fs: align symlink and hardlink parameter names with std (#7143) 2025-02-07 23:40:15 +03:30
Alice Ryhl 4b3da20c98 fs: empty reads on File should not start a background read (#7139) 2025-02-06 01:37:29 +03:30
Jason Gin b8ac94ed70 rt: add before and after task poll callbacks (#7120)
Add callbacks for poll start and stop, enabling users to instrument these points
in the runtime's life cycle.
2025-01-30 21:14:00 +00:00
Oliver Wanglerandow 5086e56dcb io: implemented get_ref and get_mut for SyncIoBridge (#7128)
Co-authored-by: ow <[email protected]>
2025-01-28 15:28:07 +01:00
Ariel Ben-YehudaandAriel Ben-Yehuda 2671ffb55b tracing: make the task tracing API unstable piblkc (#6972)
* make self-tracing public

* address review comments

* try to fix doctest

* adjust imports to fit standard

* more documentation

---------

Co-authored-by: Ariel Ben-Yehuda <[email protected]>
2025-01-27 13:09:23 -08:00
Taiki Endo 7f09959b0a chore: use [lints] to address unexpected_cfgs lint (#7124) 2025-01-25 17:46:21 +01:00
Taiki Endo fb7dec0e95 ci: test AArch64/Armv7hf Linux on ubuntu-22.04-arm runner (#7123) 2025-01-25 11:17:37 +01:00
M.Amin Rayej ee19b0ed73 net: fix warnings when building the docs (#7113) 2025-01-22 11:48:43 +01:00
Josh McKinney c081dfe3ce macros: characterization tests for ? operator fail (#7069)
When a `?` operator is used in a tokio entry point function (wrapped in
`#[tokio::main]`), which has a Option or Result return type, but where
the function does not actually return that type correctly, currently the
compiler returns two errors instead of just one. The first of which is
incorrect and only exists due to the macro expanding to an async block.

```
cannot use the `?` operator in an async block that returns `()`
```

This commit is a characterization test for this behavior to help show
when it's fixed (or even changed for better / worse)
2025-01-22 10:55:00 +01:00
M.Amin Rayej 21a13f9eea runtime: clean up magic number in registration set (#7112) 2025-01-21 14:40:32 +01:00
Motoyuki Kimura a82bdeebe9 sync: handle panic during mpsc drop (#7094) 2025-01-13 18:36:51 +01:00
Evan Rittenhouse 435e39001b sync: fix sync::broadcast::Sender<T>::closed() doctest (#7090)
The test's previous iteration could sometimes flake since we didn't
await the completion of the first task. Since the tasks only existed to
`move` the relevant `rx`'s in, to force a drop, we can omit them
entirely and drop the `rx`s via `drop()`. This prevents any
scheduling-related flakes.
2025-01-12 12:33:07 +01:00
Marshall Lee dabae570b1 ci: add spellcheck.dic validation (#7062) 2025-01-10 13:39:23 +01:00
29 6bd3be2e45 process: add Command::get_kill_on_drop() (#7086) 2025-01-10 12:35:13 +01:00
Alice Ryhl 6fc1a8c8da ci: fix ci error about wasm32-wasip1 (#7085) 2025-01-10 18:59:35 +09:00
Evan Rittenhouse 5c8cd33820 sync: add broadcast::Sender::closed (#6685) 2025-01-09 16:37:49 +01:00
Alice Ryhl 5f3296df77 chore: prepare Tokio v1.43.0 (#7079) 2025-01-08 16:57:25 +01:00
Alice Ryhl cc974a646b chore: prepare tokio-macros v2.5.0 (#7078) 2025-01-08 16:56:31 +01:00
Russell Cohen 15495fd883 metrics: improve flexibility of H2Histogram Configuration (#6963) 2025-01-08 15:00:34 +01:00
Paolo Barbolini ad4183412a io: don't call set_len before initializing vector in Blocking (#7054) 2025-01-08 10:05:58 +00:00
Alice Ryhl bd3e857737 runtime: move is_join_waker_set assertion in unset_waker (#7072) 2025-01-06 22:19:53 +01:00
Thomas Schilling 15f73666f1 runtime: fix LocalRuntime doc links (#7074) 2025-01-06 14:49:42 +00:00
Alice Ryhl fd2048dad1 ci: split miri jobs into unit and integration tests (#7071) 2025-01-06 15:39:06 +01:00
Alice Ryhl e8f39157b6 chore: use unsync loads for unsync_load (#7073)
This reverts #6203 and #6179.
2025-01-06 14:16:15 +01:00
Aeon 67f127769b net: fix ambiguity in TcpStream::try_write_vectored docs (#7067) 2025-01-06 11:16:58 +01:00
philomathic_life 463502cbaf io: clarify ReadBuf::uninit allows initialized buffers as well (#7053) 2025-01-06 11:16:15 +01:00
Sebastian Urban a1520f5525 runtime: fix thread parking on WebAssembly (#7041)
On WebAssembly the notification state was not checked
before sleeping and thus wrongfully ignored.

Additionally this refines the check whether threads are
available on a particular WebAssembly target.
2025-01-06 11:14:28 +01:00
Al Liu acd6627d6d net: add UdpSocket::peek methods (#7068) 2025-01-06 11:12:08 +01:00
Aeon 2353806daf io: change AsyncReadExt::read docs formatting (#7066) 2025-01-04 21:30:59 +01:00
tiif 7be2bfa744 net: fix typo in miri comment (#7063) 2025-01-03 10:11:05 +00:00
Andrea Ciprietti e066431c94 sync: extend documentation for watch::Receiver::wait_for (#7038) 2025-01-02 21:33:56 +01:00
tiif 2052938a9f ci: run doc tests with miri (#7060) 2025-01-02 13:14:30 +00:00
Paolo Barbolini b3ff911c38 io: use Buf::put_bytes in Repeat read impl (#7055) 2024-12-30 13:44:55 +01:00
Fancy2209 9d42b977df misc: get haiku working 2024-12-30 03:36:40 -06:00
Timo 970d880ceb task: drop the join waker of a task eagerly (#6986) 2024-12-29 18:17:02 +01:00
Felipe Lima 4ca13e6015 sync: fix typos in OnceCell docs (#7047) 2024-12-21 14:09:38 +01:00
CMelz b54b9d4338 codec: fix typo in API documentation (#7044) 2024-12-18 13:33:21 +01:00
Rafael Bachmann 10e23d1c62 docs: replace match guards by pattern matching in examples (#7035) 2024-12-15 12:01:37 +01:00
Rain aa7e0cef72 signal: add support for realtime signals on illumos (#7029)
The API was added in libc 0.2.168.

Also added a test for realtime signals.
2024-12-13 22:32:34 -06:00
Noisy bfa8cadaa0 chore: spelling and date format Corrections (#7018) 2024-12-10 13:16:41 +01:00
Josh McKinney 6d15c6cacb stream: add examples to wrapper types (#7024) 2024-12-10 11:14:18 +00:00
Motoyuki Kimura 79a2afae9f util: enable Either to use underlying AsyncWrite implementation (#7025) 2024-12-10 01:52:16 +09:00
Ariel Ben-Yehuda 48e07a6d10 taskdump: add accessor methods for backtrace (#6975) 2024-12-09 16:16:09 +09:00
tiif eb72ddde3b task: run spawn_pinned tests with miri (#7023) 2024-12-08 12:45:16 +01:00
29 dc16b12edb process: add Command::into_std() (#7014) 2024-12-07 12:56:02 +01:00
Alice Ryhl 67355c6d23 chore: prepare tokio-stream v0.1.17 (#7020) 2024-12-06 11:19:06 +01:00
Alice Ryhl 405d746d38 signal: remove oneshot channels from tests (#7015) 2024-12-05 08:36:45 -08:00
Alice Ryhl e0d1293fac ci: add instructions that explain how to fix spellcheck errors (#7016) 2024-12-05 12:28:27 +01:00
Rain 480c010b01 signal: add SignalKind::info on illumos (#6995) 2024-12-05 10:18:45 +01:00
Alice Ryhl c032ea0203 ci: detect trailing whitespace (#7013) 2024-12-04 17:23:13 +01:00
Alice Ryhl 0b31c2f73d chore: prepare tokio-util v0.7.13 (#7012) 2024-12-04 12:49:55 +01:00
Zettroke 129f9fc0c8 codec: fix incorrect handling of invalid utf-8 in LinesCodec::decode_eof (#7011) 2024-12-04 10:18:14 +00:00
Hayden Stainsby b5c227d51f tracing: move tracing instrumentation tests into tokio tests (#7007)
In #6112, tests for the tracing instrumentation were introduced. They
had to live in their own test crate under `tokio/tests` because the
`tracing-mock` crate that the tests use had not yet been published to
crates.io.

Now `tracing-mock` has been published to crates.io and so the separate
test crate and separate job to run it are no longer necessary. The
tracing instrumentation tests can be placed in with the other
integration tests in the `tokio` crate.

The tests themselves have also been updated to match the changes in the
`tracing-mock` API since the version which was being used.
2024-12-04 07:47:48 +01:00
Alice Ryhl dcae2b9eb8 ci: unfreeze FreeBSD from rustc 1.81 (#7009) 2024-12-03 14:33:28 +00:00
Alice Ryhl bb9d57017e chore: prepare Tokio v1.42.0 (#7005) 2024-12-03 14:48:39 +01:00
leopardracer af9c683d52 tests: fix typo in build test instructions (#7004) 2024-12-03 13:11:36 +00:00
Alice Ryhl 4bc5a1a058 ci: allow Unicode-3.0 license for unicode-ident (#7006)
Signed-off-by: Alice Ryhl <[email protected]>
2024-12-03 13:57:31 +01:00
quininer f8948ea021 runtime: do not defer yield_now inside block_in_place (#6999) 2024-12-02 13:52:00 +01:00
David Herberth bce9780dd3 time: use array::from_fn instead of manually creating array (#7000) 2024-12-01 19:54:30 +01:00
Alice Ryhl 38151f30cb readme: unlist 1.32.x as LTS release (#6997) 2024-11-29 13:16:35 +00:00
Alice Ryhl 5dda72d338 ci: pin valgrind to rustc 1.82 (#6998) 2024-11-29 21:50:01 +09:00
Nur c07257f99f io: simplify io readiness logic (#6966) 2024-11-21 17:31:39 +01:00
Taliyah Webb d08578fc9a time: fix a typo in Instant docs (#6982) 2024-11-20 12:02:55 +00:00
tiif 4047d7962a miri: add annotations for tests with miri ignore (#6981) 2024-11-20 11:44:28 +01:00
Maarten de Vries cbdceb91ac io: add AsyncFd::try_io() and try_io_mut() (#6967) 2024-11-19 17:55:54 +01:00
Hamir Mahal d4178cf349 tokio: avoid positional fmt params when possible (#6978) 2024-11-18 13:50:58 +01:00
tiif 2f899144ed io: avoid ptr->ref->ptr roundtrip in RegistrationSet (#6929) 2024-11-16 11:16:09 +01:00
tiif 6255598baa docs: update miri test command in CONTRIBUTING.md (#6976) 2024-11-15 17:29:11 +01:00
Jonas Fassbender 772e0ca8a6 docs: fix documentation build on Windows (#6945) 2024-11-14 01:28:55 +09:00
Michael_Liu 3b677d1fde net: fix docs discription in unix module (#6791) 2024-11-11 14:14:46 +00:00
Alice Ryhl bb7ca7507b chore: prepare Tokio v1.41.1 (#6959)
Signed-off-by: Alice Ryhl <[email protected]>
2024-11-07 11:56:09 +01:00
Russell Cohen 4a34b77af5 metrics: fix bug with wrong number of buckets for the histogram (#6957) 2024-11-07 08:45:36 +01:00
DaniPopes 8897885425 docs: fix mismatched backticks in CONTRIBUTING.md (#6951)
It's rendered correctly at least on GitHub, but syntax highlighting fails after that point.
2024-11-04 10:29:57 +00:00
Taiki Endo 0dbdd196b6 ci: update cargo-check-external-types to 0.1.13 (#6949) 2024-11-02 16:50:00 +09:00
Joseph Perez 94e55c092b net: fix typo in TcpStream internal comment (#6944) 2024-10-29 09:31:39 +00:00
Jonas Fassbender 4468f27c31 metrics: fixed flaky worker_steal_count test (#6932) 2024-10-28 09:26:45 +01:00
Jonas Fassbender 070a825999 metrics: removed race condition from global_queue_depth_multi_thread test (#6936) 2024-10-27 19:36:18 +01:00
Jonas Fassbender 946401c345 net: display net requirement for net::UdpSocket in docs (#6938) 2024-10-26 12:37:39 +02:00
Taiki Endo 0c01fd23b4 ci: use patched version of cargo-check-external-types to fix CI failure (#6937) 2024-10-26 10:27:59 +02:00
Alice Ryhl ebe241647e ci: use cargo deny (#6931) 2024-10-23 18:48:07 +02:00
Motoyuki Kimura 01e04daaa1 chore: prepare Tokio v1.41.0 (#6917) 2024-10-22 11:22:33 +02:00
Josh McKinney 92ccadeb3c runtime: fix stability feature flags for docs (#6909) 2024-10-22 11:19:57 +02:00
Russell Cohen fbfeb9a68a metrics: rename *_poll_count_* to *_poll_time_* (#6924)
A consistent bit of feedback I've heard is that the `poll_count_histogram` name is a little confusing since the value customers actually get out of it is `poll_times`.

This renames all public APIs from `poll_count` to `poll_time`. The existing APIs were deprecated with one exception: the newly added `poll_count_histogram_configuration` which hasn't been released yet was simply renamed.
2024-10-22 09:21:07 +02:00
Russell Cohen da745ff335 metrics: add H2 Histogram option to improve histogram granularity (#6897) 2024-10-21 14:05:45 +02:00
Jonas Fassbender ce1c74f1cc metrics: fix deadlock in injection_queue_depth_multi_thread test (#6916) 2024-10-21 10:04:39 +02:00
Motoyuki Kimura 28c9a14a2e metrics: rename injection_queue_depth to global_queue_depth (#6918) 2024-10-18 19:34:06 +00:00
Alice Ryhl 32e0b4325f ci: freeze FreeBSD and wasm-unknown-unknown on rustc 1.81 (#6911)
Signed-off-by: Alice Ryhl <[email protected]>
2024-10-18 13:14:17 +02:00
Rafael BachmannandRafael Bachmann 1656d8e231 sync: add mpsc::Receiver::blocking_recv_many (#6867)
Fixes: #6865
Co-authored-by: Rafael Bachmann <[email protected]>
2024-10-17 11:02:12 +02:00
Russell Cohen c9e998e4b3 ci: print the correct sort order of the dictionary on failure (#6905) 2024-10-17 10:45:38 +02:00
Noah Kennedy 512e9decfb rt: add LocalRuntime (#6808)
This change adds LocalRuntime, a new unstable runtime type which cannot be transferred across thread boundaries and supports spawn_local when called from the thread which owns the runtime.

The initial set of docs for this are iffy. Documentation is absent right now at the module level, with the docs for the LocalRuntime struct itself being somewhat duplicative of those for the `Runtime` type. This can be addressed later as stabilization nears.

This API has a few interesting implementation details:
- because it was considered beneficial to reuse the same Handle as the normal runtime, it is possible to call spawn_local from a runtime context while on a different thread from the one which drives the runtime and owns it. This forces us to check the thread ID before attempting a local spawn.
- An empty LocalOptions struct is passed into the build_local method in order to build the runtime. This will eventually have stuff in it like hooks.

Relates to #6739.
2024-10-12 10:39:23 -05:00
Motoyuki Kimura 5ada5114df task: stabilize task::Id related apis (#6891) 2024-10-11 10:38:07 +00:00
tiif 161b8c80d5 ci: test more things with miri (#6885) 2024-10-11 09:44:50 +02:00
Sören Meier 9cc4a81678 sync: add watch::Sender::sender_count (#6836)
This makes it possible to check if other senders exist. For example
If you are using a Sender as a subscriber to get a Receiver and might want
to know if the real sender is still running.
2024-10-11 04:31:11 +02:00
Name 679d7657dc io: document cancel safety of AsyncFd methods (#6890) 2024-10-09 01:08:41 +09:00
Hayden Stainsby c3a935541d task: add task size to tracing instrumentation (#6881)
In Tokio, the futures for tasks are stored on the stack unless they are
explicitly boxed, either by the user or auto-boxed by Tokio when they
are especially large. Auto-boxing now also occurs in release mode
(since #6826).

Having very large futures can be problematic as it can cause a stack
overflow. In some cases it might be desireable to have smaller futures,
even if they are placed on the heap.

This change adds the size of the future driving an async task or the
function driving a blocking task to the tracing instrumentation. In the
case of a future that is auto-boxed by Tokio, both the final size as well
the original size before boxing is included.

To do this, a new struct `SpawnMeta` gets passed down from where a
future might get boxed to where the instrumentation is added. This
contains the task name (optionally) and the original future or function
size. If the `tokio_unstable` cfg flag and the `tracing` feature aren't both
enabled, then this struct will be zero sized, which is a small improvement
on the previous behavior of unconditionally passing down an `Option<&str>`
for the name.

This will make this information immediately available in Tokio Console,
and will enable new lints which will warn users if they have large futures
(just for async tasks).

We have some tests under the `tracing-instrumentation` crate which test
that the `size.bytes` and `original_size.bytes` fields are set correctly.

The minimal version of `tracing` required for Tokio has been bumped from
0.1.25 to 0.1.29 to get the `Value` impl on `Option<T>`. Given that the current
version is 0.1.40, this seems reasonable, especially given that Tracing's MSRV
is still lower than Tokio's in the latest version.
2024-10-08 10:51:03 +02:00
Nur 29cd6ec1ec time: import Future trait from std instead of futures_core (#6884) 2024-10-07 14:13:09 +02:00
Evan RittenhouseandAlice Ryhl b68f5c7f38 task: stabilize task ids (#6793)
Co-authored-by: Alice Ryhl <[email protected]>
2024-10-06 08:50:21 +00:00
Motoyuki Kimura 6c5dbfa08c readme: update miri test command (#6883) 2024-10-05 17:22:20 +02:00
oxalica 2c14f88c90 macros: suppress clippy::needless_return in #[tokio::main] (#6874) 2024-09-28 12:33:51 +02:00
shray sharma e2e1e8e71d sync: fix Stream link in broadcast docs (#6873) 2024-09-27 13:49:06 +02:00
Timo 21df16d759 sync: apply cooperative scheduling to sync::broadcast::Receiver (#6870) 2024-09-26 16:52:46 +02:00
Timo c8af499990 sync: apply cooperative scheduling to sync::watch (#6846) 2024-09-26 12:36:22 +00:00
Nick Mathewson 623928e371 net: add conversions for unix SocketAddr (#6868) 2024-09-25 13:56:52 +00:00
Motoyuki Kimura 09bc9a05e4 chore: use boxed slice if possible (#6858) 2024-09-25 19:45:28 +09:00
Alice Ryhl 82628b8a78 metrics: don't hang in injection_queue_depth_multi_thread test (#6862) 2024-09-24 08:34:17 +02:00
Alice Ryhl 21cf5a5469 runtime: avoid pointer casts in IO driver on miri (#6859)
Signed-off-by: Alice Ryhl <[email protected]>
2024-09-23 20:32:37 +02:00
vxzyfx's github 8ef5163df8 stream: fix link on Peekable (#6861) 2024-09-23 14:56:42 +00:00
Owen Leung 542197cdb9 metrics: stabilize injection_queue_depth metric (#6854) 2024-09-22 18:38:37 +02:00
vxzyfx's github a302367b8f net: change quotes in docs (#6852) 2024-09-21 13:53:34 +02:00
Maximilian Hils b5de84d19b runtime: box futures larger than 16k on release mode (#6826) 2024-09-16 22:15:44 +02:00
Jonas Fassbender 02aaea28b9 sync: document runtime compatibility (#6833) 2024-09-16 21:58:24 +02:00
Rustin 83e922f051 macros: render more comprehensible documentation for try_join! (#6841)
Signed-off-by: Rustin170506 <[email protected]>
2024-09-14 11:34:45 +02:00
Benjamin Richner a2496548d1 net: fix examples for TcpSocket::{set_nodelay,nodelay} (#6840) 2024-09-12 15:07:16 +02:00
Motoyuki Kimura 0cea36fa3d net: fix handling of leading zero byte in from_abstract_name (#6838) 2024-09-11 20:15:12 +02:00
Nam Se Hyun d6213594ca fs: make available to wasm under tokio_unstable (#6822) 2024-09-11 12:55:07 +00:00
Alice Ryhl 91169992b2 io: recommend OwnedFd with AsyncFd (#6821)
Signed-off-by: Alice Ryhl <[email protected]>
2024-09-06 09:52:08 +02:00
Rustin 8046a87a99 macros: render more comprehensible documentation for join! (#6814)
Signed-off-by: Rustin170506 <[email protected]>
2024-09-06 09:36:25 +02:00
Sylwester Rąpała 5dcc848fc8 sync: add #[must_use] to Notified (#6828) 2024-09-06 09:03:27 +02:00
Timo bd4ccae184 time: add abstraction for RwLock to remove poisoning aspect (#6807)
With #6779 we removed unnecessary allocations from the timerwheel by
wrapping it in an `std::sync::RwLock`. Since the `Mutex` used in this
part of the project uses an abstraction in `loom::sync::Mutex` to get
rid of the poisoning aspects of `std::sync::Mutex` the same should
probably be done for the used read-write lock struct.

This commit introduces an abstraction to get rid of the poisoning
aspects of `std::sync::RwLock` by introducing a wrapper to the
`loom::sync` module similar to `loom::sync::Mutex`.

Refs: #6779
2024-09-05 23:48:05 +09:00
Dirkjan Ochtman 4ed0fa21e4 chore: prepare tokio-stream v0.1.16 (#6825) 2024-09-05 12:42:22 +02:00
Eduardo Sánchez Muñoz 12b2567b95 chore: use poll_fn from std (#6810) 2024-09-05 09:54:06 +02:00
Alice Ryhl 9681ce2b95 chore: make 1.38 an LTS (#6706) 2024-07-22 23:22:12 +02:00
428 changed files with 14122 additions and 9969 deletions
-25
View File
@@ -1,25 +0,0 @@
version: 2.1
jobs:
test-arm:
machine:
image: default
resource_class: arm.medium
environment:
# Change to pin rust version
RUST_STABLE: stable
steps:
- checkout
- run:
name: Install Rust
command: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o rustup.sh
chmod +x rustup.sh
./rustup.sh -y --default-toolchain $RUST_STABLE
source "$HOME"/.cargo/env
# Only run Tokio tests
- run: cargo test --all-features -p tokio
workflows:
ci:
jobs:
- test-arm
+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-14-1
image_family: freebsd-14-2
env:
RUST_STABLE: stable
RUST_NIGHTLY: nightly-2024-05-05
RUST_NIGHTLY: nightly-2025-01-25
RUSTFLAGS: -D warnings
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
+1 -1
View File
@@ -12,7 +12,7 @@ reproduce the failure on other operating systems, don't worry! The
[tokio-rs/illumos] team is responsible for maintaining Tokio's illumos support,
and can be called on to assist contributors with illumos-specific issues. Please
feel free to tag @tokio-rs/illumos to ask for help resolving build failures on
illumos
illumos.
[illumos]: https://www.illumos.org/
[Buildomat]: https://github.com/oxidecomputer/buildomat
-7
View File
@@ -19,10 +19,3 @@ R-loom-multi-thread:
- tokio/src/runtime/scheduler/multi_thread/**
- tokio/src/runtime/task/*
- tokio/src/runtime/task/**
R-loom-multi-thread-alt:
- tokio/src/runtime/scheduler/*
- tokio/src/runtime/scheduler/multi_thread_alt/*
- tokio/src/runtime/scheduler/multi_thread_alt/**
- tokio/src/runtime/task/*
- tokio/src/runtime/task/**
+6 -12
View File
@@ -13,18 +13,12 @@ permissions:
contents: read
jobs:
security-audit:
cargo-deny:
permissions:
checks: write # for rustsec/audit-check to create check
contents: read # for actions/checkout to fetch code
issues: write # for rustsec/audit-check to create issues
checks: write
contents: read
issues: write
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'ci skip')"
steps:
- uses: actions/checkout@v4
- name: Audit Check
# https://github.com/rustsec/audit-check/issues/2
uses: rustsec/audit-check@master
with:
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v2
+194 -78
View File
@@ -16,8 +16,10 @@ env:
RUSTUP_WINDOWS_PATH_ADD_BIN: 1
# Change to specific Rust release to pin
rust_stable: stable
rust_nightly: nightly-2024-05-05
rust_clippy: '1.77'
rust_nightly: nightly-2025-01-25
# Pin a specific miri version
rust_miri_nightly: nightly-2025-06-02
rust_clippy: '1.88'
# When updating this, also update:
# - README.md
# - tokio/README.md
@@ -45,10 +47,11 @@ jobs:
- test-workspace-all-features
- test-integration-tests-per-feature
- test-parking_lot
- test-tracing-instrumentation
- valgrind
- test-unstable
- miri
- miri-lib
- miri-test
- miri-doc
- asan
- cross-check
- cross-check-tier3
@@ -178,7 +181,7 @@ jobs:
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
@@ -244,34 +247,6 @@ 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
@@ -281,7 +256,7 @@ jobs:
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.rust_stable }}
toolchain: 1.82
- name: Install Valgrind
uses: taiki-e/install-action@valgrind
@@ -375,6 +350,39 @@ jobs:
# the unstable cfg to RustDoc
RUSTDOCFLAGS: --cfg tokio_unstable --cfg tokio_taskdump
test-uring:
name: test tokio full --cfg tokio_uring
needs: basics
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: 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 tokio full --cfg tokio_uring
run: |
set -euxo pipefail
cargo nextest run --all-features
cargo test --doc --all-features
working-directory: tokio
env:
RUSTFLAGS: --cfg tokio_uring -Dwarnings
# in order to run doctests for unstable features, we must also pass
# the unstable cfg to RustDoc
RUSTDOCFLAGS: --cfg tokio_uring
check-unstable-mt-counters:
name: check tokio full --internal-mt-counters
needs: basics
@@ -407,23 +415,67 @@ jobs:
# the unstable cfg to RustDoc
RUSTDOCFLAGS: --cfg tokio_unstable --cfg tokio_internal_mt_counters
miri:
name: miri
miri-lib:
name: miri-lib
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
- name: Install Rust ${{ env.rust_miri_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.rust_nightly }}
toolchain: ${{ env.rust_miri_nightly }}
components: miri
- name: Install cargo-nextest
uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- uses: Swatinem/rust-cache@v2
- name: miri
# Many of tests in tokio/tests and doctests use #[tokio::test] or
# #[tokio::main] that calls epoll_create1 that Miri does not support.
run: |
cargo miri test --features full --lib --no-fail-fast
cargo miri nextest run --features full --lib --no-fail-fast
working-directory: tokio
env:
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields
miri-test:
name: miri-test
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_miri_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.rust_miri_nightly }}
components: miri
- name: Install cargo-nextest
uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- uses: Swatinem/rust-cache@v2
- name: miri
run: |
cargo miri nextest run --features full --test '*' --no-fail-fast
working-directory: tokio
env:
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields
miri-doc:
name: miri-doc
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_miri_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.rust_miri_nightly }}
components: miri
- uses: Swatinem/rust-cache@v2
- name: miri-doc-test
run: |
cargo miri test --doc --all-features --no-fail-fast
working-directory: tokio
env:
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields
@@ -456,10 +508,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check semver
- name: Check `tokio` semver
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
rust-toolchain: ${{ env.rust_stable }}
package: tokio
release-type: minor
- name: Check semver for rest of the workspace
if: ${{ !startsWith(github.event.pull_request.base.ref, 'tokio-1.') }}
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
rust-toolchain: ${{ env.rust_stable }}
exclude: tokio
release-type: minor
cross-check:
@@ -492,6 +552,7 @@ jobs:
strategy:
matrix:
target:
- name: x86_64-unknown-haiku
- name: armv7-sony-vita-newlibeabihf
exclude_features: "process,signal,rt-process-signal,full"
steps:
@@ -512,16 +573,22 @@ jobs:
cross-test-with-parking_lot:
needs: basics
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- target: i686-unknown-linux-gnu
os: ubuntu-latest
rustflags: --cfg tokio_taskdump
- target: armv5te-unknown-linux-gnueabi
os: ubuntu-latest
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-24.04-arm
- target: aarch64-unknown-linux-gnu
os: ubuntu-24.04-arm
rustflags: --cfg tokio_taskdump
- target: aarch64-pc-windows-msvc
os: windows-11-arm
steps:
- uses: actions/checkout@v4
- name: Install Rust stable
@@ -552,16 +619,22 @@ jobs:
cross-test-without-parking_lot:
needs: basics
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- target: i686-unknown-linux-gnu
os: ubuntu-latest
rustflags: --cfg tokio_taskdump
- target: armv5te-unknown-linux-gnueabi
os: ubuntu-latest
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-24.04-arm
- target: aarch64-unknown-linux-gnu
os: ubuntu-24.04-arm
rustflags: --cfg tokio_taskdump
- target: aarch64-pc-windows-msvc
os: windows-11-arm
steps:
- uses: actions/checkout@v4
- name: Install Rust stable
@@ -663,6 +736,8 @@ jobs:
- { name: "--unstable", rustflags: "--cfg tokio_unstable -Dwarnings" }
# Try with unstable and taskdump feature flags
- { name: "--unstable --taskdump", rustflags: "--cfg tokio_unstable -Dwarnings --cfg tokio_taskdump" }
- { name: "--tokio_uring", rustflags: "-Dwarnings --cfg tokio_uring" }
- { name: "--unstable --taskdump --tokio_uring", rustflags: "--cfg tokio_unstable -Dwarnings --cfg tokio_taskdump --cfg tokio_uring" }
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
@@ -690,7 +765,14 @@ jobs:
toolchain: ${{ env.rust_min }}
- uses: Swatinem/rust-cache@v2
- name: "check --workspace --all-features"
run: cargo check --workspace --all-features
run: |
if [[ "${{ github.event.pull_request.base.ref }}" =~ ^tokio-1\..* ]]; then
# Only check `tokio` crate as the PR is backporting to an earlier tokio release.
cargo check -p tokio --all-features
else
# Check all crates in the workspace
cargo check --workspace --all-features
fi
env:
RUSTFLAGS: "" # remove -Dwarnings
@@ -718,7 +800,7 @@ jobs:
cargo hack check --all-features --ignore-private
- name: "check --all-features --unstable -Z minimal-versions"
env:
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_taskdump --cfg tokio_uring -Dwarnings
run: |
# Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
# from determining minimal versions based on dev-dependencies.
@@ -764,7 +846,15 @@ jobs:
docs:
name: docs
runs-on: ubuntu-latest
runs-on: ${{ matrix.run.os }}
strategy:
matrix:
run:
- os: windows-latest
- os: ubuntu-latest
RUSTFLAGS: --cfg tokio_taskdump --cfg tokio_uring
RUSTDOCFLAGS: --cfg tokio_taskdump --cfg tokio_uring
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_nightly }}
@@ -776,8 +866,8 @@ jobs:
run: |
cargo doc --lib --no-deps --all-features --document-private-items
env:
RUSTFLAGS: --cfg docsrs --cfg tokio_unstable --cfg tokio_taskdump
RUSTDOCFLAGS: --cfg docsrs --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings
RUSTFLAGS: --cfg docsrs --cfg tokio_unstable ${{ matrix.run.RUSTFLAGS }}
RUSTDOCFLAGS: --cfg docsrs --cfg tokio_unstable -Dwarnings ${{ matrix.run.RUSTDOCFLAGS }}
loom-compile:
name: build loom tests
@@ -978,10 +1068,10 @@ jobs:
targets: ${{ matrix.target }}
# Install dependencies
- name: Install cargo-hack, wasmtime, and cargo-wasi
- name: Install cargo-hack, wasmtime
uses: taiki-e/install-action@v2
with:
tool: cargo-hack,wasmtime,cargo-wasi
tool: cargo-hack,wasmtime
- uses: Swatinem/rust-cache@v2
- name: WASI test tokio full
@@ -1007,9 +1097,12 @@ jobs:
- 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
run: cargo test --target ${{ matrix.target }} --test rt_yield --features wasi-rt
if: matrix.target == 'wasm32-wasip1'
working-directory: tests-integration
env:
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
RUSTFLAGS: -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
- name: test tests-integration --features wasi-threads-rt
run: cargo test --target ${{ matrix.target }} --features wasi-threads-rt
@@ -1031,7 +1124,7 @@ 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-10-21
- nightly-2024-06-30
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ matrix.rust }}
@@ -1042,28 +1135,11 @@ jobs:
- name: Install cargo-check-external-types
uses: taiki-e/cache-cargo-install-action@v1
with:
tool: [email protected]0
tool: [email protected]3
- name: check-external-types
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
@@ -1101,17 +1177,57 @@ jobs:
- 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).
#
FILE="spellcheck.dic"
# Verify the first line is an integer.
first_line=$(head -n 1 "$FILE")
if ! [[ "$first_line" =~ ^[0-9]+$ ]]; then
echo "Error: The first line of $FILE must be an integer, but got: '$first_line'"
exit 1
fi
expected_count="$first_line"
# Check that the number of lines matches the integer.
# xargs (with no arguments) will strip leading/trailing whitespacefrom wc's output.
actual_count=$(sed '1d' "$FILE" | wc -l | xargs)
if [ "$expected_count" -ne "$actual_count" ]; then
echo "Error: The number of lines ($actual_count) does not match $expected_count."
exit 1
fi
# `sed` removes the first line (number of words).
#
# `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
(
sed '1d' $FILE | LC_ALL=en_US.UTF8 sort -uc
) || {
echo "Dictionary is not in sorted order. Correct order is:"
LC_ALL=en_US.UTF8 sort -u <(sed '1d' $FILE)
false
}
- name: Run cargo-spellcheck
run: cargo spellcheck --code 1
run: |
if ! cargo spellcheck --code 1
then
echo ''
echo ''
echo 'If this is a Rust method/type/variable name, then you should'
echo 'enclose it in backticks like this: `MyRustType`.'
echo ''
echo 'If this is a real word, then you can add it to spellcheck.dic'
exit 1
fi
- name: Detect trailing whitespace
run: |
if grep --exclude-dir=.git --exclude-dir=target -rne '\s$' .
then
echo ''
echo 'Please remove trailing whitespace from these lines.'
exit 1
fi
+1 -26
View File
@@ -91,32 +91,7 @@ jobs:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
- name: loom ${{ matrix.scope }}
run: cargo test --lib --release --features full -- $SCOPE
working-directory: tokio
env:
SCOPE: ${{ matrix.scope }}
loom-multi-thread-alt:
name: loom ALT multi-thread scheduler
# base_ref is null when it's not a pull request
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-multi-thread-alt') || (github.base_ref == null))
runs-on: ubuntu-latest
strategy:
matrix:
include:
- scope: loom_multi_thread_alt::group_a
- scope: loom_multi_thread_alt::group_b
- scope: loom_multi_thread_alt::group_c
- scope: loom_multi_thread_alt::group_d
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
- name: loom ${{ matrix.scope }}
run: cargo test --lib --release --features full -- $SCOPE
run: cargo test --lib --release --features full -- --nocapture $SCOPE
working-directory: tokio
env:
SCOPE: ${{ matrix.scope }}
+3 -12
View File
@@ -16,17 +16,8 @@ permissions:
contents: read
jobs:
security-audit:
cargo-deny:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'ci skip')"
steps:
- uses: actions/checkout@v4
- name: Install cargo-audit
run: cargo install cargo-audit
- name: Generate lockfile
run: cargo generate-lockfile
- name: Audit dependencies
run: cargo audit
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v2
+1 -1
View File
@@ -13,7 +13,7 @@ env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
# Change to specific Rust release to pin
rust_stable: stable
rust_stable: 1.82
permissions:
contents: read
+7 -7
View File
@@ -149,7 +149,7 @@ When updating this, also update:
-->
```
cargo +1.77 clippy --all --tests --all-features
cargo +1.88 clippy --all --tests --all-features
```
When building documentation, a simple `cargo doc` is not sufficient. To produce
@@ -196,12 +196,12 @@ LOOM_MAX_PREEMPTIONS=1 LOOM_MAX_BRANCHES=10000 RUSTFLAGS="--cfg loom -C debug_as
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.
run loom tests that test unstable features.
You can run miri tests with
```
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-tag-raw-pointers" \
cargo +nightly miri test --features full --lib
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields" \
cargo +nightly miri test --features full --lib --tests
```
### Performing spellcheck on tokio codebase
@@ -216,8 +216,8 @@ cargo install --locked cargo-spellcheck
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.
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
@@ -269,7 +269,7 @@ To list the available fuzzing harnesses you can run;
$ cd tokio
$ cargo fuzz list
fuzz_linked_list
````
```
Running a fuzz test is as simple as;
+15
View File
@@ -17,3 +17,18 @@ members = [
[workspace.metadata.spellcheck]
config = "spellcheck.toml"
[workspace.lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(fuzzing)',
'cfg(loom)',
'cfg(mio_unsupported_force_poll_poll)',
'cfg(tokio_allow_from_blocking_fd)',
'cfg(tokio_internal_mt_counters)',
'cfg(tokio_no_parking_lot)',
'cfg(tokio_no_tuning_tests)',
'cfg(tokio_taskdump)',
'cfg(tokio_unstable)',
'cfg(tokio_uring)',
'cfg(target_os, values("cygwin"))',
] }
+9 -8
View File
@@ -39,7 +39,7 @@ level, it provides a few major components:
* A multithreaded, work-stealing based task [scheduler].
* A reactor backed by the operating system's event queue (epoll, kqueue,
IOCP, etc...).
IOCP, etc.).
* Asynchronous [TCP and UDP][net] sockets.
These components provide the runtime components necessary for building
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.40.0", features = ["full"] }
tokio = { version = "1.47.0", features = ["full"] }
```
Then, on your main.rs:
@@ -78,7 +78,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop {
let n = match socket.read(&mut buf).await {
// socket closed
Ok(n) if n == 0 => return,
Ok(0) => return,
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
@@ -205,8 +205,8 @@ works with the MSRV of that minor release.
## Release schedule
Tokio doesn't follow a fixed release schedule, but we typically make one to two
new minor releases each month. We make patch releases for bugfixes as necessary.
Tokio doesn't follow a fixed release schedule, but we typically make one minor
release each month. We make patch releases for bugfixes as necessary.
## Bug patching policy
@@ -216,9 +216,8 @@ 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.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)
* `1.43.x` - LTS release until March 2026. (MSRV 1.70)
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
@@ -228,7 +227,7 @@ 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.32.x` patch release, you
can use the following dependency specification:
```text
tokio = { version = "~1.32", features = [...] }
tokio = { version = "~1.38", features = [...] }
```
### Previous LTS releases
@@ -238,6 +237,8 @@ tokio = { version = "~1.32", features = [...] }
* `1.18.x` - LTS release until June 2023.
* `1.20.x` - LTS release until September 2023.
* `1.25.x` - LTS release until March 2024.
* `1.32.x` - LTS release until September 2024.
* `1.36.x` - LTS release until March 2025.
## License
+7 -3
View File
@@ -3,6 +3,7 @@ name = "benches"
version = "0.0.0"
publish = false
edition = "2021"
license = "MIT"
[features]
test-util = ["tokio/test-util"]
@@ -10,12 +11,12 @@ test-util = ["tokio/test-util"]
[dependencies]
tokio = { version = "1.5.0", path = "../tokio", features = ["full"] }
criterion = "0.5.1"
rand = "0.8"
rand_chacha = "0.3"
rand = "0.9"
rand_chacha = "0.9"
[dev-dependencies]
tokio-util = { version = "0.7.0", path = "../tokio-util", features = ["full"] }
tokio-stream = { path = "../tokio-stream" }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
[target.'cfg(unix)'.dependencies]
libc = "0.2.42"
@@ -94,3 +95,6 @@ harness = false
name = "time_timeout"
path = "time_timeout.rs"
harness = false
[lints]
workspace = true
+2 -2
View File
@@ -77,7 +77,7 @@ impl SlowHddWriter {
) -> std::task::Poll<Result<usize, std::io::Error>> {
let service_res = self.as_mut().service_write(cx);
if service_res.is_pending() && self.blocking_rng.gen_bool(PROBABILITY_FLUSH_WAIT) {
if service_res.is_pending() && self.blocking_rng.random_bool(PROBABILITY_FLUSH_WAIT) {
return Poll::Pending;
}
let available = self.buffer_size - self.buffer_used;
@@ -145,7 +145,7 @@ impl ChunkReader {
fn new(chunk_size: usize, service_interval: Duration) -> Self {
let mut service_intervals = interval(service_interval);
service_intervals.set_missed_tick_behavior(MissedTickBehavior::Burst);
let data: Vec<u8> = std::iter::repeat(0).take(chunk_size).collect();
let data: Vec<u8> = std::iter::repeat_n(0, chunk_size).collect();
Self {
data,
service_intervals,
+19 -14
View File
@@ -10,10 +10,21 @@ async fn work() -> usize {
black_box(val)
}
fn basic_scheduler_spawn(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_current_thread()
fn single_rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
.unwrap()
}
fn multi_rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.build()
.unwrap()
}
fn basic_scheduler_spawn(c: &mut Criterion) {
let runtime = single_rt();
c.bench_function("basic_scheduler_spawn", |b| {
b.iter(|| {
@@ -26,9 +37,7 @@ fn basic_scheduler_spawn(c: &mut Criterion) {
}
fn basic_scheduler_spawn10(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
let runtime = single_rt();
c.bench_function("basic_scheduler_spawn10", |b| {
b.iter(|| {
@@ -46,10 +55,8 @@ fn basic_scheduler_spawn10(c: &mut Criterion) {
}
fn threaded_scheduler_spawn(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.build()
.unwrap();
let runtime = multi_rt();
c.bench_function("threaded_scheduler_spawn", |b| {
b.iter(|| {
runtime.block_on(async {
@@ -61,10 +68,8 @@ fn threaded_scheduler_spawn(c: &mut Criterion) {
}
fn threaded_scheduler_spawn10(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.build()
.unwrap();
let runtime = multi_rt();
c.bench_function("threaded_scheduler_spawn10", |b| {
b.iter(|| {
runtime.block_on(async {
+1 -1
View File
@@ -17,7 +17,7 @@ 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>());
let _ = write!(&mut message, " {i}={}", rng.random::<f64>());
}
message
.as_bytes()
+1 -1
View File
@@ -37,7 +37,7 @@ fn create_medium<const SIZE: usize>(g: &mut BenchmarkGroup<WallTime>) {
fn send_data<T: Default, const SIZE: usize>(g: &mut BenchmarkGroup<WallTime>, prefix: &str) {
let rt = rt();
g.bench_function(format!("{}_{}", prefix, SIZE), |b| {
g.bench_function(format!("{prefix}_{SIZE}"), |b| {
b.iter(|| {
let (tx, mut rx) = mpsc::channel::<T>(SIZE);
+1 -1
View File
@@ -17,7 +17,7 @@ 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>());
let _ = write!(&mut message, " {i}={}", rng.random::<f64>());
}
message
.as_bytes()
+21
View File
@@ -0,0 +1,21 @@
# https://embarkstudios.github.io/cargo-deny/cli/init.html
[graph]
all-features = true
[licenses]
allow = [
"MIT",
"Apache-2.0",
]
exceptions = [
{ allow = ["Unicode-3.0", "Unicode-DFS-2016"], crate = "unicode-ident" },
]
[bans]
multiple-versions = "allow"
wildcards = "deny"
[sources]
unknown-registry = "deny"
unknown-git = "deny"
+15 -8
View File
@@ -3,6 +3,7 @@ name = "examples"
version = "0.0.0"
publish = false
edition = "2021"
license = "MIT"
# If you copy one of the examples into a new project, you should be using
# [dependencies] instead, and delete the **path**.
@@ -22,27 +23,30 @@ serde_json = "1.0"
httparse = "1.0"
httpdate = "1.0"
once_cell = "1.5.2"
rand = "0.8.3"
[target.'cfg(windows)'.dev-dependencies.windows-sys]
version = "0.52"
version = "0.59"
[[example]]
name = "chat"
path = "chat.rs"
[[example]]
name = "connect"
path = "connect.rs"
name = "connect-tcp"
path = "connect-tcp.rs"
[[example]]
name = "connect-udp"
path = "connect-udp.rs"
[[example]]
name = "echo-tcp"
path = "echo-tcp.rs"
[[example]]
name = "echo-udp"
path = "echo-udp.rs"
[[example]]
name = "echo"
path = "echo.rs"
[[example]]
name = "hello_world"
path = "hello_world.rs"
@@ -94,3 +98,6 @@ path = "named-pipe-multi-client.rs"
[[example]]
name = "dump"
path = "dump.rs"
[lints]
workspace = true
+1 -1
View File
@@ -10,7 +10,7 @@ cargo run --example $name
```
A good starting point for the examples would be [`hello_world`](hello_world.rs)
and [`echo`](echo.rs). Additionally [the tokio website][tokioweb] contains
and [`echo-tcp`](echo-tcp.rs). Additionally [the tokio website][tokioweb] contains
additional guides for some of the examples.
For a larger "real world" example, see the [`mini-redis`][redis] repository.
+3 -3
View File
@@ -193,7 +193,7 @@ async fn process(
// A client has connected, let's let everyone know.
{
let mut state = state.lock().await;
let msg = format!("{} has joined the chat", username);
let msg = format!("{username} has joined the chat");
tracing::info!("{}", msg);
state.broadcast(addr, &msg).await;
}
@@ -210,7 +210,7 @@ async fn process(
// broadcast this message to the other users.
Some(Ok(msg)) => {
let mut state = state.lock().await;
let msg = format!("{}: {}", username, msg);
let msg = format!("{username}: {msg}");
state.broadcast(addr, &msg).await;
}
@@ -234,7 +234,7 @@ async fn process(
let mut state = state.lock().await;
state.peers.remove(&addr);
let msg = format!("{} has left the chat", username);
let msg = format!("{username} has left the chat");
tracing::info!("{}", msg);
state.broadcast(addr, &msg).await;
}
+71
View File
@@ -0,0 +1,71 @@
//! An example of hooking up stdin/stdout to a TCP stream.
//!
//! This example will connect to a socket address specified in the argument list
//! and then forward all data read on stdin to the server, printing out all data
//! received on stdout. Each line entered on stdin will be translated to a TCP
//! packet which is then sent to the remote address.
//!
//! Note that this is not currently optimized for performance, especially
//! around buffer management. Rather it's intended to show an example of
//! working with a client.
//!
//! This example can be quite useful when interacting with the other examples in
//! this repository! Many of them recommend running this as a simple "hook up
//! stdin/stdout to a server" to get up and running.
#![warn(rust_2018_idioms)]
use tokio::io::{stdin, stdout};
use tokio::net::TcpStream;
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
use bytes::Bytes;
use futures::{future, Sink, SinkExt, Stream, StreamExt};
use std::env;
use std::error::Error;
use std::net::SocketAddr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Parse what address we're going to connect to
let args = env::args().skip(1).collect::<Vec<_>>();
let addr = args
.first()
.ok_or("this program requires at least one argument")?;
let addr = addr.parse::<SocketAddr>()?;
let stdin = FramedRead::new(stdin(), BytesCodec::new());
let stdin = stdin.map(|i| i.map(|bytes| bytes.freeze()));
let stdout = FramedWrite::new(stdout(), BytesCodec::new());
connect(&addr, stdin, stdout).await?;
Ok(())
}
pub async fn connect(
addr: &SocketAddr,
mut stdin: impl Stream<Item = Result<Bytes, std::io::Error>> + Unpin,
mut stdout: impl Sink<Bytes, Error = std::io::Error> + Unpin,
) -> Result<(), Box<dyn Error>> {
let mut stream = TcpStream::connect(addr).await?;
let (r, w) = stream.split();
let mut sink = FramedWrite::new(w, BytesCodec::new());
// filter map Result<BytesMut, Error> stream into just a Bytes stream to match stdout Sink
// on the event of an Error, log the error and end the stream
let mut stream = FramedRead::new(r, BytesCodec::new())
.filter_map(|i| match i {
//BytesMut into Bytes
Ok(i) => future::ready(Some(i.freeze())),
Err(e) => {
println!("failed to read from socket; error={e}");
future::ready(None)
}
})
.map(Ok);
match future::join(sink.send_all(&mut stdin), stdout.send_all(&mut stream)).await {
(Err(e), _) | (_, Err(e)) => Err(e.into()),
_ => Ok(()),
}
}
+91
View File
@@ -0,0 +1,91 @@
//! An example of hooking up stdin/stdout to a UDP stream.
//!
//! This example will connect to a socket address specified in the argument list
//! and then forward all data read on stdin to the server, printing out all data
//! received on stdout. Each line entered on stdin will be translated to a UDP
//! packet which is then sent to the remote address.
//!
//! Note that this is not currently optimized for performance, especially
//! around buffer management. Rather it's intended to show an example of
//! working with a client.
//!
//! This example can be quite useful when interacting with the other examples in
//! this repository! Many of them recommend running this as a simple "hook up
//! stdin/stdout to a server" to get up and running.
#![warn(rust_2018_idioms)]
use tokio::io::{stdin, stdout};
use tokio::net::UdpSocket;
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
use bytes::Bytes;
use futures::{Sink, SinkExt, Stream, StreamExt};
use std::env;
use std::error::Error;
use std::net::SocketAddr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Parse what address we're going to connect to
let args = env::args().skip(1).collect::<Vec<_>>();
let addr = args
.first()
.ok_or("this program requires at least one argument")?;
let addr = addr.parse::<SocketAddr>()?;
let stdin = FramedRead::new(stdin(), BytesCodec::new());
let stdin = stdin.map(|i| i.map(|bytes| bytes.freeze()));
let stdout = FramedWrite::new(stdout(), BytesCodec::new());
connect(&addr, stdin, stdout).await?;
Ok(())
}
pub async fn connect(
addr: &SocketAddr,
stdin: impl Stream<Item = Result<Bytes, std::io::Error>> + Unpin,
stdout: impl Sink<Bytes, Error = std::io::Error> + Unpin,
) -> Result<(), Box<dyn Error>> {
// We'll bind our UDP socket to a local IP/port, but for now we
// basically let the OS pick both of those.
let bind_addr = if addr.ip().is_ipv4() {
"0.0.0.0:0"
} else {
"[::]:0"
};
let socket = UdpSocket::bind(&bind_addr).await?;
socket.connect(addr).await?;
tokio::try_join!(send(stdin, &socket), recv(stdout, &socket))?;
Ok(())
}
async fn send(
mut stdin: impl Stream<Item = Result<Bytes, std::io::Error>> + Unpin,
writer: &UdpSocket,
) -> Result<(), std::io::Error> {
while let Some(item) = stdin.next().await {
let buf = item?;
writer.send(&buf[..]).await?;
}
Ok(())
}
async fn recv(
mut stdout: impl Sink<Bytes, Error = std::io::Error> + Unpin,
reader: &UdpSocket,
) -> Result<(), std::io::Error> {
loop {
let mut buf = vec![0; 1024];
let n = reader.recv(&mut buf[..]).await?;
if n > 0 {
stdout.send(Bytes::from(buf)).await?;
}
}
}
-147
View File
@@ -1,147 +0,0 @@
//! An example of hooking up stdin/stdout to either a TCP or UDP stream.
//!
//! This example will connect to a socket address specified in the argument list
//! and then forward all data read on stdin to the server, printing out all data
//! received on stdout. An optional `--udp` argument can be passed to specify
//! that the connection should be made over UDP instead of TCP, translating each
//! line entered on stdin to a UDP packet to be sent to the remote address.
//!
//! Note that this is not currently optimized for performance, especially
//! around buffer management. Rather it's intended to show an example of
//! working with a client.
//!
//! This example can be quite useful when interacting with the other examples in
//! this repository! Many of them recommend running this as a simple "hook up
//! stdin/stdout to a server" to get up and running.
#![warn(rust_2018_idioms)]
use futures::StreamExt;
use tokio::io;
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
use std::env;
use std::error::Error;
use std::net::SocketAddr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Determine if we're going to run in TCP or UDP mode
let mut args = env::args().skip(1).collect::<Vec<_>>();
let tcp = match args.iter().position(|a| a == "--udp") {
Some(i) => {
args.remove(i);
false
}
None => true,
};
// Parse what address we're going to connect to
let addr = args
.first()
.ok_or("this program requires at least one argument")?;
let addr = addr.parse::<SocketAddr>()?;
let stdin = FramedRead::new(io::stdin(), BytesCodec::new());
let stdin = stdin.map(|i| i.map(|bytes| bytes.freeze()));
let stdout = FramedWrite::new(io::stdout(), BytesCodec::new());
if tcp {
tcp::connect(&addr, stdin, stdout).await?;
} else {
udp::connect(&addr, stdin, stdout).await?;
}
Ok(())
}
mod tcp {
use bytes::Bytes;
use futures::{future, Sink, SinkExt, Stream, StreamExt};
use std::{error::Error, io, net::SocketAddr};
use tokio::net::TcpStream;
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
pub async fn connect(
addr: &SocketAddr,
mut stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
mut stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
) -> Result<(), Box<dyn Error>> {
let mut stream = TcpStream::connect(addr).await?;
let (r, w) = stream.split();
let mut sink = FramedWrite::new(w, BytesCodec::new());
// filter map Result<BytesMut, Error> stream into just a Bytes stream to match stdout Sink
// on the event of an Error, log the error and end the stream
let mut stream = FramedRead::new(r, BytesCodec::new())
.filter_map(|i| match i {
//BytesMut into Bytes
Ok(i) => future::ready(Some(i.freeze())),
Err(e) => {
println!("failed to read from socket; error={}", e);
future::ready(None)
}
})
.map(Ok);
match future::join(sink.send_all(&mut stdin), stdout.send_all(&mut stream)).await {
(Err(e), _) | (_, Err(e)) => Err(e.into()),
_ => Ok(()),
}
}
}
mod udp {
use bytes::Bytes;
use futures::{Sink, SinkExt, Stream, StreamExt};
use std::error::Error;
use std::io;
use std::net::SocketAddr;
use tokio::net::UdpSocket;
pub async fn connect(
addr: &SocketAddr,
stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
) -> Result<(), Box<dyn Error>> {
// We'll bind our UDP socket to a local IP/port, but for now we
// basically let the OS pick both of those.
let bind_addr = if addr.ip().is_ipv4() {
"0.0.0.0:0"
} else {
"[::]:0"
};
let socket = UdpSocket::bind(&bind_addr).await?;
socket.connect(addr).await?;
tokio::try_join!(send(stdin, &socket), recv(stdout, &socket))?;
Ok(())
}
async fn send(
mut stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
writer: &UdpSocket,
) -> Result<(), io::Error> {
while let Some(item) = stdin.next().await {
let buf = item?;
writer.send(&buf[..]).await?;
}
Ok(())
}
async fn recv(
mut stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
reader: &UdpSocket,
) -> Result<(), io::Error> {
loop {
let mut buf = vec![0; 1024];
let n = reader.recv(&mut buf[..]).await?;
if n > 0 {
stdout.send(Bytes::from(buf)).await?;
}
}
}
}
-2
View File
@@ -1,5 +1,3 @@
#![allow(unknown_lints, unexpected_cfgs)]
//! This example demonstrates tokio's experimental task dumping functionality.
//! This application deadlocks. Input CTRL+C to display traces of each task, or
//! input CTRL+C twice within 1 second to quit.
+4 -4
View File
@@ -9,13 +9,13 @@
//!
//! To see this server in action, you can run this in one terminal:
//!
//! cargo run --example echo
//! cargo run --example echo-tcp
//!
//! and in another terminal you can run:
//!
//! cargo run --example connect 127.0.0.1:8080
//! cargo run --example connect-tcp 127.0.0.1:8080
//!
//! Each line you type in to the `connect` terminal should be echo'd back to
//! Each line you type in to the `connect-tcp` terminal should be echo'd back to
//! you! If you open up multiple terminals running the `connect` example you
//! should be able to see them all make progress simultaneously.
@@ -40,7 +40,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
// connections. This TCP listener is bound to the address we determined
// above and must be associated with an event loop.
let listener = TcpListener::bind(&addr).await?;
println!("Listening on: {}", addr);
println!("Listening on: {addr}");
loop {
// Asynchronously wait for an inbound socket.
+3 -3
View File
@@ -6,9 +6,9 @@
//!
//! and in another terminal you can run:
//!
//! cargo run --example connect -- --udp 127.0.0.1:8080
//! cargo run --example connect-udp 127.0.0.1:8080
//!
//! Each line you type in to the `nc` terminal should be echo'd back to you!
//! Each line you type in to the `connect-udp` terminal should be echo'd back to you!
#![warn(rust_2018_idioms)]
@@ -38,7 +38,7 @@ impl Server {
if let Some((size, peer)) = to_send {
let amt = socket.send_to(&buf[..size], &peer).await?;
println!("Echoed {}/{} bytes to {}", amt, size, peer);
println!("Echoed {amt}/{size} bytes to {peer}");
}
// If we're here then `to_send` is `None`, so we take a look for the
+5 -5
View File
@@ -13,9 +13,9 @@
//!
//! and in another terminal you can run:
//!
//! cargo run --example connect 127.0.0.1:8080
//! cargo run --example connect-tcp 127.0.0.1:8080
//!
//! Each line you type in to the `connect` terminal should be written to terminal!
//! Each line you type in to the `connect-tcp` terminal should be written to terminal!
//!
//! Minimal js example:
//!
@@ -75,7 +75,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// to our event loop. After the socket's created we inform that we're ready
// to go and start accepting connections.
let listener = TcpListener::bind(&addr).await?;
println!("Listening on: {}", addr);
println!("Listening on: {addr}");
loop {
// Asynchronously wait for an inbound socket.
@@ -96,8 +96,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// The stream will return None once the client disconnects.
while let Some(message) = framed.next().await {
match message {
Ok(bytes) => println!("bytes: {:?}", bytes),
Err(err) => println!("Socket closed with error: {:?}", err),
Ok(bytes) => println!("bytes: {bytes:?}"),
Err(err) => println!("Socket closed with error: {err:?}"),
}
}
println!("Socket received FIN packet and closed connection");
+5 -5
View File
@@ -11,11 +11,11 @@
//!
//! This in another terminal
//!
//! cargo run --example echo
//! cargo run --example echo-tcp
//!
//! And finally this in another terminal
//!
//! cargo run --example connect 127.0.0.1:8081
//! cargo run --example connect-tcp 127.0.0.1:8081
//!
//! This final terminal will connect to our proxy, which will in turn connect to
//! the echo server, and you'll be able to see data flowing between them.
@@ -38,8 +38,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
.nth(2)
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
println!("Listening on: {}", listen_addr);
println!("Proxying to: {}", server_addr);
println!("Listening on: {listen_addr}");
println!("Proxying to: {server_addr}");
let listener = TcpListener::bind(listen_addr).await?;
@@ -50,7 +50,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
copy_bidirectional(&mut inbound, &mut outbound)
.map(|r| {
if let Err(e) = r {
println!("Failed to transfer; error={}", e);
println!("Failed to transfer; error={e}");
}
})
.await
+11 -11
View File
@@ -12,9 +12,9 @@
//!
//! and next in another windows run:
//!
//! cargo run --example connect 127.0.0.1:8080
//! cargo run --example connect-tcp 127.0.0.1:8080
//!
//! In the `connect` window you can type in commands where when you hit enter
//! In the `connect-tcp` window you can type in commands where when you hit enter
//! you'll get a response from the server for that command. An example session
//! is:
//!
@@ -90,7 +90,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
let listener = TcpListener::bind(&addr).await?;
println!("Listening on: {}", addr);
println!("Listening on: {addr}");
// Create the shared state of this server that will be shared amongst all
// clients. We populate the initial database and then create the `Database`
@@ -131,11 +131,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
let response = response.serialize();
if let Err(e) = lines.send(response.as_str()).await {
println!("error on sending response; error = {:?}", e);
println!("error on sending response; error = {e:?}");
}
}
Err(e) => {
println!("error on decoding from socket; error = {:?}", e);
println!("error on decoding from socket; error = {e:?}");
}
}
}
@@ -143,7 +143,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
// The connection will be closed at this point as `lines.next()` has returned `None`.
});
}
Err(e) => println!("error accepting socket; error = {:?}", e),
Err(e) => println!("error accepting socket; error = {e:?}"),
}
}
}
@@ -162,7 +162,7 @@ fn handle_request(line: &str, db: &Arc<Database>) -> Response {
value: value.clone(),
},
None => Response::Error {
msg: format!("no key {}", key),
msg: format!("no key {key}"),
},
},
Request::Set { key, value } => {
@@ -203,7 +203,7 @@ impl Request {
value: value.to_string(),
})
}
Some(cmd) => Err(format!("unknown command: {}", cmd)),
Some(cmd) => Err(format!("unknown command: {cmd}")),
None => Err("empty input".into()),
}
}
@@ -212,13 +212,13 @@ impl Request {
impl Response {
fn serialize(&self) -> String {
match *self {
Response::Value { ref key, ref value } => format!("{} = {}", key, value),
Response::Value { ref key, ref value } => format!("{key} = {value}"),
Response::Set {
ref key,
ref value,
ref previous,
} => format!("set {} = `{}`, previous: {:?}", key, value, previous),
Response::Error { ref msg } => format!("error: {}", msg),
} => format!("set {key} = `{value}`, previous: {previous:?}"),
Response::Error { ref msg } => format!("error: {msg}"),
}
}
}
+9 -17
View File
@@ -31,13 +31,13 @@ async fn main() -> Result<(), Box<dyn Error>> {
.nth(1)
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
let server = TcpListener::bind(&addr).await?;
println!("Listening on: {}", addr);
println!("Listening on: {addr}");
loop {
let (stream, _) = server.accept().await?;
tokio::spawn(async move {
if let Err(e) = process(stream).await {
println!("failed to process connection; error = {}", e);
println!("failed to process connection; error = {e}");
}
});
}
@@ -82,9 +82,7 @@ async fn respond(req: Request<()>) -> Result<Response<String>, Box<dyn Error>> {
String::new()
}
};
let response = response
.body(body)
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
let response = response.body(body).map_err(io::Error::other)?;
Ok(response)
}
@@ -159,8 +157,8 @@ impl Decoder for Http {
let mut parsed_headers = [httparse::EMPTY_HEADER; 16];
let mut r = httparse::Request::new(&mut parsed_headers);
let status = r.parse(src).map_err(|e| {
let msg = format!("failed to parse http request: {:?}", e);
io::Error::new(io::ErrorKind::Other, msg)
let msg = format!("failed to parse http request: {e:?}");
io::Error::other(msg)
})?;
let amt = match status {
@@ -180,8 +178,7 @@ impl Decoder for Http {
headers[i] = Some((k, v));
}
let method = http::Method::try_from(r.method.unwrap())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
let method = http::Method::try_from(r.method.unwrap()).map_err(io::Error::other)?;
(
method,
@@ -191,10 +188,7 @@ impl Decoder for Http {
)
};
if version != 1 {
return Err(io::Error::new(
io::ErrorKind::Other,
"only HTTP/1.1 accepted",
));
return Err(io::Error::other("only HTTP/1.1 accepted"));
}
let data = src.split_to(amt).freeze();
let mut ret = Request::builder();
@@ -209,13 +203,11 @@ impl Decoder for Http {
None => break,
};
let value = HeaderValue::from_bytes(data.slice(v.0..v.1).as_ref())
.map_err(|_| io::Error::new(io::ErrorKind::Other, "header decode error"))?;
.map_err(|_| io::Error::other("header decode error"))?;
ret = ret.header(&data[k.0..k.1], value);
}
let req = ret
.body(())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
let req = ret.body(()).map_err(io::Error::other)?;
Ok(Some(req))
}
}
+1 -1
View File
@@ -46,7 +46,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Run both futures simultaneously of `a` and `b` sending messages back and forth.
match tokio::try_join!(a, b) {
Err(e) => println!("an error occurred; error = {:?}", e),
Err(e) => println!("an error occurred; error = {e:?}"),
_ => println!("done!"),
}
+25 -3
View File
@@ -1,4 +1,4 @@
285
306
&
+
<
@@ -12,12 +12,17 @@
0xA
0xD
100ms
100ns
10ms
10μs
~12
120s
12.5%
±1m
±1ms
1ms
1s
25%
250ms
2x
~4
@@ -25,11 +30,13 @@
450ms
50ms
8MB
ABI
accessors
adaptor
adaptors
Adaptors
AIO
ambiant
ambient
amongst
api
APIs
@@ -63,6 +70,10 @@ connectionless
coroutines
cpu
cpus
cqe
CQE
cqe's
customizable
Customizable
datagram
Datagram
@@ -70,8 +81,12 @@ datagrams
deallocate
deallocated
Deallocates
debuginfo
decrement
decrementing
demangled
dequeued
dereferenced
deregister
deregistered
deregistering
@@ -99,6 +114,7 @@ errored
EWMA
expirations
fcntl
fd
fd's
FIFOs
filename
@@ -115,9 +131,11 @@ GID
goroutines
Growable
gzip
H2
hashmaps
HashMaps
hashsets
HdrHistogram
ie
Illumos
impl
@@ -125,6 +143,7 @@ implementers
implementor
implementors
incrementing
inlining
interoperate
invariants
Invariants
@@ -151,8 +170,10 @@ metadata
mio
Mio
mio's
miri
misconfigured
mock's
monomorphization
mpmc
mpsc
multi
@@ -265,9 +286,11 @@ unparks
Unparks
unreceived
unsafety
unsets
Unsets
unsynchronized
untrusted
uring
usecases
Valgrind
Varghese
@@ -282,4 +305,3 @@ Wakers
wakeup
wakeups
workstealing
+6 -2
View File
@@ -3,12 +3,16 @@ name = "stress-test"
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
edition = "2021"
license = "MIT"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio = { path = "../tokio/", features = ["full"] }
tokio = { version = "1.0.0", path = "../tokio/", features = ["full"] }
[dev-dependencies]
rand = "0.8"
rand = "0.9"
[lints]
workspace = true
+5 -1
View File
@@ -3,6 +3,7 @@ name = "tests-build"
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
edition = "2021"
license = "MIT"
publish = false
[features]
@@ -10,7 +11,10 @@ full = ["tokio/full"]
rt = ["tokio/rt", "tokio/macros"]
[dependencies]
tokio = { path = "../tokio", optional = true }
tokio = { version = "1.0.0", path = "../tokio", optional = true }
[dev-dependencies]
trybuild = "1.0"
[lints]
workspace = true
+1 -1
View File
@@ -6,5 +6,5 @@ To run all of the tests in this directory, run the following commands:
cargo test --features full
cargo test --features rt
```
If one of the tests fail, you can pass `TRYBUILD=overwrite` to the `cargo test`
If any of the tests fail, you can pass `TRYBUILD=overwrite` to the `cargo test`
command that failed to have it regenerate the test output.
@@ -23,6 +23,40 @@ async fn extra_semicolon() -> Result<(), ()> {
Ok(());
}
/// This test is a characterization test for the `?` operator.
///
/// See <https://github.com/tokio-rs/tokio/issues/6930#issuecomment-2572502517> for more details.
///
/// It should fail with a single error message about the return type of the function, but instead
/// if fails with an extra error message due to the `?` operator being used within the async block
/// rather than the original function.
///
/// ```text
/// 28 | None?;
/// | ^ cannot use the `?` operator in an async block that returns `()`
/// ```
#[tokio::main]
async fn question_mark_operator_with_invalid_option() -> Option<()> {
None?;
}
/// This test is a characterization test for the `?` operator.
///
/// See <https://github.com/tokio-rs/tokio/issues/6930#issuecomment-2572502517> for more details.
///
/// It should fail with a single error message about the return type of the function, but instead
/// if fails with an extra error message due to the `?` operator being used within the async block
/// rather than the original function.
///
/// ```text
/// 33 | Ok(())?;
/// | ^ cannot use the `?` operator in an async block that returns `()`
/// ```
#[tokio::main]
async fn question_mark_operator_with_invalid_result() -> Result<(), ()> {
Ok(())?;
}
// https://github.com/tokio-rs/tokio/issues/4635
#[allow(redundant_semicolons)]
#[rustfmt::skip]
@@ -49,11 +49,64 @@ help: try adding an expression at the end of the block
24 + Ok(())
|
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:32:5
error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> tests/fail/macros_type_mismatch.rs:40:9
|
30 | async fn issue_4635() {
38 | #[tokio::main]
| -------------- this function should return `Result` or `Option` to accept `?`
39 | async fn question_mark_operator_with_invalid_option() -> Option<()> {
40 | None?;
| ^ cannot use the `?` operator in an async block that returns `()`
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:40:5
|
39 | async fn question_mark_operator_with_invalid_option() -> Option<()> {
| ---------- expected `Option<()>` because of return type
40 | None?;
| ^^^^^^ expected `Option<()>`, found `()`
|
= note: expected enum `Option<()>`
found unit type `()`
help: try adding an expression at the end of the block
|
40 ~ None?;;
41 + None
|
40 ~ None?;;
41 + Some(())
|
error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> tests/fail/macros_type_mismatch.rs:57:11
|
55 | #[tokio::main]
| -------------- this function should return `Result` or `Option` to accept `?`
56 | async fn question_mark_operator_with_invalid_result() -> Result<(), ()> {
57 | Ok(())?;
| ^ cannot use the `?` operator in an async block that returns `()`
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:57:5
|
56 | async fn question_mark_operator_with_invalid_result() -> Result<(), ()> {
| -------------- expected `Result<(), ()>` because of return type
57 | Ok(())?;
| ^^^^^^^^ expected `Result<(), ()>`, found `()`
|
= note: expected enum `Result<(), ()>`
found unit type `()`
help: try adding an expression at the end of the block
|
57 ~ Ok(())?;;
58 + Ok(())
|
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:66:5
|
64 | async fn issue_4635() {
| - help: try adding a return type: `-> i32`
31 | return 1;
32 | ;
65 | return 1;
66 | ;
| ^ expected `()`, found integer
+1
View File
@@ -1,4 +1,5 @@
#[test]
#[cfg_attr(miri, ignore)]
fn compile_fail_full() {
let t = trybuild::TestCases::new();
+6 -2
View File
@@ -3,6 +3,7 @@ name = "tests-integration"
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
edition = "2021"
license = "MIT"
publish = false
[[bin]]
@@ -55,8 +56,11 @@ rt = ["tokio/rt"]
rt-multi-thread = ["rt", "tokio/rt-multi-thread"]
[dependencies]
tokio = { path = "../tokio" }
tokio-test = { path = "../tokio-test", optional = true }
tokio = { version = "1.0.0", path = "../tokio" }
tokio-test = { version = "0.4", path = "../tokio-test", optional = true }
doc-comment = "0.3.1"
futures = { version = "0.3.0", features = ["async-await"] }
bytes = "1.0.0"
[lints]
workspace = true
+1 -1
View File
@@ -1,4 +1,4 @@
use futures::future::poll_fn;
use std::future::poll_fn;
fn main() {
let rt = tokio::runtime::Builder::new_multi_thread()
+4 -4
View File
@@ -1,5 +1,5 @@
#![warn(rust_2018_idioms)]
#![cfg(all(feature = "full", not(target_os = "wasi")))]
#![cfg(all(feature = "full", not(target_os = "wasi"), not(miri)))]
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
use tokio::join;
@@ -25,7 +25,7 @@ async fn feed_cat(mut cat: Child, n: usize) -> io::Result<ExitStatus> {
// Produce n lines on the child's stdout.
let write = async {
for i in 0..n {
let bytes = format!("line {}\n", i).into_bytes();
let bytes = format!("line {i}\n").into_bytes();
stdin.write_all(&bytes).await.unwrap();
}
@@ -52,7 +52,7 @@ async fn feed_cat(mut cat: Child, n: usize) -> io::Result<ExitStatus> {
(false, 0) => panic!("broken pipe"),
(true, n) if n != 0 => panic!("extraneous data"),
_ => {
let expected = format!("line {}", num_lines);
let expected = format!("line {num_lines}");
assert_eq!(expected, data);
}
};
@@ -206,7 +206,7 @@ async fn vectored_writes() {
let mut input = Bytes::from_static(b"hello\n").chain(Bytes::from_static(b"world!\n"));
let mut writes_completed = 0;
futures::future::poll_fn(|cx| loop {
std::future::poll_fn(|cx| loop {
let mut slices = [IoSlice::new(&[]); 2];
let vectored = input.chunks_vectored(&mut slices);
if vectored == 0 {
+6
View File
@@ -1,3 +1,9 @@
# 2.5.0 (Jan 8th, 2025)
- macros: suppress `clippy::needless_return` in `#[tokio::main]` ([#6874])
[#6874]: https://github.com/tokio-rs/tokio/pull/6874
# 2.4.0 (July 22nd, 2024)
- msrv: increase MSRV to 1.70 ([#6645])
+4 -1
View File
@@ -4,7 +4,7 @@ name = "tokio-macros"
# - Remove path dependencies
# - Update CHANGELOG.md.
# - Create "tokio-macros-1.x.y" git tag.
version = "2.4.0"
version = "2.5.0"
edition = "2021"
rust-version = "1.70"
authors = ["Tokio Contributors <[email protected]>"]
@@ -31,3 +31,6 @@ tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
[package.metadata.docs.rs]
all-features = true
[lints]
workspace = true
+14 -16
View File
@@ -20,7 +20,7 @@ impl RuntimeFlavor {
"single_thread" => Err("The single threaded runtime flavor is called `current_thread`.".to_string()),
"basic_scheduler" => Err("The `basic_scheduler` runtime flavor has been renamed to `current_thread`.".to_string()),
"threaded_scheduler" => Err("The `threaded_scheduler` runtime flavor has been renamed to `multi_thread`.".to_string()),
_ => Err(format!("No such runtime flavor `{}`. The runtime flavors are `current_thread` and `multi_thread`.", s)),
_ => Err(format!("No such runtime flavor `{s}`. The runtime flavors are `current_thread` and `multi_thread`.")),
}
}
}
@@ -36,7 +36,7 @@ impl UnhandledPanic {
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)),
_ => Err(format!("No such unhandled panic behavior `{s}`. The unhandled panic behaviors are `ignore` and `shutdown_runtime`.")),
}
}
@@ -239,12 +239,12 @@ fn parse_int(int: syn::Lit, span: Span, field: &str) -> Result<usize, syn::Error
Ok(value) => Ok(value),
Err(e) => Err(syn::Error::new(
span,
format!("Failed to parse value of `{}` as integer: {}", field, e),
format!("Failed to parse value of `{field}` as integer: {e}"),
)),
},
_ => Err(syn::Error::new(
span,
format!("Failed to parse value of `{}` as integer.", field),
format!("Failed to parse value of `{field}` as integer."),
)),
}
}
@@ -255,7 +255,7 @@ fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result<String, syn::E
syn::Lit::Verbatim(s) => Ok(s.to_string()),
_ => Err(syn::Error::new(
span,
format!("Failed to parse value of `{}` as string.", field),
format!("Failed to parse value of `{field}` as string."),
)),
}
}
@@ -275,7 +275,7 @@ fn parse_path(lit: syn::Lit, span: Span, field: &str) -> Result<Path, syn::Error
}
_ => Err(syn::Error::new(
span,
format!("Failed to parse value of `{}` as path.", field),
format!("Failed to parse value of `{field}` as path."),
)),
}
}
@@ -285,7 +285,7 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Erro
syn::Lit::Bool(b) => Ok(b.value),
_ => Err(syn::Error::new(
span,
format!("Failed to parse value of `{}` as bool.", field),
format!("Failed to parse value of `{field}` as bool."),
)),
}
}
@@ -342,8 +342,7 @@ fn build_config(
}
name => {
let msg = format!(
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`",
name,
"Unknown attribute {name} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`",
);
return Err(syn::Error::new_spanned(namevalue, msg));
}
@@ -358,21 +357,19 @@ fn build_config(
let msg = match name.as_str() {
"threaded_scheduler" | "multi_thread" => {
format!(
"Set the runtime flavor with #[{}(flavor = \"multi_thread\")].",
macro_name
"Set the runtime flavor with #[{macro_name}(flavor = \"multi_thread\")]."
)
}
"basic_scheduler" | "current_thread" | "single_threaded" => {
format!(
"Set the runtime flavor with #[{}(flavor = \"current_thread\")].",
macro_name
"Set the runtime flavor with #[{macro_name}(flavor = \"current_thread\")]."
)
}
"flavor" | "worker_threads" | "start_paused" | "crate" | "unhandled_panic" => {
format!("The `{}` attribute requires an argument.", name)
format!("The `{name}` attribute requires an argument.")
}
name => {
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.", name)
format!("Unknown attribute {name} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.")
}
};
return Err(syn::Error::new_spanned(path, msg));
@@ -438,8 +435,9 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
};
let body_ident = quote! { body };
// This explicit `return` is intentional. See tokio-rs/tokio#4636
let last_block = quote_spanned! {last_stmt_end_span=>
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
#[allow(clippy::expect_used, clippy::diverging_sub_expression, clippy::needless_return)]
{
return #rt
.enable_all()
-5
View File
@@ -1,4 +1,3 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -211,7 +210,6 @@ use proc_macro::TokenStream;
/// This option is only compatible with the `current_thread` runtime.
///
/// ```no_run
/// # #![allow(unknown_lints, unexpected_cfgs)]
/// #[cfg(tokio_unstable)]
/// #[tokio::main(flavor = "current_thread", unhandled_panic = "shutdown_runtime")]
/// async fn main() {
@@ -226,7 +224,6 @@ use proc_macro::TokenStream;
/// Equivalent code not using `#[tokio::main]`
///
/// ```no_run
/// # #![allow(unknown_lints, unexpected_cfgs)]
/// #[cfg(tokio_unstable)]
/// fn main() {
/// tokio::runtime::Builder::new_current_thread()
@@ -480,7 +477,6 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// This option is only compatible with the `current_thread` runtime.
///
/// ```no_run
/// # #![allow(unknown_lints, unexpected_cfgs)]
/// #[cfg(tokio_unstable)]
/// #[tokio::test(flavor = "current_thread", unhandled_panic = "shutdown_runtime")]
/// async fn my_test() {
@@ -495,7 +491,6 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// Equivalent code not using `#[tokio::test]`
///
/// ```no_run
/// # #![allow(unknown_lints, unexpected_cfgs)]
/// #[cfg(tokio_unstable)]
/// #[test]
/// fn my_test() {
+1 -1
View File
@@ -11,7 +11,7 @@ pub(crate) fn declare_output_enum(input: TokenStream) -> TokenStream {
};
let variants = (0..branches)
.map(|num| Ident::new(&format!("_{}", num), Span::call_site()))
.map(|num| Ident::new(&format!("_{num}"), Span::call_site()))
.collect::<Vec<_>>();
// Use a bitfield to track which futures completed
+23
View File
@@ -1,3 +1,26 @@
# 0.1.17 (December 6th, 2024)
- deps: fix dev-dependency on tokio-test ([#6931], [#7019])
- stream: fix link on `Peekable` ([#6861])
- sync: fix `Stream` link in broadcast docs ([#6873])
[#6861]: https://github.com/tokio-rs/tokio/pull/6861
[#6873]: https://github.com/tokio-rs/tokio/pull/6873
[#6931]: https://github.com/tokio-rs/tokio/pull/6931
[#7019]: https://github.com/tokio-rs/tokio/pull/7019
# 0.1.16 (September 5th, 2024)
This release bumps the MSRV of tokio-stream to 1.70.
- stream: add `next_many` and `poll_next_many` to `StreamMap` ([#6409])
- stream: make stream adapters public ([#6658])
- readme: add readme for tokio-stream ([#6456])
[#6409]: https://github.com/tokio-rs/tokio/pull/6409
[#6658]: https://github.com/tokio-rs/tokio/pull/6658
[#6456]: https://github.com/tokio-rs/tokio/pull/6456
# 0.1.15 (March 14th, 2024)
This release bumps the MSRV of tokio-stream to 1.63.
+5 -2
View File
@@ -4,7 +4,7 @@ name = "tokio-stream"
# - Remove path dependencies
# - Update CHANGELOG.md.
# - Create "tokio-stream-0.1.x" git tag.
version = "0.1.15"
version = "0.1.17"
edition = "2021"
rust-version = "1.70"
authors = ["Tokio Contributors <[email protected]>"]
@@ -45,7 +45,7 @@ tokio-util = { version = "0.7.0", path = "../tokio-util", optional = true }
tokio = { version = "1.2.0", path = "../tokio", features = ["full", "test-util"] }
async-stream = "0.3"
parking_lot = "0.12.0"
tokio-test = { path = "../tokio-test" }
tokio-test = { version = "0.4", path = "../tokio-test" }
futures = { version = "0.3", default-features = false }
[package.metadata.docs.rs]
@@ -56,3 +56,6 @@ rustdoc-args = ["--cfg", "docsrs"]
# This should allow `docsrs` to be read across projects, so that `tokio-stream`
# can pick up stubbed types exported by `tokio`.
rustc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
-4
View File
@@ -1,4 +1,3 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![allow(
clippy::cognitive_complexity,
clippy::large_enum_variant,
@@ -74,9 +73,6 @@
#[macro_use]
mod macros;
mod poll_fn;
pub(crate) use poll_fn::poll_fn;
pub mod wrappers;
mod stream_ext;
+1 -1
View File
@@ -17,7 +17,7 @@ unsafe impl<T> Sync for Pending<T> {}
///
/// The returned stream is never ready. Attempting to call
/// [`next()`](crate::StreamExt::next) will never complete. Use
/// [`stream::empty()`](super::empty()) to obtain a stream that is is
/// [`stream::empty()`](super::empty()) to obtain a stream that is
/// immediately empty but returns no values.
///
/// # Examples
-35
View File
@@ -1,35 +0,0 @@
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)
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ use crate::stream_ext::Fuse;
use crate::StreamExt;
pin_project! {
/// Stream returned by the [`chain`](super::StreamExt::peekable) method.
/// Stream returned by the [`peekable`](super::StreamExt::peekable) method.
pub struct Peekable<T: Stream> {
peek: Option<T::Item>,
#[pin]
+2 -1
View File
@@ -1,6 +1,7 @@
use crate::{poll_fn, Stream};
use crate::Stream;
use std::borrow::Borrow;
use std::future::poll_fn;
use std::hash::Hash;
use std::pin::Pin;
use std::task::{ready, Context, Poll};
+25 -2
View File
@@ -10,8 +10,31 @@ use std::task::{ready, Context, Poll};
/// A wrapper around [`tokio::sync::broadcast::Receiver`] that implements [`Stream`].
///
/// # Example
///
/// ```
/// use tokio::sync::broadcast;
/// use tokio_stream::wrappers::BroadcastStream;
/// use tokio_stream::StreamExt;
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> Result<(), tokio::sync::broadcast::error::SendError<u8>> {
/// let (tx, rx) = broadcast::channel(16);
/// tx.send(10)?;
/// tx.send(20)?;
/// # // prevent the doc test from hanging
/// drop(tx);
///
/// let mut stream = BroadcastStream::new(rx);
/// assert_eq!(stream.next().await, Some(Ok(10)));
/// assert_eq!(stream.next().await, Some(Ok(20)));
/// assert_eq!(stream.next().await, None);
/// # Ok(())
/// # }
/// ```
///
/// [`tokio::sync::broadcast::Receiver`]: struct@tokio::sync::broadcast::Receiver
/// [`Stream`]: trait@crate::Stream
/// [`Stream`]: trait@futures_core::Stream
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
pub struct BroadcastStream<T> {
inner: ReusableBoxFuture<'static, (Result<T, RecvError>, Receiver<T>)>,
@@ -30,7 +53,7 @@ pub enum BroadcastStreamRecvError {
impl fmt::Display for BroadcastStreamRecvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BroadcastStreamRecvError::Lagged(amt) => write!(f, "channel lagged by {}", amt),
BroadcastStreamRecvError::Lagged(amt) => write!(f, "channel lagged by {amt}"),
}
}
}
+20
View File
@@ -5,6 +5,26 @@ use tokio::time::{Instant, Interval};
/// A wrapper around [`Interval`] that implements [`Stream`].
///
/// # Example
///
/// ```
/// use tokio::time::{Duration, Instant, interval};
/// use tokio_stream::wrappers::IntervalStream;
/// use tokio_stream::StreamExt;
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// let start = Instant::now();
/// let interval = interval(Duration::from_millis(10));
/// let mut stream = IntervalStream::new(interval);
/// for _ in 0..3 {
/// if let Some(instant) = stream.next().await {
/// println!("elapsed: {:.1?}", instant.duration_since(start));
/// }
/// }
/// # }
/// ```
///
/// [`Interval`]: struct@tokio::time::Interval
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
+18
View File
@@ -8,6 +8,24 @@ use tokio::io::{AsyncBufRead, Lines};
pin_project! {
/// A wrapper around [`tokio::io::Lines`] that implements [`Stream`].
///
/// # Example
///
/// ```
/// use tokio::io::AsyncBufReadExt;
/// use tokio_stream::wrappers::LinesStream;
/// use tokio_stream::StreamExt;
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> std::io::Result<()> {
/// let input = b"Hello\nWorld\n";
/// let mut stream = LinesStream::new(input.lines());
/// while let Some(line) = stream.next().await {
/// println!("{}", line?);
/// }
/// # Ok(())
/// # }
/// ```
///
/// [`tokio::io::Lines`]: struct@tokio::io::Lines
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
+23
View File
@@ -5,6 +5,29 @@ use tokio::sync::mpsc::Receiver;
/// A wrapper around [`tokio::sync::mpsc::Receiver`] that implements [`Stream`].
///
/// # Example
///
/// ```
/// use tokio::sync::mpsc;
/// use tokio_stream::wrappers::ReceiverStream;
/// use tokio_stream::StreamExt;
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> Result<(), tokio::sync::mpsc::error::SendError<u8>> {
/// let (tx, rx) = mpsc::channel(2);
/// tx.send(10).await?;
/// tx.send(20).await?;
/// # // prevent the doc test from hanging
/// drop(tx);
///
/// let mut stream = ReceiverStream::new(rx);
/// assert_eq!(stream.next().await, Some(10));
/// assert_eq!(stream.next().await, Some(20));
/// assert_eq!(stream.next().await, None);
/// # Ok(())
/// # }
/// ```
///
/// [`tokio::sync::mpsc::Receiver`]: struct@tokio::sync::mpsc::Receiver
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
@@ -5,6 +5,29 @@ use tokio::sync::mpsc::UnboundedReceiver;
/// A wrapper around [`tokio::sync::mpsc::UnboundedReceiver`] that implements [`Stream`].
///
/// # Example
///
/// ```
/// use tokio::sync::mpsc;
/// use tokio_stream::wrappers::UnboundedReceiverStream;
/// use tokio_stream::StreamExt;
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> Result<(), tokio::sync::mpsc::error::SendError<u8>> {
/// let (tx, rx) = mpsc::unbounded_channel();
/// tx.send(10)?;
/// tx.send(20)?;
/// # // prevent the doc test from hanging
/// drop(tx);
///
/// let mut stream = UnboundedReceiverStream::new(rx);
/// assert_eq!(stream.next().await, Some(10));
/// assert_eq!(stream.next().await, Some(20));
/// assert_eq!(stream.next().await, None);
/// # Ok(())
/// # }
/// ```
///
/// [`tokio::sync::mpsc::UnboundedReceiver`]: struct@tokio::sync::mpsc::UnboundedReceiver
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
+18
View File
@@ -6,6 +6,24 @@ use tokio::fs::{DirEntry, ReadDir};
/// A wrapper around [`tokio::fs::ReadDir`] that implements [`Stream`].
///
/// # Example
///
/// ```
/// use tokio::fs::read_dir;
/// use tokio_stream::{StreamExt, wrappers::ReadDirStream};
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> std::io::Result<()> {
/// let dirs = read_dir(".").await?;
/// let mut dirs = ReadDirStream::new(dirs);
/// while let Some(dir) = dirs.next().await {
/// let dir = dir?;
/// println!("{}", dir.path().display());
/// }
/// # Ok(())
/// # }
/// ```
///
/// [`tokio::fs::ReadDir`]: struct@tokio::fs::ReadDir
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
+16
View File
@@ -5,6 +5,22 @@ use tokio::signal::unix::Signal;
/// A wrapper around [`Signal`] that implements [`Stream`].
///
/// # Example
///
/// ```no_run
/// use tokio::signal::unix::{signal, SignalKind};
/// use tokio_stream::{StreamExt, wrappers::SignalStream};
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> std::io::Result<()> {
/// let signals = signal(SignalKind::hangup())?;
/// let mut stream = SignalStream::new(signals);
/// while stream.next().await.is_some() {
/// println!("hangup signal received");
/// }
/// # Ok(())
/// # }
/// ```
/// [`Signal`]: struct@tokio::signal::unix::Signal
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
@@ -7,6 +7,23 @@ use tokio::signal::windows::{CtrlBreak, CtrlC};
///
/// [`CtrlC`]: struct@tokio::signal::windows::CtrlC
/// [`Stream`]: trait@crate::Stream
///
/// # Example
///
/// ```no_run
/// use tokio::signal::windows::ctrl_c;
/// use tokio_stream::{StreamExt, wrappers::CtrlCStream};
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> std::io::Result<()> {
/// let signals = ctrl_c()?;
/// let mut stream = CtrlCStream::new(signals);
/// while stream.next().await.is_some() {
/// println!("ctrl-c received");
/// }
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(all(windows, feature = "signal"))))]
pub struct CtrlCStream {
@@ -47,6 +64,23 @@ impl AsMut<CtrlC> for CtrlCStream {
/// A wrapper around [`CtrlBreak`] that implements [`Stream`].
///
/// # Example
///
/// ```no_run
/// use tokio::signal::windows::ctrl_break;
/// use tokio_stream::{StreamExt, wrappers::CtrlBreakStream};
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> std::io::Result<()> {
/// let signals = ctrl_break()?;
/// let mut stream = CtrlBreakStream::new(signals);
/// while stream.next().await.is_some() {
/// println!("ctrl-break received");
/// }
/// # Ok(())
/// # }
/// ```
///
/// [`CtrlBreak`]: struct@tokio::signal::windows::CtrlBreak
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
+18
View File
@@ -8,6 +8,24 @@ use tokio::io::{AsyncBufRead, Split};
pin_project! {
/// A wrapper around [`tokio::io::Split`] that implements [`Stream`].
///
/// # Example
///
/// ```
/// use tokio::io::AsyncBufReadExt;
/// use tokio_stream::{StreamExt, wrappers::SplitStream};
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> std::io::Result<()> {
/// let input = "Hello\nWorld\n".as_bytes();
/// let lines = AsyncBufReadExt::split(input, b'\n');
///
/// let mut stream = SplitStream::new(lines);
/// while let Some(line) = stream.next().await {
/// println!("length = {}", line?.len())
/// }
/// # Ok(())
/// # }
/// ```
/// [`tokio::io::Split`]: struct@tokio::io::Split
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
+27
View File
@@ -6,6 +6,33 @@ use tokio::net::{TcpListener, TcpStream};
/// A wrapper around [`TcpListener`] that implements [`Stream`].
///
/// # Example
///
/// Accept connections from both IPv4 and IPv6 listeners in the same loop:
///
/// ```no_run
/// use std::net::{Ipv4Addr, Ipv6Addr};
///
/// use tokio::net::TcpListener;
/// use tokio_stream::{StreamExt, wrappers::TcpListenerStream};
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> std::io::Result<()> {
/// let ipv4_listener = TcpListener::bind((Ipv6Addr::LOCALHOST, 8080)).await?;
/// let ipv6_listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 8080)).await?;
/// let ipv4_connections = TcpListenerStream::new(ipv4_listener);
/// let ipv6_connections = TcpListenerStream::new(ipv6_listener);
///
/// let mut connections = ipv4_connections.chain(ipv6_connections);
/// while let Some(tcp_stream) = connections.next().await {
/// let stream = tcp_stream?;
/// let peer_addr = stream.peer_addr()?;
/// println!("accepted connection; peer address = {peer_addr}");
/// }
/// # Ok(())
/// # }
/// ```
///
/// [`TcpListener`]: struct@tokio::net::TcpListener
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
@@ -6,6 +6,25 @@ use tokio::net::{UnixListener, UnixStream};
/// A wrapper around [`UnixListener`] that implements [`Stream`].
///
/// # Example
///
/// ```no_run
/// use tokio::net::UnixListener;
/// use tokio_stream::{StreamExt, wrappers::UnixListenerStream};
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> std::io::Result<()> {
/// let listener = UnixListener::bind("/tmp/sock")?;
/// let mut incoming = UnixListenerStream::new(listener);
///
/// while let Some(stream) = incoming.next().await {
/// let stream = stream?;
/// let peer_addr = stream.peer_addr()?;
/// println!("Accepted connection from: {peer_addr:?}");
/// }
/// # Ok(())
/// # }
/// ```
/// [`UnixListener`]: struct@tokio::net::UnixListener
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
+1
View File
@@ -49,6 +49,7 @@ where
}
#[tokio::test]
#[cfg_attr(miri, ignore)] // Block on https://github.com/tokio-rs/tokio/issues/6860
async fn pending_first() {
let (tx1, rx1) = mpsc::unbounded_channel_stream();
let (tx2, rx2) = mpsc::unbounded_channel_stream();
+1 -1
View File
@@ -12,7 +12,7 @@ async fn watch_stream_message_not_twice() {
let mut counter = 0;
let mut stream = WatchStream::new(rx).map(move |payload| {
println!("{}", payload);
println!("{payload}");
if payload == "goodbye" {
counter += 1;
}
+3 -3
View File
@@ -19,9 +19,6 @@ categories = ["asynchronous", "development-tools::testing"]
[dependencies]
tokio = { version = "1.2.0", path = "../tokio", features = ["rt", "sync", "time", "test-util"] }
tokio-stream = { version = "0.1.1", path = "../tokio-stream" }
async-stream = "0.3.3"
bytes = "1.0.0"
futures-core = "0.3.0"
[dev-dependencies]
@@ -30,3 +27,6 @@ futures-util = "0.3.0"
[package.metadata.docs.rs]
all-features = true
[lints]
workspace = true
+53 -9
View File
@@ -52,6 +52,7 @@ pub struct Handle {
pub struct Builder {
// Sequence of actions for the Mock to take
actions: VecDeque<Action>,
name: String,
}
#[derive(Debug, Clone)]
@@ -71,6 +72,7 @@ struct Inner {
sleep: Option<Pin<Box<Sleep>>>,
read_wait: Option<Waker>,
rx: UnboundedReceiverStream<Action>,
name: String,
}
impl Builder {
@@ -127,6 +129,12 @@ impl Builder {
self
}
/// Set name of the mock IO object to include in panic messages and debug output
pub fn name(&mut self, name: impl Into<String>) -> &mut Self {
self.name = name.into();
self
}
/// Build a `Mock` value according to the defined script.
pub fn build(&mut self) -> Mock {
let (mock, _) = self.build_with_handle();
@@ -135,7 +143,7 @@ impl Builder {
/// Build a `Mock` value paired with a handle
pub fn build_with_handle(&mut self) -> (Mock, Handle) {
let (inner, handle) = Inner::new(self.actions.clone());
let (inner, handle) = Inner::new(self.actions.clone(), self.name.clone());
let mock = Mock { inner };
@@ -184,7 +192,7 @@ impl Handle {
}
impl Inner {
fn new(actions: VecDeque<Action>) -> (Inner, Handle) {
fn new(actions: VecDeque<Action>, name: String) -> (Inner, Handle) {
let (tx, rx) = mpsc::unbounded_channel();
let rx = UnboundedReceiverStream::new(rx);
@@ -195,6 +203,7 @@ impl Inner {
read_wait: None,
rx,
waiting: None,
name,
};
let handle = Handle { tx };
@@ -256,7 +265,7 @@ impl Inner {
Action::Write(ref mut expect) => {
let n = cmp::min(src.len(), expect.len());
assert_eq!(&src[..n], &expect[..n]);
assert_eq!(&src[..n], &expect[..n], "name={} i={}", self.name, i);
// Drop data that was matched
expect.drain(..n);
@@ -418,7 +427,7 @@ impl AsyncWrite for Mock {
self.inner.actions.push_back(action);
}
Poll::Ready(None) => {
panic!("unexpected write");
panic!("unexpected write {}", self.pmsg());
}
}
}
@@ -429,7 +438,7 @@ impl AsyncWrite for Mock {
let until = Instant::now() + rem;
self.inner.sleep = Some(Box::pin(time::sleep_until(until)));
} else {
panic!("unexpected WouldBlock");
panic!("unexpected WouldBlock {}", self.pmsg());
}
}
Ok(0) => {
@@ -445,7 +454,7 @@ impl AsyncWrite for Mock {
continue;
}
None => {
panic!("unexpected write");
panic!("unexpected write {}", self.pmsg());
}
}
}
@@ -475,8 +484,16 @@ impl Drop for Mock {
}
self.inner.actions.iter().for_each(|a| match a {
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."),
Action::Read(data) => assert!(
data.is_empty(),
"There is still data left to read. {}",
self.pmsg()
),
Action::Write(data) => assert!(
data.is_empty(),
"There is still data left to write. {}",
self.pmsg()
),
_ => (),
});
}
@@ -505,6 +522,33 @@ fn is_task_ctx() -> bool {
impl fmt::Debug for Inner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Inner {{...}}")
if self.name.is_empty() {
write!(f, "Inner {{...}}")
} else {
write!(f, "Inner {{name={}, ...}}", self.name)
}
}
}
struct PanicMsgSnippet<'a>(&'a Inner);
impl<'a> fmt::Display for PanicMsgSnippet<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.name.is_empty() {
write!(f, "({} actions remain)", self.0.actions.len())
} else {
write!(
f,
"(name {}, {} actions remain)",
self.0.name,
self.0.actions.len()
)
}
}
}
impl Mock {
fn pmsg(&self) -> PanicMsgSnippet<'_> {
PanicMsgSnippet(&self.inner)
}
}
-1
View File
@@ -1,4 +1,3 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![warn(
missing_debug_implementations,
missing_docs,
+1 -2
View File
@@ -161,8 +161,7 @@ impl<T: Unpin> Drop for StreamMock<T> {
assert!(
undropped_count == 0,
"StreamMock was dropped before all actions were consumed, {} actions were not consumed",
undropped_count
"StreamMock was dropped before all actions were consumed, {undropped_count} actions were not consumed"
);
}
}
+11 -45
View File
@@ -26,11 +26,10 @@
//! ```
use std::future::Future;
use std::mem;
use std::ops;
use std::pin::Pin;
use std::sync::{Arc, Condvar, Mutex};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use std::task::{Context, Poll, Wake, Waker};
use tokio_stream::Stream;
@@ -171,7 +170,7 @@ impl MockTask {
F: FnOnce(&mut Context<'_>) -> R,
{
self.waker.clear();
let waker = self.waker();
let waker = self.clone().into_waker();
let mut cx = Context::from_waker(&waker);
f(&mut cx)
@@ -190,11 +189,8 @@ impl MockTask {
Arc::strong_count(&self.waker)
}
fn waker(&self) -> Waker {
unsafe {
let raw = to_raw(self.waker.clone());
Waker::from_raw(raw)
}
fn into_waker(self) -> Waker {
self.waker.into()
}
}
@@ -226,8 +222,14 @@ impl ThreadWaker {
_ => unreachable!(),
}
}
}
fn wake(&self) {
impl Wake for ThreadWaker {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
// First, try transitioning from IDLE -> NOTIFY, this does not require a lock.
let mut state = self.state.lock().unwrap();
let prev = *state;
@@ -247,39 +249,3 @@ impl ThreadWaker {
self.condvar.notify_one();
}
}
static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop_waker);
unsafe fn to_raw(waker: Arc<ThreadWaker>) -> RawWaker {
RawWaker::new(Arc::into_raw(waker) as *const (), &VTABLE)
}
unsafe fn from_raw(raw: *const ()) -> Arc<ThreadWaker> {
Arc::from_raw(raw as *const ThreadWaker)
}
unsafe fn clone(raw: *const ()) -> RawWaker {
let waker = from_raw(raw);
// Increment the ref count
mem::forget(waker.clone());
to_raw(waker)
}
unsafe fn wake(raw: *const ()) {
let waker = from_raw(raw);
waker.wake();
}
unsafe fn wake_by_ref(raw: *const ()) {
let waker = from_raw(raw);
waker.wake();
// We don't actually own a reference to the unparker
mem::forget(waker);
}
unsafe fn drop_waker(raw: *const ()) {
let _ = from_raw(raw);
}
+2 -2
View File
@@ -34,7 +34,7 @@ async fn read_error() {
match mock.read(&mut buf).await {
Err(error) => {
assert_eq!(error.kind(), io::ErrorKind::Other);
assert_eq!("cruel", format!("{}", error));
assert_eq!("cruel", format!("{error}"));
}
Ok(_) => panic!("error not received"),
}
@@ -87,7 +87,7 @@ async fn write_error() {
match mock.write_all(b"whoa").await {
Err(error) => {
assert_eq!(error.kind(), io::ErrorKind::Other);
assert_eq!("cruel", format!("{}", error));
assert_eq!("cruel", format!("{error}"));
}
Ok(_) => panic!("error not received"),
}
+65 -1
View File
@@ -1,3 +1,67 @@
# 0.7.15 (April 23rd, 2025)
### Fixed
- task: properly handle removed entries in `JoinMap` ([#7264])
### Updated
- deps: update hashbrown to 0.15 ([#7219])
### Documented
- task: explicitly state that `TaskTracker` does not abort tasks on Drop ([#7223])
[#7219]: https://github.com/tokio-rs/tokio/pull/7219
[#7223]: https://github.com/tokio-rs/tokio/pull/7223
[#7264]: https://github.com/tokio-rs/tokio/pull/7264
# 0.7.14 (March 12th, 2025)
### Added
- io: add `get_ref` and `get_mut` for `SyncIoBridge` ([#7128])
- io: add `read_exact_arc` ([#7165])
- sync: add `CancellationToken::run_until_cancelled_owned` ([#7081])
### Changed
- codec: optimize buffer reserve for `AnyDelimiterCodec::encode` ([#7188])
- either: enable `Either` to use underlying `AsyncWrite` implementation ([#7025])
### Fixed
- codec: fix typo in API docs ([#7044])
- util: fix example in `StreamReader` docs ([#7167])
### Documented
- io: add docs for `SyncIoBridge` with examples and alternatives ([#6815])
### Internal
- io: clean up buffer casts ([#7142])
- task: run `spawn_pinned` tests with miri ([#7023])
[#6815]: https://github.com/tokio-rs/tokio/pull/6815
[#7023]: https://github.com/tokio-rs/tokio/pull/7023
[#7025]: https://github.com/tokio-rs/tokio/pull/7025
[#7044]: https://github.com/tokio-rs/tokio/pull/7044
[#7081]: https://github.com/tokio-rs/tokio/pull/7081
[#7128]: https://github.com/tokio-rs/tokio/pull/7128
[#7142]: https://github.com/tokio-rs/tokio/pull/7142
[#7165]: https://github.com/tokio-rs/tokio/pull/7165
[#7167]: https://github.com/tokio-rs/tokio/pull/7167
[#7188]: https://github.com/tokio-rs/tokio/pull/7188
# 0.7.13 (December 4th, 2024)
### Fixed
- codec: fix incorrect handling of invalid utf-8 in `LinesCodec::decode_eof` ([#7011])
[#7011]: https://github.com/tokio-rs/tokio/pull/7011
# 0.7.12 (September 5th, 2024)
This release bumps the MSRV to 1.70. ([#6645])
@@ -127,7 +191,7 @@ This release contains one performance improvement:
[#5630]: https://github.com/tokio-rs/tokio/pull/5630
[#5632]: https://github.com/tokio-rs/tokio/pull/5632
# 0.7.7 (February 12, 2023)
# 0.7.7 (February 12th, 2023)
This release reverts the removal of the `Encoder` bound on the `FramedParts`
constructor from [#5280] since it turned out to be a breaking change. ([#5450])
+7 -4
View File
@@ -4,7 +4,7 @@ name = "tokio-util"
# - Remove path dependencies
# - Update CHANGELOG.md.
# - Create "tokio-util-0.7.x" git tag.
version = "0.7.12"
version = "0.7.15"
edition = "2021"
rust-version = "1.70"
authors = ["Tokio Contributors <[email protected]>"]
@@ -35,17 +35,17 @@ __docs_rs = ["futures-util"]
[dependencies]
tokio = { version = "1.28.0", path = "../tokio", features = ["sync"] }
bytes = "1.0.0"
bytes = "1.5.0"
futures-core = "0.3.0"
futures-sink = "0.3.0"
futures-io = { version = "0.3.0", optional = true }
futures-util = { version = "0.3.0", optional = true }
pin-project-lite = "0.2.11"
slab = { version = "0.4.4", optional = true } # Backs `DelayQueue`
tracing = { version = "0.1.25", default-features = false, features = ["std"], optional = true }
tracing = { version = "0.1.29", default-features = false, features = ["std"], optional = true }
[target.'cfg(tokio_unstable)'.dependencies]
hashbrown = { version = "0.14.0", default-features = false, optional = true }
hashbrown = { version = "0.15.0", default-features = false, optional = true }
[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
@@ -68,3 +68,6 @@ rustc-args = ["--cfg", "docsrs", "--cfg", "tokio_unstable"]
[package.metadata.playground]
features = ["full"]
[lints]
workspace = true
+5 -7
View File
@@ -141,11 +141,9 @@ impl Decoder for AnyDelimiterCodec {
// there's no max_length set, we'll read to the end of the buffer.
let read_to = cmp::min(self.max_length.saturating_add(1), buf.len());
let new_chunk_offset = buf[self.next_index..read_to].iter().position(|b| {
self.seek_delimiters
.iter()
.any(|delimiter| *b == *delimiter)
});
let new_chunk_offset = buf[self.next_index..read_to]
.iter()
.position(|b| self.seek_delimiters.contains(b));
match (self.is_discarding, new_chunk_offset) {
(true, Some(offset)) => {
@@ -217,7 +215,7 @@ where
fn encode(&mut self, chunk: T, buf: &mut BytesMut) -> Result<(), AnyDelimiterCodecError> {
let chunk = chunk.as_ref();
buf.reserve(chunk.len() + 1);
buf.reserve(chunk.len() + self.sequence_writer.len());
buf.put(chunk.as_bytes());
buf.put(self.sequence_writer.as_ref());
@@ -249,7 +247,7 @@ impl fmt::Display for AnyDelimiterCodecError {
AnyDelimiterCodecError::MaxChunkLengthExceeded => {
write!(f, "max chunk length exceeded")
}
AnyDelimiterCodecError::Io(e) => write!(f, "{}", e),
AnyDelimiterCodecError::Io(e) => write!(f, "{e}"),
}
}
}
+2 -2
View File
@@ -169,6 +169,7 @@ impl Decoder for LinesCodec {
Ok(match self.decode(buf)? {
Some(frame) => Some(frame),
None => {
self.next_index = 0;
// No terminating newline - return remaining data, if any
if buf.is_empty() || buf == &b"\r"[..] {
None
@@ -176,7 +177,6 @@ impl Decoder for LinesCodec {
let line = buf.split_to(buf.len());
let line = without_carriage_return(&line);
let line = utf8(line)?;
self.next_index = 0;
Some(line.to_string())
}
}
@@ -218,7 +218,7 @@ impl fmt::Display for LinesCodecError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LinesCodecError::MaxLineLengthExceeded => write!(f, "max line length exceeded"),
LinesCodecError::Io(e) => write!(f, "{}", e),
LinesCodecError::Io(e) => write!(f, "{e}"),
}
}
}
+1 -1
View File
@@ -224,7 +224,7 @@
//! The main method on the `Encoder` trait is the [`encode`] method. This method
//! takes an item that is being written, and a buffer to write the item to. The
//! buffer may already contain data, and in this case, the encoder should append
//! the new frame the to buffer rather than overwrite the existing data.
//! the new frame to the buffer rather than overwrite the existing data.
//!
//! It is guaranteed that, from one call to `encode` to another, the provided
//! buffer will contain the exact same data as before, except that some of the
+105
View File
@@ -1,5 +1,110 @@
//! Compatibility between the `tokio::io` and `futures-io` versions of the
//! `AsyncRead` and `AsyncWrite` traits.
//!
//! ## Bridging Tokio and Futures I/O with `compat()`
//!
//! The [`compat()`] function provides a compatibility layer that allows types implementing
//! [`tokio::io::AsyncRead`] or [`tokio::io::AsyncWrite`] to be used as their
//! [`futures::io::AsyncRead`] or [`futures::io::AsyncWrite`] counterparts — and vice versa.
//!
//! This is especially useful when working with libraries that expect I/O types from one ecosystem
//! (usually `futures`) but you are using types from the other (usually `tokio`).
//!
//! ## Compatibility Overview
//!
//! | Inner Type Implements... | `Compat<T>` Implements... |
//! |-----------------------------|-----------------------------|
//! | [`tokio::io::AsyncRead`] | [`futures::io::AsyncRead`] |
//! | [`futures::io::AsyncRead`] | [`tokio::io::AsyncRead`] |
//! | [`tokio::io::AsyncWrite`] | [`futures::io::AsyncWrite`] |
//! | [`futures::io::AsyncWrite`] | [`tokio::io::AsyncWrite`] |
//!
//! ## Feature Flag
//!
//! This functionality is available through the `compat` feature flag:
//!
//! ```toml
//! tokio-util = { version = "...", features = ["compat"] }
//! ```
//!
//! ## Example 1: Tokio -> Futures (`AsyncRead`)
//!
//! This example demonstrates sending data over a [`tokio::net::TcpStream`] and using
//! [`futures::io::AsyncReadExt::read`] from the `futures` crate to read it after adapting the
//! stream via [`compat()`].
//!
//! ```no_run
//! use tokio::net::{TcpListener, TcpStream};
//! use tokio::io::AsyncWriteExt;
//! use tokio_util::compat::TokioAsyncReadCompatExt;
//! use futures::io::AsyncReadExt;
//!
//! #[tokio::main]
//! async fn main() -> std::io::Result<()> {
//! let listener = TcpListener::bind("127.0.0.1:8081").await?;
//!
//! tokio::spawn(async {
//! let mut client = TcpStream::connect("127.0.0.1:8081").await.unwrap();
//! client.write_all(b"Hello World").await.unwrap();
//! });
//!
//! let (stream, _) = listener.accept().await?;
//!
//! // Adapt `tokio::TcpStream` to be used with `futures::io::AsyncReadExt`
//! let mut compat_stream = stream.compat();
//! let mut buffer = [0; 20];
//! let n = compat_stream.read(&mut buffer).await?;
//! println!("Received: {}", String::from_utf8_lossy(&buffer[..n]));
//!
//! Ok(())
//! }
//! ```
//!
//! ## Example 2: Futures -> Tokio (`AsyncRead`)
//!
//! The reverse is also possible: you can take a [`futures::io::AsyncRead`] (e.g. a cursor) and
//! adapt it to be used with [`tokio::io::AsyncReadExt::read_to_end`]
//!
//! ```
//! use futures::io::Cursor;
//! use tokio_util::compat::FuturesAsyncReadCompatExt;
//! use tokio::io::AsyncReadExt;
//!
//! fn main() {
//! let future = async {
//! let reader = Cursor::new(b"Hello from futures");
//! let mut compat_reader = reader.compat();
//! let mut buf = Vec::new();
//! compat_reader.read_to_end(&mut buf).await.unwrap();
//! assert_eq!(&buf, b"Hello from futures");
//! };
//!
//! // Run the future inside a Tokio runtime
//! tokio::runtime::Runtime::new().unwrap().block_on(future);
//! }
//! ```
//!
//! ## Common Use Cases
//!
//! - Using `tokio` sockets with `async-tungstenite`, `async-compression`, or `futures-rs`-based
//! libraries.
//! - Bridging I/O interfaces between mixed-ecosystem libraries.
//! - Avoiding rewrites or duplication of I/O code in async environments.
//!
//! ## See Also
//!
//! - [`Compat`] type
//! - [`TokioAsyncReadCompatExt`]
//! - [`FuturesAsyncReadCompatExt`]
//! - [`tokio::io`]
//! - [`futures::io`]
//!
//! [`futures::io`]: https://docs.rs/futures/latest/futures/io/
//! [`futures::io::AsyncRead`]: https://docs.rs/futures/latest/futures/io/trait.AsyncRead.html
//! [`futures::io::AsyncWrite`]: https://docs.rs/futures/latest/futures/io/trait.AsyncWrite.html
//! [`futures::io::AsyncReadExt::read`]: https://docs.rs/futures/latest/futures/io/trait.AsyncReadExt.html#method.read
//! [`compat()`]: TokioAsyncReadCompatExt::compat
use pin_project_lite::pin_project;
use std::io;
use std::pin::Pin;
+15
View File
@@ -150,6 +150,21 @@ where
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<tokio::io::Result<()>> {
delegate_call!(self.poll_shutdown(cx))
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[std::io::IoSlice<'_>],
) -> Poll<std::result::Result<usize, std::io::Error>> {
delegate_call!(self.poll_write_vectored(cx, bufs))
}
fn is_write_vectored(&self) -> bool {
match self {
Self::Left(l) => l.is_write_vectored(),
Self::Right(r) => r.is_write_vectored(),
}
}
}
impl<L, R> futures_core::stream::Stream for Either<L, R>
+3
View File
@@ -18,6 +18,9 @@ mod sink_writer;
mod stream_reader;
cfg_io_util! {
mod read_arc;
pub use self::read_arc::read_exact_arc;
mod sync_bridge;
pub use self::sync_bridge::SyncIoBridge;
}
+44
View File
@@ -0,0 +1,44 @@
use std::io;
use std::mem::MaybeUninit;
use std::sync::Arc;
use tokio::io::{AsyncRead, AsyncReadExt};
/// Read data from an `AsyncRead` into an `Arc`.
///
/// This uses `Arc::new_uninit_slice` and reads into the resulting uninitialized `Arc`.
///
/// # Example
///
/// ```
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// use tokio_util::io::read_exact_arc;
///
/// let read = tokio::io::repeat(42);
///
/// let arc = read_exact_arc(read, 4).await?;
///
/// assert_eq!(&arc[..], &[42; 4]);
/// # Ok(())
/// # }
/// ```
pub async fn read_exact_arc<R: AsyncRead>(read: R, len: usize) -> io::Result<Arc<[u8]>> {
tokio::pin!(read);
// TODO(MSRV 1.82): When bumping MSRV, switch to `Arc::new_uninit_slice(len)`. The following is
// equivalent, and generates the same assembly, but works without requiring MSRV 1.82.
let arc: Arc<[MaybeUninit<u8>]> = (0..len).map(|_| MaybeUninit::uninit()).collect();
// TODO(MSRV future): Use `Arc::get_mut_unchecked` once it's stabilized.
// SAFETY: We're the only owner of the `Arc`, and we keep the `Arc` valid throughout this loop
// as we write through this reference.
let mut buf = unsafe { &mut *(Arc::as_ptr(&arc) as *mut [MaybeUninit<u8>]) };
while !buf.is_empty() {
if read.read_buf(&mut buf).await? == 0 {
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "early eof"));
}
}
// TODO(MSRV 1.82): When bumping MSRV, switch to `arc.assume_init()`. The following is
// equivalent, and generates the same assembly, but works without requiring MSRV 1.82.
// SAFETY: This changes `[MaybeUninit<u8>]` to `[u8]`, and we've initialized all the bytes in
// the loop above.
Ok(unsafe { Arc::from_raw(Arc::into_raw(arc) as *const [u8]) })
}
+1 -1
View File
@@ -169,7 +169,7 @@ where
/// Convert a stream of byte chunks into an [`AsyncRead`].
///
/// The item should be a [`Result`] with the ok variant being something that
/// implements the [`Buf`] trait (e.g. `Vec<u8>` or `Bytes`). The error
/// implements the [`Buf`] trait (e.g. `Cursor<Vec<u8>>` or `Bytes`). The error
/// should be convertible into an [io error].
///
/// [`Result`]: std::result::Result
+264 -1
View File
@@ -5,7 +5,258 @@ use tokio::io::{
};
/// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or
/// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`].
/// a [`tokio::io::AsyncWrite`] synchronously as a [`std::io::Write`].
///
/// # Alternatives
///
/// In many cases, there are better alternatives to using `SyncIoBridge`, especially
/// if you want to avoid blocking the async runtime. Consider the following scenarios:
///
/// When hashing data, using `SyncIoBridge` can lead to suboptimal performance and
/// might not fully leverage the async capabilities of the system.
///
/// ### Why It Matters:
///
/// `SyncIoBridge` allows you to use asynchronous I/O operations in an synchronous
/// context by blocking the current thread. However, this can be inefficient because:
/// - **Inefficient Resource Usage**: `SyncIoBridge` takes up an entire OS thread,
/// which is inefficient compared to asynchronous code that can multiplex many
/// tasks on a single thread.
/// - **Thread Pool Saturation**: Excessive use of `SyncIoBridge` can exhaust the
/// async runtime's thread pool, reducing the number of threads available for
/// other tasks and impacting overall performance.
/// - **Missed Concurrency Benefits**: By using synchronous operations with
/// `SyncIoBridge`, you lose the ability to interleave tasks efficiently,
/// which is a key advantage of asynchronous programming.
///
/// ## Example 1: Hashing Data
///
/// The use of `SyncIoBridge` is unnecessary when hashing data. Instead, you can
/// process the data asynchronously by reading it into memory, which avoids blocking
/// the async runtime.
///
/// There are two strategies for avoiding `SyncIoBridge` when hashing data. When
/// the data fits into memory, the easiest is to read the data into a `Vec<u8>`
/// and hash it:
///
/// Explanation: This example demonstrates how to asynchronously read data from a
/// reader into memory and hash it using a synchronous hashing function. The
/// `SyncIoBridge` is avoided, ensuring that the async runtime is not blocked.
/// ```rust
/// use tokio::io::AsyncReadExt;
/// use tokio::io::AsyncRead;
/// use std::io::Cursor;
/// # mod blake3 { pub fn hash(_: &[u8]) {} }
///
/// async fn hash_contents(mut reader: impl AsyncRead + Unpin) -> Result<(), std::io::Error> {
/// // Read all data from the reader into a Vec<u8>.
/// let mut data = Vec::new();
/// reader.read_to_end(&mut data).await?;
///
/// // Hash the data using the blake3 hashing function.
/// let hash = blake3::hash(&data);
///
/// Ok(hash)
///}
///
/// #[tokio::main]
/// async fn main() -> Result<(), std::io::Error> {
/// // Example: In-memory data.
/// let data = b"Hello, world!"; // A byte slice.
/// let reader = Cursor::new(data); // Create an in-memory AsyncRead.
/// hash_contents(reader).await
/// }
/// ```
///
/// When the data doesn't fit into memory, the hashing library will usually
/// provide a `hasher` that you can repeatedly call `update` on to hash the data
/// one chunk at the time.
///
/// Explanation: This example demonstrates how to asynchronously stream data in
/// chunks for hashing. Each chunk is read asynchronously, and the hash is updated
/// incrementally. This avoids blocking and improves performance over using
/// `SyncIoBridge`.
///
/// ```rust
/// use tokio::io::AsyncReadExt;
/// use tokio::io::AsyncRead;
/// use std::io::Cursor;
/// # struct Hasher;
/// # impl Hasher { pub fn update(&mut self, _: &[u8]) {} pub fn finalize(&self) {} }
///
/// /// Asynchronously streams data from an async reader, processes it in chunks,
/// /// and hashes the data incrementally.
/// async fn hash_stream(mut reader: impl AsyncRead + Unpin, mut hasher: Hasher) -> Result<(), std::io::Error> {
/// // Create a buffer to read data into, sized for performance.
/// let mut data = vec![0; 64 * 1024];
/// loop {
/// // Read data from the reader into the buffer.
/// let len = reader.read(&mut data).await?;
/// if len == 0 { break; } // Exit loop if no more data.
///
/// // Update the hash with the data read.
/// hasher.update(&data[..len]);
/// }
///
/// // Finalize the hash after all data has been processed.
/// let hash = hasher.finalize();
///
/// Ok(hash)
///}
///
/// #[tokio::main]
/// async fn main() -> Result<(), std::io::Error> {
/// // Example: In-memory data.
/// let data = b"Hello, world!"; // A byte slice.
/// let reader = Cursor::new(data); // Create an in-memory AsyncRead.
/// let hasher = Hasher;
/// hash_stream(reader, hasher).await
/// }
/// ```
///
///
/// ## Example 2: Compressing Data
///
/// When compressing data, the use of `SyncIoBridge` is unnecessary as it introduces
/// blocking and inefficient code. Instead, you can utilize an async compression library
/// such as the [`async-compression`](https://docs.rs/async-compression/latest/async_compression/)
/// crate, which is built to handle asynchronous data streams efficiently.
///
/// Explanation: This example shows how to asynchronously compress data using an
/// async compression library. By reading and writing asynchronously, it avoids
/// blocking and is more efficient than using `SyncIoBridge` with a non-async
/// compression library.
///
/// ```ignore
/// use async_compression::tokio::write::GzipEncoder;
/// use std::io::Cursor;
/// use tokio::io::AsyncRead;
///
/// /// Asynchronously compresses data from an async reader using Gzip and an async encoder.
/// async fn compress_data(mut reader: impl AsyncRead + Unpin) -> Result<(), std::io::Error> {
/// let writer = tokio::io::sink();
///
/// // Create a Gzip encoder that wraps the writer.
/// let mut encoder = GzipEncoder::new(writer);
///
/// // Copy data from the reader to the encoder, compressing it.
/// tokio::io::copy(&mut reader, &mut encoder).await?;
///
/// Ok(())
///}
///
/// #[tokio::main]
/// async fn main() -> Result<(), std::io::Error> {
/// // Example: In-memory data.
/// let data = b"Hello, world!"; // A byte slice.
/// let reader = Cursor::new(data); // Create an in-memory AsyncRead.
/// compress_data(reader).await?;
///
/// Ok(())
/// }
/// ```
///
///
/// ## Example 3: Parsing Data Formats
///
///
/// `SyncIoBridge` is not ideal when parsing data formats such as `JSON`, as it
/// blocks async operations. A more efficient approach is to read data asynchronously
/// into memory and then `deserialize` it, avoiding unnecessary synchronization overhead.
///
/// Explanation: This example shows how to asynchronously read data into memory
/// and then parse it as `JSON`. By avoiding `SyncIoBridge`, the asynchronous runtime
/// remains unblocked, leading to better performance when working with asynchronous
/// I/O streams.
///
/// ```rust,no_run
/// use tokio::io::AsyncRead;
/// use tokio::io::AsyncReadExt;
/// use std::io::Cursor;
/// # mod serde {
/// # pub trait DeserializeOwned: 'static {}
/// # impl<T: 'static> DeserializeOwned for T {}
/// # }
/// # mod serde_json {
/// # use super::serde::DeserializeOwned;
/// # pub fn from_slice<T: DeserializeOwned>(_: &[u8]) -> Result<T, std::io::Error> {
/// # unimplemented!()
/// # }
/// # }
/// # #[derive(Debug)] struct MyStruct;
///
///
/// async fn parse_json(mut reader: impl AsyncRead + Unpin) -> Result<MyStruct, std::io::Error> {
/// // Read all data from the reader into a Vec<u8>.
/// let mut data = Vec::new();
/// reader.read_to_end(&mut data).await?;
///
/// // Deserialize the data from the Vec<u8> into a MyStruct instance.
/// let value: MyStruct = serde_json::from_slice(&data)?;
///
/// Ok(value)
///}
///
/// #[tokio::main]
/// async fn main() -> Result<(), std::io::Error> {
/// // Example: In-memory data.
/// let data = b"Hello, world!"; // A byte slice.
/// let reader = Cursor::new(data); // Create an in-memory AsyncRead.
/// parse_json(reader).await?;
/// Ok(())
/// }
/// ```
///
/// ## Correct Usage of `SyncIoBridge` inside `spawn_blocking`
///
/// `SyncIoBridge` is mainly useful when you need to interface with synchronous
/// libraries from an asynchronous context.
///
/// Explanation: This example shows how to use `SyncIoBridge` inside a `spawn_blocking`
/// task to safely perform synchronous I/O without blocking the async runtime. The
/// `spawn_blocking` ensures that the synchronous code is offloaded to a dedicated
/// thread pool, preventing it from interfering with the async tasks.
///
/// ```rust
/// use tokio::task::spawn_blocking;
/// use tokio_util::io::SyncIoBridge;
/// use tokio::io::AsyncRead;
/// use std::marker::Unpin;
/// use std::io::Cursor;
///
/// /// Wraps an async reader with `SyncIoBridge` and performs synchronous I/O operations in a blocking task.
/// async fn process_sync_io(reader: impl AsyncRead + Unpin + Send + 'static) -> Result<Vec<u8>, std::io::Error> {
/// // Wrap the async reader with `SyncIoBridge` to allow synchronous reading.
/// let mut sync_reader = SyncIoBridge::new(reader);
///
/// // Spawn a blocking task to perform synchronous I/O operations.
/// let result = spawn_blocking(move || {
/// // Create an in-memory buffer to hold the copied data.
/// let mut buffer = Vec::new();
/// // Copy data from the sync_reader to the buffer.
/// std::io::copy(&mut sync_reader, &mut buffer)?;
/// // Return the buffer containing the copied data.
/// Ok::<_, std::io::Error>(buffer)
/// })
/// .await??;
///
/// // Return the result from the blocking task.
/// Ok(result)
///}
///
/// #[tokio::main]
/// async fn main() -> Result<(), std::io::Error> {
/// // Example: In-memory data.
/// let data = b"Hello, world!"; // A byte slice.
/// let reader = Cursor::new(data); // Create an in-memory AsyncRead.
/// let result = process_sync_io(reader).await?;
///
/// // You can use `result` here as needed.
///
/// Ok(())
/// }
/// ```
///
#[derive(Debug)]
pub struct SyncIoBridge<T> {
src: T,
@@ -154,3 +405,15 @@ impl<T: Unpin> SyncIoBridge<T> {
self.src
}
}
impl<T> AsMut<T> for SyncIoBridge<T> {
fn as_mut(&mut self) -> &mut T {
&mut self.src
}
}
impl<T> AsRef<T> for SyncIoBridge<T> {
fn as_ref(&self) -> &T {
&self.src
}
}
-3
View File
@@ -1,4 +1,3 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -17,8 +16,6 @@
//! This crate is not versioned in lockstep with the core
//! [`tokio`] crate. However, `tokio-util` _will_ respect Rust's
//! semantic versioning policy, especially with regard to breaking changes.
//!
//! [`tokio`]: https://docs.rs/tokio
#[macro_use]
mod cfg;
+1 -1
View File
@@ -40,7 +40,7 @@ impl Listener for tokio::net::TcpListener {
}
fn local_addr(&self) -> Result<Self::Addr> {
self.local_addr().map(Into::into)
self.local_addr()
}
}
+1 -1
View File
@@ -13,6 +13,6 @@ impl Listener for tokio::net::UnixListener {
}
fn local_addr(&self) -> Result<Self::Addr> {
self.local_addr().map(Into::into)
self.local_addr()
}
}
+70 -16
View File
@@ -1,6 +1,7 @@
//! An asynchronously awaitable `CancellationToken`.
//! An asynchronously awaitable [`CancellationToken`].
//! The token allows to signal a cancellation request to one or more tasks.
pub(crate) mod guard;
pub(crate) mod guard_ref;
mod tree_node;
use crate::loom::sync::Arc;
@@ -10,6 +11,7 @@ use core::pin::Pin;
use core::task::{Context, Poll};
use guard::DropGuard;
use guard_ref::DropGuardRef;
use pin_project_lite::pin_project;
/// A token which can be used to signal a cancellation request to one or more
@@ -110,7 +112,7 @@ impl core::fmt::Debug for CancellationToken {
}
impl Clone for CancellationToken {
/// Creates a clone of the `CancellationToken` which will get cancelled
/// Creates a clone of the [`CancellationToken`] which will get cancelled
/// whenever the current token gets cancelled, and vice versa.
fn clone(&self) -> Self {
tree_node::increase_handle_refcount(&self.inner);
@@ -133,15 +135,15 @@ impl Default for CancellationToken {
}
impl CancellationToken {
/// Creates a new `CancellationToken` in the non-cancelled state.
/// Creates a new [`CancellationToken`] in the non-cancelled state.
pub fn new() -> CancellationToken {
CancellationToken {
inner: Arc::new(tree_node::TreeNode::new()),
}
}
/// Creates a `CancellationToken` which will get cancelled whenever the
/// current token gets cancelled. Unlike a cloned `CancellationToken`,
/// Creates a [`CancellationToken`] which will get cancelled whenever the
/// current token gets cancelled. Unlike a cloned [`CancellationToken`],
/// cancelling a child token does not cancel the parent token.
///
/// If the current token is already cancelled, the child token will get
@@ -204,12 +206,18 @@ impl CancellationToken {
tree_node::is_cancelled(&self.inner)
}
/// Returns a `Future` that gets fulfilled when cancellation is requested.
/// Returns a [`Future`] that gets fulfilled when cancellation is requested.
///
/// Equivalent to:
///
/// ```ignore
/// async fn cancelled(&self);
/// ```
///
/// The future will complete immediately if the token is already cancelled
/// when this method is called.
///
/// # Cancel safety
/// # Cancellation safety
///
/// This method is cancel safe.
pub fn cancelled(&self) -> WaitForCancellationFuture<'_> {
@@ -219,7 +227,13 @@ impl CancellationToken {
}
}
/// Returns a `Future` that gets fulfilled when cancellation is requested.
/// Returns a [`Future`] that gets fulfilled when cancellation is requested.
///
/// Equivalent to:
///
/// ```ignore
/// async fn cancelled_owned(self);
/// ```
///
/// The future will complete immediately if the token is already cancelled
/// when this method is called.
@@ -227,14 +241,14 @@ impl CancellationToken {
/// The function takes self by value and returns a future that owns the
/// token.
///
/// # Cancel safety
/// # Cancellation safety
///
/// This method is cancel safe.
pub fn cancelled_owned(self) -> WaitForCancellationFutureOwned {
WaitForCancellationFutureOwned::new(self)
}
/// Creates a `DropGuard` for this token.
/// Creates a [`DropGuard`] for this token.
///
/// Returned guard will cancel this token (and all its children) on drop
/// unless disarmed.
@@ -242,11 +256,25 @@ impl CancellationToken {
DropGuard { inner: Some(self) }
}
/// Creates a [`DropGuardRef`] for this token.
///
/// Returned guard will cancel this token (and all its children) on drop
/// unless disarmed.
pub fn drop_guard_ref(&self) -> DropGuardRef<'_> {
DropGuardRef { inner: Some(self) }
}
/// Runs a future to completion and returns its result wrapped inside of an `Option`
/// unless the `CancellationToken` is cancelled. In that case the function returns
/// unless the [`CancellationToken`] is cancelled. In that case the function returns
/// `None` and the future gets dropped.
///
/// # Cancel safety
/// # Fairness
///
/// Calling this on an already-cancelled token directly returns `None`.
/// For all subsequent polls, in case of concurrent completion and
/// cancellation, this is biased towards the future completion.
///
/// # Cancellation safety
///
/// This method is only cancel safe if `fut` is cancel safe.
pub async fn run_until_cancelled<F>(&self, fut: F) -> Option<F::Output>
@@ -281,11 +309,37 @@ impl CancellationToken {
}
}
RunUntilCancelledFuture {
cancellation: self.cancelled(),
future: fut,
if self.is_cancelled() {
None
} else {
RunUntilCancelledFuture {
cancellation: self.cancelled(),
future: fut,
}
.await
}
.await
}
/// Runs a future to completion and returns its result wrapped inside of an `Option`
/// unless the [`CancellationToken`] is cancelled. In that case the function returns
/// `None` and the future gets dropped.
///
/// The function takes self by value and returns a future that owns the token.
///
/// # Fairness
///
/// Calling this on an already-cancelled token directly returns `None`.
/// For all subsequent polls, in case of concurrent completion and
/// cancellation, this is biased towards the future completion.
///
/// # Cancellation safety
///
/// This method is only cancel safe if `fut` is cancel safe.
pub async fn run_until_cancelled_owned<F>(self, fut: F) -> Option<F::Output>
where
F: Future,
{
self.run_until_cancelled(fut).await
}
}
@@ -1,7 +1,9 @@
use crate::sync::CancellationToken;
/// A wrapper for cancellation token which automatically cancels
/// it on drop. It is created using `drop_guard` method on the `CancellationToken`.
/// it on drop. It is created using [`drop_guard`] method on the [`CancellationToken`].
///
/// [`drop_guard`]: CancellationToken::drop_guard
#[derive(Debug)]
pub struct DropGuard {
pub(super) inner: Option<CancellationToken>,
@@ -0,0 +1,32 @@
use crate::sync::CancellationToken;
/// A wrapper for cancellation token which automatically cancels
/// it on drop. It is created using [`drop_guard_ref`] method on the [`CancellationToken`].
///
/// This is a borrowed version of [`DropGuard`].
///
/// [`drop_guard_ref`]: CancellationToken::drop_guard_ref
/// [`DropGuard`]: super::DropGuard
#[derive(Debug)]
pub struct DropGuardRef<'a> {
pub(super) inner: Option<&'a CancellationToken>,
}
impl<'a> DropGuardRef<'a> {
/// Returns stored cancellation token and removes this drop guard instance
/// (i.e. it will no longer cancel token). Other guards for this token
/// are not affected.
pub fn disarm(mut self) -> &'a CancellationToken {
self.inner
.take()
.expect("`inner` can be only None in a destructor")
}
}
impl Drop for DropGuardRef<'_> {
fn drop(&mut self) {
if let Some(inner) = self.inner {
inner.cancel();
}
}
}
@@ -18,16 +18,16 @@
//! Those invariants shall be true at any time.
//!
//! 1. A node that has no parents and no handles can no longer be cancelled.
//! This is important during both cancellation and refcounting.
//! This is important during both cancellation and refcounting.
//!
//! 2. If node B *is* or *was* a child of node A, then node B was created *after* node A.
//! This is important for deadlock safety, as it is used for lock order.
//! Node B can only become the child of node A in two ways:
//! - being created with `child_node()`, in which case it is trivially true that
//! node A already existed when node B was created
//! - being moved A->C->B to A->B because node C was removed in `decrease_handle_refcount()`
//! or `cancel()`. In this case the invariant still holds, as B was younger than C, and C
//! was younger than A, therefore B is also younger than A.
//! This is important for deadlock safety, as it is used for lock order.
//! Node B can only become the child of node A in two ways:
//! - being created with `child_node()`, in which case it is trivially true that
//! node A already existed when node B was created
//! - being moved A->C->B to A->B because node C was removed in `decrease_handle_refcount()`
//! or `cancel()`. In this case the invariant still holds, as B was younger than C, and C
//! was younger than A, therefore B is also younger than A.
//!
//! 3. If two nodes are both unlocked and node A is the parent of node B, then node B is a child of
//! node A. It is important to always restore that invariant before dropping the lock of a node.
+2 -1
View File
@@ -2,7 +2,8 @@
mod cancellation_token;
pub use cancellation_token::{
guard::DropGuard, CancellationToken, WaitForCancellationFuture, WaitForCancellationFutureOwned,
guard::DropGuard, guard_ref::DropGuardRef, CancellationToken, WaitForCancellationFuture,
WaitForCancellationFutureOwned,
};
mod mpsc;
+10
View File
@@ -5,6 +5,7 @@ use tokio::task::{AbortHandle, JoinError, JoinHandle};
use std::{
future::Future,
mem::ManuallyDrop,
pin::Pin,
task::{Context, Poll},
};
@@ -46,6 +47,15 @@ impl<T> AbortOnDropHandle<T> {
pub fn abort_handle(&self) -> AbortHandle {
self.0.abort_handle()
}
/// Cancels aborting on drop and returns the original [`JoinHandle`].
pub fn detach(self) -> JoinHandle<T> {
// Avoid invoking `AbortOnDropHandle`'s `Drop` impl
let this = ManuallyDrop::new(self);
// SAFETY: `&this.0` is a reference, so it is certainly initialized, and
// it won't be double-dropped because it's in a `ManuallyDrop`
unsafe { std::ptr::read(&this.0) }
}
}
impl<T> Future for AbortOnDropHandle<T> {
+78 -113
View File
@@ -1,5 +1,5 @@
use hashbrown::hash_map::RawEntryMut;
use hashbrown::HashMap;
use hashbrown::hash_table::Entry;
use hashbrown::{HashMap, HashTable};
use std::borrow::Borrow;
use std::collections::hash_map::RandomState;
use std::fmt;
@@ -103,13 +103,8 @@ use tokio::task::{AbortHandle, Id, JoinError, JoinSet, LocalSet};
#[cfg_attr(docsrs, doc(cfg(all(feature = "rt", tokio_unstable))))]
pub struct JoinMap<K, V, S = RandomState> {
/// A map of the [`AbortHandle`]s of the tasks spawned on this `JoinMap`,
/// indexed by their keys and task IDs.
///
/// The [`Key`] type contains both the task's `K`-typed key provided when
/// spawning tasks, and the task's IDs. The IDs are stored here to resolve
/// hash collisions when looking up tasks based on their pre-computed hash
/// (as stored in the `hashes_by_task` map).
tasks_by_key: HashMap<Key<K>, AbortHandle, S>,
/// indexed by their keys.
tasks_by_key: HashTable<(K, AbortHandle)>,
/// A map from task IDs to the hash of the key associated with that task.
///
@@ -125,21 +120,6 @@ pub struct JoinMap<K, V, S = RandomState> {
tasks: JoinSet<V>,
}
/// A [`JoinMap`] key.
///
/// This holds both a `K`-typed key (the actual key as seen by the user), _and_
/// a task ID, so that hash collisions between `K`-typed keys can be resolved
/// using either `K`'s `Eq` impl *or* by checking the task IDs.
///
/// This allows looking up a task using either an actual key (such as when the
/// user queries the map with a key), *or* using a task ID and a hash (such as
/// when removing completed tasks from the map).
#[derive(Debug)]
struct Key<K> {
key: K,
id: Id,
}
impl<K, V> JoinMap<K, V> {
/// Creates a new empty `JoinMap`.
///
@@ -176,7 +156,7 @@ impl<K, V> JoinMap<K, V> {
}
}
impl<K, V, S: Clone> JoinMap<K, V, S> {
impl<K, V, S> JoinMap<K, V, S> {
/// Creates an empty `JoinMap` which will use the given hash builder to hash
/// keys.
///
@@ -226,7 +206,7 @@ impl<K, V, S: Clone> JoinMap<K, V, S> {
#[must_use]
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Self {
Self {
tasks_by_key: HashMap::with_capacity_and_hasher(capacity, hash_builder.clone()),
tasks_by_key: HashTable::with_capacity(capacity),
hashes_by_task: HashMap::with_capacity_and_hasher(capacity, hash_builder),
tasks: JoinSet::new(),
}
@@ -415,33 +395,42 @@ where
self.insert(key, task)
}
fn insert(&mut self, key: K, abort: AbortHandle) {
let hash = self.hash(&key);
fn insert(&mut self, mut key: K, mut abort: AbortHandle) {
let hash_builder = self.hashes_by_task.hasher();
let hash = hash_one(hash_builder, &key);
let id = abort.id();
let map_key = Key { id, key };
// Insert the new key into the map of tasks by keys.
let entry = self
.tasks_by_key
.raw_entry_mut()
.from_hash(hash, |k| k.key == map_key.key);
let entry =
self.tasks_by_key
.entry(hash, |(k, _)| *k == key, |(k, _)| hash_one(hash_builder, k));
match entry {
RawEntryMut::Occupied(mut occ) => {
Entry::Occupied(occ) => {
// There was a previous task spawned with the same key! Cancel
// that task, and remove its ID from the map of hashes by task IDs.
let Key { id: prev_id, .. } = occ.insert_key(map_key);
occ.insert(abort).abort();
let _prev_hash = self.hashes_by_task.remove(&prev_id);
(key, abort) = std::mem::replace(occ.into_mut(), (key, abort));
// Remove the old task ID.
let _prev_hash = self.hashes_by_task.remove(&abort.id());
debug_assert_eq!(Some(hash), _prev_hash);
// Associate the key's hash with the new task's ID, for looking up tasks by ID.
let _prev = self.hashes_by_task.insert(id, hash);
debug_assert!(_prev.is_none(), "no prior task should have had the same ID");
// Note: it's important to drop `key` and abort the task here.
// This defends against any panics during drop handling for causing inconsistent state.
abort.abort();
drop(key);
}
RawEntryMut::Vacant(vac) => {
vac.insert(map_key, abort);
Entry::Vacant(vac) => {
vac.insert((key, abort));
// Associate the key's hash with this task's ID, for looking up tasks by ID.
let _prev = self.hashes_by_task.insert(id, hash);
debug_assert!(_prev.is_none(), "no prior task should have had the same ID");
}
};
// Associate the key's hash with this task's ID, for looking up tasks by ID.
let _prev = self.hashes_by_task.insert(id, hash);
debug_assert!(_prev.is_none(), "no prior task should have had the same ID");
}
/// Waits until one of the tasks in the map completes and returns its
@@ -469,16 +458,19 @@ where
///
/// [`tokio::select!`]: tokio::select
pub async fn join_next(&mut self) -> Option<(K, Result<V, JoinError>)> {
let (res, id) = match self.tasks.join_next_with_id().await {
Some(Ok((id, output))) => (Ok(output), id),
Some(Err(e)) => {
let id = e.id();
(Err(e), id)
loop {
let (res, id) = match self.tasks.join_next_with_id().await {
Some(Ok((id, output))) => (Ok(output), id),
Some(Err(e)) => {
let id = e.id();
(Err(e), id)
}
None => return None,
};
if let Some(key) = self.remove_by_id(id) {
break Some((key, res));
}
None => return None,
};
let key = self.remove_by_id(id)?;
Some((key, res))
}
}
/// Aborts all tasks and waits for them to finish shutting down.
@@ -620,7 +612,7 @@ where
// Note: this method iterates over the tasks and keys *without* removing
// any entries, so that the keys from aborted tasks can still be
// returned when calling `join_next` in the future.
for (Key { ref key, .. }, task) in &self.tasks_by_key {
for (key, task) in &self.tasks_by_key {
if predicate(key) {
task.abort();
}
@@ -635,7 +627,7 @@ where
/// [`join_next`]: fn@Self::join_next
pub fn keys(&self) -> JoinMapKeys<'_, K, V> {
JoinMapKeys {
iter: self.tasks_by_key.keys(),
iter: self.tasks_by_key.iter(),
_value: PhantomData,
}
}
@@ -663,7 +655,7 @@ where
/// [`join_next`]: fn@Self::join_next
/// [task ID]: tokio::task::Id
pub fn contains_task(&self, task: &Id) -> bool {
self.get_by_id(task).is_some()
self.hashes_by_task.contains_key(task)
}
/// Reserves capacity for at least `additional` more tasks to be spawned
@@ -687,7 +679,9 @@ where
/// ```
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.tasks_by_key.reserve(additional);
let hash_builder = self.hashes_by_task.hasher();
self.tasks_by_key
.reserve(additional, |(k, _)| hash_one(hash_builder, k));
self.hashes_by_task.reserve(additional);
}
@@ -713,7 +707,9 @@ where
#[inline]
pub fn shrink_to_fit(&mut self) {
self.hashes_by_task.shrink_to_fit();
self.tasks_by_key.shrink_to_fit();
let hash_builder = self.hashes_by_task.hasher();
self.tasks_by_key
.shrink_to_fit(|(k, _)| hash_one(hash_builder, k));
}
/// Shrinks the capacity of the map with a lower limit. It will drop
@@ -742,27 +738,20 @@ where
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.hashes_by_task.shrink_to(min_capacity);
self.tasks_by_key.shrink_to(min_capacity)
let hash_builder = self.hashes_by_task.hasher();
self.tasks_by_key
.shrink_to(min_capacity, |(k, _)| hash_one(hash_builder, k))
}
/// Look up a task in the map by its key, returning the key and abort handle.
fn get_by_key<'map, Q: ?Sized>(&'map self, key: &Q) -> Option<(&'map Key<K>, &'map AbortHandle)>
fn get_by_key<'map, Q: ?Sized>(&'map self, key: &Q) -> Option<&'map (K, AbortHandle)>
where
Q: Hash + Eq,
K: Borrow<Q>,
{
let hash = self.hash(key);
self.tasks_by_key
.raw_entry()
.from_hash(hash, |k| k.key.borrow() == key)
}
/// Look up a task in the map by its task ID, returning the key and abort handle.
fn get_by_id<'map>(&'map self, id: &Id) -> Option<(&'map Key<K>, &'map AbortHandle)> {
let hash = self.hashes_by_task.get(id)?;
self.tasks_by_key
.raw_entry()
.from_hash(*hash, |k| &k.id == id)
let hash_builder = self.hashes_by_task.hasher();
let hash = hash_one(hash_builder, key);
self.tasks_by_key.find(hash, |(k, _)| k.borrow() == key)
}
/// Remove a task from the map by ID, returning the key for that task.
@@ -773,28 +762,25 @@ where
// Remove the entry for that hash.
let entry = self
.tasks_by_key
.raw_entry_mut()
.from_hash(hash, |k| k.id == id);
let (Key { id: _key_id, key }, handle) = match entry {
RawEntryMut::Occupied(entry) => entry.remove_entry(),
.find_entry(hash, |(_, abort)| abort.id() == id);
let (key, _) = match entry {
Ok(entry) => entry.remove().0,
_ => return None,
};
debug_assert_eq!(_key_id, id);
debug_assert_eq!(id, handle.id());
self.hashes_by_task.remove(&id);
Some(key)
}
}
/// Returns the hash for a given key.
#[inline]
fn hash<Q: ?Sized>(&self, key: &Q) -> u64
where
Q: Hash,
{
let mut hasher = self.tasks_by_key.hasher().build_hasher();
key.hash(&mut hasher);
hasher.finish()
}
/// Returns the hash for a given key.
#[inline]
fn hash_one<S: BuildHasher, Q: ?Sized>(hash_builder: &S, key: &Q) -> u64
where
Q: Hash,
{
let mut hasher = hash_builder.build_hasher();
key.hash(&mut hasher);
hasher.finish()
}
impl<K, V, S> JoinMap<K, V, S>
@@ -828,11 +814,11 @@ impl<K: fmt::Debug, V, S> fmt::Debug for JoinMap<K, V, S> {
// printing the key and task ID pairs, without format the `Key` struct
// itself or the `AbortHandle`, which would just format the task's ID
// again.
struct KeySet<'a, K: fmt::Debug, S>(&'a HashMap<Key<K>, AbortHandle, S>);
impl<K: fmt::Debug, S> fmt::Debug for KeySet<'_, K, S> {
struct KeySet<'a, K: fmt::Debug>(&'a HashTable<(K, AbortHandle)>);
impl<K: fmt::Debug> fmt::Debug for KeySet<'_, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map()
.entries(self.0.keys().map(|Key { key, id }| (key, id)))
.entries(self.0.iter().map(|(key, abort)| (key, abort.id())))
.finish()
}
}
@@ -853,31 +839,10 @@ impl<K, V> Default for JoinMap<K, V> {
}
}
// === impl Key ===
impl<K: Hash> Hash for Key<K> {
// Don't include the task ID in the hash.
#[inline]
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.key.hash(hasher);
}
}
// Because we override `Hash` for this type, we must also override the
// `PartialEq` impl, so that all instances with the same hash are equal.
impl<K: PartialEq> PartialEq for Key<K> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.key == other.key
}
}
impl<K: Eq> Eq for Key<K> {}
/// An iterator over the keys of a [`JoinMap`].
#[derive(Debug, Clone)]
pub struct JoinMapKeys<'a, K, V> {
iter: hashbrown::hash_map::Keys<'a, Key<K>, AbortHandle>,
iter: hashbrown::hash_table::Iter<'a, (K, AbortHandle)>,
/// To make it easier to change `JoinMap` in the future, keep V as a generic
/// parameter.
_value: PhantomData<&'a V>,
@@ -887,7 +852,7 @@ impl<'a, K, V> Iterator for JoinMapKeys<'a, K, V> {
type Item = &'a K;
fn next(&mut self) -> Option<&'a K> {
self.iter.next().map(|key| &key.key)
self.iter.next().map(|(key, _)| key)
}
fn size_hint(&self) -> (usize, Option<usize>) {
+8 -8
View File
@@ -14,7 +14,7 @@ use tokio::task::{spawn_local, JoinHandle, LocalSet};
/// Internally the local pool uses a [`tokio::task::LocalSet`] for each worker thread
/// in the pool. Consequently you can also use [`tokio::task::spawn_local`] (which will
/// execute on the same thread) inside the Future you supply to the various spawn methods
/// of `LocalPoolHandle`,
/// of `LocalPoolHandle`.
///
/// [`tokio::task::LocalSet`]: tokio::task::LocalSet
/// [`tokio::task::spawn_local`]: tokio::task::spawn_local
@@ -39,9 +39,9 @@ use tokio::task::{spawn_local, JoinHandle, LocalSet};
/// task::spawn_local(async move {
/// println!("{}", data_clone);
/// });
///
///
/// data.to_string()
/// }
/// }
/// }).await.unwrap();
/// println!("output: {}", output);
/// }
@@ -194,7 +194,7 @@ enum WorkerChoice {
}
struct LocalPool {
workers: Vec<LocalWorkerHandle>,
workers: Box<[LocalWorkerHandle]>,
}
impl LocalPool {
@@ -249,7 +249,7 @@ impl LocalPool {
// Send the callback to the LocalSet task
if let Err(e) = worker_spawner.send(spawn_task) {
// Propagate the error as a panic in the join handle.
panic!("Failed to send job to worker: {}", e);
panic!("Failed to send job to worker: {e}");
}
// Wait for the task's join handle
@@ -260,7 +260,7 @@ impl LocalPool {
// join handle... We assume something happened to the worker
// and the task was not spawned. Propagate the error as a
// panic in the join handle.
panic!("Worker failed to send join handle: {}", e);
panic!("Worker failed to send join handle: {e}");
}
};
@@ -284,12 +284,12 @@ impl LocalPool {
// No one else should have the join handle, so this is
// unexpected. Forward this error as a panic in the join
// handle.
panic!("spawn_pinned task was canceled: {}", e);
panic!("spawn_pinned task was canceled: {e}");
} else {
// Something unknown happened (not a panic or
// cancellation). Forward this error as a panic in the
// join handle.
panic!("spawn_pinned task failed: {}", e);
panic!("spawn_pinned task failed: {e}");
}
}
}
+2
View File
@@ -53,6 +53,8 @@ use tokio::{
/// `TaskTracker`, this does not happen. Once tasks exit, they are immediately removed from the
/// `TaskTracker`.
///
/// Note that unlike [`JoinSet`], dropping a `TaskTracker` does not abort the tasks.
///
/// # Examples
///
/// For more examples, please see the topic page on [graceful shutdown].
+1 -1
View File
@@ -665,7 +665,7 @@ impl<T> DelayQueue<T> {
// The delay is already expired, store it in the expired queue
self.expired.push(key, &mut self.slab);
}
Err((_, err)) => panic!("invalid deadline; err={:?}", err),
Err((_, err)) => panic!("invalid deadline; err={err:?}"),
}
}
+1 -1
View File
@@ -8,7 +8,7 @@
//!
//! This type must be used from within the context of the `Runtime`.
use futures_core::Future;
use std::future::Future;
use std::time::Duration;
use tokio::time::Timeout;
+2 -81
View File
@@ -39,86 +39,10 @@ const LEVEL_MULT: usize = 64;
impl<T: Stack> Level<T> {
pub(crate) fn new(level: usize) -> Level<T> {
// Rust's derived implementations for arrays require that the value
// contained by the array be `Copy`. So, here we have to manually
// initialize every single slot.
macro_rules! s {
() => {
T::default()
};
}
Level {
level,
occupied: 0,
slot: [
// It does not look like the necessary traits are
// derived for [T; 64].
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
s!(),
],
slot: std::array::from_fn(|_| T::default()),
}
}
@@ -127,10 +51,7 @@ impl<T: Stack> Level<T> {
pub(crate) fn next_expiration(&self, now: u64) -> Option<Expiration> {
// Use the `occupied` bit field to get the index of the next slot that
// needs to be processed.
let slot = match self.next_occupied_slot(now) {
Some(slot) => slot,
None => return None,
};
let slot = self.next_occupied_slot(now)?;
// From the slot index, calculate the `Instant` at which it needs to be
// processed. This value *must* be in the future with respect to `now`.

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