Compare commits

...
Author SHA1 Message Date
Eliza Weisman 00500d1b35 util: prepare v0.5.1 release (#3210)
### Added

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

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

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

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

## Solution

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

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

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

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

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

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

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

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

## Solution

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

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

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

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

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

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

Refs: #3082

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

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

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

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

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

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

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

Some small cleanup items are apparent after merge of #2951

## Solution

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

Fixes: #3103

* make clippy happy

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

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

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

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

## Solution

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

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

Closes #3073

Signed-off-by: Eliza Weisman <[email protected]>
2020-11-01 10:48:44 -08:00
Dirkjan Ochtman 2b23aa7389 util: add back public poll_read_buf() function (#3079)
This was accidentally removed in #3064.
2020-11-01 10:22:22 +01:00
Finn Behrens 382ee6bf5d net: add pid to tokio::net::unix::UCred (#2633) 2020-10-31 10:30:55 +01:00
Carl Lerche 24ed874e81 chore: prepare tokio-util v0.5.0 release (#3078) 2020-10-30 11:26:15 -07:00
Dirkjan Ochtman 3965d91a5e util: update to bytes 0.6 (#3071)
Copies the implementation of poll_read_buf() from tokio::io::util::read_buf.
2020-10-29 10:45:19 -07:00
Dirkjan Ochtman a3ef4e4cf5 util: deduplicate implementations of poll_read_buf() (#3064) 2020-10-29 13:20:38 +01:00
Naja Melan 34eb47dde5 runtime: block_on should NOT be called from async context (#3070) 2020-10-29 12:10:42 +01:00
Tom Kaitchuck c8a484bbb2 tokio: remove unused dependency (#3063)
Signed-off-by: Tom Kaitchuck <[email protected]>
2020-10-28 08:06:53 +01:00
Carl Lerche 9097ae548f chore: prepare v0.3.2 release (#3059) 2020-10-27 14:31:39 -07:00
Carl Lerche d78655337a Revert "util: upgrade tokio-util to bytes 0.6 (#3052)" (#3060)
This reverts commit fe2b997.

We are avoiding adding poll_read_buf to tokio itself for now. The patch is
reverted now in order to not block the v0.3.2 release (#3059).
2020-10-27 13:42:00 -07:00
Zahari Dichev 38605c5c85 net: change mention of net2 (#3056) 2020-10-27 09:34:17 -07:00
Dirkjan Ochtman fe2b997675 util: upgrade tokio-util to bytes 0.6 (#3052) 2020-10-27 09:30:29 +01:00
Sean McArthur 6d0ba19af5 sync: make oneshot::Sender::poll_closed public again (#3032) 2020-10-26 08:54:25 -07:00
Alice Ryhl cbb8fe6069 udp: add UdpSocket::take_error (#3051) 2020-10-26 12:50:48 +01:00
Alice Ryhl a9da220923 oneshot: update closed() docs to use tokio::select! (#3050) 2020-10-26 11:44:46 +01:00
Alice Ryhl 1c28c3b0a8 macros: prepare tokio-macros 0.3.1 (#3042) 2020-10-26 10:02:39 +01:00
nickelc e31bd321ef readme: update the MSRV to 1.45 (#3048) 2020-10-26 09:46:15 +01:00
nickelc c30ce1f65c docs: remove max_threads mentions in tokio-macros (#3038) 2020-10-24 22:34:56 +02:00
Alice Ryhl a95378a850 io: expand on de-initialization of ReadBuf (#3035) 2020-10-24 22:29:19 +02:00
Zahari Dichev ce173fdc91 docs: update docs for from_std functions (#3016)
Fixes: #3007
2020-10-24 14:26:01 +02:00
Zahari Dichev e804f88d60 sync: add mem::forget to RwLockWriteGuard::downgrade. (#2957)
Currently when `RwLockWriteGuard::downgrade` the `MAX_READS - 1`
permits are added to the semaphore. When `RwLockWriteGuard::drop`
gets invoked however another `MAX_READS` permits are added. This
results in releasing more permits that were actually aquired when
downgrading a write to a read lock. This is why we need to call
`mem::forget` on the `RwLockWriteGuard` in order to avoid
invoking the destructor.

Fixes: #2941
2020-10-23 10:07:00 -07:00
bdonlan c153913211 io: Add AsyncFd, fix io::driver shutdown (#2903)
* io: Add AsyncFd

This adds AsyncFd, a unix-only structure to allow for read/writability states
to be monitored for arbitrary file descriptors.

Issue: #2728

* driver: fix shutdown notification unreliability

Previously, there was a race window in which an IO driver shutting down could
fail to notify ScheduledIo instances of this state; in particular, notification
of outstanding ScheduledIo registrations was driven by `Driver::drop`, but
registrations bypass `Driver` and go directly to a `Weak<Inner>`. The `Driver`
holds the `Arc<Inner>` keeping `Inner` alive, but it's possible that a new
handle could be registered (or a new readiness future created for an existing
handle) after the `Driver::drop` handler runs and prior to `Inner` being
dropped.

This change fixes this in two parts: First, notification of outstanding
ScheduledIo handles is pushed down into the drop method of `Inner` instead,
and, second, we add state to ScheduledIo to ensure that we remember that the IO
driver we're bound to has shut down after the initial shutdown notification, so
that subsequent readiness future registrations can immediately return (instead
of potentially blocking indefinitely).

Fixes: #2924
2020-10-22 14:12:41 -07:00
Evan Cameron 358e4f9f80 tokio: add back poll_* for udp (#2981) 2020-10-22 09:58:00 -07:00
Zhang Jingqiang adf822f5cc net: fix typo (#3023) 2020-10-22 08:59:47 +02:00
Carl Lerche d14cbf9116 chore: prepare v0.3.1 release (#3021) 2020-10-21 16:23:35 -07:00
Carl Lerche 8bfb1c92ce sync: revert Clone impl for broadcast::Receiver (#3020)
The `Receiver` handle maintains a position in the broadcast channel for
itself. Cloning implies copying the state of the value. Intuitively,
cloning a `broadcast::Receiver` would return a new receiver with an
identical position. However, the current implementation returns a new
`Receiver` positioned at the tail of the channel.

This behavior subtlety is why `new_subscriber()` is used to create
`Receiver` handles. An alternate API should consider the position issue.

Refs: #2933
2020-10-21 15:14:52 -07:00
Carl Lerche b48fec9655 net: fix use-after-free in slab compaction (#3019)
An off-by-one bug results in freeing the incorrect page. This
also adds an `asan` CI job.

Fixes: 3014
2020-10-21 14:43:57 -07:00
Carl Lerche 8dbc3c7937 io: add AsyncReadExt::read_buf (#3003)
Brings back `read_buf` from 0.2. This will be stabilized as part of 1.0.
2020-10-21 14:08:49 -07:00
Marc-Antoine Perennou 7fbfa9b649 tokio: deduplicate spawn_blocking (#3017)
Move common code and tracing integration into Handle

Fixes #2998
Closes #3004

Signed-off-by: Marc-Antoine Perennou <[email protected]>
2020-10-21 20:00:48 +02:00
Nikolai Kuklin 7d7b79e1d5 sync: add is_closed method to watch sender (#2991) 2020-10-21 13:35:13 +02:00
Zahari Dichev 8f37544a79 io: explain how to determine number of bytes read in AsyncRead (#3011)
Fixes: #2999
2020-10-21 13:32:50 +02:00
Philip Kannegaard Hayes 43d0714898 sync: remove extra clone in Semaphore::[try_]acquire_owned (#3015) 2020-10-21 08:16:03 +02:00
Zahari Dichev 16e272ea4b fs: flush on shutdown (#3009)
Fixes: #2950
2020-10-20 17:42:32 +02:00
John-John Tedro 6d99e1c7de util: prevent read buffer from being swapped during a read_poll (#2993) 2020-10-20 11:14:02 +02:00
pluth f73a2ad238 docs: adjust TcpListener::from_std documentation to match behavior (#3002) 2020-10-19 21:37:00 -07:00
Alice Ryhl c793ead0c3 runtime: remove unneeded #[cfg(feature = "rt")] (#2996) 2020-10-19 21:35:33 -07:00
Marc-Antoine Perennou 2696794771 tokio: add Runtime::spawn_blocking (#2980)
This allows writing

rt.spawn_blocking(f);

instead of

let _enter = rt.enter();
tokio::task::spawn_blocking(f);

Signed-off-by: Marc-Antoine Perennou <[email protected]>
2020-10-19 18:49:16 +02:00
nickelc cfd643d691 docs: fix typo in runtime module documentation (#2992) 2020-10-19 15:33:10 +02:00
John-John Tedro 8d17261a4b util: add a poll_read_buf shim to tokio-util (#2972) 2020-10-19 11:06:06 +02:00
Zahari Dichev 423ecc187a io: add copy_buf (#2884) 2020-10-19 10:15:25 +02:00
Zephyr Shannon fb28caa90c sync: implement Clone for broadcast::Receiver (#2933) 2020-10-19 10:12:40 +02:00
Evan Cameron e88e64bcc0 docs: fix typos on UdpSocket (#2979) 2020-10-17 12:13:23 +02:00
messense 3cc6ce7a99 doc: update version to 0.3 in module documentation (#2974) 2020-10-16 13:49:30 +02:00
Alice Ryhl 81db03204d Fix doc typo (#2967) 2020-10-16 12:37:21 +02:00
John-John Tedro 1644511bdf Update documentation of AsyncRead to reflect use of ReadBuf 2020-10-16 11:55:35 +02:00
Carl Lerche dc9742fbea chore: post release Cargo.toml fixes (#2963) 2020-10-15 11:46:10 -07:00
Carl Lerche 12f1dffa2d chore: prepare for v0.3.0 release (#2960) 2020-10-15 09:22:07 -07:00
Taiki Endo 871289b3e7 ci: run clippy on MSRV (#2962) 2020-10-15 06:30:20 +09:00
Lucio Franco 30b40ef518 rt: update docs for 0.3 changes (#2956)
This PR updates the runtime module docs to the changes made in `0.3`
release of tokio.

Closes #2720
2020-10-13 15:49:19 -07:00
Carl Lerche 22fa883296 rt: tweak spawn_blocking docs (#2955) 2020-10-13 15:07:10 -07:00
Carl Lerche 00b6127f2e rt: switch enter to an RAII guard (#2954) 2020-10-13 15:06:22 -07:00
Ivan Petkov a249421abc process: update docs regarding zombie processes (#2952) 2020-10-13 00:42:17 +00:00
Carl Lerche 1923350880 meta: combine net and dns, use parking_lot (#2951)
This combines the `dns` and `net` feature flags. Previously, `dns` was
included as part of `net`. Given that is is rare that one would want
`dns` without `net`, DNS is now entirely gated w/ `net`.

The `parking_lot` feature is included as part of `full`.

Some misc docs are tweaked to reflect feature flag changes.
2020-10-12 16:06:02 -07:00
Taiki Endo c90681bd8e rt: simplify rt-* features (#2949)
tokio:

    merge rt-core and rt-util as rt
    rename rt-threaded to rt-multi-thread

tokio-util:

    rename rt-core to rt

Closes #2942
2020-10-12 14:13:23 -07:00
Ivan Petkov 24d0a0cfa8 chore: refactor runtime driver usage of Either (#2918) 2020-10-12 19:57:22 +00:00
Lucio FrancoandAlice Ryhl 07802b2c84 rt: worker_threads must be non-zero (#2947)
Co-authored-by: Alice Ryhl <[email protected]>
2020-10-12 15:15:40 -04:00
Taiki Endo 891de3271d net: merge tcp, udp, uds features to net feature (#2943) 2020-10-13 03:36:26 +09:00
8880222036 rt: Remove threaded_scheduler() and basic_scheduler() (#2876)
Co-authored-by: Alice Ryhl <[email protected]>
Co-authored-by: Carl Lerche <[email protected]>
2020-10-12 13:44:54 -04:00
Juan Alvarez 0893841f31 time: move error types into time::error (#2938) 2020-10-12 10:21:44 -07:00
Lucio FrancoandAlice Ryhl ec99e61945 time: Clean up Instant docs to align with std (#2946)
Co-authored-by: Alice Ryhl <[email protected]>
2020-10-12 13:06:55 -04:00
Lucio Franco f8c91f2ead io: Rename ReadBuf methods (#2945)
This changes `ReadBuf::add_filled` to `ReadBuf::advance` and
`ReadBuf::append` to `ReadBuf::put_slice`. This is just a
mechanical change.

Closes #2769
2020-10-12 12:41:40 -04:00
Zahari Dichev b575082543 sync: change chan closed(&mut self) to closed(&self) (#2939) 2020-10-12 12:09:36 -04:00
Taiki Endo c4f620cb30 chore: remove use of doc_alias feature (#2944) 2020-10-12 09:42:59 +02:00
Taiki Endo b047f647b7 net: make UCred fields private (#2936) 2020-10-11 09:31:26 +02:00
Taiki Endo 2e05399f4b sync: move broadcast error types into broadcast::error module (#2937)
Refs: #2928
2020-10-09 10:10:22 -07:00
Carl Lerche afe535283c fs: future proof File (#2930)
Changes inherent methods to take `&self` instead of `&mut self`. This
brings the API in line with `std`.

This patch is implemented by using a `tokio::sync::Mutex` to guard the
internal `File` state. This is not an ideal implementation strategy
doesn't make a big impact compared to having to dispatch operations to a
background thread followed by a blocking syscall.

In the future, the implementation can be improved as we explore async
file-system APIs provided by the operating-system (iocp / io_uring).

Closes #2927
2020-10-09 10:02:55 -07:00
Carl Lerche ee597347c5 net: switch socket methods to &self (#2934)
Switches various socket methods from &mut self to &self. This uses the intrusive
waker infrastructure to handle multiple waiters.

Refs: #2928
2020-10-09 09:16:42 -07:00
Taiki Endo 41ac1ae2bc io: make Seek and Copy private (#2935)
Refs: #2928
2020-10-09 08:33:14 -07:00
Juan Alvarez 60d81bbe10 time: rename Delay future to Sleep (#2932) 2020-10-08 20:35:12 -07:00
bdonlanandBryan Donlan b704c53b9c chore: Fix clippy lints (#2931)
Closes: #2929

Co-authored-by: Bryan Donlan <[email protected]>
2020-10-08 17:14:39 -07:00
Carl Lerche 066965cd59 net: use &self with TcpListener::accept (#2919)
Uses the infrastructure added by #2828 to enable switching
`TcpListener::accept` to use `&self`.

This also switches `poll_accept` to use `&self`. While doing introduces
a hazard, `poll_*` style functions are considered low-level. Most users
will use the `async fn` variants which are more misuse-resistant.

TcpListener::incoming() is temporarily removed as it has the same
problem as `TcpSocket::by_ref()` and will be implemented later.
2020-10-08 12:12:56 -07:00
Taiki Endo 6259893094 fs: add os::windows::OpenOptionsExt (#2923) 2020-10-08 11:09:12 +02:00
Zahari Dichev 43bd11bf2f io: remove Poll from the AsyncSeek::start_seek return value (#2885) 2020-10-08 10:56:01 +02:00
Taiki Endo d94ab62c54 util: fix a typo in sync/cancellation_token.rs (#2922) 2020-10-07 15:46:13 -07:00
Carl Lerche a9a59ea90e net: add TcpSocket for configuring a socket (#2920)
This enables the caller to configure the socket and to explicitly bind
the socket before converting it to a `TcpStream` or `TcpListener`.

Closes: #2902
2020-10-07 13:02:29 -07:00
Taiki Endo c248167173 fs: switch to our own DirEntryExt trait (#2921) 2020-10-08 02:30:25 +09:00
Evan Cameron 601a3ef93f docs: more docs for UdpSocket (#2883) 2020-10-06 18:12:02 -07:00
greenwoodcm fcdf9345bf time: clean time driver (#2905)
* remove unnecessary wheel::Poll

the timer wheel uses the `wheel::Poll` struct as input when
advancing the timer to the next time step.  the `Poll` struct
contains an instant representing the time step to advance to
and also contains an optional and mutable reference to an
`Expiration` struct.  from what I can tell, the latter field
is only used in the context of polling the wheel and does not
need to be exposed outside of that method.  without the
expiration field the `Poll` struct is nothing more than a
wrapper around the instant being polled.  this change removes
the `Poll` struct and updates integration points accordingly.

* remove Stack trait in favor of concrete Stack implementation

* remove timer Registration struct
2020-10-06 12:48:01 -07:00
Ivan Petkov 4cf45c038b process: add ProcessDriver to handle orphan reaping (#2907) 2020-10-06 17:30:16 +00:00
bdonlanandBryan Donlan 9730317e94 time: move DelayQueue to tokio-util (#2897)
This change is intended to do the minimum to unblock 0.3; as such, for now, we
duplicate the internal `time::wheel` structures in tokio-util, rather than trying
to refactor things at this stage.

Co-authored-by: Bryan Donlan <[email protected]>
2020-10-05 14:25:04 -07:00
Taiki Endo 02311dcfa1 io, stream: assert !Unpin for ext trait futures (#2913) 2020-10-06 04:44:34 +09:00
Alice Ryhl aa171f2aa9 stream: remove bytes from public API (#2908) 2020-10-05 10:33:15 -07:00
Taiki Endo c23c1ecbcb io, stream: make ext trait futures !Unpin (#2910)
Make these future `!Unpin` for compatibility with async trait methods.
2020-10-05 10:32:11 -07:00
Taiki Endo 561a71ad63 net: implement AsRawSocket on Windows (#2911) 2020-10-05 10:22:19 -07:00
Alice Ryhl 242ea01189 sync: broadcast channel API tweaks (#2898)
Removes deprecated APIs and makes some small breaking changes.
2020-10-05 09:30:48 -07:00
Mikail Bagishov 1684e1c809 io: optimize writing large buffers to windows stdio (#2888) 2020-10-05 16:07:46 +02:00
Taiki Endo 0ed4127d5c fs: seal OpenOptionsExt and DirBuilderExt (#2909) 2020-10-05 00:47:35 +09:00
Carl Lerche 1e585ccb51 io: update to Mio 0.7 (#2893)
This also makes Mio an implementation detail, removing it from the
public API.

This is based on #1767.
2020-10-02 13:54:00 -07:00
Alice Ryhl 7ec6d88b21 chore: make #[doc(hidden)] apis private (#2901) 2020-10-01 21:13:28 -07:00
Alice Ryhl 13de30c53e task: remove deprecated JoinError constructors (#2900) 2020-10-02 01:01:11 +03:00
Alice Ryhl 496e889917 Fix new clippy warning (#2899) 2020-10-02 00:59:48 +03:00
Juan Alvarez 53ccfc1fd6 time: introduce sleep and sleep_until functions (#2826) 2020-10-01 09:24:33 +02:00
Sean McArthur 971ed2c6df Seal FromStream methods with an internal argument (#2894) 2020-09-29 07:41:20 -07:00
Matt Kennedy dcb11118d2 test: fix spelling error in documentation (#2895)
Fixes: #2754
2020-09-29 13:23:11 +02:00
Linus Behrbohm 3403be5e2e stream: add iter and iter_mut methods to StreamMap (#2890) 2020-09-29 10:07:22 +02:00
Sean McArthur c6fc35aadf Seal ToSocketAddrs methods with an internal argument (#2892)
Closes #2891
2020-09-28 14:43:41 -07:00
Mikail BagishovandAlice Ryhl 078d0a2ebc sync: Add is_closed method to mpsc senders (#2726)
Co-authored-by: Alice Ryhl <[email protected]>
2020-09-28 11:37:28 -04:00
Mikail Bagishov 99d4061203 bench: fix unused_mut lint in benches (#2889) 2020-09-27 11:07:55 +02:00
Sean McArthur dfdfd61372 Fix readiness future eagerly consuming entire socket readiness (#2887)
In the `readiness` future, before inserting a waiter into the list, the current socket readiness is eagerly checked. However, it would return as a `ReadyEvent` the entire socket readiness, instead of just the interest desired from `readiness(interest)`. This would result in the later call to `clear_readiness(event)` removing all of it.

Closes #2886
2020-09-25 16:34:40 -07:00
Zahari Dichev 55d932a21f sync: add mpsc::Sender::closed future (#2840)
Adding closed future, makes it possible to select over closed and some other
work, so that the task is woken when the channel is closed and can proactively
cancel itself.

Added a mpsc::Sender::closed future that will become ready when the receiver
is closed.
2020-09-25 08:40:31 -07:00
Zahari Dichev 444660664b chore: handle std Mutex poisoning in a shim (#2872)
As tokio does not rely on poisoning, we can
avoid always unwrapping when locking by handling
the `PoisonError` in the Mutex shim.

Signed-off-by: Zahari Dichev <[email protected]>
2020-09-25 08:38:13 -07:00
Carl Lerche cf025ba45f sync: support mpsc send with &self (#2861)
Updates the mpsc channel to use the intrusive waker based sempahore.
This enables using `Sender` with `&self`.

Instead of using `Sender::poll_ready` to ensure capacity and updating
the `Sender` state, `async fn Sender::reserve()` is added. This function
returns a `Permit` value representing the reserved capacity.

Fixes: #2637
Refs: #2718 (intrusive waiters)
2020-09-24 17:26:38 -07:00
Carl Lerche 4186b0aa38 io: remove poll_{read,write}_buf from traits (#2882)
These functions have object safety issues. It also has been decided to
avoid vectored operations on the I/O traits. A later PR will bring back
vectored operations on specific types that support them.

Refs: #2879, #2716
2020-09-24 17:26:03 -07:00
bdonlanandBryan Donlan 760ae89401 chore: Use IoSlice's Copy impl to clean up some repetitive code (#2875)
As we go into 0.3 we no longer need to support older versions of Rust where
IoSlice did not implement Copy and Clone, so we can more easily initialize the
IoSlice array in net::tcp::stream.

Co-authored-by: Bryan Donlan <[email protected]>
2020-09-24 14:50:10 -07:00
Ivan Petkov 56acde069f chore: remove internal io-driver cargo feature (#2881) 2020-09-24 21:36:42 +00:00
Ivan Petkov ffa5bdb22d chore: remove internal io-readiness cargo feature (#2878) 2020-09-24 20:14:39 +00:00
Ivan Petkov a1d0681cd2 process: do not publicly turn on signal when enabled (#2871)
This change will still internally compile any `signal` resources
required when `process` is enabled on unix systems, but it will not
publicly turn on the cargo feature
2020-09-24 10:51:46 -07:00
Lucio Franco 4dfbdbff7e rt: Allow concurrent Shell:block_on calls (#2868) 2020-09-24 13:31:49 -04:00
Taiki Endo c29f13b7a5 docs: use #[doc(no_inline)] on re-exports (#2874) 2020-09-24 22:59:47 +09:00
Sean McArthur a0557840eb io: use intrusive wait list for I/O driver (#2828)
This refactors I/O registration in a few ways:

- Cleans up the cached readiness in `PollEvented`. This cache used to
  be helpful when readiness was a linked list of `*mut Node`s in
  `Registration`. Previous refactors have turned `Registration` into just
  an `AtomicUsize` holding the current readiness, so the cache is just
  extra work and complexity. Gone.
- Polling the `Registration` for readiness now gives a `ReadyEvent`,
  which includes the driver tick. This event must be passed back into
  `clear_readiness`, so that the readiness is only cleared from `Registration`
  if the tick hasn't changed. Previously, it was possible to clear the
  readiness even though another thread had *just* polled the driver and
  found the socket ready again.
- Registration now also contains an `async fn readiness`, which stores
  wakers in an instrusive linked list. This allows an unbounded number
  of tasks to register for readiness (previously, only 1 per direction (read
  and write)). By using the intrusive linked list, there is no concern of
  leaking the storage of the wakers, since they are stored inside the `async fn`
  and released when the future is dropped.
- Registration retains a `poll_readiness(Direction)` method, to support
  `AsyncRead` and `AsyncWrite`. They aren't able to use `async fn`s, and
  so there are 2 reserved slots for those methods.
- IO types where it makes sense to have multiple tasks waiting on them
  now take advantage of this new `async fn readiness`, such as `UdpSocket`
  and `UnixDatagram`.

Additionally, this makes the `io-driver` "feature" internal-only (no longer
documented, not part of public API), and adds a second internal-only
feature, `io-readiness`, to group together linked list part of registration
that is only used by some of the IO types.

After a bit of discussion, changing stream-based transports (like
`TcpStream`) to have `async fn read(&self)` is punted, since that
is likely too easy of a footgun to activate.

Refs: #2779, #2728
2020-09-23 13:02:15 -07:00
Lucio Franco f25f12d576 rt: Allow concurrent block_on's with basic_scheduler (#2804) 2020-09-23 14:35:10 -04:00
Daniel Henry-Mantilla 0f70530ee7 sync: add get_mut() for Mutex,RwLock (#2856) 2020-09-23 10:30:43 -07:00
kalcutter 3114d9e826 net: change UnixListener::poll_accept to public (#2845) 2020-09-23 16:21:18 +02:00
Alice Ryhl 5467f0a573 io: move #[cfg(not(loom))] to fix warning (#2864) 2020-09-23 11:04:52 +02:00
Mikail Bagishov 555b74c7cd io: fix stdout and stderr buffering on windows (#2734) 2020-09-23 08:16:05 +02:00
Ivan Petkov 7ae5b7bd4f signal: move driver to runtime thread (#2835)
Refactors the signal infrastructure to move the driver to the runtime
thread. This follows the model put forth by the I/O driver and time
driver.
2020-09-22 15:40:44 -07:00
Alice Ryhl e09b90ea32 macros: add #[allow(unused_mut)] to select! (#2858) 2020-09-23 05:56:45 +09:00
Taiki Endo cb8f2ceb2e chore: remove unused future/pending.rs (#2860) 2020-09-23 05:55:58 +09:00
Taiki Endo 6866b24ca1 ci: deny warnings on '--cfg tokio_unstable' tests (#2859) 2020-09-23 05:55:35 +09:00
Zahari Dichev e7091fde78 sync: Remove readiness assertion in `watch::Receiver::changed() (#2839)
*In `watch::Receiver::changed` `Notified` was polled
for the first time to ensure the waiter is registered while
assuming that the first poll will always return `Pending`.
It is the case however that another instance of `Notified`
is dropped without receiving its notification, this "orphaned"
notification can be used to satisfy another waiter without
even registering it. This commit accounts for that scenario.
2020-09-22 08:12:57 -07:00
Carl Lerche 2348f678e6 Merge remote-tracking branch 'origin/v0.2.x' into merge-v0.2 2020-09-21 14:35:38 -07:00
Carl Lerche 93f8cb8df2 sync: fix missing notification during mpsc close (#2854)
When the mpsc channel receiver closes the channel, receiving should
return `None` once all in-progress sends have completed. When a sender
reserves capacity, this prevents the receiver from fully shutting down.
Previously, when the sender, after reserving capacity, dropped without
sending a message, the receiver was not notified. This results in
blocking the shutdown process until all sender handles drop.

This patch adds a receiver notification when the channel is both closed
and all outstanding sends have completed.
2020-09-21 14:35:09 -07:00
Alice RyhlandTaiki Endo 1ac10fa80a ci: update miri flags (#2851)
* ci: update miri flags

* Update 2020-09-20 to 2020-09-21

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

Co-authored-by: Taiki Endo <[email protected]>
2020-09-21 18:57:33 +02:00
Taiki Endo ba8680d667 io: fix doc-cfg on AsyncSeekExt (#2846) 2020-09-19 20:40:37 +09:00
Taiki Endo 111894fef9 util: remove Slice wrapper (#2847) 2020-09-19 20:40:20 +09:00
Taiki Endo 68f7eff39e time: remove outdated todo comment (#2848) 2020-09-19 20:40:03 +09:00
NyloniciousandAlice Ryhl 207320dbbb process: fix some docs (#2843)
* fix docs for Command::status and output

Co-authored-by: Alice Ryhl <[email protected]>
2020-09-18 22:49:13 +00:00
Nylonicious 3fd043931e sync: fix some doc typos (#2838)
Fixes #2781.
2020-09-17 08:03:38 +02:00
Alice Ryhl 4c4699be00 doc: fix some links (#2834) 2020-09-13 15:50:40 +02:00
Frank Steffahn 8d2e3bc575 sync: add const constructors to RwLock, Notify, and Semaphore (#2833)
* Add const constructors to `RwLock`, `Notify`, and `Semaphore`.

Referring to the types in `tokio::sync`.
Also add `const` to `new` for the remaining atomic integers in `src/loom` and `UnsafeCell`.

Builds upon previous work in #2790
Closes #2756
2020-09-12 22:58:58 +02:00
20ef286553 sync: add const-constructors for some sync primitives (#2790)
Co-authored-by: Mikail Bagishov <[email protected]>
Co-authored-by: Eliza Weisman <[email protected]>
Co-authored-by: Alice Ryhl <[email protected]>
2020-09-12 11:55:03 +02:00
Carl Lerche 2bc9a48152 sync: tweak watch API (#2814)
Decouples getting the latest `watch` value from receiving the change
notification. The `Receiver` async method becomes
`Receiver::changed()`. The latest value is obtained from
`Receiver::borrow()`.

The implementation is updated to use `Notify`. This requires adding
`Notify::notify_waiters`. This method is generally useful but is kept
private for now.
2020-09-11 15:14:45 -07:00
Max Heller c5a9ede157 sync: write guard to read guard downgrading for sync::RwLock (#2733) 2020-09-11 22:00:04 +02:00
Zephyr Shannon ce0af8f7a1 docs: more doc fixes (#2831)
Previous docs look like they were based on the docs for
`insert_at`. Changed names of variables referred to and the
explanation of when the value will be returned and under what
condition it will be immediately available to make sense for a
Duration argument instead of an Instant.
2020-09-11 12:44:33 -07:00
Zephyr Shannon be7462e50f sync: document mpsc::bounded minimum buffer size (#2808) 2020-09-09 22:28:28 +02:00
xd009642 1550dda5cf stream: module level docs for tokio::stream (#2786) 2020-09-09 09:08:23 +02:00
John-John Tedro cbb14a7bb9 sync: add JoinHandle::abort (#2474) 2020-09-08 20:52:57 -07:00
Blas Rodriguez Irizar a0a356152e sync: remove rt-core from blocking_{send,recv} (#2825) 2020-09-08 20:50:38 -07:00
Igor Aleksanov ea79c95c67 util: implement Either type (#2821) 2020-09-08 09:14:08 +02:00
37f405bd3b io: move StreamReader and ReaderStream into tokio_util (#2788)
Co-authored-by: Mikail Bagishov <[email protected]>
Co-authored-by: Eliza Weisman <[email protected]>
2020-09-08 09:12:32 +02:00
Ivan Petkov 7c254eca44 process: make Child::kill async (#2823)
* This changes the `Child::kill` to be an async method which awaits the
  child after sending a kill signal. This avoids leaving zombie
  processes on Unix platforms if the caller forgets to await the child
  after the kill completes
* A `start_kill` method was also added on `Child` which only sends the
  kill signal to the child process. This allows for kill signals to be
  sent even outside of async contexts.
2020-09-08 06:03:25 +00:00
Blas Rodriguez IrizarandEliza Weisman f4d6ed03d9 runtime: add custom keep_alive functionality (#2809)
Co-authored-by: Eliza Weisman <[email protected]>
Fixes: #2585
2020-09-07 21:32:34 +02:00
Juan Alvarez 38ec4845d1 sync: rename Notify::notify() -> notify_one() (#2822)
Closes: #2813
2020-09-07 20:56:15 +02:00
Ivan Petkov 842d5565bd process: add Child::{wait,try_wait} (#2796)
* add Child::try_wait to mirror the std API
* replace Future impl on Child with `.wait()` method to bring our
  APIs closer to those in std and it allow us to
  internally fuse the future so that repeated calls to `wait` result in
  the same value (similar to std) without forcing the caller to fuse the
  outer future
* Also change `Child::id` to return an Option result to avoid
  allowing the caller to accidentally use the pid on Unix systems after
  the child has been reaped
* Also remove deprecated Child methods
2020-09-07 03:30:40 +00:00
George Malayil Philip d74eabc7d7 runtime: mention on JoinHandle that the generic parameter is the return type (#2819) 2020-09-05 22:44:42 +02:00
Blas Rodriguez IrizarandMikail Bagishov 6260ed907b tokio: document missing timer panics (#2801)
Fixes: #2696
Co-authored-by: Mikail Bagishov <[email protected]>
2020-09-05 20:33:42 +02:00
Zahari Dichev 048174012d fs: remove File::seek (#2810)
Fixes: #1993
Signed-off-by: Zahari Dichev <[email protected]>
2020-09-05 20:32:20 +02:00
Igor Aleksanov 38cab93330 runtime: improve runtime vs #[tokio::main] doc (#2820) 2020-09-05 20:22:47 +02:00
Zahari Dichev 171cb57fa1 io: add ReadBuf::take (#2817)
Signed-off-by: Zahari Dichev <[email protected]>
2020-09-05 11:09:54 -07:00
mental c9f5bc2915 util: add const fn support for internal LinkedList. (#2805) 2020-09-02 12:37:13 -07:00
Blas Rodriguez Irizar 5cdb6f8fd6 time: move throttle to StreamExt (#2752)
Ref: #2727
2020-09-02 11:52:31 +02:00
Blas Rodriguez Irizar 5a1a6dc90c sync: watch channel breaking changes (#2806)
Fixes: #2172
2020-09-01 20:57:48 -07:00
Nikolai Vazquez 827077409c fs: implement FromRawFd & FromRawHandle for File (#2792) 2020-08-28 09:53:51 +02:00
Lucio FrancoandEliza Weisman d600ab9a8f rt: Refactor Runtime::block_on to take &self (#2782)
Co-authored-by: Eliza Weisman <[email protected]>
2020-08-27 20:05:48 -04:00
Blas Rodriguez IrizarandLucio Franco d9d909cb4c util: Add TokioContext future (#2791)
Co-authored-by: Lucio Franco <[email protected]>
2020-08-27 11:33:43 -04:00
Blas Rodriguez Irizar 262b19ae96 Docs delay queue (#2793) 2020-08-27 10:36:59 -04:00
xd009642 347e18bc77 sync: add blocking_recv and blocking_send in mpsc (#2684)
Fixes: #2629
2020-08-26 21:39:06 +02:00
John-John Tedro 2e7e42bca7 sync: implement map methods of parking_lot fame (#2445)
Generally, this mimics the way `MappedRwLock*Guard`s are implemented in
`parking_lot`. By storing a raw pointer in the guards themselves
referencing the mapped data and maintaining type invariants through
`PhantomData`. I didn't try to think too much about this, so if someone
has objections I'd love to hear them.

I've also dropped the internal use of `ReleasingPermit`, since it made
the guards unecessarily large. The number of permits that need to be
released are already known by the guards themselves, and is instead
governed directly in the relevant `Drop` impls.  This has the benefit of
making the guards as small as possible, for the non-mapped variants this
means a single reference is enough.

`fmt::Debug` impls have been adjusted to behave exactly like the
delegating impls in `parking_lot`. `fmt::Display` impls have been added
for all guard types which behave the same. This does change the format
of debug impls, for which I'm not sure if we provide any guarantees.
2020-08-26 20:50:35 +02:00
James Mills 0ccc09ac92 sync: fix typo in Notify documentation (#2794) 2020-08-26 20:26:43 +02:00
wspsxing 4e12299826 runtime: add thread_name_fn method to runtime::Builder (#1921)
Fixes: #1907
2020-08-24 15:14:20 +02:00
Carl Lerche 9d58b70151 sync: move CancellationToken to tokio-util (#2721)
* sync: move CancellationToken to tokio-util

The `CancellationToken` utility is only available with the
`tokio_unstable` flag. This was done as the API is not final, but it
adds friction for users.

This patch moves `CancellationToken` to tokio-util where it is generally
available. The tokio-util crate does not have any constraints on
breaking change releases.

* fix clippy

* clippy again
2020-08-23 17:45:52 +02:00
caranatar fde72bf047 net: Add examples to UnixDatagram (#2765)
* net: adding examples for UnixDatagram

Adding examples to documentation for UnixDatagram

* net: document named UnixDatagrams persistence

Add documentation to indicate that named UnixDatagrams 'leak'
socket files after execution.

* net: rustfmt issue in UnixDatagram

Fixing rustfmt issue in UnixDatagram

* net: adding examples for UnixDatagram

Fixes: #2686
Refs: #1679
Refs: #1111
2020-08-23 17:45:10 +02:00
Blas Rodriguez Irizar 138eef3526 test: implement Drop for Mock to panic w/ unconsumed data (#2704) 2020-08-20 15:48:27 -04:00
Sean McArthur c393236dfd io: change AsyncRead to use a ReadBuf (#2758)
Works towards #2716. Changes the argument to `AsyncRead::poll_read` to
take a `ReadBuf` struct that safely manages writes to uninitialized memory.
2020-08-13 20:15:01 -07:00
Carl Lerche 71da06097b chore: reformat some imports for consistency (#2768) 2020-08-13 08:10:00 -07:00
Carl Lerche 8feebab7cd io: rewrite slab to support compaction (#2757)
The I/O driver uses a slab to store per-resource state. Doing this
provides two benefits. First, allocating state is streamlined. Second,
resources may be safely indexed using a `usize` type. The `usize` is
used passed to the OS's selector when registering for receiving events.

The original slab implementation used a `Vec` backed by `RwLock`. This
primarily caused contention when reading state. This implementation also
only **grew** the slab capacity but never shrank. In #1625, the slab was
rewritten to use a lock-free strategy. The lock contention was removed
but this implementation was still grow-only.

This change adds the ability to release memory. Similar to the previous
implementation, it structures the slab to use a vector of pages. This
enables growing the slab without having to move any previous entries. It
also adds the ability to release pages. This is done by introducing a
lock when allocating/releasing slab entries. This does not impact
benchmarks, primarily due to the existing implementation not being
"done" and also having a lock around allocating and releasing.

A `Slab::compact()` function is added. Pages are iterated. When a page
is found with no slots in use, the page is freed. The `compact()`
function is called occasionally by the I/O driver.

Fixes #2505
2020-08-11 22:28:43 -07:00
Kruno Tomola Fabro 674985d9fb fs: add comment explaing File flush is a no-op (#2761)
Signed-off-by: Kruno Tomola Fabro <[email protected]>
2020-08-11 11:51:02 -07:00
Carl Lerche 77e6bb8d06 chore: bump MSRV to 1.45 (#2759)
As 0.3 is a breaking change, the minimum supported Rust version can be
changed.
2020-08-10 21:39:10 -07:00
Cameron Taggart d8490c1626 io: use stderr in stderr documentation (#2746) 2020-08-09 09:37:55 +02:00
Blas Rodriguez Irizar e9adac288e sync: typo in impl Semaphore (#2745) 2020-08-09 08:42:04 +02:00
Blas Rodriguez Irizar 27bfe52bba sync: show correct permits in fmt::Debug (#2750)
Fixes: #2744
2020-08-08 13:41:55 -07:00
Carl Lerche 6ccefb77e2 chore: prepare for v0.3 breaking changes (#2747)
Bug fixes will be applied to the v0.2.x branch.
2020-08-07 20:27:53 -07:00
372 changed files with 19479 additions and 13845 deletions
+7 -8
View File
@@ -1,17 +1,17 @@
freebsd_instance:
image: freebsd-12-1-release-amd64
image: freebsd-12-2-release-amd64
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
# same VM. The binary will be built in 32-bit mode, but will execute on a
# 64-bit kernel and in a 64-bit environment. Our tests don't execute any of
# the system's binaries, so the environment shouldn't matter.
task:
name: FreeBSD 12.0
name: FreeBSD
env:
LOOM_MAX_PREEMPTIONS: 2
RUSTFLAGS: -Dwarnings
setup_script:
- pkg install -y curl
- pkg install -y bash curl
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile minimal --default-toolchain stable
- . $HOME/.cargo/env
@@ -23,8 +23,7 @@ task:
- . $HOME/.cargo/env
- cargo test --all
- cargo doc --all --no-deps
# TODO: Re-enable
# i686_test_script:
# - . $HOME/.cargo/env
# - |
# cargo test --all --exclude tokio-macros --target i686-unknown-freebsd
i686_test_script:
- . $HOME/.cargo/env
- |
cargo test --all --target i686-unknown-freebsd
+55
View File
@@ -0,0 +1,55 @@
name: Benchmark
on:
push:
branches:
- master
jobs:
benchmark:
name: Benchmark
runs-on: ubuntu-latest
strategy:
matrix:
bench:
- rt_multi_threaded
- sync_mpsc
- sync_rwlock
- sync_semaphore
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update stable
# Run benchmark with `go test -bench` and stores the output to a file
- name: Run benchmark
run: cargo bench --bench ${{ matrix.bench }} | tee ../output.txt
working-directory: benches
# Download previous benchmark result from cache (if exists)
- name: Download previous benchmark data
uses: actions/cache@v1
with:
path: ./cache
key: ${{ runner.os }}-benchmark
# Run `github-action-benchmark` action
- name: Store benchmark result
uses: rhysd/github-action-benchmark@v1
with:
name: ${{ matrix.bench }}
# What benchmark tool the output.txt came from
tool: 'cargo'
# Where the output from the benchmark tool is stored
output-file-path: output.txt
# # Where the previous data file is stored
# external-data-json-path: ./cache/benchmark-data.json
# Workflow will fail when an alert happens
fail-on-alert: true
# GitHub API token to make a commit comment
github-token: ${{ secrets.GITHUB_TOKEN }}
# Enable alert commit comment
comment-on-alert: true
alert-comment-cc-users: '@tokio-rs/maintainers'
auto-push: true
# Upload the updated cache file for the next job by actions/cache
+44 -13
View File
@@ -1,8 +1,8 @@
on:
push:
branches: ["v0.2.x"]
branches: ["master"]
pull_request:
branches: ["v0.2.x"]
branches: ["master"]
name: CI
@@ -10,7 +10,7 @@ env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
nightly: nightly-2020-09-21
minrust: 1.39.0
minrust: 1.45.2
jobs:
# Depends on all action sthat are required for a "successful" CI run.
@@ -53,11 +53,6 @@ jobs:
run: cargo test --features full
working-directory: tokio
# Check `tokio` with `full + parking_lot` to make sure it compiles.
- name: check tokio full,parking_lot
run: cargo check --features full,parking_lot
working-directory: tokio
# Test **all** crates in the workspace with all features.
- name: test all --all-features
run: cargo test --workspace --all-features
@@ -91,7 +86,7 @@ jobs:
run: cargo test --features full
working-directory: tokio
env:
RUSTFLAGS: '--cfg tokio_unstable'
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
miri:
name: miri
@@ -110,8 +105,23 @@ jobs:
rm -rf tokio/tests
- name: miri
run: cargo miri test --features rt-core,rt-threaded,rt-util,sync task
run: cargo miri test --features rt,rt-multi-thread,sync task
working-directory: tokio
san:
name: san
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.nightly }}
override: true
- name: asan
run: cargo test --all-features --target x86_64-unknown-linux-gnu --lib -- --test-threads 1
working-directory: tokio
env:
RUSTFLAGS: -Z sanitizer=address
ASAN_OPTIONS: detect_leaks=0
cross:
name: cross
@@ -156,7 +166,7 @@ jobs:
- name: check --each-feature --unstable
run: cargo hack check --all --each-feature -Z avoid-dev-deps
env:
RUSTFLAGS: --cfg tokio_unstable
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
minrust:
name: minrust
@@ -171,6 +181,26 @@ jobs:
- name: "test --workspace --all-features"
run: cargo check --workspace --all-features
minimal-versions:
name: minimal-versions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.nightly }}
override: true
- name: Install cargo-hack
run: cargo install cargo-hack
- name: "check --all-features -Z minimal-versions"
run: |
# Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
# from determining minimal versions based on dev-dependencies.
cargo hack --remove-dev-deps --workspace
# Update Cargo.lock to minimal version dependencies.
cargo update -Z minimal-versions
cargo check --all-features
fmt:
name: fmt
runs-on: ubuntu-latest
@@ -196,7 +226,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update stable
run: rustup update ${{ env.minrust }} && rustup default ${{ env.minrust }}
- name: Install clippy
run: rustup component add clippy
@@ -230,6 +260,7 @@ jobs:
- loom_pool::group_b
- loom_pool::group_c
- loom_pool::group_d
- time::driver
steps:
- uses: actions/checkout@v2
- name: Install Rust
@@ -239,6 +270,6 @@ jobs:
run: cargo test --lib --release --features full -- --nocapture $SCOPE
working-directory: tokio
env:
RUSTFLAGS: --cfg loom --cfg tokio_unstable
RUSTFLAGS: --cfg loom --cfg tokio_unstable -Dwarnings
LOOM_MAX_PREEMPTIONS: 2
SCOPE: ${{ matrix.scope }}
+4 -4
View File
@@ -14,15 +14,15 @@ the Rust programming language. It is:
[![Crates.io][crates-badge]][crates-url]
[![MIT licensed][mit-badge]][mit-url]
[![Build Status][azure-badge]][azure-url]
[![Build Status][actions-badge]][actions-url]
[![Discord chat][discord-badge]][discord-url]
[crates-badge]: https://img.shields.io/crates/v/tokio.svg
[crates-url]: https://crates.io/crates/tokio
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
[mit-url]: https://github.com/tokio-rs/tokio/blob/master/LICENSE
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.tokio?branchName=master
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=1&branchName=master
[actions-badge]: https://github.com/tokio-rs/tokio/workflows/CI/badge.svg
[actions-url]: https://github.com/tokio-rs/tokio/actions?query=workflow%3ACI+branch%3Amaster
[discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord&style=flat-square
[discord-url]: https://discord.gg/tokio
@@ -157,7 +157,7 @@ several other libraries, including:
## Supported Rust Versions
Tokio is built against the latest stable release. The minimum supported version is 1.39.
Tokio is built against the latest stable release. The minimum supported version is 1.45.
The current Tokio version is not guaranteed to build on Rust versions earlier than the
minimum supported version.
+13 -5
View File
@@ -5,22 +5,25 @@ publish = false
edition = "2018"
[dependencies]
tokio = { version = "0.2.0", path = "../tokio", features = ["full"] }
tokio = { version = "0.3.0", path = "../tokio", features = ["full"] }
bencher = "0.1.5"
[target.'cfg(unix)'.dependencies]
libc = "0.2.42"
[[bench]]
name = "spawn"
path = "spawn.rs"
harness = false
[[bench]]
name = "mpsc"
path = "mpsc.rs"
name = "sync_mpsc"
path = "sync_mpsc.rs"
harness = false
[[bench]]
name = "scheduler"
path = "scheduler.rs"
name = "rt_multi_threaded"
path = "rt_multi_threaded.rs"
harness = false
@@ -33,3 +36,8 @@ harness = false
name = "sync_semaphore"
path = "sync_semaphore.rs"
harness = false
[[bench]]
name = "signal"
path = "signal.rs"
harness = false
@@ -13,7 +13,7 @@ use std::sync::{mpsc, Arc};
fn spawn_many(b: &mut Bencher) {
const NUM_SPAWN: usize = 10_000;
let mut rt = rt();
let rt = rt();
let (tx, rx) = mpsc::sync_channel(1000);
let rem = Arc::new(AtomicUsize::new(0));
@@ -68,7 +68,7 @@ fn yield_many(b: &mut Bencher) {
fn ping_pong(b: &mut Bencher) {
const NUM_PINGS: usize = 1_000;
let mut rt = rt();
let rt = rt();
let (done_tx, done_rx) = mpsc::sync_channel(1000);
let rem = Arc::new(AtomicUsize::new(0));
@@ -111,7 +111,7 @@ fn ping_pong(b: &mut Bencher) {
fn chained_spawn(b: &mut Bencher) {
const ITER: usize = 1_000;
let mut rt = rt();
let rt = rt();
fn iter(done_tx: mpsc::SyncSender<()>, n: usize) {
if n == 0 {
@@ -139,9 +139,8 @@ fn chained_spawn(b: &mut Bencher) {
}
fn rt() -> Runtime {
runtime::Builder::new()
.threaded_scheduler()
.core_threads(4)
runtime::Builder::new_multi_thread()
.worker_threads(4)
.enable_all()
.build()
.unwrap()
+95
View File
@@ -0,0 +1,95 @@
//! Benchmark the delay in propagating OS signals to any listeners.
#![cfg(unix)]
use bencher::{benchmark_group, benchmark_main, Bencher};
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::runtime;
use tokio::signal::unix::{signal, SignalKind};
use tokio::sync::mpsc;
struct Spinner {
count: usize,
}
impl Future for Spinner {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.count > 3 {
Poll::Ready(())
} else {
self.count += 1;
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
impl Spinner {
fn new() -> Self {
Self { count: 0 }
}
}
pub fn send_signal(signal: libc::c_int) {
use libc::{getpid, kill};
unsafe {
assert_eq!(kill(getpid(), signal), 0);
}
}
fn many_signals(bench: &mut Bencher) {
let num_signals = 10;
let (tx, mut rx) = mpsc::channel(num_signals);
// Intentionally single threaded to measure delays in propagating wakes
let rt = runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let spawn_signal = |kind| {
let tx = tx.clone();
rt.spawn(async move {
let mut signal = signal(kind).expect("failed to create signal");
while signal.recv().await.is_some() {
if tx.send(()).await.is_err() {
break;
}
}
});
};
for _ in 0..num_signals {
// Pick some random signals which don't terminate the test harness
spawn_signal(SignalKind::child());
spawn_signal(SignalKind::io());
}
drop(tx);
// Turn the runtime for a while to ensure that all the spawned
// tasks have been polled at least once
rt.block_on(Spinner::new());
bench.iter(|| {
rt.block_on(async {
send_signal(libc::SIGCHLD);
for _ in 0..num_signals {
rx.recv().await.expect("channel closed");
}
send_signal(libc::SIGIO);
for _ in 0..num_signals {
rx.recv().await.expect("channel closed");
}
});
});
}
benchmark_group!(signal_group, many_signals,);
benchmark_main!(signal_group);
+8 -14
View File
@@ -10,8 +10,7 @@ async fn work() -> usize {
}
fn basic_scheduler_local_spawn(bench: &mut Bencher) {
let mut runtime = tokio::runtime::Builder::new()
.basic_scheduler()
let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
runtime.block_on(async {
@@ -23,8 +22,7 @@ fn basic_scheduler_local_spawn(bench: &mut Bencher) {
}
fn threaded_scheduler_local_spawn(bench: &mut Bencher) {
let mut runtime = tokio::runtime::Builder::new()
.threaded_scheduler()
let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
runtime.block_on(async {
@@ -36,25 +34,21 @@ fn threaded_scheduler_local_spawn(bench: &mut Bencher) {
}
fn basic_scheduler_remote_spawn(bench: &mut Bencher) {
let runtime = tokio::runtime::Builder::new()
.basic_scheduler()
let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
let handle = runtime.handle();
bench.iter(|| {
let h = handle.spawn(work());
let h = runtime.spawn(work());
black_box(h);
});
}
fn threaded_scheduler_remote_spawn(bench: &mut Bencher) {
let runtime = tokio::runtime::Builder::new()
.threaded_scheduler()
.build()
.unwrap();
let handle = runtime.handle();
let runtime = tokio::runtime::Builder::new_multi_thread().build().unwrap();
bench.iter(|| {
let h = handle.spawn(work());
let h = runtime.spawn(work());
black_box(h);
});
}
+17 -30
View File
@@ -4,6 +4,13 @@ use tokio::sync::mpsc;
type Medium = [usize; 64];
type Large = [Medium; 64];
fn rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap()
}
fn create_1_medium(b: &mut Bencher) {
b.iter(|| {
black_box(&mpsc::channel::<Medium>(1));
@@ -24,7 +31,7 @@ fn create_100_000_medium(b: &mut Bencher) {
fn send_medium(b: &mut Bencher) {
b.iter(|| {
let (mut tx, mut rx) = mpsc::channel::<Medium>(1000);
let (tx, mut rx) = mpsc::channel::<Medium>(1000);
let _ = tx.try_send([0; 64]);
@@ -34,7 +41,7 @@ fn send_medium(b: &mut Bencher) {
fn send_large(b: &mut Bencher) {
b.iter(|| {
let (mut tx, mut rx) = mpsc::channel::<Large>(1000);
let (tx, mut rx) = mpsc::channel::<Large>(1000);
let _ = tx.try_send([[0; 64]; 64]);
@@ -43,18 +50,14 @@ fn send_large(b: &mut Bencher) {
}
fn contention_bounded(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
let rt = rt();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for _ in 0..5 {
let mut tx = tx.clone();
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
@@ -70,18 +73,14 @@ fn contention_bounded(b: &mut Bencher) {
}
fn contention_bounded_full(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
let rt = rt();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(100);
for _ in 0..5 {
let mut tx = tx.clone();
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
@@ -97,11 +96,7 @@ fn contention_bounded_full(b: &mut Bencher) {
}
fn contention_unbounded(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
let rt = rt();
b.iter(|| {
rt.block_on(async move {
@@ -124,15 +119,11 @@ fn contention_unbounded(b: &mut Bencher) {
}
fn uncontented_bounded(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
let rt = rt();
b.iter(|| {
rt.block_on(async move {
let (mut tx, mut rx) = mpsc::channel::<usize>(1_000_000);
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for i in 0..5000 {
tx.send(i).await.unwrap();
@@ -146,11 +137,7 @@ fn uncontented_bounded(b: &mut Bencher) {
}
fn uncontented_unbounded(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
let rt = rt();
b.iter(|| {
rt.block_on(async move {
+8 -13
View File
@@ -3,9 +3,8 @@ use std::sync::Arc;
use tokio::{sync::RwLock, task};
fn read_uncontended(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap();
@@ -22,9 +21,8 @@ fn read_uncontended(b: &mut Bencher) {
}
fn read_concurrent_uncontended_multi(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap();
@@ -51,8 +49,7 @@ fn read_concurrent_uncontended_multi(b: &mut Bencher) {
}
fn read_concurrent_uncontended(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
@@ -78,9 +75,8 @@ fn read_concurrent_uncontended(b: &mut Bencher) {
}
fn read_concurrent_contended_multi(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap();
@@ -108,8 +104,7 @@ fn read_concurrent_contended_multi(b: &mut Bencher) {
}
fn read_concurrent_contended(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
+8 -13
View File
@@ -3,9 +3,8 @@ use std::sync::Arc;
use tokio::{sync::Semaphore, task};
fn uncontended(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap();
@@ -27,9 +26,8 @@ async fn task(s: Arc<Semaphore>) {
}
fn uncontended_concurrent_multi(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap();
@@ -51,8 +49,7 @@ fn uncontended_concurrent_multi(b: &mut Bencher) {
}
fn uncontended_concurrent_single(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
@@ -73,9 +70,8 @@ fn uncontended_concurrent_single(b: &mut Bencher) {
}
fn contended_concurrent_multi(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6)
.build()
.unwrap();
@@ -97,8 +93,7 @@ fn contended_concurrent_multi(b: &mut Bencher) {
}
fn contended_concurrent_single(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
+3 -3
View File
@@ -7,11 +7,11 @@ edition = "2018"
# If you copy one of the examples into a new project, you should be using
# [dependencies] instead.
[dev-dependencies]
tokio = { version = "0.2.0", path = "../tokio", features = ["full", "tracing"] }
tokio = { version = "0.3.0", path = "../tokio", features = ["full", "tracing"] }
tracing = "0.1"
tracing-subscriber = { version = "0.2.7", default-features = false, features = ["fmt", "ansi", "env-filter", "chrono", "tracing-log"] }
tokio-util = { version = "0.3.0", path = "../tokio-util", features = ["full"] }
bytes = "0.5"
tokio-util = { version = "0.5.0", path = "../tokio-util", features = ["full"] }
bytes = "0.6"
futures = "0.3.0"
http = "0.2"
serde = "1.0"
+1 -1
View File
@@ -77,7 +77,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Bind a TCP listener to the socket address.
//
// Note that this is the Tokio TcpListener, which is fully async.
let mut listener = TcpListener::bind(&addr).await?;
let listener = TcpListener::bind(&addr).await?;
tracing::info!("server running on {}", addr);
+3 -5
View File
@@ -96,7 +96,6 @@ mod udp {
use std::error::Error;
use std::io;
use std::net::SocketAddr;
use tokio::net::udp::{RecvHalf, SendHalf};
use tokio::net::UdpSocket;
pub async fn connect(
@@ -114,16 +113,15 @@ mod udp {
let socket = UdpSocket::bind(&bind_addr).await?;
socket.connect(addr).await?;
let (mut r, mut w) = socket.split();
future::try_join(send(stdin, &mut w), recv(stdout, &mut r)).await?;
future::try_join(send(stdin, &socket), recv(stdout, &socket)).await?;
Ok(())
}
async fn send(
mut stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
writer: &mut SendHalf,
writer: &UdpSocket,
) -> Result<(), io::Error> {
while let Some(item) = stdin.next().await {
let buf = item?;
@@ -135,7 +133,7 @@ mod udp {
async fn recv(
mut stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
reader: &mut RecvHalf,
reader: &UdpSocket,
) -> Result<(), io::Error> {
loop {
let mut buf = vec![0; 1024];
+1 -1
View File
@@ -26,7 +26,7 @@ struct Server {
impl Server {
async fn run(self) -> Result<(), io::Error> {
let Server {
mut socket,
socket,
mut buf,
mut to_send,
} = self;
+1 -1
View File
@@ -39,7 +39,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Next up we create a TCP listener which will listen for incoming
// connections. This TCP listener is bound to the address we determined
// above and must be associated with an event loop.
let mut listener = TcpListener::bind(&addr).await?;
let listener = TcpListener::bind(&addr).await?;
println!("Listening on: {}", addr);
loop {
+1 -1
View File
@@ -74,7 +74,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// above and must be associated with an event loop, so we pass in a handle
// to our event loop. After the socket's created we inform that we're ready
// to go and start accepting connections.
let mut listener = TcpListener::bind(&addr).await?;
let listener = TcpListener::bind(&addr).await?;
println!("Listening on: {}", addr);
loop {
+1 -1
View File
@@ -43,7 +43,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
println!("Listening on: {}", listen_addr);
println!("Proxying to: {}", server_addr);
let mut listener = TcpListener::bind(listen_addr).await?;
let listener = TcpListener::bind(listen_addr).await?;
while let Ok((inbound, _)) = listener.accept().await {
let transfer = transfer(inbound, server_addr.clone()).map(|r| {
+1 -1
View File
@@ -89,7 +89,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
.nth(1)
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
let mut listener = TcpListener::bind(&addr).await?;
let listener = TcpListener::bind(&addr).await?;
println!("Listening on: {}", addr);
// Create the shared state of this server that will be shared amongst all
+3 -5
View File
@@ -30,19 +30,17 @@ async fn main() -> Result<(), Box<dyn Error>> {
let addr = env::args()
.nth(1)
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
let mut server = TcpListener::bind(&addr).await?;
let mut incoming = server.incoming();
let server = TcpListener::bind(&addr).await?;
println!("Listening on: {}", addr);
while let Some(Ok(stream)) = incoming.next().await {
loop {
let (stream, _) = server.accept().await?;
tokio::spawn(async move {
if let Err(e) = process(stream).await {
println!("failed to process connection; error = {}", e);
}
});
}
Ok(())
}
async fn process(stream: TcpStream) -> Result<(), Box<dyn Error>> {
+1 -1
View File
@@ -55,7 +55,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
}
.parse()?;
let mut socket = UdpSocket::bind(local_addr).await?;
let socket = UdpSocket::bind(local_addr).await?;
const MAX_DATAGRAM_SIZE: usize = 65_507;
socket.connect(&remote_addr).await?;
let data = get_stdin_data()?;
+1
View File
@@ -7,6 +7,7 @@ publish = false
[features]
full = ["tokio/full"]
rt = ["tokio/rt", "tokio/macros"]
[dependencies]
tokio = { path = "../tokio", optional = true }
@@ -0,0 +1,6 @@
use tests_build::tokio;
#[tokio::main]
async fn my_fn() {}
fn main() {}
@@ -0,0 +1,7 @@
error: The default runtime flavor is `multi_thread`, but the `rt-multi-thread` feature is disabled.
--> $DIR/macros_core_no_default.rs:3:1
|
3 | #[tokio::main]
| ^^^^^^^^^^^^^^
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -4,7 +4,7 @@ error: the async keyword is missing from the function declaration
4 | fn main_is_not_async() {}
| ^^
error: Unknown attribute foo is specified; expected `basic_scheduler` or `threaded_scheduler`
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`
--> $DIR/macros_invalid_input.rs:6:15
|
6 | #[tokio::main(foo)]
@@ -28,7 +28,7 @@ error: the test function cannot accept arguments
16 | async fn test_fn_has_args(_x: u8) {}
| ^^^^^^
error: Unknown attribute foo is specified; expected `basic_scheduler` or `threaded_scheduler`
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`
--> $DIR/macros_invalid_input.rs:18:15
|
18 | #[tokio::test(foo)]
+4 -1
View File
@@ -1,9 +1,12 @@
#[test]
fn compile_fail() {
fn compile_fail_full() {
let t = trybuild::TestCases::new();
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_invalid_input.rs");
#[cfg(all(feature = "rt", not(feature = "full")))]
t.compile_fail("tests/fail/macros_core_no_default.rs");
drop(t);
}
+4 -4
View File
@@ -8,16 +8,16 @@ publish = false
[features]
full = [
"macros",
"rt-core",
"rt-threaded",
"rt",
"rt-multi-thread",
"tokio/full",
"tokio-test"
]
macros = ["tokio/macros"]
sync = ["tokio/sync"]
rt-core = ["tokio/rt-core"]
rt-threaded = ["rt-core", "tokio/rt-threaded"]
rt = ["tokio/rt"]
rt-multi-thread = ["rt", "tokio/rt-multi-thread"]
[dependencies]
tokio = { path = "../tokio" }
+9 -12
View File
@@ -1,4 +1,4 @@
#![cfg(feature = "macros")]
#![cfg(all(feature = "macros", feature = "rt"))]
#[tokio::main]
async fn basic_main() -> usize {
@@ -10,18 +10,15 @@ async fn generic_fun<T: Default>() -> T {
T::default()
}
#[cfg(feature = "rt-core")]
mod spawn {
#[tokio::main]
async fn spawning() -> usize {
let join = tokio::spawn(async { 1 });
join.await.unwrap()
}
#[tokio::main]
async fn spawning() -> usize {
let join = tokio::spawn(async { 1 });
join.await.unwrap()
}
#[test]
fn main_with_spawn() {
assert_eq!(1, spawning());
}
#[test]
fn main_with_spawn() {
assert_eq!(1, spawning());
}
#[test]
+24 -1
View File
@@ -72,7 +72,7 @@ async fn feed_cat(mut cat: Child, n: usize) -> io::Result<ExitStatus> {
};
// Compose reading and writing concurrently.
future::join3(write, read, cat)
future::join3(write, read, cat.wait())
.map(|(_, _, status)| status)
.await
}
@@ -125,3 +125,26 @@ async fn status_closes_any_pipes() {
assert_ok!(child.await);
}
#[tokio::test]
async fn try_wait() {
let mut child = cat().spawn().unwrap();
let id = child.id().expect("missing id");
assert!(id > 0);
assert_eq!(None, assert_ok!(child.try_wait()));
// Drop the child's stdio handles so it can terminate
drop(child.stdin.take());
drop(child.stderr.take());
drop(child.stdout.take());
assert_ok!(child.wait().await);
// test that the `.try_wait()` method is fused just like the stdlib
assert!(assert_ok!(child.try_wait()).unwrap().success());
// Can't get id after process has exited
assert_eq!(child.id(), None);
}
-32
View File
@@ -1,32 +0,0 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "sync")]
use tokio::runtime;
use tokio::sync::oneshot;
use std::sync::mpsc;
use std::thread;
#[test]
fn basic_shell_rt() {
let (feed_tx, feed_rx) = mpsc::channel::<oneshot::Sender<()>>();
let th = thread::spawn(move || {
for tx in feed_rx.iter() {
tx.send(()).unwrap();
}
});
for _ in 0..1_000 {
let mut rt = runtime::Builder::new().build().unwrap();
let (tx, rx) = oneshot::channel();
feed_tx.send(tx).unwrap();
rt.block_on(rx).unwrap();
}
drop(feed_tx);
th.join().unwrap();
}
+20 -5
View File
@@ -1,3 +1,17 @@
# 0.3.1 (October 25, 2020)
### Fixed
- fix incorrect docs regarding `max_threads` option ([#3038])
# 0.3.0 (October 15, 2020)
- Track `tokio` 0.3 release.
### Changed
- options are renamed to track `tokio` runtime builder fn names.
- `#[tokio::main]` macro requires `rt-multi-thread` when no `flavor` is specified.
# 0.2.5 (February 27, 2019)
### Fixed
@@ -30,9 +44,10 @@
- Initial release
[#2225]: https://github.com/tokio-rs/tokio/pull/2225
[#2177]: https://github.com/tokio-rs/tokio/pull/2177
[#2152]: https://github.com/tokio-rs/tokio/pull/2152
[#2038]: https://github.com/tokio-rs/tokio/pull/2038
[#2022]: https://github.com/tokio-rs/tokio/pull/2022
[#1954]: https://github.com/tokio-rs/tokio/pull/1954
[#2022]: https://github.com/tokio-rs/tokio/pull/2022
[#2038]: https://github.com/tokio-rs/tokio/pull/2038
[#2152]: https://github.com/tokio-rs/tokio/pull/2152
[#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
+4 -4
View File
@@ -6,14 +6,14 @@ name = "tokio-macros"
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "v0.1.x" git tag.
version = "0.2.5"
# - Create "v0.3.x" git tag.
version = "0.3.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-macros/0.2.5/tokio_macros"
documentation = "https://docs.rs/tokio-macros/0.3.1/tokio_macros"
description = """
Tokio's proc macros.
"""
@@ -30,7 +30,7 @@ quote = "1"
syn = { version = "1.0.3", features = ["full"] }
[dev-dependencies]
tokio = { version = "0.2.0", path = "../tokio", features = ["full"] }
tokio = { version = "0.3.0", path = "../tokio", features = ["full"] }
[package.metadata.docs.rs]
all-features = true
+182 -241
View File
@@ -1,18 +1,144 @@
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use std::num::NonZeroUsize;
use syn::spanned::Spanned;
#[derive(Clone, Copy, PartialEq)]
enum Runtime {
Basic,
enum RuntimeFlavor {
CurrentThread,
Threaded,
}
impl RuntimeFlavor {
fn from_str(s: &str) -> Result<RuntimeFlavor, String> {
match s {
"current_thread" => Ok(RuntimeFlavor::CurrentThread),
"multi_thread" => Ok(RuntimeFlavor::Threaded),
"single_thread" => Err("The single threaded runtime flavor is called `current_thread`.".to_string()),
"basic_scheduler" => Err("The `basic_scheduler` runtime flavor has been renamed to `current_thread`.".to_string()),
"threaded_scheduler" => Err("The `threaded_scheduler` runtime flavor has been renamed to `multi_thread`.".to_string()),
_ => Err(format!("No such runtime flavor `{}`. The runtime flavors are `current_thread` and `multi_thread`.", s)),
}
}
}
struct FinalConfig {
flavor: RuntimeFlavor,
worker_threads: Option<usize>,
}
struct Configuration {
rt_multi_thread_available: bool,
default_flavor: RuntimeFlavor,
flavor: Option<RuntimeFlavor>,
worker_threads: Option<(usize, Span)>,
}
impl Configuration {
fn new(is_test: bool, rt_multi_thread: bool) -> Self {
Configuration {
rt_multi_thread_available: rt_multi_thread,
default_flavor: match is_test {
true => RuntimeFlavor::CurrentThread,
false => RuntimeFlavor::Threaded,
},
flavor: None,
worker_threads: None,
}
}
fn set_flavor(&mut self, runtime: syn::Lit, span: Span) -> Result<(), syn::Error> {
if self.flavor.is_some() {
return Err(syn::Error::new(span, "`flavor` set multiple times."));
}
let runtime_str = parse_string(runtime, span, "flavor")?;
let runtime =
RuntimeFlavor::from_str(&runtime_str).map_err(|err| syn::Error::new(span, err))?;
self.flavor = Some(runtime);
Ok(())
}
fn set_worker_threads(
&mut self,
worker_threads: syn::Lit,
span: Span,
) -> Result<(), syn::Error> {
if self.worker_threads.is_some() {
return Err(syn::Error::new(
span,
"`worker_threads` set multiple times.",
));
}
let worker_threads = parse_int(worker_threads, span, "worker_threads")?;
if worker_threads == 0 {
return Err(syn::Error::new(span, "`worker_threads` may not be 0."));
}
self.worker_threads = Some((worker_threads, span));
Ok(())
}
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),
}),
(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))
}
}
}
}
fn parse_int(int: syn::Lit, span: Span, field: &str) -> Result<usize, syn::Error> {
match int {
syn::Lit::Int(lit) => match lit.base10_parse::<usize>() {
Ok(value) => Ok(value),
Err(e) => Err(syn::Error::new(
span,
format!("Failed to parse {} as integer: {}", field, e),
)),
},
_ => Err(syn::Error::new(
span,
format!("Failed to parse {} as integer.", field),
)),
}
}
fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result<String, syn::Error> {
match int {
syn::Lit::Str(s) => Ok(s.value()),
syn::Lit::Verbatim(s) => Ok(s.to_string()),
_ => Err(syn::Error::new(
span,
format!("Failed to parse {} as string.", field),
)),
}
}
fn parse_knobs(
mut input: syn::ItemFn,
args: syn::AttributeArgs,
is_test: bool,
rt_threaded: bool,
rt_multi_thread: bool,
) -> Result<TokenStream, syn::Error> {
let sig = &mut input.sig;
let body = &input.block;
@@ -26,9 +152,12 @@ fn parse_knobs(
sig.asyncness = None;
let mut runtime = None;
let mut core_threads = None;
let mut max_threads = None;
let macro_name = if is_test {
"tokio::test"
} else {
"tokio::main"
};
let mut config = Configuration::new(is_test, rt_multi_thread);
for arg in args {
match arg {
@@ -39,65 +168,18 @@ fn parse_knobs(
return Err(syn::Error::new_spanned(namevalue, msg));
}
match ident.unwrap().to_string().to_lowercase().as_str() {
"core_threads" => {
if rt_threaded {
match &namevalue.lit {
syn::Lit::Int(expr) => {
let num = expr.base10_parse::<NonZeroUsize>().unwrap();
if num.get() > 1 {
runtime = Some(Runtime::Threaded);
} else {
runtime = Some(Runtime::Basic);
}
if let Some(v) = max_threads {
if v < num {
return Err(syn::Error::new_spanned(
namevalue,
"max_threads cannot be less than core_threads",
));
}
}
core_threads = Some(num);
}
_ => {
return Err(syn::Error::new_spanned(
namevalue,
"core_threads argument must be an int",
))
}
}
} else {
return Err(syn::Error::new_spanned(
namevalue,
"core_threads can only be set with rt-threaded feature flag enabled",
));
}
"worker_threads" => {
config.set_worker_threads(namevalue.lit.clone(), namevalue.span())?;
}
"flavor" => {
config.set_flavor(namevalue.lit.clone(), namevalue.span())?;
}
"core_threads" => {
let msg = "Attribute `core_threads` is renamed to `worker_threads`";
return Err(syn::Error::new_spanned(namevalue, msg));
}
"max_threads" => match &namevalue.lit {
syn::Lit::Int(expr) => {
let num = expr.base10_parse::<NonZeroUsize>().unwrap();
if let Some(v) = core_threads {
if num < v {
return Err(syn::Error::new_spanned(
namevalue,
"max_threads cannot be less than core_threads",
));
}
}
max_threads = Some(num);
}
_ => {
return Err(syn::Error::new_spanned(
namevalue,
"max_threads argument must be an int",
))
}
},
name => {
let msg = format!("Unknown attribute pair {} is specified; expected one of: `core_threads`, `max_threads`", name);
let msg = format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`", name);
return Err(syn::Error::new_spanned(namevalue, msg));
}
}
@@ -108,16 +190,28 @@ fn parse_knobs(
let msg = "Must have specified ident";
return Err(syn::Error::new_spanned(path, msg));
}
match ident.unwrap().to_string().to_lowercase().as_str() {
"threaded_scheduler" => {
runtime = Some(runtime.unwrap_or_else(|| Runtime::Threaded))
let name = ident.unwrap().to_string().to_lowercase();
let msg = match name.as_str() {
"threaded_scheduler" | "multi_thread" => {
format!(
"Set the runtime flavor with #[{}(flavor = \"multi_thread\")].",
macro_name
)
}
"basic_scheduler" | "current_thread" | "single_threaded" => {
format!(
"Set the runtime flavor with #[{}(flavor = \"current_thread\")].",
macro_name
)
}
"flavor" | "worker_threads" => {
format!("The `{}` attribute requires an argument.", name)
}
"basic_scheduler" => runtime = Some(runtime.unwrap_or_else(|| Runtime::Basic)),
name => {
let msg = format!("Unknown attribute {} is specified; expected `basic_scheduler` or `threaded_scheduler`", name);
return Err(syn::Error::new_spanned(path, msg));
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`", name)
}
}
};
return Err(syn::Error::new_spanned(path, msg));
}
other => {
return Err(syn::Error::new_spanned(
@@ -128,15 +222,18 @@ fn parse_knobs(
}
}
let mut rt = quote! { tokio::runtime::Builder::new().basic_scheduler() };
if rt_threaded && (runtime == Some(Runtime::Threaded) || (runtime.is_none() && !is_test)) {
rt = quote! { #rt.threaded_scheduler() };
}
if let Some(v) = core_threads.map(|v| v.get()) {
rt = quote! { #rt.core_threads(#v) };
}
if let Some(v) = max_threads.map(|v| v.get()) {
rt = quote! { #rt.max_threads(#v) };
let config = config.build()?;
let mut rt = match config.flavor {
RuntimeFlavor::CurrentThread => quote! {
tokio::runtime::Builder::new_current_thread()
},
RuntimeFlavor::Threaded => quote! {
tokio::runtime::Builder::new_multi_thread()
},
};
if let Some(v) = config.worker_threads {
rt = quote! { #rt.worker_threads(#v) };
}
let header = {
@@ -165,21 +262,21 @@ fn parse_knobs(
}
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub(crate) fn main(args: TokenStream, item: TokenStream, rt_threaded: bool) -> TokenStream {
pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
let input = syn::parse_macro_input!(item as syn::ItemFn);
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
if input.sig.ident == "main" && !input.sig.inputs.is_empty() {
let msg = "the main function cannot accept arguments";
return syn::Error::new_spanned(&input.sig.inputs, msg)
return syn::Error::new_spanned(&input.sig.ident, msg)
.to_compile_error()
.into();
}
parse_knobs(input, args, false, rt_threaded).unwrap_or_else(|e| e.to_compile_error().into())
parse_knobs(input, args, false, rt_multi_thread).unwrap_or_else(|e| e.to_compile_error().into())
}
pub(crate) fn test(args: TokenStream, item: TokenStream, rt_threaded: bool) -> TokenStream {
pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
let input = syn::parse_macro_input!(item as syn::ItemFn);
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
@@ -199,161 +296,5 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_threaded: bool) -> T
.into();
}
parse_knobs(input, args, true, rt_threaded).unwrap_or_else(|e| e.to_compile_error().into())
}
pub(crate) mod old {
use proc_macro::TokenStream;
use quote::quote;
enum Runtime {
Basic,
Threaded,
Auto,
}
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub(crate) fn main(args: TokenStream, item: TokenStream) -> TokenStream {
let mut input = syn::parse_macro_input!(item as syn::ItemFn);
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let sig = &mut input.sig;
let name = &sig.ident;
let inputs = &sig.inputs;
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 syn::Error::new_spanned(sig.fn_token, msg)
.to_compile_error()
.into();
} else if name == "main" && !inputs.is_empty() {
let msg = "the main function cannot accept arguments";
return syn::Error::new_spanned(&sig.inputs, msg)
.to_compile_error()
.into();
}
sig.asyncness = None;
let mut runtime = Runtime::Auto;
for arg in args {
if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = arg {
let ident = path.get_ident();
if ident.is_none() {
let msg = "Must have specified ident";
return syn::Error::new_spanned(path, msg).to_compile_error().into();
}
match ident.unwrap().to_string().to_lowercase().as_str() {
"threaded_scheduler" => runtime = Runtime::Threaded,
"basic_scheduler" => runtime = Runtime::Basic,
name => {
let msg = format!("Unknown attribute {} is specified; expected `basic_scheduler` or `threaded_scheduler`", name);
return syn::Error::new_spanned(path, msg).to_compile_error().into();
}
}
}
}
let result = match runtime {
Runtime::Threaded | Runtime::Auto => quote! {
#(#attrs)*
#vis #sig {
tokio::runtime::Runtime::new().unwrap().block_on(async { #body })
}
},
Runtime::Basic => quote! {
#(#attrs)*
#vis #sig {
tokio::runtime::Builder::new()
.basic_scheduler()
.enable_all()
.build()
.unwrap()
.block_on(async { #body })
}
},
};
result.into()
}
pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as syn::ItemFn);
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let ret = &input.sig.output;
let name = &input.sig.ident;
let body = &input.block;
let attrs = &input.attrs;
let vis = input.vis;
for attr in attrs {
if attr.path.is_ident("test") {
let msg = "second test attribute is supplied";
return syn::Error::new_spanned(&attr, msg)
.to_compile_error()
.into();
}
}
if input.sig.asyncness.is_none() {
let msg = "the async keyword is missing from the function declaration";
return syn::Error::new_spanned(&input.sig.fn_token, msg)
.to_compile_error()
.into();
} else 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();
}
let mut runtime = Runtime::Auto;
for arg in args {
if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = arg {
let ident = path.get_ident();
if ident.is_none() {
let msg = "Must have specified ident";
return syn::Error::new_spanned(path, msg).to_compile_error().into();
}
match ident.unwrap().to_string().to_lowercase().as_str() {
"threaded_scheduler" => runtime = Runtime::Threaded,
"basic_scheduler" => runtime = Runtime::Basic,
name => {
let msg = format!("Unknown attribute {} is specified; expected `basic_scheduler` or `threaded_scheduler`", name);
return syn::Error::new_spanned(path, msg).to_compile_error().into();
}
}
}
}
let result = match runtime {
Runtime::Threaded => quote! {
#[::core::prelude::v1::test]
#(#attrs)*
#vis fn #name() #ret {
tokio::runtime::Runtime::new().unwrap().block_on(async { #body })
}
},
Runtime::Basic | Runtime::Auto => quote! {
#[::core::prelude::v1::test]
#(#attrs)*
#vis fn #name() #ret {
tokio::runtime::Builder::new()
.basic_scheduler()
.enable_all()
.build()
.unwrap()
.block_on(async { #body })
}
},
};
result.into()
}
parse_knobs(input, args, true, rt_multi_thread).unwrap_or_else(|e| e.to_compile_error().into())
}
+90 -164
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio-macros/0.2.5")]
#![doc(html_root_url = "https://docs.rs/tokio-macros/0.3.1")]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -24,18 +24,44 @@ mod select;
use proc_macro::TokenStream;
/// Marks async function to be executed by selected runtime. This macro helps set up a `Runtime`
/// without requiring the user to use [Runtime](../tokio/runtime/struct.Runtime.html) or
/// [Builder](../tokio/runtime/struct.builder.html) directly.
/// Marks async function to be executed by the selected runtime. This macro helps
/// set up a `Runtime` without requiring the user to use
/// [Runtime](../tokio/runtime/struct.Runtime.html) or
/// [Builder](../tokio/runtime/struct.Builder.html) directly.
///
/// ## Options:
/// Note: This macro is designed to be simplistic and targets applications that
/// do not require a complex setup. If the provided functionality is not
/// sufficient, you may be interested in using
/// [Builder](../tokio/runtime/struct.Builder.html), which provides a more
/// powerful interface.
///
/// If you want to set the number of worker threads used for asynchronous code, use the
/// `core_threads` option.
/// Note: This macro can be used on any function and not just the `main`
/// function. Using it on a non-main function makes the function behave
/// as if it was synchronous by starting a new runtime each time it is called.
/// If the function is called often, it is preferable to create the runtime using
/// the runtime builder so the runtime can be reused across calls.
///
/// - `core_threads=n` - Sets core threads to `n` (requires `rt-threaded` feature).
/// - `max_threads=n` - Sets max threads to `n` (requires `rt-core` or `rt-threaded` feature).
/// - `basic_scheduler` - Use the basic schduler (requires `rt-core`).
/// # Multi-threaded runtime
///
/// To use the multi-threaded runtime, the macro can be configured using
///
/// ```
/// #[tokio::main(flavor = "multi_thread", worker_threads = 10)]
/// # async fn main() {}
/// ```
///
/// The `worker_threads` option configures the number of worker threads, and
/// defaults to the number of cpus on the system. This is the default flavor.
///
/// # Current thread runtime
///
/// To use the single-threaded runtime known as the `current_thread` runtime,
/// the macro can be configured using
///
/// ```
/// #[tokio::main(flavor = "current_thread")]
/// # async fn main() {}
/// ```
///
/// ## Function arguments:
///
@@ -43,7 +69,7 @@ use proc_macro::TokenStream;
///
/// ## Usage
///
/// ### Using default
/// ### Using the multi-thread runtime
///
/// ```rust
/// #[tokio::main]
@@ -56,8 +82,7 @@ use proc_macro::TokenStream;
///
/// ```rust
/// fn main() {
/// tokio::runtime::Builder::new()
/// .threaded_scheduler()
/// tokio::runtime::Builder::new_multi_thread()
/// .enable_all()
/// .build()
/// .unwrap()
@@ -67,12 +92,12 @@ use proc_macro::TokenStream;
/// }
/// ```
///
/// ### Using basic scheduler
/// ### Using current thread runtime
///
/// The basic scheduler is single-threaded.
///
/// ```rust
/// #[tokio::main(basic_scheduler)]
/// #[tokio::main(flavor = "current_thread")]
/// async fn main() {
/// println!("Hello world");
/// }
@@ -82,8 +107,7 @@ use proc_macro::TokenStream;
///
/// ```rust
/// fn main() {
/// tokio::runtime::Builder::new()
/// .basic_scheduler()
/// tokio::runtime::Builder::new_current_thread()
/// .enable_all()
/// .build()
/// .unwrap()
@@ -93,10 +117,10 @@ use proc_macro::TokenStream;
/// }
/// ```
///
/// ### Set number of core threads
/// ### Set number of worker threads
///
/// ```rust
/// #[tokio::main(core_threads = 2)]
/// #[tokio::main(worker_threads = 2)]
/// async fn main() {
/// println!("Hello world");
/// }
@@ -106,9 +130,8 @@ use proc_macro::TokenStream;
///
/// ```rust
/// fn main() {
/// tokio::runtime::Builder::new()
/// .threaded_scheduler()
/// .core_threads(2)
/// tokio::runtime::Builder::new_multi_thread()
/// .worker_threads(2)
/// .enable_all()
/// .build()
/// .unwrap()
@@ -120,14 +143,13 @@ use proc_macro::TokenStream;
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
/// If you rename the tokio crate in your dependencies this macro will not work.
/// If you must rename the 0.3 version of tokio because you're also using the
/// 0.1 version of tokio, you _must_ make the tokio 0.3 crate available as
/// `tokio` in the module where this macro is expanded.
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args, item, true)
}
@@ -135,11 +157,6 @@ pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
/// without requiring the user to use [Runtime](../tokio/runtime/struct.Runtime.html) or
/// [Builder](../tokio/runtime/struct.builder.html) directly.
///
/// ## Options:
///
/// - `basic_scheduler` - All tasks are executed on the current thread.
/// - `threaded_scheduler` - Uses the multi-threaded scheduler. Used by default (requires `rt-threaded` feature).
///
/// ## Function arguments:
///
/// Arguments are allowed for any functions aside from `main` which is special
@@ -149,7 +166,7 @@ pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
/// ### Using default
///
/// ```rust
/// #[tokio::main]
/// #[tokio::main(flavor = "current_thread")]
/// async fn main() {
/// println!("Hello world");
/// }
@@ -159,29 +176,7 @@ pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
///
/// ```rust
/// fn main() {
/// tokio::runtime::Runtime::new()
/// .unwrap()
/// .block_on(async {
/// println!("Hello world");
/// })
/// }
/// ```
///
/// ### Select runtime
///
/// ```rust
/// #[tokio::main(basic_scheduler)]
/// async fn main() {
/// println!("Hello world");
/// }
/// ```
///
/// Equivalent code not using `#[tokio::main]`
///
/// ```rust
/// fn main() {
/// tokio::runtime::Builder::new()
/// .basic_scheduler()
/// tokio::runtime::Builder::new_current_thread()
/// .enable_all()
/// .build()
/// .unwrap()
@@ -194,80 +189,24 @@ pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// will not work. If you must rename the 0.3 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// tokio 0.3 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
entry::old::main(args, item)
}
/// Marks async function to be executed by selected runtime. This macro helps set up a `Runtime`
/// without requiring the user to use [Runtime](../tokio/runtime/struct.Runtime.html) or
/// [Builder](../tokio/runtime/struct.builder.html) directly.
///
/// ## Options:
///
/// - `max_threads=n` - Sets max threads to `n`.
///
/// ## Function arguments:
///
/// Arguments are allowed for any functions aside from `main` which is special
///
/// ## Usage
///
/// ### Using default
///
/// ```rust
/// #[tokio::main]
/// async fn main() {
/// println!("Hello world");
/// }
/// ```
///
/// Equivalent code not using `#[tokio::main]`
///
/// ```rust
/// fn main() {
/// tokio::runtime::Builder::new()
/// .basic_scheduler()
/// .enable_all()
/// .build()
/// .unwrap()
/// .block_on(async {
/// println!("Hello world");
/// })
/// }
/// ```
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main_basic(args: TokenStream, item: TokenStream) -> TokenStream {
pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args, item, false)
}
/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `core_threads=n` - Sets core threads to `n` (requires `rt-threaded` feature).
/// - `max_threads=n` - Sets max threads to `n` (requires `rt-core` or `rt-threaded` feature).
///
/// ## Usage
///
/// ### Select runtime
/// ### Multi-thread runtime
///
/// ```no_run
/// #[tokio::test(core_threads = 1)]
/// #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
/// async fn my_test() {
/// assert!(true);
/// }
@@ -275,6 +214,8 @@ pub fn main_basic(args: TokenStream, item: TokenStream) -> TokenStream {
///
/// ### Using default
///
/// The default test runtime is single-threaded.
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
@@ -285,35 +226,19 @@ pub fn main_basic(args: TokenStream, item: TokenStream) -> TokenStream {
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// will not work. If you must rename the 0.3 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// tokio 0.3 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
pub fn test_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, true)
}
/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `basic_scheduler` - All tasks are executed on the current thread. Used by default.
/// - `threaded_scheduler` - Use multi-threaded scheduler (requires `rt-threaded` feature).
///
/// ## Usage
///
/// ### Select runtime
///
/// ```no_run
/// #[tokio::test(threaded_scheduler)]
/// async fn my_test() {
/// assert!(true);
/// }
/// ```
///
/// ### Using default
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
@@ -324,40 +249,41 @@ pub fn test_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// will not work. If you must rename the 0.3 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// tokio 0.3 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
entry::old::test(args, item)
pub fn test_rt(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, false)
}
/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `max_threads=n` - Sets max threads to `n`.
///
/// ## Usage
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
/// assert!(true);
/// }
/// Always fails with the error message below.
/// ```text
/// The #[tokio::main] macro requires rt or rt-multi-thread.
/// ```
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
pub fn test_basic(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, false)
pub fn main_fail(_args: TokenStream, _item: TokenStream) -> TokenStream {
syn::Error::new(
proc_macro2::Span::call_site(),
"The #[tokio::main] macro requires rt or rt-multi-thread.",
)
.to_compile_error()
.into()
}
/// Always fails with the error message below.
/// ```text
/// The #[tokio::test] macro requires rt or rt-multi-thread.
/// ```
#[proc_macro_attribute]
pub fn test_fail(_args: TokenStream, _item: TokenStream) -> TokenStream {
syn::Error::new(
proc_macro2::Span::call_site(),
"The #[tokio::test] macro requires rt or rt-multi-thread.",
)
.to_compile_error()
.into()
}
/// Implementation detail of the `select!` macro. This macro is **not** intended
+4
View File
@@ -1,3 +1,7 @@
# 0.3.0 (October 15, 2020)
- Track `tokio` 0.3 release.
# 0.2.1 (April 17, 2020)
- Add `Future` and `Stream` implementations for `task::Spawn<T>`.
+6 -6
View File
@@ -6,27 +6,27 @@ name = "tokio-test"
# - Update doc url
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "v0.2.x" git tag.
version = "0.2.1"
# - Create "v0.3.x" git tag.
version = "0.3.0"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-test/0.2.1/tokio_test"
documentation = "https://docs.rs/tokio-test/0.3.0/tokio_test"
description = """
Testing utilities for Tokio- and futures-based code
"""
categories = ["asynchronous", "testing"]
[dependencies]
tokio = { version = "0.2.0", path = "../tokio", features = ["rt-core", "stream", "sync", "time", "test-util"] }
tokio = { version = "0.3.0", path = "../tokio", features = ["rt", "stream", "sync", "time", "test-util"] }
bytes = "0.5.0"
bytes = "0.6.0"
futures-core = "0.3.0"
[dev-dependencies]
tokio = { version = "0.2.0", path = "../tokio", features = ["full"] }
tokio = { version = "0.3.0", path = "../tokio", features = ["full"] }
futures-util = "0.3.0"
[package.metadata.docs.rs]
+46 -35
View File
@@ -18,11 +18,10 @@
//! [`AsyncRead`]: tokio::io::AsyncRead
//! [`AsyncWrite`]: tokio::io::AsyncWrite
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::sync::mpsc;
use tokio::time::{self, Delay, Duration, Instant};
use tokio::time::{self, Duration, Instant, Sleep};
use bytes::Buf;
use futures_core::ready;
use std::collections::VecDeque;
use std::future::Future;
@@ -68,7 +67,7 @@ enum Action {
struct Inner {
actions: VecDeque<Action>,
waiting: Option<Instant>,
sleep: Option<Delay>,
sleep: Option<Sleep>,
read_wait: Option<Waker>,
rx: mpsc::UnboundedReceiver<Action>,
}
@@ -201,23 +200,24 @@ impl Inner {
}
fn poll_action(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<Action>> {
self.rx.poll_recv(cx)
use futures_core::stream::Stream;
Pin::new(&mut self.rx).poll_next(cx)
}
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
fn read(&mut self, dst: &mut ReadBuf<'_>) -> io::Result<()> {
match self.action() {
Some(&mut Action::Read(ref mut data)) => {
// Figure out how much to copy
let n = cmp::min(dst.len(), data.len());
let n = cmp::min(dst.remaining(), data.len());
// Copy the data into the `dst` slice
(&mut dst[..n]).copy_from_slice(&data[..n]);
dst.put_slice(&data[..n]);
// Drain the data from the source
data.drain(..n);
// Return the number of bytes read
Ok(n)
Ok(())
}
Some(&mut Action::ReadError(ref mut err)) => {
// As the
@@ -229,7 +229,7 @@ impl Inner {
// Either waiting or expecting a write
Err(io::ErrorKind::WouldBlock.into())
}
None => Ok(0),
None => Ok(()),
}
}
@@ -348,8 +348,8 @@ impl AsyncRead for Mock {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
loop {
if let Some(ref mut sleep) = self.inner.sleep {
ready!(Pin::new(sleep).poll(cx));
@@ -358,29 +358,35 @@ impl AsyncRead for Mock {
// If a sleep is set, it has already fired
self.inner.sleep = None;
// Capture 'filled' to monitor if it changed
let filled = buf.filled().len();
match self.inner.read(buf) {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
if let Some(rem) = self.inner.remaining_wait() {
let until = Instant::now() + rem;
self.inner.sleep = Some(time::delay_until(until));
self.inner.sleep = Some(time::sleep_until(until));
} else {
self.inner.read_wait = Some(cx.waker().clone());
return Poll::Pending;
}
}
Ok(0) => {
// TODO: Extract
match ready!(self.inner.poll_action(cx)) {
Some(action) => {
self.inner.actions.push_back(action);
continue;
}
None => {
return Poll::Ready(Ok(0));
Ok(()) => {
if buf.filled().len() == filled {
match ready!(self.inner.poll_action(cx)) {
Some(action) => {
self.inner.actions.push_back(action);
continue;
}
None => {
return Poll::Ready(Ok(()));
}
}
} else {
return Poll::Ready(Ok(()));
}
}
ret => return Poll::Ready(ret),
Err(e) => return Poll::Ready(Err(e)),
}
}
}
@@ -404,7 +410,7 @@ impl AsyncWrite for Mock {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
if let Some(rem) = self.inner.remaining_wait() {
let until = Instant::now() + rem;
self.inner.sleep = Some(time::delay_until(until));
self.inner.sleep = Some(time::sleep_until(until));
} else {
panic!("unexpected WouldBlock");
}
@@ -434,16 +440,6 @@ impl AsyncWrite for Mock {
}
}
fn poll_write_buf<B: Buf>(
self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
buf: &mut B,
) -> Poll<io::Result<usize>> {
let n = ready!(self.poll_write(cx, buf.bytes()))?;
buf.advance(n);
Poll::Ready(Ok(n))
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
@@ -453,6 +449,21 @@ impl AsyncWrite for Mock {
}
}
/// Ensures that Mock isn't dropped with data "inside".
impl Drop for Mock {
fn drop(&mut self) {
// Avoid double panicking, since makes debugging much harder.
if std::thread::panicking() {
return;
}
self.inner.actions.iter().for_each(|a| match a {
Action::Read(data) => assert!(data.is_empty(), "There is still data left to read."),
Action::Write(data) => assert!(data.is_empty(), "There is still data left to write."),
_ => (),
})
}
}
/*
/// Returns `true` if called from the context of a futures-rs Task
fn is_task_ctx() -> bool {
+2 -3
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio-test/0.2.1")]
#![doc(html_root_url = "https://docs.rs/tokio-test/0.3.0")]
#![warn(
missing_debug_implementations,
missing_docs,
@@ -28,8 +28,7 @@ pub mod task;
pub fn block_on<F: std::future::Future>(future: F) -> F::Output {
use tokio::runtime;
let mut rt = runtime::Builder::new()
.basic_scheduler()
let rt = runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
+1 -1
View File
@@ -11,7 +11,7 @@ use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use tokio::stream::Stream;
/// TOOD: dox
/// TODO: dox
pub fn spawn<T>(task: T) -> Spawn<T> {
Spawn {
task: MockTask::new(),
+3 -3
View File
@@ -1,6 +1,6 @@
#![warn(rust_2018_idioms)]
use tokio::time::{delay_until, Duration, Instant};
use tokio::time::{sleep_until, Duration, Instant};
use tokio_test::block_on;
#[test]
@@ -18,10 +18,10 @@ fn async_fn() {
}
#[test]
fn test_delay() {
fn test_sleep() {
let deadline = Instant::now() + Duration::from_millis(100);
block_on(async {
delay_until(deadline).await;
sleep_until(deadline).await;
});
}
+14
View File
@@ -70,3 +70,17 @@ async fn write_error() {
mock.write_all(b"world!").await.expect("write 2");
}
#[tokio::test]
#[should_panic]
async fn mock_panics_read_data_left() {
use tokio_test::io::Builder;
Builder::new().read(b"read").build();
}
#[tokio::test]
#[should_panic]
async fn mock_panics_write_data_left() {
use tokio_test::io::Builder;
Builder::new().write(b"write").build();
}
+20
View File
@@ -1,3 +1,23 @@
# 0.5.1 (December 3, 2020)
### Added
- io: `poll_read_buf` util fn (#2972).
- io: `poll_write_buf` util fn with vectored write support (#3156).
# 0.5.0 (October 30, 2020)
### Changed
- io: update `bytes` to 0.6 (#3071).
# 0.4.0 (October 15, 2020)
### Added
- sync: `CancellationToken` for coordinating task cancellation (#2747).
- rt: `TokioContext` sets the Tokio runtime for the duration of a future (#2791)
- io: `StreamReader`/`ReaderStream` map between `AsyncRead` values and `Stream`
of bytes (#2788).
- time: `DelayQueue` to manage many delays (#2897).
# 0.3.1 (March 18, 2020)
### Fixed
+14 -9
View File
@@ -7,13 +7,13 @@ name = "tokio-util"
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "v0.2.x" git tag.
version = "0.3.1"
version = "0.5.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-util/0.3.1/tokio_util"
documentation = "https://docs.rs/tokio-util/0.5.1/tokio_util"
description = """
Additional utilities for working with Tokio.
"""
@@ -24,27 +24,32 @@ categories = ["asynchronous"]
default = []
# Shorthand for enabling everything
full = ["codec", "udp", "compat"]
full = ["codec", "compat", "io", "time", "net", "rt"]
net = ["tokio/net"]
compat = ["futures-io",]
codec = ["tokio/stream"]
udp = ["tokio/udp"]
time = ["tokio/time","slab"]
io = []
rt = ["tokio/rt"]
[dependencies]
tokio = { version = "0.2.5", path = "../tokio" }
tokio = { version = "0.3.4", path = "../tokio" }
bytes = "0.5.0"
bytes = "0.6.0"
futures-core = "0.3.0"
futures-sink = "0.3.0"
futures-io = { version = "0.3.0", optional = true }
log = "0.4"
pin-project-lite = "0.1.4"
pin-project-lite = "0.2.0"
slab = { version = "0.4.1", optional = true } # Backs `DelayQueue`
[dev-dependencies]
tokio = { version = "0.2.0", path = "../tokio", features = ["full"] }
tokio-test = { version = "0.2.0", path = "../tokio-test" }
tokio = { version = "0.3.0", path = "../tokio", features = ["full"] }
tokio-test = { version = "0.3.0", path = "../tokio-test" }
futures = "0.3.0"
futures-test = "0.3.5"
[package.metadata.docs.rs]
all-features = true
+23 -3
View File
@@ -18,11 +18,31 @@ macro_rules! cfg_compat {
}
}
macro_rules! cfg_udp {
macro_rules! cfg_net {
($($item:item)*) => {
$(
#[cfg(all(feature = "udp", feature = "codec"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "udp", feature = "codec"))))]
#[cfg(all(feature = "net", feature = "codec"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "net", feature = "codec"))))]
$item
)*
}
}
macro_rules! cfg_io {
($($item:item)*) => {
$(
#[cfg(feature = "io")]
#[cfg_attr(docsrs, doc(cfg(feature = "io")))]
$item
)*
}
}
macro_rules! cfg_rt {
($($item:item)*) => {
$(
#[cfg(feature = "rt")]
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
$item
)*
}
+1 -1
View File
@@ -33,7 +33,7 @@ use std::io;
/// # }
/// # }
/// #
/// # #[tokio::main(core_threads = 1)]
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> Result<(), std::io::Error> {
/// let my_async_read = File::open("filename.txt").await?;
/// let my_stream_of_bytes = FramedRead::new(my_async_read, BytesCodec::new());
+6 -6
View File
@@ -6,7 +6,7 @@ use tokio::{
stream::Stream,
};
use bytes::{Buf, BytesMut};
use bytes::BytesMut;
use futures_core::ready;
use futures_sink::Sink;
use log::trace;
@@ -118,6 +118,8 @@ where
type Item = Result<U::Item, U::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
use crate::util::poll_read_buf;
let mut pinned = self.project();
let state: &mut ReadFrame = pinned.state.borrow_mut();
loop {
@@ -148,7 +150,7 @@ where
// got room for at least one byte to read to ensure that we don't
// get a spurious 0 that looks like EOF
state.buffer.reserve(1);
let bytect = match pinned.inner.as_mut().poll_read_buf(cx, &mut state.buffer)? {
let bytect = match poll_read_buf(pinned.inner.as_mut(), cx, &mut state.buffer)? {
Poll::Ready(ct) => ct,
Poll::Pending => return Poll::Pending,
};
@@ -187,6 +189,7 @@ where
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
use crate::util::poll_write_buf;
trace!("flushing framed transport");
let mut pinned = self.project();
@@ -194,8 +197,7 @@ where
let WriteFrame { buffer } = pinned.state.borrow_mut();
trace!("writing; remaining={}", buffer.len());
let buf = &buffer;
let n = ready!(pinned.inner.as_mut().poll_write(cx, &buf))?;
let n = ready!(poll_write_buf(pinned.inner.as_mut(), cx, buffer))?;
if n == 0 {
return Poll::Ready(Err(io::Error::new(
@@ -205,8 +207,6 @@ where
)
.into()));
}
pinned.state.borrow_mut().buffer.advance(n);
}
// Try flushing the underlying IO
+5
View File
@@ -100,6 +100,11 @@ impl<T, D> FramedRead<T, D> {
pub fn read_buffer(&self) -> &BytesMut {
&self.inner.state.buffer
}
/// Returns a mutable reference to the read buffer.
pub fn read_buffer_mut(&mut self) -> &mut BytesMut {
&mut self.inner.state.buffer
}
}
// This impl just defers to the underlying FramedImpl
+22 -6
View File
@@ -1,5 +1,6 @@
//! Compatibility between the `tokio::io` and `futures-io` versions of the
//! `AsyncRead` and `AsyncWrite` traits.
use futures_core::ready;
use pin_project_lite::pin_project;
use std::io;
use std::pin::Pin;
@@ -19,7 +20,7 @@ pin_project! {
/// `futures_io::AsyncRead` to implement `tokio::io::AsyncRead`.
pub trait FuturesAsyncReadCompatExt: futures_io::AsyncRead {
/// Wraps `self` with a compatibility layer that implements
/// `tokio_io::AsyncWrite`.
/// `tokio_io::AsyncRead`.
fn compat(self) -> Compat<Self>
where
Self: Sized,
@@ -107,9 +108,18 @@ where
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
futures_io::AsyncRead::poll_read(self.project().inner, cx, buf)
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<io::Result<()>> {
// We can't trust the inner type to not peak at the bytes,
// so we must defensively initialize the buffer.
let slice = buf.initialize_unfilled();
let n = ready!(futures_io::AsyncRead::poll_read(
self.project().inner,
cx,
slice
))?;
buf.advance(n);
Poll::Ready(Ok(()))
}
}
@@ -120,9 +130,15 @@ where
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
slice: &mut [u8],
) -> Poll<io::Result<usize>> {
tokio::io::AsyncRead::poll_read(self.project().inner, cx, buf)
let mut buf = tokio::io::ReadBuf::new(slice);
ready!(tokio::io::AsyncRead::poll_read(
self.project().inner,
cx,
&mut buf
))?;
Poll::Ready(Ok(buf.filled().len()))
}
}
+78
View File
@@ -0,0 +1,78 @@
//! Tokio context aware futures utilities.
//!
//! This module includes utilities around integrating tokio with other runtimes
//! by allowing the context to be attached to futures. This allows spawning
//! futures on other executors while still using tokio to drive them. This
//! can be useful if you need to use a tokio based library in an executor/runtime
//! that does not provide a tokio context.
use pin_project_lite::pin_project;
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use tokio::runtime::Runtime;
pin_project! {
/// `TokioContext` allows connecting a custom executor with the tokio runtime.
///
/// It contains a `Handle` to the runtime. A handle to the runtime can be
/// obtain by calling the `Runtime::handle()` method.
pub struct TokioContext<'a, F> {
#[pin]
inner: F,
handle: &'a Runtime,
}
}
impl<F: Future> Future for TokioContext<'_, F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let me = self.project();
let handle = me.handle;
let fut = me.inner;
let _enter = handle.enter();
fut.poll(cx)
}
}
/// Trait extension that simplifies bundling a `Handle` with a `Future`.
pub trait RuntimeExt {
/// Convenience method that takes a Future and returns a `TokioContext`.
///
/// # Example: calling Tokio Runtime from a custom ThreadPool
///
/// ```no_run
/// use tokio_util::context::RuntimeExt;
/// use tokio::time::{sleep, Duration};
///
/// let rt = tokio::runtime::Builder::new_multi_thread()
/// .enable_all()
/// .build()
/// .unwrap();
///
/// let rt2 = tokio::runtime::Builder::new_multi_thread()
/// .build()
/// .unwrap();
///
/// let fut = sleep(Duration::from_millis(2));
///
/// rt.block_on(
/// rt2
/// .wrap(async { sleep(Duration::from_millis(2)).await }),
/// );
///```
fn wrap<F: Future>(&self, fut: F) -> TokioContext<'_, F>;
}
impl RuntimeExt for Runtime {
fn wrap<F: Future>(&self, fut: F) -> TokioContext<'_, F> {
TokioContext {
inner: fut,
handle: self,
}
}
}
+190
View File
@@ -0,0 +1,190 @@
//! Module defining an Either type.
use std::{
future::Future,
io::SeekFrom,
pin::Pin,
task::{Context, Poll},
};
use tokio::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf, Result};
/// Combines two different futures, streams, or sinks having the same associated types into a single type.
///
/// This type implements common asynchronous traits such as [`Future`] and those in Tokio.
///
/// [`Future`]: std::future::Future
///
/// # Example
///
/// The following code will not work:
///
/// ```compile_fail
/// # fn some_condition() -> bool { true }
/// # async fn some_async_function() -> u32 { 10 }
/// # async fn other_async_function() -> u32 { 20 }
/// #[tokio::main]
/// async fn main() {
/// let result = if some_condition() {
/// some_async_function()
/// } else {
/// other_async_function() // <- Will print: "`if` and `else` have incompatible types"
/// };
///
/// println!("Result is {}", result.await);
/// }
/// ```
///
// This is because although the output types for both futures is the same, the exact future
// types are different, but the compiler must be able to choose a single type for the
// `result` variable.
///
/// When the output type is the same, we can wrap each future in `Either` to avoid the
/// issue:
///
/// ```
/// use tokio_util::either::Either;
/// # fn some_condition() -> bool { true }
/// # async fn some_async_function() -> u32 { 10 }
/// # async fn other_async_function() -> u32 { 20 }
///
/// #[tokio::main]
/// async fn main() {
/// let result = if some_condition() {
/// Either::Left(some_async_function())
/// } else {
/// Either::Right(other_async_function())
/// };
///
/// let value = result.await;
/// println!("Result is {}", value);
/// # assert_eq!(value, 10);
/// }
/// ```
#[allow(missing_docs)] // Doc-comments for variants in this particular case don't make much sense.
#[derive(Debug, Clone)]
pub enum Either<L, R> {
Left(L),
Right(R),
}
/// A small helper macro which reduces amount of boilerplate in the actual trait method implementation.
/// It takes an invokation of method as an argument (e.g. `self.poll(cx)`), and redirects it to either
/// enum variant held in `self`.
macro_rules! delegate_call {
($self:ident.$method:ident($($args:ident),+)) => {
unsafe {
match $self.get_unchecked_mut() {
Self::Left(l) => Pin::new_unchecked(l).$method($($args),+),
Self::Right(r) => Pin::new_unchecked(r).$method($($args),+),
}
}
}
}
impl<L, R, O> Future for Either<L, R>
where
L: Future<Output = O>,
R: Future<Output = O>,
{
type Output = O;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
delegate_call!(self.poll(cx))
}
}
impl<L, R> AsyncRead for Either<L, R>
where
L: AsyncRead,
R: AsyncRead,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<Result<()>> {
delegate_call!(self.poll_read(cx, buf))
}
}
impl<L, R> AsyncBufRead for Either<L, R>
where
L: AsyncBufRead,
R: AsyncBufRead,
{
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
delegate_call!(self.poll_fill_buf(cx))
}
fn consume(self: Pin<&mut Self>, amt: usize) {
delegate_call!(self.consume(amt))
}
}
impl<L, R> AsyncSeek for Either<L, R>
where
L: AsyncSeek,
R: AsyncSeek,
{
fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> Result<()> {
delegate_call!(self.start_seek(position))
}
fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<u64>> {
delegate_call!(self.poll_complete(cx))
}
}
impl<L, R> AsyncWrite for Either<L, R>
where
L: AsyncWrite,
R: AsyncWrite,
{
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
delegate_call!(self.poll_write(cx, buf))
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<tokio::io::Result<()>> {
delegate_call!(self.poll_flush(cx))
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<tokio::io::Result<()>> {
delegate_call!(self.poll_shutdown(cx))
}
}
impl<L, R> futures_core::stream::Stream for Either<L, R>
where
L: futures_core::stream::Stream,
R: futures_core::stream::Stream<Item = L::Item>,
{
type Item = L::Item;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
delegate_call!(self.poll_next(cx))
}
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::{
io::{repeat, AsyncReadExt, Repeat},
stream::{once, Once, StreamExt},
};
#[tokio::test]
async fn either_is_stream() {
let mut either: Either<Once<u32>, Once<u32>> = Either::Left(once(1));
assert_eq!(Some(1u32), either.next().await);
}
#[tokio::test]
async fn either_is_async_read() {
let mut buffer = [0; 3];
let mut either: Either<Repeat, Repeat> = Either::Right(repeat(0b101));
either.read_exact(&mut buffer).await.unwrap();
assert_eq!(buffer, [0b101, 0b101, 0b101]);
}
}
+16
View File
@@ -0,0 +1,16 @@
//! Helpers for IO related tasks.
//!
//! These types are often used in combination with hyper or reqwest, as they
//! allow converting between a hyper [`Body`] and [`AsyncRead`].
//!
//! [`Body`]: https://docs.rs/hyper/0.13/hyper/struct.Body.html
//! [`AsyncRead`]: tokio::io::AsyncRead
mod read_buf;
mod reader_stream;
mod stream_reader;
pub use self::read_buf::read_buf;
pub use self::reader_stream::ReaderStream;
pub use self::stream_reader::StreamReader;
pub use crate::util::{poll_read_buf, poll_write_buf};
+65
View File
@@ -0,0 +1,65 @@
use bytes::BufMut;
use std::future::Future;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::AsyncRead;
/// Read data from an `AsyncRead` into an implementer of the [`BufMut`] trait.
///
/// [`BufMut`]: bytes::BufMut
///
/// # Example
///
/// ```
/// use bytes::{Bytes, BytesMut};
/// use tokio::stream;
/// use tokio::io::Result;
/// use tokio_util::io::{StreamReader, read_buf};
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
///
/// // Create a reader from an iterator. This particular reader will always be
/// // ready.
/// let mut read = StreamReader::new(stream::iter(vec![Result::Ok(Bytes::from_static(&[0, 1, 2, 3]))]));
///
/// let mut buf = BytesMut::new();
/// let mut reads = 0;
///
/// loop {
/// reads += 1;
/// let n = read_buf(&mut read, &mut buf).await?;
///
/// if n == 0 {
/// break;
/// }
/// }
///
/// // one or more reads might be necessary.
/// assert!(reads >= 1);
/// assert_eq!(&buf[..], &[0, 1, 2, 3]);
/// # Ok(())
/// # }
/// ```
pub async fn read_buf<R, B>(read: &mut R, buf: &mut B) -> io::Result<usize>
where
R: AsyncRead + Unpin,
B: BufMut,
{
return ReadBufFn(read, buf).await;
struct ReadBufFn<'a, R, B>(&'a mut R, &'a mut B);
impl<'a, R, B> Future for ReadBufFn<'a, R, B>
where
R: AsyncRead + Unpin,
B: BufMut,
{
type Output = io::Result<usize>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = &mut *self;
crate::util::poll_read_buf(Pin::new(this.0), cx, this.1)
}
}
}
+102
View File
@@ -0,0 +1,102 @@
use bytes::{Bytes, BytesMut};
use futures_core::stream::Stream;
use pin_project_lite::pin_project;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::AsyncRead;
const CAPACITY: usize = 4096;
pin_project! {
/// Convert an [`AsyncRead`] into a [`Stream`] of byte chunks.
///
/// This stream is fused. It performs the inverse operation of
/// [`StreamReader`].
///
/// # Example
///
/// ```
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// use tokio::stream::StreamExt;
/// use tokio_util::io::ReaderStream;
///
/// // Create a stream of data.
/// let data = b"hello, world!";
/// let mut stream = ReaderStream::new(&data[..]);
///
/// // Read all of the chunks into a vector.
/// let mut stream_contents = Vec::new();
/// while let Some(chunk) = stream.next().await {
/// stream_contents.extend_from_slice(&chunk?);
/// }
///
/// // Once the chunks are concatenated, we should have the
/// // original data.
/// assert_eq!(stream_contents, data);
/// # Ok(())
/// # }
/// ```
///
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`StreamReader`]: crate::io::StreamReader
/// [`Stream`]: tokio::stream::Stream
#[derive(Debug)]
pub struct ReaderStream<R> {
// Reader itself.
//
// This value is `None` if the stream has terminated.
#[pin]
reader: Option<R>,
// Working buffer, used to optimize allocations.
buf: BytesMut,
}
}
impl<R: AsyncRead> ReaderStream<R> {
/// Convert an [`AsyncRead`] into a [`Stream`] with item type
/// `Result<Bytes, std::io::Error>`.
///
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`Stream`]: tokio::stream::Stream
pub fn new(reader: R) -> Self {
ReaderStream {
reader: Some(reader),
buf: BytesMut::new(),
}
}
}
impl<R: AsyncRead> Stream for ReaderStream<R> {
type Item = std::io::Result<Bytes>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
use crate::util::poll_read_buf;
let mut this = self.as_mut().project();
let reader = match this.reader.as_pin_mut() {
Some(r) => r,
None => return Poll::Ready(None),
};
if this.buf.capacity() == 0 {
this.buf.reserve(CAPACITY);
}
match poll_read_buf(reader, cx, &mut this.buf) {
Poll::Pending => Poll::Pending,
Poll::Ready(Err(err)) => {
self.project().reader.set(None);
Poll::Ready(Some(Err(err)))
}
Poll::Ready(Ok(0)) => {
self.project().reader.set(None);
Poll::Ready(None)
}
Poll::Ready(Ok(_)) => {
let chunk = this.buf.split();
Poll::Ready(Some(Ok(chunk.freeze())))
}
}
}
}
+158
View File
@@ -0,0 +1,158 @@
use bytes::Buf;
use futures_core::stream::Stream;
use pin_project_lite::pin_project;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncBufRead, AsyncRead, ReadBuf};
pin_project! {
/// Convert a [`Stream`] of byte chunks into an [`AsyncRead`].
///
/// This type performs the inverse operation of [`ReaderStream`].
///
/// # Example
///
/// ```
/// use bytes::Bytes;
/// use tokio::io::{AsyncReadExt, Result};
/// use tokio_util::io::StreamReader;
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
///
/// // Create a stream from an iterator.
/// let stream = tokio::stream::iter(vec![
/// Result::Ok(Bytes::from_static(&[0, 1, 2, 3])),
/// Result::Ok(Bytes::from_static(&[4, 5, 6, 7])),
/// Result::Ok(Bytes::from_static(&[8, 9, 10, 11])),
/// ]);
///
/// // Convert it to an AsyncRead.
/// let mut read = StreamReader::new(stream);
///
/// // Read five bytes from the stream.
/// let mut buf = [0; 5];
/// read.read_exact(&mut buf).await?;
/// assert_eq!(buf, [0, 1, 2, 3, 4]);
///
/// // Read the rest of the current chunk.
/// assert_eq!(read.read(&mut buf).await?, 3);
/// assert_eq!(&buf[..3], [5, 6, 7]);
///
/// // Read the next chunk.
/// assert_eq!(read.read(&mut buf).await?, 4);
/// assert_eq!(&buf[..4], [8, 9, 10, 11]);
///
/// // We have now reached the end.
/// assert_eq!(read.read(&mut buf).await?, 0);
///
/// # Ok(())
/// # }
/// ```
///
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`Stream`]: tokio::stream::Stream
/// [`ReaderStream`]: crate::io::ReaderStream
#[derive(Debug)]
pub struct StreamReader<S, B> {
#[pin]
inner: S,
chunk: Option<B>,
}
}
impl<S, B, E> StreamReader<S, B>
where
S: Stream<Item = Result<B, E>>,
B: Buf,
E: Into<std::io::Error>,
{
/// Convert a stream of byte chunks into an [`AsyncRead`](tokio::io::AsyncRead).
///
/// The item should be a [`Result`] with the ok variant being something that
/// implements the [`Buf`] trait (e.g. `Vec<u8>` or `Bytes`). The error
/// should be convertible into an [io error].
///
/// [`Result`]: std::result::Result
/// [`Buf`]: bytes::Buf
/// [io error]: std::io::Error
pub fn new(stream: S) -> Self {
Self {
inner: stream,
chunk: None,
}
}
/// Do we have a chunk and is it non-empty?
fn has_chunk(self: Pin<&mut Self>) -> bool {
if let Some(chunk) = self.project().chunk {
chunk.remaining() > 0
} else {
false
}
}
}
impl<S, B, E> AsyncRead for StreamReader<S, B>
where
S: Stream<Item = Result<B, E>>,
B: Buf,
E: Into<std::io::Error>,
{
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
if buf.remaining() == 0 {
return Poll::Ready(Ok(()));
}
let inner_buf = match self.as_mut().poll_fill_buf(cx) {
Poll::Ready(Ok(buf)) => buf,
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
Poll::Pending => return Poll::Pending,
};
let len = std::cmp::min(inner_buf.len(), buf.remaining());
buf.put_slice(&inner_buf[..len]);
self.consume(len);
Poll::Ready(Ok(()))
}
}
impl<S, B, E> AsyncBufRead for StreamReader<S, B>
where
S: Stream<Item = Result<B, E>>,
B: Buf,
E: Into<std::io::Error>,
{
fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
loop {
if self.as_mut().has_chunk() {
// This unwrap is very sad, but it can't be avoided.
let buf = self.project().chunk.as_ref().unwrap().bytes();
return Poll::Ready(Ok(buf));
} else {
match self.as_mut().project().inner.poll_next(cx) {
Poll::Ready(Some(Ok(chunk))) => {
// Go around the loop in case the chunk is empty.
*self.as_mut().project().chunk = Some(chunk);
}
Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())),
Poll::Ready(None) => return Poll::Ready(Ok(&[])),
Poll::Pending => return Poll::Pending,
}
}
}
}
fn consume(self: Pin<&mut Self>, amt: usize) {
if amt > 0 {
self.project()
.chunk
.as_mut()
.expect("No chunk present")
.advance(amt);
}
}
}
+165 -2
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio-util/0.3.1")]
#![doc(html_root_url = "https://docs.rs/tokio-util/0.5.1")]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -24,14 +24,177 @@
#[macro_use]
mod cfg;
mod loom;
cfg_codec! {
pub mod codec;
}
cfg_udp! {
cfg_net! {
pub mod udp;
}
cfg_compat! {
pub mod compat;
}
cfg_io! {
pub mod io;
}
cfg_rt! {
pub mod context;
}
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};
use bytes::{Buf, BufMut};
use futures_core::ready;
use std::io::{self, IoSlice};
use std::mem::MaybeUninit;
use std::pin::Pin;
use std::task::{Context, Poll};
/// Try to read data from an `AsyncRead` into an implementer of the [`BufMut`] trait.
///
/// [`BufMut`]: bytes::Buf
///
/// # Example
///
/// ```
/// use bytes::{Bytes, BytesMut};
/// use tokio::stream;
/// use tokio::io::Result;
/// use tokio_util::io::{StreamReader, poll_read_buf};
/// use futures::future::poll_fn;
/// use std::pin::Pin;
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
///
/// // Create a reader from an iterator. This particular reader will always be
/// // ready.
/// let mut read = StreamReader::new(stream::iter(vec![Result::Ok(Bytes::from_static(&[0, 1, 2, 3]))]));
///
/// let mut buf = BytesMut::new();
/// let mut reads = 0;
///
/// loop {
/// reads += 1;
/// let n = poll_fn(|cx| poll_read_buf(Pin::new(&mut read), cx, &mut buf)).await?;
///
/// if n == 0 {
/// break;
/// }
/// }
///
/// // one or more reads might be necessary.
/// assert!(reads >= 1);
/// assert_eq!(&buf[..], &[0, 1, 2, 3]);
/// # Ok(())
/// # }
/// ```
#[cfg_attr(not(feature = "io"), allow(unreachable_pub))]
pub fn poll_read_buf<T: AsyncRead, B: BufMut>(
io: Pin<&mut T>,
cx: &mut Context<'_>,
buf: &mut B,
) -> Poll<io::Result<usize>> {
if !buf.has_remaining_mut() {
return Poll::Ready(Ok(0));
}
let n = {
let dst = buf.bytes_mut();
let dst = unsafe { &mut *(dst as *mut _ as *mut [MaybeUninit<u8>]) };
let mut buf = ReadBuf::uninit(dst);
let ptr = buf.filled().as_ptr();
ready!(io.poll_read(cx, &mut buf)?);
// Ensure the pointer does not change from under us
assert_eq!(ptr, buf.filled().as_ptr());
buf.filled().len()
};
// Safety: This is guaranteed to be the number of initialized (and read)
// bytes due to the invariants provided by `ReadBuf::filled`.
unsafe {
buf.advance_mut(n);
}
Poll::Ready(Ok(n))
}
/// Try to write data from an implementer of the [`Buf`] trait to an
/// [`AsyncWrite`], advancing the buffer's internal cursor.
///
/// This function will use [vectored writes] when the [`AsyncWrite`] supports
/// vectored writes.
///
/// # Examples
///
/// [`File`] implements [`AsyncWrite`] and [`Cursor<&[u8]>`] implements
/// [`Buf`]:
///
/// ```no_run
/// use tokio_util::io::poll_write_buf;
/// use tokio::io;
/// use tokio::fs::File;
///
/// use bytes::Buf;
/// use std::io::Cursor;
/// use std::pin::Pin;
/// use futures::future::poll_fn;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut file = File::create("foo.txt").await?;
/// let mut buf = Cursor::new(b"data to write");
///
/// // Loop until the entire contents of the buffer are written to
/// // the file.
/// while buf.has_remaining() {
/// poll_fn(|cx| poll_write_buf(Pin::new(&mut file), cx, &mut buf)).await?;
/// }
///
/// Ok(())
/// }
/// ```
///
/// [`Buf`]: bytes::Buf
/// [`AsyncWrite`]: tokio::io::AsyncWrite
/// [`File`]: tokio::fs::File
/// [vectored writes]: tokio::io::AsyncWrite::poll_write_vectored
#[cfg_attr(not(feature = "io"), allow(unreachable_pub))]
pub fn poll_write_buf<T: AsyncWrite, B: Buf>(
io: Pin<&mut T>,
cx: &mut Context<'_>,
buf: &mut B,
) -> Poll<io::Result<usize>> {
const MAX_BUFS: usize = 64;
if !buf.has_remaining() {
return Poll::Ready(Ok(0));
}
let n = if io.is_write_vectored() {
let mut slices = [IoSlice::new(&[]); MAX_BUFS];
let cnt = buf.bytes_vectored(&mut slices);
ready!(io.poll_write_vectored(cx, &slices[..cnt]))?
} else {
ready!(io.poll_write(cx, buf.bytes()))?
};
buf.advance(n);
Poll::Ready(Ok(n))
}
}
+1
View File
@@ -0,0 +1 @@
pub(crate) use std::sync;
@@ -3,7 +3,7 @@
use crate::loom::sync::atomic::AtomicUsize;
use crate::loom::sync::Mutex;
use crate::util::intrusive_double_linked_list::{LinkedList, ListNode};
use crate::sync::intrusive_double_linked_list::{LinkedList, ListNode};
use core::future::Future;
use core::pin::Pin;
@@ -37,14 +37,14 @@ use core::task::{Context, Poll, Waker};
/// // The token was cancelled
/// 5
/// }
/// _ = tokio::time::delay_for(std::time::Duration::from_secs(9999)) => {
/// _ = tokio::time::sleep(std::time::Duration::from_secs(9999)) => {
/// 99
/// }
/// }
/// });
///
/// tokio::spawn(async move {
/// tokio::time::delay_for(std::time::Duration::from_millis(10)).await;
/// tokio::time::sleep(std::time::Duration::from_millis(10)).await;
/// token.cancel();
/// });
///
@@ -129,6 +129,12 @@ impl Drop for CancellationToken {
}
}
impl Default for CancellationToken {
fn default() -> CancellationToken {
CancellationToken::new()
}
}
impl CancellationToken {
/// Creates a new CancellationToken in the non-cancelled state.
pub fn new() -> CancellationToken {
@@ -179,14 +185,14 @@ impl CancellationToken {
/// // The token was cancelled
/// 5
/// }
/// _ = tokio::time::delay_for(std::time::Duration::from_secs(9999)) => {
/// _ = tokio::time::sleep(std::time::Duration::from_secs(9999)) => {
/// 99
/// }
/// }
/// });
///
/// tokio::spawn(async move {
/// tokio::time::delay_for(std::time::Duration::from_millis(10)).await;
/// tokio::time::sleep(std::time::Duration::from_millis(10)).await;
/// token.cancel();
/// });
///
@@ -618,7 +624,7 @@ impl CancellationTokenState {
if removed_child {
// If the token removed itself from the parents list, it can reset
// the the parent ref status. If it is isn't able to do so, because the
// the parent ref status. If it is isn't able to do so, because the
// parent removed it from the list, there is no need to do this.
// The parent ref acts as as another reference count. Therefore
// removing this reference can free the object.
+6
View File
@@ -0,0 +1,6 @@
//! Synchronization primitives
mod cancellation_token;
pub use cancellation_token::{CancellationToken, WaitForCancellationFuture};
mod intrusive_double_linked_list;
+1
View File
@@ -0,0 +1 @@
@@ -5,14 +5,16 @@
//! [`DelayQueue`]: struct@DelayQueue
use crate::time::wheel::{self, Wheel};
use crate::time::{delay_until, Delay, Duration, Error, Instant};
use futures_core::ready;
use tokio::time::{error::Error, sleep_until, Duration, Instant, Sleep};
use slab::Slab;
use std::cmp;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{self, Poll};
use std::task::{self, Poll, Waker};
/// A queue of delayed elements.
///
@@ -50,8 +52,8 @@ use std::task::{self, Poll};
///
/// # Implementation
///
/// The [`DelayQueue`] is backed by a separate instance of the same timer wheel used internally by
/// Tokio's standalone timer utilities such as [`delay_for`]. Because of this, it offers the same
/// The [`DelayQueue`] is backed by a separate instance of a timer wheel similar to that used internally
/// by Tokio's standalone timer utilities such as [`sleep`]. Because of this, it offers the same
/// performance and scalability benefits.
///
/// State associated with each entry is stored in a [`slab`]. This amortizes the cost of allocation,
@@ -65,7 +67,8 @@ use std::task::{self, Poll};
/// Using `DelayQueue` to manage cache entries.
///
/// ```rust,no_run
/// use tokio::time::{delay_queue, DelayQueue, Error};
/// use tokio::time::error::Error;
/// use tokio_util::time::{DelayQueue, delay_queue};
///
/// use futures::ready;
/// use std::collections::HashMap;
@@ -118,7 +121,7 @@ use std::task::{self, Poll};
/// [`poll_expired`]: method@Self::poll_expired
/// [`Stream::poll_expired`]: method@Self::poll_expired
/// [`DelayQueue`]: struct@DelayQueue
/// [`delay_for`]: fn@super::delay_for
/// [`sleep`]: fn@tokio::time::sleep
/// [`slab`]: slab
/// [`capacity`]: method@Self::capacity
/// [`reserve`]: method@Self::reserve
@@ -135,13 +138,18 @@ pub struct DelayQueue<T> {
expired: Stack<T>,
/// Delay expiring when the *first* item in the queue expires
delay: Option<Delay>,
delay: Option<Sleep>,
/// Wheel polling state
poll: wheel::Poll,
wheel_now: u64,
/// Instant at which the timer starts
start: Instant,
/// Waker that is invoked when we potentially need to reset the timer.
/// Because we lazily create the timer when the first entry is created, we
/// need to awaken any poller that polled us before that point.
waker: Option<Waker>,
}
/// An entry in `DelayQueue` that has expired and removed.
@@ -210,7 +218,7 @@ impl<T> DelayQueue<T> {
/// # Examples
///
/// ```rust
/// # use tokio::time::DelayQueue;
/// # use tokio_util::time::DelayQueue;
/// let delay_queue: DelayQueue<u32> = DelayQueue::new();
/// ```
pub fn new() -> DelayQueue<T> {
@@ -226,7 +234,7 @@ impl<T> DelayQueue<T> {
/// # Examples
///
/// ```rust
/// # use tokio::time::DelayQueue;
/// # use tokio_util::time::DelayQueue;
/// # use std::time::Duration;
///
/// # #[tokio::main]
@@ -248,8 +256,9 @@ impl<T> DelayQueue<T> {
slab: Slab::with_capacity(capacity),
expired: Stack::default(),
delay: None,
poll: wheel::Poll::new(0),
wheel_now: 0,
start: Instant::now(),
waker: None,
}
}
@@ -281,7 +290,8 @@ impl<T> DelayQueue<T> {
/// Basic usage
///
/// ```rust
/// use tokio::time::{DelayQueue, Duration, Instant};
/// use tokio::time::{Duration, Instant};
/// use tokio_util::time::DelayQueue;
///
/// # #[tokio::main]
/// # async fn main() {
@@ -326,11 +336,15 @@ impl<T> DelayQueue<T> {
};
if should_set_delay {
if let Some(waker) = self.waker.take() {
waker.wake();
}
let delay_time = self.start + Duration::from_millis(when);
if let Some(ref mut delay) = &mut self.delay {
delay.reset(delay_time);
} else {
self.delay = Some(delay_until(delay_time));
self.delay = Some(sleep_until(delay_time));
}
}
@@ -344,6 +358,15 @@ impl<T> DelayQueue<T> {
&mut self,
cx: &mut task::Context<'_>,
) -> Poll<Option<Result<Expired<T>, Error>>> {
if !self
.waker
.as_ref()
.map(|w| w.will_wake(cx.waker()))
.unwrap_or(false)
{
self.waker = Some(cx.waker().clone());
}
let item = ready!(self.poll_idx(cx));
Poll::Ready(item.map(|result| {
result.map(|idx| {
@@ -366,30 +389,32 @@ impl<T> DelayQueue<T> {
/// This function is identical to `insert_at`, but takes a `Duration`
/// instead of an `Instant`.
///
/// `value` is stored in the queue until `when` is reached. At which point,
/// `value` will be returned from [`poll_expired`]. If `when` has already been
/// reached, then `value` is immediately made available to poll.
/// `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
/// zero, 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
/// `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
/// a different item in the queue.
/// 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 `timeout` has elapsed
/// or by calling [`remove`]. At this point, the caller must not
/// use the returned [`Key`] again as it may reference a different
/// item in the queue.
///
/// See [type] level documentation for more details.
///
/// # Panics
///
/// This function panics if `timeout` is greater than the maximum supported
/// duration.
/// This function panics if `timeout` is greater than the maximum
/// duration supported by the timer in the current `Runtime`.
///
/// # Examples
///
/// Basic usage
///
/// ```rust
/// use tokio::time::DelayQueue;
/// use tokio_util::time::DelayQueue;
/// use std::time::Duration;
///
/// # #[tokio::main]
@@ -458,7 +483,7 @@ impl<T> DelayQueue<T> {
/// Basic usage
///
/// ```rust
/// use tokio::time::DelayQueue;
/// use tokio_util::time::DelayQueue;
/// use std::time::Duration;
///
/// # #[tokio::main]
@@ -501,7 +526,8 @@ impl<T> DelayQueue<T> {
/// Basic usage
///
/// ```rust
/// use tokio::time::{DelayQueue, Duration, Instant};
/// use tokio::time::{Duration, Instant};
/// use tokio_util::time::DelayQueue;
///
/// # #[tokio::main]
/// # async fn main() {
@@ -526,6 +552,7 @@ impl<T> DelayQueue<T> {
let next_deadline = self.next_deadline();
if let (Some(ref mut delay), Some(deadline)) = (&mut self.delay, next_deadline) {
// This should awaken us if necessary (ie, if already expired)
delay.reset(deadline);
}
}
@@ -557,7 +584,7 @@ impl<T> DelayQueue<T> {
/// Basic usage
///
/// ```rust
/// use tokio::time::DelayQueue;
/// use tokio_util::time::DelayQueue;
/// use std::time::Duration;
///
/// # #[tokio::main]
@@ -587,7 +614,7 @@ impl<T> DelayQueue<T> {
/// # Examples
///
/// ```rust
/// use tokio::time::DelayQueue;
/// use tokio_util::time::DelayQueue;
/// use std::time::Duration;
///
/// # #[tokio::main]
@@ -615,7 +642,7 @@ impl<T> DelayQueue<T> {
/// # Examples
///
/// ```rust
/// use tokio::time::DelayQueue;
/// use tokio_util::time::DelayQueue;
///
/// let delay_queue: DelayQueue<i32> = DelayQueue::with_capacity(10);
/// assert_eq!(delay_queue.capacity(), 10);
@@ -629,7 +656,7 @@ impl<T> DelayQueue<T> {
/// # Examples
///
/// ```rust
/// use tokio::time::DelayQueue;
/// use tokio_util::time::DelayQueue;
/// use std::time::Duration;
///
/// # #[tokio::main]
@@ -664,7 +691,7 @@ impl<T> DelayQueue<T> {
/// # Examples
///
/// ```
/// use tokio::time::DelayQueue;
/// use tokio_util::time::DelayQueue;
/// use std::time::Duration;
///
/// # #[tokio::main]
@@ -689,7 +716,7 @@ impl<T> DelayQueue<T> {
/// # Examples
///
/// ```
/// use tokio::time::DelayQueue;
/// use tokio_util::time::DelayQueue;
/// use std::time::Duration;
///
/// # #[tokio::main]
@@ -726,13 +753,13 @@ impl<T> DelayQueue<T> {
let now = crate::time::ms(delay.deadline() - self.start, crate::time::Round::Down);
self.poll = wheel::Poll::new(now);
self.wheel_now = now;
}
// We poll the wheel to get the next value out before finding the next deadline.
let wheel_idx = self.wheel.poll(&mut self.poll, &mut self.slab);
let wheel_idx = self.wheel.poll(self.wheel_now, &mut self.slab);
self.delay = self.next_deadline().map(delay_until);
self.delay = self.next_deadline().map(sleep_until);
if let Some(idx) = wheel_idx {
return Poll::Ready(Some(Ok(idx)));
@@ -764,7 +791,6 @@ impl<T> Default for DelayQueue<T> {
}
}
#[cfg(feature = "stream")]
impl<T> futures_core::Stream for DelayQueue<T> {
// DelayQueue seems much more specific, where a user may care that it
// has reached capacity, so return those errors instead of panicking.
+47
View File
@@ -0,0 +1,47 @@
//! Additional utilities for tracking time.
//!
//! This module provides additional utilities for executing code after a set period
//! of time. Currently there is only one:
//!
//! * `DelayQueue`: A queue where items are returned once the requested delay
//! has expired.
//!
//! This type must be used from within the context of the `Runtime`.
use std::time::Duration;
mod wheel;
#[doc(inline)]
pub mod delay_queue;
pub use delay_queue::DelayQueue;
// ===== Internal utils =====
enum Round {
Up,
Down,
}
/// Convert a `Duration` to milliseconds, rounding up and saturating at
/// `u64::MAX`.
///
/// The saturating is fine because `u64::MAX` milliseconds are still many
/// million years.
#[inline]
fn ms(duration: Duration, round: Round) -> u64 {
const NANOS_PER_MILLI: u32 = 1_000_000;
const MILLIS_PER_SEC: u64 = 1_000;
// Round up.
let millis = match round {
Round::Up => (duration.subsec_nanos() + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI,
Round::Down => duration.subsec_millis(),
};
duration
.as_secs()
.saturating_mul(MILLIS_PER_SEC)
.saturating_add(u64::from(millis))
}
@@ -51,13 +51,6 @@ pub(crate) enum InsertError {
Invalid,
}
/// Poll expirations from the wheel
#[derive(Debug, Default)]
pub(crate) struct Poll {
now: u64,
expiration: Option<Expiration>,
}
impl<T> Wheel<T>
where
T: Stack,
@@ -136,19 +129,18 @@ where
self.next_expiration().map(|expiration| expiration.deadline)
}
pub(crate) fn poll(&mut self, poll: &mut Poll, store: &mut T::Store) -> Option<T::Owned> {
/// Advances the timer up to the instant represented by `now`.
pub(crate) fn poll(&mut self, now: u64, store: &mut T::Store) -> Option<T::Owned> {
loop {
if poll.expiration.is_none() {
poll.expiration = self.next_expiration().and_then(|expiration| {
if expiration.deadline > poll.now {
None
} else {
Some(expiration)
}
});
}
let expiration = self.next_expiration().and_then(|expiration| {
if expiration.deadline > now {
None
} else {
Some(expiration)
}
});
match poll.expiration {
match expiration {
Some(ref expiration) => {
if let Some(item) = self.poll_expiration(expiration, store) {
return Some(item);
@@ -157,12 +149,14 @@ where
self.set_elapsed(expiration.deadline);
}
None => {
self.set_elapsed(poll.now);
// in this case the poll did not indicate an expiration
// _and_ we were not able to find a next expiration in
// the current list of timers. advance to the poll's
// current time and do nothing else.
self.set_elapsed(now);
return None;
}
}
poll.expiration = None;
}
}
@@ -197,6 +191,10 @@ where
res
}
/// iteratively find entries that are between the wheel's current
/// time and the expiration time. for each in that population either
/// return it for notification (in the case of the last level) or tier
/// it down to the next level (in all other cases).
pub(crate) fn poll_expiration(
&mut self,
expiration: &Expiration,
@@ -251,15 +249,6 @@ fn level_for(elapsed: u64, when: u64) -> usize {
significant / 6
}
impl Poll {
pub(crate) fn new(now: u64) -> Poll {
Poll {
now,
expiration: None,
}
}
}
#[cfg(all(test, not(loom)))]
mod test {
use super::*;
+49 -17
View File
@@ -1,17 +1,16 @@
use crate::codec::{Decoder, Encoder};
use tokio::{net::UdpSocket, stream::Stream};
use tokio::{io::ReadBuf, net::UdpSocket, stream::Stream};
use bytes::{BufMut, BytesMut};
use futures_core::ready;
use futures_sink::Sink;
use std::io;
use std::mem::MaybeUninit;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{io, mem::MaybeUninit};
/// A unified `Stream` and `Sink` interface to an underlying `UdpSocket`, using
/// A unified [`Stream`] and [`Sink`] interface to an underlying `UdpSocket`, using
/// the `Encoder` and `Decoder` traits to encode and decode frames.
///
/// Raw UDP sockets work with datagrams, but higher-level code usually wants to
@@ -20,13 +19,17 @@ use std::task::{Context, Poll};
/// handle encoding and decoding of messages frames. Note that the incoming and
/// outgoing frame types may be distinct.
///
/// This function returns a *single* object that is both `Stream` and `Sink`;
/// This function returns a *single* object that is both [`Stream`] and [`Sink`];
/// grouping this into a single object is often useful for layering things which
/// require both read and write access to the underlying object.
///
/// If you want to work more directly with the streams and sink, consider
/// calling `split` on the `UdpFramed` returned by this method, which will break
/// calling [`split`] on the `UdpFramed` returned by this method, which will break
/// them into separate objects, allowing them to interact more easily.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`split`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.split
#[must_use = "sinks do nothing unless polled"]
#[cfg_attr(docsrs, doc(all(feature = "codec", feature = "udp")))]
#[derive(Debug)]
@@ -41,6 +44,9 @@ pub struct UdpFramed<C> {
current_addr: Option<SocketAddr>,
}
const INITIAL_RD_CAPACITY: usize = 64 * 1024;
const INITIAL_WR_CAPACITY: usize = 8 * 1024;
impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
type Item = Result<(C::Item, SocketAddr), C::Error>;
@@ -50,7 +56,7 @@ impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
pin.rd.reserve(INITIAL_RD_CAPACITY);
loop {
// Are there are still bytes left in the read buffer to decode?
// Are there still bytes left in the read buffer to decode?
if pin.is_readable {
if let Some(frame) = pin.codec.decode_eof(&mut pin.rd)? {
let current_addr = pin
@@ -69,13 +75,14 @@ impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
let addr = unsafe {
// Convert `&mut [MaybeUnit<u8>]` to `&mut [u8]` because we will be
// writing to it via `poll_recv_from` and therefore initializing the memory.
let buf: &mut [u8] =
&mut *(pin.rd.bytes_mut() as *mut [MaybeUninit<u8>] as *mut [u8]);
let buf = &mut *(pin.rd.bytes_mut() as *mut _ as *mut [MaybeUninit<u8>]);
let mut read = ReadBuf::uninit(buf);
let ptr = read.filled().as_ptr();
let res = ready!(Pin::new(&mut pin.socket).poll_recv_from(cx, &mut read));
let res = ready!(Pin::new(&mut pin.socket).poll_recv_from(cx, buf));
let (n, addr) = res?;
pin.rd.advance_mut(n);
assert_eq!(ptr, read.filled().as_ptr());
let addr = res?;
pin.rd.advance_mut(read.filled().len());
addr
};
@@ -148,15 +155,12 @@ impl<I, C: Encoder<I> + Unpin> Sink<(I, SocketAddr)> for UdpFramed<C> {
}
}
const INITIAL_RD_CAPACITY: usize = 64 * 1024;
const INITIAL_WR_CAPACITY: usize = 8 * 1024;
impl<C> UdpFramed<C> {
/// Create a new `UdpFramed` backed by the given socket and codec.
///
/// See struct level documentation for more details.
pub fn new(socket: UdpSocket, codec: C) -> UdpFramed<C> {
UdpFramed {
Self {
socket,
codec,
out_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0)),
@@ -195,4 +199,32 @@ impl<C> UdpFramed<C> {
pub fn into_inner(self) -> UdpSocket {
self.socket
}
/// Returns a reference to the underlying codec wrapped by
/// `Framed`.
///
/// Note that care should be taken to not tamper with the underlying codec
/// as it may corrupt the stream of frames otherwise being worked with.
pub fn codec(&self) -> &C {
&self.codec
}
/// Returns a mutable reference to the underlying codec wrapped by
/// `UdpFramed`.
///
/// Note that care should be taken to not tamper with the underlying codec
/// as it may corrupt the stream of frames otherwise being worked with.
pub fn codec_mut(&mut self) -> &mut C {
&mut self.codec
}
/// Returns a reference to the read buffer.
pub fn read_buffer(&self) -> &BytesMut {
&self.rd
}
/// Returns a mutable reference to the read buffer.
pub fn read_buffer_mut(&mut self) -> &mut BytesMut {
&mut self.rd
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
//! UDP framing
mod frame;
pub use self::frame::UdpFramed;
pub use frame::UdpFramed;
+24
View File
@@ -0,0 +1,24 @@
#![cfg(feature = "rt")]
#![warn(rust_2018_idioms)]
use tokio::runtime::Builder;
use tokio::time::*;
use tokio_util::context::RuntimeExt;
#[test]
fn tokio_context_with_another_runtime() {
let rt1 = Builder::new_multi_thread()
.worker_threads(1)
// no timer!
.build()
.unwrap();
let rt2 = Builder::new_multi_thread()
.worker_threads(1)
.enable_all()
.build()
.unwrap();
// Without the `HandleExt.wrap()` there would be a panic because there is
// no timer running, since it would be referencing runtime r1.
let _ = rt1.block_on(rt2.wrap(async move { sleep(Duration::from_millis(2)).await }));
}
+2 -2
View File
@@ -55,8 +55,8 @@ impl AsyncRead for DontReadIntoThis {
fn poll_read(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
_buf: &mut [u8],
) -> Poll<io::Result<usize>> {
_buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<io::Result<()>> {
unreachable!()
}
}
+10 -23
View File
@@ -1,6 +1,6 @@
#![warn(rust_2018_idioms)]
use tokio::io::AsyncRead;
use tokio::io::{AsyncRead, ReadBuf};
use tokio_test::assert_ready;
use tokio_test::task;
use tokio_util::codec::{Decoder, FramedRead};
@@ -185,8 +185,8 @@ fn read_partial_would_block_then_err() {
#[test]
fn huge_size() {
let mut task = task::spawn(());
let data = [0; 32 * 1024];
let mut framed = FramedRead::new(Slice(&data[..]), BigDecoder);
let data = &[0; 32 * 1024][..];
let mut framed = FramedRead::new(data, BigDecoder);
task.enter(|cx, _| {
assert_read!(pin!(framed).poll_next(cx), 0);
@@ -212,7 +212,7 @@ fn huge_size() {
#[test]
fn data_remaining_is_error() {
let mut task = task::spawn(());
let slice = Slice(&[0; 5]);
let slice = &[0; 5][..];
let mut framed = FramedRead::new(slice, U32Decoder);
task.enter(|cx, _| {
@@ -264,32 +264,19 @@ impl AsyncRead for Mock {
fn poll_read(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
use io::ErrorKind::WouldBlock;
match self.calls.pop_front() {
Some(Ok(data)) => {
debug_assert!(buf.len() >= data.len());
buf[..data.len()].copy_from_slice(&data[..]);
Ready(Ok(data.len()))
debug_assert!(buf.remaining() >= data.len());
buf.put_slice(&data);
Ready(Ok(()))
}
Some(Err(ref e)) if e.kind() == WouldBlock => Pending,
Some(Err(e)) => Ready(Err(e)),
None => Ready(Ok(0)),
None => Ready(Ok(())),
}
}
}
// TODO this newtype is necessary because `&[u8]` does not currently implement `AsyncRead`
struct Slice<'a>(&'a [u8]);
impl AsyncRead for Slice<'_> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.0).poll_read(cx, buf)
}
}
@@ -1,9 +1,8 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::AsyncRead;
use tokio::io::{AsyncRead, ReadBuf};
use tokio::stream::StreamExt;
/// produces at most `remaining` zeros, that returns error.
@@ -16,18 +15,19 @@ impl AsyncRead for Reader {
fn poll_read(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<std::io::Result<usize>> {
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
let this = Pin::into_inner(self);
assert_ne!(buf.len(), 0);
assert_ne!(buf.remaining(), 0);
if this.remaining > 0 {
let n = std::cmp::min(this.remaining, buf.len());
let n = std::cmp::min(this.remaining, buf.remaining());
let n = std::cmp::min(n, 31);
for x in &mut buf[..n] {
for x in &mut buf.initialize_unfilled_to(n)[..n] {
*x = 0;
}
buf.advance(n);
this.remaining -= n;
Poll::Ready(Ok(n))
Poll::Ready(Ok(()))
} else {
Poll::Ready(Err(std::io::Error::from_raw_os_error(22)))
}
@@ -37,11 +37,12 @@ impl AsyncRead for Reader {
#[tokio::test]
async fn correct_behavior_on_errors() {
let reader = Reader { remaining: 8000 };
let mut stream = tokio::io::reader_stream(reader);
let mut stream = tokio_util::io::ReaderStream::new(reader);
let mut zeros_received = 0;
let mut had_error = false;
loop {
let item = stream.next().await.unwrap();
println!("{:?}", item);
match item {
Ok(bytes) => {
let bytes = &*bytes;
@@ -1,14 +1,14 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]
use bytes::Bytes;
use tokio::io::{stream_reader, AsyncReadExt};
use tokio::io::AsyncReadExt;
use tokio::stream::iter;
use tokio_util::io::StreamReader;
#[tokio::test]
async fn test_stream_reader() -> std::io::Result<()> {
let stream = iter(vec![
Ok(Bytes::from_static(&[])),
std::io::Result::Ok(Bytes::from_static(&[])),
Ok(Bytes::from_static(&[0, 1, 2, 3])),
Ok(Bytes::from_static(&[])),
Ok(Bytes::from_static(&[4, 5, 6, 7])),
@@ -17,7 +17,7 @@ async fn test_stream_reader() -> std::io::Result<()> {
Ok(Bytes::from_static(&[])),
]);
let mut read = stream_reader(stream);
let mut read = StreamReader::new(stream);
let mut buf = [0; 5];
read.read_exact(&mut buf).await?;
+7 -7
View File
@@ -1,6 +1,6 @@
#![warn(rust_2018_idioms)]
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio_test::task;
use tokio_test::{
assert_err, assert_ok, assert_pending, assert_ready, assert_ready_err, assert_ready_ok,
@@ -707,18 +707,18 @@ impl AsyncRead for Mock {
fn poll_read(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
dst: &mut [u8],
) -> Poll<io::Result<usize>> {
dst: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
match self.calls.pop_front() {
Some(Ready(Ok(Op::Data(data)))) => {
debug_assert!(dst.len() >= data.len());
dst[..data.len()].copy_from_slice(&data[..]);
Ready(Ok(data.len()))
debug_assert!(dst.remaining() >= data.len());
dst.put_slice(&data);
Ready(Ok(()))
}
Some(Ready(Ok(_))) => panic!(),
Some(Ready(Err(e))) => Ready(Err(e)),
Some(Pending) => Pending,
None => Ready(Ok(0)),
None => Ready(Ok(())),
}
}
}
@@ -1,7 +1,7 @@
#![cfg(tokio_unstable)]
#![warn(rust_2018_idioms)]
use tokio::pin;
use tokio::sync::CancellationToken;
use tokio_util::sync::CancellationToken;
use core::future::Future;
use core::task::{Context, Poll};
@@ -186,8 +186,8 @@ fn drop_multiple_child_tokens() {
for drop_first_child_first in &[true, false] {
let token = CancellationToken::new();
let mut child_tokens = [None, None, None];
for i in 0..child_tokens.len() {
child_tokens[i] = Some(token.child_token());
for child in &mut child_tokens {
*child = Some(token.child_token());
}
assert!(!token.is_cancelled());
@@ -2,8 +2,9 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]
use tokio::time::{self, delay_for, DelayQueue, Duration, Instant};
use tokio::time::{self, sleep, sleep_until, Duration, Instant};
use tokio_test::{assert_ok, assert_pending, assert_ready, task};
use tokio_util::time::DelayQueue;
macro_rules! poll {
($queue:ident) => {
@@ -28,7 +29,7 @@ async fn single_immediate_delay() {
let _key = queue.insert_at("foo", Instant::now());
// Advance time by 1ms to handle thee rounding
delay_for(ms(1)).await;
sleep(ms(1)).await;
assert_ready_ok!(poll!(queue));
@@ -46,7 +47,7 @@ async fn multi_immediate_delays() {
let _k = queue.insert_at("2", Instant::now());
let _k = queue.insert_at("3", Instant::now());
delay_for(ms(1)).await;
sleep(ms(1)).await;
let mut res = vec![];
@@ -58,7 +59,7 @@ async fn multi_immediate_delays() {
let entry = assert_ready!(poll!(queue));
assert!(entry.is_none());
res.sort();
res.sort_unstable();
assert_eq!("1", res[0]);
assert_eq!("2", res[1]);
@@ -74,11 +75,11 @@ async fn single_short_delay() {
assert_pending!(poll!(queue));
delay_for(ms(1)).await;
sleep(ms(1)).await;
assert!(!queue.is_woken());
delay_for(ms(5)).await;
sleep(ms(5)).await;
assert!(queue.is_woken());
@@ -106,9 +107,10 @@ async fn multi_delay_at_start() {
assert_pending!(poll!(queue));
assert!(!queue.is_woken());
let start = Instant::now();
for elapsed in 0..1200 {
delay_for(ms(1)).await;
let elapsed = elapsed + 1;
tokio::time::sleep_until(start + ms(elapsed)).await;
if delays.contains(&elapsed) {
assert!(queue.is_woken());
@@ -116,7 +118,12 @@ async fn multi_delay_at_start() {
assert_pending!(poll!(queue));
} else if queue.is_woken() {
let cascade = &[192, 960];
assert!(cascade.contains(&elapsed), "elapsed={}", elapsed);
assert!(
cascade.contains(&elapsed),
"elapsed={} dt={:?}",
elapsed,
Instant::now() - start
);
assert_pending!(poll!(queue));
}
@@ -130,7 +137,7 @@ async fn insert_in_past_fires_immediately() {
let mut queue = task::spawn(DelayQueue::new());
let now = Instant::now();
delay_for(ms(10)).await;
sleep(ms(10)).await;
queue.insert_at("foo", now);
@@ -150,7 +157,7 @@ async fn remove_entry() {
let entry = queue.remove(&key);
assert_eq!(entry.into_inner(), "foo");
delay_for(ms(10)).await;
sleep(ms(10)).await;
let entry = assert_ready!(poll!(queue));
assert!(entry.is_none());
@@ -166,19 +173,19 @@ async fn reset_entry() {
let key = queue.insert_at("foo", now + ms(5));
assert_pending!(poll!(queue));
delay_for(ms(1)).await;
sleep(ms(1)).await;
queue.reset_at(&key, now + ms(10));
assert_pending!(poll!(queue));
delay_for(ms(7)).await;
sleep(ms(7)).await;
assert!(!queue.is_woken());
assert_pending!(poll!(queue));
delay_for(ms(3)).await;
sleep(ms(3)).await;
assert!(queue.is_woken());
@@ -197,16 +204,16 @@ async fn reset_much_later() {
let mut queue = task::spawn(DelayQueue::new());
let now = Instant::now();
delay_for(ms(1)).await;
sleep(ms(1)).await;
let key = queue.insert_at("foo", now + ms(200));
assert_pending!(poll!(queue));
delay_for(ms(3)).await;
sleep(ms(3)).await;
queue.reset_at(&key, now + ms(5));
queue.reset_at(&key, now + ms(10));
delay_for(ms(20)).await;
sleep(ms(20)).await;
assert!(queue.is_woken());
}
@@ -219,21 +226,21 @@ async fn reset_twice() {
let mut queue = task::spawn(DelayQueue::new());
let now = Instant::now();
delay_for(ms(1)).await;
sleep(ms(1)).await;
let key = queue.insert_at("foo", now + ms(200));
assert_pending!(poll!(queue));
delay_for(ms(3)).await;
sleep(ms(3)).await;
queue.reset_at(&key, now + ms(50));
delay_for(ms(20)).await;
sleep(ms(20)).await;
queue.reset_at(&key, now + ms(40));
delay_for(ms(20)).await;
sleep(ms(20)).await;
assert!(queue.is_woken());
}
@@ -246,7 +253,7 @@ async fn remove_expired_item() {
let now = Instant::now();
delay_for(ms(10)).await;
sleep(ms(10)).await;
let key = queue.insert_at("foo", now);
@@ -272,7 +279,7 @@ async fn expires_before_last_insert() {
assert_pending!(poll!(queue));
delay_for(ms(600)).await;
sleep(ms(600)).await;
assert!(queue.is_woken());
@@ -297,18 +304,18 @@ async fn multi_reset() {
queue.reset_at(&two, now + ms(350));
queue.reset_at(&one, now + ms(400));
delay_for(ms(310)).await;
sleep(ms(310)).await;
assert_pending!(poll!(queue));
delay_for(ms(50)).await;
sleep(ms(50)).await;
let entry = assert_ready_ok!(poll!(queue));
assert_eq!(*entry.get_ref(), "two");
assert_pending!(poll!(queue));
delay_for(ms(50)).await;
sleep(ms(50)).await;
let entry = assert_ready_ok!(poll!(queue));
assert_eq!(*entry.get_ref(), "one");
@@ -332,7 +339,7 @@ async fn expire_first_key_when_reset_to_expire_earlier() {
queue.reset_at(&one, now + ms(100));
delay_for(ms(100)).await;
sleep(ms(100)).await;
assert!(queue.is_woken());
@@ -355,7 +362,7 @@ async fn expire_second_key_when_reset_to_expire_earlier() {
queue.reset_at(&two, now + ms(100));
delay_for(ms(100)).await;
sleep(ms(100)).await;
assert!(queue.is_woken());
@@ -377,7 +384,7 @@ async fn reset_first_expiring_item_to_expire_later() {
assert_pending!(poll!(queue));
queue.reset_at(&one, now + ms(300));
delay_for(ms(250)).await;
sleep(ms(250)).await;
assert!(queue.is_woken());
@@ -399,11 +406,11 @@ async fn insert_before_first_after_poll() {
let _two = queue.insert_at("two", now + ms(100));
delay_for(ms(99)).await;
sleep(ms(99)).await;
assert!(!queue.is_woken());
assert_pending!(poll!(queue));
delay_for(ms(1)).await;
sleep(ms(1)).await;
assert!(queue.is_woken());
@@ -425,7 +432,7 @@ async fn insert_after_ready_poll() {
assert_pending!(poll!(queue));
delay_for(ms(100)).await;
sleep(ms(100)).await;
assert!(queue.is_woken());
@@ -437,7 +444,7 @@ async fn insert_after_ready_poll() {
queue.insert_at("foo", now + ms(500));
}
res.sort();
res.sort_unstable();
assert_eq!("1", res[0]);
assert_eq!("2", res[1]);
@@ -456,7 +463,7 @@ async fn reset_later_after_slot_starts() {
assert_pending!(poll!(queue));
delay_for(ms(80)).await;
sleep_until(now + Duration::from_millis(80)).await;
assert!(!queue.is_woken());
@@ -471,10 +478,10 @@ async fn reset_later_after_slot_starts() {
assert_pending!(poll!(queue));
delay_for(ms(39)).await;
sleep_until(now + Duration::from_millis(119)).await;
assert!(!queue.is_woken());
delay_for(ms(1)).await;
sleep(ms(1)).await;
assert!(queue.is_woken());
let entry = assert_ready_ok!(poll!(queue)).into_inner();
@@ -494,7 +501,7 @@ async fn reset_inserted_expired() {
assert_eq!(1, queue.len());
delay_for(ms(200)).await;
sleep(ms(200)).await;
let entry = assert_ready_ok!(poll!(queue)).into_inner();
assert_eq!(entry, "foo");
@@ -514,7 +521,7 @@ async fn reset_earlier_after_slot_starts() {
assert_pending!(poll!(queue));
delay_for(ms(80)).await;
sleep_until(now + Duration::from_millis(80)).await;
assert!(!queue.is_woken());
@@ -529,10 +536,10 @@ async fn reset_earlier_after_slot_starts() {
assert_pending!(poll!(queue));
delay_for(ms(39)).await;
sleep_until(now + Duration::from_millis(119)).await;
assert!(!queue.is_woken());
delay_for(ms(1)).await;
sleep(ms(1)).await;
assert!(queue.is_woken());
let entry = assert_ready_ok!(poll!(queue)).into_inner();
@@ -551,7 +558,7 @@ async fn insert_in_past_after_poll_fires_immediately() {
assert_pending!(poll!(queue));
delay_for(ms(80)).await;
sleep(ms(80)).await;
assert!(!queue.is_woken());
queue.insert_at("bar", now + ms(40));
+2
View File
@@ -1,3 +1,5 @@
#![warn(rust_2018_idioms)]
use tokio::{net::UdpSocket, stream::StreamExt};
use tokio_util::codec::{Decoder, Encoder, LinesCodec};
use tokio_util::udp::UdpFramed;
+141
View File
@@ -1,3 +1,144 @@
# 0.3.5 (November 30, 2020)
### Fixed
- rt: fix `shutdown_timeout(0)` (#3196).
- time: fixed race condition with small sleeps (#3069).
### Added
- io: `AsyncFd::with_interest()` (#3167).
- signal: `CtrlC` stream on windows (#3186).
# 0.3.4 (November 18, 2020)
### Fixed
- stream: `StreamMap` `Default` impl bound (#3093).
- io: `AsyncFd::into_inner()` should deregister the FD (#3104).
### Changed
- meta: `parking_lot` feature enabled with `full` (#3119).
### Added
- io: `AsyncWrite` vectored writes (#3149).
- net: TCP/UDP readiness and non-blocking ops (#3130, #2743, #3138).
- net: TCP socket option (linger, send/recv buf size) (#3145, #3143).
- net: PID field in `UCred` with solaris/illumos (#3085).
- rt: `runtime::Handle` allows spawning onto a runtime (#3079).
- sync: `Notify::notify_waiters()` (#3098).
- sync: `acquire_many()`, `try_acquire_many()` to `Semaphore` (#3067).
# 0.3.3 (November 2, 2020)
Fixes a soundness hole by adding a missing `Send` bound to
`Runtime::spawn_blocking()`.
### Fixed
- rt: include missing `Send`, fixing soundness hole (#3089).
- tracing: avoid huge trace span names (#3074).
### Added
- net: `TcpSocket::reuseport()`, `TcpSocket::set_reuseport()` (#3083).
- net: `TcpSocket::reuseaddr()` (#3093).
- net: `TcpSocket::local_addr()` (#3093).
- net: add pid to `UCred` (#2633).
# 0.3.2 (October 27, 2020)
Adds `AsyncFd` as a replacement for v0.2's `PollEvented`.
### Fixed
- io: fix a potential deadlock when shutting down the I/O driver (#2903).
- sync: `RwLockWriteGuard::downgrade()` bug (#2957).
### Added
- io: `AsyncFd` for receiving readiness events on raw FDs (#2903).
- net: `poll_*` function on `UdpSocket` (#2981).
- net: `UdpSocket::take_error()` (#3051).
- sync: `oneshot::Sender::poll_closed()` (#3032).
# 0.3.1 (October 21, 2020)
This release fixes an use-after-free in the IO driver. Additionally, the `read_buf`
and `write_buf` methods have been added back to the IO traits, as the bytes crate
is now on track to reach version 1.0 together with Tokio.
### Fixed
- net: fix use-after-free (#3019).
- fs: ensure buffered data is written on shutdown (#3009).
### Added
- io: `copy_buf()` (#2884).
- io: `AsyncReadExt::read_buf()`, `AsyncReadExt::write_buf()` for working with
`Buf`/`BufMut` (#3003).
- rt: `Runtime::spawn_blocking()` (#2980).
- sync: `watch::Sender::is_closed()` (#2991).
# 0.3.0 (October 15, 2020)
This represents a 1.0 beta release. APIs are polished and future-proofed. APIs
not included for 1.0 stabilization have been removed.
Biggest changes are:
- I/O driver internal rewrite. The windows implementation includes significant
changes.
- Runtime API is polished, especially with how it interacts with feature flag
combinations.
- Feature flags are simplified
- `rt-core` and `rt-util` are combined to `rt`
- `rt-threaded` is renamed to `rt-multi-thread` to match builder API
- `tcp`, `udp`, `uds`, `dns` are combied to `net`.
- `parking_lot` is included with `full`
### Changes
- meta: Minimum supported Rust version is now 1.45.
- io: `AsyncRead` trait now takes `ReadBuf` in order to safely handle reading
into uninitialized memory (#2758).
- io: Internal I/O driver storage is now able to compact (#2757).
- rt: `Runtime::block_on` now takes `&self` (#2782).
- sync: `watch` reworked to decouple receiving a change notification from
receiving the value (#2814, #2806).
- sync: `Notify::notify` is renamed to `notify_one` (#2822).
- process: `Child::kill` is now an `async fn` that cleans zombies (#2823).
- sync: use `const fn` constructors as possible (#2833, #2790)
- signal: reduce cross-thread notification (#2835).
- net: tcp,udp,uds types support operations with `&self` (#2828, #2919, #2934).
- sync: blocking `mpsc` channel supports `send` with `&self` (#2861).
- time: rename `delay_for` and `delay_until` to `sleep` and `sleep_until` (#2826).
- io: upgrade to `mio` 0.7 (#2893).
- io: `AsyncSeek` trait is tweaked (#2885).
- fs: `File` operations take `&self` (#2930).
- rt: runtime API, and `#[tokio::main]` macro polish (#2876)
- rt: `Runtime::enter` uses an RAII guard instead of a closure (#2954).
- net: the `from_std` function on all sockets no longer sets socket into non-blocking mode (#2893)
### Added
- sync: `map` function to lock guards (#2445).
- sync: `blocking_recv` and `blocking_send` fns to `mpsc` for use outside of Tokio (#2685).
- rt: `Builder::thread_name_fn` for configuring thread names (#1921).
- fs: impl `FromRawFd` and `FromRawHandle` for `File` (#2792).
- process: `Child::wait` and `Child::try_wait` (#2796).
- rt: support configuring thread keep-alive duration (#2809).
- rt: `task::JoinHandle::abort` forcibly cancels a spawned task (#2474).
- sync: `RwLock` write guard to read guard downgrading (#2733).
- net: add `poll_*` functions that take `&self` to all net types (#2845)
- sync: `get_mut()` for `Mutex`, `RwLock` (#2856).
- sync: `mpsc::Sender::closed()` waits for `Receiver` half to close (#2840).
- sync: `mpsc::Sender::is_closed()` returns true if `Receiver` half is closed (#2726).
- stream: `iter` and `iter_mut` to `StreamMap` (#2890).
- net: implement `AsRawSocket` on windows (#2911).
- net: `TcpSocket` creates a socket without binding or listening (#2920).
### Removed
- io: vectored ops are removed from `AsyncRead`, `AsyncWrite` traits (#2882).
- io: `mio` is removed from the public API. `PollEvented` and` Registration` are
removed (#2893).
- io: remove `bytes` from public API. `Buf` and `BufMut` implementation are
removed (#2908).
- time: `DelayQueue` is moved to `tokio-util` (#2897).
### Fixed
- io: `stdout` and `stderr` buffering on windows (#2734).
# 0.2.22 (July 21, 2020)
### Fixes
+56 -58
View File
@@ -7,13 +7,13 @@ name = "tokio"
# - Cargo.toml
# - README.md
# - Update CHANGELOG.md.
# - Create "v0.2.x" git tag.
version = "0.2.22"
# - Create "v0.3.x" git tag.
version = "0.3.5"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
readme = "README.md"
documentation = "https://docs.rs/tokio/0.2.22/tokio/"
documentation = "https://docs.rs/tokio/0.3.5/tokio/"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
description = """
@@ -29,92 +29,88 @@ default = []
# enable everything
full = [
"blocking",
"dns",
"fs",
"io-driver",
"io-util",
"io-std",
"macros",
"net",
"parking_lot",
"process",
"rt-core",
"rt-util",
"rt-threaded",
"rt",
"rt-multi-thread",
"signal",
"stream",
"sync",
"time",
]
blocking = ["rt-core"]
dns = ["rt-core"]
fs = ["rt-core", "io-util"]
io-driver = ["mio", "lazy_static"]
io-util = ["memchr"]
fs = []
io-util = ["memchr", "bytes"]
# stdin, stdout, stderr
io-std = ["rt-core"]
io-std = []
macros = ["tokio-macros"]
net = ["dns", "tcp", "udp", "uds"]
process = [
"io-driver",
"libc",
"mio-named-pipes",
"signal",
"winapi/consoleapi",
"winapi/minwindef",
"winapi/threadpoollegacyapiset",
"winapi/winerror",
]
# Includes basic task execution capabilities
rt-core = ["slab"]
rt-util = []
rt-threaded = [
"num_cpus",
"rt-core",
]
signal = [
"io-driver",
net = [
"lazy_static",
"libc",
"mio-uds",
"mio/os-poll",
"mio/os-util",
"mio/tcp",
"mio/udp",
"mio/uds",
]
process = [
"bytes",
"lazy_static",
"libc",
"mio/os-poll",
"mio/os-util",
"mio/uds",
"signal-hook-registry",
"winapi/threadpoollegacyapiset",
]
# Includes basic task execution capabilities
rt = ["slab"]
rt-multi-thread = [
"num_cpus",
"rt",
]
signal = [
"lazy_static",
"libc",
"mio/os-poll",
"mio/uds",
"mio/os-util",
"signal-hook-registry",
"winapi/consoleapi",
"winapi/minwindef",
]
stream = ["futures-core"]
sync = ["fnv"]
sync = []
test-util = []
tcp = ["io-driver", "iovec"]
time = ["slab"]
udp = ["io-driver"]
uds = ["io-driver", "mio-uds", "libc"]
time = []
[dependencies]
tokio-macros = { version = "0.2.4", path = "../tokio-macros", optional = true }
tokio-macros = { version = "0.3.0", path = "../tokio-macros", optional = true }
bytes = "0.5.0"
pin-project-lite = "0.1.1"
pin-project-lite = "0.2.0"
# Everything else is optional...
fnv = { version = "1.0.6", optional = true }
bytes = { version = "0.6.0", optional = true }
futures-core = { version = "0.3.0", optional = true }
lazy_static = { version = "1.0.2", optional = true }
lazy_static = { version = "1.4.0", optional = true }
memchr = { version = "2.2", optional = true }
mio = { version = "0.6.20", optional = true }
iovec = { version = "0.1.4", optional = true }
mio = { version = "0.7.6", optional = true }
num_cpus = { version = "1.8.0", optional = true }
parking_lot = { version = "0.11.0", optional = true } # Not in full
slab = { version = "0.4.1", optional = true } # Backs `DelayQueue`
tracing = { version = "0.1.16", default-features = false, features = ["std"], optional = true } # Not in full
parking_lot = { version = "0.11.0", optional = true }
slab = { version = "0.4.1", optional = true }
tracing = { version = "0.1.21", default-features = false, features = ["std"], optional = true } # Not in full
[target.'cfg(unix)'.dependencies]
mio-uds = { version = "0.6.5", optional = true }
libc = { version = "0.2.42", optional = true }
signal-hook-registry = { version = "1.1.1", optional = true }
[target.'cfg(windows)'.dependencies]
mio-named-pipes = { version = "0.1.6", optional = true }
[target.'cfg(unix)'.dev-dependencies]
libc = { version = "0.2.42" }
nix = { version = "0.19.0" }
[target.'cfg(windows)'.dependencies.winapi]
version = "0.3.8"
@@ -122,15 +118,17 @@ default-features = false
optional = true
[dev-dependencies]
tokio-test = { version = "0.2.0", path = "../tokio-test" }
tokio-test = { version = "0.3.0", path = "../tokio-test" }
futures = { version = "0.3.0", features = ["async-await"] }
futures-test = "0.3.0"
proptest = "0.9.4"
proptest = "0.10.0"
tempfile = "3.1.0"
[target.'cfg(loom)'.dev-dependencies]
loom = { version = "0.3.5", features = ["futures", "checkpoint"] }
[build-dependencies]
autocfg = "1" # Needed for conditionally enabling `track-caller`
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
+4 -4
View File
@@ -14,15 +14,15 @@ the Rust programming language. It is:
[![Crates.io][crates-badge]][crates-url]
[![MIT licensed][mit-badge]][mit-url]
[![Build Status][azure-badge]][azure-url]
[![Build Status][actions-badge]][actions-url]
[![Discord chat][discord-badge]][discord-url]
[crates-badge]: https://img.shields.io/crates/v/tokio.svg
[crates-url]: https://crates.io/crates/tokio
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
[mit-url]: https://github.com/tokio-rs/tokio/blob/master/LICENSE
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.tokio?branchName=master
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=1&branchName=master
[actions-badge]: https://github.com/tokio-rs/tokio/workflows/CI/badge.svg
[actions-url]: https://github.com/tokio-rs/tokio/actions?query=workflow%3ACI+branch%3Amaster
[discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord&style=flat-square
[discord-url]: https://discord.gg/tokio
@@ -157,7 +157,7 @@ several other libraries, including:
## Supported Rust Versions
Tokio is built against the latest stable release. The minimum supported version is 1.39.
Tokio is built against the latest stable release. The minimum supported version is 1.45.
The current Tokio version is not guaranteed to build on Rust versions earlier than the
minimum supported version.
+22
View File
@@ -0,0 +1,22 @@
use autocfg::AutoCfg;
fn main() {
match AutoCfg::new() {
Ok(ac) => {
// The #[track_caller] attribute was stabilized in rustc 1.46.0.
if ac.probe_rustc_version(1, 46) {
autocfg::emit("tokio_track_caller")
}
}
Err(e) => {
// If we couldn't detect the compiler version and features, just
// print a warning. This isn't a fatal error: we can still build
// Tokio, we just can't enable cfgs automatically.
println!(
"cargo:warning=tokio: failed to detect compiler features: {}",
e
);
}
}
}
+276
View File
@@ -0,0 +1,276 @@
# Refactor I/O driver
Describes changes to the I/O driver for the Tokio 0.3 release.
## Goals
* Support `async fn` on I/O types with `&self`.
* Refine the `Registration` API.
### Non-goals
* Implement `AsyncRead` / `AsyncWrite` for `&TcpStream` or other reference type.
## Overview
Currently, I/O types require `&mut self` for `async` functions. The reason for
this is the task's waker is stored in the I/O resource's internal state
(`ScheduledIo`) instead of in the future returned by the `async` function.
Because of this limitation, I/O types limit the number of wakers to one per
direction (a direction is either read-related events or write-related events).
Moving the waker from the internal I/O resource's state to the operation's
future enables multiple wakers to be registered per operation. The "intrusive
wake list" strategy used by `Notify` applies to this case, though there are some
concerns unique to the I/O driver.
## Reworking the `Registration` type
While `Registration` is made private (per #2728), it remains in Tokio as an
implementation detail backing I/O resources such as `TcpStream`. The API of
`Registration` is updated to support waiting for an arbitrary interest set with
`&self`. This supports concurrent waiters with a different readiness interest.
```rust
struct Registration { ... }
// TODO: naming
struct ReadyEvent {
tick: u32,
ready: mio::Ready,
}
impl Registration {
/// `interest` must be a super set of **all** interest sets specified in
/// the other methods. This is the interest set passed to `mio`.
pub fn new<T>(io: &T, interest: mio::Ready) -> io::Result<Registration>
where T: mio::Evented;
/// Awaits for any readiness event included in `interest`. Returns a
/// `ReadyEvent` representing the received readiness event.
async fn readiness(&self, interest: mio::Ready) -> io::Result<ReadyEvent>;
/// Clears resource level readiness represented by the specified `ReadyEvent`
async fn clear_readiness(&self, ready_event: ReadyEvent);
```
A new registration is created for a `T: mio::Evented` and a `interest`. This
creates a `ScheduledIo` entry with the I/O driver and registers the resource
with `mio`.
Because Tokio uses **edge-triggered** notifications, the I/O driver only
receives readiness from the OS once the ready state **changes**. The I/O driver
must track each resource's known readiness state. This helps prevent syscalls
when the process knows the syscall should return with `EWOULDBLOCK`.
A call to `readiness()` checks if the currently known resource readiness
overlaps with `interest`. If it does, then the `readiness()` immediately
returns. If it does not, then the task waits until the I/O driver receives a
readiness event.
The pseudocode to perform a TCP read is as follows.
```rust
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
loop {
// Await readiness
let event = self.readiness(interest).await?;
match self.mio_socket.read(buf) {
Ok(v) => return Ok(v),
Err(ref e) if e.kind() == WouldBlock => {
self.clear_readiness(event);
}
Err(e) => return Err(e),
}
}
}
```
## Reworking the `ScheduledIo` type
The `ScheduledIo` type is switched to use an intrusive waker linked list. Each
entry in the linked list includes the `interest` set passed to `readiness()`.
```rust
#[derive(Debug)]
pub(crate) struct ScheduledIo {
/// Resource's known state packed with other state that must be
/// atomically updated.
readiness: AtomicUsize,
/// Tracks tasks waiting on the resource
waiters: Mutex<Waiters>,
}
#[derive(Debug)]
struct Waiters {
// List of intrusive waiters.
list: LinkedList<Waiter>,
/// Waiter used by `AsyncRead` implementations.
reader: Option<Waker>,
/// Waiter used by `AsyncWrite` implementations.
writer: Option<Waker>,
}
// This struct is contained by the **future** returned by `readiness()`.
#[derive(Debug)]
struct Waiter {
/// Intrusive linked-list pointers
pointers: linked_list::Pointers<Waiter>,
/// Waker for task waiting on I/O resource
waiter: Option<Waker>,
/// Readiness events being waited on. This is
/// the value passed to `readiness()`
interest: mio::Ready,
/// Should not be `Unpin`.
_p: PhantomPinned,
}
```
When an I/O event is received from `mio`, the associated resources' readiness is
updated and the waiter list is iterated. All waiters with `interest` that
overlap the received readiness event are notified. Any waiter with an `interest`
that does not overlap the readiness event remains in the list.
## Cancel interest on drop
The future returned by `readiness()` uses an intrusive linked list to store the
waker with `ScheduledIo`. Because `readiness()` can be called concurrently, many
wakers may be stored simultaneously in the list. If the `readiness()` future is
dropped early, it is essential that the waker is removed from the list. This
prevents leaking memory.
## Race condition
Consider how many tasks may concurrently attempt I/O operations. This, combined
with how Tokio uses edge-triggered events, can result in a race condition. Let's
revisit the TCP read function:
```rust
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
loop {
// Await readiness
let event = self.readiness(interest).await?;
match self.mio_socket.read(buf) {
Ok(v) => return Ok(v),
Err(ref e) if e.kind() == WouldBlock => {
self.clear_readiness(event);
}
Err(e) => return Err(e),
}
}
}
```
If care is not taken, if between `mio_socket.read(buf)` returning and
`clear_readiness(event)` is called, a readiness event arrives, the `read()`
function could deadlock. This happens because the readiness event is received,
`clear_readiness()` unsets the readiness event, and on the next iteration,
`readiness().await` will block forever as a new readiness event is not received.
The current I/O driver handles this condition by always registering the task's
waker before performing the operation. This is not ideal as it will result in
unnecessary task notification.
Instead, we will use a strategy to prevent clearing readiness if an "unseen"
readiness event has been received. The I/O driver will maintain a "tick" value.
Every time the `mio` `poll()` function is called, the tick is incremented. Each
readiness event has an associated tick. When the I/O driver sets the resource's
readiness, the driver's tick is packed into the atomic `usize`.
The `ScheduledIo` readiness `AtomicUsize` is structured as:
```
| reserved | generation | driver tick | readinesss |
|----------+------------+--------------+------------|
| 1 bit | 7 bits + 8 bits + 16 bits |
```
The `reserved` and `generation` components exist today.
The `readiness()` function returns a `ReadyEvent` value. This value includes the
`tick` component read with the resource's readiness value. When
`clear_readiness()` is called, the `ReadyEvent` is provided. Readiness is only
cleared if the current `tick` matches the `tick` included in the `ReadyEvent`.
If the tick values do not match, the call to `readiness()` on the next iteration
will not block and the new `tick` is included in the new `ReadyToken.`
TODO
## Implementing `AsyncRead` / `AsyncWrite`
The `AsyncRead` and `AsyncWrite` traits use a "poll" based API. This means that
it is not possible to use an intrusive linked list to track the waker.
Additionally, there is no future associated with the operation which means it is
not possible to cancel interest in the readiness events.
To implement `AsyncRead` and `AsyncWrite`, `ScheduledIo` includes dedicated
waker values for the read direction and the write direction. These values are
used to store the waker. Specific `interest` is not tracked for `AsyncRead` and
`AsyncWrite` implementations. It is assumed that only events of interest are:
* Read ready
* Read closed
* Write ready
* Write closed
Note that "read closed" and "write closed" are only available with Mio 0.7. With
Mio 0.6, things were a bit messy.
It is only possible to implement `AsyncRead` and `AsyncWrite` for resource types
themselves and not for `&Resource`. Implementing the traits for `&Resource`
would permit concurrent operations to the resource. Because only a single waker
is stored per direction, any concurrent usage would result in deadlocks. An
alterate implementation would call for a `Vec<Waker>` but this would result in
memory leaks.
## Enabling reads and writes for `&TcpStream`
Instead of implementing `AsyncRead` and `AsyncWrite` for `&TcpStream`, a new
function is added to `TcpStream`.
```rust
impl TcpStream {
/// Naming TBD
fn by_ref(&self) -> TcpStreamRef<'_>;
}
struct TcpStreamRef<'a> {
stream: &'a TcpStream,
// `Waiter` is the node in the intrusive waiter linked-list
read_waiter: Waiter,
write_waiter: Waiter,
}
```
Now, `AsyncRead` and `AsyncWrite` can be implemented on `TcpStreamRef<'a>`. When
the `TcpStreamRef` is dropped, all associated waker resources are cleaned up.
### Removing all the `split()` functions
With `TcpStream::by_ref()`, `TcpStream::split()` is no longer needed. Instead,
it is possible to do something as follows.
```rust
let rd = my_stream.by_ref();
let wr = my_stream.by_ref();
select! {
// use `rd` and `wr` in separate branches.
}
```
It is also possible to sotre a `TcpStream` in an `Arc`.
```rust
let arc_stream = Arc::new(my_tcp_stream);
let n = arc_stream.by_ref().read(buf).await?;
```
+48
View File
@@ -0,0 +1,48 @@
cfg_rt! {
pub(crate) use crate::runtime::spawn_blocking;
pub(crate) use crate::task::JoinHandle;
}
cfg_not_rt! {
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
pub(crate) fn spawn_blocking<F, R>(_f: F) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
assert_send_sync::<JoinHandle<std::cell::Cell<()>>>();
panic!("requires the `rt` Tokio feature flag")
}
pub(crate) struct JoinHandle<R> {
_p: std::marker::PhantomData<R>,
}
unsafe impl<T: Send> Send for JoinHandle<T> {}
unsafe impl<T: Send> Sync for JoinHandle<T> {}
impl<R> Future for JoinHandle<R> {
type Output = Result<R, std::io::Error>;
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!()
}
}
impl<T> fmt::Debug for JoinHandle<T>
where
T: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("JoinHandle").finish()
}
}
fn assert_send_sync<T: Send + Sync>() {
}
}
+10 -11
View File
@@ -1,3 +1,5 @@
#![cfg_attr(not(feature = "full"), allow(dead_code))]
//! Opt-in yield points for improved cooperative scheduling.
//!
//! A single call to [`poll`] on a top-level task may potentially do a lot of
@@ -81,7 +83,7 @@ impl Budget {
}
}
cfg_rt_threaded! {
cfg_rt_multi_thread! {
impl Budget {
fn has_remaining(self) -> bool {
self.0.map(|budget| budget > 0).unwrap_or(true)
@@ -96,14 +98,6 @@ pub(crate) fn budget<R>(f: impl FnOnce() -> R) -> R {
with_budget(Budget::initial(), f)
}
cfg_rt_threaded! {
/// Set the current task's budget
#[cfg(feature = "blocking")]
pub(crate) fn set(budget: Budget) {
CURRENT.with(|cell| cell.set(budget))
}
}
#[inline(always)]
fn with_budget<R>(budget: Budget, f: impl FnOnce() -> R) -> R {
struct ResetGuard<'a> {
@@ -128,14 +122,19 @@ fn with_budget<R>(budget: Budget, f: impl FnOnce() -> R) -> R {
})
}
cfg_rt_threaded! {
cfg_rt_multi_thread! {
/// Set the current task's budget
pub(crate) fn set(budget: Budget) {
CURRENT.with(|cell| cell.set(budget))
}
#[inline(always)]
pub(crate) fn has_budget_remaining() -> bool {
CURRENT.with(|cell| cell.get().has_remaining())
}
}
cfg_blocking_impl! {
cfg_rt! {
/// Forcibly remove the budgeting constraints early.
///
/// Returns the remaining budget
+5 -2
View File
@@ -1,10 +1,13 @@
use crate::fs::asyncify;
use std::path::Path;
/// Copies the contents of one file to another. This function will also copy the permission bits of the original file to the destination file.
/// Copies the contents of one file to another. This function will also copy the permission bits
/// of the original file to the destination file.
/// This function will overwrite the contents of to.
///
/// This is the async equivalent of `std::fs::copy`.
/// This is the async equivalent of [`std::fs::copy`][std].
///
/// [std]: fn@std::fs::copy
///
/// # Examples
///
+133 -163
View File
@@ -5,7 +5,8 @@
use self::State::*;
use crate::fs::{asyncify, sys};
use crate::io::blocking::Buf;
use crate::io::{AsyncRead, AsyncSeek, AsyncWrite};
use crate::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
use crate::sync::Mutex;
use std::fmt;
use std::fs::{Metadata, Permissions};
@@ -80,12 +81,18 @@ use std::task::Poll::*;
/// ```
pub struct File {
std: Arc<sys::File>,
inner: Mutex<Inner>,
}
struct Inner {
state: State,
/// Errors from writes/flushes are returned in write/flush calls. If a write
/// error is observed while performing a read, it is saved until the next
/// write / flush call.
last_write_err: Option<io::ErrorKind>,
pos: u64,
}
#[derive(Debug)]
@@ -197,70 +204,11 @@ impl File {
pub fn from_std(std: sys::File) -> File {
File {
std: Arc::new(std),
state: State::Idle(Some(Buf::with_capacity(0))),
last_write_err: None,
}
}
/// Seeks to an offset, in bytes, in a stream.
///
/// # Examples
///
/// ```no_run
/// use tokio::fs::File;
/// use tokio::prelude::*;
///
/// use std::io::SeekFrom;
///
/// # async fn dox() -> std::io::Result<()> {
/// let mut file = File::open("foo.txt").await?;
/// file.seek(SeekFrom::Start(6)).await?;
///
/// let mut contents = vec![0u8; 10];
/// file.read_exact(&mut contents).await?;
/// # Ok(())
/// # }
/// ```
///
/// The [`read_exact`] method is defined on the [`AsyncReadExt`] trait.
///
/// [`read_exact`]: fn@crate::io::AsyncReadExt::read_exact
/// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt
pub async fn seek(&mut self, mut pos: SeekFrom) -> io::Result<u64> {
self.complete_inflight().await;
let mut buf = match self.state {
Idle(ref mut buf_cell) => buf_cell.take().unwrap(),
_ => unreachable!(),
};
// Factor in any unread data from the buf
if !buf.is_empty() {
let n = buf.discard_read();
if let SeekFrom::Current(ref mut offset) = pos {
*offset += n;
}
}
let std = self.std.clone();
// Start the operation
self.state = Busy(sys::run(move || {
let res = (&*std).seek(pos);
(Operation::Seek(res), buf)
}));
let (op, buf) = match self.state {
Idle(_) => unreachable!(),
Busy(ref mut rx) => rx.await.unwrap(),
};
self.state = Idle(Some(buf));
match op {
Operation::Seek(res) => res,
_ => unreachable!(),
inner: Mutex::new(Inner {
state: State::Idle(Some(Buf::with_capacity(0))),
last_write_err: None,
pos: 0,
}),
}
}
@@ -287,8 +235,9 @@ impl File {
///
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
pub async fn sync_all(&mut self) -> io::Result<()> {
self.complete_inflight().await;
pub async fn sync_all(&self) -> io::Result<()> {
let mut inner = self.inner.lock().await;
inner.complete_inflight().await;
let std = self.std.clone();
asyncify(move || std.sync_all()).await
@@ -321,8 +270,9 @@ impl File {
///
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
pub async fn sync_data(&mut self) -> io::Result<()> {
self.complete_inflight().await;
pub async fn sync_data(&self) -> io::Result<()> {
let mut inner = self.inner.lock().await;
inner.complete_inflight().await;
let std = self.std.clone();
asyncify(move || std.sync_data()).await
@@ -358,10 +308,11 @@ impl File {
///
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
pub async fn set_len(&mut self, size: u64) -> io::Result<()> {
self.complete_inflight().await;
pub async fn set_len(&self, size: u64) -> io::Result<()> {
let mut inner = self.inner.lock().await;
inner.complete_inflight().await;
let mut buf = match self.state {
let mut buf = match inner.state {
Idle(ref mut buf_cell) => buf_cell.take().unwrap(),
_ => unreachable!(),
};
@@ -374,7 +325,7 @@ impl File {
let std = self.std.clone();
self.state = Busy(sys::run(move || {
inner.state = Busy(sys::run(move || {
let res = if let Some(seek) = seek {
(&*std).seek(seek).and_then(|_| std.set_len(size))
} else {
@@ -386,15 +337,17 @@ impl File {
(Operation::Seek(res), buf)
}));
let (op, buf) = match self.state {
let (op, buf) = match inner.state {
Idle(_) => unreachable!(),
Busy(ref mut rx) => rx.await?,
};
self.state = Idle(Some(buf));
inner.state = Idle(Some(buf));
match op {
Operation::Seek(res) => res.map(|_| ()),
Operation::Seek(res) => res.map(|pos| {
inner.pos = pos;
}),
_ => unreachable!(),
}
}
@@ -459,7 +412,7 @@ impl File {
/// # }
/// ```
pub async fn into_std(mut self) -> sys::File {
self.complete_inflight().await;
self.inner.get_mut().complete_inflight().await;
Arc::try_unwrap(self.std).expect("Arc::try_unwrap failed")
}
@@ -526,42 +479,32 @@ impl File {
let std = self.std.clone();
asyncify(move || std.set_permissions(perm)).await
}
async fn complete_inflight(&mut self) {
use crate::future::poll_fn;
if let Err(e) = poll_fn(|cx| Pin::new(&mut *self).poll_flush(cx)).await {
self.last_write_err = Some(e.kind());
}
}
}
impl AsyncRead for File {
unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [std::mem::MaybeUninit<u8>]) -> bool {
// https://github.com/rust-lang/rust/blob/09c817eeb29e764cfc12d0a8d94841e3ffe34023/src/libstd/fs.rs#L668
false
}
fn poll_read(
mut self: Pin<&mut Self>,
self: Pin<&mut Self>,
cx: &mut Context<'_>,
dst: &mut [u8],
) -> Poll<io::Result<usize>> {
dst: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let me = self.get_mut();
let inner = me.inner.get_mut();
loop {
match self.state {
match inner.state {
Idle(ref mut buf_cell) => {
let mut buf = buf_cell.take().unwrap();
if !buf.is_empty() {
let n = buf.copy_to(dst);
buf.copy_to(dst);
*buf_cell = Some(buf);
return Ready(Ok(n));
return Ready(Ok(()));
}
buf.ensure_capacity_for(dst);
let std = self.std.clone();
let std = me.std.clone();
self.state = Busy(sys::run(move || {
inner.state = Busy(sys::run(move || {
let res = buf.read_from(&mut &*std);
(Operation::Read(res), buf)
}));
@@ -571,29 +514,32 @@ impl AsyncRead for File {
match op {
Operation::Read(Ok(_)) => {
let n = buf.copy_to(dst);
self.state = Idle(Some(buf));
return Ready(Ok(n));
buf.copy_to(dst);
inner.state = Idle(Some(buf));
return Ready(Ok(()));
}
Operation::Read(Err(e)) => {
assert!(buf.is_empty());
self.state = Idle(Some(buf));
inner.state = Idle(Some(buf));
return Ready(Err(e));
}
Operation::Write(Ok(_)) => {
assert!(buf.is_empty());
self.state = Idle(Some(buf));
inner.state = Idle(Some(buf));
continue;
}
Operation::Write(Err(e)) => {
assert!(self.last_write_err.is_none());
self.last_write_err = Some(e.kind());
self.state = Idle(Some(buf));
assert!(inner.last_write_err.is_none());
inner.last_write_err = Some(e.kind());
inner.state = Idle(Some(buf));
}
Operation::Seek(_) => {
Operation::Seek(result) => {
assert!(buf.is_empty());
self.state = Idle(Some(buf));
inner.state = Idle(Some(buf));
if let Ok(pos) = result {
inner.pos = pos;
}
continue;
}
}
@@ -604,13 +550,13 @@ impl AsyncRead for File {
}
impl AsyncSeek for File {
fn start_seek(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
mut pos: SeekFrom,
) -> Poll<io::Result<()>> {
fn start_seek(self: Pin<&mut Self>, mut pos: SeekFrom) -> io::Result<()> {
let me = self.get_mut();
let inner = me.inner.get_mut();
loop {
match self.state {
match inner.state {
Busy(_) => panic!("must wait for poll_complete before calling start_seek"),
Idle(ref mut buf_cell) => {
let mut buf = buf_cell.take().unwrap();
@@ -623,49 +569,41 @@ impl AsyncSeek for File {
}
}
let std = self.std.clone();
let std = me.std.clone();
self.state = Busy(sys::run(move || {
inner.state = Busy(sys::run(move || {
let res = (&*std).seek(pos);
(Operation::Seek(res), buf)
}));
return Ready(Ok(()));
}
Busy(ref mut rx) => {
let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
self.state = Idle(Some(buf));
match op {
Operation::Read(_) => {}
Operation::Write(Err(e)) => {
assert!(self.last_write_err.is_none());
self.last_write_err = Some(e.kind());
}
Operation::Write(_) => {}
Operation::Seek(_) => {}
}
return Ok(());
}
}
}
}
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
let inner = self.inner.get_mut();
loop {
match self.state {
Idle(_) => panic!("must call start_seek before calling poll_complete"),
match inner.state {
Idle(_) => return Poll::Ready(Ok(inner.pos)),
Busy(ref mut rx) => {
let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
self.state = Idle(Some(buf));
inner.state = Idle(Some(buf));
match op {
Operation::Read(_) => {}
Operation::Write(Err(e)) => {
assert!(self.last_write_err.is_none());
self.last_write_err = Some(e.kind());
assert!(inner.last_write_err.is_none());
inner.last_write_err = Some(e.kind());
}
Operation::Write(_) => {}
Operation::Seek(res) => return Ready(res),
Operation::Seek(res) => {
if let Ok(pos) = res {
inner.pos = pos;
}
return Ready(res);
}
}
}
}
@@ -675,16 +613,19 @@ impl AsyncSeek for File {
impl AsyncWrite for File {
fn poll_write(
mut self: Pin<&mut Self>,
self: Pin<&mut Self>,
cx: &mut Context<'_>,
src: &[u8],
) -> Poll<io::Result<usize>> {
if let Some(e) = self.last_write_err.take() {
let me = self.get_mut();
let inner = me.inner.get_mut();
if let Some(e) = inner.last_write_err.take() {
return Ready(Err(e.into()));
}
loop {
match self.state {
match inner.state {
Idle(ref mut buf_cell) => {
let mut buf = buf_cell.take().unwrap();
@@ -695,9 +636,9 @@ impl AsyncWrite for File {
};
let n = buf.copy_from(src);
let std = self.std.clone();
let std = me.std.clone();
self.state = Busy(sys::run(move || {
inner.state = Busy(sys::run(move || {
let res = if let Some(seek) = seek {
(&*std).seek(seek).and_then(|_| buf.write_to(&mut &*std))
} else {
@@ -711,7 +652,7 @@ impl AsyncWrite for File {
}
Busy(ref mut rx) => {
let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
self.state = Idle(Some(buf));
inner.state = Idle(Some(buf));
match op {
Operation::Read(_) => {
@@ -737,27 +678,12 @@ impl AsyncWrite for File {
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
if let Some(e) = self.last_write_err.take() {
return Ready(Err(e.into()));
}
let (op, buf) = match self.state {
Idle(_) => return Ready(Ok(())),
Busy(ref mut rx) => ready!(Pin::new(rx).poll(cx))?,
};
// The buffer is not used here
self.state = Idle(Some(buf));
match op {
Operation::Read(_) => Ready(Ok(())),
Operation::Write(res) => Ready(res),
Operation::Seek(_) => Ready(Ok(())),
}
let inner = self.inner.get_mut();
inner.poll_flush(cx)
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
Poll::Ready(Ok(()))
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
self.poll_flush(cx)
}
}
@@ -782,9 +708,53 @@ impl std::os::unix::io::AsRawFd for File {
}
}
#[cfg(unix)]
impl std::os::unix::io::FromRawFd for File {
unsafe fn from_raw_fd(fd: std::os::unix::io::RawFd) -> Self {
sys::File::from_raw_fd(fd).into()
}
}
#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for File {
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
self.std.as_raw_handle()
}
}
#[cfg(windows)]
impl std::os::windows::io::FromRawHandle for File {
unsafe fn from_raw_handle(handle: std::os::windows::io::RawHandle) -> Self {
sys::File::from_raw_handle(handle).into()
}
}
impl Inner {
async fn complete_inflight(&mut self) {
use crate::future::poll_fn;
if let Err(e) = poll_fn(|cx| Pin::new(&mut *self).poll_flush(cx)).await {
self.last_write_err = Some(e.kind());
}
}
fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
if let Some(e) = self.last_write_err.take() {
return Ready(Err(e.into()));
}
let (op, buf) = match self.state {
Idle(_) => return Ready(Ok(())),
Busy(ref mut rx) => ready!(Pin::new(rx).poll(cx))?,
};
// The buffer is not used here
self.state = Idle(Some(buf));
match op {
Operation::Read(_) => Ready(Ok(())),
Operation::Write(res) => Ready(res),
Operation::Seek(_) => Ready(Ok(())),
}
}
}
+3 -3
View File
@@ -22,7 +22,7 @@
//! `std::io::ErrorKind::WouldBlock` if a *worker* thread can not be converted
//! to a *backup* thread immediately.
//!
//! [`AsyncRead`]: https://docs.rs/tokio-io/0.1/tokio_io/trait.AsyncRead.html
//! [`AsyncRead`]: trait@crate::io::AsyncRead
mod canonicalize;
pub use self::canonicalize::canonicalize;
@@ -107,6 +107,6 @@ mod sys {
pub(crate) use std::fs::File;
// TODO: don't rename
pub(crate) use crate::runtime::spawn_blocking as run;
pub(crate) use crate::task::JoinHandle as Blocking;
pub(crate) use crate::blocking::spawn_blocking as run;
pub(crate) use crate::blocking::JoinHandle as Blocking;
}
+1 -2
View File
@@ -383,8 +383,7 @@ impl OpenOptions {
Ok(File::from_std(std))
}
/// Returns a mutable reference to the the underlying std::fs::OpenOptions
#[cfg(unix)]
/// Returns a mutable reference to the underlying `std::fs::OpenOptions`
pub(super) fn as_inner_mut(&mut self) -> &mut std::fs::OpenOptions {
&mut self.0
}
+8 -1
View File
@@ -3,7 +3,7 @@ use crate::fs::dir_builder::DirBuilder;
/// Unix-specific extensions to [`DirBuilder`].
///
/// [`DirBuilder`]: crate::fs::DirBuilder
pub trait DirBuilderExt {
pub trait DirBuilderExt: sealed::Sealed {
/// Sets the mode to create new directories with.
///
/// This option defaults to 0o777.
@@ -27,3 +27,10 @@ impl DirBuilderExt for DirBuilder {
self
}
}
impl sealed::Sealed for DirBuilder {}
pub(crate) mod sealed {
#[doc(hidden)]
pub trait Sealed {}
}
+44
View File
@@ -0,0 +1,44 @@
use crate::fs::DirEntry;
use std::os::unix::fs::DirEntryExt as _;
/// Unix-specific extension methods for [`fs::DirEntry`].
///
/// This mirrors the definition of [`std::os::unix::fs::DirEntryExt`].
///
/// [`fs::DirEntry`]: crate::fs::DirEntry
/// [`std::os::unix::fs::DirEntryExt`]: std::os::unix::fs::DirEntryExt
pub trait DirEntryExt: sealed::Sealed {
/// Returns the underlying `d_ino` field in the contained `dirent`
/// structure.
///
/// # Examples
///
/// ```
/// use tokio::fs;
/// use tokio::fs::os::unix::DirEntryExt;
///
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// let mut entries = fs::read_dir(".").await?;
/// while let Some(entry) = entries.next_entry().await? {
/// // Here, `entry` is a `DirEntry`.
/// println!("{:?}: {}", entry.file_name(), entry.ino());
/// }
/// # Ok(())
/// # }
/// ```
fn ino(&self) -> u64;
}
impl DirEntryExt for DirEntry {
fn ino(&self) -> u64 {
self.as_inner().ino()
}
}
impl sealed::Sealed for DirEntry {}
pub(crate) mod sealed {
#[doc(hidden)]
pub trait Sealed {}
}
+3
View File
@@ -8,3 +8,6 @@ pub use self::open_options_ext::OpenOptionsExt;
mod dir_builder_ext;
pub use self::dir_builder_ext::DirBuilderExt;
mod dir_entry_ext;
pub use self::dir_entry_ext::DirEntryExt;
+9 -3
View File
@@ -1,14 +1,13 @@
use crate::fs::open_options::OpenOptions;
use std::os::unix::fs::OpenOptionsExt as StdOpenOptionsExt;
use std::os::unix::fs::OpenOptionsExt as _;
/// Unix-specific extensions to [`fs::OpenOptions`].
///
/// This mirrors the definition of [`std::os::unix::fs::OpenOptionsExt`].
///
///
/// [`fs::OpenOptions`]: crate::fs::OpenOptions
/// [`std::os::unix::fs::OpenOptionsExt`]: std::os::unix::fs::OpenOptionsExt
pub trait OpenOptionsExt {
pub trait OpenOptionsExt: sealed::Sealed {
/// Sets the mode bits that a new file will be created with.
///
/// If a new file is created as part of an `OpenOptions::open` call then this
@@ -77,3 +76,10 @@ impl OpenOptionsExt for OpenOptions {
self
}
}
impl sealed::Sealed for OpenOptions {}
pub(crate) mod sealed {
#[doc(hidden)]
pub trait Sealed {}
}
+3
View File
@@ -5,3 +5,6 @@ pub use self::symlink_dir::symlink_dir;
mod symlink_file;
pub use self::symlink_file::symlink_file;
mod open_options_ext;
pub use self::open_options_ext::OpenOptionsExt;
+214
View File
@@ -0,0 +1,214 @@
use crate::fs::open_options::OpenOptions;
use std::os::windows::fs::OpenOptionsExt as _;
/// Unix-specific extensions to [`fs::OpenOptions`].
///
/// This mirrors the definition of [`std::os::windows::fs::OpenOptionsExt`].
///
/// [`fs::OpenOptions`]: crate::fs::OpenOptions
/// [`std::os::windows::fs::OpenOptionsExt`]: std::os::windows::fs::OpenOptionsExt
pub trait OpenOptionsExt: sealed::Sealed {
/// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
/// with the specified value.
///
/// This will override the `read`, `write`, and `append` flags on the
/// `OpenOptions` structure. This method provides fine-grained control over
/// the permissions to read, write and append data, attributes (like hidden
/// and system), and extended attributes.
///
/// # Examples
///
/// ```no_run
/// use tokio::fs::OpenOptions;
/// use tokio::fs::os::windows::OpenOptionsExt;
///
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// // Open without read and write permission, for example if you only need
/// // to call `stat` on the file
/// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?;
/// # Ok(())
/// # }
/// ```
///
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
fn access_mode(&mut self, access: u32) -> &mut Self;
/// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with
/// the specified value.
///
/// By default `share_mode` is set to
/// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows
/// other processes to read, write, and delete/rename the same file
/// while it is open. Removing any of the flags will prevent other
/// processes from performing the corresponding operation until the file
/// handle is closed.
///
/// # Examples
///
/// ```no_run
/// use tokio::fs::OpenOptions;
/// use tokio::fs::os::windows::OpenOptionsExt;
///
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// // Do not allow others to read or modify this file while we have it open
/// // for writing.
/// let file = OpenOptions::new()
/// .write(true)
/// .share_mode(0)
/// .open("foo.txt").await?;
/// # Ok(())
/// # }
/// ```
///
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
fn share_mode(&mut self, val: u32) -> &mut Self;
/// Sets extra flags for the `dwFileFlags` argument to the call to
/// [`CreateFile2`] to the specified value (or combines it with
/// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes`
/// for [`CreateFile`]).
///
/// Custom flags can only set flags, not remove flags set by Rust's options.
/// This option overwrites any previously set custom flags.
///
/// # Examples
///
/// ```no_run
/// use winapi::um::winbase::FILE_FLAG_DELETE_ON_CLOSE;
/// use tokio::fs::OpenOptions;
/// use tokio::fs::os::windows::OpenOptionsExt;
///
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// let file = OpenOptions::new()
/// .create(true)
/// .write(true)
/// .custom_flags(FILE_FLAG_DELETE_ON_CLOSE)
/// .open("foo.txt").await?;
/// # Ok(())
/// # }
/// ```
///
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
/// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
fn custom_flags(&mut self, flags: u32) -> &mut Self;
/// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to
/// the specified value (or combines it with `custom_flags` and
/// `security_qos_flags` to set the `dwFlagsAndAttributes` for
/// [`CreateFile`]).
///
/// If a _new_ file is created because it does not yet exist and
/// `.create(true)` or `.create_new(true)` are specified, the new file is
/// given the attributes declared with `.attributes()`.
///
/// If an _existing_ file is opened with `.create(true).truncate(true)`, its
/// existing attributes are preserved and combined with the ones declared
/// with `.attributes()`.
///
/// In all other cases the attributes get ignored.
///
/// # Examples
///
/// ```no_run
/// use winapi::um::winnt::FILE_ATTRIBUTE_HIDDEN;
/// use tokio::fs::OpenOptions;
/// use tokio::fs::os::windows::OpenOptionsExt;
///
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// let file = OpenOptions::new()
/// .write(true)
/// .create(true)
/// .attributes(FILE_ATTRIBUTE_HIDDEN)
/// .open("foo.txt").await?;
/// # Ok(())
/// # }
/// ```
///
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
/// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
fn attributes(&mut self, val: u32) -> &mut Self;
/// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to
/// the specified value (or combines it with `custom_flags` and `attributes`
/// to set the `dwFlagsAndAttributes` for [`CreateFile`]).
///
/// By default `security_qos_flags` is not set. It should be specified when
/// opening a named pipe, to control to which degree a server process can
/// act on behalf of a client process (security impersonation level).
///
/// When `security_qos_flags` is not set, a malicious program can gain the
/// elevated privileges of a privileged Rust process when it allows opening
/// user-specified paths, by tricking it into opening a named pipe. So
/// arguably `security_qos_flags` should also be set when opening arbitrary
/// paths. However the bits can then conflict with other flags, specifically
/// `FILE_FLAG_OPEN_NO_RECALL`.
///
/// For information about possible values, see [Impersonation Levels] on the
/// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set
/// automatically when using this method.
///
/// # Examples
///
/// ```no_run
/// use winapi::um::winbase::SECURITY_IDENTIFICATION;
/// use tokio::fs::OpenOptions;
/// use tokio::fs::os::windows::OpenOptionsExt;
///
/// # #[tokio::main]
/// # async fn main() -> std::io::Result<()> {
/// let file = OpenOptions::new()
/// .write(true)
/// .create(true)
///
/// // Sets the flag value to `SecurityIdentification`.
/// .security_qos_flags(SECURITY_IDENTIFICATION)
///
/// .open(r"\\.\pipe\MyPipe").await?;
/// # Ok(())
/// # }
/// ```
///
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
/// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
/// [Impersonation Levels]:
/// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
fn security_qos_flags(&mut self, flags: u32) -> &mut Self;
}
impl OpenOptionsExt for OpenOptions {
fn access_mode(&mut self, access: u32) -> &mut OpenOptions {
self.as_inner_mut().access_mode(access);
self
}
fn share_mode(&mut self, share: u32) -> &mut OpenOptions {
self.as_inner_mut().share_mode(share);
self
}
fn custom_flags(&mut self, flags: u32) -> &mut OpenOptions {
self.as_inner_mut().custom_flags(flags);
self
}
fn attributes(&mut self, attributes: u32) -> &mut OpenOptions {
self.as_inner_mut().attributes(attributes);
self
}
fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions {
self.as_inner_mut().security_qos_flags(flags);
self
}
}
impl sealed::Sealed for OpenOptions {}
pub(crate) mod sealed {
#[doc(hidden)]
pub trait Sealed {}
}
+5 -9
View File
@@ -4,8 +4,6 @@ use std::ffi::OsString;
use std::fs::{FileType, Metadata};
use std::future::Future;
use std::io;
#[cfg(unix)]
use std::os::unix::fs::DirEntryExt;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::Arc;
@@ -55,8 +53,7 @@ impl ReadDir {
poll_fn(|cx| self.poll_next_entry(cx)).await
}
#[doc(hidden)]
pub fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Option<DirEntry>>> {
fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Option<DirEntry>>> {
loop {
match self.0 {
State::Idle(ref mut std) => {
@@ -234,11 +231,10 @@ impl DirEntry {
let std = self.0.clone();
asyncify(move || std.file_type()).await
}
}
#[cfg(unix)]
impl DirEntryExt for DirEntry {
fn ino(&self) -> u64 {
self.0.ino()
/// Returns a reference to the underlying `std::fs::DirEntry`
#[cfg(unix)]
pub(super) fn as_inner(&self) -> &std::fs::DirEntry {
&self.0
}
}
+3 -1
View File
@@ -5,7 +5,9 @@ use std::{io, path::Path};
/// Creates a future which will open a file for reading and read the entire
/// contents into a string and return said string.
///
/// This is the async equivalent of `std::fs::read_to_string`.
/// This is the async equivalent of [`std::fs::read_to_string`][std].
///
/// [std]: fn@std::fs::read_to_string
///
/// # Examples
///
+3 -1
View File
@@ -5,7 +5,9 @@ use std::{io, path::Path};
/// Creates a future that will open a file for writing and write the entire
/// contents of `contents` to it.
///
/// This is the async equivalent of `std::fs::write`.
/// This is the async equivalent of [`std::fs::write`][std].
///
/// [std]: fn@std::fs::write
///
/// # Examples
///
+15
View File
@@ -0,0 +1,15 @@
use std::future::Future;
cfg_rt! {
pub(crate) fn block_on<F: Future>(f: F) -> F::Output {
let mut e = crate::runtime::enter::enter(false);
e.block_on(f).unwrap()
}
}
cfg_not_rt! {
pub(crate) fn block_on<F: Future>(f: F) -> F::Output {
let mut park = crate::park::thread::CachedParkThread::new();
park.block_on(f).unwrap()
}
}
+16 -7
View File
@@ -1,15 +1,24 @@
#![allow(unused_imports, dead_code)]
#![cfg_attr(not(feature = "macros"), allow(unreachable_pub))]
//! Asynchronous values.
mod maybe_done;
pub use maybe_done::{maybe_done, MaybeDone};
#[cfg(any(feature = "macros", feature = "process"))]
pub(crate) mod maybe_done;
mod poll_fn;
pub use poll_fn::poll_fn;
mod ready;
pub(crate) use ready::{ok, Ready};
cfg_not_loom! {
mod ready;
pub(crate) use ready::{ok, Ready};
}
mod try_join;
pub(crate) use try_join::try_join3;
cfg_process! {
mod try_join;
pub(crate) use try_join::try_join3;
}
cfg_sync! {
mod block_on;
pub(crate) use block_on::block_on;
}
-44
View File
@@ -1,44 +0,0 @@
use sdt::pin::Pin;
use std::future::Future;
use std::marker;
use std::task::{Context, Poll};
/// Future for the [`pending()`] function.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
struct Pending<T> {
_data: marker::PhantomData<T>,
}
/// Creates a future which never resolves, representing a computation that never
/// finishes.
///
/// The returned future will forever return [`Poll::Pending`].
///
/// # Examples
///
/// ```no_run
/// use tokio::future;
///
/// #[tokio::main]
/// async fn main {
/// future::pending().await;
/// unreachable!();
/// }
/// ```
pub async fn pending() -> ! {
Pending {
_data: marker::PhantomData,
}
.await
}
impl<T> Future for Pending<T> {
type Output = !;
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<T> {
Poll::Pending
}
}
impl<T> Unpin for Pending<T> {}

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