Compare commits

...
Author SHA1 Message Date
Alice Ryhl efdba5fcf0 chore: prepare Tokio v1.52.4 (#8289) 2026-07-16 15:23:48 +02:00
Alice Ryhl b0ba02e755 Merge 'tokio-1.51.4' into 'tokio-1.52.x' (#8288) 2026-07-16 12:18:21 +00:00
Alice Ryhl 7bcd2d343d taskdump: remove crate disambiguators from output (#8288) 2026-07-16 12:17:13 +00:00
Alice Ryhl f84b209126 chore: prepare Tokio v1.51.4 (#8286) 2026-07-16 13:59:24 +02:00
Amey Pawar eacb98e189 runtime: don't skip the driver when before_park schedules work (#8222) 2026-07-16 13:16:47 +02:00
Alice Ryhl d87569164f chore: prepare Tokio v1.52.3 (#8130) 2026-05-08 14:52:32 +02:00
Alice Ryhl e1aebb031c Merge 'tokio-1.51.3' into 'tokio-1.52.x' (#8129) 2026-05-08 09:30:59 +00:00
Alice Ryhl fd63094ee0 chore: prepare Tokio v1.51.3 (#8127) 2026-05-08 10:45:32 +02:00
Alice Ryhl 8c600d0fd2 Merge 'tokio-1.47.5' into 'tokio-1.51.x' (#8123) 2026-05-07 13:59:06 +02:00
Alice Ryhl 11bfc1345b chore: prepare Tokio v1.47.5 (#8122) 2026-05-07 13:55:35 +02:00
Alice Ryhl f085b6211b sync: notify receivers in mpsc OwnedPermit::release() method (#8075) 2026-05-07 09:32:14 +02:00
Alice Ryhl 30d25ccb8b sync: require that an RwLock has max_readers != 0 (#8076) 2026-05-07 09:31:12 +02:00
Alice Ryhl 9fccf5339d sync: return Empty from try_recv() when mpsc is closed with outstanding permits (#8074) 2026-05-07 09:30:50 +02:00
Alice Ryhl ebf61b45b5 sync: fix underflow in mpsc channel len() (#8062) 2026-05-07 09:29:33 +02:00
Alice Ryhl 4abe9d732e chore: prepare Tokio v1.52.2 (#8115) 2026-05-04 14:39:25 +02:00
Alice Ryhl f82bcf3f45 Merge 'tokio-1.51.2' into 'tokio-1.52.x' (#8114) 2026-05-04 12:16:52 +02:00
Alice Ryhl 7db9bc41f1 test: revert "remove churn() task from lifo_stealable" (#8114)
This reverts commit 6c03e03898.
2026-05-04 12:15:37 +02:00
Alice Ryhl 64834ec701 chore: prepare Tokio v1.51.2 (#8113) 2026-05-04 12:11:07 +02:00
Alice Ryhl 967f5715a7 runtime: revert "steal tasks from the LIFO slot" (#8100)
This reverts commit eeb55c733b.
2026-05-04 10:08:30 +02:00
Alice Ryhl 9271e3ed05 Merge tokio-1.51.x (for #8101) into tokio-1.52.x (#8106) 2026-05-02 15:13:06 +02:00
Alice Ryhl cd1823f43e Revert "Pin stable to 1.94 for tokio-1.51.x" (#8106)
As we are merging tokio-1.51.x into tokio-1.52.x and then master, we
should not include this change.

This reverts commit bde3f20b0f.
2026-05-02 15:12:29 +02:00
Alice Ryhl a97cf12ed9 Merge tokio-1.47.x (commit 670a907c55) into tokio-1.51.x (#8105) 2026-05-02 13:52:07 +02:00
Alice Ryhl bde3f20b0f Pin stable to 1.94 for tokio-1.51.x (#8105) 2026-05-02 13:51:31 +02:00
Alice RyhlandMattia Pitossi 670a907c55 ci: fix CI on tokio-1.47.x (#8101)
Co-authored-by: Mattia Pitossi <[email protected]>
2026-05-02 13:33:40 +02:00
Mawer d3565a2923 fs: rename Windows symlink dir test (#8098) 2026-04-30 10:47:45 +02:00
Alice Ryhl 8f81e0814b time: avoid stack overflow in runtime constructor (#8093) 2026-04-29 05:26:57 +00:00
stormshield-fabs 47cc31590f tokio-stream: implement FusedStream for Fuse (#8090) 2026-04-28 17:33:54 +02:00
Alex Gaynor dc0f728162 blocking: introduce a regression test for #8056 (#8068) 2026-04-28 12:47:49 +02:00
Ralf Jung 89713ad1ec miri tests: fstat is supported now (#8088) 2026-04-27 15:23:40 +03:00
Eliza Weisman 6c03e03898 test: remove churn() task from lifo_stealable (#8070)
Currently, the `rt_threaded::lifo_stealable` test I added in #7431
spawns an additional task which sleeps on a 4ms timer in a loop. This
ensures that no worker remains permanently parked. This was added
because it was necessary to stop the LIFO slot deadlock from occurring
prior to changes in the logic for determining whether to notify another
worker, which is what @Darksonn  was referring to in [this comment][1].
Removing the `churn()` test makes the test actually validate that
another worker is notified to steal the LIFO task, and that the changes
from #7431 will *always* prevent a LIFO slot deadlock, regardless of the
behavior of other tasks on the runtime. See also [this comment][2] for
further discussion.

[1]: https://github.com/tokio-rs/tokio/pull/7431#discussion_r2184724657
[2]: https://github.com/tokio-rs/tokio/pull/8069#issuecomment-4274244723
2026-04-20 21:03:42 +02:00
Alice Ryhl 16bc4e2e28 time: add #[track_caller] and panic docs to timeout_at() (#8077) 2026-04-20 20:22:36 +02:00
BarryandMattia Pitossi 1afb391350 net: document pipe try_read*/try_write* readiness behavior (#8032)
Add a Notes section to all five try_* methods on pipe::Sender and
pipe::Receiver explaining that the runtime's I/O driver only delivers
readiness events after control is yielded back to it, so calling
try_read/try_write before any .await returns WouldBlock even when the
operation could otherwise succeed.

This is the same readiness model used by every other Tokio I/O type;
the pipe docs simply did not previously call it out. Refs #7625.

---------

Co-authored-by: Mattia Pitossi <[email protected]>
2026-04-19 19:52:41 +02:00
Mattia Pitossi b010b5ddaf test taskdump docs (#8064) 2026-04-17 23:32:22 +03:00
Eliza Weisman 905c146aed chore: prepare to release v1.52.1 (#8059)
# 1.52.1 (April 16th, 2026)

## Fixed

- runtime: revert [#7757] to fix [a regression][#8056] that causes
  `spawn_blocking` to hang ([#8057])

[#7757]: https://github.com/tokio-rs/tokio/pull/7757
[#8056]: https://github.com/tokio-rs/tokio/pull/8056
[#8057]: https://github.com/tokio-rs/tokio/pull/8057
2026-04-16 21:28:05 +00:00
Eliza Weisman 56aaa43e91 rt: revert #7757 to fix regression in spawn_blocking (#8057)
This reverts commit 1604bc3351.

Unfortunately, this commit introduced a regression that causes programs
using `spawn_blocking` to hang (see #8056). To fix the regression, we
need to undo this change and publish a v1.52.1 release as soon as
possible.

In the future, we may wish to bring back a sharded queue for
`spawn_blocking` tasks, either based on the implementation added in
#7757 or a new one. However, since this is a substantial change to the
runtime internals, I think such a change should probably be done as an
unstable, opt-in `tokio::runtime::Builder` setting initially, so that we
don't regress existing users. I had hoped we could do this now, but
unfortunately, the sharded queue implementation from #7757 is kind of
tightly coupled with the rest of the `spawn_blocking` machinery and
cannot be easily swapped out --- and the hang still occurs with
`NUM_SHARDS` set to 1, so there isn't an easy way to turn it on and off.
Therefore, in the interest of getting a fix out ASAP, this is just a
simple revert.

Fixes #8056
2026-04-16 20:57:22 +00:00
Eliza Weisman 57ff47ab58 ci: update trybuild to expect output from rustc 1.95.0 (#8058) 2026-04-16 20:32:53 +00:00
dependabot[bot] 812de3e134 ci: bump taiki-e/cache-cargo-install-action from 1 to 3 (#8053)
Bumps [taiki-e/cache-cargo-install-action](https://github.com/taiki-e/cache-cargo-install-action) from 1 to 3.
- [Release notes](https://github.com/taiki-e/cache-cargo-install-action/releases)
- [Changelog](https://github.com/taiki-e/cache-cargo-install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/cache-cargo-install-action/compare/v1...v3)

---
updated-dependencies:
- dependency-name: taiki-e/cache-cargo-install-action
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-16 09:30:55 +02:00
Martin Grigorov ba82e73c7b ci: use Dependabot to keep github actions up to date (#8052) 2026-04-16 08:51:22 +02:00
Mattia Pitossi 2e85f9ddf8 ci: replace cirrus-ci with freebsd-vm (#8041) 2026-04-16 08:40:04 +02:00
Alice Ryhl a7e1cd8ff8 ci: update GitHub Actions workflows to use latest tool versions (#8047) 2026-04-15 16:57:43 +00:00
Eliza Weisman 5f7be0ac42 chore: perpare 1.52.0 (#8045)
# 1.52.0 (April 14th, 2026)

## Added

- io: `AioSource::register_borrowed` for I/O safety support ([#7992])
- net: add `try_io` function to `unix::pipe` sender and receiver types
  ([#8030])

## Added (unstable)

- runtime: `Builder::enable_eager_driver_handoff` setting enable eager
  hand off of the I/O and time drivers before polling tasks ([#8010])
- taskdump: add `trace_with()` for customized task dumps ([#8025])
- taskdump: allow `impl FnMut()` in `trace_with` instead of just `fn()`
  ([#8040])
- fs: support `io_uring` in `AsyncRead` for `File` ([#7907])

## Changed

- runtime: improve `spawn_blocking` scalability with sharded queue
  ([#7757])
- runtime: use `compare_exchange_weak()` in worker queue ([#8028])

## Fixed

- runtime: overflow second half of tasks when local queue is filled
  instead of first half ([#8029])

## Documented

- docs: fix typo in `oneshot::Sender::send` docs ([#8026])
- docs: hide #[tokio::main] attribute in the docs of `sync::watch`
  ([#8035])
- net: add docs on `ConnectionRefused` errors with UDP sockets ([#7870])

[#7757]: https://github.com/tokio-rs/tokio/pull/7757
[#7870]: https://github.com/tokio-rs/tokio/pull/7870
[#7907]: https://github.com/tokio-rs/tokio/pull/7907
[#7992]: https://github.com/tokio-rs/tokio/pull/7992
[#8010]: https://github.com/tokio-rs/tokio/pull/8010
[#8025]: https://github.com/tokio-rs/tokio/pull/8025
[#8026]: https://github.com/tokio-rs/tokio/pull/8026
[#8028]: https://github.com/tokio-rs/tokio/pull/8028
[#8029]: https://github.com/tokio-rs/tokio/pull/8029
[#8030]: https://github.com/tokio-rs/tokio/pull/8030
[#8035]: https://github.com/tokio-rs/tokio/pull/8035
[#8040]: https://github.com/tokio-rs/tokio/pull/8040
2026-04-14 11:56:01 -07:00
Alice Ryhl 36d12d2686 taskdump: allow impl FnMut() in taskdumps instead of just fn() (#8040) 2026-04-14 19:18:56 +02:00
Carter Green f943312865 fs: support io-uring in AsyncRead for File (#7907) 2026-04-13 20:36:23 +02:00
Timo 5db10f538b net: add 'try_io' function to 'unix::pipe' sender and receiver types (#8030) 2026-04-12 15:06:06 +02:00
Russell Cohen bbdba7101d taskdump: add trace_with for customized task dumps (#8025)
provided trace function.
2026-04-12 11:13:24 +02:00
xtqqczze 7cfce54386 ci: update FreeBSD image to 14.4 (#8038) 2026-04-11 22:20:55 +02:00
Alice Ryhl 81370e6202 net: add docs on ConnectionRefused errors with udp sockets (#7870) 2026-04-11 20:06:08 +00:00
Alice Ryhl 203af02126 runtime: overflow second half of tasks when local queue is filled instead of first half (#8029) 2026-04-11 12:14:26 +02:00
Joel Dice de230926dc net: temporarily disable tcp_stream try_read_buf test on WASI (#8036)
This test is flaky on WASI until
https://github.com/bytecodealliance/wasmtime/issues/13040 has been addressed.

See https://github.com/tokio-rs/tokio/issues/8034 for additional details.
2026-04-11 12:06:46 +02:00
Abhinav 432ec3f2b2 sync: hide #[tokio::main] attribute in the docs of sync::watch (#8035) 2026-04-10 13:15:37 -07:00
Alex Gaynor 1604bc3351 rt: improve spawn_blocking scalability with sharded queue (#7757) 2026-04-10 16:06:19 +02:00
Alice Ryhl 4c7d8b2a14 runtime: use compare_exchange_weak() in worker queue (#8028) 2026-04-10 09:05:18 +02:00
Alan Somers d7db722bb4 io: AioSource now employs IO Safety (#7992) 2026-04-08 22:59:32 +02:00
Eliza Weisman fccc28f1d0 runtime: optional eager I/O driver/timer handoff when polling tasks (#8010) 2026-04-08 22:46:27 +02:00
winningMove 927df0e9d9 sync: fix typo in oneshot send doc (#8026) 2026-04-08 19:31:22 +02:00
Alice Ryhl 98df02d7a4 chore: prepare Tokio v1.51.1 (#8023) 2026-04-08 12:37:44 +02:00
dentiny 3ea11e2a5f sync: fix semaphore reopens after forget (#8021) 2026-04-08 07:59:54 +00:00
Mattia Pitossi c79121391d rt: do not leak fd when cancelling io_uring open operation (#7983) 2026-04-08 07:37:19 +02:00
Marvin Vogt ad8c59add6 net: surface errors from SO_ERROR on recv for UDP sockets on Linux (#8001) 2026-04-04 21:46:10 +02:00
Alice Ryhl 654d38b132 metrics: fix worker_local_schedule_count test (#8008) 2026-04-04 09:20:01 +02:00
Mattia Pitossi 857ba80933 docs: improve contributing docs on how to specify crates dependency versions (#8009) 2026-04-04 09:03:49 +02:00
Alice Ryhl 95b9342da7 chore: remove path deps for tokio-macros 2.7.0 (#8007) 2026-04-03 11:10:55 +02:00
Alice Ryhl 0af06b7bab chore: prepare Tokio v1.51.0 (#8005) 2026-04-03 10:42:05 +02:00
Alice Ryhl 01a7f1dfab chore: prepare tokio-macros v2.7.0 (#8004) 2026-04-03 09:37:58 +02:00
Eliza Weisman eeb55c733b runtime: steal tasks from the LIFO slot (#7431) 2026-04-03 09:11:58 +02:00
Daksh 1fc450aefb runtime: stabilize LocalRuntime (#7557) 2026-04-02 13:13:24 +00:00
Alice Ryhl 324218f9bb Merge tag 'tokio-1.47.4' (#8003) 2026-04-02 14:16:40 +02:00
Alice Ryhl aa65d0d0b8 chore: prepare Tokio v1.47.4 (#8002) 2026-04-02 14:12:06 +02:00
LeoniePhiline bf18ed452d sync: fix panic in Chan::recv_many when called with non-empty vector on closed channel (#7991)
`Chan::recv_many` intends to assert that no slots
have been consumed when exiting with `Ready` via
the `rx_closed` code path.

Instead of asserting no items were added to the
buffer, it asserted buffer emptiness, incorrectly
making assumptions about the provided buffer.

When `recv_many` was called on an empty channel
with idle semaphore after the receiver was closed,
the method would panic.

The branch coverage had been previously missing.

This changeset corrects the assertion
and adds tests covering the code path.

Fixes #7990.
2026-04-02 13:16:03 +02:00
Joel Dice 43134f1e57 wasm: add wasm32-wasip2 networking support (#7933)
Motivation

This adds networking support for the `wasm32-wasip2` target platform, which
includes more extensive support for sockets than `wasm32-wasip1`.

Solution

The bulk of the changes are in https://github.com/tokio-rs/mio/pull/1931.  This
patch mainly tweaks a few `cfg` directives to indicate `wasm32-wasip2`'s
additional capabilities.

Note that this is a draft PR until until
https://github.com/tokio-rs/mio/pull/1931 and
https://github.com/rust-lang/socket2/pull/639 have been include in stable
releases of their respective projects.

Also note that I've added a `wasm32-wasip2` target to CI and triaged each test
which was previously disabled for WASI into one of three categories:

- Disabled on both WASIp1 and p2 due to not-yet-supported features such as multithreading
- Disabled on p1 but enabled on p2
- Disabled on p1 and _temporarily_ disabled on p2 due to `wasi-libc` bugfixes which have been merged but not yet included in a Rust release.  I'll open an issue to re-enable them when the fixes land in Rust.

Future Work

In the future, we could consider adding support for `tokio::net::lookup_host`.
WASIp2 natively supports asynchronous DNS lookups and is single threaded,
whereas Tokio currently assumes DNS lookups are blocking and require
multithreading to emulate async lookups.  A WASIp2-specific implementation could
do the lookup directly without multithreading.

WASIp2 also supports single-threaded, asynchronous file I/O, timers, etc.  We
could either support those directly or wait for WASIp3's multithreading support,
in which case most of `tokio::fs` (as well as `tokio::net::lookup_host`, etc.)
_should_ work unchanged via `wasi-libc` and worker threads.

Currently, building for WASIp2 requires RUSTFLAGS="--cfg tokio_unstable"`.  Once
we have a solid maintenance plan, we can remove that requirement.
2026-04-02 11:53:17 +02:00
Adam Martinez b4c3246d33 macros: improve overall macro hygiene (#7997)
Multiple macro expansions were previously assumming the existence of
some standard library symbols to both be in-scope and referring to the
right symbol, and not only having the same identifier.

This has now been refactored into using reexports from the `support`
module under the `macros` module. Some other macro invocations were also
using absolute standard library paths instead of calling into the afore
mentioned module through the special `$crate` macro. This has been
changed, thus also reexporting some other items in `support`.
2026-04-02 11:46:57 +02:00
Mattia Pitossi 7947fa4bd7 rt: add runtime name (#7924) 2026-04-01 09:46:27 +00:00
Hegui Dai 9f132172db sync: fix notify_waiters priority in Notify (#7996)
Previously, if a `notify_waiters()` was followed by a `notify_one()`,
an unpolled `Notified` future created before the `notify_waiters()`
call would consume the `NOTIFIED` permit created by the
`notify_one()` call.

This commit fixes this by verifying the `notify_waiters_calls` count
before optimistically attempting to acquire the `NOTIFIED` permit. If
the count indicates a `notify_waiters()` call has already happened, the
future transitions directly to `State::Done` and leaves the permit
intact for other waiters.

Fixes: #7965
2026-03-31 13:52:02 +02:00
Sim-hu 6752f50154 examples: add graceful shutdown example (#7962) 2026-03-30 11:05:53 +02:00
xtqqczze 6c72168a93 ci: remove Rust 1.94.0 workarounds (#7987) 2026-03-30 10:44:47 +02:00
Pino Toscano ee4de81806 net: use null get_peer_cred on Hurd (#7989) 2026-03-27 09:38:54 +01:00
Jess Izen a8435c0fc8 examples: add FD table pre-warming example (#7978) 2026-03-26 08:17:27 -07:00
Charlie Tonneslan 467d614267 bench: use is_multiple_of instead of manual modulo check (#7984)
Addresses clippy::manual_is_multiple_of warning on latest stable.
2026-03-22 14:00:12 +01:00
Martin Grigorov 66c786540e chore: Do not show "Available on non-loom only." doc label (#7977)
* chore: Do not show "Available on non-loom only." doc label

Closes #7976

Uses the unstable #[doc(cfg()]. https://github.com/rust-lang/rust/issues/43781

* Use build.rs to detect nightly builds

* Use `docsrs` instead of `nightly`. Drop build.rs

* Use doc(auto_cfg(hide(config_name)))

* Use same nightly on CirrusCI (FreeBSD) as on Github Actions CI
2026-03-18 15:14:02 +02:00
figsoda dd1196d369 ci: patch workspace members to disambiguate --package (#7967) 2026-03-18 07:27:46 +01:00
figsoda 2db0070eb8 stream: impl FromStream for std::collections::* (#7966) 2026-03-16 16:00:49 +01:00
Russell Cohen 26de3187e7 runtime: add tokio::runtime::worker_index() (#7921) 2026-03-14 22:44:41 -07:00
Chris Denton d3c7f56c10 ci: workaround for OpenOptionsExt in 1.94.0 (#7968) 2026-03-13 10:53:13 +00:00
Alex Gaynor ee04abe797 bench: add remote_spawn benchmark for inject queue contention (#7944) 2026-03-11 10:13:39 +01:00
figsoda aa1e7e2b9d stream: impl FromStream for BTreeSet (#7954) 2026-03-09 10:20:21 +00:00
ADD-SP 961757f548 ci: freeze rustc on 1.93.1 (#7961) 2026-03-09 07:43:48 +01:00
Carl Lerche e67cd87f71 chore: link to tokioconf.com (#7953) 2026-03-06 12:00:42 -08:00
winlogon a502d1b5c8 loom: remove StaticAtomicU64 (#7902) 2026-03-04 16:16:50 +01:00
ADD-SP 0273e45ead chore: prepare Tokio v1.50.0 (#7934) 2026-03-03 10:39:42 +01:00
ADD-SP e3ee4e58dc chore: prepare tokio-macros v2.6.1 (#7943) 2026-03-02 19:17:41 +01:00
Evan Cameron 8c980ea75a io: add write_all_vectored to tokio-util (#7768) 2026-02-27 11:16:43 +01:00
Mattia Pitossi e35fd6d6b7 ci: fix patch during clippy step (#7935)
* fix the ci issue

* fix readme

* fix ci
2026-02-27 09:40:05 +02:00
LIParadise 03fe44c103 runtime: fix event_interval doc (#7932) 2026-02-24 16:30:53 +01:00
ADD-SP d18e5dfbb0 io: fix race in Mock::poll_write (#7882) 2026-02-23 20:49:37 -08:00
ADD-SP f21f2693f0 runtime: fix race condition during the blocking pool shutdown (#7922) 2026-02-23 22:50:27 +01:00
Andrew Lin d81e8f0acb macros: remove (most) local use declarations in tokio::select! (#7929) 2026-02-23 10:46:18 +01:00
Phong Chuong 25e7f2641e rt: fix missing quotation in docs (#7925) 2026-02-20 23:41:12 +02:00
Phong Chuong e1a91ef114 util: fix typo in docs (#7926) 2026-02-20 23:36:27 +02:00
0rlych1kk4 1b11840f53 task: clarify when to use spawn_blocking vs dedicated threads (#7923) 2026-02-20 14:19:27 +00:00
Maxime Grenu 09f92b5aed sync: clarify that recv returns None once closed and no more messages (#7920)
The previous documentation stated:
  'As such, Receiver::poll returns Ok(Ready(None))'

This was misleading: when all Sender handles are dropped, recv does NOT
immediately return None. Buffered messages already in the channel can
still be received. Only after all senders are dropped AND the channel
has been fully drained does recv return None.

Also update the method reference from the internal Receiver::poll to the
public API: Receiver::recv and Receiver::poll_recv.

Closes #6053
2026-02-20 08:34:11 +00:00
Maxime Grenu e65040f061 sync: clarify RwLock fairness documentation (#7919)
The previous wording 'if a task that wishes to acquire the write lock is
at the head of the queue, read locks will not be given out' was
misleading: it implied that readers are only blocked when the writer is
first in the queue. In reality, due to the FIFO ordering, any write
request queued *before* a read request will block that reader.

Replace with a more accurate description: 'a read lock will not be given
out until all write lock requests that were queued before it have been
acquired and released.'

Closes #6901
2026-02-19 15:57:42 +01:00
ADD-SP c23735d43b runtime: fix TOCTOU issue when decreasing num_idle_threads (#7918) 2026-02-17 21:50:26 -08:00
Varun Chawla d83921bc51 runtime: fix double increment of num_idle_threads on shutdown (#7910) 2026-02-16 23:19:50 -08:00
cui 96f64f4ee2 codec: fix is_readable should be buffer empty or not (#7912) 2026-02-16 17:33:35 +01:00
Zen 00d10c22f8 task: fix two typos (#7913) 2026-02-16 10:51:10 +00:00
Zen a25d95a8c4 io: clarify the behavior of AsyncWriteExt::shutdown() (#7908) 2026-02-14 11:42:24 -08:00
Stepan Koltsov 9e7e1ef7ad io: Explain how to flush stdout/stderr (#7904) 2026-02-12 14:06:48 +00:00
Mattia Pitossi 5bcd7c1d09 task: fix task module feature flags in docs (#7891) 2026-02-12 12:27:25 +01:00
winlogon 2486d49778 tests: skip issue_7144 if strace is missing (#7903) 2026-02-12 12:26:20 +01:00
Tim Vilgot Mikael Fredenberg 9dacb1c53e tokio: replace some futures with poll_fn (#7895) 2026-02-09 13:30:31 +01:00
Alex H 8167f87137 task: add AbortOnDrop (#7855) 2026-02-09 13:23:46 +01:00
Mattia Pitossi 159f70bc55 ci: fix ambiguity issue during tokio releases (#7848) 2026-02-09 11:04:59 +01:00
Jack Kleeman d1e4db1018 sync: drop rx waker when oneshot receiver is dropped (#7886) 2026-02-09 10:17:45 +01:00
DaniPopes 530af3f331 runtime: correct the default thread name in docs (#7896) 2026-02-08 03:34:44 -08:00
George Burgess IV d89e998922 net: fix GET_BUF_SIZE constant for target_os = "android" (#7889)
We recently tried to upgrade Android to the newest tokio, but tests that
use these constants fail, since Android is provided the non-Linux
constants.
2026-02-06 15:08:04 +01:00
tsyrulb 306ed1c30b stream: bump minimum tokio version to 1.38 (#7887)
tokio-stream 0.1.18 added `Stream::size_hint` for
`ReceiverStream` and `UnboundedReceiverStream` (PR #7492),
which calls `Receiver::is_closed()` and `Receiver::len()`
(added in tokio 1.37.0) and `Receiver::capacity()` and
`Receiver::max_capacity()` (added in tokio 1.38.0).

The declared minimum of tokio 1.15.0 is no longer sufficient,
causing compilation failures when resolved via
`-Z direct-minimal-versions`.

Refs: #7492
2026-02-06 11:45:30 +01:00
Tim Vilgot Mikael Fredenberg 7e952697e8 signal: specialize windows Registry (#7885) 2026-02-06 11:29:34 +01:00
n4n5 c0943f99f0 rt: clarify the documentation of Runtime::spawn (#7803) 2026-02-04 11:27:46 +01:00
DaniPopes 187a2146a7 runtime: shorten default thread name to fit in Linux limit (#7880)
Linux thread names are truncated at 15 characters.
Currently, Tokio threads show up as "tokio-runtime-w", whereas
shortening "runtime" to "rt" would make the thread name perfectly fit
in the 15 character limit.
2026-02-03 16:23:30 +01:00
Muzzaiyyan Hussain f61374f931 io: fix incorrect and confusing AsyncWrite documentation (#7875) 2026-02-03 16:22:34 +01:00
ADD-SP 0d6c7af3e4 runtime: wake deferred tasks before entering block_in_place (#7879) 2026-02-03 14:07:09 +01:00
Alice Ryhl 9dc9b53ae1 io: implement vectored writes for write_buf (#7871) 2026-01-30 19:52:57 +01:00
Tim Vilgot Mikael Fredenberg c3b31ba2ab signal: guarantee that listeners never return None (#7869) 2026-01-29 19:24:47 -08:00
Alice Ryhl b68ea4156a io: hardcode platform list for poll_write (#7872) 2026-01-29 15:12:10 +01:00
Chinmoy Das 8f6d0864c3 macros: improve error message for return type mismatch in #[tokio::main] (#7856) 2026-01-25 01:09:05 +09:00
mu001999 63abec0525 macros: use call_site hygiene to avoid unused qualification (#7866) 2026-01-22 14:54:43 +01:00
Mattia Pitossi 8fd44d9bdc docs: fix link to tokio::select! (#7867) 2026-01-20 23:05:01 +02:00
Shawn 450fa2d09d fs: add tests for when fs::hard_link fails (#7863) 2026-01-19 12:51:47 +00:00
Andrea Bozzo 8cfa309126 time: add docs about auto-advance and when to use sleep (#7858) 2026-01-19 09:12:37 +01:00
Mattia PitossiandMartin Grigorov bf185b61ff docs: fix broken links of select! (#7860)
Co-authored-by: Martin Grigorov <[email protected]>
2026-01-17 10:10:59 -08:00
Mattia Pitossi 27d1383581 deps: bump tokio to 1.47.0 (#7862) 2026-01-17 10:06:00 -08:00
Alice Ryhl 11f9d4db73 macros: add test for special return types (#7857) 2026-01-15 10:25:45 +01:00
F4RAN 1280cf81de io: always cleanup AsyncFd registration list on deregister (#7773) 2026-01-14 15:02:59 +01:00
vrtgs 67682ac2e8 io: add optimizer hint that memchr returns in-bounds pointer (#7792) 2026-01-14 14:58:33 +01:00
Finn Sheng b88c02c55e time: implement FusedStream for IntervalStream (#7854) 2026-01-14 14:49:03 +01:00
Tim Vilgot Mikael Fredenberg 240cc44da8 signal: remember the result of SetConsoleCtrlHandler (#7833)
The unix implementation remembers whether it failed or not, so the
windows implementation should do so as well. This PR also replaces the
`(Once, AtomicState)` pair with `OnceLock` and tries to remember the
original errno value.
2026-01-14 13:20:41 +01:00
IgorErin 7ed6da6733 runtime: add comments to schedule_option_task_without_yield (#7851) 2026-01-13 10:56:50 -08:00
Tahmid 7a2135f426 runtime: avoid lock acquisition after uring init (#7850) 2026-01-09 17:06:14 -08:00
Alice Ryhl 0913cad381 runtime: revert "avoid lock acquisition after uring init" (#7849)
This reverts commit ff9681b3c6.
2026-01-09 10:15:06 +01:00
Tahmid ff9681b3c6 runtime: avoid lock acquisition after uring init (#7843) 2026-01-08 19:24:29 -08:00
Alice Ryhl f1cb007a28 net: add TcpStream::set_zero_linger (#7837) 2026-01-08 08:18:04 +00:00
Zachary Becker 2d4853ea16 examples: use select! instead of join! in connect_(tcp|udp) examples (#7842) 2026-01-07 22:52:10 -08:00
Marc-Antoine Perennou d65165f7b5 rt: make is_rt_shutdown_err method public (#7771) 2026-01-07 08:26:01 +00:00
Mattia Pitossi 71a1a3da7a tokio: update outdated unstable features section (#7839) 2026-01-07 09:03:12 +01:00
Mattia Pitossi df73fa2188 runtime: panic when event_interval is set to 0 (#7838) 2026-01-06 09:31:56 +00:00
Alice Ryhl 09ad5367b8 runtime: avoid redundant unpark in current_thread scheduler (#7834) 2026-01-05 11:12:31 +01:00
Alice Ryhl 934f68d91c runtime: don't park in current_thread if before_park defers waker (#7835) 2026-01-05 10:13:07 +01:00
Alice Ryhl 41d1877689 chore: prepare tokio-test 0.4.5 (#7831) 2026-01-04 13:53:43 +01:00
Alice Ryhl 60b083b630 chore: prepare tokio-stream 0.1.18 (#7830) 2026-01-04 13:53:30 +01:00
Alice Ryhl 9cc02cc88d chore: prepare tokio-util 0.7.18 (#7829) 2026-01-04 13:53:14 +01:00
Mattia Pitossi d2799d791b task: improve the docs of Builder::spawn_local (#7828) 2026-01-04 19:58:03 +08:00
Stepan Koltsov 4d4870f291 task: doc that task drops before JoinHandle completion (#7825) 2026-01-03 13:41:03 +00:00
Tahmid fdb150901a fs: check for io-uring opcode support (#7815) 2026-01-03 13:39:07 +01:00
Mattia Pitossi 426a562780 rt: remove allow(dead_code) after JoinSet stabilization (#7826) 2026-01-03 11:01:57 +01:00
Alice Ryhl e3b89bbefa chore: prepare Tokio v1.49.0 (#7824) 2026-01-03 10:58:44 +01:00
Alice Ryhl 4f577b84e9 Merge 'tokio-1.47.3' into 'master' 2026-01-02 21:31:26 +01:00
Alice Ryhl f320197693 chore: prepare Tokio v1.47.3 (#7823) 2026-01-02 21:07:41 +01:00
Qi ea6b144cd1 ci: freeze rustc on nightly-2025-01-25 in netlify.toml (#7652)
Signed-off-by: ADD-SP <[email protected]>
2026-01-02 20:49:12 +01:00
Qi 264e703296 Merge tokio-1.43.4 into tokio-1.47.x (#7822) 2026-01-02 20:11:03 +01:00
Qi dfb0f00838 chore: prepare Tokio v1.43.4 (#7821) 2026-01-03 01:52:06 +08:00
Qi 4a91f197b0 ci: fix wasm32-wasip1 tests (#7788)
(cherry picked from commit 1b17a7e241)
2026-01-03 01:32:50 +08:00
Martin Grigorov 601c383ab6 ci: upgrade FreeBSD from 14.2 to 14.3 (#7758)
14.2 is no more available:

```
$ gcloud compute images list --project freebsd-org-cloud-dev --no-standard-images
NAME                                             PROJECT                FAMILY                       DEPRECATED  STATUS
freebsd-13-5-release-amd64-gce                   freebsd-org-cloud-dev  freebsd-13-5                             READY
freebsd-13-5-stable-amd64-v20251030              freebsd-org-cloud-dev  freebsd-13-5-snap                        READY
freebsd-13-5-stable-amd64-v20251107              freebsd-org-cloud-dev  freebsd-13-5-snap                        READY
freebsd-14-3-release-amd64-ufs-gce               freebsd-org-cloud-dev  freebsd-14-3                             READY
freebsd-14-3-stable-amd64-ufs-20251120           freebsd-org-cloud-dev  freebsd-14-3-snap                        READY
freebsd-14-3-stable-amd64-ufs-20251127           freebsd-org-cloud-dev  freebsd-14-3-snap                        READY
freebsd-14-3-stable-amd64-zfs-20251113           freebsd-org-cloud-dev  freebsd-14-3-snap                        READY
freebsd-14-3-stable-amd64-zfs-20251120           freebsd-org-cloud-dev  freebsd-14-3-snap                        READY
freebsd-14-3-stable-amd64-zfs-20251127           freebsd-org-cloud-dev  freebsd-14-3-snap                        READY
freebsd-15-0-release-amd64-ufs                   freebsd-org-cloud-dev  freebsd-15-0-amd64-ufs                   READY
freebsd-15-0-release-amd64-zfs                   freebsd-org-cloud-dev  freebsd-15-0-amd64-zfs                   READY
freebsd-15-0-stable-amd64-ufs-20251120           freebsd-org-cloud-dev  freebsd-15-0-amd64-ufs-snap              READY
freebsd-15-0-stable-amd64-ufs-20251127           freebsd-org-cloud-dev  freebsd-15-0-amd64-ufs-snap              READY
freebsd-15-0-stable-amd64-zfs-20251120           freebsd-org-cloud-dev  freebsd-15-0-amd64-zfs-snap              READY
freebsd-15-0-stable-amd64-zfs-20251127           freebsd-org-cloud-dev  freebsd-15-0-amd64-zfs-snap              READY
freebsd-16-0-current-amd64-ufs-20251110          freebsd-org-cloud-dev  freebsd-16-0-snap                        READY
freebsd-16-0-current-amd64-zfs-20251110          freebsd-org-cloud-dev  freebsd-16-0-snap                        READY
freebsd-16-0-current-arm64-aarch64-ufs-20251111  freebsd-org-cloud-dev  freebsd-16-0-snap                        READY
freebsd-16-0-current-arm64-aarch64-zfs-20251111  freebsd-org-cloud-dev  freebsd-16-0-snap                        READY
```

(cherry picked from commit 5471a5835e)
2026-01-03 01:27:34 +08:00
KR-bluejay 484cb52d8d sync: return TryRecvError::Disconnected from Receiver::try_recv after Receiver::close (#7686)
(cherry picked from commit d060401f6c)
2026-01-03 01:27:28 +08:00
Mattia Pitossi 16f20c34ed rt: mention LocalRuntime in new_current_thread docs (#7820) 2026-01-02 16:45:51 +01:00
Tim Vilgot Mikael Fredenberg 46674789ab signal: optimize unix signal storage to skip zero (#7819) 2026-01-02 14:24:55 +00:00
Tim Vilgot Mikael Fredenberg 8f4ebfd2f7 signal: specialize unix OsStorage (#7818) 2026-01-01 22:18:29 +01:00
Aman Gupta bd3940c14f docs: fix typos in bounded.rs and park.rs (#7817) 2026-01-01 19:22:17 +01:00
Mattia Pitossi c27bed36ac examples: improve the style of chat.rs (#7812) 2025-12-31 22:19:43 +08:00
Stepan Koltsov 6d3feb581f task: Better typed RawTask::try_read_output (#7806) 2025-12-30 16:53:20 +02:00
n4n5 a708ad19cb chore: fix minor typos (#7804) 2025-12-30 16:09:38 +09:00
Andrea Bozzo 4bc2a15d28 io: add SyncIoBridge cross-references to copy and copy_buf (#7798) 2025-12-29 14:29:56 +00:00
Joe Thomas 33566434bb metrics: clarify that num_alive_tasks is not strongly consistent (#7614) 2025-12-29 21:45:58 +08:00
Jan TojnarandThomas de Zeeuw 7388f2d2ea net: add support for TCLASS option on IPv6 (#7781)
Co-authored-by: Thomas de Zeeuw <[email protected]>
2025-12-29 12:38:26 +08:00
Tim Vilgot Mikael Fredenberg 0a3e386269 time: improve the readability of alternative timer (#7801) 2025-12-28 19:01:05 +08:00
vrtgs d666068be7 fs: handle EINTR in fs::write for io-uring (#7786) 2025-12-24 23:18:25 +08:00
Qi 1b17a7e241 ci: fix wasm32-wasip1 tests (#7788) 2025-12-24 10:07:28 +08:00
xibeiyoumian 5b91709edf chore: fix some minor typos in the comments (#7785)
Signed-off-by: xibeiyoumian <[email protected]>
2025-12-23 20:24:13 +08:00
Aaron Chen 08f40652aa macros: remove extern crate proc_macro (#7783) 2025-12-21 18:42:14 +08:00
Clara Engler 6403b5370e readme: remove TokioConf 2026 CFP announcement (#7774) 2025-12-20 10:09:00 +01:00
Qi 064181f386 io: add tokio_util::io::simplex (#7565)
Signed-off-by: ADD-SP <[email protected]>
2025-12-18 20:35:01 +08:00
QiandAlice Ryhl 009a2567d0 sync: clarify the cancellation safety of oneshot::Receiver (#7780)
Signed-off-by: ADD-SP <[email protected]>
Co-authored-by: Alice Ryhl <[email protected]>
2025-12-18 19:21:59 +08:00
Qi 231a3a69f9 task: stabilize the LocalSet::id() (#7776)
Signed-off-by: ADD-SP <[email protected]>
2025-12-16 19:02:09 +08:00
Chinedu Francis Nwafili 0fa7755e97 runtime: stabilize runtime::id::Id (#7125) 2025-12-16 00:29:10 +08:00
Clara Engler d3fe35593d net: clarify the cancellation safety of the TcpStream::peek (#7305) 2025-12-15 10:02:13 +08:00
Owen GriffithsandQi 0ec0a85461 io: document the default capacity of the ReaderStream (#7147)
Signed-off-by: ADD-SP <[email protected]>
Co-authored-by: Qi <[email protected]>
2025-12-12 23:22:17 +08:00
Qi 97d06ae1a6 macros: fix the hygiene issue of join! and try_join! (#7766)
Signed-off-by: ADD-SP <[email protected]>
2025-12-08 18:39:29 +08:00
Mattia Pitossi b5054e1dff docs: break up CONTRIBUTING.md into several parts (#7762) 2025-12-07 19:10:54 +08:00
Daksh 398eef8120 fs: support io_uring with tokio::fs::read (#7696) 2025-12-05 15:57:32 +08:00
Tethys Svensson c8116ecd7b stream: work around the rustc bug in StreamExt::collect (#7754) 2025-12-05 10:24:29 +08:00
Martin Grigorov 5471a5835e ci: upgrade FreeBSD from 14.2 to 14.3 (#7758)
14.2 is no more available:

```
$ gcloud compute images list --project freebsd-org-cloud-dev --no-standard-images
NAME                                             PROJECT                FAMILY                       DEPRECATED  STATUS
freebsd-13-5-release-amd64-gce                   freebsd-org-cloud-dev  freebsd-13-5                             READY
freebsd-13-5-stable-amd64-v20251030              freebsd-org-cloud-dev  freebsd-13-5-snap                        READY
freebsd-13-5-stable-amd64-v20251107              freebsd-org-cloud-dev  freebsd-13-5-snap                        READY
freebsd-14-3-release-amd64-ufs-gce               freebsd-org-cloud-dev  freebsd-14-3                             READY
freebsd-14-3-stable-amd64-ufs-20251120           freebsd-org-cloud-dev  freebsd-14-3-snap                        READY
freebsd-14-3-stable-amd64-ufs-20251127           freebsd-org-cloud-dev  freebsd-14-3-snap                        READY
freebsd-14-3-stable-amd64-zfs-20251113           freebsd-org-cloud-dev  freebsd-14-3-snap                        READY
freebsd-14-3-stable-amd64-zfs-20251120           freebsd-org-cloud-dev  freebsd-14-3-snap                        READY
freebsd-14-3-stable-amd64-zfs-20251127           freebsd-org-cloud-dev  freebsd-14-3-snap                        READY
freebsd-15-0-release-amd64-ufs                   freebsd-org-cloud-dev  freebsd-15-0-amd64-ufs                   READY
freebsd-15-0-release-amd64-zfs                   freebsd-org-cloud-dev  freebsd-15-0-amd64-zfs                   READY
freebsd-15-0-stable-amd64-ufs-20251120           freebsd-org-cloud-dev  freebsd-15-0-amd64-ufs-snap              READY
freebsd-15-0-stable-amd64-ufs-20251127           freebsd-org-cloud-dev  freebsd-15-0-amd64-ufs-snap              READY
freebsd-15-0-stable-amd64-zfs-20251120           freebsd-org-cloud-dev  freebsd-15-0-amd64-zfs-snap              READY
freebsd-15-0-stable-amd64-zfs-20251127           freebsd-org-cloud-dev  freebsd-15-0-amd64-zfs-snap              READY
freebsd-16-0-current-amd64-ufs-20251110          freebsd-org-cloud-dev  freebsd-16-0-snap                        READY
freebsd-16-0-current-amd64-zfs-20251110          freebsd-org-cloud-dev  freebsd-16-0-snap                        READY
freebsd-16-0-current-arm64-aarch64-ufs-20251111  freebsd-org-cloud-dev  freebsd-16-0-snap                        READY
freebsd-16-0-current-arm64-aarch64-zfs-20251111  freebsd-org-cloud-dev  freebsd-16-0-snap                        READY
```
2025-12-04 16:52:29 +08:00
Alex Gaynor be99e7aa04 benches: add spawn_blocking concurrency benchmark (#7748) 2025-12-03 10:20:50 +01:00
Alice Ryhl 3bf2e53f0b net: deprecate {TcpStream,TcpSocket}::set_linger (#7752) 2025-12-02 13:15:48 +01:00
Mattia Pitossi ab3996a6dd time: update outdated docs of Wheel (#7749) 2025-11-29 18:06:33 +08:00
Ralf Jung c03a37fa0b tokio: enable more tests in Miri (#7734) 2025-11-29 15:08:47 +08:00
Qi 73d733a341 time: add alternative timer for better multicore scalability (#7467)
This change introduces per-worker timer wheels in the time subsystem
to reduce the lock contention.

Key changes:
- Each worker now maintains a local timer wheel.
- Timer insertions are performed locally.
- Timer cancellations are forwarded via a
  dedicated cross-worker cancellation queue.

Relevant RFC: https://github.com/tokio-rs/tokio/issues/7384

---------

Signed-off-by: ADD-SP <[email protected]>
2025-11-27 09:29:28 +08:00
Elichai Turkel 749322d351 task: implement Extend for JoinSet (#7195) 2025-11-25 11:31:21 +01:00
jinronga 963b631754 refactor: introduce constants for default addresses and improve error handling in TCP examples (#7741)
- Added `DEFAULT_ADDR` constant to `chat.rs` and `echo-tcp.rs` for better maintainability.
- Enhanced error logging in `connect-tcp.rs` and `echo-tcp.rs` to include connection addresses.
- Improved peer management in `chat.rs` by automatically cleaning up disconnected peers.
2025-11-25 08:44:41 +02:00
Paolo Barbolini 9a1b076c00 io: replace Result<T, io::Error> with io::Result<T> in AsyncWrite (#7740) 2025-11-23 17:26:48 +08:00
Mohamed Macow c434ed7865 net: clarify the drop behavior of unix::OwnedWriteHalf (#7742) 2025-11-23 17:18:24 +08:00
Seaker 4714ca168d net: clarify the platform-dependent backlog in TcpSocket docs (#7738) 2025-11-16 18:44:35 +08:00
Mattia Pitossi 5e3ad02fb1 sync: fix a typo in the docs of PollSender::is_closed (#7737) 2025-11-15 18:43:02 +08:00
Qi 12412afea4 deps: bump tokio to 1.44.0 (#7733) 2025-11-13 11:47:34 +02:00
Mattia Pitossi cae083a26f docs: fix typos in README (#7731) 2025-11-12 12:33:24 +01:00
Carl Lerche fd7a8d7c65 chore: add TokioConf 2026 CFP announcement (#7730)
* chore: add TokioConf 2026 CFP announcement

* sync readmes
2025-11-12 08:53:40 +02:00
Qi d709df2571 ci: bump miri to nightly-2025-11-09 (#7726) 2025-11-09 18:53:16 +02:00
Qi 665f08b5ad tokio: enable the unsafe_op_in_unsafe_fn lint at the crate level (#7711)
Signed-off-by: ADD-SP <[email protected]>
2025-11-09 12:35:08 +01:00
Mattia Pitossi d4641ba9fc util: use <ptr>::addr instead of unsafe impl (#7725) 2025-11-08 23:49:09 +01:00
Motoyuki Kimura 2bf80f0ac6 runtime: disable io-uring on EPERM (#7724) 2025-11-08 17:13:12 +08:00
Muhamad Awad 62ecff895a stream: add ChunksTimeout::into_remainder (#7715) 2025-11-06 20:15:10 +08:00
Benjamin RanandBenjamin Ran d84a9e9af3 util: enable loom tests (#7644)
Co-authored-by: Benjamin Ran <[email protected]>
2025-11-06 19:20:08 +08:00
Ari Seyhun 0671c205cc sync: improve the docs for the errors of mpsc (#7722)
* docs: fix documentation comments for mpsc error enums

* docs: improve code docs for `TryRecvError`

* docs: improve code docs for mpsc `SendError`
2025-11-05 12:45:13 +00:00
Qi 1ece2f1fa7 task: remove unnecessary trait bounds on the Debug implementation (#7720)
Remove the trait bounds of the `Debug` impl for `JoinQueue`
and `AbortOnDropHandle`.

Signed-off-by: ADD-SP <[email protected]>
2025-11-04 11:15:29 +01:00
Ari Seyhun 12319f26d0 sync: add missing period to mpsc::Sender::try_send docs (#7721) 2025-11-03 21:20:25 +08:00
Qi 454fd8c347 chore: prepare tokio-util v0.7.17 (#7719)
Signed-off-by: ADD-SP <[email protected]>
2025-11-02 15:33:25 +01:00
Conrad Ludgate 4421022c25 codec: remove unnecessary trait bounds on all Framed constructors (#7716) 2025-10-29 20:08:13 +08:00
Daksh 5a709e391b io_uring: change Completable to not return io::Result (#7702) 2025-10-24 22:17:17 +02:00
Alice Ryhl 5efb1c3b16 io: doc that AsyncWrite does not inherit from Write (#7705) 2025-10-23 12:07:02 +02:00
Alice Ryhl f490029b8f runtime: revert "replace manual vtable definitions with Wake" (#7699)
This reverts commit 4380de9fe9.
2025-10-21 12:59:05 +02:00
Mattia Pitossi d25778f67d task: add tests for task::Builder::spawn_local (#7697) 2025-10-20 20:43:41 +08:00
Qi b8318fa172 task: add tests for spawn_local in panic scenarios (#7694)
Signed-off-by: ADD-SP <[email protected]>
2025-10-20 20:25:00 +08:00
Alice Ryhl acfdb87e2b task: use #[tokio::test] explicitly in tests/task_builder.rs (#7698) 2025-10-20 11:15:04 +00:00
KR-bluejay d060401f6c sync: return TryRecvError::Disconnected from Receiver::try_recv after Receiver::close (#7686) 2025-10-18 12:57:07 +02:00
FrancescoV1985 5dacc2e2a8 task: add tests for spawn_local and spawn_local_on (#7609)
Add tests for task collections (TaskTracker, JoinSet, JoinMap).
2025-10-16 23:44:57 +08:00
Mattia Pitossi 444d3f5c49 task: add example for spawn_local usage on local runtime (#7689) 2025-10-16 23:13:16 +08:00
Mattia Pitossi d23a838732 runtime: add tests for spawn local on multi and current runtimes (#7687) 2025-10-15 22:09:46 +03:00
Alice Ryhl 2137f7d953 process: remove obsolete allow(deprecated) from is_rt_shutdown_err (#7685) 2025-10-15 14:17:56 +02:00
Alice Ryhl 51e9dc0943 Merge 'tokio-1.47.2' into 'master' (#7683) 2025-10-14 20:30:15 +02:00
Qi 3762a6a990 chore: prepare Tokio v1.47.2 (#7681)
Signed-off-by: ADD-SP <[email protected]>
2025-10-15 01:12:04 +08:00
Jess Izen 07f6cc7e1d macros: fix the hygiene issue of join! and try_join! (#7638)
(cherry picked from commit eb99e476e6)
2025-10-15 00:29:11 +08:00
Qi 308e3e6871 ci: add lockfile for LTS branch
This is to lock the following dependencies:

- `parking_lot_core` to `0.9.11`
- `parking_lot` to `0.12.4`
- `lock_api` to `0.4.13`

See <https://github.com/tokio-rs/tokio/issues/7653>
for more details.

Signed-off-by: ADD-SP <[email protected]>
2025-10-15 00:12:53 +08:00
Qi 5a1879c2d0 Merge 'tokio-1.43.3' into 'tokio-1.47.x' 2025-10-15 00:10:36 +08:00
Qi de6ef21a81 chore: prepare Tokio v1.43.3
Signed-off-by: ADD-SP <[email protected]>
2025-10-14 22:54:10 +08:00
Qi 90551d234f deps: bump the locked slap to 0.4.11
Signed-off-by: ADD-SP <[email protected]>
2025-10-14 22:54:09 +08:00
Qi bd4c3dddca deps: bump the locked tracing-subscriber to 0.3.20
Signed-off-by: ADD-SP <[email protected]>
2025-10-14 22:54:09 +08:00
Alice Ryhl 49b331855e process: fix error when runtime is shut down on nightly-2025-10-12 (#7672)
(cherry picked from commit 9e5527d1d5)
2025-10-14 22:13:55 +08:00
Martin Grigorov da292dfb66 sync: close the broadcast::Sender in broadcast::Sender::new() (#7629)
(cherry picked from commit 6d1ae62868)
2025-10-14 22:13:55 +08:00
Adam Ning b9feac8d68 runtime: use release in wake_by_ref() even if already woken (#7622)
(cherry picked from commit 67869be3d7)
2025-10-14 22:13:54 +08:00
Qi 4fee6e3500 ci: update macros_type_mismatch for Rust 1.90.0 (#7630)
Signed-off-by: ADD-SP <[email protected]>
(cherry picked from commit 2af3e4430a)
2025-10-14 22:13:54 +08:00
Motoyuki Kimura b1e69e560c ci: pin the rust version for wasm tests (#7518)
(cherry picked from commit 987675e843)
2025-10-14 22:13:54 +08:00
Alice Ryhl 556820ff84 chore: prepare Tokio v1.48.0 (#7677) 2025-10-14 15:07:25 +02:00
Alice Ryhl fd1659a052 chore: prepare tokio-macros v2.6.0 (#7676) 2025-10-14 14:24:54 +02:00
Alice Ryhl 53e8acac64 ci: update nightly version to 2025-10-12 (#7670) 2025-10-14 12:56:46 +02:00
Alice Ryhl 9e5527d1d5 process: fix error when runtime is shut down on nightly-2025-10-12 (#7672) 2025-10-14 12:56:41 +02:00
Sean McArthur 25a24de0e6 net: remove PollEvented noise from Debug formats (#7675) 2025-10-13 21:02:37 +00:00
Mattia Pitossi c1fa25f300 task: clarify the behavior of several spawn_local methods (#7669) 2025-10-12 10:46:23 +08:00
Denis Davydov e7e02fcf0f fs: use FileOptions inside fs::File to support uring (#7617) 2025-10-10 17:06:13 +02:00
tottoto f7a7f62959 ci: remove cargo-deny Unicode-DFS-2016 license exception config (#7619) 2025-10-10 16:55:48 +02:00
QiandAlice Ryhl d1f1499f63 tokio: use cargo feature for taskdump support instead of cfg (#7655)
Signed-off-by: ADD-SP <[email protected]>
Co-authored-by: Alice Ryhl <[email protected]>
2025-10-10 11:31:41 +08:00
Samuele ad6f618952 runtime: clarify the behavior of Handle::block_on (#7665) 2025-10-10 10:01:17 +08:00
tison 0f9ae13c31 task: add LocalKey::try_get (#7666)
Signed-off-by: tison <[email protected]>
2025-10-09 10:19:27 +02:00
whollins 9255d96b1b deps: bump windows-sys to version 0.61 (#7645) 2025-10-07 10:40:42 +02:00
Qi ffcc9f7c95 tokio: fix the docs of feature flag (#7663)
Signed-off-by: ADD-SP <[email protected]>
2025-10-06 19:34:56 +08:00
Mattia 1a4cf319b5 sync: improve the docs of UnboundedSender::send (#7661) 2025-10-05 15:14:38 +08:00
Motoyuki Kimura 3698a6f153 fs: support io_uring in fs::write (#7567) 2025-10-02 11:01:18 +00:00
Alice Ryhl 5b4cbbc39e tokio: raise MSRV to 1.71 (#7658) 2025-10-02 11:14:17 +02:00
Ruiyang Sun c1f0c76fa0 macros: suppress clippy::unwrap_in_result in #[tokio::main] (#7651) 2025-10-02 16:53:25 +08:00
Tudyx d0953e833d task: simplify the example of TaskTracker (#7657) 2025-10-02 15:07:28 +08:00
Qi b157f5da76 runtime: add guide for choosing between runtime types (#7635)
Signed-off-by: ADD-SP <[email protected]>
2025-10-01 22:00:46 +08:00
Xinye TaoandXinye Tao 35470bfc6e sync: clarify bounded channel panic behavior (#7641)
Signed-off-by: Xinye Tao <[email protected]>
Co-authored-by: Xinye Tao <[email protected]>
2025-10-01 10:38:04 +02:00
Alice Ryhl 95edd8515e docs: fix some docs links (#7654) 2025-09-30 12:35:46 +00:00
Qi a0f7f5c94a fs: emit compilation error without tokio_unstable for io-uring (#7634)
Signed-off-by: ADD-SP <[email protected]>
2025-09-30 19:53:19 +08:00
Qi 02486978d1 ci: freeze rustc on nightly-2025-01-25 in netlify.toml (#7652)
Signed-off-by: ADD-SP <[email protected]>
2025-09-29 23:51:20 +08:00
Lucas Black 8ccf2fb92e ci: unfreeze wasm tests from rustc 1.88.0 (#7537) 2025-09-26 21:29:01 +08:00
Nikolai Kuklin bce76c515f task: add try_join_next and try_join_next_with_id on JoinQueue (#7636) 2025-09-25 20:31:31 +08:00
Martin Grigorov b48586f560 tokio: fix typos in tokio/CHANGELOG.md (#7643) 2025-09-23 22:12:41 +08:00
Jess Izen eb99e476e6 macros: fix the hygiene issue of join! and try_join! (#7638) 2025-09-21 13:47:22 +08:00
Daniel Sharifi b9b532485b sync: clarify the behavior of tokio::sync::watch::Receiver (#7584) 2025-09-20 22:02:56 +08:00
Nikolai Kuklin 1b98d5ad85 task: add tokio_util::task::JoinQueue (#7590) 2025-09-20 15:38:59 +08:00
Martin Grigorov 6d1ae62868 sync: close the broadcast::Sender in broadcast::Sender::new() (#7629) 2025-09-20 14:44:55 +08:00
Motoyuki KimuraandEmile Fugulin 3b5a15dfdf fs: use the Cargo feature for io-uring support instead of cfg (#7621)
Co-authored-by: Emile Fugulin <[email protected]>
2025-09-19 23:22:44 +08:00
Qi 2af3e4430a ci: update macros_type_mismatch for Rust 1.90.0 (#7630)
Signed-off-by: ADD-SP <[email protected]>
2025-09-19 16:11:21 +02:00
Adam Ning 67869be3d7 runtime: use release in wake_by_ref() even if already woken (#7622) 2025-09-18 13:41:23 +02:00
Vaibhav Gupta c6b16cc861 net: clarify the supported platform of set_reuseport() and reuseport() (#7628) 2025-09-17 21:10:10 +08:00
Martin Grigorov 7c197c7784 runtime: clarify the edge case of Builder::global_queue_interval() (#7605) 2025-09-16 21:41:18 +08:00
Martin GrigorovandAlice Ryhl 5f3f5b0be4 sync: improve the docs of sync::watch (#7601)
Co-authored-by: Alice Ryhl <[email protected]>
2025-09-15 19:56:29 +08:00
tottoto 32a1acc85f examples: bump http crate from 0.2 to 1 (#7618) 2025-09-15 18:22:57 +08:00
Sean Oxley 7f455b2d93 task: clarify the task ID reuse guarantees (#7577) 2025-09-15 18:18:16 +08:00
Martin Grigorov 86de2e306b util: fix pending_only_on_first_poll_with_cancellation_token_owned_test to use an owned cancellation token (#7613)
The name of the test suggests that it should test the
with_cancellation_token_owned() extension method
2025-09-15 16:22:42 +09:00
Martin Grigorov 6dc4f85f0b net: clarify the behavior of UCred::pid() on Cygwin (#7611) 2025-09-15 00:05:05 +08:00
Martin Grigorov 637fc1d103 tokio: fix minor errors in tokio/CHANGELOG.md (#7608) 2025-09-11 10:02:34 +00:00
Martin Grigorov 7a0ca807be time: add #[track_caller] to FutureExt::timeout (#7588)
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
2025-09-11 09:18:40 +02:00
Martin Grigorov d8e8037de5 net: fix copy/paste errors in udp peek methods (#7604) 2025-09-10 16:33:35 +02:00
Martin Grigorov de978c47e0 task: remove duplicated code in JoinMap::remove_by_id (#7603) 2025-09-10 22:16:15 +08:00
Qi 024bd60933 task: improve the example of poll_proceed (#7586)
Signed-off-by: ADD-SP <[email protected]>
2025-09-10 20:57:03 +08:00
Aatif Syed 7127e257a7 io: export Chain of AsyncReadExt::chain (#7599) 2025-09-10 10:45:42 +02:00
Martin Grigorov 94b6df699b fs: fill the destination buffer with 0s for MockFile::read() (#7596) 2025-09-10 10:41:20 +02:00
Martin Grigorov 510b9ea9dc macros: Update the version used for Git tag sample for release steps (#7598)
Change 1.x.y to x.y.z so that it does not become obsolete again when
tokio-macros 3.x is released
2025-09-09 15:52:58 +02:00
Martin Grigorov 0fc23971d9 macros: add missing local flavor to tokio::main error message (#7597) 2025-09-09 21:26:16 +08:00
unvalley f07233f742 test: add tests for time in wasm32-unknown-unknown (#7510) 2025-09-09 12:31:14 +02:00
Martin Grigorov 8efd04e382 sync: reword allocation failure paragraph in broadcast docs (#7595) 2025-09-09 09:05:14 +00:00
Martin Grigorov 044eaa1a41 fs: preserve max_buf_size when cloning a File (#7593) 2025-09-09 08:34:35 +00:00
Martin Grigorov 03bb6e29b1 fs: add File::max_buf_size (#7594) 2025-09-09 08:12:07 +00:00
Martin Grigorov ac4c95972e sync: fix implementation of unused RwLock::try_* methods (#7587)
bd4ccae184 introduced a wrapper for the
RwLock to get rid of poisoning aspects.

By mistake (?!) its try_read/write methods actually delegate to
read/write() and this would lead to blocking

Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
2025-09-09 09:39:01 +02:00
Martin Grigorov 14e739c306 readme: fix the version used as an example how to use the latest minor of LTS (#7592) 2025-09-09 09:37:47 +02:00
Qi 9f59c6952e ci: remove the job test-pass (#7575)
Signed-off-by: ADD-SP <[email protected]>
2025-09-08 12:07:40 +02:00
Martin Grigorov 86400a1920 io: fix typos in the docs of AsyncFd readiness guards (#7583)
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
2025-09-06 15:58:41 +08:00
Martin Grigorov c9b4e4c110 examples: fix the write length in the connect-tcp example (#7581)
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
2025-09-06 15:49:07 +08:00
Martin Grigorov 3eb515e1a7 examples: update outdated example name connect to connect-tcp (#7582)
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
2025-09-06 14:01:40 +08:00
Martin Grigorov 1ed2a1436f process: fix unit test for trailing LF in uname -r (#7579)
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
2025-09-06 13:52:16 +08:00
Martin Grigorov c6aceed643 io: clarify the zero capacity case of AsyncRead::poll_read (#7580)
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
2025-09-06 13:42:35 +08:00
Martin Grigorov 4590828fb4 stream: improve the the docs of TcpListenerStream (#7578)
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
2025-09-06 13:36:41 +08:00
Roman a99a351802 sync: use UnsafeCell::get_mut in Mutex::get_mut and RwLock::get_mut (#7569) 2025-09-04 22:39:28 +08:00
Aatif Syed d1e06f831e net: implement AsRef<Self> for TcpStream and UnixStream (#7573) 2025-09-04 10:31:49 +00:00
Sam 37ca2f049c sync: remove inner mutex in SetOnce (#7554) 2025-09-03 17:37:50 +02:00
Varun Doshi c8371d45bc codec: add {FramedRead,FramedWrite}::into_parts() (#7566) 2025-09-03 13:38:51 +02:00
Daniel Sharifi adc3e19ba7 time: clarify the cancellation safety of the DelayQueue (#7564) 2025-08-31 20:57:17 +08:00
Alex Bakon 925c614c89 time: reduce the generated code size of Timeout<T>::poll (#7535) 2025-08-19 13:42:55 +02:00
Asger Hautop Drewsen dd74c7c1bf task: implement Ord for task::Id (#7530) 2025-08-15 09:52:12 +02:00
Logan Praneis 23263231fd net: qualify that SO_REUSEADDR is only set on Unix (#7533) 2025-08-14 09:25:55 +08:00
CrazyFrog 86528741f9 ci: update GitHub actions/checkout to v5 (#7529) 2025-08-13 12:57:45 +02:00
Noam Soloveichik 131afd3c53 net: clarify socket gets closed on drop (#7526) 2025-08-11 16:26:01 +00:00
Qi 9ed6f70b81 ci: remove a typo from spellcheck.dic (#7524)
Signed-off-by: ADD-SP <[email protected]>
2025-08-10 20:31:09 +08:00
mxsm 46f7d87962 runtime: fix a typo in comment of MAX_LIFO_POLLS_PER_TICK (#7520) 2025-08-09 22:29:43 +08:00
Motoyuki Kimura 987675e843 ci: pin the rust version for wasm tests (#7518) 2025-08-08 10:09:31 +00:00
Qi 11d7c0486a task: inline the docs of TaskTracker while re-exporting it (#7516)
Signed-off-by: ADD-SP <[email protected]>
2025-08-08 09:54:25 +02:00
Motoyuki Kimura 3e84a198e4 fs: add io_uring open operation (#7321) 2025-08-08 09:51:42 +02:00
Qi 7497561fed net: render the cygwin in the docs of quickack and set_quickack (#7515)
Signed-off-by: ADD-SP <[email protected]>
2025-08-06 09:01:27 +08:00
Luca Bruno ef5b6af7f6 future: clarify the fairness of FutureExt for cancellation adapters (#7512)
This fixes the docstrings on `FutureExt` so that the bias and fairness
notes are correct and consistent in all cases.
All cancellation-related wrappers are biased towards the completion of
the inner future, but they do initially check if the token is
already cancelled at construction time.
2025-08-04 21:26:11 +08:00
Motoyuki Kimura 0922aa2a0b ci: fix clippy warnings triggered under specific cfg (#7495) 2025-08-04 10:36:17 +00:00
Michael Zhao 2403b91d75 process: upgrade Command::spawn_with to use FnOnce (#7511) 2025-08-03 18:27:37 +00:00
Conrad Ludgate f1d3b065b6 task: fix flaky joinmap test during abort (#7509) 2025-08-03 20:00:33 +02:00
Alice Ryhl cf6b50a3fd chore: prepare tokio-util v0.7.16 (#7507) 2025-08-03 11:12:41 +02:00
Conrad Ludgate 416e36b0df task: stabilise JoinMap (#7075) 2025-08-03 07:58:28 +00:00
Alice Ryhl 9741c90f9f sync: document cancel safety on SetOnce::wait (#7506) 2025-08-03 07:42:30 +00:00
Lucas Black 4e3f17bce3 codec: also apply capacity to read buffer in Framed::with_capacity (#7500) 2025-08-01 21:26:33 +02:00
Alice Ryhl f020b5c4b5 ci: fix incorrect tokio version in Cargo.lock 2025-08-01 13:26:54 +02:00
Alice Ryhl 86cbf81e15 Merge 'tokio-1.47.1' into 'master' 2025-08-01 13:21:45 +02:00
Alice Ryhl be8ee45b3f chore: prepare Tokio v1.47.1 (#7504) 2025-08-01 13:20:34 +02:00
Alice Ryhl d9b19166cd Merge 'tokio-1.43.2' into 'tokio-1.47.x' (#7503) 2025-08-01 13:03:06 +02:00
Alice Ryhl db8edc620f chore: prepare Tokio v1.43.2 (#7502) 2025-08-01 12:57:50 +02:00
Alice Ryhl e47565b086 blocking: clarify that spawn_blocking is aborted if not yet started (#7501) 2025-08-01 10:32:37 +00:00
Alice Ryhl 4730984d66 readme: add 1.47 as LTS release (#7497)
(cherry picked from commit ad2e19ffe1)
2025-08-01 11:34:35 +02:00
Alice Ryhl 1979615cbf process: fix panic from spurious pidfd wakeup (#7494) 2025-08-01 09:27:15 +00:00
Lucas Black 1bc50825f3 codec: add FramedWrite::with_capacity (#7493) 2025-08-01 11:23:22 +02:00
Alice Ryhl f669a609cf ci: add lockfile for LTS branch
This is to fix CI failures from backtrace in rustdoc jobs.
2025-08-01 11:07:17 +02:00
Alice Ryhl ad2e19ffe1 readme: add 1.47 as LTS release (#7497) 2025-07-31 16:45:10 +02:00
Qi 5f04d14d81 net: add TcpStream::quickack and TcpStream::set_quickack (#7490)
Signed-off-by: ADD-SP <[email protected]>
2025-07-31 19:58:01 +08:00
Motoyuki Kimura 01ea8f22ea ci: add kernel-version-test workflow for io_uring tests (#7486) 2025-07-31 20:52:28 +09:00
Łukasz Sobczak 9f423053fb sync: umplement Stream::size_hint for ReceiverStream and UnboundedReceiverStream (#7492) 2025-07-29 15:35:09 +00:00
yanyuxing 0e5c5d64f5 future: add adapters of CancellationToken for FutureExt (#7475) 2025-07-29 18:09:13 +08:00
Luca Bruno 1b27e17ff8 net: add SocketAddr::as_abstract_name (#7491) 2025-07-29 11:07:58 +02:00
Jess Izen 8fc62c06c7 metrics: reorder metrics to be grouped by cfg-gates (#7453) 2025-07-29 07:40:57 +02:00
James Kay 4b96af6040 macros: add "local" runtime flavor (#7375) 2025-07-28 13:59:00 +02:00
tkoyasak ce41896f8d sync: fix broken link of Python asyncio.Event in SetOnce docs (#7485) 2025-07-27 08:28:24 +08:00
tkoyasak c8ab78a84f changelog: fix incorrect PR number for 1.47.0 (#7484) 2025-07-27 08:24:09 +08:00
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
Alice Ryhl 9681ce2b95 chore: make 1.38 an LTS (#6706) 2024-07-22 23:22:12 +02:00
582 changed files with 34715 additions and 14953 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
-58
View File
@@ -1,58 +0,0 @@
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
env:
RUST_STABLE: stable
RUST_NIGHTLY: nightly-2024-05-05
RUSTFLAGS: -D warnings
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
# same VM. The binary will be built in 32-bit mode, but will execute on a
# 64-bit kernel and in a 64-bit environment. Our tests don't execute any of
# the system's binaries, so the environment shouldn't matter.
task:
name: FreeBSD 64-bit
setup_script:
- pkg install -y bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_STABLE
- . $HOME/.cargo/env
- |
echo "~~~~ rustc --version ~~~~"
rustc --version
test_script:
- . $HOME/.cargo/env
- cargo test --all --all-features
task:
name: FreeBSD docs
env:
RUSTFLAGS: --cfg docsrs --cfg tokio_unstable
RUSTDOCFLAGS: --cfg docsrs --cfg tokio_unstable -Dwarnings
setup_script:
- pkg install -y bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_NIGHTLY
- . $HOME/.cargo/env
- |
echo "~~~~ rustc --version ~~~~"
rustc --version
test_script:
- . $HOME/.cargo/env
- cargo doc --lib --no-deps --all-features --document-private-items
task:
name: FreeBSD 32-bit
setup_script:
- pkg install -y bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_STABLE
- . $HOME/.cargo/env
- rustup target add i686-unknown-freebsd
- |
echo "~~~~ rustc --version ~~~~"
rustc --version
test_script:
- . $HOME/.cargo/env
- cargo test --all --all-features --target i686-unknown-freebsd
+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
@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
+31 -19
View File
@@ -1,28 +1,40 @@
R-loom-blocking:
- changed-files:
- any-glob-to-any-file:
- tokio/src/runtime/blocking/*
- tokio/src/runtime/blocking/**/*
R-loom-sync:
- tokio/src/sync/*
- tokio/src/sync/**/*
- changed-files:
- any-glob-to-any-file:
- tokio/src/sync/*
- tokio/src/sync/**/*
R-loom-time-driver:
- tokio/src/runtime/time/*
- tokio/src/runtime/time/**/*
- changed-files:
- any-glob-to-any-file:
- tokio/src/runtime/time/*
- tokio/src/runtime/time/**/*
R-loom-current-thread:
- tokio/src/runtime/scheduler/*
- tokio/src/runtime/scheduler/current_thread/*
- tokio/src/runtime/task/*
- tokio/src/runtime/task/**
- changed-files:
- any-glob-to-any-file:
- tokio/src/runtime/scheduler/*
- tokio/src/runtime/scheduler/current_thread/*
- tokio/src/runtime/task/*
- tokio/src/runtime/task/**
R-loom-multi-thread:
- tokio/src/runtime/scheduler/*
- tokio/src/runtime/scheduler/multi_thread/*
- tokio/src/runtime/scheduler/multi_thread/**
- tokio/src/runtime/task/*
- tokio/src/runtime/task/**
- changed-files:
- any-glob-to-any-file:
- tokio/src/runtime/scheduler/*
- tokio/src/runtime/scheduler/multi_thread/*
- tokio/src/runtime/scheduler/multi_thread/**
- tokio/src/runtime/task/*
- tokio/src/runtime/task/**
R-loom-multi-thread-alt:
- tokio/src/runtime/scheduler/*
- tokio/src/runtime/scheduler/multi_thread_alt/*
- tokio/src/runtime/scheduler/multi_thread_alt/**
- tokio/src/runtime/task/*
- tokio/src/runtime/task/**
R-loom-util:
- changed-files:
- any-glob-to-any-file:
- tokio-util/src/*
- tokio-util/src/**/*
+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@v6
- uses: EmbarkStudios/cargo-deny-action@v2
+471 -223
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository_owner == 'tokio-rs'
steps:
- uses: actions/labeler@v3
- uses: actions/labeler@v6
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true
+29 -22
View File
@@ -23,13 +23,29 @@ permissions:
contents: read
jobs:
loom-blocking:
name: loom tokio::runtime::spawn_blocking
# 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-blocking') || (github.base_ref == null))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
- name: run tests
run: cargo test --lib --release --features full -- --nocapture loom_blocking
working-directory: tokio
loom-sync:
name: loom tokio::sync
# base_ref is null when it's not a pull request
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-sync') || (github.base_ref == null))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -45,14 +61,14 @@ jobs:
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-time-driver') || (github.base_ref == null))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
- name: run tests
run: cargo test --lib --release --features full -- --nocapture runtime::time::tests
run: cargo test --lib --release --features full -- --nocapture runtime::time
working-directory: tokio
loom-current-thread:
@@ -61,7 +77,7 @@ jobs:
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-current-thread') || (github.base_ref == null))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -84,39 +100,30 @@ jobs:
- scope: loom_multi_thread::group_c
- scope: loom_multi_thread::group_d
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- 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 }}
loom-multi-thread-alt:
name: loom ALT multi-thread scheduler
loom-util:
name: loom tokio-util
# 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))
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-util') || (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
- uses: actions/checkout@v6
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
- name: loom ${{ matrix.scope }}
run: cargo test --lib --release --features full -- $SCOPE
working-directory: tokio
env:
SCOPE: ${{ matrix.scope }}
- name: run tests
run: cargo test --lib --release --features full -- --nocapture
working-directory: tokio-util
+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@v6
- uses: EmbarkStudios/cargo-deny-action@v2
+2 -2
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
@@ -27,7 +27,7 @@ jobs:
stress-test:
- simple_echo_tcp
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -0,0 +1,103 @@
name: Uring Kernel Version Test
on:
workflow_call:
inputs:
kernel_version:
description: 'Version of the Linux kernel to build'
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest
env:
KERNEL_VERSION: ${{ inputs.kernel_version }}
steps:
- uses: actions/checkout@v6
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential bison flex libssl-dev libelf-dev \
qemu-system-x86 busybox-static cpio xz-utils wget
- name: Cache Linux source
id: cache-kernel
uses: actions/cache@v5
with:
path: linux-${{ env.KERNEL_VERSION }}
key: kernel-${{ env.KERNEL_VERSION }}
- name: Download & build Linux kernel
if: steps.cache-kernel.outputs.cache-hit != 'true'
run: |
MAJOR=${KERNEL_VERSION%%.*}
wget https://cdn.kernel.org/pub/linux/kernel/v${MAJOR}.x/linux-${KERNEL_VERSION}.tar.xz
tar xf linux-${KERNEL_VERSION}.tar.xz
cd linux-${KERNEL_VERSION}
make defconfig
make -j$(nproc)
- name: Generate test binaries with io_uring enabled
run: |
# Build both integration (tokio/tests/) and unit (e.g., tokio/src/fs/file/tests.rs) tests with io_uring enabled
rustup target add x86_64-unknown-linux-musl
RUSTFLAGS="--cfg tokio_unstable" \
cargo test -p tokio --features full,io-uring \
--target x86_64-unknown-linux-musl --test 'fs*' --lib --no-run
- name: Prepare initramfs + tests binaries
run: |
set -e
rm -rf initramfs
mkdir -p initramfs/{bin,bin/tests,sbin,proc,sys,tmp}
# Copy test binaries into initramfs
for bin in target/x86_64-unknown-linux-musl/debug/deps/{fs_*,tokio-*}; do
if [ -f "$bin" ] && [ -x "$bin" ]; then
cp "$bin" initramfs/bin/tests
fi
done
# Add BusyBox & symlinks
cp /usr/bin/busybox initramfs/bin/
for cmd in sh mount uname true sleep; do ln -sf busybox initramfs/bin/$cmd; done
ln -sf ../bin/busybox initramfs/sbin/poweroff
# Generate init script
cat > initramfs/init << 'EOF'
#!/bin/sh
set -e
mkdir -p /dev
# create device nodes, as some tests require them
mknod /dev/null c 1 3
mknod /dev/zero c 1 5
mknod /dev/tty c 5 0
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mkdir -p /tmp && mount -t tmpfs -o mode=1777 tmpfs /tmp
for f in /bin/tests/*; do RUST_BACKTRACE=1 "$f" ; done
EOF
chmod +x initramfs/init
# Pack into a CPIO archive
(cd initramfs && find . -print0 \
| cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz)
- name: Run tests in QEMU
run: |
qemu-system-x86_64 \
-kernel linux-${{ env.KERNEL_VERSION }}/arch/x86/boot/bzImage \
-initrd initramfs.cpio.gz \
-append "console=ttyS0 rootfstype=ramfs panic=1" \
-nographic -no-reboot -m 1024 -action panic=exit-failure 2>&1 | tee qemu-output.log
# qemu always exits with 0, so we check if the tests passed by using grep.
if grep -q "test result: FAILED" qemu-output.log; then
echo "tests failed (QEMU exited abnormally)"
exit 1
else
echo "all tests passed"
fi
+1
View File
@@ -3,3 +3,4 @@ Cargo.lock
.cargo/config.toml
.cargo/config
+18 -698
View File
@@ -1,7 +1,6 @@
# Contributing to Tokio
:balloon: Thanks for your help improving the project! We are so happy to have
you!
Thanks for your help improving Tokio! We are so happy to have you!
There are opportunities to contribute to Tokio at any level. It doesn't matter if
you are just getting started with Rust or are the most weathered expert, we can
@@ -9,15 +8,11 @@ use your help.
**No contribution is too small and all contributions are valued.**
This guide will help you get started. **Do not let this guide intimidate you**.
It should be considered a map to help you navigate the process.
See the [contributing guidelines] to get started.
The [dev channel][dev] is available for any concerns not covered in this guide, please join
us!
[contributing guidelines]: docs/contributing/README.md
[dev]: https://discord.gg/tokio
## Conduct
## Code of Conduct
The Tokio project adheres to the [Rust Code of Conduct][coc]. This describes
the _minimum_ behavior expected from all contributors. Instances of violations of the
@@ -26,709 +21,34 @@ Code of Conduct can be reported by contacting the project team at
[coc]: https://github.com/rust-lang/rust/blob/master/CODE_OF_CONDUCT.md
## Contributing in Issues
## Need Help?
For any issue, there are fundamentally three ways an individual can contribute:
Reach out to us on the [Discord server] for any concern not covered in this guide.
1. By opening the issue for discussion: For instance, if you believe that you
have discovered a bug in Tokio, creating a new issue in [the tokio-rs/tokio
issue tracker][issue] is the way to report it.
2. By helping to triage the issue: This can be done by providing
supporting details (a test case that demonstrates a bug), providing
suggestions on how to address the issue, or ensuring that the issue is tagged
correctly.
3. By helping to resolve the issue: Typically this is done either in the form of
demonstrating that the issue reported is not a problem after all, or more
often, by opening a Pull Request that changes some bit of something in
Tokio in a concrete and reviewable manner.
[issue]: https://github.com/tokio-rs/tokio/issues
**Anybody can participate in any stage of contribution**. We urge you to
participate in the discussion around bugs and participate in reviewing PRs.
### Asking for General Help
If you have reviewed existing documentation and still have questions or are
having problems, you can [open a discussion] asking for help.
In exchange for receiving help, we ask that you contribute back a documentation
PR that helps others avoid the problems that you encountered.
[open a discussion]: https://github.com/tokio-rs/tokio/discussions/new
### Submitting a Bug Report
When opening a new issue in the Tokio issue tracker, you will be presented
with a basic template that should be filled in. If you believe that you have
uncovered a bug, please fill out this form, following the template to the best
of your ability. Do not worry if you cannot answer every detail, just fill in
what you can.
The two most important pieces of information we need in order to properly
evaluate the report is a description of the behavior you are seeing and a simple
test case we can use to recreate the problem on our own. If we cannot recreate
the issue, it becomes impossible for us to fix.
In order to rule out the possibility of bugs introduced by userland code, test
cases should be limited, as much as possible, to using only Tokio APIs.
See [How to create a Minimal, Complete, and Verifiable example][mcve].
[mcve]: https://stackoverflow.com/help/mcve
### Triaging a Bug Report
Once an issue has been opened, it is not uncommon for there to be discussion
around it. Some contributors may have differing opinions about the issue,
including whether the behavior being seen is a bug or a feature. This discussion
is part of the process and should be kept focused, helpful, and professional.
Short, clipped responses—that provide neither additional context nor supporting
detail—are not helpful or professional. To many, such responses are simply
annoying and unfriendly.
Contributors are encouraged to help one another make forward progress as much as
possible, empowering one another to solve issues collaboratively. If you choose
to comment on an issue that you feel either is not a problem that needs to be
fixed, or if you encounter information in an issue that you feel is incorrect,
explain why you feel that way with additional supporting context, and be willing
to be convinced that you may be wrong. By doing so, we can often reach the
correct outcome much faster.
### Resolving a Bug Report
In the majority of cases, issues are resolved by opening a Pull Request. The
process for opening and reviewing a Pull Request is similar to that of opening
and triaging issues, but carries with it a necessary review and approval
workflow that ensures that the proposed changes meet the minimal quality and
functional guidelines of the Tokio project.
## Pull Requests
Pull Requests are the way concrete changes are made to the code, documentation,
and dependencies in the Tokio repository.
Even tiny pull requests (e.g., one character pull request fixing a typo in API
documentation) are greatly appreciated. Before making a large change, it is
usually a good idea to first open an issue describing the change to solicit
feedback and guidance. This will increase the likelihood of the PR getting
merged.
### Cargo Commands
Due to the extensive use of features in Tokio, you will often need to add extra
arguments to many common cargo commands. This section lists some commonly needed
commands.
Some commands just need the `--all-features` argument:
```
cargo build --all-features
cargo check --all-features
cargo test --all-features
```
Ideally, you should use the same version of clippy as the one used in CI
(defined by `env.rust_clippy` in [ci.yml][ci.yml]), because newer versions
might have new lints:
[ci.yml]: .github/workflows/ci.yml
<!--
When updating this, also update:
- .github/workflows/ci.yml
- README.md
- tokio/README.md
- tokio/Cargo.toml
- tokio-util/Cargo.toml
- tokio-test/Cargo.toml
- tokio-stream/Cargo.toml
-->
```
cargo +1.77 clippy --all --tests --all-features
```
When building documentation, a simple `cargo doc` is not sufficient. To produce
documentation equivalent to what will be produced in docs.rs's builds of Tokio's
docs, please use:
```
RUSTDOCFLAGS="--cfg docsrs --cfg tokio_unstable" RUSTFLAGS="--cfg docsrs --cfg tokio_unstable" cargo +nightly doc --all-features [--open]
```
This turns on indicators to display the Cargo features required for
conditionally compiled APIs in Tokio, and it enables documentation of unstable
Tokio features. Notice that it is necessary to pass cfg flags to both RustDoc
*and* rustc.
There is a more concise way to build docs.rs-equivalent docs by using [`cargo
docs-rs`], which reads the above documentation flags out of Tokio's Cargo.toml
as docs.rs itself does.
[`cargo docs-rs`]: https://github.com/dtolnay/cargo-docs-rs
```
cargo install --locked cargo-docs-rs
cargo +nightly docs-rs [--open]
```
The `cargo fmt` command does not work on the Tokio codebase. You can use the
command below instead:
```
# Mac or Linux
rustfmt --check --edition 2021 $(git ls-files '*.rs')
# Powershell
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2021 $_.FullName }
```
The `--check` argument prints the things that need to be fixed. If you remove
it, `rustfmt` will update your files locally instead.
You can run loom tests with
```
cd tokio # tokio crate in workspace
LOOM_MAX_PREEMPTIONS=1 LOOM_MAX_BRANCHES=10000 RUSTFLAGS="--cfg loom -C debug_assertions" \
cargo test --lib --release --features full -- --test-threads=1 --nocapture
```
Additionally, you can also add `--cfg tokio_unstable` to the `RUSTFLAGS` environment variable to
run loom tests that test unstable features.
You can run miri tests with
```
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-tag-raw-pointers" \
cargo +nightly miri test --features full --lib
```
### Performing spellcheck on tokio codebase
You can perform spell-check on tokio codebase. For details of how to use the spellcheck tool, feel free to visit
https://github.com/drahnr/cargo-spellcheck
```
# First install the spell-check plugin
cargo install --locked cargo-spellcheck
# Then run the cargo spell check command
cargo spellcheck check
```
if the command rejects a word, you should backtick the rejected word if it's code related. If not, the
rejected word should be put into `spellcheck.dic` file.
Note that when you add a word into the file, you should also update the first line which tells the spellcheck tool
the total number of words included in the file
### Tests
If the change being proposed alters code (as opposed to only documentation for
example), it is either adding new functionality to Tokio or it is fixing
existing, broken functionality. In both of these cases, the pull request should
include one or more tests to ensure that Tokio does not regress in the future.
There are two ways to write tests: [integration tests][integration-tests]
and [documentation tests][documentation-tests].
(Tokio avoids [unit tests][unit-tests] as much as possible).
Tokio uses [conditional compilation attributes][conditional-compilation]
throughout the codebase, to modify rustc's behavior. Code marked with such
attributes can be enabled using RUSTFLAGS and RUSTDOCFLAGS environment
variables. One of the most prevalent flags passed in these variables is
the `--cfg` option. To run tests in a particular file, check first what
options #![cfg] declaration defines for that file.
For instance, to run a test marked with the 'tokio_unstable' cfg option,
you must pass this flag to the compiler when running the test.
```
$ RUSTFLAGS="--cfg tokio_unstable" cargo test -p tokio --all-features --test rt_metrics
```
#### Integration tests
Integration tests go in the same crate as the code they are testing. Each sub
crate should have a `dev-dependency` on `tokio` itself. This makes all Tokio
utilities available to use in tests, no matter the crate being tested.
The best strategy for writing a new integration test is to look at existing
integration tests in the crate and follow the style.
#### Fuzz tests
Some of our crates include a set of fuzz tests, this will be marked by a
directory `fuzz`. It is a good idea to run fuzz tests after each change.
To get started with fuzz testing you'll need to install
[cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz).
`cargo install --locked cargo-fuzz`
To list the available fuzzing harnesses you can run;
```bash
$ cd tokio
$ cargo fuzz list
fuzz_linked_list
````
Running a fuzz test is as simple as;
`cargo fuzz run fuzz_linked_list`
**NOTE**: Keep in mind that by default when running a fuzz test the fuzz
harness will run forever and will only exit if you `ctrl-c` or it finds
a bug.
#### Documentation tests
Ideally, every API has at least one [documentation test] that demonstrates how to
use the API. Documentation tests are run with `cargo test --doc`. This ensures
that the example is correct and provides additional test coverage.
The trick to documentation tests is striking a balance between being succinct
for a reader to understand and actually testing the API.
Same as with integration tests, when writing a documentation test, the full
`tokio` crate is available. This is especially useful for getting access to the
runtime to run the example.
The documentation tests will be visible from both the crate specific
documentation **and** the `tokio` facade documentation via the re-export. The
example should be written from the point of view of a user that is using the
`tokio` crate. As such, the example should use the API via the facade and not by
directly referencing the crate.
The type level example for `tokio_timer::Timeout` provides a good example of a
documentation test:
```
/// // import the `timeout` function, usually this is done
/// // with `use tokio::prelude::*`
/// use tokio::prelude::FutureExt;
/// use futures::Stream;
/// use futures::sync::mpsc;
/// use std::time::Duration;
///
/// # fn main() {
/// let (tx, rx) = mpsc::unbounded();
/// # tx.unbounded_send(()).unwrap();
/// # drop(tx);
///
/// let process = rx.for_each(|item| {
/// // do something with `item`
/// # drop(item);
/// # Ok(())
/// });
///
/// # tokio::runtime::current_thread::block_on_all(
/// // Wrap the future with a `Timeout` set to expire in 10 milliseconds.
/// process.timeout(Duration::from_millis(10))
/// # ).unwrap();
/// # }
```
Given that this is a *type* level documentation test and the primary way users
of `tokio` will create an instance of `Timeout` is by using
`FutureExt::timeout`, this is how the documentation test is structured.
Lines that start with `/// #` are removed when the documentation is generated.
They are only there to get the test to run. The `block_on_all` function is the
easiest way to execute a future from a test.
If this were a documentation test for the `Timeout::new` function, then the
example would explicitly use `Timeout::new`. For example:
```
/// use tokio::timer::Timeout;
/// use futures::Future;
/// use futures::sync::oneshot;
/// use std::time::Duration;
///
/// # fn main() {
/// let (tx, rx) = oneshot::channel();
/// # tx.send(()).unwrap();
///
/// # tokio::runtime::current_thread::block_on_all(
/// // Wrap the future with a `Timeout` set to expire in 10 milliseconds.
/// Timeout::new(rx, Duration::from_millis(10))
/// # ).unwrap();
/// # }
```
### Benchmarks
You can run benchmarks locally for the changes you've made to the tokio codebase.
Tokio currently uses [Criterion](https://github.com/bheisler/criterion.rs) as its benchmarking tool. To run a benchmark
against the changes you have made, for example, you can run;
```bash
cd benches
# Run all benchmarks.
cargo bench
# Run all tests in the `benches/fs.rs` file
cargo bench --bench fs
# Run the `async_read_buf` benchmark in `benches/fs.rs` specifically.
cargo bench async_read_buf
# After running benches, you can check the statistics under `tokio/target/criterion/`
```
You can also refer to Criterion docs for additional options and details.
### Commits
It is a recommended best practice to keep your changes as logically grouped as
possible within individual commits. There is no limit to the number of commits
any single Pull Request may have, and many contributors find it easier to review
changes that are split across multiple commits.
That said, if you have a number of commits that are "checkpoints" and don't
represent a single logical change, please squash those together.
Note that multiple commits often get squashed when they are landed (see the
notes about [commit squashing](#commit-squashing)).
#### Commit message guidelines
A good commit message should describe what changed and why.
1. The first line should:
* contain a short description of the change (preferably 50 characters or less,
and no more than 72 characters)
* be entirely in lowercase with the exception of proper nouns, acronyms, and
the words that refer to code, like function/variable names
* start with an imperative verb
* not have a period at the end
* be prefixed with the name of the module being changed; usually this is the
same as the M-* label on the PR
Examples:
* time: introduce `Timeout` and deprecate `Deadline`
* codec: export `Encoder`, `Decoder`, `Framed*`
* ci: fix the FreeBSD ci configuration
2. Keep the second line blank.
3. Wrap all other lines at 72 columns (except for long URLs).
4. If your patch fixes an open issue, you can add a reference to it at the end
of the log. Use the `Fixes: #` prefix and the issue number. For other
references use `Refs: #`. `Refs` may include multiple issues, separated by a
comma.
Examples:
- `Fixes: #1337`
- `Refs: #1234`
Sample complete commit message:
```txt
module: explain the commit in one line
Body of commit message is a few lines of text, explaining things
in more detail, possibly giving some background about the issue
being fixed, etc.
The body of the commit message can be several paragraphs, and
please do proper word-wrap and keep columns shorter than about
72 characters or so. That way, `git log` will show things
nicely even when it is indented.
Fixes: #1337
Refs: #453, #154
```
### Opening the Pull Request
From within GitHub, opening a new Pull Request will present you with a
[template] that should be filled out. Please try to do your best at filling out
the details, but feel free to skip parts if you're not sure what to put.
[template]: .github/PULL_REQUEST_TEMPLATE.md
### Discuss and update
You will probably get feedback or requests for changes to your Pull Request.
This is a big part of the submission process so don't be discouraged! Some
contributors may sign off on the Pull Request right away, others may have
more detailed comments or feedback. This is a necessary part of the process
in order to evaluate whether the changes are correct and necessary.
**Any community member can review a PR and you might get conflicting feedback**.
Keep an eye out for comments from code owners to provide guidance on conflicting
feedback.
**Once the PR is open, do not rebase the commits**. See [Commit Squashing](#commit-squashing) for
more details.
### Commit Squashing
In most cases, **do not squash commits that you add to your Pull Request during
the review process**. When the commits in your Pull Request land, they may be
squashed into one commit per logical change. Metadata will be added to the
commit message (including links to the Pull Request, links to relevant issues,
and the names of the reviewers). The commit history of your Pull Request,
however, will stay intact on the Pull Request page.
## Reviewing Pull Requests
**Any Tokio community member is welcome to review any pull request**.
All Tokio contributors who choose to review and provide feedback on Pull
Requests have a responsibility to both the project and the individual making the
contribution. Reviews and feedback must be helpful, insightful, and geared
towards improving the contribution as opposed to simply blocking it. If there
are reasons why you feel the PR should not land, explain what those are. Do not
expect to be able to block a Pull Request from advancing simply because you say
"No" without giving an explanation. Be open to having your mind changed. Be open
to working with the contributor to make the Pull Request better.
Reviews that are dismissive or disrespectful of the contributor or any other
reviewers are strictly counter to the Code of Conduct.
When reviewing a Pull Request, the primary goals are for the codebase to improve
and for the person submitting the request to succeed. **Even if a Pull Request
does not land, the submitters should come away from the experience feeling like
their effort was not wasted or unappreciated**. Every Pull Request from a new
contributor is an opportunity to grow the community.
### Review a bit at a time.
Do not overwhelm new contributors.
It is tempting to micro-optimize and make everything about relative performance,
perfect grammar, or exact style matches. Do not succumb to that temptation.
Focus first on the most significant aspects of the change:
1. Does this change make sense for Tokio?
2. Does this change make Tokio better, even if only incrementally?
3. Are there clear bugs or larger scale issues that need attending to?
4. Is the commit message readable and correct? If it contains a breaking change
is it clear enough?
Note that only **incremental** improvement is needed to land a PR. This means
that the PR does not need to be perfect, only better than the status quo. Follow
up PRs may be opened to continue iterating.
When changes are necessary, *request* them, do not *demand* them, and **do not
assume that the submitter already knows how to add a test or run a benchmark**.
Specific performance optimization techniques, coding styles and conventions
change over time. The first impression you give to a new contributor never does.
Nits (requests for small changes that are not essential) are fine, but try to
avoid stalling the Pull Request. Most nits can typically be fixed by the Tokio
Collaborator landing the Pull Request but they can also be an opportunity for
the contributor to learn a bit more about the project.
It is always good to clearly indicate nits when you comment: e.g.
`Nit: change foo() to bar(). But this is not blocking.`
If your comments were addressed but were not folded automatically after new
commits or if they proved to be mistaken, please, [hide them][hiding-a-comment]
with the appropriate reason to keep the conversation flow concise and relevant.
### Be aware of the person behind the code
Be aware that *how* you communicate requests and reviews in your feedback can
have a significant impact on the success of the Pull Request. Yes, we may land
a particular change that makes Tokio better, but the individual might just not
want to have anything to do with Tokio ever again. The goal is not just having
good code.
### Abandoned or Stalled Pull Requests
If a Pull Request appears to be abandoned or stalled, it is polite to first
check with the contributor to see if they intend to continue the work before
checking if they would mind if you took it over (especially if it just has nits
left). When doing so, it is courteous to give the original contributor credit
for the work they started (either by preserving their name and email address in
the commit log, or by using an `Author: ` meta-data tag in the commit.
_Adapted from the [Node.js contributing guide][node]_.
[node]: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md
[hiding-a-comment]: https://help.github.com/articles/managing-disruptive-comments/#hiding-a-comment
[documentation test]: https://doc.rust-lang.org/rustdoc/documentation-tests.html
## Keeping track of issues and PRs
The Tokio GitHub repository has a lot of issues and PRs to keep track of. This
section explains the meaning of various labels, as well as our [GitHub
project][project]. The section is primarily targeted at maintainers. Most
contributors aren't able to set these labels.
### Area
The area label describes the crates relevant to this issue or PR.
- **A-tokio** This issue concerns the main Tokio crate.
- **A-tokio-util** This issue concerns the `tokio-util` crate.
- **A-tokio-tls** This issue concerns the `tokio-tls` crate. Only used for
older issues, as the crate has been moved to another repository.
- **A-tokio-test** The issue concerns the `tokio-test` crate.
- **A-tokio-macros** This issue concerns the `tokio-macros` crate. Should only
be used for the procedural macros, and not `join!` or `select!`.
- **A-ci** This issue concerns our GitHub Actions setup.
### Category
- **C-bug** This is a bug-report. Bug-fix PRs use `C-enhancement` instead.
- **C-enhancement** This is a PR that adds a new features.
- **C-maintenance** This is an issue or PR about stuff such as documentation,
GitHub Actions or code quality.
- **C-feature-request** This is a feature request. Implementations of feature
requests use `C-enhancement` instead.
- **C-feature-accepted** If you submit a PR for this feature request, we wont
close it with the reason "we don't want this". Issues with this label should
also have the `C-feature-request` label.
- **C-musing** Stuff like tracking issues or roadmaps. "musings about a better
world"
- **C-proposal** A proposal of some kind, and a request for comments.
- **C-question** A user question. Large overlap with GitHub discussions.
- **C-request** A non-feature request, e.g. "please add deprecation notices to
`-alpha.*` versions of crates"
### Calls for participation
- **E-help-wanted** Stuff where we want help. Often seen together with `C-bug`
or `C-feature-accepted`.
- **E-easy** This is easy, ranging from quick documentation fixes to stuff you
can do after reading the tutorial on our website.
- **E-medium** This is not `E-easy` or `E-hard`.
- **E-hard** This either involves very tricky code, is something we don't know
how to solve, or is difficult for some other reason.
- **E-needs-mvce** This bug is missing a minimal complete and verifiable
example.
The "E-" prefix is the same as used in the Rust compiler repository. Some
issues are missing a difficulty rating, but feel free to ask on our Discord
server if you want to know how difficult an issue likely is.
### Module
The module label provides a more fine grained categorization than **Area**.
- **M-blocking** Things relevant to `spawn_blocking`, `block_in_place`.
- **M-codec** The `tokio_util::codec` module.
- **M-compat** The `tokio_util::compat` module.
- **M-coop** Things relevant to coop.
- **M-fs** The `tokio::fs` module.
- **M-io** The `tokio::io` module.
- **M-macros** Issues about any kind of macro.
- **M-net** The `tokio::net` module.
- **M-process** The `tokio::process` module.
- **M-runtime** The `tokio::runtime` module.
- **M-signal** The `tokio::signal` module.
- **M-sync** The `tokio::sync` module.
- **M-task** The `tokio::task` module.
- **M-time** The `tokio::time` module.
- **M-tracing** Tracing support in Tokio.
### Topic
Some extra information.
- **T-docs** This is about documentation.
- **T-performance** This is about performance.
- **T-v0.1.x** This is about old Tokio.
Any label not listed here is not in active use.
[project]: https://github.com/orgs/tokio-rs/projects/1
[Discord server]: https://discord.gg/tokio
## LTS guarantees
Tokio ≥1.0.0 comes with LTS guarantees:
* A minimum of 5 years of maintenance.
* A minimum of 3 years before a hypothetical 2.0 release.
In Tokio ≥1.0.0, each LTS release comes with the guarantee of at least one year of
backported fixes.
The goal of these guarantees is to provide stability to the ecosystem.
## Minimum Supported Rust Version (MSRV)
* All Tokio ≥1.0.0 releases will support at least a 6-month old Rust
compiler release.
* The MSRV will only be increased on 1.x releases.
* All Tokio ≥1.0.0 releases will support at least a 6-month old Rust
compiler release.
* The MSRV will only be increased on 1.x releases.
## Versioning Policy
With Tokio ≥1.0.0:
* Patch (1.\_.x) releases _should only_ contain bug fixes or documentation
changes. Besides this, these releases should not substantially change
runtime behavior.
* Minor (1.x) releases may contain new functionality, MSRV increases (see
above), minor dependency updates, deprecations, and larger internal
implementation changes.
* Patch (1.\_.x) releases _should only_ contain bug fixes or documentation
changes. Besides this, these releases should not substantially change
runtime behavior.
* Minor (1.x) releases may contain new functionality, MSRV increases (see
above), minor dependency updates, deprecations, and larger internal
implementation changes.
This is as defined by [Semantic Versioning 2.0](https://semver.org/).
## Releasing
Since the Tokio project consists of a number of crates, many of which depend on
each other, releasing new versions to crates.io can involve some complexities.
When releasing a new version of a crate, follow these steps:
1. **Ensure that the release crate has no path dependencies.** When the HEAD
version of a Tokio crate requires unreleased changes in another Tokio crate,
the crates.io dependency on the second crate will be replaced with a path
dependency. Crates with path dependencies cannot be published, so before
publishing the dependent crate, any path dependencies must also be published.
This should be done through a form of depth-first tree traversal:
1. Starting with the first path dependency in the crate to be released,
inspect the `Cargo.toml` for the dependency. If the dependency has any
path dependencies of its own, repeat this step with the first such
dependency.
2. Begin the release process for the path dependency.
3. Once the path dependency has been published to crates.io, update the
dependent crate to depend on the crates.io version.
4. When all path dependencies have been published, the dependent crate may
be published.
To verify that a crate is ready to publish, run:
```bash
bin/publish --dry-run <CRATE NAME> <CRATE VERSION>
```
2. **Update Cargo metadata.** After releasing any path dependencies, update the
`version` field in `Cargo.toml` to the new version, and the `documentation`
field to the docs.rs URL of the new version.
3. **Update other documentation links.** Update the "Documentation" link in the
crate's `README.md` to point to the docs.rs URL of the new version.
4. **Update the changelog for the crate.** Each crate in the Tokio repository
has its own `CHANGELOG.md` in that crate's subdirectory. Any changes to that
crate since the last release should be added to the changelog. Change
descriptions may be taken from the Git history, but should be edited to
ensure a consistent format, based on [Keep A Changelog][keep-a-changelog].
Other entries in that crate's changelog may also be used for reference.
5. **Perform a final audit for breaking changes.** Compare the HEAD version of
crate with the Git tag for the most recent release version. If there are any
breaking API changes, determine if those changes can be made without breaking
existing APIs. If so, resolve those issues. Otherwise, if it is necessary to
make a breaking release, update the version numbers to reflect this.
6. **Open a pull request with your changes.** Once that pull request has been
approved by a maintainer and the pull request has been merged, continue to
the next step.
7. **Release the crate.** Run the following command:
```bash
bin/publish <NAME OF CRATE> <VERSION>
```
Your editor and prompt you to edit a message for the tag. Copy the changelog
entry for that release version into your editor and close the window.
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog/blob/master/CHANGELOG.md
[unit-tests]: https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html
[integration-tests]: https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html
[documentation-tests]: https://doc.rust-lang.org/rust-by-example/testing/doc_testing.html
[conditional-compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
+20
View File
@@ -15,5 +15,25 @@ members = [
"tests-integration",
]
[patch.crates-io]
tokio = { path = "tokio" }
tokio-macros = { path = "tokio-macros" }
tokio-stream = { path = "tokio-stream" }
tokio-test = { path = "tokio-test" }
tokio-util = { path = "tokio-util" }
[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_unstable)',
'cfg(target_os, values("cygwin"))',
] }
+24 -16
View File
@@ -1,3 +1,7 @@
*[TokioConf 2026 program and tickets are now available!](https://tokioconf.com)*
---
# Tokio
A runtime for writing reliable, asynchronous, and slim applications with
@@ -39,7 +43,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
@@ -52,11 +56,11 @@ an asynchronous application.
A basic TCP echo server with Tokio.
Make sure you activated the full features of the tokio crate on Cargo.toml:
Make sure you enable the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.40.0", features = ["full"] }
tokio = { version = "1.52.4", features = ["full"] }
```
Then, on your main.rs:
@@ -78,7 +82,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);
@@ -103,7 +107,7 @@ More examples can be found [here][examples]. For a larger "real world" example,
[examples]: https://github.com/tokio-rs/tokio/tree/master/examples
[mini-redis]: https://github.com/tokio-rs/mini-redis/
To see a list of the available features flags that can be enabled, check our
To see a list of the available feature flags that can be enabled, check our
[docs][feature-flag-docs].
## Getting Help
@@ -125,7 +129,7 @@ question. You can also ask your question on [the discussions page][discussions].
you! We have a [contributing guide][guide] to help you get involved in the Tokio
project.
[guide]: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md
[guide]: https://github.com/tokio-rs/tokio/blob/master/docs/contributing/README.md
## Related Projects
@@ -186,12 +190,13 @@ When updating this, also update:
Tokio will keep a rolling MSRV (minimum supported rust version) policy of **at
least** 6 months. When increasing the MSRV, the new Rust version must have been
released at least six months ago. The current MSRV is 1.70.
released at least six months ago. The current MSRV is 1.71.
Note that the MSRV is not increased automatically, and only as part of a minor
release. The MSRV history for past minor releases can be found below:
* 1.39 to now - Rust 1.70
* 1.48 to now - Rust 1.71
* 1.39 to 1.47 - Rust 1.70
* 1.30 to 1.38 - Rust 1.63
* 1.27 to 1.29 - Rust 1.56
* 1.17 to 1.26 - Rust 1.49
@@ -205,8 +210,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,19 +221,18 @@ 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.47.x` - LTS release until September 2026. (MSRV 1.70)
* `1.51.x` - LTS release until March 2027. (MSRV 1.71)
Each LTS release will continue to receive backported fixes for at least a year.
If you wish to use a fixed minor release in your project, we recommend that you
use an LTS release.
To use a fixed minor version, you can specify the version with a tilde. For
example, to specify that you wish to use the newest `1.32.x` patch release, you
example, to specify that you wish to use the newest `1.47.x` patch release, you
can use the following dependency specification:
```text
tokio = { version = "~1.32", features = [...] }
tokio = { version = "~1.47", features = [...] }
```
### Previous LTS releases
@@ -238,6 +242,10 @@ 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.
* `1.38.x` - LTS release until July 2025.
* `1.43.x` - LTS release until March 2026.
## License
@@ -248,5 +256,5 @@ This project is licensed under the [MIT license].
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
for inclusion in Tokio by you shall be licensed as MIT, without any additional
terms or conditions.
+17 -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,16 @@ harness = false
name = "time_timeout"
path = "time_timeout.rs"
harness = false
[[bench]]
name = "spawn_blocking"
path = "spawn_blocking.rs"
harness = false
[[bench]]
name = "remote_spawn"
path = "remote_spawn.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,
+103
View File
@@ -0,0 +1,103 @@
//! Benchmark remote task spawning (push_remote_task) at different concurrency
//! levels on the multi-threaded scheduler.
//!
//! This measures contention on the scheduler's inject queue mutex when multiple
//! external (non-worker) threads spawn tasks into the tokio runtime simultaneously.
//! Every rt.spawn() from an external thread unconditionally goes through
//! push_remote_task, making this a direct measurement of inject queue contention.
//!
//! For each parallelism level N (1, 2, 4, 8, 16, 32, 64, capped at available parallelism):
//! - Spawns N std::threads (external to the runtime)
//! - Each thread spawns TOTAL_TASKS / N tasks into the runtime via rt.spawn()
//! - All threads are synchronized with a barrier to maximize contention
//! - Tasks are trivial no-ops to isolate the push overhead
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use std::sync::Barrier;
use tokio::runtime::{self, Runtime};
/// Total number of tasks spawned across all threads per iteration.
/// Must be divisible by the largest parallelism level (64).
const TOTAL_TASKS: usize = 12_800;
const _: () = assert!(
TOTAL_TASKS.is_multiple_of(64),
"TOTAL_TASKS must be divisible by 64"
);
fn remote_spawn_contention(c: &mut Criterion) {
let parallelism_levels = parallelism_levels();
let mut group = c.benchmark_group("remote_spawn");
for num_threads in &parallelism_levels {
let num_threads = *num_threads;
group.bench_with_input(
BenchmarkId::new("threads", num_threads),
&num_threads,
|b, &num_threads| {
let rt = rt();
let tasks_per_thread = TOTAL_TASKS / num_threads;
let barrier = Barrier::new(num_threads);
b.iter_custom(|iters| {
let mut total_duration = std::time::Duration::ZERO;
for _ in 0..iters {
let start = std::time::Instant::now();
let all_handles = std::thread::scope(|s| {
let handles: Vec<_> = (0..num_threads)
.map(|_| {
let barrier = &barrier;
let rt = &rt;
s.spawn(move || {
let mut join_handles = Vec::with_capacity(tasks_per_thread);
barrier.wait();
for _ in 0..tasks_per_thread {
join_handles.push(rt.spawn(async {}));
}
join_handles
})
})
.collect();
handles
.into_iter()
.flat_map(|h| h.join().unwrap())
.collect::<Vec<_>>()
});
total_duration += start.elapsed();
rt.block_on(async {
for h in all_handles {
h.await.unwrap();
}
});
}
total_duration
});
},
);
}
group.finish();
}
fn parallelism_levels() -> Vec<usize> {
let max_parallelism = std::thread::available_parallelism()
.map(|p| p.get())
.unwrap_or(1);
[1, 2, 4, 8, 16, 32, 64]
.into_iter()
.filter(|&n| n <= max_parallelism)
.collect()
}
fn rt() -> Runtime {
runtime::Builder::new_multi_thread().build().unwrap()
}
criterion_group!(remote_spawn_benches, remote_spawn_contention);
criterion_main!(remote_spawn_benches);
+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 {
+73
View File
@@ -0,0 +1,73 @@
//! Benchmark spawn_blocking at different concurrency levels on the multi-threaded scheduler.
//!
//! For each parallelism level N (1, 2, 4, 8, 16, 32, 64, capped at available parallelism):
//! - Spawns N regular async tasks
//! - Each task spawns M batches of B spawn_blocking tasks (no-ops)
//! - Each batch is awaited to completion before starting the next
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use tokio::runtime::{self, Runtime};
use tokio::task::JoinSet;
/// Number of batches per task
const NUM_BATCHES: usize = 100;
/// Number of spawn_blocking calls per batch
const BATCH_SIZE: usize = 16;
fn spawn_blocking_concurrency(c: &mut Criterion) {
let max_parallelism = std::thread::available_parallelism()
.map(|p| p.get())
.unwrap_or(1);
let parallelism_levels: Vec<usize> = [1, 2, 4, 8, 16, 32, 64]
.into_iter()
.filter(|&n| n <= max_parallelism)
.collect();
let mut group = c.benchmark_group("spawn_blocking");
for num_tasks in parallelism_levels {
group.bench_with_input(
BenchmarkId::new("concurrency", num_tasks),
&num_tasks,
|b, &num_tasks| {
let rt = rt();
b.iter(|| {
rt.block_on(async {
let mut tasks = JoinSet::new();
for _ in 0..num_tasks {
tasks.spawn(async {
for _ in 0..NUM_BATCHES {
let mut batch = JoinSet::new();
for _ in 0..BATCH_SIZE {
batch.spawn_blocking(|| black_box(0));
}
batch.join_all().await;
}
});
}
tasks.join_all().await;
});
});
},
);
}
group.finish();
}
fn rt() -> Runtime {
runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
}
criterion_group!(spawn_blocking_benches, spawn_blocking_concurrency);
criterion_main!(spawn_blocking_benches);
+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"], crate = "unicode-ident" },
]
[bans]
multiple-versions = "allow"
wildcards = "deny"
[sources]
unknown-registry = "deny"
unknown-git = "deny"
+46
View File
@@ -0,0 +1,46 @@
# Contributing
This guide will help you get started. **Do not let this guide intimidate you**.
It should be considered a map to help you navigate the process.
## Quick start
If you are unsure where to begin, use the following guides:
- Want to report or triage a bug? Start with [Contributing in Issues](contributing-in-issues.md).
- Looking for something to work on? Filter issues by [`E-help-wanted`](https://github.com/tokio-rs/tokio/labels/E-help-wanted).
- Planning to submit a PR? Read [Pull Requests](pull-requests.md) for the full workflow and required checks.
- Want to understand what the labels on issues mean? See [Keeping track of issues and PRs](keeping-track-of-issues-and-prs.md).
- Interested in code review? See [Reviewing Pull Requests](reviewing-pull-requests.md).
## Table of Contents
- [Contributing in Issues](contributing-in-issues.md)
- [Asking for General Help](contributing-in-issues.md#asking-for-general-help)
- [Submitting a Bug Report](contributing-in-issues.md#submitting-a-bug-report)
- [Triaging a Bug Report](contributing-in-issues.md#triaging-a-bug-report)
- [Resolving a Bug Report](contributing-in-issues.md#resolving-a-bug-report)
- [Pull Requests](pull-requests.md)
- [Cargo Commands](pull-requests.md#cargo-commands)
- [Performing spellcheck on tokio codebase](pull-requests.md#performing-spellcheck-on-tokio-codebase)
- [Tests](pull-requests.md#tests)
- [Integration tests](pull-requests.md#integration-tests)
- [Fuzz tests](pull-requests.md#fuzz-tests)
- [Documentation tests](pull-requests.md#documentation-tests)
- [Benchmarks](pull-requests.md#benchmarks)
- [Commits](pull-requests.md#commits)
- [Commit message guidelines](pull-requests.md#commit-message-guidelines)
- [Opening the Pull Request](pull-requests.md#opening-the-pull-request)
- [Discuss and update](pull-requests.md#discuss-and-update)
- [Commit Squashing](pull-requests.md#commit-squashing)
- [Reviewing Pull Requests](reviewing-pull-requests.md)
- [Review a bit at a time](reviewing-pull-requests.md#review-a-bit-at-a-time)
- [Be aware of the person behind the code](reviewing-pull-requests.md#be-aware-of-the-person-behind-the-code)
- [Abandoned or Stalled Pull Requests](reviewing-pull-requests.md#abandoned-or-stalled-pull-requests)
- [How to specify crates dependencies versions](how-to-specify-crates-dependencies-versions.md)
- [Keeping track of issues and PRs](keeping-track-of-issues-and-prs.md)
- [Area](keeping-track-of-issues-and-prs.md#area)
- [Category](keeping-track-of-issues-and-prs.md#category)
- [Calls for participation](keeping-track-of-issues-and-prs.md#calls-for-participation)
- [Module](keeping-track-of-issues-and-prs.md#module)
- [Topic](keeping-track-of-issues-and-prs.md#topic)
@@ -0,0 +1,79 @@
## Contributing in Issues
For any issue, there are fundamentally three ways an individual can contribute:
1. By opening the issue for discussion: For instance, if you believe that you
have discovered a bug in Tokio, creating a new issue in [the tokio-rs/tokio
issue tracker][issue] is the way to report it.
2. By helping to triage the issue: This can be done by providing
supporting details (a test case that demonstrates a bug), providing
suggestions on how to address the issue, or ensuring that the issue is tagged
correctly.
3. By helping to resolve the issue: Typically this is done either in the form of
demonstrating that the issue reported is not a problem after all, or more
often, by opening a Pull Request that changes some bit of something in
Tokio in a concrete and reviewable manner.
[issue]: https://github.com/tokio-rs/tokio/issues
**Anybody can participate in any stage of contribution**. We urge you to
participate in the discussion around bugs and participate in reviewing PRs.
### Asking for General Help
If you have reviewed existing documentation and still have questions or are
having problems, you can [open a discussion] asking for help.
In exchange for receiving help, we ask that you contribute back a documentation
PR that helps others avoid the problems that you encountered.
[open a discussion]: https://github.com/tokio-rs/tokio/discussions/new/choose
### Submitting a Bug Report
When opening a new issue in the Tokio issue tracker, you will be presented
with a basic template that should be filled in. If you believe that you have
uncovered a bug, please fill out this form, following the template to the best
of your ability. Do not worry if you cannot answer every detail, just fill in
what you can.
The two most important pieces of information we need in order to properly
evaluate the report is a description of the behavior you are seeing and a simple
test case we can use to recreate the problem on our own. If we cannot recreate
the issue, it becomes impossible for us to fix.
In order to rule out the possibility of bugs introduced by userland code, test
cases should be limited, as much as possible, to using only Tokio APIs.
See [How to create a Minimal, Complete, and Verifiable example][mcve].
[mcve]: https://stackoverflow.com/help/mcve
### Triaging a Bug Report
Once an issue has been opened, it is not uncommon for there to be discussion
around it. Some contributors may have differing opinions about the issue,
including whether the behavior being seen is a bug or a feature. This discussion
is part of the process and should be kept focused, helpful, and professional.
Short, clipped responses—that provide neither additional context nor supporting
detail—are not helpful or professional. To many, such responses are simply
annoying and unfriendly.
Contributors are encouraged to help one another make forward progress as much as
possible, empowering one another to solve issues collaboratively. If you choose
to comment on an issue that you feel either is not a problem that needs to be
fixed, or if you encounter information in an issue that you feel is incorrect,
explain why you feel that way with additional supporting context, and be willing
to be convinced that you may be wrong. By doing so, we can often reach the
correct outcome much faster.
### Resolving a Bug Report
In the majority of cases, issues are resolved by opening a Pull Request. The
process for opening and reviewing a Pull Request is similar to that of opening
and triaging issues, but carries with it a necessary review and approval
workflow that ensures that the proposed changes meet the minimal quality and
functional guidelines of the Tokio project.
@@ -0,0 +1,32 @@
# How to specify crates dependencies versions
Each crate (e.g., `tokio-util`, `tokio-stream`, etc.) should specify dependencies
according to the following rules:
1. The listed version should be the oldest version that the crate works with
(e.g., if `tokio-util` works with `tokio` version `1.44` but not `1.43`, then
`tokio-util` should specify version `1.44` for its `tokio` dependency).
We don't require users to use the latest version unnecessarily.
2. When a crate starts using a newer feature in a dependency, the version
should be bumped to the version that introduced it.
3. If a crate depends on an unreleased feature in a dependency, it may use
`path =` dependency to specify this. Since path dependencies must be removed
during the release of the crate, this ensures that it can't be released until
the dependency has a new version.
Consider the following example from `tokio-stream`:
```toml
[dependencies]
futures-core = { version = "0.3.0" }
pin-project-lite = "0.2.11"
tokio = { version = "1.38.0", path = "../tokio", features = ["sync"] }
```
In this case, local development of `tokio-stream` uses the local version
of `tokio` via the `path` dependency. This means that it's currently not
possible to release `tokio-stream`, and `tokio` should be released first.
Once a new version of `tokio` is released (in this example the `1.38.0`),
the path dependency should be removed.
As mentioned before, this version should only be bumped when adding a new
feature in the crate that relies on a newer version.
@@ -0,0 +1,89 @@
## Keeping track of issues and PRs
The Tokio GitHub repository has a lot of issues and PRs to keep track of. This
section explains the meaning of various labels, as well as our [GitHub
project][project]. The section is primarily targeted at maintainers. Most
contributors aren't able to set these labels.
### Area
The area label describes the crates relevant to this issue or PR.
- **A-ci** This issue concerns our GitHub Actions setup.
- **A-tokio** This issue concerns the main Tokio crate.
- **A-readme** This issue is related to documentation such as README.md.
- **A-benches** This issue concerns the benchmarks.
- **A-examples** This issue concerns the examples.
- **A-tokio-test** The issue concerns the `tokio-test` crate.
- **A-tokio-util** This issue concerns the `tokio-util` crate.
- **A-tokio-macros** This issue concerns the `tokio-macros` crate. Should only
be used for the procedural macros, and not `join!` or `select!`.
- **A-tokio-stream** This issue concerns the `tokio-stream` crate.
### Category
- **C-bug** This is a bug-report. Bug-fix PRs use `C-enhancement` instead.
- **C-enhancement** This is a PR that adds a new features.
- **C-maintenance** This is an issue or PR about stuff such as documentation,
GitHub Actions or code quality.
- **C-feature-request** This is a feature request. Implementations of feature
requests use `C-enhancement` instead.
- **C-feature-accepted** If you submit a PR for this feature request, we won't
close it with the reason "we don't want this". Issues with this label should
also have the `C-feature-request` label.
- **C-musing** Stuff like tracking issues or roadmaps. "musings about a better
world"
- **C-proposal** A proposal of some kind, and a request for comments.
- **C-question** A user question. Large overlap with GitHub discussions.
- **C-request** A non-feature request, e.g. "please add deprecation notices to
`-alpha.*` versions of crates"
### Calls for participation
- **E-help-wanted** Stuff where we want help. Often seen together with `C-bug`
or `C-feature-accepted`.
- **E-easy** This is easy, ranging from quick documentation fixes to stuff you
can do after reading the tutorial on our website.
- **E-medium** This is not `E-easy` or `E-hard`.
- **E-hard** This either involves very tricky code, is something we don't know
how to solve, or is challenging for some other reason.
- **E-needs-mvce** This bug is missing a minimal complete and verifiable
example.
The "E-" prefix is the same as used in the Rust compiler repository. Some
issues are missing a difficulty rating, but feel free to ask on our Discord
server if you want to know how challenging an issue likely is.
### Module
The module label provides a more fine grained categorization than **Area**.
- **M-blocking** Things relevant to `spawn_blocking`, `block_in_place`.
- **M-codec** The `tokio_util::codec` module.
- **M-compat** The `tokio_util::compat` module.
- **M-coop** Things relevant to coop.
- **M-fs** The `tokio::fs` module.
- **M-io** The `tokio::io` module.
- **M-macros** Issues about any kind of macro.
- **M-metrics** Things relevant to `tokio::runtime::metrics`.
- **M-net** The `tokio::net` module.
- **M-process** The `tokio::process` module.
- **M-runtime** The `tokio::runtime` module.
- **M-signal** The `tokio::signal` module.
- **M-sync** The `tokio::sync` module.
- **M-task** The `tokio::task` module.
- **M-time** The `tokio::time` module.
- **M-tracing** Tracing support in Tokio.
- **M-taskdump** Things relevant to taskdump.
### Topic
Some extra information.
- **T-docs** This is about documentation.
- **T-performance** This is about performance.
- **T-v0.1.x** This is about old Tokio.
Any label not listed here is not in active use.
[project]: https://github.com/orgs/tokio-rs/projects/1
+346
View File
@@ -0,0 +1,346 @@
## Pull Requests
Pull Requests are the way concrete changes are made to the code, documentation,
and dependencies in the Tokio repository.
Even tiny pull requests (e.g., one-character pull request fixing a typo in API
documentation) are greatly appreciated. Before making a large change, it is
usually a good idea to first open an issue describing the change to solicit
feedback and guidance. This will increase the likelihood of the PR getting
merged.
### Cargo Commands
Due to the extensive use of features in Tokio, you will often need to add extra
arguments to many common cargo commands. This section lists some commonly needed
commands.
Some commands just need the `--all-features` argument:
```
cargo build --all-features
cargo check --all-features
cargo test --all-features
```
**NOTE**: there are some features that are not supported in every system, so you might
need to specify which features you want to pass to cargo (e.g., `cargo check --features=full,io-uring`)
Ideally, you should use the same version of clippy as the one used in CI
(defined by `env.rust_clippy` in [ci.yml][ci.yml]), because newer versions
might have new lints:
[ci.yml]: ../../.github/workflows/ci.yml
<!--
When updating this, also update:
- .github/workflows/ci.yml
- README.md
- tokio/README.md
- tokio/Cargo.toml
- tokio-util/Cargo.toml
- tokio-test/Cargo.toml
- tokio-stream/Cargo.toml
-->
```
cargo +1.88 clippy --all --tests --all-features
```
When building documentation, a simple `cargo doc` is not sufficient. To produce
documentation equivalent to what will be produced in docs.rs's builds of Tokio's
docs, please use:
```
RUSTDOCFLAGS="--cfg docsrs --cfg tokio_unstable" RUSTFLAGS="--cfg docsrs --cfg tokio_unstable" cargo +nightly doc --all-features [--open]
```
This turns on indicators to display the Cargo features required for
conditionally compiled APIs in Tokio, and it enables documentation of unstable
Tokio features. Notice that it is necessary to pass cfg flags to both RustDoc
*and* rustc.
There is a more concise way to build docs.rs-equivalent docs by using [`cargo
docs-rs`], which reads the above documentation flags out of Tokio's Cargo.toml
as docs.rs itself does.
[`cargo docs-rs`]: https://github.com/dtolnay/cargo-docs-rs
```
cargo install --locked cargo-docs-rs
cargo +nightly docs-rs [--open]
```
The `cargo fmt` command does not work on the Tokio codebase. You can use the
command below instead:
```
# Mac or Linux
rustfmt --check --edition 2021 $(git ls-files '*.rs')
# Powershell
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2021 $_.FullName }
```
The `--check` argument prints the things that need to be fixed. If you remove
it, `rustfmt` will update your files locally instead.
You can run loom tests with
```
cd tokio # tokio crate in workspace
LOOM_MAX_PREEMPTIONS=1 LOOM_MAX_BRANCHES=10000 RUSTFLAGS="--cfg loom -C debug_assertions" \
cargo test --lib --release --features full -- --test-threads=1 --nocapture
```
Additionally, you can also add `--cfg tokio_unstable` to the `RUSTFLAGS` environment variable to
run loom tests that test unstable features.
You can run miri tests with
```
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-strict-provenance" \
cargo +nightly miri test --features full --lib --tests
```
### Performing spellcheck on tokio codebase
You can perform a spell-check on the Tokio codebase. For details of how to use the spellcheck tool, feel free to visit
https://github.com/drahnr/cargo-spellcheck
```
# First install the spell-check plugin
cargo install --locked cargo-spellcheck
# Then run the cargo spell check command
cargo spellcheck check
```
If the command rejects a word, you should backtick the rejected word if it's code related. If not, the
rejected word should be put into `spellcheck.dic` file.
Note that when you add a word into the file, you should also update the first line which tells the spellcheck tool
the total number of words included in the file
### Tests
If the change being proposed alters code (as opposed to only documentation for
example), it is either adding new functionality to Tokio or it is fixing
existing, broken functionality. In both of these cases, the pull request should
include one or more tests to ensure that Tokio does not regress in the future.
There are two ways to write tests: [integration tests][integration-tests]
and [documentation tests][documentation-tests].
(Tokio avoids [unit tests][unit-tests] as much as possible).
Tokio uses [conditional compilation attributes][conditional-compilation]
throughout the codebase, to modify rustc's behavior. Code marked with such
attributes can be enabled using `RUSTFLAGS` and `RUSTDOCFLAGS` environment
variables. One of the most prevalent flags passed in these variables is
the `--cfg` option. To run tests in a particular file, check first what
options #![cfg] declaration defines for that file.
For instance, to run a test marked with the 'tokio_unstable' cfg option,
you must pass this flag to the compiler when running the test.
```
$ RUSTFLAGS="--cfg tokio_unstable" cargo test -p tokio --all-features --test rt_metrics
```
#### Integration tests
Integration tests go in the same crate as the code they are testing. Each sub
crate should have a `dev-dependency` on `tokio` itself. This makes all Tokio
utilities available to use in tests, no matter the crate being tested.
The best strategy for writing a new integration test is to look at existing
integration tests in the crate and follow the style.
#### Fuzz tests
Some of our crates include a set of fuzz tests, this will be marked by a
directory `fuzz`. It is a good idea to run fuzz tests after each change.
To get started with fuzz testing you'll need to install
[cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz).
`cargo install --locked cargo-fuzz`
To list the available fuzzing harnesses you can run;
```bash
$ cd tokio
$ cargo fuzz list
fuzz_linked_list
```
Running a fuzz test is as simple as;
`cargo fuzz run fuzz_linked_list`
**NOTE**: Keep in mind that by default when running a fuzz test the fuzz
harness will run forever and will only exit if you `ctrl-c` or it finds
a bug.
#### Documentation tests
Ideally, every API has at least one [documentation test] that demonstrates how to
use the API. Documentation tests are run with `cargo test --doc`. This ensures
that the example is correct and provides additional test coverage.
The trick to documentation tests is striking a balance between being succinct
for a reader to understand and actually testing the API.
Same as with integration tests, when writing a documentation test, the full
`tokio` crate is available. This is especially useful for getting access to the
runtime to run the example.
The documentation tests will be visible from both the crate-specific
documentation **and** the `tokio` facade documentation via the re-export. The
example should be written from the point of view of a user that is using the
`tokio` crate. As such, the example should use the API via the facade and not by
directly referencing the crate.
The type level example for `tokio::time::timeout` provides a good example of a
documentation test:
```
/// Create a new `Timeout` set to expire in 10 milliseconds.
///
/// ```rust
/// use tokio::time::timeout;
/// use tokio::sync::oneshot;
///
/// use std::time::Duration;
///
/// # async fn dox() {
/// let (tx, rx) = oneshot::channel();
/// # tx.send(()).unwrap();
///
/// // Wrap the future with a `Timeout` set to expire in 10 milliseconds.
/// if let Err(_) = timeout(Duration::from_millis(10), rx).await {
/// println!("did not receive value within 10 ms");
/// }
/// # }
/// ```
```
Lines that start with `/// #` are removed when the documentation is generated.
### Benchmarks
You can run benchmarks locally for the changes you've made to the tokio codebase.
Tokio currently uses [Criterion](https://github.com/bheisler/criterion.rs) as its benchmarking tool. To run a benchmark
against the changes you have made, for example, you can run;
```bash
cd benches
# Run all benchmarks.
cargo bench
# Run all tests in the `benches/fs.rs` file
cargo bench --bench fs
# Run the `async_read_buf` benchmark in `benches/fs.rs` specifically.
cargo bench async_read_buf
# After running benches, you can check the statistics under `tokio/target/criterion/`
```
You can also refer to [Criterion] docs for additional options and details.
[Criterion]: https://docs.rs/criterion/latest/criterion/
### Commits
It is a recommended best practice to keep your changes as logically grouped as
possible within individual commits. There is no limit to the number of commits
any single Pull Request may have, and many contributors find it easier to review
changes that are split across multiple commits.
That said, if you have a number of commits that are "checkpoints" and don't
represent a single logical change, please squash those together.
Note that multiple commits often get squashed when they are landed (see the
notes about [commit squashing](#commit-squashing)).
#### Commit message guidelines
A good commit message should describe what changed and why.
1. The first line should:
* contain a short description of the change (preferably 50 characters or less,
and no more than 72 characters)
* be entirely in lowercase with the exception of proper nouns, acronyms, and
the words that refer to code, like function/variable names
* start with an imperative verb
* not have a period at the end
* be prefixed with the name of the module being changed; usually this is the
same as the M-* label on the PR
Examples:
* time: introduce `Timeout` and deprecate `Deadline`
* codec: export `Encoder`, `Decoder`, `Framed*`
* ci: fix the FreeBSD ci configuration
2. Keep the second line blank.
3. Wrap all other lines at 72 columns (except for long URLs).
4. If your patch fixes an open issue, you can add a reference to it at the end
of the log. Use the `Fixes: #` prefix and the issue number. For other
references use `Refs: #`. `Refs` may include multiple issues, separated by a
comma.
Examples:
- `Fixes: #1337`
- `Refs: #1234`
Sample complete commit message:
```txt
module: explain the commit in one line
Body of commit message is a few lines of text, explaining things
in more detail, possibly giving some background about the issue
being fixed, etc.
The body of the commit message can be several paragraphs, and
please do proper word-wrap and keep columns shorter than about
72 characters or so. That way, `git log` will show things
nicely even when it is indented.
Fixes: #1337
Refs: #453, #154
```
### Opening the Pull Request
From within GitHub, opening a new Pull Request will present you with a
[template] that should be filled out. Please try to do your best at filling out
the details, but feel free to skip parts if you're not sure what to put.
[template]: ../../.github/PULL_REQUEST_TEMPLATE.md
### Discuss and update
You will probably get feedback or requests for changes to your Pull Request.
This is a big part of the submission process so don't be discouraged! Some
contributors may sign off on the Pull Request right away, others may have
more detailed comments or feedback. This is a necessary part of the process
in order to evaluate whether the changes are correct and necessary.
**Any community member can review a PR and you might get conflicting feedback**.
Keep an eye out for comments from code owners to provide guidance on conflicting
feedback.
**Once the PR is open, do not rebase the commits**. See [Commit Squashing](#commit-squashing) for
more details.
### Commit Squashing
In most cases, **do not squash commits that you add to your Pull Request during
the review process**. When the commits in your Pull Request land, they may be
squashed into one commit per logical change. Metadata will be added to the
commit message (including links to the Pull Request, links to relevant issues,
and the names of the reviewers). The commit history of your Pull Request,
however, will stay intact on the Pull Request page.
[integration-tests]: https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html
[unit-tests]: https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html
[documentation-tests]: https://doc.rust-lang.org/rust-by-example/testing/doc_testing.html
[conditional-compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
@@ -0,0 +1,80 @@
## Reviewing Pull Requests
**Any Tokio community member is welcome to review any pull request**.
All Tokio contributors who choose to review and provide feedback on Pull
Requests have a responsibility to both the project and the individual making the
contribution. Reviews and feedback must be helpful, insightful, and geared
towards improving the contribution as opposed to simply blocking it. If there
are reasons why you feel the PR should not land, explain what those are. Do not
expect to be able to block a Pull Request from advancing simply because you say
"No" without giving an explanation. Be open to having your mind changed. Be open
to working with the contributor to make the Pull Request better.
Reviews that are dismissive or disrespectful of the contributor or any other
reviewers are strictly counter to the Code of Conduct.
When reviewing a Pull Request, the primary goals are for the codebase to improve
and for the person submitting the request to succeed. **Even if a Pull Request
does not land, the submitters should come away from the experience feeling like
their effort was not wasted or unappreciated**. Every Pull Request from a new
contributor is an opportunity to grow the community.
### Review a bit at a time
Do not overwhelm new contributors.
It is tempting to micro-optimize and make everything about relative performance,
perfect grammar, or exact style matches. Do not succumb to that temptation.
Focus first on the most significant aspects of the change:
1. Does this change make sense for Tokio?
2. Does this change make Tokio better, even if only incrementally?
3. Are there clear bugs or larger scale issues that need attending to?
4. Is the commit message readable and correct? If it contains a breaking change
is it clear enough?
Note that only **incremental** improvement is needed to land a PR. This means
that the PR does not need to be perfect, only better than the status quo. Follow
up PRs may be opened to continue iterating.
When changes are necessary, *request* them, do not *demand* them, and **do not
assume that the submitter already knows how to add a test or run a benchmark**.
Specific performance optimization techniques, coding styles and conventions
change over time. The first impression you give to a new contributor never does.
Nits (requests for small changes that are not essential) are fine, but try to
avoid stalling the Pull Request. Most nits can typically be fixed by the Tokio
Collaborator landing the Pull Request but they can also be an opportunity for
the contributor to learn a bit more about the project.
It is always good to clearly indicate nits when you comment: e.g.
`Nit: change foo() to bar(). But this is not blocking.`
If your comments were addressed but were not folded automatically after new
commits or if they proved to be mistaken, please, [hide them][hiding-a-comment]
with the appropriate reason to keep the conversation flow concise and relevant.
### Be aware of the person behind the code
Be aware that *how* you communicate requests and reviews in your feedback can
have a significant impact on the success of the Pull Request. Yes, we may land
a particular change that makes Tokio better, but the individual might just not
want to have anything to do with Tokio ever again. The goal is not just having
good code.
### Abandoned or Stalled Pull Requests
If a Pull Request appears to be abandoned or stalled, it is polite to first
check with the contributor to see if they intend to continue the work before
checking if they would mind if you took it over (especially if it just has nits
left). When doing so, it is courteous to give the original contributor credit
for the work they started (either by preserving their name and email address in
the commit log, or by using an `Author: ` meta-data tag in the commit.
_Adapted from the [Node.js contributing guide][node]_.
[node]: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md
[hiding-a-comment]: https://help.github.com/articles/managing-disruptive-comments/#hiding-a-comment
+30 -9
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**.
@@ -15,34 +16,47 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3.1", default-features = false, features = ["fmt", "ansi", "env-filter", "tracing-log"] }
bytes = "1.0.0"
futures = { version = "0.3.0", features = ["thread-pool"]}
http = "0.2"
http = "1"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
httparse = "1.0"
httpdate = "1.0"
once_cell = "1.5.2"
rand = "0.8.3"
[target.'cfg(target_os = "linux")'.dev-dependencies]
libc = "0.2"
[target.'cfg(all(tokio_unstable, target_os = "linux"))'.dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full", "tracing", "taskdump"] }
[target.'cfg(windows)'.dev-dependencies.windows-sys]
version = "0.52"
version = "0.61"
[[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 = "graceful-shutdown"
path = "graceful-shutdown.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 +108,10 @@ path = "named-pipe-multi-client.rs"
[[example]]
name = "dump"
path = "dump.rs"
[[example]]
name = "prewarm-fd-table"
path = "prewarm-fd-table.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.
+35 -22
View File
@@ -39,6 +39,8 @@ use std::io;
use std::net::SocketAddr;
use std::sync::Arc;
const DEFAULT_ADDR: &str = "127.0.0.1:6142";
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter};
@@ -70,14 +72,14 @@ async fn main() -> Result<(), Box<dyn Error>> {
let addr = env::args()
.nth(1)
.unwrap_or_else(|| "127.0.0.1:6142".to_string());
.unwrap_or_else(|| DEFAULT_ADDR.to_string());
// Bind a TCP listener to the socket address.
//
// Note that this is the Tokio TcpListener, which is fully async.
let listener = TcpListener::bind(&addr).await?;
tracing::info!("server running on {}", addr);
tracing::info!("server running on {addr}");
loop {
// Asynchronously wait for an inbound TcpStream.
@@ -88,9 +90,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Spawn our handler to be run asynchronously.
tokio::spawn(async move {
tracing::debug!("accepted connection");
tracing::debug!("accepted connection from {addr}");
if let Err(e) = process(state, stream, addr).await {
tracing::info!("an error occurred; error = {:?}", e);
tracing::warn!("Connection from {addr} failed: {e:?}");
}
});
}
@@ -138,12 +140,24 @@ impl Shared {
/// Send a `LineCodec` encoded message to every peer, except
/// for the sender.
///
/// This function also cleans up disconnected peers automatically.
async fn broadcast(&mut self, sender: SocketAddr, message: &str) {
for peer in self.peers.iter_mut() {
if *peer.0 != sender {
let _ = peer.1.send(message.into());
let mut failed_peers = Vec::new();
let message = message.to_string(); // Clone once for all sends
for (addr, tx) in self.peers.iter() {
if *addr != sender && tx.send(message.clone()).is_err() {
// Receiver has been dropped, mark for removal
failed_peers.push(*addr);
}
}
// Clean up disconnected peers
for addr in failed_peers {
self.peers.remove(&addr);
tracing::debug!("Removed disconnected peer: {addr}");
}
}
}
@@ -178,13 +192,10 @@ async fn process(
lines.send("Please enter your username:").await?;
// Read the first line from the `LineCodec` stream to get the username.
let username = match lines.next().await {
Some(Ok(line)) => line,
let Some(Ok(username)) = lines.next().await else {
// We didn't get a line so we return early here.
_ => {
tracing::error!("Failed to get username from {}. Client disconnected.", addr);
return Ok(());
}
tracing::error!("Failed to get username from {addr}. Client disconnected.");
return Ok(());
};
// Register our peer with state which internally sets up some channels.
@@ -193,8 +204,8 @@ 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);
tracing::info!("{}", msg);
let msg = format!("{username} has joined the chat");
tracing::info!("{msg}");
state.broadcast(addr, &msg).await;
}
@@ -203,24 +214,26 @@ async fn process(
tokio::select! {
// A message was received from a peer. Send it to the current user.
Some(msg) = peer.rx.recv() => {
peer.lines.send(&msg).await?;
if let Err(e) = peer.lines.send(&msg).await {
tracing::error!("Failed to send message to {username}: {e:?}");
break;
}
}
result = peer.lines.next() => match result {
// A message was received from the current user, we should
// 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;
}
// An error occurred.
Some(Err(e)) => {
tracing::error!(
"an error occurred while processing messages for {}; error = {:?}",
username,
e
"an error occurred while processing messages for {username}; error = {e:?}"
);
break;
}
// The stream has been exhausted.
None => break,
@@ -234,8 +247,8 @@ async fn process(
let mut state = state.lock().await;
state.peers.remove(&addr);
let msg = format!("{} has left the chat", username);
tracing::info!("{}", msg);
let msg = format!("{username} has left the chat");
tracing::info!("{msg}");
state.broadcast(addr, &msg).await;
}
+73
View File
@@ -0,0 +1,73 @@
//! 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) => {
eprintln!("failed to read from socket; error={e}");
future::ready(None)
}
})
.map(Ok);
tokio::select! {
r = sink.send_all(&mut stdin) => r?,
r = stdout.send_all(&mut stream) => r?,
}
Ok(())
}
+94
View File
@@ -0,0 +1,94 @@
//! 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::select! {
r = send(stdin, &socket) => r?,
r = recv(stdout, &socket) => r?,
}
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::copy_from_slice(&buf[..n])).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?;
}
}
}
}
-4
View File
@@ -1,12 +1,9 @@
#![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.
#[cfg(all(
tokio_unstable,
tokio_taskdump,
target_os = "linux",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
))]
@@ -84,7 +81,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(not(all(
tokio_unstable,
tokio_taskdump,
target_os = "linux",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
)))]
+27 -20
View File
@@ -9,14 +9,14 @@
//!
//! 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
//! you! If you open up multiple terminals running the `connect` example you
//! 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-tcp` example you
//! should be able to see them all make progress simultaneously.
#![warn(rust_2018_idioms)]
@@ -27,6 +27,9 @@ use tokio::net::TcpListener;
use std::env;
use std::error::Error;
const DEFAULT_ADDR: &str = "127.0.0.1:8080";
const BUFFER_SIZE: usize = 4096;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Allow passing an address to listen on as the first argument of this
@@ -34,17 +37,17 @@ async fn main() -> Result<(), Box<dyn Error>> {
// 127.0.0.1:8080 for connections.
let addr = env::args()
.nth(1)
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
.unwrap_or_else(|| DEFAULT_ADDR.to_string());
// Next up we create a TCP listener which will listen for incoming
// 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.
let (mut socket, _) = listener.accept().await?;
let (mut socket, addr) = listener.accept().await?;
// And this is where much of the magic of this server happens. We
// crucially want all clients to make progress concurrently, rather than
@@ -55,23 +58,27 @@ async fn main() -> Result<(), Box<dyn Error>> {
// which will allow all of our clients to be processed concurrently.
tokio::spawn(async move {
let mut buf = vec![0; 1024];
let mut buf = vec![0; BUFFER_SIZE];
// In a loop, read data from the socket and write the data back.
loop {
let n = socket
.read(&mut buf)
.await
.expect("failed to read data from socket");
if n == 0 {
return;
match socket.read(&mut buf).await {
Ok(0) => {
// Connection closed by peer
return;
}
Ok(n) => {
// Write the data back. If writing fails, log the error and exit.
if let Err(e) = socket.write_all(&buf[0..n]).await {
eprintln!("Failed to write to socket {}: {}", addr, e);
return;
}
}
Err(e) => {
eprintln!("Failed to read from socket {}: {}", addr, e);
return;
}
}
socket
.write_all(&buf[0..n])
.await
.expect("failed to write data to 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
+120
View File
@@ -0,0 +1,120 @@
//! Graceful shutdown example.
//!
//! This example follows the same approach described in the
//! [Graceful Shutdown tutorial](https://tokio.rs/tokio/topics/shutdown):
//!
//! - A [`CancellationToken`] tells tasks to stop accepting new work.
//! - A [`TaskTracker`] waits for in-flight work to complete.
//!
//! It runs a TCP echo server on `127.0.0.1:6142`. When Ctrl+C is
//! pressed, the server stops accepting connections and waits for all
//! active connections to finish before exiting.
//!
//! Start the server:
//!
//! cargo run --example graceful-shutdown
//!
//! Then connect with:
//!
//! nc 127.0.0.1 6142
//!
//! Press Ctrl+C on the server to trigger a graceful shutdown.
#![warn(rust_2018_idioms)]
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::{TcpListener, TcpStream};
use tokio::time::{self, Duration};
use tokio_util::sync::CancellationToken;
use tokio_util::task::TaskTracker;
use std::error::Error;
use std::net::SocketAddr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let listener = TcpListener::bind("127.0.0.1:6142").await?;
println!("listening on 127.0.0.1:6142");
let token = CancellationToken::new();
let tracker = TaskTracker::new();
loop {
tokio::select! {
result = listener.accept() => {
let (socket, addr) = match result {
Ok(conn) => conn,
Err(e) => {
// Transient errors (e.g. fd exhaustion) are recoverable,
// so we log and continue. A production server might add a
// backoff or break on fatal errors to avoid a busy loop.
eprintln!("failed to accept: {e}");
continue;
}
};
println!("accepted connection from {addr}");
let token = token.clone();
tracker.spawn(handle_connection(socket, addr, token));
}
_ = tokio::signal::ctrl_c() => {
println!("\nshutdown signal received, waiting for connections to finish");
break;
}
}
}
// Signal all tasks to stop and wait for them to complete.
token.cancel();
tracker.close();
tracker.wait().await;
println!("shutdown complete");
Ok(())
}
async fn handle_connection(mut socket: TcpStream, addr: SocketAddr, token: CancellationToken) {
tokio::select! {
_ = echo(&mut socket) => {}
_ = token.cancelled() => {
notify_shutdown(&mut socket).await;
}
}
println!("connection from {addr} closed");
}
/// Reads lines from the client and writes them back.
///
/// Called for every accepted connection. Runs until the client disconnects
/// or a read/write error occurs.
async fn echo(socket: &mut TcpStream) {
let (reader, mut writer) = socket.split();
let mut reader = BufReader::new(reader);
let mut line = String::new();
loop {
match reader.read_line(&mut line).await {
Ok(0) | Err(_) => return,
Ok(_) => {
if writer.write_all(line.as_bytes()).await.is_err() {
return;
}
line.clear();
}
}
}
}
/// Sends a shutdown notice to the client before closing the connection.
///
/// Called when the cancellation token fires. Uses a timeout so that a
/// slow or unresponsive client cannot hold up the server shutdown.
async fn notify_shutdown(socket: &mut TcpStream) {
let _ = time::timeout(
Duration::from_secs(1),
socket.write_all(b"server shutting down\n"),
)
.await;
}
+86
View File
@@ -0,0 +1,86 @@
//! Demonstrates pre-warming the Linux file descriptor table to avoid latency
//! spikes caused by file descriptor table growth in multi-threaded processes.
//!
//! On Linux, the kernel's FD table is grown lazily and protected by RCU
//! synchronization. In multi-threaded processes, when a syscall like `socket()`
//! triggers a table resize, the calling thread blocks until all RCU readers
//! quiesce. This can cause stalls of tens of milliseconds on tokio worker threads,
//! blocking the entire event loop (not just one task).
//!
//! The workaround is to force the kernel to expand the FD table once per process
//! (before any runtime starts), by duplicating an FD to a high slot and then
//! closing it. The kernel never shrinks the FD table during a process's lifetime,
//! so the capacity persists.
//!
//! This is most relevant for services that open many connections concurrently
//! (e.g. HTTP servers, connection pools). The pre-warm target should be at least
//! your expected peak FD count, and must not exceed `RLIMIT_NOFILE`.
//!
//! See: <https://github.com/tokio-rs/tokio/issues/7970>
//!
//! Usage:
//!
//! cargo run --example prewarm-fd-table
#![warn(rust_2018_idioms)]
/// Pre-warms the FD table using `fcntl(F_DUPFD_CLOEXEC)` to duplicate an FD
/// into a high slot, expanding the table in a single syscall. `F_DUPFD_CLOEXEC`
/// allocates the lowest available FD >= `target`, so it never clobbers an
/// existing FD.
#[cfg(target_os = "linux")]
fn prewarm_fd_table(target: i32) -> std::io::Result<()> {
use std::os::unix::io::{FromRawFd, OwnedFd};
let dev_null = std::fs::File::open("/dev/null")?;
let raw = unsafe {
libc::fcntl(
std::os::unix::io::AsRawFd::as_raw_fd(&dev_null),
libc::F_DUPFD_CLOEXEC,
target,
)
};
if raw < 0 {
return Err(std::io::Error::last_os_error());
}
// Close both FDs. The table capacity persists.
let _owned = unsafe { OwnedFd::from_raw_fd(raw) };
drop(dev_null);
Ok(())
}
/// Fully safe alternative using only stdlib. Requires O(n) syscalls instead of
/// one, but avoids `unsafe` entirely.
#[cfg(target_os = "linux")]
#[allow(dead_code)]
fn prewarm_fd_table_safe(target: i32) -> std::io::Result<()> {
let f = std::fs::File::open("/dev/null")?;
let _fds: Vec<_> = (0..target)
.map(|_| f.try_clone())
.collect::<Result<_, _>>()?;
Ok(())
}
fn main() {
#[cfg(target_os = "linux")]
{
const FD_TARGET: i32 = 10_000;
println!("Pre-warming FD table to {FD_TARGET} entries...");
if let Err(e) = prewarm_fd_table(FD_TARGET) {
eprintln!("Warning: failed to pre-warm FD table: {e}");
} else {
println!("FD table pre-warmed successfully.");
}
}
// Build the runtime *after* pre-warming.
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async {});
}
+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
+12 -12
View File
@@ -12,14 +12,14 @@
//!
//! 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:
//!
//!
//! $ cargo run --example connect 127.0.0.1:8080
//! $ cargo run --example connect-tcp 127.0.0.1:8080
//! GET foo
//! foo = bar
//! GET FOOBAR
@@ -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!"),
}
+4 -2
View File
@@ -8,9 +8,11 @@
RUSTDOCFLAGS="""
--cfg docsrs \
--cfg tokio_unstable \
--cfg tokio_taskdump \
"""
RUSTFLAGS="--cfg tokio_unstable --cfg tokio_taskdump --cfg docsrs"
RUSTFLAGS="""
--cfg docsrs \
--cfg tokio_unstable
"""
[[redirects]]
from = "/"
+36 -4
View File
@@ -1,4 +1,4 @@
285
316
&
+
<
@@ -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
@@ -57,21 +64,31 @@ codec
codecs
combinator
combinators
condvar
config
Config
connectionless
coroutines
cpu
cpus
cqe
CQE
cqe's
customizable
Customizable
Cygwin
datagram
Datagram
datagrams
deallocate
deallocated
Deallocates
debuginfo
decrement
decrementing
demangled
dequeued
dereferenced
deregister
deregistered
deregistering
@@ -99,6 +116,7 @@ errored
EWMA
expirations
fcntl
fd
fd's
FIFOs
filename
@@ -115,9 +133,12 @@ GID
goroutines
Growable
gzip
H2
hashmaps
HashMaps
hashsets
HdrHistogram
ICMP
ie
Illumos
impl
@@ -125,6 +146,7 @@ implementers
implementor
implementors
incrementing
inlining
interoperate
invariants
Invariants
@@ -143,6 +165,7 @@ Lauck
libc
lifecycle
lifo
LLVM
lookups
macOS
MacOS
@@ -151,8 +174,10 @@ metadata
mio
Mio
mio's
miri
misconfigured
mock's
monomorphization
mpmc
mpsc
multi
@@ -163,15 +188,17 @@ mut
mutex
Mutex
Nagle
namespace
nonblocking
nondecreasing
noop
ntasks
NUMA
ok
oneshot
opcode
ORed
os
overweighing
parker
parsers
peekable
@@ -183,6 +210,7 @@ POSIX
proxied
qos
RAII
RCU
reallocations
recv's
refactors
@@ -214,6 +242,7 @@ spawner
Splitter
spmc
spsc
SQE
src
stabilised
startup
@@ -265,9 +294,11 @@ unparks
Unparks
unreceived
unsafety
unsets
Unsets
unsynchronized
untrusted
uring
usecases
Valgrind
Varghese
@@ -281,5 +312,6 @@ wakers
Wakers
wakeup
wakeups
WASI
workstealing
ZST
+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
+7 -5
View File
@@ -4,6 +4,7 @@
"crt-objects-fallback": "false",
"crt-static-respected": true,
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
"default-uwtable": true,
"dynamic-linking": true,
"env": "gnu",
"has-rpath": true,
@@ -12,10 +13,10 @@
"llvm-target": "i686-unknown-linux-gnu",
"max-atomic-width": 32,
"metadata": {
"description": null,
"host_tools": null,
"std": null,
"tier": null
"description": "32-bit Linux (kernel 3.2, glibc 2.17+)",
"host_tools": true,
"std": true,
"tier": 1
},
"os": "linux",
"position-independent-executables": true,
@@ -28,6 +29,7 @@
]
},
"relro-level": "full",
"rustc-abi": "x86-sse2",
"stack-probes": {
"kind": "inline"
},
@@ -42,5 +44,5 @@
"target-family": [
"unix"
],
"target-pointer-width": "32"
"target-pointer-width": 32
}
+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.
@@ -68,4 +68,7 @@ async fn test_has_second_test_attr_rust_2021() {}
#[tokio::test]
async fn test_has_generated_second_test_attr() {}
#[tokio::test(name = 123)]
async fn test_name_not_string() {}
fn main() {}
@@ -4,7 +4,7 @@ error: the `async` keyword is missing from the function declaration
6 | fn main_is_not_async() {}
| ^^
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`, `name`.
--> tests/fail/macros_invalid_input.rs:8:15
|
8 | #[tokio::main(foo)]
@@ -22,13 +22,13 @@ error: the `async` keyword is missing from the function declaration
15 | fn test_is_not_async() {}
| ^^
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`, `name`.
--> tests/fail/macros_invalid_input.rs:17:15
|
17 | #[tokio::test(foo)]
| ^^^
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`, `name`.
--> tests/fail/macros_invalid_input.rs:20:15
|
20 | #[tokio::test(foo = 123)]
@@ -40,7 +40,7 @@ error: Failed to parse value of `flavor` as string.
23 | #[tokio::test(flavor = 123)]
| ^^^
error: No such runtime flavor `foo`. The runtime flavors are `current_thread` and `multi_thread`.
error: No such runtime flavor `foo`. The runtime flavors are `current_thread`, `local`, and `multi_thread`.
--> tests/fail/macros_invalid_input.rs:26:24
|
26 | #[tokio::test(flavor = "foo")]
@@ -119,3 +119,9 @@ error: second test attribute is supplied, consider removing or changing the orde
| ^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `tokio::test` (in Nightly builds, run with -Z macro-backtrace for more info)
error: Failed to parse value of `name` as string.
--> tests/fail/macros_invalid_input.rs:71:22
|
71 | #[tokio::test(name = 123)]
| ^^^
+42
View File
@@ -0,0 +1,42 @@
use tests_build::tokio;
#[tokio::main]
async fn main() {
// do not leak `RotatorSelect`
let _ = tokio::join!(async {
fn foo(_: impl RotatorSelect) {}
});
// do not leak `std::task::Poll::Pending`
let _ = tokio::join!(async { Pending });
// do not leak `std::task::Poll::Ready`
let _ = tokio::join!(async { Ready(0) });
// do not leak `std::future::Future`
let _ = tokio::join!(async {
struct MyFuture;
impl Future for MyFuture {
type Output = ();
fn poll(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
todo!()
}
}
});
// do not leak `std::pin::Pin`
let _ = tokio::join!(async {
let mut x = 5;
let _ = Pin::new(&mut x);
});
// do not leak `std::future::poll_fn`
let _ = tokio::join!(async {
let _ = poll_fn(|_cx| todo!());
});
}
+60
View File
@@ -0,0 +1,60 @@
error[E0405]: cannot find trait `RotatorSelect` in this scope
--> tests/fail/macros_join.rs:7:24
|
7 | fn foo(_: impl RotatorSelect) {}
| ^^^^^^^^^^^^^ not found in this scope
error[E0425]: cannot find value `Pending` in this scope
--> tests/fail/macros_join.rs:11:34
|
11 | let _ = tokio::join!(async { Pending });
| ^^^^^^^ not found in this scope
|
help: consider importing this unit variant
|
1 + use std::task::Poll::Pending;
|
error[E0425]: cannot find function, tuple struct or tuple variant `Ready` in this scope
--> tests/fail/macros_join.rs:14:34
|
14 | let _ = tokio::join!(async { Ready(0) });
| ^^^^^ not found in this scope
|
help: consider importing this tuple variant
|
1 + use std::task::Poll::Ready;
|
error[E0405]: cannot find trait `Future` in this scope
--> tests/fail/macros_join.rs:20:14
|
20 | impl Future for MyFuture {
| ^^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use std::future::Future;
|
error[E0433]: cannot find type `Pin` in this scope
--> tests/fail/macros_join.rs:35:17
|
35 | let _ = Pin::new(&mut x);
| ^^^ use of undeclared type `Pin`
|
help: consider importing this struct
|
1 + use std::pin::Pin;
|
error[E0425]: cannot find function `poll_fn` in this scope
--> tests/fail/macros_join.rs:40:17
|
40 | let _ = poll_fn(|_cx| todo!());
| ^^^^^^^ not found in this scope
|
help: consider importing this function
|
1 + use std::future::poll_fn;
|
+42
View File
@@ -0,0 +1,42 @@
use tests_build::tokio;
#[tokio::main]
async fn main() {
// do not leak `RotatorSelect`
let _ = tokio::try_join!(async {
fn foo(_: impl RotatorSelect) {}
});
// do not leak `std::task::Poll::Pending`
let _ = tokio::try_join!(async { Pending });
// do not leak `std::task::Poll::Ready`
let _ = tokio::try_join!(async { Ready(0) });
// do not leak `std::future::Future`
let _ = tokio::try_join!(async {
struct MyFuture;
impl Future for MyFuture {
type Output = ();
fn poll(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
todo!()
}
}
});
// do not leak `std::pin::Pin`
let _ = tokio::try_join!(async {
let mut x = 5;
let _ = Pin::new(&mut x);
});
// do not leak `std::future::poll_fn`
let _ = tokio::try_join!(async {
let _ = poll_fn(|_cx| todo!());
});
}
@@ -0,0 +1,60 @@
error[E0405]: cannot find trait `RotatorSelect` in this scope
--> tests/fail/macros_try_join.rs:7:24
|
7 | fn foo(_: impl RotatorSelect) {}
| ^^^^^^^^^^^^^ not found in this scope
error[E0425]: cannot find value `Pending` in this scope
--> tests/fail/macros_try_join.rs:11:38
|
11 | let _ = tokio::try_join!(async { Pending });
| ^^^^^^^ not found in this scope
|
help: consider importing this unit variant
|
1 + use std::task::Poll::Pending;
|
error[E0425]: cannot find function, tuple struct or tuple variant `Ready` in this scope
--> tests/fail/macros_try_join.rs:14:38
|
14 | let _ = tokio::try_join!(async { Ready(0) });
| ^^^^^ not found in this scope
|
help: consider importing this tuple variant
|
1 + use std::task::Poll::Ready;
|
error[E0405]: cannot find trait `Future` in this scope
--> tests/fail/macros_try_join.rs:20:14
|
20 | impl Future for MyFuture {
| ^^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use std::future::Future;
|
error[E0433]: cannot find type `Pin` in this scope
--> tests/fail/macros_try_join.rs:35:17
|
35 | let _ = Pin::new(&mut x);
| ^^^ use of undeclared type `Pin`
|
help: consider importing this struct
|
1 + use std::pin::Pin;
|
error[E0425]: cannot find function `poll_fn` in this scope
--> tests/fail/macros_try_join.rs:40:17
|
40 | let _ = poll_fn(|_cx| todo!());
| ^^^^^^^ not found in this scope
|
help: consider importing this function
|
1 + use std::future::poll_fn;
|
@@ -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]
@@ -1,3 +1,14 @@
error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:3:1: 3:15}` to be a future that resolves to `()`, but it resolves to `Result<(), _>`
--> tests/fail/macros_type_mismatch.rs:3:1
|
3 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `()`, found `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:3:1: 3:15}` to `&dyn Future<Output = ()>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:5:5
|
@@ -15,6 +26,17 @@ help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panic
5 | Ok(()).expect("REASON")
| +++++++++++++++++
error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:8:1: 8:15}` to be a future that resolves to `()`, but it resolves to `Result<(), _>`
--> tests/fail/macros_type_mismatch.rs:8:1
|
8 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `()`, found `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:8:1: 8:15}` to `&dyn Future<Output = ()>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:10:5
|
@@ -25,13 +47,24 @@ error[E0308]: mismatched types
found enum `Result<(), _>`
help: a return type might be missing here
|
9 | async fn missing_return_type() -> _ {
9 | async fn missing_return_type() -> _ {
| ++++
help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panicking if the value is a `Result::Err`
|
10 | return Ok(());.expect("REASON")
| +++++++++++++++++
error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:13:1: 13:15}` to be a future that resolves to `Result<(), ()>`, but it resolves to `()`
--> tests/fail/macros_type_mismatch.rs:13:1
|
13 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `Result<(), ()>`, found `()`
|
= note: expected enum `Result<(), ()>`
found unit type `()`
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:13:1: 13:15}` to `&dyn Future<Output = Result<(), ()>>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:23:5
|
@@ -49,11 +82,95 @@ 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[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:38:1: 38:15}` to be a future that resolves to `Option<()>`, but it resolves to `()`
--> tests/fail/macros_type_mismatch.rs:38:1
|
38 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `Option<()>`, found `()`
|
= note: expected enum `Option<()>`
found unit type `()`
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:38:1: 38:15}` to `&dyn Future<Output = Option<()>>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
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[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:55:1: 55:15}` to be a future that resolves to `Result<(), ()>`, but it resolves to `()`
--> tests/fail/macros_type_mismatch.rs:55:1
|
55 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `Result<(), ()>`, found `()`
|
= note: expected enum `Result<(), ()>`
found unit type `()`
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:55:1: 55:15}` to `&dyn Future<Output = Result<(), ()>>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
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[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:63:1: 63:15}` to be a future that resolves to `()`, but it resolves to `{integer}`
--> tests/fail/macros_type_mismatch.rs:63:1
|
63 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `()`, found integer
|
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:63:1: 63:15}` to `&dyn Future<Output = ()>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
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
+13
View File
@@ -1,4 +1,5 @@
#[test]
#[cfg_attr(miri, ignore)]
fn compile_fail_full() {
let t = trybuild::TestCases::new();
@@ -11,12 +12,24 @@ fn compile_fail_full() {
#[cfg(feature = "full")]
t.pass("tests/pass/macros_main_loop.rs");
#[cfg(feature = "full")]
t.pass("tests/pass/impl_trait.rs");
#[cfg(feature = "full")]
t.pass("tests/pass/use_builder_outer.rs");
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_invalid_input.rs");
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_dead_code.rs");
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_join.rs");
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_try_join.rs");
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_type_mismatch.rs");
+23
View File
@@ -0,0 +1,23 @@
use tests_build::tokio;
#[tokio::main]
async fn never() -> ! {
loop {}
}
#[tokio::main]
async fn impl_trait() -> impl Iterator<Item = impl core::fmt::Debug> {
[()].into_iter()
}
#[tokio::main]
async fn impl_trait2() -> Result<(), impl core::fmt::Debug> {
Err(())
}
fn main() {
if impl_trait().count() == 10 {
never();
}
let _ = impl_trait2();
}
@@ -0,0 +1,9 @@
#![deny(unused_qualifications)]
use tests_build::tokio;
pub use tokio::runtime;
#[tokio::main]
async fn main() {
if true {}
}
+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
+11 -1
View File
@@ -16,6 +16,13 @@ async fn spawning() -> usize {
join.await.unwrap()
}
#[cfg(tokio_unstable)]
#[tokio::main(flavor = "local")]
async fn local_main() -> usize {
let join = tokio::task::spawn_local(async { 1 });
join.await.unwrap()
}
#[test]
fn main_with_spawn() {
assert_eq!(1, spawning());
@@ -24,5 +31,8 @@ fn main_with_spawn() {
#[test]
fn shell() {
assert_eq!(1, basic_main());
assert_eq!(bool::default(), generic_fun::<bool>())
assert_eq!(bool::default(), generic_fun::<bool>());
#[cfg(tokio_unstable)]
assert_eq!(1, local_main());
}
+3 -3
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);
}
};
+35
View File
@@ -1,3 +1,38 @@
# 2.7.0 (April 3rd, 2026)
- macros: stabilize `LocalRuntime` ([#7557])
- macros: add runtime name ([#7924])
[#7557]: https://github.com/tokio-rs/tokio/pull/7557
[#7924]: https://github.com/tokio-rs/tokio/pull/7924
# 2.6.1 (Mar 2nd, 2026)
- macros: improve error message for return type mismatch in #[tokio::main] ([#7856])
- macros: use call_site hygiene to avoid unused qualification ([#7866])
[#7856]: https://github.com/tokio-rs/tokio/pull/7856
[#7866]: https://github.com/tokio-rs/tokio/pull/7866
# 2.6.0 (Oct 14th, 2025)
The MSRV is raised to 1.71.
- msrv: increase MSRV to 1.71 ([#7658])
- macros: add `local` runtime flavor ([#7375], [#7597])
- macros: suppress `clippy::unwrap_in_result` in `#[tokio::main]` ([#7651])
[#7375]: https://github.com/tokio-rs/tokio/pull/7375
[#7597]: https://github.com/tokio-rs/tokio/pull/7597
[#7651]: https://github.com/tokio-rs/tokio/pull/7651
[#7658]: https://github.com/tokio-rs/tokio/pull/7658
# 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])
+8 -5
View File
@@ -1,12 +1,12 @@
[package]
name = "tokio-macros"
# When releasing to crates.io:
# - Remove path dependencies
# - Remove path dependencies (if any)
# - Update CHANGELOG.md.
# - Create "tokio-macros-1.x.y" git tag.
version = "2.4.0"
# - Create "tokio-macros-x.y.z" git tag.
version = "2.7.0"
edition = "2021"
rust-version = "1.70"
rust-version = "1.71"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -27,7 +27,10 @@ quote = "1"
syn = { version = "2.0", features = ["full"] }
[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
tokio = { version = "1.0.0", features = ["full", "test-util"] }
[package.metadata.docs.rs]
all-features = true
[lints]
workspace = true
+126 -34
View File
@@ -10,6 +10,7 @@ type AttributeArgs = syn::punctuated::Punctuated<syn::Meta, syn::Token![,]>;
enum RuntimeFlavor {
CurrentThread,
Threaded,
Local,
}
impl RuntimeFlavor {
@@ -17,10 +18,11 @@ impl RuntimeFlavor {
match s {
"current_thread" => Ok(RuntimeFlavor::CurrentThread),
"multi_thread" => Ok(RuntimeFlavor::Threaded),
"local" => Ok(RuntimeFlavor::Local),
"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`, `local`, and `multi_thread`.")),
}
}
}
@@ -36,7 +38,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`.")),
}
}
@@ -51,6 +53,7 @@ impl UnhandledPanic {
}
struct FinalConfig {
name: Option<String>,
flavor: RuntimeFlavor,
worker_threads: Option<usize>,
start_paused: Option<bool>,
@@ -60,6 +63,7 @@ struct FinalConfig {
/// Config used in case of the attribute not being able to build a valid config
const DEFAULT_ERROR_CONFIG: FinalConfig = FinalConfig {
name: None,
flavor: RuntimeFlavor::CurrentThread,
worker_threads: None,
start_paused: None,
@@ -68,6 +72,7 @@ const DEFAULT_ERROR_CONFIG: FinalConfig = FinalConfig {
};
struct Configuration {
name: Option<String>,
rt_multi_thread_available: bool,
default_flavor: RuntimeFlavor,
flavor: Option<RuntimeFlavor>,
@@ -81,6 +86,7 @@ struct Configuration {
impl Configuration {
fn new(is_test: bool, rt_multi_thread: bool) -> Self {
Configuration {
name: None,
rt_multi_thread_available: rt_multi_thread,
default_flavor: match is_test {
true => RuntimeFlavor::CurrentThread,
@@ -95,6 +101,16 @@ impl Configuration {
}
}
fn set_name(&mut self, name: syn::Lit, span: Span) -> Result<(), syn::Error> {
if self.name.is_some() {
return Err(syn::Error::new(span, "`name` set multiple times."));
}
let runtime_name = parse_string(name, span, "name")?;
self.name = Some(runtime_name);
Ok(())
}
fn set_flavor(&mut self, runtime: syn::Lit, span: Span) -> Result<(), syn::Error> {
if self.flavor.is_some() {
return Err(syn::Error::new(span, "`flavor` set multiple times."));
@@ -177,15 +193,16 @@ impl Configuration {
use RuntimeFlavor as F;
let flavor = self.flavor.unwrap_or(self.default_flavor);
let worker_threads = match (flavor, self.worker_threads) {
(F::CurrentThread, Some((_, worker_threads_span))) => {
(F::CurrentThread | F::Local, Some((_, worker_threads_span))) => {
let msg = format!(
"The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[{}(flavor = \"multi_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(worker_threads_span, msg));
}
(F::CurrentThread, None) => None,
(F::CurrentThread | F::Local, None) => None,
(F::Threaded, worker_threads) if self.rt_multi_thread_available => {
worker_threads.map(|(val, _span)| val)
}
@@ -207,7 +224,7 @@ impl Configuration {
);
return Err(syn::Error::new(start_paused_span, msg));
}
(F::CurrentThread, Some((start_paused, _))) => Some(start_paused),
(F::CurrentThread | F::Local, Some((start_paused, _))) => Some(start_paused),
(_, None) => None,
};
@@ -219,11 +236,12 @@ impl Configuration {
);
return Err(syn::Error::new(unhandled_panic_span, msg));
}
(F::CurrentThread, Some((unhandled_panic, _))) => Some(unhandled_panic),
(F::CurrentThread | F::Local, Some((unhandled_panic, _))) => Some(unhandled_panic),
(_, None) => None,
};
Ok(FinalConfig {
name: self.name.clone(),
crate_name: self.crate_name.clone(),
flavor,
worker_threads,
@@ -239,12 +257,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 +273,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 +293,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,11 +303,40 @@ 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."),
)),
}
}
fn contains_impl_trait(ty: &syn::Type) -> bool {
match ty {
syn::Type::ImplTrait(_) => true,
syn::Type::Array(t) => contains_impl_trait(&t.elem),
syn::Type::Ptr(t) => contains_impl_trait(&t.elem),
syn::Type::Reference(t) => contains_impl_trait(&t.elem),
syn::Type::Slice(t) => contains_impl_trait(&t.elem),
syn::Type::Tuple(t) => t.elems.iter().any(contains_impl_trait),
syn::Type::Paren(t) => contains_impl_trait(&t.elem),
syn::Type::Group(t) => contains_impl_trait(&t.elem),
syn::Type::Path(t) => match t.path.segments.last() {
Some(segment) => match &segment.arguments {
syn::PathArguments::AngleBracketed(args) => args.args.iter().any(|arg| match arg {
syn::GenericArgument::Type(t) => contains_impl_trait(t),
syn::GenericArgument::AssocType(t) => contains_impl_trait(&t.ty),
_ => false,
}),
syn::PathArguments::Parenthesized(args) => {
args.inputs.iter().any(contains_impl_trait)
|| matches!(&args.output, syn::ReturnType::Type(_, t) if contains_impl_trait(t))
}
syn::PathArguments::None => false,
},
None => false,
},
_ => false,
}
}
fn build_config(
input: &ItemFn,
args: AttributeArgs,
@@ -340,10 +387,12 @@ fn build_config(
config
.set_unhandled_panic(lit.clone(), syn::spanned::Spanned::span(lit))?;
}
"name" => {
config.set_name(lit.clone(), syn::spanned::Spanned::span(lit))?;
}
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`, `name`.",
);
return Err(syn::Error::new_spanned(namevalue, msg));
}
@@ -358,21 +407,20 @@ 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)
"flavor" | "worker_threads" | "start_paused" | "crate" | "unhandled_panic"
| "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`, `name`.")
}
};
return Err(syn::Error::new_spanned(path, msg));
@@ -408,16 +456,32 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
let crate_path = config
.crate_name
.map(ToTokens::into_token_stream)
.unwrap_or_else(|| Ident::new("tokio", last_stmt_start_span).into_token_stream());
.unwrap_or_else(|| {
Ident::new("tokio", Span::call_site().located_at(last_stmt_start_span))
.into_token_stream()
});
let use_builder = quote_spanned! {Span::call_site().located_at(last_stmt_start_span)=>
use #crate_path::runtime::Builder;
};
let mut rt = match config.flavor {
RuntimeFlavor::CurrentThread => quote_spanned! {last_stmt_start_span=>
#crate_path::runtime::Builder::new_current_thread()
},
RuntimeFlavor::CurrentThread | RuntimeFlavor::Local => {
quote_spanned! {last_stmt_start_span=>
Builder::new_current_thread()
}
}
RuntimeFlavor::Threaded => quote_spanned! {last_stmt_start_span=>
#crate_path::runtime::Builder::new_multi_thread()
Builder::new_multi_thread()
},
};
let build = if let RuntimeFlavor::Local = config.flavor {
quote_spanned! {last_stmt_start_span=> build_local(Default::default())}
} else {
quote_spanned! {last_stmt_start_span=> build()}
};
if let Some(v) = config.worker_threads {
rt = quote_spanned! {last_stmt_start_span=> #rt.worker_threads(#v) };
}
@@ -428,6 +492,9 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
let unhandled_panic = v.into_tokens(&crate_path);
rt = quote_spanned! {last_stmt_start_span=> #rt.unhandled_panic(#unhandled_panic) };
}
if let Some(v) = config.name {
rt = quote_spanned! {last_stmt_start_span=> #rt.name(#v) };
}
let generated_attrs = if is_test {
quote! {
@@ -438,15 +505,20 @@ 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, clippy::unwrap_in_result)]
{
#use_builder
return #rt
.enable_all()
.build()
.#build
.expect("Failed building the Runtime")
.block_on(#body_ident);
}
};
let body = input.body();
@@ -460,22 +532,42 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
//
// We don't do this for the main function as it should only be used once so
// there will be no benefit.
let output_type = match &input.sig.output {
// For functions with no return value syn doesn't print anything,
// but that doesn't work as `Output` for our boxed `Future`, so
// default to `()` (the same type as the function output).
syn::ReturnType::Default => quote! { () },
syn::ReturnType::Type(_, ret_type) => quote! { #ret_type },
};
let body = if is_test {
let output_type = match &input.sig.output {
// For functions with no return value syn doesn't print anything,
// but that doesn't work as `Output` for our boxed `Future`, so
// default to `()` (the same type as the function output).
syn::ReturnType::Default => quote! { () },
syn::ReturnType::Type(_, ret_type) => quote! { #ret_type },
};
quote! {
let body = async #body;
#crate_path::pin!(body);
let body: ::core::pin::Pin<&mut dyn ::core::future::Future<Output = #output_type>> = body;
}
} else {
// force typecheck without runtime overhead
let check_block = match &input.sig.output {
syn::ReturnType::Type(_, t)
if matches!(**t, syn::Type::Never(_)) || contains_impl_trait(t) =>
{
quote! {}
}
_ => quote! {
if false {
let _: &dyn ::core::future::Future<Output = #output_type> = &body;
}
},
};
quote! {
let body = async #body;
// Compile-time assertion that the future's output matches the return type.
let body = {
#check_block
body
};
}
};
+106 -25
View File
@@ -1,4 +1,3 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -13,11 +12,6 @@
//! Macros for use with Tokio
// This `extern` is required for older `rustc` versions but newer `rustc`
// versions warn about the unused `extern crate`.
#[allow(unused_extern_crates)]
extern crate proc_macro;
mod entry;
mod select;
@@ -47,7 +41,12 @@ use proc_macro::TokenStream;
/// Awaiting on other futures from the function provided here will not
/// perform as fast as those spawned as workers.
///
/// # Multi-threaded runtime
/// # Runtime flavors
///
/// The macro can be configured with a `flavor` parameter to select
/// different runtime configurations.
///
/// ## Multi-threaded
///
/// To use the multi-threaded runtime, the macro can be configured using
///
@@ -62,23 +61,56 @@ use proc_macro::TokenStream;
/// Note: The multi-threaded runtime requires the `rt-multi-thread` feature
/// flag.
///
/// # Current thread runtime
/// ## Current-thread
///
/// To use the single-threaded runtime known as the `current_thread` runtime,
/// the macro can be configured using
///
/// ```
/// ```rust
/// #[tokio::main(flavor = "current_thread")]
/// # async fn main() {}
/// ```
///
/// ## Function arguments:
/// ## Local
///
/// Arguments are allowed for any functions aside from `main` which is special
/// To use the [local runtime], the macro can be configured using
///
/// ## Usage
/// ```rust
/// #[tokio::main(flavor = "local")]
/// # async fn main() {}
/// ```
///
/// ### Using the multi-thread runtime
/// # Function arguments
///
/// Arguments are allowed for any functions, aside from `main` which is special.
///
/// # Usage
///
/// ## Set the name of the runtime
///
/// ```rust
/// #[tokio::main(name = "my-runtime")]
/// async fn main() {
/// println!("Hello world");
/// }
/// ```
///
/// Equivalent code not using `#[tokio::main]`
///
/// ```rust
/// fn main() {
/// tokio::runtime::Builder::new_multi_thread()
/// .enable_all()
/// .name("my-runtime")
/// .build()
/// .unwrap()
/// .block_on(async {
/// println!("Hello world");
/// })
/// }
/// ```
///
/// ## Using the multi-threaded runtime
///
/// ```rust
/// #[tokio::main]
@@ -101,7 +133,7 @@ use proc_macro::TokenStream;
/// }
/// ```
///
/// ### Using current thread runtime
/// ## Using the current-thread runtime
///
/// The basic scheduler is single-threaded.
///
@@ -126,7 +158,34 @@ use proc_macro::TokenStream;
/// }
/// ```
///
/// ### Set number of worker threads
/// ## Using the local runtime
///
/// The [local runtime] is similar to the current-thread runtime but
/// supports [`task::spawn_local`](../tokio/task/fn.spawn_local.html).
///
/// ```rust
/// #[tokio::main(flavor = "local")]
/// async fn main() {
/// println!("Hello world");
/// }
/// ```
///
/// Equivalent code not using `#[tokio::main]`
///
/// ```rust
/// fn main() {
/// tokio::runtime::Builder::new_current_thread()
/// .enable_all()
/// .build_local(tokio::runtime::LocalOptions::default())
/// .unwrap()
/// .block_on(async {
/// println!("Hello world");
/// })
/// }
/// ```
///
///
/// ## Set number of worker threads
///
/// ```rust
/// #[tokio::main(worker_threads = 2)]
@@ -150,7 +209,7 @@ use proc_macro::TokenStream;
/// }
/// ```
///
/// ### Configure the runtime to start with time paused
/// ## Configure the runtime to start with time paused
///
/// ```rust
/// #[tokio::main(flavor = "current_thread", start_paused = true)]
@@ -176,7 +235,7 @@ use proc_macro::TokenStream;
///
/// Note that `start_paused` requires the `test-util` feature to be enabled.
///
/// ### Rename package
/// ## Rename package
///
/// ```rust
/// use tokio as tokio1;
@@ -203,7 +262,7 @@ use proc_macro::TokenStream;
/// }
/// ```
///
/// ### Configure unhandled panic behavior
/// ## Configure unhandled panic behavior
///
/// Available options are `shutdown_runtime` and `ignore`. For more details, see
/// [`Builder::unhandled_panic`].
@@ -211,7 +270,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,12 +284,11 @@ 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()
/// .enable_all()
/// .unhandled_panic(UnhandledPanic::ShutdownRuntime)
/// .unhandled_panic(tokio::runtime::UnhandledPanic::ShutdownRuntime)
/// .build()
/// .unwrap()
/// .block_on(async {
@@ -250,6 +307,7 @@ use proc_macro::TokenStream;
///
/// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic
/// [unstable]: ../tokio/index.html#unstable-features
/// [local runtime]: ../tokio/runtime/struct.LocalRuntime.html
#[proc_macro_attribute]
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args.into(), item.into(), true).into()
@@ -361,6 +419,31 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
///
/// ## Usage
///
/// ### Set the name of the runtime
///
/// ```no_run
/// #[tokio::test(name = "my-test-runtime")]
/// async fn my_test() {
/// assert!(true);
/// }
/// ```
///
/// Equivalent code not using `#[tokio::test]`
///
/// ```no_run
/// #[test]
/// fn my_test() {
/// tokio::runtime::Builder::new_current_thread()
/// .enable_all()
/// .name("my-test-runtime")
/// .build()
/// .unwrap()
/// .block_on(async {
/// assert!(true);
/// })
/// }
/// ```
///
/// ### Using the multi-thread runtime
///
/// ```no_run
@@ -480,7 +563,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() {
@@ -488,14 +570,13 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// panic!("This panic will shutdown the runtime.");
/// }).await;
/// }
/// # #[cfg(not(tokio_unstable))]
///
/// # fn main() { }
/// ```
///
/// Equivalent code not using `#[tokio::test]`
///
/// ```no_run
/// # #![allow(unknown_lints, unexpected_cfgs)]
/// #[cfg(tokio_unstable)]
/// #[test]
/// fn my_test() {
@@ -510,7 +591,7 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// }).await;
/// })
/// }
/// # #[cfg(not(tokio_unstable))]
///
/// # fn main() { }
/// ```
///
+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
+33
View File
@@ -1,3 +1,36 @@
# 0.1.18 (January 4th, 2026)
### Added
- stream: add `ChunksTimeout::into_remainder` ([#7715])
- stream: add examples to wrapper types ([#7024])
- sync: implement `Stream::size_hint` for `ReceiverStream` and `UnboundedReceiverStream` ([#7492])
### Fixed
- stream: work around the rustc bug in `StreamExt::collect` ([#7754])
### Documented
- stream: improve the the docs of `TcpListenerStream` ([#7578])
[#7024]: https://github.com/tokio-rs/tokio/pull/7024
[#7492]: https://github.com/tokio-rs/tokio/pull/7492
[#7578]: https://github.com/tokio-rs/tokio/pull/7578
[#7715]: https://github.com/tokio-rs/tokio/pull/7715
[#7754]: https://github.com/tokio-rs/tokio/pull/7754
# 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.
+10 -7
View File
@@ -1,12 +1,12 @@
[package]
name = "tokio-stream"
# When releasing to crates.io:
# - Remove path dependencies
# - Remove path dependencies (if any)
# - Update CHANGELOG.md.
# - Create "tokio-stream-0.1.x" git tag.
version = "0.1.16"
version = "0.1.18"
edition = "2021"
rust-version = "1.70"
rust-version = "1.71"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -38,14 +38,14 @@ signal = ["tokio/signal"]
[dependencies]
futures-core = { version = "0.3.0" }
pin-project-lite = "0.2.11"
tokio = { version = "1.15.0", path = "../tokio", features = ["sync"] }
tokio-util = { version = "0.7.0", path = "../tokio-util", optional = true }
tokio = { version = "1.38.0", features = ["sync"] }
tokio-util = { version = "0.7.0", optional = true }
[dev-dependencies]
tokio = { version = "1.2.0", path = "../tokio", features = ["full", "test-util"] }
tokio = { version = "1.38.0", features = ["full", "test-util"] }
async-stream = "0.3"
parking_lot = "0.12.0"
tokio-test = { path = "../tokio-test" }
tokio-test = "0.4"
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
+5 -5
View File
@@ -26,12 +26,12 @@ unsafe impl<T> Sync for Empty<T> {}
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// let mut none = stream::empty::<i32>();
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// let mut none = stream::empty::<i32>();
///
/// assert_eq!(None, none.next().await);
/// }
/// assert_eq!(None, none.next().await);
/// # }
/// ```
pub const fn empty<T>() -> Empty<T> {
Empty(PhantomData)
+6 -7
View File
@@ -1,4 +1,3 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![allow(
clippy::cognitive_complexity,
clippy::large_enum_variant,
@@ -35,14 +34,14 @@
//! ```rust
//! use tokio_stream::{self as stream, StreamExt};
//!
//! #[tokio::main]
//! async fn main() {
//! let mut stream = stream::iter(vec![0, 1, 2]);
//! # #[tokio::main(flavor = "current_thread")]
//! # async fn main() {
//! let mut stream = stream::iter(vec![0, 1, 2]);
//!
//! while let Some(value) = stream.next().await {
//! println!("Got {}", value);
//! }
//! while let Some(value) = stream.next().await {
//! println!("Got {}", value);
//! }
//! # }
//! ```
//!
//! # Returning a Stream from a function
+8 -8
View File
@@ -22,16 +22,16 @@ impl<I> Unpin for Once<I> {}
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// // one is the loneliest number
/// let mut one = stream::once(1);
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// // one is the loneliest number
/// let mut one = stream::once(1);
///
/// assert_eq!(Some(1), one.next().await);
/// assert_eq!(Some(1), one.next().await);
///
/// // just one, that's all we get
/// assert_eq!(None, one.next().await);
/// }
/// // just one, that's all we get
/// assert_eq!(None, one.next().await);
/// # }
/// ```
pub fn once<T>(value: T) -> Once<T> {
Once {
+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
+13 -13
View File
@@ -17,20 +17,20 @@ pin_project! {
/// ```
/// use tokio_stream::{StreamExt, StreamMap, StreamNotifyClose};
///
/// #[tokio::main]
/// async fn main() {
/// let mut map = StreamMap::new();
/// let stream = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// let stream2 = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// map.insert(0, stream);
/// map.insert(1, stream2);
/// while let Some((key, val)) = map.next().await {
/// match val {
/// Some(val) => println!("got {val:?} from stream {key:?}"),
/// None => println!("stream {key:?} closed"),
/// }
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// let mut map = StreamMap::new();
/// let stream = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// let stream2 = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// map.insert(0, stream);
/// map.insert(1, stream2);
/// while let Some((key, val)) = map.next().await {
/// match val {
/// Some(val) => println!("got {val:?} from stream {key:?}"),
/// None => println!("stream {key:?} closed"),
/// }
/// }
/// # }
/// ```
#[must_use = "streams do nothing unless polled"]
pub struct StreamNotifyClose<S> {
@@ -83,7 +83,7 @@ where
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if let Some(inner) = &self.inner {
// We always return +1 because when there's stream there's atleast one more item.
// We always return +1 because when there's a stream there's at least one more item.
let (l, u) = inner.size_hint();
(l.saturating_add(1), u.and_then(|u| u.checked_add(1)))
} else {
+61 -57
View File
@@ -129,7 +129,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -171,8 +171,9 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
///
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(vec![Ok(1), Ok(2), Err("nope")]);
@@ -203,7 +204,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -239,7 +240,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -283,7 +284,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -418,7 +419,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -454,7 +455,7 @@ pub trait StreamExt: Stream {
///
/// # Examples
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -514,7 +515,10 @@ pub trait StreamExt: Stream {
/// }
/// }
///
/// # /*
/// #[tokio::main]
/// # */
/// # #[tokio::main(flavor = "current_thread")]
/// async fn main() {
/// let mut stream = Alternate { state: 0 };
///
@@ -551,7 +555,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -580,7 +584,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -606,7 +610,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -637,7 +641,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
/// let mut stream = stream::iter(vec![1,2,3,4,1]).skip_while(|x| *x < 3);
@@ -680,7 +684,7 @@ pub trait StreamExt: Stream {
/// Basic usage:
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -695,7 +699,7 @@ pub trait StreamExt: Stream {
/// Stopping at the first `false`:
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -739,7 +743,7 @@ pub trait StreamExt: Stream {
/// Basic usage:
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -754,7 +758,7 @@ pub trait StreamExt: Stream {
/// Stopping at the first `true`:
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
@@ -787,21 +791,21 @@ pub trait StreamExt: Stream {
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// let one = stream::iter(vec![1, 2, 3]);
/// let two = stream::iter(vec![4, 5, 6]);
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// let one = stream::iter(vec![1, 2, 3]);
/// let two = stream::iter(vec![4, 5, 6]);
///
/// let mut stream = one.chain(two);
/// let mut stream = one.chain(two);
///
/// assert_eq!(stream.next().await, Some(1));
/// assert_eq!(stream.next().await, Some(2));
/// assert_eq!(stream.next().await, Some(3));
/// assert_eq!(stream.next().await, Some(4));
/// assert_eq!(stream.next().await, Some(5));
/// assert_eq!(stream.next().await, Some(6));
/// assert_eq!(stream.next().await, None);
/// }
/// assert_eq!(stream.next().await, Some(1));
/// assert_eq!(stream.next().await, Some(2));
/// assert_eq!(stream.next().await, Some(3));
/// assert_eq!(stream.next().await, Some(4));
/// assert_eq!(stream.next().await, Some(5));
/// assert_eq!(stream.next().await, Some(6));
/// assert_eq!(stream.next().await, None);
/// # }
/// ```
fn chain<U>(self, other: U) -> Chain<Self, U>
where
@@ -823,7 +827,7 @@ pub trait StreamExt: Stream {
/// # Examples
/// Basic usage:
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, *};
///
@@ -874,16 +878,16 @@ pub trait StreamExt: Stream {
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// let doubled: Vec<i32> =
/// stream::iter(vec![1, 2, 3])
/// .map(|x| x * 2)
/// .collect()
/// .await;
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// let doubled: Vec<i32> =
/// stream::iter(vec![1, 2, 3])
/// .map(|x| x * 2)
/// .collect()
/// .await;
///
/// assert_eq!(vec![2, 4, 6], doubled);
/// }
/// assert_eq!(vec![2, 4, 6], doubled);
/// # }
/// ```
///
/// Collecting a stream of `Result` values
@@ -891,28 +895,28 @@ pub trait StreamExt: Stream {
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// // A stream containing only `Ok` values will be collected
/// let values: Result<Vec<i32>, &str> =
/// stream::iter(vec![Ok(1), Ok(2), Ok(3)])
/// .collect()
/// .await;
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// // A stream containing only `Ok` values will be collected
/// let values: Result<Vec<i32>, &str> =
/// stream::iter(vec![Ok(1), Ok(2), Ok(3)])
/// .collect()
/// .await;
///
/// assert_eq!(Ok(vec![1, 2, 3]), values);
/// assert_eq!(Ok(vec![1, 2, 3]), values);
///
/// // A stream containing `Err` values will return the first error.
/// let results = vec![Ok(1), Err("no"), Ok(2), Ok(3), Err("nein")];
/// // A stream containing `Err` values will return the first error.
/// let results = vec![Ok(1), Err("no"), Ok(2), Ok(3), Err("nein")];
///
/// let values: Result<Vec<i32>, &str> =
/// stream::iter(results)
/// .collect()
/// .await;
/// let values: Result<Vec<i32>, &str> =
/// stream::iter(results)
/// .collect()
/// .await;
///
/// assert_eq!(Err("no"), values);
/// }
/// assert_eq!(Err("no"), values);
/// # }
/// ```
fn collect<T>(self) -> Collect<Self, T>
fn collect<T>(self) -> Collect<Self, T, T::InternalCollection>
where
T: FromStream<Self::Item>,
Self: Sized,
@@ -945,7 +949,7 @@ pub trait StreamExt: Stream {
/// Suppose we have a stream `int_stream` that yields 3 numbers (1, 2, 3):
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
/// use std::time::Duration;
@@ -1031,7 +1035,7 @@ pub trait StreamExt: Stream {
/// Suppose we have a stream `int_stream` that yields 3 numbers (1, 2, 3):
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
/// use std::time::Duration;
@@ -33,6 +33,12 @@ impl<S: Stream> ChunksTimeout<S> {
cap: max_size,
}
}
/// Consumes the [`ChunksTimeout`] and then returns all buffered items.
pub fn into_remainder(mut self: Pin<&mut Self>) -> Vec<S::Item> {
let me = self.as_mut().project();
std::mem::take(me.items)
}
}
impl<S: Stream> Stream for ChunksTimeout<S> {
+143 -9
View File
@@ -1,25 +1,25 @@
use crate::Stream;
use core::future::Future;
use core::marker::PhantomPinned;
use core::marker::{PhantomData, PhantomPinned};
use core::mem;
use core::pin::Pin;
use core::task::{ready, Context, Poll};
use pin_project_lite::pin_project;
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
use std::hash::Hash;
// Do not export this struct until `FromStream` can be unsealed.
pin_project! {
/// Future returned by the [`collect`](super::StreamExt::collect) method.
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[derive(Debug)]
pub struct Collect<T, U>
where
T: Stream,
U: FromStream<T::Item>,
pub struct Collect<T, U, C>
{
#[pin]
stream: T,
collection: U::InternalCollection,
collection: C,
_output: PhantomData<U>,
// Make this future `!Unpin` for compatibility with async trait methods.
#[pin]
_pin: PhantomPinned,
@@ -38,24 +38,25 @@ pin_project! {
/// enhancements to the Rust language.
pub trait FromStream<T>: sealed::FromStreamPriv<T> {}
impl<T, U> Collect<T, U>
impl<T, U> Collect<T, U, U::InternalCollection>
where
T: Stream,
U: FromStream<T::Item>,
{
pub(super) fn new(stream: T) -> Collect<T, U> {
pub(super) fn new(stream: T) -> Collect<T, U, U::InternalCollection> {
let (lower, upper) = stream.size_hint();
let collection = U::initialize(sealed::Internal, lower, upper);
Collect {
stream,
collection,
_output: PhantomData,
_pin: PhantomPinned,
}
}
}
impl<T, U> Future for Collect<T, U>
impl<T, U> Future for Collect<T, U, U::InternalCollection>
where
T: Stream,
U: FromStream<T::Item>,
@@ -136,6 +137,139 @@ impl<T> sealed::FromStreamPriv<T> for Vec<T> {
}
}
impl<T> FromStream<T> for VecDeque<T> {}
impl<T> sealed::FromStreamPriv<T> for VecDeque<T> {
type InternalCollection = VecDeque<T>;
fn initialize(_: sealed::Internal, lower: usize, _upper: Option<usize>) -> VecDeque<T> {
VecDeque::with_capacity(lower)
}
fn extend(_: sealed::Internal, collection: &mut VecDeque<T>, item: T) -> bool {
collection.push_back(item);
true
}
fn finalize(_: sealed::Internal, collection: &mut VecDeque<T>) -> VecDeque<T> {
mem::take(collection)
}
}
impl<T> FromStream<T> for LinkedList<T> {}
impl<T> sealed::FromStreamPriv<T> for LinkedList<T> {
type InternalCollection = LinkedList<T>;
fn initialize(_: sealed::Internal, _lower: usize, _upper: Option<usize>) -> LinkedList<T> {
LinkedList::new()
}
fn extend(_: sealed::Internal, collection: &mut LinkedList<T>, item: T) -> bool {
collection.push_back(item);
true
}
fn finalize(_: sealed::Internal, collection: &mut LinkedList<T>) -> LinkedList<T> {
mem::take(collection)
}
}
impl<T: Ord> FromStream<T> for BTreeSet<T> {}
impl<T: Ord> sealed::FromStreamPriv<T> for BTreeSet<T> {
type InternalCollection = BTreeSet<T>;
fn initialize(_: sealed::Internal, _lower: usize, _upper: Option<usize>) -> BTreeSet<T> {
BTreeSet::new()
}
fn extend(_: sealed::Internal, collection: &mut BTreeSet<T>, item: T) -> bool {
collection.insert(item);
true
}
fn finalize(_: sealed::Internal, collection: &mut BTreeSet<T>) -> BTreeSet<T> {
mem::take(collection)
}
}
impl<K: Ord, V> FromStream<(K, V)> for BTreeMap<K, V> {}
impl<K: Ord, V> sealed::FromStreamPriv<(K, V)> for BTreeMap<K, V> {
type InternalCollection = BTreeMap<K, V>;
fn initialize(_: sealed::Internal, _lower: usize, _upper: Option<usize>) -> BTreeMap<K, V> {
BTreeMap::new()
}
fn extend(_: sealed::Internal, collection: &mut BTreeMap<K, V>, (key, value): (K, V)) -> bool {
collection.insert(key, value);
true
}
fn finalize(_: sealed::Internal, collection: &mut BTreeMap<K, V>) -> BTreeMap<K, V> {
mem::take(collection)
}
}
impl<T: Eq + Hash> FromStream<T> for HashSet<T> {}
impl<T: Eq + Hash> sealed::FromStreamPriv<T> for HashSet<T> {
type InternalCollection = HashSet<T>;
fn initialize(_: sealed::Internal, lower: usize, _upper: Option<usize>) -> HashSet<T> {
HashSet::with_capacity(lower)
}
fn extend(_: sealed::Internal, collection: &mut HashSet<T>, item: T) -> bool {
collection.insert(item);
true
}
fn finalize(_: sealed::Internal, collection: &mut HashSet<T>) -> HashSet<T> {
mem::take(collection)
}
}
impl<K: Eq + Hash, V> FromStream<(K, V)> for HashMap<K, V> {}
impl<K: Eq + Hash, V> sealed::FromStreamPriv<(K, V)> for HashMap<K, V> {
type InternalCollection = HashMap<K, V>;
fn initialize(_: sealed::Internal, lower: usize, _upper: Option<usize>) -> HashMap<K, V> {
HashMap::with_capacity(lower)
}
fn extend(_: sealed::Internal, collection: &mut HashMap<K, V>, (key, value): (K, V)) -> bool {
collection.insert(key, value);
true
}
fn finalize(_: sealed::Internal, collection: &mut HashMap<K, V>) -> HashMap<K, V> {
mem::take(collection)
}
}
impl<T: Ord> FromStream<T> for BinaryHeap<T> {}
impl<T: Ord> sealed::FromStreamPriv<T> for BinaryHeap<T> {
type InternalCollection = BinaryHeap<T>;
fn initialize(_: sealed::Internal, lower: usize, _upper: Option<usize>) -> BinaryHeap<T> {
BinaryHeap::with_capacity(lower)
}
fn extend(_: sealed::Internal, collection: &mut BinaryHeap<T>, item: T) -> bool {
collection.push(item);
true
}
fn finalize(_: sealed::Internal, collection: &mut BinaryHeap<T>) -> BinaryHeap<T> {
mem::take(collection)
}
}
impl<T> FromStream<T> for Box<[T]> {}
impl<T> sealed::FromStreamPriv<T> for Box<[T]> {
+10
View File
@@ -1,5 +1,6 @@
use crate::Stream;
use futures_core::FusedStream;
use pin_project_lite::pin_project;
use std::pin::Pin;
use std::task::{ready, Context, Poll};
@@ -51,3 +52,12 @@ where
}
}
}
impl<T> FusedStream for Fuse<T>
where
T: Stream,
{
fn is_terminated(&self) -> bool {
self.stream.is_none()
}
}
+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]
+67 -72
View File
@@ -65,57 +65,57 @@ use std::task::{ready, Context, Poll};
/// use tokio::sync::mpsc;
/// use std::pin::Pin;
///
/// #[tokio::main]
/// async fn main() {
/// let (tx1, mut rx1) = mpsc::channel::<usize>(10);
/// let (tx2, mut rx2) = mpsc::channel::<usize>(10);
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// let (tx1, mut rx1) = mpsc::channel::<usize>(10);
/// let (tx2, mut rx2) = mpsc::channel::<usize>(10);
///
/// // Convert the channels to a `Stream`.
/// let rx1 = Box::pin(async_stream::stream! {
/// while let Some(item) = rx1.recv().await {
/// yield item;
/// }
/// }) as Pin<Box<dyn Stream<Item = usize> + Send>>;
///
/// let rx2 = Box::pin(async_stream::stream! {
/// while let Some(item) = rx2.recv().await {
/// yield item;
/// }
/// }) as Pin<Box<dyn Stream<Item = usize> + Send>>;
///
/// tokio::spawn(async move {
/// tx1.send(1).await.unwrap();
///
/// // This value will never be received. The send may or may not return
/// // `Err` depending on if the remote end closed first or not.
/// let _ = tx1.send(2).await;
/// });
///
/// tokio::spawn(async move {
/// tx2.send(3).await.unwrap();
/// let _ = tx2.send(4).await;
/// });
///
/// let mut map = StreamMap::new();
///
/// // Insert both streams
/// map.insert("one", rx1);
/// map.insert("two", rx2);
///
/// // Read twice
/// for _ in 0..2 {
/// let (key, val) = map.next().await.unwrap();
///
/// if key == "one" {
/// assert_eq!(val, 1);
/// } else {
/// assert_eq!(val, 3);
/// }
///
/// // Remove the stream to prevent reading the next value
/// map.remove(key);
/// // Convert the channels to a `Stream`.
/// let rx1 = Box::pin(async_stream::stream! {
/// while let Some(item) = rx1.recv().await {
/// yield item;
/// }
/// }) as Pin<Box<dyn Stream<Item = usize> + Send>>;
///
/// let rx2 = Box::pin(async_stream::stream! {
/// while let Some(item) = rx2.recv().await {
/// yield item;
/// }
/// }) as Pin<Box<dyn Stream<Item = usize> + Send>>;
///
/// tokio::spawn(async move {
/// tx1.send(1).await.unwrap();
///
/// // This value will never be received. The send may or may not return
/// // `Err` depending on if the remote end closed first or not.
/// let _ = tx1.send(2).await;
/// });
///
/// tokio::spawn(async move {
/// tx2.send(3).await.unwrap();
/// let _ = tx2.send(4).await;
/// });
///
/// let mut map = StreamMap::new();
///
/// // Insert both streams
/// map.insert("one", rx1);
/// map.insert("two", rx2);
///
/// // Read twice
/// for _ in 0..2 {
/// let (key, val) = map.next().await.unwrap();
///
/// if key == "one" {
/// assert_eq!(val, 1);
/// } else {
/// assert_eq!(val, 3);
/// }
///
/// // Remove the stream to prevent reading the next value
/// map.remove(key);
/// }
/// # }
/// ```
///
/// This example models a read-only client to a chat system with channels. The
@@ -185,20 +185,20 @@ use std::task::{ready, Context, Poll};
/// ```
/// use tokio_stream::{StreamExt, StreamMap, StreamNotifyClose};
///
/// #[tokio::main]
/// async fn main() {
/// let mut map = StreamMap::new();
/// let stream = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// let stream2 = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// map.insert(0, stream);
/// map.insert(1, stream2);
/// while let Some((key, val)) = map.next().await {
/// match val {
/// Some(val) => println!("got {val:?} from stream {key:?}"),
/// None => println!("stream {key:?} closed"),
/// }
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// let mut map = StreamMap::new();
/// let stream = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// let stream2 = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// map.insert(0, stream);
/// map.insert(1, stream2);
/// while let Some((key, val)) = map.next().await {
/// match val {
/// Some(val) => println!("got {val:?} from stream {key:?}"),
/// None => println!("stream {key:?} closed"),
/// }
/// }
/// # }
/// ```
#[derive(Debug)]
@@ -579,9 +579,11 @@ where
/// # Cancel safety
///
/// This method is cancel safe. If `next_many` is used as the event in a
/// [`tokio::select!`](tokio::select) statement and some other branch
/// completes first, it is guaranteed that no items were received on any of
/// the underlying streams.
/// [`tokio::select!`] statement and some other branch completes first,
/// it is guaranteed that no items were received on any of the underlying
/// streams.
///
/// [`tokio::select!`]: https://docs.rs/tokio/latest/tokio/macro.select.html
pub async fn next_many(&mut self, buffer: &mut Vec<(K, V::Item)>, limit: usize) -> usize {
poll_fn(|cx| self.poll_next_many(cx, buffer, limit)).await
}
@@ -734,22 +736,15 @@ mod rand {
#[cfg(not(loom))]
pub(crate) mod rand {
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hash, Hasher};
use std::hash::BuildHasher;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering::Relaxed;
static COUNTER: AtomicU32 = AtomicU32::new(1);
pub(crate) fn seed() -> u64 {
let rand_state = RandomState::new();
let mut hasher = rand_state.build_hasher();
// Hash some unique-ish data to generate some new state
COUNTER.fetch_add(1, Relaxed).hash(&mut hasher);
// Get the seed
hasher.finish()
RandomState::new().hash_one(COUNTER.fetch_add(1, Relaxed))
}
}
+8 -4
View File
@@ -22,9 +22,9 @@ cfg_sync! {
}
cfg_signal! {
#[cfg(unix)]
#[cfg(all(unix, not(loom)))]
mod signal_unix;
#[cfg(unix)]
#[cfg(all(unix, not(loom)))]
pub use signal_unix::SignalStream;
#[cfg(any(windows, docsrs))]
@@ -39,12 +39,14 @@ cfg_time! {
}
cfg_net! {
#[cfg(not(loom))]
mod tcp_listener;
#[cfg(not(loom))]
pub use tcp_listener::TcpListenerStream;
#[cfg(unix)]
#[cfg(all(unix, not(loom)))]
mod unix_listener;
#[cfg(unix)]
#[cfg(all(unix, not(loom)))]
pub use unix_listener::UnixListenerStream;
}
@@ -57,6 +59,8 @@ cfg_io_util! {
}
cfg_fs! {
#[cfg(not(loom))]
mod read_dir;
#[cfg(not(loom))]
pub use read_dir::ReadDirStream;
}
+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}"),
}
}
}
+27
View File
@@ -1,10 +1,31 @@
use crate::Stream;
use futures_core::stream::FusedStream;
use std::pin::Pin;
use std::task::{Context, Poll};
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)]
@@ -37,6 +58,12 @@ impl Stream for IntervalStream {
}
}
impl FusedStream for IntervalStream {
fn is_terminated(&self) -> bool {
false
}
}
impl AsRef<Interval> for IntervalStream {
fn as_ref(&self) -> &Interval {
&self.inner
+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)]
+42
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)]
@@ -44,6 +67,25 @@ impl<T> Stream for ReceiverStream<T> {
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.inner.poll_recv(cx)
}
/// Returns the bounds of the stream based on the underlying receiver.
///
/// For open channels, it returns `(receiver.len(), None)`.
///
/// For closed channels, it returns `(receiver.len(), Some(used_capacity))`
/// where `used_capacity` is calculated as `receiver.max_capacity() -
/// receiver.capacity()`. This accounts for any [`Permit`] that is still
/// able to send a message.
///
/// [`Permit`]: struct@tokio::sync::mpsc::Permit
fn size_hint(&self) -> (usize, Option<usize>) {
if self.inner.is_closed() {
let used_capacity = self.inner.max_capacity() - self.inner.capacity();
(self.inner.len(), Some(used_capacity))
} else {
(self.inner.len(), None)
}
}
}
impl<T> AsRef<Receiver<T>> for ReceiverStream<T> {
@@ -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)]
@@ -38,6 +61,20 @@ impl<T> Stream for UnboundedReceiverStream<T> {
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.inner.poll_recv(cx)
}
/// Returns the bounds of the stream based on the underlying receiver.
///
/// For open channels, it returns `(receiver.len(), None)`.
///
/// For closed channels, it returns `(receiver.len(), receiver.len())`.
fn size_hint(&self) -> (usize, Option<usize>) {
if self.inner.is_closed() {
let len = self.inner.len();
(len, Some(len))
} else {
(self.inner.len(), None)
}
}
}
impl<T> AsRef<UnboundedReceiver<T>> for UnboundedReceiverStream<T> {
+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)]
+30
View File
@@ -6,6 +6,36 @@ 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
/// # #[cfg(not(target_family = "wasm"))]
/// # {
/// 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((Ipv4Addr::LOCALHOST, 8080)).await?;
/// let ipv6_listener = TcpListener::bind((Ipv6Addr::LOCALHOST, 8080)).await?;
/// let ipv4_connections = TcpListenerStream::new(ipv4_listener);
/// let ipv6_connections = TcpListenerStream::new(ipv6_listener);
///
/// let mut connections = ipv4_connections.merge(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)]
+3 -3
View File
@@ -17,7 +17,7 @@ use tokio::sync::watch::error::RecvError;
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{StreamExt, wrappers::WatchStream};
/// use tokio::sync::watch;
@@ -33,7 +33,7 @@ use tokio::sync::watch::error::RecvError;
/// ```
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use tokio_stream::{StreamExt, wrappers::WatchStream};
/// use tokio::sync::watch;
@@ -51,7 +51,7 @@ use tokio::sync::watch::error::RecvError;
/// Example with [`WatchStream<T>::from_changes`]:
///
/// ```
/// # #[tokio::main]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// use futures::future::FutureExt;
/// use tokio::sync::watch;
+109
View File
@@ -0,0 +1,109 @@
use futures::{Stream, StreamExt};
use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
#[tokio::test]
async fn size_hint_stream_open() {
let (tx, rx) = mpsc::channel(4);
tx.send(1).await.unwrap();
tx.send(2).await.unwrap();
let mut stream = ReceiverStream::new(rx);
assert_eq!(stream.size_hint(), (2, None));
stream.next().await;
assert_eq!(stream.size_hint(), (1, None));
stream.next().await;
assert_eq!(stream.size_hint(), (0, None));
}
#[tokio::test]
async fn size_hint_stream_closed() {
let (tx, rx) = mpsc::channel(4);
tx.send(1).await.unwrap();
tx.send(2).await.unwrap();
let mut stream = ReceiverStream::new(rx);
stream.close();
assert_eq!(stream.size_hint(), (2, Some(2)));
stream.next().await;
assert_eq!(stream.size_hint(), (1, Some(1)));
stream.next().await;
assert_eq!(stream.size_hint(), (0, Some(0)));
}
#[tokio::test]
async fn size_hint_sender_dropped() {
let (tx, rx) = mpsc::channel(4);
tx.send(1).await.unwrap();
tx.send(2).await.unwrap();
let mut stream = ReceiverStream::new(rx);
drop(tx);
assert_eq!(stream.size_hint(), (2, Some(2)));
stream.next().await;
assert_eq!(stream.size_hint(), (1, Some(1)));
stream.next().await;
assert_eq!(stream.size_hint(), (0, Some(0)));
}
#[test]
fn size_hint_stream_instantly_closed() {
let (_tx, rx) = mpsc::channel::<i32>(4);
let mut stream = ReceiverStream::new(rx);
stream.close();
assert_eq!(stream.size_hint(), (0, Some(0)));
}
#[tokio::test]
async fn size_hint_stream_closed_permits_send() {
let (tx, rx) = mpsc::channel(4);
tx.send(1).await.unwrap();
let permit1 = tx.reserve().await.unwrap();
let permit2 = tx.reserve().await.unwrap();
let mut stream = ReceiverStream::new(rx);
stream.close();
assert_eq!(stream.size_hint(), (1, Some(3)));
permit1.send(2);
assert_eq!(stream.size_hint(), (2, Some(3)));
stream.next().await;
assert_eq!(stream.size_hint(), (1, Some(2)));
stream.next().await;
assert_eq!(stream.size_hint(), (0, Some(1)));
permit2.send(3);
assert_eq!(stream.size_hint(), (1, Some(1)));
stream.next().await;
assert_eq!(stream.size_hint(), (0, Some(0)));
assert_eq!(stream.next().await, None);
}
#[tokio::test]
async fn size_hint_stream_closed_permits_drop() {
let (tx, rx) = mpsc::channel(4);
tx.send(1).await.unwrap();
let permit1 = tx.reserve().await.unwrap();
let permit2 = tx.reserve().await.unwrap();
let mut stream = ReceiverStream::new(rx);
stream.close();
assert_eq!(stream.size_hint(), (1, Some(3)));
drop(permit1);
assert_eq!(stream.size_hint(), (1, Some(2)));
stream.next().await;
assert_eq!(stream.size_hint(), (0, Some(1)));
drop(permit2);
assert_eq!(stream.size_hint(), (0, Some(0)));
assert_eq!(stream.next().await, None);
}
@@ -0,0 +1,63 @@
use futures::{Stream, StreamExt};
use tokio::sync::mpsc;
use tokio_stream::wrappers::UnboundedReceiverStream;
#[tokio::test]
async fn size_hint_stream_open() {
let (tx, rx) = mpsc::unbounded_channel();
tx.send(1).unwrap();
tx.send(2).unwrap();
let mut stream = UnboundedReceiverStream::new(rx);
assert_eq!(stream.size_hint(), (2, None));
stream.next().await;
assert_eq!(stream.size_hint(), (1, None));
stream.next().await;
assert_eq!(stream.size_hint(), (0, None));
}
#[tokio::test]
async fn size_hint_stream_closed() {
let (tx, rx) = mpsc::unbounded_channel();
tx.send(1).unwrap();
tx.send(2).unwrap();
let mut stream = UnboundedReceiverStream::new(rx);
stream.close();
assert_eq!(stream.size_hint(), (2, Some(2)));
stream.next().await;
assert_eq!(stream.size_hint(), (1, Some(1)));
stream.next().await;
assert_eq!(stream.size_hint(), (0, Some(0)));
}
#[tokio::test]
async fn size_hint_sender_dropped() {
let (tx, rx) = mpsc::unbounded_channel();
tx.send(1).unwrap();
tx.send(2).unwrap();
let mut stream = UnboundedReceiverStream::new(rx);
drop(tx);
assert_eq!(stream.size_hint(), (2, Some(2)));
stream.next().await;
assert_eq!(stream.size_hint(), (1, Some(1)));
stream.next().await;
assert_eq!(stream.size_hint(), (0, Some(0)));
}
#[test]
fn size_hint_stream_instantly_closed() {
let (_tx, rx) = mpsc::unbounded_channel::<i32>();
let mut stream = UnboundedReceiverStream::new(rx);
stream.close();
assert_eq!(stream.size_hint(), (0, Some(0)));
}

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