Compare commits

...
Author SHA1 Message Date
Cristian Alejandro Enguídanos Nebot 83d82f97eb sync: fix clippy warning in docs (#5873) 2023-07-17 10:13:14 +02:00
Carl Lerche 1b1b9dc7e3 chore: prepare Tokio v1.29.1 release 2023-06-29 14:27:28 -07:00
Carl Lerche 012c848401 rt: fix nesting block_in_place with block_on (#5837)
This patch fixes a bug where nesting `block_in_place` with a `block_on`
between could lead to a panic. This happened because the nested
`block_in_place` would try to acquire a core on return when it should
not attempt to do so. The `block_on` between the two nested
`block_in_place` altered the thread-local state to lead to the incorrect
behavior.

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

Fixes #5239
2023-06-29 14:23:46 -07:00
Carl Lerche 48c55768fd chore: prepare Tokio v1.29.0 release (#5826) 2023-06-27 13:37:10 -07:00
Diggory Blake 657fd883d2 task: add guarantee about when a spawned task may be polled (#5816) 2023-06-27 19:05:09 +02:00
Dustin J. Mitchell 6b076a2743 fs: wait for in-flight ops before cloning File (#5803)
If there is an ongoing operation on a file, wait for that to complete
before performing the clone in `File::try_clone`. This avoids a race
between the ongoing operation and any subsequent operations performed on
the clone.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The text and the command have been updated to reflect the current state
of CI and best practices.
2023-04-16 15:46:01 +02:00
Jens Reidel effead29d1 time: add DelayQueue::peek (#5569)
Signed-off-by: Jens Reidel <[email protected]>
2023-04-16 09:43:03 +02:00
Alexander van Ratingen 3b16564ce0 sync: add OwnedSemaphorePermit::semaphore (#5618) 2023-04-15 20:55:39 +02:00
John-John Tedro fc1e03f91b macros: make entrypoints more efficient (#5621) 2023-04-15 20:55:00 +02:00
John-John Tedro ea5d448ee8 ci: gate costly checks behind basic ones (#5622) 2023-04-15 20:16:42 +02:00
Iron(III) Oxide abc93f615e fuzz: fix fuzz warnings (#5614) 2023-04-13 16:07:12 +02:00
mTsBucy1 cf9a03c107 macros: hide internal constant in select! macro (#5617) 2023-04-12 21:32:38 +09:00
Alice Ryhl 3b45e8614d stream: update StreamMap fuzz test (#5600)
This fuzz test touches some quadratic time code paths. This changes the
test to limit the test size so that the test doesn't time out.
2023-04-11 10:40:10 +02:00
Alice Ryhl b02c550c52 ci: fix FreeBSD ci (#5613) 2023-04-10 15:26:59 +02:00
Oddbjørn Grødem 03912b9cf7 sync: add same_channel to broadcast channel (#5607) 2023-04-07 14:18:47 +02:00
Daniel Sedlak d4afbad6e5 tokio: fix typos (#5604) 2023-04-06 14:29:12 +02:00
Daniel Netzer b1ca0d8b12 ci: fix typo in ci.yml (#5599) 2023-04-05 09:35:47 +00:00
Iron(III) Oxide 16cdb109f4 io: impl BufMut for ReadBuf (#5590) 2023-04-04 22:00:41 +02:00
Taiki Endo 88445e762c deps: update windows-sys to 0.48 (#5591) 2023-04-02 09:31:27 +02:00
teor 3c403d6ee8 coop: fix typo in poll_proceed() doc comment (#5589) 2023-03-30 13:25:05 +02:00
Dmitry Rodionov d63d659078 sync: fix typo in tokio::sync::watch::Sender docs (#5587) 2023-03-29 18:05:24 +00:00
Flavio Bizzarri b31f1a4662 net: add recv_buf for UdpSocket and UnixDatagram (#5583) 2023-03-28 18:17:22 +00:00
Qiu Chaofan 663e56e983 net: support AIX get_peer_cred (#5065) 2023-03-28 11:25:12 +02:00
Alice Ryhl 1df874ead4 chore: prepare Tokio v1.27.0 (#5584) 2023-03-27 23:55:48 +02:00
Alice Ryhl 614fe357fc chore: prepare tokio-macros v2.0.0 (#5580) 2023-03-27 22:45:54 +02:00
sgasseandSimon B. Gasse 68b02db154 time: fix wake-up with interval on Ready (#5553)
When `tokio::time::Interval::poll_tick()` returns `Poll::Pending`, it
schedules itself for being woken up again through the waker of the
passed context, which is correct behavior.

However when `Poll::Ready(_)` is returned, the interval timer should be
reset but not scheduled to be woken up again as this is up to the
caller.

This commit fixes the bug by introducing a `reset_without_reregister`
method on `TimerEntry` which is called by `Intervall::poll_tick(cx)` in
case the delay poll returns `Poll::Ready(_)`.

Co-authored-by: Simon B. Gasse <[email protected]>
2023-03-27 17:39:53 +02:00
Alice Ryhl 822af18cf5 sync: fix Semaphore::MAX_PERMITS test (#5582) 2023-03-25 18:09:05 +00:00
David Pedersen 92d33b7181 macros: update syn (#5572) 2023-03-23 23:38:59 +01:00
Marcelo Diop-Gonzalez 1cb7bf11b3 time: clean up redundant check in Wheel::poll() (#5574)
The condition checked in the and_then() call is the same as is checked
in the match below, so we can clean it up by just matching on
next_expiration() directly.
2023-03-23 09:53:54 -07:00
João Marcos 768ede65c1 io: refer to ReaderStream and StreamReader in module docs (#5576) 2023-03-23 15:31:40 +00:00
João Marcos 54a394696f io: add details to docs of tokio::io::copy[_buf] (#5575) 2023-03-23 15:19:51 +00:00
Hayden Stainsby 35dd635630 tracing: fix spawn_blocking location fields (#5573)
In a previous PR (#4128), the `spawn.location` field on task spans was
structured into 3 separate fields for the `file`, `line`, and `col`.
There is a separately created span for blocking tasks which was missed.

This caused tasks created with `spawn_blocking` to appear in
`tokio-console` without a location, but with an additional "free form"
field containing the formatted source code location.

This change modifies this span to use the same format. The span creation
needs to be separate from the other task spans because it records the
function name. This information is useful in the `spawn_blocking` case,
but can be "catastrophically long" in the `async fn` case and was
removed in #3074.
2023-03-22 22:08:01 +01:00
Timmy Xiao a7bb054414 tokio: update stream, util, test to 2021 edition (#5571) 2023-03-21 17:36:40 +00:00
Alice Ryhl 0c8e8248f8 tokio: bump MSRV to 1.56 (#5559) 2023-03-21 18:06:47 +01:00
Alice Ryhl 2dfe4e8885 time: fix repeatedly_reset_entry_inserted_as_expired test (#5570) 2023-03-21 18:06:22 +01:00
Alice Ryhl b489acb46c sync: try to lock the parent first in CancellationToken (#5561) 2023-03-21 14:20:33 +01:00
Ivan Zderadicka d46c844bb9 examples: fix panic in tinyhttp example (#5328)
As method in httparse result is no longer part of input buffer it needs
to be handled separately.
2023-03-20 18:52:38 +01:00
devensiv 4cd4b02389 io: fix wait operation on mock (#5554) 2023-03-19 12:35:39 +00:00
daxpedda 17cc283f58 macros: accept path as crate rename (#5557) 2023-03-19 12:28:43 +01:00
Alice Ryhl 0a93ed7e7a io: use memchr from libc (#5558) 2023-03-19 10:49:19 +00:00
Alice Ryhl cef98e25e7 macros: define cancellation safety (#5525) 2023-03-18 21:25:23 +00:00
Tymoteusz Wiśniewski e7bd754231 fs: fuse std iterator in ReadDir (#5555)
Implementation of Tokio's ReadDir assumes that ReadDir from std is
fused, but that's not the case on Windows. This change wraps the std
iterator in std::iter::Fuse to make its usage correct.
2023-03-18 14:07:40 +01:00
Austin Bonander d459a93453 net: add UdpSocket::peek_sender() (#5520) 2023-03-17 10:22:42 +01:00
Shaye Garg f177aad6e4 task: add JoinHandle::abort_handle (#5543) 2023-03-16 14:02:48 +01:00
Tymoteusz Wiśniewski 4ea632005d tokio: make all windows docs visible in unix builds (#5530) 2023-03-14 23:33:31 +01:00
Matilda Smeds bfc43795f9 doc: explain testing in contributing guide (#5537)
* Add links to fundamental testing concepts in Rust
* Add information about conditional compilation attributes
  and how to use them to run tests with cargo
2023-03-12 21:01:58 +01:00
Tymoteusz Wiśniewski 89329cd07f io: add missing AsFd/AsHandle impls (#5540)
* io: add AsFd/AsHandle impls for stdio

* io: add AsFd impl for AsyncFd

* net: add AsHandle impl for named pipe types
2023-03-12 20:01:19 +01:00
Steven Fackler e34978233b io: add implementations of AsFd/AsHandle/AsSocket (#5514) 2023-03-11 17:04:01 +01:00
Andrew Halle 8eb94a33c0 time: fix a typo in timeout docs (#5538)
Replace instances of "immediatelly" with "immediately".
2023-03-11 10:40:19 +01:00
amab8901 2b7b1a0494 io: add async_io helper method to sockets (#5512) 2023-03-09 10:12:55 +00:00
Filipe Rodrigues 002f4a28c8 sync: add RwLockWriteGuard::{downgrade_map, try_downgrade_map} (#5527) 2023-03-08 17:06:08 +01:00
Tymoteusz Wiśniewski ff2f286c12 fs: fix File::from_raw_fd test (#5528) 2023-03-04 11:45:42 +01:00
Aaron Chen bd4ce68864 process: change "with regards" to "with regard to" in the docs (#5529) 2023-03-04 09:48:29 +00:00
Tymoteusz Wiśniewski abd92fb27f net: document usage of ready with stream halves (#5515) 2023-03-03 12:21:29 +01:00
Noah Kennedy 9931901d5c chore: list 1.25.x as LTS release (#5524) 2023-03-01 23:42:18 +00:00
248 changed files with 11649 additions and 2325 deletions
-2
View File
@@ -1,2 +0,0 @@
# [build]
# rustflags = ["--cfg", "tokio_unstable"]
+4 -4
View File
@@ -1,7 +1,7 @@
only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'master' || $CIRRUS_BRANCH =~ 'tokio-.*')
auto_cancellation: $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH !=~ 'tokio-.*'
freebsd_instance:
image_family: freebsd-12-4
image_family: freebsd-13-1
env:
RUST_STABLE: stable
RUST_NIGHTLY: nightly-2022-10-25
@@ -14,7 +14,7 @@ env:
task:
name: FreeBSD 64-bit
setup_script:
- pkg install -y bash curl
- pkg install -y bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_STABLE
- . $HOME/.cargo/env
@@ -31,7 +31,7 @@ task:
RUSTFLAGS: --cfg docsrs --cfg tokio_unstable
RUSTDOCFLAGS: --cfg docsrs --cfg tokio_unstable -Dwarnings
setup_script:
- pkg install -y bash curl
- pkg install -y bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_NIGHTLY
- . $HOME/.cargo/env
@@ -45,7 +45,7 @@ task:
task:
name: FreeBSD 32-bit
setup_script:
- pkg install -y bash curl
- pkg install -y bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_STABLE
- . $HOME/.cargo/env
+1 -1
View File
@@ -1 +1 @@
msrv = "1.49"
msrv = "1.56"
+175 -65
View File
@@ -11,7 +11,7 @@ env:
RUST_BACKTRACE: 1
# Change to specific Rust release to pin
rust_stable: stable
rust_nightly: nightly-2022-11-03
rust_nightly: nightly-2023-05-18
rust_clippy: 1.65.0
# When updating this, also update:
# - README.md
@@ -21,7 +21,7 @@ env:
# - tokio-util/Cargo.toml
# - tokio-test/Cargo.toml
# - tokio-stream/Cargo.toml
rust_min: 1.49.0
rust_min: 1.56.0
defaults:
run:
@@ -31,7 +31,7 @@ permissions:
contents: read
jobs:
# Depends on all action sthat are required for a "successful" CI run.
# Depends on all actions that are required for a "successful" CI run.
tests-pass:
name: all systems go
runs-on: ubuntu-latest
@@ -42,7 +42,6 @@ jobs:
- test-unstable
- miri
- asan
- semver
- cross-check
- cross-test
- no-atomic-u64
@@ -56,13 +55,29 @@ jobs:
- check-readme
- test-hyper
- x86_64-fortanix-unknown-sgx
- check-redox
- wasm32-unknown-unknown
- wasm32-wasi
- check-external-types
- check-fuzzing
- check-unstable-mt-counters
steps:
- run: exit 0
# Basic actions that must pass before we kick off more expensive tests.
basics:
name: basic checks
runs-on: ubuntu-latest
needs:
- clippy
- fmt
- docs
- minrust
steps:
- run: exit 0
test:
needs: basics
name: test tokio full
runs-on: ${{ matrix.os }}
strategy:
@@ -119,6 +134,7 @@ jobs:
# This relies on the potentially affected Tokio type being listed in
# `tokio/tokio/tests/async_send_sync.rs`.
name: compile tests with parking lot send guards
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -135,6 +151,7 @@ jobs:
valgrind:
name: valgrind
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -167,13 +184,14 @@ jobs:
test-unstable:
name: test tokio full --unstable
needs: basics
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- windows-latest
- ubuntu-latest
- macos-latest
include:
- os: windows-latest
- os: ubuntu-latest
- os: macos-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
@@ -191,8 +209,59 @@ jobs:
# the unstable cfg to RustDoc
RUSTDOCFLAGS: --cfg tokio_unstable
test-unstable-taskdump:
name: test tokio full --unstable --taskdump
needs: basics
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
# Run `tokio` with "unstable" and "taskdump" cfg flags.
- name: test tokio full --cfg unstable --cfg taskdump
run: cargo test --all-features
working-directory: tokio
env:
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings
# in order to run doctests for unstable features, we must also pass
# the unstable cfg to RustDoc
RUSTDOCFLAGS: --cfg tokio_unstable --cfg tokio_taskdump
check-unstable-mt-counters:
name: check tokio full --internal-mt-counters
needs: basics
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- uses: Swatinem/rust-cache@v2
# Run `tokio` with "unstable" and "taskdump" cfg flags.
- name: check tokio full --cfg unstable --cfg internal-mt-counters
run: cargo test --all-features
working-directory: tokio
env:
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_internal_mt_counters -Dwarnings
# in order to run doctests for unstable features, we must also pass
# the unstable cfg to RustDoc
RUSTDOCFLAGS: --cfg tokio_unstable --cfg tokio_internal_mt_counters
miri:
name: miri
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -212,6 +281,7 @@ jobs:
asan:
name: asan
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -224,31 +294,29 @@ jobs:
toolchain: ${{ env.rust_nightly }}
- uses: Swatinem/rust-cache@v2
- name: asan
run: cargo test --workspace --all-features --target x86_64-unknown-linux-gnu --tests -- --test-threads 1
run: cargo test --workspace --all-features --target x86_64-unknown-linux-gnu --tests -- --test-threads 1 --nocapture
env:
RUSTFLAGS: -Z sanitizer=address
RUSTFLAGS: -Z sanitizer=address --cfg tokio_no_tuning_tests
# Ignore `trybuild` errors as they are irrelevant and flaky on nightly
TRYBUILD: overwrite
semver:
name: semver
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_stable }}
- name: Install cargo-semver-checks
uses: taiki-e/install-action@v2
with:
tool: cargo-semver-checks
- name: Check semver compatibility
run: |
cargo semver-checks check-release --release-type minor
# Re-enable this after the next release.
#
#semver:
# name: semver
# needs: basics
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - name: Check semver
# uses: obi1kenobi/cargo-semver-checks-action@v2
# with:
# rust-toolchain: ${{ env.rust_stable }}
# release-type: minor
cross-check:
name: cross-check
needs: basics
runs-on: ubuntu-latest
strategy:
matrix:
@@ -273,14 +341,17 @@ jobs:
cross-test:
name: cross-test
needs: basics
runs-on: ubuntu-latest
strategy:
matrix:
include:
- target: i686-unknown-linux-gnu
rustflags: --cfg tokio_taskdump
- target: arm-unknown-linux-gnueabihf
- target: armv7-unknown-linux-gnueabihf
- target: aarch64-unknown-linux-gnu
rustflags: --cfg tokio_taskdump
# Run a platform without AtomicU64 and no const Mutex::new
- target: arm-unknown-linux-gnueabihf
@@ -297,18 +368,19 @@ jobs:
# First run with all features (including parking_lot)
- run: cross test -p tokio --all-features --target ${{ matrix.target }} --tests
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_ipv6 ${{ matrix.rustflags }}
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_ipv6 --cfg tokio_no_tuning_tests ${{ matrix.rustflags }}
# Now run without parking_lot
- name: Remove `parking_lot` from `full` feature
run: sed -i '0,/parking_lot/{/parking_lot/d;}' tokio/Cargo.toml
# The `tokio_no_parking_lot` cfg is here to ensure the `sed` above does not silently break.
- run: cross test -p tokio --features full,test-util --target ${{ matrix.target }} --tests
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_ipv6 --cfg tokio_no_parking_lot ${{ matrix.rustflags }}
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_ipv6 --cfg tokio_no_parking_lot --cfg tokio_no_tuning_tests ${{ matrix.rustflags }}
# See https://github.com/tokio-rs/tokio/issues/5187
no-atomic-u64:
name: Test i686-unknown-linux-gnu without AtomicU64
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -323,20 +395,21 @@ jobs:
- uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: i686-unknown-linux-gnu
- run: cargo test -Zbuild-std --target target-specs/i686-unknown-linux-gnu.json -p tokio --all-features
- run: cargo test -Zbuild-std --target target-specs/i686-unknown-linux-gnu.json -p tokio --all-features -- --test-threads 1 --nocapture
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_atomic_u64
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings --cfg tokio_no_atomic_u64
# https://github.com/tokio-rs/tokio/pull/5356
# https://github.com/tokio-rs/tokio/issues/5373
- run: cargo hack build -p tokio --feature-powerset --depth 2 -Z avoid-dev-deps --keep-going
- run: cargo hack build -p tokio --feature-powerset --depth 2 --keep-going
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_atomic_u64 --cfg tokio_no_const_mutex_new
- run: cargo hack build -p tokio --feature-powerset --depth 2 -Z avoid-dev-deps --keep-going
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings --cfg tokio_no_atomic_u64 --cfg tokio_no_const_mutex_new
- run: cargo hack build -p tokio --feature-powerset --depth 2 --keep-going
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_atomic_u64
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings --cfg tokio_no_atomic_u64
features:
name: features
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -349,12 +422,17 @@ jobs:
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- name: check --feature-powerset
run: cargo hack check --all --feature-powerset --depth 2 -Z avoid-dev-deps --keep-going
run: cargo hack check --all --feature-powerset --depth 2 --keep-going
# Try with unstable feature flags
- name: check --feature-powerset --unstable
run: cargo hack check --all --feature-powerset --depth 2 -Z avoid-dev-deps --keep-going
run: cargo hack check --all --feature-powerset --depth 2 --keep-going
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
# Try with unstable and taskdump feature flags
- name: check --feature-powerset --unstable --taskdump
run: cargo hack check --all --feature-powerset --depth 2 --keep-going
env:
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings
minrust:
name: minrust
@@ -366,19 +444,6 @@ jobs:
with:
toolchain: ${{ env.rust_min }}
- uses: Swatinem/rust-cache@v2
# First compile just the main tokio crate with minrust and newest version
# of all dependencies, then pin once_cell and compile the rest of the
# crates with the pinned once_cell version.
#
# This is necessary because tokio-util transitively depends on once_cell,
# which is not compatible with the current minrust after the 1.15.0
# release.
- name: "check -p tokio --all-features"
run: cargo check -p tokio --all-features
env:
RUSTFLAGS: "" # remove -Dwarnings
- name: "pin once_cell version"
run: cargo update -p once_cell --precise 1.14.0
- name: "check --workspace --all-features"
run: cargo check --workspace --all-features
env:
@@ -386,6 +451,7 @@ jobs:
minimal-versions:
name: minimal-versions
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -406,7 +472,7 @@ jobs:
cargo hack check --all-features --ignore-private
- name: "check --all-features --unstable -Z minimal-versions"
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings
run: |
# Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
# from determining minimal versions based on dev-dependencies.
@@ -430,8 +496,8 @@ jobs:
- name: "rustfmt --check"
# Workaround for rust-lang/cargo#7732
run: |
if ! rustfmt --check --edition 2018 $(git ls-files '*.rs'); then
printf "Please run \`rustfmt --edition 2018 \$(git ls-files '*.rs')\` to fix rustfmt errors.\nSee CONTRIBUTING.md for more details.\n" >&2
if ! rustfmt --check --edition 2021 $(git ls-files '*.rs'); then
printf "Please run \`rustfmt --edition 2021 \$(git ls-files '*.rs')\` to fix rustfmt errors.\nSee CONTRIBUTING.md for more details.\n" >&2
exit 1
fi
@@ -463,11 +529,12 @@ jobs:
- name: "doc --lib --all-features"
run: cargo doc --lib --no-deps --all-features --document-private-items
env:
RUSTFLAGS: --cfg docsrs --cfg tokio_unstable
RUSTDOCFLAGS: --cfg docsrs --cfg tokio_unstable -Dwarnings
RUSTFLAGS: --cfg docsrs --cfg tokio_unstable --cfg tokio_taskdump
RUSTDOCFLAGS: --cfg docsrs --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings
loom-compile:
name: build loom tests
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -496,6 +563,7 @@ jobs:
test-hyper:
name: Test hyper
needs: basics
runs-on: ${{ matrix.os }}
strategy:
matrix:
@@ -529,6 +597,7 @@ jobs:
x86_64-fortanix-unknown-sgx:
name: build tokio for x86_64-fortanix-unknown-sgx
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -543,8 +612,24 @@ jobs:
run: cargo build --target x86_64-fortanix-unknown-sgx --features rt,sync
working-directory: tokio
check-redox:
name: build tokio for redox-os
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_nightly }}
target: x86_64-unknown-redox
- name: check tokio on redox
run: cargo check --target x86_64-unknown-redox --all-features
working-directory: tokio
wasm32-unknown-unknown:
name: test tokio for wasm32-unknown-unknown
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -561,6 +646,7 @@ jobs:
wasm32-wasi:
name: wasm32-wasi
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -607,25 +693,49 @@ jobs:
working-directory: tests-integration
check-external-types:
name: check-external-types
name: check-external-types (${{ matrix.os }})
needs: basics
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- windows-latest
- ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust nightly-2022-11-16
uses: dtolnay/rust-toolchain@master
with:
rust:
# `check-external-types` requires a specific Rust nightly version. See
# the README for details: https://github.com/awslabs/cargo-check-external-types
toolchain: nightly-2022-11-16
- nightly-2023-05-31
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ matrix.rust }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- uses: Swatinem/rust-cache@v2
- name: Install cargo-check-external-types
uses: taiki-e/cache-cargo-install-action@v1
with:
tool: [email protected]
- name: check-external-types
run: |
set -x
cargo install cargo-check-external-types --locked --version 0.1.6
cargo check-external-types --all-features --config external-types.toml
run: cargo check-external-types --all-features --config external-types.toml
working-directory: tokio
check-fuzzing:
name: check-fuzzing
needs: basics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.rust_nightly }}
- uses: Swatinem/rust-cache@v2
- name: Install cargo-fuzz
run: cargo install cargo-fuzz
- name: Check /tokio/
run: cargo fuzz check --all-features
working-directory: tokio
- name: Check /tokio-stream/
run: cargo fuzz check --all-features
working-directory: tokio-stream
+15 -9
View File
@@ -24,13 +24,19 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
scope:
- --skip loom_pool
- loom_pool::group_a
- loom_pool::group_b
- loom_pool::group_c
- loom_pool::group_d
- time::driver
include:
- scope: --skip loom_pool
max_preemptions: 2
- scope: loom_pool::group_a
max_preemptions: 2
- scope: loom_pool::group_b
max_preemptions: 2
- scope: loom_pool::group_c
max_preemptions: 2
- scope: loom_pool::group_d
max_preemptions: 2
- scope: time::driver
max_preemptions: 2
steps:
- uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_stable }}
@@ -42,7 +48,7 @@ jobs:
run: cargo test --lib --release --features full -- --nocapture $SCOPE
working-directory: tokio
env:
RUSTFLAGS: --cfg loom --cfg tokio_unstable -Dwarnings
LOOM_MAX_PREEMPTIONS: 2
RUSTFLAGS: --cfg loom --cfg tokio_unstable -Dwarnings -C debug-assertions
LOOM_MAX_PREEMPTIONS: ${{ matrix.max_preemptions }}
LOOM_MAX_BRANCHES: 10000
SCOPE: ${{ matrix.scope }}
+1
View File
@@ -2,3 +2,4 @@ target
Cargo.lock
.cargo/config.toml
.cargo/config
+29 -8
View File
@@ -131,8 +131,11 @@ cargo check --all-features
cargo test --all-features
```
Clippy must be run using the MSRV, so Tokio can avoid having to `#[allow]` new
lints whose fixes would be incompatible with the current MSRV:
Ideally, you should use the same version of clippy as the one used in CI
(defined by `env.rust_clippy` in [ci.yml][ci.yml]), because newer versions
might have new lints:
[ci.yml]: .github/workflows/ci.yml
<!--
When updating this, also update:
@@ -146,7 +149,7 @@ When updating this, also update:
-->
```
cargo +1.49.0 clippy --all --tests --all-features
cargo +1.65.0 clippy --all --tests --all-features
```
When building documentation normally, the markers that list the features
@@ -170,10 +173,10 @@ command below instead:
```
# Mac or Linux
rustfmt --check --edition 2018 $(git ls-files '*.rs')
rustfmt --check --edition 2021 $(git ls-files '*.rs')
# Powershell
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2018 $_.FullName }
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2021 $_.FullName }
```
The `--check` argument prints the things that need to be fixed. If you remove
it, `rustfmt` will update your files locally instead.
@@ -197,8 +200,22 @@ If the change being proposed alters code (as opposed to only documentation for
example), it is either adding new functionality to Tokio or it is fixing
existing, broken functionality. In both of these cases, the pull request should
include one or more tests to ensure that Tokio does not regress in the future.
There are two ways to write tests: integration tests and documentation tests
(Tokio avoids unit tests as much as possible).
There are two ways to write tests: [integration tests][integration-tests]
and [documentation tests][documentation-tests].
(Tokio avoids [unit tests][unit-tests] as much as possible).
Tokio uses [conditional compilation attributes][conditional-compilation]
throughout the codebase, to modify rustc's behavior. Code marked with such
attributes can be enabled using RUSTFLAGS and RUSTDOCFLAGS environment
variables. One of the most prevalent flags passed in these variables is
the `--cfg` option. To run tests in a particular file, check first what
options #![cfg] declaration defines for that file.
For instance, to run a test marked with the 'tokio_unstable' cfg option,
you must pass this flag to the compiler when running the test.
```
$ RUSTFLAGS="--cfg tokio_unstable" cargo test -p tokio --all-features --test rt_metrics
```
#### Integration tests
@@ -213,7 +230,7 @@ integration tests in the crate and follow the style.
Some of our crates include a set of fuzz tests, this will be marked by a
directory `fuzz`. It is a good idea to run fuzz tests after each change.
To get started with fuzz testing you'll need to install
To get started with fuzz testing you'll need to install
[cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz).
`cargo install cargo-fuzz`
@@ -658,3 +675,7 @@ When releasing a new version of a crate, follow these steps:
entry for that release version into your editor and close the window.
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog/blob/master/CHANGELOG.md
[unit-tests]: https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html
[integration-tests]: https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html
[documentation-tests]: https://doc.rust-lang.org/rust-by-example/testing/doc_testing.html
[conditional-compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
+1 -1
View File
@@ -1,5 +1,5 @@
[workspace]
resolver = "2"
members = [
"tokio",
"tokio-macros",
+18 -4
View File
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.26.0", features = ["full"] }
tokio = { version = "1.29.1", features = ["full"] }
```
Then, on your main.rs:
@@ -187,7 +187,20 @@ When updating this, also update:
Tokio will keep a rolling MSRV (minimum supported rust version) policy of **at
least** 6 months. When increasing the MSRV, the new Rust version must have been
released at least six months ago. The current MSRV is 1.49.0.
released at least six months ago. The current MSRV is 1.56.0.
Note that the MSRV is not increased automatically, and only as part of a minor
release. The MSRV history for past minor releases can be found below:
* 1.27 to now - Rust 1.56
* 1.17 to 1.26 - Rust 1.49
* 1.15 to 1.16 - Rust 1.46
* 1.0 to 1.14 - Rust 1.45
Note that although we try to avoid the situation where a dependency transitively
increases the MSRV of Tokio, we do not guarantee that this does not happen.
However, every minor release will have some set of versions of dependencies that
works with the MSRV of that minor release.
## Release schedule
@@ -202,8 +215,9 @@ warrants a patch release with a fix for the bug, it will be backported and
released as a new patch release for each LTS minor version. Our current LTS
releases are:
* `1.18.x` - LTS release until June 2023
* `1.20.x` - LTS release until September 2023.
* `1.18.x` - LTS release until June 2023. (MSRV 1.49)
* `1.20.x` - LTS release until September 2023. (MSRV 1.49)
* `1.25.x` - LTS release until March 2024. (MSRV 1.49)
Each LTS release will continue to receive backported fixes for at least a year.
If you wish to use a fixed minor release in your project, we recommend that you
+10 -1
View File
@@ -2,7 +2,7 @@
name = "benches"
version = "0.0.0"
publish = false
edition = "2018"
edition = "2021"
[features]
test-util = ["tokio/test-util"]
@@ -40,11 +40,20 @@ name = "sync_watch"
path = "sync_watch.rs"
harness = false
[[bench]]
name = "rt_current_thread"
path = "rt_current_thread.rs"
harness = false
[[bench]]
name = "rt_multi_threaded"
path = "rt_multi_threaded.rs"
harness = false
[[bench]]
name = "sync_notify"
path = "sync_notify.rs"
harness = false
[[bench]]
name = "sync_rwlock"
+83
View File
@@ -0,0 +1,83 @@
//! Benchmark implementation details of the threaded scheduler. These benches are
//! intended to be used as a form of regression testing and not as a general
//! purpose benchmark demonstrating real-world performance.
use tokio::runtime::{self, Runtime};
use bencher::{benchmark_group, benchmark_main, Bencher};
const NUM_SPAWN: usize = 1_000;
fn spawn_many_local(b: &mut Bencher) {
let rt = rt();
let mut handles = Vec::with_capacity(NUM_SPAWN);
b.iter(|| {
rt.block_on(async {
for _ in 0..NUM_SPAWN {
handles.push(tokio::spawn(async move {}));
}
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
});
}
fn spawn_many_remote_idle(b: &mut Bencher) {
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt_handle.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
});
}
fn spawn_many_remote_busy(b: &mut Bencher) {
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
rt.spawn(async {
fn iter() {
tokio::spawn(async { iter() });
}
iter()
});
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt_handle.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
});
}
fn rt() -> Runtime {
runtime::Builder::new_current_thread().build().unwrap()
}
benchmark_group!(
scheduler,
spawn_many_local,
spawn_many_remote_idle,
spawn_many_remote_busy
);
benchmark_main!(scheduler);
+117 -5
View File
@@ -6,13 +6,16 @@ use tokio::runtime::{self, Runtime};
use tokio::sync::oneshot;
use bencher::{benchmark_group, benchmark_main, Bencher};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::{mpsc, Arc};
use std::time::{Duration, Instant};
fn spawn_many(b: &mut Bencher) {
const NUM_SPAWN: usize = 10_000;
const NUM_WORKERS: usize = 4;
const NUM_SPAWN: usize = 10_000;
const STALL_DUR: Duration = Duration::from_micros(10);
fn spawn_many_local(b: &mut Bencher) {
let rt = rt();
let (tx, rx) = mpsc::sync_channel(1000);
@@ -38,6 +41,99 @@ fn spawn_many(b: &mut Bencher) {
});
}
fn spawn_many_remote_idle(b: &mut Bencher) {
let rt = rt();
let mut handles = Vec::with_capacity(NUM_SPAWN);
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
});
}
// The runtime is busy with tasks that consume CPU time and yield. Yielding is a
// lower notification priority than spawning / regular notification.
fn spawn_many_remote_busy1(b: &mut Bencher) {
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
let flag = Arc::new(AtomicBool::new(true));
// Spawn some tasks to keep the runtimes busy
for _ in 0..(2 * NUM_WORKERS) {
let flag = flag.clone();
rt.spawn(async move {
while flag.load(Relaxed) {
tokio::task::yield_now().await;
stall();
}
});
}
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt_handle.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
});
flag.store(false, Relaxed);
}
// The runtime is busy with tasks that consume CPU time and spawn new high-CPU
// tasks. Spawning goes via a higher notification priority than yielding.
fn spawn_many_remote_busy2(b: &mut Bencher) {
const NUM_SPAWN: usize = 1_000;
let rt = rt();
let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN);
let flag = Arc::new(AtomicBool::new(true));
// Spawn some tasks to keep the runtimes busy
for _ in 0..(NUM_WORKERS) {
let flag = flag.clone();
fn iter(flag: Arc<AtomicBool>) {
tokio::spawn(async {
if flag.load(Relaxed) {
stall();
iter(flag);
}
});
}
rt.spawn(async {
iter(flag);
});
}
b.iter(|| {
for _ in 0..NUM_SPAWN {
handles.push(rt_handle.spawn(async {}));
}
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
});
flag.store(false, Relaxed);
}
fn yield_many(b: &mut Bencher) {
const NUM_YIELD: usize = 1_000;
const TASKS: usize = 200;
@@ -140,12 +236,28 @@ fn chained_spawn(b: &mut Bencher) {
fn rt() -> Runtime {
runtime::Builder::new_multi_thread()
.worker_threads(4)
.worker_threads(NUM_WORKERS)
.enable_all()
.build()
.unwrap()
}
benchmark_group!(scheduler, spawn_many, ping_pong, yield_many, chained_spawn,);
fn stall() {
let now = Instant::now();
while now.elapsed() < STALL_DUR {
std::thread::yield_now();
}
}
benchmark_group!(
scheduler,
spawn_many_local,
spawn_many_remote_idle,
spawn_many_remote_busy1,
spawn_many_remote_busy2,
ping_pong,
yield_many,
chained_spawn,
);
benchmark_main!(scheduler);
+90
View File
@@ -0,0 +1,90 @@
use bencher::Bencher;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use tokio::sync::Notify;
fn rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap()
}
fn notify_waiters<const N_WAITERS: usize>(b: &mut Bencher) {
let rt = rt();
let notify = Arc::new(Notify::new());
let counter = Arc::new(AtomicUsize::new(0));
for _ in 0..N_WAITERS {
rt.spawn({
let notify = notify.clone();
let counter = counter.clone();
async move {
loop {
notify.notified().await;
counter.fetch_add(1, Ordering::Relaxed);
}
}
});
}
const N_ITERS: usize = 500;
b.iter(|| {
counter.store(0, Ordering::Relaxed);
loop {
notify.notify_waiters();
if counter.load(Ordering::Relaxed) >= N_ITERS {
break;
}
}
});
}
fn notify_one<const N_WAITERS: usize>(b: &mut Bencher) {
let rt = rt();
let notify = Arc::new(Notify::new());
let counter = Arc::new(AtomicUsize::new(0));
for _ in 0..N_WAITERS {
rt.spawn({
let notify = notify.clone();
let counter = counter.clone();
async move {
loop {
notify.notified().await;
counter.fetch_add(1, Ordering::Relaxed);
}
}
});
}
const N_ITERS: usize = 500;
b.iter(|| {
counter.store(0, Ordering::Relaxed);
loop {
notify.notify_one();
if counter.load(Ordering::Relaxed) >= N_ITERS {
break;
}
}
});
}
bencher::benchmark_group!(
notify_waiters_simple,
notify_waiters::<10>,
notify_waiters::<50>,
notify_waiters::<100>,
notify_waiters::<200>,
notify_waiters::<500>
);
bencher::benchmark_group!(
notify_one_simple,
notify_one::<10>,
notify_one::<50>,
notify_one::<100>,
notify_one::<200>,
notify_one::<500>
);
bencher::benchmark_main!(notify_waiters_simple, notify_one_simple);
+6 -2
View File
@@ -2,7 +2,7 @@
name = "examples"
version = "0.0.0"
publish = false
edition = "2018"
edition = "2021"
# If you copy one of the examples into a new project, you should be using
# [dependencies] instead, and delete the **path**.
@@ -25,7 +25,7 @@ once_cell = "1.5.2"
rand = "0.8.3"
[target.'cfg(windows)'.dev-dependencies.windows-sys]
version = "0.45"
version = "0.48"
[[example]]
name = "chat"
@@ -90,3 +90,7 @@ path = "named-pipe-ready.rs"
[[example]]
name = "named-pipe-multi-client"
path = "named-pipe-multi-client.rs"
[[example]]
name = "dump"
path = "dump.rs"
+90
View File
@@ -0,0 +1,90 @@
//! This example demonstrates tokio's experimental task dumping functionality.
//! This application deadlocks. Input CTRL+C to display traces of each task, or
//! input CTRL+C twice within 1 second to quit.
#[cfg(all(
tokio_unstable,
tokio_taskdump,
target_os = "linux",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
))]
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
use std::sync::Arc;
use tokio::sync::Barrier;
#[inline(never)]
async fn a(barrier: Arc<Barrier>) {
b(barrier).await
}
#[inline(never)]
async fn b(barrier: Arc<Barrier>) {
c(barrier).await
}
#[inline(never)]
async fn c(barrier: Arc<Barrier>) {
barrier.wait().await;
}
// Prints a task dump upon receipt of CTRL+C, or returns if CTRL+C is
// inputted twice within a second.
async fn dump_or_quit() {
use tokio::time::{timeout, Duration, Instant};
let handle = tokio::runtime::Handle::current();
let mut last_signal: Option<Instant> = None;
// wait for CTRL+C
while let Ok(_) = tokio::signal::ctrl_c().await {
// exit if a CTRL+C is inputted twice within 1 second
if let Some(time_since_last_signal) = last_signal.map(|i| i.elapsed()) {
if time_since_last_signal < Duration::from_secs(1) {
return;
}
}
last_signal = Some(Instant::now());
// capture a dump, and print each trace
println!("{:-<80}", "");
if let Ok(dump) = timeout(Duration::from_secs(2), handle.dump()).await {
for (i, task) in dump.tasks().iter().enumerate() {
let trace = task.trace();
println!("TASK {i}:");
println!("{trace}\n");
}
} else {
println!("Task dumping timed out. Use a native debugger (like gdb) to debug the deadlock.");
}
println!("{:-<80}", "");
println!("Input CTRL+C twice within 1 second to exit.");
}
}
println!("This program has a deadlock.");
println!("Input CTRL+C to print a task dump.");
println!("Input CTRL+C twice within 1 second to exit.");
// oops! this barrier waits for one more task than will ever come.
let barrier = Arc::new(Barrier::new(3));
let task_1 = tokio::spawn(a(barrier.clone()));
let task_2 = tokio::spawn(a(barrier));
tokio::select!(
_ = dump_or_quit() => {},
_ = task_1 => {},
_ = task_2 => {},
);
Ok(())
}
#[cfg(not(all(
tokio_unstable,
tokio_taskdump,
target_os = "linux",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
)))]
fn main() {
println!("task dumps are not available")
}
+5 -2
View File
@@ -180,8 +180,11 @@ impl Decoder for Http {
headers[i] = Some((k, v));
}
let method = http::Method::try_from(r.method.unwrap())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
(
toslice(r.method.unwrap().as_bytes()),
method,
toslice(r.path.unwrap().as_bytes()),
r.version.unwrap(),
amt,
@@ -195,7 +198,7 @@ impl Decoder for Http {
}
let data = src.split_to(amt).freeze();
let mut ret = Request::builder();
ret = ret.method(&data[method.0..method.1]);
ret = ret.method(method);
let s = data.slice(path.0..path.1);
let s = unsafe { String::from_utf8_unchecked(Vec::from(s.as_ref())) };
ret = ret.uri(s);
+1 -1
View File
@@ -2,7 +2,7 @@
name = "stress-test"
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
edition = "2018"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -2,7 +2,7 @@
name = "tests-build"
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
edition = "2018"
edition = "2021"
publish = false
[features]
@@ -36,13 +36,10 @@ async fn test_worker_threads_not_int() {}
async fn test_worker_threads_and_current_thread() {}
#[tokio::test(crate = 456)]
async fn test_crate_not_ident_int() {}
async fn test_crate_not_path_int() {}
#[tokio::test(crate = "456")]
async fn test_crate_not_ident_invalid() {}
#[tokio::test(crate = "abc::edf")]
async fn test_crate_not_ident_path() {}
async fn test_crate_not_path_invalid() {}
#[tokio::test]
#[test]
@@ -64,34 +64,28 @@ error: The `worker_threads` option requires the `multi_thread` runtime flavor. U
35 | #[tokio::test(flavor = "current_thread", worker_threads = 4)]
| ^
error: Failed to parse value of `crate` as ident.
error: Failed to parse value of `crate` as path.
--> $DIR/macros_invalid_input.rs:38:23
|
38 | #[tokio::test(crate = 456)]
| ^^^
error: Failed to parse value of `crate` as ident: "456"
error: Failed to parse value of `crate` as path: "456"
--> $DIR/macros_invalid_input.rs:41:23
|
41 | #[tokio::test(crate = "456")]
| ^^^^^
error: Failed to parse value of `crate` as ident: "abc::edf"
--> $DIR/macros_invalid_input.rs:44:23
|
44 | #[tokio::test(crate = "abc::edf")]
| ^^^^^^^^^^
error: second test attribute is supplied
--> $DIR/macros_invalid_input.rs:48:1
--> $DIR/macros_invalid_input.rs:45:1
|
48 | #[test]
45 | #[test]
| ^^^^^^^
error: duplicated attribute
--> $DIR/macros_invalid_input.rs:48:1
--> $DIR/macros_invalid_input.rs:45:1
|
48 | #[test]
45 | #[test]
| ^^^^^^^
|
note: the lint level is defined here
@@ -1,33 +1,33 @@
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:5:5
--> tests/fail/macros_type_mismatch.rs:5:5
|
4 | async fn missing_semicolon_or_return_type() {
| - help: a return type might be missing here: `-> _`
5 | Ok(())
| ^^^^^^ expected `()`, found enum `Result`
| ^^^^^^ expected `()`, found `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:10:5
--> tests/fail/macros_type_mismatch.rs:10:5
|
9 | async fn missing_return_type() {
| - help: a return type might be missing here: `-> _`
10 | return Ok(());
| ^^^^^^^^^^^^^^ expected `()`, found enum `Result`
| ^^^^^^^^^^^^^^ expected `()`, found `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:23:5
--> tests/fail/macros_type_mismatch.rs:23:5
|
14 | async fn extra_semicolon() -> Result<(), ()> {
| -------------- expected `Result<(), ()>` because of return type
...
23 | Ok(());
| ^^^^^^^ expected enum `Result`, found `()`
| ^^^^^^^ expected `Result<(), ()>`, found `()`
|
= note: expected enum `Result<(), ()>`
found unit type `()`
@@ -38,7 +38,7 @@ help: try adding an expression at the end of the block
|
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:32:5
--> tests/fail/macros_type_mismatch.rs:32:5
|
30 | async fn issue_4635() {
| - help: try adding a return type: `-> i32`
+1 -1
View File
@@ -2,7 +2,7 @@
name = "tests-integration"
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
edition = "2018"
edition = "2021"
publish = false
[[bin]]
-1
View File
@@ -7,7 +7,6 @@ use tokio::process::{Child, Command};
use tokio_test::assert_ok;
use futures::future::{self, FutureExt};
use std::convert::TryInto;
use std::env;
use std::io;
use std::process::{ExitStatus, Stdio};
+23
View File
@@ -1,3 +1,26 @@
# 2.1.0 (April 25th, 2023)
- macros: fix typo in `#[tokio::test]` docs ([#5636])
- macros: make entrypoints more efficient ([#5621])
[#5621]: https://github.com/tokio-rs/tokio/pull/5621
[#5636]: https://github.com/tokio-rs/tokio/pull/5636
# 2.0.0 (March 24th, 2023)
This major release updates the dependency on the syn crate to 2.0.0, and
increases the MSRV to 1.56.
As part of this release, we are adopting a policy of depending on a specific minor
release of tokio-macros. This prevents Tokio from being able to pull in many different
versions of tokio-macros.
- macros: update `syn` ([#5572])
- macros: accept path as crate rename ([#5557])
[#5572]: https://github.com/tokio-rs/tokio/pull/5572
[#5557]: https://github.com/tokio-rs/tokio/pull/5557
# 1.8.2 (November 30th, 2022)
- fix a regression introduced in 1.8.1 ([#5244])
+4 -4
View File
@@ -4,9 +4,9 @@ name = "tokio-macros"
# - Remove path dependencies
# - Update CHANGELOG.md.
# - Create "tokio-macros-1.x.y" git tag.
version = "1.8.2"
edition = "2018"
rust-version = "1.49"
version = "2.1.0"
edition = "2021"
rust-version = "1.56"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -24,7 +24,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0.7"
quote = "1"
syn = { version = "1.0.56", features = ["full"] }
syn = { version = "2.0", features = ["full"] }
[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
+171 -74
View File
@@ -1,10 +1,10 @@
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use proc_macro2::{Span, TokenStream, TokenTree};
use quote::{quote, quote_spanned, ToTokens};
use syn::parse::Parser;
use syn::parse::{Parse, ParseStream, Parser};
use syn::{braced, Attribute, Ident, Path, Signature, Visibility};
// syn::AttributeArgs does not implement syn::Parse
type AttributeArgs = syn::punctuated::Punctuated<syn::NestedMeta, syn::Token![,]>;
type AttributeArgs = syn::punctuated::Punctuated<syn::Meta, syn::Token![,]>;
#[derive(Clone, Copy, PartialEq)]
enum RuntimeFlavor {
@@ -29,7 +29,7 @@ struct FinalConfig {
flavor: RuntimeFlavor,
worker_threads: Option<usize>,
start_paused: Option<bool>,
crate_name: Option<String>,
crate_name: Option<Path>,
}
/// Config used in case of the attribute not being able to build a valid config
@@ -47,7 +47,7 @@ struct Configuration {
worker_threads: Option<(usize, Span)>,
start_paused: Option<(bool, Span)>,
is_test: bool,
crate_name: Option<String>,
crate_name: Option<Path>,
}
impl Configuration {
@@ -112,8 +112,8 @@ impl Configuration {
if self.crate_name.is_some() {
return Err(syn::Error::new(span, "`crate` set multiple times."));
}
let name_ident = parse_ident(name, span, "crate")?;
self.crate_name = Some(name_ident.to_string());
let name_path = parse_path(name, span, "crate")?;
self.crate_name = Some(name_path);
Ok(())
}
@@ -199,23 +199,22 @@ fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result<String, syn::E
}
}
fn parse_ident(lit: syn::Lit, span: Span, field: &str) -> Result<Ident, syn::Error> {
fn parse_path(lit: syn::Lit, span: Span, field: &str) -> Result<Path, syn::Error> {
match lit {
syn::Lit::Str(s) => {
let err = syn::Error::new(
span,
format!(
"Failed to parse value of `{}` as ident: \"{}\"",
"Failed to parse value of `{}` as path: \"{}\"",
field,
s.value()
),
);
let path = s.parse::<syn::Path>().map_err(|_| err.clone())?;
path.get_ident().cloned().ok_or(err)
s.parse::<syn::Path>().map_err(|_| err.clone())
}
_ => Err(syn::Error::new(
span,
format!("Failed to parse value of `{}` as ident.", field),
format!("Failed to parse value of `{}` as path.", field),
)),
}
}
@@ -231,7 +230,7 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Erro
}
fn build_config(
input: syn::ItemFn,
input: &ItemFn,
args: AttributeArgs,
is_test: bool,
rt_multi_thread: bool,
@@ -246,7 +245,7 @@ fn build_config(
for arg in args {
match arg {
syn::NestedMeta::Meta(syn::Meta::NameValue(namevalue)) => {
syn::Meta::NameValue(namevalue) => {
let ident = namevalue
.path
.get_ident()
@@ -255,34 +254,26 @@ fn build_config(
})?
.to_string()
.to_lowercase();
let lit = match &namevalue.value {
syn::Expr::Lit(syn::ExprLit { lit, .. }) => lit,
expr => return Err(syn::Error::new_spanned(expr, "Must be a literal")),
};
match ident.as_str() {
"worker_threads" => {
config.set_worker_threads(
namevalue.lit.clone(),
syn::spanned::Spanned::span(&namevalue.lit),
)?;
config.set_worker_threads(lit.clone(), syn::spanned::Spanned::span(lit))?;
}
"flavor" => {
config.set_flavor(
namevalue.lit.clone(),
syn::spanned::Spanned::span(&namevalue.lit),
)?;
config.set_flavor(lit.clone(), syn::spanned::Spanned::span(lit))?;
}
"start_paused" => {
config.set_start_paused(
namevalue.lit.clone(),
syn::spanned::Spanned::span(&namevalue.lit),
)?;
config.set_start_paused(lit.clone(), syn::spanned::Spanned::span(lit))?;
}
"core_threads" => {
let msg = "Attribute `core_threads` is renamed to `worker_threads`";
return Err(syn::Error::new_spanned(namevalue, msg));
}
"crate" => {
config.set_crate_name(
namevalue.lit.clone(),
syn::spanned::Spanned::span(&namevalue.lit),
)?;
config.set_crate_name(lit.clone(), syn::spanned::Spanned::span(lit))?;
}
name => {
let msg = format!(
@@ -293,7 +284,7 @@ fn build_config(
}
}
}
syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
syn::Meta::Path(path) => {
let name = path
.get_ident()
.ok_or_else(|| syn::Error::new_spanned(&path, "Must have specified ident"))?
@@ -333,18 +324,13 @@ fn build_config(
config.build()
}
fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> TokenStream {
fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream {
input.sig.asyncness = None;
// If type mismatch occurs, the current rustc points to the last statement.
let (last_stmt_start_span, last_stmt_end_span) = {
let mut last_stmt = input
.block
.stmts
.last()
.map(ToTokens::into_token_stream)
.unwrap_or_default()
.into_iter();
let mut last_stmt = input.stmts.last().cloned().unwrap_or_default().into_iter();
// `Span` on stable Rust has a limitation that only points to the first
// token, not the whole tokens. We can work around this limitation by
// using the first/last span of the tokens like
@@ -354,23 +340,24 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
(start, end)
};
let crate_name = config.crate_name.as_deref().unwrap_or("tokio");
let crate_ident = Ident::new(crate_name, last_stmt_start_span);
let crate_path = config
.crate_name
.map(ToTokens::into_token_stream)
.unwrap_or_else(|| Ident::new("tokio", last_stmt_start_span).into_token_stream());
let mut rt = match config.flavor {
RuntimeFlavor::CurrentThread => quote_spanned! {last_stmt_start_span=>
#crate_ident::runtime::Builder::new_current_thread()
#crate_path::runtime::Builder::new_current_thread()
},
RuntimeFlavor::Threaded => quote_spanned! {last_stmt_start_span=>
#crate_ident::runtime::Builder::new_multi_thread()
#crate_path::runtime::Builder::new_multi_thread()
},
};
if let Some(v) = config.worker_threads {
rt = quote! { #rt.worker_threads(#v) };
rt = quote_spanned! {last_stmt_start_span=> #rt.worker_threads(#v) };
}
if let Some(v) = config.start_paused {
rt = quote! { #rt.start_paused(#v) };
rt = quote_spanned! {last_stmt_start_span=> #rt.start_paused(#v) };
}
let header = if is_test {
@@ -381,10 +368,8 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
quote! {}
};
let body = &input.block;
let brace_token = input.block.brace_token;
let body_ident = quote! { body };
let block_expr = quote_spanned! {last_stmt_end_span=>
let last_block = quote_spanned! {last_stmt_end_span=>
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
{
return #rt
@@ -395,6 +380,8 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
}
};
let body = input.body();
// For test functions pin the body to the stack and use `Pin<&mut dyn
// Future>` to reduce the amount of `Runtime::block_on` (and related
// functions) copies we generate during compilation due to the generic
@@ -414,7 +401,7 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
};
quote! {
let body = async #body;
#crate_ident::pin!(body);
#crate_path::pin!(body);
let body: ::std::pin::Pin<&mut dyn ::std::future::Future<Output = #output_type>> = body;
}
} else {
@@ -423,25 +410,11 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
}
};
input.block = syn::parse2(quote! {
{
#body
#block_expr
}
})
.expect("Parsing failure");
input.block.brace_token = brace_token;
let result = quote! {
#header
#input
};
result.into()
input.into_tokens(header, body, last_block)
}
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
tokens.extend(TokenStream::from(error.into_compile_error()));
tokens.extend(error.into_compile_error());
tokens
}
@@ -450,7 +423,7 @@ pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
// If any of the steps for this macro fail, we still want to expand to an item that is as close
// to the expected output as possible. This helps out IDEs such that completions and other
// related features keep working.
let input: syn::ItemFn = match syn::parse(item.clone()) {
let input: ItemFn = match syn::parse2(item.clone()) {
Ok(it) => it,
Err(e) => return token_stream_with_error(item, e),
};
@@ -460,8 +433,8 @@ pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
Err(syn::Error::new_spanned(&input.sig.ident, msg))
} else {
AttributeArgs::parse_terminated
.parse(args)
.and_then(|args| build_config(input.clone(), args, false, rt_multi_thread))
.parse2(args)
.and_then(|args| build_config(&input, args, false, rt_multi_thread))
};
match config {
@@ -474,17 +447,17 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
// If any of the steps for this macro fail, we still want to expand to an item that is as close
// to the expected output as possible. This helps out IDEs such that completions and other
// related features keep working.
let input: syn::ItemFn = match syn::parse(item.clone()) {
let input: ItemFn = match syn::parse2(item.clone()) {
Ok(it) => it,
Err(e) => return token_stream_with_error(item, e),
};
let config = if let Some(attr) = input.attrs.iter().find(|attr| attr.path.is_ident("test")) {
let config = if let Some(attr) = input.attrs().find(|attr| attr.meta.path().is_ident("test")) {
let msg = "second test attribute is supplied";
Err(syn::Error::new_spanned(attr, msg))
} else {
AttributeArgs::parse_terminated
.parse(args)
.and_then(|args| build_config(input.clone(), args, true, rt_multi_thread))
.parse2(args)
.and_then(|args| build_config(&input, args, true, rt_multi_thread))
};
match config {
@@ -492,3 +465,127 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
Err(e) => token_stream_with_error(parse_knobs(input, true, DEFAULT_ERROR_CONFIG), e),
}
}
struct ItemFn {
outer_attrs: Vec<Attribute>,
vis: Visibility,
sig: Signature,
brace_token: syn::token::Brace,
inner_attrs: Vec<Attribute>,
stmts: Vec<proc_macro2::TokenStream>,
}
impl ItemFn {
/// Access all attributes of the function item.
fn attrs(&self) -> impl Iterator<Item = &Attribute> {
self.outer_attrs.iter().chain(self.inner_attrs.iter())
}
/// Get the body of the function item in a manner so that it can be
/// conveniently used with the `quote!` macro.
fn body(&self) -> Body<'_> {
Body {
brace_token: self.brace_token,
stmts: &self.stmts,
}
}
/// Convert our local function item into a token stream.
fn into_tokens(
self,
header: proc_macro2::TokenStream,
body: proc_macro2::TokenStream,
last_block: proc_macro2::TokenStream,
) -> TokenStream {
let mut tokens = proc_macro2::TokenStream::new();
header.to_tokens(&mut tokens);
// Outer attributes are simply streamed as-is.
for attr in self.outer_attrs {
attr.to_tokens(&mut tokens);
}
// Inner attributes require extra care, since they're not supported on
// blocks (which is what we're expanded into) we instead lift them
// outside of the function. This matches the behaviour of `syn`.
for mut attr in self.inner_attrs {
attr.style = syn::AttrStyle::Outer;
attr.to_tokens(&mut tokens);
}
self.vis.to_tokens(&mut tokens);
self.sig.to_tokens(&mut tokens);
self.brace_token.surround(&mut tokens, |tokens| {
body.to_tokens(tokens);
last_block.to_tokens(tokens);
});
tokens
}
}
impl Parse for ItemFn {
#[inline]
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
// This parse implementation has been largely lifted from `syn`, with
// the exception of:
// * We don't have access to the plumbing necessary to parse inner
// attributes in-place.
// * We do our own statements parsing to avoid recursively parsing
// entire statements and only look for the parts we're interested in.
let outer_attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let sig: Signature = input.parse()?;
let content;
let brace_token = braced!(content in input);
let inner_attrs = Attribute::parse_inner(&content)?;
let mut buf = proc_macro2::TokenStream::new();
let mut stmts = Vec::new();
while !content.is_empty() {
if let Some(semi) = content.parse::<Option<syn::Token![;]>>()? {
semi.to_tokens(&mut buf);
stmts.push(buf);
buf = proc_macro2::TokenStream::new();
continue;
}
// Parse a single token tree and extend our current buffer with it.
// This avoids parsing the entire content of the sub-tree.
buf.extend([content.parse::<TokenTree>()?]);
}
if !buf.is_empty() {
stmts.push(buf);
}
Ok(Self {
outer_attrs,
vis,
sig,
brace_token,
inner_attrs,
stmts,
})
}
}
struct Body<'a> {
brace_token: syn::token::Brace,
// Statements, with terminating `;`.
stmts: &'a [TokenStream],
}
impl ToTokens for Body<'_> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
self.brace_token.surround(tokens, |tokens| {
for stmt in self.stmts {
stmt.to_tokens(tokens);
}
})
}
}
+6 -7
View File
@@ -204,12 +204,12 @@ use proc_macro::TokenStream;
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args, item, true)
entry::main(args.into(), item.into(), true).into()
}
/// Marks async function to be executed by selected runtime. This macro helps set up a `Runtime`
/// without requiring the user to use [Runtime](../tokio/runtime/struct.Runtime.html) or
/// [Builder](../tokio/runtime/struct.builder.html) directly.
/// [Builder](../tokio/runtime/struct.Builder.html) directly.
///
/// ## Function arguments:
///
@@ -269,7 +269,7 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args, item, false)
entry::main(args.into(), item.into(), false).into()
}
/// Marks async function to be executed by runtime, suitable to test environment.
@@ -295,8 +295,7 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// ```
///
/// The `worker_threads` option configures the number of worker threads, and
/// defaults to the number of cpus on the system. This is the default
/// flavor.
/// defaults to the number of cpus on the system.
///
/// Note: The multi-threaded runtime requires the `rt-multi-thread` feature
/// flag.
@@ -427,7 +426,7 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// ```
#[proc_macro_attribute]
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, true)
entry::test(args.into(), item.into(), true).into()
}
/// Marks async function to be executed by runtime, suitable to test environment
@@ -442,7 +441,7 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
/// ```
#[proc_macro_attribute]
pub fn test_rt(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, false)
entry::test(args.into(), item.into(), false).into()
}
/// Always fails with the error message below.
+3 -4
View File
@@ -1,7 +1,7 @@
use proc_macro::{TokenStream, TokenTree};
use proc_macro2::Span;
use quote::quote;
use syn::Ident;
use syn::{parse::Parser, Ident};
pub(crate) fn declare_output_enum(input: TokenStream) -> TokenStream {
// passed in is: `(_ _ _)` with one `_` per branch
@@ -46,7 +46,7 @@ pub(crate) fn clean_pattern_macro(input: TokenStream) -> TokenStream {
// If this isn't a pattern, we return the token stream as-is. The select!
// macro is using it in a location requiring a pattern, so an error will be
// emitted there.
let mut input: syn::Pat = match syn::parse(input.clone()) {
let mut input: syn::Pat = match syn::Pat::parse_single.parse(input.clone()) {
Ok(it) => it,
Err(_) => return input,
};
@@ -58,7 +58,6 @@ pub(crate) fn clean_pattern_macro(input: TokenStream) -> TokenStream {
// Removes any occurrences of ref or mut in the provided pattern.
fn clean_pattern(pat: &mut syn::Pat) {
match pat {
syn::Pat::Box(_box) => {}
syn::Pat::Lit(_literal) => {}
syn::Pat::Macro(_macro) => {}
syn::Pat::Path(_path) => {}
@@ -94,7 +93,7 @@ fn clean_pattern(pat: &mut syn::Pat) {
}
}
syn::Pat::TupleStruct(tuple) => {
for elem in tuple.pat.elems.iter_mut() {
for elem in tuple.elems.iter_mut() {
clean_pattern(elem);
}
}
+19
View File
@@ -1,3 +1,22 @@
# 0.1.14 (April 26th, 2023)
This bugfix release bumps the minimum version of Tokio to 1.15, which is
necessary for `timeout_repeating` to compile. ([#5657])
[#5657]: https://github.com/tokio-rs/tokio/pull/5657
# 0.1.13 (April 25th, 2023)
This release bumps the MSRV of tokio-stream to 1.56.
- stream: add "full" feature flag ([#5639])
- stream: add `StreamExt::timeout_repeating` ([#5577])
- stream: add `StreamNotifyClose` ([#4851])
[#4851]: https://github.com/tokio-rs/tokio/pull/4851
[#5577]: https://github.com/tokio-rs/tokio/pull/5577
[#5639]: https://github.com/tokio-rs/tokio/pull/5639
# 0.1.12 (January 20, 2023)
- time: remove `Unpin` bound on `Throttle` methods ([#5105])
+15 -5
View File
@@ -4,9 +4,9 @@ name = "tokio-stream"
# - Remove path dependencies
# - Update CHANGELOG.md.
# - Create "tokio-stream-0.1.x" git tag.
version = "0.1.12"
edition = "2018"
rust-version = "1.49"
version = "0.1.14"
edition = "2021"
rust-version = "1.56"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -18,6 +18,16 @@ categories = ["asynchronous"]
[features]
default = ["time"]
full = [
"time",
"net",
"io-util",
"fs",
"sync",
"signal"
]
time = ["tokio/time"]
net = ["tokio/net"]
io-util = ["tokio/io-util"]
@@ -27,8 +37,8 @@ signal = ["tokio/signal"]
[dependencies]
futures-core = { version = "0.3.0" }
pin-project-lite = "0.2.0"
tokio = { version = "1.8.0", path = "../tokio", features = ["sync"] }
pin-project-lite = "0.2.7"
tokio = { version = "1.15.0", path = "../tokio", features = ["sync"] }
tokio-util = { version = "0.7.0", path = "../tokio-util", optional = true }
[dev-dependencies]
+1 -1
View File
@@ -2,7 +2,7 @@
name = "tokio-stream-fuzz"
version = "0.0.0"
publish = false
edition = "2018"
edition = "2021"
[package.metadata]
cargo-fuzz = true
@@ -3,17 +3,8 @@
use libfuzzer_sys::fuzz_target;
use std::pin::Pin;
use tokio_stream::{self as stream, pending, Stream, StreamExt, StreamMap};
use tokio_test::{assert_ok, assert_pending, assert_ready, task};
macro_rules! assert_ready_some {
($($t:tt)*) => {
match assert_ready!($($t)*) {
Some(v) => v,
None => panic!("expected `Some`, got `None`"),
}
};
}
use tokio_stream::{self as stream, Stream, StreamMap};
use tokio_test::{assert_pending, assert_ready, task};
macro_rules! assert_ready_none {
($($t:tt)*) => {
@@ -28,7 +19,7 @@ fn pin_box<T: Stream<Item = U> + 'static, U>(s: T) -> Pin<Box<dyn Stream<Item =
Box::pin(s)
}
fuzz_target!(|data: &[u8]| {
fuzz_target!(|data: [bool; 64]| {
use std::task::{Context, Poll};
struct DidPoll<T> {
@@ -45,11 +36,12 @@ fuzz_target!(|data: &[u8]| {
}
}
for _ in 0..10 {
// Try the test with each possible length.
for len in 0..data.len() {
let mut map = task::spawn(StreamMap::new());
let mut expect = 0;
for (i, is_empty) in data.iter().map(|x| *x != 0).enumerate() {
for (i, is_empty) in data[..len].iter().copied().enumerate() {
let inner = if is_empty {
pin_box(stream::empty::<()>())
} else {
+9 -6
View File
@@ -63,12 +63,12 @@
//! [`tokio-util`] provides the [`StreamReader`] and [`ReaderStream`]
//! types when the io feature is enabled.
//!
//! [`tokio-util`]: https://docs.rs/tokio-util/0.4/tokio_util/codec/index.html
//! [`tokio::io`]: https://docs.rs/tokio/1.0/tokio/io/index.html
//! [`AsyncRead`]: https://docs.rs/tokio/1.0/tokio/io/trait.AsyncRead.html
//! [`AsyncWrite`]: https://docs.rs/tokio/1.0/tokio/io/trait.AsyncWrite.html
//! [`ReaderStream`]: https://docs.rs/tokio-util/0.4/tokio_util/io/struct.ReaderStream.html
//! [`StreamReader`]: https://docs.rs/tokio-util/0.4/tokio_util/io/struct.StreamReader.html
//! [`tokio-util`]: https://docs.rs/tokio-util/latest/tokio_util/codec/index.html
//! [`tokio::io`]: https://docs.rs/tokio/latest/tokio/io/index.html
//! [`AsyncRead`]: https://docs.rs/tokio/latest/tokio/io/trait.AsyncRead.html
//! [`AsyncWrite`]: https://docs.rs/tokio/latest/tokio/io/trait.AsyncWrite.html
//! [`ReaderStream`]: https://docs.rs/tokio-util/latest/tokio_util/io/struct.ReaderStream.html
//! [`StreamReader`]: https://docs.rs/tokio-util/latest/tokio_util/io/struct.StreamReader.html
#[macro_use]
mod macros;
@@ -96,5 +96,8 @@ pub use pending::{pending, Pending};
mod stream_map;
pub use stream_map::StreamMap;
mod stream_close;
pub use stream_close::StreamNotifyClose;
#[doc(no_inline)]
pub use futures_core::Stream;
+93
View File
@@ -0,0 +1,93 @@
use crate::Stream;
use pin_project_lite::pin_project;
use std::pin::Pin;
use std::task::{Context, Poll};
pin_project! {
/// A `Stream` that wraps the values in an `Option`.
///
/// Whenever the wrapped stream yields an item, this stream yields that item
/// wrapped in `Some`. When the inner stream ends, then this stream first
/// yields a `None` item, and then this stream will also end.
///
/// # Example
///
/// Using `StreamNotifyClose` to handle closed streams with `StreamMap`.
///
/// ```
/// use tokio_stream::{StreamExt, StreamMap, StreamNotifyClose};
///
/// #[tokio::main]
/// async fn main() {
/// let mut map = StreamMap::new();
/// let stream = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// let stream2 = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// map.insert(0, stream);
/// map.insert(1, stream2);
/// while let Some((key, val)) = map.next().await {
/// match val {
/// Some(val) => println!("got {val:?} from stream {key:?}"),
/// None => println!("stream {key:?} closed"),
/// }
/// }
/// }
/// ```
#[must_use = "streams do nothing unless polled"]
pub struct StreamNotifyClose<S> {
#[pin]
inner: Option<S>,
}
}
impl<S> StreamNotifyClose<S> {
/// Create a new `StreamNotifyClose`.
pub fn new(stream: S) -> Self {
Self {
inner: Some(stream),
}
}
/// Get back the inner `Stream`.
///
/// Returns `None` if the stream has reached its end.
pub fn into_inner(self) -> Option<S> {
self.inner
}
}
impl<S> Stream for StreamNotifyClose<S>
where
S: Stream,
{
type Item = Option<S::Item>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
// We can't invoke poll_next after it ended, so we unset the inner stream as a marker.
match self
.as_mut()
.project()
.inner
.as_pin_mut()
.map(|stream| S::poll_next(stream, cx))
{
Some(Poll::Ready(Some(item))) => Poll::Ready(Some(Some(item))),
Some(Poll::Ready(None)) => {
self.project().inner.set(None);
Poll::Ready(Some(None))
}
Some(Poll::Pending) => Poll::Pending,
None => Poll::Ready(None),
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if let Some(inner) = &self.inner {
// We always return +1 because when there's stream there's atleast one more item.
let (l, u) = inner.size_hint();
(l.saturating_add(1), u.and_then(|u| u.checked_add(1)))
} else {
(0, Some(0))
}
}
}
+114 -2
View File
@@ -57,8 +57,10 @@ use try_next::TryNext;
cfg_time! {
pub(crate) mod timeout;
pub(crate) mod timeout_repeating;
use timeout::Timeout;
use tokio::time::Duration;
use timeout_repeating::TimeoutRepeating;
use tokio::time::{Duration, Interval};
mod throttle;
use throttle::{throttle, Throttle};
mod chunks_timeout;
@@ -924,7 +926,9 @@ pub trait StreamExt: Stream {
/// If the wrapped stream yields a value before the deadline is reached, the
/// value is returned. Otherwise, an error is returned. The caller may decide
/// to continue consuming the stream and will eventually get the next source
/// stream value once it becomes available.
/// stream value once it becomes available. See
/// [`timeout_repeating`](StreamExt::timeout_repeating) for an alternative
/// where the timeouts will repeat.
///
/// # Notes
///
@@ -971,6 +975,25 @@ pub trait StreamExt: Stream {
/// assert_eq!(int_stream.try_next().await, Ok(None));
/// # }
/// ```
///
/// Once a timeout error is received, no further events will be received
/// unless the wrapped stream yields a value (timeouts do not repeat).
///
/// ```
/// # #[tokio::main(flavor = "current_thread", start_paused = true)]
/// # async fn main() {
/// use tokio_stream::{StreamExt, wrappers::IntervalStream};
/// use std::time::Duration;
/// let interval_stream = IntervalStream::new(tokio::time::interval(Duration::from_millis(100)));
/// let timeout_stream = interval_stream.timeout(Duration::from_millis(10));
/// tokio::pin!(timeout_stream);
///
/// // Only one timeout will be received between values in the source stream.
/// assert!(timeout_stream.try_next().await.is_ok());
/// assert!(timeout_stream.try_next().await.is_err(), "expected one timeout");
/// assert!(timeout_stream.try_next().await.is_ok(), "expected no more timeouts");
/// # }
/// ```
#[cfg(all(feature = "time"))]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
fn timeout(self, duration: Duration) -> Timeout<Self>
@@ -980,6 +1003,95 @@ pub trait StreamExt: Stream {
Timeout::new(self, duration)
}
/// Applies a per-item timeout to the passed stream.
///
/// `timeout_repeating()` takes an [`Interval`](tokio::time::Interval) that
/// controls the time each element of the stream has to complete before
/// timing out.
///
/// If the wrapped stream yields a value before the deadline is reached, the
/// value is returned. Otherwise, an error is returned. The caller may decide
/// to continue consuming the stream and will eventually get the next source
/// stream value once it becomes available. Unlike `timeout()`, if no value
/// becomes available before the deadline is reached, additional errors are
/// returned at the specified interval. See [`timeout`](StreamExt::timeout)
/// for an alternative where the timeouts do not repeat.
///
/// # Notes
///
/// This function consumes the stream passed into it and returns a
/// wrapped version of it.
///
/// Polling the returned stream will continue to poll the inner stream even
/// if one or more items time out.
///
/// # Examples
///
/// Suppose we have a stream `int_stream` that yields 3 numbers (1, 2, 3):
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
/// use std::time::Duration;
/// # let int_stream = stream::iter(1..=3);
///
/// let int_stream = int_stream.timeout_repeating(tokio::time::interval(Duration::from_secs(1)));
/// tokio::pin!(int_stream);
///
/// // When no items time out, we get the 3 elements in succession:
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(2)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(3)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
///
/// // If the second item times out, we get an error and continue polling the stream:
/// # let mut int_stream = stream::iter(vec![Ok(1), Err(()), Ok(2), Ok(3)]);
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert!(int_stream.try_next().await.is_err());
/// assert_eq!(int_stream.try_next().await, Ok(Some(2)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(3)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
///
/// // If we want to stop consuming the source stream the first time an
/// // element times out, we can use the `take_while` operator:
/// # let int_stream = stream::iter(vec![Ok(1), Err(()), Ok(2), Ok(3)]);
/// let mut int_stream = int_stream.take_while(Result::is_ok);
///
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
/// # }
/// ```
///
/// Timeout errors will be continuously produced at the specified interval
/// until the wrapped stream yields a value.
///
/// ```
/// # #[tokio::main(flavor = "current_thread", start_paused = true)]
/// # async fn main() {
/// use tokio_stream::{StreamExt, wrappers::IntervalStream};
/// use std::time::Duration;
/// let interval_stream = IntervalStream::new(tokio::time::interval(Duration::from_millis(23)));
/// let timeout_stream = interval_stream.timeout_repeating(tokio::time::interval(Duration::from_millis(9)));
/// tokio::pin!(timeout_stream);
///
/// // Multiple timeouts will be received between values in the source stream.
/// assert!(timeout_stream.try_next().await.is_ok());
/// assert!(timeout_stream.try_next().await.is_err(), "expected one timeout");
/// assert!(timeout_stream.try_next().await.is_err(), "expected a second timeout");
/// // Will eventually receive another value from the source stream...
/// assert!(timeout_stream.try_next().await.is_ok(), "expected non-timeout");
/// # }
/// ```
#[cfg(all(feature = "time"))]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
fn timeout_repeating(self, interval: Interval) -> TimeoutRepeating<Self>
where
Self: Sized,
{
TimeoutRepeating::new(self, interval)
}
/// Slows down a stream by enforcing a delay between items.
///
/// The underlying timer behind this utility has a granularity of one millisecond.
+1 -1
View File
@@ -23,7 +23,7 @@ pin_project! {
}
}
/// Error returned by `Timeout`.
/// Error returned by `Timeout` and `TimeoutRepeating`.
#[derive(Debug, PartialEq, Eq)]
pub struct Elapsed(());
@@ -0,0 +1,56 @@
use crate::stream_ext::Fuse;
use crate::{Elapsed, Stream};
use tokio::time::Interval;
use core::pin::Pin;
use core::task::{Context, Poll};
use pin_project_lite::pin_project;
pin_project! {
/// Stream returned by the [`timeout_repeating`](super::StreamExt::timeout_repeating) method.
#[must_use = "streams do nothing unless polled"]
#[derive(Debug)]
pub struct TimeoutRepeating<S> {
#[pin]
stream: Fuse<S>,
#[pin]
interval: Interval,
}
}
impl<S: Stream> TimeoutRepeating<S> {
pub(super) fn new(stream: S, interval: Interval) -> Self {
TimeoutRepeating {
stream: Fuse::new(stream),
interval,
}
}
}
impl<S: Stream> Stream for TimeoutRepeating<S> {
type Item = Result<S::Item, Elapsed>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut me = self.project();
match me.stream.poll_next(cx) {
Poll::Ready(v) => {
if v.is_some() {
me.interval.reset();
}
return Poll::Ready(v.map(Ok));
}
Poll::Pending => {}
};
ready!(me.interval.poll_tick(cx));
Poll::Ready(Some(Err(Elapsed::new())))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (lower, _) = self.stream.size_hint();
// The timeout stream may insert an error an infinite number of times.
(lower, None)
}
}
+31 -1
View File
@@ -42,10 +42,18 @@ use std::task::{Context, Poll};
/// to be merged, it may be advisable to use tasks sending values on a shared
/// [`mpsc`] channel.
///
/// # Notes
///
/// `StreamMap` removes finished streams automatically, without alerting the user.
/// In some scenarios, the caller would want to know on closed streams.
/// To do this, use [`StreamNotifyClose`] as a wrapper to your stream.
/// It will return None when the stream is closed.
///
/// [`StreamExt::merge`]: crate::StreamExt::merge
/// [`mpsc`]: https://docs.rs/tokio/1.0/tokio/sync/mpsc/index.html
/// [`pin!`]: https://docs.rs/tokio/1.0/tokio/macro.pin.html
/// [`Box::pin`]: std::boxed::Box::pin
/// [`StreamNotifyClose`]: crate::StreamNotifyClose
///
/// # Examples
///
@@ -170,6 +178,28 @@ use std::task::{Context, Poll};
/// }
/// }
/// ```
///
/// Using `StreamNotifyClose` to handle closed streams with `StreamMap`.
///
/// ```
/// use tokio_stream::{StreamExt, StreamMap, StreamNotifyClose};
///
/// #[tokio::main]
/// async fn main() {
/// let mut map = StreamMap::new();
/// let stream = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// let stream2 = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
/// map.insert(0, stream);
/// map.insert(1, stream2);
/// while let Some((key, val)) = map.next().await {
/// match val {
/// Some(val) => println!("got {val:?} from stream {key:?}"),
/// None => println!("stream {key:?} closed"),
/// }
/// }
/// }
/// ```
#[derive(Debug)]
pub struct StreamMap<K, V> {
/// Streams stored in the map
@@ -568,7 +598,7 @@ where
}
}
impl<K, V> std::iter::FromIterator<(K, V)> for StreamMap<K, V>
impl<K, V> FromIterator<(K, V)> for StreamMap<K, V>
where
K: Hash + Eq,
{
+11
View File
@@ -0,0 +1,11 @@
use tokio_stream::{StreamExt, StreamNotifyClose};
#[tokio::test]
async fn basic_usage() {
let mut stream = StreamNotifyClose::new(tokio_stream::iter(vec![0, 1]));
assert_eq!(stream.next().await, Some(Some(0)));
assert_eq!(stream.next().await, Some(Some(1)));
assert_eq!(stream.next().await, Some(None));
assert_eq!(stream.next().await, None);
}
+2 -2
View File
@@ -5,8 +5,8 @@ name = "tokio-test"
# - Update CHANGELOG.md.
# - Create "tokio-test-0.4.x" git tag.
version = "0.4.2"
edition = "2018"
rust-version = "1.49"
edition = "2021"
rust-version = "1.56"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
+2
View File
@@ -310,6 +310,8 @@ impl Inner {
if now < until {
break;
} else {
self.waiting = None;
}
} else {
self.waiting = Some(Instant::now() + *dur);
+63
View File
@@ -2,6 +2,7 @@
use std::io;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::time::{Duration, Instant};
use tokio_test::io::Builder;
#[tokio::test]
@@ -84,3 +85,65 @@ async fn mock_panics_write_data_left() {
use tokio_test::io::Builder;
Builder::new().write(b"write").build();
}
#[tokio::test(start_paused = true)]
async fn wait() {
const FIRST_WAIT: Duration = Duration::from_secs(1);
let mut mock = Builder::new()
.wait(FIRST_WAIT)
.read(b"hello ")
.read(b"world!")
.build();
let mut buf = [0; 256];
let start = Instant::now(); // record the time the read call takes
//
let n = mock.read(&mut buf).await.expect("read 1");
assert_eq!(&buf[..n], b"hello ");
println!("time elapsed after first read {:?}", start.elapsed());
let n = mock.read(&mut buf).await.expect("read 2");
assert_eq!(&buf[..n], b"world!");
println!("time elapsed after second read {:?}", start.elapsed());
// make sure the .wait() instruction worked
assert!(
start.elapsed() >= FIRST_WAIT,
"consuming the whole mock only took {}ms",
start.elapsed().as_millis()
);
}
#[tokio::test(start_paused = true)]
async fn multiple_wait() {
const FIRST_WAIT: Duration = Duration::from_secs(1);
const SECOND_WAIT: Duration = Duration::from_secs(1);
let mut mock = Builder::new()
.wait(FIRST_WAIT)
.read(b"hello ")
.wait(SECOND_WAIT)
.read(b"world!")
.build();
let mut buf = [0; 256];
let start = Instant::now(); // record the time it takes to consume the mock
let n = mock.read(&mut buf).await.expect("read 1");
assert_eq!(&buf[..n], b"hello ");
println!("time elapsed after first read {:?}", start.elapsed());
let n = mock.read(&mut buf).await.expect("read 2");
assert_eq!(&buf[..n], b"world!");
println!("time elapsed after second read {:?}", start.elapsed());
// make sure the .wait() instruction worked
assert!(
start.elapsed() >= FIRST_WAIT + SECOND_WAIT,
"consuming the whole mock only took {}ms",
start.elapsed().as_millis()
);
}
+27
View File
@@ -1,3 +1,30 @@
# 0.7.8 (April 25th, 2023)
This release bumps the MSRV of tokio-util to 1.56.
### Added
- time: add `DelayQueue::peek` ([#5569])
### Changed
This release contains one performance improvement:
- sync: try to lock the parent first in `CancellationToken` ([#5561])
### Fixed
- time: fix panic in `DelayQueue` ([#5630])
### Documented
- sync: improve `CancellationToken` doc on child tokens ([#5632])
[#5561]: https://github.com/tokio-rs/tokio/pull/5561
[#5569]: https://github.com/tokio-rs/tokio/pull/5569
[#5630]: https://github.com/tokio-rs/tokio/pull/5630
[#5632]: https://github.com/tokio-rs/tokio/pull/5632
# 0.7.7 (February 12, 2023)
This release reverts the removal of the `Encoder` bound on the `FramedParts`
+6 -5
View File
@@ -4,9 +4,9 @@ name = "tokio-util"
# - Remove path dependencies
# - Update CHANGELOG.md.
# - Create "tokio-util-0.7.x" git tag.
version = "0.7.7"
edition = "2018"
rust-version = "1.49"
version = "0.7.8"
edition = "2021"
rust-version = "1.56"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
@@ -34,13 +34,13 @@ rt = ["tokio/rt", "tokio/sync", "futures-util", "hashbrown"]
__docs_rs = ["futures-util"]
[dependencies]
tokio = { version = "1.22.0", path = "../tokio", features = ["sync"] }
tokio = { version = "1.28.0", path = "../tokio", features = ["sync"] }
bytes = "1.0.0"
futures-core = "0.3.0"
futures-sink = "0.3.0"
futures-io = { version = "0.3.0", optional = true }
futures-util = { version = "0.3.0", optional = true }
pin-project-lite = "0.2.0"
pin-project-lite = "0.2.7"
slab = { version = "0.4.4", optional = true } # Backs `DelayQueue`
tracing = { version = "0.1.25", default-features = false, features = ["std"], optional = true }
@@ -56,6 +56,7 @@ async-stream = "0.3.0"
futures = "0.3.0"
futures-test = "0.3.5"
parking_lot = "0.12.0"
tempfile = "3.1.0"
[package.metadata.docs.rs]
all-features = true
+2
View File
@@ -227,6 +227,8 @@ impl<T: tokio::io::AsyncSeek> futures_io::AsyncSeek for Compat<T> {
pos: io::SeekFrom,
) -> Poll<io::Result<u64>> {
if self.seek_pos != Some(pos) {
// Ensure previous seeks have finished before starting a new one
ready!(self.as_mut().project().inner.poll_complete(cx))?;
self.as_mut().project().inner.start_seek(pos)?;
*self.as_mut().project().seek_pos = Some(pos);
}
+46
View File
@@ -52,6 +52,42 @@ impl<R: AsyncRead, F: FnMut(&[u8])> AsyncRead for InspectReader<R, F> {
}
}
impl<R: AsyncWrite, F> AsyncWrite for InspectReader<R, F> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::result::Result<usize, std::io::Error>> {
self.project().reader.poll_write(cx, buf)
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::result::Result<(), std::io::Error>> {
self.project().reader.poll_flush(cx)
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::result::Result<(), std::io::Error>> {
self.project().reader.poll_shutdown(cx)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<Result<usize>> {
self.project().reader.poll_write_vectored(cx, bufs)
}
fn is_write_vectored(&self) -> bool {
self.reader.is_write_vectored()
}
}
pin_project! {
/// An adapter that lets you inspect the data that's being written.
///
@@ -132,3 +168,13 @@ impl<W: AsyncWrite, F: FnMut(&[u8])> AsyncWrite for InspectWriter<W, F> {
self.writer.is_write_vectored()
}
}
impl<W: AsyncRead, F> AsyncRead for InspectWriter<W, F> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
self.project().writer.poll_read(cx, buf)
}
}
+2
View File
@@ -55,6 +55,8 @@ pub mod sync;
pub mod either;
pub use bytes;
#[cfg(any(feature = "io", feature = "codec"))]
mod util {
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
+4 -1
View File
@@ -97,6 +97,8 @@ impl core::fmt::Debug for CancellationToken {
}
impl Clone for CancellationToken {
/// Creates a clone of the `CancellationToken` which will get cancelled
/// whenever the current token gets cancelled, and vice versa.
fn clone(&self) -> Self {
tree_node::increase_handle_refcount(&self.inner);
CancellationToken {
@@ -126,7 +128,8 @@ impl CancellationToken {
}
/// Creates a `CancellationToken` which will get cancelled whenever the
/// current token gets cancelled.
/// current token gets cancelled. Unlike a cloned `CancellationToken`,
/// cancelling a child token does not cancel the parent token.
///
/// If the current token is already cancelled, the child token will get
/// returned in cancelled state.
@@ -151,47 +151,43 @@ fn with_locked_node_and_parent<F, Ret>(node: &Arc<TreeNode>, func: F) -> Ret
where
F: FnOnce(MutexGuard<'_, Inner>, Option<MutexGuard<'_, Inner>>) -> Ret,
{
let mut potential_parent = {
let locked_node = node.inner.lock().unwrap();
match locked_node.parent.clone() {
Some(parent) => parent,
// If we locked the node and its parent is `None`, we are in a valid state
// and can return.
None => return func(locked_node, None),
}
};
use std::sync::TryLockError;
let mut locked_node = node.inner.lock().unwrap();
// Every time this fails, the number of ancestors of the node decreases,
// so the loop must succeed after a finite number of iterations.
loop {
// Deadlock safety:
//
// Due to invariant #2, we know that we have to lock the parent first, and then the child.
// This is true even if the potential_parent is no longer the current parent or even its
// sibling, as the invariant still holds.
let locked_parent = potential_parent.inner.lock().unwrap();
let locked_node = node.inner.lock().unwrap();
let actual_parent = match locked_node.parent.clone() {
Some(parent) => parent,
// If we locked the node and its parent is `None`, we are in a valid state
// and can return.
None => {
// Was the wrong parent, so unlock it before calling `func`
drop(locked_parent);
return func(locked_node, None);
}
// Look up the parent of the currently locked node.
let potential_parent = match locked_node.parent.as_ref() {
Some(potential_parent) => potential_parent.clone(),
None => return func(locked_node, None),
};
// Loop until we managed to lock both the node and its parent
if Arc::ptr_eq(&actual_parent, &potential_parent) {
return func(locked_node, Some(locked_parent));
// Lock the parent. This may require unlocking the child first.
let locked_parent = match potential_parent.inner.try_lock() {
Ok(locked_parent) => locked_parent,
Err(TryLockError::WouldBlock) => {
drop(locked_node);
// Deadlock safety:
//
// Due to invariant #2, the potential parent must come before
// the child in the creation order. Therefore, we can safely
// lock the child while holding the parent lock.
let locked_parent = potential_parent.inner.lock().unwrap();
locked_node = node.inner.lock().unwrap();
locked_parent
}
Err(TryLockError::Poisoned(err)) => Err(err).unwrap(),
};
// If we unlocked the child, then the parent may have changed. Check
// that we still have the right parent.
if let Some(actual_parent) = locked_node.parent.as_ref() {
if Arc::ptr_eq(actual_parent, &potential_parent) {
return func(locked_node, Some(locked_parent));
}
}
// Drop locked_parent before reassigning to potential_parent,
// as potential_parent is borrowed in it
drop(locked_node);
drop(locked_parent);
potential_parent = actual_parent;
}
}
@@ -243,11 +239,7 @@ fn remove_child(parent: &mut Inner, mut node: MutexGuard<'_, Inner>) {
let len = parent.children.len();
if 4 * len <= parent.children.capacity() {
// equal to:
// parent.children.shrink_to(2 * len);
// but shrink_to was not yet stabilized in our minimal compatible version
let old_children = std::mem::replace(&mut parent.children, Vec::with_capacity(2 * len));
parent.children.extend(old_children);
parent.children.shrink_to(2 * len);
}
}
+50 -9
View File
@@ -44,7 +44,7 @@ enum State<T> {
pub struct PollSender<T> {
sender: Option<Sender<T>>,
state: State<T>,
acquire: ReusableBoxFuture<'static, Result<OwnedPermit<T>, PollSendError<T>>>,
acquire: PollSenderFuture<T>,
}
// Creates a future for acquiring a permit from the underlying channel. This is used to ensure
@@ -64,13 +64,56 @@ async fn make_acquire_future<T>(
}
}
impl<T: Send + 'static> PollSender<T> {
type InnerFuture<'a, T> = ReusableBoxFuture<'a, Result<OwnedPermit<T>, PollSendError<T>>>;
#[derive(Debug)]
// TODO: This should be replace with a type_alias_impl_trait to eliminate `'static` and all the transmutes
struct PollSenderFuture<T>(InnerFuture<'static, T>);
impl<T> PollSenderFuture<T> {
/// Create with an empty inner future with no `Send` bound.
fn empty() -> Self {
// We don't use `make_acquire_future` here because our relaxed bounds on `T` are not
// compatible with the transitive bounds required by `Sender<T>`.
Self(ReusableBoxFuture::new(async { unreachable!() }))
}
}
impl<T: Send> PollSenderFuture<T> {
/// Create with an empty inner future.
fn new() -> Self {
let v = InnerFuture::new(make_acquire_future(None));
// This is safe because `make_acquire_future(None)` is actually `'static`
Self(unsafe { mem::transmute::<InnerFuture<'_, T>, InnerFuture<'static, T>>(v) })
}
/// Poll the inner future.
fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Result<OwnedPermit<T>, PollSendError<T>>> {
self.0.poll(cx)
}
/// Replace the inner future.
fn set(&mut self, sender: Option<Sender<T>>) {
let inner: *mut InnerFuture<'static, T> = &mut self.0;
let inner: *mut InnerFuture<'_, T> = inner.cast();
// SAFETY: The `make_acquire_future(sender)` future must not exist after the type `T`
// becomes invalid, and this casts away the type-level lifetime check for that. However, the
// inner future is never moved out of this `PollSenderFuture<T>`, so the future will not
// live longer than the `PollSenderFuture<T>` lives. A `PollSenderFuture<T>` is guaranteed
// to not exist after the type `T` becomes invalid, because it is annotated with a `T`, so
// this is ok.
let inner = unsafe { &mut *inner };
inner.set(make_acquire_future(sender));
}
}
impl<T: Send> PollSender<T> {
/// Creates a new `PollSender`.
pub fn new(sender: Sender<T>) -> Self {
Self {
sender: Some(sender.clone()),
state: State::Idle(sender),
acquire: ReusableBoxFuture::new(make_acquire_future(None)),
acquire: PollSenderFuture::new(),
}
}
@@ -97,7 +140,7 @@ impl<T: Send + 'static> PollSender<T> {
State::Idle(sender) => {
// Start trying to acquire a permit to reserve a slot for our send, and
// immediately loop back around to poll it the first time.
self.acquire.set(make_acquire_future(Some(sender)));
self.acquire.set(Some(sender));
(None, State::Acquiring)
}
State::Acquiring => match self.acquire.poll(cx) {
@@ -194,7 +237,7 @@ impl<T: Send + 'static> PollSender<T> {
match self.state {
State::Idle(_) => self.state = State::Closed,
State::Acquiring => {
self.acquire.set(make_acquire_future(None));
self.acquire.set(None);
self.state = State::Closed;
}
_ => {}
@@ -215,7 +258,7 @@ impl<T: Send + 'static> PollSender<T> {
// We're currently trying to reserve a slot to send into.
State::Acquiring => {
// Replacing the future drops the in-flight one.
self.acquire.set(make_acquire_future(None));
self.acquire.set(None);
// If we haven't closed yet, we have to clone our stored sender since we have no way
// to get it back from the acquire future we just dropped.
@@ -255,9 +298,7 @@ impl<T> Clone for PollSender<T> {
Self {
sender,
state,
// We don't use `make_acquire_future` here because our relaxed bounds on `T` are not
// compatible with the transitive bounds required by `Sender<T>`.
acquire: ReusableBoxFuture::new(async { unreachable!() }),
acquire: PollSenderFuture::empty(),
}
}
}
+54
View File
@@ -316,6 +316,60 @@ where
self.insert(key, task);
}
/// Spawn the blocking code on the blocking threadpool and store it in this `JoinMap` with the provided
/// key.
///
/// If a task previously existed in the `JoinMap` for this key, that task
/// will be cancelled and replaced with the new one. The previous task will
/// be removed from the `JoinMap`; a subsequent call to [`join_next`] will
/// *not* return a cancelled [`JoinError`] for that task.
///
/// Note that blocking tasks cannot be cancelled after execution starts.
/// Replaced blocking tasks will still run to completion if the task has begun
/// to execute when it is replaced. A blocking task which is replaced before
/// it has been scheduled on a blocking worker thread will be cancelled.
///
/// # Panics
///
/// This method panics if called outside of a Tokio runtime.
///
/// [`join_next`]: Self::join_next
#[track_caller]
pub fn spawn_blocking<F>(&mut self, key: K, f: F)
where
F: FnOnce() -> V,
F: Send + 'static,
V: Send,
{
let task = self.tasks.spawn_blocking(f);
self.insert(key, task)
}
/// Spawn the blocking code on the blocking threadpool of the provided runtime and store it in this
/// `JoinMap` with the provided key.
///
/// If a task previously existed in the `JoinMap` for this key, that task
/// will be cancelled and replaced with the new one. The previous task will
/// be removed from the `JoinMap`; a subsequent call to [`join_next`] will
/// *not* return a cancelled [`JoinError`] for that task.
///
/// Note that blocking tasks cannot be cancelled after execution starts.
/// Replaced blocking tasks will still run to completion if the task has begun
/// to execute when it is replaced. A blocking task which is replaced before
/// it has been scheduled on a blocking worker thread will be cancelled.
///
/// [`join_next`]: Self::join_next
#[track_caller]
pub fn spawn_blocking_on<F>(&mut self, key: K, f: F, handle: &Handle)
where
F: FnOnce() -> V,
F: Send + 'static,
V: Send,
{
let task = self.tasks.spawn_blocking_on(f, handle);
self.insert(key, task);
}
/// Spawn the provided task on the current [`LocalSet`] and store it in this
/// `JoinMap` with the provided key.
///
+40 -1
View File
@@ -62,7 +62,7 @@ use std::task::{self, Poll, Waker};
/// performance and scalability benefits.
///
/// State associated with each entry is stored in a [`slab`]. This amortizes the cost of allocation,
/// and allows reuse of the memory allocated for expired entires.
/// and allows reuse of the memory allocated for expired entries.
///
/// Capacity can be checked using [`capacity`] and allocated preemptively by using
/// the [`reserve`] method.
@@ -874,6 +874,41 @@ impl<T> DelayQueue<T> {
self.slab.compact();
}
/// Gets the [`Key`] that [`poll_expired`] will pull out of the queue next, without
/// pulling it out or waiting for the deadline to expire.
///
/// Entries that have already expired may be returned in any order, but it is
/// guaranteed that this method returns them in the same order as when items
/// are popped from the `DelayQueue`.
///
/// # Examples
///
/// Basic usage
///
/// ```rust
/// use tokio_util::time::DelayQueue;
/// use std::time::Duration;
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
///
/// let key1 = delay_queue.insert("foo", Duration::from_secs(10));
/// let key2 = delay_queue.insert("bar", Duration::from_secs(5));
/// let key3 = delay_queue.insert("baz", Duration::from_secs(15));
///
/// assert_eq!(delay_queue.peek().unwrap(), key2);
/// # }
/// ```
///
/// [`Key`]: struct@Key
/// [`poll_expired`]: method@Self::poll_expired
pub fn peek(&self) -> Option<Key> {
use self::wheel::Stack;
self.expired.peek().or_else(|| self.wheel.peek())
}
/// Returns the next time to poll as determined by the wheel
fn next_deadline(&mut self) -> Option<Instant> {
self.wheel
@@ -1166,6 +1201,10 @@ impl<T> wheel::Stack for Stack<T> {
}
}
fn peek(&self) -> Option<Self::Owned> {
self.head
}
#[track_caller]
fn remove(&mut self, item: &Self::Borrowed, store: &mut Self::Store) {
let key = *item;
+26 -2
View File
@@ -140,11 +140,31 @@ impl<T: Stack> Level<T> {
// TODO: This can probably be simplified w/ power of 2 math
let level_start = now - (now % level_range);
let deadline = level_start + slot as u64 * slot_range;
let mut deadline = level_start + slot as u64 * slot_range;
if deadline < now {
// A timer is in a slot "prior" to the current time. This can occur
// because we do not have an infinite hierarchy of timer levels, and
// eventually a timer scheduled for a very distant time might end up
// being placed in a slot that is beyond the end of all of the
// arrays.
//
// To deal with this, we first limit timers to being scheduled no
// more than MAX_DURATION ticks in the future; that is, they're at
// most one rotation of the top level away. Then, we force timers
// that logically would go into the top+1 level, to instead go into
// the top level's slots.
//
// What this means is that the top level's slots act as a
// pseudo-ring buffer, and we rotate around them indefinitely. If we
// compute a deadline before now, and it's the top level, it
// therefore means we're actually looking at a slot in the future.
debug_assert_eq!(self.level, super::NUM_LEVELS - 1);
deadline += level_range;
}
debug_assert!(
deadline >= now,
"deadline={}; now={}; level={}; slot={}; occupied={:b}",
"deadline={:016X}; now={:016X}; level={}; slot={}; occupied={:b}",
deadline,
now,
self.level,
@@ -206,6 +226,10 @@ impl<T: Stack> Level<T> {
ret
}
pub(crate) fn peek_entry_slot(&self, slot: usize) -> Option<T::Owned> {
self.slot[slot].peek()
}
}
impl<T> fmt::Debug for Level<T> {
+15 -2
View File
@@ -139,6 +139,12 @@ where
self.next_expiration().map(|expiration| expiration.deadline)
}
/// Next key that will expire
pub(crate) fn peek(&self) -> Option<T::Owned> {
self.next_expiration()
.and_then(|expiration| self.peek_entry(&expiration))
}
/// Advances the timer up to the instant represented by `now`.
pub(crate) fn poll(&mut self, now: u64, store: &mut T::Store) -> Option<T::Owned> {
loop {
@@ -244,6 +250,10 @@ where
self.levels[expiration.level].pop_entry_slot(expiration.slot, store)
}
fn peek_entry(&self, expiration: &Expiration) -> Option<T::Owned> {
self.levels[expiration.level].peek_entry_slot(expiration.slot)
}
fn level_for(&self, when: u64) -> usize {
level_for(self.elapsed, when)
}
@@ -254,8 +264,11 @@ fn level_for(elapsed: u64, when: u64) -> usize {
// Mask in the trailing bits ignored by the level calculation in order to cap
// the possible leading zeros
let masked = elapsed ^ when | SLOT_MASK;
let mut masked = elapsed ^ when | SLOT_MASK;
if masked >= MAX_DURATION {
// Fudge the timer into the top level
masked = MAX_DURATION - 1;
}
let leading_zeros = masked.leading_zeros() as usize;
let significant = 63 - leading_zeros;
significant / 6
+3
View File
@@ -22,6 +22,9 @@ pub(crate) trait Stack: Default {
/// Pop an item from the stack
fn pop(&mut self, store: &mut Self::Store) -> Option<Self::Owned>;
/// Peek into the stack.
fn peek(&self) -> Option<Self::Owned>;
fn remove(&mut self, item: &Self::Borrowed, store: &mut Self::Store);
fn when(item: &Self::Borrowed, store: &Self::Store) -> u64;
+43
View File
@@ -0,0 +1,43 @@
#![cfg(all(feature = "compat"))]
#![cfg(not(target_os = "wasi"))] // WASI does not support all fs operations
#![warn(rust_2018_idioms)]
use futures_io::SeekFrom;
use futures_util::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
use tempfile::NamedTempFile;
use tokio::fs::OpenOptions;
use tokio_util::compat::TokioAsyncWriteCompatExt;
#[tokio::test]
async fn compat_file_seek() -> futures_util::io::Result<()> {
let temp_file = NamedTempFile::new()?;
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(temp_file)
.await?
.compat_write();
file.write_all(&[0, 1, 2, 3, 4, 5]).await?;
file.write_all(&[6, 7]).await?;
assert_eq!(file.stream_position().await?, 8);
// Modify elements at position 2.
assert_eq!(file.seek(SeekFrom::Start(2)).await?, 2);
file.write_all(&[8, 9]).await?;
file.flush().await?;
// Verify we still have 8 elements.
assert_eq!(file.seek(SeekFrom::End(0)).await?, 8);
// Seek back to the start of the file to read and verify contents.
file.seek(SeekFrom::Start(0)).await?;
let mut buf = Vec::new();
let num_bytes = file.read_to_end(&mut buf).await?;
assert_eq!(&buf[..num_bytes], &[0, 1, 8, 9, 4, 5, 6, 7]);
Ok(())
}
+23
View File
@@ -27,6 +27,29 @@ async fn simple() {
send.send_item(42).unwrap();
}
#[tokio::test]
async fn simple_ref() {
let v = vec![1, 2, 3i32];
let (send, mut recv) = channel(3);
let mut send = PollSender::new(send);
for vi in v.iter() {
let mut reserve = spawn(poll_fn(|cx| send.poll_reserve(cx)));
assert_ready_ok!(reserve.poll());
send.send_item(vi).unwrap();
}
let mut reserve = spawn(poll_fn(|cx| send.poll_reserve(cx)));
assert_pending!(reserve.poll());
assert_eq!(*recv.recv().await.unwrap(), 1);
assert!(reserve.is_woken());
assert_ready_ok!(reserve.poll());
drop(recv);
send.send_item(&42).unwrap();
}
#[tokio::test]
async fn repeated_poll_reserve() {
let (send, mut recv) = channel::<i32>(1);
+63
View File
@@ -2,6 +2,7 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]
use futures::StreamExt;
use tokio::time::{self, sleep, sleep_until, Duration, Instant};
use tokio_test::{assert_pending, assert_ready, task};
use tokio_util::time::DelayQueue;
@@ -257,6 +258,10 @@ async fn reset_twice() {
#[tokio::test]
async fn repeatedly_reset_entry_inserted_as_expired() {
time::pause();
// Instants before the start of the test seem to break in wasm.
time::sleep(ms(1000)).await;
let mut queue = task::spawn(DelayQueue::new());
let now = Instant::now();
@@ -556,6 +561,10 @@ async fn reset_later_after_slot_starts() {
#[tokio::test]
async fn reset_inserted_expired() {
time::pause();
// Instants before the start of the test seem to break in wasm.
time::sleep(ms(1000)).await;
let mut queue = task::spawn(DelayQueue::new());
let now = Instant::now();
@@ -778,6 +787,22 @@ async fn compact_change_deadline() {
assert!(entry.is_none());
}
#[tokio::test(start_paused = true)]
async fn item_expiry_greater_than_wheel() {
// This function tests that a delay queue that has existed for at least 2^36 milliseconds won't panic when a new item is inserted.
let mut queue = DelayQueue::new();
for _ in 0..2 {
tokio::time::advance(Duration::from_millis(1 << 35)).await;
queue.insert(0, Duration::from_millis(0));
queue.next().await;
}
// This should not panic
let no_panic = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
queue.insert(1, Duration::from_millis(1));
}));
assert!(no_panic.is_ok());
}
#[cfg_attr(target_os = "wasi", ignore = "FIXME: Does not seem to work with WASI")]
#[tokio::test(start_paused = true)]
async fn remove_after_compact() {
@@ -815,6 +840,44 @@ async fn remove_after_compact_poll() {
assert!(panic.is_err());
}
#[tokio::test(start_paused = true)]
async fn peek() {
let mut queue = task::spawn(DelayQueue::new());
let now = Instant::now();
let key = queue.insert_at("foo", now + ms(5));
let key2 = queue.insert_at("bar", now);
let key3 = queue.insert_at("baz", now + ms(10));
assert_eq!(queue.peek(), Some(key2));
sleep(ms(6)).await;
assert_eq!(queue.peek(), Some(key2));
let entry = assert_ready_some!(poll!(queue));
assert_eq!(entry.get_ref(), &"bar");
assert_eq!(queue.peek(), Some(key));
let entry = assert_ready_some!(poll!(queue));
assert_eq!(entry.get_ref(), &"foo");
assert_eq!(queue.peek(), Some(key3));
assert_pending!(poll!(queue));
sleep(ms(5)).await;
assert_eq!(queue.peek(), Some(key3));
let entry = assert_ready_some!(poll!(queue));
assert_eq!(entry.get_ref(), &"baz");
assert!(queue.peek().is_none());
}
fn ms(n: u64) -> Duration {
Duration::from_millis(n)
}
+214
View File
@@ -1,3 +1,189 @@
# 1.29.1 (June 29, 2023)
### Fixed
- rt: fix nesting two `block_in_place` with a `block_on` between ([#5837])
[#5837]: https://github.com/tokio-rs/tokio/pull/5837
# 1.29.0 (June 27, 2023)
Technically a breaking change, the `Send` implementation is removed from
`runtime::EnterGuard`. This change fixes a bug and should not impact most users.
### Breaking
- rt: `EnterGuard` should not be `Send` ([#5766])
### Fixed
- fs: reduce blocking ops in `fs::read_dir` ([#5653])
- rt: fix possible starvation ([#5686], [#5712])
- rt: fix stacked borrows issue in `JoinSet` ([#5693])
- rt: panic if `EnterGuard` dropped incorrect order ([#5772])
- time: do not overflow to signal value ([#5710])
- fs: wait for in-flight ops before cloning `File` ([#5803])
### Changed
- rt: reduce time to poll tasks scheduled from outside the runtime ([#5705], [#5720])
### Added
- net: add uds doc alias for unix sockets ([#5659])
- rt: add metric for number of tasks ([#5628])
- sync: implement more traits for channel errors ([#5666])
- net: add nodelay methods on TcpSocket ([#5672])
- sync: add `broadcast::Receiver::blocking_recv` ([#5690])
- process: add `raw_arg` method to `Command` ([#5704])
- io: support PRIORITY epoll events ([#5566])
- task: add `JoinSet::poll_join_next` ([#5721])
- net: add support for Redox OS ([#5790])
### Unstable
- rt: add the ability to dump task backtraces ([#5608], [#5676], [#5708], [#5717])
- rt: instrument task poll times with a histogram ([#5685])
[#5766]: https://github.com/tokio-rs/tokio/pull/5766
[#5653]: https://github.com/tokio-rs/tokio/pull/5653
[#5686]: https://github.com/tokio-rs/tokio/pull/5686
[#5712]: https://github.com/tokio-rs/tokio/pull/5712
[#5693]: https://github.com/tokio-rs/tokio/pull/5693
[#5772]: https://github.com/tokio-rs/tokio/pull/5772
[#5710]: https://github.com/tokio-rs/tokio/pull/5710
[#5803]: https://github.com/tokio-rs/tokio/pull/5803
[#5705]: https://github.com/tokio-rs/tokio/pull/5705
[#5720]: https://github.com/tokio-rs/tokio/pull/5720
[#5659]: https://github.com/tokio-rs/tokio/pull/5659
[#5628]: https://github.com/tokio-rs/tokio/pull/5628
[#5666]: https://github.com/tokio-rs/tokio/pull/5666
[#5672]: https://github.com/tokio-rs/tokio/pull/5672
[#5690]: https://github.com/tokio-rs/tokio/pull/5690
[#5704]: https://github.com/tokio-rs/tokio/pull/5704
[#5566]: https://github.com/tokio-rs/tokio/pull/5566
[#5721]: https://github.com/tokio-rs/tokio/pull/5721
[#5790]: https://github.com/tokio-rs/tokio/pull/5790
[#5608]: https://github.com/tokio-rs/tokio/pull/5608
[#5676]: https://github.com/tokio-rs/tokio/pull/5676
[#5708]: https://github.com/tokio-rs/tokio/pull/5708
[#5717]: https://github.com/tokio-rs/tokio/pull/5717
[#5685]: https://github.com/tokio-rs/tokio/pull/5685
# 1.28.2 (May 28, 2023)
Forward ports 1.18.6 changes.
### Fixed
- deps: disable default features for mio ([#5728])
[#5728]: https://github.com/tokio-rs/tokio/pull/5728
# 1.28.1 (May 10th, 2023)
This release fixes a mistake in the build script that makes `AsFd`
implementations unavailable on Rust 1.63. ([#5677])
[#5677]: https://github.com/tokio-rs/tokio/pull/5677
# 1.28.0 (April 25th, 2023)
### Added
- io: add `AsyncFd::async_io` ([#5542])
- io: impl BufMut for ReadBuf ([#5590])
- net: add `recv_buf` for `UdpSocket` and `UnixDatagram` ([#5583])
- sync: add `OwnedSemaphorePermit::semaphore` ([#5618])
- sync: add `same_channel` to broadcast channel ([#5607])
- sync: add `watch::Receiver::wait_for` ([#5611])
- task: add `JoinSet::spawn_blocking` and `JoinSet::spawn_blocking_on` ([#5612])
### Changed
- deps: update windows-sys to 0.48 ([#5591])
- io: make `read_to_end` not grow unnecessarily ([#5610])
- macros: make entrypoints more efficient ([#5621])
- sync: improve Debug impl for `RwLock` ([#5647])
- sync: reduce contention in `Notify` ([#5503])
### Fixed
- net: support `get_peer_cred` on AIX ([#5065])
- sync: avoid deadlocks in `broadcast` with custom wakers ([#5578])
### Documented
- sync: fix typo in `Semaphore::MAX_PERMITS` ([#5645])
- sync: fix typo in `tokio::sync::watch::Sender` docs ([#5587])
[#5065]: https://github.com/tokio-rs/tokio/pull/5065
[#5503]: https://github.com/tokio-rs/tokio/pull/5503
[#5542]: https://github.com/tokio-rs/tokio/pull/5542
[#5578]: https://github.com/tokio-rs/tokio/pull/5578
[#5583]: https://github.com/tokio-rs/tokio/pull/5583
[#5587]: https://github.com/tokio-rs/tokio/pull/5587
[#5590]: https://github.com/tokio-rs/tokio/pull/5590
[#5591]: https://github.com/tokio-rs/tokio/pull/5591
[#5607]: https://github.com/tokio-rs/tokio/pull/5607
[#5610]: https://github.com/tokio-rs/tokio/pull/5610
[#5611]: https://github.com/tokio-rs/tokio/pull/5611
[#5612]: https://github.com/tokio-rs/tokio/pull/5612
[#5618]: https://github.com/tokio-rs/tokio/pull/5618
[#5621]: https://github.com/tokio-rs/tokio/pull/5621
[#5645]: https://github.com/tokio-rs/tokio/pull/5645
[#5647]: https://github.com/tokio-rs/tokio/pull/5647
# 1.27.0 (March 27th, 2023)
This release bumps the MSRV of Tokio to 1.56. ([#5559])
### Added
- io: add `async_io` helper method to sockets ([#5512])
- io: add implementations of `AsFd`/`AsHandle`/`AsSocket` ([#5514], [#5540])
- net: add `UdpSocket::peek_sender()` ([#5520])
- sync: add `RwLockWriteGuard::{downgrade_map, try_downgrade_map}` ([#5527])
- task: add `JoinHandle::abort_handle` ([#5543])
### Changed
- io: use `memchr` from `libc` ([#5558])
- macros: accept path as crate rename in `#[tokio::main]` ([#5557])
- macros: update to syn 2.0.0 ([#5572])
- time: don't register for a wakeup when `Interval` returns `Ready` ([#5553])
### Fixed
- fs: fuse std iterator in `ReadDir` ([#5555])
- tracing: fix `spawn_blocking` location fields ([#5573])
- time: clean up redundant check in `Wheel::poll()` ([#5574])
### Documented
- macros: define cancellation safety ([#5525])
- io: add details to docs of `tokio::io::copy[_buf]` ([#5575])
- io: refer to `ReaderStream` and `StreamReader` in module docs ([#5576])
[#5512]: https://github.com/tokio-rs/tokio/pull/5512
[#5514]: https://github.com/tokio-rs/tokio/pull/5514
[#5520]: https://github.com/tokio-rs/tokio/pull/5520
[#5525]: https://github.com/tokio-rs/tokio/pull/5525
[#5527]: https://github.com/tokio-rs/tokio/pull/5527
[#5540]: https://github.com/tokio-rs/tokio/pull/5540
[#5543]: https://github.com/tokio-rs/tokio/pull/5543
[#5553]: https://github.com/tokio-rs/tokio/pull/5553
[#5555]: https://github.com/tokio-rs/tokio/pull/5555
[#5557]: https://github.com/tokio-rs/tokio/pull/5557
[#5558]: https://github.com/tokio-rs/tokio/pull/5558
[#5559]: https://github.com/tokio-rs/tokio/pull/5559
[#5572]: https://github.com/tokio-rs/tokio/pull/5572
[#5573]: https://github.com/tokio-rs/tokio/pull/5573
[#5574]: https://github.com/tokio-rs/tokio/pull/5574
[#5575]: https://github.com/tokio-rs/tokio/pull/5575
[#5576]: https://github.com/tokio-rs/tokio/pull/5576
# 1.26.0 (March 1st, 2023)
### Fixed
@@ -81,6 +267,16 @@
[#5513]: https://github.com/tokio-rs/tokio/pull/5513
[#5517]: https://github.com/tokio-rs/tokio/pull/5517
# 1.25.1 (May 28, 2023)
Forward ports 1.18.6 changes.
### Fixed
- deps: disable default features for mio ([#5728])
[#5728]: https://github.com/tokio-rs/tokio/pull/5728
# 1.25.0 (January 28, 2023)
### Fixed
@@ -417,6 +613,16 @@ wasm32-wasi target is given unstable support for the `net` feature.
[#4956]: https://github.com/tokio-rs/tokio/pull/4956
[#4959]: https://github.com/tokio-rs/tokio/pull/4959
# 1.20.5 (May 28, 2023)
Forward ports 1.18.6 changes.
### Fixed
- deps: disable default features for mio ([#5728])
[#5728]: https://github.com/tokio-rs/tokio/pull/5728
# 1.20.4 (January 17, 2023)
Forward ports 1.18.5 changes.
@@ -563,6 +769,14 @@ This release fixes a bug in `Notified::enable`. ([#4747])
[#4729]: https://github.com/tokio-rs/tokio/pull/4729
[#4739]: https://github.com/tokio-rs/tokio/pull/4739
# 1.18.6 (May 28, 2023)
### Fixed
- deps: disable default features for mio ([#5728])
[#5728]: https://github.com/tokio-rs/tokio/pull/5728
# 1.18.5 (January 17, 2023)
### Fixed
+19 -18
View File
@@ -6,9 +6,9 @@ name = "tokio"
# - README.md
# - Update CHANGELOG.md.
# - Create "v1.x.y" git tag.
version = "1.26.0"
edition = "2018"
rust-version = "1.49"
version = "1.29.1"
edition = "2021"
rust-version = "1.56"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
readme = "README.md"
@@ -42,7 +42,7 @@ full = [
]
fs = []
io-util = ["memchr", "bytes"]
io-util = ["bytes"]
# stdin, stdout, stderr
io-std = []
macros = ["tokio-macros"]
@@ -97,47 +97,48 @@ stats = []
autocfg = "1.1"
[dependencies]
tokio-macros = { version = "1.7.0", path = "../tokio-macros", optional = true }
tokio-macros = { version = "~2.1.0", path = "../tokio-macros", optional = true }
pin-project-lite = "0.2.0"
pin-project-lite = "0.2.7"
# Everything else is optional...
bytes = { version = "1.0.0", optional = true }
memchr = { version = "2.2", optional = true }
mio = { version = "0.8.4", optional = true }
mio = { version = "0.8.6", optional = true, default-features = false }
num_cpus = { version = "1.8.0", optional = true }
parking_lot = { version = "0.12.0", optional = true }
[target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies]
socket2 = { version = "0.4.4", optional = true, features = [ "all" ] }
socket2 = { version = "0.4.9", optional = true, features = [ "all" ] }
# Currently unstable. The API exposed by these features may be broken at any time.
# Requires `--cfg tokio_unstable` to enable.
[target.'cfg(tokio_unstable)'.dependencies]
tracing = { version = "0.1.25", default-features = false, features = ["std"], optional = true } # Not in full
# Currently unstable. The API exposed by these features may be broken at any time.
# Requires `--cfg tokio_unstable` to enable.
[target.'cfg(tokio_taskdump)'.dependencies]
backtrace = { version = "0.3.58" }
[target.'cfg(unix)'.dependencies]
libc = { version = "0.2.42", optional = true }
libc = { version = "0.2.145", optional = true }
signal-hook-registry = { version = "1.1.1", optional = true }
[target.'cfg(unix)'.dev-dependencies]
libc = { version = "0.2.42" }
libc = { version = "0.2.145" }
nix = { version = "0.26", default-features = false, features = ["fs", "socket"] }
[target.'cfg(windows)'.dependencies.windows-sys]
version = "0.45"
version = "0.48"
optional = true
[target.'cfg(docsrs)'.dependencies.windows-sys]
version = "0.45"
[target.'cfg(windows)'.dev-dependencies.windows-sys]
version = "0.48"
features = [
"Win32_Foundation",
"Win32_Security_Authorization",
]
[target.'cfg(windows)'.dev-dependencies.ntapi]
version = "0.3.6"
[dev-dependencies]
tokio-test = { version = "0.4.0", path = "../tokio-test" }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
@@ -146,7 +147,7 @@ mockall = "0.11.1"
async-stream = "0.3"
[target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dev-dependencies]
socket2 = "0.4"
socket2 = "0.4.9"
tempfile = "3.1.0"
[target.'cfg(not(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown")))'.dev-dependencies]
+18 -4
View File
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.26.0", features = ["full"] }
tokio = { version = "1.29.1", features = ["full"] }
```
Then, on your main.rs:
@@ -187,7 +187,20 @@ When updating this, also update:
Tokio will keep a rolling MSRV (minimum supported rust version) policy of **at
least** 6 months. When increasing the MSRV, the new Rust version must have been
released at least six months ago. The current MSRV is 1.49.0.
released at least six months ago. The current MSRV is 1.56.0.
Note that the MSRV is not increased automatically, and only as part of a minor
release. The MSRV history for past minor releases can be found below:
* 1.27 to now - Rust 1.56
* 1.17 to 1.26 - Rust 1.49
* 1.15 to 1.16 - Rust 1.46
* 1.0 to 1.14 - Rust 1.45
Note that although we try to avoid the situation where a dependency transitively
increases the MSRV of Tokio, we do not guarantee that this does not happen.
However, every minor release will have some set of versions of dependencies that
works with the MSRV of that minor release.
## Release schedule
@@ -202,8 +215,9 @@ warrants a patch release with a fix for the bug, it will be backported and
released as a new patch release for each LTS minor version. Our current LTS
releases are:
* `1.18.x` - LTS release until June 2023
* `1.20.x` - LTS release until September 2023.
* `1.18.x` - LTS release until June 2023. (MSRV 1.49)
* `1.20.x` - LTS release until September 2023. (MSRV 1.49)
* `1.25.x` - LTS release until March 2024. (MSRV 1.49)
Each LTS release will continue to receive backported fixes for at least a year.
If you wish to use a fixed minor release in your project, we recommend that you
+38 -31
View File
@@ -10,13 +10,6 @@ const CONST_THREAD_LOCAL_PROBE: &str = r#"
}
"#;
const ADDR_OF_PROBE: &str = r#"
{
let my_var = 10;
::std::ptr::addr_of!(my_var)
}
"#;
const CONST_MUTEX_NEW_PROBE: &str = r#"
{
static MY_MUTEX: ::std::sync::Mutex<i32> = ::std::sync::Mutex::new(1);
@@ -24,6 +17,20 @@ const CONST_MUTEX_NEW_PROBE: &str = r#"
}
"#;
const AS_FD_PROBE: &str = r#"
{
#[allow(unused_imports)]
#[cfg(unix)]
use std::os::unix::prelude::AsFd as _;
#[allow(unused_imports)]
#[cfg(windows)]
use std::os::windows::prelude::AsSocket as _;
#[allow(unused_imports)]
#[cfg(target_os = "wasi")]
use std::os::wasi::prelude::AsFd as _;
}
"#;
const TARGET_HAS_ATOMIC_PROBE: &str = r#"
{
#[cfg(target_has_atomic = "ptr")]
@@ -40,9 +47,9 @@ const TARGET_ATOMIC_U64_PROBE: &str = r#"
fn main() {
let mut enable_const_thread_local = false;
let mut enable_addr_of = false;
let mut enable_target_has_atomic = false;
let mut enable_const_mutex_new = false;
let mut enable_as_fd = false;
let mut target_needs_atomic_u64_fallback = false;
match AutoCfg::new() {
@@ -67,21 +74,6 @@ fn main() {
}
}
// The `addr_of` and `addr_of_mut` macros were stabilized in 1.51.
if ac.probe_rustc_version(1, 52) {
enable_addr_of = true;
} else if ac.probe_rustc_version(1, 51) {
// This compiler claims to be 1.51, but there are some nightly
// compilers that claim to be 1.51 without supporting the
// feature. Explicitly probe to check if code using them
// compiles.
//
// The oldest nightly that supports the feature is 2021-01-31.
if ac.probe_expression(ADDR_OF_PROBE) {
enable_addr_of = true;
}
}
// The `target_has_atomic` cfg was stabilized in 1.60.
if ac.probe_rustc_version(1, 61) {
enable_target_has_atomic = true;
@@ -117,6 +109,21 @@ fn main() {
enable_const_mutex_new = true;
}
}
// The `AsFd` family of traits were made stable in 1.63.
if ac.probe_rustc_version(1, 64) {
enable_as_fd = true;
} else if ac.probe_rustc_version(1, 63) {
// This compiler claims to be 1.63, but there are some nightly
// compilers that claim to be 1.63 without supporting the
// feature. Explicitly probe to check if code using them
// compiles.
//
// The oldest nightly that supports the feature is 2022-06-16.
if ac.probe_expression(AS_FD_PROBE) {
enable_as_fd = true;
}
}
}
Err(e) => {
@@ -138,14 +145,6 @@ fn main() {
autocfg::emit("tokio_no_const_thread_local")
}
if !enable_addr_of {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_addr_of"
autocfg::emit("tokio_no_addr_of")
}
if !enable_target_has_atomic {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
@@ -162,6 +161,14 @@ fn main() {
autocfg::emit("tokio_no_const_mutex_new")
}
if !enable_as_fd {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_as_fd"
autocfg::emit("tokio_no_as_fd");
}
if target_needs_atomic_u64_fallback {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
+1 -1
View File
@@ -2,7 +2,7 @@
name = "tokio-fuzz"
version = "0.0.0"
publish = false
edition = "2018"
edition = "2021"
[package.metadata]
cargo-fuzz = true
+40 -1
View File
@@ -13,7 +13,7 @@ pub mod windows {
/// See [std::os::windows::io::AsRawHandle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html)
pub trait AsRawHandle {
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html#tymethod.as_raw_handle)
/// See [std::os::windows::io::AsRawHandle::as_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html#tymethod.as_raw_handle)
fn as_raw_handle(&self) -> RawHandle;
}
@@ -22,5 +22,44 @@ pub mod windows {
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html#tymethod.from_raw_handle)
unsafe fn from_raw_handle(handle: RawHandle) -> Self;
}
/// See [std::os::windows::io::RawSocket](https://doc.rust-lang.org/std/os/windows/io/type.RawSocket.html)
pub type RawSocket = crate::doc::NotDefinedHere;
/// See [std::os::windows::io::AsRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html)
pub trait AsRawSocket {
/// See [std::os::windows::io::AsRawSocket::as_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html#tymethod.as_raw_socket)
fn as_raw_socket(&self) -> RawSocket;
}
/// See [std::os::windows::io::FromRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawSocket.html)
pub trait FromRawSocket {
/// See [std::os::windows::io::FromRawSocket::from_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawSocket.html#tymethod.from_raw_socket)
unsafe fn from_raw_socket(sock: RawSocket) -> Self;
}
/// See [std::os::windows::io::IntoRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawSocket.html)
pub trait IntoRawSocket {
/// See [std::os::windows::io::IntoRawSocket::into_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawSocket.html#tymethod.into_raw_socket)
fn into_raw_socket(self) -> RawSocket;
}
/// See [std::os::windows::io::BorrowedHandle](https://doc.rust-lang.org/std/os/windows/io/struct.BorrowedHandle.html)
pub type BorrowedHandle<'handle> = crate::doc::NotDefinedHere;
/// See [std::os::windows::io::AsHandle](https://doc.rust-lang.org/std/os/windows/io/trait.AsHandle.html)
pub trait AsHandle {
/// See [std::os::windows::io::AsHandle::as_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsHandle.html#tymethod.as_handle)
fn as_handle(&self) -> BorrowedHandle<'_>;
}
/// See [std::os::windows::io::BorrowedSocket](https://doc.rust-lang.org/std/os/windows/io/struct.BorrowedSocket.html)
pub type BorrowedSocket<'socket> = crate::doc::NotDefinedHere;
/// See [std::os::windows::io::AsSocket](https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html)
pub trait AsSocket {
/// See [std::os::windows::io::AsSocket::as_socket](https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html#tymethod.as_socket)
fn as_socket(&self) -> BorrowedSocket<'_>;
}
}
}
+52 -12
View File
@@ -399,6 +399,7 @@ impl File {
/// # }
/// ```
pub async fn try_clone(&self) -> io::Result<File> {
self.inner.lock().await.complete_inflight().await;
let std = self.std.clone();
let std_file = asyncify(move || std.try_clone()).await?;
Ok(File::from_std(std_file))
@@ -498,6 +499,7 @@ impl AsyncRead for File {
cx: &mut Context<'_>,
dst: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
ready!(crate::trace::trace_leaf(cx));
let me = self.get_mut();
let inner = me.inner.get_mut();
@@ -594,6 +596,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));
let inner = self.inner.get_mut();
loop {
@@ -629,6 +632,7 @@ impl AsyncWrite for File {
cx: &mut Context<'_>,
src: &[u8],
) -> Poll<io::Result<usize>> {
ready!(crate::trace::trace_leaf(cx));
let me = self.get_mut();
let inner = me.inner.get_mut();
@@ -695,11 +699,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));
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));
self.poll_flush(cx)
}
}
@@ -725,6 +731,15 @@ impl std::os::unix::io::AsRawFd for File {
}
}
#[cfg(all(unix, not(tokio_no_as_fd)))]
impl std::os::unix::io::AsFd for File {
fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> {
unsafe {
std::os::unix::io::BorrowedFd::borrow_raw(std::os::unix::io::AsRawFd::as_raw_fd(self))
}
}
}
#[cfg(unix)]
impl std::os::unix::io::FromRawFd for File {
unsafe fn from_raw_fd(fd: std::os::unix::io::RawFd) -> Self {
@@ -732,17 +747,32 @@ impl std::os::unix::io::FromRawFd for File {
}
}
#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for File {
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
self.std.as_raw_handle()
}
}
cfg_windows! {
use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle};
#[cfg(not(tokio_no_as_fd))]
use crate::os::windows::io::{AsHandle, BorrowedHandle};
#[cfg(windows)]
impl std::os::windows::io::FromRawHandle for File {
unsafe fn from_raw_handle(handle: std::os::windows::io::RawHandle) -> Self {
StdFile::from_raw_handle(handle).into()
impl AsRawHandle for File {
fn as_raw_handle(&self) -> RawHandle {
self.std.as_raw_handle()
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsHandle for File {
fn as_handle(&self) -> BorrowedHandle<'_> {
unsafe {
BorrowedHandle::borrow_raw(
AsRawHandle::as_raw_handle(self),
)
}
}
}
impl FromRawHandle for File {
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
StdFile::from_raw_handle(handle).into()
}
}
}
@@ -750,8 +780,18 @@ impl Inner {
async fn complete_inflight(&mut self) {
use crate::future::poll_fn;
if let Err(e) = poll_fn(|cx| Pin::new(&mut *self).poll_flush(cx)).await {
self.last_write_err = Some(e.kind());
poll_fn(|cx| self.poll_complete_inflight(cx)).await
}
fn poll_complete_inflight(&mut self, cx: &mut Context<'_>) -> Poll<()> {
ready!(crate::trace::trace_leaf(cx));
match self.poll_flush(cx) {
Poll::Ready(Err(e)) => {
self.last_write_err = Some(e.kind());
Poll::Ready(())
}
Poll::Ready(Ok(())) => Poll::Ready(()),
Poll::Pending => Poll::Pending,
}
}
+1 -3
View File
@@ -115,9 +115,7 @@ feature! {
pub use self::symlink::symlink;
}
feature! {
#![windows]
cfg_windows! {
mod symlink_dir;
pub use self::symlink_dir::symlink_dir;
+6 -7
View File
@@ -10,6 +10,11 @@ use mock_open_options::MockOpenOptions as StdOpenOptions;
#[cfg(not(test))]
use std::fs::OpenOptions as StdOpenOptions;
#[cfg(unix)]
use std::os::unix::fs::OpenOptionsExt;
#[cfg(windows)]
use std::os::windows::fs::OpenOptionsExt;
/// Options and flags which can be used to configure how a file is opened.
///
/// This builder exposes the ability to configure how a [`File`] is opened and
@@ -399,8 +404,6 @@ impl OpenOptions {
feature! {
#![unix]
use std::os::unix::fs::OpenOptionsExt;
impl OpenOptions {
/// Sets the mode bits that a new file will be created with.
///
@@ -464,11 +467,7 @@ feature! {
}
}
feature! {
#![windows]
use std::os::windows::fs::OpenOptionsExt;
cfg_windows! {
impl OpenOptions {
/// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
/// with the specified value.
+30 -25
View File
@@ -35,9 +35,9 @@ pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
asyncify(|| -> io::Result<ReadDir> {
let mut std = std::fs::read_dir(path)?;
let mut buf = VecDeque::with_capacity(CHUNK_SIZE);
ReadDir::next_chunk(&mut buf, &mut std);
let remain = ReadDir::next_chunk(&mut buf, &mut std);
Ok(ReadDir(State::Idle(Some((buf, std)))))
Ok(ReadDir(State::Idle(Some((buf, std, remain)))))
})
.await
}
@@ -66,8 +66,8 @@ pub struct ReadDir(State);
#[derive(Debug)]
enum State {
Idle(Option<(VecDeque<io::Result<DirEntry>>, std::fs::ReadDir)>),
Pending(JoinHandle<(VecDeque<io::Result<DirEntry>>, std::fs::ReadDir)>),
Idle(Option<(VecDeque<io::Result<DirEntry>>, std::fs::ReadDir, bool)>),
Pending(JoinHandle<(VecDeque<io::Result<DirEntry>>, std::fs::ReadDir, bool)>),
}
impl ReadDir {
@@ -103,38 +103,35 @@ impl ReadDir {
loop {
match self.0 {
State::Idle(ref mut data) => {
let (buf, _) = data.as_mut().unwrap();
let (buf, _, ref remain) = data.as_mut().unwrap();
if let Some(ent) = buf.pop_front() {
return Poll::Ready(ent.map(Some));
};
} else if !remain {
return Poll::Ready(Ok(None));
}
let (mut buf, mut std) = data.take().unwrap();
let (mut buf, mut std, _) = data.take().unwrap();
self.0 = State::Pending(spawn_blocking(move || {
ReadDir::next_chunk(&mut buf, &mut std);
(buf, std)
let remain = ReadDir::next_chunk(&mut buf, &mut std);
(buf, std, remain)
}));
}
State::Pending(ref mut rx) => {
let (mut buf, std) = ready!(Pin::new(rx).poll(cx))?;
let ret = match buf.pop_front() {
Some(Ok(x)) => Ok(Some(x)),
Some(Err(e)) => Err(e),
None => Ok(None),
};
self.0 = State::Idle(Some((buf, std)));
return Poll::Ready(ret);
self.0 = State::Idle(Some(ready!(Pin::new(rx).poll(cx))?));
}
}
}
}
fn next_chunk(buf: &mut VecDeque<io::Result<DirEntry>>, std: &mut std::fs::ReadDir) {
for ret in std.by_ref().take(CHUNK_SIZE) {
fn next_chunk(buf: &mut VecDeque<io::Result<DirEntry>>, std: &mut std::fs::ReadDir) -> bool {
for _ in 0..CHUNK_SIZE {
let ret = match std.next() {
Some(ret) => ret,
None => return false,
};
let success = ret.is_ok();
buf.push_back(ret.map(|std| DirEntry {
@@ -142,7 +139,9 @@ impl ReadDir {
target_os = "solaris",
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks"
target_os = "vxworks",
target_os = "nto",
target_os = "vita",
)))]
file_type: std.file_type().ok(),
std: Arc::new(std),
@@ -152,6 +151,8 @@ impl ReadDir {
break;
}
}
true
}
}
@@ -201,7 +202,9 @@ pub struct DirEntry {
target_os = "solaris",
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks"
target_os = "vxworks",
target_os = "nto",
target_os = "vita",
)))]
file_type: Option<FileType>,
std: Arc<std::fs::DirEntry>,
@@ -332,7 +335,9 @@ impl DirEntry {
target_os = "solaris",
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks"
target_os = "vxworks",
target_os = "nto",
target_os = "vita",
)))]
if let Some(file_type) = self.file_type {
return Ok(file_type);
+1 -1
View File
@@ -10,7 +10,7 @@ use std::path::Path;
///
/// This is an async version of [`std::os::windows::fs::symlink_dir`][std]
///
/// [std]: std::os::windows::fs::symlink_dir
/// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
pub async fn symlink_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
let src = src.as_ref().to_owned();
let dst = dst.as_ref().to_owned();
+1 -1
View File
@@ -10,7 +10,7 @@ use std::path::Path;
///
/// This is an async version of [`std::os::windows::fs::symlink_file`][std]
///
/// [std]: std::os::windows::fs::symlink_file
/// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
pub async fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
let src = src.as_ref().to_owned();
let dst = dst.as_ref().to_owned();
+563 -20
View File
@@ -1,4 +1,4 @@
use crate::io::Interest;
use crate::io::{Interest, Ready};
use crate::runtime::io::{ReadyEvent, Registration};
use crate::runtime::scheduler;
@@ -201,13 +201,14 @@ pub struct AsyncFdReadyMutGuard<'a, T: AsRawFd> {
event: Option<ReadyEvent>,
}
const ALL_INTEREST: Interest = Interest::READABLE.add(Interest::WRITABLE);
impl<T: AsRawFd> AsyncFd<T> {
/// Creates an AsyncFd backed by (and taking ownership of) an object
/// implementing [`AsRawFd`]. The backing file descriptor is cached at the
/// time of creation.
///
/// Only configures the [`Interest::READABLE`] and [`Interest::WRITABLE`] interests. For more
/// control, use [`AsyncFd::with_interest`].
///
/// This method must be called in the context of a tokio runtime.
///
/// # Panics
@@ -220,11 +221,12 @@ impl<T: AsRawFd> AsyncFd<T> {
where
T: AsRawFd,
{
Self::with_interest(inner, ALL_INTEREST)
Self::with_interest(inner, Interest::READABLE | Interest::WRITABLE)
}
/// Creates new instance as `new` with additional ability to customize interest,
/// allowing to specify whether file descriptor will be polled for read, write or both.
/// Creates an AsyncFd backed by (and taking ownership of) an object
/// implementing [`AsRawFd`], with a specific [`Interest`]. The backing
/// file descriptor is cached at the time of creation.
///
/// # Panics
///
@@ -440,7 +442,96 @@ impl<T: AsRawFd> AsyncFd<T> {
.into()
}
async fn readiness(&self, interest: Interest) -> io::Result<AsyncFdReadyGuard<'_, T>> {
/// Waits for any of the requested ready states, returning a
/// [`AsyncFdReadyGuard`] that must be dropped to resume
/// polling for the requested ready states.
///
/// The function may complete without the file descriptor being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// When an IO operation does return `io::ErrorKind::WouldBlock`, the readiness must be cleared.
/// When a combined interest is used, it is important to clear only the readiness
/// that is actually observed to block. For instance when the combined
/// interest `Interest::READABLE | Interest::WRITABLE` is used, and a read blocks, only
/// read readiness should be cleared using the [`AsyncFdReadyGuard::clear_ready_matching`] method:
/// `guard.clear_ready_matching(Ready::READABLE)`.
/// Also clearing the write readiness in this case would be incorrect. The [`AsyncFdReadyGuard::clear_ready`]
/// method clears all readiness flags.
///
/// This method takes `&self`, so it is possible to call this method
/// concurrently with other methods on this struct. This method only
/// provides shared access to the inner IO resource when handling the
/// [`AsyncFdReadyGuard`].
///
/// # Examples
///
/// Concurrently read and write to a [`std::net::TcpStream`] on the same task without
/// splitting.
///
/// ```no_run
/// use std::error::Error;
/// use std::io;
/// use std::io::{Read, Write};
/// use std::net::TcpStream;
/// use tokio::io::unix::AsyncFd;
/// use tokio::io::{Interest, Ready};
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080")?;
/// stream.set_nonblocking(true)?;
/// let stream = AsyncFd::new(stream)?;
///
/// loop {
/// let mut guard = stream
/// .ready(Interest::READABLE | Interest::WRITABLE)
/// .await?;
///
/// if guard.ready().is_readable() {
/// let mut data = vec![0; 1024];
/// // Try to read data, this may still fail with `WouldBlock`
/// // if the readiness event is a false positive.
/// match stream.get_ref().read(&mut data) {
/// Ok(n) => {
/// println!("read {} bytes", n);
/// }
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
/// // a read has blocked, but a write might still succeed.
/// // clear only the read readiness.
/// guard.clear_ready_matching(Ready::READABLE);
/// continue;
/// }
/// Err(e) => {
/// return Err(e.into());
/// }
/// }
/// }
///
/// if guard.ready().is_writable() {
/// // Try to write data, this may still fail with `WouldBlock`
/// // if the readiness event is a false positive.
/// match stream.get_ref().write(b"hello world") {
/// Ok(n) => {
/// println!("write {} bytes", n);
/// }
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
/// // a write has blocked, but a read might still succeed.
/// // clear only the write readiness.
/// guard.clear_ready_matching(Ready::WRITABLE);
/// continue;
/// }
/// Err(e) => {
/// return Err(e.into());
/// }
/// }
/// }
/// }
/// }
/// ```
pub async fn ready(&self, interest: Interest) -> io::Result<AsyncFdReadyGuard<'_, T>> {
let event = self.registration.readiness(interest).await?;
Ok(AsyncFdReadyGuard {
@@ -449,7 +540,94 @@ impl<T: AsRawFd> AsyncFd<T> {
})
}
async fn readiness_mut(
/// Waits for any of the requested ready states, returning a
/// [`AsyncFdReadyMutGuard`] that must be dropped to resume
/// polling for the requested ready states.
///
/// The function may complete without the file descriptor being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// When an IO operation does return `io::ErrorKind::WouldBlock`, the readiness must be cleared.
/// When a combined interest is used, it is important to clear only the readiness
/// that is actually observed to block. For instance when the combined
/// interest `Interest::READABLE | Interest::WRITABLE` is used, and a read blocks, only
/// read readiness should be cleared using the [`AsyncFdReadyMutGuard::clear_ready_matching`] method:
/// `guard.clear_ready_matching(Ready::READABLE)`.
/// Also clearing the write readiness in this case would be incorrect.
/// The [`AsyncFdReadyMutGuard::clear_ready`] method clears all readiness flags.
///
/// This method takes `&mut self`, so it is possible to access the inner IO
/// resource mutably when handling the [`AsyncFdReadyMutGuard`].
///
/// # Examples
///
/// Concurrently read and write to a [`std::net::TcpStream`] on the same task without
/// splitting.
///
/// ```no_run
/// use std::error::Error;
/// use std::io;
/// use std::io::{Read, Write};
/// use std::net::TcpStream;
/// use tokio::io::unix::AsyncFd;
/// use tokio::io::{Interest, Ready};
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080")?;
/// stream.set_nonblocking(true)?;
/// let mut stream = AsyncFd::new(stream)?;
///
/// loop {
/// let mut guard = stream
/// .ready_mut(Interest::READABLE | Interest::WRITABLE)
/// .await?;
///
/// if guard.ready().is_readable() {
/// let mut data = vec![0; 1024];
/// // Try to read data, this may still fail with `WouldBlock`
/// // if the readiness event is a false positive.
/// match guard.get_inner_mut().read(&mut data) {
/// Ok(n) => {
/// println!("read {} bytes", n);
/// }
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
/// // a read has blocked, but a write might still succeed.
/// // clear only the read readiness.
/// guard.clear_ready_matching(Ready::READABLE);
/// continue;
/// }
/// Err(e) => {
/// return Err(e.into());
/// }
/// }
/// }
///
/// if guard.ready().is_writable() {
/// // Try to write data, this may still fail with `WouldBlock`
/// // if the readiness event is a false positive.
/// match guard.get_inner_mut().write(b"hello world") {
/// Ok(n) => {
/// println!("write {} bytes", n);
/// }
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
/// // a write has blocked, but a read might still succeed.
/// // clear only the write readiness.
/// guard.clear_ready_matching(Ready::WRITABLE);
/// continue;
/// }
/// Err(e) => {
/// return Err(e.into());
/// }
/// }
/// }
/// }
/// }
/// ```
pub async fn ready_mut(
&mut self,
interest: Interest,
) -> io::Result<AsyncFdReadyMutGuard<'_, T>> {
@@ -471,7 +649,7 @@ impl<T: AsRawFd> AsyncFd<T> {
/// [`AsyncFdReadyGuard`].
#[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
pub async fn readable<'a>(&'a self) -> io::Result<AsyncFdReadyGuard<'a, T>> {
self.readiness(Interest::READABLE).await
self.ready(Interest::READABLE).await
}
/// Waits for the file descriptor to become readable, returning a
@@ -482,7 +660,7 @@ impl<T: AsRawFd> AsyncFd<T> {
/// resource mutably when handling the [`AsyncFdReadyMutGuard`].
#[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
pub async fn readable_mut<'a>(&'a mut self) -> io::Result<AsyncFdReadyMutGuard<'a, T>> {
self.readiness_mut(Interest::READABLE).await
self.ready_mut(Interest::READABLE).await
}
/// Waits for the file descriptor to become writable, returning a
@@ -495,7 +673,7 @@ impl<T: AsRawFd> AsyncFd<T> {
/// [`AsyncFdReadyGuard`].
#[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
pub async fn writable<'a>(&'a self) -> io::Result<AsyncFdReadyGuard<'a, T>> {
self.readiness(Interest::WRITABLE).await
self.ready(Interest::WRITABLE).await
}
/// Waits for the file descriptor to become writable, returning a
@@ -506,7 +684,110 @@ impl<T: AsRawFd> AsyncFd<T> {
/// resource mutably when handling the [`AsyncFdReadyMutGuard`].
#[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
pub async fn writable_mut<'a>(&'a mut self) -> io::Result<AsyncFdReadyMutGuard<'a, T>> {
self.readiness_mut(Interest::WRITABLE).await
self.ready_mut(Interest::WRITABLE).await
}
/// Reads or writes from the file descriptor using a user-provided IO operation.
///
/// The `async_io` method is a convenience utility that waits for the file
/// descriptor to become ready, and then executes the provided IO operation.
/// Since file descriptors may be marked ready spuriously, the closure will
/// be called repeatedly until it returns something other than a
/// [`WouldBlock`] error. This is done using the following loop:
///
/// ```no_run
/// # use std::io::{self, Result};
/// # struct Dox<T> { inner: T }
/// # impl<T> Dox<T> {
/// # async fn writable(&self) -> Result<&Self> {
/// # Ok(self)
/// # }
/// # fn try_io<R>(&self, _: impl FnMut(&T) -> Result<R>) -> Result<Result<R>> {
/// # panic!()
/// # }
/// async fn async_io<R>(&self, mut f: impl FnMut(&T) -> io::Result<R>) -> io::Result<R> {
/// loop {
/// // or `readable` if called with the read interest.
/// let guard = self.writable().await?;
///
/// match guard.try_io(&mut f) {
/// Ok(result) => return result,
/// Err(_would_block) => continue,
/// }
/// }
/// }
/// # }
/// ```
///
/// The closure should only return a [`WouldBlock`] error if it has performed
/// an IO operation on the file descriptor that failed due to the file descriptor not being
/// ready. Returning a [`WouldBlock`] error in any other situation will
/// incorrectly clear the readiness flag, which can cause the file descriptor to
/// behave incorrectly.
///
/// The closure should not perform the IO operation using any of the methods
/// defined on the Tokio [`AsyncFd`] type, as this will mess with the
/// readiness flag and can cause the file descriptor to behave incorrectly.
///
/// This method is not intended to be used with combined interests.
/// The closure should perform only one type of IO operation, so it should not
/// require more than one ready state. This method may panic or sleep forever
/// if it is called with a combined interest.
///
/// # Examples
///
/// This example sends some bytes on the inner [`std::net::UdpSocket`]. The `async_io`
/// method waits for readiness, and retries if the send operation does block. This example
/// is equivalent to the one given for [`try_io`].
///
/// ```no_run
/// use tokio::io::{Interest, unix::AsyncFd};
///
/// use std::io;
/// use std::net::UdpSocket;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let socket = UdpSocket::bind("0.0.0.0:8080")?;
/// socket.set_nonblocking(true)?;
/// let async_fd = AsyncFd::new(socket)?;
///
/// let written = async_fd
/// .async_io(Interest::WRITABLE, |inner| inner.send(&[1, 2]))
/// .await?;
///
/// println!("wrote {written} bytes");
///
/// Ok(())
/// }
/// ```
///
/// [`try_io`]: AsyncFdReadyGuard::try_io
/// [`WouldBlock`]: std::io::ErrorKind::WouldBlock
pub async fn async_io<R>(
&self,
interest: Interest,
mut f: impl FnMut(&T) -> io::Result<R>,
) -> io::Result<R> {
self.registration
.async_io(interest, || f(self.get_ref()))
.await
}
/// Reads or writes from the file descriptor using a user-provided IO operation.
///
/// The behavior is the same as [`async_io`], except that the closure can mutate the inner
/// value of the [`AsyncFd`].
///
/// [`async_io`]: AsyncFd::async_io
pub async fn async_io_mut<R>(
&mut self,
interest: Interest,
mut f: impl FnMut(&mut T) -> io::Result<R>,
) -> io::Result<R> {
self.registration
.async_io(interest, || f(self.inner.as_mut().unwrap()))
.await
}
}
@@ -516,6 +797,13 @@ impl<T: AsRawFd> AsRawFd for AsyncFd<T> {
}
}
#[cfg(not(tokio_no_as_fd))]
impl<T: AsRawFd> std::os::unix::io::AsFd for AsyncFd<T> {
fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> {
unsafe { std::os::unix::io::BorrowedFd::borrow_raw(self.as_raw_fd()) }
}
}
impl<T: std::fmt::Debug + AsRawFd> std::fmt::Debug for AsyncFd<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AsyncFd")
@@ -531,22 +819,117 @@ impl<T: AsRawFd> Drop for AsyncFd<T> {
}
impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
/// Indicates to tokio that the file descriptor is no longer ready. The
/// internal readiness flag will be cleared, and tokio will wait for the
/// Indicates to tokio that the file descriptor is no longer ready. All
/// internal readiness flags will be cleared, and tokio will wait for the
/// next edge-triggered readiness notification from the OS.
///
/// This function is commonly used with guards returned by [`AsyncFd::readable`] and
/// [`AsyncFd::writable`].
///
/// It is critical that this function not be called unless your code
/// _actually observes_ that the file descriptor is _not_ ready. Do not call
/// it simply because, for example, a read succeeded; it should be called
/// when a read is observed to block.
///
/// [`drop`]: method@std::mem::drop
pub fn clear_ready(&mut self) {
if let Some(event) = self.event.take() {
self.async_fd.registration.clear_readiness(event);
}
}
/// Indicates to tokio that the file descriptor no longer has a specific readiness.
/// The internal readiness flag will be cleared, and tokio will wait for the
/// next edge-triggered readiness notification from the OS.
///
/// This function is useful in combination with the [`AsyncFd::ready`] method when a
/// combined interest like `Interest::READABLE | Interest::WRITABLE` is used.
///
/// It is critical that this function not be called unless your code
/// _actually observes_ that the file descriptor is _not_ ready for the provided `Ready`.
/// Do not call it simply because, for example, a read succeeded; it should be called
/// when a read is observed to block. Only clear the specific readiness that is observed to
/// block. For example when a read blocks when using a combined interest,
/// only clear `Ready::READABLE`.
///
/// # Examples
///
/// Concurrently read and write to a [`std::net::TcpStream`] on the same task without
/// splitting.
///
/// ```no_run
/// use std::error::Error;
/// use std::io;
/// use std::io::{Read, Write};
/// use std::net::TcpStream;
/// use tokio::io::unix::AsyncFd;
/// use tokio::io::{Interest, Ready};
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080")?;
/// stream.set_nonblocking(true)?;
/// let stream = AsyncFd::new(stream)?;
///
/// loop {
/// let mut guard = stream
/// .ready(Interest::READABLE | Interest::WRITABLE)
/// .await?;
///
/// if guard.ready().is_readable() {
/// let mut data = vec![0; 1024];
/// // Try to read data, this may still fail with `WouldBlock`
/// // if the readiness event is a false positive.
/// match stream.get_ref().read(&mut data) {
/// Ok(n) => {
/// println!("read {} bytes", n);
/// }
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
/// // a read has blocked, but a write might still succeed.
/// // clear only the read readiness.
/// guard.clear_ready_matching(Ready::READABLE);
/// continue;
/// }
/// Err(e) => {
/// return Err(e.into());
/// }
/// }
/// }
///
/// if guard.ready().is_writable() {
/// // Try to write data, this may still fail with `WouldBlock`
/// // if the readiness event is a false positive.
/// match stream.get_ref().write(b"hello world") {
/// Ok(n) => {
/// println!("write {} bytes", n);
/// }
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
/// // a write has blocked, but a read might still succeed.
/// // clear only the write readiness.
/// guard.clear_ready_matching(Ready::WRITABLE);
/// continue;
/// }
/// Err(e) => {
/// return Err(e.into());
/// }
/// }
/// }
/// }
/// }
/// ```
pub fn clear_ready_matching(&mut self, ready: Ready) {
if let Some(mut event) = self.event.take() {
self.async_fd
.registration
.clear_readiness(event.with_ready(ready));
// the event is no longer ready for the readiness that was just cleared
event.ready = event.ready - ready;
if !event.ready.is_empty() {
self.event = Some(event);
}
}
}
/// This method should be invoked when you intentionally want to keep the
/// ready flag asserted.
///
@@ -556,6 +939,20 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
// no-op
}
/// Get the [`Ready`] value associated with this guard.
///
/// This method will return the empty readiness state if
/// [`AsyncFdReadyGuard::clear_ready`] has been called on
/// the guard.
///
/// [`Ready`]: crate::io::Ready
pub fn ready(&self) -> Ready {
match &self.event {
Some(event) => event.ready,
None => Ready::EMPTY,
}
}
/// Performs the provided IO operation.
///
/// If `f` returns a [`WouldBlock`] error, the readiness state associated
@@ -571,6 +968,43 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
/// `AsyncFdReadyGuard` no longer expresses the readiness state that was queried to
/// create this `AsyncFdReadyGuard`.
///
/// # Examples
///
/// This example sends some bytes to the inner [`std::net::UdpSocket`]. Waiting
/// for write-readiness and retrying when the send operation does block are explicit.
/// This example can be written more succinctly using [`AsyncFd::async_io`].
///
/// ```no_run
/// use tokio::io::unix::AsyncFd;
///
/// use std::io;
/// use std::net::UdpSocket;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let socket = UdpSocket::bind("0.0.0.0:8080")?;
/// socket.set_nonblocking(true)?;
/// let async_fd = AsyncFd::new(socket)?;
///
/// let written = loop {
/// let mut guard = async_fd.writable().await?;
/// match guard.try_io(|inner| inner.get_ref().send(&[1, 2])) {
/// Ok(result) => {
/// break result?;
/// }
/// Err(_would_block) => {
/// // try_io already cleared the file descriptor's readiness state
/// continue;
/// }
/// }
/// };
///
/// println!("wrote {written} bytes");
///
/// Ok(())
/// }
/// ```
///
/// [`WouldBlock`]: std::io::ErrorKind::WouldBlock
// Alias for old name in 0.x
#[cfg_attr(docsrs, doc(alias = "with_io"))]
@@ -604,22 +1038,117 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
}
impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
/// Indicates to tokio that the file descriptor is no longer ready. The
/// internal readiness flag will be cleared, and tokio will wait for the
/// Indicates to tokio that the file descriptor is no longer ready. All
/// internal readiness flags will be cleared, and tokio will wait for the
/// next edge-triggered readiness notification from the OS.
///
/// This function is commonly used with guards returned by [`AsyncFd::readable_mut`] and
/// [`AsyncFd::writable_mut`].
///
/// It is critical that this function not be called unless your code
/// _actually observes_ that the file descriptor is _not_ ready. Do not call
/// it simply because, for example, a read succeeded; it should be called
/// when a read is observed to block.
///
/// [`drop`]: method@std::mem::drop
pub fn clear_ready(&mut self) {
if let Some(event) = self.event.take() {
self.async_fd.registration.clear_readiness(event);
}
}
/// Indicates to tokio that the file descriptor no longer has a specific readiness.
/// The internal readiness flag will be cleared, and tokio will wait for the
/// next edge-triggered readiness notification from the OS.
///
/// This function is useful in combination with the [`AsyncFd::ready_mut`] method when a
/// combined interest like `Interest::READABLE | Interest::WRITABLE` is used.
///
/// It is critical that this function not be called unless your code
/// _actually observes_ that the file descriptor is _not_ ready for the provided `Ready`.
/// Do not call it simply because, for example, a read succeeded; it should be called
/// when a read is observed to block. Only clear the specific readiness that is observed to
/// block. For example when a read blocks when using a combined interest,
/// only clear `Ready::READABLE`.
///
/// # Examples
///
/// Concurrently read and write to a [`std::net::TcpStream`] on the same task without
/// splitting.
///
/// ```no_run
/// use std::error::Error;
/// use std::io;
/// use std::io::{Read, Write};
/// use std::net::TcpStream;
/// use tokio::io::unix::AsyncFd;
/// use tokio::io::{Interest, Ready};
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080")?;
/// stream.set_nonblocking(true)?;
/// let mut stream = AsyncFd::new(stream)?;
///
/// loop {
/// let mut guard = stream
/// .ready_mut(Interest::READABLE | Interest::WRITABLE)
/// .await?;
///
/// if guard.ready().is_readable() {
/// let mut data = vec![0; 1024];
/// // Try to read data, this may still fail with `WouldBlock`
/// // if the readiness event is a false positive.
/// match guard.get_inner_mut().read(&mut data) {
/// Ok(n) => {
/// println!("read {} bytes", n);
/// }
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
/// // a read has blocked, but a write might still succeed.
/// // clear only the read readiness.
/// guard.clear_ready_matching(Ready::READABLE);
/// continue;
/// }
/// Err(e) => {
/// return Err(e.into());
/// }
/// }
/// }
///
/// if guard.ready().is_writable() {
/// // Try to write data, this may still fail with `WouldBlock`
/// // if the readiness event is a false positive.
/// match guard.get_inner_mut().write(b"hello world") {
/// Ok(n) => {
/// println!("write {} bytes", n);
/// }
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
/// // a write has blocked, but a read might still succeed.
/// // clear only the write readiness.
/// guard.clear_ready_matching(Ready::WRITABLE);
/// continue;
/// }
/// Err(e) => {
/// return Err(e.into());
/// }
/// }
/// }
/// }
/// }
/// ```
pub fn clear_ready_matching(&mut self, ready: Ready) {
if let Some(mut event) = self.event.take() {
self.async_fd
.registration
.clear_readiness(event.with_ready(ready));
// the event is no longer ready for the readiness that was just cleared
event.ready = event.ready - ready;
if !event.ready.is_empty() {
self.event = Some(event);
}
}
}
/// This method should be invoked when you intentionally want to keep the
/// ready flag asserted.
///
@@ -629,6 +1158,20 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
// no-op
}
/// Get the [`Ready`] value associated with this guard.
///
/// This method will return the empty readiness state if
/// [`AsyncFdReadyGuard::clear_ready`] has been called on
/// the guard.
///
/// [`Ready`]: super::Ready
pub fn ready(&self) -> Ready {
match &self.event {
Some(event) => event.ready,
None => Ready::EMPTY,
}
}
/// Performs the provided IO operation.
///
/// If `f` returns a [`WouldBlock`] error, the readiness state associated
+26
View File
@@ -44,6 +44,11 @@ impl Interest {
/// Writable interest includes write-closed events.
pub const WRITABLE: Interest = Interest(mio::Interest::WRITABLE);
/// Returns a `Interest` set representing priority completion interests.
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
pub const PRIORITY: Interest = Interest(mio::Interest::PRIORITY);
/// Returns true if the value includes readable interest.
///
/// # Examples
@@ -78,6 +83,25 @@ impl Interest {
self.0.is_writable()
}
/// Returns true if the value includes priority interest.
///
/// # Examples
///
/// ```
/// use tokio::io::Interest;
///
/// assert!(!Interest::READABLE.is_priority());
/// assert!(Interest::PRIORITY.is_priority());
///
/// let both = Interest::READABLE | Interest::PRIORITY;
/// assert!(both.is_priority());
/// ```
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
pub const fn is_priority(self) -> bool {
self.0.is_priority()
}
/// Add together two `Interest` values.
///
/// This function works from a `const` context.
@@ -104,6 +128,8 @@ impl Interest {
match self {
Interest::READABLE => Ready::READABLE | Ready::READ_CLOSED,
Interest::WRITABLE => Ready::WRITABLE | Ready::WRITE_CLOSED,
#[cfg(any(target_os = "linux", target_os = "android"))]
Interest::PRIORITY => Ready::PRIORITY | Ready::READ_CLOSED,
_ => Ready::EMPTY,
}
}
+20 -14
View File
@@ -130,19 +130,23 @@
//! other words, these types must never block the thread, and instead the
//! current task is notified when the I/O resource is ready.
//!
//! ## Conversion to and from Sink/Stream
//! ## Conversion to and from Stream/Sink
//!
//! It is often convenient to encapsulate the reading and writing of
//! bytes and instead work with a [`Sink`] or [`Stream`] of some data
//! type that is encoded as bytes and/or decoded from bytes. Tokio
//! provides some utility traits in the [tokio-util] crate that
//! abstract the asynchronous buffering that is required and allows
//! you to write [`Encoder`] and [`Decoder`] functions working with a
//! buffer of bytes, and then use that ["codec"] to transform anything
//! that implements [`AsyncRead`] and [`AsyncWrite`] into a `Sink`/`Stream` of
//! your structured data.
//! It is often convenient to encapsulate the reading and writing of bytes in a
//! [`Stream`] or [`Sink`] of data.
//!
//! [tokio-util]: https://docs.rs/tokio-util/0.6/tokio_util/codec/index.html
//! Tokio provides simple wrappers for converting [`AsyncRead`] to [`Stream`]
//! and vice-versa in the [tokio-util] crate, see [`ReaderStream`] and
//! [`StreamReader`].
//!
//! There are also utility traits that abstract the asynchronous buffering
//! necessary to write your own adaptors for encoding and decoding bytes to/from
//! your structured data, allowing to transform something that implements
//! [`AsyncRead`]/[`AsyncWrite`] into a [`Stream`]/[`Sink`], see [`Decoder`] and
//! [`Encoder`] in the [tokio-util::codec] module.
//!
//! [tokio-util]: https://docs.rs/tokio-util
//! [tokio-util::codec]: https://docs.rs/tokio-util/latest/tokio_util/codec/index.html
//!
//! # Standard input and output
//!
@@ -167,9 +171,11 @@
//! [`AsyncWrite`]: trait@AsyncWrite
//! [`AsyncReadExt`]: trait@AsyncReadExt
//! [`AsyncWriteExt`]: trait@AsyncWriteExt
//! ["codec"]: https://docs.rs/tokio-util/0.6/tokio_util/codec/index.html
//! [`Encoder`]: https://docs.rs/tokio-util/0.6/tokio_util/codec/trait.Encoder.html
//! [`Decoder`]: https://docs.rs/tokio-util/0.6/tokio_util/codec/trait.Decoder.html
//! ["codec"]: https://docs.rs/tokio-util/latest/tokio_util/codec/index.html
//! [`Encoder`]: https://docs.rs/tokio-util/latest/tokio_util/codec/trait.Encoder.html
//! [`Decoder`]: https://docs.rs/tokio-util/latest/tokio_util/codec/trait.Decoder.html
//! [`ReaderStream`]: https://docs.rs/tokio-util/latest/tokio_util/io/struct.ReaderStream.html
//! [`StreamReader`]: https://docs.rs/tokio-util/latest/tokio_util/io/struct.StreamReader.html
//! [`Error`]: struct@Error
//! [`ErrorKind`]: enum@ErrorKind
//! [`Result`]: type@Result
+3
View File
@@ -73,6 +73,9 @@ cfg_io_driver! {
impl<E: Source> PollEvented<E> {
/// Creates a new `PollEvented` associated with the default reactor.
///
/// The returned `PollEvented` has readable and writable interests. For more control, use
/// [`Self::new_with_interest`].
///
/// # Panics
///
/// This function panics if thread-local runtime is not set.
+27
View File
@@ -270,6 +270,33 @@ impl<'a> ReadBuf<'a> {
}
}
#[cfg(feature = "io-util")]
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
unsafe impl<'a> bytes::BufMut for ReadBuf<'a> {
fn remaining_mut(&self) -> usize {
self.remaining()
}
// SAFETY: The caller guarantees that at least `cnt` unfilled bytes have been initialized.
unsafe fn advance_mut(&mut self, cnt: usize) {
self.assume_init(cnt);
self.advance(cnt);
}
fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
// SAFETY: No region of `unfilled` will be deinitialized because it is
// exposed as an `UninitSlice`, whose API guarantees that the memory is
// never deinitialized.
let unfilled = unsafe { self.unfilled_mut() };
let len = unfilled.len();
let ptr = unfilled.as_mut_ptr() as *mut u8;
// SAFETY: The pointer is valid for `len` bytes because it comes from a
// slice of that length.
unsafe { bytes::buf::UninitSlice::from_raw_parts_mut(ptr, len) }
}
}
impl fmt::Debug for ReadBuf<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReadBuf")
+51 -4
View File
@@ -7,6 +7,8 @@ const READABLE: usize = 0b0_01;
const WRITABLE: usize = 0b0_10;
const READ_CLOSED: usize = 0b0_0100;
const WRITE_CLOSED: usize = 0b0_1000;
#[cfg(any(target_os = "linux", target_os = "android"))]
const PRIORITY: usize = 0b1_0000;
/// Describes the readiness state of an I/O resources.
///
@@ -31,7 +33,17 @@ impl Ready {
/// Returns a `Ready` representing write closed readiness.
pub const WRITE_CLOSED: Ready = Ready(WRITE_CLOSED);
/// Returns a `Ready` representing priority readiness.
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
pub const PRIORITY: Ready = Ready(PRIORITY);
/// Returns a `Ready` representing readiness for all operations.
#[cfg(any(target_os = "linux", target_os = "android"))]
pub const ALL: Ready = Ready(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED | PRIORITY);
/// Returns a `Ready` representing readiness for all operations.
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub const ALL: Ready = Ready(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED);
// Must remain crate-private to avoid adding a public dependency on Mio.
@@ -65,6 +77,13 @@ impl Ready {
ready |= Ready::WRITE_CLOSED;
}
#[cfg(any(target_os = "linux", target_os = "android"))]
{
if event.is_priority() {
ready |= Ready::PRIORITY;
}
}
ready
}
@@ -144,6 +163,23 @@ impl Ready {
self.contains(Ready::WRITE_CLOSED)
}
/// Returns `true` if the value includes priority `readiness`.
///
/// # Examples
///
/// ```
/// use tokio::io::Ready;
///
/// assert!(!Ready::EMPTY.is_priority());
/// assert!(!Ready::WRITABLE.is_priority());
/// assert!(Ready::PRIORITY.is_priority());
/// ```
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
pub fn is_priority(self) -> bool {
self.contains(Ready::PRIORITY)
}
/// Returns true if `self` is a superset of `other`.
///
/// `other` may represent more than one readiness operations, in which case
@@ -191,6 +227,12 @@ cfg_io_readiness! {
ready |= Ready::WRITE_CLOSED;
}
#[cfg(any(target_os = "linux", target_os = "android"))]
if interest.is_priority() {
ready |= Ready::PRIORITY;
ready |= Ready::READ_CLOSED;
}
ready
}
@@ -240,11 +282,16 @@ impl ops::Sub<Ready> for Ready {
impl fmt::Debug for Ready {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("Ready")
.field("is_readable", &self.is_readable())
let mut fmt = fmt.debug_struct("Ready");
fmt.field("is_readable", &self.is_readable())
.field("is_writable", &self.is_writable())
.field("is_read_closed", &self.is_read_closed())
.field("is_write_closed", &self.is_write_closed())
.finish()
.field("is_write_closed", &self.is_write_closed());
#[cfg(any(target_os = "linux", target_os = "android"))]
fmt.field("is_priority", &self.is_priority());
fmt.finish()
}
}
+34 -7
View File
@@ -74,16 +74,43 @@ cfg_io_std! {
}
#[cfg(unix)]
impl std::os::unix::io::AsRawFd for Stderr {
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
std::io::stderr().as_raw_fd()
mod sys {
#[cfg(not(tokio_no_as_fd))]
use std::os::unix::io::{AsFd, BorrowedFd};
use std::os::unix::io::{AsRawFd, RawFd};
use super::Stderr;
impl AsRawFd for Stderr {
fn as_raw_fd(&self) -> RawFd {
std::io::stderr().as_raw_fd()
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsFd for Stderr {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
}
}
}
#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for Stderr {
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
std::io::stderr().as_raw_handle()
cfg_windows! {
#[cfg(not(tokio_no_as_fd))]
use crate::os::windows::io::{AsHandle, BorrowedHandle};
use crate::os::windows::io::{AsRawHandle, RawHandle};
impl AsRawHandle for Stderr {
fn as_raw_handle(&self) -> RawHandle {
std::io::stderr().as_raw_handle()
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsHandle for Stderr {
fn as_handle(&self) -> BorrowedHandle<'_> {
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
}
}
}
+34 -7
View File
@@ -49,16 +49,43 @@ cfg_io_std! {
}
#[cfg(unix)]
impl std::os::unix::io::AsRawFd for Stdin {
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
std::io::stdin().as_raw_fd()
mod sys {
#[cfg(not(tokio_no_as_fd))]
use std::os::unix::io::{AsFd, BorrowedFd};
use std::os::unix::io::{AsRawFd, RawFd};
use super::Stdin;
impl AsRawFd for Stdin {
fn as_raw_fd(&self) -> RawFd {
std::io::stdin().as_raw_fd()
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsFd for Stdin {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
}
}
}
#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for Stdin {
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
std::io::stdin().as_raw_handle()
cfg_windows! {
#[cfg(not(tokio_no_as_fd))]
use crate::os::windows::io::{AsHandle, BorrowedHandle};
use crate::os::windows::io::{AsRawHandle, RawHandle};
impl AsRawHandle for Stdin {
fn as_raw_handle(&self) -> RawHandle {
std::io::stdin().as_raw_handle()
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsHandle for Stdin {
fn as_handle(&self) -> BorrowedHandle<'_> {
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
}
}
}
+34 -7
View File
@@ -73,16 +73,43 @@ cfg_io_std! {
}
#[cfg(unix)]
impl std::os::unix::io::AsRawFd for Stdout {
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
std::io::stdout().as_raw_fd()
mod sys {
#[cfg(not(tokio_no_as_fd))]
use std::os::unix::io::{AsFd, BorrowedFd};
use std::os::unix::io::{AsRawFd, RawFd};
use super::Stdout;
impl AsRawFd for Stdout {
fn as_raw_fd(&self) -> RawFd {
std::io::stdout().as_raw_fd()
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsFd for Stdout {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
}
}
}
#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for Stdout {
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
std::io::stdout().as_raw_handle()
cfg_windows! {
#[cfg(not(tokio_no_as_fd))]
use crate::os::windows::io::{AsHandle, BorrowedHandle};
use crate::os::windows::io::{AsRawHandle, RawHandle};
impl AsRawHandle for Stdout {
fn as_raw_handle(&self) -> RawHandle {
std::io::stdout().as_raw_handle()
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsHandle for Stdout {
fn as_handle(&self) -> BorrowedHandle<'_> {
unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) }
}
}
}
+1 -1
View File
@@ -146,7 +146,7 @@ cfg_io_util! {
/// [`next_line`] method.
/// * Use [`tokio_util::codec::LinesCodec`][LinesCodec].
///
/// [LinesCodec]: https://docs.rs/tokio-util/0.6/tokio_util/codec/struct.LinesCodec.html
/// [LinesCodec]: https://docs.rs/tokio-util/latest/tokio_util/codec/struct.LinesCodec.html
/// [`read_until`]: Self::read_until
/// [`lines`]: Self::lines
/// [`next_line`]: crate::io::Lines::next_line
+9 -1
View File
@@ -153,14 +153,22 @@ cfg_io_util! {
///
/// This function returns a future that will continuously read data from
/// `reader` and then write it into `writer` in a streaming fashion until
/// `reader` returns EOF.
/// `reader` returns EOF or fails.
///
/// On success, the total number of bytes that were copied from `reader` to
/// `writer` is returned.
///
/// This is an asynchronous version of [`std::io::copy`][std].
///
/// A heap-allocated copy buffer with 8 KB is created to take data from the
/// reader to the writer, check [`copy_buf`] if you want an alternative for
/// [`AsyncBufRead`]. You can use `copy_buf` with [`BufReader`] to change the
/// buffer capacity.
///
/// [std]: std::io::copy
/// [`copy_buf`]: crate::io::copy_buf
/// [`AsyncBufRead`]: crate::io::AsyncBufRead
/// [`BufReader`]: crate::io::BufReader
///
/// # Errors
///
+7 -1
View File
@@ -24,11 +24,17 @@ cfg_io_util! {
///
/// This function returns a future that will continuously read data from
/// `reader` and then write it into `writer` in a streaming fashion until
/// `reader` returns EOF.
/// `reader` returns EOF or fails.
///
/// On success, the total number of bytes that were copied from `reader` to
/// `writer` is returned.
///
/// This is a [`tokio::io::copy`] alternative for [`AsyncBufRead`] readers
/// with no extra buffer allocation, since [`AsyncBufRead`] allow access
/// to the reader's inner buffer.
///
/// [`tokio::io::copy`]: crate::io::copy
/// [`AsyncBufRead`]: crate::io::AsyncBufRead
///
/// # Errors
///
+2
View File
@@ -53,6 +53,7 @@ impl AsyncRead for Empty {
cx: &mut Context<'_>,
_: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
ready!(crate::trace::trace_leaf(cx));
ready!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(()))
}
@@ -61,6 +62,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!(poll_proceed_and_make_progress(cx));
Poll::Ready(Ok(&[]))
}
+4
View File
@@ -233,6 +233,7 @@ impl AsyncRead for Pipe {
cx: &mut task::Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
ready!(crate::trace::trace_leaf(cx));
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
let ret = self.poll_read_internal(cx, buf);
@@ -249,6 +250,7 @@ impl AsyncRead for Pipe {
cx: &mut task::Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
ready!(crate::trace::trace_leaf(cx));
self.poll_read_internal(cx, buf)
}
}
@@ -261,6 +263,7 @@ impl AsyncWrite for Pipe {
cx: &mut task::Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
ready!(crate::trace::trace_leaf(cx));
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
let ret = self.poll_write_internal(cx, buf);
@@ -277,6 +280,7 @@ impl AsyncWrite for Pipe {
cx: &mut task::Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
ready!(crate::trace::trace_leaf(cx));
self.poll_write_internal(cx, buf)
}
}
+42 -11
View File
@@ -1,11 +1,11 @@
use crate::io::util::vec_with_initialized::{into_read_buf_parts, VecU8, VecWithInitialized};
use crate::io::AsyncRead;
use crate::io::{AsyncRead, ReadBuf};
use pin_project_lite::pin_project;
use std::future::Future;
use std::io;
use std::marker::PhantomPinned;
use std::mem;
use std::mem::{self, MaybeUninit};
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -67,16 +67,47 @@ fn poll_read_to_end<V: VecU8, R: AsyncRead + ?Sized>(
// has 4 bytes while still making large reads if the reader does have a ton
// of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every
// time is 4,500 times (!) slower than this if the reader has a very small
// amount of data to return.
buf.reserve(32);
// amount of data to return. When the vector is full with its starting
// capacity, we first try to read into a small buffer to see if we reached
// an EOF. This only happens when the starting capacity is >= NUM_BYTES, since
// we allocate at least NUM_BYTES each time. This avoids the unnecessary
// allocation that we attempt before reading into the vector.
const NUM_BYTES: usize = 32;
let try_small_read = buf.try_small_read_first(NUM_BYTES);
// Get a ReadBuf into the vector.
let mut read_buf = buf.get_read_buf();
let mut read_buf;
let poll_result;
let filled_before = read_buf.filled().len();
let poll_result = read.poll_read(cx, &mut read_buf);
let filled_after = read_buf.filled().len();
let n = filled_after - filled_before;
let n = if try_small_read {
// Read some bytes using a small read.
let mut small_buf: [MaybeUninit<u8>; NUM_BYTES] = [MaybeUninit::uninit(); NUM_BYTES];
let mut small_read_buf = ReadBuf::uninit(&mut small_buf);
poll_result = read.poll_read(cx, &mut small_read_buf);
let to_write = small_read_buf.filled();
// Ensure we have enough space to fill our vector with what we read.
read_buf = buf.get_read_buf();
if to_write.len() > read_buf.remaining() {
buf.reserve(NUM_BYTES);
read_buf = buf.get_read_buf();
}
read_buf.put_slice(to_write);
to_write.len()
} else {
// Ensure we have enough space for reading.
buf.reserve(NUM_BYTES);
read_buf = buf.get_read_buf();
// Read data directly into vector.
let filled_before = read_buf.filled().len();
poll_result = read.poll_read(cx, &mut read_buf);
// Compute the number of bytes read.
read_buf.filled().len() - filled_before
};
// Update the length of the vector using the result of poll_read.
let read_buf_parts = into_read_buf_parts(read_buf);
@@ -87,11 +118,11 @@ fn poll_read_to_end<V: VecU8, R: AsyncRead + ?Sized>(
// In this case, nothing should have been read. However we still
// update the vector in case the poll_read call initialized parts of
// the vector's unused capacity.
debug_assert_eq!(filled_before, filled_after);
debug_assert_eq!(n, 0);
Poll::Pending
}
Poll::Ready(Err(err)) => {
debug_assert_eq!(filled_before, filled_after);
debug_assert_eq!(n, 0);
Poll::Ready(Err(err))
}
Poll::Ready(Ok(())) => Poll::Ready(Ok(n)),
+1
View File
@@ -1,4 +1,5 @@
use crate::io::AsyncBufRead;
use crate::util::memchr;
use pin_project_lite::pin_project;
use std::future::Future;
+11
View File
@@ -28,6 +28,7 @@ pub(crate) struct VecWithInitialized<V> {
// The number of initialized bytes in the vector.
// Always between `vec.len()` and `vec.capacity()`.
num_initialized: usize,
starting_capacity: usize,
}
impl VecWithInitialized<Vec<u8>> {
@@ -47,6 +48,7 @@ where
// to its length are initialized.
Self {
num_initialized: vec.as_mut().len(),
starting_capacity: vec.as_ref().capacity(),
vec,
}
}
@@ -111,6 +113,15 @@ where
vec.set_len(parts.len);
}
}
// Returns a boolean telling the caller to try reading into a small local buffer first if true.
// Doing so would avoid overallocating when vec is filled to capacity and we reached EOF.
pub(crate) fn try_small_read_first(&self, num_bytes: usize) -> bool {
let vec = self.vec.as_ref();
vec.capacity() - vec.len() < num_bytes
&& self.starting_capacity == vec.capacity()
&& self.starting_capacity >= num_bytes
}
}
pub(crate) struct ReadBufParts {
+50
View File
@@ -487,6 +487,21 @@ compile_error!("Tokio's build script has incorrectly detected wasm.");
))]
compile_error!("Only features sync,macros,io-util,rt,time are supported on wasm.");
#[cfg(all(not(tokio_unstable), tokio_taskdump))]
compile_error!("The `tokio_taskdump` feature requires `--cfg tokio_unstable`.");
#[cfg(all(
tokio_taskdump,
not(all(
target_os = "linux",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
))
))]
compile_error!(
"The `tokio_taskdump` feature is only currently supported on \
linux, on `aarch64`, `x86` and `x86_64`."
);
// Includes re-exports used by macros.
//
// This module is not intended to be part of the public API. In general, any
@@ -552,6 +567,40 @@ cfg_time! {
pub mod time;
}
mod trace {
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
cfg_taskdump! {
pub(crate) use crate::runtime::task::trace::trace_leaf;
}
cfg_not_taskdump! {
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn trace_leaf(_: &mut std::task::Context<'_>) -> std::task::Poll<()> {
std::task::Poll::Ready(())
}
}
#[cfg_attr(not(feature = "sync"), allow(dead_code))]
pub(crate) fn async_trace_leaf() -> impl Future<Output = ()> {
struct Trace;
impl Future for Trace {
type Output = ();
#[inline(always)]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
trace_leaf(cx)
}
}
Trace
}
}
mod util;
/// Due to the `Stream` trait's inclusion in `std` landing later than Tokio's 1.0
@@ -658,5 +707,6 @@ cfg_macros! {
#[cfg(test)]
fn is_unpin<T: Unpin>() {}
/// fuzz test (fuzz_linked_list)
#[cfg(fuzzing)]
pub mod fuzz;
+217
View File
@@ -0,0 +1,217 @@
//! A `Barrier` that provides `wait_timeout`.
//!
//! This implementation mirrors that of the Rust standard library.
use crate::loom::sync::{Condvar, Mutex};
use std::fmt;
use std::time::{Duration, Instant};
/// A barrier enables multiple threads to synchronize the beginning
/// of some computation.
///
/// # Examples
///
/// ```
/// use std::sync::{Arc, Barrier};
/// use std::thread;
///
/// let mut handles = Vec::with_capacity(10);
/// let barrier = Arc::new(Barrier::new(10));
/// for _ in 0..10 {
/// let c = Arc::clone(&barrier);
/// // The same messages will be printed together.
/// // You will NOT see any interleaving.
/// handles.push(thread::spawn(move|| {
/// println!("before wait");
/// c.wait();
/// println!("after wait");
/// }));
/// }
/// // Wait for other threads to finish.
/// for handle in handles {
/// handle.join().unwrap();
/// }
/// ```
pub(crate) struct Barrier {
lock: Mutex<BarrierState>,
cvar: Condvar,
num_threads: usize,
}
// The inner state of a double barrier
struct BarrierState {
count: usize,
generation_id: usize,
}
/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads
/// in the [`Barrier`] have rendezvoused.
///
/// # Examples
///
/// ```
/// use std::sync::Barrier;
///
/// let barrier = Barrier::new(1);
/// let barrier_wait_result = barrier.wait();
/// ```
pub(crate) struct BarrierWaitResult(bool);
impl fmt::Debug for Barrier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Barrier").finish_non_exhaustive()
}
}
impl Barrier {
/// Creates a new barrier that can block a given number of threads.
///
/// A barrier will block `n`-1 threads which call [`wait()`] and then wake
/// up all threads at once when the `n`th thread calls [`wait()`].
///
/// [`wait()`]: Barrier::wait
///
/// # Examples
///
/// ```
/// use std::sync::Barrier;
///
/// let barrier = Barrier::new(10);
/// ```
#[must_use]
pub(crate) fn new(n: usize) -> Barrier {
Barrier {
lock: Mutex::new(BarrierState {
count: 0,
generation_id: 0,
}),
cvar: Condvar::new(),
num_threads: n,
}
}
/// Blocks the current thread until all threads have rendezvoused here.
///
/// Barriers are re-usable after all threads have rendezvoused once, and can
/// be used continuously.
///
/// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
/// returns `true` from [`BarrierWaitResult::is_leader()`] when returning
/// from this function, and all other threads will receive a result that
/// will return `false` from [`BarrierWaitResult::is_leader()`].
///
/// # Examples
///
/// ```
/// use std::sync::{Arc, Barrier};
/// use std::thread;
///
/// let mut handles = Vec::with_capacity(10);
/// let barrier = Arc::new(Barrier::new(10));
/// for _ in 0..10 {
/// let c = Arc::clone(&barrier);
/// // The same messages will be printed together.
/// // You will NOT see any interleaving.
/// handles.push(thread::spawn(move|| {
/// println!("before wait");
/// c.wait();
/// println!("after wait");
/// }));
/// }
/// // Wait for other threads to finish.
/// for handle in handles {
/// handle.join().unwrap();
/// }
/// ```
pub(crate) fn wait(&self) -> BarrierWaitResult {
let mut lock = self.lock.lock();
let local_gen = lock.generation_id;
lock.count += 1;
if lock.count < self.num_threads {
// We need a while loop to guard against spurious wakeups.
// https://en.wikipedia.org/wiki/Spurious_wakeup
while local_gen == lock.generation_id {
lock = self.cvar.wait(lock).unwrap();
}
BarrierWaitResult(false)
} else {
lock.count = 0;
lock.generation_id = lock.generation_id.wrapping_add(1);
self.cvar.notify_all();
BarrierWaitResult(true)
}
}
/// Blocks the current thread until all threads have rendezvoused here for
/// at most `timeout` duration.
pub(crate) fn wait_timeout(&self, timeout: Duration) -> Option<BarrierWaitResult> {
// This implementation mirrors `wait`, but with each blocking operation
// replaced by a timeout-amenable alternative.
let deadline = Instant::now() + timeout;
// Acquire `self.lock` with at most `timeout` duration.
let mut lock = loop {
if let Some(guard) = self.lock.try_lock() {
break guard;
} else if Instant::now() > deadline {
return None;
} else {
std::thread::yield_now();
}
};
// Shrink the `timeout` to account for the time taken to acquire `lock`.
let timeout = deadline.saturating_duration_since(Instant::now());
let local_gen = lock.generation_id;
lock.count += 1;
if lock.count < self.num_threads {
// We need a while loop to guard against spurious wakeups.
// https://en.wikipedia.org/wiki/Spurious_wakeup
while local_gen == lock.generation_id {
let (guard, timeout_result) = self.cvar.wait_timeout(lock, timeout).unwrap();
lock = guard;
if timeout_result.timed_out() {
return None;
}
}
Some(BarrierWaitResult(false))
} else {
lock.count = 0;
lock.generation_id = lock.generation_id.wrapping_add(1);
self.cvar.notify_all();
Some(BarrierWaitResult(true))
}
}
}
impl fmt::Debug for BarrierWaitResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BarrierWaitResult")
.field("is_leader", &self.is_leader())
.finish()
}
}
impl BarrierWaitResult {
/// Returns `true` if this thread is the "leader thread" for the call to
/// [`Barrier::wait()`].
///
/// Only one thread will have `true` returned from their result, all other
/// threads will have `false` returned.
///
/// # Examples
///
/// ```
/// use std::sync::Barrier;
///
/// let barrier = Barrier::new(1);
/// let barrier_wait_result = barrier.wait();
/// println!("{:?}", barrier_wait_result.is_leader());
/// ```
#[must_use]
pub(crate) fn is_leader(&self) -> bool {
self.0
}
}
+3
View File
@@ -4,6 +4,7 @@ mod atomic_u16;
mod atomic_u32;
mod atomic_u64;
mod atomic_usize;
mod barrier;
mod mutex;
#[cfg(feature = "parking_lot")]
mod parking_lot;
@@ -76,6 +77,8 @@ pub(crate) mod sync {
pub(crate) use std::sync::atomic::{fence, AtomicBool, AtomicPtr, AtomicU8, Ordering};
}
pub(crate) use super::barrier::Barrier;
}
pub(crate) mod sys {
-31
View File
@@ -1,7 +1,6 @@
//! This module defines a macro that lets you go from a raw pointer to a struct
//! to a raw pointer to a field of the struct.
#[cfg(not(tokio_no_addr_of))]
macro_rules! generate_addr_of_methods {
(
impl<$($gen:ident)*> $struct_name:ty {$(
@@ -21,33 +20,3 @@ macro_rules! generate_addr_of_methods {
)*}
};
}
// The `addr_of_mut!` macro is only available for MSRV at least 1.51.0. This
// version of the macro uses a workaround for older versions of rustc.
#[cfg(tokio_no_addr_of)]
macro_rules! generate_addr_of_methods {
(
impl<$($gen:ident)*> $struct_name:ty {$(
$(#[$attrs:meta])*
$vis:vis unsafe fn $fn_name:ident(self: NonNull<Self>) -> NonNull<$field_type:ty> {
&self$(.$field_name:tt)+
}
)*}
) => {
impl<$($gen)*> $struct_name {$(
$(#[$attrs])*
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
let me = me.as_ptr();
let me_u8 = me as *mut u8;
let field_offset = {
let me_ref = &*me;
let field_ref_u8 = (&me_ref $(.$field_name)+ ) as *const $field_type as *const u8;
field_ref_u8.offset_from(me_u8)
};
::core::ptr::NonNull::new_unchecked(me_u8.offset(field_offset).cast())
}
)*}
};
}
+50
View File
@@ -13,6 +13,18 @@ macro_rules! feature {
}
}
/// Enables Windows-specific code.
/// Use this macro instead of `cfg(windows)` to generate docs properly.
macro_rules! cfg_windows {
($($item:item)*) => {
$(
#[cfg(any(all(doc, docsrs), windows))]
#[cfg_attr(docsrs, doc(cfg(windows)))]
$item
)*
}
}
/// Enables enter::block_on.
macro_rules! cfg_block_on {
($($item:item)*) => {
@@ -361,6 +373,44 @@ macro_rules! cfg_not_rt_multi_thread {
}
}
macro_rules! cfg_taskdump {
($($item:item)*) => {
$(
#[cfg(all(
tokio_unstable,
tokio_taskdump,
feature = "rt",
target_os = "linux",
any(
target_arch = "aarch64",
target_arch = "x86",
target_arch = "x86_64"
)
))]
$item
)*
};
}
macro_rules! cfg_not_taskdump {
($($item:item)*) => {
$(
#[cfg(not(all(
tokio_unstable,
tokio_taskdump,
feature = "rt",
target_os = "linux",
any(
target_arch = "aarch64",
target_arch = "x86",
target_arch = "x86_64"
)
)))]
$item
)*
};
}
macro_rules! cfg_test_util {
($($item:item)*) => {
$(
+1 -5
View File
@@ -1,11 +1,7 @@
macro_rules! if_loom {
($($t:tt)*) => {{
#[cfg(loom)]
const LOOM: bool = true;
#[cfg(not(loom))]
const LOOM: bool = false;
if LOOM {
{
$($t)*
}
}}
-4
View File
@@ -23,10 +23,6 @@ cfg_trace! {
mod trace;
}
#[macro_use]
#[cfg(feature = "rt")]
pub(crate) mod scoped_tls;
cfg_macros! {
#[macro_use]
mod select;
-77
View File
@@ -1,77 +0,0 @@
use crate::loom::thread::LocalKey;
use std::cell::Cell;
use std::marker;
/// Sets a reference as a thread-local.
macro_rules! scoped_thread_local {
($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => (
$(#[$attrs])*
$vis static $name: $crate::macros::scoped_tls::ScopedKey<$ty>
= $crate::macros::scoped_tls::ScopedKey {
inner: {
tokio_thread_local!(static FOO: ::std::cell::Cell<*const ()> = const {
std::cell::Cell::new(::std::ptr::null())
});
&FOO
},
_marker: ::std::marker::PhantomData,
};
)
}
/// Type representing a thread local storage key corresponding to a reference
/// to the type parameter `T`.
pub(crate) struct ScopedKey<T> {
pub(crate) inner: &'static LocalKey<Cell<*const ()>>,
pub(crate) _marker: marker::PhantomData<T>,
}
unsafe impl<T> Sync for ScopedKey<T> {}
impl<T> ScopedKey<T> {
/// Inserts a value into this scoped thread local storage slot for a
/// duration of a closure.
pub(crate) fn set<F, R>(&'static self, t: &T, f: F) -> R
where
F: FnOnce() -> R,
{
struct Reset {
key: &'static LocalKey<Cell<*const ()>>,
val: *const (),
}
impl Drop for Reset {
fn drop(&mut self) {
self.key.with(|c| c.set(self.val));
}
}
let prev = self.inner.with(|c| {
let prev = c.get();
c.set(t as *const _ as *const ());
prev
});
let _reset = Reset {
key: self.inner,
val: prev,
};
f()
}
/// Gets a value out of this scoped variable.
pub(crate) fn with<F, R>(&'static self, f: F) -> R
where
F: FnOnce(Option<&T>) -> R,
{
let val = self.inner.with(|c| c.get());
if val.is_null() {
f(None)
} else {
unsafe { f(Some(&*(val as *const T))) }
}
}
}
+7
View File
@@ -131,6 +131,13 @@
/// correctly even if it is restarted while waiting at an `.await`, then it is
/// cancellation safe.
///
/// Cancellation safety can be defined in the following way: If you have a
/// future that has not yet completed, then it must be a no-op to drop that
/// future and recreate it. This definition is motivated by the situation where
/// a `select!` is used in a loop. Without this guarantee, you would lose your
/// progress when another branch completes and you restart the `select!` by
/// going around the loop.
///
/// Be aware that cancelling something that is not cancellation safe is not
/// necessarily wrong. For example, if you are cancelling a task because the
/// application is shutting down, then you probably don't care that partially
+25 -5
View File
@@ -5,7 +5,6 @@ cfg_not_wasi! {
use crate::net::{to_socket_addrs, ToSocketAddrs};
}
use std::convert::TryFrom;
use std::fmt;
use std::io;
use std::net::{self, SocketAddr};
@@ -407,6 +406,13 @@ mod sys {
self.io.as_raw_fd()
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsFd for TcpListener {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
}
}
}
cfg_unstable! {
@@ -420,17 +426,31 @@ cfg_unstable! {
self.io.as_raw_fd()
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsFd for TcpListener {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
}
}
}
}
#[cfg(windows)]
mod sys {
use super::TcpListener;
use std::os::windows::prelude::*;
cfg_windows! {
use crate::os::windows::io::{AsRawSocket, RawSocket};
#[cfg(not(tokio_no_as_fd))]
use crate::os::windows::io::{AsSocket, BorrowedSocket};
impl AsRawSocket for TcpListener {
fn as_raw_socket(&self) -> RawSocket {
self.io.as_raw_socket()
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsSocket for TcpListener {
fn as_socket(&self) -> BorrowedSocket<'_> {
unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) }
}
}
}
+87 -23
View File
@@ -4,12 +4,18 @@ use std::fmt;
use std::io;
use std::net::SocketAddr;
#[cfg(all(unix, not(tokio_no_as_fd)))]
use std::os::unix::io::{AsFd, BorrowedFd};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
use std::time::Duration;
cfg_windows! {
use crate::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
#[cfg(not(tokio_no_as_fd))]
use crate::os::windows::io::{AsSocket, BorrowedSocket};
}
cfg_net! {
/// A TCP socket that has not yet been converted to a `TcpStream` or
/// `TcpListener`.
@@ -398,6 +404,51 @@ impl TcpSocket {
self.inner.linger()
}
/// Sets the value of the `TCP_NODELAY` option on this socket.
///
/// If set, this option disables the Nagle algorithm. This means that segments are always
/// sent as soon as possible, even if there is only a small amount of data. When not set,
/// data is buffered until there is a sufficient amount to send out, thereby avoiding
/// the frequent sending of small packets.
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpSocket;
///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
/// let socket = TcpSocket::new_v4()?;
///
/// println!("{:?}", socket.nodelay()?);
/// # Ok(())
/// # }
/// ```
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.inner.set_nodelay(nodelay)
}
/// Gets the value of the `TCP_NODELAY` option on this socket.
///
/// For more information about this option, see [`set_nodelay`].
///
/// [`set_nodelay`]: TcpSocket::set_nodelay
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpSocket;
///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
/// let stream = TcpSocket::new_v4()?;
///
/// stream.set_nodelay(true)?;
/// # Ok(())
/// # }
/// ```
pub fn nodelay(&self) -> io::Result<bool> {
self.inner.nodelay()
}
/// Gets the value of the `IP_TOS` option for this socket.
///
/// For more information about this option, see [`set_tos`].
@@ -737,6 +788,13 @@ impl AsRawFd for TcpSocket {
}
}
#[cfg(all(unix, not(tokio_no_as_fd)))]
impl AsFd for TcpSocket {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
}
}
#[cfg(unix)]
impl FromRawFd for TcpSocket {
/// Converts a `RawFd` to a `TcpSocket`.
@@ -758,30 +816,36 @@ impl IntoRawFd for TcpSocket {
}
}
#[cfg(windows)]
impl IntoRawSocket for TcpSocket {
fn into_raw_socket(self) -> RawSocket {
self.inner.into_raw_socket()
cfg_windows! {
impl IntoRawSocket for TcpSocket {
fn into_raw_socket(self) -> RawSocket {
self.inner.into_raw_socket()
}
}
}
#[cfg(windows)]
impl AsRawSocket for TcpSocket {
fn as_raw_socket(&self) -> RawSocket {
self.inner.as_raw_socket()
impl AsRawSocket for TcpSocket {
fn as_raw_socket(&self) -> RawSocket {
self.inner.as_raw_socket()
}
}
}
#[cfg(windows)]
impl FromRawSocket for TcpSocket {
/// Converts a `RawSocket` to a `TcpStream`.
///
/// # Notes
///
/// The caller is responsible for ensuring that the socket is in
/// non-blocking mode.
unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
let inner = socket2::Socket::from_raw_socket(socket);
TcpSocket { inner }
#[cfg(not(tokio_no_as_fd))]
impl AsSocket for TcpSocket {
fn as_socket(&self) -> BorrowedSocket<'_> {
unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) }
}
}
impl FromRawSocket for TcpSocket {
/// Converts a `RawSocket` to a `TcpStream`.
///
/// # Notes
///
/// The caller is responsible for ensuring that the socket is in
/// non-blocking mode.
unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
let inner = socket2::Socket::from_raw_socket(socket);
TcpSocket { inner }
}
}
}
+12 -6
View File
@@ -141,9 +141,9 @@ impl ReadHalf<'_> {
/// Waits for any of the requested ready states.
///
/// This function is usually paired with `try_read()` or `try_write()`. It
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
/// This function is usually paired with [`try_read()`]. It can be used instead
/// of [`readable()`] to check the returned ready set for [`Ready::READABLE`]
/// and [`Ready::READ_CLOSED`] events.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
@@ -153,6 +153,9 @@ impl ReadHalf<'_> {
///
/// This function is equivalent to [`TcpStream::ready`].
///
/// [`try_read()`]: Self::try_read
/// [`readable()`]: Self::readable
///
/// # Cancel safety
///
/// This method is cancel safe. Once a readiness event occurs, the method
@@ -275,9 +278,9 @@ impl ReadHalf<'_> {
impl WriteHalf<'_> {
/// Waits for any of the requested ready states.
///
/// This function is usually paired with `try_read()` or `try_write()`. It
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
/// This function is usually paired with [`try_write()`]. It can be used instead
/// of [`writable()`] to check the returned ready set for [`Ready::WRITABLE`]
/// and [`Ready::WRITE_CLOSED`] events.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
@@ -287,6 +290,9 @@ impl WriteHalf<'_> {
///
/// This function is equivalent to [`TcpStream::ready`].
///
/// [`try_write()`]: Self::try_write
/// [`writable()`]: Self::writable
///
/// # Cancel safety
///
/// This method is cancel safe. Once a readiness event occurs, the method
+12 -6
View File
@@ -196,9 +196,9 @@ impl OwnedReadHalf {
/// Waits for any of the requested ready states.
///
/// This function is usually paired with `try_read()` or `try_write()`. It
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
/// This function is usually paired with [`try_read()`]. It can be used instead
/// of [`readable()`] to check the returned ready set for [`Ready::READABLE`]
/// and [`Ready::READ_CLOSED`] events.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
@@ -208,6 +208,9 @@ impl OwnedReadHalf {
///
/// This function is equivalent to [`TcpStream::ready`].
///
/// [`try_read()`]: Self::try_read
/// [`readable()`]: Self::readable
///
/// # Cancel safety
///
/// This method is cancel safe. Once a readiness event occurs, the method
@@ -357,9 +360,9 @@ impl OwnedWriteHalf {
/// Waits for any of the requested ready states.
///
/// This function is usually paired with `try_read()` or `try_write()`. It
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
/// This function is usually paired with [`try_write()`]. It can be used instead
/// of [`writable()`] to check the returned ready set for [`Ready::WRITABLE`]
/// and [`Ready::WRITE_CLOSED`] events.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
@@ -369,6 +372,9 @@ impl OwnedWriteHalf {
///
/// This function is equivalent to [`TcpStream::ready`].
///
/// [`try_write()`]: Self::try_write
/// [`writable()`]: Self::writable
///
/// # Cancel safety
///
/// This method is cancel safe. Once a readiness event occurs, the method

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