Compare commits

...
Author SHA1 Message Date
ADD-SP 75fef53d0a chore: prepare Tokio v1.53.1 (#8303) 2026-07-20 19:05:08 +02:00
Jens Holdgaard Pedersen ae9d011213 signal: restore MSRV by removing OnceLock::wait from the Windows handler (#8300)
OnceLock::wait was stabilized in Rust 1.86, so its use in the Windows
console ctrl handler broke tokio's declared rust-version of 1.71 on
windows targets in 1.53.0 (any cargo check with a 1.71..1.86 toolchain
fails with E0599).

The wait existed only because SetConsoleCtrlHandler was called inside
REGISTRY's get_or_init closure, i.e. before the OnceLock was actually
initialized, leaving a window where an invoked handler could observe an
uninitialized REGISTRY. Initialize the registry first and register the
OS handler afterwards (exactly once, through a second OnceLock that
also caches a registration failure so every subsequent call reports the
same error, matching the previous behavior). The handler can then rely
on plain get(): registration happens-after initialization, so an
invoked handler always finds the registry.

Verified with cargo +1.71 check -p tokio --features full
--target x86_64-pc-windows-msvc (fails with the reported E0599 before
this change, clean after) and --all-targets on stable for the same
target.

Fixes #8299
2026-07-20 19:04:08 +02:00
ADD-SP eb4988dc2e time: fix the loom test of the race between cancellation/insertion (#8302) 2026-07-20 19:10:31 +08:00
Alexander KireyevandADD-SP 91d3b4c0bc time: fix alt timer cancellation and insertion race (#8252)
Co-authored-by: ADD-SP <[email protected]>
2026-07-20 18:32:46 +08:00
K-tecchan a46338401b runtime: remove dead link definition in Runtime::block_on (#8301) 2026-07-20 13:47:27 +08:00
Alice Ryhl be689a35f5 chore: prepare Tokio v1.53.0 (#8294) 2026-07-17 10:12:50 +02:00
Alice Ryhl 50f76c71ec chore: prepare tokio-macros v2.7.1 (#8295) 2026-07-17 10:12:38 +02:00
Alice Ryhl f61fccad3c Merge 'tokio-1.52.4' into 'master' (#8290) 2026-07-16 13:27:30 +00:00
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
Minh Vu 5e16ee00fa task: avoid replacing the JoinQueue waker in try_join_next (#8279)
Do not poll pending join handles with a noop waker, since that can replace the waker registered by poll_join_next and leave its caller asleep.
2026-07-16 13:15:11 +02:00
cong-or 88212ab64a sync: document memory ordering guarantees for Semaphore (#8119) 2026-07-16 09:43:52 +02:00
kai-xlr 9cae638de6 examples: add UDP binding to all interfaces docs (#8283)
Closes #6737
2026-07-15 17:05:27 +00:00
Tobias Bucher 315a320e96 io: fix typo in SimplexStream docs (#8284) 2026-07-15 16:51:52 +00:00
MAAZIZ Adel Ayoub dac81bf8c8 sync: wake mpsc receiver when a queued reserve[_many] returns permits (#8260) 2026-07-14 09:56:18 +02:00
Minh Vu 145f124d98 ci: preserve QEMU exit status in uring kernel tests (#8271) 2026-07-14 09:52:32 +02:00
Minh Vu 6e8475dd60 time: wake DelayQueue after resetting to expired (#8274)
Resetting an item into the expired stack bypassed the existing Sleep reset
path, leaving a pending consumer asleep until an unrelated deadline.
2026-07-14 09:33:59 +02:00
Mattia Pitossi b6f30cae9a rt: flush CQE in case of CQE overflow (#8277) 2026-07-13 19:33:23 +08:00
Rachit2323 4638b65f9c stream: implement Peekable::size_hint (#8109) 2026-07-13 13:00:59 +02:00
kai-xlr fe258f5e6d net: use getpeereid for QNX peer credentials (#8270)
QNX (nto) does not provide LOCAL_PEEREID, which is used by the
impl_netbsd module. This causes a compilation failure on QNX.

Use getpeereid() instead, following the same pattern as impl_dragonfly
and impl_aix. This provides uid/gid but not pid, so UCred::pid()
returns None on QNX.
2026-07-13 12:36:10 +02:00
kai-xlr 33f46a5395 io: add warning about stdout reordering with multiple handles (#8276) 2026-07-12 21:12:48 +08:00
linkmauve 9c465e2f42 tokio-stream: Simplify TakeWhile with Option::filter() (#8268)
Option::filter() exists since Rust 1.27.0, and is much more readable
than the open coded variant that was there before, using
Option::and_then().

This has been found by clippy.
2026-07-10 21:33:24 +02:00
linkmauve d5950a8890 refactor: fix variable name typos (#8267) 2026-07-10 20:53:52 +02:00
linkmauve c793a631f7 fs: add safe impl From<OwnedFd> for File (#8266)
This impl was missing to be able to create a tokio::fs::File directly
from an OwnedFd, which can be done in a safe way.  Going through RawFd
required unsafe for the same operation.

And same for Windows using a OwnedHandle instead.
2026-07-10 20:02:38 +02:00
bfd4ddf597 docs: correct spelling typos in comments and doc strings (#8263)
Fix several spelling errors found in comments and documentation:
- mulithreading -> multithreading (tcp_shutdown.rs)
- succeded -> succeeded (signal/windows/sys.rs)
- implementor/implementors -> implementer/implementers (multiple files)

Co-authored-by: maxtaran2010 <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: Alice Ryhl <[email protected]>
2026-07-10 11:03:18 +00:00
Alice Ryhl cd3bcca32b taskdump: remove crate disambiguators from output (#8264) 2026-07-10 12:09:45 +02:00
wanglei01 c4c6265a07 net: support Nuttx target os (#8259)
Signed-off-by: wanglei <[email protected]>
2026-07-07 16:01:24 +02:00
Dongpo Liu bb2815ae23 task: explain why yield_now defers its waker (#8254)
Since #5223, `yield_now` does not wake the task immediately. Instead,
the waker is handed to the scheduler via `context::defer`, which wakes
it only after running out of ready tasks and polling the IO/timer
driver. Add a comment explaining this, as the reasoning is not obvious
from the bare `context::defer` call.
2026-07-07 15:40:18 +02:00
Leo Blöcher e06f16259d sync: reset Chan::rx_waker in chan::Rx's Drop impl (#8095)
I recently fixed a memory leak in an application where tokio's RawTask
storage was being kept alive by a leaked Waker. The task itself was
polling an `mpsc::Receiver` before then being aborted. During cleanup,
all references to the RawTask were dropped, except for the one stored in
`mpsc::chan::Chan::rx_waker`. While the `Receiver` was dropped as part
of the task's future, one of the channel's `Sender`s was leaked outside
the task. This meant the `Chan` was never dropped and its `rx_waker`
contained the leaked Waker.

I fixed the leak by properly cleaning up the `Sender`, but I also think
keeping `rx_waker` around in this case is unnecessary. Once `chan::Rx`
is dropped, it can't be polled anymore, so waking up the registered task
will always be spurious.

The commit includes a regression test to illustrate the problem that is
fixed by removing the waker explicitly.
2026-07-06 14:15:51 +02:00
Minh Vu 962420a4f3 codec: document UdpFramed decoder errors (#8248) 2026-07-06 13:44:17 +02:00
c637f6e73d fs: implement rename using io-uring (#7800)
---------

Co-authored-by: Mattia Pitossi <[email protected]>
Co-authored-by: vrtgs <[email protected]>
Co-authored-by: Daksh <[email protected]>
2026-07-03 11:18:20 +02:00
MAAZIZ Adel Ayoub dd683aba3f io: do not treat zero-length reads as EOF in Chain (#8251) 2026-07-03 08:31:33 +02:00
yoda77777 9fe3c5619d sync: clarify broadcast lagging semantics and pin them with tests (#8239)
Expand module and API docs around RecvError::Lagged / TryRecvError::Lagged
so capacity rounding, miss counts, and post-lag resume behavior are explicit.
Add integration tests covering slow receivers within capacity, overflow,
per-receiver lag, async recv, and cursor advancement after Lagged.
2026-07-01 16:34:17 +00:00
ebubekir karaca 448d1227a1 io: add #[inline] to IO trait impls for in-memory types (#8242)
Add #[inline] hints to poll_read, poll_write, start_seek, poll_complete, poll_fill_buf, and consume implementations for in-memory types (&[u8], Vec<u8>, Cursor<T>).

These are small, leaf implementations that benefit from cross-crate inlining, enabling LLVM to optimize call sites in downstream crates (including bounds-check elision and dead-code path elimination).

Benchmarks show ~16% improvement for slice reads and ~20% for cursor writes.
2026-07-01 18:28:37 +02:00
Minh Vu 98104c36f1 stream: honor StreamMap::next_many limit (#8215) 2026-07-01 17:55:20 +02:00
WhySoBad 61aeb33f51 net: re-enable miri for tests with readable readiness for a socket after a short read (#8238) 2026-07-01 17:52:48 +02:00
MAAZIZ Adel Ayoub 7b354d22a9 stream: stop polling the underlying stream once map_while yields None (#8233) 2026-06-30 11:18:31 +08:00
Shuang Li 4bbd2f4d7c docs: fix missing brace in reactor-refactor.md (#8236) 2026-06-28 23:42:35 +03:00
Tim Vilgot Mikael Fredenberg 930ca7436b signal: merge windows statics (#8231) 2026-06-28 10:24:29 -07:00
46c830117b fs(tests): do not leak FDs with shutdown background (#8184)
---------

Co-authored-by: Martin Grigorov <[email protected]>
Co-authored-by: Mattia Pitossi <[email protected]>
2026-06-25 17:20:53 +02:00
dependabot[bot] 8f37021c16 ci: bump actions/checkout from 6 to 7 (#8229)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  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-06-25 09:25:48 +03:00
dependabot[bot] cdaa78fc37 ci: bump actions/cache from 5 to 6 (#8230)
Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '6'
  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-06-25 08:51:00 +03:00
Matteo Monti dba1f20585 sync: implement cancellation token getter for drop guard (#8226) 2026-06-24 15:16:23 +02:00
Nanasi 95b8895da8 stream: update coop handling for empty and once (#8227) 2026-06-24 12:38:36 +00:00
Giorgio Maria Federico Birnthaler 73e8937cc5 util: fix typo in blocking_check.rs comment (#8223)
Replace 'i dont' with 'i don't' in the cfg(not(unix)) stub of
'check_socket_for_blocking'. The line is a casual internal comment
about why WASI is not yet supported there; it just needs the missing
apostrophe.
2026-06-23 20:03:19 +00:00
Ebrahim Eldesoky 630ec12a4f stream: use cooperative budgeting in tokio_stream::iter (#8218) 2026-06-23 11:42:03 +00:00
GraymanandTim Vilgot Mikael Fredenberg 060f66c665 docs: clarify cancel safety wording (#8181)
Co-authored-by: Tim Vilgot Mikael Fredenberg <[email protected]>
2026-06-23 09:13:01 +00:00
Prashant Singh Chouhan a59f9a0a94 test: add Spawn::poll_until_idle (#8213) 2026-06-23 10:25:14 +02:00
Robert Holt aee321206b metrics: add task schedule latency metric (#7986) 2026-06-23 10:23:34 +02:00
Tim Vilgot Mikael Fredenberg 5f52f113d4 tracing: remove unnecessary span clone (#8126) 2026-06-23 07:51:55 +00:00
Minh Vu f59aae423e stream: fix overflow in StreamMap::size_hint (#8216) 2026-06-23 09:49:54 +02:00
Minh Vu dc3a883b99 examples: connect proxy upstream per client (#8217) 2026-06-23 09:49:33 +02:00
Patrick WehbeandPatrick Wehbe 66e29121b3 time: fix reversed poll order in timeout doc (#8214)
The timeout doc said the timeout is checked before polling the future,
but the impl polls the future first and only then checks the delay (see
the Future impl for Timeout, which calls me.value.poll before
poll_delay). Reverse the clause to match the implementation. This is
also what makes the rest of the sentence correct: the future can
complete and exceed the timeout without an error precisely because it is
polled before the timeout is checked.

Co-authored-by: Patrick Wehbe <[email protected]>
2026-06-19 22:32:30 +02:00
Tim Vilgot Mikael Fredenberg daa653d94f tokio: remove needless generic type on LinkedList (#8188) 2026-06-19 23:28:28 +08:00
Mahdi Ali-RaihanandVrtgs 7892f6020d Implemented io-uring Op<Statx> and applied to read_uring and fs::try_exists (#8080)
* implement Op<Statx> and use it to implement fs::try_exists

* fix statx on unavailable platforms

* complete using only io-uring operations for read_uring

* Implemented io-uring::Op<Statx> and apply it accordingly to read_uring, try_exists

* Added test for io uring statx operations. Checks for cancellations, shutdown, stating multiple files, ELOOP, ENAMETOOLONG, EACCES

* Removed musl as supported platform for io_uring statx operations, as statx is supported on 1.25+ musl, and MSRV that uses 1.25 on all *-linux-musl platforms is 1.93

* Removed pending checks for cancel_op_future since io_uring not available on Linux <5.1, removed stat permission denied test case since it doesn't work on Linux 4.19

* Removed redundant cfg attributes on functions, added STATX_BTIME flag in statx operation, use assert pending in cancel ops

* Uncommented stat_permission_denied test and make sure it doesn't run on platforms that don't support io_uring, removed unnecessary comments, and added TODO on symlink_metadata for when Metadata::from_statx is stabilized

* Statx fd leak drop test added and added STATX_BTIME to file_metadata

* Remove unnecessary utils function, refactored code in statx and statx fd leak test, and removed cfg_io_uring gates (localized the feature gate to the pertinent part of the code that uses it in read_uring)

---------

Co-authored-by: Vrtgs <[email protected]>
2026-06-13 13:30:47 -04:00
ADD-SPandAlice Ryhl da044f27d7 net: disable some Miri tests for TCP socket (#8205)
Co-authored-by: Alice Ryhl <[email protected]>
2026-06-12 10:12:08 +08:00
Mukhammedali Berektassuly ecb5125a67 runtime: document interaction with fork() (#8202) 2026-06-10 12:05:27 +00:00
Jean SIMARD bde8967853 task: add Stream wrapper for JoinSet (#8189) 2026-06-10 09:04:27 +02:00
Satyam Gupta 21e4a2a282 taskdump: support taskdumps on s390x (#8192)
Signed-off-by: satyamg1620 <[email protected]>
2026-06-10 08:55:09 +02:00
Amey Pawar 2e7930fe58 net: accept ConnectionReset in shutdown_after_tcp_reset test (#8196)
The test asserts shutdown() returns Ok(()) after the peer resets the
connection (linger = 0). This holds on Linux and macOS, but on FreeBSD the
kernel can finish processing the RST before shutdown() runs, so it returns
ConnectionReset and the test fails intermittently -- the oneshot only
synchronizes the application-level drop, not the kernel's RST processing.

Accept Ok(()) or ConnectionReset for the post-reset shutdown, since both
are valid for a connection the peer has already reset.
2026-06-09 15:00:24 +00:00
Sabnock 67637b348a macros: clarify tokio::main expansion (#8193) 2026-06-09 14:22:46 +02:00
Nicolò Paternoster 778e9d97d9 tests: fix typo in io_uring support functions docs (#8186) 2026-06-03 17:37:57 +02:00
elomatreb 71362aa609 net: add SocketAddr methods to Unix sockets (#8144) 2026-06-03 09:23:30 +02:00
WhySoBad 2de86e557c net: enable more Miri tests for TCP socket (#8180) 2026-06-02 23:50:54 +08:00
RyanStewart 326bd2cc44 codec: use libc::memchr for LinesCodec delimiter scan (#8141) 2026-06-02 17:23:18 +02:00
WhySoBad 32312ae0d6 net: enable Miri tests for TCP socket (#8156) 2026-05-26 10:40:09 +08:00
Qiqi Zhang 37ced33efd time: ensure timers stay in the same runtime after .reset() (#8169) 2026-05-26 10:29:50 +08:00
Tim Vilgot Mikael Fredenberg 923e72345c time: move lazy-registration state into Sleep (#8132) 2026-05-25 08:11:09 +08:00
Mattia Pitossi f619fc0587 docs: improve contributing guidelines (#8166) 2026-05-24 09:10:16 +02:00
吴杨帆 82fe082ef1 fs: clarify create_dir_all succeeds if path exists (#8149) 2026-05-21 15:43:17 +02:00
Joe Grund 1fe1b0e727 task: add JoinMap::try_join_next (#8099) 2026-05-21 15:41:55 +02:00
Minh Vu c6af672353 io: advance partially written buffers correctly in write_all_vectored (#8159) 2026-05-21 01:21:35 -07:00
Abhinav 2a05f364b7 ci: set copyback to false for FreeBSD jobs (#8155) 2026-05-20 14:32:23 +02:00
Mattia Pitossi c6d58ce7e7 ci: fix macOS runners (#8145) 2026-05-15 08:24:07 +02:00
vip892766gma de360cfc7e tokio-stream: fix duplicated word in changelog (#8143) 2026-05-14 20:33:43 -07:00
RyanStewart 7d3b0ad192 sync: remove useless conversion in oneshot Receiver::poll (#8142) 2026-05-13 09:05:35 +02:00
Russell Cohen 0121120b6d taskdump: skip double wake on Trace::capture/Trace::trace_with (#8043) 2026-05-12 19:56:50 +02:00
Mattia Pitossi bdcea6b2cd fs: skip some io_uring tests on Kernels that don't support it (#8134) 2026-05-11 13:58:50 +02:00
Tim Vilgot Mikael Fredenberg ee0dc90926 time: consolidate mutex locks on spurious poll (#8124) 2026-05-08 11:02:54 -07:00
Alice Ryhl 78594a7497 Merge 'tokio-1.52.3' into 'master' (#8131) 2026-05-08 13:00:22 +00: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
xtqqczze 067f229371 io: replace Vec method truncate(0) with clear (#8125)
https://rust-lang.github.io/rust-clippy/master/index.html#manual_clear
2026-05-08 08:38:49 +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
231 changed files with 5724 additions and 1421 deletions
+2
View File
@@ -15,6 +15,8 @@ R-loom-time-driver:
- any-glob-to-any-file:
- tokio/src/runtime/time/*
- tokio/src/runtime/time/**/*
- tokio/src/runtime/time_alt/*
- tokio/src/runtime/time_alt/**/*
R-loom-current-thread:
- changed-files:
+1 -1
View File
@@ -20,5 +20,5 @@ jobs:
issues: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: EmbarkStudios/cargo-deny-action@v2
+74 -48
View File
@@ -18,7 +18,7 @@ env:
rust_stable: stable
rust_nightly: nightly-2025-10-12
# Pin a specific miri version
rust_miri_nightly: nightly-2026-04-27
rust_miri_nightly: nightly-2026-06-29
rust_clippy: '1.88'
# When updating this, also update:
# - README.md
@@ -64,7 +64,7 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -75,6 +75,9 @@ jobs:
tool: cargo-nextest
- uses: Swatinem/rust-cache@v2
with:
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
cache-bin: ${{ matrix.os != 'macos-latest' }}
# Run `tokio` with stable features. This excludes testing utilities which
# can alter the runtime behavior of Tokio.
@@ -96,7 +99,7 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -107,6 +110,9 @@ jobs:
tool: cargo-nextest
- uses: Swatinem/rust-cache@v2
with:
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
cache-bin: ${{ matrix.os != 'macos-latest' }}
- name: test --features ${{ env.TOKIO_STABLE_FEATURES }}
run: |
@@ -135,7 +141,7 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -146,6 +152,9 @@ jobs:
tool: cargo-nextest
- uses: Swatinem/rust-cache@v2
with:
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
cache-bin: ${{ matrix.os != 'macos-latest' }}
- name: test --features ${{ env.TOKIO_STABLE_FEATURES }} panic=abort
run: |
@@ -168,7 +177,7 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -179,6 +188,9 @@ jobs:
tool: cargo-hack
- uses: Swatinem/rust-cache@v2
with:
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
cache-bin: ${{ matrix.os != 'macos-latest' }}
# Run integration tests for each feature
- name: test tests-integration --each-feature
@@ -208,7 +220,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -234,7 +246,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -275,7 +287,7 @@ jobs:
- { os: ubuntu-latest, extra_features: io-uring }
- { os: macos-latest, extra_features: "" }
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -287,6 +299,9 @@ jobs:
tool: cargo-nextest
- uses: Swatinem/rust-cache@v2
with:
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
cache-bin: ${{ matrix.os != 'macos-latest' }}
# Run `tokio` with "unstable" cfg flag.
- name: test tokio full --cfg unstable
run: |
@@ -309,7 +324,7 @@ jobs:
include:
- os: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -343,7 +358,7 @@ jobs:
include:
- os: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -372,7 +387,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_miri_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -395,7 +410,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_miri_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -418,7 +433,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_miri_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -437,7 +452,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install llvm
# Required to resolve symbols in sanitizer output
run: sudo apt-get install -y llvm
@@ -459,7 +474,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Check `tokio` semver
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
@@ -488,7 +503,7 @@ jobs:
- powerpc64-unknown-linux-gnu
- arm-linux-androideabi
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -513,7 +528,7 @@ jobs:
# - name: armv7-sony-vita-newlibeabihf
# exclude_features: "process,signal,rt-process-signal,full,taskdump"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@nightly
with:
@@ -547,7 +562,7 @@ jobs:
- target: aarch64-pc-windows-msvc
os: windows-11-arm
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
@@ -597,7 +612,7 @@ jobs:
- target: aarch64-pc-windows-msvc
os: windows-11-arm
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
@@ -638,7 +653,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -670,7 +685,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -686,8 +701,9 @@ jobs:
# https://github.com/tokio-rs/tokio/pull/5356
# https://github.com/tokio-rs/tokio/issues/5373
- name: Check
# We use `--skip io-uring` since io-uring crate doesn't provide a binding for the i686 target.
run: cargo hack check -Zbuild-std --target target-specs/i686-unknown-linux-gnu.json -p tokio --feature-powerset --skip io-uring --depth 2 --keep-going
# We use `--skip io-uring,schedule-latency` since io-uring crate doesn't provide a binding for the i686 target
# and schedule latency tracking is only supported on 64-bit targets.
run: cargo hack check -Zbuild-std --target target-specs/i686-unknown-linux-gnu.json -p tokio --feature-powerset --skip io-uring,schedule-latency --depth 2 --keep-going
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
@@ -700,15 +716,15 @@ jobs:
include:
- name: ""
rustflags: ""
exclude_features: "io-uring,taskdump"
exclude_features: "io-uring,taskdump,schedule-latency"
- name: "--unstable"
rustflags: "--cfg tokio_unstable -Dwarnings"
exclude_features: "io-uring,taskdump"
- name: "--unstable io-uring,taskdump"
exclude_features: "io-uring,taskdump,schedule-latency"
- name: "--unstable io-uring,taskdump,schedule-latency"
rustflags: "--cfg tokio_unstable -Dwarnings"
exclude_features: ""
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -727,7 +743,7 @@ jobs:
name: minrust
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_min }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -756,7 +772,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -789,7 +805,7 @@ jobs:
name: fmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -809,7 +825,7 @@ jobs:
name: clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_clippy }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -849,7 +865,7 @@ jobs:
extra_features: "tracing,io-uring,taskdump"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -866,7 +882,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -882,7 +898,7 @@ jobs:
name: Check README
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Verify that both READMEs are identical
run: diff README.md tokio/README.md
@@ -901,7 +917,7 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -937,6 +953,8 @@ jobs:
# relative to the `$workspace` and defaults to "target" if not explicitly given.
# default: ". -> target"
workspaces: "./hyper"
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
cache-bin: ${{ matrix.os != 'macos-latest' }}
- name: Test hyper
run: cargo test --features full
@@ -953,7 +971,7 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -985,6 +1003,8 @@ jobs:
# relative to the `$workspace` and defaults to "target" if not explicitly given.
# default: ". -> target"
workspaces: "./quinn"
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
cache-bin: ${{ matrix.os != 'macos-latest' }}
- name: Test Quinn
working-directory: quinn
@@ -997,7 +1017,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -1014,7 +1034,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@master
with:
@@ -1038,7 +1058,7 @@ jobs:
- name: macros sync time rt
features: "macros sync time rt"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -1061,7 +1081,7 @@ jobs:
- wasm32-wasip1
- wasm32-wasip1-threads
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -1122,7 +1142,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -1157,7 +1177,7 @@ jobs:
# includes all unstable features.
extra_features: "tracing,io-uring,taskdump"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ matrix.rust }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -1181,7 +1201,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -1201,7 +1221,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@stable
with:
@@ -1210,7 +1230,7 @@ jobs:
uses: taiki-e/install-action@v2
with:
tool: cargo-spellcheck
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Make sure dictionary words are sorted and unique
run: |
FILE="spellcheck.dic"
@@ -1298,12 +1318,14 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Test in FreeBSD
uses: vmactions/freebsd-vm@v1
with:
release: '14.4'
envs: "TOKIO_STABLE_FEATURES RUSTFLAGS"
sync: rsync
copyback: false
prepare: |
pkg install -y curl
curl https://sh.rustup.rs -sSf --output rustup.sh
@@ -1322,7 +1344,7 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Build docs in FreeBSD
uses: vmactions/freebsd-vm@v1
env:
@@ -1331,6 +1353,8 @@ jobs:
with:
release: '14.4'
envs: "TOKIO_STABLE_FEATURES RUSTDOCFLAGS RUSTFLAGS"
sync: rsync
copyback: false
prepare: |
pkg install -y curl
curl https://sh.rustup.rs -sSf --output rustup.sh
@@ -1348,12 +1372,14 @@ jobs:
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Test in FreeBSD
uses: vmactions/freebsd-vm@v1
with:
release: '14.4'
envs: "TOKIO_STABLE_FEATURES RUSTFLAGS"
sync: rsync
copyback: false
prepare: |
pkg install -y curl
curl https://sh.rustup.rs -sSf --output rustup.sh
+6 -6
View File
@@ -29,7 +29,7 @@ jobs:
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
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -45,7 +45,7 @@ jobs:
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-sync') || (github.base_ref == null))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -61,7 +61,7 @@ jobs:
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-time-driver') || (github.base_ref == null))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -77,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@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -100,7 +100,7 @@ jobs:
- scope: loom_multi_thread::group_c
- scope: loom_multi_thread::group_d
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -118,7 +118,7 @@ jobs:
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-util') || (github.base_ref == null))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
+1 -1
View File
@@ -19,5 +19,5 @@ jobs:
cargo-deny:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: EmbarkStudios/cargo-deny-action@v2
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
stress-test:
- simple_echo_tcp
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
@@ -14,7 +14,7 @@ jobs:
env:
KERNEL_VERSION: ${{ inputs.kernel_version }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install system dependencies
run: |
@@ -25,7 +25,7 @@ jobs:
- name: Cache Linux source
id: cache-kernel
uses: actions/cache@v5
uses: actions/cache@v6
with:
path: linux-${{ env.KERNEL_VERSION }}
key: kernel-${{ env.KERNEL_VERSION }}
@@ -40,6 +40,8 @@ jobs:
make defconfig
make -j$(nproc)
# We are running tests also on a Kernel version that does not support io_uring
# to check if the fallback mechanism works
- 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
@@ -87,16 +89,31 @@ jobs:
| cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz)
- name: Run tests in QEMU
shell: bash
run: |
set -euo pipefail
set +e
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_status=${PIPESTATUS[0]} tee_status=${PIPESTATUS[1]}
set -e
if [ "$qemu_status" -ne 0 ]; then
echo "QEMU exited with status $qemu_status"
exit "$qemu_status"
fi
if [ "$tee_status" -ne 0 ]; then
echo "tee exited with status $tee_status"
exit "$tee_status"
fi
# 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)"
echo "tests reported failures"
exit 1
else
echo "all tests passed"
+1 -1
View File
@@ -60,7 +60,7 @@ Make sure you enable the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.52.2", features = ["full"] }
tokio = { version = "1.53.1", features = ["full"] }
```
Then, on your main.rs:
+4 -4
View File
@@ -73,7 +73,7 @@ impl SlowHddWriter {
fn write_bytes(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
writeable: usize,
writable: usize,
) -> std::task::Poll<Result<usize, std::io::Error>> {
let service_res = self.as_mut().service_write(cx);
@@ -86,7 +86,7 @@ impl SlowHddWriter {
assert!(service_res.is_pending());
Poll::Pending
} else {
let written = available.min(writeable);
let written = available.min(writable);
self.buffer_used += written;
Poll::Ready(Ok(written))
}
@@ -123,8 +123,8 @@ impl AsyncWrite for SlowHddWriter {
cx: &mut std::task::Context<'_>,
bufs: &[std::io::IoSlice<'_>],
) -> std::task::Poll<Result<usize, std::io::Error>> {
let writeable = bufs.iter().fold(0, |acc, buf| acc + buf.len());
self.write_bytes(cx, writeable)
let writable = bufs.iter().fold(0, |acc, buf| acc + buf.len());
self.write_bytes(cx, writable)
}
fn is_write_vectored(&self) -> bool {
+3 -2
View File
@@ -8,7 +8,8 @@ It should be considered a map to help you navigate the process.
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).
- Looking for something to work on? Check the open [`issues`](https://github.com/tokio-rs/tokio/issues)
(some might be in progress or still under discussion, so leave a comment before starting the work).
- 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).
@@ -22,7 +23,7 @@ If you are unsure where to begin, use the following guides:
- [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)
- [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)
@@ -81,8 +81,10 @@ The module label provides a more fine grained categorization than **Area**.
Some extra information.
- **T-docs** This is about documentation.
- **T-io-uring** This is about io-uring (Linux).
- **T-performance** This is about performance.
- **T-v0.1.x** This is about old Tokio.
- **T-wasm** This is about Web Assembly.
Any label not listed here is not in active use.
+11 -7
View File
@@ -4,10 +4,12 @@ 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.
documentation) are greatly appreciated.
> [!NOTE]
> 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
@@ -23,8 +25,10 @@ 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`)
> [!NOTE]
> There are some features that are not available in some systems (e.g., `io-uring`
> which is Linux only). In that case you cannot use the `--all-features` and you
> should specify only the features that are supported (e.g., `--features=full`).
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
@@ -99,7 +103,7 @@ MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-strict-provenance" \
cargo +nightly miri test --features full --lib --tests
```
### Performing spellcheck on tokio codebase
### 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
+12 -2
View File
@@ -5,7 +5,12 @@
#[cfg(all(
tokio_unstable,
target_os = "linux",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
any(
target_arch = "aarch64",
target_arch = "x86",
target_arch = "x86_64",
target_arch = "s390x"
)
))]
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -82,7 +87,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(not(all(
tokio_unstable,
target_os = "linux",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
any(
target_arch = "aarch64",
target_arch = "x86",
target_arch = "x86_64",
target_arch = "s390x"
)
)))]
fn main() {
println!("task dumps are not available")
+22
View File
@@ -9,6 +9,28 @@
//! cargo run --example connect-udp 127.0.0.1:8080
//!
//! Each line you type in to the `connect-udp` terminal should be echo'd back to you!
//!
//! # Binding to all interfaces
//!
//! By default this example binds to `127.0.0.1` so it is only reachable
//! from the local machine.
//!
//! To listen on all interfaces instead:
//!
//! ```sh
//! cargo run --example echo-udp -- 0.0.0.0:8080
//! ```
//!
//! Binding to `0.0.0.0` exposes the server on all network interfaces.
//! Only do this in trusted network environments.
//!
//! On multi-homed systems, a UDP socket bound to a wildcard address
//! (`0.0.0.0` or `::`) cannot always send replies from the same local IP
//! that received the packet. Replies may therefore originate from a
//! different address than the client targeted. See [this Cloudflare blog
//! post][udp-blog] for more details.
//!
//! [udp-blog]: https://blog.cloudflare.com/everything-you-ever-wanted-to-know-about-udp-sockets-but-were-afraid-to-ask-part-1
#![warn(rust_2018_idioms)]
+12 -9
View File
@@ -25,7 +25,6 @@
use tokio::io::copy_bidirectional;
use tokio::net::{TcpListener, TcpStream};
use futures::FutureExt;
use std::env;
use std::error::Error;
@@ -44,16 +43,20 @@ async fn main() -> Result<(), Box<dyn Error>> {
let listener = TcpListener::bind(listen_addr).await?;
while let Ok((mut inbound, _)) = listener.accept().await {
let mut outbound = TcpStream::connect(server_addr.clone()).await?;
let server_addr = server_addr.clone();
tokio::spawn(async move {
copy_bidirectional(&mut inbound, &mut outbound)
.map(|r| {
if let Err(e) = r {
println!("Failed to transfer; error={e}");
}
})
.await
let mut outbound = match TcpStream::connect(server_addr).await {
Ok(outbound) => outbound,
Err(e) => {
println!("Failed to connect; error={e}");
return;
}
};
if let Err(e) = copy_bidirectional(&mut inbound, &mut outbound).await {
println!("Failed to transfer; error={e}");
}
});
}
+2 -1
View File
@@ -1,4 +1,4 @@
323
324
&
+
<
@@ -89,6 +89,7 @@ decrementing
demangled
dequeued
dereferenced
derefs
deregister
deregistered
deregistering
+6
View File
@@ -1,3 +1,9 @@
# 2.7.1 (July 17th, 2026)
- macros: clarify `tokio::main` expansion ([#8193])
[#8193]: https://github.com/tokio-rs/tokio/pull/8193
# 2.7.0 (April 3rd, 2026)
- macros: stabilize `LocalRuntime` ([#7557])
+1 -1
View File
@@ -4,7 +4,7 @@ name = "tokio-macros"
# - Remove path dependencies (if any)
# - Update CHANGELOG.md.
# - Create "tokio-macros-x.y.z" git tag.
version = "2.7.0"
version = "2.7.1"
edition = "2021"
rust-version = "1.71"
authors = ["Tokio Contributors <[email protected]>"]
+6 -4
View File
@@ -29,10 +29,11 @@ use proc_macro::TokenStream;
/// powerful interface.
///
/// Note: This macro can be used on any function and not just the `main`
/// function. Using it on a non-main function makes the function behave as if it
/// was synchronous by starting a new runtime each time it is called. If the
/// function is called often, it is preferable to create the runtime using the
/// runtime builder so the runtime can be reused across calls.
/// function. Although the function is written with `async fn`, this macro
/// expands it to a synchronous function that starts a runtime each time it is
/// called. If the function is called often, it is preferable to create the
/// runtime using the runtime builder so the runtime can be reused across calls.
/// For details on the expansion, see [Bridging with sync code][bridging].
///
/// # Non-worker async function
///
@@ -308,6 +309,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
/// [bridging]: https://tokio.rs/tokio/topics/bridging#what-tokiomain-expands-to
#[proc_macro_attribute]
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args.into(), item.into(), true).into()
+1 -1
View File
@@ -12,7 +12,7 @@
### Documented
- stream: improve the the docs of `TcpListenerStream` ([#7578])
- stream: improve the docs of `TcpListenerStream` ([#7578])
[#7024]: https://github.com/tokio-rs/tokio/pull/7024
[#7492]: https://github.com/tokio-rs/tokio/pull/7492
+2
View File
@@ -24,6 +24,7 @@ full = [
"net",
"io-util",
"fs",
"rt",
"sync",
"signal"
]
@@ -32,6 +33,7 @@ time = ["tokio/time"]
net = ["tokio/net"]
io-util = ["tokio/io-util"]
fs = ["tokio/fs"]
rt = ["tokio/rt"]
sync = ["tokio/sync", "tokio-util"]
signal = ["tokio/signal"]
+9 -1
View File
@@ -40,7 +40,15 @@ pub const fn empty<T>() -> Empty<T> {
impl<T> Stream for Empty<T> {
type Item = T;
fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<T>> {
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<T>> {
#[cfg(feature = "rt")]
{
use tokio::task::coop;
let coop = std::task::ready!(coop::poll_proceed(_cx));
coop.made_progress();
}
Poll::Ready(None)
}
+26 -8
View File
@@ -8,6 +8,7 @@ use core::task::{Context, Poll};
#[must_use = "streams do nothing unless polled"]
pub struct Iter<I> {
iter: I,
#[cfg(not(feature = "rt"))]
yield_amt: usize,
}
@@ -36,6 +37,7 @@ where
{
Iter {
iter: i.into_iter(),
#[cfg(not(feature = "rt"))]
yield_amt: 0,
}
}
@@ -47,17 +49,33 @@ where
type Item = I::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<I::Item>> {
// TODO: add coop back
if self.yield_amt >= 32 {
self.yield_amt = 0;
#[cfg(feature = "rt")]
{
use tokio::task::coop;
cx.waker().wake_by_ref();
let coop = std::task::ready!(coop::poll_proceed(cx));
let item = self.iter.next();
Poll::Pending
} else {
self.yield_amt += 1;
coop.made_progress();
Poll::Ready(self.iter.next())
Poll::Ready(item)
}
#[cfg(not(feature = "rt"))]
{
if self.yield_amt >= 32 {
self.yield_amt = 0;
cx.waker().wake_by_ref();
Poll::Pending
} else {
let item = self.iter.next();
self.yield_amt += 1;
Poll::Ready(item)
}
}
}
+10
View File
@@ -57,3 +57,13 @@ macro_rules! cfg_signal {
)*
}
}
macro_rules! cfg_rt {
($($item:item)*) => {
$(
#[cfg(feature = "rt")]
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
$item
)*
}
}
+19 -9
View File
@@ -1,6 +1,5 @@
use crate::{Iter, Stream};
use crate::Stream;
use core::option;
use core::pin::Pin;
use core::task::{Context, Poll};
@@ -8,7 +7,7 @@ use core::task::{Context, Poll};
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct Once<T> {
iter: Iter<option::IntoIter<T>>,
value: Option<T>,
}
impl<I> Unpin for Once<I> {}
@@ -34,19 +33,30 @@ impl<I> Unpin for Once<I> {}
/// # }
/// ```
pub fn once<T>(value: T) -> Once<T> {
Once {
iter: crate::iter(Some(value)),
}
Once { value: Some(value) }
}
impl<T> Stream for Once<T> {
type Item = T;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
Pin::new(&mut self.iter).poll_next(cx)
fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<T>> {
#[cfg(feature = "rt")]
{
use tokio::task::coop;
let coop = std::task::ready!(coop::poll_proceed(_cx));
coop.made_progress();
}
Poll::Ready(self.value.take())
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
if self.value.is_some() {
(1, Some(1))
} else {
(0, Some(0))
}
}
}
+33 -2
View File
@@ -3,6 +3,7 @@ use crate::Stream;
use core::fmt;
use core::pin::Pin;
use core::task::{Context, Poll};
use futures_core::FusedStream;
use pin_project_lite::pin_project;
pin_project! {
@@ -12,6 +13,7 @@ pin_project! {
#[pin]
stream: St,
f: F,
done: bool,
}
}
@@ -22,13 +24,18 @@ where
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MapWhile")
.field("stream", &self.stream)
.field("done", &self.done)
.finish()
}
}
impl<St, F> MapWhile<St, F> {
pub(super) fn new(stream: St, f: F) -> Self {
MapWhile { stream, f }
MapWhile {
stream,
f,
done: false,
}
}
}
@@ -41,12 +48,36 @@ where
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
let me = self.project();
if *me.done {
return Poll::Ready(None);
}
let f = me.f;
me.stream.poll_next(cx).map(|opt| opt.and_then(f))
let done = me.done;
me.stream.poll_next(cx).map(|opt| {
let mapped = opt.and_then(f);
if mapped.is_none() {
*done = true;
}
mapped
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.done {
return (0, Some(0));
}
let (_, upper) = self.stream.size_hint();
(0, upper)
}
}
impl<St, F> FusedStream for MapWhile<St, F>
where
Self: Stream,
{
fn is_terminated(&self) -> bool {
self.done
}
}
+8
View File
@@ -47,4 +47,12 @@ impl<T: Stream> Stream for Peekable<T> {
this.stream.poll_next(cx)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let peek_len = if self.peek.is_some() { 1 } else { 0 };
let (lo, hi) = self.stream.size_hint();
let lo = lo.saturating_add(peek_len);
let hi = hi.and_then(|x| x.checked_add(peek_len));
(lo, hi)
}
}
+1 -7
View File
@@ -49,13 +49,7 @@ where
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if !*self.as_mut().project().done {
self.as_mut().project().stream.poll_next(cx).map(|ready| {
let ready = ready.and_then(|item| {
if !(self.as_mut().project().predicate)(&item) {
None
} else {
Some(item)
}
});
let ready = ready.filter(self.as_mut().project().predicate);
if ready.is_none() {
*self.as_mut().project().done = true;
+7 -3
View File
@@ -632,6 +632,10 @@ where
should_loop = true;
idx = idx.wrapping_add(1) % self.entries.len();
if added == limit {
break;
}
}
Poll::Ready(None) => {
// Remove the entry
@@ -685,15 +689,15 @@ where
}
fn size_hint(&self) -> (usize, Option<usize>) {
let mut ret = (0, Some(0));
let mut ret: (usize, Option<usize>) = (0, Some(0));
for (_, stream) in &self.entries {
let hint = stream.size_hint();
ret.0 += hint.0;
ret.0 = ret.0.saturating_add(hint.0);
match (ret.1, hint.1) {
(Some(a), Some(b)) => ret.1 = Some(a + b),
(Some(a), Some(b)) => ret.1 = a.checked_add(b),
(Some(_), None) => ret.1 = None,
_ => {}
}
+5
View File
@@ -13,6 +13,11 @@ pub use mpsc_bounded::ReceiverStream;
mod mpsc_unbounded;
pub use mpsc_unbounded::UnboundedReceiverStream;
cfg_rt! {
mod task;
pub use task::JoinSetStream;
}
cfg_sync! {
mod broadcast;
pub use broadcast::BroadcastStream;
+78
View File
@@ -0,0 +1,78 @@
use crate::Stream;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::task::{JoinError, JoinSet};
/// A wrapper around [`tokio::task::JoinSet`] that implements [`Stream`].
///
/// # Example
///
/// ```
/// use tokio::task::JoinSet;
/// use tokio_stream::wrappers::JoinSetStream;
/// use tokio_stream::StreamExt;
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> Result<(), tokio::task::JoinError> {
/// let set: JoinSet<_> = (0..2).map(|i| async move { i }).collect();
///
/// let mut stream = JoinSetStream::new(set);
/// assert_eq!(stream.next().await.transpose()?, Some(0));
/// assert_eq!(stream.next().await.transpose()?, Some(1));
/// assert_eq!(stream.next().await.transpose()?, None);
/// # Ok(())
/// # }
/// ```
///
/// [`tokio::task::JoinSet`]: struct@tokio::task::JoinSet
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
pub struct JoinSetStream<T> {
inner: JoinSet<T>,
}
impl<T> JoinSetStream<T> {
/// Create a new `JoinSetStream`.
pub fn new(join_set: JoinSet<T>) -> Self {
Self { inner: join_set }
}
/// Get back the inner `JoinSet`.
pub fn into_inner(self) -> JoinSet<T> {
self.inner
}
}
impl<T: 'static> Stream for JoinSetStream<T> {
type Item = Result<T, JoinError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.inner.poll_join_next(cx)
}
/// Returns the bounds of the stream based on the underlying `JoinSet`.
///
/// It returns `(set.len(), Some(set.len()))`.
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.inner.len();
(size, Some(size))
}
}
impl<T> AsRef<JoinSet<T>> for JoinSetStream<T> {
fn as_ref(&self) -> &JoinSet<T> {
&self.inner
}
}
impl<T> AsMut<JoinSet<T>> for JoinSetStream<T> {
fn as_mut(&mut self) -> &mut JoinSet<T> {
&mut self.inner
}
}
impl<T> From<JoinSet<T>> for JoinSetStream<T> {
fn from(join_set: JoinSet<T>) -> Self {
Self::new(join_set)
}
}
+39
View File
@@ -0,0 +1,39 @@
#![cfg(feature = "rt")]
use futures::{Stream, StreamExt};
use std::collections::HashSet;
use tokio::task::JoinSet;
use tokio_stream::wrappers::JoinSetStream;
#[tokio::test]
async fn size_hint_stream() {
let set: JoinSet<_> = (0..2).map(|i| async move { i }).collect();
let mut stream = JoinSetStream::new(set);
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 join_set_as_stream() {
let set: JoinSet<_> = (0..2).map(|i| async move { i }).collect();
let stream = JoinSetStream::new(set);
let values: HashSet<_> = stream.map(|result| result.unwrap()).collect().await;
assert_eq!(values, HashSet::from([0, 1]));
}
// Cannot run this test when “unwind” is disabled
// since `JoinSet` use it to catch futures that panics.
#[cfg(panic = "unwind")]
#[tokio::test]
async fn join_set_as_stream_panics_with_error() {
let set: JoinSet<_> = std::iter::once(async move { panic!("boom!") }).collect();
let mut stream = JoinSetStream::new(set);
let result = stream.next().await.transpose();
assert!(matches!(result, Err(e) if e.is_panic()));
}
+20
View File
@@ -129,6 +129,26 @@ async fn take_while_terminated_after_predicate_fails() {
assert!(stream.is_terminated());
}
// ── map_while ─────────────────────────────────────────────────────────────────
#[tokio::test]
async fn map_while_not_terminated_before_closure_returns_none() {
let stream =
tokio_stream::iter(vec![1, 2, 3]).map_while(|x| if x < 10 { Some(x) } else { None });
assert!(!stream.is_terminated());
}
#[tokio::test]
async fn map_while_terminated_after_closure_returns_none() {
let mut stream =
tokio_stream::iter(vec![1, 5, 2]).map_while(|x| if x < 3 { Some(x) } else { None });
assert_eq!(stream.next().await, Some(1));
assert!(!stream.is_terminated());
// closure returns `None` on 5 → done flag set
assert_eq!(stream.next().await, None);
assert!(stream.is_terminated());
}
// ── then ─────────────────────────────────────────────────────────────────────
#[tokio::test]
+37 -3
View File
@@ -1,7 +1,6 @@
use tokio_stream as stream;
use tokio_test::task;
use std::iter;
use tokio_stream::{self as stream, Stream};
use tokio_test::{assert_pending, assert_ready, task};
#[tokio::test]
async fn coop() {
@@ -9,6 +8,7 @@ async fn coop() {
for _ in 0..10_000 {
if stream.poll_next().is_pending() {
tokio::task::yield_now().await;
assert!(stream.is_woken());
return;
}
@@ -16,3 +16,37 @@ async fn coop() {
panic!("did not yield");
}
#[tokio::test]
async fn test_iter_coop_budget() {
let mut stream = task::spawn(stream::iter(iter::repeat(1)));
// Tokio's default budget is 128.
// Fallback yield_amt is 32.
let limit = if cfg!(feature = "rt") { 128 } else { 32 };
for i in 0..limit {
let res = stream.poll_next();
assert!(res.is_ready(), "Should be ready at index {i}");
}
// Next poll should be pending
assert_pending!(stream.poll_next());
tokio::task::yield_now().await;
assert!(stream.is_woken());
}
#[tokio::test]
async fn test_iter_size_hint() {
let stream = stream::iter(vec![1, 2, 3]);
assert_eq!(stream.size_hint(), (3, Some(3)));
}
#[tokio::test]
async fn test_iter_eof_behavior() {
let mut stream = task::spawn(stream::iter(vec![1]));
assert_ready!(stream.poll_next());
assert_ready!(stream.poll_next()); // EOF should be ready None
}
+22
View File
@@ -0,0 +1,22 @@
use tokio_stream::StreamExt;
#[tokio::test]
async fn map_while_yields_until_closure_returns_none() {
let mut stream =
tokio_stream::iter(1..=10).map_while(|x| if x < 4 { Some(x + 3) } else { None });
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);
}
#[tokio::test]
async fn map_while_does_not_poll_after_closure_returns_none() {
// Once the closure returns `None`, the underlying stream must not be polled
// again, so the trailing `2` is never yielded.
let mut stream =
tokio_stream::iter(vec![1, 5, 2]).map_while(|x| if x < 3 { Some(x) } else { None });
assert_eq!(stream.next().await, Some(1));
assert_eq!(stream.next().await, None);
assert_eq!(stream.next().await, None);
}
+123
View File
@@ -0,0 +1,123 @@
use tokio_stream::{self as stream, Stream, StreamExt};
#[tokio::test]
async fn size_hint_without_peek() {
let mut s = stream::iter(vec![1, 2, 3]).peekable();
assert_eq!(s.size_hint(), (3, Some(3)));
s.next().await;
assert_eq!(s.size_hint(), (2, Some(2)));
s.next().await;
assert_eq!(s.size_hint(), (1, Some(1)));
s.next().await;
assert_eq!(s.size_hint(), (0, Some(0)));
}
#[tokio::test]
async fn size_hint_with_peek() {
let mut s = stream::iter(vec![1, 2, 3]).peekable();
// before peek: all items are in inner stream
assert_eq!(s.size_hint(), (3, Some(3)));
// after peek: one item moves into self.peek buffer — total must still be 3
let _ = s.peek().await;
assert_eq!(s.size_hint(), (3, Some(3)));
// consume the peeked item via next()
assert_eq!(s.next().await, Some(1));
assert_eq!(s.size_hint(), (2, Some(2)));
// peek again
let _ = s.peek().await;
assert_eq!(s.size_hint(), (2, Some(2)));
s.next().await;
assert_eq!(s.size_hint(), (1, Some(1)));
// peek the last item
let _ = s.peek().await;
assert_eq!(s.size_hint(), (1, Some(1)));
s.next().await;
assert_eq!(s.size_hint(), (0, Some(0)));
}
#[tokio::test]
async fn size_hint_empty_stream() {
let mut s = stream::iter(Vec::<i32>::new()).peekable();
assert_eq!(s.size_hint(), (0, Some(0)));
assert_eq!(s.peek().await, None);
assert_eq!(s.size_hint(), (0, Some(0)));
}
#[tokio::test]
async fn peek_returns_correct_item() {
let mut s = stream::iter(vec![10, 20, 30]).peekable();
assert_eq!(s.peek().await, Some(&10));
assert_eq!(s.peek().await, Some(&10)); // second peek returns same item
assert_eq!(s.next().await, Some(10)); // next() gives same item
assert_eq!(s.next().await, Some(20));
assert_eq!(s.next().await, Some(30));
assert_eq!(s.next().await, None);
}
#[tokio::test]
async fn size_hint_overflow() {
// When inner stream reports usize::MAX upper bound and an item is peeked,
// checked_add must return None rather than wrapping.
struct MaxHint(bool); // bool = whether to return one item
impl Stream for MaxHint {
type Item = ();
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<()>> {
if self.0 {
self.0 = false;
std::task::Poll::Ready(Some(()))
} else {
std::task::Poll::Ready(None)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::MAX, Some(usize::MAX))
}
}
let mut s = MaxHint(true).peekable();
// before peek: delegates directly to inner
assert_eq!(s.size_hint(), (usize::MAX, Some(usize::MAX)));
// after peek: peek_len=1, checked_add(1) on usize::MAX must give None not panic
let _ = s.peek().await;
assert_eq!(s.size_hint(), (usize::MAX, None));
}
#[tokio::test]
async fn size_hint_unbounded_upper() {
// A stream that reports unknown upper bound
struct Unbounded;
impl Stream for Unbounded {
type Item = u32;
fn poll_next(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<u32>> {
std::task::Poll::Ready(Some(42))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(1, None)
}
}
let mut s = Unbounded.peekable();
assert_eq!(s.size_hint(), (1, None));
let _ = s.peek().await;
assert_eq!(s.size_hint(), (2, None)); // still unbounded after peek
}
+66
View File
@@ -225,6 +225,30 @@ fn size_hint_without_upper() {
assert_eq!(size_hint, (3, None));
}
#[test]
fn size_hint_overflow() {
struct Monster;
impl Stream for Monster {
type Item = ();
fn poll_next(self: Pin<&mut Self>, _cx: &mut std::task::Context<'_>) -> Poll<Option<()>> {
panic!()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::MAX, Some(usize::MAX))
}
}
let mut map = StreamMap::new();
map.insert("a", Monster);
map.insert("b", Monster);
assert_eq!(map.size_hint(), (usize::MAX, None));
}
#[test]
fn new_capacity_zero() {
let map = StreamMap::<&str, stream::Pending<()>>::new();
@@ -405,6 +429,27 @@ async fn poll_next_many_enough() {
assert!(buffer.contains(&(1, 1)));
}
#[tokio::test]
async fn poll_next_many_does_not_exceed_limit() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(iter([0usize].into_iter())) as UsizeStream);
stream_map.insert(1, Box::pin(iter([1usize].into_iter())) as UsizeStream);
let mut buffer = vec![];
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut buffer, 1)).await;
assert_eq!(n, 1);
assert_eq!(buffer.len(), 1);
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut buffer, 1)).await;
assert_eq!(n, 1);
assert_eq!(buffer.len(), 2);
assert!(buffer.contains(&(0, 0)));
assert!(buffer.contains(&(1, 1)));
}
#[tokio::test]
async fn poll_next_many_correctly_loops_around() {
for _ in 0..10 {
@@ -519,6 +564,27 @@ async fn next_many_enough() {
assert!(buffer.contains(&(1, 1)));
}
#[tokio::test]
async fn next_many_does_not_exceed_limit() {
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
stream_map.insert(0, Box::pin(iter([0usize].into_iter())) as UsizeStream);
stream_map.insert(1, Box::pin(iter([1usize].into_iter())) as UsizeStream);
let mut buffer = vec![];
let n = poll_fn(|cx| pin!(stream_map.next_many(&mut buffer, 1)).poll(cx)).await;
assert_eq!(n, 1);
assert_eq!(buffer.len(), 1);
let n = poll_fn(|cx| pin!(stream_map.next_many(&mut buffer, 1)).poll(cx)).await;
assert_eq!(n, 1);
assert_eq!(buffer.len(), 2);
assert!(buffer.contains(&(0, 0)));
assert!(buffer.contains(&(1, 1)));
}
#[tokio::test]
async fn next_many_correctly_loops_around() {
for _ in 0..10 {
+40
View File
@@ -70,6 +70,9 @@ const IDLE: usize = 0;
const WAKE: usize = 1;
const SLEEP: usize = 2;
/// Default maximum number of poll iterations in [`Spawn::poll_until_idle`].
const POLL_UNTIL_IDLE_MAX_ITERATIONS: usize = 150;
impl<T> Spawn<T> {
/// Consumes `self` returning the inner value
pub fn into_inner(self) -> T
@@ -123,6 +126,43 @@ impl<T: Future> Spawn<T> {
let fut = self.future.as_mut();
self.task.enter(|cx| fut.poll(cx))
}
/// Polls the future until it is idle.
///
/// A future is considered idle when it either completes, or returns
/// [`Poll::Pending`] without a pending wake notification.
///
/// Unlike [`poll`](Self::poll), this method keeps polling while the future
/// returns [`Poll::Pending`] but has received a wake notification, advancing
/// the future as far as possible without waiting for external events.
///
/// Polling is bounded to avoid infinite loops when a future wakes without
/// making progress.
///
/// # Panics
///
/// Panics if the iteration limit is exceeded.
///
/// # Example
///
/// ```
/// use tokio_test::task;
///
/// let mut task = task::spawn(async { 42 });
///
/// assert!(task.poll_until_idle().is_ready());
/// ```
pub fn poll_until_idle(&mut self) -> Poll<T::Output> {
for _ in 0..POLL_UNTIL_IDLE_MAX_ITERATIONS {
let result = self.poll();
if result.is_ready() || !self.is_woken() {
return result;
}
}
panic!(
"poll_until_idle exceeded {POLL_UNTIL_IDLE_MAX_ITERATIONS} iterations; future may be waking without making progress"
);
}
}
impl<T: Stream> Spawn<T> {
+79
View File
@@ -1,3 +1,4 @@
use std::future::{pending, Future};
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio_stream::Stream;
@@ -23,3 +24,81 @@ fn test_spawn_stream_size_hint() {
let spawn = task::spawn(SizedStream);
assert_eq!(spawn.size_hint(), (100, Some(200)));
}
#[test]
fn poll_until_idle_ready() {
let mut task = task::spawn(async { 42 });
assert_eq!(task.poll_until_idle(), Poll::Ready(42));
}
#[test]
fn poll_until_idle_pending_not_woken() {
let mut task = task::spawn(pending::<()>());
assert!(task.poll_until_idle().is_pending());
}
struct WakeThenReady {
step: u8,
}
impl Future for WakeThenReady {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
match self.step {
0 => {
self.step = 1;
cx.waker().wake_by_ref();
Poll::Pending
}
_ => Poll::Ready(()),
}
}
}
#[test]
fn poll_until_idle_advances_on_wake() {
let mut task = task::spawn(WakeThenReady { step: 0 });
assert!(task.poll_until_idle().is_ready());
}
struct WakeNTimes {
remaining: u8,
}
impl Future for WakeNTimes {
type Output = u8;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<u8> {
if self.remaining == 0 {
return Poll::Ready(0);
}
self.remaining -= 1;
cx.waker().wake_by_ref();
Poll::Pending
}
}
#[test]
fn poll_until_idle_multiple_wakes() {
let mut task = task::spawn(WakeNTimes { remaining: 3 });
assert_eq!(task.poll_until_idle(), Poll::Ready(0));
}
struct WakeForever;
impl Future for WakeForever {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
cx.waker().wake_by_ref();
Poll::Pending
}
}
#[test]
#[should_panic(expected = "poll_until_idle exceeded 150 iterations")]
fn poll_until_idle_panics_on_infinite_wake() {
let mut task = task::spawn(WakeForever);
let _ = task.poll_until_idle();
}
+4 -1
View File
@@ -25,7 +25,7 @@ full = ["codec", "compat", "io-util", "time", "net", "rt", "join-map"]
net = ["tokio/net"]
compat = ["futures-io"]
codec = []
codec = ["libc"]
time = ["tokio/time", "slab"]
io = []
io-util = ["io", "tokio/rt", "tokio/io-util"]
@@ -46,6 +46,9 @@ slab = { version = "0.4.4", optional = true } # Backs `DelayQueue`
tracing = { version = "0.1.29", default-features = false, features = ["std"], optional = true }
hashbrown = { version = "0.15.0", default-features = false, optional = true }
[target.'cfg(unix)'.dependencies]
libc = { version = "0.2.168", optional = true } # Backs the LinesCodec delimiter scan via libc::memchr
[dev-dependencies]
tokio = { version = "1.0.0", features = ["full"] }
tokio-test = "0.4.0"
+1 -1
View File
@@ -47,7 +47,7 @@ pub trait Decoder {
/// implementation of `decode_eof` to yield an `io::Error` when the decoder
/// fails to consume all available data.
///
/// Note that implementors of this trait can simply indicate `type Error =
/// Note that implementers of this trait can simply indicate `type Error =
/// io::Error` to use I/O errors as this type.
///
/// [`FramedRead`]: crate::codec::FramedRead
+1 -3
View File
@@ -115,9 +115,7 @@ impl Decoder for LinesCodec {
// there's no max_length set, we'll read to the end of the buffer.
let read_to = cmp::min(self.max_length.saturating_add(1), buf.len());
let newline_offset = buf[self.next_index..read_to]
.iter()
.position(|b| *b == b'\n');
let newline_offset = crate::util::memchr::memchr(b'\n', &buf[self.next_index..read_to]);
match (self.is_discarding, newline_offset) {
(true, Some(offset)) => {
+2 -2
View File
@@ -12,7 +12,7 @@
//! The following example demonstrates how to use a codec such as [`LinesCodec`] to
//! write framed data. [`FramedWrite`] can be used to achieve this. Data sent to
//! [`FramedWrite`] are first framed according to a specific codec, and then sent to
//! an implementor of [`AsyncWrite`].
//! an implementer of [`AsyncWrite`].
//!
//! ```
//! use futures::sink::SinkExt;
@@ -43,7 +43,7 @@
//! # Example decoding using `LinesCodec`
//! The following example demonstrates how to use a codec such as [`LinesCodec`] to
//! read a stream of framed data. [`FramedRead`] can be used to achieve this. [`FramedRead`]
//! will keep reading from an [`AsyncRead`] implementor until a whole frame, according to a codec,
//! will keep reading from an [`AsyncRead`] implementer until a whole frame, according to a codec,
//! can be parsed.
//!
//!```
+2 -2
View File
@@ -147,8 +147,8 @@ fn advance_slices<'a>(bufs: &mut &mut [IoSlice<'a>], n: usize) {
*bufs = &mut std::mem::take(bufs)[remove..];
if let Some(first) = bufs.first_mut() {
let buf = &first[..left];
// necessary due to limitating in the borrow checker,
let buf = &first[left..];
// Necessary due to a limitation in the borrow checker,
// when tokio MSRV reaches 1.81.0 this entire function
// can be replaced with `IoSlice::advance_slices`
//
@@ -10,6 +10,13 @@ pub struct DropGuard {
}
impl DropGuard {
/// Returns a reference to the cancellation token wrapped by this guard.
pub fn token(&self) -> &CancellationToken {
self.inner
.as_ref()
.expect("`inner` can only be None in a destructor")
}
/// Returns stored cancellation token and removes this drop guard instance
/// (i.e. it will no longer cancel token). Other guards for this token
/// are not affected.
@@ -13,6 +13,13 @@ pub struct DropGuardRef<'a> {
}
impl<'a> DropGuardRef<'a> {
/// Returns a reference to the cancellation token wrapped by this guard.
pub fn token(&self) -> &CancellationToken {
self.inner
.as_ref()
.expect("`inner` can only be None in a destructor")
}
/// Returns stored cancellation token and removes this drop guard instance
/// (i.e. it will no longer cancel token). Other guards for this token
/// are not affected.
+55 -1
View File
@@ -446,7 +446,7 @@ where
/// * `Some((key, Ok(value)))` if one of the tasks in this `JoinMap` has
/// completed. The `value` is the return value of that ask, and `key` is
/// the key associated with the task.
/// * `Some((key, Err(err))` if one of the tasks in this `JoinMap` has
/// * `Some((key, Err(err)))` if one of the tasks in this `JoinMap` has
/// panicked or been aborted. `key` is the key associated with the task
/// that panicked or was aborted.
/// * `None` if the `JoinMap` is empty.
@@ -468,6 +468,60 @@ where
}
}
/// Tries to join one of the tasks in the map that has completed and
/// returns its output, along with the key corresponding to that task.
///
/// Returns `None` if there are no completed tasks, or if the map is empty.
///
/// # Returns
///
/// This function returns:
///
/// * `Some((key, Ok(value)))` if one of the tasks in this `JoinMap` has
/// completed. The `value` is the return value of that task, and `key`
/// is the key associated with the task.
/// * `Some((key, Err(err)))` if one of the tasks in this `JoinMap` has
/// panicked or been aborted. `key` is the key associated with the task
/// that panicked or was aborted.
/// * `None` if there are no completed tasks ready to be joined, or the
/// `JoinMap` is empty.
///
/// # Examples
///
/// ```
/// use tokio_util::task::JoinMap;
///
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// let mut map = JoinMap::new();
/// map.spawn("answer", async { 42 });
///
/// let (key, res) = loop {
/// if let Some(joined) = map.try_join_next() {
/// break joined;
/// }
/// tokio::task::yield_now().await;
/// };
///
/// assert_eq!(key, "answer");
/// assert_eq!(res.unwrap(), 42);
/// # }
/// ```
pub fn try_join_next(&mut self) -> Option<(K, Result<V, JoinError>)> {
loop {
let (res, id) = match self.tasks.try_join_next_with_id()? {
Ok((id, output)) => (Ok(output), id),
Err(e) => {
let id = e.id();
(Err(e), id)
}
};
if let Some(key) = self.remove_by_id(id) {
break Some((key, res));
}
}
}
/// Aborts all tasks and waits for them to finish shutting down.
///
/// Calling this method is equivalent to calling [`abort_all`] and then calling [`join_next`] in
+4
View File
@@ -188,6 +188,10 @@ impl<T> JoinQueue<T> {
/// Note that on success the handle will panic on subsequent polls
/// since it becomes consumed.
fn try_poll_handle(jh: &mut AbortOnDropHandle<T>) -> Option<Result<T, JoinError>> {
if !jh.is_finished() {
return None;
}
let waker = futures_util::task::noop_waker();
let mut cx = Context::from_waker(&waker);
+11 -3
View File
@@ -864,11 +864,19 @@ impl<T> DelayQueue<T> {
self.slab[*key].expired = false;
self.insert_idx(when, *key);
let inserted_expired = self.slab[*key].expired;
let next_deadline = self.next_deadline();
if let (Some(ref mut delay), Some(deadline)) = (&mut self.delay, next_deadline) {
// This should awaken us if necessary (ie, if already expired)
delay.as_mut().reset(deadline);
match (next_deadline, &mut self.delay) {
(None, _) => self.delay = None,
(Some(deadline), Some(delay)) => delay.as_mut().reset(deadline),
(Some(deadline), None) => self.delay = Some(Box::pin(sleep_until(deadline))),
}
if inserted_expired {
if let Some(waker) = self.waker.take() {
waker.wake();
}
}
}
+6
View File
@@ -22,6 +22,12 @@ use std::{
/// handle encoding and decoding of messages frames. Note that the incoming and
/// outgoing frame types may be distinct.
///
/// A single datagram may decode into multiple frames. `UdpFramed` will keep
/// calling [`Decoder::decode_eof`] with the current datagram until it returns
/// `Ok(None)`. If a decoder wants to discard a malformed datagram and continue
/// receiving later datagrams, it should consume or clear the remaining bytes
/// from the buffer before returning `Err`.
///
/// This function returns a *single* object that is both [`Stream`] and [`Sink`];
/// grouping this into a single object is often useful for layering things which
/// require both read and write access to the underlying object.
+102
View File
@@ -0,0 +1,102 @@
//! Search for a byte in a byte array using libc.
//!
//! When nothing pulls in libc, then just use a trivial implementation. Note
//! that we only depend on libc on unix.
#[cfg(not(all(unix, feature = "libc")))]
fn memchr_inner(needle: u8, haystack: &[u8]) -> Option<usize> {
haystack.iter().position(|val| needle == *val)
}
#[cfg(all(unix, feature = "libc"))]
fn memchr_inner(needle: u8, haystack: &[u8]) -> Option<usize> {
let start = haystack.as_ptr();
// SAFETY: `start` is valid for `haystack.len()` bytes.
let ptr = (unsafe { libc::memchr(start.cast(), needle as _, haystack.len()) })
.cast::<u8>()
.cast_const();
if ptr.is_null() {
None
} else {
// SAFETY: `ptr` will always be in bounds, since libc guarantees that the ptr will either
// be to an element inside the array or the ptr will be null
// since the ptr is in bounds the offset must also always be non null
// and there can't be more than isize::MAX elements inside an array
// as rust guarantees that the maximum number of bytes a allocation
// may occupy is isize::MAX
unsafe {
// TODO(MSRV 1.87): When bumping MSRV, switch to `ptr.byte_offset_from_unsigned(start)`.
Some(usize::try_from(ptr.offset_from(start)).unwrap_unchecked())
}
}
}
pub(crate) fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
let index = memchr_inner(needle, haystack)?;
// SAFETY: `memchr_inner` returns Some(index) and in that case index must point to an element in haystack
// or `memchr_inner` None which is guarded by the `?` operator above
// therefore the index must **always** point to an element in the array
// and so this indexing operation is safe
// TODO(MSRV 1.81): When bumping MSRV, switch to `std::hint::assert_unchecked(haystack.get(..=index).is_some());`
unsafe {
if haystack.get(..=index).is_none() {
std::hint::unreachable_unchecked()
}
}
Some(index)
}
#[cfg(test)]
mod tests {
use super::memchr;
#[test]
fn memchr_test() {
let haystack = b"123abc456\0\xffabc\n";
assert_eq!(memchr(b'1', haystack), Some(0));
assert_eq!(memchr(b'2', haystack), Some(1));
assert_eq!(memchr(b'3', haystack), Some(2));
assert_eq!(memchr(b'4', haystack), Some(6));
assert_eq!(memchr(b'5', haystack), Some(7));
assert_eq!(memchr(b'6', haystack), Some(8));
assert_eq!(memchr(b'7', haystack), None);
assert_eq!(memchr(b'a', haystack), Some(3));
assert_eq!(memchr(b'b', haystack), Some(4));
assert_eq!(memchr(b'c', haystack), Some(5));
assert_eq!(memchr(b'd', haystack), None);
assert_eq!(memchr(b'A', haystack), None);
assert_eq!(memchr(0, haystack), Some(9));
assert_eq!(memchr(0xff, haystack), Some(10));
assert_eq!(memchr(0xfe, haystack), None);
assert_eq!(memchr(1, haystack), None);
assert_eq!(memchr(b'\n', haystack), Some(14));
assert_eq!(memchr(b'\r', haystack), None);
}
#[test]
fn memchr_all() {
let mut arr = Vec::new();
for b in 0..=255 {
arr.push(b);
}
for b in 0..=255 {
assert_eq!(memchr(b, &arr), Some(b as usize));
}
arr.reverse();
for b in 0..=255 {
assert_eq!(memchr(b, &arr), Some(255 - b as usize));
}
}
#[test]
fn memchr_empty() {
for b in 0..=255 {
assert_eq!(memchr(b, b""), None);
}
}
}
+2
View File
@@ -1,4 +1,6 @@
mod maybe_dangling;
#[cfg(feature = "codec")]
pub(crate) mod memchr;
#[cfg(any(feature = "io", feature = "codec"))]
mod poll_buf;
+75
View File
@@ -10,6 +10,55 @@ use std::io::IoSlice;
use std::pin::Pin;
use std::task::{Context, Poll};
struct PartialVectoredWriter {
buf: BytesMut,
max_write: usize,
}
impl AsyncWrite for PartialVectoredWriter {
fn poll_write(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
_buf: &[u8],
) -> Poll<io::Result<usize>> {
panic!("shouldn't be called")
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Ok(()).into()
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Ok(()).into()
}
fn poll_write_vectored(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<Result<usize, io::Error>> {
let mut remaining = self.max_write;
let mut written = 0;
for buf in bufs {
if remaining == 0 {
break;
}
let n = remaining.min(buf.len());
self.buf.extend_from_slice(&buf[..n]);
remaining -= n;
written += n;
}
Ok(written).into()
}
fn is_write_vectored(&self) -> bool {
true
}
}
#[tokio::test]
async fn test_write_all_vectored() {
struct Wr {
@@ -140,3 +189,29 @@ async fn write_all_vectored_with_empty_slice() {
write_all_vectored(&mut wr, buf).await.unwrap();
assert_eq!(&wr.buf[..], b"hello");
}
#[tokio::test]
async fn write_all_vectored_should_continue_with_unwritten_suffix_if_write_stops_inside_buffer() {
let mut wr = PartialVectoredWriter {
buf: BytesMut::with_capacity(64),
max_write: 3,
};
let buf = &mut [IoSlice::new(b"hello"), IoSlice::new(b"world")];
write_all_vectored(&mut wr, buf).await.unwrap();
assert_eq!(&wr.buf[..], b"helloworld");
}
#[tokio::test]
async fn write_all_vectored_should_continue_with_next_buffer_if_write_ends_on_boundary() {
let mut wr = PartialVectoredWriter {
buf: BytesMut::with_capacity(64),
max_write: 2,
};
let buf = &mut [IoSlice::new(b"ab"), IoSlice::new(b"cd")];
write_all_vectored(&mut wr, buf).await.unwrap();
assert_eq!(&wr.buf[..], b"abcd");
}
+125
View File
@@ -358,6 +358,131 @@ async fn abort_all() {
}
}
#[tokio::test]
async fn try_join_next_empty() {
let mut map: JoinMap<usize, ()> = JoinMap::new();
assert!(map.try_join_next().is_none());
}
#[tokio::test]
async fn try_join_next_no_ready_task() {
let mut map = JoinMap::new();
let (_tx, rx) = oneshot::channel::<()>();
map.spawn("pending", async move {
let _ = rx.await;
});
// Task is not yet ready.
assert!(map.try_join_next().is_none());
assert_eq!(map.len(), 1);
}
#[tokio::test]
async fn try_join_next_completed_task() {
let mut map = JoinMap::new();
map.spawn("hello", async { 42 });
let mut got = None;
while got.is_none() {
got = map.try_join_next();
if got.is_none() {
tokio::task::yield_now().await;
}
}
let (key, res) = got.unwrap();
assert_eq!(key, "hello");
assert_eq!(res.unwrap(), 42);
assert!(map.is_empty());
}
#[tokio::test]
async fn try_join_next_aborted_task() {
let mut map = JoinMap::new();
map.spawn("forever", async {
futures::future::pending::<()>().await;
});
assert!(map.abort("forever"));
let mut got = None;
while got.is_none() {
got = map.try_join_next();
if got.is_none() {
tokio::task::yield_now().await;
}
}
let (key, res) = got.unwrap();
assert_eq!(key, "forever");
assert!(res.unwrap_err().is_cancelled());
assert!(map.is_empty());
}
#[tokio::test(flavor = "current_thread")]
async fn try_join_next_advances_through_multiple() {
const N: u32 = 8;
static SEM: tokio::sync::Semaphore = tokio::sync::Semaphore::const_new(0);
let mut map = JoinMap::new();
for i in 0..N {
map.spawn(i, async move {
SEM.add_permits(1);
i
});
}
// Wait until all tasks have signalled completion. On the current_thread
// runtime this means they have actually finished.
let _ = SEM.acquire_many(N).await.unwrap();
let mut seen = vec![false; N as usize];
let mut count = 0;
loop {
match map.try_join_next() {
Some((key, res)) => {
let v = res.expect("task should have completed successfully");
assert_eq!(key, v);
seen[v as usize] = true;
count += 1;
}
None if map.is_empty() => break,
None => tokio::task::yield_now().await,
}
}
assert_eq!(count, N);
assert!(seen.into_iter().all(|b| b));
assert!(map.try_join_next().is_none());
}
#[tokio::test]
async fn try_join_next_skips_replaced_task() {
let mut map = JoinMap::new();
let (tx1, rx1) = oneshot::channel::<()>();
map.spawn(1, async {
let _ = rx1.await;
11
});
tx1.send(()).unwrap();
tokio::task::yield_now().await;
let (tx2, rx2) = oneshot::channel::<()>();
map.spawn(1, async {
let _ = rx2.await;
22
});
tx2.send(()).unwrap();
tokio::task::yield_now().await;
let (key, res) = map.try_join_next().unwrap();
assert_eq!(key, 1);
assert_eq!(res.unwrap(), 22);
assert!(map.try_join_next().is_none());
assert!(map.is_empty());
}
#[tokio::test]
async fn duplicate_keys() {
let mut map = JoinMap::new();
+57
View File
@@ -1,5 +1,8 @@
#![warn(rust_2018_idioms)]
use std::task::Context;
use futures_test::task::new_count_waker;
use tokio::sync::oneshot;
use tokio::task::yield_now;
use tokio::time::Duration;
@@ -276,6 +279,60 @@ async fn test_join_queue_try_join_next() {
check_try_join_next_is_noop(&mut queue);
}
#[tokio::test]
async fn test_join_queue_try_join_next_does_not_replace_waker() {
let (send, recv) = oneshot::channel();
let mut queue = JoinQueue::new();
queue.spawn(async move {
recv.await.unwrap();
42
});
let (waker, wake_count) = new_count_waker();
let mut cx = Context::from_waker(&waker);
assert_pending!(queue.poll_join_next(&mut cx));
assert_eq!(wake_count, 0);
assert!(queue.try_join_next().is_none());
send.send(()).unwrap();
yield_now().await;
assert_eq!(wake_count, 1);
assert_eq!(
assert_ready!(queue.poll_join_next(&mut cx))
.unwrap()
.unwrap(),
42
);
}
#[tokio::test]
async fn test_join_queue_try_join_next_with_id_does_not_replace_waker() {
let (send, recv) = oneshot::channel();
let mut queue = JoinQueue::new();
queue.spawn(async move {
recv.await.unwrap();
42
});
let (waker, wake_count) = new_count_waker();
let mut cx = Context::from_waker(&waker);
assert_pending!(queue.poll_join_next_with_id(&mut cx));
assert_eq!(wake_count, 0);
assert!(queue.try_join_next_with_id().is_none());
send.send(()).unwrap();
yield_now().await;
assert_eq!(wake_count, 1);
let (_, output) = assert_ready!(queue.poll_join_next_with_id(&mut cx))
.unwrap()
.unwrap();
assert_eq!(output, 42);
}
#[tokio::test]
async fn test_join_queue_try_join_next_disabled_coop() {
// This number is large enough to trigger coop. Without using `tokio::task::coop::unconstrained`
+21
View File
@@ -202,6 +202,27 @@ async fn reset_entry() {
assert!(entry.is_none())
}
#[tokio::test]
async fn reset_to_past_wakes_pending_queue() {
time::pause();
let mut queue = task::spawn(DelayQueue::new());
let key = queue.insert("foo", ms(10_000));
assert_pending!(poll!(queue));
assert!(!queue.is_woken());
queue.reset_at(&key, Instant::now() - ms(100));
assert!(queue.is_woken());
let entry = assert_ready_some!(poll!(queue));
assert_eq!(*entry.get_ref(), "foo");
let entry = assert_ready!(poll!(queue));
assert!(entry.is_none());
}
// Reproduces tokio-rs/tokio#849.
#[tokio::test]
async fn reset_much_later() {
+1 -1
View File
@@ -1,6 +1,6 @@
#![warn(rust_2018_idioms)]
#![cfg(not(target_os = "wasi"))] // Wasi doesn't support UDP
#![cfg(not(miri))] // No `socket` in Miri.
#![cfg(not(miri))] // No UDP sockets in Miri.
#![cfg(not(loom))] // No udp / UdpFramed in loom
use tokio::net::UdpSocket;
+156
View File
@@ -1,3 +1,128 @@
# 1.53.1 (July 20th, 2026)
### Fixed
- signal: restore MSRV by removing `OnceLock::wait` from the Windows handler ([#8300])
### Fixed (unstable)
- time: fix alt timer cancellation and insertion race ([#8252])
### Documented
- runtime: remove dead link definition in Runtime::block_on ([#8301])
[#8252]: https://github.com/tokio-rs/tokio/pull/8252
[#8300]: https://github.com/tokio-rs/tokio/pull/8300
[#8301]: https://github.com/tokio-rs/tokio/pull/8301
# 1.53.0 (July 17th, 2026)
### Added
- fs: implement `From<OwnedFd>` and `From<OwnedHandle>` for `File` ([#8266])
- metrics: add task schedule latency metric ([#7986])
- net: add `SocketAddr` methods to Unix sockets ([#8144])
### Changed
- io: add `#[inline]` to IO trait impls for in-memory types ([#8242])
- net: implement UCred::pid on FreeBSD ([#8086])
- net: support Nuttx target os ([#8259])
- signal: refactor global variables on Windows ([#8231])
- sync: `mpsc::{Receiver,UnboundedReceiver}` now drops waker on drop, even if there are still senders ([#8095])
- taskdump: support taskdumps on s390x ([#8192])
- time: add `#[track_caller]` to `timeout_at()` ([#8077])
- time: consolidate mutex locks on spurious poll ([#8124])
- time: defer waker clone on spurious poll ([#8107])
- time: move lazy-registration state into `Sleep` ([#8132])
- tracing: remove unnecessary span clone ([#8126])
### Fixed
- io: do not treat zero-length reads as EOF in `Chain` ([#8251])
- net: use getpeereid for QNX peer credentials ([#8270])
- runtime: avoid illegal state in `FastRand` ([#8078])
- sync: wake mpsc receiver when a queued `reserve[_many]` returns permits ([#8260])
- taskdump: skip double wake on `Trace::capture`/`Trace::trace_with` ([#8043])
- time: avoid stack overflow in runtime constructor ([#8093])
- time (alt timer): ensure timers stay in the same runtime after `.reset()` ([#8169])
### IO uring (unstable)
- fs: use io-uring for `fs::try_exists` ([#8080])
- fs: use io-uring for renaming files ([#7800])
- rt: flush io-uring CQE in case of CQE overflow ([#8277])
### Documented
- docs: clarify cancel safety wording ([#8181])
- fs: clarify `create_dir_all` succeeds if path exists ([#8149])
- io: add warning about stdout reordering with multiple handles ([#8276])
- net: document pipe `try_read*`/`try_write*` readiness behavior ([#8032])
- runtime: document interaction with fork() ([#8202])
- sync: clarify broadcast lagging semantics ([#8239])
- sync: document memory ordering guarantees for Semaphore ([#8119])
- task: explain why `yield_now` defers its waker ([#8254])
- time: add panic docs to `timeout_at()` ([#8077])
- time: fix reversed poll order in timeout doc ([#8214])
[#7800]: https://github.com/tokio-rs/tokio/pull/7800
[#7986]: https://github.com/tokio-rs/tokio/pull/7986
[#8032]: https://github.com/tokio-rs/tokio/pull/8032
[#8043]: https://github.com/tokio-rs/tokio/pull/8043
[#8077]: https://github.com/tokio-rs/tokio/pull/8077
[#8078]: https://github.com/tokio-rs/tokio/pull/8078
[#8080]: https://github.com/tokio-rs/tokio/pull/8080
[#8086]: https://github.com/tokio-rs/tokio/pull/8086
[#8093]: https://github.com/tokio-rs/tokio/pull/8093
[#8095]: https://github.com/tokio-rs/tokio/pull/8095
[#8107]: https://github.com/tokio-rs/tokio/pull/8107
[#8119]: https://github.com/tokio-rs/tokio/pull/8119
[#8124]: https://github.com/tokio-rs/tokio/pull/8124
[#8126]: https://github.com/tokio-rs/tokio/pull/8126
[#8132]: https://github.com/tokio-rs/tokio/pull/8132
[#8144]: https://github.com/tokio-rs/tokio/pull/8144
[#8149]: https://github.com/tokio-rs/tokio/pull/8149
[#8169]: https://github.com/tokio-rs/tokio/pull/8169
[#8181]: https://github.com/tokio-rs/tokio/pull/8181
[#8192]: https://github.com/tokio-rs/tokio/pull/8192
[#8193]: https://github.com/tokio-rs/tokio/pull/8193
[#8202]: https://github.com/tokio-rs/tokio/pull/8202
[#8214]: https://github.com/tokio-rs/tokio/pull/8214
[#8231]: https://github.com/tokio-rs/tokio/pull/8231
[#8239]: https://github.com/tokio-rs/tokio/pull/8239
[#8242]: https://github.com/tokio-rs/tokio/pull/8242
[#8251]: https://github.com/tokio-rs/tokio/pull/8251
[#8254]: https://github.com/tokio-rs/tokio/pull/8254
[#8259]: https://github.com/tokio-rs/tokio/pull/8259
[#8260]: https://github.com/tokio-rs/tokio/pull/8260
[#8266]: https://github.com/tokio-rs/tokio/pull/8266
[#8270]: https://github.com/tokio-rs/tokio/pull/8270
[#8276]: https://github.com/tokio-rs/tokio/pull/8276
[#8277]: https://github.com/tokio-rs/tokio/pull/8277
# 1.52.4 (July 16th, 2026)
### Fixed
- runtime: don't skip the driver when `before_park` schedules work ([#8222])
### Fixed (unstable)
- taskdump: remove crate disambiguators from output ([#8264])
[#8264]: https://github.com/tokio-rs/tokio/pull/8264
# 1.52.3 (May 8th, 2026)
### Fixed
* sync: fix underflow in mpsc channel `len()` ([#8062])
* sync: notify receivers in mpsc `OwnedPermit::release()` method ([#8075])
* sync: require that an `RwLock` has `max_readers != 0` ([#8076])
* sync: return `Empty` from `try_recv()` when mpsc is closed with outstanding permits ([#8074])
# 1.52.2 (May 4th, 2026)
This release reverts the LIFO slot stealing change introduced in 1.51.0
@@ -55,6 +180,23 @@ This release reverts the LIFO slot stealing change introduced in 1.51.0
[#8035]: https://github.com/tokio-rs/tokio/pull/8035
[#8040]: https://github.com/tokio-rs/tokio/pull/8040
# 1.51.4 (July 16th, 2026)
### Fixed
- runtime: don't skip the driver when `before_park` schedules work ([#8222])
[#8222]: https://github.com/tokio-rs/tokio/pull/8222
# 1.51.3 (May 8th, 2026)
### Fixed
* sync: fix underflow in mpsc channel `len()` ([#8062])
* sync: notify receivers in mpsc `OwnedPermit::release()` method ([#8075])
* sync: require that an `RwLock` has `max_readers != 0` ([#8076])
* sync: return `Empty` from `try_recv()` when mpsc is closed with outstanding permits ([#8074])
# 1.51.2 (May 4th, 2026)
This release reverts the LIFO slot stealing change introduced in 1.51.0
@@ -387,6 +529,20 @@ The MSRV is increased to 1.71.
[#7672]: https://github.com/tokio-rs/tokio/pull/7672
[#7675]: https://github.com/tokio-rs/tokio/pull/7675
# 1.47.5 (May 7th, 2026)
### Fixed
* sync: fix underflow in mpsc channel `len()` ([#8062])
* sync: notify receivers in mpsc `OwnedPermit::release()` method ([#8075])
* sync: require that an `RwLock` has `max_readers != 0` ([#8076])
* sync: return `Empty` from `try_recv()` when mpsc is closed with outstanding permits ([#8074])
[#8062]: https://github.com/tokio-rs/tokio/pull/8062
[#8074]: https://github.com/tokio-rs/tokio/pull/8074
[#8075]: https://github.com/tokio-rs/tokio/pull/8075
[#8076]: https://github.com/tokio-rs/tokio/pull/8076
# 1.47.4 (April 2nd, 2026)
### Fixed
+3 -1
View File
@@ -6,7 +6,7 @@ name = "tokio"
# - README.md
# - Update CHANGELOG.md.
# - Create "v1.x.y" git tag.
version = "1.52.2"
version = "1.53.1"
edition = "2021"
rust-version = "1.71"
authors = ["Tokio Contributors <[email protected]>"]
@@ -88,6 +88,8 @@ time = []
io-uring = ["dep:io-uring", "libc", "mio/os-poll", "mio/os-ext", "dep:slab"]
# Unstable feature. Requires `--cfg tokio_unstable` to enable.
taskdump = ["dep:backtrace"]
# Unstable feature. Requires `--cfg tokio_unstable` to enable.
schedule-latency = []
[dependencies]
tokio-macros = { version = "~2.7.0", optional = true }
+1 -1
View File
@@ -60,7 +60,7 @@ Make sure you enable the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.52.2", features = ["full"] }
tokio = { version = "1.53.1", features = ["full"] }
```
Then, on your main.rs:
+1
View File
@@ -52,6 +52,7 @@ impl Registration {
/// Clears resource level readiness represented by the specified `ReadyEvent`
async fn clear_readiness(&self, ready_event: ReadyEvent);
}
```
A new registration is created for a `T: mio::Evented` and a `interest`. This
+3
View File
@@ -26,6 +26,9 @@ use std::path::Path;
/// when a directory is being created (after it is determined to not exist) are
/// outlined by [`fs::create_dir`].
///
/// If the final component at `path` already exists and is a directory, this
/// function will also return successfully without error.
///
/// Notable exception is made for situations where any of the directories
/// specified in the `path` could not be created as it was being created concurrently.
/// Such cases are considered to be successful. That is, calling `create_dir_all`
+21 -8
View File
@@ -599,7 +599,7 @@ impl AsyncRead for File {
cx: &mut Context<'_>,
dst: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
let me = self.get_mut();
let inner = me.inner.get_mut();
@@ -694,7 +694,7 @@ impl AsyncSeek for File {
}
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
let inner = self.inner.get_mut();
loop {
@@ -730,7 +730,7 @@ impl AsyncWrite for File {
cx: &mut Context<'_>,
src: &[u8],
) -> Poll<io::Result<usize>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
let me = self.get_mut();
let inner = me.inner.get_mut();
@@ -801,7 +801,7 @@ impl AsyncWrite for File {
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<Result<usize, io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
let me = self.get_mut();
let inner = me.inner.get_mut();
@@ -872,13 +872,13 @@ impl AsyncWrite for File {
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
let inner = self.inner.get_mut();
inner.poll_flush(cx)
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
self.poll_flush(cx)
}
}
@@ -897,6 +897,13 @@ impl fmt::Debug for File {
}
}
#[cfg(unix)]
impl From<std::os::fd::OwnedFd> for File {
fn from(fd: std::os::fd::OwnedFd) -> Self {
Self::from_std(StdFile::from(fd))
}
}
#[cfg(unix)]
impl std::os::unix::io::AsRawFd for File {
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
@@ -923,7 +930,13 @@ impl std::os::unix::io::FromRawFd for File {
}
cfg_windows! {
use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle, AsHandle, BorrowedHandle};
use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle, AsHandle, BorrowedHandle, OwnedHandle};
impl From<OwnedHandle> for File {
fn from(handle: OwnedHandle) -> Self {
Self::from_std(StdFile::from(handle))
}
}
impl AsRawHandle for File {
fn as_raw_handle(&self) -> RawHandle {
@@ -1077,7 +1090,7 @@ impl Inner {
}
fn poll_complete_inflight(&mut self, cx: &mut Context<'_>) -> Poll<()> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
match self.poll_flush(cx) {
Poll::Ready(Err(e)) => {
self.last_write_err = Some(e.kind());
+13
View File
@@ -38,6 +38,10 @@ mock! {
pub fn try_clone(&self) -> io::Result<Self>;
}
#[cfg(windows)]
impl From<std::os::windows::io::OwnedHandle> for File {
fn from(handle: std::os::windows::io::OwnedHandle) -> Self;
}
#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for File {
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle;
}
@@ -106,6 +110,15 @@ impl From<MockFile> for OwnedFd {
}
}
#[cfg(all(test, unix))]
impl From<OwnedFd> for MockFile {
#[inline]
fn from(file: OwnedFd) -> MockFile {
use std::os::fd::IntoRawFd;
unsafe { MockFile::from_raw_fd(IntoRawFd::into_raw_fd(file)) }
}
}
tokio_thread_local! {
static QUEUE: RefCell<VecDeque<Box<dyn FnOnce() + Send>>> = RefCell::new(VecDeque::new())
}
+8 -5
View File
@@ -518,6 +518,10 @@ impl OpenOptions {
/// [`Other`]: std::io::ErrorKind::Other
/// [`PermissionDenied`]: std::io::ErrorKind::PermissionDenied
pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
self.open_inner(path.as_ref()).await
}
async fn open_inner(&self, path: &Path) -> io::Result<File> {
match &self.inner {
Kind::Std(opts) => Self::std_open(opts, path).await,
#[cfg(all(
@@ -535,7 +539,7 @@ impl OpenOptions {
.check_and_init(io_uring::opcode::OpenAt::CODE)
.await?
{
Op::open(path.as_ref(), opts)?.await
Op::open(path, opts)?.await
} else {
let opts = opts.clone().into();
Self::std_open(&opts, path).await
@@ -544,12 +548,11 @@ impl OpenOptions {
}
}
async fn std_open(opts: &StdOpenOptions, path: impl AsRef<Path>) -> io::Result<File> {
let path = path.as_ref().to_owned();
async fn std_open(opts: &StdOpenOptions, path: &Path) -> io::Result<File> {
let path = path.to_owned();
let opts = opts.clone();
let std = asyncify(move || opts.open(path)).await?;
Ok(File::from_std(std))
Ok(asyncify(move || opts.open(path)).await?.into())
}
#[cfg(windows)]
+17 -3
View File
@@ -54,14 +54,23 @@ use std::{io, path::Path};
/// }
/// ```
pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
let path = path.as_ref().to_owned();
let path = path.as_ref();
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux"
// libc::statx is only supported on these platforms
// FIXME: Add musl target env when our minimum supported
// rust version is 1.93. To clarify, statx support is
// introduced to musl in 1.25 as mentioned officially here:
// https://musl.libc.org/releases.html.
// However, rustup target_env building for *-linux-musl
// uses 1.25 musl on all *-linux-musl platforms starting
// in 1.93 stable rust version.
// https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
any(target_env = "gnu", target_os = "android")
))]
{
use crate::fs::read_uring;
@@ -72,9 +81,14 @@ pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
.check_and_init(io_uring::opcode::Read::CODE)
.await?
{
return read_uring(&path).await;
return read_uring(path).await;
}
}
read_spawn_blocking(path).await
}
async fn read_spawn_blocking(path: &Path) -> io::Result<Vec<u8>> {
let path = path.to_owned();
asyncify(move || std::fs::read(path)).await
}
+1 -1
View File
@@ -74,7 +74,7 @@ impl ReadDir {
///
/// # Cancel safety
///
/// This method is cancellation safe.
/// This method is cancel safe.
pub async fn next_entry(&mut self) -> io::Result<Option<DirEntry>> {
use std::future::poll_fn;
poll_fn(|cx| self.poll_next_entry(cx)).await
+18 -1
View File
@@ -19,9 +19,26 @@ const MAX_READ_SIZE: usize = 64 * 1024 * 1024;
pub(crate) async fn read_uring(path: &Path) -> io::Result<Vec<u8>> {
let file = OpenOptions::new().read(true).open(path).await?;
// TODO: use io uring in the future to obtain metadata
#[cfg(not(any(target_env = "gnu", target_os = "android")))]
let size_hint: Option<usize> = file.metadata().await.map(|m| m.len() as usize).ok();
#[cfg(
// libc::statx is only supported on these platforms
// FIXME: Add musl target env when our minimum supported
// rust version is 1.93. To clarify, statx support is
// introduced to musl in 1.25 as mentioned officially here:
// https://musl.libc.org/releases.html.
// However, rustup target_env building for *-linux-musl
// uses 1.25 musl on all *-linux-musl platforms starting
// in 1.93 stable rust version.
// https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
any(target_env = "gnu", target_os = "android")
)]
let size_hint = Op::file_metadata(&file)?
.await
.map(|m| m.len() as usize)
.ok();
let fd: OwnedFd = file
.try_into_std()
.expect("unexpected in-flight operation detected")
+31 -2
View File
@@ -10,8 +10,37 @@ use std::path::Path;
///
/// This is an async version of [`std::fs::rename`].
pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
let from = from.as_ref().to_owned();
let to = to.as_ref().to_owned();
let from = from.as_ref();
let to = to.as_ref();
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
target_os = "linux",
))]
{
use crate::io::uring::rename::Rename;
use crate::runtime::driver::op::Op;
let handle = crate::runtime::Handle::current();
let driver_handle = handle.inner.driver().io();
type RenameOp = Op<Rename>;
if driver_handle
.check_and_init(io_uring::opcode::RenameAt::CODE)
.await?
{
return RenameOp::rename(from, to)?.await;
}
}
rename_blocking(from, to).await
}
async fn rename_blocking(from: &Path, to: &Path) -> io::Result<()> {
let [from, to] = [from, to].map(Path::to_owned);
asyncify(move || std::fs::rename(from, to)).await
}
+62 -1
View File
@@ -23,6 +23,67 @@ use std::path::Path;
/// # }
/// ```
pub async fn try_exists(path: impl AsRef<Path>) -> io::Result<bool> {
let path = path.as_ref().to_owned();
let path = path.as_ref();
#[cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
// libc::statx is only supported on these platforms
// FIXME: Add musl target env when our minimum supported
// rust version is 1.93. To clarify, statx support is
// introduced to musl in 1.25 as mentioned officially here:
// https://musl.libc.org/releases.html.
// However, rustup target_env building for *-linux-musl
// uses 1.25 musl on all *-linux-musl platforms starting
// in 1.93 stable rust version.
// https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
any(target_env = "gnu", target_os = "android")
))]
{
let handle = crate::runtime::Handle::current();
let driver_handle = handle.inner.driver().io();
if driver_handle
.check_and_init(io_uring::opcode::Statx::CODE)
.await?
{
return try_exists_uring(path).await;
}
}
try_exists_spawn_blocking(path).await
}
cfg_io_uring! {
#[inline]
#[cfg(
// libc::statx is only supported on these platforms
// FIXME: Add musl target env when our minimum supported
// rust version is 1.93. To clarify, statx support is
// introduced to musl in 1.25 as mentioned officially here:
// https://musl.libc.org/releases.html.
// However, rustup target_env building for *-linux-musl
// uses 1.25 musl on all *-linux-musl platforms starting
// in 1.93 stable rust version.
// https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
any(target_env = "gnu", target_os = "android")
)]
async fn try_exists_uring(path: &Path) -> io::Result<bool> {
use crate::runtime::driver::op::Op;
match Op::metadata(path)?.await {
Ok(_) => Ok(true),
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
Err(error) => Err(error),
}
}
}
async fn try_exists_spawn_blocking(path: &Path) -> io::Result<bool> {
let path = path.to_owned();
// FIXME: When MSRV is 1.81, change this to
// std::fs::exists() to be consistent with
// all other tokio::fs operations
asyncify(move || path.try_exists()).await
}
+4
View File
@@ -97,20 +97,24 @@ where
}
impl AsyncBufRead for &[u8] {
#[inline]
fn poll_fill_buf(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
Poll::Ready(Ok(*self))
}
#[inline]
fn consume(mut self: Pin<&mut Self>, amt: usize) {
*self = &self[amt..];
}
}
impl<T: AsRef<[u8]> + Unpin> AsyncBufRead for io::Cursor<T> {
#[inline]
fn poll_fill_buf(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
Poll::Ready(io::BufRead::fill_buf(self.get_mut()))
}
#[inline]
fn consume(self: Pin<&mut Self>, amt: usize) {
io::BufRead::consume(self.get_mut(), amt);
}
+2
View File
@@ -94,6 +94,7 @@ where
}
impl AsyncRead for &[u8] {
#[inline]
fn poll_read(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
@@ -108,6 +109,7 @@ impl AsyncRead for &[u8] {
}
impl<T: AsRef<[u8]> + Unpin> AsyncRead for io::Cursor<T> {
#[inline]
fn poll_read(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
+2
View File
@@ -87,9 +87,11 @@ where
}
impl<T: AsRef<[u8]> + Unpin> AsyncSeek for io::Cursor<T> {
#[inline]
fn start_seek(mut self: Pin<&mut Self>, pos: SeekFrom) -> io::Result<()> {
io::Seek::seek(&mut *self, pos).map(drop)
}
#[inline]
fn poll_complete(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<u64>> {
Poll::Ready(Ok(self.get_mut().position()))
}
+25
View File
@@ -251,6 +251,7 @@ where
}
impl AsyncWrite for Vec<u8> {
#[inline]
fn poll_write(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
@@ -260,6 +261,7 @@ impl AsyncWrite for Vec<u8> {
Poll::Ready(Ok(buf.len()))
}
#[inline]
fn poll_write_vectored(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
@@ -268,20 +270,24 @@ impl AsyncWrite for Vec<u8> {
Poll::Ready(io::Write::write_vectored(&mut *self, bufs))
}
#[inline]
fn is_write_vectored(&self) -> bool {
true
}
#[inline]
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
#[inline]
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
impl AsyncWrite for io::Cursor<&mut [u8]> {
#[inline]
fn poll_write(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
@@ -290,6 +296,7 @@ impl AsyncWrite for io::Cursor<&mut [u8]> {
Poll::Ready(io::Write::write(&mut *self, buf))
}
#[inline]
fn poll_write_vectored(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
@@ -298,20 +305,24 @@ impl AsyncWrite for io::Cursor<&mut [u8]> {
Poll::Ready(io::Write::write_vectored(&mut *self, bufs))
}
#[inline]
fn is_write_vectored(&self) -> bool {
true
}
#[inline]
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(io::Write::flush(&mut *self))
}
#[inline]
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}
impl AsyncWrite for io::Cursor<&mut Vec<u8>> {
#[inline]
fn poll_write(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
@@ -320,6 +331,7 @@ impl AsyncWrite for io::Cursor<&mut Vec<u8>> {
Poll::Ready(io::Write::write(&mut *self, buf))
}
#[inline]
fn poll_write_vectored(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
@@ -328,20 +340,24 @@ impl AsyncWrite for io::Cursor<&mut Vec<u8>> {
Poll::Ready(io::Write::write_vectored(&mut *self, bufs))
}
#[inline]
fn is_write_vectored(&self) -> bool {
true
}
#[inline]
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(io::Write::flush(&mut *self))
}
#[inline]
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}
impl AsyncWrite for io::Cursor<Vec<u8>> {
#[inline]
fn poll_write(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
@@ -350,6 +366,7 @@ impl AsyncWrite for io::Cursor<Vec<u8>> {
Poll::Ready(io::Write::write(&mut *self, buf))
}
#[inline]
fn poll_write_vectored(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
@@ -358,20 +375,24 @@ impl AsyncWrite for io::Cursor<Vec<u8>> {
Poll::Ready(io::Write::write_vectored(&mut *self, bufs))
}
#[inline]
fn is_write_vectored(&self) -> bool {
true
}
#[inline]
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(io::Write::flush(&mut *self))
}
#[inline]
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}
impl AsyncWrite for io::Cursor<Box<[u8]>> {
#[inline]
fn poll_write(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
@@ -380,6 +401,7 @@ impl AsyncWrite for io::Cursor<Box<[u8]>> {
Poll::Ready(io::Write::write(&mut *self, buf))
}
#[inline]
fn poll_write_vectored(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
@@ -388,14 +410,17 @@ impl AsyncWrite for io::Cursor<Box<[u8]>> {
Poll::Ready(io::Write::write_vectored(&mut *self, bufs))
}
#[inline]
fn is_write_vectored(&self) -> bool {
true
}
#[inline]
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(io::Write::flush(&mut *self))
}
#[inline]
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
+2 -2
View File
@@ -214,7 +214,7 @@ impl Buf {
self.pos += n;
if self.pos == self.buf.len() {
self.buf.truncate(0);
self.buf.clear();
self.pos = 0;
}
@@ -315,7 +315,7 @@ cfg_fs! {
pub(crate) fn discard_read(&mut self) -> i64 {
let ret = -(self.bytes().len() as i64);
self.pos = 0;
self.buf.truncate(0);
self.buf.clear();
ret
}
+4 -4
View File
@@ -15,8 +15,8 @@ use std::task::{ready, Context, Poll};
/// Like [`mio::event::Source`], but for POSIX AIO only.
///
/// Tokio's consumer must pass an implementor of this trait to create a
/// [`Aio`] object. Implementors must implement at least one of [`AioSource::register`] and
/// Tokio's consumer must pass an implementer of this trait to create a
/// [`Aio`] object. Implementers must implement at least one of [`AioSource::register`] and
/// [`AioSource::register_borrowed`].
pub trait AioSource {
/// Registers this AIO event source with Tokio's reactor.
@@ -27,7 +27,7 @@ pub trait AioSource {
/// source may end up notifying the wrong file.
#[deprecated(since = "1.52.0", note = "use register_borrowed instead")]
fn register(&mut self, _kq: RawFd, _token: usize) {
// This default implementation exists so new AioSource implementors that implement the
// This default implementation exists so new AioSource implementers that implement the
// register_borrowed method can compile without the need to implement register.
unimplemented!("Use AioSource::register_borrowed instead")
}
@@ -35,7 +35,7 @@ pub trait AioSource {
/// Registers this AIO event source with Tokio's reactor.
fn register_borrowed(&mut self, kq: BorrowedFd<'_>, token: usize) {
// This default implementation serves to provide backwards compatibility with AioSource
// implementors written before 1.52.0 that only implemented the unsafe `register` method.
// implementers written before 1.52.0 that only implemented the unsafe `register` method.
#[allow(deprecated)]
self.register(kq.as_raw_fd(), token)
}
+39
View File
@@ -15,6 +15,45 @@ cfg_io_std! {
/// to occur as a single write, so multiple threads writing data with
/// [`write_all`] may result in interleaved output.
///
/// # Warning
///
/// Each call to [`stdout()`] creates a **new** handle with its own
/// internal state. Writes through different handles are not
/// coordinated, so creating a new handle in a loop can cause output
/// to appear out of order:
///
/// ```no_run
/// # use tokio::io::{self, AsyncWriteExt};
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// // WRONG: creates a new handle each iteration
/// for i in 0..10 {
/// let mut out = io::stdout();
/// out.write_all(b"data").await?;
/// out.write_all(b"\n").await?;
/// // out is dropped here; its last write may still be
/// // running when the next iteration starts
/// }
/// # Ok(())
/// # }
/// ```
///
/// To preserve order, create one handle outside the loop and
/// reuse it:
///
/// ```no_run
/// # use tokio::io::{self, AsyncWriteExt};
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// let mut out = io::stdout();
/// for i in 0..10 {
/// out.write_all(b"data").await?;
/// out.write_all(b"\n").await?;
/// }
/// # Ok(())
/// # }
/// ```
///
/// Created by the [`stdout`] function.
///
/// [`stdout`]: stdout()
+2
View File
@@ -1,4 +1,6 @@
pub(crate) mod open;
pub(crate) mod read;
pub(crate) mod rename;
pub(crate) mod statx;
pub(crate) mod utils;
pub(crate) mod write;
+60
View File
@@ -0,0 +1,60 @@
use super::utils::cstr;
use crate::runtime::driver::op::{CancelData, Cancellable, Completable, CqeResult, Op};
use io_uring::{opcode, types};
use std::ffi::CString;
use std::io;
use std::path::Path;
#[derive(Debug)]
pub(crate) struct Rename {
/// This field will be read by the kernel during the operation, so we
/// need to ensure it is valid for the entire duration of the operation.
_from: CString,
_to: CString,
}
impl Completable for Rename {
type Output = io::Result<()>;
fn complete(self, cqe: CqeResult) -> Self::Output {
cqe.result.map(drop)
}
fn complete_with_error(self, error: io::Error) -> Self::Output {
Err(error)
}
}
impl Cancellable for Rename {
fn cancel(self) -> CancelData {
CancelData::Rename(self)
}
}
impl Op<Rename> {
pub(crate) fn rename(from: &Path, to: &Path) -> io::Result<Op<Rename>> {
let from = cstr(from)?;
let to = cstr(to)?;
let rename_op = opcode::RenameAt::new(
types::Fd(libc::AT_FDCWD),
from.as_ptr(),
types::Fd(libc::AT_FDCWD),
to.as_ptr(),
)
.build();
// SAFETY: Parameters are valid for the entire duration of the operation
Ok(unsafe {
Op::new(
rename_op,
Rename {
_from: from,
_to: to,
},
)
})
}
}
+161
View File
@@ -0,0 +1,161 @@
#![cfg(all(
tokio_unstable,
feature = "io-uring",
feature = "rt",
feature = "fs",
// libc::statx is only supported on these platforms
// FIXME: Add musl target env when our minimum supported
// rust version is 1.93. To clarify, statx support is
// introduced to musl in 1.25 as mentioned officially here:
// https://musl.libc.org/releases.html.
// However, rustup target_env building for *-linux-musl
// uses 1.25 musl on all *-linux-musl platforms starting
// in 1.93 stable rust version.
// https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
any(target_env = "gnu", target_os = "android")
))]
use crate::fs::File;
use crate::io::uring::utils::cstr;
use crate::runtime::driver::op::{CancelData, Cancellable, Completable, CqeResult, Op};
use io_uring::{opcode, types};
use libc::statx;
use std::ffi::{CStr, CString};
use std::fmt::{Debug, Formatter};
use std::io;
use std::mem::MaybeUninit;
use std::os::fd::AsRawFd;
use std::path::Path;
pub(crate) struct Metadata(statx);
impl Metadata {
/// Returns the size of the file, in bytes, this metadata is for.
pub(crate) fn len(&self) -> u64 {
self.0.stx_size
}
}
impl Debug for Metadata {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut debug = f.debug_struct("Metadata");
debug.field("len", &self.len());
debug.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub(crate) struct Statx {
/// This field will be read by the kernel during the operation, so we
/// need to ensure it is valid for the entire duration of the operation.
_path: CString,
buffer: Box<MaybeUninit<statx>>,
}
impl Completable for Statx {
type Output = io::Result<Metadata>;
fn complete(self, cqe: CqeResult) -> Self::Output {
// SAFETY: On success, we always receive 0, which should guarantee
// that the information about a file is stored inside the
// statx buffer. On failure, we'll receive an Error value.
// Refer to man page description and return value:
// https://man7.org/linux/man-pages/man2/statx.2.html
cqe.result
.map(|_| Metadata(unsafe { *self.buffer.as_ptr() }))
}
fn complete_with_error(self, error: io::Error) -> Self::Output {
Err(error)
}
}
impl Cancellable for Statx {
fn cancel(self) -> CancelData {
CancelData::Statx(self)
}
}
impl Op<Statx> {
/// Submit a request to retrieve a file's status.
#[inline]
fn statx(path: &Path, flags: i32) -> io::Result<Op<Statx>> {
let path = cstr(path)?;
let mut buffer = Box::new(MaybeUninit::<statx>::uninit());
let statx_op = opcode::Statx::new(
types::Fd(libc::AT_FDCWD),
path.as_ptr(),
buffer.as_mut_ptr().cast(),
)
.flags(flags)
.mask(libc::STATX_BASIC_STATS | libc::STATX_BTIME)
.build();
// SAFETY: Parameters are valid for the entire duration of the operation
Ok(unsafe {
Op::new(
statx_op,
Statx {
_path: path,
buffer,
},
)
})
}
/// Retrieves the metadata information of the given path, following symlinks
/// if the path provided points to a symlink location.
#[inline]
pub(crate) fn metadata(path: &Path) -> io::Result<Op<Statx>> {
Op::statx(path, libc::AT_STATX_SYNC_AS_STAT)
}
/// Retrieves the metadata information of the given file
pub(crate) fn file_metadata(file: &File) -> io::Result<Op<Statx>> {
let mut buffer = Box::new(MaybeUninit::<statx>::uninit());
let empty_path: &'static CStr = c"";
// io-uring was introduced in linux 5.1
// pass in an empty path instead of null to target the file descriptor
// status as specified by man:
// https://man7.org/linux/man-pages/man2/statx.2.html
let statx_op = opcode::Statx::new(
types::Fd(file.as_raw_fd()),
// it should be fine to pass in `empty_path` whose lifetime
// does not exceed the `file_metadata()` function as a ptr here
// because we want to stat the dirfd not this pathname
empty_path.as_ptr(),
buffer.as_mut_ptr().cast(),
)
.flags(libc::AT_STATX_SYNC_AS_STAT | libc::AT_EMPTY_PATH)
.mask(libc::STATX_BASIC_STATS | libc::STATX_BTIME)
.build();
// SAFETY: Parameters are valid for the entire duration of the operation
Ok(unsafe {
Op::new(
statx_op,
Statx {
_path: empty_path.into(),
buffer,
},
)
})
}
// TODO: Once `Metadata::from_statx` is stabilized, we can use use this function
// to enable io-uring support on `tokio::fs::symlink_metadata`.
// See this PR for more detail: https://github.com/tokio-rs/tokio/pull/8080
// See `Metadata::from_statx` tracking issue to see progress:
// https://github.com/rust-lang/rust/issues/156268
/// Retrieves the metadata information of the given path without following symlinks.
#[inline]
#[allow(dead_code)]
pub(crate) fn symlink_metadata(path: &Path) -> io::Result<Op<Statx>> {
Op::statx(
path,
libc::AT_STATX_SYNC_AS_STAT | libc::AT_SYMLINK_NOFOLLOW,
)
}
}
+7 -7
View File
@@ -39,8 +39,8 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// If the method is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// If used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then some data may have been partially read. Any
/// partially read bytes are appended to `buf`, and the method can be
/// called again to continue reading until `byte`.
@@ -129,9 +129,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may have been partially
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may have been partially
/// read, and this data is lost. There are no guarantees regarding the
/// contents of `buf` when the call is cancelled. The current
/// implementation replaces `buf` with the empty string, but this may
@@ -269,8 +269,8 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is cancel safe. If you use it as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then it is guaranteed that no data was read.
///
/// [`consume`]: crate::io::AsyncBufReadExt::consume
+71 -71
View File
@@ -147,8 +147,8 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is cancel safe. If you use it as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If you use it as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then it is guaranteed that no data was read.
///
/// # Examples
@@ -215,8 +215,8 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is cancel safe. If you use it as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If you use it as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then it is guaranteed that no data was read.
///
/// # Examples
@@ -291,9 +291,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may already have been
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may already have been
/// read into `buf`.
///
/// # Examples
@@ -347,8 +347,8 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is cancel safe. If this method is used as an event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If this method is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, it is guaranteed that no data were read.
///
/// # Examples
@@ -391,8 +391,8 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is cancel safe. If this method is used as an event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If this method is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, it is guaranteed that no data were read.
///
/// # Examples
@@ -436,9 +436,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -480,9 +480,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -524,9 +524,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -568,9 +568,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -611,9 +611,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -656,9 +656,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -699,9 +699,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -745,9 +745,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -791,9 +791,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -834,9 +834,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -879,9 +879,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -923,9 +923,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -967,9 +967,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -1011,9 +1011,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -1054,9 +1054,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -1099,9 +1099,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -1142,9 +1142,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -1188,9 +1188,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -1234,9 +1234,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
@@ -1277,9 +1277,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may be lost.
/// This method is not cancel safe. If the method is used as a
/// branch in [`tokio::select!`](crate::select) and another
/// branch completes first, then some data may be lost.
///
/// # Examples
///
+17 -17
View File
@@ -103,9 +103,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is cancellation safe in the sense that if it is used as
/// the event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then it is guaranteed that no data was
/// This method is cancel safe. If it is used as a branch in
/// [`tokio::select!`](crate::select) and another branch completes
/// first, then it is guaranteed that no data was
/// written to this `AsyncWrite`.
///
/// # Examples
@@ -146,9 +146,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is cancellation safe in the sense that if it is used as
/// the event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then it is guaranteed that no data was
/// This method is cancel safe. If it is used as a branch in
/// [`tokio::select!`](crate::select) and another branch completes
/// first, then it is guaranteed that no data was
/// written to this `AsyncWrite`.
///
/// # Examples
@@ -223,9 +223,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is cancellation safe in the sense that if it is used as
/// the event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then it is guaranteed that no data was
/// This method is cancel safe. If it is used as a branch in
/// [`tokio::select!`](crate::select) and another branch completes
/// first, then it is guaranteed that no data was
/// written to this `AsyncWrite`.
///
/// # Examples
@@ -294,8 +294,8 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// If `write_all_buf` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// If `write_all_buf` is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then the data in the provided buffer may have been
/// partially written. However, it is guaranteed that the provided
/// buffer has been [advanced] by the amount of bytes that have been
@@ -354,9 +354,9 @@ cfg_io_util! {
///
/// # Cancel safety
///
/// This method is not cancellation safe. If it is used as the event
/// in a [`tokio::select!`](crate::select) statement and some other
/// branch completes first, then the provided buffer may have been
/// This method is not cancel safe. If it is used as a branch in
/// [`tokio::select!`](crate::select) and another branch completes
/// first, then the provided buffer may have been
/// partially written, but future calls to `write_all` will start over
/// from the beginning of the buffer.
///
@@ -1253,9 +1253,9 @@ cfg_io_util! {
///
/// This method is cancel safe.
///
/// If `flush` is used as the event in a [`tokio::select!`](crate::select)
/// statement and some other branch completes first, then the data in the
/// buffered data in this `AsyncWrite` may have been partially flushed.
/// If `flush` is used as a branch in [`tokio::select!`](crate::select)
/// and another branch completes first, then the buffered data in this
/// `AsyncWrite` may have been partially flushed.
/// However, it is guaranteed that the buffer is advanced by the amount of
/// bytes that have been partially flushed.
///
+3 -1
View File
@@ -94,7 +94,9 @@ where
if !*me.done_first {
let rem = buf.remaining();
ready!(me.first.poll_read(cx, buf))?;
if buf.remaining() == rem {
// A read that fills nothing only indicates EOF if the buffer
// had capacity to begin with.
if buf.remaining() == rem && rem != 0 {
*me.done_first = true;
} else {
return Poll::Ready(Ok(()));
+1 -1
View File
@@ -82,7 +82,7 @@ impl CopyBuffer {
R: AsyncRead + ?Sized,
W: AsyncWrite + ?Sized,
{
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
#[cfg(any(
feature = "fs",
feature = "io-std",
+7 -7
View File
@@ -71,7 +71,7 @@ impl AsyncRead for Empty {
cx: &mut Context<'_>,
_: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(()))
}
@@ -80,7 +80,7 @@ impl AsyncRead for Empty {
impl AsyncBufRead for Empty {
#[inline]
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(&[]))
}
@@ -96,21 +96,21 @@ impl AsyncWrite for Empty {
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(buf.len()))
}
#[inline]
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(()))
}
#[inline]
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(()))
}
@@ -126,7 +126,7 @@ impl AsyncWrite for Empty {
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<Result<usize, io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
let num_bytes = bufs.iter().map(|b| b.len()).sum();
Poll::Ready(Ok(num_bytes))
@@ -141,7 +141,7 @@ impl AsyncSeek for Empty {
#[inline]
fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(0))
}
+1 -1
View File
@@ -49,7 +49,7 @@ where
///
/// # Cancel safety
///
/// This method is cancellation safe.
/// This method is cancel safe.
///
/// # Examples
///
+8 -8
View File
@@ -185,7 +185,7 @@ impl Drop for DuplexStream {
/// Creates unidirectional buffer that acts like in memory pipe.
///
/// The `max_buf_size` argument is the maximum amount of bytes that can be
/// written to a buffer before the it returns `Poll::Pending`.
/// written to a buffer before it returns `Poll::Pending`.
///
/// # Unify reader and writer
///
@@ -217,7 +217,7 @@ impl SimplexStream {
/// version with separate reader and writer you can use [`simplex`] function.
///
/// The `max_buf_size` argument is the maximum amount of bytes that can be
/// written to a buffer before the it returns `Poll::Pending`.
/// written to a buffer before it returns `Poll::Pending`.
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
pub fn new_unsplit(max_buf_size: usize) -> SimplexStream {
SimplexStream {
@@ -331,7 +331,7 @@ impl AsyncRead for SimplexStream {
cx: &mut task::Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
let coop = ready!(crate::task::coop::poll_proceed(cx));
let ret = self.poll_read_internal(cx, buf);
@@ -348,7 +348,7 @@ impl AsyncRead for SimplexStream {
cx: &mut task::Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
self.poll_read_internal(cx, buf)
}
}
@@ -361,7 +361,7 @@ impl AsyncWrite for SimplexStream {
cx: &mut task::Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
let coop = ready!(crate::task::coop::poll_proceed(cx));
let ret = self.poll_write_internal(cx, buf);
@@ -378,7 +378,7 @@ impl AsyncWrite for SimplexStream {
cx: &mut task::Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
self.poll_write_internal(cx, buf)
}
}
@@ -389,7 +389,7 @@ impl AsyncWrite for SimplexStream {
cx: &mut task::Context<'_>,
bufs: &[std::io::IoSlice<'_>],
) -> Poll<Result<usize, std::io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
let coop = ready!(crate::task::coop::poll_proceed(cx));
let ret = self.poll_write_vectored_internal(cx, bufs);
@@ -406,7 +406,7 @@ impl AsyncWrite for SimplexStream {
cx: &mut task::Context<'_>,
bufs: &[std::io::IoSlice<'_>],
) -> Poll<Result<usize, std::io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
self.poll_write_vectored_internal(cx, bufs)
}
}
+1 -1
View File
@@ -84,7 +84,7 @@ cfg_io_util! {
// used by `BufReader` and `BufWriter`
// https://github.com/rust-lang/rust/blob/master/library/std/src/sys_common/io.rs#L1
// https://github.com/rust-lang/rust/blob/main/library/std/src/sys/io/mod.rs#L72
const DEFAULT_BUF_SIZE: usize = 8 * 1024;
cfg_coop! {
+1 -1
View File
@@ -56,7 +56,7 @@ impl AsyncRead for Repeat {
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
buf.put_bytes(self.byte, buf.remaining());
Poll::Ready(Ok(()))
+3 -3
View File
@@ -57,21 +57,21 @@ impl AsyncWrite for Sink {
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(buf.len()))
}
#[inline]
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(()))
}
#[inline]
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
ready!(crate::trace::trace_leaf(cx));
ready!(crate::trace::trace_leaf());
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(()))
}
+1 -1
View File
@@ -5,7 +5,7 @@ use std::mem::MaybeUninit;
///
/// # Safety
///
/// The implementor must guarantee that the vector returned by the
/// The implementer must guarantee that the vector returned by the
/// `as_mut` and `as_mut` methods do not change from one call to
/// another.
pub(crate) unsafe trait VecU8: AsRef<Vec<u8>> + AsMut<Vec<u8>> {}
+19 -4
View File
@@ -354,6 +354,7 @@
//! Some feature flags are only available when specifying the `tokio_unstable` flag:
//!
//! - `tracing`: Enables tracing events.
//! - `schedule-latency`: Allows measurement of task scheduling latencies.
//! - `io-uring`: Enables `io-uring` (Linux only).
//! - `taskdump`: Enables `taskdump` (Linux only).
//!
@@ -488,14 +489,28 @@ compile_error!("The `taskdump` feature requires `--cfg tokio_unstable`.");
not(doc),
not(all(
target_os = "linux",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
any(
target_arch = "aarch64",
target_arch = "x86",
target_arch = "x86_64",
target_arch = "s390x"
)
))
))]
compile_error!(
"The `taskdump` feature is only currently supported on \
linux, on `aarch64`, `x86` and `x86_64`."
linux, on `aarch64`, `x86`, `x86_64` and `s390x`."
);
#[cfg(all(not(tokio_unstable), feature = "schedule-latency"))]
compile_error!("The `schedule-latency` feature requires `--cfg tokio_unstable`.");
#[cfg(all(
feature = "schedule-latency",
not(all(target_pointer_width = "64", target_has_atomic = "64"))
))]
compile_error!("The `schedule-latency` feature is only currently supported on 64-bit targets.");
// Includes re-exports used by macros.
//
// This module is not intended to be part of the public API. In general, any
@@ -574,14 +589,14 @@ mod trace {
cfg_not_taskdump! {
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn trace_leaf(_: &mut std::task::Context<'_>) -> std::task::Poll<()> {
pub(crate) fn trace_leaf() -> std::task::Poll<()> {
std::task::Poll::Ready(())
}
}
#[cfg_attr(not(feature = "sync"), allow(dead_code))]
pub(crate) async fn async_trace_leaf() {
std::future::poll_fn(trace_leaf).await
std::future::poll_fn(|_cx| trace_leaf()).await
}
}
+25 -3
View File
@@ -517,7 +517,8 @@ macro_rules! cfg_taskdump {
any(
target_arch = "aarch64",
target_arch = "x86",
target_arch = "x86_64"
target_arch = "x86_64",
target_arch = "s390x"
)
))]
#[cfg_attr(
@@ -530,7 +531,8 @@ macro_rules! cfg_taskdump {
any(
target_arch = "aarch64",
target_arch = "x86",
target_arch = "x86_64"
target_arch = "x86_64",
target_arch = "s390x"
)
)))
)]
@@ -550,7 +552,8 @@ macro_rules! cfg_not_taskdump {
any(
target_arch = "aarch64",
target_arch = "x86",
target_arch = "x86_64"
target_arch = "x86_64",
target_arch = "s390x"
)
)))]
$item
@@ -746,3 +749,22 @@ macro_rules! cfg_io_uring {
)*
};
}
macro_rules! cfg_schedule_latency {
($($item:item)*) => {
$(
#[cfg(feature = "schedule-latency")]
#[cfg_attr(docsrs, doc(cfg(feature = "schedule-latency")))]
$item
)*
};
}
macro_rules! cfg_not_schedule_latency {
($($item:item)*) => {
$(
#[cfg(not(feature = "schedule-latency"))]
$item
)*
}
}
+5
View File
@@ -95,6 +95,11 @@ macro_rules! doc {
/// describes whether they are cancel safe. The lists in this section are not
/// exhaustive.
///
/// Cancellation safety describes what happens when a future is dropped
/// before it completes. Whether something is cancellation safe depends on
/// the behavior of the future passed to `select!`, which may come from an
/// async method, an async expression, or another future-producing operation.
///
/// The following methods are cancellation safe:
///
/// * [`tokio::sync::mpsc::Receiver::recv`](crate::sync::mpsc::Receiver::recv)
+3 -4
View File
@@ -69,7 +69,7 @@ impl TcpListener {
/// to this listener. The port allocated can be queried via the `local_addr`
/// method.
///
/// The address type can be any implementor of the [`ToSocketAddrs`] trait.
/// The address type can be any implementer of the [`ToSocketAddrs`] trait.
/// If `addr` yields multiple addresses, bind will be attempted with each of
/// the addresses until one succeeds and returns the listener. If none of
/// the addresses succeed in creating a listener, the error returned from
@@ -91,7 +91,6 @@ impl TcpListener {
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// let listener = TcpListener::bind("127.0.0.1:2345").await?;
///
/// // use the listener
@@ -134,8 +133,8 @@ impl TcpListener {
///
/// # Cancel safety
///
/// This method is cancel safe. If the method is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If the method is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then it is guaranteed that no new connections were
/// accepted by this method.
///
+4 -1
View File
@@ -286,6 +286,7 @@ impl TcpSocket {
not(target_os = "solaris"),
not(target_os = "illumos"),
not(target_os = "cygwin"),
not(target_os = "nuttx"),
))]
#[cfg_attr(
docsrs,
@@ -294,6 +295,7 @@ impl TcpSocket {
not(target_os = "solaris"),
not(target_os = "illumos"),
not(target_os = "cygwin"),
not(target_os = "nuttx"),
)))
)]
pub fn set_reuseport(&self, reuseport: bool) -> io::Result<()> {
@@ -331,6 +333,7 @@ impl TcpSocket {
not(target_os = "solaris"),
not(target_os = "illumos"),
not(target_os = "cygwin"),
not(target_os = "nuttx"),
))]
#[cfg_attr(
docsrs,
@@ -339,6 +342,7 @@ impl TcpSocket {
not(target_os = "solaris"),
not(target_os = "illumos"),
not(target_os = "cygwin"),
not(target_os = "nuttx"),
)))
)]
pub fn reuseport(&self) -> io::Result<bool> {
@@ -945,7 +949,6 @@ impl TcpSocket {
///
/// #[tokio::main]
/// async fn main() -> std::io::Result<()> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// let socket2_socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
/// socket2_socket.set_nonblocking(true)?;
///
+2 -3
View File
@@ -231,7 +231,6 @@ impl TcpStream {
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// let mut data = [0u8; 12];
/// # if false {
/// let listener = TcpListener::bind("127.0.0.1:34254").await?;
@@ -1075,8 +1074,8 @@ impl TcpStream {
///
/// # Cancel safety
///
/// This method is cancel safe. If the method is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If the method is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then it is guaranteed that no peek was performed, and
/// that `buf` has not been modified.
///
+12 -12
View File
@@ -140,7 +140,7 @@ impl UdpSocket {
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No UDP sockets in miri.
/// let sock = UdpSocket::bind("0.0.0.0:8080").await?;
/// // use `sock`
/// # let _ = sock;
@@ -308,7 +308,7 @@ impl UdpSocket {
/// # use std::{io, net::SocketAddr};
/// # #[tokio::main]
/// # async fn main() -> io::Result<()> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No UDP sockets in miri.
/// let addr = "0.0.0.0:8080".parse::<SocketAddr>().unwrap();
/// let peer = "127.0.0.1:11100".parse::<SocketAddr>().unwrap();
/// let sock = UdpSocket::bind(addr).await?;
@@ -545,8 +545,8 @@ impl UdpSocket {
///
/// # Cancel safety
///
/// This method is cancel safe. If `send` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If `send` is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then it is guaranteed that the message was not sent.
///
/// # Examples
@@ -762,8 +762,8 @@ impl UdpSocket {
///
/// # Cancel safety
///
/// This method is cancel safe. If `recv` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If `recv` is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, it is guaranteed that no messages were received on this
/// socket.
///
@@ -1149,7 +1149,7 @@ impl UdpSocket {
/// Sends data on the socket to the given address. On success, returns the
/// number of bytes written.
///
/// Address type can be any implementor of [`ToSocketAddrs`] trait. See its
/// Address type can be any implementer of [`ToSocketAddrs`] trait. See its
/// documentation for concrete examples.
///
/// It is possible for `addr` to yield multiple addresses, but `send_to`
@@ -1162,8 +1162,8 @@ impl UdpSocket {
///
/// # Cancel safety
///
/// This method is cancel safe. If `send_to` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If `send_to` is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then it is guaranteed that the message was not sent.
///
/// # Example
@@ -1291,8 +1291,8 @@ impl UdpSocket {
///
/// # Cancel safety
///
/// This method is cancel safe. If `recv_from` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If `recv_from` is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, it is guaranteed that no messages were received on this
/// socket.
///
@@ -2288,7 +2288,7 @@ impl UdpSocket {
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No UDP sockets in miri.
/// // Create a socket
/// let socket = UdpSocket::bind("0.0.0.0:8080").await?;
///
+20 -20
View File
@@ -39,7 +39,7 @@ cfg_net_unix! {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
/// use tempfile::tempdir;
///
@@ -377,7 +377,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
/// use tempfile::tempdir;
///
@@ -469,7 +469,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
/// use std::os::unix::net::UnixDatagram as StdUDS;
/// use tempfile::tempdir;
@@ -536,7 +536,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
/// use tempfile::tempdir;
///
@@ -576,7 +576,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
/// use tempfile::tempdir;
///
@@ -612,8 +612,8 @@ impl UnixDatagram {
///
/// # Cancel safety
///
/// This method is cancel safe. If `send` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If `send` is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then it is guaranteed that the message was not sent.
///
/// # Examples
@@ -742,8 +742,8 @@ impl UnixDatagram {
///
/// # Cancel safety
///
/// This method is cancel safe. If `recv` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If `recv` is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, it is guaranteed that no messages were received on this
/// socket.
///
@@ -903,7 +903,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
/// use tempfile::tempdir;
///
@@ -1062,8 +1062,8 @@ impl UnixDatagram {
///
/// # Cancel safety
///
/// This method is cancel safe. If `send_to` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If `send_to` is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, then it is guaranteed that the message was not sent.
///
/// # Examples
@@ -1071,7 +1071,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
/// use tempfile::tempdir;
///
@@ -1112,8 +1112,8 @@ impl UnixDatagram {
///
/// # Cancel safety
///
/// This method is cancel safe. If `recv_from` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// This method is cancel safe. If `recv_from` is used as a branch in
/// [`tokio::select!`](crate::select) and another branch
/// completes first, it is guaranteed that no messages were received on this
/// socket.
///
@@ -1122,7 +1122,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
/// use tempfile::tempdir;
///
@@ -1439,7 +1439,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
/// use tempfile::tempdir;
///
@@ -1462,7 +1462,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
///
/// // Create an unbound socket
@@ -1487,7 +1487,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
/// use tempfile::tempdir;
///
@@ -1535,7 +1535,7 @@ impl UnixDatagram {
/// # use std::error::Error;
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn Error>> {
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
/// use tokio::net::UnixDatagram;
///
/// // Create an unbound socket

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