Compare commits

...
173 Commits
Author SHA1 Message Date
Carl Lerche 069a7b6c59 chore: prepare Tokio 1.5.2 release
The previous commit forgot to update the version in Cargo.toml
2021-07-19 12:17:56 -07:00
Carl Lerche 9883f33048 chore: prepare Tokio v.1.5.2 2021-07-19 11:40:03 -07:00
Diggory Blake 3137848834 runtime: drop future when polling cancelled future (#3965) 2021-07-19 11:40:03 -07:00
Alice Ryhl e385108920 rt: remotely abort tasks on JoinHandle::abort (#3934) 2021-07-06 13:41:25 -07:00
Alice Ryhl a5ee2f0d3d chore: prepare Tokio v1.5.0 (#3695) 2021-04-12 21:24:47 +02:00
Alice Ryhl 917aad684b chore: prepare tokio-util 0.6.6 (#3696) 2021-04-12 21:23:20 +02:00
Alice Ryhl e366cf9b3e chore: upgrade loom to 0.5.0 (#3697) 2021-04-12 20:28:48 +02:00
Alice Ryhl 3a02d34d3a sync: document that Semaphore is fair (#3693) 2021-04-12 15:46:42 +02:00
conblem adad8fc3cd io: add a copy_bidirectional utility (#3572) 2021-04-12 14:18:45 +02:00
David Pedersen 08f1b67fcb sync: add mpsc::Sender::capacity (#3690)
Simply exposes the number of available permits of the semaphore.

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

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

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

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

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

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

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

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

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

## Solution

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

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

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

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

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

[`PollSender`]: https://docs.rs/tokio-util/0.6.4/tokio_util/sync/struct.PollSender.html
[`mpsc::Sender::send`]: https://docs.rs/tokio/1.3.0/tokio/sync/mpsc/struct.Sender.html
2021-03-16 17:55:06 +01:00
David Pedersen cc90a5c679 chore: mention fix for building docs in contributing guide (#3618)
I ran into this when writing the docs for `Handle::block_on`. Seems to
be caused by a bug in cargo. Until that is fixed I think it makes sense
to mention it in the contributing guide.
2021-03-16 21:39:28 +09:00
David Pedersen f107c4f49b timer: fix double newline in module docs (#3617) 2021-03-16 21:37:50 +09:00
Zahari Dichev e6a9167bb7 runtime: avoid unnecessary polling of block_on future (#3582) 2021-03-16 10:30:18 +01:00
setpill 345b29ca11 sync: improve Mutex FIFO explanation (#3615) 2021-03-12 21:24:05 +01:00
Nate Mara b75d02a2b6 macros: introduce biased argument for select! (#3603) 2021-03-12 21:20:20 +01:00
Faraz a1b4bdee61 runtime: mark EnterGuard with must_use (#3609) 2021-03-12 09:06:08 +01:00
Zahari Dichev edfff7551a util: anotate time module as requiring time feature (#3606)
Signed-off-by: Zahari Dichev <[email protected]>
2021-03-11 16:02:01 +01:00
Rob Ede 1cda0f16a2 doc: alias sleep to delay (#3604) 2021-03-10 23:26:11 +01:00
Lucio Franco e40ec3e424 chore: Prepare tokio-test v0.4.1 release (#3602) 2021-03-10 16:04:08 -05:00
Jake HamandJake Ham 6919f7cede test: Make Mock both Send and Sync (#3594)
Co-authored-by: Jake Ham <[email protected]>
2021-03-10 14:29:22 -05:00
cssivision bcb95db4e2 chore: fix clippy warnings in newer versions (#3588) 2021-03-10 08:42:24 +01:00
Matthew James Briggs 8c5cde9bc3 stream: documentation note for throttle Unpin (#3600) 2021-03-10 08:39:43 +01:00
Alice Ryhl b08b5edb2c chore: prepare tokio-util v0.6.4 (#3599) 2021-03-09 22:11:38 +01:00
Alice Ryhl 88863a0c5d chore: prepare tokio-stream v0.1.4 (#3598) 2021-03-09 22:10:57 +01:00
Alice Ryhl d0e4dd1d7b chore: prepare Tokio v1.3.0 (#3597) 2021-03-09 22:09:51 +01:00
Andrew Liu 872bc09e83 examples: update chat example (#3587) 2021-03-09 19:54:10 +01:00
Alice Ryhl db1d90453c util: fuse PollSemaphore (#3578) 2021-03-09 19:22:36 +01:00
Thomas Orozco 05eeea570e coop: expose an unconstrained() opt-out (#3547) 2021-03-09 19:10:15 +01:00
Henry Gomersall f70b9b84f7 sync: documenation for oneshot (#3592) 2021-03-09 18:58:45 +01:00
nickelc 704de8c01b stream: remove duplicate doc(test(..)) & cfg_attr declarations (#3571) 2021-03-06 20:54:28 +09:00
Thomas Orozco 47be928444 sync: yield initial value in WatchStream (#3576) 2021-03-05 21:35:12 +01:00
Zhang Jingqiang e06b257e09 sync: add same_channel method to mpsc Senders (#3532) 2021-03-04 17:45:40 +01:00
Alice Ryhl 0867a6fc03 util: add pollable mpsc::Sender (#3490) 2021-03-04 17:29:30 +01:00
Gus Wynn 0d838bf5ad io: fix link to std src in util (#3568)
The big move or 'src' in rust-lang caused this link to be broken
2021-03-02 02:50:13 +09:00
Max Bruckner fc23f8a1a5 sync: add {try_,}acquire_many_owned to Semaphore (#3535) 2021-03-01 14:37:00 +01:00
Pirmin Kalberer fd93ecf5e0 stream: remove duplicate doc_cfg declaration (#3561) 2021-02-27 22:47:26 +01:00
Stepan Koltsov d2ad7afd21 time: do not panic on timeout(Duration::MAX) (#3551)
It is tempting to use very large `Duration` value to get a practically
infinite timeout.

Before this commit Tokio panics on checked Instant + Duration
overflow.

This commit implements very simple fix: if Instant + Duration
overflows, we use duration = 30 years. Better fix should avoid
firing a timer on duration overflow. It requires deeper understanding
how timers work, but also it is not clear, for example, what
`Sleep::deadline` function should return.

Similar fix is done for `sleep`.
2021-02-26 10:04:08 +01:00
r-zig 5756a005a6 codec: LinesCodec should only return MaxLineLengthExceeded once per line (#3556) 2021-02-25 23:21:59 +01:00
Paho Lurie-Gregg 017a483b5e io: update AsyncRead::poll_read doc (#3557)
The trait-level documentation accurately describes this method, but the
function-level documentation imples that an implementer should overwrite
the entire buf, rather than just the unfilled portion.
2021-02-25 23:19:02 +01:00
Stepan Koltsov 112e160b62 sync: allow oneshot::Receiver::close after successful try_recv (#3552)
Before this commit `close` after successful `try_recv` panics.

My use case is this: on drop, I call `close` to prevent pushing a
message to the queue, and then fetch the message if any and process
it.

But if message is already processed, `close` panics.  And there is
no API to know if message was already fetched or not (except for
writing a wrapped which would track that info, which would be an
overkill).

But generally `close` operation should be safe to be called any
time.
2021-02-25 20:28:24 +01:00
r-zig c9d2a36c7b codec: AnyDelimiter codec (#3406) 2021-02-23 16:56:11 +01:00
Kestrer 8efed43fa7 runtime: update documentation of thread limits (#3527)
Since ed5a94eb2d, there is no longer a
limit of 32,768 threads on the worker threads and blocking thread pool,
but the documentation has not been updated. This commit removes that
part of the documentation.
2021-02-23 16:54:50 +01:00
Marko Mikulicic 7de18af82c doc: sync interval.rs and time/mod.rs docs (#3533) 2021-02-22 11:45:49 +01:00
xlUpandAaron Taner 52457dcf5b doc: fix typos (#3541)
Co-authored-by: Aaron Taner <[email protected]>
2021-02-22 17:31:45 +09:00
Adam Harvey 53558cb489 doc: note the EOF behaviour of read_until (#3536) 2021-02-20 10:22:51 +01:00
Taiki EndoandAlice Ryhl 36d7dab504 chore: remove html_root_url (#3489)
Co-authored-by: Alice Ryhl <[email protected]>
2021-02-18 14:11:39 -08:00
Kornel 6fd06aaeec doc: doc aliases for pre-1.0 function names (#3523) 2021-02-17 13:00:44 +01:00
Pen Tree 36bcfa6b9d doc: rename notify to notify_one (#3526) 2021-02-15 20:21:27 +01:00
Alice Ryhl e3f2dcf5bc sync: add back RwLockWriteGuard::map and RwLockWriteGuard::try_map (#3348) 2021-02-13 12:53:21 +01:00
Alice Ryhl 7c6a1c4637 net: update UdpSocket splitting doc (#3517) 2021-02-13 11:13:42 +01:00
Alice Ryhl 4099bfdef0 sync: do not recommend join_all for Barrier (#3514) 2021-02-12 10:43:11 +01:00
Alice Ryhl 469b43de6a time: fix typo in Sleep doc (#3515) 2021-02-11 10:30:48 +01:00
Alice Ryhl e827829402 signal: add Signal wrappers to tokio-stream (#3510) 2021-02-07 11:30:18 +01:00
Alice Ryhl 58bd242831 net: add into_std for net types without it (#3509) 2021-02-06 23:01:47 +01:00
Alice Ryhl 6fd9084d47 runtime: add link to LocalSet on new_current_thread (#3508) 2021-02-06 19:08:59 +01:00
Alice Ryhl 572a897d43 chore: prepare tokio v1.2.0 (#3506) 2021-02-05 23:27:42 +01:00
Alice Ryhl d41882e2a1 chore: prepare tokio-macros 1.1.0 (#3505) 2021-02-05 22:21:29 +01:00
Alice Ryhl 23fdc2b3c4 chore: prepare tokio-stream 0.1.3 (#3507) 2021-02-05 22:18:56 +01:00
Alice Ryhl 0a04954d5c stream: update features and doc for broadcast/watch stream (#3504) 2021-02-05 20:39:27 +01:00
Steven Fackler fcb6d041b9 time: make test-util paused time fully deterministic (#3492)
The time driver stores an Instant internally used as a "base" for future
time calculations. Since this is generated as the Runtime is being
constructed, it previously always happened before the user had a chance
to pause time. The fractional-millisecond variations in the timing
around the runtime construction and time pause cause tests running
entirely in paused time to be very slightly deterministic, with the time
driver advancing time by 1 millisecond more or less depending on how the
sub-millisecond components of the `Instant`s involved compared.

To avoid this, there is now a new option on `runtime::Builder` which
will create a `Runtime` with time "instantly" paused. This, along with a
small change to have the time driver use the provided clock as the
source for its start time allow totally deterministic tests with paused
time.
2021-02-05 20:12:25 +01:00
Fuyang Liu 1c1e0e3fc9 tokio-stream: add wrapper for broadcast and watch (#3384) 2021-02-05 19:56:24 +01:00
Fuyang Liu 77ca8a934c signal: make Signal::poll_recv method public (#3383)
Signed-off-by: Fuyang Liu <[email protected]>
2021-02-05 19:43:58 +01:00
surechen 3e5a0a7df6 runtime: fix a spelling mistake (#3503) 2021-02-04 12:30:31 +01:00
Artem VorotnikovandTaiki Endo cc97fb8a5f Suppress deprecated warnings for spin_loop_hint (#3497)
* Suppress deprecated warnings for spin_loop_hint

* Update tokio/src/loom/std/mod.rs

Co-authored-by: Taiki Endo <[email protected]>

* Update tokio/src/sync/task/atomic_waker.rs

Co-authored-by: Taiki Endo <[email protected]>

* fmt

Co-authored-by: Taiki Endo <[email protected]>
2021-02-01 15:55:21 +03:00
Alice Ryhl 60d88840f4 chore: prepare tokio-util v0.6.3 (#3488) 2021-01-31 12:39:22 +01:00
Alice Ryhl ee1f0c473f util: remove tokio-stream dependency from tokio-util (#3487) 2021-01-31 11:08:40 +01:00
Rob Ede 06d6adf4b7 util: use ReusableBoxFuture for PollSemaphore (#3463) 2021-01-30 10:57:04 +01:00
Alice Ryhl 891aba5f71 util: add ReusableBoxFuture utility (#3464) 2021-01-30 00:03:01 +01:00
Carl Lerche 10c3b2f4a3 Merge branch 'tokio-1.1.x' 2021-01-29 14:13:24 -08:00
Markus Westerlind 6f988728bb runtime: minimize the amount of duplicated code (#3416) 2021-01-29 21:45:29 +01:00
Carl Lerche 085f819f28 Merge branch 'tokio-1.0.x' into 'tokio-1.1.x' 2021-01-28 20:23:50 -08:00
Carl Lerche 5d0a81fb91 io: fix memory leak during shutdown (#3477)
In some cases, a cycle is created between I/O driver wakers and the I/O
driver resource slab. This patch clears stored wakers when an I/O
resource is dropped, breaking the cycle.

Fixes #3228
2021-01-28 17:49:10 -08:00
Ivan Petkov 1f9765fb5f process: add missing drop child stdin to wait() (#3421) 2021-01-28 23:08:44 +01:00
Jamie 225e8ea05f sync: update RwLock doc to mention possibility of deadlock (#3389) 2021-01-28 16:38:49 +01:00
Alice Ryhl cd7526873c net: improve discoverability of TcpSocket (#3471) 2021-01-28 14:09:14 +01:00
vitalyd 4968f59af6 sync: update Sender::try_reserve doc (#3476) 2021-01-28 10:31:23 +01:00
Ivan Petkov 34f1d3d040 process: allow for ergonomically piping stdio of one child into another (#3466)
* Add `TryInto<Stdio>` impls for `ChildStd{in,out,err}` for ergonomic
  conversions into `std::process::Stdio` so callers can perform the
  conversion without needing to manipulate raw fds/handles directly
2021-01-27 01:12:54 +00:00
Ontonator f13a9dd87a tokio: update dates in CHANGELOG.md to 2021 (#3469) 2021-01-25 12:04:58 +01:00
Alice Ryhl 35b1a4d0b3 chore: prepare Tokio 1.1.0 (#3462) 2021-01-22 23:05:37 +01:00
Alice Ryhl 6da5087b06 sync: improve bounded mpsc documentation (#3458) 2021-01-22 19:28:37 +01:00
Alice Ryhl c6f08120d0 task: add LocalSet example (#3438) 2021-01-21 16:36:26 +01:00
Alice Ryhl 29bd5fad5c sync: add link to PollSemaphore (#3456) 2021-01-21 15:20:55 +01:00
Alice Ryhl f2a048d990 time: document that Sleep is not Unpin (#3457) 2021-01-21 15:19:17 +01:00
Alice Ryhl 198363f4f1 chore: prepare tokio-util 0.6.2 (#3453) 2021-01-21 11:46:50 +01:00
vitalyd 5b7c7d565a io: use a sealed trait to constrain VecWithInitialized (#3450) 2021-01-21 11:32:42 +01:00
Blas Rodriguez Irizar 117fc2ef3e tests: fix ping pong saturation (#3390) 2021-01-21 11:02:40 +01:00
William Bain c4f66ed121 util: fix panics on updating DelayQueue entries (#3270) 2021-01-21 10:56:44 +01:00
Oğuz Bilgener 7d5b12c509 sync: fix panic in broadcast::Receiver drop (#3434) 2021-01-20 23:12:51 +01:00
Alice Ryhl cc0911aa64 net: update datagram docs on splitting (#3448) 2021-01-20 16:28:57 +01:00
Henry Gomersall 6f8a4d7a0b docs: trivial typo fix (#3449) 2021-01-20 16:28:35 +01:00
Arve Knudsen fdde5583f8 runtime: consolidate errors for context missing (#3441) 2021-01-20 14:29:13 +01:00
Rain 36cf95ab62 io: add ReadBuf::inner_mut (#3443) 2021-01-20 13:56:14 +01:00
Alice Ryhl 27b2d681e6 util: add pollable Semaphore (#3444) 2021-01-20 09:53:31 +01:00
Alice Ryhl c1cf6b7504 io: keep track of initialized bytes in read_to_end (#3426) 2021-01-20 09:49:24 +01:00
Henry Gomersall 5402c948ee io: clarify AsyncFd docs about changes of the inner fd (#3430) 2021-01-17 21:39:58 +01:00
Carl Lerche 2af9b755c5 Merge branch 'tokio-1.0.x' 2021-01-16 11:36:21 -08:00
Jacob O'Toole 7ac44a2d28 process: add documentation to process::Child fields (#3437) 2021-01-16 19:42:51 +01:00
Carl Lerche 3dc5f6c0ab chore: release tokio v1.0.2 (#3429) 2021-01-15 15:05:38 -08:00
Alice Ryhl 0a7117fd9a io: fix unsoundness in read_to_end (#3428) 2021-01-15 15:05:38 -08:00
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
Nylonicious ecc32d1dca sync: add RwLock try_read and try_write methods (#3400)
Closes #2284
2021-01-14 15:05:07 -08:00
Markus Westerlind 204603b7de rt: reduce duplicated code in task harness (#3314)
Task spawning is a common operation that results in a lot of instantiations
of the task code. Reducing the amount of generated code should lead to
faster compile times overall.

This patch extracts code that does not depend on a type parameter into
functions with fewer or no type parameters at all. Reducing the amount of
duplicated code.
2021-01-14 10:07:56 -08:00
Taiki Endo 30b4a7486a chore: use CARGO_BIN_EXE instead of inspecting env::current_exe() (#3427) 2021-01-14 09:59:37 -08:00
Taiki Endo 0595902ba4 Update issue template (#3425)
`cargo tree` subcommand is available by default since Rust 1.44.
2021-01-14 23:09:05 +09:00
Lucio FrancoandAlice Ryhl 672be92a03 mpsc: Add Sender::try_reserve function (#3418)
* mpsc: Add `Sender::try_reserve` function

* Update tokio/src/sync/mpsc/bounded.rs

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

* Fix doc links

Co-authored-by: Alice Ryhl <[email protected]>
2021-01-13 14:24:23 -05:00
Alice Ryhl 766a89bf94 chore: prepare tokio-stream 0.1.2 (#3414) 2021-01-12 21:38:51 +01:00
David Kellum 8b9bb41809 docs: add tokio 1.0 policies to contributing guide (#3386) 2021-01-12 21:15:44 +01:00
Taiki Endo d37486dd06 util: remove path deps (#3413)
* util: remove path deps

* ci: run clippy with --all-features

* ci: run tests with --all-features on FreeBSD CI
2021-01-13 00:19:57 +09:00
Daiki Mizukami ed667c669a chore: prepare tokio-util v0.6.1 release (#3402) 2021-01-12 12:46:07 +01:00
Milan da09213c9f doc: typo fixes (#3401) 2021-01-11 21:39:50 -08:00
Taiki Endo 40d959263b macros: fix unused_braces in generated code (#3404) 2021-01-11 19:20:40 +09:00
Shantanu Sharma 0b8bdf9d32 runtime: fix typo (#3385) 2021-01-10 20:59:14 +09:00
cssivision 51a7082d13 docs: test-util feature is not enabled with full (#3397) 2021-01-09 14:01:10 +01:00
Cameron 22c27b9282 task: wake LocalSet on spawn_local (#3369) 2021-01-09 11:46:51 +01:00
David Raifaizen 2fe2f0401b util: expose FramedWrite/Framed::write_buffer/write_buffer_mut 2021-01-08 23:20:07 +01:00
Rust大闸蟹 e42317b072 chore: remove macro_rules semicolon (#3381)
fix build warning
2021-01-06 15:46:37 +09:00
Alice Ryhl 3549092a0c docs: fix tokio-stream features not being displayed (#3378) 2021-01-05 11:43:09 +01:00
Alice Ryhl 3b840fb305 stream: remove path deps (#3376) 2021-01-04 21:04:22 +01:00
Alice Ryhl 4beb1862ac chore: prepare tokio-stream 0.1.1 (#3374) 2021-01-04 19:46:01 +01:00
Alice Ryhl 7f17822ed9 stream: add Stream wrappers in tokio-stream (#3343) 2021-01-04 19:03:18 +01:00
cssivision 3b6bee822d net: add try_read_buf and try_recv_buf (#3351) 2021-01-02 10:37:34 +01:00
Rob Ede 56272b2ec7 stream: move async-stream to dev-deps (#3366) 2021-01-02 13:43:41 +09:00
John-John Tedro 53707f5d9d sync: remove underused internal Borrow wrapper (#3363) 2021-01-01 13:04:38 -08:00
Daiki Mizukami 36918e05e5 util: add more get_{ref,mut,pin_mut} methods (#3364)
This commit adds:

- Framed::get_pin_mut
- FramedRead::get_pin_mut
- FramedWrite::get_pin_mut
- StreamReader::get_ref
- StreamReader::get_mut
- StreamReader::get_pin_mut
- StreamReader::into_inner
2021-01-01 13:05:15 +01:00
Arniu Tseng cbd4f4472e docs: update CHANGELOG.md (#3336) 2020-12-27 16:36:36 +01:00
Danilo MartinsandDanilo 2d9bea8d63 runtime: fix typo (#3354)
Co-authored-by: Danilo <[email protected]>
2020-12-27 16:17:00 +01:00
zhongjnandAlice Ryhl 0048d43713 macros: improve select! error message (#3352)
Co-authored-by: Alice Ryhl <[email protected]>
2020-12-27 23:50:32 +09:00
Taiki Endo 770044caa7 chore: replace deprecated compare_and_swap with compare_exchange (#3331) 2020-12-27 10:44:21 +01:00
AJ Frantz c4929264bc examples: adjust buffering in examples (#3328) 2020-12-27 10:43:02 +01:00
Alice Ryhl 4d7b73f5b3 chore: create stream_ext module (#3342) 2020-12-26 17:05:51 +01: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
240 changed files with 12975 additions and 3428 deletions
+2 -2
View File
@@ -21,9 +21,9 @@ task:
rustc --version
test_script:
- . $HOME/.cargo/env
- cargo test --all
- cargo test --all --all-features
- cargo doc --all --no-deps
i686_test_script:
- . $HOME/.cargo/env
- |
cargo test --all --target i686-unknown-freebsd
cargo test --all --all-features --target i686-unknown-freebsd
+1 -6
View File
@@ -9,12 +9,7 @@ assignees: ''
**Version**
List the versions of all `tokio` crates you are using. The easiest way to get
this information is using `cargo-tree`.
`cargo install cargo-tree`
(see install here: https://github.com/sfackler/cargo-tree)
Then:
this information is using `cargo tree` subcommand:
`cargo tree | grep tokio`
+35 -3
View File
@@ -1,8 +1,8 @@
on:
push:
branches: ["master"]
branches: ["master", "tokio-*.x"]
pull_request:
branches: ["master"]
branches: ["master", "tokio-*.x"]
name: CI
@@ -28,6 +28,7 @@ jobs:
- clippy
- docs
- loom
- valgrind
steps:
- run: exit 0
@@ -67,6 +68,37 @@ jobs:
run: cargo hack test --each-feature
working-directory: tests-build
valgrind:
name: valgrind
runs-on: ubuntu-latest
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
# Compile tests
- name: cargo build test-mem
run: cargo build --features rt-net --bin test-mem
working-directory: tests-integration
# Run with valgrind
- name: Run valgrind test-mem
run: valgrind --leak-check=full --show-leak-kinds=all ./target/debug/test-mem
# Compile tests
- name: cargo build test-process-signal
run: cargo build --features rt-process-signal --bin test-process-signal
working-directory: tests-integration
# Run with valgrind
- name: Run valgrind test-process-signal
run: valgrind --leak-check=full --show-leak-kinds=all ./target/debug/test-process-signal
test-unstable:
name: test tokio full --unstable
runs-on: ${{ matrix.os }}
@@ -232,7 +264,7 @@ jobs:
# Run clippy
- name: "clippy --all"
run: cargo clippy --all --tests
run: cargo clippy --all --tests --all-features
docs:
name: docs
+59 -12
View File
@@ -124,17 +124,27 @@ arguments to many common cargo commands. This section lists some commonly needed
commands.
Some commands just need the `--all-features` argument:
```
cargo build --all-features
cargo check --all-features
cargo test --all-features
```
When building documentation normally, the markers that list the features
required for various parts of Tokio are missing. To build the documentation
correctly, use this command:
```
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
```
There is currently a [bug in cargo] that means documentation cannot be built
from the root of the workspace. If you `cd` into the `tokio` subdirectory the
command shown above will work.
[bug in cargo]: https://github.com/rust-lang/cargo/issues/9274
The `cargo fmt` command does not work on the Tokio codebase. You can use the
command below instead:
@@ -427,11 +437,14 @@ _Adapted from the [Node.js contributing guide][node]_.
## Keeping track of issues and PRs
The Tokio GitHub repository has a lot of issues and PRs, which is not easy to
keep track of. This section explains the meaning of various labels, as well as
our [GitHub project][project]. The section is primarily targeted at maintainers.
The Tokio GitHub repository has a lot of issues and PRs to keep track of. This
section explains the meaning of various labels, as well as our [GitHub
project][project]. The section is primarily targeted at maintainers. Most
contributors aren't able to set these labels.
**Area.** The area label describes the crates relevant to this issue or PR.
### Area
The area label describes the crates relevant to this issue or PR.
- **A-tokio** This issue concerns the main Tokio crate.
- **A-tokio-util** This issue concerns the `tokio-util` crate.
@@ -442,7 +455,7 @@ our [GitHub project][project]. The section is primarily targeted at maintainers.
be used for the procedural macros, and not `join!` or `select!`.
- **A-ci** This issue concerns our GitHub Actions setup.
**Category.** The category label describes the category.
### Category
- **C-bug** This is a bug-report. Bug-fix PRs use `C-enhancement` instead.
- **C-enhancement** This is a PR that adds a new features.
@@ -460,8 +473,7 @@ our [GitHub project][project]. The section is primarily targeted at maintainers.
- **C-request** A non-feature request, e.g. "please add deprecation notices to
`-alpha.*` versions of crates"
**Call for participation.** I don't know why it's called `E-`. Many issues are
missing a difficulty rating, and you should feel free to add one.
### Calls for participation
- **E-help-wanted** Stuff where we want help. Often seen together with `C-bug`
or `C-feature-accepted`.
@@ -473,7 +485,13 @@ missing a difficulty rating, and you should feel free to add one.
- **E-needs-mvce** This bug is missing a minimal complete and verifiable
example.
**Module.** A more fine groaned categorization than area.
The "E-" prefix is the same as used in the Rust compiler repository. Some
issues are missing a difficulty rating, but feel free to ask on our Discord
server if you want to know how difficult an issue likely is.
### Module
The module label provides a more fine grained categorization than **Area**.
- **M-blocking** Things relevant to `spawn_blocking`, `block_in_place`.
- **M-codec** The `tokio_util::codec` module.
@@ -491,7 +509,9 @@ missing a difficulty rating, and you should feel free to add one.
- **M-time** The `tokio::time` module.
- **M-tracing** Tracing support in Tokio.
**Topic.** Some extra information.
### Topic
Some extra information.
- **T-docs** This is about documentation.
- **T-performance** This is about performance.
@@ -501,6 +521,34 @@ Any label not listed here is not in active use.
[project]: https://github.com/orgs/tokio-rs/projects/1
## LTS guarantees
Tokio ≥1.0.0 comes with LTS guarantees:
* A minimum of 5 years of maintenance.
* A minimum of 3 years before a hypothetical 2.0 release.
The goal of these guarantees is to provide stability to the ecosystem.
## Mininum Supported Rust Version (MSRV)
* All Tokio ≥1.0.0 releases will support at least a 6-month old Rust
compiler release.
* The MSRV will only be increased on 1.x releases.
## Versioning Policy
With Tokio ≥1.0.0:
* Patch (1.\_.x) releases _should only_ contain bug fixes or documentation
changes. Besides this, these releases should not substantially change
runtime behavior.
* Minor (1.x) releases may contain new functionality, MSRV increases (see
above), minor dependency updates, deprecations, and larger internal
implementation changes.
This is as defined by [Semantic Versioning 2.0](https://semver.org/).
## Releasing
Since the Tokio project consists of a number of crates, many of which depend on
@@ -533,9 +581,8 @@ When releasing a new version of a crate, follow these steps:
2. **Update Cargo metadata.** After releasing any path dependencies, update the
`version` field in `Cargo.toml` to the new version, and the `documentation`
field to the docs.rs URL of the new version.
3. **Update other documentation links.** Update the `#![doc(html_root_url)]`
attribute in the crate's `lib.rs` and the "Documentation" link in the crate's
`README.md` to point to the docs.rs URL of the new version.
3. **Update other documentation links.** Update the "Documentation" link in the
crate's `README.md` to point to the docs.rs URL of the new version.
4. **Update the changelog for the crate.** Each crate in the Tokio repository
has its own `CHANGELOG.md` in that crate's subdirectory. Any changes to that
crate since the last release should be added to the changelog. Change
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2019 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+9 -1
View File
@@ -50,7 +50,15 @@ an asynchronous application.
## Example
A basic TCP echo server with Tokio:
A basic TCP echo server with Tokio.
Make sure you activated the full features of the tokio crate on Cargo.toml:
```text
[dependencies]
tokio = { version = "1.4.0", features = ["full"] }
```
Then, on your main.rs:
```rust,no_run
use tokio::net::TcpListener;
+3 -4
View File
@@ -7,11 +7,10 @@ edition = "2018"
# If you copy one of the examples into a new project, you should be using
# [dependencies] instead.
[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full", "tracing"] }
tokio-util = { version = "0.6.0", path = "../tokio-util", features = ["full"] }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
tokio = { version = "1.0.0", features = ["full", "tracing"] }
tokio-util = { version = "0.6.3", features = ["full"] }
tokio-stream = { version = "0.1" }
async-stream = "0.3"
tracing = "0.1"
tracing-subscriber = { version = "0.2.7", default-features = false, features = ["fmt", "ansi", "env-filter", "chrono", "tracing-log"] }
bytes = "1.0.0"
+31 -69
View File
@@ -28,8 +28,8 @@
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::{mpsc, Mutex};
use tokio_stream::{Stream, StreamExt};
use tokio_util::codec::{Framed, LinesCodec, LinesCodecError};
use tokio_stream::StreamExt;
use tokio_util::codec::{Framed, LinesCodec};
use futures::SinkExt;
use std::collections::HashMap;
@@ -37,9 +37,7 @@ use std::env;
use std::error::Error;
use std::io;
use std::net::SocketAddr;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
@@ -101,6 +99,9 @@ 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
@@ -124,7 +125,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: Pin<Box<dyn Stream<Item = String> + Send>>,
rx: Rx,
}
impl Shared {
@@ -156,58 +157,15 @@ impl Peer {
let addr = lines.get_ref().peer_addr()?;
// Create a channel for this peer
let (tx, mut rx) = mpsc::unbounded_channel();
let (tx, 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 })
}
}
#[derive(Debug)]
enum Message {
/// A message that should be broadcasted to others.
Broadcast(String),
/// A message that should be received by a client
Received(String),
}
// Peer implements `Stream` in a way that polls both the `Rx`, and `Framed` types.
// A message is produced whenever an event is ready until the `Framed` stream returns `None`.
impl Stream for Peer {
type Item = Result<Message, LinesCodecError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
// First poll the `UnboundedReceiver`.
if let Poll::Ready(Some(v)) = Pin::new(&mut self.rx).poll_next(cx) {
return Poll::Ready(Some(Ok(Message::Received(v))));
}
// Secondly poll the `Framed` stream.
let result: Option<_> = futures::ready!(Pin::new(&mut self.lines).poll_next(cx));
Poll::Ready(match result {
// We've received a message we should broadcast to others.
Some(Ok(message)) => Some(Ok(Message::Broadcast(message))),
// An error occurred.
Some(Err(e)) => Some(Err(e)),
// The stream has been exhausted.
None => None,
})
}
}
/// Process an individual chat client
async fn process(
state: Arc<Mutex<Shared>>,
@@ -241,28 +199,32 @@ async fn process(
}
// Process incoming messages until our stream is exhausted by a disconnect.
while let Some(result) = peer.next().await {
match result {
// A message was received from the current user, we should
// broadcast this message to the other users.
Ok(Message::Broadcast(msg)) => {
let mut state = state.lock().await;
let msg = format!("{}: {}", username, msg);
state.broadcast(addr, &msg).await;
}
// A message was received from a peer. Send it to the
// current user.
Ok(Message::Received(msg)) => {
loop {
tokio::select! {
// A message was received from a peer. Send it to the current user.
Some(msg) = peer.rx.recv() => {
peer.lines.send(&msg).await?;
}
Err(e) => {
tracing::error!(
"an error occurred while processing messages for {}; error = {:?}",
username,
e
);
}
result = peer.lines.next() => match result {
// A message was received from the current user, we should
// broadcast this message to the other users.
Some(Ok(msg)) => {
let mut state = state.lock().await;
let msg = format!("{}: {}", username, msg);
state.broadcast(addr, &msg).await;
}
// An error occurred.
Some(Err(e)) => {
tracing::error!(
"an error occurred while processing messages for {}; error = {:?}",
username,
e
);
}
// The stream has been exhausted.
None => break,
},
}
}
+2 -2
View File
@@ -92,7 +92,7 @@ mod tcp {
mod udp {
use bytes::Bytes;
use futures::{future, Sink, SinkExt, Stream, StreamExt};
use futures::{Sink, SinkExt, Stream, StreamExt};
use std::error::Error;
use std::io;
use std::net::SocketAddr;
@@ -114,7 +114,7 @@ mod udp {
let socket = UdpSocket::bind(&bind_addr).await?;
socket.connect(addr).await?;
future::try_join(send(stdin, &socket), recv(stdout, &socket)).await?;
tokio::try_join!(send(stdin, &socket), recv(stdout, &socket))?;
Ok(())
}
+1 -1
View File
@@ -55,7 +55,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
// which will allow all of our clients to be processed concurrently.
tokio::spawn(async move {
let mut buf = [0; 1024];
let mut buf = vec![0; 1024];
// In a loop, read data from the socket and write the data back.
loop {
+1 -2
View File
@@ -26,7 +26,6 @@ use tokio::io;
use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream};
use futures::future::try_join;
use futures::FutureExt;
use std::env;
use std::error::Error;
@@ -74,7 +73,7 @@ async fn transfer(mut inbound: TcpStream, proxy_addr: String) -> Result<(), Box<
wi.shutdown().await
};
try_join(client_to_server, server_to_client).await?;
tokio::try_join!(client_to_server, server_to_client)?;
Ok(())
}
+1 -1
View File
@@ -45,7 +45,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
let b = pong(&mut b);
// Run both futures simultaneously of `a` and `b` sending messages back and forth.
match futures::future::try_join(a, b).await {
match tokio::try_join!(a, b) {
Err(e) => println!("an error occurred; error = {:?}", e),
_ => println!("done!"),
}
+18 -3
View File
@@ -12,12 +12,27 @@ async fn main_attr_has_path_args() {}
#[tokio::test]
fn test_is_not_async() {}
#[tokio::test]
async fn test_fn_has_args(_x: u8) {}
#[tokio::test(foo)]
async fn test_attr_has_args() {}
#[tokio::test(foo = 123)]
async fn test_unexpected_attr() {}
#[tokio::test(flavor = 123)]
async fn test_flavor_not_string() {}
#[tokio::test(flavor = "foo")]
async fn test_unknown_flavor() {}
#[tokio::test(flavor = "multi_thread", start_paused = false)]
async fn test_multi_thread_with_start_paused() {}
#[tokio::test(flavor = "multi_thread", worker_threads = "foo")]
async fn test_worker_threads_not_int() {}
#[tokio::test(flavor = "current_thread", worker_threads = 4)]
async fn test_worker_threads_and_current_thread() {}
#[tokio::test]
#[test]
async fn test_has_second_test_attr() {}
@@ -1,10 +1,10 @@
error: the async keyword is missing from the function declaration
error: the `async` keyword is missing from the function declaration
--> $DIR/macros_invalid_input.rs:4:1
|
4 | fn main_is_not_async() {}
| ^^
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
--> $DIR/macros_invalid_input.rs:6:15
|
6 | #[tokio::main(foo)]
@@ -16,26 +16,56 @@ error: Must have specified ident
9 | #[tokio::main(threadpool::bar)]
| ^^^^^^^^^^^^^^^
error: the async keyword is missing from the function declaration
error: the `async` keyword is missing from the function declaration
--> $DIR/macros_invalid_input.rs:13:1
|
13 | fn test_is_not_async() {}
| ^^
error: the test function cannot accept arguments
--> $DIR/macros_invalid_input.rs:16:27
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
--> $DIR/macros_invalid_input.rs:15:15
|
16 | async fn test_fn_has_args(_x: u8) {}
| ^^^^^^
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`
--> $DIR/macros_invalid_input.rs:18:15
|
18 | #[tokio::test(foo)]
15 | #[tokio::test(foo)]
| ^^^
error: second test attribute is supplied
--> $DIR/macros_invalid_input.rs:22:1
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
--> $DIR/macros_invalid_input.rs:18:15
|
22 | #[test]
18 | #[tokio::test(foo = 123)]
| ^^^^^^^^^
error: Failed to parse value of `flavor` as string.
--> $DIR/macros_invalid_input.rs:21:24
|
21 | #[tokio::test(flavor = 123)]
| ^^^
error: No such runtime flavor `foo`. The runtime flavors are `current_thread` and `multi_thread`.
--> $DIR/macros_invalid_input.rs:24:24
|
24 | #[tokio::test(flavor = "foo")]
| ^^^^^
error: The `start_paused` option requires the `current_thread` runtime flavor. Use `#[tokio::test(flavor = "current_thread")]`
--> $DIR/macros_invalid_input.rs:27:55
|
27 | #[tokio::test(flavor = "multi_thread", start_paused = false)]
| ^^^^^
error: Failed to parse value of `worker_threads` as integer.
--> $DIR/macros_invalid_input.rs:30:57
|
30 | #[tokio::test(flavor = "multi_thread", worker_threads = "foo")]
| ^^^^^
error: The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[tokio::test(flavor = "multi_thread")]`
--> $DIR/macros_invalid_input.rs:33:59
|
33 | #[tokio::test(flavor = "current_thread", worker_threads = 4)]
| ^
error: second test attribute is supplied
--> $DIR/macros_invalid_input.rs:37:1
|
37 | #[test]
| ^^^^^^^
+3
View File
@@ -2,6 +2,9 @@
fn compile_fail_full() {
let t = trybuild::TestCases::new();
#[cfg(feature = "full")]
t.pass("tests/pass/forward_args_and_output.rs");
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_invalid_input.rs");
@@ -0,0 +1,13 @@
use tests_build::tokio;
fn main() {}
// arguments and output type is forwarded so other macros can access them
#[tokio::test]
async fn test_fn_has_args(_x: u8) {}
#[tokio::test]
async fn test_has_output() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
+16 -2
View File
@@ -5,7 +5,23 @@ authors = ["Tokio Contributors <[email protected]>"]
edition = "2018"
publish = false
[[bin]]
name = "test-cat"
[[bin]]
name = "test-mem"
required-features = ["rt-net"]
[[bin]]
name = "test-process-signal"
required-features = ["rt-process-signal"]
[features]
# For mem check
rt-net = ["tokio/rt", "tokio/rt-multi-thread", "tokio/net"]
# For test-process-signal
rt-process-signal = ["rt", "tokio/process", "tokio/signal"]
full = [
"macros",
"rt",
@@ -23,6 +39,4 @@ rt-multi-thread = ["rt", "tokio/rt-multi-thread"]
tokio = { path = "../tokio" }
tokio-test = { path = "../tokio-test", optional = true }
doc-comment = "0.3.1"
[dev-dependencies]
futures = { version = "0.3.0", features = ["async-await"] }
+21
View File
@@ -0,0 +1,21 @@
use futures::future::poll_fn;
fn main() {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.enable_io()
.build()
.unwrap();
rt.block_on(async {
let listener = tokio::net::TcpListener::bind("0.0.0.0:0").await.unwrap();
tokio::spawn(async move {
loop {
poll_fn(|cx| listener.poll_accept(cx)).await.unwrap();
}
});
});
std::thread::sleep(std::time::Duration::from_millis(50));
drop(rt);
}
@@ -0,0 +1,11 @@
// https://github.com/tokio-rs/tokio/issues/3550
fn main() {
for _ in 0..1000 {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
drop(rt);
}
}
+53 -11
View File
@@ -1,26 +1,19 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
use tokio::join;
use tokio::process::{Child, Command};
use tokio_test::assert_ok;
use futures::future::{self, FutureExt};
use std::convert::TryInto;
use std::env;
use std::io;
use std::process::{ExitStatus, Stdio};
fn cat() -> Command {
let mut me = env::current_exe().unwrap();
me.pop();
if me.ends_with("deps") {
me.pop();
}
me.push("test-cat");
let mut cmd = Command::new(me);
let mut cmd = Command::new(env!("CARGO_BIN_EXE_test-cat"));
cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
cmd
}
@@ -148,3 +141,52 @@ async fn try_wait() {
// Can't get id after process has exited
assert_eq!(child.id(), None);
}
#[tokio::test]
async fn pipe_from_one_command_to_another() {
let mut first = cat().spawn().expect("first cmd");
let mut third = cat().spawn().expect("third cmd");
// Convert ChildStdout to Stdio
let second_stdin: Stdio = first
.stdout
.take()
.expect("first.stdout")
.try_into()
.expect("first.stdout into Stdio");
// Convert ChildStdin to Stdio
let second_stdout: Stdio = third
.stdin
.take()
.expect("third.stdin")
.try_into()
.expect("third.stdin into Stdio");
let mut second = cat()
.stdin(second_stdin)
.stdout(second_stdout)
.spawn()
.expect("first cmd");
let msg = "hello world! please pipe this message through";
let mut stdin = first.stdin.take().expect("first.stdin");
let write = async move { stdin.write_all(msg.as_bytes()).await };
let mut stdout = third.stdout.take().expect("third.stdout");
let read = async move {
let mut data = String::new();
stdout.read_to_string(&mut data).await.map(|_| data)
};
let (read, write, first_status, second_status, third_status) =
join!(read, write, first.wait(), second.wait(), third.wait());
assert_eq!(msg, read.expect("read result"));
write.expect("write result");
assert!(first_status.expect("first status").success());
assert!(second_status.expect("second status").success());
assert!(third_status.expect("third status").success());
}
+5
View File
@@ -1,3 +1,7 @@
# 1.1.0 (February 5, 2021)
- add `start_paused` option to macros ([#3492])
# 1.0.0 (December 23, 2020)
- track `tokio` 1.0 release.
@@ -55,3 +59,4 @@
[#2177]: https://github.com/tokio-rs/tokio/pull/2177
[#2225]: https://github.com/tokio-rs/tokio/pull/2225
[#3038]: https://github.com/tokio-rs/tokio/pull/3038
[#3492]: https://github.com/tokio-rs/tokio/pull/3492
+2 -3
View File
@@ -2,18 +2,17 @@
name = "tokio-macros"
# When releasing to crates.io:
# - Remove path dependencies
# - Update html_root_url.
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-macros-1.0.x" git tag.
version = "1.0.0"
version = "1.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-macros/1.0.0/tokio_macros"
documentation = "https://docs.rs/tokio-macros/1.1.0/tokio_macros"
description = """
Tokio's proc macros.
"""
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2020 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+113 -61
View File
@@ -1,7 +1,6 @@
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::spanned::Spanned;
#[derive(Clone, Copy, PartialEq)]
enum RuntimeFlavor {
@@ -25,6 +24,7 @@ impl RuntimeFlavor {
struct FinalConfig {
flavor: RuntimeFlavor,
worker_threads: Option<usize>,
start_paused: Option<bool>,
}
struct Configuration {
@@ -32,6 +32,8 @@ struct Configuration {
default_flavor: RuntimeFlavor,
flavor: Option<RuntimeFlavor>,
worker_threads: Option<(usize, Span)>,
start_paused: Option<(bool, Span)>,
is_test: bool,
}
impl Configuration {
@@ -44,6 +46,8 @@ impl Configuration {
},
flavor: None,
worker_threads: None,
start_paused: None,
is_test,
}
}
@@ -79,31 +83,67 @@ impl Configuration {
Ok(())
}
fn set_start_paused(&mut self, start_paused: syn::Lit, span: Span) -> Result<(), syn::Error> {
if self.start_paused.is_some() {
return Err(syn::Error::new(span, "`start_paused` set multiple times."));
}
let start_paused = parse_bool(start_paused, span, "start_paused")?;
self.start_paused = Some((start_paused, span));
Ok(())
}
fn macro_name(&self) -> &'static str {
if self.is_test {
"tokio::test"
} else {
"tokio::main"
}
}
fn build(&self) -> Result<FinalConfig, syn::Error> {
let flavor = self.flavor.unwrap_or(self.default_flavor);
use RuntimeFlavor::*;
match (flavor, self.worker_threads) {
(CurrentThread, Some((_, worker_threads_span))) => Err(syn::Error::new(
worker_threads_span,
"The `worker_threads` option requires the `multi_thread` runtime flavor.",
)),
(CurrentThread, None) => Ok(FinalConfig {
flavor,
worker_threads: None,
}),
(Threaded, worker_threads) if self.rt_multi_thread_available => Ok(FinalConfig {
flavor,
worker_threads: worker_threads.map(|(val, _span)| val),
}),
let worker_threads = match (flavor, self.worker_threads) {
(CurrentThread, Some((_, worker_threads_span))) => {
let msg = format!(
"The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[{}(flavor = \"multi_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(worker_threads_span, msg));
}
(CurrentThread, None) => None,
(Threaded, worker_threads) if self.rt_multi_thread_available => {
worker_threads.map(|(val, _span)| val)
}
(Threaded, _) => {
let msg = if self.flavor.is_none() {
"The default runtime flavor is `multi_thread`, but the `rt-multi-thread` feature is disabled."
} else {
"The runtime flavor `multi_thread` requires the `rt-multi-thread` feature."
};
Err(syn::Error::new(Span::call_site(), msg))
return Err(syn::Error::new(Span::call_site(), msg));
}
}
};
let start_paused = match (flavor, self.start_paused) {
(Threaded, Some((_, start_paused_span))) => {
let msg = format!(
"The `start_paused` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(start_paused_span, msg));
}
(CurrentThread, Some((start_paused, _))) => Some(start_paused),
(_, None) => None,
};
Ok(FinalConfig {
flavor,
worker_threads,
start_paused,
})
}
}
@@ -113,12 +153,12 @@ fn parse_int(int: syn::Lit, span: Span, field: &str) -> Result<usize, syn::Error
Ok(value) => Ok(value),
Err(e) => Err(syn::Error::new(
span,
format!("Failed to parse {} as integer: {}", field, e),
format!("Failed to parse value of `{}` as integer: {}", field, e),
)),
},
_ => Err(syn::Error::new(
span,
format!("Failed to parse {} as integer.", field),
format!("Failed to parse value of `{}` as integer.", field),
)),
}
}
@@ -129,7 +169,17 @@ fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result<String, syn::E
syn::Lit::Verbatim(s) => Ok(s.to_string()),
_ => Err(syn::Error::new(
span,
format!("Failed to parse {} as string.", field),
format!("Failed to parse value of `{}` as string.", field),
)),
}
}
fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Error> {
match bool {
syn::Lit::Bool(b) => Ok(b.value),
_ => Err(syn::Error::new(
span,
format!("Failed to parse value of `{}` as bool.", field),
)),
}
}
@@ -140,24 +190,13 @@ fn parse_knobs(
is_test: bool,
rt_multi_thread: bool,
) -> Result<TokenStream, syn::Error> {
let sig = &mut input.sig;
let body = &input.block;
let attrs = &input.attrs;
let vis = input.vis;
if sig.asyncness.is_none() {
let msg = "the async keyword is missing from the function declaration";
return Err(syn::Error::new_spanned(sig.fn_token, msg));
if input.sig.asyncness.take().is_none() {
let msg = "the `async` keyword is missing from the function declaration";
return Err(syn::Error::new_spanned(input.sig.fn_token, msg));
}
sig.asyncness = None;
let macro_name = if is_test {
"tokio::test"
} else {
"tokio::main"
};
let mut config = Configuration::new(is_test, rt_multi_thread);
let macro_name = config.macro_name();
for arg in args {
match arg {
@@ -169,17 +208,32 @@ fn parse_knobs(
}
match ident.unwrap().to_string().to_lowercase().as_str() {
"worker_threads" => {
config.set_worker_threads(namevalue.lit.clone(), namevalue.span())?;
config.set_worker_threads(
namevalue.lit.clone(),
syn::spanned::Spanned::span(&namevalue.lit),
)?;
}
"flavor" => {
config.set_flavor(namevalue.lit.clone(), namevalue.span())?;
config.set_flavor(
namevalue.lit.clone(),
syn::spanned::Spanned::span(&namevalue.lit),
)?;
}
"start_paused" => {
config.set_start_paused(
namevalue.lit.clone(),
syn::spanned::Spanned::span(&namevalue.lit),
)?;
}
"core_threads" => {
let msg = "Attribute `core_threads` is renamed to `worker_threads`";
return Err(syn::Error::new_spanned(namevalue, msg));
}
name => {
let msg = format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`", name);
let msg = format!(
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`",
name,
);
return Err(syn::Error::new_spanned(namevalue, msg));
}
}
@@ -204,11 +258,11 @@ fn parse_knobs(
macro_name
)
}
"flavor" | "worker_threads" => {
"flavor" | "worker_threads" | "start_paused" => {
format!("The `{}` attribute requires an argument.", name)
}
name => {
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`", name)
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`", name)
}
};
return Err(syn::Error::new_spanned(path, msg));
@@ -235,27 +289,32 @@ fn parse_knobs(
if let Some(v) = config.worker_threads {
rt = quote! { #rt.worker_threads(#v) };
}
if let Some(v) = config.start_paused {
rt = quote! { #rt.start_paused(#v) };
}
let header = {
if is_test {
quote! {
#[::core::prelude::v1::test]
}
} else {
quote! {}
let header = if is_test {
quote! {
#[::core::prelude::v1::test]
}
} else {
quote! {}
};
let body = &input.block;
input.block = syn::parse_quote! {
{
#rt
.enable_all()
.build()
.unwrap()
.block_on(async #body)
}
};
let result = quote! {
#header
#(#attrs)*
#vis #sig {
#rt
.enable_all()
.build()
.unwrap()
.block_on(async { #body })
}
#input
};
Ok(result.into())
@@ -289,12 +348,5 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
}
}
if !input.sig.inputs.is_empty() {
let msg = "the test function cannot accept arguments";
return syn::Error::new_spanned(&input.sig.inputs, msg)
.to_compile_error()
.into();
}
parse_knobs(input, args, true, rt_multi_thread).unwrap_or_else(|e| e.to_compile_error().into())
}
+37 -1
View File
@@ -1,4 +1,3 @@
#![doc(html_root_url = "https://docs.rs/tokio-macros/1.0.0")]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -144,6 +143,32 @@ use proc_macro::TokenStream;
/// }
/// ```
///
/// ### Configure the runtime to start with time paused
///
/// ```rust
/// #[tokio::main(flavor = "current_thread", start_paused = true)]
/// async fn main() {
/// println!("Hello world");
/// }
/// ```
///
/// Equivalent code not using `#[tokio::main]`
///
/// ```rust
/// fn main() {
/// tokio::runtime::Builder::new_current_thread()
/// .enable_all()
/// .start_paused(true)
/// .build()
/// .unwrap()
/// .block_on(async {
/// println!("Hello world");
/// })
/// }
/// ```
///
/// Note that `start_paused` requires the `test-util` feature to be enabled.
///
/// ### NOTE:
///
/// If you rename the Tokio crate in your dependencies this macro will not work.
@@ -225,6 +250,17 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// }
/// ```
///
/// ### Configure the runtime to start with time paused
///
/// ```no_run
/// #[tokio::test(start_paused = true)]
/// async fn my_test() {
/// assert!(true);
/// }
/// ```
///
/// Note that `start_paused` requires the `test-util` feature to be enabled.
///
/// ### NOTE:
///
/// If you rename the Tokio crate in your dependencies this macro will not work.
+54 -1
View File
@@ -1,3 +1,56 @@
# 0.1.5 (March 20, 2021)
### Fixed
- stream: documentation note for throttle `Unpin` ([#3600])
[#3600]: https://github.com/tokio-rs/tokio/pull/3600
# 0.1.4 (March 9, 2021)
Added
- signal: add `Signal` wrapper ([#3510])
Fixed
- stream: remove duplicate `doc_cfg` declaration ([#3561])
- sync: yield initial value in `WatchStream` ([#3576])
[#3510]: https://github.com/tokio-rs/tokio/pull/3510
[#3561]: https://github.com/tokio-rs/tokio/pull/3561
[#3576]: https://github.com/tokio-rs/tokio/pull/3576
# 0.1.3 (February 5, 2021)
Added
- sync: add wrapper for broadcast and watch ([#3384], [#3504])
[#3384]: https://github.com/tokio-rs/tokio/pull/3384
[#3504]: https://github.com/tokio-rs/tokio/pull/3504
# 0.1.2 (January 12, 2021)
Fixed
- docs: fix some wrappers missing in documentation ([#3378])
[#3378]: https://github.com/tokio-rs/tokio/pull/3378
# 0.1.1 (January 4, 2021)
Added
- add `Stream` wrappers ([#3343])
Fixed
- move `async-stream` to `dev-dependencies` ([#3366])
[#3366]: https://github.com/tokio-rs/tokio/pull/3366
[#3343]: https://github.com/tokio-rs/tokio/pull/3343
# 0.1.0 (December 23, 2020)
- Initial release
- Initial release
+16 -7
View File
@@ -2,18 +2,17 @@
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"
version = "0.1.5"
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"
documentation = "https://docs.rs/tokio-stream/0.1.5/tokio_stream"
description = """
Utilities to work with `Stream` and `tokio`.
"""
@@ -22,16 +21,26 @@ categories = ["asynchronous"]
[features]
default = ["time"]
time = ["tokio/time"]
net = ["tokio/net"]
io-util = ["tokio/io-util"]
fs = ["tokio/fs"]
sync = ["tokio/sync", "tokio-util"]
signal = ["tokio/signal"]
[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"
tokio = { version = "1.2.0", path = "../tokio", features = ["sync"] }
tokio-util = { version = "0.6.3", optional = true }
[dev-dependencies]
tokio = { version = "1.0", path = "../tokio", features = ["full"] }
tokio = { version = "1.2.0", path = "../tokio", features = ["full", "test-util"] }
async-stream = "0.3"
tokio-test = { path = "../tokio-test" }
futures = { version = "0.3", default-features = false }
proptest = "0.10.0"
proptest = "1"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2020 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+3 -922
View File
@@ -1,4 +1,3 @@
#![doc(html_root_url = "https://docs.rs/tokio-stream/0.1.0")]
#![allow(
clippy::cognitive_complexity,
clippy::large_enum_variant,
@@ -10,18 +9,12 @@
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.
//!
@@ -81,46 +74,17 @@
#[macro_use]
mod macros;
mod all;
use all::AllFuture;
pub mod wrappers;
mod any;
use any::AnyFuture;
mod chain;
use chain::Chain;
mod collect;
use collect::Collect;
pub use collect::FromStream;
mod stream_ext;
pub use stream_ext::{collect::FromStream, StreamExt};
mod empty;
pub use empty::{empty, Empty};
mod filter;
use filter::Filter;
mod filter_map;
use filter_map::FilterMap;
mod fold;
use fold::FoldFuture;
mod fuse;
use fuse::Fuse;
mod iter;
pub use iter::{iter, Iter};
mod map;
use map::Map;
mod merge;
use merge::Merge;
mod next;
use next::Next;
mod once;
pub use once::{once, Once};
@@ -130,888 +94,5 @@ pub use pending::{pending, Pending};
mod stream_map;
pub use stream_map::StreamMap;
mod skip;
use skip::Skip;
mod skip_while;
use skip_while::SkipWhile;
mod try_next;
use try_next::TryNext;
mod take;
use take::Take;
mod take_while;
use take_while::TakeWhile;
cfg_time! {
mod timeout;
use timeout::Timeout;
use tokio::time::Duration;
mod throttle;
use crate::throttle::{throttle, Throttle};
}
#[doc(no_inline)]
pub use futures_core::Stream;
/// 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.
///
/// Equivalent to:
///
/// ```ignore
/// async fn next(&mut self) -> Option<Self::Item>;
/// ```
///
/// Note that because `next` doesn't take ownership over the stream,
/// the [`Stream`] type must be [`Unpin`]. If you want to use `next` with a
/// [`!Unpin`](Unpin) stream, you'll first have to pin the stream. This can
/// be done by boxing the stream using [`Box::pin`] or
/// pinning it to the stack using the `pin_mut!` macro from the `pin_utils`
/// crate.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=3);
///
/// assert_eq!(stream.next().await, Some(1));
/// assert_eq!(stream.next().await, Some(2));
/// assert_eq!(stream.next().await, Some(3));
/// assert_eq!(stream.next().await, None);
/// # }
/// ```
fn next(&mut self) -> Next<'_, Self>
where
Self: Unpin,
{
Next::new(self)
}
/// Consumes and returns the next item in the stream. If an error is
/// encountered before the next item, the error is returned instead.
///
/// Equivalent to:
///
/// ```ignore
/// async fn try_next(&mut self) -> Result<Option<T>, E>;
/// ```
///
/// This is similar to the [`next`](StreamExt::next) combinator,
/// but returns a [`Result<Option<T>, E>`](Result) rather than
/// an [`Option<Result<T, E>>`](Option), making for easy use
/// with the [`?`](std::ops::Try) operator.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(vec![Ok(1), Ok(2), Err("nope")]);
///
/// assert_eq!(stream.try_next().await, Ok(Some(1)));
/// assert_eq!(stream.try_next().await, Ok(Some(2)));
/// assert_eq!(stream.try_next().await, Err("nope"));
/// # }
/// ```
fn try_next<T, E>(&mut self) -> TryNext<'_, Self>
where
Self: Stream<Item = Result<T, E>> + Unpin,
{
TryNext::new(self)
}
/// Maps this stream's items to a different type, returning a new stream of
/// the resulting type.
///
/// The provided closure is executed over all elements of this stream as
/// they are made available. It is executed inline with calls to
/// [`poll_next`](Stream::poll_next).
///
/// Note that this function consumes the stream passed into it and returns a
/// wrapped version of it, similar to the existing `map` methods in the
/// standard library.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let stream = stream::iter(1..=3);
/// let mut stream = stream.map(|x| x + 3);
///
/// assert_eq!(stream.next().await, Some(4));
/// assert_eq!(stream.next().await, Some(5));
/// assert_eq!(stream.next().await, Some(6));
/// # }
/// ```
fn map<T, F>(self, f: F) -> Map<Self, F>
where
F: FnMut(Self::Item) -> T,
Self: Sized,
{
Map::new(self, f)
}
/// Combine two streams into one by interleaving the output of both as it
/// is produced.
///
/// Values are produced from the merged stream in the order they arrive from
/// the two source streams. If both source streams provide values
/// simultaneously, the merge stream alternates between them. This provides
/// some level of fairness. You should not chain calls to `merge`, as this
/// will break the fairness of the merging.
///
/// The merged stream completes once **both** source streams complete. When
/// one source stream completes before the other, the merge stream
/// exclusively polls the remaining stream.
///
/// For merging multiple streams, consider using [`StreamMap`] instead.
///
/// [`StreamMap`]: crate::StreamMap
///
/// # Examples
///
/// ```
/// use tokio_stream::{StreamExt, Stream};
/// use tokio::sync::mpsc;
/// use tokio::time;
///
/// use std::time::Duration;
/// use std::pin::Pin;
///
/// # /*
/// #[tokio::main]
/// # */
/// # #[tokio::main(flavor = "current_thread")]
/// async fn main() {
/// # time::pause();
/// 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);
///
/// tokio::spawn(async move {
/// // Send some values immediately
/// tx1.send(1).await.unwrap();
/// tx1.send(2).await.unwrap();
///
/// // Let the other task send values
/// time::sleep(Duration::from_millis(20)).await;
///
/// tx1.send(4).await.unwrap();
/// });
///
/// tokio::spawn(async move {
/// // Wait for the first task to send values
/// time::sleep(Duration::from_millis(5)).await;
///
/// tx2.send(3).await.unwrap();
///
/// time::sleep(Duration::from_millis(25)).await;
///
/// // Send the final value
/// tx2.send(5).await.unwrap();
/// });
///
/// assert_eq!(1, rx.next().await.unwrap());
/// assert_eq!(2, rx.next().await.unwrap());
/// assert_eq!(3, rx.next().await.unwrap());
/// assert_eq!(4, rx.next().await.unwrap());
/// assert_eq!(5, rx.next().await.unwrap());
///
/// // The merged stream is consumed
/// assert!(rx.next().await.is_none());
/// }
/// ```
fn merge<U>(self, other: U) -> Merge<Self, U>
where
U: Stream<Item = Self::Item>,
Self: Sized,
{
Merge::new(self, other)
}
/// Filters the values produced by this stream according to the provided
/// predicate.
///
/// As values of this stream are made available, the provided predicate `f`
/// will be run against them. If the predicate
/// resolves to `true`, then the stream will yield the value, but if the
/// predicate resolves to `false`, then the value
/// will be discarded and the next value will be produced.
///
/// Note that this function consumes the stream passed into it and returns a
/// wrapped version of it, similar to [`Iterator::filter`] method in the
/// standard library.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let stream = stream::iter(1..=8);
/// let mut evens = stream.filter(|x| x % 2 == 0);
///
/// assert_eq!(Some(2), evens.next().await);
/// assert_eq!(Some(4), evens.next().await);
/// assert_eq!(Some(6), evens.next().await);
/// assert_eq!(Some(8), evens.next().await);
/// assert_eq!(None, evens.next().await);
/// # }
/// ```
fn filter<F>(self, f: F) -> Filter<Self, F>
where
F: FnMut(&Self::Item) -> bool,
Self: Sized,
{
Filter::new(self, f)
}
/// Filters the values produced by this stream while simultaneously mapping
/// them to a different type according to the provided closure.
///
/// As values of this stream are made available, the provided function will
/// be run on them. If the predicate `f` resolves to
/// [`Some(item)`](Some) then the stream will yield the value `item`, but if
/// it resolves to [`None`], then the value will be skipped.
///
/// Note that this function consumes the stream passed into it and returns a
/// wrapped version of it, similar to [`Iterator::filter_map`] method in the
/// standard library.
///
/// # Examples
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let stream = stream::iter(1..=8);
/// let mut evens = stream.filter_map(|x| {
/// if x % 2 == 0 { Some(x + 1) } else { None }
/// });
///
/// assert_eq!(Some(3), evens.next().await);
/// assert_eq!(Some(5), evens.next().await);
/// assert_eq!(Some(7), evens.next().await);
/// assert_eq!(Some(9), evens.next().await);
/// assert_eq!(None, evens.next().await);
/// # }
/// ```
fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where
F: FnMut(Self::Item) -> Option<T>,
Self: Sized,
{
FilterMap::new(self, f)
}
/// Creates a stream which ends after the first `None`.
///
/// After a stream returns `None`, behavior is undefined. Future calls to
/// `poll_next` may or may not return `Some(T)` again or they may panic.
/// `fuse()` adapts a stream, ensuring that after `None` is given, it will
/// return `None` forever.
///
/// # Examples
///
/// ```
/// use tokio_stream::{Stream, StreamExt};
///
/// use std::pin::Pin;
/// use std::task::{Context, Poll};
///
/// // a stream which alternates between Some and None
/// struct Alternate {
/// state: i32,
/// }
///
/// impl Stream for Alternate {
/// type Item = i32;
///
/// fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<i32>> {
/// let val = self.state;
/// self.state = self.state + 1;
///
/// // if it's even, Some(i32), else None
/// if val % 2 == 0 {
/// Poll::Ready(Some(val))
/// } else {
/// Poll::Ready(None)
/// }
/// }
/// }
///
/// #[tokio::main]
/// async fn main() {
/// let mut stream = Alternate { state: 0 };
///
/// // the stream goes back and forth
/// assert_eq!(stream.next().await, Some(0));
/// assert_eq!(stream.next().await, None);
/// assert_eq!(stream.next().await, Some(2));
/// assert_eq!(stream.next().await, None);
///
/// // however, once it is fused
/// let mut stream = stream.fuse();
///
/// assert_eq!(stream.next().await, Some(4));
/// assert_eq!(stream.next().await, None);
///
/// // it will always return `None` after the first time.
/// assert_eq!(stream.next().await, None);
/// assert_eq!(stream.next().await, None);
/// assert_eq!(stream.next().await, None);
/// }
/// ```
fn fuse(self) -> Fuse<Self>
where
Self: Sized,
{
Fuse::new(self)
}
/// Creates a new stream of at most `n` items of the underlying stream.
///
/// Once `n` items have been yielded from this stream then it will always
/// return that the stream is done.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=10).take(3);
///
/// assert_eq!(Some(1), stream.next().await);
/// assert_eq!(Some(2), stream.next().await);
/// assert_eq!(Some(3), stream.next().await);
/// assert_eq!(None, stream.next().await);
/// # }
/// ```
fn take(self, n: usize) -> Take<Self>
where
Self: Sized,
{
Take::new(self, n)
}
/// Take elements from this stream while the provided predicate
/// resolves to `true`.
///
/// This function, like `Iterator::take_while`, will take elements from the
/// stream until the predicate `f` resolves to `false`. Once one element
/// returns false it will always return that the stream is done.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=10).take_while(|x| *x <= 3);
///
/// assert_eq!(Some(1), stream.next().await);
/// assert_eq!(Some(2), stream.next().await);
/// assert_eq!(Some(3), stream.next().await);
/// assert_eq!(None, stream.next().await);
/// # }
/// ```
fn take_while<F>(self, f: F) -> TakeWhile<Self, F>
where
F: FnMut(&Self::Item) -> bool,
Self: Sized,
{
TakeWhile::new(self, f)
}
/// Creates a new stream that will skip the `n` first items of the
/// underlying stream.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=10).skip(7);
///
/// assert_eq!(Some(8), stream.next().await);
/// assert_eq!(Some(9), stream.next().await);
/// assert_eq!(Some(10), stream.next().await);
/// assert_eq!(None, stream.next().await);
/// # }
/// ```
fn skip(self, n: usize) -> Skip<Self>
where
Self: Sized,
{
Skip::new(self, n)
}
/// Skip elements from the underlying stream while the provided predicate
/// resolves to `true`.
///
/// This function, like [`Iterator::skip_while`], will ignore elemets from the
/// stream until the predicate `f` resolves to `false`. Once one element
/// returns false, the rest of the elements will be yielded.
///
/// [`Iterator::skip_while`]: std::iter::Iterator::skip_while()
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
/// let mut stream = stream::iter(vec![1,2,3,4,1]).skip_while(|x| *x < 3);
///
/// assert_eq!(Some(3), stream.next().await);
/// assert_eq!(Some(4), stream.next().await);
/// assert_eq!(Some(1), stream.next().await);
/// assert_eq!(None, stream.next().await);
/// # }
/// ```
fn skip_while<F>(self, f: F) -> SkipWhile<Self, F>
where
F: FnMut(&Self::Item) -> bool,
Self: Sized,
{
SkipWhile::new(self, f)
}
/// Tests if every element of the stream matches a predicate.
///
/// Equivalent to:
///
/// ```ignore
/// async fn all<F>(&mut self, f: F) -> bool;
/// ```
///
/// `all()` takes a closure that returns `true` or `false`. It applies
/// this closure to each element of the stream, and if they all return
/// `true`, then so does `all`. If any of them return `false`, it
/// returns `false`. An empty stream returns `true`.
///
/// `all()` is short-circuiting; in other words, it will stop processing
/// as soon as it finds a `false`, given that no matter what else happens,
/// the result will also be `false`.
///
/// An empty stream returns `true`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
/// assert!(stream::iter(&a).all(|&x| x > 0).await);
///
/// assert!(!stream::iter(&a).all(|&x| x > 2).await);
/// # }
/// ```
///
/// Stopping at the first `false`:
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
/// let mut iter = stream::iter(&a);
///
/// assert!(!iter.all(|&x| x != 2).await);
///
/// // we can still use `iter`, as there are more elements.
/// assert_eq!(iter.next().await, Some(&3));
/// # }
/// ```
fn all<F>(&mut self, f: F) -> AllFuture<'_, Self, F>
where
Self: Unpin,
F: FnMut(Self::Item) -> bool,
{
AllFuture::new(self, f)
}
/// Tests if any element of the stream matches a predicate.
///
/// Equivalent to:
///
/// ```ignore
/// async fn any<F>(&mut self, f: F) -> bool;
/// ```
///
/// `any()` takes a closure that returns `true` or `false`. It applies
/// this closure to each element of the stream, and if any of them return
/// `true`, then so does `any()`. If they all return `false`, it
/// returns `false`.
///
/// `any()` is short-circuiting; in other words, it will stop processing
/// as soon as it finds a `true`, given that no matter what else happens,
/// the result will also be `true`.
///
/// An empty stream returns `false`.
///
/// Basic usage:
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
/// assert!(stream::iter(&a).any(|&x| x > 0).await);
///
/// assert!(!stream::iter(&a).any(|&x| x > 5).await);
/// # }
/// ```
///
/// Stopping at the first `true`:
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
/// let mut iter = stream::iter(&a);
///
/// assert!(iter.any(|&x| x != 2).await);
///
/// // we can still use `iter`, as there are more elements.
/// assert_eq!(iter.next().await, Some(&2));
/// # }
/// ```
fn any<F>(&mut self, f: F) -> AnyFuture<'_, Self, F>
where
Self: Unpin,
F: FnMut(Self::Item) -> bool,
{
AnyFuture::new(self, f)
}
/// Combine two streams into one by first returning all values from the
/// first stream then all values from the second stream.
///
/// As long as `self` still has values to emit, no values from `other` are
/// emitted, even if some are ready.
///
/// # Examples
///
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// let one = stream::iter(vec![1, 2, 3]);
/// let two = stream::iter(vec![4, 5, 6]);
///
/// let mut stream = one.chain(two);
///
/// assert_eq!(stream.next().await, Some(1));
/// assert_eq!(stream.next().await, Some(2));
/// assert_eq!(stream.next().await, Some(3));
/// assert_eq!(stream.next().await, Some(4));
/// assert_eq!(stream.next().await, Some(5));
/// assert_eq!(stream.next().await, Some(6));
/// assert_eq!(stream.next().await, None);
/// }
/// ```
fn chain<U>(self, other: U) -> Chain<Self, U>
where
U: Stream<Item = Self::Item>,
Self: Sized,
{
Chain::new(self, other)
}
/// A combinator that applies a function to every element in a stream
/// producing a single, final value.
///
/// Equivalent to:
///
/// ```ignore
/// async fn fold<B, F>(self, init: B, f: F) -> B;
/// ```
///
/// # Examples
/// Basic usage:
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, *};
///
/// let s = stream::iter(vec![1u8, 2, 3]);
/// let sum = s.fold(0, |acc, x| acc + x).await;
///
/// assert_eq!(sum, 6);
/// # }
/// ```
fn fold<B, F>(self, init: B, f: F) -> FoldFuture<Self, B, F>
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
FoldFuture::new(self, init, f)
}
/// Drain stream pushing all emitted values into a collection.
///
/// Equivalent to:
///
/// ```ignore
/// async fn collect<T>(self) -> T;
/// ```
///
/// `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`]: https://docs.rs/bytes/0.6.0/bytes/struct.Bytes.html
///
/// # `Result`
///
/// `collect()` can also be used with streams of type `Result<T, E>` where
/// `T: FromStream<_>`. In this case, `collect()` will stream as long as
/// values yielded from the stream are `Ok(_)`. If `Err(_)` is encountered,
/// streaming is terminated and `collect()` returns the `Err`.
///
/// # Notes
///
/// `FromStream` is currently a sealed trait. Stabilization is pending
/// enhancements to the Rust language.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// let doubled: Vec<i32> =
/// stream::iter(vec![1, 2, 3])
/// .map(|x| x * 2)
/// .collect()
/// .await;
///
/// assert_eq!(vec![2, 4, 6], doubled);
/// }
/// ```
///
/// Collecting a stream of `Result` values
///
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// // A stream containing only `Ok` values will be collected
/// let values: Result<Vec<i32>, &str> =
/// stream::iter(vec![Ok(1), Ok(2), Ok(3)])
/// .collect()
/// .await;
///
/// assert_eq!(Ok(vec![1, 2, 3]), values);
///
/// // A stream containing `Err` values will return the first error.
/// let results = vec![Ok(1), Err("no"), Ok(2), Ok(3), Err("nein")];
///
/// let values: Result<Vec<i32>, &str> =
/// stream::iter(results)
/// .collect()
/// .await;
///
/// assert_eq!(Err("no"), values);
/// }
/// ```
fn collect<T>(self) -> Collect<Self, T>
where
T: FromStream<Self::Item>,
Self: Sized,
{
Collect::new(self)
}
/// Applies a per-item timeout to the passed stream.
///
/// `timeout()` takes a `Duration` that represents the maximum amount of
/// time each element of the stream has to complete before timing out.
///
/// If the wrapped stream yields a value before the deadline is reached, the
/// value is returned. Otherwise, an error is returned. The caller may decide
/// to continue consuming the stream and will eventually get the next source
/// stream value once it becomes available.
///
/// # Notes
///
/// This function consumes the stream passed into it and returns a
/// wrapped version of it.
///
/// Polling the returned stream will continue to poll the inner stream even
/// if one or more items time out.
///
/// # Examples
///
/// Suppose we have a stream `int_stream` that yields 3 numbers (1, 2, 3):
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
/// use std::time::Duration;
/// # let int_stream = stream::iter(1..=3);
///
/// let int_stream = int_stream.timeout(Duration::from_secs(1));
/// tokio::pin!(int_stream);
///
/// // When no items time out, we get the 3 elements in succession:
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(2)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(3)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
///
/// // If the second item times out, we get an error and continue polling the stream:
/// # let mut int_stream = stream::iter(vec![Ok(1), Err(()), Ok(2), Ok(3)]);
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert!(int_stream.try_next().await.is_err());
/// assert_eq!(int_stream.try_next().await, Ok(Some(2)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(3)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
///
/// // If we want to stop consuming the source stream the first time an
/// // element times out, we can use the `take_while` operator:
/// # let int_stream = stream::iter(vec![Ok(1), Err(()), Ok(2), Ok(3)]);
/// let mut int_stream = int_stream.take_while(Result::is_ok);
///
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
/// # }
/// ```
#[cfg(all(feature = "time"))]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
fn timeout(self, duration: Duration) -> Timeout<Self>
where
Self: Sized,
{
Timeout::new(self, duration)
}
/// Slows down a stream by enforcing a delay between items.
///
/// # Example
///
/// Create a throttled stream.
/// ```rust,no_run
/// use std::time::Duration;
/// use tokio_stream::StreamExt;
///
/// # async fn dox() {
/// 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
/// println!("{:?}", item_stream.next().await);
/// }
/// # }
/// ```
#[cfg(all(feature = "time"))]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
fn throttle(self, duration: Duration) -> Throttle<Self>
where
Self: Sized,
{
throttle(duration, self)
}
}
impl<St: ?Sized> StreamExt for St where St: Stream {}
/// Merge the size hints from two streams.
fn merge_size_hints(
(left_low, left_high): (usize, Option<usize>),
(right_low, right_hign): (usize, Option<usize>),
) -> (usize, Option<usize>) {
let low = left_low.saturating_add(right_low);
let high = match (left_high, right_hign) {
(Some(h1), Some(h2)) => h1.checked_add(h2),
_ => None,
};
(low, high)
}
+50
View File
@@ -1,3 +1,33 @@
macro_rules! cfg_fs {
($($item:item)*) => {
$(
#[cfg(feature = "fs")]
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
$item
)*
}
}
macro_rules! cfg_io_util {
($($item:item)*) => {
$(
#[cfg(feature = "io-util")]
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
$item
)*
}
}
macro_rules! cfg_net {
($($item:item)*) => {
$(
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
$item
)*
}
}
macro_rules! cfg_time {
($($item:item)*) => {
$(
@@ -8,6 +38,26 @@ macro_rules! cfg_time {
}
}
macro_rules! cfg_sync {
($($item:item)*) => {
$(
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
$item
)*
}
}
macro_rules! cfg_signal {
($($item:item)*) => {
$(
#[cfg(feature = "signal")]
#[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
$item
)*
}
}
macro_rules! ready {
($e:expr $(,)?) => {
match $e {
+917
View File
@@ -0,0 +1,917 @@
use futures_core::Stream;
mod all;
use all::AllFuture;
mod any;
use any::AnyFuture;
mod chain;
use chain::Chain;
pub(crate) mod collect;
use collect::{Collect, FromStream};
mod filter;
use filter::Filter;
mod filter_map;
use filter_map::FilterMap;
mod fold;
use fold::FoldFuture;
mod fuse;
use fuse::Fuse;
mod map;
use map::Map;
mod merge;
use merge::Merge;
mod next;
use next::Next;
mod skip;
use skip::Skip;
mod skip_while;
use skip_while::SkipWhile;
mod try_next;
use try_next::TryNext;
mod take;
use take::Take;
mod take_while;
use take_while::TakeWhile;
cfg_time! {
mod timeout;
use timeout::Timeout;
use tokio::time::Duration;
mod throttle;
use throttle::{throttle, Throttle};
}
/// 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.
///
/// Equivalent to:
///
/// ```ignore
/// async fn next(&mut self) -> Option<Self::Item>;
/// ```
///
/// Note that because `next` doesn't take ownership over the stream,
/// the [`Stream`] type must be [`Unpin`]. If you want to use `next` with a
/// [`!Unpin`](Unpin) stream, you'll first have to pin the stream. This can
/// be done by boxing the stream using [`Box::pin`] or
/// pinning it to the stack using the `pin_mut!` macro from the `pin_utils`
/// crate.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=3);
///
/// assert_eq!(stream.next().await, Some(1));
/// assert_eq!(stream.next().await, Some(2));
/// assert_eq!(stream.next().await, Some(3));
/// assert_eq!(stream.next().await, None);
/// # }
/// ```
fn next(&mut self) -> Next<'_, Self>
where
Self: Unpin,
{
Next::new(self)
}
/// Consumes and returns the next item in the stream. If an error is
/// encountered before the next item, the error is returned instead.
///
/// Equivalent to:
///
/// ```ignore
/// async fn try_next(&mut self) -> Result<Option<T>, E>;
/// ```
///
/// This is similar to the [`next`](StreamExt::next) combinator,
/// but returns a [`Result<Option<T>, E>`](Result) rather than
/// an [`Option<Result<T, E>>`](Option), making for easy use
/// with the [`?`](std::ops::Try) operator.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(vec![Ok(1), Ok(2), Err("nope")]);
///
/// assert_eq!(stream.try_next().await, Ok(Some(1)));
/// assert_eq!(stream.try_next().await, Ok(Some(2)));
/// assert_eq!(stream.try_next().await, Err("nope"));
/// # }
/// ```
fn try_next<T, E>(&mut self) -> TryNext<'_, Self>
where
Self: Stream<Item = Result<T, E>> + Unpin,
{
TryNext::new(self)
}
/// Maps this stream's items to a different type, returning a new stream of
/// the resulting type.
///
/// The provided closure is executed over all elements of this stream as
/// they are made available. It is executed inline with calls to
/// [`poll_next`](Stream::poll_next).
///
/// Note that this function consumes the stream passed into it and returns a
/// wrapped version of it, similar to the existing `map` methods in the
/// standard library.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let stream = stream::iter(1..=3);
/// let mut stream = stream.map(|x| x + 3);
///
/// assert_eq!(stream.next().await, Some(4));
/// assert_eq!(stream.next().await, Some(5));
/// assert_eq!(stream.next().await, Some(6));
/// # }
/// ```
fn map<T, F>(self, f: F) -> Map<Self, F>
where
F: FnMut(Self::Item) -> T,
Self: Sized,
{
Map::new(self, f)
}
/// Combine two streams into one by interleaving the output of both as it
/// is produced.
///
/// Values are produced from the merged stream in the order they arrive from
/// the two source streams. If both source streams provide values
/// simultaneously, the merge stream alternates between them. This provides
/// some level of fairness. You should not chain calls to `merge`, as this
/// will break the fairness of the merging.
///
/// The merged stream completes once **both** source streams complete. When
/// one source stream completes before the other, the merge stream
/// exclusively polls the remaining stream.
///
/// For merging multiple streams, consider using [`StreamMap`] instead.
///
/// [`StreamMap`]: crate::StreamMap
///
/// # Examples
///
/// ```
/// use tokio_stream::{StreamExt, Stream};
/// use tokio::sync::mpsc;
/// use tokio::time;
///
/// use std::time::Duration;
/// use std::pin::Pin;
///
/// # /*
/// #[tokio::main]
/// # */
/// # #[tokio::main(flavor = "current_thread")]
/// async fn main() {
/// # time::pause();
/// 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);
///
/// tokio::spawn(async move {
/// // Send some values immediately
/// tx1.send(1).await.unwrap();
/// tx1.send(2).await.unwrap();
///
/// // Let the other task send values
/// time::sleep(Duration::from_millis(20)).await;
///
/// tx1.send(4).await.unwrap();
/// });
///
/// tokio::spawn(async move {
/// // Wait for the first task to send values
/// time::sleep(Duration::from_millis(5)).await;
///
/// tx2.send(3).await.unwrap();
///
/// time::sleep(Duration::from_millis(25)).await;
///
/// // Send the final value
/// tx2.send(5).await.unwrap();
/// });
///
/// assert_eq!(1, rx.next().await.unwrap());
/// assert_eq!(2, rx.next().await.unwrap());
/// assert_eq!(3, rx.next().await.unwrap());
/// assert_eq!(4, rx.next().await.unwrap());
/// assert_eq!(5, rx.next().await.unwrap());
///
/// // The merged stream is consumed
/// assert!(rx.next().await.is_none());
/// }
/// ```
fn merge<U>(self, other: U) -> Merge<Self, U>
where
U: Stream<Item = Self::Item>,
Self: Sized,
{
Merge::new(self, other)
}
/// Filters the values produced by this stream according to the provided
/// predicate.
///
/// As values of this stream are made available, the provided predicate `f`
/// will be run against them. If the predicate
/// resolves to `true`, then the stream will yield the value, but if the
/// predicate resolves to `false`, then the value
/// will be discarded and the next value will be produced.
///
/// Note that this function consumes the stream passed into it and returns a
/// wrapped version of it, similar to [`Iterator::filter`] method in the
/// standard library.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let stream = stream::iter(1..=8);
/// let mut evens = stream.filter(|x| x % 2 == 0);
///
/// assert_eq!(Some(2), evens.next().await);
/// assert_eq!(Some(4), evens.next().await);
/// assert_eq!(Some(6), evens.next().await);
/// assert_eq!(Some(8), evens.next().await);
/// assert_eq!(None, evens.next().await);
/// # }
/// ```
fn filter<F>(self, f: F) -> Filter<Self, F>
where
F: FnMut(&Self::Item) -> bool,
Self: Sized,
{
Filter::new(self, f)
}
/// Filters the values produced by this stream while simultaneously mapping
/// them to a different type according to the provided closure.
///
/// As values of this stream are made available, the provided function will
/// be run on them. If the predicate `f` resolves to
/// [`Some(item)`](Some) then the stream will yield the value `item`, but if
/// it resolves to [`None`], then the value will be skipped.
///
/// Note that this function consumes the stream passed into it and returns a
/// wrapped version of it, similar to [`Iterator::filter_map`] method in the
/// standard library.
///
/// # Examples
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let stream = stream::iter(1..=8);
/// let mut evens = stream.filter_map(|x| {
/// if x % 2 == 0 { Some(x + 1) } else { None }
/// });
///
/// assert_eq!(Some(3), evens.next().await);
/// assert_eq!(Some(5), evens.next().await);
/// assert_eq!(Some(7), evens.next().await);
/// assert_eq!(Some(9), evens.next().await);
/// assert_eq!(None, evens.next().await);
/// # }
/// ```
fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where
F: FnMut(Self::Item) -> Option<T>,
Self: Sized,
{
FilterMap::new(self, f)
}
/// Creates a stream which ends after the first `None`.
///
/// After a stream returns `None`, behavior is undefined. Future calls to
/// `poll_next` may or may not return `Some(T)` again or they may panic.
/// `fuse()` adapts a stream, ensuring that after `None` is given, it will
/// return `None` forever.
///
/// # Examples
///
/// ```
/// use tokio_stream::{Stream, StreamExt};
///
/// use std::pin::Pin;
/// use std::task::{Context, Poll};
///
/// // a stream which alternates between Some and None
/// struct Alternate {
/// state: i32,
/// }
///
/// impl Stream for Alternate {
/// type Item = i32;
///
/// fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<i32>> {
/// let val = self.state;
/// self.state = self.state + 1;
///
/// // if it's even, Some(i32), else None
/// if val % 2 == 0 {
/// Poll::Ready(Some(val))
/// } else {
/// Poll::Ready(None)
/// }
/// }
/// }
///
/// #[tokio::main]
/// async fn main() {
/// let mut stream = Alternate { state: 0 };
///
/// // the stream goes back and forth
/// assert_eq!(stream.next().await, Some(0));
/// assert_eq!(stream.next().await, None);
/// assert_eq!(stream.next().await, Some(2));
/// assert_eq!(stream.next().await, None);
///
/// // however, once it is fused
/// let mut stream = stream.fuse();
///
/// assert_eq!(stream.next().await, Some(4));
/// assert_eq!(stream.next().await, None);
///
/// // it will always return `None` after the first time.
/// assert_eq!(stream.next().await, None);
/// assert_eq!(stream.next().await, None);
/// assert_eq!(stream.next().await, None);
/// }
/// ```
fn fuse(self) -> Fuse<Self>
where
Self: Sized,
{
Fuse::new(self)
}
/// Creates a new stream of at most `n` items of the underlying stream.
///
/// Once `n` items have been yielded from this stream then it will always
/// return that the stream is done.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=10).take(3);
///
/// assert_eq!(Some(1), stream.next().await);
/// assert_eq!(Some(2), stream.next().await);
/// assert_eq!(Some(3), stream.next().await);
/// assert_eq!(None, stream.next().await);
/// # }
/// ```
fn take(self, n: usize) -> Take<Self>
where
Self: Sized,
{
Take::new(self, n)
}
/// Take elements from this stream while the provided predicate
/// resolves to `true`.
///
/// This function, like `Iterator::take_while`, will take elements from the
/// stream until the predicate `f` resolves to `false`. Once one element
/// returns false it will always return that the stream is done.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=10).take_while(|x| *x <= 3);
///
/// assert_eq!(Some(1), stream.next().await);
/// assert_eq!(Some(2), stream.next().await);
/// assert_eq!(Some(3), stream.next().await);
/// assert_eq!(None, stream.next().await);
/// # }
/// ```
fn take_while<F>(self, f: F) -> TakeWhile<Self, F>
where
F: FnMut(&Self::Item) -> bool,
Self: Sized,
{
TakeWhile::new(self, f)
}
/// Creates a new stream that will skip the `n` first items of the
/// underlying stream.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let mut stream = stream::iter(1..=10).skip(7);
///
/// assert_eq!(Some(8), stream.next().await);
/// assert_eq!(Some(9), stream.next().await);
/// assert_eq!(Some(10), stream.next().await);
/// assert_eq!(None, stream.next().await);
/// # }
/// ```
fn skip(self, n: usize) -> Skip<Self>
where
Self: Sized,
{
Skip::new(self, n)
}
/// Skip elements from the underlying stream while the provided predicate
/// resolves to `true`.
///
/// This function, like [`Iterator::skip_while`], will ignore elemets from the
/// stream until the predicate `f` resolves to `false`. Once one element
/// returns false, the rest of the elements will be yielded.
///
/// [`Iterator::skip_while`]: std::iter::Iterator::skip_while()
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
/// let mut stream = stream::iter(vec![1,2,3,4,1]).skip_while(|x| *x < 3);
///
/// assert_eq!(Some(3), stream.next().await);
/// assert_eq!(Some(4), stream.next().await);
/// assert_eq!(Some(1), stream.next().await);
/// assert_eq!(None, stream.next().await);
/// # }
/// ```
fn skip_while<F>(self, f: F) -> SkipWhile<Self, F>
where
F: FnMut(&Self::Item) -> bool,
Self: Sized,
{
SkipWhile::new(self, f)
}
/// Tests if every element of the stream matches a predicate.
///
/// Equivalent to:
///
/// ```ignore
/// async fn all<F>(&mut self, f: F) -> bool;
/// ```
///
/// `all()` takes a closure that returns `true` or `false`. It applies
/// this closure to each element of the stream, and if they all return
/// `true`, then so does `all`. If any of them return `false`, it
/// returns `false`. An empty stream returns `true`.
///
/// `all()` is short-circuiting; in other words, it will stop processing
/// as soon as it finds a `false`, given that no matter what else happens,
/// the result will also be `false`.
///
/// An empty stream returns `true`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
/// assert!(stream::iter(&a).all(|&x| x > 0).await);
///
/// assert!(!stream::iter(&a).all(|&x| x > 2).await);
/// # }
/// ```
///
/// Stopping at the first `false`:
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
/// let mut iter = stream::iter(&a);
///
/// assert!(!iter.all(|&x| x != 2).await);
///
/// // we can still use `iter`, as there are more elements.
/// assert_eq!(iter.next().await, Some(&3));
/// # }
/// ```
fn all<F>(&mut self, f: F) -> AllFuture<'_, Self, F>
where
Self: Unpin,
F: FnMut(Self::Item) -> bool,
{
AllFuture::new(self, f)
}
/// Tests if any element of the stream matches a predicate.
///
/// Equivalent to:
///
/// ```ignore
/// async fn any<F>(&mut self, f: F) -> bool;
/// ```
///
/// `any()` takes a closure that returns `true` or `false`. It applies
/// this closure to each element of the stream, and if any of them return
/// `true`, then so does `any()`. If they all return `false`, it
/// returns `false`.
///
/// `any()` is short-circuiting; in other words, it will stop processing
/// as soon as it finds a `true`, given that no matter what else happens,
/// the result will also be `true`.
///
/// An empty stream returns `false`.
///
/// Basic usage:
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
/// assert!(stream::iter(&a).any(|&x| x > 0).await);
///
/// assert!(!stream::iter(&a).any(|&x| x > 5).await);
/// # }
/// ```
///
/// Stopping at the first `true`:
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
///
/// let a = [1, 2, 3];
///
/// let mut iter = stream::iter(&a);
///
/// assert!(iter.any(|&x| x != 2).await);
///
/// // we can still use `iter`, as there are more elements.
/// assert_eq!(iter.next().await, Some(&2));
/// # }
/// ```
fn any<F>(&mut self, f: F) -> AnyFuture<'_, Self, F>
where
Self: Unpin,
F: FnMut(Self::Item) -> bool,
{
AnyFuture::new(self, f)
}
/// Combine two streams into one by first returning all values from the
/// first stream then all values from the second stream.
///
/// As long as `self` still has values to emit, no values from `other` are
/// emitted, even if some are ready.
///
/// # Examples
///
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// let one = stream::iter(vec![1, 2, 3]);
/// let two = stream::iter(vec![4, 5, 6]);
///
/// let mut stream = one.chain(two);
///
/// assert_eq!(stream.next().await, Some(1));
/// assert_eq!(stream.next().await, Some(2));
/// assert_eq!(stream.next().await, Some(3));
/// assert_eq!(stream.next().await, Some(4));
/// assert_eq!(stream.next().await, Some(5));
/// assert_eq!(stream.next().await, Some(6));
/// assert_eq!(stream.next().await, None);
/// }
/// ```
fn chain<U>(self, other: U) -> Chain<Self, U>
where
U: Stream<Item = Self::Item>,
Self: Sized,
{
Chain::new(self, other)
}
/// A combinator that applies a function to every element in a stream
/// producing a single, final value.
///
/// Equivalent to:
///
/// ```ignore
/// async fn fold<B, F>(self, init: B, f: F) -> B;
/// ```
///
/// # Examples
/// Basic usage:
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, *};
///
/// let s = stream::iter(vec![1u8, 2, 3]);
/// let sum = s.fold(0, |acc, x| acc + x).await;
///
/// assert_eq!(sum, 6);
/// # }
/// ```
fn fold<B, F>(self, init: B, f: F) -> FoldFuture<Self, B, F>
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
FoldFuture::new(self, init, f)
}
/// Drain stream pushing all emitted values into a collection.
///
/// Equivalent to:
///
/// ```ignore
/// async fn collect<T>(self) -> T;
/// ```
///
/// `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`]: https://docs.rs/bytes/0.6.0/bytes/struct.Bytes.html
///
/// # `Result`
///
/// `collect()` can also be used with streams of type `Result<T, E>` where
/// `T: FromStream<_>`. In this case, `collect()` will stream as long as
/// values yielded from the stream are `Ok(_)`. If `Err(_)` is encountered,
/// streaming is terminated and `collect()` returns the `Err`.
///
/// # Notes
///
/// `FromStream` is currently a sealed trait. Stabilization is pending
/// enhancements to the Rust language.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// let doubled: Vec<i32> =
/// stream::iter(vec![1, 2, 3])
/// .map(|x| x * 2)
/// .collect()
/// .await;
///
/// assert_eq!(vec![2, 4, 6], doubled);
/// }
/// ```
///
/// Collecting a stream of `Result` values
///
/// ```
/// use tokio_stream::{self as stream, StreamExt};
///
/// #[tokio::main]
/// async fn main() {
/// // A stream containing only `Ok` values will be collected
/// let values: Result<Vec<i32>, &str> =
/// stream::iter(vec![Ok(1), Ok(2), Ok(3)])
/// .collect()
/// .await;
///
/// assert_eq!(Ok(vec![1, 2, 3]), values);
///
/// // A stream containing `Err` values will return the first error.
/// let results = vec![Ok(1), Err("no"), Ok(2), Ok(3), Err("nein")];
///
/// let values: Result<Vec<i32>, &str> =
/// stream::iter(results)
/// .collect()
/// .await;
///
/// assert_eq!(Err("no"), values);
/// }
/// ```
fn collect<T>(self) -> Collect<Self, T>
where
T: FromStream<Self::Item>,
Self: Sized,
{
Collect::new(self)
}
/// Applies a per-item timeout to the passed stream.
///
/// `timeout()` takes a `Duration` that represents the maximum amount of
/// time each element of the stream has to complete before timing out.
///
/// If the wrapped stream yields a value before the deadline is reached, the
/// value is returned. Otherwise, an error is returned. The caller may decide
/// to continue consuming the stream and will eventually get the next source
/// stream value once it becomes available.
///
/// # Notes
///
/// This function consumes the stream passed into it and returns a
/// wrapped version of it.
///
/// Polling the returned stream will continue to poll the inner stream even
/// if one or more items time out.
///
/// # Examples
///
/// Suppose we have a stream `int_stream` that yields 3 numbers (1, 2, 3):
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{self as stream, StreamExt};
/// use std::time::Duration;
/// # let int_stream = stream::iter(1..=3);
///
/// let int_stream = int_stream.timeout(Duration::from_secs(1));
/// tokio::pin!(int_stream);
///
/// // When no items time out, we get the 3 elements in succession:
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(2)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(3)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
///
/// // If the second item times out, we get an error and continue polling the stream:
/// # let mut int_stream = stream::iter(vec![Ok(1), Err(()), Ok(2), Ok(3)]);
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert!(int_stream.try_next().await.is_err());
/// assert_eq!(int_stream.try_next().await, Ok(Some(2)));
/// assert_eq!(int_stream.try_next().await, Ok(Some(3)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
///
/// // If we want to stop consuming the source stream the first time an
/// // element times out, we can use the `take_while` operator:
/// # let int_stream = stream::iter(vec![Ok(1), Err(()), Ok(2), Ok(3)]);
/// let mut int_stream = int_stream.take_while(Result::is_ok);
///
/// assert_eq!(int_stream.try_next().await, Ok(Some(1)));
/// assert_eq!(int_stream.try_next().await, Ok(None));
/// # }
/// ```
#[cfg(all(feature = "time"))]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
fn timeout(self, duration: Duration) -> Timeout<Self>
where
Self: Sized,
{
Timeout::new(self, duration)
}
/// Slows down a stream by enforcing a delay between items.
///
/// # Example
///
/// Create a throttled stream.
/// ```rust,no_run
/// use std::time::Duration;
/// use tokio_stream::StreamExt;
///
/// # async fn dox() {
/// 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
/// println!("{:?}", item_stream.next().await);
/// }
/// # }
/// ```
#[cfg(all(feature = "time"))]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
fn throttle(self, duration: Duration) -> Throttle<Self>
where
Self: Sized,
{
throttle(duration, self)
}
}
impl<St: ?Sized> StreamExt for St where St: Stream {}
/// Merge the size hints from two streams.
fn merge_size_hints(
(left_low, left_high): (usize, Option<usize>),
(right_low, right_hign): (usize, Option<usize>),
) -> (usize, Option<usize>) {
let low = left_low.saturating_add(right_low);
let high = match (left_high, right_hign) {
(Some(h1), Some(h2)) => h1.checked_add(h2),
_ => None,
};
(low, high)
}
@@ -38,18 +38,21 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let me = self.project();
let next = futures_core::ready!(Pin::new(me.stream).poll_next(cx));
let mut stream = Pin::new(me.stream);
match next {
Some(v) => {
if !(me.f)(v) {
Poll::Ready(false)
} else {
cx.waker().wake_by_ref();
Poll::Pending
// Take a maximum of 32 items from the stream before yielding.
for _ in 0..32 {
match futures_core::ready!(stream.as_mut().poll_next(cx)) {
Some(v) => {
if !(me.f)(v) {
return Poll::Ready(false);
}
}
None => return Poll::Ready(true),
}
None => Poll::Ready(true),
}
cx.waker().wake_by_ref();
Poll::Pending
}
}
@@ -38,18 +38,21 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let me = self.project();
let next = futures_core::ready!(Pin::new(me.stream).poll_next(cx));
let mut stream = Pin::new(me.stream);
match next {
Some(v) => {
if (me.f)(v) {
Poll::Ready(true)
} else {
cx.waker().wake_by_ref();
Poll::Pending
// Take a maximum of 32 items from the stream before yielding.
for _ in 0..32 {
match futures_core::ready!(stream.as_mut().poll_next(cx)) {
Some(v) => {
if (me.f)(v) {
return Poll::Ready(true);
}
}
None => return Poll::Ready(false),
}
None => Poll::Ready(false),
}
cx.waker().wake_by_ref();
Poll::Pending
}
}
@@ -1,4 +1,5 @@
use crate::{Fuse, Stream};
use crate::stream_ext::Fuse;
use crate::Stream;
use core::pin::Pin;
use core::task::{Context, Poll};
@@ -1,4 +1,5 @@
use crate::{Fuse, Stream};
use crate::stream_ext::Fuse;
use crate::Stream;
use core::pin::Pin;
use core::task::{Context, Poll};
@@ -23,7 +23,8 @@ where
}
pin_project! {
/// Stream for the [`throttle`](throttle) function.
/// Stream for the [`throttle`](throttle) function. This object is `!Unpin`. If you need it to
/// implement `Unpin` you can pin your throttle like this: `Box::pin(your_throttle)`.
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct Throttle<T> {
@@ -1,4 +1,5 @@
use crate::{Fuse, Stream};
use crate::stream_ext::Fuse;
use crate::Stream;
use tokio::time::{Instant, Sleep};
use core::future::Future;
@@ -1,4 +1,5 @@
use crate::{Next, Stream};
use crate::stream_ext::Next;
use crate::Stream;
use core::future::Future;
use core::marker::PhantomPinned;
+71
View File
@@ -0,0 +1,71 @@
//! Wrappers for Tokio types that implement `Stream`.
//!
#![cfg_attr(
unix,
doc = "You are viewing documentation built under unix. To view windows-specific wrappers, change to the `x86_64-pc-windows-msvc` platform."
)]
#![cfg_attr(
windows,
doc = "You are viewing documentation built under windows. To view unix-specific wrappers, change to the `x86_64-unknown-linux-gnu` platform."
)]
/// Error types for the wrappers.
pub mod errors {
cfg_sync! {
pub use crate::wrappers::broadcast::BroadcastStreamRecvError;
}
}
mod mpsc_bounded;
pub use mpsc_bounded::ReceiverStream;
mod mpsc_unbounded;
pub use mpsc_unbounded::UnboundedReceiverStream;
cfg_sync! {
mod broadcast;
pub use broadcast::BroadcastStream;
mod watch;
pub use watch::WatchStream;
}
cfg_signal! {
#[cfg(unix)]
mod signal_unix;
#[cfg(unix)]
pub use signal_unix::SignalStream;
#[cfg(windows)]
mod signal_windows;
#[cfg(windows)]
pub use signal_windows::{CtrlCStream, CtrlBreakStream};
}
cfg_time! {
mod interval;
pub use interval::IntervalStream;
}
cfg_net! {
mod tcp_listener;
pub use tcp_listener::TcpListenerStream;
#[cfg(unix)]
mod unix_listener;
#[cfg(unix)]
pub use unix_listener::UnixListenerStream;
}
cfg_io_util! {
mod split;
pub use split::SplitStream;
mod lines;
pub use lines::LinesStream;
}
cfg_fs! {
mod read_dir;
pub use read_dir::ReadDirStream;
}
+63
View File
@@ -0,0 +1,63 @@
use std::pin::Pin;
use tokio::sync::broadcast::error::RecvError;
use tokio::sync::broadcast::Receiver;
use futures_core::Stream;
use tokio_util::sync::ReusableBoxFuture;
use std::fmt;
use std::task::{Context, Poll};
/// A wrapper around [`tokio::sync::broadcast::Receiver`] that implements [`Stream`].
///
/// [`tokio::sync::broadcast::Receiver`]: struct@tokio::sync::broadcast::Receiver
/// [`Stream`]: trait@crate::Stream
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
pub struct BroadcastStream<T> {
inner: ReusableBoxFuture<(Result<T, RecvError>, Receiver<T>)>,
}
/// An error returned from the inner stream of a [`BroadcastStream`].
#[derive(Debug, PartialEq)]
pub enum BroadcastStreamRecvError {
/// The receiver lagged too far behind. Attempting to receive again will
/// return the oldest message still retained by the channel.
///
/// Includes the number of skipped messages.
Lagged(u64),
}
async fn make_future<T: Clone>(mut rx: Receiver<T>) -> (Result<T, RecvError>, Receiver<T>) {
let result = rx.recv().await;
(result, rx)
}
impl<T: 'static + Clone + Send> BroadcastStream<T> {
/// Create a new `BroadcastStream`.
pub fn new(rx: Receiver<T>) -> Self {
Self {
inner: ReusableBoxFuture::new(make_future(rx)),
}
}
}
impl<T: 'static + Clone + Send> Stream for BroadcastStream<T> {
type Item = Result<T, BroadcastStreamRecvError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let (result, rx) = ready!(self.inner.poll(cx));
self.inner.set(make_future(rx));
match result {
Ok(item) => Poll::Ready(Some(Ok(item))),
Err(RecvError::Closed) => Poll::Ready(None),
Err(RecvError::Lagged(n)) => {
Poll::Ready(Some(Err(BroadcastStreamRecvError::Lagged(n))))
}
}
}
}
impl<T> fmt::Debug for BroadcastStream<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BroadcastStream").finish()
}
}
+50
View File
@@ -0,0 +1,50 @@
use crate::Stream;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::time::{Instant, Interval};
/// A wrapper around [`Interval`] that implements [`Stream`].
///
/// [`Interval`]: struct@tokio::time::Interval
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
pub struct IntervalStream {
inner: Interval,
}
impl IntervalStream {
/// Create a new `IntervalStream`.
pub fn new(interval: Interval) -> Self {
Self { inner: interval }
}
/// Get back the inner `Interval`.
pub fn into_inner(self) -> Interval {
self.inner
}
}
impl Stream for IntervalStream {
type Item = Instant;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Instant>> {
self.inner.poll_tick(cx).map(Some)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(std::usize::MAX, None)
}
}
impl AsRef<Interval> for IntervalStream {
fn as_ref(&self) -> &Interval {
&self.inner
}
}
impl AsMut<Interval> for IntervalStream {
fn as_mut(&mut self) -> &mut Interval {
&mut self.inner
}
}
+60
View File
@@ -0,0 +1,60 @@
use crate::Stream;
use pin_project_lite::pin_project;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncBufRead, Lines};
pin_project! {
/// A wrapper around [`tokio::io::Lines`] that implements [`Stream`].
///
/// [`tokio::io::Lines`]: struct@tokio::io::Lines
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
pub struct LinesStream<R> {
#[pin]
inner: Lines<R>,
}
}
impl<R> LinesStream<R> {
/// Create a new `LinesStream`.
pub fn new(lines: Lines<R>) -> Self {
Self { inner: lines }
}
/// Get back the inner `Lines`.
pub fn into_inner(self) -> Lines<R> {
self.inner
}
/// Obtain a pinned reference to the inner `Lines<R>`.
#[allow(clippy::wrong_self_convention)] // https://github.com/rust-lang/rust-clippy/issues/4546
pub fn as_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Lines<R>> {
self.project().inner
}
}
impl<R: AsyncBufRead> Stream for LinesStream<R> {
type Item = io::Result<String>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project()
.inner
.poll_next_line(cx)
.map(Result::transpose)
}
}
impl<R> AsRef<Lines<R>> for LinesStream<R> {
fn as_ref(&self) -> &Lines<R> {
&self.inner
}
}
impl<R> AsMut<Lines<R>> for LinesStream<R> {
fn as_mut(&mut self) -> &mut Lines<R> {
&mut self.inner
}
}
+59
View File
@@ -0,0 +1,59 @@
use crate::Stream;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::sync::mpsc::Receiver;
/// A wrapper around [`tokio::sync::mpsc::Receiver`] that implements [`Stream`].
///
/// [`tokio::sync::mpsc::Receiver`]: struct@tokio::sync::mpsc::Receiver
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
pub struct ReceiverStream<T> {
inner: Receiver<T>,
}
impl<T> ReceiverStream<T> {
/// Create a new `ReceiverStream`.
pub fn new(recv: Receiver<T>) -> Self {
Self { inner: recv }
}
/// Get back the inner `Receiver`.
pub fn into_inner(self) -> Receiver<T> {
self.inner
}
/// Closes the receiving half of a channel without dropping it.
///
/// This prevents any further messages from being sent on the channel while
/// still enabling the receiver to drain messages that are buffered. Any
/// outstanding [`Permit`] values will still be able to send messages.
///
/// To guarantee no messages are dropped, after calling `close()`, you must
/// receive all items from the stream until `None` is returned.
///
/// [`Permit`]: struct@tokio::sync::mpsc::Permit
pub fn close(&mut self) {
self.inner.close()
}
}
impl<T> Stream for ReceiverStream<T> {
type Item = T;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.inner.poll_recv(cx)
}
}
impl<T> AsRef<Receiver<T>> for ReceiverStream<T> {
fn as_ref(&self) -> &Receiver<T> {
&self.inner
}
}
impl<T> AsMut<Receiver<T>> for ReceiverStream<T> {
fn as_mut(&mut self) -> &mut Receiver<T> {
&mut self.inner
}
}
@@ -0,0 +1,53 @@
use crate::Stream;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::sync::mpsc::UnboundedReceiver;
/// A wrapper around [`tokio::sync::mpsc::UnboundedReceiver`] that implements [`Stream`].
///
/// [`tokio::sync::mpsc::UnboundedReceiver`]: struct@tokio::sync::mpsc::UnboundedReceiver
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
pub struct UnboundedReceiverStream<T> {
inner: UnboundedReceiver<T>,
}
impl<T> UnboundedReceiverStream<T> {
/// Create a new `UnboundedReceiverStream`.
pub fn new(recv: UnboundedReceiver<T>) -> Self {
Self { inner: recv }
}
/// Get back the inner `UnboundedReceiver`.
pub fn into_inner(self) -> UnboundedReceiver<T> {
self.inner
}
/// Closes the receiving half of a channel without dropping it.
///
/// This prevents any further messages from being sent on the channel while
/// still enabling the receiver to drain messages that are buffered.
pub fn close(&mut self) {
self.inner.close()
}
}
impl<T> Stream for UnboundedReceiverStream<T> {
type Item = T;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.inner.poll_recv(cx)
}
}
impl<T> AsRef<UnboundedReceiver<T>> for UnboundedReceiverStream<T> {
fn as_ref(&self) -> &UnboundedReceiver<T> {
&self.inner
}
}
impl<T> AsMut<UnboundedReceiver<T>> for UnboundedReceiverStream<T> {
fn as_mut(&mut self) -> &mut UnboundedReceiver<T> {
&mut self.inner
}
}
+47
View File
@@ -0,0 +1,47 @@
use crate::Stream;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::fs::{DirEntry, ReadDir};
/// A wrapper around [`tokio::fs::ReadDir`] that implements [`Stream`].
///
/// [`tokio::fs::ReadDir`]: struct@tokio::fs::ReadDir
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
pub struct ReadDirStream {
inner: ReadDir,
}
impl ReadDirStream {
/// Create a new `ReadDirStream`.
pub fn new(read_dir: ReadDir) -> Self {
Self { inner: read_dir }
}
/// Get back the inner `ReadDir`.
pub fn into_inner(self) -> ReadDir {
self.inner
}
}
impl Stream for ReadDirStream {
type Item = io::Result<DirEntry>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.inner.poll_next_entry(cx).map(Result::transpose)
}
}
impl AsRef<ReadDir> for ReadDirStream {
fn as_ref(&self) -> &ReadDir {
&self.inner
}
}
impl AsMut<ReadDir> for ReadDirStream {
fn as_mut(&mut self) -> &mut ReadDir {
&mut self.inner
}
}
+46
View File
@@ -0,0 +1,46 @@
use crate::Stream;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::signal::unix::Signal;
/// A wrapper around [`Signal`] that implements [`Stream`].
///
/// [`Signal`]: struct@tokio::signal::unix::Signal
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(all(unix, feature = "signal"))))]
pub struct SignalStream {
inner: Signal,
}
impl SignalStream {
/// Create a new `SignalStream`.
pub fn new(interval: Signal) -> Self {
Self { inner: interval }
}
/// Get back the inner `Signal`.
pub fn into_inner(self) -> Signal {
self.inner
}
}
impl Stream for SignalStream {
type Item = ();
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<()>> {
self.inner.poll_recv(cx)
}
}
impl AsRef<Signal> for SignalStream {
fn as_ref(&self) -> &Signal {
&self.inner
}
}
impl AsMut<Signal> for SignalStream {
fn as_mut(&mut self) -> &mut Signal {
&mut self.inner
}
}
@@ -0,0 +1,88 @@
use crate::Stream;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::signal::windows::{CtrlBreak, CtrlC};
/// A wrapper around [`CtrlC`] that implements [`Stream`].
///
/// [`CtrlC`]: struct@tokio::signal::windows::CtrlC
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(all(windows, feature = "signal"))))]
pub struct CtrlCStream {
inner: CtrlC,
}
impl CtrlCStream {
/// Create a new `CtrlCStream`.
pub fn new(interval: CtrlC) -> Self {
Self { inner: interval }
}
/// Get back the inner `CtrlC`.
pub fn into_inner(self) -> CtrlC {
self.inner
}
}
impl Stream for CtrlCStream {
type Item = ();
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<()>> {
self.inner.poll_recv(cx)
}
}
impl AsRef<CtrlC> for CtrlCStream {
fn as_ref(&self) -> &CtrlC {
&self.inner
}
}
impl AsMut<CtrlC> for CtrlCStream {
fn as_mut(&mut self) -> &mut CtrlC {
&mut self.inner
}
}
/// A wrapper around [`CtrlBreak`] that implements [`Stream`].
///
/// [`CtrlBreak`]: struct@tokio::signal::windows::CtrlBreak
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(all(windows, feature = "signal"))))]
pub struct CtrlBreakStream {
inner: CtrlBreak,
}
impl CtrlBreakStream {
/// Create a new `CtrlBreakStream`.
pub fn new(interval: CtrlBreak) -> Self {
Self { inner: interval }
}
/// Get back the inner `CtrlBreak`.
pub fn into_inner(self) -> CtrlBreak {
self.inner
}
}
impl Stream for CtrlBreakStream {
type Item = ();
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<()>> {
self.inner.poll_recv(cx)
}
}
impl AsRef<CtrlBreak> for CtrlBreakStream {
fn as_ref(&self) -> &CtrlBreak {
&self.inner
}
}
impl AsMut<CtrlBreak> for CtrlBreakStream {
fn as_mut(&mut self) -> &mut CtrlBreak {
&mut self.inner
}
}
+60
View File
@@ -0,0 +1,60 @@
use crate::Stream;
use pin_project_lite::pin_project;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncBufRead, Split};
pin_project! {
/// A wrapper around [`tokio::io::Split`] that implements [`Stream`].
///
/// [`tokio::io::Split`]: struct@tokio::io::Split
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
pub struct SplitStream<R> {
#[pin]
inner: Split<R>,
}
}
impl<R> SplitStream<R> {
/// Create a new `SplitStream`.
pub fn new(split: Split<R>) -> Self {
Self { inner: split }
}
/// Get back the inner `Split`.
pub fn into_inner(self) -> Split<R> {
self.inner
}
/// Obtain a pinned reference to the inner `Split<R>`.
#[allow(clippy::wrong_self_convention)] // https://github.com/rust-lang/rust-clippy/issues/4546
pub fn as_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Split<R>> {
self.project().inner
}
}
impl<R: AsyncBufRead> Stream for SplitStream<R> {
type Item = io::Result<Vec<u8>>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project()
.inner
.poll_next_segment(cx)
.map(Result::transpose)
}
}
impl<R> AsRef<Split<R>> for SplitStream<R> {
fn as_ref(&self) -> &Split<R> {
&self.inner
}
}
impl<R> AsMut<Split<R>> for SplitStream<R> {
fn as_mut(&mut self) -> &mut Split<R> {
&mut self.inner
}
}
+54
View File
@@ -0,0 +1,54 @@
use crate::Stream;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::net::{TcpListener, TcpStream};
/// A wrapper around [`TcpListener`] that implements [`Stream`].
///
/// [`TcpListener`]: struct@tokio::net::TcpListener
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
pub struct TcpListenerStream {
inner: TcpListener,
}
impl TcpListenerStream {
/// Create a new `TcpListenerStream`.
pub fn new(listener: TcpListener) -> Self {
Self { inner: listener }
}
/// Get back the inner `TcpListener`.
pub fn into_inner(self) -> TcpListener {
self.inner
}
}
impl Stream for TcpListenerStream {
type Item = io::Result<TcpStream>;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<io::Result<TcpStream>>> {
match self.inner.poll_accept(cx) {
Poll::Ready(Ok((stream, _))) => Poll::Ready(Some(Ok(stream))),
Poll::Ready(Err(err)) => Poll::Ready(Some(Err(err))),
Poll::Pending => Poll::Pending,
}
}
}
impl AsRef<TcpListener> for TcpListenerStream {
fn as_ref(&self) -> &TcpListener {
&self.inner
}
}
impl AsMut<TcpListener> for TcpListenerStream {
fn as_mut(&mut self) -> &mut TcpListener {
&mut self.inner
}
}
@@ -0,0 +1,54 @@
use crate::Stream;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::net::{UnixListener, UnixStream};
/// A wrapper around [`UnixListener`] that implements [`Stream`].
///
/// [`UnixListener`]: struct@tokio::net::UnixListener
/// [`Stream`]: trait@crate::Stream
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(all(unix, feature = "net"))))]
pub struct UnixListenerStream {
inner: UnixListener,
}
impl UnixListenerStream {
/// Create a new `UnixListenerStream`.
pub fn new(listener: UnixListener) -> Self {
Self { inner: listener }
}
/// Get back the inner `UnixListener`.
pub fn into_inner(self) -> UnixListener {
self.inner
}
}
impl Stream for UnixListenerStream {
type Item = io::Result<UnixStream>;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<io::Result<UnixStream>>> {
match self.inner.poll_accept(cx) {
Poll::Ready(Ok((stream, _))) => Poll::Ready(Some(Ok(stream))),
Poll::Ready(Err(err)) => Poll::Ready(Some(Err(err))),
Poll::Pending => Poll::Pending,
}
}
}
impl AsRef<UnixListener> for UnixListenerStream {
fn as_ref(&self) -> &UnixListener {
&self.inner
}
}
impl AsMut<UnixListener> for UnixListenerStream {
fn as_mut(&mut self) -> &mut UnixListener {
&mut self.inner
}
}
+96
View File
@@ -0,0 +1,96 @@
use std::pin::Pin;
use tokio::sync::watch::Receiver;
use futures_core::Stream;
use tokio_util::sync::ReusableBoxFuture;
use std::fmt;
use std::task::{Context, Poll};
use tokio::sync::watch::error::RecvError;
/// A wrapper around [`tokio::sync::watch::Receiver`] that implements [`Stream`].
///
/// This stream will always start by yielding the current value when the WatchStream is polled,
/// regardles of whether it was the initial value or sent afterwards.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{StreamExt, wrappers::WatchStream};
/// use tokio::sync::watch;
///
/// let (tx, rx) = watch::channel("hello");
/// let mut rx = WatchStream::new(rx);
///
/// assert_eq!(rx.next().await, Some("hello"));
///
/// tx.send("goodbye").unwrap();
/// assert_eq!(rx.next().await, Some("goodbye"));
/// # }
/// ```
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio_stream::{StreamExt, wrappers::WatchStream};
/// use tokio::sync::watch;
///
/// let (tx, rx) = watch::channel("hello");
/// let mut rx = WatchStream::new(rx);
///
/// tx.send("goodbye").unwrap();
/// assert_eq!(rx.next().await, Some("goodbye"));
/// # }
/// ```
///
/// [`tokio::sync::watch::Receiver`]: struct@tokio::sync::watch::Receiver
/// [`Stream`]: trait@crate::Stream
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
pub struct WatchStream<T> {
inner: ReusableBoxFuture<(Result<(), RecvError>, Receiver<T>)>,
}
async fn make_future<T: Clone + Send + Sync>(
mut rx: Receiver<T>,
) -> (Result<(), RecvError>, Receiver<T>) {
let result = rx.changed().await;
(result, rx)
}
impl<T: 'static + Clone + Unpin + Send + Sync> WatchStream<T> {
/// Create a new `WatchStream`.
pub fn new(rx: Receiver<T>) -> Self {
Self {
inner: ReusableBoxFuture::new(async move { (Ok(()), rx) }),
}
}
}
impl<T: Clone + 'static + Send + Sync> Stream for WatchStream<T> {
type Item = T;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let (result, rx) = ready!(self.inner.poll(cx));
match result {
Ok(_) => {
let received = (*rx.borrow()).clone();
self.inner.set(make_future(rx));
Poll::Ready(Some(received))
}
Err(_) => {
self.inner.set(make_future(rx));
Poll::Ready(None)
}
}
}
}
impl<T> Unpin for WatchStream<T> {}
impl<T> fmt::Debug for WatchStream<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WatchStream").finish()
}
}
+6
View File
@@ -1,3 +1,9 @@
# 0.4.1 (March 10, 2021)
- Fix `io::Mock` to be `Send` and `Sync` ([#3594])
[#3594]: https://github.com/tokio-rs/tokio/pull/3594
# 0.4.0 (December 23, 2020)
- Track `tokio` 1.0 release.
+4 -5
View File
@@ -2,25 +2,24 @@
name = "tokio-test"
# When releasing to crates.io:
# - Remove path dependencies
# - Update html_root_url.
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-test-0.4.x" git tag.
version = "0.4.0"
version = "0.4.1"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-test/0.4.0/tokio_test"
documentation = "https://docs.rs/tokio-test/0.4.1/tokio_test"
description = """
Testing utilities for Tokio- and futures-based code
"""
categories = ["asynchronous", "testing"]
[dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["rt", "sync", "time", "test-util"] }
tokio = { version = "1.2.0", path = "../tokio", features = ["rt", "sync", "time", "test-util"] }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
async-stream = "0.3"
@@ -28,7 +27,7 @@ bytes = "1.0.0"
futures-core = "0.3.0"
[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
tokio = { version = "1.2.0", path = "../tokio", features = ["full"] }
futures-util = "0.3.0"
[package.metadata.docs.rs]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2020 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+4 -8
View File
@@ -21,6 +21,7 @@
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::sync::mpsc;
use tokio::time::{self, Duration, Instant, Sleep};
use tokio_stream::wrappers::UnboundedReceiverStream;
use futures_core::{ready, Stream};
use std::collections::VecDeque;
@@ -69,8 +70,7 @@ struct Inner {
waiting: Option<Instant>,
sleep: Option<Pin<Box<Sleep>>>,
read_wait: Option<Waker>,
// rx: mpsc::UnboundedReceiver<Action>,
rx: Pin<Box<dyn Stream<Item = Action> + Send>>,
rx: UnboundedReceiverStream<Action>,
}
impl Builder {
@@ -185,13 +185,9 @@ impl Handle {
impl Inner {
fn new(actions: VecDeque<Action>) -> (Inner, Handle) {
let (tx, mut rx) = mpsc::unbounded_channel();
let (tx, rx) = mpsc::unbounded_channel();
let rx = Box::pin(async_stream::stream! {
while let Some(item) = rx.recv().await {
yield item;
}
});
let rx = UnboundedReceiverStream::new(rx);
let inner = Inner {
actions,
+2 -3
View File
@@ -1,4 +1,3 @@
#![doc(html_root_url = "https://docs.rs/tokio-test/0.4.0")]
#![warn(
missing_debug_implementations,
missing_docs,
@@ -22,9 +21,9 @@ pub mod task;
/// future completes.
///
/// For more information, see the documentation for
/// [`tokio::runtime::current_thread::Runtime::block_on`][runtime-block-on].
/// [`tokio::runtime::Runtime::block_on`][runtime-block-on].
///
/// [runtime-block-on]: https://docs.rs/tokio/0.2.0-alpha.2/tokio/runtime/current_thread/struct.Runtime.html#method.block_on
/// [runtime-block-on]: https://docs.rs/tokio/1.3.0/tokio/runtime/struct.Runtime.html#method.block_on
pub fn block_on<F: std::future::Future>(future: F) -> F::Output {
use tokio::runtime;
+78
View File
@@ -1,3 +1,74 @@
# 0.6.6 (April 12, 2021)
### Added
- util: makes `Framed` and `FramedStream` resumable after eof ([#3272])
- util: add `PollSemaphore::{add_permits, available_permits}` ([#3683])
### Fixed
- chore: avoid allocation if `PollSemaphore` is unused ([#3634])
[#3272]: https://github.com/tokio-rs/tokio/pull/3272
[#3634]: https://github.com/tokio-rs/tokio/pull/3634
[#3683]: https://github.com/tokio-rs/tokio/pull/3683
# 0.6.5 (March 20, 2021)
### Fixed
- util: annotate time module as requiring `time` feature ([#3606])
[#3606]: https://github.com/tokio-rs/tokio/pull/3606
# 0.6.4 (March 9, 2021)
### Added
- codec: `AnyDelimiter` codec ([#3406])
- sync: add pollable `mpsc::Sender` ([#3490])
### Fixed
- codec: `LinesCodec` should only return `MaxLineLengthExceeded` once per line ([#3556])
- sync: fuse PollSemaphore ([#3578])
[#3406]: https://github.com/tokio-rs/tokio/pull/3406
[#3490]: https://github.com/tokio-rs/tokio/pull/3490
[#3556]: https://github.com/tokio-rs/tokio/pull/3556
[#3578]: https://github.com/tokio-rs/tokio/pull/3578
# 0.6.3 (January 31, 2021)
### Added
- sync: add `ReusableBoxFuture` utility ([#3464])
### Changed
- sync: use `ReusableBoxFuture` for `PollSemaphore` ([#3463])
- deps: remove `async-stream` dependency ([#3463])
- deps: remove `tokio-stream` dependency ([#3487])
# 0.6.2 (January 21, 2021)
### Added
- sync: add pollable `Semaphore` ([#3444])
### Fixed
- time: fix panics on updating `DelayQueue` entries ([#3270])
# 0.6.1 (January 12, 2021)
### Added
- codec: `get_ref()`, `get_mut()`, `get_pin_mut()` and `into_inner()` for
`Framed`, `FramedRead`, `FramedWrite` and `StreamReader` ([#3364]).
- codec: `write_buffer()` and `write_buffer_mut()` for `Framed` and
`FramedWrite` ([#3387]).
# 0.6.0 (December 23, 2020)
### Changed
@@ -53,6 +124,13 @@
- Initial release
[#3487]: https://github.com/tokio-rs/tokio/pull/3487
[#3464]: https://github.com/tokio-rs/tokio/pull/3464
[#3463]: https://github.com/tokio-rs/tokio/pull/3463
[#3444]: https://github.com/tokio-rs/tokio/pull/3444
[#3387]: https://github.com/tokio-rs/tokio/pull/3387
[#3364]: https://github.com/tokio-rs/tokio/pull/3364
[#3270]: https://github.com/tokio-rs/tokio/pull/3270
[#2326]: https://github.com/tokio-rs/tokio/pull/2326
[#2215]: https://github.com/tokio-rs/tokio/pull/2215
[#2198]: https://github.com/tokio-rs/tokio/pull/2198
+8 -10
View File
@@ -2,18 +2,17 @@
name = "tokio-util"
# When releasing to crates.io:
# - Remove path dependencies
# - Update html_root_url.
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "tokio-util-0.6.x" git tag.
version = "0.6.0"
version = "0.6.6"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-util/0.6.0/tokio_util"
documentation = "https://docs.rs/tokio-util/0.6.6/tokio_util"
description = """
Additional utilities for working with Tokio.
"""
@@ -36,8 +35,7 @@ rt = ["tokio/rt"]
__docs_rs = ["futures-util"]
[dependencies]
tokio = { version = "1.0.0", path = "../tokio" }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
tokio = { version = "1.0.0", features = ["sync"] }
bytes = "1.0.0"
futures-core = "0.3.0"
@@ -49,13 +47,13 @@ pin-project-lite = "0.2.0"
slab = { version = "0.4.1", optional = true } # Backs `DelayQueue`
[dev-dependencies]
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"
tokio = { version = "1.0.0", features = ["full"] }
tokio-test = { version = "0.4.0" }
tokio-stream = { version = "0.1" }
async-stream = "0.3.0"
futures = "0.3.0"
futures-test = "0.3.5"
[package.metadata.docs.rs]
all-features = true
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2020 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
+10
View File
@@ -47,3 +47,13 @@ macro_rules! cfg_rt {
)*
}
}
macro_rules! cfg_time {
($($item:item)*) => {
$(
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
$item
)*
}
}
+263
View File
@@ -0,0 +1,263 @@
use crate::codec::decoder::Decoder;
use crate::codec::encoder::Encoder;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::{cmp, fmt, io, str, usize};
const DEFAULT_SEEK_DELIMITERS: &[u8] = b",;\n\r";
const DEFAULT_SEQUENCE_WRITER: &[u8] = b",";
/// A simple [`Decoder`] and [`Encoder`] implementation that splits up data into chunks based on any character in the given delimiter string.
///
/// [`Decoder`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
///
/// # Example
/// Decode string of bytes containing various different delimiters.
///
/// [`BytesMut`]: bytes::BytesMut
/// [`Error`]: std::io::Error
///
/// ```
/// use tokio_util::codec::{AnyDelimiterCodec, Decoder};
/// use bytes::{BufMut, BytesMut};
///
/// #
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> Result<(), std::io::Error> {
/// let mut codec = AnyDelimiterCodec::new(b",;\r\n".to_vec(),b";".to_vec());
/// let buf = &mut BytesMut::new();
/// buf.reserve(200);
/// buf.put_slice(b"chunk 1,chunk 2;chunk 3\n\r");
/// assert_eq!("chunk 1", codec.decode(buf).unwrap().unwrap());
/// assert_eq!("chunk 2", codec.decode(buf).unwrap().unwrap());
/// assert_eq!("chunk 3", codec.decode(buf).unwrap().unwrap());
/// assert_eq!("", codec.decode(buf).unwrap().unwrap());
/// assert_eq!(None, codec.decode(buf).unwrap());
/// # Ok(())
/// # }
/// ```
///
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct AnyDelimiterCodec {
// Stored index of the next index to examine for the delimiter character.
// This is used to optimize searching.
// For example, if `decode` was called with `abc` and the delimiter is '{}', it would hold `3`,
// because that is the next index to examine.
// The next time `decode` is called with `abcde}`, the method will
// only look at `de}` before returning.
next_index: usize,
/// The maximum length for a given chunk. If `usize::MAX`, chunks will be
/// read until a delimiter character is reached.
max_length: usize,
/// Are we currently discarding the remainder of a chunk which was over
/// the length limit?
is_discarding: bool,
/// The bytes that are using for search during decode
seek_delimiters: Vec<u8>,
/// The bytes that are using for encoding
sequence_writer: Vec<u8>,
}
impl AnyDelimiterCodec {
/// Returns a `AnyDelimiterCodec` for splitting up data into chunks.
///
/// # Note
///
/// The returned `AnyDelimiterCodec` will not have an upper bound on the length
/// of a buffered chunk. See the documentation for [`new_with_max_length`]
/// for information on why this could be a potential security risk.
///
/// [`new_with_max_length`]: crate::codec::AnyDelimiterCodec::new_with_max_length()
pub fn new(seek_delimiters: Vec<u8>, sequence_writer: Vec<u8>) -> AnyDelimiterCodec {
AnyDelimiterCodec {
next_index: 0,
max_length: usize::MAX,
is_discarding: false,
seek_delimiters,
sequence_writer,
}
}
/// Returns a `AnyDelimiterCodec` with a maximum chunk length limit.
///
/// If this is set, calls to `AnyDelimiterCodec::decode` will return a
/// [`AnyDelimiterCodecError`] when a chunk exceeds the length limit. Subsequent calls
/// will discard up to `limit` bytes from that chunk until a delimiter
/// character is reached, returning `None` until the delimiter over the limit
/// has been fully discarded. After that point, calls to `decode` will
/// function as normal.
///
/// # Note
///
/// Setting a length limit is highly recommended for any `AnyDelimiterCodec` which
/// will be exposed to untrusted input. Otherwise, the size of the buffer
/// that holds the chunk currently being read is unbounded. An attacker could
/// exploit this unbounded buffer by sending an unbounded amount of input
/// without any delimiter characters, causing unbounded memory consumption.
///
/// [`AnyDelimiterCodecError`]: crate::codec::AnyDelimiterCodecError
pub fn new_with_max_length(
seek_delimiters: Vec<u8>,
sequence_writer: Vec<u8>,
max_length: usize,
) -> Self {
AnyDelimiterCodec {
max_length,
..AnyDelimiterCodec::new(seek_delimiters, sequence_writer)
}
}
/// Returns the maximum chunk length when decoding.
///
/// ```
/// use std::usize;
/// use tokio_util::codec::AnyDelimiterCodec;
///
/// let codec = AnyDelimiterCodec::new(b",;\n".to_vec(), b";".to_vec());
/// assert_eq!(codec.max_length(), usize::MAX);
/// ```
/// ```
/// use tokio_util::codec::AnyDelimiterCodec;
///
/// let codec = AnyDelimiterCodec::new_with_max_length(b",;\n".to_vec(), b";".to_vec(), 256);
/// assert_eq!(codec.max_length(), 256);
/// ```
pub fn max_length(&self) -> usize {
self.max_length
}
}
impl Decoder for AnyDelimiterCodec {
type Item = Bytes;
type Error = AnyDelimiterCodecError;
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Bytes>, AnyDelimiterCodecError> {
loop {
// Determine how far into the buffer we'll search for a delimiter. If
// there's no max_length set, we'll read to the end of the buffer.
let read_to = cmp::min(self.max_length.saturating_add(1), buf.len());
let new_chunk_offset = buf[self.next_index..read_to].iter().position(|b| {
self.seek_delimiters
.iter()
.any(|delimiter| *b == *delimiter)
});
match (self.is_discarding, new_chunk_offset) {
(true, Some(offset)) => {
// If we found a new chunk, discard up to that offset and
// then stop discarding. On the next iteration, we'll try
// to read a chunk normally.
buf.advance(offset + self.next_index + 1);
self.is_discarding = false;
self.next_index = 0;
}
(true, None) => {
// Otherwise, we didn't find a new chunk, so we'll discard
// everything we read. On the next iteration, we'll continue
// discarding up to max_len bytes unless we find a new chunk.
buf.advance(read_to);
self.next_index = 0;
if buf.is_empty() {
return Ok(None);
}
}
(false, Some(offset)) => {
// Found a chunk!
let new_chunk_index = offset + self.next_index;
self.next_index = 0;
let mut chunk = buf.split_to(new_chunk_index + 1);
chunk.truncate(chunk.len() - 1);
let chunk = chunk.freeze();
return Ok(Some(chunk));
}
(false, None) if buf.len() > self.max_length => {
// Reached the maximum length without finding a
// new chunk, return an error and start discarding on the
// next call.
self.is_discarding = true;
return Err(AnyDelimiterCodecError::MaxChunkLengthExceeded);
}
(false, None) => {
// We didn't find a chunk or reach the length limit, so the next
// call will resume searching at the current offset.
self.next_index = read_to;
return Ok(None);
}
}
}
}
fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<Bytes>, AnyDelimiterCodecError> {
Ok(match self.decode(buf)? {
Some(frame) => Some(frame),
None => {
// return remaining data, if any
if buf.is_empty() {
None
} else {
let chunk = buf.split_to(buf.len());
self.next_index = 0;
Some(chunk.freeze())
}
}
})
}
}
impl<T> Encoder<T> for AnyDelimiterCodec
where
T: AsRef<str>,
{
type Error = AnyDelimiterCodecError;
fn encode(&mut self, chunk: T, buf: &mut BytesMut) -> Result<(), AnyDelimiterCodecError> {
let chunk = chunk.as_ref();
buf.reserve(chunk.len() + 1);
buf.put(chunk.as_bytes());
buf.put(self.sequence_writer.as_ref());
Ok(())
}
}
impl Default for AnyDelimiterCodec {
fn default() -> Self {
Self::new(
DEFAULT_SEEK_DELIMITERS.to_vec(),
DEFAULT_SEQUENCE_WRITER.to_vec(),
)
}
}
/// An error occured while encoding or decoding a chunk.
#[derive(Debug)]
pub enum AnyDelimiterCodecError {
/// The maximum chunk length was exceeded.
MaxChunkLengthExceeded,
/// An IO error occurred.
Io(io::Error),
}
impl fmt::Display for AnyDelimiterCodecError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AnyDelimiterCodecError::MaxChunkLengthExceeded => {
write!(f, "max chunk length exceeded")
}
AnyDelimiterCodecError::Io(e) => write!(f, "{}", e),
}
}
}
impl From<io::Error> for AnyDelimiterCodecError {
fn from(e: io::Error) -> AnyDelimiterCodecError {
AnyDelimiterCodecError::Io(e)
}
}
impl std::error::Error for AnyDelimiterCodecError {}
+21 -2
View File
@@ -16,6 +16,20 @@ use std::io;
/// implementing stateful streaming parsers. In many cases, though, this type
/// will simply be a unit struct (e.g. `struct HttpDecoder`).
///
/// For some underlying data-sources, namely files and FIFOs,
/// it's possible to temporarily read 0 bytes by reaching EOF.
///
/// In these cases `decode_eof` will be called until it signals
/// fullfillment of all closing frames by returning `Ok(None)`.
/// After that, repeated attempts to read from the [`Framed`] or [`FramedRead`]
/// will not invoke `decode` or `decode_eof` again, until data can be read
/// during a retry.
///
/// It is up to the Decoder to keep track of a restart after an EOF,
/// and to decide how to handle such an event by, for example,
/// allowing frames to cross EOF boundaries, re-emitting opening frames, or
/// reseting the entire internal state.
///
/// [`Framed`]: crate::codec::Framed
/// [`FramedRead`]: crate::codec::FramedRead
pub trait Decoder {
@@ -115,13 +129,18 @@ pub trait Decoder {
/// This method defaults to calling `decode` and returns an error if
/// `Ok(None)` is returned while there is unconsumed data in `buf`.
/// Typically this doesn't need to be implemented unless the framing
/// protocol differs near the end of the stream.
/// protocol differs near the end of the stream, or if you need to construct
/// frames _across_ eof boundaries on sources that can be resumed.
///
/// Note that the `buf` argument may be empty. If a previous call to
/// `decode_eof` consumed all the bytes in the buffer, `decode_eof` will be
/// called again until it returns `None`, indicating that there are no more
/// frames to yield. This behavior enables returning finalization frames
/// that may not be based on inbound data.
///
/// Once `None` has been returned, `decode_eof` won't be called again until
/// an attempt to resume the stream has been made, where the underlying stream
/// actually returned more data.
fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
match self.decode(buf)? {
Some(frame) => Ok(Some(frame)),
@@ -153,7 +172,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`]: futures_core::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Framed`]: crate::codec::Framed
fn framed<T: AsyncRead + AsyncWrite + Sized>(self, io: T) -> Framed<T, Self>
+30 -5
View File
@@ -2,8 +2,8 @@ use crate::codec::decoder::Decoder;
use crate::codec::encoder::Encoder;
use crate::codec::framed_impl::{FramedImpl, RWFrames, ReadFrame, WriteFrame};
use futures_core::Stream;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_stream::Stream;
use bytes::BytesMut;
use futures_sink::Sink;
@@ -20,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`]: futures_core::Stream
/// [`Sink`]: futures_sink::Sink
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`Decoder::framed`]: crate::codec::Decoder::framed()
@@ -52,7 +52,12 @@ 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
/// Note that, for some byte sources, the stream can be resumed after an EOF
/// by reading from it, even after it has returned `None`. Repeated attempts
/// to do so, without new data available, continue to return `None` without
/// creating more (closing) frames.
///
/// [`Stream`]: futures_core::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Decode`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
@@ -86,7 +91,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`]: futures_core::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Decode`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
@@ -131,7 +136,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`]: futures_core::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Decoder`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
@@ -170,6 +175,16 @@ impl<T, U> Framed<T, U> {
&mut self.inner.inner
}
/// Returns a pinned mutable reference to the underlying I/O stream wrapped by
/// `Framed`.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
self.project().inner.project().inner
}
/// Returns a reference to the underlying codec wrapped by
/// `Framed`.
///
@@ -198,6 +213,16 @@ impl<T, U> Framed<T, U> {
&mut self.inner.state.read.buffer
}
/// Returns a reference to the write buffer.
pub fn write_buffer(&self) -> &BytesMut {
&self.inner.state.write.buffer
}
/// Returns a mutable reference to the write buffer.
pub fn write_buffer_mut(&mut self) -> &mut BytesMut {
&mut self.inner.state.write.buffer
}
/// Consumes the `Framed`, returning its underlying I/O stream.
///
/// Note that care should be taken to not tamper with the underlying stream
+67 -12
View File
@@ -1,8 +1,8 @@
use crate::codec::decoder::Decoder;
use crate::codec::encoder::Encoder;
use futures_core::Stream;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_stream::Stream;
use bytes::BytesMut;
use futures_core::ready;
@@ -120,42 +120,97 @@ where
let mut pinned = self.project();
let state: &mut ReadFrame = pinned.state.borrow_mut();
// The following loops implements a state machine with each state corresponding
// to a combination of the `is_readable` and `eof` flags. States persist across
// loop entries and most state transitions occur with a return.
//
// The intitial state is `reading`.
//
// | state | eof | is_readable |
// |---------|-------|-------------|
// | reading | false | false |
// | framing | false | true |
// | pausing | true | true |
// | paused | true | false |
//
// `decode_eof`
// returns `Some` read 0 bytes
// │ │ │ │
// │ ▼ │ ▼
// ┌───────┐ `decode_eof` ┌──────┐
// ┌──read 0 bytes──▶│pausing│─returns `None`─▶│paused│──┐
// │ └───────┘ └──────┘ │
// pending read┐ │ ┌──────┐ │ ▲ │
// │ │ │ │ │ │ │ │
// │ ▼ │ │ `decode` returns `Some`│ pending read
// │ ╔═══════╗ ┌───────┐◀─┘ │
// └──║reading║─read n>0 bytes─▶│framing│ │
// ╚═══════╝ └───────┘◀──────read n>0 bytes┘
// ▲ │
// │ │
// └─`decode` returns `None`─┘
loop {
// Repeatedly call `decode` or `decode_eof` as long as it is
// "readable". Readable is defined as not having returned `None`. If
// the upstream has returned EOF, and the decoder is no longer
// readable, it can be assumed that the decoder will never become
// readable again, at which point the stream is terminated.
// Repeatedly call `decode` or `decode_eof` while the buffer is "readable",
// i.e. it _might_ contain data consumable as a frame or closing frame.
// Both signal that there is no such data by returning `None`.
//
// If `decode` couldn't read a frame and the upstream source has returned eof,
// `decode_eof` will attemp to decode the remaining bytes as closing frames.
//
// If the underlying AsyncRead is resumable, we may continue after an EOF,
// but must finish emmiting all of it's associated `decode_eof` frames.
// Furthermore, we don't want to emit any `decode_eof` frames on retried
// reads after an EOF unless we've actually read more data.
if state.is_readable {
// pausing or framing
if state.eof {
// pausing
let frame = pinned.codec.decode_eof(&mut state.buffer)?;
if frame.is_none() {
state.is_readable = false; // prepare pausing -> paused
}
// implicit pausing -> pausing or pausing -> paused
return Poll::Ready(frame.map(Ok));
}
// framing
trace!("attempting to decode a frame");
if let Some(frame) = pinned.codec.decode(&mut state.buffer)? {
trace!("frame decoded from buffer");
// implicit framing -> framing
return Poll::Ready(Some(Ok(frame)));
}
// framing -> reading
state.is_readable = false;
}
assert!(!state.eof);
// Otherwise, try to read more data and try again. Make sure we've
// got room for at least one byte to read to ensure that we don't
// get a spurious 0 that looks like EOF
// reading or paused
// If we can't build a frame yet, try to read more data and try again.
// Make sure we've got room for at least one byte to read to ensure
// that we don't get a spurious 0 that looks like EOF.
state.buffer.reserve(1);
let bytect = match poll_read_buf(pinned.inner.as_mut(), cx, &mut state.buffer)? {
Poll::Ready(ct) => ct,
// implicit reading -> reading or implicit paused -> paused
Poll::Pending => return Poll::Pending,
};
if bytect == 0 {
if state.eof {
// We're already at an EOF, and since we've reached this path
// we're also not readable. This implies that we've already finished
// our `decode_eof` handling, so we can simply return `None`.
// implicit paused -> paused
return Poll::Ready(None);
}
// prepare reading -> paused
state.eof = true;
} else {
// prepare paused -> framing or noop reading -> framing
state.eof = false;
}
// paused -> framing or reading -> framing or reading -> pausing
state.is_readable = true;
}
}
+12 -2
View File
@@ -1,8 +1,8 @@
use crate::codec::framed_impl::{FramedImpl, ReadFrame};
use crate::codec::Decoder;
use futures_core::Stream;
use tokio::io::AsyncRead;
use tokio_stream::Stream;
use bytes::BytesMut;
use futures_sink::Sink;
@@ -14,7 +14,7 @@ use std::task::{Context, Poll};
pin_project! {
/// A [`Stream`] of messages decoded from an [`AsyncRead`].
///
/// [`Stream`]: tokio_stream::Stream
/// [`Stream`]: futures_core::Stream
/// [`AsyncRead`]: tokio::io::AsyncRead
pub struct FramedRead<T, D> {
#[pin]
@@ -78,6 +78,16 @@ impl<T, D> FramedRead<T, D> {
&mut self.inner.inner
}
/// Returns a pinned mutable reference to the underlying I/O stream wrapped by
/// `FramedRead`.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
self.project().inner.project().inner
}
/// Consumes the `FramedRead`, returning its underlying I/O stream.
///
/// Note that care should be taken to not tamper with the underlying stream
+22 -1
View File
@@ -1,9 +1,10 @@
use crate::codec::encoder::Encoder;
use crate::codec::framed_impl::{FramedImpl, WriteFrame};
use futures_core::Stream;
use tokio::io::AsyncWrite;
use tokio_stream::Stream;
use bytes::BytesMut;
use futures_sink::Sink;
use pin_project_lite::pin_project;
use std::fmt;
@@ -58,6 +59,16 @@ impl<T, E> FramedWrite<T, E> {
&mut self.inner.inner
}
/// Returns a pinned mutable reference to the underlying I/O stream wrapped by
/// `FramedWrite`.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
self.project().inner.project().inner
}
/// Consumes the `FramedWrite`, returning its underlying I/O stream.
///
/// Note that care should be taken to not tamper with the underlying stream
@@ -76,6 +87,16 @@ impl<T, E> FramedWrite<T, E> {
pub fn encoder_mut(&mut self) -> &mut E {
&mut self.inner.codec
}
/// Returns a reference to the write buffer.
pub fn write_buffer(&self) -> &BytesMut {
&self.inner.state.buffer
}
/// Returns a mutable reference to the write buffer.
pub fn write_buffer_mut(&mut self) -> &mut BytesMut {
&mut self.inner.state.buffer
}
}
// This impl just defers to the underlying FramedImpl
+4 -4
View File
@@ -535,14 +535,14 @@ impl LengthDelimitedCodec {
Ok(Some(n))
}
fn decode_data(&self, n: usize, src: &mut BytesMut) -> io::Result<Option<BytesMut>> {
fn decode_data(&self, n: usize, src: &mut BytesMut) -> Option<BytesMut> {
// At this point, the buffer has already had the required capacity
// reserved. All there is to do is read.
if src.len() < n {
return Ok(None);
return None;
}
Ok(Some(src.split_to(n)))
Some(src.split_to(n))
}
}
@@ -562,7 +562,7 @@ impl Decoder for LengthDelimitedCodec {
DecodeState::Data(n) => n,
};
match self.decode_data(n, src)? {
match self.decode_data(n, src) {
Some(data) => {
// Update the decode state
self.state = DecodeState::Head;
+1 -1
View File
@@ -133,7 +133,7 @@ impl Decoder for LinesCodec {
buf.advance(read_to);
self.next_index = 0;
if buf.is_empty() {
return Err(LinesCodecError::MaxLineLengthExceeded);
return Ok(None);
}
}
(false, Some(offset)) => {
+4 -1
View File
@@ -246,7 +246,7 @@
//!
//! [`AsyncRead`]: tokio::io::AsyncRead
//! [`AsyncWrite`]: tokio::io::AsyncWrite
//! [`Stream`]: tokio_stream::Stream
//! [`Stream`]: futures_core::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
@@ -285,3 +285,6 @@ pub use self::length_delimited::{LengthDelimitedCodec, LengthDelimitedCodecError
mod lines_codec;
pub use self::lines_codec::{LinesCodec, LinesCodecError};
mod any_delimiter_codec;
pub use self::any_delimiter_codec::{AnyDelimiterCodec, AnyDelimiterCodecError};
+2 -2
View File
@@ -40,7 +40,7 @@ pin_project! {
///
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`StreamReader`]: crate::io::StreamReader
/// [`Stream`]: tokio_stream::Stream
/// [`Stream`]: futures_core::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`]: futures_core::Stream
pub fn new(reader: R) -> Self {
ReaderStream {
reader: Some(reader),
+31 -1
View File
@@ -51,7 +51,7 @@ pin_project! {
/// ```
///
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`Stream`]: tokio_stream::Stream
/// [`Stream`]: futures_core::Stream
/// [`ReaderStream`]: crate::io::ReaderStream
#[derive(Debug)]
pub struct StreamReader<S, B> {
@@ -93,6 +93,36 @@ where
}
}
impl<S, B> StreamReader<S, B> {
/// Gets a reference to the underlying stream.
///
/// It is inadvisable to directly read from the underlying stream.
pub fn get_ref(&self) -> &S {
&self.inner
}
/// Gets a mutable reference to the underlying stream.
///
/// It is inadvisable to directly read from the underlying stream.
pub fn get_mut(&mut self) -> &mut S {
&mut self.inner
}
/// Gets a pinned mutable reference to the underlying stream.
///
/// It is inadvisable to directly read from the underlying stream.
pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut S> {
self.project().inner
}
/// Consumes this `BufWriter`, returning the underlying stream.
///
/// Note that any leftover data in the internal buffer is lost.
pub fn into_inner(self) -> S {
self.inner
}
}
impl<S, B, E> AsyncRead for StreamReader<S, B>
where
S: Stream<Item = Result<B, E>>,
+4 -4
View File
@@ -1,4 +1,3 @@
#![doc(html_root_url = "https://docs.rs/tokio-util/0.6.0")]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -46,13 +45,14 @@ cfg_rt! {
pub mod context;
}
cfg_time! {
pub mod time;
}
pub mod sync;
pub mod either;
#[cfg(feature = "time")]
pub mod time;
#[cfg(any(feature = "io", feature = "codec"))]
mod util {
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
+9
View File
@@ -4,3 +4,12 @@ mod cancellation_token;
pub use cancellation_token::{CancellationToken, WaitForCancellationFuture};
mod intrusive_double_linked_list;
mod mpsc;
pub use mpsc::PollSender;
mod poll_semaphore;
pub use poll_semaphore::PollSemaphore;
mod reusable_box;
pub use reusable_box::ReusableBoxFuture;
+221
View File
@@ -0,0 +1,221 @@
use futures_core::ready;
use futures_sink::Sink;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::sync::mpsc::{error::SendError, Sender};
use super::ReusableBoxFuture;
// This implementation was chosen over something based on permits because to get a
// `tokio::sync::mpsc::Permit` out of the `inner` future, you must transmute the
// lifetime on the permit to `'static`.
/// A wrapper around [`mpsc::Sender`] that can be polled.
///
/// [`mpsc::Sender`]: tokio::sync::mpsc::Sender
#[derive(Debug)]
pub struct PollSender<T> {
/// is none if closed
sender: Option<Arc<Sender<T>>>,
is_sending: bool,
inner: ReusableBoxFuture<Result<(), SendError<T>>>,
}
// By reusing the same async fn for both Some and None, we make sure every
// future passed to ReusableBoxFuture has the same underlying type, and hence
// the same size and alignment.
async fn make_future<T>(data: Option<(Arc<Sender<T>>, T)>) -> Result<(), SendError<T>> {
match data {
Some((sender, value)) => sender.send(value).await,
None => unreachable!(
"This future should not be pollable, as is_sending should be set to false."
),
}
}
impl<T: Send + 'static> PollSender<T> {
/// Create a new `PollSender`.
pub fn new(sender: Sender<T>) -> Self {
Self {
sender: Some(Arc::new(sender)),
is_sending: false,
inner: ReusableBoxFuture::new(make_future(None)),
}
}
/// Start sending a new item.
///
/// This method panics if a send is currently in progress. To ensure that no
/// send is in progress, call `poll_send_done` first until it returns
/// `Poll::Ready`.
///
/// If this method returns an error, that indicates that the channel is
/// closed. Note that this method is not guaranteed to return an error if
/// the channel is closed, but in that case the error would be reported by
/// the first call to `poll_send_done`.
pub fn start_send(&mut self, value: T) -> Result<(), SendError<T>> {
if self.is_sending {
panic!("start_send called while not ready.");
}
match self.sender.clone() {
Some(sender) => {
self.inner.set(make_future(Some((sender, value))));
self.is_sending = true;
Ok(())
}
None => Err(SendError(value)),
}
}
/// If a send is in progress, poll for its completion. If no send is in progress,
/// this method returns `Poll::Ready(Ok(()))`.
///
/// This method can return the following values:
///
/// - `Poll::Ready(Ok(()))` if the in-progress send has been completed, or there is
/// no send in progress (even if the channel is closed).
/// - `Poll::Ready(Err(err))` if the in-progress send failed because the channel has
/// been closed.
/// - `Poll::Pending` if a send is in progress, but it could not complete now.
///
/// When this method returns `Poll::Pending`, the current task is scheduled
/// to receive a wakeup when the message is sent, or when the entire channel
/// is closed (but not if just this sender is closed by
/// `close_this_sender`). Note that on multiple calls to `poll_send_done`,
/// only the `Waker` from the `Context` passed to the most recent call is
/// scheduled to receive a wakeup.
///
/// If this method returns `Poll::Ready`, then `start_send` is guaranteed to
/// not panic.
pub fn poll_send_done(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), SendError<T>>> {
if !self.is_sending {
return Poll::Ready(Ok(()));
}
let result = self.inner.poll(cx);
if result.is_ready() {
self.is_sending = false;
}
if let Poll::Ready(Err(_)) = &result {
self.sender = None;
}
result
}
/// Check whether the channel is ready to send more messages now.
///
/// If this method returns `true`, then `start_send` is guaranteed to not
/// panic.
///
/// If the channel is closed, this method returns `true`.
pub fn is_ready(&self) -> bool {
!self.is_sending
}
/// Check whether the channel has been closed.
pub fn is_closed(&self) -> bool {
match &self.sender {
Some(sender) => sender.is_closed(),
None => true,
}
}
/// Clone the underlying `Sender`.
///
/// If this method returns `None`, then the channel is closed. (But it is
/// not guaranteed to return `None` if the channel is closed.)
pub fn clone_inner(&self) -> Option<Sender<T>> {
self.sender.as_ref().map(|sender| (&**sender).clone())
}
/// Access the underlying `Sender`.
///
/// If this method returns `None`, then the channel is closed. (But it is
/// not guaranteed to return `None` if the channel is closed.)
pub fn inner_ref(&self) -> Option<&Sender<T>> {
self.sender.as_deref()
}
// This operation is supported because it is required by the Sink trait.
/// Close this sender. No more messages can be sent from this sender.
///
/// Note that this only closes the channel from the view-point of this
/// sender. The channel remains open until all senders have gone away, or
/// until the [`Receiver`] closes the channel.
///
/// If there is a send in progress when this method is called, that send is
/// unaffected by this operation, and `poll_send_done` can still be called
/// to complete that send.
///
/// [`Receiver`]: tokio::sync::mpsc::Receiver
pub fn close_this_sender(&mut self) {
self.sender = None;
}
/// Abort the current in-progress send, if any.
///
/// Returns `true` if a send was aborted.
pub fn abort_send(&mut self) -> bool {
if self.is_sending {
self.inner.set(make_future(None));
self.is_sending = false;
true
} else {
false
}
}
}
impl<T> Clone for PollSender<T> {
/// Clones this `PollSender`. The resulting clone will not have any
/// in-progress send operations, even if the current `PollSender` does.
fn clone(&self) -> PollSender<T> {
Self {
sender: self.sender.clone(),
is_sending: false,
inner: ReusableBoxFuture::new(async { unreachable!() }),
}
}
}
impl<T: Send + 'static> Sink<T> for PollSender<T> {
type Error = SendError<T>;
/// This is equivalent to calling [`poll_send_done`].
///
/// [`poll_send_done`]: PollSender::poll_send_done
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Pin::into_inner(self).poll_send_done(cx)
}
/// This is equivalent to calling [`poll_send_done`].
///
/// [`poll_send_done`]: PollSender::poll_send_done
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Pin::into_inner(self).poll_send_done(cx)
}
/// This is equivalent to calling [`start_send`].
///
/// [`start_send`]: PollSender::start_send
fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> {
Pin::into_inner(self).start_send(item)
}
/// This method will first flush the `PollSender`, and then close it by
/// calling [`close_this_sender`].
///
/// If a send fails while flushing because the [`Receiver`] has gone away,
/// then this function returns an error. The channel is still successfully
/// closed in this situation.
///
/// [`close_this_sender`]: PollSender::close_this_sender
/// [`Receiver`]: tokio::sync::mpsc::Receiver
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
ready!(self.as_mut().poll_flush(cx))?;
Pin::into_inner(self).close_this_sender();
Poll::Ready(Ok(()))
}
}
+136
View File
@@ -0,0 +1,136 @@
use futures_core::{ready, Stream};
use std::fmt;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::sync::{AcquireError, OwnedSemaphorePermit, Semaphore, TryAcquireError};
use super::ReusableBoxFuture;
/// A wrapper around [`Semaphore`] that provides a `poll_acquire` method.
///
/// [`Semaphore`]: tokio::sync::Semaphore
pub struct PollSemaphore {
semaphore: Arc<Semaphore>,
permit_fut: Option<ReusableBoxFuture<Result<OwnedSemaphorePermit, AcquireError>>>,
}
impl PollSemaphore {
/// Create a new `PollSemaphore`.
pub fn new(semaphore: Arc<Semaphore>) -> Self {
Self {
semaphore,
permit_fut: None,
}
}
/// Closes the semaphore.
pub fn close(&self) {
self.semaphore.close()
}
/// Obtain a clone of the inner semaphore.
pub fn clone_inner(&self) -> Arc<Semaphore> {
self.semaphore.clone()
}
/// Get back the inner semaphore.
pub fn into_inner(self) -> Arc<Semaphore> {
self.semaphore
}
/// Poll to acquire a permit from the semaphore.
///
/// This can return the following values:
///
/// - `Poll::Pending` if a permit is not currently available.
/// - `Poll::Ready(Some(permit))` if a permit was acquired.
/// - `Poll::Ready(None)` if the semaphore has been closed.
///
/// When this method returns `Poll::Pending`, the current task is scheduled
/// to receive a wakeup when a permit becomes available, or when the
/// semaphore is closed. Note that on multiple calls to `poll_acquire`, only
/// the `Waker` from the `Context` passed to the most recent call is
/// scheduled to receive a wakeup.
pub fn poll_acquire(&mut self, cx: &mut Context<'_>) -> Poll<Option<OwnedSemaphorePermit>> {
let permit_future = match self.permit_fut.as_mut() {
Some(fut) => fut,
None => {
// avoid allocations completely if we can grab a permit immediately
match Arc::clone(&self.semaphore).try_acquire_owned() {
Ok(permit) => return Poll::Ready(Some(permit)),
Err(TryAcquireError::Closed) => return Poll::Ready(None),
Err(TryAcquireError::NoPermits) => {}
}
let next_fut = Arc::clone(&self.semaphore).acquire_owned();
self.permit_fut
.get_or_insert(ReusableBoxFuture::new(next_fut))
}
};
let result = ready!(permit_future.poll(cx));
let next_fut = Arc::clone(&self.semaphore).acquire_owned();
permit_future.set(next_fut);
match result {
Ok(permit) => Poll::Ready(Some(permit)),
Err(_closed) => {
self.permit_fut = None;
Poll::Ready(None)
}
}
}
/// Returns the current number of available permits.
///
/// This is equivalent to the [`Semaphore::available_permits`] method on the
/// `tokio::sync::Semaphore` type.
///
/// [`Semaphore::available_permits`]: tokio::sync::Semaphore::available_permits
pub fn available_permits(&self) -> usize {
self.semaphore.available_permits()
}
/// Adds `n` new permits to the semaphore.
///
/// The maximum number of permits is `usize::MAX >> 3`, and this function
/// will panic if the limit is exceeded.
///
/// This is equivalent to the [`Semaphore::add_permits`] method on the
/// `tokio::sync::Semaphore` type.
///
/// [`Semaphore::add_permits`]: tokio::sync::Semaphore::add_permits
pub fn add_permits(&self, n: usize) {
self.semaphore.add_permits(n);
}
}
impl Stream for PollSemaphore {
type Item = OwnedSemaphorePermit;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<OwnedSemaphorePermit>> {
Pin::into_inner(self).poll_acquire(cx)
}
}
impl Clone for PollSemaphore {
fn clone(&self) -> PollSemaphore {
PollSemaphore::new(self.clone_inner())
}
}
impl fmt::Debug for PollSemaphore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PollSemaphore")
.field("semaphore", &self.semaphore)
.finish()
}
}
impl AsRef<Semaphore> for PollSemaphore {
fn as_ref(&self) -> &Semaphore {
&*self.semaphore
}
}
+151
View File
@@ -0,0 +1,151 @@
use std::alloc::Layout;
use std::future::Future;
use std::panic::AssertUnwindSafe;
use std::pin::Pin;
use std::ptr::{self, NonNull};
use std::task::{Context, Poll};
use std::{fmt, panic};
/// A reusable `Pin<Box<dyn Future<Output = T> + Send>>`.
///
/// This type lets you replace the future stored in the box without
/// reallocating when the size and alignment permits this.
pub struct ReusableBoxFuture<T> {
boxed: NonNull<dyn Future<Output = T> + Send>,
}
impl<T> ReusableBoxFuture<T> {
/// Create a new `ReusableBoxFuture<T>` containing the provided future.
pub fn new<F>(future: F) -> Self
where
F: Future<Output = T> + Send + 'static,
{
let boxed: Box<dyn Future<Output = T> + Send> = Box::new(future);
let boxed = Box::into_raw(boxed);
// SAFETY: Box::into_raw does not return null pointers.
let boxed = unsafe { NonNull::new_unchecked(boxed) };
Self { boxed }
}
/// Replace the future currently stored in this box.
///
/// This reallocates if and only if the layout of the provided future is
/// different from the layout of the currently stored future.
pub fn set<F>(&mut self, future: F)
where
F: Future<Output = T> + Send + 'static,
{
if let Err(future) = self.try_set(future) {
*self = Self::new(future);
}
}
/// Replace the future currently stored in this box.
///
/// This function never reallocates, but returns an error if the provided
/// future has a different size or alignment from the currently stored
/// future.
pub fn try_set<F>(&mut self, future: F) -> Result<(), F>
where
F: Future<Output = T> + Send + 'static,
{
// SAFETY: The pointer is not dangling.
let self_layout = {
let dyn_future: &(dyn Future<Output = T> + Send) = unsafe { self.boxed.as_ref() };
Layout::for_value(dyn_future)
};
if Layout::new::<F>() == self_layout {
// SAFETY: We just checked that the layout of F is correct.
unsafe {
self.set_same_layout(future);
}
Ok(())
} else {
Err(future)
}
}
/// Set the current future.
///
/// # Safety
///
/// This function requires that the layout of the provided future is the
/// same as `self.layout`.
unsafe fn set_same_layout<F>(&mut self, future: F)
where
F: Future<Output = T> + Send + 'static,
{
// Drop the existing future, catching any panics.
let result = panic::catch_unwind(AssertUnwindSafe(|| {
ptr::drop_in_place(self.boxed.as_ptr());
}));
// Overwrite the future behind the pointer. This is safe because the
// allocation was allocated with the same size and alignment as the type F.
let self_ptr: *mut F = self.boxed.as_ptr() as *mut F;
ptr::write(self_ptr, future);
// Update the vtable of self.boxed. The pointer is not null because we
// just got it from self.boxed, which is not null.
self.boxed = NonNull::new_unchecked(self_ptr);
// If the old future's destructor panicked, resume unwinding.
match result {
Ok(()) => {}
Err(payload) => {
panic::resume_unwind(payload);
}
}
}
/// Get a pinned reference to the underlying future.
pub fn get_pin(&mut self) -> Pin<&mut (dyn Future<Output = T> + Send)> {
// SAFETY: The user of this box cannot move the box, and we do not move it
// either.
unsafe { Pin::new_unchecked(self.boxed.as_mut()) }
}
/// Poll the future stored inside this box.
pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll<T> {
self.get_pin().poll(cx)
}
}
impl<T> Future for ReusableBoxFuture<T> {
type Output = T;
/// Poll the future stored inside this box.
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
Pin::into_inner(self).get_pin().poll(cx)
}
}
// The future stored inside ReusableBoxFuture<T> must be Send.
unsafe impl<T> Send for ReusableBoxFuture<T> {}
// The only method called on self.boxed is poll, which takes &mut self, so this
// struct being Sync does not permit any invalid access to the Future, even if
// the future is not Sync.
unsafe impl<T> Sync for ReusableBoxFuture<T> {}
// Just like a Pin<Box<dyn Future>> is always Unpin, so is this type.
impl<T> Unpin for ReusableBoxFuture<T> {}
impl<T> Drop for ReusableBoxFuture<T> {
fn drop(&mut self) {
unsafe {
drop(Box::from_raw(self.boxed.as_ptr()));
}
}
}
impl<T> fmt::Debug for ReusableBoxFuture<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReusableBoxFuture").finish()
}
}
+69 -66
View File
@@ -30,24 +30,24 @@ use std::task::{self, Poll, Waker};
///
/// Once delays have been configured, the `DelayQueue` is used via its
/// [`Stream`] implementation. [`poll_expired`] is called. If an entry has reached its
/// deadline, it is returned. If not, `Poll::Pending` indicating that the
/// deadline, it is returned. If not, `Poll::Pending` is returned indicating that the
/// current task will be notified once the deadline has been reached.
///
/// # `Stream` implementation
///
/// Items are retrieved from the queue via [`DelayQueue::poll_expired`]. If no delays have
/// expired, no items are returned. In this case, `NotReady` is returned and the
/// expired, no items are returned. In this case, `Poll::Pending` is returned and the
/// current task is registered to be notified once the next item's delay has
/// expired.
///
/// If no items are in the queue, i.e. `is_empty()` returns `true`, then `poll`
/// returns `Ready(None)`. This indicates that the stream has reached an end.
/// returns `Poll::Ready(None)`. This indicates that the stream has reached an end.
/// However, if a new item is inserted *after*, `poll` will once again start
/// returning items or `NotReady.
/// returning items or `Poll::Pending`.
///
/// Items are returned ordered by their expirations. Items that are configured
/// to expire first will be returned first. There are no ordering guarantees
/// for items configured to expire the same instant. Also note that delays are
/// for items configured to expire at the same instant. Also note that delays are
/// rounded to the closest millisecond.
///
/// # Implementation
@@ -152,7 +152,7 @@ pub struct DelayQueue<T> {
waker: Option<Waker>,
}
/// An entry in `DelayQueue` that has expired and removed.
/// An entry in `DelayQueue` that has expired and been removed.
///
/// Values are returned by [`DelayQueue::poll_expired`].
///
@@ -211,7 +211,7 @@ struct Data<T> {
const MAX_ENTRIES: usize = (1 << 30) - 1;
impl<T> DelayQueue<T> {
/// Creates a new, empty, `DelayQueue`
/// Creates a new, empty, `DelayQueue`.
///
/// The queue will not allocate storage until items are inserted into it.
///
@@ -239,15 +239,15 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::with_capacity(10);
/// let mut delay_queue = DelayQueue::with_capacity(10);
///
/// // These insertions are done without further allocation
/// for i in 0..10 {
/// delay_queue.insert(i, Duration::from_secs(i));
/// }
/// // These insertions are done without further allocation
/// for i in 0..10 {
/// delay_queue.insert(i, Duration::from_secs(i));
/// }
///
/// // This will make the queue allocate additional storage
/// delay_queue.insert(11, Duration::from_secs(11));
/// // This will make the queue allocate additional storage
/// delay_queue.insert(11, Duration::from_secs(11));
/// # }
/// ```
pub fn with_capacity(capacity: usize) -> DelayQueue<T> {
@@ -272,8 +272,8 @@ impl<T> DelayQueue<T> {
/// `value` will be returned from [`poll_expired`]. If `when` has already been
/// reached, then `value` is immediately made available to poll.
///
/// The return value represents the insertion and is used at an argument to
/// [`remove`] and [`reset`]. Note that [`Key`] is token and is reused once
/// The return value represents the insertion and is used as an argument to
/// [`remove`] and [`reset`]. Note that [`Key`] is a token and is reused once
/// `value` is removed from the queue either by calling [`poll_expired`] after
/// `when` is reached or by calling [`remove`]. At this point, the caller
/// must take care to not use the returned [`Key`] again as it may reference
@@ -295,13 +295,13 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert_at(
/// "foo", Instant::now() + Duration::from_secs(5));
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert_at(
/// "foo", Instant::now() + Duration::from_secs(5));
///
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// # }
/// ```
///
@@ -353,7 +353,7 @@ impl<T> DelayQueue<T> {
/// Attempts to pull out the next value of the delay queue, registering the
/// current task for wakeup if the value is not yet available, and returning
/// None if the queue is exhausted.
/// `None` if the queue is exhausted.
pub fn poll_expired(
&mut self,
cx: &mut task::Context<'_>,
@@ -391,7 +391,7 @@ impl<T> DelayQueue<T> {
///
/// `value` is stored in the queue until `timeout` duration has
/// elapsed after `insert` was called. At that point, `value` will
/// be returned from [`poll_expired`]. If `timeout` a Duration of
/// be returned from [`poll_expired`]. If `timeout` is a `Duration` of
/// zero, then `value` is immediately made available to poll.
///
/// The return value represents the insertion and is used as an
@@ -419,12 +419,12 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
///
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// # }
/// ```
///
@@ -452,11 +452,12 @@ impl<T> DelayQueue<T> {
}
}
/// Removes the key fom the expired queue or the timer wheel
/// depending on its expiration status
/// Removes the key from the expired queue or the timer wheel
/// depending on its expiration status.
///
/// # Panics
/// Panics if the key is not contained in the expired queue or the wheel
///
/// Panics if the key is not contained in the expired queue or the wheel.
fn remove_key(&mut self, key: &Key) {
use crate::time::wheel::Stack;
@@ -488,12 +489,12 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
///
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// // Remove the entry
/// let item = delay_queue.remove(&key);
/// assert_eq!(*item.get_ref(), "foo");
/// # }
/// ```
pub fn remove(&mut self, key: &Key) -> Expired<T> {
@@ -531,14 +532,14 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
///
/// // "foo" is scheduled to be returned in 5 seconds
/// // "foo" is scheduled to be returned in 5 seconds
///
/// delay_queue.reset_at(&key, Instant::now() + Duration::from_secs(10));
/// delay_queue.reset_at(&key, Instant::now() + Duration::from_secs(10));
///
/// // "foo"is now scheduled to be returned in 10 seconds
/// // "foo" is now scheduled to be returned in 10 seconds
/// # }
/// ```
pub fn reset_at(&mut self, key: &Key, when: Instant) {
@@ -548,6 +549,8 @@ impl<T> DelayQueue<T> {
let when = self.normalize_deadline(when);
self.slab[key.index].when = when;
self.slab[key.index].expired = false;
self.insert_idx(when, key.index);
let next_deadline = self.next_deadline();
@@ -557,7 +560,7 @@ impl<T> DelayQueue<T> {
}
}
/// Returns the next time poll as determined by the wheel
/// Returns the next time to poll as determined by the wheel
fn next_deadline(&mut self) -> Option<Instant> {
self.wheel
.poll_at()
@@ -589,14 +592,14 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
/// let mut delay_queue = DelayQueue::new();
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
///
/// // "foo" is scheduled to be returned in 5 seconds
/// // "foo" is scheduled to be returned in 5 seconds
///
/// delay_queue.reset(&key, Duration::from_secs(10));
/// delay_queue.reset(&key, Duration::from_secs(10));
///
/// // "foo"is now scheduled to be returned in 10 seconds
/// // "foo"is now scheduled to be returned in 10 seconds
/// # }
/// ```
pub fn reset(&mut self, key: &Key, timeout: Duration) {
@@ -619,15 +622,15 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let mut delay_queue = DelayQueue::new();
///
/// delay_queue.insert("foo", Duration::from_secs(5));
/// delay_queue.insert("foo", Duration::from_secs(5));
///
/// assert!(!delay_queue.is_empty());
/// assert!(!delay_queue.is_empty());
///
/// delay_queue.clear();
/// delay_queue.clear();
///
/// assert!(delay_queue.is_empty());
/// assert!(delay_queue.is_empty());
/// # }
/// ```
pub fn clear(&mut self) {
@@ -661,10 +664,10 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue: DelayQueue<i32> = DelayQueue::with_capacity(10);
/// assert_eq!(delay_queue.len(), 0);
/// delay_queue.insert(3, Duration::from_secs(5));
/// assert_eq!(delay_queue.len(), 1);
/// let mut delay_queue: DelayQueue<i32> = DelayQueue::with_capacity(10);
/// assert_eq!(delay_queue.len(), 0);
/// delay_queue.insert(3, Duration::from_secs(5));
/// assert_eq!(delay_queue.len(), 1);
/// # }
/// ```
pub fn len(&self) -> usize {
@@ -696,12 +699,12 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// let mut delay_queue = DelayQueue::new();
///
/// delay_queue.insert("hello", Duration::from_secs(10));
/// delay_queue.reserve(10);
/// delay_queue.insert("hello", Duration::from_secs(10));
/// delay_queue.reserve(10);
///
/// assert!(delay_queue.capacity() >= 11);
/// assert!(delay_queue.capacity() >= 11);
/// # }
/// ```
pub fn reserve(&mut self, additional: usize) {
@@ -711,7 +714,7 @@ impl<T> DelayQueue<T> {
/// Returns `true` if there are no items in the queue.
///
/// Note that this function returns `false` even if all items have not yet
/// expired and a call to `poll` will return `NotReady`.
/// expired and a call to `poll` will return `Poll::Pending`.
///
/// # Examples
///
@@ -721,11 +724,11 @@ impl<T> DelayQueue<T> {
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut delay_queue = DelayQueue::new();
/// assert!(delay_queue.is_empty());
/// let mut delay_queue = DelayQueue::new();
/// assert!(delay_queue.is_empty());
///
/// delay_queue.insert("hello", Duration::from_secs(5));
/// assert!(!delay_queue.is_empty());
/// delay_queue.insert("hello", Duration::from_secs(5));
/// assert!(!delay_queue.is_empty());
/// # }
/// ```
pub fn is_empty(&self) -> bool {
+2 -4
View File
@@ -46,7 +46,7 @@ impl<T: Stack> Level<T> {
() => {
T::default()
};
};
}
Level {
level,
@@ -233,14 +233,13 @@ fn slot_for(duration: u64, level: usize) -> usize {
((duration >> (level * 6)) % LEVEL_MULT as u64) as usize
}
/*
#[cfg(all(test, not(loom)))]
mod test {
use super::*;
#[test]
fn test_slot_for() {
for pos in 1..64 {
for pos in 0..64 {
assert_eq!(pos as usize, slot_for(pos, 0));
}
@@ -252,4 +251,3 @@ mod test {
}
}
}
*/
+14 -4
View File
@@ -116,9 +116,17 @@ where
Ok(())
}
/// Remove `item` from thee timing wheel.
/// Remove `item` from the timing wheel.
pub(crate) fn remove(&mut self, item: &T::Borrowed, store: &mut T::Store) {
let when = T::when(item, store);
assert!(
self.elapsed <= when,
"elapsed={}; when={}",
self.elapsed,
when
);
let level = self.level_for(when);
self.levels[level].remove_entry(when, item, store);
@@ -240,9 +248,11 @@ where
}
fn level_for(elapsed: u64, when: u64) -> usize {
let masked = elapsed ^ when;
const SLOT_MASK: u64 = (1 << 6) - 1;
assert!(masked != 0, "elapsed={}; when={}", elapsed, when);
// Mask in the trailing bits ignored by the level calculation in order to cap
// the possible leading zeros
let masked = elapsed ^ when | SLOT_MASK;
let leading_zeros = masked.leading_zeros() as usize;
let significant = 63 - leading_zeros;
@@ -255,7 +265,7 @@ mod test {
#[test]
fn test_level_for() {
for pos in 1..64 {
for pos in 0..64 {
assert_eq!(
0,
level_for(0, pos),
+2 -2
View File
@@ -1,7 +1,7 @@
use crate::codec::{Decoder, Encoder};
use futures_core::Stream;
use tokio::{io::ReadBuf, net::UdpSocket};
use tokio_stream::Stream;
use bytes::{BufMut, BytesMut};
use futures_core::ready;
@@ -28,7 +28,7 @@ use std::{io, mem::MaybeUninit};
/// 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
/// [`Stream`]: futures_core::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"]
+245 -1
View File
@@ -1,6 +1,6 @@
#![warn(rust_2018_idioms)]
use tokio_util::codec::{BytesCodec, Decoder, Encoder, LinesCodec};
use tokio_util::codec::{AnyDelimiterCodec, BytesCodec, Decoder, Encoder, LinesCodec};
use bytes::{BufMut, Bytes, BytesMut};
@@ -201,7 +201,26 @@ fn lines_decoder_discard_repeat() {
buf.put_slice(b"aa");
assert!(codec.decode(buf).is_err());
buf.put_slice(b"a");
assert_eq!(None, codec.decode(buf).unwrap());
}
// Regression test for [subsequent calls to LinesCodec decode does not return the desired results bug](https://github.com/tokio-rs/tokio/issues/3555)
#[test]
fn lines_decoder_max_length_underrun_twice() {
const MAX_LENGTH: usize = 11;
let mut codec = LinesCodec::new_with_max_length(MAX_LENGTH);
let buf = &mut BytesMut::new();
buf.reserve(200);
buf.put_slice(b"line ");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b"too very l");
assert!(codec.decode(buf).is_err());
buf.put_slice(b"aaaaaaaaaaaaaaaaaaaaaaa");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b"ong\nshort\n");
assert_eq!("short", codec.decode(buf).unwrap().unwrap());
}
#[test]
@@ -215,3 +234,228 @@ fn lines_encoder() {
codec.encode("line 2", &mut buf).unwrap();
assert_eq!("line 1\nline 2\n", buf);
}
#[test]
fn any_delimiters_decoder_any_character() {
let mut codec = AnyDelimiterCodec::new(b",;\n\r".to_vec(), b",".to_vec());
let buf = &mut BytesMut::new();
buf.reserve(200);
buf.put_slice(b"chunk 1,chunk 2;chunk 3\n\r");
assert_eq!("chunk 1", codec.decode(buf).unwrap().unwrap());
assert_eq!("chunk 2", codec.decode(buf).unwrap().unwrap());
assert_eq!("chunk 3", codec.decode(buf).unwrap().unwrap());
assert_eq!("", codec.decode(buf).unwrap().unwrap());
assert_eq!(None, codec.decode(buf).unwrap());
assert_eq!(None, codec.decode_eof(buf).unwrap());
buf.put_slice(b"k");
assert_eq!(None, codec.decode(buf).unwrap());
assert_eq!("k", codec.decode_eof(buf).unwrap().unwrap());
assert_eq!(None, codec.decode(buf).unwrap());
assert_eq!(None, codec.decode_eof(buf).unwrap());
}
#[test]
fn any_delimiters_decoder_max_length() {
const MAX_LENGTH: usize = 7;
let mut codec =
AnyDelimiterCodec::new_with_max_length(b",;\n\r".to_vec(), b",".to_vec(), MAX_LENGTH);
let buf = &mut BytesMut::new();
buf.reserve(200);
buf.put_slice(b"chunk 1 is too long\nchunk 2\nchunk 3\r\nchunk 4\n\r\n");
assert!(codec.decode(buf).is_err());
let chunk = codec.decode(buf).unwrap().unwrap();
assert!(
chunk.len() <= MAX_LENGTH,
"{:?}.len() <= {:?}",
chunk,
MAX_LENGTH
);
assert_eq!("chunk 2", chunk);
let chunk = codec.decode(buf).unwrap().unwrap();
assert!(
chunk.len() <= MAX_LENGTH,
"{:?}.len() <= {:?}",
chunk,
MAX_LENGTH
);
assert_eq!("chunk 3", chunk);
// \r\n cause empty chunk
let chunk = codec.decode(buf).unwrap().unwrap();
assert!(
chunk.len() <= MAX_LENGTH,
"{:?}.len() <= {:?}",
chunk,
MAX_LENGTH
);
assert_eq!("", chunk);
let chunk = codec.decode(buf).unwrap().unwrap();
assert!(
chunk.len() <= MAX_LENGTH,
"{:?}.len() <= {:?}",
chunk,
MAX_LENGTH
);
assert_eq!("chunk 4", chunk);
let chunk = codec.decode(buf).unwrap().unwrap();
assert!(
chunk.len() <= MAX_LENGTH,
"{:?}.len() <= {:?}",
chunk,
MAX_LENGTH
);
assert_eq!("", chunk);
let chunk = codec.decode(buf).unwrap().unwrap();
assert!(
chunk.len() <= MAX_LENGTH,
"{:?}.len() <= {:?}",
chunk,
MAX_LENGTH
);
assert_eq!("", chunk);
assert_eq!(None, codec.decode(buf).unwrap());
assert_eq!(None, codec.decode_eof(buf).unwrap());
buf.put_slice(b"k");
assert_eq!(None, codec.decode(buf).unwrap());
let chunk = codec.decode_eof(buf).unwrap().unwrap();
assert!(
chunk.len() <= MAX_LENGTH,
"{:?}.len() <= {:?}",
chunk,
MAX_LENGTH
);
assert_eq!("k", chunk);
assert_eq!(None, codec.decode(buf).unwrap());
assert_eq!(None, codec.decode_eof(buf).unwrap());
// Delimiter that's one character too long. This could cause an out of bounds
// error if we peek at the next characters using slice indexing.
buf.put_slice(b"aaabbbcc");
assert!(codec.decode(buf).is_err());
}
#[test]
fn any_delimiter_decoder_max_length_underrun() {
const MAX_LENGTH: usize = 7;
let mut codec =
AnyDelimiterCodec::new_with_max_length(b",;\n\r".to_vec(), b",".to_vec(), MAX_LENGTH);
let buf = &mut BytesMut::new();
buf.reserve(200);
buf.put_slice(b"chunk ");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b"too l");
assert!(codec.decode(buf).is_err());
buf.put_slice(b"ong\n");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b"chunk 2");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b",");
assert_eq!("chunk 2", codec.decode(buf).unwrap().unwrap());
}
#[test]
fn any_delimiter_decoder_max_length_underrun_twice() {
const MAX_LENGTH: usize = 11;
let mut codec =
AnyDelimiterCodec::new_with_max_length(b",;\n\r".to_vec(), b",".to_vec(), MAX_LENGTH);
let buf = &mut BytesMut::new();
buf.reserve(200);
buf.put_slice(b"chunk ");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b"too very l");
assert!(codec.decode(buf).is_err());
buf.put_slice(b"aaaaaaaaaaaaaaaaaaaaaaa");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b"ong\nshort\n");
assert_eq!("short", codec.decode(buf).unwrap().unwrap());
}
#[test]
fn any_delimiter_decoder_max_length_bursts() {
const MAX_LENGTH: usize = 11;
let mut codec =
AnyDelimiterCodec::new_with_max_length(b",;\n\r".to_vec(), b",".to_vec(), MAX_LENGTH);
let buf = &mut BytesMut::new();
buf.reserve(200);
buf.put_slice(b"chunk ");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b"too l");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b"ong\n");
assert!(codec.decode(buf).is_err());
}
#[test]
fn any_delimiter_decoder_max_length_big_burst() {
const MAX_LENGTH: usize = 11;
let mut codec =
AnyDelimiterCodec::new_with_max_length(b",;\n\r".to_vec(), b",".to_vec(), MAX_LENGTH);
let buf = &mut BytesMut::new();
buf.reserve(200);
buf.put_slice(b"chunk ");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b"too long!\n");
assert!(codec.decode(buf).is_err());
}
#[test]
fn any_delimiter_decoder_max_length_delimiter_between_decodes() {
const MAX_LENGTH: usize = 5;
let mut codec =
AnyDelimiterCodec::new_with_max_length(b",;\n\r".to_vec(), b",".to_vec(), MAX_LENGTH);
let buf = &mut BytesMut::new();
buf.reserve(200);
buf.put_slice(b"hello");
assert_eq!(None, codec.decode(buf).unwrap());
buf.put_slice(b",world");
assert_eq!("hello", codec.decode(buf).unwrap().unwrap());
}
#[test]
fn any_delimiter_decoder_discard_repeat() {
const MAX_LENGTH: usize = 1;
let mut codec =
AnyDelimiterCodec::new_with_max_length(b",;\n\r".to_vec(), b",".to_vec(), MAX_LENGTH);
let buf = &mut BytesMut::new();
buf.reserve(200);
buf.put_slice(b"aa");
assert!(codec.decode(buf).is_err());
buf.put_slice(b"a");
assert_eq!(None, codec.decode(buf).unwrap());
}
#[test]
fn any_delimiter_encoder() {
let mut codec = AnyDelimiterCodec::new(b",".to_vec(), b";--;".to_vec());
let mut buf = BytesMut::new();
codec.encode("chunk 1", &mut buf).unwrap();
assert_eq!("chunk 1;--;", buf);
codec.encode("chunk 2", &mut buf).unwrap();
assert_eq!("chunk 1;--;chunk 2;--;", buf);
}
+23
View File
@@ -254,6 +254,29 @@ fn multi_frames_on_eof() {
});
}
#[test]
fn read_eof_then_resume() {
let mut task = task::spawn(());
let mock = mock! {
Ok(b"\x00\x00\x00\x01".to_vec()),
Ok(b"".to_vec()),
Ok(b"\x00\x00\x00\x02".to_vec()),
Ok(b"".to_vec()),
Ok(b"\x00\x00\x00\x03".to_vec()),
};
let mut framed = FramedRead::new(mock, U32Decoder);
task.enter(|cx, _| {
assert_read!(pin!(framed).poll_next(cx), 1);
assert!(assert_ready!(pin!(framed).poll_next(cx)).is_none());
assert_read!(pin!(framed).poll_next(cx), 2);
assert!(assert_ready!(pin!(framed).poll_next(cx)).is_none());
assert_read!(pin!(framed).poll_next(cx), 3);
assert!(assert_ready!(pin!(framed).poll_next(cx)).is_none());
assert!(assert_ready!(pin!(framed).poll_next(cx)).is_none());
});
}
// ===== Mock ======
struct Mock {
+95
View File
@@ -0,0 +1,95 @@
use futures::future::poll_fn;
use tokio::sync::mpsc::channel;
use tokio_test::task::spawn;
use tokio_test::{assert_pending, assert_ready, assert_ready_err, assert_ready_ok};
use tokio_util::sync::PollSender;
#[tokio::test]
async fn test_simple() {
let (send, mut recv) = channel(3);
let mut send = PollSender::new(send);
for i in 1..=3i32 {
send.start_send(i).unwrap();
assert_ready_ok!(spawn(poll_fn(|cx| send.poll_send_done(cx))).poll());
}
send.start_send(4).unwrap();
let mut fourth_send = spawn(poll_fn(|cx| send.poll_send_done(cx)));
assert_pending!(fourth_send.poll());
assert_eq!(recv.recv().await.unwrap(), 1);
assert!(fourth_send.is_woken());
assert_ready_ok!(fourth_send.poll());
drop(recv);
// Here, start_send is not guaranteed to fail, but if it doesn't the first
// call to poll_send_done should.
if send.start_send(5).is_ok() {
assert_ready_err!(spawn(poll_fn(|cx| send.poll_send_done(cx))).poll());
}
}
#[tokio::test]
async fn test_abort() {
let (send, mut recv) = channel(3);
let mut send = PollSender::new(send);
let send2 = send.clone_inner().unwrap();
for i in 1..=3i32 {
send.start_send(i).unwrap();
assert_ready_ok!(spawn(poll_fn(|cx| send.poll_send_done(cx))).poll());
}
send.start_send(4).unwrap();
{
let mut fourth_send = spawn(poll_fn(|cx| send.poll_send_done(cx)));
assert_pending!(fourth_send.poll());
assert_eq!(recv.recv().await.unwrap(), 1);
assert!(fourth_send.is_woken());
}
let mut send2_send = spawn(send2.send(5));
assert_pending!(send2_send.poll());
send.abort_send();
assert!(send2_send.is_woken());
assert_ready_ok!(send2_send.poll());
assert_eq!(recv.recv().await.unwrap(), 2);
assert_eq!(recv.recv().await.unwrap(), 3);
assert_eq!(recv.recv().await.unwrap(), 5);
}
#[tokio::test]
async fn close_sender_last() {
let (send, mut recv) = channel::<i32>(3);
let mut send = PollSender::new(send);
let mut recv_task = spawn(recv.recv());
assert_pending!(recv_task.poll());
send.close_this_sender();
assert!(recv_task.is_woken());
assert!(assert_ready!(recv_task.poll()).is_none());
}
#[tokio::test]
async fn close_sender_not_last() {
let (send, mut recv) = channel::<i32>(3);
let send2 = send.clone();
let mut send = PollSender::new(send);
let mut recv_task = spawn(recv.recv());
assert_pending!(recv_task.poll());
send.close_this_sender();
assert!(!recv_task.is_woken());
assert_pending!(recv_task.poll());
drop(send2);
assert!(recv_task.is_woken());
assert!(assert_ready!(recv_task.poll()).is_none());
}
+36
View File
@@ -0,0 +1,36 @@
use std::future::Future;
use std::sync::Arc;
use std::task::Poll;
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
use tokio_util::sync::PollSemaphore;
type SemRet = Option<OwnedSemaphorePermit>;
fn semaphore_poll<'a>(
sem: &'a mut PollSemaphore,
) -> tokio_test::task::Spawn<impl Future<Output = SemRet> + 'a> {
let fut = futures::future::poll_fn(move |cx| sem.poll_acquire(cx));
tokio_test::task::spawn(fut)
}
#[tokio::test]
async fn it_works() {
let sem = Arc::new(Semaphore::new(1));
let mut poll_sem = PollSemaphore::new(sem.clone());
let permit = sem.acquire().await.unwrap();
let mut poll = semaphore_poll(&mut poll_sem);
assert!(poll.poll().is_pending());
drop(permit);
assert!(matches!(poll.poll(), Poll::Ready(Some(_))));
drop(poll);
sem.close();
assert!(semaphore_poll(&mut poll_sem).await.is_none());
// Check that it is fused.
assert!(semaphore_poll(&mut poll_sem).await.is_none());
assert!(semaphore_poll(&mut poll_sem).await.is_none());
}
+72
View File
@@ -0,0 +1,72 @@
use futures::future::FutureExt;
use std::alloc::Layout;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio_util::sync::ReusableBoxFuture;
#[test]
fn test_different_futures() {
let fut = async move { 10 };
// Not zero sized!
assert_eq!(Layout::for_value(&fut).size(), 1);
let mut b = ReusableBoxFuture::new(fut);
assert_eq!(b.get_pin().now_or_never(), Some(10));
b.try_set(async move { 20 })
.unwrap_or_else(|_| panic!("incorrect size"));
assert_eq!(b.get_pin().now_or_never(), Some(20));
b.try_set(async move { 30 })
.unwrap_or_else(|_| panic!("incorrect size"));
assert_eq!(b.get_pin().now_or_never(), Some(30));
}
#[test]
fn test_different_sizes() {
let fut1 = async move { 10 };
let val = [0u32; 1000];
let fut2 = async move { val[0] };
let fut3 = ZeroSizedFuture {};
assert_eq!(Layout::for_value(&fut1).size(), 1);
assert_eq!(Layout::for_value(&fut2).size(), 4004);
assert_eq!(Layout::for_value(&fut3).size(), 0);
let mut b = ReusableBoxFuture::new(fut1);
assert_eq!(b.get_pin().now_or_never(), Some(10));
b.set(fut2);
assert_eq!(b.get_pin().now_or_never(), Some(0));
b.set(fut3);
assert_eq!(b.get_pin().now_or_never(), Some(5));
}
struct ZeroSizedFuture {}
impl Future for ZeroSizedFuture {
type Output = u32;
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<u32> {
Poll::Ready(5)
}
}
#[test]
fn test_zero_sized() {
let fut = ZeroSizedFuture {};
// Zero sized!
assert_eq!(Layout::for_value(&fut).size(), 0);
let mut b = ReusableBoxFuture::new(fut);
assert_eq!(b.get_pin().now_or_never(), Some(5));
assert_eq!(b.get_pin().now_or_never(), Some(5));
b.try_set(ZeroSizedFuture {})
.unwrap_or_else(|_| panic!("incorrect size"));
assert_eq!(b.get_pin().now_or_never(), Some(5));
assert_eq!(b.get_pin().now_or_never(), Some(5));
}
+61
View File
@@ -245,6 +245,35 @@ async fn reset_twice() {
assert!(queue.is_woken());
}
/// Regression test: Given an entry inserted with a deadline in the past, so
/// that it is placed directly on the expired queue, reset the entry to a
/// deadline in the future. Validate that this leaves the entry and queue in an
/// internally consistent state by running an additional reset on the entry
/// before polling it to completion.
#[tokio::test]
async fn repeatedly_reset_entry_inserted_as_expired() {
time::pause();
let mut queue = task::spawn(DelayQueue::new());
let now = Instant::now();
let key = queue.insert_at("foo", now - ms(100));
queue.reset_at(&key, now + ms(100));
queue.reset_at(&key, now + ms(50));
assert_pending!(poll!(queue));
time::sleep_until(now + ms(60)).await;
assert!(queue.is_woken());
let entry = assert_ready_ok!(poll!(queue)).into_inner();
assert_eq!(entry, "foo");
let entry = assert_ready!(poll!(queue));
assert!(entry.is_none());
}
#[tokio::test]
async fn remove_expired_item() {
time::pause();
@@ -261,6 +290,38 @@ async fn remove_expired_item() {
assert_eq!(entry.into_inner(), "foo");
}
/// Regression test: it should be possible to remove entries which fall in the
/// 0th slot of the internal timer wheel — that is, entries whose expiration
/// (a) falls at the beginning of one of the wheel's hierarchical levels and (b)
/// is equal to the wheel's current elapsed time.
#[tokio::test]
async fn remove_at_timer_wheel_threshold() {
time::pause();
let mut queue = task::spawn(DelayQueue::new());
let now = Instant::now();
let key1 = queue.insert_at("foo", now + ms(64));
let key2 = queue.insert_at("bar", now + ms(64));
sleep(ms(80)).await;
let entry = assert_ready_ok!(poll!(queue)).into_inner();
match entry {
"foo" => {
let entry = queue.remove(&key2).into_inner();
assert_eq!(entry, "bar");
}
"bar" => {
let entry = queue.remove(&key1).into_inner();
assert_eq!(entry, "foo");
}
other => panic!("other: {:?}", other),
}
}
#[tokio::test]
async fn expires_before_last_insert() {
time::pause();
+928 -367
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -2,18 +2,17 @@
name = "tokio"
# When releasing to crates.io:
# - Remove path dependencies
# - Update html_root_url.
# - Update doc url
# - Cargo.toml
# - README.md
# - Update CHANGELOG.md.
# - Create "v1.0.x" git tag.
version = "1.0.0"
version = "1.5.2"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
readme = "README.md"
documentation = "https://docs.rs/tokio/1.0.0/tokio/"
documentation = "https://docs.rs/tokio/1.5.2/tokio/"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
description = """
@@ -86,7 +85,7 @@ test-util = []
time = []
[dependencies]
tokio-macros = { version = "1.0.0", path = "../tokio-macros", optional = true }
tokio-macros = { version = "1.1.0", path = "../tokio-macros", optional = true }
pin-project-lite = "0.2.0"
@@ -120,12 +119,13 @@ optional = true
tokio-test = { version = "0.4.0", path = "../tokio-test" }
tokio-stream = { version = "0.1", path = "../tokio-stream" }
futures = { version = "0.3.0", features = ["async-await"] }
proptest = "0.10.0"
proptest = "1"
rand = "0.8.0"
tempfile = "3.1.0"
async-stream = "0.3"
[target.'cfg(loom)'.dev-dependencies]
loom = { version = "0.4", features = ["futures", "checkpoint"] }
loom = { version = "0.5", features = ["futures", "checkpoint"] }
[build-dependencies]
autocfg = "1" # Needed for conditionally enabling `track-caller`
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2020 Tokio Contributors
Copyright (c) 2021 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated

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