Compare commits

...
161 Commits
Author SHA1 Message Date
Carl Lerche 7147042534 chore: prepare Tokio v1.8.2 release 2021-07-19 11:16:26 -07:00
Diggory Blake 634fcac2d9 runtime: drop future when polling cancelled future (#3965) 2021-07-19 11:16:26 -07:00
Carl Lerche e0c527f383 chore: prepare Tokio v1.8.1 release 2021-07-06 15:37:17 -07:00
Carl Lerche 1160e8864c Merge branch 'tokio-1.7.x' into merge-1.7.2 2021-07-06 15:36:24 -07:00
Carl Lerche 998a125717 chore: prepare Tokio 1.7.2 release 2021-07-06 14:35:36 -07:00
Carl Lerche cbfdc9d69e Merge branch 'tokio-1.6.x' into merge-1.6.3 2021-07-06 14:34:34 -07:00
Carl Lerche 08337c5f79 chore: prepare Tokio v1.6.3 2021-07-06 13:45:47 -07:00
Carl Lerche 87f4969fbc Merge branch 'tokio-1.5.x' into merge-1.5.x 2021-07-06 13:44:11 -07:00
Alice Ryhl e385108920 rt: remotely abort tasks on JoinHandle::abort (#3934) 2021-07-06 13:41:25 -07:00
Alice Ryhl 677107d8d9 chore: prepare Tokio v1.8.0 (#3912) 2021-07-02 22:21:10 +02:00
Alice Ryhl c8ecfc894d sync: fix watch borrow_and_update (#3913) 2021-07-02 10:14:34 -07:00
Taiki Endo 08ed41f339 chore: fix typos (#3907) 2021-07-01 02:06:56 +09:00
Alice Ryhl 90e1935c48 test: test with tracing (#3906) 2021-06-30 09:40:10 -07:00
Alice Ryhl b877629cb1 net: handle HUP event with UnixStream (#3898)
Fixes: #3879
2021-06-30 09:39:06 -07:00
Mikhail Zabaluev 0531549b6e io: efficient implementation of vectored writes for BufWriter (#3163) 2021-06-29 20:26:47 +02:00
Alice Ryhl 38204f5fba time: fix Timeout size_hint (#3902) 2021-06-29 20:06:10 +02:00
Jacob Rothstein 8fa29cb00a rt: add tokio::task::Builder (#3881)
Adds a builder API for spawning tasks. Initially, this enables the caller to name the spawned
task in order to provide better visibility into all tasks in the system.
2021-06-29 10:47:30 -07:00
Alice Ryhl b521cc2689 doc: document cancellation safety (#3900)
This patch documents cancellation safety. It also moves the "Avoid racy if preconditions"
section in the select! documentation since otherwise the first code block on the page
shows how not to use it, which seems counterintuitive.
2021-06-29 10:35:42 -07:00
Jake Shadle 57c90c9750 net: add read/try_read etc methods to NamedPipeServer (#3899) 2021-06-29 10:05:20 +02:00
Mikail Bagishov d35ff7064f sync: add drop guard for cancellation token (#3839) 2021-06-28 12:34:58 +02:00
Milo ab0791b817 time: add wait alias to sleep (#3897) 2021-06-28 10:48:07 +02:00
Jake Shadle 959c5c997f net: add ready/try methods to NamedPipeClient (#3866) 2021-06-28 10:43:57 +02:00
sb64 845626410a sync: implement From<T> for OnceCell<T> (#3877) 2021-06-22 15:11:01 +02:00
Carl Lerche 3207479fa7 Merge branch 'tokio-1.7.x' into merge-1.7.1 2021-06-18 16:37:21 -07:00
Carl Lerche 7601dc6d2a rt: avoid early task shutdown (#3870)
Tokio 1.7.0 introduced a change intended to eagerly shutdown newly
spawned tasks if the runtime is in the process of shutting down.
However, it introduced a bug where already spawned tasks could be
shutdown too early, resulting in the potential introduction of deadlocks
if tasks acquired mutexes in drop handlers.

Fixes #3869
2021-06-18 16:32:53 -07:00
Alice Ryhl d1aa2df80e sync: add Receiver::borrow_and_update (#3813) 2021-06-16 18:42:05 +02:00
sb64 e979ad7f2d time: allow users to specify Interval behavior when delayed (#3721) 2021-06-16 13:51:27 +02:00
sb64 5ad3dd3378 time: document auto-advancing behavior of runtime (#3763) 2021-06-16 13:51:12 +02:00
teor 60bd40d529 time: fix typo in Instant::saturating_duration_since docs (#3864) 2021-06-16 12:50:30 +02:00
brain0 4a93af4d25 io: add get_{ref,mut} methods to AsyncFdReadyGuard and AsyncFdReadyMutGuard. (#3807) 2021-06-16 12:45:05 +02:00
Alice Ryhl 34c6a26c01 chore: prepare Tokio v1.7.0 (#3863) 2021-06-15 19:39:33 +02:00
John-John TedroandAlice Ryhl 97e7830364 net: provide NamedPipe{Client, Server} types and builders (#3760)
This builds on https://github.com/tokio-rs/mio/pull/1351 and introduces the
tokio::net::windows::named_pipe module which provides low level types for
building and communicating asynchronously over windows named pipes.

Named pipes require the `net` feature flag to be enabled on Windows.

Co-authored-by: Alice Ryhl <[email protected]>
2021-06-15 08:13:31 +02:00
Carl Lerche 606206ecad Merge branch 'tokio-1.6.x' into merge-1.6.x 2021-06-14 17:51:30 -07:00
Carl Lerche dfe4013ff2 chore: prepare Tokio 1.6.2 release (#3859) 2021-06-14 17:50:34 -07:00
Carl Lerche 18779aa2e2 time: fix time::advance() with sub-ms durations (#3852)
Instead of using sleep in time::advance, this fixes the root of the issue. When futures passed
to `Runtime::block_on` are woken, it bypassed all the machinery around advancing time. By
intercepting wakes in the time driver, we know when the block_on task is woken and skip
advancing time in that case.

Fixes #3837
2021-06-14 16:36:04 -07:00
Alan Somers 2c24a028f6 chore: re-enable test_socket_pair on FreeBSD (#3844)
It was disabled due to an OS bug that has been fixed on all currently
supported releases of FreeBSD
2021-06-14 13:14:35 -07:00
Sean McArthur f55b77aadd tracing: emit waker op as str instead as Debug (#3853) 2021-06-14 10:37:24 -07:00
Yotam Ofek 21de476ae7 sync: export sync::notify::Notified future publicly (#3840) 2021-06-14 10:05:34 +02:00
Alice Ryhl cb147a2b3f sync: deprecate mpsc::RecvError (#3833) 2021-06-11 11:01:34 +02:00
Bart Pelle f759240254 chore: update version in README.md (#3851) 2021-06-11 11:01:22 +02:00
AtulJatia 2e44cd29df net: TcpSocket from std::net::TcpStream (#3838) 2021-06-11 10:58:58 +02:00
Oğuz Bilgener 2ab1fb00a9 sync: add examples to Semaphore (#3808) 2021-06-11 10:46:16 +02:00
Evan Mesterhazy d16e50639a doc: clarify EOF condition for AsyncReadExt::read_buf (#3850) 2021-06-09 15:00:32 +02:00
Sean McArthur e7d74b3119 tracing: instrument task wakers (#3836)
## Motivation

In support of tokio-rs/console#37, we want to understand when a specific task's waker has been interacted with, such as when it is awoken, or if it's forgotten (not cloned), etc.

## Solution

When the tracing feature is enabled, a super trait of Future (InstrumentedFuture) is implemented for Instrumented<F> that allows grabbing the task's ID (well, its span ID), and stores that in the raw task trailer. The waker vtable then emits events and includes that ID.
2021-06-08 14:50:40 -07:00
Tony Han d101feac50 runtime: fix typo in task state doc (#3835) 2021-06-04 22:31:04 +02:00
Taiki Endo 9d8b37d51a macros: suppress clippy::default_numeric_fallback lint in generated code (#3831) 2021-06-02 18:16:23 +09:00
James Hallowell d4c89758fc sync: fix spelling mistake in mpsc::Sender docs (#3828) 2021-06-01 09:56:34 +02:00
Taiki Endo 932be12481 ci: ignore private crates in minimal-versions check (#3827) 2021-05-31 23:39:06 +09:00
Edward Shen bdd6765016 doc: clarify limits on return values of AsyncWrite::poll_write (#3820) 2021-05-31 19:55:55 +09:00
Carl Lerche 8f6d8b25bf chore: add FUNDING.yml (#3824)
Reference Tokio sponsorship
2021-05-28 13:55:16 -07:00
Carl Lerche eb7aee980c Merge branch 'tokio-1.6.x' into merge-1.6.1 2021-05-28 10:04:08 -07:00
Alice Ryhl 1baea398c4 chore: prepare Tokio v1.6.1 2021-05-28 09:59:28 -07:00
Alice Ryhl 3fbcf1ba50 Revert "fs: try doing a non-blocking read before punting to the threadpool (#3518)"
This reverts commit 39706b198c.
2021-05-28 09:59:28 -07:00
j-vanderstoep 21264f1d33 tcp: assign an unused port (#3817) 2021-05-28 09:03:27 +02:00
Rafael Bachmann 81ee3d202a doc: fix minor doc typo (stray backtick) (#3814) 2021-05-26 14:23:39 +02:00
Alice Ryhl a39e6c2439 runtime: immediately drop new task when runtime is shut down (#3752) 2021-05-26 10:24:56 +02:00
Petros Angelatos 4abeca7bc5 io: impl AsyncSeek for BufStream (#3810)
Signed-off-by: Petros Angelatos <[email protected]>
2021-05-26 07:11:47 +09:00
GITSRC 5ed84e1cd8 doc: update README.md (#3806) 2021-05-21 09:55:53 +02:00
Lucio Franco 44a070d1b4 doc: add missing backtick (#3799) 2021-05-19 10:14:08 +02:00
Aaron Taner ce9ca45c92 doc: fix invalid #[doc(inline)] warnings on latest nightly. (#3788)
This commit fixed issue #3787 by removing [doc(inline)] from
macro `cfg_macros` and added proper #[doc(inline)] attributes
to `pub use` items inside `cfg_macros` calls.

It's probably not `cfg_macros`s responsibility to inlining public
macros, though it's conveninent to do so. Notice that in lib.rs:

cfg_macros! {
    /// Implementation detail of the `select!` macro. This macro is **not**
    /// intended to be used as part of the public API and is permitted to
    /// change.
    #[doc(hidden)]
    pub use tokio_macros::select_priv_declare_output_enum;

    ...
}

`#[doc(hidden)]` and `#[doc(inline)]` are conflict with each other
in the sense of correctness.

Fixes: #3787
2021-05-18 01:28:17 +09:00
Folyd f3ed064a26 chore: update *::{max, min}_value() to const *::MAX and *::MIN respectively (#3794) 2021-05-17 12:31:58 +02:00
Sören Meier 652f0ae728 sync: add receiver_count to watch::Sender (#3729) 2021-05-15 11:56:24 +02:00
Alice Ryhl ff9b0ef7ca chore: prepare tokio-test v0.4.2 (#3786) 2021-05-14 18:24:13 +02:00
Alice Ryhl aaa150d211 chore: prepare tokio-stream v0.1.6 (#3785) 2021-05-14 18:22:44 +02:00
Alice Ryhl deb1f98125 chore: prepare tokio-util v0.6.7 (#3784) 2021-05-14 18:22:08 +02:00
Alice Ryhl 3a659c47c3 chore: prepare tokio-mcaros v1.2.0 (#3783) 2021-05-14 18:20:17 +02:00
Alice Ryhl 580dc9594c chore: prepare Tokio v1.6.0 (#3782) 2021-05-14 18:19:10 +02:00
Taiki Endo 8c395dfe61 io: impl AsyncSeek for BufReader/BufWriter/BufStream (#3491) 2021-05-14 19:32:00 +09:00
Ivan Petkov e188e99ca3 process: avoid redundant effort to reap orphan processes (#3743) 2021-05-14 10:21:21 +02:00
Kestrer 0b93bd511d net: support non-blocking vectored I/O (#3761) 2021-05-14 09:38:01 +02:00
Alice Ryhl d846bf24b1 sync: Barrier doc should use task, not thread (#3780) 2021-05-13 10:14:10 +02:00
Taiki Endo 9ff7d8c352 net: hide net::unix::datagram module from docs (#3775) 2021-05-10 04:35:09 +09:00
Taiki Endo 8324317005 doc: fix doc-cfg on io::duplex, io::copy_bidirectional, and task::unconstrained (#3773) 2021-05-10 04:03:42 +09:00
Taiki Endo 7207e3ca43 signal: use std::os::raw::c_int instead of libc::c_int on public API (#3774) 2021-05-10 02:53:57 +09:00
Taiki Endo 312321cbd3 chore: enable syntax highlighting for toml in readme 2021-05-09 16:21:36 +02:00
Taiki Endo 17c7ce616c benches: fix build error (#3769) 2021-05-09 22:26:20 +09:00
Taiki Endo f9ce18a524 macros: improve diagnostics on type mismatch (#3766) 2021-05-09 18:58:05 +09:00
Chris Moore 05c3cf3a0a util: impl AsRawFd/AsRawHandle for Compat<T> (#3765) 2021-05-08 12:55:04 +02:00
John-John Tedro 2c5dc83019 cargo: path dependencies for all tokio things (#3764) 2021-05-08 12:54:30 +02:00
sb64 6b9bdd5ca2 chore: fix CI for Rust 1.52 (#3758) 2021-05-06 22:26:27 +02:00
Stefan d4075a4457 io: wake pending writers on DuplexStream close (#3756) 2021-05-06 18:30:39 +02:00
Stefan Sydow 177522cd43 benchmark: add file reading benchmarks (#3013) 2021-05-05 21:49:00 +02:00
Alice Ryhl 7ac341b526 task: update documentation on block_in_place (#3753) 2021-05-05 21:44:02 +02:00
Taiki Endo c4b6b130f3 chore: bump nightly to nightly-2021-04-25 (#3754) 2021-05-05 17:39:17 +02:00
Simon Lindholm 6845a93cbf sync: preserve permit state in notify_waiters (#3660) 2021-05-05 15:20:24 +02:00
Russell Cohen 8ef39dfb22 tokio-test: add assert_elapsed macro (#3728) 2021-05-05 12:09:28 +02:00
MGlolenstine 541b0c3af2 process: updated example (#3748) 2021-05-05 11:51:05 +02:00
Kai Mast 55c5d12451 util: require full feature in tokio-util tests (#3636) 2021-05-05 18:21:25 +09:00
Chế Vũ Gia Hy a945ce0996 stream: implement Error and Display for BroadcastStreamRecvError (#3745) 2021-05-05 11:15:42 +02:00
Russell Cohen 14bb2f624f io: add write_all_buf to AsyncWriteExt (#3737) 2021-05-01 20:54:38 +02:00
Marco Ieni a08ce0d3e0 ci: check docs of private items (#3715) 2021-04-30 13:25:50 +02:00
Arthur Silva 078e317c0b tokio: update CHANGELOG.md (#3735) 2021-04-30 00:26:28 +02:00
kamulos ce9697f2a2 doc: fix missing backtick in select doc (#3732) 2021-04-27 22:23:03 +09:00
Alice Ryhl 73466f4b6c task: update JoinHandle panic message (#3727) 2021-04-25 09:03:14 +02:00
Sunjay Varma 0ba1e3c3e1 sync: add a MutexGuard::map method that returns a MappedMutexGuard (#2472) 2021-04-23 17:26:02 +02:00
Nylonicious 1e2f893da2 chore: fix some clippy lints (#3720) 2021-04-22 13:33:42 +02:00
Carl Lerche 2b9b558108 time: prevent time::advance from going too far (#3712)
Previously, `time::advance` would set the mocked clock forward the
requested amount, then yield. However, if there was no work ready to
perform immediately, this would result in advancing to the next expiring
sleep.

Now, `time::advance(...)` will unblock at the requested time. The
difference between `time::advance(...)` and `time::sleep(...)` is a bit
fuzzy. The main difference is `time::sleep(...)` operates on the current
task and `time::advance(...)` operates at the runtime level.

Fixes #3710
2021-04-21 15:23:35 -07:00
Eliza Weisman d6da67b2e6 sync: add mpsc::Sender::{reserve_owned, try_reserve_owned} (#3704)
* sync: add `mpsc::Sender::{reserve_owned, try_reserve_owned}`

## Motivation

The `mpsc::Sender::reserve` method currently returns a permit that borrows
from the `Sender`. It would be nice to have a version of it that returns
an owned permit.

## Solution

This branch adds an `OwnedPermit` type and `Sender::{reserve_owned,
try_reserve_owned}` methods. Unlike the comparable methods on
`Semaphore`, these methods do *not* require an `Arc<Sender>` as the
receiver; this is because the sender internally reference counts the
channel and is already cheap to clone. Requiring an `Arc` would simply
add an unnecessary second layer of reference counting, which is not
ideal; instead, the documentation encourages the user to clone the
sender prior to calling `reserve_owned` when necessary.

Since these methods take the `Sender` by value, they also have the ability
to _return_ the `Sender` from a successful `OwnedPermit::send`. This
allows them to be used without additional clones. Essentially, this is a
very simple type-level encoding of the sender's state, with the
transitions
```
      ┌──────┐
 ┌───►│Sender├───┐
 │    └──────┘   │
send          reserve
 │ ┌───────────┐ │
 └─┤OwnedPermit│◄┘
   └───────────┘
```

Additionally, I added an `OwnedPermit::release`, which returns the
`Sender` and releases the permit *without* sending a message.

Closes #3688 

Signed-off-by: Eliza Weisman <[email protected]>
2021-04-15 12:55:57 -07:00
madjack c8a6bb0b90 time: add getter for Interval's period (#3705) 2021-04-15 14:21:52 +02:00
Evan Cameron 9eeec039f2 util: make UdpFramed take Borrow<UdpSocket> (#3451) 2021-04-14 20:16:23 +02:00
William Manley 39706b198c fs: try doing a non-blocking read before punting to the threadpool (#3518) 2021-04-14 15:44:49 +02:00
baoyachi. Aka Rust Hairy crabs cab4a592ac chore: update version in README.md (#3698) 2021-04-13 16:17:14 +09:00
Alice Ryhl a5ee2f0d3d chore: prepare Tokio v1.5.0 (#3695) 2021-04-12 21:24:47 +02:00
Alice Ryhl 917aad684b chore: prepare tokio-util 0.6.6 (#3696) 2021-04-12 21:23:20 +02:00
Alice Ryhl e366cf9b3e chore: upgrade loom to 0.5.0 (#3697) 2021-04-12 20:28:48 +02:00
Alice Ryhl 3a02d34d3a sync: document that Semaphore is fair (#3693) 2021-04-12 15:46:42 +02:00
conblem adad8fc3cd io: add a copy_bidirectional utility (#3572) 2021-04-12 14:18:45 +02:00
David Pedersen 08f1b67fcb sync: add mpsc::Sender::capacity (#3690)
Simply exposes the number of available permits of the semaphore.

This makes some kinds of bookkeeping easier without having to manually keep counts using atomics.

Fixes #2642
2021-04-12 09:23:03 +02:00
David Pedersen 28d6879897 macros: forward input arguments in #[tokio::test] (#3691)
Fixes #2388

Previously `#[tokio::test]` would error on functions that took
arguments. That meant other attribute macros couldn't do further
transformations on them. This changes that so arguments are forwarded as
is.

Whatever else might be included on the function is forwarded as well.
For example return type, generics, etc.

Worth noting that this is only for compatibility with other macros.
`#[test]`s that take arguments will still fail to compile.

A bit odd that [trybuild] tests don't fail `#[test]` functions with
arguments which is why the new tests are run with `t.pass(...)`. They do
actually fail if part of a real crate.

[trybuild]: https://crates.io/crates/trybuild
2021-04-11 20:39:05 +02:00
John-John Tedro 1a72b28f53 rt: fix panic in JoinHandle::abort() when called from other thread (#3672)
When aborting a task registered with a current-thread scheduler from off runtime, the tasks may not
be immediately unlinked from the runtime. Instead, send a message to the runtime, notifying it to
remove the aborted task.

Fixes #3662
2021-04-08 13:49:39 -07:00
Geoffry Song 1d5655272b runtime: use Park::Error: Debug for better panic messages (#3641) 2021-04-08 20:30:43 +02:00
Matthias Beyer 5513b6b825 time: change 300ms to 1s in documentation (#3685) 2021-04-08 11:04:27 +02:00
ty 787aca1826 net: implement IntoRawFd for TcpSocket (#3684) 2021-04-08 09:28:44 +02:00
Zahari Dichev e89c8981f1 sync: allow configuring RwLock max reads (#3644) 2021-04-07 20:44:29 +02:00
Eliza Weisman bf8c77bea1 util: add PollSemaphore::{add_permits, available_permits} (#3683)
## Motivation

The `tokio::sync::Semaphore` type provides a
[`Semaphore::available_permits` method][1] which returns the current
number of permits available on the semaphore.
`tokio_util::sync::PollSemaphore` [does not expose such a method][2]. It
is possible to use `PollSemaphore::into_inner` or
`PollSemaphore::clone_inner` to unwrap the inner semaphore, but this may
require cloning/dropping the semaphore's `Arc` which shouldn't be
necessary to access the permits.

## Solution

This commit adds `PollSemaphore::available_permits`. It also adds
`PollSemaphore::add_permits` and an `AsRef<Semaphore>` impl while
we're here.

[1]: https://docs.rs/tokio/1.4.0/tokio/sync/struct.Semaphore.html#method.available_permits
[2]: https://docs.rs/tokio-util/0.6.5/tokio_util/sync/struct.PollSemaphore.html#implementations

Closes #3682
2021-04-07 11:40:03 -07:00
Lucas Zanela 0074b963b8 doc: add features = "full" to readme (#3680) 2021-04-06 11:03:21 +02:00
David Pedersen eabb7ce61c io: add AsyncWriteExt::write_vectored (#3678)
Fixes #3676
2021-04-06 00:02:00 +02:00
David Pedersen 618d2bfc71 macros: various error message improvements (#3677)
Improves a few of the error messages for `#[tokio::main]` and `#[tokio::test]`.

Also adds a note to the docs about `start_paused` requiring the `test-util` feature which wasn't mentioned previously.
2021-04-05 22:40:42 +02:00
b-naber f6e4e85dfb sync: add OnceCell (#3591) 2021-04-05 22:03:57 +02:00
xd009642 f93bc9bad1 sync: don't panic in oneshot::try_recv (#3674) 2021-04-04 20:03:00 +02:00
Michael P. Jung b05b9a1788 sync: add Semaphore::is_closed (#3673) 2021-04-04 19:14:41 +02:00
Donough Liu b42f21ec3e doc: fix incorrect link in doc (#3670) 2021-04-01 15:24:29 +02:00
Nylonicious 8fc49dc522 chore: update years in all licenses (#3665) 2021-03-30 21:45:13 +02:00
Amos Wenger 9ec3393650 net: typo in socket.rs (#3663) 2021-03-30 20:44:54 +02:00
Simon Lindholm f7c181c2c4 sync: fix notifications getting dropped on receiver drop (#3652) 2021-03-30 09:18:07 +02:00
Alice Ryhl fee76ea7d5 chore: try to avoid noalias attributes on intrusive linked list (#3654) 2021-03-29 22:38:29 +02:00
CorinJG 1a80d6eee5 sync: improve doc on blocking mutex (#3645) 2021-03-27 13:26:10 +01:00
Taiki Endo 7384813979 chore: update proptest to 1 (#3653) 2021-03-27 19:34:11 +09:00
Taiki Endo a257a3a2b1 io: add AsyncSeekExt::stream_position (#3650) 2021-03-27 12:28:51 +09:00
Alexandr 9a3603fa75 chore: fix several typos in docs (#3651) 2021-03-27 12:27:52 +09:00
Alice Ryhl 724ba348d1 chore: fix warning from new Rust version (#3647) 2021-03-26 19:22:43 +09:00
Kai Jewson 0dc4769708 sync: add OwnedRwLockReadGuard and OwnedRwLockWriteGuard (#3340) 2021-03-23 13:40:17 +01:00
Mihail Malo 6f896d8846 macros: add missing => in doc (#3638) 2021-03-22 21:37:29 +01:00
Daniel Franklin 10abc45da1 io: clarify requirements of AsyncFd (#3635) 2021-03-22 21:16:40 +01:00
Moritz Gunz 227b3e0d9c chore: avoid allocation if PollSemaphore is unused (#3634) 2021-03-22 17:02:30 +01:00
quininer c659e4a757 task: add sync_scope for LocalKey (#3612) 2021-03-22 17:02:10 +01:00
somethingelseentirely 8ed825fd49 util: makes Framed and FramedStream resumable after eof (#3272) 2021-03-22 09:09:01 +01:00
Ibraheem Ahmed 63395f061e macros: fix typo in select macro docs (#3629) 2021-03-21 20:10:28 +01:00
Kai Jewson 69b129b405 stream: avoid yielding in AllFuture and AnyFuture (#3625) 2021-03-21 09:34:18 +01:00
David Pedersen 0bfcbc8be5 runtime: fix unclear docs for {Handle,Runtime}::block_on (#3628) 2021-03-20 14:52:25 +01:00
David Pedersen 81f47e8866 util: fix indentation and typos in DelayQueue docs (#3626) 2021-03-20 14:31:12 +01:00
David Pedersen b4918adbd8 chore: prepare tokio-stream v0.1.5 (#3624) 2021-03-20 12:17:37 +01:00
David Pedersen b1310ad14d chore: prepare tokio-util v0.6.5 (#3622) 2021-03-20 12:16:56 +01:00
David Pedersen dcac336dc7 chore: prepare Tokio v1.4.0 (#3621) 2021-03-20 12:16:15 +01:00
David Pedersen c39d9867bb runtime: add Handle::block_on (#3569)
Add `runtime::Handle::block_on`. The function enters the runtime context and
blocks the current thread while the future executes.

Refs: #3097
Fixes #2965, #3096
2021-03-20 10:52:28 +01:00
Ivan Petkov e4f76688a0 runtime: fix memory leak/growth when creating many runtimes (#3564) 2021-03-16 19:31:46 +01:00
Zahari Dichev e6103d6661 docs: add link to PollSender (#3613)
There was no link to [`PollSender`] in [`mpsc::Sender`] docs which made it
less discoverable.

[`PollSender`]: https://docs.rs/tokio-util/0.6.4/tokio_util/sync/struct.PollSender.html
[`mpsc::Sender::send`]: https://docs.rs/tokio/1.3.0/tokio/sync/mpsc/struct.Sender.html
2021-03-16 17:55:06 +01:00
David Pedersen cc90a5c679 chore: mention fix for building docs in contributing guide (#3618)
I ran into this when writing the docs for `Handle::block_on`. Seems to
be caused by a bug in cargo. Until that is fixed I think it makes sense
to mention it in the contributing guide.
2021-03-16 21:39:28 +09:00
David Pedersen f107c4f49b timer: fix double newline in module docs (#3617) 2021-03-16 21:37:50 +09:00
Zahari Dichev e6a9167bb7 runtime: avoid unnecessary polling of block_on future (#3582) 2021-03-16 10:30:18 +01:00
setpill 345b29ca11 sync: improve Mutex FIFO explanation (#3615) 2021-03-12 21:24:05 +01:00
Nate Mara b75d02a2b6 macros: introduce biased argument for select! (#3603) 2021-03-12 21:20:20 +01:00
Faraz a1b4bdee61 runtime: mark EnterGuard with must_use (#3609) 2021-03-12 09:06:08 +01:00
Zahari Dichev edfff7551a util: anotate time module as requiring time feature (#3606)
Signed-off-by: Zahari Dichev <[email protected]>
2021-03-11 16:02:01 +01:00
Rob Ede 1cda0f16a2 doc: alias sleep to delay (#3604) 2021-03-10 23:26:11 +01:00
Lucio Franco e40ec3e424 chore: Prepare tokio-test v0.4.1 release (#3602) 2021-03-10 16:04:08 -05:00
Jake HamandJake Ham 6919f7cede test: Make Mock both Send and Sync (#3594)
Co-authored-by: Jake Ham <[email protected]>
2021-03-10 14:29:22 -05:00
cssivision bcb95db4e2 chore: fix clippy warnings in newer versions (#3588) 2021-03-10 08:42:24 +01:00
Matthew James Briggs 8c5cde9bc3 stream: documentation note for throttle Unpin (#3600) 2021-03-10 08:39:43 +01:00
Alice Ryhl b08b5edb2c chore: prepare tokio-util v0.6.4 (#3599) 2021-03-09 22:11:38 +01:00
Alice Ryhl 88863a0c5d chore: prepare tokio-stream v0.1.4 (#3598) 2021-03-09 22:10:57 +01:00
245 changed files with 13497 additions and 1516 deletions
+1
View File
@@ -0,0 +1 @@
msrv = "1.45"
+3
View File
@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [tokio-rs]
+24 -8
View File
@@ -9,7 +9,7 @@ name: CI
env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
nightly: nightly-2020-09-21
nightly: nightly-2021-04-25
minrust: 1.45.2
jobs:
@@ -68,6 +68,13 @@ jobs:
run: cargo hack test --each-feature
working-directory: tests-build
# Build benchmarks. Run of benchmarks is done by bench.yml workflow.
- name: build benches
run: cargo build --benches
working-directory: benches
# bench.yml workflow runs benchmarks only on linux.
if: startsWith(matrix.os, 'ubuntu')
valgrind:
name: valgrind
runs-on: ubuntu-latest
@@ -82,14 +89,23 @@ jobs:
sudo apt-get install -y valgrind
# Compile tests
- name: cargo build
- name: cargo build test-mem
run: cargo build --features rt-net --bin test-mem
working-directory: tests-integration
# Run with valgrind
- name: Run valgrind
- name: Run valgrind test-mem
run: valgrind --leak-check=full --show-leak-kinds=all ./target/debug/test-mem
# Compile tests
- name: cargo build test-process-signal
run: cargo build --features rt-process-signal --bin test-process-signal
working-directory: tests-integration
# Run with valgrind
- name: Run valgrind test-process-signal
run: valgrind --leak-check=full --show-leak-kinds=all ./target/debug/test-process-signal
test-unstable:
name: test tokio full --unstable
runs-on: ${{ matrix.os }}
@@ -106,7 +122,7 @@ jobs:
# Run `tokio` with "unstable" cfg flag.
- name: test tokio full --cfg unstable
run: cargo test --features full
run: cargo test --all-features
working-directory: tokio
env:
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
@@ -222,7 +238,7 @@ jobs:
cargo hack --remove-dev-deps --workspace
# Update Cargo.lock to minimal version dependencies.
cargo update -Z minimal-versions
cargo check --all-features
cargo hack check --all-features --ignore-private
fmt:
name: fmt
@@ -249,7 +265,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update ${{ env.minrust }} && rustup default ${{ env.minrust }}
run: rustup update 1.52.1 && rustup default 1.52.1
- name: Install clippy
run: rustup component add clippy
@@ -268,9 +284,9 @@ jobs:
override: true
- name: "doc --lib --all-features"
run: cargo doc --lib --no-deps --all-features
run: cargo doc --lib --no-deps --all-features --document-private-items
env:
RUSTDOCFLAGS: --cfg docsrs
RUSTDOCFLAGS: --cfg docsrs -Dwarnings
loom:
name: loom
+10
View File
@@ -124,17 +124,27 @@ arguments to many common cargo commands. This section lists some commonly needed
commands.
Some commands just need the `--all-features` argument:
```
cargo build --all-features
cargo check --all-features
cargo test --all-features
```
When building documentation normally, the markers that list the features
required for various parts of Tokio are missing. To build the documentation
correctly, use this command:
```
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
```
There is currently a [bug in cargo] that means documentation cannot be built
from the root of the workspace. If you `cd` into the `tokio` subdirectory the
command shown above will work.
[bug in cargo]: https://github.com/rust-lang/cargo/issues/9274
The `cargo fmt` command does not work on the Tokio codebase. You can use the
command below instead:
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2019 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+10 -2
View File
@@ -50,7 +50,15 @@ an asynchronous application.
## Example
A basic TCP echo server with Tokio:
A basic TCP echo server with Tokio.
Make sure you activated the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.8.0", features = ["full"] }
```
Then, on your main.rs:
```rust,no_run
use tokio::net::TcpListener;
@@ -58,7 +66,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut listener = TcpListener::bind("127.0.0.1:8080").await?;
let listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (mut socket, _) = listener.accept().await?;
+10 -1
View File
@@ -5,9 +5,13 @@ publish = false
edition = "2018"
[dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
tokio = { version = "1.5.0", path = "../tokio", features = ["full"] }
bencher = "0.1.5"
[dev-dependencies]
tokio-util = { version = "0.6.6", path = "../tokio-util", features = ["full"] }
tokio-stream = { path = "../tokio-stream" }
[target.'cfg(unix)'.dependencies]
libc = "0.2.42"
@@ -41,3 +45,8 @@ harness = false
name = "signal"
path = "signal.rs"
harness = false
[[bench]]
name = "fs"
path = "fs.rs"
harness = false
+103
View File
@@ -0,0 +1,103 @@
#![cfg(unix)]
use tokio_stream::StreamExt;
use tokio::fs::File;
use tokio::io::AsyncReadExt;
use tokio_util::codec::{BytesCodec, FramedRead /*FramedWrite*/};
use bencher::{benchmark_group, benchmark_main, Bencher};
use std::fs::File as StdFile;
use std::io::Read as StdRead;
fn rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
.build()
.unwrap()
}
const BLOCK_COUNT: usize = 1_000;
const BUFFER_SIZE: usize = 4096;
const DEV_ZERO: &'static str = "/dev/zero";
fn async_read_codec(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
let task = || async {
let file = File::open(DEV_ZERO).await.unwrap();
let mut input_stream = FramedRead::with_capacity(file, BytesCodec::new(), BUFFER_SIZE);
for _i in 0..BLOCK_COUNT {
let _bytes = input_stream.next().await.unwrap();
}
};
rt.block_on(task());
});
}
fn async_read_buf(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
let task = || async {
let mut file = File::open(DEV_ZERO).await.unwrap();
let mut buffer = [0u8; BUFFER_SIZE];
for _i in 0..BLOCK_COUNT {
let count = file.read(&mut buffer).await.unwrap();
if count == 0 {
break;
}
}
};
rt.block_on(task());
});
}
fn async_read_std_file(b: &mut Bencher) {
let rt = rt();
let task = || async {
let mut file = tokio::task::block_in_place(|| Box::pin(StdFile::open(DEV_ZERO).unwrap()));
for _i in 0..BLOCK_COUNT {
let mut buffer = [0u8; BUFFER_SIZE];
let mut file_ref = file.as_mut();
tokio::task::block_in_place(move || {
file_ref.read_exact(&mut buffer).unwrap();
});
}
};
b.iter(|| {
rt.block_on(task());
});
}
fn sync_read(b: &mut Bencher) {
b.iter(|| {
let mut file = StdFile::open(DEV_ZERO).unwrap();
let mut buffer = [0u8; BUFFER_SIZE];
for _i in 0..BLOCK_COUNT {
file.read_exact(&mut buffer).unwrap();
}
});
}
benchmark_group!(
file,
async_read_std_file,
async_read_buf,
async_read_codec,
sync_read
);
benchmark_main!(file);
+1 -1
View File
@@ -1,4 +1,4 @@
//! Benchmark implementation details of the theaded scheduler. These benches are
//! 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.
+18 -3
View File
@@ -7,9 +7,9 @@ edition = "2018"
# If you copy one of the examples into a new project, you should be using
# [dependencies] instead.
[dev-dependencies]
tokio = { version = "1.0.0", features = ["full", "tracing"] }
tokio-util = { version = "0.6.3", features = ["full"] }
tokio-stream = { version = "0.1" }
tokio = { version = "1.0.0", path = "../tokio",features = ["full", "tracing"] }
tokio-util = { version = "0.6.3", path = "../tokio-util",features = ["full"] }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
tracing = "0.1"
tracing-subscriber = { version = "0.2.7", default-features = false, features = ["fmt", "ansi", "env-filter", "chrono", "tracing-log"] }
@@ -22,7 +22,10 @@ serde_json = "1.0"
httparse = "1.0"
time = "0.1"
once_cell = "1.5.2"
rand = "0.8.3"
[target.'cfg(windows)'.dev-dependencies.winapi]
version = "0.3.8"
[[example]]
name = "chat"
@@ -76,3 +79,15 @@ path = "custom-executor.rs"
[[example]]
name = "custom-executor-tokio-context"
path = "custom-executor-tokio-context.rs"
[[example]]
name = "named-pipe"
path = "named-pipe.rs"
[[example]]
name = "named-pipe-ready"
path = "named-pipe-ready.rs"
[[example]]
name = "named-pipe-multi-client"
path = "named-pipe-multi-client.rs"
+98
View File
@@ -0,0 +1,98 @@
use std::io;
#[cfg(windows)]
async fn windows_main() -> io::Result<()> {
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
use tokio::time;
use winapi::shared::winerror;
const PIPE_NAME: &str = r"\\.\pipe\named-pipe-multi-client";
const N: usize = 10;
// The first server needs to be constructed early so that clients can
// be correctly connected. Otherwise a waiting client will error.
//
// Here we also make use of `first_pipe_instance`, which will ensure
// that there are no other servers up and running already.
let mut server = ServerOptions::new()
.first_pipe_instance(true)
.create(PIPE_NAME)?;
let server = tokio::spawn(async move {
// Artificial workload.
time::sleep(Duration::from_secs(1)).await;
for _ in 0..N {
// Wait for client to connect.
server.connect().await?;
let mut inner = server;
// Construct the next server to be connected before sending the one
// we already have of onto a task. This ensures that the server
// isn't closed (after it's done in the task) before a new one is
// available. Otherwise the client might error with
// `io::ErrorKind::NotFound`.
server = ServerOptions::new().create(PIPE_NAME)?;
let _ = tokio::spawn(async move {
let mut buf = vec![0u8; 4];
inner.read_exact(&mut buf).await?;
inner.write_all(b"pong").await?;
Ok::<_, io::Error>(())
});
}
Ok::<_, io::Error>(())
});
let mut clients = Vec::new();
for _ in 0..N {
clients.push(tokio::spawn(async move {
// This showcases a generic connect loop.
//
// We immediately try to create a client, if it's not found or
// the pipe is busy we use the specialized wait function on the
// client builder.
let mut client = loop {
match ClientOptions::new().open(PIPE_NAME) {
Ok(client) => break client,
Err(e) if e.raw_os_error() == Some(winerror::ERROR_PIPE_BUSY as i32) => (),
Err(e) => return Err(e),
}
time::sleep(Duration::from_millis(5)).await;
};
let mut buf = [0u8; 4];
client.write_all(b"ping").await?;
client.read_exact(&mut buf).await?;
Ok::<_, io::Error>(buf)
}));
}
for client in clients {
let result = client.await?;
assert_eq!(&result?[..], b"pong");
}
server.await??;
Ok(())
}
#[tokio::main]
async fn main() -> io::Result<()> {
#[cfg(windows)]
{
windows_main().await?;
}
#[cfg(not(windows))]
{
println!("Named pipes are only supported on Windows!");
}
Ok(())
}
+161
View File
@@ -0,0 +1,161 @@
use std::io;
#[cfg(windows)]
async fn windows_main() -> io::Result<()> {
use tokio::io::Interest;
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
const PIPE_NAME: &str = r"\\.\pipe\named-pipe-single-client";
let server = ServerOptions::new().create(PIPE_NAME)?;
let server = tokio::spawn(async move {
// Note: we wait for a client to connect.
server.connect().await?;
let buf = {
let mut read_buf = [0u8; 5];
let mut read_buf_cursor = 0;
loop {
server.readable().await?;
let buf = &mut read_buf[read_buf_cursor..];
match server.try_read(buf) {
Ok(n) => {
read_buf_cursor += n;
if read_buf_cursor == read_buf.len() {
break;
}
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
return Err(e);
}
}
}
read_buf
};
{
let write_buf = b"pong\n";
let mut write_buf_cursor = 0;
loop {
let buf = &write_buf[write_buf_cursor..];
if buf.is_empty() {
break;
}
server.writable().await?;
match server.try_write(buf) {
Ok(n) => {
write_buf_cursor += n;
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
return Err(e);
}
}
}
}
Ok::<_, io::Error>(buf)
});
let client = tokio::spawn(async move {
// There's no need to use a connect loop here, since we know that the
// server is already up - `open` was called before spawning any of the
// tasks.
let client = ClientOptions::new().open(PIPE_NAME)?;
let mut read_buf = [0u8; 5];
let mut read_buf_cursor = 0;
let write_buf = b"ping\n";
let mut write_buf_cursor = 0;
loop {
let mut interest = Interest::READABLE;
if write_buf_cursor < write_buf.len() {
interest |= Interest::WRITABLE;
}
let ready = client.ready(interest).await?;
if ready.is_readable() {
let buf = &mut read_buf[read_buf_cursor..];
match client.try_read(buf) {
Ok(n) => {
read_buf_cursor += n;
if read_buf_cursor == read_buf.len() {
break;
}
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
return Err(e);
}
}
}
if ready.is_writable() {
let buf = &write_buf[write_buf_cursor..];
if buf.is_empty() {
continue;
}
match client.try_write(buf) {
Ok(n) => {
write_buf_cursor += n;
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
return Err(e);
}
}
}
}
let buf = String::from_utf8_lossy(&read_buf).into_owned();
Ok::<_, io::Error>(buf)
});
let (server, client) = tokio::try_join!(server, client)?;
assert_eq!(server?, *b"ping\n");
assert_eq!(client?, "pong\n");
Ok(())
}
#[tokio::main]
async fn main() -> io::Result<()> {
#[cfg(windows)]
{
windows_main().await?;
}
#[cfg(not(windows))]
{
println!("Named pipes are only supported on Windows!");
}
Ok(())
}
+60
View File
@@ -0,0 +1,60 @@
use std::io;
#[cfg(windows)]
async fn windows_main() -> io::Result<()> {
use tokio::io::AsyncWriteExt;
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
const PIPE_NAME: &str = r"\\.\pipe\named-pipe-single-client";
let server = ServerOptions::new().create(PIPE_NAME)?;
let server = tokio::spawn(async move {
// Note: we wait for a client to connect.
server.connect().await?;
let mut server = BufReader::new(server);
let mut buf = String::new();
server.read_line(&mut buf).await?;
server.write_all(b"pong\n").await?;
Ok::<_, io::Error>(buf)
});
let client = tokio::spawn(async move {
// There's no need to use a connect loop here, since we know that the
// server is already up - `open` was called before spawning any of the
// tasks.
let client = ClientOptions::new().open(PIPE_NAME)?;
let mut client = BufReader::new(client);
let mut buf = String::new();
client.write_all(b"ping\n").await?;
client.read_line(&mut buf).await?;
Ok::<_, io::Error>(buf)
});
let (server, client) = tokio::try_join!(server, client)?;
assert_eq!(server?, "ping\n");
assert_eq!(client?, "pong\n");
Ok(())
}
#[tokio::main]
async fn main() -> io::Result<()> {
#[cfg(windows)]
{
windows_main().await?;
}
#[cfg(not(windows))]
{
println!("Named pipes are only supported on Windows!");
}
Ok(())
}
@@ -0,0 +1,8 @@
#![deny(dead_code)]
use tests_build::tokio;
#[tokio::main]
async fn f() {}
fn main() {}
@@ -0,0 +1,11 @@
error: function is never used: `f`
--> $DIR/macros_dead_code.rs:6:10
|
6 | async fn f() {}
| ^
|
note: the lint level is defined here
--> $DIR/macros_dead_code.rs:1:9
|
1 | #![deny(dead_code)]
| ^^^^^^^^^
+18 -3
View File
@@ -12,12 +12,27 @@ async fn main_attr_has_path_args() {}
#[tokio::test]
fn test_is_not_async() {}
#[tokio::test]
async fn test_fn_has_args(_x: u8) {}
#[tokio::test(foo)]
async fn test_attr_has_args() {}
#[tokio::test(foo = 123)]
async fn test_unexpected_attr() {}
#[tokio::test(flavor = 123)]
async fn test_flavor_not_string() {}
#[tokio::test(flavor = "foo")]
async fn test_unknown_flavor() {}
#[tokio::test(flavor = "multi_thread", start_paused = false)]
async fn test_multi_thread_with_start_paused() {}
#[tokio::test(flavor = "multi_thread", worker_threads = "foo")]
async fn test_worker_threads_not_int() {}
#[tokio::test(flavor = "current_thread", worker_threads = 4)]
async fn test_worker_threads_and_current_thread() {}
#[tokio::test]
#[test]
async fn test_has_second_test_attr() {}
@@ -1,4 +1,4 @@
error: the async keyword is missing from the function declaration
error: the `async` keyword is missing from the function declaration
--> $DIR/macros_invalid_input.rs:4:1
|
4 | fn main_is_not_async() {}
@@ -16,26 +16,56 @@ error: Must have specified ident
9 | #[tokio::main(threadpool::bar)]
| ^^^^^^^^^^^^^^^
error: the async keyword is missing from the function declaration
error: the `async` keyword is missing from the function declaration
--> $DIR/macros_invalid_input.rs:13:1
|
13 | fn test_is_not_async() {}
| ^^
error: the test function cannot accept arguments
--> $DIR/macros_invalid_input.rs:16:27
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
--> $DIR/macros_invalid_input.rs:15:15
|
16 | async fn test_fn_has_args(_x: u8) {}
| ^^^^^^
15 | #[tokio::test(foo)]
| ^^^
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
--> $DIR/macros_invalid_input.rs:18:15
|
18 | #[tokio::test(foo)]
| ^^^
18 | #[tokio::test(foo = 123)]
| ^^^^^^^^^
error: Failed to parse value of `flavor` as string.
--> $DIR/macros_invalid_input.rs:21:24
|
21 | #[tokio::test(flavor = 123)]
| ^^^
error: No such runtime flavor `foo`. The runtime flavors are `current_thread` and `multi_thread`.
--> $DIR/macros_invalid_input.rs:24:24
|
24 | #[tokio::test(flavor = "foo")]
| ^^^^^
error: The `start_paused` option requires the `current_thread` runtime flavor. Use `#[tokio::test(flavor = "current_thread")]`
--> $DIR/macros_invalid_input.rs:27:55
|
27 | #[tokio::test(flavor = "multi_thread", start_paused = false)]
| ^^^^^
error: Failed to parse value of `worker_threads` as integer.
--> $DIR/macros_invalid_input.rs:30:57
|
30 | #[tokio::test(flavor = "multi_thread", worker_threads = "foo")]
| ^^^^^
error: The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[tokio::test(flavor = "multi_thread")]`
--> $DIR/macros_invalid_input.rs:33:59
|
33 | #[tokio::test(flavor = "current_thread", worker_threads = 4)]
| ^
error: second test attribute is supplied
--> $DIR/macros_invalid_input.rs:22:1
--> $DIR/macros_invalid_input.rs:37:1
|
22 | #[test]
37 | #[test]
| ^^^^^^^
@@ -0,0 +1,32 @@
use tests_build::tokio;
#[tokio::main]
async fn missing_semicolon_or_return_type() {
Ok(())
}
#[tokio::main]
async fn missing_return_type() {
/* TODO(taiki-e): one of help messages still wrong
help: consider using a semicolon here
|
16 | return Ok(());;
|
*/
return Ok(());
}
#[tokio::main]
async fn extra_semicolon() -> Result<(), ()> {
/* TODO(taiki-e): help message still wrong
help: try using a variant of the expected enum
|
29 | Ok(Ok(());)
|
29 | Err(Ok(());)
|
*/
Ok(());
}
fn main() {}
@@ -0,0 +1,51 @@
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:5:5
|
5 | Ok(())
| ^^^^^^ expected `()`, found enum `Result`
|
= note: expected unit type `()`
found enum `Result<(), _>`
help: consider using a semicolon here
|
5 | Ok(());
| ^
help: try adding a return type
|
4 | async fn missing_semicolon_or_return_type() -> Result<(), _> {
| ^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:16:5
|
16 | return Ok(());
| ^^^^^^^^^^^^^^ expected `()`, found enum `Result`
|
= note: expected unit type `()`
found enum `Result<(), _>`
help: consider using a semicolon here
|
16 | return Ok(());;
| ^
help: try adding a return type
|
9 | async fn missing_return_type() -> Result<(), _> {
| ^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:29:5
|
20 | async fn extra_semicolon() -> Result<(), ()> {
| -------------- expected `Result<(), ()>` because of return type
...
29 | Ok(());
| ^^^^^^^ expected enum `Result`, found `()`
|
= note: expected enum `Result<(), ()>`
found unit type `()`
help: try using a variant of the expected enum
|
29 | Ok(Ok(());)
|
29 | Err(Ok(());)
|
+9
View File
@@ -2,9 +2,18 @@
fn compile_fail_full() {
let t = trybuild::TestCases::new();
#[cfg(feature = "full")]
t.pass("tests/pass/forward_args_and_output.rs");
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_invalid_input.rs");
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_dead_code.rs");
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_type_mismatch.rs");
#[cfg(all(feature = "rt", not(feature = "full")))]
t.compile_fail("tests/fail/macros_core_no_default.rs");
@@ -0,0 +1,13 @@
use tests_build::tokio;
fn main() {}
// arguments and output type is forwarded so other macros can access them
#[tokio::test]
async fn test_fn_has_args(_x: u8) {}
#[tokio::test]
async fn test_has_output() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
+6
View File
@@ -12,9 +12,15 @@ name = "test-cat"
name = "test-mem"
required-features = ["rt-net"]
[[bin]]
name = "test-process-signal"
required-features = ["rt-process-signal"]
[features]
# For mem check
rt-net = ["tokio/rt", "tokio/rt-multi-thread", "tokio/net"]
# For test-process-signal
rt-process-signal = ["rt", "tokio/process", "tokio/signal"]
full = [
"macros",
@@ -0,0 +1,11 @@
// https://github.com/tokio-rs/tokio/issues/3550
fn main() {
for _ in 0..1000 {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
drop(rt);
}
}
+10
View File
@@ -1,3 +1,13 @@
# 1.2.0 (May 14, 2021)
- macros: forward input arguments in `#[tokio::test]` ([#3691])
- macros: improve diagnostics on type mismatch ([#3766])
- macros: various error message improvements ([#3677])
[#3677]: https://github.com/tokio-rs/tokio/pull/3677
[#3691]: https://github.com/tokio-rs/tokio/pull/3691
[#3766]: https://github.com/tokio-rs/tokio/pull/3766
# 1.1.0 (February 5, 2021)
- add `start_paused` option to macros ([#3492])
+3 -3
View File
@@ -6,13 +6,13 @@ name = "tokio-macros"
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-macros-1.0.x" git tag.
version = "1.1.0"
version = "1.2.0"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-macros/1.1.0/tokio_macros"
documentation = "https://docs.rs/tokio-macros/1.2.0/tokio_macros"
description = """
Tokio's proc macros.
"""
@@ -26,7 +26,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0.7"
quote = "1"
syn = { version = "1.0.3", features = ["full"] }
syn = { version = "1.0.56", features = ["full"] }
[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2020 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+81 -53
View File
@@ -1,7 +1,6 @@
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::spanned::Spanned;
use quote::{quote, quote_spanned, ToTokens};
#[derive(Clone, Copy, PartialEq)]
enum RuntimeFlavor {
@@ -34,6 +33,7 @@ struct Configuration {
flavor: Option<RuntimeFlavor>,
worker_threads: Option<(usize, Span)>,
start_paused: Option<(bool, Span)>,
is_test: bool,
}
impl Configuration {
@@ -47,6 +47,7 @@ impl Configuration {
flavor: None,
worker_threads: None,
start_paused: None,
is_test,
}
}
@@ -92,16 +93,25 @@ impl Configuration {
Ok(())
}
fn macro_name(&self) -> &'static str {
if self.is_test {
"tokio::test"
} else {
"tokio::main"
}
}
fn build(&self) -> Result<FinalConfig, syn::Error> {
let flavor = self.flavor.unwrap_or(self.default_flavor);
use RuntimeFlavor::*;
let worker_threads = match (flavor, self.worker_threads) {
(CurrentThread, Some((_, worker_threads_span))) => {
return Err(syn::Error::new(
worker_threads_span,
"The `worker_threads` option requires the `multi_thread` runtime flavor.",
))
let msg = format!(
"The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[{}(flavor = \"multi_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(worker_threads_span, msg));
}
(CurrentThread, None) => None,
(Threaded, worker_threads) if self.rt_multi_thread_available => {
@@ -119,10 +129,11 @@ impl Configuration {
let start_paused = match (flavor, self.start_paused) {
(Threaded, Some((_, start_paused_span))) => {
return Err(syn::Error::new(
start_paused_span,
"The `start_paused` option requires the `current_thread` runtime flavor.",
));
let msg = format!(
"The `start_paused` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(start_paused_span, msg));
}
(CurrentThread, Some((start_paused, _))) => Some(start_paused),
(_, None) => None,
@@ -142,12 +153,12 @@ fn parse_int(int: syn::Lit, span: Span, field: &str) -> Result<usize, syn::Error
Ok(value) => Ok(value),
Err(e) => Err(syn::Error::new(
span,
format!("Failed to parse {} as integer: {}", field, e),
format!("Failed to parse value of `{}` as integer: {}", field, e),
)),
},
_ => Err(syn::Error::new(
span,
format!("Failed to parse {} as integer.", field),
format!("Failed to parse value of `{}` as integer.", field),
)),
}
}
@@ -158,7 +169,7 @@ fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result<String, syn::E
syn::Lit::Verbatim(s) => Ok(s.to_string()),
_ => Err(syn::Error::new(
span,
format!("Failed to parse {} as string.", field),
format!("Failed to parse value of `{}` as string.", field),
)),
}
}
@@ -168,7 +179,7 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Erro
syn::Lit::Bool(b) => Ok(b.value),
_ => Err(syn::Error::new(
span,
format!("Failed to parse {} as bool.", field),
format!("Failed to parse value of `{}` as bool.", field),
)),
}
}
@@ -179,24 +190,13 @@ fn parse_knobs(
is_test: bool,
rt_multi_thread: bool,
) -> Result<TokenStream, syn::Error> {
let sig = &mut input.sig;
let body = &input.block;
let attrs = &input.attrs;
let vis = input.vis;
if sig.asyncness.is_none() {
let msg = "the async keyword is missing from the function declaration";
return Err(syn::Error::new_spanned(sig.fn_token, msg));
if input.sig.asyncness.take().is_none() {
let msg = "the `async` keyword is missing from the function declaration";
return Err(syn::Error::new_spanned(input.sig.fn_token, msg));
}
sig.asyncness = None;
let macro_name = if is_test {
"tokio::test"
} else {
"tokio::main"
};
let mut config = Configuration::new(is_test, rt_multi_thread);
let macro_name = config.macro_name();
for arg in args {
match arg {
@@ -208,20 +208,32 @@ fn parse_knobs(
}
match ident.unwrap().to_string().to_lowercase().as_str() {
"worker_threads" => {
config.set_worker_threads(namevalue.lit.clone(), namevalue.span())?;
config.set_worker_threads(
namevalue.lit.clone(),
syn::spanned::Spanned::span(&namevalue.lit),
)?;
}
"flavor" => {
config.set_flavor(namevalue.lit.clone(), namevalue.span())?;
config.set_flavor(
namevalue.lit.clone(),
syn::spanned::Spanned::span(&namevalue.lit),
)?;
}
"start_paused" => {
config.set_start_paused(namevalue.lit.clone(), namevalue.span())?;
config.set_start_paused(
namevalue.lit.clone(),
syn::spanned::Spanned::span(&namevalue.lit),
)?;
}
"core_threads" => {
let msg = "Attribute `core_threads` is renamed to `worker_threads`";
return Err(syn::Error::new_spanned(namevalue, msg));
}
name => {
let msg = format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`", name);
let msg = format!(
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`",
name,
);
return Err(syn::Error::new_spanned(namevalue, msg));
}
}
@@ -266,11 +278,29 @@ fn parse_knobs(
let config = config.build()?;
// 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();
// `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
// `syn::Error::new_spanned` does.
let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span());
let end = last_stmt.last().map_or(start, |t| t.span());
(start, end)
};
let mut rt = match config.flavor {
RuntimeFlavor::CurrentThread => quote! {
RuntimeFlavor::CurrentThread => quote_spanned! {last_stmt_start_span=>
tokio::runtime::Builder::new_current_thread()
},
RuntimeFlavor::Threaded => quote! {
RuntimeFlavor::Threaded => quote_spanned! {last_stmt_start_span=>
tokio::runtime::Builder::new_multi_thread()
},
};
@@ -281,26 +311,31 @@ fn parse_knobs(
rt = quote! { #rt.start_paused(#v) };
}
let header = {
if is_test {
quote! {
#[::core::prelude::v1::test]
}
} else {
quote! {}
let header = if is_test {
quote! {
#[::core::prelude::v1::test]
}
} else {
quote! {}
};
let result = quote! {
#header
#(#attrs)*
#vis #sig {
let body = &input.block;
let brace_token = input.block.brace_token;
input.block = syn::parse2(quote_spanned! {last_stmt_end_span=>
{
#rt
.enable_all()
.build()
.unwrap()
.block_on(async #body)
}
})
.unwrap();
input.block.brace_token = brace_token;
let result = quote! {
#header
#input
};
Ok(result.into())
@@ -334,12 +369,5 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
}
}
if !input.sig.inputs.is_empty() {
let msg = "the test function cannot accept arguments";
return syn::Error::new_spanned(&input.sig.inputs, msg)
.to_compile_error()
.into();
}
parse_knobs(input, args, true, rt_multi_thread).unwrap_or_else(|e| e.to_compile_error().into())
}
+4
View File
@@ -167,6 +167,8 @@ use proc_macro::TokenStream;
/// }
/// ```
///
/// Note that `start_paused` requires the `test-util` feature to be enabled.
///
/// ### NOTE:
///
/// If you rename the Tokio crate in your dependencies this macro will not work.
@@ -257,6 +259,8 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// }
/// ```
///
/// Note that `start_paused` requires the `test-util` feature to be enabled.
///
/// ### NOTE:
///
/// If you rename the Tokio crate in your dependencies this macro will not work.
+36
View File
@@ -1,3 +1,39 @@
# 0.1.6 (May 14, 2021)
### Added
- stream: implement `Error` and `Display` for `BroadcastStreamRecvError` ([#3745])
### Fixed
- stream: avoid yielding in `AllFuture` and `AnyFuture` ([#3625])
[#3745]: https://github.com/tokio-rs/tokio/pull/3745
[#3625]: https://github.com/tokio-rs/tokio/pull/3625
# 0.1.5 (March 20, 2021)
### Fixed
- stream: documentation note for throttle `Unpin` ([#3600])
[#3600]: https://github.com/tokio-rs/tokio/pull/3600
# 0.1.4 (March 9, 2021)
Added
- signal: add `Signal` wrapper ([#3510])
Fixed
- stream: remove duplicate `doc_cfg` declaration ([#3561])
- sync: yield initial value in `WatchStream` ([#3576])
[#3510]: https://github.com/tokio-rs/tokio/pull/3510
[#3561]: https://github.com/tokio-rs/tokio/pull/3561
[#3576]: https://github.com/tokio-rs/tokio/pull/3576
# 0.1.3 (February 5, 2021)
Added
+6 -6
View File
@@ -6,13 +6,13 @@ name = "tokio-stream"
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-stream-0.1.x" git tag.
version = "0.1.3"
version = "0.1.6"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-stream/0.1.3/tokio_stream"
documentation = "https://docs.rs/tokio-stream/0.1.6/tokio_stream"
description = """
Utilities to work with `Stream` and `tokio`.
"""
@@ -30,16 +30,16 @@ signal = ["tokio/signal"]
[dependencies]
futures-core = { version = "0.3.0" }
pin-project-lite = "0.2.0"
tokio = { version = "1.2.0", features = ["sync"] }
tokio-util = { version = "0.6.3", optional = true }
tokio = { version = "1.2.0", path = "../tokio", features = ["sync"] }
tokio-util = { version = "0.6.3", path = "../tokio-util", optional = true }
[dev-dependencies]
tokio = { version = "1.2.0", features = ["full", "test-util"] }
tokio = { version = "1.2.0", path = "../tokio", features = ["full", "test-util"] }
async-stream = "0.3"
tokio-test = { path = "../tokio-test" }
futures = { version = "0.3", default-features = false }
proptest = "0.10.0"
proptest = "1"
[package.metadata.docs.rs]
all-features = true
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2020 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+1 -1
View File
@@ -515,7 +515,7 @@ pub trait StreamExt: Stream {
/// Skip elements from the underlying stream while the provided predicate
/// resolves to `true`.
///
/// This function, like [`Iterator::skip_while`], will ignore elemets from the
/// This function, like [`Iterator::skip_while`], will ignore elements from the
/// stream until the predicate `f` resolves to `false`. Once one element
/// returns false, the rest of the elements will be yielded.
///
+12 -9
View File
@@ -38,18 +38,21 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let me = self.project();
let next = futures_core::ready!(Pin::new(me.stream).poll_next(cx));
let mut stream = Pin::new(me.stream);
match next {
Some(v) => {
if !(me.f)(v) {
Poll::Ready(false)
} else {
cx.waker().wake_by_ref();
Poll::Pending
// Take a maximum of 32 items from the stream before yielding.
for _ in 0..32 {
match futures_core::ready!(stream.as_mut().poll_next(cx)) {
Some(v) => {
if !(me.f)(v) {
return Poll::Ready(false);
}
}
None => return Poll::Ready(true),
}
None => Poll::Ready(true),
}
cx.waker().wake_by_ref();
Poll::Pending
}
}
+12 -9
View File
@@ -38,18 +38,21 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let me = self.project();
let next = futures_core::ready!(Pin::new(me.stream).poll_next(cx));
let mut stream = Pin::new(me.stream);
match next {
Some(v) => {
if (me.f)(v) {
Poll::Ready(true)
} else {
cx.waker().wake_by_ref();
Poll::Pending
// Take a maximum of 32 items from the stream before yielding.
for _ in 0..32 {
match futures_core::ready!(stream.as_mut().poll_next(cx)) {
Some(v) => {
if (me.f)(v) {
return Poll::Ready(true);
}
}
None => return Poll::Ready(false),
}
None => Poll::Ready(false),
}
cx.waker().wake_by_ref();
Poll::Pending
}
}
+2 -1
View File
@@ -23,7 +23,8 @@ where
}
pin_project! {
/// Stream for the [`throttle`](throttle) function.
/// Stream for the [`throttle`](throttle) function. This object is `!Unpin`. If you need it to
/// implement `Unpin` you can pin your throttle like this: `Box::pin(your_throttle)`.
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct Throttle<T> {
+12 -1
View File
@@ -69,7 +69,18 @@ impl<S: Stream> Stream for Timeout<S> {
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.stream.size_hint()
let (lower, upper) = self.stream.size_hint();
// The timeout stream may insert an error before and after each message
// from the underlying stream, but no more than one error between each
// message. Hence the upper bound is computed as 2x+1.
// Using a helper function to enable use of question mark operator.
fn twice_plus_one(value: Option<usize>) -> Option<usize> {
value?.checked_mul(2)?.checked_add(1)
}
(lower, twice_plus_one(upper))
}
}
+3 -3
View File
@@ -605,10 +605,10 @@ mod rand {
/// Fast random number generate
///
/// Implement xorshift64+: 2 32-bit xorshift sequences added together.
/// Shift triplet [17,7,16] was calculated as indicated in Marsaglia's
/// Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
/// Shift triplet `[17,7,16]` was calculated as indicated in Marsaglia's
/// Xorshift paper: <https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf>
/// This generator passes the SmallCrush suite, part of TestU01 framework:
/// http://simul.iro.umontreal.ca/testu01/tu01.html
/// <http://simul.iro.umontreal.ca/testu01/tu01.html>
#[derive(Debug)]
pub(crate) struct FastRand {
one: Cell<u32>,
+10
View File
@@ -27,6 +27,16 @@ pub enum BroadcastStreamRecvError {
Lagged(u64),
}
impl fmt::Display for BroadcastStreamRecvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BroadcastStreamRecvError::Lagged(amt) => write!(f, "channel lagged by {}", amt),
}
}
}
impl std::error::Error for BroadcastStreamRecvError {}
async fn make_future<T: Clone>(mut rx: Receiver<T>) -> (Result<T, RecvError>, Receiver<T>) {
let result = rx.recv().await;
(result, rx)
+1 -1
View File
@@ -11,7 +11,7 @@ use tokio::sync::watch::error::RecvError;
/// A wrapper around [`tokio::sync::watch::Receiver`] that implements [`Stream`].
///
/// This stream will always start by yielding the current value when the WatchStream is polled,
/// regardles of whether it was the initial value or sent afterwards.
/// regardless of whether it was the initial value or sent afterwards.
///
/// # Examples
///
+2
View File
@@ -1,3 +1,5 @@
#![allow(clippy::diverging_sub_expression)]
use std::rc::Rc;
#[allow(dead_code)]
+2 -2
View File
@@ -89,12 +89,12 @@ fn size_overflow() {
}
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::max_value(), Some(usize::max_value()))
(usize::MAX, Some(usize::MAX))
}
}
let m1 = Monster;
let m2 = Monster;
let m = m1.chain(m2);
assert_eq!(m.size_hint(), (usize::max_value(), None));
assert_eq!(m.size_hint(), (usize::MAX, None));
}
+2 -2
View File
@@ -72,12 +72,12 @@ fn size_overflow() {
}
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::max_value(), Some(usize::max_value()))
(usize::MAX, Some(usize::MAX))
}
}
let m1 = Monster;
let m2 = Monster;
let m = m1.merge(m2);
assert_eq!(m.size_hint(), (usize::max_value(), None));
assert_eq!(m.size_hint(), (usize::MAX, None));
}
+12
View File
@@ -1,3 +1,15 @@
# 0.4.2 (May 14, 2021)
- test: add `assert_elapsed!` macro ([#3728])
[#3728]: https://github.com/tokio-rs/tokio/pull/3728
# 0.4.1 (March 10, 2021)
- Fix `io::Mock` to be `Send` and `Sync` ([#3594])
[#3594]: https://github.com/tokio-rs/tokio/pull/3594
# 0.4.0 (December 23, 2020)
- Track `tokio` 1.0 release.
+4 -4
View File
@@ -6,20 +6,20 @@ name = "tokio-test"
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-test-0.4.x" git tag.
version = "0.4.0"
version = "0.4.2"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-test/0.4.0/tokio_test"
documentation = "https://docs.rs/tokio-test/0.4.2/tokio_test"
description = """
Testing utilities for Tokio- and futures-based code
"""
categories = ["asynchronous", "testing"]
[dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["rt", "sync", "time", "test-util"] }
tokio = { version = "1.2.0", path = "../tokio", features = ["rt", "sync", "time", "test-util"] }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
async-stream = "0.3"
@@ -27,7 +27,7 @@ bytes = "1.0.0"
futures-core = "0.3.0"
[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
tokio = { version = "1.2.0", path = "../tokio", features = ["full"] }
futures-util = "0.3.0"
[package.metadata.docs.rs]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2020 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+4 -8
View File
@@ -21,6 +21,7 @@
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::sync::mpsc;
use tokio::time::{self, Duration, Instant, Sleep};
use tokio_stream::wrappers::UnboundedReceiverStream;
use futures_core::{ready, Stream};
use std::collections::VecDeque;
@@ -69,8 +70,7 @@ struct Inner {
waiting: Option<Instant>,
sleep: Option<Pin<Box<Sleep>>>,
read_wait: Option<Waker>,
// rx: mpsc::UnboundedReceiver<Action>,
rx: Pin<Box<dyn Stream<Item = Action> + Send>>,
rx: UnboundedReceiverStream<Action>,
}
impl Builder {
@@ -185,13 +185,9 @@ impl Handle {
impl Inner {
fn new(actions: VecDeque<Action>) -> (Inner, Handle) {
let (tx, mut rx) = mpsc::unbounded_channel();
let (tx, rx) = mpsc::unbounded_channel();
let rx = Box::pin(async_stream::stream! {
while let Some(item) = rx.recv().await {
yield item;
}
});
let rx = UnboundedReceiverStream::new(rx);
let inner = Inner {
actions,
+3 -3
View File
@@ -10,7 +10,7 @@
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]
//! Tokio and Futures based testing utilites
//! Tokio and Futures based testing utilities
pub mod io;
@@ -21,9 +21,9 @@ pub mod task;
/// future completes.
///
/// For more information, see the documentation for
/// [`tokio::runtime::current_thread::Runtime::block_on`][runtime-block-on].
/// [`tokio::runtime::Runtime::block_on`][runtime-block-on].
///
/// [runtime-block-on]: https://docs.rs/tokio/0.2.0-alpha.2/tokio/runtime/current_thread/struct.Runtime.html#method.block_on
/// [runtime-block-on]: https://docs.rs/tokio/1.3.0/tokio/runtime/struct.Runtime.html#method.block_on
pub fn block_on<F: std::future::Future>(future: F) -> F::Output {
use tokio::runtime;
+34
View File
@@ -259,3 +259,37 @@ macro_rules! assert_err {
}
}};
}
/// Asserts that an exact duration has elapsed since since the start instant ±1ms.
///
/// ```rust
/// use tokio::time::{self, Instant};
/// use std::time::Duration;
/// use tokio_test::assert_elapsed;
/// # async fn test_time_passed() {
///
/// let start = Instant::now();
/// let dur = Duration::from_millis(50);
/// time::sleep(dur).await;
/// assert_elapsed!(start, dur);
/// # }
/// ```
///
/// This 1ms buffer is required because Tokio's hashed-wheel timer has finite time resolution and
/// will not always sleep for the exact interval.
#[macro_export]
macro_rules! assert_elapsed {
($start:expr, $dur:expr) => {{
let elapsed = $start.elapsed();
// type ascription improves compiler error when wrong type is passed
let lower: std::time::Duration = $dur;
// Handles ms rounding
assert!(
elapsed >= lower && elapsed <= lower + std::time::Duration::from_millis(1),
"actual = {:?}, expected = {:?}",
elapsed,
lower
);
}};
}
+1 -1
View File
@@ -180,7 +180,7 @@ impl ThreadWaker {
}
}
/// Clears any previously received wakes, avoiding potential spurrious
/// Clears any previously received wakes, avoiding potential spurious
/// wake notifications. This should only be called immediately before running the
/// task.
fn clear(&self) {
+50
View File
@@ -1,3 +1,53 @@
# 0.6.7 (May 14, 2021)
### Added
- udp: make `UdpFramed` take `Borrow<UdpSocket>` ([#3451])
- compat: implement `AsRawFd`/`AsRawHandle` for `Compat<T>` ([#3765])
[#3451]: https://github.com/tokio-rs/tokio/pull/3451
[#3765]: https://github.com/tokio-rs/tokio/pull/3765
# 0.6.6 (April 12, 2021)
### Added
- util: makes `Framed` and `FramedStream` resumable after eof ([#3272])
- util: add `PollSemaphore::{add_permits, available_permits}` ([#3683])
### Fixed
- chore: avoid allocation if `PollSemaphore` is unused ([#3634])
[#3272]: https://github.com/tokio-rs/tokio/pull/3272
[#3634]: https://github.com/tokio-rs/tokio/pull/3634
[#3683]: https://github.com/tokio-rs/tokio/pull/3683
# 0.6.5 (March 20, 2021)
### Fixed
- util: annotate time module as requiring `time` feature ([#3606])
[#3606]: https://github.com/tokio-rs/tokio/pull/3606
# 0.6.4 (March 9, 2021)
### Added
- codec: `AnyDelimiter` codec ([#3406])
- sync: add pollable `mpsc::Sender` ([#3490])
### Fixed
- codec: `LinesCodec` should only return `MaxLineLengthExceeded` once per line ([#3556])
- sync: fuse PollSemaphore ([#3578])
[#3406]: https://github.com/tokio-rs/tokio/pull/3406
[#3490]: https://github.com/tokio-rs/tokio/pull/3490
[#3556]: https://github.com/tokio-rs/tokio/pull/3556
[#3578]: https://github.com/tokio-rs/tokio/pull/3578
# 0.6.3 (January 31, 2021)
### Added
+6 -6
View File
@@ -6,13 +6,13 @@ name = "tokio-util"
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-util-0.6.x" git tag.
version = "0.6.3"
version = "0.6.7"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-util/0.6.3/tokio_util"
documentation = "https://docs.rs/tokio-util/0.6.7/tokio_util"
description = """
Additional utilities for working with Tokio.
"""
@@ -35,7 +35,7 @@ rt = ["tokio/rt"]
__docs_rs = ["futures-util"]
[dependencies]
tokio = { version = "1.0.0", features = ["sync"] }
tokio = { version = "1.0.0", path = "../tokio", features = ["sync"] }
bytes = "1.0.0"
futures-core = "0.3.0"
@@ -47,9 +47,9 @@ pin-project-lite = "0.2.0"
slab = { version = "0.4.1", optional = true } # Backs `DelayQueue`
[dev-dependencies]
tokio = { version = "1.0.0", features = ["full"] }
tokio-test = { version = "0.4.0" }
tokio-stream = { version = "0.1" }
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
tokio-test = { version = "0.4.0", path = "../tokio-test" }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
async-stream = "0.3.0"
futures = "0.3.0"
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2020 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+10
View File
@@ -47,3 +47,13 @@ macro_rules! cfg_rt {
)*
}
}
macro_rules! cfg_time {
($($item:item)*) => {
$(
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
$item
)*
}
}
+1 -1
View File
@@ -234,7 +234,7 @@ impl Default for AnyDelimiterCodec {
}
}
/// An error occured while encoding or decoding a chunk.
/// An error occurred while encoding or decoding a chunk.
#[derive(Debug)]
pub enum AnyDelimiterCodecError {
/// The maximum chunk length was exceeded.
+20 -1
View File
@@ -16,6 +16,20 @@ use std::io;
/// implementing stateful streaming parsers. In many cases, though, this type
/// will simply be a unit struct (e.g. `struct HttpDecoder`).
///
/// For some underlying data-sources, namely files and FIFOs,
/// it's possible to temporarily read 0 bytes by reaching EOF.
///
/// In these cases `decode_eof` will be called until it signals
/// fullfillment of all closing frames by returning `Ok(None)`.
/// After that, repeated attempts to read from the [`Framed`] or [`FramedRead`]
/// will not invoke `decode` or `decode_eof` again, until data can be read
/// during a retry.
///
/// It is up to the Decoder to keep track of a restart after an EOF,
/// and to decide how to handle such an event by, for example,
/// allowing frames to cross EOF boundaries, re-emitting opening frames, or
/// resetting the entire internal state.
///
/// [`Framed`]: crate::codec::Framed
/// [`FramedRead`]: crate::codec::FramedRead
pub trait Decoder {
@@ -115,13 +129,18 @@ pub trait Decoder {
/// This method defaults to calling `decode` and returns an error if
/// `Ok(None)` is returned while there is unconsumed data in `buf`.
/// Typically this doesn't need to be implemented unless the framing
/// protocol differs near the end of the stream.
/// protocol differs near the end of the stream, or if you need to construct
/// frames _across_ eof boundaries on sources that can be resumed.
///
/// Note that the `buf` argument may be empty. If a previous call to
/// `decode_eof` consumed all the bytes in the buffer, `decode_eof` will be
/// called again until it returns `None`, indicating that there are no more
/// frames to yield. This behavior enables returning finalization frames
/// that may not be based on inbound data.
///
/// Once `None` has been returned, `decode_eof` won't be called again until
/// an attempt to resume the stream has been made, where the underlying stream
/// actually returned more data.
fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
match self.decode(buf)? {
Some(frame) => Ok(Some(frame)),
+5
View File
@@ -52,6 +52,11 @@ where
/// calling [`split`] on the `Framed` returned by this method, which will
/// break them into separate objects, allowing them to interact more easily.
///
/// Note that, for some byte sources, the stream can be resumed after an EOF
/// by reading from it, even after it has returned `None`. Repeated attempts
/// to do so, without new data available, continue to return `None` without
/// creating more (closing) frames.
///
/// [`Stream`]: futures_core::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Decode`]: crate::codec::Decoder
+66 -11
View File
@@ -120,42 +120,97 @@ where
let mut pinned = self.project();
let state: &mut ReadFrame = pinned.state.borrow_mut();
// The following loops implements a state machine with each state corresponding
// to a combination of the `is_readable` and `eof` flags. States persist across
// loop entries and most state transitions occur with a return.
//
// The initial state is `reading`.
//
// | state | eof | is_readable |
// |---------|-------|-------------|
// | reading | false | false |
// | framing | false | true |
// | pausing | true | true |
// | paused | true | false |
//
// `decode_eof`
// returns `Some` read 0 bytes
// │ │ │ │
// │ ▼ │ ▼
// ┌───────┐ `decode_eof` ┌──────┐
// ┌──read 0 bytes──▶│pausing│─returns `None`─▶│paused│──┐
// │ └───────┘ └──────┘ │
// pending read┐ │ ┌──────┐ │ ▲ │
// │ │ │ │ │ │ │ │
// │ ▼ │ │ `decode` returns `Some`│ pending read
// │ ╔═══════╗ ┌───────┐◀─┘ │
// └──║reading║─read n>0 bytes─▶│framing│ │
// ╚═══════╝ └───────┘◀──────read n>0 bytes┘
// ▲ │
// │ │
// └─`decode` returns `None`─┘
loop {
// Repeatedly call `decode` or `decode_eof` as long as it is
// "readable". Readable is defined as not having returned `None`. If
// the upstream has returned EOF, and the decoder is no longer
// readable, it can be assumed that the decoder will never become
// readable again, at which point the stream is terminated.
// Repeatedly call `decode` or `decode_eof` while the buffer is "readable",
// i.e. it _might_ contain data consumable as a frame or closing frame.
// Both signal that there is no such data by returning `None`.
//
// If `decode` couldn't read a frame and the upstream source has returned eof,
// `decode_eof` will attempt to decode the remaining bytes as closing frames.
//
// If the underlying AsyncRead is resumable, we may continue after an EOF,
// but must finish emitting all of it's associated `decode_eof` frames.
// Furthermore, we don't want to emit any `decode_eof` frames on retried
// reads after an EOF unless we've actually read more data.
if state.is_readable {
// pausing or framing
if state.eof {
// pausing
let frame = pinned.codec.decode_eof(&mut state.buffer)?;
if frame.is_none() {
state.is_readable = false; // prepare pausing -> paused
}
// implicit pausing -> pausing or pausing -> paused
return Poll::Ready(frame.map(Ok));
}
// framing
trace!("attempting to decode a frame");
if let Some(frame) = pinned.codec.decode(&mut state.buffer)? {
trace!("frame decoded from buffer");
// implicit framing -> framing
return Poll::Ready(Some(Ok(frame)));
}
// framing -> reading
state.is_readable = false;
}
assert!(!state.eof);
// Otherwise, try to read more data and try again. Make sure we've
// got room for at least one byte to read to ensure that we don't
// get a spurious 0 that looks like EOF
// reading or paused
// If we can't build a frame yet, try to read more data and try again.
// Make sure we've got room for at least one byte to read to ensure
// that we don't get a spurious 0 that looks like EOF.
state.buffer.reserve(1);
let bytect = match poll_read_buf(pinned.inner.as_mut(), cx, &mut state.buffer)? {
Poll::Ready(ct) => ct,
// implicit reading -> reading or implicit paused -> paused
Poll::Pending => return Poll::Pending,
};
if bytect == 0 {
if state.eof {
// We're already at an EOF, and since we've reached this path
// we're also not readable. This implies that we've already finished
// our `decode_eof` handling, so we can simply return `None`.
// implicit paused -> paused
return Poll::Ready(None);
}
// prepare reading -> paused
state.eof = true;
} else {
// prepare paused -> framing or noop reading -> framing
state.eof = false;
}
// paused -> framing or reading -> framing or reading -> pausing
state.is_readable = true;
}
}
+5 -5
View File
@@ -486,7 +486,7 @@ impl LengthDelimitedCodec {
// Skip the required bytes
src.advance(self.builder.length_field_offset);
// match endianess
// match endianness
let n = if self.builder.length_field_is_big_endian {
src.get_uint(field_len)
} else {
@@ -535,14 +535,14 @@ impl LengthDelimitedCodec {
Ok(Some(n))
}
fn decode_data(&self, n: usize, src: &mut BytesMut) -> io::Result<Option<BytesMut>> {
fn decode_data(&self, n: usize, src: &mut BytesMut) -> Option<BytesMut> {
// At this point, the buffer has already had the required capacity
// reserved. All there is to do is read.
if src.len() < n {
return Ok(None);
return None;
}
Ok(Some(src.split_to(n)))
Some(src.split_to(n))
}
}
@@ -562,7 +562,7 @@ impl Decoder for LengthDelimitedCodec {
DecodeState::Data(n) => n,
};
match self.decode_data(n, src)? {
match self.decode_data(n, src) {
Some(data) => {
// Update the decode state
self.state = DecodeState::Head;
+2 -2
View File
@@ -203,12 +203,12 @@ impl Default for LinesCodec {
}
}
/// An error occured while encoding or decoding a line.
/// An error occurred while encoding or decoding a line.
#[derive(Debug)]
pub enum LinesCodecError {
/// The maximum line length was exceeded.
MaxLineLengthExceeded,
/// An IO error occured.
/// An IO error occurred.
Io(io::Error),
}
+14
View File
@@ -215,3 +215,17 @@ where
tokio::io::AsyncWrite::poll_shutdown(self.project().inner, cx)
}
}
#[cfg(unix)]
impl<T: std::os::unix::io::AsRawFd> std::os::unix::io::AsRawFd for Compat<T> {
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
self.inner.as_raw_fd()
}
}
#[cfg(windows)]
impl<T: std::os::windows::io::AsRawHandle> std::os::windows::io::AsRawHandle for Compat<T> {
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
self.inner.as_raw_handle()
}
}
+1 -1
View File
@@ -67,7 +67,7 @@ pub enum Either<L, R> {
}
/// A small helper macro which reduces amount of boilerplate in the actual trait method implementation.
/// It takes an invokation of method as an argument (e.g. `self.poll(cx)`), and redirects it to either
/// It takes an invocation of method as an argument (e.g. `self.poll(cx)`), and redirects it to either
/// enum variant held in `self`.
macro_rules! delegate_call {
($self:ident.$method:ident($($args:ident),+)) => {
+4 -3
View File
@@ -45,13 +45,14 @@ cfg_rt! {
pub mod context;
}
cfg_time! {
pub mod time;
}
pub mod sync;
pub mod either;
#[cfg(feature = "time")]
pub mod time;
#[cfg(any(feature = "io", feature = "codec"))]
mod util {
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
+13 -2
View File
@@ -1,5 +1,6 @@
//! An asynchronously awaitable `CancellationToken`.
//! The token allows to signal a cancellation request to one or more tasks.
pub(crate) mod guard;
use crate::loom::sync::atomic::AtomicUsize;
use crate::loom::sync::Mutex;
@@ -11,6 +12,8 @@ use core::ptr::NonNull;
use core::sync::atomic::Ordering;
use core::task::{Context, Poll, Waker};
use guard::DropGuard;
/// A token which can be used to signal a cancellation request to one or more
/// tasks.
///
@@ -275,6 +278,14 @@ impl CancellationToken {
}
}
/// Creates a `DropGuard` for this token.
///
/// Returned guard will cancel this token (and all its children) on drop
/// unless disarmed.
pub fn drop_guard(self) -> DropGuard {
DropGuard { inner: Some(self) }
}
unsafe fn register(
&self,
wait_node: &mut ListNode<WaitQueueEntry>,
@@ -764,8 +775,8 @@ impl CancellationTokenState {
return Poll::Ready(());
}
// So far the token is not cancelled. However it could be cancelld before
// we get the chance to store the `Waker`. Therfore we need to check
// So far the token is not cancelled. However it could be cancelled before
// we get the chance to store the `Waker`. Therefore we need to check
// for cancellation again inside the mutex.
let mut guard = self.synchronized.lock().unwrap();
if guard.is_cancelled {
@@ -0,0 +1,27 @@
use crate::sync::CancellationToken;
/// A wrapper for cancellation token which automatically cancels
/// it on drop. It is created using `drop_guard` method on the `CancellationToken`.
#[derive(Debug)]
pub struct DropGuard {
pub(super) inner: Option<CancellationToken>,
}
impl DropGuard {
/// Returns stored cancellation token and removes this drop guard instance
/// (i.e. it will no longer cancel token). Other guards for this token
/// are not affected.
pub fn disarm(mut self) -> CancellationToken {
self.inner
.take()
.expect("`inner` can be only None in a destructor")
}
}
impl Drop for DropGuard {
fn drop(&mut self) {
if let Some(inner) = &self.inner {
inner.cancel();
}
}
}
@@ -222,7 +222,7 @@ impl<T> LinkedList<T> {
}
}
/// Returns whether the linked list doesn not contain any node
/// Returns whether the linked list does not contain any node
pub fn is_empty(&self) -> bool {
if self.head.is_some() {
return false;
+1 -1
View File
@@ -1,7 +1,7 @@
//! Synchronization primitives
mod cancellation_token;
pub use cancellation_token::{CancellationToken, WaitForCancellationFuture};
pub use cancellation_token::{guard::DropGuard, CancellationToken, WaitForCancellationFuture};
mod intrusive_double_linked_list;
+1 -4
View File
@@ -126,10 +126,7 @@ impl<T: Send + 'static> PollSender<T> {
/// If this method returns `None`, then the channel is closed. (But it is
/// not guaranteed to return `None` if the channel is closed.)
pub fn clone_inner(&self) -> Option<Sender<T>> {
match &self.sender {
Some(sender) => Some((&**sender).clone()),
None => None,
}
self.sender.as_ref().map(|sender| (&**sender).clone())
}
/// Access the underlying `Sender`.
+54 -8
View File
@@ -3,7 +3,7 @@ use std::fmt;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::sync::{AcquireError, OwnedSemaphorePermit, Semaphore};
use tokio::sync::{AcquireError, OwnedSemaphorePermit, Semaphore, TryAcquireError};
use super::ReusableBoxFuture;
@@ -12,17 +12,15 @@ use super::ReusableBoxFuture;
/// [`Semaphore`]: tokio::sync::Semaphore
pub struct PollSemaphore {
semaphore: Arc<Semaphore>,
permit_fut: ReusableBoxFuture<Result<OwnedSemaphorePermit, AcquireError>>,
permit_fut: Option<ReusableBoxFuture<Result<OwnedSemaphorePermit, AcquireError>>>,
}
impl PollSemaphore {
/// Create a new `PollSemaphore`.
pub fn new(semaphore: Arc<Semaphore>) -> Self {
let fut = Arc::clone(&semaphore).acquire_owned();
Self {
semaphore,
permit_fut: ReusableBoxFuture::new(fut),
permit_fut: None,
}
}
@@ -55,16 +53,58 @@ impl PollSemaphore {
/// the `Waker` from the `Context` passed to the most recent call is
/// scheduled to receive a wakeup.
pub fn poll_acquire(&mut self, cx: &mut Context<'_>) -> Poll<Option<OwnedSemaphorePermit>> {
let result = ready!(self.permit_fut.poll(cx));
let permit_future = match self.permit_fut.as_mut() {
Some(fut) => fut,
None => {
// avoid allocations completely if we can grab a permit immediately
match Arc::clone(&self.semaphore).try_acquire_owned() {
Ok(permit) => return Poll::Ready(Some(permit)),
Err(TryAcquireError::Closed) => return Poll::Ready(None),
Err(TryAcquireError::NoPermits) => {}
}
let next_fut = Arc::clone(&self.semaphore).acquire_owned();
self.permit_fut
.get_or_insert(ReusableBoxFuture::new(next_fut))
}
};
let result = ready!(permit_future.poll(cx));
let next_fut = Arc::clone(&self.semaphore).acquire_owned();
self.permit_fut.set(next_fut);
permit_future.set(next_fut);
match result {
Ok(permit) => Poll::Ready(Some(permit)),
Err(_closed) => Poll::Ready(None),
Err(_closed) => {
self.permit_fut = None;
Poll::Ready(None)
}
}
}
/// Returns the current number of available permits.
///
/// This is equivalent to the [`Semaphore::available_permits`] method on the
/// `tokio::sync::Semaphore` type.
///
/// [`Semaphore::available_permits`]: tokio::sync::Semaphore::available_permits
pub fn available_permits(&self) -> usize {
self.semaphore.available_permits()
}
/// Adds `n` new permits to the semaphore.
///
/// The maximum number of permits is `usize::MAX >> 3`, and this function
/// will panic if the limit is exceeded.
///
/// This is equivalent to the [`Semaphore::add_permits`] method on the
/// `tokio::sync::Semaphore` type.
///
/// [`Semaphore::add_permits`]: tokio::sync::Semaphore::add_permits
pub fn add_permits(&self, n: usize) {
self.semaphore.add_permits(n);
}
}
impl Stream for PollSemaphore {
@@ -88,3 +128,9 @@ impl fmt::Debug for PollSemaphore {
.finish()
}
}
impl AsRef<Semaphore> for PollSemaphore {
fn as_ref(&self) -> &Semaphore {
&*self.semaphore
}
}
+67 -66
View File
@@ -30,24 +30,24 @@ use std::task::{self, Poll, Waker};
///
/// Once delays have been configured, the `DelayQueue` is used via its
/// [`Stream`] implementation. [`poll_expired`] is called. If an entry has reached its
/// deadline, it is returned. If not, `Poll::Pending` indicating that the
/// deadline, it is returned. If not, `Poll::Pending` is returned indicating that the
/// current task will be notified once the deadline has been reached.
///
/// # `Stream` implementation
///
/// Items are retrieved from the queue via [`DelayQueue::poll_expired`]. If no delays have
/// expired, no items are returned. In this case, `Pending` is returned and the
/// expired, no items are returned. In this case, `Poll::Pending` is returned and the
/// current task is registered to be notified once the next item's delay has
/// expired.
///
/// If no items are in the queue, i.e. `is_empty()` returns `true`, then `poll`
/// returns `Ready(None)`. This indicates that the stream has reached an end.
/// returns `Poll::Ready(None)`. This indicates that the stream has reached an end.
/// However, if a new item is inserted *after*, `poll` will once again start
/// returning items or `Pending.
/// returning items or `Poll::Pending`.
///
/// Items are returned ordered by their expirations. Items that are configured
/// to expire first will be returned first. There are no ordering guarantees
/// for items configured to expire the same instant. Also note that delays are
/// for items configured to expire at the same instant. Also note that delays are
/// rounded to the closest millisecond.
///
/// # Implementation
@@ -152,7 +152,7 @@ pub struct DelayQueue<T> {
waker: Option<Waker>,
}
/// An entry in `DelayQueue` that has expired and removed.
/// An entry in `DelayQueue` that has expired and been removed.
///
/// Values are returned by [`DelayQueue::poll_expired`].
///
@@ -211,7 +211,7 @@ struct Data<T> {
const MAX_ENTRIES: usize = (1 << 30) - 1;
impl<T> DelayQueue<T> {
/// Creates a new, empty, `DelayQueue`
/// Creates a new, empty, `DelayQueue`.
///
/// The queue will not allocate storage until items are inserted into it.
///
@@ -239,15 +239,15 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::with_capacity(10);
/// let mut delay_queue = DelayQueue::with_capacity(10);
///
/// // These insertions are done without further allocation
/// for i in 0..10 {
/// delay_queue.insert(i, Duration::from_secs(i));
/// }
/// // These insertions are done without further allocation
/// for i in 0..10 {
/// delay_queue.insert(i, Duration::from_secs(i));
/// }
///
/// // This will make the queue allocate additional storage
/// delay_queue.insert(11, Duration::from_secs(11));
/// // This will make the queue allocate additional storage
/// delay_queue.insert(11, Duration::from_secs(11));
/// # }
/// ```
pub fn with_capacity(capacity: usize) -> DelayQueue<T> {
@@ -272,8 +272,8 @@ impl<T> DelayQueue<T> {
/// `value` will be returned from [`poll_expired`]. If `when` has already been
/// reached, then `value` is immediately made available to poll.
///
/// The return value represents the insertion and is used at an argument to
/// [`remove`] and [`reset`]. Note that [`Key`] is token and is reused once
/// The return value represents the insertion and is used as an argument to
/// [`remove`] and [`reset`]. Note that [`Key`] is a token and is reused once
/// `value` is removed from the queue either by calling [`poll_expired`] after
/// `when` is reached or by calling [`remove`]. At this point, the caller
/// must take care to not use the returned [`Key`] again as it may reference
@@ -295,13 +295,13 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert_at(
/// "foo", Instant::now() + Duration::from_secs(5));
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert_at(
/// "foo", Instant::now() + Duration::from_secs(5));
///
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// # }
/// ```
///
@@ -353,7 +353,7 @@ impl<T> DelayQueue<T> {
/// Attempts to pull out the next value of the delay queue, registering the
/// current task for wakeup if the value is not yet available, and returning
/// None if the queue is exhausted.
/// `None` if the queue is exhausted.
pub fn poll_expired(
&mut self,
cx: &mut task::Context<'_>,
@@ -391,7 +391,7 @@ impl<T> DelayQueue<T> {
///
/// `value` is stored in the queue until `timeout` duration has
/// elapsed after `insert` was called. At that point, `value` will
/// be returned from [`poll_expired`]. If `timeout` a Duration of
/// be returned from [`poll_expired`]. If `timeout` is a `Duration` of
/// zero, then `value` is immediately made available to poll.
///
/// The return value represents the insertion and is used as an
@@ -419,12 +419,12 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
///
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// # }
/// ```
///
@@ -452,11 +452,12 @@ impl<T> DelayQueue<T> {
}
}
/// Removes the key fom the expired queue or the timer wheel
/// depending on its expiration status
/// Removes the key from the expired queue or the timer wheel
/// depending on its expiration status.
///
/// # Panics
/// Panics if the key is not contained in the expired queue or the wheel
///
/// Panics if the key is not contained in the expired queue or the wheel.
fn remove_key(&mut self, key: &Key) {
use crate::time::wheel::Stack;
@@ -488,12 +489,12 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
///
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// # }
/// ```
pub fn remove(&mut self, key: &Key) -> Expired<T> {
@@ -531,14 +532,14 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
///
/// // "foo" is scheduled to be returned in 5 seconds
/// // "foo" is scheduled to be returned in 5 seconds
///
/// delay_queue.reset_at(&key, Instant::now() + Duration::from_secs(10));
/// delay_queue.reset_at(&key, Instant::now() + Duration::from_secs(10));
///
/// // "foo" is now scheduled to be returned in 10 seconds
/// // "foo" is now scheduled to be returned in 10 seconds
/// # }
/// ```
pub fn reset_at(&mut self, key: &Key, when: Instant) {
@@ -559,7 +560,7 @@ impl<T> DelayQueue<T> {
}
}
/// Returns the next time poll as determined by the wheel
/// Returns the next time to poll as determined by the wheel
fn next_deadline(&mut self) -> Option<Instant> {
self.wheel
.poll_at()
@@ -591,14 +592,14 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
///
/// // "foo" is scheduled to be returned in 5 seconds
/// // "foo" is scheduled to be returned in 5 seconds
///
/// delay_queue.reset(&key, Duration::from_secs(10));
/// delay_queue.reset(&key, Duration::from_secs(10));
///
/// // "foo"is now scheduled to be returned in 10 seconds
/// // "foo"is now scheduled to be returned in 10 seconds
/// # }
/// ```
pub fn reset(&mut self, key: &Key, timeout: Duration) {
@@ -621,15 +622,15 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let mut delay_queue = DelayQueue::new();
///
/// delay_queue.insert("foo", Duration::from_secs(5));
/// delay_queue.insert("foo", Duration::from_secs(5));
///
/// assert!(!delay_queue.is_empty());
/// assert!(!delay_queue.is_empty());
///
/// delay_queue.clear();
/// delay_queue.clear();
///
/// assert!(delay_queue.is_empty());
/// assert!(delay_queue.is_empty());
/// # }
/// ```
pub fn clear(&mut self) {
@@ -663,10 +664,10 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue: DelayQueue<i32> = DelayQueue::with_capacity(10);
/// assert_eq!(delay_queue.len(), 0);
/// delay_queue.insert(3, Duration::from_secs(5));
/// assert_eq!(delay_queue.len(), 1);
/// let mut delay_queue: DelayQueue<i32> = DelayQueue::with_capacity(10);
/// assert_eq!(delay_queue.len(), 0);
/// delay_queue.insert(3, Duration::from_secs(5));
/// assert_eq!(delay_queue.len(), 1);
/// # }
/// ```
pub fn len(&self) -> usize {
@@ -698,12 +699,12 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let mut delay_queue = DelayQueue::new();
///
/// delay_queue.insert("hello", Duration::from_secs(10));
/// delay_queue.reserve(10);
/// delay_queue.insert("hello", Duration::from_secs(10));
/// delay_queue.reserve(10);
///
/// assert!(delay_queue.capacity() >= 11);
/// assert!(delay_queue.capacity() >= 11);
/// # }
/// ```
pub fn reserve(&mut self, additional: usize) {
@@ -713,7 +714,7 @@ impl<T> DelayQueue<T> {
/// Returns `true` if there are no items in the queue.
///
/// Note that this function returns `false` even if all items have not yet
/// expired and a call to `poll` will return `Pending`.
/// expired and a call to `poll` will return `Poll::Pending`.
///
/// # Examples
///
@@ -723,11 +724,11 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// assert!(delay_queue.is_empty());
/// let mut delay_queue = DelayQueue::new();
/// assert!(delay_queue.is_empty());
///
/// delay_queue.insert("hello", Duration::from_secs(5));
/// assert!(!delay_queue.is_empty());
/// delay_queue.insert("hello", Duration::from_secs(5));
/// assert!(!delay_queue.is_empty());
/// # }
/// ```
pub fn is_empty(&self) -> bool {
+1 -1
View File
@@ -12,9 +12,9 @@ use std::time::Duration;
mod wheel;
#[doc(inline)]
pub mod delay_queue;
#[doc(inline)]
pub use delay_queue::DelayQueue;
// ===== Internal utils =====
+35 -20
View File
@@ -6,9 +6,12 @@ use tokio::{io::ReadBuf, net::UdpSocket};
use bytes::{BufMut, BytesMut};
use futures_core::ready;
use futures_sink::Sink;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{
borrow::Borrow,
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
};
use std::{io, mem::MaybeUninit};
/// A unified [`Stream`] and [`Sink`] interface to an underlying `UdpSocket`, using
@@ -32,10 +35,10 @@ use std::{io, mem::MaybeUninit};
/// [`Sink`]: futures_sink::Sink
/// [`split`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.split
#[must_use = "sinks do nothing unless polled"]
#[cfg_attr(docsrs, doc(all(feature = "codec", feature = "udp")))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "codec", feature = "udp"))))]
#[derive(Debug)]
pub struct UdpFramed<C> {
socket: UdpSocket,
pub struct UdpFramed<C, T = UdpSocket> {
socket: T,
codec: C,
rd: BytesMut,
wr: BytesMut,
@@ -48,7 +51,13 @@ pub struct UdpFramed<C> {
const INITIAL_RD_CAPACITY: usize = 64 * 1024;
const INITIAL_WR_CAPACITY: usize = 8 * 1024;
impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
impl<C, T> Unpin for UdpFramed<C, T> {}
impl<C, T> Stream for UdpFramed<C, T>
where
T: Borrow<UdpSocket>,
C: Decoder,
{
type Item = Result<(C::Item, SocketAddr), C::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
@@ -79,7 +88,7 @@ impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
let buf = &mut *(pin.rd.chunk_mut() as *mut _ as *mut [MaybeUninit<u8>]);
let mut read = ReadBuf::uninit(buf);
let ptr = read.filled().as_ptr();
let res = ready!(Pin::new(&mut pin.socket).poll_recv_from(cx, &mut read));
let res = ready!(pin.socket.borrow().poll_recv_from(cx, &mut read));
assert_eq!(ptr, read.filled().as_ptr());
let addr = res?;
@@ -93,7 +102,11 @@ impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
}
}
impl<I, C: Encoder<I> + Unpin> Sink<(I, SocketAddr)> for UdpFramed<C> {
impl<I, C, T> Sink<(I, SocketAddr)> for UdpFramed<C, T>
where
T: Borrow<UdpSocket>,
C: Encoder<I>,
{
type Error = C::Error;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@@ -125,13 +138,13 @@ impl<I, C: Encoder<I> + Unpin> Sink<(I, SocketAddr)> for UdpFramed<C> {
}
let Self {
ref mut socket,
ref socket,
ref mut out_addr,
ref mut wr,
..
} = *self;
let n = ready!(socket.poll_send_to(cx, &wr, *out_addr))?;
let n = ready!(socket.borrow().poll_send_to(cx, &wr, *out_addr))?;
let wrote_all = n == self.wr.len();
self.wr.clear();
@@ -156,11 +169,14 @@ impl<I, C: Encoder<I> + Unpin> Sink<(I, SocketAddr)> for UdpFramed<C> {
}
}
impl<C> UdpFramed<C> {
impl<C, T> UdpFramed<C, T>
where
T: Borrow<UdpSocket>,
{
/// Create a new `UdpFramed` backed by the given socket and codec.
///
/// See struct level documentation for more details.
pub fn new(socket: UdpSocket, codec: C) -> UdpFramed<C> {
pub fn new(socket: T, codec: C) -> UdpFramed<C, T> {
Self {
socket,
codec,
@@ -180,27 +196,21 @@ impl<C> UdpFramed<C> {
/// Care should be taken to not tamper with the underlying stream of data
/// coming in as it may corrupt the stream of frames otherwise being worked
/// with.
pub fn get_ref(&self) -> &UdpSocket {
pub fn get_ref(&self) -> &T {
&self.socket
}
/// Returns a mutable reference to the underlying I/O stream wrapped by
/// `Framed`.
/// Returns a mutable reference to the underlying I/O stream wrapped by `Framed`.
///
/// # Note
///
/// Care should be taken to not tamper with the underlying stream of data
/// coming in as it may corrupt the stream of frames otherwise being worked
/// with.
pub fn get_mut(&mut self) -> &mut UdpSocket {
pub fn get_mut(&mut self) -> &mut T {
&mut self.socket
}
/// Consumes the `Framed`, returning its underlying I/O stream.
pub fn into_inner(self) -> UdpSocket {
self.socket
}
/// Returns a reference to the underlying codec wrapped by
/// `Framed`.
///
@@ -228,4 +238,9 @@ impl<C> UdpFramed<C> {
pub fn read_buffer_mut(&mut self) -> &mut BytesMut {
&mut self.rd
}
/// Consumes the `Framed`, returning its underlying I/O stream.
pub fn into_inner(self) -> T {
self.socket
}
}
+2
View File
@@ -0,0 +1,2 @@
#![cfg(not(feature = "full"))]
compile_error!("run tokio-util tests with `--features full`");
+23
View File
@@ -254,6 +254,29 @@ fn multi_frames_on_eof() {
});
}
#[test]
fn read_eof_then_resume() {
let mut task = task::spawn(());
let mock = mock! {
Ok(b"\x00\x00\x00\x01".to_vec()),
Ok(b"".to_vec()),
Ok(b"\x00\x00\x00\x02".to_vec()),
Ok(b"".to_vec()),
Ok(b"\x00\x00\x00\x03".to_vec()),
};
let mut framed = FramedRead::new(mock, U32Decoder);
task.enter(|cx, _| {
assert_read!(pin!(framed).poll_next(cx), 1);
assert!(assert_ready!(pin!(framed).poll_next(cx)).is_none());
assert_read!(pin!(framed).poll_next(cx), 2);
assert!(assert_ready!(pin!(framed).poll_next(cx)).is_none());
assert_read!(pin!(framed).poll_next(cx), 3);
assert!(assert_ready!(pin!(framed).poll_next(cx)).is_none());
assert!(assert_ready!(pin!(framed).poll_next(cx)).is_none());
});
}
// ===== Mock ======
struct Mock {
+3 -3
View File
@@ -6,9 +6,9 @@ use tokio_util::sync::PollSemaphore;
type SemRet = Option<OwnedSemaphorePermit>;
fn semaphore_poll<'a>(
sem: &'a mut PollSemaphore,
) -> tokio_test::task::Spawn<impl Future<Output = SemRet> + 'a> {
fn semaphore_poll(
sem: &mut PollSemaphore,
) -> tokio_test::task::Spawn<impl Future<Output = SemRet> + '_> {
let fut = futures::future::poll_fn(move |cx| sem.poll_acquire(cx));
tokio_test::task::spawn(fut)
}
+29
View File
@@ -10,6 +10,7 @@ use futures::future::try_join;
use futures::future::FutureExt;
use futures::sink::SinkExt;
use std::io;
use std::sync::Arc;
#[cfg_attr(any(target_os = "macos", target_os = "ios"), allow(unused_assignments))]
#[tokio::test]
@@ -101,3 +102,31 @@ async fn send_framed_lines_codec() -> std::io::Result<()> {
Ok(())
}
#[tokio::test]
async fn framed_half() -> std::io::Result<()> {
let a_soc = Arc::new(UdpSocket::bind("127.0.0.1:0").await?);
let b_soc = a_soc.clone();
let a_addr = a_soc.local_addr()?;
let b_addr = b_soc.local_addr()?;
let mut a = UdpFramed::new(a_soc, ByteCodec);
let mut b = UdpFramed::new(b_soc, LinesCodec::new());
let msg = b"1\r\n2\r\n3\r\n".to_vec();
a.send((&msg, b_addr)).await?;
let msg = b"4\r\n5\r\n6\r\n".to_vec();
a.send((&msg, b_addr)).await?;
assert_eq!(b.next().await.unwrap().unwrap(), ("1".to_string(), a_addr));
assert_eq!(b.next().await.unwrap().unwrap(), ("2".to_string(), a_addr));
assert_eq!(b.next().await.unwrap().unwrap(), ("3".to_string(), a_addr));
assert_eq!(b.next().await.unwrap().unwrap(), ("4".to_string(), a_addr));
assert_eq!(b.next().await.unwrap().unwrap(), ("5".to_string(), a_addr));
assert_eq!(b.next().await.unwrap().unwrap(), ("6".to_string(), a_addr));
Ok(())
}
+269
View File
@@ -1,3 +1,272 @@
# 1.8.2 (July 19, 2021)
Fixes a missed edge case from 1.8.1.
### Fixed
- runtime: drop canceled future on next poll (#3965)
# 1.8.1 (July 6, 2021)
Forward ports 1.5.1 fixes.
### Fixed
- runtime: remotely abort tasks on `JoinHandle::abort` ([#3934])
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
# 1.8.0 (July 2, 2021)
### Added
- io: add `get_{ref,mut}` methods to `AsyncFdReadyGuard` and `AsyncFdReadyMutGuard` ([#3807])
- io: efficient implementation of vectored writes for `BufWriter` ([#3163])
- net: add ready/try methods to `NamedPipe{Client,Server}` ([#3866], [#3899])
- sync: add `watch::Receiver::borrow_and_update` ([#3813])
- sync: implement `From<T>` for `OnceCell<T>` ([#3877])
- time: allow users to specify Interval behaviour when delayed ([#3721])
### Added (unstable)
- rt: add `tokio::task::Builder` ([#3881])
### Fixed
- net: handle HUP event with `UnixStream` ([#3898])
### Documented
- doc: document cancellation safety ([#3900])
- time: add wait alias to sleep ([#3897])
- time: document auto-advancing behaviour of runtime ([#3763])
[#3163]: https://github.com/tokio-rs/tokio/pull/3163
[#3721]: https://github.com/tokio-rs/tokio/pull/3721
[#3763]: https://github.com/tokio-rs/tokio/pull/3763
[#3807]: https://github.com/tokio-rs/tokio/pull/3807
[#3813]: https://github.com/tokio-rs/tokio/pull/3813
[#3866]: https://github.com/tokio-rs/tokio/pull/3866
[#3877]: https://github.com/tokio-rs/tokio/pull/3877
[#3881]: https://github.com/tokio-rs/tokio/pull/3881
[#3897]: https://github.com/tokio-rs/tokio/pull/3897
[#3898]: https://github.com/tokio-rs/tokio/pull/3898
[#3899]: https://github.com/tokio-rs/tokio/pull/3899
[#3900]: https://github.com/tokio-rs/tokio/pull/3900
# 1.7.2 (July 6, 2021)
Forward ports 1.5.1 fixes.
### Fixed
- runtime: remotely abort tasks on `JoinHandle::abort` ([#3934])
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
# 1.7.1 (June 18, 2021)
### Fixed
- runtime: fix early task shutdown during runtime shutdown ([#3870])
[#3870]: https://github.com/tokio-rs/tokio/pull/3870
# 1.7.0 (June 15, 2021)
### Added
- net: add named pipes on windows ([#3760])
- net: add `TcpSocket` from `std::net::TcpStream` conversion ([#3838])
- sync: add `receiver_count` to `watch::Sender` ([#3729])
- sync: export `sync::notify::Notified` future publicly ([#3840])
- tracing: instrument task wakers ([#3836])
### Fixed
- macros: suppress `clippy::default_numeric_fallback` lint in generated code ([#3831])
- runtime: immediately drop new tasks when runtime is shut down ([#3752])
- sync: deprecate unused `mpsc::RecvError` type ([#3833])
### Documented
- io: clarify EOF condition for `AsyncReadExt::read_buf` ([#3850])
- io: clarify limits on return values of `AsyncWrite::poll_write` ([#3820])
- sync: add examples to Semaphore ([#3808])
[#3729]: https://github.com/tokio-rs/tokio/pull/3729
[#3752]: https://github.com/tokio-rs/tokio/pull/3752
[#3760]: https://github.com/tokio-rs/tokio/pull/3760
[#3808]: https://github.com/tokio-rs/tokio/pull/3808
[#3820]: https://github.com/tokio-rs/tokio/pull/3820
[#3831]: https://github.com/tokio-rs/tokio/pull/3831
[#3833]: https://github.com/tokio-rs/tokio/pull/3833
[#3836]: https://github.com/tokio-rs/tokio/pull/3836
[#3838]: https://github.com/tokio-rs/tokio/pull/3838
[#3840]: https://github.com/tokio-rs/tokio/pull/3840
[#3850]: https://github.com/tokio-rs/tokio/pull/3850
# 1.6.3 (July 6, 2021)
Forward ports 1.5.1 fixes.
### Fixed
- runtime: remotely abort tasks on `JoinHandle::abort` ([#3934])
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
# 1.6.2 (June 14, 2021)
### Fixes
- test: sub-ms `time:advance` regression introduced in 1.6 ([#3852])
[#3852]: https://github.com/tokio-rs/tokio/pull/3852
# 1.6.1 (May 28, 2021)
This release reverts [#3518] because it doesn't work on some kernels due to
a kernel bug. ([#3803])
[#3518]: https://github.com/tokio-rs/tokio/issues/3518
[#3803]: https://github.com/tokio-rs/tokio/issues/3803
# 1.6.0 (May 14, 2021)
### Added
- fs: try doing a non-blocking read before punting to the threadpool ([#3518])
- io: add `write_all_buf` to `AsyncWriteExt` ([#3737])
- io: implement `AsyncSeek` for `BufReader`, `BufWriter`, and `BufStream` ([#3491])
- net: support non-blocking vectored I/O ([#3761])
- sync: add `mpsc::Sender::{reserve_owned, try_reserve_owned}` ([#3704])
- sync: add a `MutexGuard::map` method that returns a `MappedMutexGuard` ([#2472])
- time: add getter for Interval's period ([#3705])
### Fixed
- io: wake pending writers on `DuplexStream` close ([#3756])
- process: avoid redundant effort to reap orphan processes ([#3743])
- signal: use `std::os::raw::c_int` instead of `libc::c_int` on public API ([#3774])
- sync: preserve permit state in `notify_waiters` ([#3660])
- task: update `JoinHandle` panic message ([#3727])
- time: prevent `time::advance` from going too far ([#3712])
### Documented
- net: hide `net::unix::datagram` module from docs ([#3775])
- process: updated example ([#3748])
- sync: `Barrier` doc should use task, not thread ([#3780])
- task: update documentation on `block_in_place` ([#3753])
[#2472]: https://github.com/tokio-rs/tokio/pull/2472
[#3491]: https://github.com/tokio-rs/tokio/pull/3491
[#3518]: https://github.com/tokio-rs/tokio/pull/3518
[#3660]: https://github.com/tokio-rs/tokio/pull/3660
[#3704]: https://github.com/tokio-rs/tokio/pull/3704
[#3705]: https://github.com/tokio-rs/tokio/pull/3705
[#3712]: https://github.com/tokio-rs/tokio/pull/3712
[#3727]: https://github.com/tokio-rs/tokio/pull/3727
[#3737]: https://github.com/tokio-rs/tokio/pull/3737
[#3743]: https://github.com/tokio-rs/tokio/pull/3743
[#3748]: https://github.com/tokio-rs/tokio/pull/3748
[#3753]: https://github.com/tokio-rs/tokio/pull/3753
[#3756]: https://github.com/tokio-rs/tokio/pull/3756
[#3761]: https://github.com/tokio-rs/tokio/pull/3761
[#3774]: https://github.com/tokio-rs/tokio/pull/3774
[#3775]: https://github.com/tokio-rs/tokio/pull/3775
[#3780]: https://github.com/tokio-rs/tokio/pull/3780
# 1.5.1 (July 6, 2021)
### Fixed
- runtime: remotely abort tasks on `JoinHandle::abort` ([#3934])
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
# 1.5.0 (April 12, 2021)
### Added
- io: add `AsyncSeekExt::stream_position` ([#3650])
- io: add `AsyncWriteExt::write_vectored` ([#3678])
- io: add a `copy_bidirectional` utility ([#3572])
- net: implement `IntoRawFd` for `TcpSocket` ([#3684])
- sync: add `OnceCell` ([#3591])
- sync: add `OwnedRwLockReadGuard` and `OwnedRwLockWriteGuard` ([#3340])
- sync: add `Semaphore::is_closed` ([#3673])
- sync: add `mpsc::Sender::capacity` ([#3690])
- sync: allow configuring `RwLock` max reads ([#3644])
- task: add `sync_scope` for `LocalKey` ([#3612])
### Fixed
- chore: try to avoid `noalias` attributes on intrusive linked list ([#3654])
- rt: fix panic in `JoinHandle::abort()` when called from other threads ([#3672])
- sync: don't panic in `oneshot::try_recv` ([#3674])
- sync: fix notifications getting dropped on receiver drop ([#3652])
- sync: fix `Semaphore` permit overflow calculation ([#3644])
### Documented
- io: clarify requirements of `AsyncFd` ([#3635])
- runtime: fix unclear docs for `{Handle,Runtime}::block_on` ([#3628])
- sync: document that `Semaphore` is fair ([#3693])
- sync: improve doc on blocking mutex ([#3645])
[#3340]: https://github.com/tokio-rs/tokio/pull/3340
[#3572]: https://github.com/tokio-rs/tokio/pull/3572
[#3591]: https://github.com/tokio-rs/tokio/pull/3591
[#3612]: https://github.com/tokio-rs/tokio/pull/3612
[#3628]: https://github.com/tokio-rs/tokio/pull/3628
[#3635]: https://github.com/tokio-rs/tokio/pull/3635
[#3644]: https://github.com/tokio-rs/tokio/pull/3644
[#3645]: https://github.com/tokio-rs/tokio/pull/3645
[#3650]: https://github.com/tokio-rs/tokio/pull/3650
[#3652]: https://github.com/tokio-rs/tokio/pull/3652
[#3654]: https://github.com/tokio-rs/tokio/pull/3654
[#3672]: https://github.com/tokio-rs/tokio/pull/3672
[#3673]: https://github.com/tokio-rs/tokio/pull/3673
[#3674]: https://github.com/tokio-rs/tokio/pull/3674
[#3678]: https://github.com/tokio-rs/tokio/pull/3678
[#3684]: https://github.com/tokio-rs/tokio/pull/3684
[#3690]: https://github.com/tokio-rs/tokio/pull/3690
[#3693]: https://github.com/tokio-rs/tokio/pull/3693
# 1.4.0 (March 20, 2021)
### Added
- macros: introduce biased argument for `select!` ([#3603])
- runtime: add `Handle::block_on` ([#3569])
### Fixed
- runtime: avoid unnecessary polling of `block_on` future ([#3582])
- runtime: fix memory leak/growth when creating many runtimes ([#3564])
- runtime: mark `EnterGuard` with `must_use` ([#3609])
### Documented
- chore: mention fix for building docs in contributing guide ([#3618])
- doc: add link to `PollSender` ([#3613])
- doc: alias sleep to delay ([#3604])
- sync: improve `Mutex` FIFO explanation ([#3615])
- timer: fix double newline in module docs ([#3617])
[#3564]: https://github.com/tokio-rs/tokio/pull/3564
[#3613]: https://github.com/tokio-rs/tokio/pull/3613
[#3618]: https://github.com/tokio-rs/tokio/pull/3618
[#3617]: https://github.com/tokio-rs/tokio/pull/3617
[#3582]: https://github.com/tokio-rs/tokio/pull/3582
[#3615]: https://github.com/tokio-rs/tokio/pull/3615
[#3603]: https://github.com/tokio-rs/tokio/pull/3603
[#3609]: https://github.com/tokio-rs/tokio/pull/3609
[#3604]: https://github.com/tokio-rs/tokio/pull/3604
[#3569]: https://github.com/tokio-rs/tokio/pull/3569
# 1.3.0 (March 9, 2021)
### Added
+9 -4
View File
@@ -7,12 +7,12 @@ name = "tokio"
# - README.md
# - Update CHANGELOG.md.
# - Create "v1.0.x" git tag.
version = "1.3.0"
version = "1.8.2"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
readme = "README.md"
documentation = "https://docs.rs/tokio/1.3.0/tokio/"
documentation = "https://docs.rs/tokio/1.8.2/tokio/"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
description = """
@@ -54,6 +54,7 @@ net = [
"mio/tcp",
"mio/udp",
"mio/uds",
"winapi/namedpipeapi",
]
process = [
"bytes",
@@ -115,17 +116,21 @@ version = "0.3.8"
default-features = false
optional = true
[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" }
futures = { version = "0.3.0", features = ["async-await"] }
proptest = "0.10.0"
proptest = "1"
rand = "0.8.0"
tempfile = "3.1.0"
async-stream = "0.3"
socket2 = "0.4"
[target.'cfg(loom)'.dev-dependencies]
loom = { version = "0.4", features = ["futures", "checkpoint"] }
loom = { version = "0.5", features = ["futures", "checkpoint"] }
[build-dependencies]
autocfg = "1" # Needed for conditionally enabling `track-caller`
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2020 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+3 -3
View File
@@ -228,7 +228,7 @@ It is only possible to implement `AsyncRead` and `AsyncWrite` for resource types
themselves and not for `&Resource`. Implementing the traits for `&Resource`
would permit concurrent operations to the resource. Because only a single waker
is stored per direction, any concurrent usage would result in deadlocks. An
alterate implementation would call for a `Vec<Waker>` but this would result in
alternate implementation would call for a `Vec<Waker>` but this would result in
memory leaks.
## Enabling reads and writes for `&TcpStream`
@@ -268,9 +268,9 @@ select! {
}
```
It is also possible to sotre a `TcpStream` in an `Arc`.
It is also possible to store a `TcpStream` in an `Arc`.
```rust
let arc_stream = Arc::new(my_tcp_stream);
let n = arc_stream.by_ref().read(buf).await?;
```
```
+23
View File
@@ -0,0 +1,23 @@
//! Types which are documented locally in the Tokio crate, but does not actually
//! live here.
//!
//! **Note** this module is only visible on docs.rs, you cannot use it directly
//! in your own code.
/// The name of a type which is not defined here.
///
/// This is typically used as an alias for another type, like so:
///
/// ```rust,ignore
/// /// See [some::other::location](https://example.com).
/// type DEFINED_ELSEWHERE = crate::doc::NotDefinedHere;
/// ```
///
/// This type is uninhabitable like the [`never` type] to ensure that no one
/// will ever accidentally use it.
///
/// [`never` type]: https://doc.rust-lang.org/std/primitive.never.html
pub enum NotDefinedHere {}
pub mod os;
pub mod winapi;
+26
View File
@@ -0,0 +1,26 @@
//! See [std::os](https://doc.rust-lang.org/std/os/index.html).
/// Platform-specific extensions to `std` for Windows.
///
/// See [std::os::windows](https://doc.rust-lang.org/std/os/windows/index.html).
pub mod windows {
/// Windows-specific extensions to general I/O primitives.
///
/// See [std::os::windows::io](https://doc.rust-lang.org/std/os/windows/io/index.html).
pub mod io {
/// See [std::os::windows::io::RawHandle](https://doc.rust-lang.org/std/os/windows/io/type.RawHandle.html)
pub type RawHandle = crate::doc::NotDefinedHere;
/// 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)
fn as_raw_handle(&self) -> RawHandle;
}
/// See [std::os::windows::io::FromRawHandle](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html)
pub trait FromRawHandle {
/// 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;
}
}
}
+66
View File
@@ -0,0 +1,66 @@
//! See [winapi].
//!
//! [winapi]: https://docs.rs/winapi
/// See [winapi::shared](https://docs.rs/winapi/*/winapi/shared/index.html).
pub mod shared {
/// See [winapi::shared::winerror](https://docs.rs/winapi/*/winapi/shared/winerror/index.html).
#[allow(non_camel_case_types)]
pub mod winerror {
/// See [winapi::shared::winerror::ERROR_ACCESS_DENIED][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_ACCESS_DENIED.html
pub type ERROR_ACCESS_DENIED = crate::doc::NotDefinedHere;
/// See [winapi::shared::winerror::ERROR_PIPE_BUSY][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_PIPE_BUSY.html
pub type ERROR_PIPE_BUSY = crate::doc::NotDefinedHere;
/// See [winapi::shared::winerror::ERROR_MORE_DATA][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_MORE_DATA.html
pub type ERROR_MORE_DATA = crate::doc::NotDefinedHere;
}
}
/// See [winapi::um](https://docs.rs/winapi/*/winapi/um/index.html).
pub mod um {
/// See [winapi::um::winbase](https://docs.rs/winapi/*/winapi/um/winbase/index.html).
#[allow(non_camel_case_types)]
pub mod winbase {
/// See [winapi::um::winbase::PIPE_TYPE_MESSAGE][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_TYPE_MESSAGE.html
pub type PIPE_TYPE_MESSAGE = crate::doc::NotDefinedHere;
/// See [winapi::um::winbase::PIPE_TYPE_BYTE][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_TYPE_BYTE.html
pub type PIPE_TYPE_BYTE = crate::doc::NotDefinedHere;
/// See [winapi::um::winbase::PIPE_CLIENT_END][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_CLIENT_END.html
pub type PIPE_CLIENT_END = crate::doc::NotDefinedHere;
/// See [winapi::um::winbase::PIPE_SERVER_END][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_SERVER_END.html
pub type PIPE_SERVER_END = crate::doc::NotDefinedHere;
/// See [winapi::um::winbase::SECURITY_IDENTIFICATION][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.SECURITY_IDENTIFICATION.html
pub type SECURITY_IDENTIFICATION = crate::doc::NotDefinedHere;
}
/// See [winapi::um::minwinbase](https://docs.rs/winapi/*/winapi/um/minwinbase/index.html).
#[allow(non_camel_case_types)]
pub mod minwinbase {
/// See [winapi::um::minwinbase::SECURITY_ATTRIBUTES][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/minwinbase/constant.SECURITY_ATTRIBUTES.html
pub type SECURITY_ATTRIBUTES = crate::doc::NotDefinedHere;
}
}
+11
View File
@@ -22,3 +22,14 @@ cfg_sync! {
mod block_on;
pub(crate) use block_on::block_on;
}
cfg_trace! {
mod trace;
pub(crate) use trace::InstrumentedFuture as Future;
}
cfg_not_trace! {
cfg_rt! {
pub(crate) use std::future::Future;
}
}
+11
View File
@@ -0,0 +1,11 @@
use std::future::Future;
pub(crate) trait InstrumentedFuture: Future {
fn id(&self) -> Option<tracing::Id>;
}
impl<F: Future> InstrumentedFuture for tracing::instrument::Instrumented<F> {
fn id(&self) -> Option<tracing::Id> {
self.span().id()
}
}
+43 -11
View File
@@ -8,7 +8,8 @@ use std::{task::Context, task::Poll};
/// Associates an IO object backed by a Unix file descriptor with the tokio
/// reactor, allowing for readiness to be polled. The file descriptor must be of
/// a type that can be used with the OS polling facilities (ie, `poll`, `epoll`,
/// `kqueue`, etc), such as a network socket or pipe.
/// `kqueue`, etc), such as a network socket or pipe, and the file descriptor
/// must have the nonblocking mode set to true.
///
/// Creating an AsyncFd registers the file descriptor with the current tokio
/// Reactor, allowing you to directly await the file descriptor being readable
@@ -36,18 +37,19 @@ use std::{task::Context, task::Poll};
///
/// On some platforms, the readiness detecting mechanism relies on
/// edge-triggered notifications. This means that the OS will only notify Tokio
/// when the file descriptor transitions from not-ready to ready. Tokio
/// internally tracks when it has received a ready notification, and when
/// when the file descriptor transitions from not-ready to ready. For this to
/// work you should first try to read or write and only poll for readiness
/// if that fails with an error of [`std::io::ErrorKind::WouldBlock`].
///
/// Tokio internally tracks when it has received a ready notification, and when
/// readiness checking functions like [`readable`] and [`writable`] are called,
/// if the readiness flag is set, these async functions will complete
/// immediately.
///
/// This however does mean that it is critical to ensure that this ready flag is
/// cleared when (and only when) the file descriptor ceases to be ready. The
/// [`AsyncFdReadyGuard`] returned from readiness checking functions serves this
/// function; after calling a readiness-checking async function, you must use
/// this [`AsyncFdReadyGuard`] to signal to tokio whether the file descriptor is no
/// longer in a ready state.
/// immediately. This however does mean that it is critical to ensure that this
/// ready flag is cleared when (and only when) the file descriptor ceases to be
/// ready. The [`AsyncFdReadyGuard`] returned from readiness checking functions
/// serves this function; after calling a readiness-checking async function,
/// you must use this [`AsyncFdReadyGuard`] to signal to tokio whether the file
/// descriptor is no longer in a ready state.
///
/// ## Use with to a poll-based API
///
@@ -538,6 +540,16 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
result => Ok(result),
}
}
/// Returns a shared reference to the inner [`AsyncFd`].
pub fn get_ref(&self) -> &AsyncFd<Inner> {
self.async_fd
}
/// Returns a shared reference to the backing object of the inner [`AsyncFd`].
pub fn get_inner(&self) -> &Inner {
self.get_ref().get_ref()
}
}
impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
@@ -599,6 +611,26 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
result => Ok(result),
}
}
/// Returns a shared reference to the inner [`AsyncFd`].
pub fn get_ref(&self) -> &AsyncFd<Inner> {
self.async_fd
}
/// Returns a mutable reference to the inner [`AsyncFd`].
pub fn get_mut(&mut self) -> &mut AsyncFd<Inner> {
self.async_fd
}
/// Returns a shared reference to the backing object of the inner [`AsyncFd`].
pub fn get_inner(&self) -> &Inner {
self.get_ref().get_ref()
}
/// Returns a mutable reference to the backing object of the inner [`AsyncFd`].
pub fn get_inner_mut(&mut self) -> &mut Inner {
self.get_mut().get_mut()
}
}
impl<'a, T: std::fmt::Debug + AsRawFd> std::fmt::Debug for AsyncFdReadyGuard<'a, T> {
+5 -1
View File
@@ -45,7 +45,11 @@ use std::task::{Context, Poll};
pub trait AsyncWrite {
/// Attempt to write bytes from `buf` into the object.
///
/// On success, returns `Poll::Ready(Ok(num_bytes_written))`.
/// On success, returns `Poll::Ready(Ok(num_bytes_written))`. If successful,
/// then it must be guaranteed that `n <= buf.len()`. A return value of `0`
/// typically means that the underlying object is no longer able to accept
/// bytes and will likely not be able to in the future as well, or that the
/// buffer provided is empty.
///
/// If the object is not ready for writing, the method returns
/// `Poll::Pending` and arranges for the current task (via
+2 -2
View File
@@ -1,4 +1,4 @@
#![cfg_attr(not(feature = "net"), allow(unreachable_pub))]
#![cfg_attr(not(feature = "net"), allow(dead_code, unreachable_pub))]
use crate::io::driver::Ready;
@@ -58,7 +58,7 @@ impl Interest {
self.0.is_writable()
}
/// Add together two `Interst` values.
/// Add together two `Interest` values.
///
/// This function works from a `const` context.
///
+2 -2
View File
@@ -96,7 +96,7 @@ const ADDRESS: bit::Pack = bit::Pack::least_significant(24);
//
// The generation prevents a race condition where a slab slot is reused for a
// new socket while the I/O driver is about to apply a readiness event. The
// generaton value is checked when setting new readiness. If the generation do
// generation value is checked when setting new readiness. If the generation do
// not match, then the readiness event is discarded.
const GENERATION: bit::Pack = ADDRESS.then(7);
@@ -273,7 +273,7 @@ cfg_not_rt! {
/// This function panics if there is no current reactor set, or if the `rt`
/// feature flag is not enabled.
pub(super) fn current() -> Self {
panic!(crate::util::error::CONTEXT_MISSING_ERROR)
panic!("{}", crate::util::error::CONTEXT_MISSING_ERROR)
}
}
}
+10 -4
View File
@@ -1,3 +1,5 @@
#![cfg_attr(not(feature = "net"), allow(dead_code))]
use crate::io::driver::{Direction, Handle, Interest, ReadyEvent, ScheduledIo};
use crate::util::slab;
@@ -12,8 +14,9 @@ cfg_io_driver! {
/// that it will receive task notifications on readiness. This is the lowest
/// level API for integrating with a reactor.
///
/// The association between an I/O resource is made by calling [`new`]. Once
/// the association is established, it remains established until the
/// The association between an I/O resource is made by calling
/// [`new_with_interest_and_handle`].
/// Once the association is established, it remains established until the
/// registration instance is dropped.
///
/// A registration instance represents two separate readiness streams. One
@@ -34,7 +37,7 @@ cfg_io_driver! {
/// stream. The write readiness event stream is only for `Ready::writable()`
/// events.
///
/// [`new`]: method@Self::new
/// [`new_with_interest_and_handle`]: method@Self::new_with_interest_and_handle
/// [`poll_read_ready`]: method@Self::poll_read_ready`
/// [`poll_write_ready`]: method@Self::poll_write_ready`
#[derive(Debug)]
@@ -233,7 +236,10 @@ cfg_io_readiness! {
crate::future::poll_fn(|cx| {
if self.handle.inner().is_none() {
return Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, "reactor gone")));
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::Other,
crate::util::error::RUNTIME_SHUTTING_DOWN_ERROR
)));
}
Pin::new(&mut fut).poll(cx).map(Ok)
+5 -5
View File
@@ -84,9 +84,9 @@ cfg_io_readiness! {
// The `ScheduledIo::readiness` (`AtomicUsize`) is packed full of goodness.
//
// | reserved | generation | driver tick | readinesss |
// |----------+------------+--------------+------------|
// | 1 bit | 7 bits + 8 bits + 16 bits |
// | reserved | generation | driver tick | readiness |
// |----------+------------+--------------+-----------|
// | 1 bit | 7 bits + 8 bits + 16 bits |
const READINESS: bit::Pack = bit::Pack::least_significant(16);
@@ -443,7 +443,7 @@ cfg_io_readiness! {
// Currently ready!
let tick = TICK.unpack(curr) as u8;
*state = State::Done;
return Poll::Ready(ReadyEvent { ready, tick });
return Poll::Ready(ReadyEvent { tick, ready });
}
// Wasn't ready, take the lock (and check again while locked).
@@ -462,7 +462,7 @@ cfg_io_readiness! {
// Currently ready!
let tick = TICK.unpack(curr) as u8;
*state = State::Done;
return Poll::Ready(ReadyEvent { ready, tick });
return Poll::Ready(ReadyEvent { tick, ready });
}
// Not ready even after locked, insert into list...
+1 -1
View File
@@ -246,7 +246,7 @@ cfg_io_util! {
pub(crate) mod seek;
pub(crate) mod util;
pub use util::{
copy, copy_buf, duplex, empty, repeat, sink, AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt,
copy, copy_bidirectional, copy_buf, duplex, empty, repeat, sink, AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt,
BufReader, BufStream, BufWriter, DuplexStream, Empty, Lines, Repeat, Sink, Split, Take,
};
}
+15 -16
View File
@@ -10,10 +10,10 @@ cfg_io_driver! {
/// [`std::io::Write`] traits with the reactor that drives it.
///
/// `PollEvented` uses [`Registration`] internally to take a type that
/// implements [`mio::Evented`] as well as [`std::io::Read`] and or
/// implements [`mio::event::Source`] as well as [`std::io::Read`] and or
/// [`std::io::Write`] and associate it with a reactor that will drive it.
///
/// Once the [`mio::Evented`] type is wrapped by `PollEvented`, it can be
/// Once the [`mio::event::Source`] type is wrapped by `PollEvented`, it can be
/// used from within the future's execution model. As such, the
/// `PollEvented` type provides [`AsyncRead`] and [`AsyncWrite`]
/// implementations using the underlying I/O resource as well as readiness
@@ -40,13 +40,13 @@ cfg_io_driver! {
/// [`poll_read_ready`] again will also indicate read readiness.
///
/// When the operation is attempted and is unable to succeed due to the I/O
/// resource not being ready, the caller must call [`clear_read_ready`] or
/// [`clear_write_ready`]. This clears the readiness state until a new
/// resource not being ready, the caller must call `clear_read_ready` or
/// `clear_write_ready`. This clears the readiness state until a new
/// readiness event is received.
///
/// This allows the caller to implement additional functions. For example,
/// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and
/// [`clear_read_ready`].
/// `clear_read_ready`.
///
/// ## Platform-specific events
///
@@ -54,17 +54,11 @@ cfg_io_driver! {
/// These events are included as part of the read readiness event stream. The
/// write readiness event stream is only for `Ready::writable()` events.
///
/// [`std::io::Read`]: trait@std::io::Read
/// [`std::io::Write`]: trait@std::io::Write
/// [`AsyncRead`]: trait@AsyncRead
/// [`AsyncWrite`]: trait@AsyncWrite
/// [`mio::Evented`]: trait@mio::Evented
/// [`Registration`]: struct@Registration
/// [`TcpListener`]: struct@crate::net::TcpListener
/// [`clear_read_ready`]: method@Self::clear_read_ready
/// [`clear_write_ready`]: method@Self::clear_write_ready
/// [`poll_read_ready`]: method@Self::poll_read_ready
/// [`poll_write_ready`]: method@Self::poll_write_ready
/// [`AsyncRead`]: crate::io::AsyncRead
/// [`AsyncWrite`]: crate::io::AsyncWrite
/// [`TcpListener`]: crate::net::TcpListener
/// [`poll_read_ready`]: Registration::poll_read_ready
/// [`poll_write_ready`]: Registration::poll_write_ready
pub(crate) struct PollEvented<E: Source> {
io: Option<E>,
registration: Registration,
@@ -121,6 +115,11 @@ impl<E: Source> PollEvented<E> {
}
/// Returns a reference to the registration
#[cfg(any(
feature = "net",
all(unix, feature = "process"),
all(unix, feature = "signal"),
))]
pub(crate) fn registration(&self) -> &Registration {
&self.registration
}
+3 -3
View File
@@ -45,7 +45,7 @@ impl<'a> ReadBuf<'a> {
/// Creates a new `ReadBuf` from a fully uninitialized buffer.
///
/// Use `assume_init` if part of the buffer is known to be already inintialized.
/// Use `assume_init` if part of the buffer is known to be already initialized.
#[inline]
pub fn uninit(buf: &'a mut [MaybeUninit<u8>]) -> ReadBuf<'a> {
ReadBuf {
@@ -85,7 +85,7 @@ impl<'a> ReadBuf<'a> {
#[inline]
pub fn take(&mut self, n: usize) -> ReadBuf<'_> {
let max = std::cmp::min(self.remaining(), n);
// Saftey: We don't set any of the `unfilled_mut` with `MaybeUninit::uninit`.
// Safety: We don't set any of the `unfilled_mut` with `MaybeUninit::uninit`.
unsafe { ReadBuf::uninit(&mut self.unfilled_mut()[..max]) }
}
@@ -217,7 +217,7 @@ impl<'a> ReadBuf<'a> {
///
/// # Panics
///
/// Panics if the filled region of the buffer would become larger than the intialized region.
/// Panics if the filled region of the buffer would become larger than the initialized region.
#[inline]
pub fn set_filled(&mut self, n: usize) {
assert!(
+2 -2
View File
@@ -52,10 +52,10 @@ where
buf = &buf[..crate::io::blocking::MAX_BUF];
// Now there are two possibilites.
// Now there are two possibilities.
// If caller gave is binary buffer, we **should not** shrink it
// anymore, because excessive shrinking hits performance.
// If caller gave as binary buffer, we **must** additionaly
// If caller gave as binary buffer, we **must** additionally
// shrink it to strip incomplete char at the end of buffer.
// that's why check we will perform now is allowed to have
// false-positive.
+35 -9
View File
@@ -105,8 +105,10 @@ cfg_io_util! {
/// async fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
/// ```
///
/// This function does not provide any guarantees about whether it
/// completes immediately or asynchronously
/// This method does not provide any guarantees about whether it
/// completes immediately or asynchronously.
///
/// # Return
///
/// If the return value of this method is `Ok(n)`, then it must be
/// guaranteed that `0 <= n <= buf.len()`. A nonzero `n` value indicates
@@ -136,6 +138,12 @@ cfg_io_util! {
/// variant will be returned. If an error is returned then it must be
/// guaranteed that no bytes were read.
///
/// # Cancel safety
///
/// This method is cancel safe. If you use it as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// completes first, then it is guaranteed that no data was read.
///
/// # Examples
///
/// [`File`][crate::fs::File]s implement `Read`:
@@ -175,14 +183,19 @@ cfg_io_util! {
/// Usually, only a single `read` syscall is issued, even if there is
/// more space in the supplied buffer.
///
/// This function does not provide any guarantees about whether it
/// completes immediately or asynchronously
/// This method does not provide any guarantees about whether it
/// completes immediately or asynchronously.
///
/// # Return
///
/// On a successful read, the number of read bytes is returned. If the
/// supplied buffer is not empty and the function returns `Ok(0)` then
/// the source has reached an "end-of-file" event.
/// A nonzero `n` value indicates that the buffer `buf` has been filled
/// in with `n` bytes of data from this source. If `n` is `0`, then it
/// can indicate one of two scenarios:
///
/// 1. This reader has reached its "end of file" and will likely no longer
/// be able to produce bytes. Note that this does not mean that the
/// reader will *always* no longer be able to produce bytes.
/// 2. The buffer specified had a remaining capacity of zero.
///
/// # Errors
///
@@ -190,6 +203,12 @@ cfg_io_util! {
/// variant will be returned. If an error is returned then it must be
/// guaranteed that no bytes were read.
///
/// # Cancel safety
///
/// This method is cancel safe. If you use it as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// completes first, then it is guaranteed that no data was read.
///
/// # Examples
///
/// [`File`] implements `Read` and [`BytesMut`] implements [`BufMut`]:
@@ -254,6 +273,13 @@ cfg_io_util! {
/// it has read, but it will never read more than would be necessary to
/// completely fill the buffer.
///
/// # Cancel safety
///
/// This method is not cancellation safe. If the method is used as the
/// event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then some data may already have been
/// read into `buf`.
///
/// # Examples
///
/// [`File`][crate::fs::File]s implement `Read`:
@@ -579,7 +605,7 @@ cfg_io_util! {
/// async fn main() -> io::Result<()> {
/// let mut reader = Cursor::new(vec![0x80, 0, 0, 0, 0, 0, 0, 0]);
///
/// assert_eq!(i64::min_value(), reader.read_i64().await?);
/// assert_eq!(i64::MIN, reader.read_i64().await?);
/// Ok(())
/// }
/// ```
@@ -659,7 +685,7 @@ cfg_io_util! {
/// 0, 0, 0, 0, 0, 0, 0, 0
/// ]);
///
/// assert_eq!(i128::min_value(), reader.read_i128().await?);
/// assert_eq!(i128::MIN, reader.read_i128().await?);
/// Ok(())
/// }
/// ```
+11
View File
@@ -66,6 +66,17 @@ cfg_io_util! {
{
seek(self, pos)
}
/// Creates a future which will return the current seek position from the
/// start of the stream.
///
/// This is equivalent to `self.seek(SeekFrom::Current(0))`.
fn stream_position(&mut self) -> Seek<'_, Self>
where
Self: Unpin,
{
self.seek(SeekFrom::Current(0))
}
}
}
+144 -7
View File
@@ -2,6 +2,7 @@ use crate::io::util::flush::{flush, Flush};
use crate::io::util::shutdown::{shutdown, Shutdown};
use crate::io::util::write::{write, Write};
use crate::io::util::write_all::{write_all, WriteAll};
use crate::io::util::write_all_buf::{write_all_buf, WriteAllBuf};
use crate::io::util::write_buf::{write_buf, WriteBuf};
use crate::io::util::write_int::{
WriteI128, WriteI128Le, WriteI16, WriteI16Le, WriteI32, WriteI32Le, WriteI64, WriteI64Le,
@@ -11,7 +12,9 @@ use crate::io::util::write_int::{
WriteU128, WriteU128Le, WriteU16, WriteU16Le, WriteU32, WriteU32Le, WriteU64, WriteU64Le,
WriteU8,
};
use crate::io::util::write_vectored::{write_vectored, WriteVectored};
use crate::io::AsyncWrite;
use std::io::IoSlice;
use bytes::Buf;
@@ -94,6 +97,13 @@ cfg_io_util! {
/// It is **not** considered an error if the entire buffer could not be
/// written to this writer.
///
/// # Cancel safety
///
/// This method is cancellation safe in the sense that if it is used as
/// the event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then it is guaranteed that no data was
/// written to this `AsyncWrite`.
///
/// # Examples
///
/// ```no_run
@@ -116,6 +126,53 @@ cfg_io_util! {
write(self, src)
}
/// Like [`write`], except that it writes from a slice of buffers.
///
/// Equivalent to:
///
/// ```ignore
/// async fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize>;
/// ```
///
/// See [`AsyncWrite::poll_write_vectored`] for more details.
///
/// # Cancel safety
///
/// This method is cancellation safe in the sense that if it is used as
/// the event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then it is guaranteed that no data was
/// written to this `AsyncWrite`.
///
/// # Examples
///
/// ```no_run
/// use tokio::io::{self, AsyncWriteExt};
/// use tokio::fs::File;
/// use std::io::IoSlice;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut file = File::create("foo.txt").await?;
///
/// let bufs: &[_] = &[
/// IoSlice::new(b"hello"),
/// IoSlice::new(b" "),
/// IoSlice::new(b"world"),
/// ];
///
/// file.write_vectored(&bufs).await?;
///
/// Ok(())
/// }
/// ```
///
/// [`write`]: AsyncWriteExt::write
fn write_vectored<'a, 'b>(&'a mut self, bufs: &'a [IoSlice<'b>]) -> WriteVectored<'a, 'b, Self>
where
Self: Unpin,
{
write_vectored(self, bufs)
}
/// Writes a buffer into this writer, advancing the buffer's internal
/// cursor.
@@ -152,12 +209,20 @@ cfg_io_util! {
/// It is **not** considered an error if the entire buffer could not be
/// written to this writer.
///
/// # Cancel safety
///
/// This method is cancellation safe in the sense that if it is used as
/// the event in a [`tokio::select!`](crate::select) statement and some
/// other branch completes first, then it is guaranteed that no data was
/// written to this `AsyncWrite`.
///
/// # Examples
///
/// [`File`] implements `Read` and [`Cursor<&[u8]>`] implements [`Buf`]:
/// [`File`] implements [`AsyncWrite`] and [`Cursor`]`<&[u8]>` implements [`Buf`]:
///
/// [`File`]: crate::fs::File
/// [`Buf`]: bytes::Buf
/// [`Cursor`]: std::io::Cursor
///
/// ```no_run
/// use tokio::io::{self, AsyncWriteExt};
@@ -190,6 +255,70 @@ cfg_io_util! {
write_buf(self, src)
}
/// Attempts to write an entire buffer into this writer
///
/// Equivalent to:
///
/// ```ignore
/// async fn write_all_buf(&mut self, buf: impl Buf) -> Result<(), io::Error> {
/// while buf.has_remaining() {
/// self.write_buf(&mut buf).await?;
/// }
/// Ok(())
/// }
/// ```
///
/// This method will continuously call [`write`] until
/// [`buf.has_remaining()`](bytes::Buf::has_remaining) returns false. This method will not
/// return until the entire buffer has been successfully written or an error occurs. The
/// first error generated will be returned.
///
/// The buffer is advanced after each chunk is successfully written. After failure,
/// `src.chunk()` will return the chunk that failed to write.
///
/// # Cancel safety
///
/// If `write_all_buf` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// completes first, then the data in the provided buffer may have been
/// partially written. However, it is guaranteed that the provided
/// buffer has been [advanced] by the amount of bytes that have been
/// partially written.
///
/// # Examples
///
/// [`File`] implements [`AsyncWrite`] and [`Cursor`]`<&[u8]>` implements [`Buf`]:
///
/// [`File`]: crate::fs::File
/// [`Buf`]: bytes::Buf
/// [`Cursor`]: std::io::Cursor
/// [advanced]: bytes::Buf::advance
///
/// ```no_run
/// use tokio::io::{self, AsyncWriteExt};
/// use tokio::fs::File;
///
/// use std::io::Cursor;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut file = File::create("foo.txt").await?;
/// let mut buffer = Cursor::new(b"data to write");
///
/// file.write_all_buf(&mut buffer).await?;
/// Ok(())
/// }
/// ```
///
/// [`write`]: AsyncWriteExt::write
fn write_all_buf<'a, B>(&'a mut self, src: &'a mut B) -> WriteAllBuf<'a, Self, B>
where
Self: Sized + Unpin,
B: Buf,
{
write_all_buf(self, src)
}
/// Attempts to write an entire buffer into this writer.
///
/// Equivalent to:
@@ -203,6 +332,14 @@ cfg_io_util! {
/// has been successfully written or such an error occurs. The first
/// error generated from this method will be returned.
///
/// # Cancel safety
///
/// This method is not cancellation safe. If it is used as the event
/// in a [`tokio::select!`](crate::select) statement and some other
/// branch completes first, then the provided buffer may have been
/// partially written, but future calls to `write_all` will start over
/// from the beginning of the buffer.
///
/// # Errors
///
/// This function will return the first error that [`write`] returns.
@@ -524,8 +661,8 @@ cfg_io_util! {
/// async fn main() -> io::Result<()> {
/// let mut writer = Vec::new();
///
/// writer.write_i64(i64::min_value()).await?;
/// writer.write_i64(i64::max_value()).await?;
/// writer.write_i64(i64::MIN).await?;
/// writer.write_i64(i64::MAX).await?;
///
/// assert_eq!(writer, b"\x80\x00\x00\x00\x00\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff");
/// Ok(())
@@ -602,7 +739,7 @@ cfg_io_util! {
/// async fn main() -> io::Result<()> {
/// let mut writer = Vec::new();
///
/// writer.write_i128(i128::min_value()).await?;
/// writer.write_i128(i128::MIN).await?;
///
/// assert_eq!(writer, vec![
/// 0x80, 0, 0, 0, 0, 0, 0, 0,
@@ -833,8 +970,8 @@ cfg_io_util! {
/// async fn main() -> io::Result<()> {
/// let mut writer = Vec::new();
///
/// writer.write_i64_le(i64::min_value()).await?;
/// writer.write_i64_le(i64::max_value()).await?;
/// writer.write_i64_le(i64::MIN).await?;
/// writer.write_i64_le(i64::MAX).await?;
///
/// assert_eq!(writer, b"\x00\x00\x00\x00\x00\x00\x00\x80\xff\xff\xff\xff\xff\xff\xff\x7f");
/// Ok(())
@@ -911,7 +1048,7 @@ cfg_io_util! {
/// async fn main() -> io::Result<()> {
/// let mut writer = Vec::new();
///
/// writer.write_i128_le(i128::min_value()).await?;
/// writer.write_i128_le(i128::MIN).await?;
///
/// assert_eq!(writer, vec![
/// 0, 0, 0, 0, 0, 0, 0,
+133 -3
View File
@@ -1,11 +1,11 @@
use crate::io::util::DEFAULT_BUF_SIZE;
use crate::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
use pin_project_lite::pin_project;
use std::io;
use std::io::{self, IoSlice, SeekFrom};
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{cmp, fmt};
use std::{cmp, fmt, mem};
pin_project! {
/// The `BufReader` struct adds buffering to any reader.
@@ -30,6 +30,7 @@ pin_project! {
pub(super) buf: Box<[u8]>,
pub(super) pos: usize,
pub(super) cap: usize,
pub(super) seek_state: SeekState,
}
}
@@ -48,6 +49,7 @@ impl<R: AsyncRead> BufReader<R> {
buf: buffer.into_boxed_slice(),
pos: 0,
cap: 0,
seek_state: SeekState::Init,
}
}
@@ -141,6 +143,122 @@ impl<R: AsyncRead> AsyncBufRead for BufReader<R> {
}
}
#[derive(Debug, Clone, Copy)]
pub(super) enum SeekState {
/// start_seek has not been called.
Init,
/// start_seek has been called, but poll_complete has not yet been called.
Start(SeekFrom),
/// Waiting for completion of the first poll_complete in the `n.checked_sub(remainder).is_none()` branch.
PendingOverflowed(i64),
/// Waiting for completion of poll_complete.
Pending,
}
/// Seek to an offset, in bytes, in the underlying reader.
///
/// The position used for seeking with `SeekFrom::Current(_)` is the
/// position the underlying reader would be at if the `BufReader` had no
/// internal buffer.
///
/// Seeking always discards the internal buffer, even if the seek position
/// would otherwise fall within it. This guarantees that calling
/// `.into_inner()` immediately after a seek yields the underlying reader
/// at the same position.
///
/// See [`AsyncSeek`] for more details.
///
/// Note: In the edge case where you're seeking with `SeekFrom::Current(n)`
/// where `n` minus the internal buffer length overflows an `i64`, two
/// seeks will be performed instead of one. If the second seek returns
/// `Err`, the underlying reader will be left at the same position it would
/// have if you called `seek` with `SeekFrom::Current(0)`.
impl<R: AsyncRead + AsyncSeek> AsyncSeek for BufReader<R> {
fn start_seek(self: Pin<&mut Self>, pos: SeekFrom) -> io::Result<()> {
// We needs to call seek operation multiple times.
// And we should always call both start_seek and poll_complete,
// as start_seek alone cannot guarantee that the operation will be completed.
// poll_complete receives a Context and returns a Poll, so it cannot be called
// inside start_seek.
*self.project().seek_state = SeekState::Start(pos);
Ok(())
}
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
let res = match mem::replace(self.as_mut().project().seek_state, SeekState::Init) {
SeekState::Init => {
// 1.x AsyncSeek recommends calling poll_complete before start_seek.
// We don't have to guarantee that the value returned by
// poll_complete called without start_seek is correct,
// so we'll return 0.
return Poll::Ready(Ok(0));
}
SeekState::Start(SeekFrom::Current(n)) => {
let remainder = (self.cap - self.pos) as i64;
// it should be safe to assume that remainder fits within an i64 as the alternative
// means we managed to allocate 8 exbibytes and that's absurd.
// But it's not out of the realm of possibility for some weird underlying reader to
// support seeking by i64::MIN so we need to handle underflow when subtracting
// remainder.
if let Some(offset) = n.checked_sub(remainder) {
self.as_mut()
.get_pin_mut()
.start_seek(SeekFrom::Current(offset))?;
self.as_mut().get_pin_mut().poll_complete(cx)?
} else {
// seek backwards by our remainder, and then by the offset
self.as_mut()
.get_pin_mut()
.start_seek(SeekFrom::Current(-remainder))?;
if self.as_mut().get_pin_mut().poll_complete(cx)?.is_pending() {
*self.as_mut().project().seek_state = SeekState::PendingOverflowed(n);
return Poll::Pending;
}
// https://github.com/rust-lang/rust/pull/61157#issuecomment-495932676
self.as_mut().discard_buffer();
self.as_mut()
.get_pin_mut()
.start_seek(SeekFrom::Current(n))?;
self.as_mut().get_pin_mut().poll_complete(cx)?
}
}
SeekState::PendingOverflowed(n) => {
if self.as_mut().get_pin_mut().poll_complete(cx)?.is_pending() {
*self.as_mut().project().seek_state = SeekState::PendingOverflowed(n);
return Poll::Pending;
}
// https://github.com/rust-lang/rust/pull/61157#issuecomment-495932676
self.as_mut().discard_buffer();
self.as_mut()
.get_pin_mut()
.start_seek(SeekFrom::Current(n))?;
self.as_mut().get_pin_mut().poll_complete(cx)?
}
SeekState::Start(pos) => {
// Seeking with Start/End doesn't care about our buffer length.
self.as_mut().get_pin_mut().start_seek(pos)?;
self.as_mut().get_pin_mut().poll_complete(cx)?
}
SeekState::Pending => self.as_mut().get_pin_mut().poll_complete(cx)?,
};
match res {
Poll::Ready(res) => {
self.discard_buffer();
Poll::Ready(Ok(res))
}
Poll::Pending => {
*self.as_mut().project().seek_state = SeekState::Pending;
Poll::Pending
}
}
}
}
impl<R: AsyncRead + AsyncWrite> AsyncWrite for BufReader<R> {
fn poll_write(
self: Pin<&mut Self>,
@@ -150,6 +268,18 @@ impl<R: AsyncRead + AsyncWrite> AsyncWrite for BufReader<R> {
self.get_pin_mut().poll_write(cx, buf)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
self.get_pin_mut().poll_write_vectored(cx, bufs)
}
fn is_write_vectored(&self) -> bool {
self.get_ref().is_write_vectored()
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.get_pin_mut().poll_flush(cx)
}
+46 -2
View File
@@ -1,8 +1,8 @@
use crate::io::util::{BufReader, BufWriter};
use crate::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
use pin_project_lite::pin_project;
use std::io;
use std::io::{self, IoSlice, SeekFrom};
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -94,9 +94,11 @@ impl<RW> From<BufWriter<BufReader<RW>>> for BufStream<RW> {
buf: rbuf,
pos,
cap,
seek_state: rseek_state,
},
buf: wbuf,
written,
seek_state: wseek_state,
} = b;
BufStream {
@@ -105,10 +107,12 @@ impl<RW> From<BufWriter<BufReader<RW>>> for BufStream<RW> {
inner,
buf: wbuf,
written,
seek_state: wseek_state,
},
buf: rbuf,
pos,
cap,
seek_state: rseek_state,
},
}
}
@@ -123,6 +127,18 @@ impl<RW: AsyncRead + AsyncWrite> AsyncWrite for BufStream<RW> {
self.project().inner.poll_write(cx, buf)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
self.project().inner.poll_write_vectored(cx, bufs)
}
fn is_write_vectored(&self) -> bool {
self.inner.is_write_vectored()
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.project().inner.poll_flush(cx)
}
@@ -142,6 +158,34 @@ impl<RW: AsyncRead + AsyncWrite> AsyncRead for BufStream<RW> {
}
}
/// Seek to an offset, in bytes, in the underlying stream.
///
/// The position used for seeking with `SeekFrom::Current(_)` is the
/// position the underlying stream would be at if the `BufStream` had no
/// internal buffer.
///
/// Seeking always discards the internal buffer, even if the seek position
/// would otherwise fall within it. This guarantees that calling
/// `.into_inner()` immediately after a seek yields the underlying reader
/// at the same position.
///
/// See [`AsyncSeek`] for more details.
///
/// Note: In the edge case where you're seeking with `SeekFrom::Current(n)`
/// where `n` minus the internal buffer length overflows an `i64`, two
/// seeks will be performed instead of one. If the second seek returns
/// `Err`, the underlying reader will be left at the same position it would
/// have if you called `seek` with `SeekFrom::Current(0)`.
impl<RW: AsyncRead + AsyncWrite + AsyncSeek> AsyncSeek for BufStream<RW> {
fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> {
self.project().inner.start_seek(position)
}
fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
self.project().inner.poll_complete(cx)
}
}
impl<RW: AsyncRead + AsyncWrite> AsyncBufRead for BufStream<RW> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
self.project().inner.poll_fill_buf(cx)
+126 -2
View File
@@ -1,9 +1,9 @@
use crate::io::util::DEFAULT_BUF_SIZE;
use crate::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
use pin_project_lite::pin_project;
use std::fmt;
use std::io::{self, Write};
use std::io::{self, IoSlice, SeekFrom, Write};
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -34,6 +34,7 @@ pin_project! {
pub(super) inner: W,
pub(super) buf: Vec<u8>,
pub(super) written: usize,
pub(super) seek_state: SeekState,
}
}
@@ -50,6 +51,7 @@ impl<W: AsyncWrite> BufWriter<W> {
inner,
buf: Vec::with_capacity(cap),
written: 0,
seek_state: SeekState::Init,
}
}
@@ -131,6 +133,72 @@ impl<W: AsyncWrite> AsyncWrite for BufWriter<W> {
}
}
fn poll_write_vectored(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
mut bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
if self.inner.is_write_vectored() {
let total_len = bufs
.iter()
.fold(0usize, |acc, b| acc.saturating_add(b.len()));
if total_len > self.buf.capacity() - self.buf.len() {
ready!(self.as_mut().flush_buf(cx))?;
}
let me = self.as_mut().project();
if total_len >= me.buf.capacity() {
// It's more efficient to pass the slices directly to the
// underlying writer than to buffer them.
// The case when the total_len calculation saturates at
// usize::MAX is also handled here.
me.inner.poll_write_vectored(cx, bufs)
} else {
bufs.iter().for_each(|b| me.buf.extend_from_slice(b));
Poll::Ready(Ok(total_len))
}
} else {
// Remove empty buffers at the beginning of bufs.
while bufs.first().map(|buf| buf.len()) == Some(0) {
bufs = &bufs[1..];
}
if bufs.is_empty() {
return Poll::Ready(Ok(0));
}
// Flush if the first buffer doesn't fit.
let first_len = bufs[0].len();
if first_len > self.buf.capacity() - self.buf.len() {
ready!(self.as_mut().flush_buf(cx))?;
debug_assert!(self.buf.is_empty());
}
let me = self.as_mut().project();
if first_len >= me.buf.capacity() {
// The slice is at least as large as the buffering capacity,
// so it's better to write it directly, bypassing the buffer.
debug_assert!(me.buf.is_empty());
return me.inner.poll_write(cx, &bufs[0]);
} else {
me.buf.extend_from_slice(&bufs[0]);
bufs = &bufs[1..];
}
let mut total_written = first_len;
debug_assert!(total_written != 0);
// Append the buffers that fit in the internal buffer.
for buf in bufs {
if buf.len() > me.buf.capacity() - me.buf.len() {
break;
} else {
me.buf.extend_from_slice(buf);
total_written += buf.len();
}
}
Poll::Ready(Ok(total_written))
}
}
fn is_write_vectored(&self) -> bool {
true
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
ready!(self.as_mut().flush_buf(cx))?;
self.get_pin_mut().poll_flush(cx)
@@ -142,6 +210,62 @@ impl<W: AsyncWrite> AsyncWrite for BufWriter<W> {
}
}
#[derive(Debug, Clone, Copy)]
pub(super) enum SeekState {
/// start_seek has not been called.
Init,
/// start_seek has been called, but poll_complete has not yet been called.
Start(SeekFrom),
/// Waiting for completion of poll_complete.
Pending,
}
/// Seek to the offset, in bytes, in the underlying writer.
///
/// Seeking always writes out the internal buffer before seeking.
impl<W: AsyncWrite + AsyncSeek> AsyncSeek for BufWriter<W> {
fn start_seek(self: Pin<&mut Self>, pos: SeekFrom) -> io::Result<()> {
// We need to flush the internal buffer before seeking.
// It receives a `Context` and returns a `Poll`, so it cannot be called
// inside `start_seek`.
*self.project().seek_state = SeekState::Start(pos);
Ok(())
}
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
let pos = match self.seek_state {
SeekState::Init => {
return self.project().inner.poll_complete(cx);
}
SeekState::Start(pos) => Some(pos),
SeekState::Pending => None,
};
// Flush the internal buffer before seeking.
ready!(self.as_mut().flush_buf(cx))?;
let mut me = self.project();
if let Some(pos) = pos {
// Ensure previous seeks have finished before starting a new one
ready!(me.inner.as_mut().poll_complete(cx))?;
if let Err(e) = me.inner.as_mut().start_seek(pos) {
*me.seek_state = SeekState::Init;
return Poll::Ready(Err(e));
}
}
match me.inner.poll_complete(cx) {
Poll::Ready(res) => {
*me.seek_state = SeekState::Init;
Poll::Ready(res)
}
Poll::Pending => {
*me.seek_state = SeekState::Pending;
Poll::Pending
}
}
}
}
impl<W: AsyncWrite + AsyncRead> AsyncRead for BufWriter<W> {
fn poll_read(
self: Pin<&mut Self>,

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