Compare commits

...
126 Commits
Author SHA1 Message Date
Carl Lerche 5d35c907f6 chore: release tokio v1.0.2 (#3429) 2021-01-14 16:37:40 -08:00
Alice Ryhl ad8fb41103 io: fix unsoundness in read_to_end (#3428) 2021-01-14 15:09:07 -08:00
Alice Ryhl 2330edc875 chore: prepare tokio 1.0.1 release (#3347) 2020-12-25 22:20:05 +01:00
Alice Ryhl fe2664a4e1 sync: remove RwLockWriteGuard::map and RwLockWriteGuard::try_map (#3345) 2020-12-25 21:36:48 +01:00
Laurențiu Nicola 1eefbc250a docs: remove stream feature from docs (#3335) 2020-12-24 23:17:18 +09:00
Carl Lerche a66017f049 chore: prepare Tokio 1.0 release (#3319) 2020-12-23 09:26:14 -08:00
Paolo Barbolini aa6597ba66 compat: update traits naming to match tokio 1.0 (#3324) 2020-12-23 09:15:56 -08:00
Taiki Endo ce0e9c67cf chore: Revert "use #[non_exhaustive] instead of private unit field" (#3323)
This reverts commit 575938d457.
2020-12-23 08:27:58 -08:00
Taiki Endo 575938d457 chore: use #[non_exhaustive] instead of private unit field (#3320) 2020-12-23 22:48:33 +09:00
Carl Lerche 0deaeb8494 chore: remove unused slab dependency (#3318) 2020-12-22 21:56:22 -08:00
Luke Steensen a8dda19da4 chore: update to released bytes 1.0 (#3317) 2020-12-22 17:09:26 -08:00
Sean McArthur be9fdb697d time: make Interval::poll_tick() public (#3316) 2020-12-22 12:31:14 -08:00
Carl Lerche 2ee9520d10 fs: misc small API tweaks (#3315) 2020-12-22 11:13:41 -08:00
Carlos B 7d28e4cdbb examples: add futures executor threadpool (#3198) 2020-12-22 20:08:43 +01:00
Alice Ryhl 0b83b3b8cc fs,sync: expose poll_ fns on misc types (#3308)
Includes methods on:

* fs::DirEntry
* io::Lines
* io::Split
* sync::mpsc::Receiver
* sync::misc::UnboundedReceiver
2020-12-22 09:28:14 -08:00
Alice Ryhl f95ad18980 net: clarify when wakeups are sent (#3310) 2020-12-22 07:38:14 -08:00
Taiki Endo eee3ca65d6 deps: update rand to 0.8, loom to 0.4 (#3307) 2020-12-22 10:28:35 +01:00
Alice Ryhl 564c943309 io: rename AsyncFd::with_io() and rm with_poll() (#3306) 2020-12-21 15:42:38 -08:00
BhargavandAlice Ryhl c7671a0384 io: add _mut variants of methods on AsyncFd (#3304)
Co-authored-by: Alice Ryhl <[email protected]>
2020-12-21 22:51:28 +01:00
Carl Lerche 2893359988 chore: update to bytes 1.0 git branch (#3301)
Updates the code base to track the `bytes` git branch. This is in
preparation for the 1.0 release.

Closes #3058
2020-12-19 15:57:16 -08:00
Carl Lerche 5e5f513542 chore: remove some left over stream feature code (#3300)
Removes the `stream` feature flag from `Cargo.toml` and removes the
`futures-core` dependency. Once `Stream` lands in `std`, a feature flag
is most likely not needed.
2020-12-19 14:15:00 -08:00
Arve Knudsen 1b70507894 net: remove {Tcp,Unix}Stream::shutdown() (#3298)
`shutdown()` on `AsyncWrite` performs a TCP shutdown. This avoids method
conflicts.

Closes #3294
2020-12-19 12:17:52 -08:00
Alice Ryhl 78f2340d25 tokio: remove prelude (#3299)
Closes: #3257
2020-12-19 11:42:24 -08:00
Florian Hübsch e41e6cddbb docs: tokio::main macro is also supported on rt (#3243)
Fixes: #3144
Refs: #2225
2020-12-19 19:12:08 +01:00
Lucio Franco b99b00eb30 rt: change max_threads to max_blocking_threads (#3287)
Fixes #2802
2020-12-19 08:04:04 -08:00
Alice Ryhl d948ccedfc chore: fix stress test (#3297) 2020-12-19 12:11:10 +01:00
Alice RyhlandLucio Franco 3ecaf9fd9a codec: write documentation for codec (#3283)
Co-authored-by: Lucio Franco <[email protected]>
2020-12-18 21:32:27 +01:00
Carl Lerche abd4c00255 time: enforce current_thread rt for time::pause (#3289)
Pausing time is a capability added to assist with testing Tokio code
dependent on time. Currently, the capability implicitly requires the
current_thread runtime.

This change enforces the requirement by panicking if called from a
multi-threaded runtime.
2020-12-17 15:37:08 -08:00
Lucio FrancoandAlice Ryhl c5861ef62f docs: Add more comprehensive stream docs (#3286)
* docs: Add more comprehensive stream docs

* Apply suggestions from code review

Co-authored-by: Alice Ryhl <[email protected]>

* Fix doc tests

Co-authored-by: Alice Ryhl <[email protected]>
2020-12-17 11:46:09 -05:00
Lucio Franco 59e4b35f49 stream: Fix a few doc issues (#3285) 2020-12-17 10:35:49 -05:00
Carl Lerche d74d17307d time: remove Box from Sleep (#3278)
Removes the box from `Sleep`, taking advantage of intrusive wakers. The
`Sleep` future is now `!Unpin`.

Closes #3267
2020-12-16 21:51:34 -08:00
Lucio Franco 8efa62013b Move stream items into tokio-stream (#3277)
This change removes all references to `Stream` from
within the `tokio` crate and moves them into a new
`tokio-stream` crate. Most types have had their
`impl Stream` removed as well in-favor of their
inherent methods.

Closes #2870
2020-12-15 20:24:38 -08:00
Zahari Dichev fcce78b33a sync: add Semaphore::close (#3065)
## Motivation
The need to expose `Semaphore::close` as explained in #3061. 

## Solution
Expose `Semaphore::close`

Signed-off-by: Zahari Dichev <[email protected]>
2020-12-15 10:22:45 -08:00
Carl Lerche 79d25b0a48 tracing: switch to unstable for 1.0. (#3266)
Enabling `tracing` integration now requires compiling with `--cfg
tokio_unstable`. Once `tracing-core` guarantees the same level of
stability as Tokio 1.0, unstable can be removed.

Closes #3258
2020-12-14 17:22:31 -08:00
Carl Lerche 3f29212cb7 fs: use cfgs on fns instead of OS ext traits (#3264)
Instead of using OS specific extension traits, OS specific methods are
moved onto the structs themselves and guarded with `cfg`. The API
documentation should highlight the function is platform specific.

Closes #2925
2020-12-14 09:29:10 -08:00
Joshua Nelson 48961fac89 Document SinkExt and other futures traits on docs.rs (#3271)
These are extremely hard to find if you don't already know they exist.
2020-12-14 10:28:36 -05:00
Carl Lerche a7833e3007 sync: remove try_recv() from mpsc types (#3263)
The mpsc `try_recv()` functions have an issue where a sent message
happens-before a call to `try_recv()` but `try_recv()` returns `None`.
Fixing this is non-trivial, so the function is removed for 1.0. When the
bug is fixed, the function can be added back.

Closes #2020
2020-12-13 21:39:51 -08:00
Jacob O'Toole 1f862d2e95 sync: add watch::Sender::borrow() (#3269) 2020-12-13 20:46:11 -08:00
Sylvain Kerkour 9149d7bfae docs: mention blocking thread timeout in src/lib.rs (#3253) 2020-12-13 16:24:16 +01:00
cssivision 4c55419453 net: add UnixStream readiness and non-blocking ops (#3246) 2020-12-13 16:21:11 +01:00
Aldas f26f444f42 doc: added tracing to the feature flags section (#3254) 2020-12-13 15:26:56 +01:00
cssivision be2cb7a5ce net: check for false-positives in TcpStream::ready doc test (#3255) 2020-12-13 15:24:59 +01:00
Sunjay Varma df20c162ae sync: add blocking_recv method to UnboundedReceiver, similar to Receiver::blocking_recv (#3262) 2020-12-12 08:47:35 -08:00
Alice Ryhl c1ec469ad2 util: add constructors to TokioContext (#3221) 2020-12-11 20:41:22 -08:00
Lucio Franco b01b2dacf2 net: update TcpStream::poll_peek to use ReadBuf (#3259)
Closes #2987
2020-12-11 20:40:24 -08:00
Evan Cameron 68717c7efa net: remove empty udp module (#3260) 2020-12-11 14:45:57 -05:00
Alice Ryhl 69e62ef89e sync: make TryAcquireError public (#3250)
The [`Semaphore::try_acquire`][1] method currently returns a private error type.

[1]: https://docs.rs/tokio/0.3/tokio/sync/struct.Semaphore.html#method.try_acquire
2020-12-10 19:56:05 -08:00
Nylonicious 16c2e0983c net: Pass SocketAddr by value (#3125) 2020-12-10 14:58:27 -05:00
Yusuke Tanaka 4b1d76ec8f docs: fix typo in signal module documentation (#3249) 2020-12-10 08:11:45 -08:00
Clemens Koza 9646b4bce3 toml: enable test-util feature for the playground (#3224) 2020-12-10 10:39:05 +01:00
Alice Ryhland@tijsvd f60860af7e watch: fix spurious wakeup (#3234)
Co-authored-by: @tijsvd
2020-12-10 09:46:01 +01:00
cssivision 2a30e13f38 net: expose poll_* methods on UnixDatagram (#3223) 2020-12-10 08:36:43 +01:00
Nylonicious 52cd240053 task: add missing feature flags for task_local and spawn_blocking (#3237) 2020-12-09 23:49:28 +01:00
Carl Lerche 473ddaa277 chore: prepare for Tokio 1.0 work (#3238) 2020-12-09 09:42:05 -08:00
bdonlan 9706ca92a8 time: Fix race condition in timer drop (#3229)
Dropping a timer on the millisecond that it was scheduled for, when it was on
the pending list, could result in a panic previously, as we did not record the
pending-list state in cached_when.

Hopefully fixes: ZcashFoundation/zebra#1452
2020-12-08 16:42:43 -08:00
Blas Rodriguez Irizar fc7a4b3c6e chore: fix stress test (#3233) 2020-12-09 07:38:25 +09:00
Blas Rodriguez Irizar e01391351b Add stress test (#3222)
Created a simple echo TCP server that on two different runtimes that is
called from a GitHub action using Valgrind to ensure that there are
no memory leaks.

Fixes: #3022
2020-12-07 21:12:22 -08:00
bdonlan 57dffb9dfe rt: fix deadlock in shutdown (#3228)
Previously, the runtime shutdown logic would first-hand control over all cores
to a single thread, which would sequentially shut down all tasks on the core
and then wait for them to complete.

This could deadlock when one task is waiting for a later core's task to
complete. For example, in the newly added test, we have a `block_in_place` task
that is waiting for another task to be dropped. If the latter task adds its
core to the shutdown list later than the former, we end up waiting forever for
the `block_in_place` task to complete.

Additionally, there also was a bug wherein we'd attempt to park on the parker
after shutting it down which was fixed as part of the refactors above.

This change restructures the code to bring all tasks to a halt (and do any
parking needed) before we collapse to a single thread to avoid this deadlock.

There was also an issue in which canceled tasks would not unpark the
originating thread, due to what appears to be some sort of optimization gone
wrong. This has been fixed to be much more conservative in selecting when not
to unpark the source thread (this may be too conservative; please take a look
at the changes to `release()`).

Fixes: #2789
2020-12-07 20:55:02 -08:00
Carl Lerche 62023dffe5 sync: forward port 0.2 mpsc test (#3225)
Forward ports the test included in #3215. The mpsc sempahore has been
replaced in 0.3 and does not include the bug being fixed.
2020-12-07 11:24:15 -08:00
Fuyang Liu 0707f4c192 net: add TcpStream::into_std (#3189) 2020-12-06 14:33:04 +01:00
Iban Eguia 0dbba13984 deps: replace lazy_static with once_cell (#3187) 2020-12-04 10:23:13 +01:00
John-John Tedro a125ebd745 rt: fix panic in task abort when off rt (#3159)
A call to `JoinHandle::abort` releases a task. When called from outside of the runtime,
this panics due to the current implementation checking for a thread-local worker context.

This change makes accessing the thread-local context optional under release, by falling
back to remotely marking a task remotely as dropped. Behaving the same as if the core
was stolen by another worker.

Fixes #3157
2020-12-03 21:29:59 -08:00
Eliza Weisman 00500d1b35 util: prepare v0.5.1 release (#3210)
### Added

- io: `poll_read_buf` util fn (#2972).
- io: `poll_write_buf` util fn with vectored write support (#3156).

Signed-off-by: Eliza Weisman <[email protected]>
2020-12-03 15:30:52 -08:00
Eliza Weisman 647299866a util: add writev-aware poll_write_buf (#3156)
## Motivation

In Tokio 0.2, `AsyncRead` and `AsyncWrite` had `poll_write_buf` and
`poll_read_buf` methods for reading and writing to implementers of
`bytes` `Buf` and `BufMut` traits. In 0.3, these were removed, but
`poll_read_buf` was added as a free function in `tokio-util`. However,
there is currently no `poll_write_buf`.

Now that `AsyncWrite` has regained support for vectored writes in #3149,
there's a lot of potential benefit in having a `poll_write_buf` that
uses vectored writes when supported and non-vectored writes when not
supported, so that users don't have to reimplement this.

## Solution

This PR adds a `poll_write_buf` function to `tokio_util::io`, analogous
to the existing `poll_read_buf` function.

This function writes from a `Buf` to an `AsyncWrite`, advancing the
`Buf`'s internal cursor. In addition, when the `AsyncWrite` supports
vectored writes (i.e. its `is_write_vectored` method returns `true`),
it will use vectored IO.

I copied the documentation for this functions from the docs from Tokio
0.2's `AsyncWrite::poll_write_buf` , with some minor modifications as
appropriate.

Finally, I fixed a minor issue in the existing docs for `poll_read_buf`
and `read_buf`, and updated `tokio_util::codec` to use `poll_write_buf`.

Signed-off-by: Eliza Weisman <[email protected]>
2020-12-03 11:19:16 -08:00
Blas Rodriguez Irizar a6051a61ec sync: make add_permits panic with usize::MAX >> 3 permits (#3188) 2020-12-02 22:58:28 +01:00
cssivision a8e0f0a919 example: add back udp-codec example (#3205) 2020-12-01 12:20:20 +09:00
Alan Somers 7ae8135b62 process: fix the process_kill_on_drop.rs test on non-Linux systems (#3203)
"disown" is a bash builtin, not part of POSIX sh.
2020-12-01 10:20:49 +09:00
Alan Somers 353b0544a0 ci: reenable CI on FreeBSD i686 (#3204)
It was temporarily disabled in 06c473e628
and never reenabled.
2020-12-01 10:20:18 +09:00
Alan Somers 128495168d ci: switch FreeBSD CI environment to 12.2-RELEASE (#3202)
12.1 will be EoL in two months.
2020-12-01 10:19:54 +09:00
Carl Lerche 08548583b9 chore: prepare v0.3.5 release (#3201) 2020-11-30 12:57:31 -08:00
HK416-is-all-you-need 7707ba88ef io: add AsyncFd::with_interest (#3167)
Fixes #3072
2020-11-30 11:11:18 -08:00
Ivan Tham 72d6346c0d macros: #[tokio::main] can be used on non-main (#3199) 2020-11-30 17:34:11 +01:00
Kyle Kosic a85fdb884d runtime: test for shutdown_timeout(0) (#3196) 2020-11-29 21:30:19 +01:00
Alice Ryhl c55d846f4b util: add rt to tokio-util full feature (#3194) 2020-11-29 09:48:31 +01:00
Max Sharnoff 0acd06b42a runtime: fix shutdown_timeout(0) blocking (#3174) 2020-11-28 19:31:13 +01:00
Niklas Fiekas 4912943419 signal: expose CtrlC stream on windows (#3186)
* Make tokio::signal::windows::ctrl_c() public.
* Stop referring to private tokio::signal::windows::Event in module
  documentation.

Closes #3178
2020-11-27 19:53:17 +00:00
Rajiv Chauhan 5e406a7a47 macros: fix outdated documentation (#3180)
1. Changed 0.2 to 0.3
2. Changed ‘multi’ to ‘single’ to indicate that the behavior is single threaded
2020-11-26 19:46:15 +01:00
Max Sharnoff de33ee85ce time: replace 'ouClockTimeide' in internal docs with 'outside' (#3171) 2020-11-24 10:23:20 +01:00
漂流 874fc3320b codec: add read_buffer_mut to FramedRead (#3166) 2020-11-24 09:39:16 +01:00
bdonlan ae67851f11 time: use intrusive lists for timer tracking (#3080)
More-or-less a half-rewrite of the current time driver, supporting the
use of intrusive futures for timer registration.

Fixes: #3028, #3069
2020-11-23 10:42:50 -08:00
Eliza Weisman f927f01a34 macros: fix rustfmt on 1.48.0 (#3160)
## Motivation

Looks like the Rust 1.48.0 version of `rustfmt` changed some formatting
rules (fixed some bugs?), and some of the code in `tokio-macros` is no
longer correctly formatted. This is breaking CI.

## Solution

This commit runs rustfmt on Rust 1.48.0. This fixes CI.

Closes #3158
2020-11-20 10:19:26 -08:00
cssivision 49abfdb2ac util: fix typo in udp/frame.rs (#3154) 2020-11-20 15:06:14 +09:00
Carl Lerche 479c545c20 chore: prepare v0.3.4 release (#3152) 2020-11-18 12:38:13 -08:00
Sean McArthur 34fcef258b io: add vectored writes to AsyncWrite (#3149)
This adds `AsyncWrite::poll_write_vectored`, and implements it for
`TcpStream` and `UnixStream`.

Refs: #3135.
2020-11-18 10:41:47 -08:00
Zeki Sherif 7d11aa8668 net: add SO_LINGER get/set to TcpStream (#3143) 2020-11-17 09:58:00 -08:00
Carl Lerche 0ea2307650 net: add UdpSocket readiness and non-blocking ops (#3138)
Adds `ready()`, `readable()`, and `writable()` async methods for waiting
for socket readiness. Adds `try_send`, `try_send_to`, `try_recv`, and
`try_recv_from` for performing non-blocking operations on the socket.

This is the UDP equivalent of #3130.
2020-11-16 15:44:01 -08:00
Zahari Dichev d0ebb41547 sync: add Notify::notify_waiters (#3098)
This PR makes `Notify::notify_waiters` public. The method
already exists, but it changes the way `notify_waiters`,
is used. Previously in order for the consumer to
register interest, in a notification triggered by
`notify_waiters`, the `Notified` future had to be
polled. This introduced friction when using the api
as the future had to be pinned before polled.

This change introduces a counter that tracks how many
times `notified_waiters` has been called. Upon creation of
the future the number of times is loaded. When first
polled the future compares this number with the count
state of the `Notify` type. This avoids the need for
registering the waiter upfront.

Fixes: #3066
2020-11-16 12:49:35 -08:00
Eliza Weisman f5cb4c2042 net: Add send/recv buf size methods to TcpSocket (#3145)
This commit adds `set_{send, recv}_buffer_size` methods to `TcpSocket`
for setting the size of the TCP send and receive buffers, and `{send,
recv}_buffer_size` methods for returning the current value. These just
call into similar methods on `mio`'s `TcpSocket` type, which were added
in tokio-rs/mio#1384.

Refs: #3082

Signed-off-by: Eliza Weisman <[email protected]>
2020-11-16 12:29:03 -08:00
masnagam 4e39c9b818 net: restore TcpStream::{poll_read_ready, poll_write_ready} (#2743) 2020-11-16 09:51:06 -08:00
Carl Lerche 97c2c4203c chore: automate running benchmarks (#3140)
Uses Github actions to run benchmarks.
2020-11-13 19:30:52 -08:00
Taiki Endo 60366ca0fa chore: update pin-project-lite to 0.2.0 (#3139) 2020-11-13 15:24:06 -08:00
Carl Lerche 850bfc9efa net: add missing doc cfg on TcpSocket (#3137)
This adds the missing `net` feature flag in the generated API
documentation.
2020-11-13 11:05:22 -08:00
Carl Lerche 02b1117dca net: add TcpStream::ready and non-blocking ops (#3130)
Adds function to await for readiness on the TcpStream and non-blocking read/write functions.

`async fn TcpStream::ready(Interest)` waits for socket readiness satisfying **any** of the specified
interest. There are also two shorthand functions, `readable()` and `writable()`.

Once the stream is in a ready state, the caller may perform non-blocking operations on it using
`try_read()` and `try_write()`. These function return `WouldBlock` if the stream is not, in fact, ready.

The await readiness function are similar to `AsyncFd`, but do not require a guard. The guard in
`AsyncFd` protect against a potential race between receiving the readiness notification and clearing
it. The guard is needed as Tokio does not control the operations. With `TcpStream`, the `try_read()`
and `try_write()` function handle clearing stream readiness as needed.

This also exposes `Interest` and `Ready`, both defined in Tokio as wrappers for Mio types. These
types will also be useful for fixing #3072 .

Other I/O types, such as `TcpListener`, `UdpSocket`, `Unix*` should get similar functions, but this
is left for later PRs.

Refs: #3130
2020-11-12 20:07:43 -08:00
Nylonicious 685da8dadd fs: small documentation fixes (#3133) 2020-11-12 10:24:13 +01:00
Alice Ryhl 6a0e23c654 ci: minimal version check (#3131) 2020-11-11 23:08:34 +01:00
Alice Ryhl 9d0c0dd22c time: document maximum sleep duration (#3126) 2020-11-11 11:31:22 -08:00
Alice Ryhl 6d5423f3e9 stream: add docs regarding futures' StreamExt (#3128) 2020-11-11 11:20:14 -08:00
Ivan Petkov ebb8bab060 process: fix potential file descriptor leak (#3129) 2020-11-11 11:10:27 -08:00
Carl Lerche ce891a4df1 io: driver internal cleanup (#3124)
* Removes duplicated code by moving it to `Registration`.
* impl `Deref` for `PollEvented` to avoid `get_ref()`.
* Avoid extra waker clones in I/O driver.
* Add `Interest` wrapper around `mio::Interest`.
2020-11-11 09:28:21 -08:00
David Kellum d869e16990 Minor cleanup of parking_lot feature, now in full (#3119)
## Motivation

Some small cleanup items are apparent after merge of #2951

## Solution

Delete a now incorrect comment in Cargo.toml, and remove a now redundant CI test step.
2020-11-10 15:01:58 -08:00
Carl Lerche e1256d8ca4 io: update AsyncFd to use Registration (#3113) 2020-11-10 09:40:20 -08:00
Darius Carrier a52f5071bf sync: add acquire_many and try_acquire_many to Sempahore (#3067)
Fixes: #1550
2020-11-10 09:39:30 -08:00
Taiki Endo f1f8c3cde6 chore: update proptest and nix (#3110) 2020-11-08 20:47:44 +09:00
Oliver Gould c2e843d928 tokio-test: Update bytes to v0.6 (#3107) 2020-11-08 05:54:41 +09:00
bdonlanandBryan Donlan a43ec09b55 async_fd: make into_inner() deregister the fd (#3104)
* async_fd: make into_inner() deregister the fd

Fixes: #3103

* make clippy happy

Co-authored-by: Bryan Donlan <[email protected]>
2020-11-07 10:12:06 +01:00
Maarten de Vries 90c2a510e2 net: report PID in UCred for Solaris and Illumos. (#3085) 2020-11-06 17:04:19 +01:00
Alice Ryhl f51ddc5958 net: add set_nonblocking to doc (#3100) 2020-11-06 17:01:22 +01:00
Evan Cameron 47658a6da5 util: resurrect UdpFramed (#3044) 2020-11-06 16:59:15 +01:00
bdonlanandBryan Donlan d7e3fcb9ee rt: remove last slab dependency (#2917)
This removes the last slab dependency by replacing the current slab-based
JoinHandle tracking with one based on HashMap instead.

Co-authored-by: Bryan Donlan <[email protected]>
2020-11-05 10:38:37 -08:00
0b3918bce9 rt: bring back a public Handle type (#3076)
Signed-off-by: Marc-Antoine Perennou <[email protected]>
Co-authored-by: Alice Ryhl <[email protected]>
Co-authored-by: Carl Lerche <[email protected]>
2020-11-05 13:00:13 +01:00
Akira Hayakawa e309da0bee util: remove stream feature flag from DelayQueue (#3087) 2020-11-03 09:20:24 +01:00
Carl Lerche bbc8eb0f91 chore: update CI badge (#3091) 2020-11-03 09:17:47 +01:00
Artem Vorotnikov 06c7e73e99 stream: fix StreamMap Default bound (#3093) 2020-11-03 09:09:05 +01:00
Carl Lerche 42de3bc7a4 chore: prepare v0.3.3 release (#3090) 2020-11-02 15:36:17 -08:00
Alice Ryhl 20a2b9e263 rt: add missing Send bound (#3089) 2020-11-02 15:30:07 -08:00
Zeki Sherif ae4e8d7ad1 net: add get/set reuseport, reuseaddr, localaddr for TcpSocket (#3083) 2020-11-02 12:59:56 -08:00
Alice Ryhl 7a18ca2be0 doc: add from_std change to CHANGELOG (#3075) 2020-11-02 10:25:43 -08:00
Naja Melan 4a7b7c52d1 util: copy paste error in documentation for Compat (#3088) 2020-11-02 13:25:51 +01:00
Eliza Weisman fede3db76a tracing: replace future names with spawn locations in task spans (#3074)
## Motivation

Currently, the per-task `tracing` spans generated by tokio's `tracing`
feature flag include the `std::any::type_name` of the future that was
spawned. When future combinators and/or libraries like Tower are in use,
these future names can get _quite_ long. Furthermore, when formatting
the `tracing` spans with their parent spans as context, any other task
spans in the span context where the future was spawned from can _also_
include extremely long future names.

In some cases, this can result in extremely high memory use just to
store the future names. For example, in Linkerd, when we enable
`tokio=trace` to enable the task spans, there's a spawned task whose
future name is _232990 characters long_. A proxy with only 14 spawned
tasks generates a task list that's over 690 KB. Enabling task spans
under load results in the process getting OOM killed very quickly.

## Solution

This branch removes future type names from the spans generated by
`spawn`. As a replacement, to allow identifying which `spawn` call a
span corresponds to, the task span now contains the source code location
where `spawn` was called, when the compiler supports the
`#[track_caller]` attribute. Since `track_caller` was stabilized in Rust
1.46.0, and our minimum supported Rust version is 1.45.0, we can't
assume that `#[track_caller]` is always available. Instead, we have a
RUSTFLAGS cfg, `tokio_track_caller`, that guards whether or not we use
it. I've also added a `build.rs` that detects the compiler minor
version, and sets the cfg flag automatically if the current compiler
version is >= 1.46. This means users shouldn't have to enable
`tokio_track_caller` manually.

Here's the trace output from the `chat` example, before this change:
![Screenshot_20201030_110157](https://user-images.githubusercontent.com/2796466/97741071-6d408800-1a9f-11eb-9ed6-b25e72f58c7b.png)
...and after:
![Screenshot_20201030_110303](https://user-images.githubusercontent.com/2796466/97741112-7e899480-1a9f-11eb-9197-c5a3f9ea1c05.png)

Closes #3073

Signed-off-by: Eliza Weisman <[email protected]>
2020-11-01 10:48:44 -08:00
Dirkjan Ochtman 2b23aa7389 util: add back public poll_read_buf() function (#3079)
This was accidentally removed in #3064.
2020-11-01 10:22:22 +01:00
Finn Behrens 382ee6bf5d net: add pid to tokio::net::unix::UCred (#2633) 2020-10-31 10:30:55 +01:00
Carl Lerche 24ed874e81 chore: prepare tokio-util v0.5.0 release (#3078) 2020-10-30 11:26:15 -07:00
Dirkjan Ochtman 3965d91a5e util: update to bytes 0.6 (#3071)
Copies the implementation of poll_read_buf() from tokio::io::util::read_buf.
2020-10-29 10:45:19 -07:00
Dirkjan Ochtman a3ef4e4cf5 util: deduplicate implementations of poll_read_buf() (#3064) 2020-10-29 13:20:38 +01:00
Naja Melan 34eb47dde5 runtime: block_on should NOT be called from async context (#3070) 2020-10-29 12:10:42 +01:00
Tom Kaitchuck c8a484bbb2 tokio: remove unused dependency (#3063)
Signed-off-by: Tom Kaitchuck <[email protected]>
2020-10-28 08:06:53 +01:00
253 changed files with 9688 additions and 4123 deletions
+7 -8
View File
@@ -1,17 +1,17 @@
freebsd_instance:
image: freebsd-12-1-release-amd64
image: freebsd-12-2-release-amd64
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
# same VM. The binary will be built in 32-bit mode, but will execute on a
# 64-bit kernel and in a 64-bit environment. Our tests don't execute any of
# the system's binaries, so the environment shouldn't matter.
task:
name: FreeBSD 12.0
name: FreeBSD
env:
LOOM_MAX_PREEMPTIONS: 2
RUSTFLAGS: -Dwarnings
setup_script:
- pkg install -y curl
- pkg install -y bash curl
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain stable
- . $HOME/.cargo/env
@@ -23,8 +23,7 @@ task:
- . $HOME/.cargo/env
- cargo test --all
- cargo doc --all --no-deps
# TODO: Re-enable
# i686_test_script:
# - . $HOME/.cargo/env
# - |
# cargo test --all --exclude tokio-macros --target i686-unknown-freebsd
i686_test_script:
- . $HOME/.cargo/env
- |
cargo test --all --target i686-unknown-freebsd
+55
View File
@@ -0,0 +1,55 @@
name: Benchmark
on:
push:
branches:
- master
jobs:
benchmark:
name: Benchmark
runs-on: ubuntu-latest
strategy:
matrix:
bench:
- rt_multi_threaded
- sync_mpsc
- sync_rwlock
- sync_semaphore
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update stable
# Run benchmark with `go test -bench` and stores the output to a file
- name: Run benchmark
run: cargo bench --bench ${{ matrix.bench }} | tee ../output.txt
working-directory: benches
# Download previous benchmark result from cache (if exists)
- name: Download previous benchmark data
uses: actions/cache@v1
with:
path: ./cache
key: ${{ runner.os }}-benchmark
# Run `github-action-benchmark` action
- name: Store benchmark result
uses: rhysd/github-action-benchmark@v1
with:
name: ${{ matrix.bench }}
# What benchmark tool the output.txt came from
tool: 'cargo'
# Where the output from the benchmark tool is stored
output-file-path: output.txt
# # Where the previous data file is stored
# external-data-json-path: ./cache/benchmark-data.json
# Workflow will fail when an alert happens
fail-on-alert: true
# GitHub API token to make a commit comment
github-token: ${{ secrets.GITHUB_TOKEN }}
# Enable alert commit comment
comment-on-alert: true
alert-comment-cc-users: '@tokio-rs/maintainers'
auto-push: true
# Upload the updated cache file for the next job by actions/cache
+23 -7
View File
@@ -1,8 +1,8 @@
on:
push:
branches: ["master"]
branches: ["master", "tokio-*.x"]
pull_request:
branches: ["master"]
branches: ["master", "tokio-*.x"]
name: CI
@@ -53,11 +53,6 @@ jobs:
run: cargo test --features full
working-directory: tokio
# Check `tokio` with `full + parking_lot` to make sure it compiles.
- name: check tokio full,parking_lot
run: cargo check --features full,parking_lot
working-directory: tokio
# Test **all** crates in the workspace with all features.
- name: test all --all-features
run: cargo test --workspace --all-features
@@ -186,6 +181,26 @@ jobs:
- name: "test --workspace --all-features"
run: cargo check --workspace --all-features
minimal-versions:
name: minimal-versions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.nightly }}
override: true
- name: Install cargo-hack
run: cargo install cargo-hack
- name: "check --all-features -Z minimal-versions"
run: |
# Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
# from determining minimal versions based on dev-dependencies.
cargo hack --remove-dev-deps --workspace
# Update Cargo.lock to minimal version dependencies.
cargo update -Z minimal-versions
cargo check --all-features
fmt:
name: fmt
runs-on: ubuntu-latest
@@ -245,6 +260,7 @@ jobs:
- loom_pool::group_b
- loom_pool::group_c
- loom_pool::group_d
- time::driver
steps:
- uses: actions/checkout@v2
- name: Install Rust
+32
View File
@@ -0,0 +1,32 @@
name: Stress Test
on:
pull_request:
push:
branches:
- master
jobs:
stess-test:
name: Stress Test
runs-on: ubuntu-latest
strategy:
matrix:
stress-test:
- simple_echo_tcp
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update stable
- name: Install Valgrind
run: |
sudo apt-get update -y
sudo apt-get install -y valgrind
# Compiles each of the stress test examples.
- name: Compile stress test examples
run: cargo build -p stress-test --release --example ${{ matrix.stress-test }}
# Runs each of the examples using Valgrind. Detects leaks and displays them.
- name: Run valgrind
run: valgrind --leak-check=full --show-leak-kinds=all ./target/release/examples/${{ matrix.stress-test }}
-1
View File
@@ -486,7 +486,6 @@ missing a difficulty rating, and you should feel free to add one.
- **M-process** The `tokio::process` module.
- **M-runtime** The `tokio::runtime` module.
- **M-signal** The `tokio::signal` module.
- **M-stream** The `tokio::stream` module.
- **M-sync** The `tokio::sync` module.
- **M-task** The `tokio::task` module.
- **M-time** The `tokio::time` module.
+2
View File
@@ -4,11 +4,13 @@ members = [
"tokio",
"tokio-macros",
"tokio-test",
"tokio-stream",
"tokio-util",
# Internal
"benches",
"examples",
"stress-test",
"tests-build",
"tests-integration",
]
+4 -5
View File
@@ -14,22 +14,21 @@ the Rust programming language. It is:
[![Crates.io][crates-badge]][crates-url]
[![MIT licensed][mit-badge]][mit-url]
[![Build Status][azure-badge]][azure-url]
[![Build Status][actions-badge]][actions-url]
[![Discord chat][discord-badge]][discord-url]
[crates-badge]: https://img.shields.io/crates/v/tokio.svg
[crates-url]: https://crates.io/crates/tokio
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
[mit-url]: https://github.com/tokio-rs/tokio/blob/master/LICENSE
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.tokio?branchName=master
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=1&branchName=master
[actions-badge]: https://github.com/tokio-rs/tokio/workflows/CI/badge.svg
[actions-url]: https://github.com/tokio-rs/tokio/actions?query=workflow%3ACI+branch%3Amaster
[discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord&style=flat-square
[discord-url]: https://discord.gg/tokio
[Website](https://tokio.rs) |
[Guides](https://tokio.rs/tokio/tutorial) |
[API Docs](https://docs.rs/tokio/latest/tokio) |
[Roadmap](https://github.com/tokio-rs/tokio/blob/master/ROADMAP.md) |
[Chat](https://discord.gg/tokio)
## Overview
@@ -55,7 +54,7 @@ A basic TCP echo server with Tokio:
```rust,no_run
use tokio::net::TcpListener;
use tokio::prelude::*;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
-67
View File
@@ -1,67 +0,0 @@
# Tokio Roadmap
## A Roadmap to 1.0
The question of "why not 1.0?" has come up a few times. After all, Tokio 0.1 has
been stable for three years. The short answer: because it isn't time. There is
nobody who would rather ship a Tokio 1.0 than us. It also isn't something to rush.
After all, `async / await` only landed in the stable Rust channel weeks ago.
There has been no significant production validation yet, except maybe fuchsia
and that seems like a fairly specialized use case. This release of Tokio
includes significant new code and new strategies with feature flags. Also, there
are still big open questions, such as the [proposed changes][pr-1744] to
`AsyncRead` and `AsyncWrite`.
Tokio 1.0 will be released as soon as the APIs are proven to handle real-world
production cases.
### Tokio 1.0 in Q3 2020 with LTS support
The Tokio 1.0 release will be **no later** than Q3 2020. It will also come with
"long-term support" guarantees:
* A minimum of 5 years of maintenance.
* A minimum of 3 years before a hypothetical 2.0 release.
When Tokio 1.0 is released in Q3 2020, on-going support, security fixes, and
critical bug fixes are guaranteed until **at least** Q3 2025. Tokio 2.0 will not
be released until **at least** Q3 2023 (though, ideally there will never be a
Tokio 2.0 release).
### How to get there
While Tokio 0.1 probably should have been a 1.0, Tokio 0.2 will be a **true**
0.2 release. There will be breaking change releases every 2 ~ 3 months until 1.0.
These changes will be **much** smaller than going from 0.1 -> 0.2. It is
expected that the 1.0 release will look a lot like 0.2.
### What is expected to change
The biggest change will be the `AsyncRead` and `AsyncWrite` traits. Based on
experience gained over the past 3 years, there are a couple of issues to
address:
* Be able to **safely** use uninitialized memory as a read buffer.
* Practical read vectored and write vectored APIs.
There are a few strategies to solve these problems. These strategies need to be
investigated and the solution validated. You can see [this comment][pr-1744-comment] for a
detailed statement of the problem.
The other major change, which has been in the works for a while, is updating
Mio. Mio 0.6 was first released almost 4 years ago and has not had a breaking
change since. Mio 0.7 has been in the works for a while. It includes a full
rewrite of the windows support as well as a refined API. More will be written
about this shortly.
Finally, now that the API is starting to stabilize, effort will be put into
documentation. Tokio 0.2 is being released before updating the website and many
of the old content will no longer be relevant. In the coming weeks, expect to
see updates there.
So, we have our work cut out for us. We hope you enjoy this 0.2 release and are
looking forward to your feedback and help.
[pr-1744]: https://github.com/tokio-rs/tokio/pull/1744
[pr-1744-comment]: https://github.com/tokio-rs/tokio/pull/1744#issuecomment-553575438
+5 -5
View File
@@ -5,7 +5,7 @@ publish = false
edition = "2018"
[dependencies]
tokio = { version = "0.3.0", path = "../tokio", features = ["full"] }
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
bencher = "0.1.5"
[target.'cfg(unix)'.dependencies]
@@ -17,13 +17,13 @@ path = "spawn.rs"
harness = false
[[bench]]
name = "mpsc"
path = "mpsc.rs"
name = "sync_mpsc"
path = "sync_mpsc.rs"
harness = false
[[bench]]
name = "scheduler"
path = "scheduler.rs"
name = "rt_multi_threaded"
path = "rt_multi_threaded.rs"
harness = false
+8 -4
View File
@@ -30,22 +30,26 @@ fn create_100_000_medium(b: &mut Bencher) {
}
fn send_medium(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
let (tx, mut rx) = mpsc::channel::<Medium>(1000);
let _ = tx.try_send([0; 64]);
let _ = rt.block_on(tx.send([0; 64]));
rx.try_recv().unwrap();
rt.block_on(rx.recv()).unwrap();
});
}
fn send_large(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
let (tx, mut rx) = mpsc::channel::<Large>(1000);
let _ = tx.try_send([[0; 64]; 64]);
let _ = rt.block_on(tx.send([[0; 64]; 64]));
rx.try_recv().unwrap();
rt.block_on(rx.recv()).unwrap();
});
}
+18 -4
View File
@@ -7,18 +7,23 @@ edition = "2018"
# If you copy one of the examples into a new project, you should be using
# [dependencies] instead.
[dev-dependencies]
tokio = { version = "0.3.0", path = "../tokio", features = ["full", "tracing"] }
tokio = { version = "1.0.0", path = "../tokio", features = ["full", "tracing"] }
tokio-util = { version = "0.6.0", path = "../tokio-util", features = ["full"] }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
async-stream = "0.3"
tracing = "0.1"
tracing-subscriber = { version = "0.2.7", default-features = false, features = ["fmt", "ansi", "env-filter", "chrono", "tracing-log"] }
tokio-util = { version = "0.4.0", path = "../tokio-util", features = ["full"] }
bytes = "0.5"
futures = "0.3.0"
bytes = "1.0.0"
futures = { version = "0.3.0", features = ["thread-pool"]}
http = "0.2"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
httparse = "1.0"
time = "0.1"
once_cell = "1.5.2"
[[example]]
name = "chat"
@@ -63,3 +68,12 @@ path = "udp-codec.rs"
[[example]]
name = "tinyhttp"
path = "tinyhttp.rs"
[[example]]
name = "custom-executor"
path = "custom-executor.rs"
[[example]]
name = "custom-executor-tokio-context"
path = "custom-executor-tokio-context.rs"
+9 -6
View File
@@ -27,8 +27,8 @@
#![warn(rust_2018_idioms)]
use tokio::net::{TcpListener, TcpStream};
use tokio::stream::{Stream, StreamExt};
use tokio::sync::{mpsc, Mutex};
use tokio_stream::{Stream, StreamExt};
use tokio_util::codec::{Framed, LinesCodec, LinesCodecError};
use futures::SinkExt;
@@ -101,9 +101,6 @@ async fn main() -> Result<(), Box<dyn Error>> {
/// Shorthand for the transmit half of the message channel.
type Tx = mpsc::UnboundedSender<String>;
/// Shorthand for the receive half of the message channel.
type Rx = mpsc::UnboundedReceiver<String>;
/// Data that is shared between all peers in the chat server.
///
/// This is the set of `Tx` handles for all connected clients. Whenever a
@@ -127,7 +124,7 @@ struct Peer {
///
/// This is used to receive messages from peers. When a message is received
/// off of this `Rx`, it will be written to the socket.
rx: Rx,
rx: Pin<Box<dyn Stream<Item = String> + Send>>,
}
impl Shared {
@@ -159,11 +156,17 @@ impl Peer {
let addr = lines.get_ref().peer_addr()?;
// Create a channel for this peer
let (tx, rx) = mpsc::unbounded_channel();
let (tx, mut rx) = mpsc::unbounded_channel();
// Add an entry for this `Peer` in the shared state map.
state.lock().await.peers.insert(addr, tx);
let rx = Box::pin(async_stream::stream! {
while let Some(item) = rx.recv().await {
yield item;
}
});
Ok(Peer { lines, rx })
}
}
+32
View File
@@ -0,0 +1,32 @@
// This example shows how to use the tokio runtime with any other executor
//
//It takes advantage from RuntimeExt which provides the extension to customize your
//runtime.
use tokio::net::TcpListener;
use tokio::runtime::Builder;
use tokio::sync::oneshot;
use tokio_util::context::RuntimeExt;
fn main() {
let (tx, rx) = oneshot::channel();
let rt1 = Builder::new_multi_thread()
.worker_threads(1)
// no timer!
.build()
.unwrap();
let rt2 = Builder::new_multi_thread()
.worker_threads(1)
.enable_all()
.build()
.unwrap();
// Without the `HandleExt.wrap()` there would be a panic because there is
// no timer running, since it would be referencing runtime r1.
let _ = rt1.block_on(rt2.wrap(async move {
let listener = TcpListener::bind("0.0.0.0:0").await.unwrap();
println!("addr: {:?}", listener.local_addr());
tx.send(()).unwrap();
}));
futures::executor::block_on(rx).unwrap();
}
+56
View File
@@ -0,0 +1,56 @@
// This example shows how to use the tokio runtime with any other executor
//
// The main components are a spawn fn that will wrap futures in a special future
// that will always enter the tokio context on poll. This only spawns one extra thread
// to manage and run the tokio drivers in the background.
use tokio::net::TcpListener;
use tokio::sync::oneshot;
fn main() {
let (tx, rx) = oneshot::channel();
my_custom_runtime::spawn(async move {
let listener = TcpListener::bind("0.0.0.0:0").await.unwrap();
println!("addr: {:?}", listener.local_addr());
tx.send(()).unwrap();
});
futures::executor::block_on(rx).unwrap();
}
mod my_custom_runtime {
use once_cell::sync::Lazy;
use std::future::Future;
use tokio_util::context::TokioContext;
pub fn spawn(f: impl Future<Output = ()> + Send + 'static) {
EXECUTOR.spawn(f);
}
struct ThreadPool {
inner: futures::executor::ThreadPool,
rt: tokio::runtime::Runtime,
}
static EXECUTOR: Lazy<ThreadPool> = Lazy::new(|| {
// Spawn tokio runtime on a single background thread
// enabling IO and timers.
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
let inner = futures::executor::ThreadPool::builder().create().unwrap();
ThreadPool { inner, rt }
});
impl ThreadPool {
fn spawn(&self, f: impl Future<Output = ()> + Send + 'static) {
let handle = self.rt.handle().clone();
self.inner.spawn_ok(TokioContext::new(f, handle));
}
}
}
+1 -1
View File
@@ -55,7 +55,7 @@
#![warn(rust_2018_idioms)]
use tokio::net::TcpListener;
use tokio::stream::StreamExt;
use tokio_stream::StreamExt;
use tokio_util::codec::{BytesCodec, Decoder};
use std::env;
+1 -1
View File
@@ -42,7 +42,7 @@
#![warn(rust_2018_idioms)]
use tokio::net::TcpListener;
use tokio::stream::StreamExt;
use tokio_stream::StreamExt;
use tokio_util::codec::{Framed, LinesCodec};
use futures::SinkExt;
+1 -1
View File
@@ -20,7 +20,7 @@ use http::{header::HeaderValue, Request, Response, StatusCode};
extern crate serde_derive;
use std::{env, error::Error, fmt, io};
use tokio::net::{TcpListener, TcpStream};
use tokio::stream::StreamExt;
use tokio_stream::StreamExt;
use tokio_util::codec::{Decoder, Encoder, Framed};
#[tokio::main]
+1 -7
View File
@@ -1,8 +1,3 @@
fn main() {}
// Disabled while future of UdpFramed is decided on.
// See https://github.com/tokio-rs/tokio/issues/2830
/*
//! This example leverages `BytesCodec` to create a UDP client and server which
//! speak a custom protocol.
//!
@@ -14,8 +9,8 @@ fn main() {}
#![warn(rust_2018_idioms)]
use tokio::net::UdpSocket;
use tokio::stream::StreamExt;
use tokio::{io, time};
use tokio_stream::StreamExt;
use tokio_util::codec::BytesCodec;
use tokio_util::udp::UdpFramed;
@@ -83,4 +78,3 @@ async fn pong(socket: &mut UdpFramed<BytesCodec>) -> Result<(), io::Error> {
Ok(())
}
*/
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "stress-test"
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio = { path = "../tokio/", features = ["full"] }
[dev-dependencies]
rand = "0.8"
+58
View File
@@ -0,0 +1,58 @@
//! Simple TCP echo server to check memory leaks using Valgrind.
use std::{thread::sleep, time::Duration};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{TcpListener, TcpSocket},
runtime::Builder,
sync::oneshot,
};
const TCP_ENDPOINT: &str = "127.0.0.1:8080";
const NUM_MSGS: usize = 100;
const MSG_SIZE: usize = 1024;
fn main() {
let rt = Builder::new_multi_thread().enable_io().build().unwrap();
let rt2 = Builder::new_multi_thread().enable_io().build().unwrap();
rt.spawn(async {
let listener = TcpListener::bind(TCP_ENDPOINT).await.unwrap();
let (mut socket, _) = listener.accept().await.unwrap();
let (mut rd, mut wr) = socket.split();
while tokio::io::copy(&mut rd, &mut wr).await.is_ok() {}
});
// wait a bit so that the listener binds.
sleep(Duration::from_millis(100));
// create a channel to let the main thread know that all the messages were sent and received.
let (tx, mut rx) = oneshot::channel();
rt2.spawn(async {
let addr = TCP_ENDPOINT.parse().unwrap();
let socket = TcpSocket::new_v4().unwrap();
let mut stream = socket.connect(addr).await.unwrap();
let mut buff = [0; MSG_SIZE];
for _ in 0..NUM_MSGS {
let one_mega_random_bytes: Vec<u8> =
(0..MSG_SIZE).map(|_| rand::random::<u8>()).collect();
stream
.write_all(one_mega_random_bytes.as_slice())
.await
.unwrap();
stream.read(&mut buff).await.unwrap();
}
tx.send(()).unwrap();
});
loop {
// check that we're done.
match rx.try_recv() {
Err(oneshot::error::TryRecvError::Empty) => (),
Err(oneshot::error::TryRecvError::Closed) => panic!("channel got closed..."),
Ok(()) => break,
}
}
}
+4
View File
@@ -1,3 +1,7 @@
# 1.0.0 (December 23, 2020)
- track `tokio` 1.0 release.
# 0.3.1 (October 25, 2020)
### Fixed
+4 -4
View File
@@ -6,14 +6,14 @@ name = "tokio-macros"
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "v0.3.x" git tag.
version = "0.3.1"
# - Create "tokio-macros-1.0.x" git tag.
version = "1.0.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/0.3.1/tokio_macros"
documentation = "https://docs.rs/tokio-macros/1.0.0/tokio_macros"
description = """
Tokio's proc macros.
"""
@@ -30,7 +30,7 @@ quote = "1"
syn = { version = "1.0.3", features = ["full"] }
[dev-dependencies]
tokio = { version = "0.3.0", path = "../tokio", features = ["full"] }
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
[package.metadata.docs.rs]
all-features = true
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2019 Tokio Contributors
Copyright (c) 2020 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+12 -6
View File
@@ -193,17 +193,23 @@ fn parse_knobs(
let name = ident.unwrap().to_string().to_lowercase();
let msg = match name.as_str() {
"threaded_scheduler" | "multi_thread" => {
format!("Set the runtime flavor with #[{}(flavor = \"multi_thread\")].", macro_name)
},
format!(
"Set the runtime flavor with #[{}(flavor = \"multi_thread\")].",
macro_name
)
}
"basic_scheduler" | "current_thread" | "single_threaded" => {
format!("Set the runtime flavor with #[{}(flavor = \"current_thread\")].", macro_name)
},
format!(
"Set the runtime flavor with #[{}(flavor = \"current_thread\")].",
macro_name
)
}
"flavor" | "worker_threads" => {
format!("The `{}` attribute requires an argument.", name)
},
}
name => {
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`", name)
},
}
};
return Err(syn::Error::new_spanned(path, msg));
}
+29 -23
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio-macros/0.3.1")]
#![doc(html_root_url = "https://docs.rs/tokio-macros/1.0.0")]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -24,8 +24,8 @@ mod select;
use proc_macro::TokenStream;
/// Marks async function to be executed by the selected runtime. This macro helps
/// set up a `Runtime` without requiring the user to use
/// Marks async function to be executed by the selected runtime. This macro
/// helps set up a `Runtime` without requiring the user to use
/// [Runtime](../tokio/runtime/struct.Runtime.html) or
/// [Builder](../tokio/runtime/struct.Builder.html) directly.
///
@@ -35,6 +35,12 @@ use proc_macro::TokenStream;
/// [Builder](../tokio/runtime/struct.Builder.html), which provides a more
/// powerful interface.
///
/// Note: This macro can be used on any function and not just the `main`
/// function. Using it on a non-main function makes the function behave as if it
/// was synchronous by starting a new runtime each time it is called. If the
/// function is called often, it is preferable to create the runtime using the
/// runtime builder so the runtime can be reused across calls.
///
/// # Multi-threaded runtime
///
/// To use the multi-threaded runtime, the macro can be configured using
@@ -47,6 +53,9 @@ use proc_macro::TokenStream;
/// The `worker_threads` option configures the number of worker threads, and
/// defaults to the number of cpus on the system. This is the default flavor.
///
/// Note: The multi-threaded runtime requires the `rt-multi-thread` feature
/// flag.
///
/// # Current thread runtime
///
/// To use the single-threaded runtime known as the `current_thread` runtime,
@@ -137,10 +146,10 @@ use proc_macro::TokenStream;
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro will not work.
/// If you must rename the 0.2 version of tokio because you're also using the
/// 0.1 version of tokio, you _must_ make the tokio 0.2 crate available as
/// `tokio` in the module where this macro is expanded.
/// If you rename the Tokio crate in your dependencies this macro will not work.
/// If you must rename the current version of Tokio because you're also using an
/// older version of Tokio, you _must_ make the current version of Tokio
/// available as `tokio` in the module where this macro is expanded.
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
@@ -182,11 +191,10 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
/// If you rename the Tokio crate in your dependencies this macro will not work.
/// If you must rename the current version of Tokio because you're also using an
/// older version of Tokio, you _must_ make the current version of Tokio
/// available as `tokio` in the module where this macro is expanded.
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
@@ -208,7 +216,7 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
///
/// ### Using default
///
/// The default test runtime is multi-threaded.
/// The default test runtime is single-threaded.
///
/// ```no_run
/// #[tokio::test]
@@ -219,11 +227,10 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
/// If you rename the Tokio crate in your dependencies this macro will not work.
/// If you must rename the current version of Tokio because you're also using an
/// older version of Tokio, you _must_ make the current version of Tokio
/// available as `tokio` in the module where this macro is expanded.
#[proc_macro_attribute]
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, true)
@@ -242,11 +249,10 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
/// If you rename the Tokio crate in your dependencies this macro will not work.
/// If you must rename the current version of Tokio because you're also using an
/// older version of Tokio, you _must_ make the current version of Tokio
/// available as `tokio` in the module where this macro is expanded.
#[proc_macro_attribute]
pub fn test_rt(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, false)
+3
View File
@@ -0,0 +1,3 @@
# 0.1.0 (December 23, 2020)
- Initial release
+37
View File
@@ -0,0 +1,37 @@
[package]
name = "tokio-stream"
# When releasing to crates.io:
# - Remove path dependencies
# - Update html_root_url.
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-stream-0.1.x" git tag.
version = "0.1.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-stream/0.1.0/tokio_stream"
description = """
Utilities to work with `Stream` and `tokio`.
"""
categories = ["asynchronous"]
[features]
default = ["time"]
time = ["tokio/time"]
[dependencies]
futures-core = { version = "0.3.0" }
pin-project-lite = "0.2.0"
tokio = { version = "1.0", path = "../tokio", features = ["sync"] }
async-stream = "0.3"
[dev-dependencies]
tokio = { version = "1.0", path = "../tokio", features = ["full"] }
tokio-test = { path = "../tokio-test" }
futures = { version = "0.3", default-features = false }
proptest = "0.10.0"
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2020 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::future::Future;
use core::marker::PhantomPinned;
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::future::Future;
use core::marker::PhantomPinned;
@@ -1,4 +1,4 @@
use crate::stream::{Fuse, Stream};
use crate::{Fuse, Stream};
use core::pin::Pin;
use core::task::{Context, Poll};
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::future::Future;
use core::marker::PhantomPinned;
@@ -26,7 +26,7 @@ pin_project! {
}
}
/// Convert from a [`Stream`](crate::stream::Stream).
/// Convert from a [`Stream`](crate::Stream).
///
/// This trait is not intended to be used directly. Instead, call
/// [`StreamExt::collect()`](super::StreamExt::collect).
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::marker::PhantomData;
use core::pin::Pin;
@@ -24,7 +24,7 @@ unsafe impl<T> Sync for Empty<T> {}
/// Basic usage:
///
/// ```
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::fmt;
use core::pin::Pin;
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::fmt;
use core::pin::Pin;
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::future::Future;
use core::marker::PhantomPinned;
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use pin_project_lite::pin_project;
use std::pin::Pin;
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::pin::Pin;
use core::task::{Context, Poll};
@@ -8,6 +8,7 @@ use core::task::{Context, Poll};
#[must_use = "streams do nothing unless polled"]
pub struct Iter<I> {
iter: I,
yield_amt: usize,
}
impl<I> Unpin for Iter<I> {}
@@ -20,7 +21,7 @@ impl<I> Unpin for Iter<I> {}
///
/// ```
/// # async fn dox() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(vec![17, 19]);
///
@@ -35,6 +36,7 @@ where
{
Iter {
iter: i.into_iter(),
yield_amt: 0,
}
}
@@ -45,9 +47,18 @@ where
type Item = I::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<I::Item>> {
let coop = ready!(crate::coop::poll_proceed(cx));
coop.made_progress();
Poll::Ready(self.iter.next())
// TODO: add coop back
if self.yield_amt >= 32 {
self.yield_amt = 0;
cx.waker().wake_by_ref();
Poll::Pending
} else {
self.yield_amt += 1;
Poll::Ready(self.iter.next())
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
@@ -1,9 +1,34 @@
#![doc(html_root_url = "https://docs.rs/tokio-stream/0.1.0")]
#![allow(
clippy::cognitive_complexity,
clippy::large_enum_variant,
clippy::needless_doctest_main
)]
#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub
)]
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]
#![cfg_attr(docsrs, feature(doc_cfg))]
//! Stream utilities for Tokio.
//!
//! A `Stream` is an asynchronous sequence of values. It can be thought of as
//! an asynchronous version of the standard library's `Iterator` trait.
//!
//! This module provides helpers to work with them. For examples of usage and a more in-depth
//! This crate provides helpers to work with them. For examples of usage and a more in-depth
//! description of streams you can also refer to the [streams
//! tutorial](https://tokio.rs/tokio/tutorial/streams) on the tokio website.
//!
@@ -15,7 +40,7 @@
//! `while let` loop as follows:
//!
//! ```rust
//! use tokio::stream::{self, StreamExt};
//! use tokio_stream::{self as stream, StreamExt};
//!
//! #[tokio::main]
//! async fn main() {
@@ -46,13 +71,16 @@
//! [`tokio-util`] provides the [`StreamReader`] and [`ReaderStream`]
//! types when the io feature is enabled.
//!
//! [tokio-util]: https://docs.rs/tokio-util/0.3/tokio_util/codec/index.html
//! [`tokio::io`]: crate::io
//! [`AsyncRead`]: crate::io::AsyncRead
//! [`AsyncWrite`]: crate::io::AsyncWrite
//! [`tokio-util`]: https://docs.rs/tokio-util/0.4/tokio_util/codec/index.html
//! [`tokio::io`]: https://docs.rs/tokio/1.0/tokio/io/index.html
//! [`AsyncRead`]: https://docs.rs/tokio/1.0/tokio/io/trait.AsyncRead.html
//! [`AsyncWrite`]: https://docs.rs/tokio/1.0/tokio/io/trait.AsyncWrite.html
//! [`ReaderStream`]: https://docs.rs/tokio-util/0.4/tokio_util/io/struct.ReaderStream.html
//! [`StreamReader`]: https://docs.rs/tokio-util/0.4/tokio_util/io/struct.StreamReader.html
#[macro_use]
mod macros;
mod all;
use all::AllFuture;
@@ -120,16 +148,47 @@ use take_while::TakeWhile;
cfg_time! {
mod timeout;
use timeout::Timeout;
use crate::time::Duration;
use tokio::time::Duration;
mod throttle;
use crate::stream::throttle::{throttle, Throttle};
use crate::throttle::{throttle, Throttle};
}
#[doc(no_inline)]
pub use futures_core::Stream;
/// An extension trait for `Stream`s that provides a variety of convenient
/// combinator functions.
/// An extension trait for the [`Stream`] trait that provides a variety of
/// convenient combinator functions.
///
/// Be aware that the `Stream` trait in Tokio is a re-export of the trait found
/// in the [futures] crate, however both Tokio and futures provide separate
/// `StreamExt` utility traits, and some utilities are only available on one of
/// these traits. Click [here][futures-StreamExt] to see the other `StreamExt`
/// trait in the futures crate.
///
/// If you need utilities from both `StreamExt` traits, you should prefer to
/// import one of them, and use the other through the fully qualified call
/// syntax. For example:
/// ```
/// // import one of the traits:
/// use futures::stream::StreamExt;
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
///
/// let a = tokio_stream::iter(vec![1, 3, 5]);
/// let b = tokio_stream::iter(vec![2, 4, 6]);
///
/// // use the fully qualified call syntax for the other trait:
/// let merged = tokio_stream::StreamExt::merge(a, b);
///
/// // use normal call notation for futures::stream::StreamExt::collect
/// let output: Vec<_> = merged.collect().await;
/// assert_eq!(output, vec![1, 2, 3, 4, 5, 6]);
/// # }
/// ```
///
/// [`Stream`]: crate::Stream
/// [futures]: https://docs.rs/futures
/// [futures-StreamExt]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html
pub trait StreamExt: Stream {
/// Consumes and returns the next value in the stream or `None` if the
/// stream is finished.
@@ -152,7 +211,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=3);
///
@@ -188,7 +247,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(vec![Ok(1), Ok(2), Err("nope")]);
///
@@ -220,7 +279,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let stream = stream::iter(1..=3);
/// let mut stream = stream.map(|x| x + 3);
@@ -253,16 +312,17 @@ pub trait StreamExt: Stream {
///
/// For merging multiple streams, consider using [`StreamMap`] instead.
///
/// [`StreamMap`]: crate::stream::StreamMap
/// [`StreamMap`]: crate::StreamMap
///
/// # Examples
///
/// ```
/// use tokio::stream::StreamExt;
/// use tokio_stream::{StreamExt, Stream};
/// use tokio::sync::mpsc;
/// use tokio::time;
///
/// use std::time::Duration;
/// use std::pin::Pin;
///
/// # /*
/// #[tokio::main]
@@ -270,8 +330,21 @@ pub trait StreamExt: Stream {
/// # #[tokio::main(flavor = "current_thread")]
/// async fn main() {
/// # time::pause();
/// let (tx1, rx1) = mpsc::channel(10);
/// let (tx2, rx2) = mpsc::channel(10);
/// let (tx1, mut rx1) = mpsc::channel::<usize>(10);
/// let (tx2, mut rx2) = mpsc::channel::<usize>(10);
///
/// // Convert the channels to a `Stream`.
/// let rx1 = Box::pin(async_stream::stream! {
/// while let Some(item) = rx1.recv().await {
/// yield item;
/// }
/// }) as Pin<Box<dyn Stream<Item = usize> + Send>>;
///
/// let rx2 = Box::pin(async_stream::stream! {
/// while let Some(item) = rx2.recv().await {
/// yield item;
/// }
/// }) as Pin<Box<dyn Stream<Item = usize> + Send>>;
///
/// let mut rx = rx1.merge(rx2);
///
@@ -334,7 +407,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let stream = stream::iter(1..=8);
/// let mut evens = stream.filter(|x| x % 2 == 0);
@@ -370,7 +443,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let stream = stream::iter(1..=8);
/// let mut evens = stream.filter_map(|x| {
@@ -402,7 +475,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// use tokio::stream::{Stream, StreamExt};
/// use tokio_stream::{Stream, StreamExt};
///
/// use std::pin::Pin;
/// use std::task::{Context, Poll};
@@ -467,7 +540,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=10).take(3);
///
@@ -496,7 +569,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=10).take_while(|x| *x <= 3);
///
@@ -522,7 +595,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=10).skip(7);
///
@@ -553,7 +626,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
/// let mut stream = stream::iter(vec![1,2,3,4,1]).skip_while(|x| *x < 3);
///
/// assert_eq!(Some(3), stream.next().await);
@@ -596,7 +669,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
@@ -611,7 +684,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
@@ -655,7 +728,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
@@ -670,7 +743,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
@@ -699,7 +772,7 @@ pub trait StreamExt: Stream {
/// # Examples
///
/// ```
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
@@ -739,7 +812,7 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, *};
/// use tokio_stream::{self as stream, *};
///
/// let s = stream::iter(vec![1u8, 2, 3]);
/// let sum = s.fold(0, |acc, x| acc + x).await;
@@ -766,7 +839,9 @@ pub trait StreamExt: Stream {
/// `collect` streams all values, awaiting as needed. Values are pushed into
/// a collection. A number of different target collection types are
/// supported, including [`Vec`](std::vec::Vec),
/// [`String`](std::string::String), and [`Bytes`](bytes::Bytes).
/// [`String`](std::string::String), and [`Bytes`].
///
/// [`Bytes`]: https://docs.rs/bytes/0.6.0/bytes/struct.Bytes.html
///
/// # `Result`
///
@@ -785,7 +860,7 @@ pub trait StreamExt: Stream {
/// Basic usage:
///
/// ```
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
@@ -802,7 +877,7 @@ pub trait StreamExt: Stream {
/// Collecting a stream of `Result` values
///
/// ```
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
@@ -858,11 +933,12 @@ pub trait StreamExt: Stream {
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
/// use std::time::Duration;
/// # let int_stream = stream::iter(1..=3);
///
/// let mut int_stream = int_stream.timeout(Duration::from_secs(1));
/// let int_stream = int_stream.timeout(Duration::from_secs(1));
/// tokio::pin!(int_stream);
///
/// // When no items time out, we get the 3 elements in succession:
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
@@ -903,10 +979,11 @@ pub trait StreamExt: Stream {
/// Create a throttled stream.
/// ```rust,no_run
/// use std::time::Duration;
/// use tokio::stream::StreamExt;
/// use tokio_stream::StreamExt;
///
/// # async fn dox() {
/// let mut item_stream = futures::stream::repeat("one").throttle(Duration::from_secs(2));
/// let item_stream = futures::stream::repeat("one").throttle(Duration::from_secs(2));
/// tokio::pin!(item_stream);
///
/// loop {
/// // The string will be produced at most every 2 seconds
+18
View File
@@ -0,0 +1,18 @@
macro_rules! cfg_time {
($($item:item)*) => {
$(
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
$item
)*
}
}
macro_rules! ready {
($e:expr $(,)?) => {
match $e {
std::task::Poll::Ready(t) => t,
std::task::Poll::Pending => return std::task::Poll::Pending,
}
};
}
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::fmt;
use core::pin::Pin;
@@ -1,4 +1,4 @@
use crate::stream::{Fuse, Stream};
use crate::{Fuse, Stream};
use core::pin::Pin;
use core::task::{Context, Poll};
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::future::Future;
use core::marker::PhantomPinned;
@@ -1,4 +1,4 @@
use crate::stream::{self, Iter, Stream};
use crate::{Iter, Stream};
use core::option;
use core::pin::Pin;
@@ -20,7 +20,7 @@ impl<I> Unpin for Once<I> {}
/// # Examples
///
/// ```
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
@@ -35,7 +35,7 @@ impl<I> Unpin for Once<I> {}
/// ```
pub fn once<T>(value: T) -> Once<T> {
Once {
iter: stream::iter(Some(value).into_iter()),
iter: crate::iter(Some(value).into_iter()),
}
}
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::marker::PhantomData;
use core::pin::Pin;
@@ -16,7 +16,7 @@ unsafe impl<T> Sync for Pending<T> {}
/// Creates a stream that is never ready
///
/// The returned stream is never ready. Attempting to call
/// [`next()`](crate::stream::StreamExt::next) will never complete. Use
/// [`next()`](crate::StreamExt::next) will never complete. Use
/// [`stream::empty()`](super::empty()) to obtain a stream that is is
/// immediately empty but returns no values.
///
@@ -25,7 +25,7 @@ unsafe impl<T> Sync for Pending<T> {}
/// Basic usage:
///
/// ```no_run
/// use tokio::stream::{self, StreamExt};
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::fmt;
use core::pin::Pin;
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::fmt;
use core::pin::Pin;
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use std::borrow::Borrow;
use std::hash::Hash;
@@ -42,9 +42,9 @@ use std::task::{Context, Poll};
/// to be merged, it may be advisable to use tasks sending values on a shared
/// [`mpsc`] channel.
///
/// [`StreamExt::merge`]: crate::stream::StreamExt::merge
/// [`mpsc`]: crate::sync::mpsc
/// [`pin!`]: macro@pin
/// [`StreamExt::merge`]: crate::StreamExt::merge
/// [`mpsc`]: https://docs.rs/tokio/1.0/tokio/sync/mpsc/index.html
/// [`pin!`]: https://docs.rs/tokio/1.0/tokio/macro.pin.html
/// [`Box::pin`]: std::boxed::Box::pin
///
/// # Examples
@@ -52,13 +52,27 @@ use std::task::{Context, Poll};
/// Merging two streams, then remove them after receiving the first value
///
/// ```
/// use tokio::stream::{StreamExt, StreamMap};
/// use tokio_stream::{StreamExt, StreamMap, Stream};
/// use tokio::sync::mpsc;
/// use std::pin::Pin;
///
/// #[tokio::main]
/// async fn main() {
/// let (tx1, rx1) = mpsc::channel(10);
/// let (tx2, rx2) = mpsc::channel(10);
/// let (tx1, mut rx1) = mpsc::channel::<usize>(10);
/// let (tx2, mut rx2) = mpsc::channel::<usize>(10);
///
/// // Convert the channels to a `Stream`.
/// let rx1 = Box::pin(async_stream::stream! {
/// while let Some(item) = rx1.recv().await {
/// yield item;
/// }
/// }) as Pin<Box<dyn Stream<Item = usize> + Send>>;
///
/// let rx2 = Box::pin(async_stream::stream! {
/// while let Some(item) = rx2.recv().await {
/// yield item;
/// }
/// }) as Pin<Box<dyn Stream<Item = usize> + Send>>;
///
/// tokio::spawn(async move {
/// tx1.send(1).await.unwrap();
@@ -103,7 +117,7 @@ use std::task::{Context, Poll};
/// sent to the client over a socket.
///
/// ```no_run
/// use tokio::stream::{Stream, StreamExt, StreamMap};
/// use tokio_stream::{Stream, StreamExt, StreamMap};
///
/// enum Command {
/// Join(String),
@@ -112,13 +126,13 @@ use std::task::{Context, Poll};
///
/// fn commands() -> impl Stream<Item = Command> {
/// // Streams in user commands by parsing `stdin`.
/// # tokio::stream::pending()
/// # tokio_stream::pending()
/// }
///
/// // Join a channel, returns a stream of messages received on the channel.
/// fn join(channel: &str) -> impl Stream<Item = String> + Unpin {
/// // left as an exercise to the reader
/// # tokio::stream::pending()
/// # tokio_stream::pending()
/// }
///
/// #[tokio::main]
@@ -156,7 +170,7 @@ use std::task::{Context, Poll};
/// }
/// }
/// ```
#[derive(Debug, Default)]
#[derive(Debug)]
pub struct StreamMap<K, V> {
/// Streams stored in the map
entries: Vec<(K, V)>,
@@ -170,7 +184,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, pending};
/// use tokio_stream::{StreamMap, pending};
///
/// let mut map = StreamMap::new();
///
@@ -193,7 +207,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, pending};
/// use tokio_stream::{StreamMap, pending};
///
/// let mut map = StreamMap::new();
///
@@ -217,7 +231,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, Pending};
/// use tokio_stream::{StreamMap, Pending};
///
/// let map: StreamMap<&str, Pending<()>> = StreamMap::new();
/// ```
@@ -233,7 +247,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, Pending};
/// use tokio_stream::{StreamMap, Pending};
///
/// let map: StreamMap<&str, Pending<()>> = StreamMap::with_capacity(10);
/// ```
@@ -250,7 +264,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, pending};
/// use tokio_stream::{StreamMap, pending};
///
/// let mut map = StreamMap::new();
///
@@ -273,7 +287,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, pending};
/// use tokio_stream::{StreamMap, pending};
///
/// let mut map = StreamMap::new();
///
@@ -296,7 +310,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, pending};
/// use tokio_stream::{StreamMap, pending};
///
/// let mut map = StreamMap::new();
///
@@ -320,7 +334,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, Pending};
/// use tokio_stream::{StreamMap, Pending};
///
/// let map: StreamMap<i32, Pending<()>> = StreamMap::with_capacity(100);
/// assert!(map.capacity() >= 100);
@@ -334,7 +348,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, pending};
/// use tokio_stream::{StreamMap, pending};
///
/// let mut a = StreamMap::new();
/// assert_eq!(a.len(), 0);
@@ -367,7 +381,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, pending};
/// use tokio_stream::{StreamMap, pending};
///
/// let mut a = StreamMap::new();
/// a.insert(1, pending::<i32>());
@@ -388,7 +402,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, pending};
/// use tokio_stream::{StreamMap, pending};
///
/// let mut map = StreamMap::new();
///
@@ -416,7 +430,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, pending};
/// use tokio_stream::{StreamMap, pending};
///
/// let mut map = StreamMap::new();
/// map.insert(1, pending::<i32>());
@@ -445,7 +459,7 @@ impl<K, V> StreamMap<K, V> {
/// # Examples
///
/// ```
/// use tokio::stream::{StreamMap, pending};
/// use tokio_stream::{StreamMap, pending};
///
/// let mut map = StreamMap::new();
/// map.insert(1, pending::<i32>());
@@ -476,7 +490,7 @@ where
fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<Option<(usize, V::Item)>> {
use Poll::*;
let start = crate::util::thread_rng_n(self.entries.len() as u32) as usize;
let start = self::rand::thread_rng_n(self.entries.len() as u32) as usize;
let mut idx = start;
for _ in 0..self.entries.len() {
@@ -513,6 +527,12 @@ where
}
}
impl<K, V> Default for StreamMap<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K, V> Stream for StreamMap<K, V>
where
K: Clone + Unpin,
@@ -547,3 +567,98 @@ where
ret
}
}
mod rand {
use std::cell::Cell;
mod loom {
#[cfg(not(loom))]
pub(crate) mod rand {
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hash, Hasher};
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering::Relaxed;
static COUNTER: AtomicU32 = AtomicU32::new(1);
pub(crate) fn seed() -> u64 {
let rand_state = RandomState::new();
let mut hasher = rand_state.build_hasher();
// Hash some unique-ish data to generate some new state
COUNTER.fetch_add(1, Relaxed).hash(&mut hasher);
// Get the seed
hasher.finish()
}
}
#[cfg(loom)]
pub(crate) mod rand {
pub(crate) fn seed() -> u64 {
1
}
}
}
/// 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
/// This generator passes the SmallCrush suite, part of TestU01 framework:
/// http://simul.iro.umontreal.ca/testu01/tu01.html
#[derive(Debug)]
pub(crate) struct FastRand {
one: Cell<u32>,
two: Cell<u32>,
}
impl FastRand {
/// Initialize a new, thread-local, fast random number generator.
pub(crate) fn new(seed: u64) -> FastRand {
let one = (seed >> 32) as u32;
let mut two = seed as u32;
if two == 0 {
// This value cannot be zero
two = 1;
}
FastRand {
one: Cell::new(one),
two: Cell::new(two),
}
}
pub(crate) fn fastrand_n(&self, n: u32) -> u32 {
// This is similar to fastrand() % n, but faster.
// See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
let mul = (self.fastrand() as u64).wrapping_mul(n as u64);
(mul >> 32) as u32
}
fn fastrand(&self) -> u32 {
let mut s1 = self.one.get();
let s0 = self.two.get();
s1 ^= s1 << 17;
s1 = s1 ^ s0 ^ s1 >> 7 ^ s0 >> 16;
self.one.set(s0);
self.two.set(s1);
s0.wrapping_add(s1)
}
}
// Used by `StreamMap`
pub(crate) fn thread_rng_n(n: u32) -> u32 {
thread_local! {
static THREAD_RNG: FastRand = FastRand::new(loom::rand::seed());
}
THREAD_RNG.with(|rng| rng.fastrand_n(n))
}
}
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::cmp;
use core::fmt;
@@ -1,4 +1,4 @@
use crate::stream::Stream;
use crate::Stream;
use core::fmt;
use core::pin::Pin;
@@ -1,7 +1,7 @@
//! Slow down a stream by enforcing a delay between items.
use crate::stream::Stream;
use crate::time::{Duration, Instant, Sleep};
use crate::Stream;
use tokio::time::{Duration, Instant, Sleep};
use std::future::Future;
use std::marker::Unpin;
@@ -14,14 +14,8 @@ pub(super) fn throttle<T>(duration: Duration, stream: T) -> Throttle<T>
where
T: Stream,
{
let delay = if duration == Duration::from_millis(0) {
None
} else {
Some(Sleep::new_timeout(Instant::now() + duration, duration))
};
Throttle {
delay,
delay: tokio::time::sleep_until(Instant::now() + duration),
duration,
has_delayed: true,
stream,
@@ -33,8 +27,8 @@ pin_project! {
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct Throttle<T> {
// `None` when duration is zero.
delay: Option<Sleep>,
#[pin]
delay: Sleep,
duration: Duration,
// Set to true when `delay` has returned ready, but `stream` hasn't.
@@ -75,23 +69,29 @@ impl<T: Unpin> Throttle<T> {
impl<T: Stream> Stream for Throttle<T> {
type Item = T::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
if !self.has_delayed && self.delay.is_some() {
ready!(Pin::new(self.as_mut().project().delay.as_mut().unwrap()).poll(cx));
*self.as_mut().project().has_delayed = true;
fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
let mut me = self.project();
let dur = *me.duration;
if !*me.has_delayed && !is_zero(dur) {
ready!(me.delay.as_mut().poll(cx));
*me.has_delayed = true;
}
let value = ready!(self.as_mut().project().stream.poll_next(cx));
let value = ready!(me.stream.poll_next(cx));
if value.is_some() {
let dur = self.duration;
if let Some(ref mut delay) = self.as_mut().project().delay {
delay.reset(Instant::now() + dur);
if !is_zero(dur) {
me.delay.reset(Instant::now() + dur);
}
*self.as_mut().project().has_delayed = false;
*me.has_delayed = false;
}
Poll::Ready(value)
}
}
fn is_zero(dur: Duration) -> bool {
dur == Duration::from_millis(0)
}
@@ -1,10 +1,11 @@
use crate::stream::{Fuse, Stream};
use crate::time::{error::Elapsed, Instant, Sleep};
use crate::{Fuse, Stream};
use tokio::time::{Instant, Sleep};
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use pin_project_lite::pin_project;
use std::fmt;
use std::time::Duration;
pin_project! {
@@ -14,16 +15,21 @@ pin_project! {
pub struct Timeout<S> {
#[pin]
stream: Fuse<S>,
#[pin]
deadline: Sleep,
duration: Duration,
poll_deadline: bool,
}
}
/// Error returned by `Timeout`.
#[derive(Debug, PartialEq)]
pub struct Elapsed(());
impl<S: Stream> Timeout<S> {
pub(super) fn new(stream: S, duration: Duration) -> Self {
let next = Instant::now() + duration;
let deadline = Sleep::new_timeout(next, duration);
let deadline = tokio::time::sleep_until(next);
Timeout {
stream: Fuse::new(stream),
@@ -37,22 +43,24 @@ impl<S: Stream> Timeout<S> {
impl<S: Stream> Stream for Timeout<S> {
type Item = Result<S::Item, Elapsed>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
match self.as_mut().project().stream.poll_next(cx) {
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let me = self.project();
match me.stream.poll_next(cx) {
Poll::Ready(v) => {
if v.is_some() {
let next = Instant::now() + self.duration;
self.as_mut().project().deadline.reset(next);
*self.as_mut().project().poll_deadline = true;
let next = Instant::now() + *me.duration;
me.deadline.reset(next);
*me.poll_deadline = true;
}
return Poll::Ready(v.map(Ok));
}
Poll::Pending => {}
};
if self.poll_deadline {
ready!(Pin::new(self.as_mut().project().deadline).poll(cx));
*self.as_mut().project().poll_deadline = false;
if *me.poll_deadline {
ready!(me.deadline.poll(cx));
*me.poll_deadline = false;
return Poll::Ready(Some(Err(Elapsed::new())));
}
@@ -63,3 +71,25 @@ impl<S: Stream> Stream for Timeout<S> {
self.stream.size_hint()
}
}
// ===== impl Elapsed =====
impl Elapsed {
pub(crate) fn new() -> Self {
Elapsed(())
}
}
impl fmt::Display for Elapsed {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
"deadline has elapsed".fmt(fmt)
}
}
impl std::error::Error for Elapsed {}
impl From<Elapsed> for std::io::Error {
fn from(_err: Elapsed) -> std::io::Error {
std::io::ErrorKind::TimedOut.into()
}
}
@@ -1,4 +1,4 @@
use crate::stream::{Next, Stream};
use crate::{Next, Stream};
use core::future::Future;
use core::marker::PhantomPinned;
+105
View File
@@ -0,0 +1,105 @@
use std::rc::Rc;
#[allow(dead_code)]
type BoxStream<T> = std::pin::Pin<Box<dyn tokio_stream::Stream<Item = T>>>;
#[allow(dead_code)]
fn require_send<T: Send>(_t: &T) {}
#[allow(dead_code)]
fn require_sync<T: Sync>(_t: &T) {}
#[allow(dead_code)]
fn require_unpin<T: Unpin>(_t: &T) {}
#[allow(dead_code)]
struct Invalid;
trait AmbiguousIfSend<A> {
fn some_item(&self) {}
}
impl<T: ?Sized> AmbiguousIfSend<()> for T {}
impl<T: ?Sized + Send> AmbiguousIfSend<Invalid> for T {}
trait AmbiguousIfSync<A> {
fn some_item(&self) {}
}
impl<T: ?Sized> AmbiguousIfSync<()> for T {}
impl<T: ?Sized + Sync> AmbiguousIfSync<Invalid> for T {}
trait AmbiguousIfUnpin<A> {
fn some_item(&self) {}
}
impl<T: ?Sized> AmbiguousIfUnpin<()> for T {}
impl<T: ?Sized + Unpin> AmbiguousIfUnpin<Invalid> for T {}
macro_rules! into_todo {
($typ:ty) => {{
let x: $typ = todo!();
x
}};
}
macro_rules! async_assert_fn {
($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): Send & Sync) => {
#[allow(unreachable_code)]
#[allow(unused_variables)]
const _: fn() = || {
let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* );
require_send(&f);
require_sync(&f);
};
};
($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): Send & !Sync) => {
#[allow(unreachable_code)]
#[allow(unused_variables)]
const _: fn() = || {
let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* );
require_send(&f);
AmbiguousIfSync::some_item(&f);
};
};
($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): !Send & Sync) => {
#[allow(unreachable_code)]
#[allow(unused_variables)]
const _: fn() = || {
let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* );
AmbiguousIfSend::some_item(&f);
require_sync(&f);
};
};
($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): !Send & !Sync) => {
#[allow(unreachable_code)]
#[allow(unused_variables)]
const _: fn() = || {
let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* );
AmbiguousIfSend::some_item(&f);
AmbiguousIfSync::some_item(&f);
};
};
($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): !Unpin) => {
#[allow(unreachable_code)]
#[allow(unused_variables)]
const _: fn() = || {
let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* );
AmbiguousIfUnpin::some_item(&f);
};
};
($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): Unpin) => {
#[allow(unreachable_code)]
#[allow(unused_variables)]
const _: fn() = || {
let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* );
require_unpin(&f);
};
};
}
async_assert_fn!(tokio_stream::empty<Rc<u8>>(): Send & Sync);
async_assert_fn!(tokio_stream::pending<Rc<u8>>(): Send & Sync);
async_assert_fn!(tokio_stream::iter(std::vec::IntoIter<u8>): Send & Sync);
async_assert_fn!(tokio_stream::StreamExt::next(&mut BoxStream<()>): !Unpin);
async_assert_fn!(tokio_stream::StreamExt::try_next(&mut BoxStream<Result<(), ()>>): !Unpin);
async_assert_fn!(tokio_stream::StreamExt::all(&mut BoxStream<()>, fn(())->bool): !Unpin);
async_assert_fn!(tokio_stream::StreamExt::any(&mut BoxStream<()>, fn(())->bool): !Unpin);
async_assert_fn!(tokio_stream::StreamExt::fold(&mut BoxStream<()>, (), fn((), ())->()): !Unpin);
async_assert_fn!(tokio_stream::StreamExt::collect<Vec<()>>(&mut BoxStream<()>): !Unpin);
@@ -1,7 +1,12 @@
use tokio::stream::{self, Stream, StreamExt};
use tokio::sync::mpsc;
use tokio_stream::{self as stream, Stream, StreamExt};
use tokio_test::{assert_pending, assert_ready, task};
mod support {
pub(crate) mod mpsc;
}
use support::mpsc;
#[tokio::test]
async fn basic_usage() {
let one = stream::iter(vec![1, 2, 3]);
@@ -36,8 +41,8 @@ async fn basic_usage() {
#[tokio::test]
async fn pending_first() {
let (tx1, rx1) = mpsc::unbounded_channel();
let (tx2, rx2) = mpsc::unbounded_channel();
let (tx1, rx1) = mpsc::unbounded_channel_stream();
let (tx2, rx2) = mpsc::unbounded_channel_stream();
let mut stream = task::spawn(rx1.chain(rx2));
assert_eq!(stream.size_hint(), (0, None));
@@ -74,7 +79,7 @@ async fn pending_first() {
fn size_overflow() {
struct Monster;
impl tokio::stream::Stream for Monster {
impl tokio_stream::Stream for Monster {
type Item = ();
fn poll_next(
self: std::pin::Pin<&mut Self>,
@@ -1,7 +1,12 @@
use tokio::stream::{self, StreamExt};
use tokio::sync::mpsc;
use tokio_stream::{self as stream, StreamExt};
use tokio_test::{assert_pending, assert_ready, assert_ready_err, assert_ready_ok, task};
mod support {
pub(crate) mod mpsc;
}
use support::mpsc;
#[allow(clippy::let_unit_value)]
#[tokio::test]
async fn empty_unit() {
@@ -37,7 +42,7 @@ async fn empty_result() {
#[tokio::test]
async fn collect_vec_items() {
let (tx, rx) = mpsc::unbounded_channel();
let (tx, rx) = mpsc::unbounded_channel_stream();
let mut fut = task::spawn(rx.collect::<Vec<i32>>());
assert_pending!(fut.poll());
@@ -58,7 +63,8 @@ async fn collect_vec_items() {
#[tokio::test]
async fn collect_string_items() {
let (tx, rx) = mpsc::unbounded_channel();
let (tx, rx) = mpsc::unbounded_channel_stream();
let mut fut = task::spawn(rx.collect::<String>());
assert_pending!(fut.poll());
@@ -79,7 +85,8 @@ async fn collect_string_items() {
#[tokio::test]
async fn collect_str_items() {
let (tx, rx) = mpsc::unbounded_channel();
let (tx, rx) = mpsc::unbounded_channel_stream();
let mut fut = task::spawn(rx.collect::<String>());
assert_pending!(fut.poll());
@@ -100,7 +107,8 @@ async fn collect_str_items() {
#[tokio::test]
async fn collect_results_ok() {
let (tx, rx) = mpsc::unbounded_channel();
let (tx, rx) = mpsc::unbounded_channel_stream();
let mut fut = task::spawn(rx.collect::<Result<String, &str>>());
assert_pending!(fut.poll());
@@ -121,7 +129,8 @@ async fn collect_results_ok() {
#[tokio::test]
async fn collect_results_err() {
let (tx, rx) = mpsc::unbounded_channel();
let (tx, rx) = mpsc::unbounded_channel_stream();
let mut fut = task::spawn(rx.collect::<Result<String, &str>>());
assert_pending!(fut.poll());
@@ -1,4 +1,4 @@
use tokio::stream::{self, Stream, StreamExt};
use tokio_stream::{self as stream, Stream, StreamExt};
#[tokio::test]
async fn basic_usage() {
@@ -1,4 +1,4 @@
use tokio::stream::{Stream, StreamExt};
use tokio_stream::{Stream, StreamExt};
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -1,4 +1,4 @@
use tokio::stream;
use tokio_stream as stream;
use tokio_test::task;
use std::iter;
@@ -1,8 +1,13 @@
use tokio::stream::{self, Stream, StreamExt};
use tokio::sync::mpsc;
use tokio_stream::{self as stream, Stream, StreamExt};
use tokio_test::task;
use tokio_test::{assert_pending, assert_ready};
mod support {
pub(crate) mod mpsc;
}
use support::mpsc;
#[tokio::test]
async fn merge_sync_streams() {
let mut s = stream::iter(vec![0, 2, 4, 6]).merge(stream::iter(vec![1, 3, 5]));
@@ -18,8 +23,8 @@ async fn merge_sync_streams() {
#[tokio::test]
async fn merge_async_streams() {
let (tx1, rx1) = mpsc::unbounded_channel();
let (tx2, rx2) = mpsc::unbounded_channel();
let (tx1, rx1) = mpsc::unbounded_channel_stream();
let (tx2, rx2) = mpsc::unbounded_channel_stream();
let mut rx = task::spawn(rx1.merge(rx2));
@@ -57,7 +62,7 @@ async fn merge_async_streams() {
fn size_overflow() {
struct Monster;
impl tokio::stream::Stream for Monster {
impl tokio_stream::Stream for Monster {
type Item = ();
fn poll_next(
self: std::pin::Pin<&mut Self>,
@@ -1,4 +1,4 @@
use tokio::stream::{self, Stream, StreamExt};
use tokio_stream::{self as stream, Stream, StreamExt};
#[tokio::test]
async fn basic_usage() {
@@ -1,4 +1,4 @@
use tokio::stream::{self, Stream, StreamExt};
use tokio_stream::{self as stream, Stream, StreamExt};
use tokio_test::{assert_pending, task};
#[tokio::test]
@@ -1,7 +1,12 @@
use tokio::stream::{self, pending, Stream, StreamExt, StreamMap};
use tokio::sync::mpsc;
use tokio_stream::{self as stream, pending, Stream, StreamExt, StreamMap};
use tokio_test::{assert_ok, assert_pending, assert_ready, task};
mod support {
pub(crate) mod mpsc;
}
use support::mpsc;
use std::pin::Pin;
macro_rules! assert_ready_some {
@@ -38,7 +43,8 @@ async fn empty() {
#[tokio::test]
async fn single_entry() {
let mut map = task::spawn(StreamMap::new());
let (tx, rx) = mpsc::unbounded_channel();
let (tx, rx) = mpsc::unbounded_channel_stream();
let rx = Box::pin(rx);
assert_ready_none!(map.poll_next());
@@ -76,8 +82,11 @@ async fn single_entry() {
#[tokio::test]
async fn multiple_entries() {
let mut map = task::spawn(StreamMap::new());
let (tx1, rx1) = mpsc::unbounded_channel();
let (tx2, rx2) = mpsc::unbounded_channel();
let (tx1, rx1) = mpsc::unbounded_channel_stream();
let (tx2, rx2) = mpsc::unbounded_channel_stream();
let rx1 = Box::pin(rx1);
let rx2 = Box::pin(rx2);
map.insert("foo", rx1);
map.insert("bar", rx2);
@@ -132,7 +141,9 @@ async fn multiple_entries() {
#[tokio::test]
async fn insert_remove() {
let mut map = task::spawn(StreamMap::new());
let (tx, rx) = mpsc::unbounded_channel();
let (tx, rx) = mpsc::unbounded_channel_stream();
let rx = Box::pin(rx);
assert_ready_none!(map.poll_next());
@@ -160,8 +171,11 @@ async fn insert_remove() {
#[tokio::test]
async fn replace() {
let mut map = task::spawn(StreamMap::new());
let (tx1, rx1) = mpsc::unbounded_channel();
let (tx2, rx2) = mpsc::unbounded_channel();
let (tx1, rx1) = mpsc::unbounded_channel_stream();
let (tx2, rx2) = mpsc::unbounded_channel_stream();
let rx1 = Box::pin(rx1);
let rx2 = Box::pin(rx2);
assert!(map.insert("foo", rx1).is_none());
@@ -1,7 +1,7 @@
#![cfg(feature = "full")]
use tokio::stream::{self, StreamExt};
use tokio::time::{self, sleep, Duration};
use tokio_stream::{self, StreamExt};
use tokio_test::*;
use futures::StreamExt as _;
@@ -78,7 +78,7 @@ async fn return_elapsed_errors_only_once() {
// error is returned.
assert_pending!(stream.poll_next());
//
time::advance(ms(50)).await;
time::advance(ms(51)).await;
let v = assert_ready!(stream.poll_next());
assert!(v.unwrap().is_err()); // timeout!
+15
View File
@@ -0,0 +1,15 @@
use async_stream::stream;
use tokio::sync::mpsc::{self, UnboundedSender};
use tokio_stream::Stream;
pub fn unbounded_channel_stream<T: Unpin>() -> (UnboundedSender<T>, impl Stream<Item = T>) {
let (tx, mut rx) = mpsc::unbounded_channel();
let stream = stream! {
while let Some(item) = rx.recv().await {
yield item;
}
};
(tx, stream)
}
@@ -1,8 +1,8 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]
use tokio::stream::StreamExt;
use tokio::time;
use tokio_stream::StreamExt;
use tokio_test::*;
use std::time::Duration;
+4
View File
@@ -1,3 +1,7 @@
# 0.4.0 (December 23, 2020)
- Track `tokio` 1.0 release.
# 0.3.0 (October 15, 2020)
- Track `tokio` 0.3 release.
+8 -6
View File
@@ -6,27 +6,29 @@ name = "tokio-test"
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "v0.3.x" git tag.
version = "0.3.0"
# - Create "tokio-test-0.4.x" git tag.
version = "0.4.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-test/0.3.0/tokio_test"
documentation = "https://docs.rs/tokio-test/0.4.0/tokio_test"
description = """
Testing utilities for Tokio- and futures-based code
"""
categories = ["asynchronous", "testing"]
[dependencies]
tokio = { version = "0.3.0", path = "../tokio", features = ["rt", "stream", "sync", "time", "test-util"] }
tokio = { version = "1.0.0", path = "../tokio", features = ["rt", "sync", "time", "test-util"] }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
async-stream = "0.3"
bytes = "0.5.0"
bytes = "1.0.0"
futures-core = "0.3.0"
[dev-dependencies]
tokio = { version = "0.3.0", path = "../tokio", features = ["full"] }
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
futures-util = "0.3.0"
[package.metadata.docs.rs]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2019 Tokio Contributors
Copyright (c) 2020 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+20 -9
View File
@@ -22,8 +22,9 @@ use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::sync::mpsc;
use tokio::time::{self, Duration, Instant, Sleep};
use futures_core::ready;
use futures_core::{ready, Stream};
use std::collections::VecDeque;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
@@ -63,13 +64,13 @@ enum Action {
WriteError(Option<Arc<io::Error>>),
}
#[derive(Debug)]
struct Inner {
actions: VecDeque<Action>,
waiting: Option<Instant>,
sleep: Option<Sleep>,
sleep: Option<Pin<Box<Sleep>>>,
read_wait: Option<Waker>,
rx: mpsc::UnboundedReceiver<Action>,
// rx: mpsc::UnboundedReceiver<Action>,
rx: Pin<Box<dyn Stream<Item = Action> + Send>>,
}
impl Builder {
@@ -184,7 +185,13 @@ impl Handle {
impl Inner {
fn new(actions: VecDeque<Action>) -> (Inner, Handle) {
let (tx, rx) = mpsc::unbounded_channel();
let (tx, mut rx) = mpsc::unbounded_channel();
let rx = Box::pin(async_stream::stream! {
while let Some(item) = rx.recv().await {
yield item;
}
});
let inner = Inner {
actions,
@@ -200,8 +207,6 @@ impl Inner {
}
fn poll_action(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<Action>> {
use futures_core::stream::Stream;
Pin::new(&mut self.rx).poll_next(cx)
}
@@ -365,7 +370,7 @@ impl AsyncRead for Mock {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
if let Some(rem) = self.inner.remaining_wait() {
let until = Instant::now() + rem;
self.inner.sleep = Some(time::sleep_until(until));
self.inner.sleep = Some(Box::pin(time::sleep_until(until)));
} else {
self.inner.read_wait = Some(cx.waker().clone());
return Poll::Pending;
@@ -410,7 +415,7 @@ impl AsyncWrite for Mock {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
if let Some(rem) = self.inner.remaining_wait() {
let until = Instant::now() + rem;
self.inner.sleep = Some(time::sleep_until(until));
self.inner.sleep = Some(Box::pin(time::sleep_until(until)));
} else {
panic!("unexpected WouldBlock");
}
@@ -485,3 +490,9 @@ fn is_task_ctx() -> bool {
r
}
*/
impl fmt::Debug for Inner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Inner {{...}}")
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio-test/0.3.0")]
#![doc(html_root_url = "https://docs.rs/tokio-test/0.4.0")]
#![warn(
missing_debug_implementations,
missing_docs,
+1 -1
View File
@@ -9,7 +9,7 @@ use std::pin::Pin;
use std::sync::{Arc, Condvar, Mutex};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use tokio::stream::Stream;
use tokio_stream::Stream;
/// TODO: dox
pub fn spawn<T>(task: T) -> Spawn<T> {
+19
View File
@@ -1,3 +1,22 @@
# 0.6.0 (December 23, 2020)
### Changed
- depend on `tokio` 1.0.
### Added
- rt: add constructors to `TokioContext` (#3221).
# 0.5.1 (December 3, 2020)
### Added
- io: `poll_read_buf` util fn (#2972).
- io: `poll_write_buf` util fn with vectored write support (#3156).
# 0.5.0 (October 30, 2020)
### Changed
- io: update `bytes` to 0.6 (#3071).
# 0.4.0 (October 15, 2020)
### Added
+18 -11
View File
@@ -6,14 +6,14 @@ name = "tokio-util"
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "v0.2.x" git tag.
version = "0.4.0"
# - Create "tokio-util-0.6.x" git tag.
version = "0.6.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-util/0.4.0/tokio_util"
documentation = "https://docs.rs/tokio-util/0.6.0/tokio_util"
description = """
Additional utilities for working with Tokio.
"""
@@ -24,32 +24,39 @@ categories = ["asynchronous"]
default = []
# Shorthand for enabling everything
full = ["codec", "compat", "io", "time"]
full = ["codec", "compat", "io", "time", "net", "rt"]
net = ["tokio/net"]
compat = ["futures-io",]
codec = ["tokio/stream"]
codec = []
time = ["tokio/time","slab"]
io = []
rt = ["tokio/rt"]
[dependencies]
tokio = { version = "0.3.0", path = "../tokio" }
__docs_rs = ["futures-util"]
bytes = "0.5.0"
[dependencies]
tokio = { version = "1.0.0", path = "../tokio" }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
bytes = "1.0.0"
futures-core = "0.3.0"
futures-sink = "0.3.0"
futures-io = { version = "0.3.0", optional = true }
futures-util = { version = "0.3.0", optional = true }
log = "0.4"
pin-project-lite = "0.1.4"
pin-project-lite = "0.2.0"
slab = { version = "0.4.1", optional = true } # Backs `DelayQueue`
[dev-dependencies]
tokio = { version = "0.3.0", path = "../tokio", features = ["full"] }
tokio-test = { version = "0.3.0", path = "../tokio-test" }
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
tokio-test = { version = "0.4.0", path = "../tokio-test" }
futures = "0.3.0"
futures-test = "0.3.5"
async-stream = "0.3.0"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2019 Tokio Contributors
Copyright (c) 2020 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+3 -5
View File
@@ -18,17 +18,15 @@ macro_rules! cfg_compat {
}
}
/*
macro_rules! cfg_udp {
macro_rules! cfg_net {
($($item:item)*) => {
$(
#[cfg(all(feature = "udp", feature = "codec"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "udp", feature = "codec"))))]
#[cfg(all(feature = "net", feature = "codec"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "net", feature = "codec"))))]
$item
)*
}
}
*/
macro_rules! cfg_io {
($($item:item)*) => {
+1 -1
View File
@@ -153,7 +153,7 @@ pub trait Decoder {
/// calling `split` on the [`Framed`] returned by this method, which will
/// break them into separate objects, allowing them to interact more easily.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Stream`]: tokio_stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Framed`]: crate::codec::Framed
fn framed<T: AsyncRead + AsyncWrite + Sized>(self, io: T) -> Framed<T, Self>
+6 -8
View File
@@ -2,10 +2,8 @@ use crate::codec::decoder::Decoder;
use crate::codec::encoder::Encoder;
use crate::codec::framed_impl::{FramedImpl, RWFrames, ReadFrame, WriteFrame};
use tokio::{
io::{AsyncRead, AsyncWrite},
stream::Stream,
};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_stream::Stream;
use bytes::BytesMut;
use futures_sink::Sink;
@@ -22,7 +20,7 @@ pin_project! {
/// You can create a `Framed` instance by using the [`Decoder::framed`] adapter, or
/// by using the `new` function seen below.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Stream`]: tokio_stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`Decoder::framed`]: crate::codec::Decoder::framed()
@@ -54,7 +52,7 @@ where
/// calling [`split`] on the `Framed` returned by this method, which will
/// break them into separate objects, allowing them to interact more easily.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Stream`]: tokio_stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Decode`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
@@ -88,7 +86,7 @@ where
/// calling [`split`] on the `Framed` returned by this method, which will
/// break them into separate objects, allowing them to interact more easily.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Stream`]: tokio_stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Decode`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
@@ -133,7 +131,7 @@ impl<T, U> Framed<T, U> {
/// calling [`split`] on the `Framed` returned by this method, which will
/// break them into separate objects, allowing them to interact more easily.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Stream`]: tokio_stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Decoder`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
+6 -10
View File
@@ -1,12 +1,10 @@
use crate::codec::decoder::Decoder;
use crate::codec::encoder::Encoder;
use tokio::{
io::{AsyncRead, AsyncWrite},
stream::Stream,
};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_stream::Stream;
use bytes::{Buf, BytesMut};
use bytes::BytesMut;
use futures_core::ready;
use futures_sink::Sink;
use log::trace;
@@ -150,7 +148,7 @@ where
// 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(cx, pinned.inner.as_mut(), &mut state.buffer)? {
let bytect = match poll_read_buf(pinned.inner.as_mut(), cx, &mut state.buffer)? {
Poll::Ready(ct) => ct,
Poll::Pending => return Poll::Pending,
};
@@ -189,6 +187,7 @@ where
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
use crate::util::poll_write_buf;
trace!("flushing framed transport");
let mut pinned = self.project();
@@ -196,8 +195,7 @@ where
let WriteFrame { buffer } = pinned.state.borrow_mut();
trace!("writing; remaining={}", buffer.len());
let buf = &buffer;
let n = ready!(pinned.inner.as_mut().poll_write(cx, &buf))?;
let n = ready!(poll_write_buf(pinned.inner.as_mut(), cx, buffer))?;
if n == 0 {
return Poll::Ready(Err(io::Error::new(
@@ -207,8 +205,6 @@ where
)
.into()));
}
pinned.state.borrow_mut().buffer.advance(n);
}
// Try flushing the underlying IO
+8 -2
View File
@@ -1,7 +1,8 @@
use crate::codec::framed_impl::{FramedImpl, ReadFrame};
use crate::codec::Decoder;
use tokio::{io::AsyncRead, stream::Stream};
use tokio::io::AsyncRead;
use tokio_stream::Stream;
use bytes::BytesMut;
use futures_sink::Sink;
@@ -13,7 +14,7 @@ use std::task::{Context, Poll};
pin_project! {
/// A [`Stream`] of messages decoded from an [`AsyncRead`].
///
/// [`Stream`]: tokio::stream::Stream
/// [`Stream`]: tokio_stream::Stream
/// [`AsyncRead`]: tokio::io::AsyncRead
pub struct FramedRead<T, D> {
#[pin]
@@ -100,6 +101,11 @@ impl<T, D> FramedRead<T, D> {
pub fn read_buffer(&self) -> &BytesMut {
&self.inner.state.buffer
}
/// Returns a mutable reference to the read buffer.
pub fn read_buffer_mut(&mut self) -> &mut BytesMut {
&mut self.inner.state.buffer
}
}
// This impl just defers to the underlying FramedImpl
+2 -1
View File
@@ -1,7 +1,8 @@
use crate::codec::encoder::Encoder;
use crate::codec::framed_impl::{FramedImpl, WriteFrame};
use tokio::{io::AsyncWrite, stream::Stream};
use tokio::io::AsyncWrite;
use tokio_stream::Stream;
use futures_sink::Sink;
use pin_project_lite::pin_project;
+1 -1
View File
@@ -39,7 +39,7 @@
//! Specifically, given the following:
//!
//! ```
//! use tokio::prelude::*;
//! use tokio::io::{AsyncRead, AsyncWrite};
//! use tokio_util::codec::{Framed, LengthDelimitedCodec};
//!
//! use futures::SinkExt;
+252 -8
View File
@@ -1,18 +1,262 @@
//! Adaptors from AsyncRead/AsyncWrite to Stream/Sink
//!
//! Raw I/O objects work with byte sequences, but higher-level code
//! usually wants to batch these into meaningful chunks, called
//! "frames".
//! Raw I/O objects work with byte sequences, but higher-level code usually
//! wants to batch these into meaningful chunks, called "frames".
//!
//! This module contains adapters to go from streams of bytes,
//! [`AsyncRead`] and [`AsyncWrite`], to framed streams implementing
//! [`Sink`] and [`Stream`]. Framed streams are also known as
//! transports.
//! This module contains adapters to go from streams of bytes, [`AsyncRead`] and
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
//! Framed streams are also known as transports.
//!
//! # The Decoder trait
//!
//! A [`Decoder`] is used together with [`FramedRead`] or [`Framed`] to turn an
//! [`AsyncRead`] into a [`Stream`]. The job of the decoder trait is to specify
//! how sequences of bytes are turned into a sequence of frames, and to
//! determine where the boundaries between frames are. The job of the
//! `FramedRead` is to repeatedly switch between reading more data from the IO
//! resource, and asking the decoder whether we have received enough data to
//! decode another frame of data.
//!
//! The main method on the `Decoder` trait is the [`decode`] method. This method
//! takes as argument the data that has been read so far, and when it is called,
//! it will be in one of the following situations:
//!
//! 1. The buffer contains less than a full frame.
//! 2. The buffer contains exactly a full frame.
//! 3. The buffer contains more than a full frame.
//!
//! In the first situation, the decoder should return `Ok(None)`.
//!
//! In the second situation, the decoder should clear the provided buffer and
//! return `Ok(Some(the_decoded_frame))`.
//!
//! In the third situation, the decoder should use a method such as [`split_to`]
//! or [`advance`] to modify the buffer such that the frame is removed from the
//! buffer, but any data in the buffer after that frame should still remain in
//! the buffer. The decoder should also return `Ok(Some(the_decoded_frame))` in
//! this case.
//!
//! Finally the decoder may return an error if the data is invalid in some way.
//! The decoder should _not_ return an error just because it has yet to receive
//! a full frame.
//!
//! It is guaranteed that, from one call to `decode` to another, the provided
//! buffer will contain the exact same data as before, except that if more data
//! has arrived through the IO resource, that data will have been appended to
//! the buffer. This means that reading frames from a `FramedRead` is
//! essentially equivalent to the following loop:
//!
//! ```no_run
//! use tokio::io::AsyncReadExt;
//! # // This uses async_stream to create an example that compiles.
//! # fn foo() -> impl futures_core::Stream<Item = std::io::Result<bytes::BytesMut>> { async_stream::try_stream! {
//! # use tokio_util::codec::Decoder;
//! # let mut decoder = tokio_util::codec::BytesCodec::new();
//! # let io_resource = &mut &[0u8, 1, 2, 3][..];
//!
//! let mut buf = bytes::BytesMut::new();
//! loop {
//! // The read_buf call will append to buf rather than overwrite existing data.
//! let len = io_resource.read_buf(&mut buf).await?;
//!
//! if len == 0 {
//! while let Some(frame) = decoder.decode_eof(&mut buf)? {
//! yield frame;
//! }
//! break;
//! }
//!
//! while let Some(frame) = decoder.decode(&mut buf)? {
//! yield frame;
//! }
//! }
//! # }}
//! ```
//! The example above uses `yield` whenever the `Stream` produces an item.
//!
//! ## Example decoder
//!
//! As an example, consider a protocol that can be used to send strings where
//! each frame is a four byte integer that contains the length of the frame,
//! followed by that many bytes of string data. The decoder fails with an error
//! if the string data is not valid utf-8 or too long.
//!
//! Such a decoder can be written like this:
//! ```
//! use tokio_util::codec::Decoder;
//! use bytes::{BytesMut, Buf};
//!
//! struct MyStringDecoder {}
//!
//! const MAX: usize = 8 * 1024 * 1024;
//!
//! impl Decoder for MyStringDecoder {
//! type Item = String;
//! type Error = std::io::Error;
//!
//! fn decode(
//! &mut self,
//! src: &mut BytesMut
//! ) -> Result<Option<Self::Item>, Self::Error> {
//! if src.len() < 4 {
//! // Not enough data to read length marker.
//! return Ok(None);
//! }
//!
//! // Read length marker.
//! let mut length_bytes = [0u8; 4];
//! length_bytes.copy_from_slice(&src[..4]);
//! let length = u32::from_le_bytes(length_bytes) as usize;
//!
//! // Check that the length is not too large to avoid a denial of
//! // service attack where the server runs out of memory.
//! if length > MAX {
//! return Err(std::io::Error::new(
//! std::io::ErrorKind::InvalidData,
//! format!("Frame of length {} is too large.", length)
//! ));
//! }
//!
//! if src.len() < 4 + length {
//! // The full string has not yet arrived.
//! //
//! // We reserve more space in the buffer. This is not strictly
//! // necessary, but is a good idea performance-wise.
//! src.reserve(4 + length - src.len());
//!
//! // We inform the Framed that we need more bytes to form the next
//! // frame.
//! return Ok(None);
//! }
//!
//! // Use advance to modify src such that it no longer contains
//! // this frame.
//! let data = src[4..4 + length].to_vec();
//! src.advance(4 + length);
//!
//! // Convert the data to a string, or fail if it is not valid utf-8.
//! match String::from_utf8(data) {
//! Ok(string) => Ok(Some(string)),
//! Err(utf8_error) => {
//! Err(std::io::Error::new(
//! std::io::ErrorKind::InvalidData,
//! utf8_error.utf8_error(),
//! ))
//! },
//! }
//! }
//! }
//! ```
//!
//! # The Encoder trait
//!
//! An [`Encoder`] is used together with [`FramedWrite`] or [`Framed`] to turn
//! an [`AsyncWrite`] into a [`Sink`]. The job of the encoder trait is to
//! specify how frames are turned into a sequences of bytes. The job of the
//! `FramedWrite` is to take the resulting sequence of bytes and write it to the
//! IO resource.
//!
//! The main method on the `Encoder` trait is the [`encode`] method. This method
//! takes an item that is being written, and a buffer to write the item to. The
//! buffer may already contain data, and in this case, the encoder should append
//! the new frame the to buffer rather than overwrite the existing data.
//!
//! It is guaranteed that, from one call to `encode` to another, the provided
//! buffer will contain the exact same data as before, except that some of the
//! data may have been removed from the front of the buffer. Writing to a
//! `FramedWrite` is essentially equivalent to the following loop:
//!
//! ```no_run
//! use tokio::io::AsyncWriteExt;
//! use bytes::Buf; // for advance
//! # use tokio_util::codec::Encoder;
//! # async fn next_frame() -> bytes::Bytes { bytes::Bytes::new() }
//! # async fn no_more_frames() { }
//! # #[tokio::main] async fn main() -> std::io::Result<()> {
//! # let mut io_resource = tokio::io::sink();
//! # let mut encoder = tokio_util::codec::BytesCodec::new();
//!
//! const MAX: usize = 8192;
//!
//! let mut buf = bytes::BytesMut::new();
//! loop {
//! tokio::select! {
//! num_written = io_resource.write(&buf), if !buf.is_empty() => {
//! buf.advance(num_written?);
//! },
//! frame = next_frame(), if buf.len() < MAX => {
//! encoder.encode(frame, &mut buf)?;
//! },
//! _ = no_more_frames() => {
//! io_resource.write_all(&buf).await?;
//! io_resource.shutdown().await?;
//! return Ok(());
//! },
//! }
//! }
//! # }
//! ```
//! Here the `next_frame` method corresponds to any frames you write to the
//! `FramedWrite`. The `no_more_frames` method corresponds to closing the
//! `FramedWrite` with [`SinkExt::close`].
//!
//! ## Example encoder
//!
//! As an example, consider a protocol that can be used to send strings where
//! each frame is a four byte integer that contains the length of the frame,
//! followed by that many bytes of string data. The encoder will fail if the
//! string is too long.
//!
//! Such an encoder can be written like this:
//! ```
//! use tokio_util::codec::Encoder;
//! use bytes::BytesMut;
//!
//! struct MyStringEncoder {}
//!
//! const MAX: usize = 8 * 1024 * 1024;
//!
//! impl Encoder<String> for MyStringEncoder {
//! type Error = std::io::Error;
//!
//! fn encode(&mut self, item: String, dst: &mut BytesMut) -> Result<(), Self::Error> {
//! // Don't send a string if it is longer than the other end will
//! // accept.
//! if item.len() > MAX {
//! return Err(std::io::Error::new(
//! std::io::ErrorKind::InvalidData,
//! format!("Frame of length {} is too large.", item.len())
//! ));
//! }
//!
//! // Convert the length into a byte array.
//! // The cast to u32 cannot overflow due to the length check above.
//! let len_slice = u32::to_le_bytes(item.len() as u32);
//!
//! // Reserve space in the buffer.
//! dst.reserve(4 + item.len());
//!
//! // Write the length and string to the buffer.
//! dst.extend_from_slice(&len_slice);
//! dst.extend_from_slice(item.as_bytes());
//! Ok(())
//! }
//! }
//! ```
//!
//! [`AsyncRead`]: tokio::io::AsyncRead
//! [`AsyncWrite`]: tokio::io::AsyncWrite
//! [`Stream`]: tokio::stream::Stream
//! [`Stream`]: tokio_stream::Stream
//! [`Sink`]: futures_sink::Sink
//! [`SinkExt::close`]: https://docs.rs/futures/0.3/futures/sink/trait.SinkExt.html#method.close
//! [`FramedRead`]: struct@crate::codec::FramedRead
//! [`FramedWrite`]: struct@crate::codec::FramedWrite
//! [`Framed`]: struct@crate::codec::Framed
//! [`Decoder`]: trait@crate::codec::Decoder
//! [`decode`]: fn@crate::codec::Decoder::decode
//! [`encode`]: fn@crate::codec::Encoder::encode
//! [`split_to`]: fn@bytes::BytesMut::split_to
//! [`advance`]: fn@bytes::Buf::advance
mod bytes_codec;
pub use self::bytes_codec::BytesCodec;
+5 -5
View File
@@ -20,7 +20,7 @@ pin_project! {
/// `futures_io::AsyncRead` to implement `tokio::io::AsyncRead`.
pub trait FuturesAsyncReadCompatExt: futures_io::AsyncRead {
/// Wraps `self` with a compatibility layer that implements
/// `tokio_io::AsyncWrite`.
/// `tokio_io::AsyncRead`.
fn compat(self) -> Compat<Self>
where
Self: Sized,
@@ -48,7 +48,7 @@ impl<T: futures_io::AsyncWrite> FuturesAsyncWriteCompatExt for T {}
/// Extension trait that allows converting a type implementing
/// `tokio::io::AsyncRead` to implement `futures_io::AsyncRead`.
pub trait Tokio02AsyncReadCompatExt: tokio::io::AsyncRead {
pub trait TokioAsyncReadCompatExt: tokio::io::AsyncRead {
/// Wraps `self` with a compatibility layer that implements
/// `futures_io::AsyncRead`.
fn compat(self) -> Compat<Self>
@@ -59,11 +59,11 @@ pub trait Tokio02AsyncReadCompatExt: tokio::io::AsyncRead {
}
}
impl<T: tokio::io::AsyncRead> Tokio02AsyncReadCompatExt for T {}
impl<T: tokio::io::AsyncRead> TokioAsyncReadCompatExt for T {}
/// Extension trait that allows converting a type implementing
/// `tokio::io::AsyncWrite` to implement `futures_io::AsyncWrite`.
pub trait Tokio02AsyncWriteCompatExt: tokio::io::AsyncWrite {
pub trait TokioAsyncWriteCompatExt: tokio::io::AsyncWrite {
/// Wraps `self` with a compatibility layer that implements
/// `futures_io::AsyncWrite`.
fn compat_write(self) -> Compat<Self>
@@ -74,7 +74,7 @@ pub trait Tokio02AsyncWriteCompatExt: tokio::io::AsyncWrite {
}
}
impl<T: tokio::io::AsyncWrite> Tokio02AsyncWriteCompatExt for T {}
impl<T: tokio::io::AsyncWrite> TokioAsyncWriteCompatExt for T {}
// === impl Compat ===
+133 -21
View File
@@ -12,21 +12,123 @@ use std::{
pin::Pin,
task::{Context, Poll},
};
use tokio::runtime::Runtime;
use tokio::runtime::{Handle, Runtime};
pin_project! {
/// `TokioContext` allows connecting a custom executor with the tokio runtime.
/// `TokioContext` allows running futures that must be inside Tokio's
/// context on a non-Tokio runtime.
///
/// It contains a `Handle` to the runtime. A handle to the runtime can be
/// obtain by calling the `Runtime::handle()` method.
pub struct TokioContext<'a, F> {
/// It contains a [`Handle`] to the runtime. A handle to the runtime can be
/// obtain by calling the [`Runtime::handle()`] method.
///
/// Note that the `TokioContext` wrapper only works if the `Runtime` it is
/// connected to has not yet been destroyed. You must keep the `Runtime`
/// alive until the future has finished executing.
///
/// **Warning:** If `TokioContext` is used together with a [current thread]
/// runtime, that runtime must be inside a call to `block_on` for the
/// wrapped future to work. For this reason, it is recommended to use a
/// [multi thread] runtime, even if you configure it to only spawn one
/// worker thread.
///
/// # Examples
///
/// This example creates two runtimes, but only [enables time] on one of
/// them. It then uses the context of the runtime with the timer enabled to
/// execute a [`sleep`] future on the runtime with timing disabled.
/// ```
/// use tokio::time::{sleep, Duration};
/// use tokio_util::context::RuntimeExt;
///
/// // This runtime has timers enabled.
/// let rt = tokio::runtime::Builder::new_multi_thread()
/// .enable_all()
/// .build()
/// .unwrap();
///
/// // This runtime has timers disabled.
/// let rt2 = tokio::runtime::Builder::new_multi_thread()
/// .build()
/// .unwrap();
///
/// // Wrap the sleep future in the context of rt.
/// let fut = rt.wrap(async { sleep(Duration::from_millis(2)).await });
///
/// // Execute the future on rt2.
/// rt2.block_on(fut);
/// ```
///
/// [`Handle`]: struct@tokio::runtime::Handle
/// [`Runtime::handle()`]: fn@tokio::runtime::Runtime::handle
/// [`RuntimeExt`]: trait@crate::context::RuntimeExt
/// [`new_static`]: fn@Self::new_static
/// [`sleep`]: fn@tokio::time::sleep
/// [current thread]: fn@tokio::runtime::Builder::new_current_thread
/// [enables time]: fn@tokio::runtime::Builder::enable_time
/// [multi thread]: fn@tokio::runtime::Builder::new_multi_thread
pub struct TokioContext<F> {
#[pin]
inner: F,
handle: &'a Runtime,
handle: Handle,
}
}
impl<F: Future> Future for TokioContext<'_, F> {
impl<F> TokioContext<F> {
/// Associate the provided future with the context of the runtime behind
/// the provided `Handle`.
///
/// This constructor uses a `'static` lifetime to opt-out of checking that
/// the runtime still exists.
///
/// # Examples
///
/// This is the same as the example above, but uses the `new` constructor
/// rather than [`RuntimeExt::wrap`].
///
/// [`RuntimeExt::wrap`]: fn@RuntimeExt::wrap
///
/// ```
/// use tokio::time::{sleep, Duration};
/// use tokio_util::context::TokioContext;
///
/// // This runtime has timers enabled.
/// let rt = tokio::runtime::Builder::new_multi_thread()
/// .enable_all()
/// .build()
/// .unwrap();
///
/// // This runtime has timers disabled.
/// let rt2 = tokio::runtime::Builder::new_multi_thread()
/// .build()
/// .unwrap();
///
/// let fut = TokioContext::new(
/// async { sleep(Duration::from_millis(2)).await },
/// rt.handle().clone(),
/// );
///
/// // Execute the future on rt2.
/// rt2.block_on(fut);
/// ```
pub fn new(future: F, handle: Handle) -> TokioContext<F> {
TokioContext {
inner: future,
handle,
}
}
/// Obtain a reference to the handle inside this `TokioContext`.
pub fn handle(&self) -> &Handle {
&self.handle
}
/// Remove the association between the Tokio runtime and the wrapped future.
pub fn into_inner(self) -> F {
self.inner
}
}
impl<F: Future> Future for TokioContext<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
@@ -39,40 +141,50 @@ impl<F: Future> Future for TokioContext<'_, F> {
}
}
/// Trait extension that simplifies bundling a `Handle` with a `Future`.
/// Extension trait that simplifies bundling a `Handle` with a `Future`.
pub trait RuntimeExt {
/// Convenience method that takes a Future and returns a `TokioContext`.
/// Create a [`TokioContext`] that wraps the provided future and runs it in
/// this runtime's context.
///
/// # Example: calling Tokio Runtime from a custom ThreadPool
/// # Examples
///
/// ```no_run
/// use tokio_util::context::RuntimeExt;
/// This example creates two runtimes, but only [enables time] on one of
/// them. It then uses the context of the runtime with the timer enabled to
/// execute a [`sleep`] future on the runtime with timing disabled.
///
/// ```
/// use tokio::time::{sleep, Duration};
/// use tokio_util::context::RuntimeExt;
///
/// // This runtime has timers enabled.
/// let rt = tokio::runtime::Builder::new_multi_thread()
/// .enable_all()
/// .build()
/// .unwrap();
///
/// // This runtime has timers disabled.
/// let rt2 = tokio::runtime::Builder::new_multi_thread()
/// .build()
/// .unwrap();
///
/// let fut = sleep(Duration::from_millis(2));
/// // Wrap the sleep future in the context of rt.
/// let fut = rt.wrap(async { sleep(Duration::from_millis(2)).await });
///
/// rt.block_on(
/// rt2
/// .wrap(async { sleep(Duration::from_millis(2)).await }),
/// );
///```
fn wrap<F: Future>(&self, fut: F) -> TokioContext<'_, F>;
/// // Execute the future on rt2.
/// rt2.block_on(fut);
/// ```
///
/// [`TokioContext`]: struct@crate::context::TokioContext
/// [`sleep`]: fn@tokio::time::sleep
/// [enables time]: fn@tokio::runtime::Builder::enable_time
fn wrap<F: Future>(&self, fut: F) -> TokioContext<F>;
}
impl RuntimeExt for Runtime {
fn wrap<F: Future>(&self, fut: F) -> TokioContext<'_, F> {
fn wrap<F: Future>(&self, fut: F) -> TokioContext<F> {
TokioContext {
inner: fut,
handle: self,
handle: self.handle().clone(),
}
}
}
+2 -4
View File
@@ -167,10 +167,8 @@ where
#[cfg(test)]
mod tests {
use super::*;
use tokio::{
io::{repeat, AsyncReadExt, Repeat},
stream::{once, Once, StreamExt},
};
use tokio::io::{repeat, AsyncReadExt, Repeat};
use tokio_stream::{once, Once, StreamExt};
#[tokio::test]
async fn either_is_stream() {
+1 -2
View File
@@ -6,12 +6,11 @@
//! [`Body`]: https://docs.rs/hyper/0.13/hyper/struct.Body.html
//! [`AsyncRead`]: tokio::io::AsyncRead
mod poll_read_buf;
mod read_buf;
mod reader_stream;
mod stream_reader;
pub use self::poll_read_buf::poll_read_buf;
pub use self::read_buf::read_buf;
pub use self::reader_stream::ReaderStream;
pub use self::stream_reader::StreamReader;
pub use crate::util::{poll_read_buf, poll_write_buf};
-90
View File
@@ -1,90 +0,0 @@
use bytes::BufMut;
use futures_core::ready;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, ReadBuf};
/// Try to read data from an `AsyncRead` into an implementer of the [`Buf`] trait.
///
/// [`Buf`]: bytes::Buf
///
/// # Example
///
/// ```
/// use bytes::{Bytes, BytesMut};
/// use tokio::stream;
/// use tokio::io::Result;
/// use tokio_util::io::{StreamReader, poll_read_buf};
/// use futures::future::poll_fn;
/// use std::pin::Pin;
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
///
/// // Create a reader from an iterator. This particular reader will always be
/// // ready.
/// let mut read = StreamReader::new(stream::iter(vec![Result::Ok(Bytes::from_static(&[0, 1, 2, 3]))]));
///
/// let mut buf = BytesMut::new();
/// let mut reads = 0;
///
/// loop {
/// reads += 1;
/// let n = poll_fn(|cx| poll_read_buf(Pin::new(&mut read), cx, &mut buf)).await?;
///
/// if n == 0 {
/// break;
/// }
/// }
///
/// // one or more reads might be necessary.
/// assert!(reads >= 1);
/// assert_eq!(&buf[..], &[0, 1, 2, 3]);
/// # Ok(())
/// # }
/// ```
pub fn poll_read_buf<R, B>(
read: Pin<&mut R>,
cx: &mut Context<'_>,
buf: &mut B,
) -> Poll<io::Result<usize>>
where
R: AsyncRead,
B: BufMut,
{
if !buf.has_remaining_mut() {
return Poll::Ready(Ok(0));
}
let n = {
let mut buf = ReadBuf::uninit(buf.bytes_mut());
let before = buf.filled().as_ptr();
ready!(read.poll_read(cx, &mut buf)?);
// This prevents a malicious read implementation from swapping out the
// buffer being read, which would allow `filled` to be advanced without
// actually initializing the provided buffer.
//
// We avoid this by asserting that the `ReadBuf` instance wraps the same
// memory address both before and after the poll. Which will panic in
// case its swapped.
//
// See https://github.com/tokio-rs/tokio/issues/2827 for more info.
assert! {
std::ptr::eq(before, buf.filled().as_ptr()),
"Read buffer must not be changed during a read poll. \
See https://github.com/tokio-rs/tokio/issues/2827 for more info."
};
buf.filled().len()
};
// Safety: This is guaranteed to be the number of initialized (and read)
// bytes due to the invariants provided by `ReadBuf::filled`.
unsafe {
buf.advance_mut(n);
}
Poll::Ready(Ok(n))
}
+4 -4
View File
@@ -5,15 +5,15 @@ use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::AsyncRead;
/// Read data from an `AsyncRead` into an implementer of the [`Buf`] trait.
/// Read data from an `AsyncRead` into an implementer of the [`BufMut`] trait.
///
/// [`Buf`]: bytes::Buf
/// [`BufMut`]: bytes::BufMut
///
/// # Example
///
/// ```
/// use bytes::{Bytes, BytesMut};
/// use tokio::stream;
/// use tokio_stream as stream;
/// use tokio::io::Result;
/// use tokio_util::io::{StreamReader, read_buf};
/// # #[tokio::main]
@@ -59,7 +59,7 @@ where
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = &mut *self;
super::poll_read_buf(Pin::new(this.0), cx, this.1)
crate::util::poll_read_buf(Pin::new(this.0), cx, this.1)
}
}
}
+4 -4
View File
@@ -18,7 +18,7 @@ pin_project! {
/// ```
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// use tokio::stream::StreamExt;
/// use tokio_stream::StreamExt;
/// use tokio_util::io::ReaderStream;
///
/// // Create a stream of data.
@@ -40,7 +40,7 @@ pin_project! {
///
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`StreamReader`]: crate::io::StreamReader
/// [`Stream`]: tokio::stream::Stream
/// [`Stream`]: tokio_stream::Stream
#[derive(Debug)]
pub struct ReaderStream<R> {
// Reader itself.
@@ -58,7 +58,7 @@ impl<R: AsyncRead> ReaderStream<R> {
/// `Result<Bytes, std::io::Error>`.
///
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`Stream`]: tokio::stream::Stream
/// [`Stream`]: tokio_stream::Stream
pub fn new(reader: R) -> Self {
ReaderStream {
reader: Some(reader),
@@ -83,7 +83,7 @@ impl<R: AsyncRead> Stream for ReaderStream<R> {
this.buf.reserve(CAPACITY);
}
match poll_read_buf(cx, reader, &mut this.buf) {
match poll_read_buf(reader, cx, &mut this.buf) {
Poll::Pending => Poll::Pending,
Poll::Ready(Err(err)) => {
self.project().reader.set(None);
+3 -3
View File
@@ -21,7 +21,7 @@ pin_project! {
/// # async fn main() -> std::io::Result<()> {
///
/// // Create a stream from an iterator.
/// let stream = tokio::stream::iter(vec![
/// let stream = tokio_stream::iter(vec![
/// Result::Ok(Bytes::from_static(&[0, 1, 2, 3])),
/// Result::Ok(Bytes::from_static(&[4, 5, 6, 7])),
/// Result::Ok(Bytes::from_static(&[8, 9, 10, 11])),
@@ -51,7 +51,7 @@ pin_project! {
/// ```
///
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`Stream`]: tokio::stream::Stream
/// [`Stream`]: tokio_stream::Stream
/// [`ReaderStream`]: crate::io::ReaderStream
#[derive(Debug)]
pub struct StreamReader<S, B> {
@@ -131,7 +131,7 @@ where
loop {
if self.as_mut().has_chunk() {
// This unwrap is very sad, but it can't be avoided.
let buf = self.project().chunk.as_ref().unwrap().bytes();
let buf = self.project().chunk.as_ref().unwrap().chunk();
return Poll::Ready(Ok(buf));
} else {
match self.as_mut().project().inner.poll_next(cx) {
+126 -19
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio-util/0.4.0")]
#![doc(html_root_url = "https://docs.rs/tokio-util/0.6.0")]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -30,14 +30,9 @@ cfg_codec! {
pub mod codec;
}
/*
Disabled due to removal of poll_ functions on UdpSocket.
See https://github.com/tokio-rs/tokio/issues/2830
cfg_udp! {
cfg_net! {
pub mod udp;
}
*/
cfg_compat! {
pub mod compat;
@@ -60,34 +55,146 @@ pub mod time;
#[cfg(any(feature = "io", feature = "codec"))]
mod util {
use tokio::io::{AsyncRead, ReadBuf};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use bytes::BufMut;
use bytes::{Buf, BufMut};
use futures_core::ready;
use std::io;
use std::io::{self, IoSlice};
use std::mem::MaybeUninit;
use std::pin::Pin;
use std::task::{Context, Poll};
pub(crate) fn poll_read_buf<T: AsyncRead>(
cx: &mut Context<'_>,
/// Try to read data from an `AsyncRead` into an implementer of the [`BufMut`] trait.
///
/// [`BufMut`]: bytes::Buf
///
/// # Example
///
/// ```
/// use bytes::{Bytes, BytesMut};
/// use tokio_stream as stream;
/// use tokio::io::Result;
/// use tokio_util::io::{StreamReader, poll_read_buf};
/// use futures::future::poll_fn;
/// use std::pin::Pin;
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
///
/// // Create a reader from an iterator. This particular reader will always be
/// // ready.
/// let mut read = StreamReader::new(stream::iter(vec![Result::Ok(Bytes::from_static(&[0, 1, 2, 3]))]));
///
/// let mut buf = BytesMut::new();
/// let mut reads = 0;
///
/// loop {
/// reads += 1;
/// let n = poll_fn(|cx| poll_read_buf(Pin::new(&mut read), cx, &mut buf)).await?;
///
/// if n == 0 {
/// break;
/// }
/// }
///
/// // one or more reads might be necessary.
/// assert!(reads >= 1);
/// assert_eq!(&buf[..], &[0, 1, 2, 3]);
/// # Ok(())
/// # }
/// ```
#[cfg_attr(not(feature = "io"), allow(unreachable_pub))]
pub fn poll_read_buf<T: AsyncRead, B: BufMut>(
io: Pin<&mut T>,
buf: &mut impl BufMut,
cx: &mut Context<'_>,
buf: &mut B,
) -> Poll<io::Result<usize>> {
if !buf.has_remaining_mut() {
return Poll::Ready(Ok(0));
}
let orig = buf.bytes_mut().as_ptr() as *const u8;
let mut b = ReadBuf::uninit(buf.bytes_mut());
let n = {
let dst = buf.chunk_mut();
let dst = unsafe { &mut *(dst as *mut _ as *mut [MaybeUninit<u8>]) };
let mut buf = ReadBuf::uninit(dst);
let ptr = buf.filled().as_ptr();
ready!(io.poll_read(cx, &mut buf)?);
ready!(io.poll_read(cx, &mut b))?;
let n = b.filled().len();
// Ensure the pointer does not change from under us
assert_eq!(ptr, buf.filled().as_ptr());
buf.filled().len()
};
// Safety: we can assume `n` bytes were read, since they are in`filled`.
assert_eq!(orig, b.filled().as_ptr());
// Safety: This is guaranteed to be the number of initialized (and read)
// bytes due to the invariants provided by `ReadBuf::filled`.
unsafe {
buf.advance_mut(n);
}
Poll::Ready(Ok(n))
}
/// Try to write data from an implementer of the [`Buf`] trait to an
/// [`AsyncWrite`], advancing the buffer's internal cursor.
///
/// This function will use [vectored writes] when the [`AsyncWrite`] supports
/// vectored writes.
///
/// # Examples
///
/// [`File`] implements [`AsyncWrite`] and [`Cursor<&[u8]>`] implements
/// [`Buf`]:
///
/// ```no_run
/// use tokio_util::io::poll_write_buf;
/// use tokio::io;
/// use tokio::fs::File;
///
/// use bytes::Buf;
/// use std::io::Cursor;
/// use std::pin::Pin;
/// use futures::future::poll_fn;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut file = File::create("foo.txt").await?;
/// let mut buf = Cursor::new(b"data to write");
///
/// // Loop until the entire contents of the buffer are written to
/// // the file.
/// while buf.has_remaining() {
/// poll_fn(|cx| poll_write_buf(Pin::new(&mut file), cx, &mut buf)).await?;
/// }
///
/// Ok(())
/// }
/// ```
///
/// [`Buf`]: bytes::Buf
/// [`AsyncWrite`]: tokio::io::AsyncWrite
/// [`File`]: tokio::fs::File
/// [vectored writes]: tokio::io::AsyncWrite::poll_write_vectored
#[cfg_attr(not(feature = "io"), allow(unreachable_pub))]
pub fn poll_write_buf<T: AsyncWrite, B: Buf>(
io: Pin<&mut T>,
cx: &mut Context<'_>,
buf: &mut B,
) -> Poll<io::Result<usize>> {
const MAX_BUFS: usize = 64;
if !buf.has_remaining() {
return Poll::Ready(Ok(0));
}
let n = if io.is_write_vectored() {
let mut slices = [IoSlice::new(&[]); MAX_BUFS];
let cnt = buf.chunks_vectored(&mut slices);
ready!(io.poll_write_vectored(cx, &slices[..cnt]))?
} else {
ready!(io.poll_write(cx, buf.chunk()))?
};
buf.advance(n);
Poll::Ready(Ok(n))
}
}
+26 -7
View File
@@ -14,7 +14,7 @@ use std::cmp;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{self, Poll};
use std::task::{self, Poll, Waker};
/// A queue of delayed elements.
///
@@ -138,13 +138,18 @@ pub struct DelayQueue<T> {
expired: Stack<T>,
/// Delay expiring when the *first* item in the queue expires
delay: Option<Sleep>,
delay: Option<Pin<Box<Sleep>>>,
/// Wheel polling state
wheel_now: u64,
/// Instant at which the timer starts
start: Instant,
/// Waker that is invoked when we potentially need to reset the timer.
/// Because we lazily create the timer when the first entry is created, we
/// need to awaken any poller that polled us before that point.
waker: Option<Waker>,
}
/// An entry in `DelayQueue` that has expired and removed.
@@ -253,6 +258,7 @@ impl<T> DelayQueue<T> {
delay: None,
wheel_now: 0,
start: Instant::now(),
waker: None,
}
}
@@ -330,11 +336,15 @@ impl<T> DelayQueue<T> {
};
if should_set_delay {
if let Some(waker) = self.waker.take() {
waker.wake();
}
let delay_time = self.start + Duration::from_millis(when);
if let Some(ref mut delay) = &mut self.delay {
delay.reset(delay_time);
delay.as_mut().reset(delay_time);
} else {
self.delay = Some(sleep_until(delay_time));
self.delay = Some(Box::pin(sleep_until(delay_time)));
}
}
@@ -348,6 +358,15 @@ impl<T> DelayQueue<T> {
&mut self,
cx: &mut task::Context<'_>,
) -> Poll<Option<Result<Expired<T>, Error>>> {
if !self
.waker
.as_ref()
.map(|w| w.will_wake(cx.waker()))
.unwrap_or(false)
{
self.waker = Some(cx.waker().clone());
}
let item = ready!(self.poll_idx(cx));
Poll::Ready(item.map(|result| {
result.map(|idx| {
@@ -533,7 +552,8 @@ impl<T> DelayQueue<T> {
let next_deadline = self.next_deadline();
if let (Some(ref mut delay), Some(deadline)) = (&mut self.delay, next_deadline) {
delay.reset(deadline);
// This should awaken us if necessary (ie, if already expired)
delay.as_mut().reset(deadline);
}
}
@@ -739,7 +759,7 @@ impl<T> DelayQueue<T> {
// We poll the wheel to get the next value out before finding the next deadline.
let wheel_idx = self.wheel.poll(self.wheel_now, &mut self.slab);
self.delay = self.next_deadline().map(sleep_until);
self.delay = self.next_deadline().map(|when| Box::pin(sleep_until(when)));
if let Some(idx) = wheel_idx {
return Poll::Ready(Some(Ok(idx)));
@@ -771,7 +791,6 @@ impl<T> Default for DelayQueue<T> {
}
}
#[cfg(feature = "stream")]
impl<T> futures_core::Stream for DelayQueue<T> {
// DelayQueue seems much more specific, where a user may care that it
// has reached capacity, so return those errors instead of panicking.
+51 -18
View File
@@ -1,17 +1,17 @@
use crate::codec::{Decoder, Encoder};
use tokio::{net::UdpSocket, stream::Stream};
use tokio::{io::ReadBuf, net::UdpSocket};
use tokio_stream::Stream;
use bytes::{BufMut, BytesMut};
use futures_core::ready;
use futures_sink::Sink;
use std::io;
use std::mem::MaybeUninit;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{io, mem::MaybeUninit};
/// A unified `Stream` and `Sink` interface to an underlying `UdpSocket`, using
/// A unified [`Stream`] and [`Sink`] interface to an underlying `UdpSocket`, using
/// the `Encoder` and `Decoder` traits to encode and decode frames.
///
/// Raw UDP sockets work with datagrams, but higher-level code usually wants to
@@ -20,13 +20,17 @@ use std::task::{Context, Poll};
/// handle encoding and decoding of messages frames. Note that the incoming and
/// outgoing frame types may be distinct.
///
/// This function returns a *single* object that is both `Stream` and `Sink`;
/// This function returns a *single* object that is both [`Stream`] and [`Sink`];
/// grouping this into a single object is often useful for layering things which
/// require both read and write access to the underlying object.
///
/// If you want to work more directly with the streams and sink, consider
/// calling `split` on the `UdpFramed` returned by this method, which will break
/// calling [`split`] on the `UdpFramed` returned by this method, which will break
/// them into separate objects, allowing them to interact more easily.
///
/// [`Stream`]: tokio_stream::Stream
/// [`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")))]
#[derive(Debug)]
@@ -41,6 +45,9 @@ pub struct UdpFramed<C> {
current_addr: Option<SocketAddr>,
}
const INITIAL_RD_CAPACITY: usize = 64 * 1024;
const INITIAL_WR_CAPACITY: usize = 8 * 1024;
impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
type Item = Result<(C::Item, SocketAddr), C::Error>;
@@ -50,7 +57,7 @@ impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
pin.rd.reserve(INITIAL_RD_CAPACITY);
loop {
// Are there are still bytes left in the read buffer to decode?
// Are there still bytes left in the read buffer to decode?
if pin.is_readable {
if let Some(frame) = pin.codec.decode_eof(&mut pin.rd)? {
let current_addr = pin
@@ -69,13 +76,14 @@ impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
let addr = unsafe {
// Convert `&mut [MaybeUnit<u8>]` to `&mut [u8]` because we will be
// writing to it via `poll_recv_from` and therefore initializing the memory.
let buf: &mut [u8] =
&mut *(pin.rd.bytes_mut() as *mut [MaybeUninit<u8>] as *mut [u8]);
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::new(&mut pin.socket).poll_recv_from(cx, buf));
let (n, addr) = res?;
pin.rd.advance_mut(n);
assert_eq!(ptr, read.filled().as_ptr());
let addr = res?;
pin.rd.advance_mut(read.filled().len());
addr
};
@@ -123,7 +131,7 @@ impl<I, C: Encoder<I> + Unpin> Sink<(I, SocketAddr)> for UdpFramed<C> {
..
} = *self;
let n = ready!(socket.poll_send_to(cx, &wr, &out_addr))?;
let n = ready!(socket.poll_send_to(cx, &wr, *out_addr))?;
let wrote_all = n == self.wr.len();
self.wr.clear();
@@ -148,15 +156,12 @@ impl<I, C: Encoder<I> + Unpin> Sink<(I, SocketAddr)> for UdpFramed<C> {
}
}
const INITIAL_RD_CAPACITY: usize = 64 * 1024;
const INITIAL_WR_CAPACITY: usize = 8 * 1024;
impl<C> UdpFramed<C> {
/// 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> {
UdpFramed {
Self {
socket,
codec,
out_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0)),
@@ -195,4 +200,32 @@ impl<C> UdpFramed<C> {
pub fn into_inner(self) -> UdpSocket {
self.socket
}
/// Returns a reference to the underlying codec wrapped by
/// `Framed`.
///
/// Note that care should be taken to not tamper with the underlying codec
/// as it may corrupt the stream of frames otherwise being worked with.
pub fn codec(&self) -> &C {
&self.codec
}
/// Returns a mutable reference to the underlying codec wrapped by
/// `UdpFramed`.
///
/// Note that care should be taken to not tamper with the underlying codec
/// as it may corrupt the stream of frames otherwise being worked with.
pub fn codec_mut(&mut self) -> &mut C {
&mut self.codec
}
/// Returns a reference to the read buffer.
pub fn read_buffer(&self) -> &BytesMut {
&self.rd
}
/// Returns a mutable reference to the read buffer.
pub fn read_buffer_mut(&mut self) -> &mut BytesMut {
&mut self.rd
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
//! UDP framing
mod frame;
pub use self::frame::UdpFramed;
pub use frame::UdpFramed;
+2 -2
View File
@@ -1,6 +1,6 @@
#![warn(rust_2018_idioms)]
use tokio::{prelude::*, stream::StreamExt};
use tokio_stream::StreamExt;
use tokio_test::assert_ok;
use tokio_util::codec::{Decoder, Encoder, Framed, FramedParts};
@@ -51,7 +51,7 @@ impl Read for DontReadIntoThis {
}
}
impl AsyncRead for DontReadIntoThis {
impl tokio::io::AsyncRead for DontReadIntoThis {
fn poll_read(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
+1 -1
View File
@@ -3,7 +3,7 @@
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, ReadBuf};
use tokio::stream::StreamExt;
use tokio_stream::StreamExt;
/// produces at most `remaining` zeros, that returns error.
/// each time it reads at most 31 byte.
+1 -1
View File
@@ -2,7 +2,7 @@
use bytes::Bytes;
use tokio::io::AsyncReadExt;
use tokio::stream::iter;
use tokio_stream::iter;
use tokio_util::io::StreamReader;
#[tokio::test]
+15 -9
View File
@@ -2,7 +2,7 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]
use tokio::time::{self, sleep, Duration, Instant};
use tokio::time::{self, sleep, sleep_until, Duration, Instant};
use tokio_test::{assert_ok, assert_pending, assert_ready, task};
use tokio_util::time::DelayQueue;
@@ -107,9 +107,10 @@ async fn multi_delay_at_start() {
assert_pending!(poll!(queue));
assert!(!queue.is_woken());
let start = Instant::now();
for elapsed in 0..1200 {
sleep(ms(1)).await;
let elapsed = elapsed + 1;
tokio::time::sleep_until(start + ms(elapsed)).await;
if delays.contains(&elapsed) {
assert!(queue.is_woken());
@@ -117,7 +118,12 @@ async fn multi_delay_at_start() {
assert_pending!(poll!(queue));
} else if queue.is_woken() {
let cascade = &[192, 960];
assert!(cascade.contains(&elapsed), "elapsed={}", elapsed);
assert!(
cascade.contains(&elapsed),
"elapsed={} dt={:?}",
elapsed,
Instant::now() - start
);
assert_pending!(poll!(queue));
}
@@ -205,7 +211,7 @@ async fn reset_much_later() {
sleep(ms(3)).await;
queue.reset_at(&key, now + ms(5));
queue.reset_at(&key, now + ms(10));
sleep(ms(20)).await;
@@ -402,7 +408,7 @@ async fn insert_before_first_after_poll() {
sleep(ms(99)).await;
assert!(!queue.is_woken());
assert_pending!(poll!(queue));
sleep(ms(1)).await;
@@ -457,7 +463,7 @@ async fn reset_later_after_slot_starts() {
assert_pending!(poll!(queue));
sleep(ms(80)).await;
sleep_until(now + Duration::from_millis(80)).await;
assert!(!queue.is_woken());
@@ -472,7 +478,7 @@ async fn reset_later_after_slot_starts() {
assert_pending!(poll!(queue));
sleep(ms(39)).await;
sleep_until(now + Duration::from_millis(119)).await;
assert!(!queue.is_woken());
sleep(ms(1)).await;
@@ -515,7 +521,7 @@ async fn reset_earlier_after_slot_starts() {
assert_pending!(poll!(queue));
sleep(ms(80)).await;
sleep_until(now + Duration::from_millis(80)).await;
assert!(!queue.is_woken());
@@ -530,7 +536,7 @@ async fn reset_earlier_after_slot_starts() {
assert_pending!(poll!(queue));
sleep(ms(39)).await;
sleep_until(now + Duration::from_millis(119)).await;
assert!(!queue.is_woken());
sleep(ms(1)).await;

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