Compare commits

...
Author SHA1 Message Date
Alice Ryhl 2afa276c8c chore: prepare Tokio 0.2.25 (#3478) 2021-01-28 15:35:37 +01:00
Alice Ryhl 312c981e3f runtime: update panic messages to include version (#3460) 2021-01-28 14:39:15 +01:00
Linus Färnstrand 62e24b710c chore: upgrade mio dependency (#3207)
Brings in versions of net2 and miow that does not make invalid
memory layout assumptions on std::net::SocketAddr
2021-01-21 09:21:19 -08:00
Nylonicious dccc58aa37 task: add missing feature flags for task_local (#3236) 2020-12-10 08:29:12 +01:00
Luke Steensen 95b943506b chore: prepare 0.2.24 release (#3227) 2020-12-09 11:58:11 -05:00
Carl Lerche 3d17488d9a sync: fix mpsc bug related to closing the channel (#3215)
When closing a channel, it is possible to get into an invalid state when
outstanding permits release capacity back to the channel.
2020-12-07 11:04:37 -08:00
Alice Ryhl c63057ebc5 chore: prepare v0.2.23 release (#3114) 2020-11-12 11:40:26 -08:00
Alice Ryhl 1016de28fc ci: minimal version check (v0.2.x) (#3132) 2020-11-12 10:25:36 +01:00
Alice Ryhl d2ad49aa6e chore: prepare tokio-macros v0.2.6 (#3127) 2020-11-11 20:42:47 +01:00
Lucio FrancoandBlas Rodriguez Irizar 85d8029e9b util: Add TokioContext future (#2791) (#2958)
Co-authored-by: Lucio Franco <[email protected]>
Co-authored-by: Blas Rodriguez Irizar <[email protected]>
2020-10-14 19:36:24 -04:00
Taiki Endo b69d4a6108 v0.2.x: disable clippy check (#2961) 2020-10-14 17:11:12 -04:00
kalcutter a517dbf605 net: make UnixListener::poll_accept public (#2880)
This makes it consistent with `TcpListener::poll_accept` and other
public poll methods the library provides. This particular method is
useful for writing generic code that accepts connections since async
functions can't easily be used with traits. It is possible to
generically accept connections with `Incoming`, however, this doesn't
return the incoming `SocketAddr`.
2020-09-24 17:36:44 -07:00
Carl Lerche c0c7124a4b 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:29:22 -07:00
Alice RyhlandBlas Rodriguez Irizar 2b96b1773d ci: update nightly and fix all sorts of new failures (#2852)
* ci: update miri flags

* ci: fix doc warnings

* doc: fix some links

Cherry-pick of 18ed761 from #2834

* ci: cherry-pick 00a2849

From: #2793

* ci: cherry-pick 6b61212

From: #2793

Co-authored-by: Blas Rodriguez Irizar <[email protected]>
2020-09-21 18:57:27 +02:00
John-John Tedro f0328f7810 sync: implement map methods of parking_lot fame (#2771)
* sync: Implement map methods of parking_lot fame

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-27 08:23:38 +02:00
Mikail Bagishov 30d4ec0a20 io: add ReaderStream (#2714) 2020-08-23 17:47:20 +02:00
Blas Rodriguez Irizar 1167c09ae8 process: document remote killing for Child (#2736)
* process: document remote killing for Child

Fixes: #2703
2020-08-05 00:59:10 +00:00
南浦月 7276d47072 net: impl ToSocketAddrs for (String, u16) (#2724) 2020-08-01 15:27:14 +02:00
Max Bruckner 9f0b6d3166 sync: suspectible -> susceptible (#2732) 2020-07-31 22:10:31 +02:00
Mikail Bagishov 8fda719845 sync: better Debug for Mutex (#2725) 2020-07-31 21:00:23 +02:00
Émile Grégoire 646fbae765 rt: fix potential leak during runtime shutdown (#2649)
JoinHandle of threads created by the pool are now tracked and properly joined at
shutdown. If the thread does not return within the timeout, then it's not joined and
left to the OS for cleanup.

Also, break a cycle between wakers held by the timer and the runtime.

Fixes #2641, #2535
2020-07-28 20:43:19 -07:00
Kevin Leimkuhler 1562bb3144 add: Add UdpSocket::{try_send,try_send_to} methods (#1979) 2020-07-28 17:09:56 -07:00
Jon Gjengset 0366a3e6d1 Reset coop budget when blocking in block_on (#2711)
Previously, we would fail to reset the coop budget in this case, making
it so that `coop::poll_proceed` would perpetually yield `Poll::Pending`
in nested executers even when run in `block_in_place`.

This is also a further improvement on #2645.
2020-07-28 19:58:33 -04:00
Alice Ryhl 03b68f4e75 io: rewrite read_to_end and read_to_string (#2560)
The new implementation changes the behavior such that set_len is called
after poll_read. The motivation of this change is that it makes it much
more obvious that a rouge panic won't give the caller access to a vector
containing exposed uninitialized memory. The new implementation also
makes sure to not zero memory twice.

Additionally, it makes the various implementations more consistent with
each other regarding the naming of variables, and whether we store how many
bytes we have read, or how many were in the container originally.

Fixes: #2544
2020-07-28 15:45:02 -07:00
084fcd7954 chore: update parking_lot dependency to 0.11.0 (#2676)
Co-authored-by: Jasper Hugo <[email protected]>
Co-authored-by: Alice Ryhl <[email protected]>
2020-07-28 15:43:08 -07:00
Alice Ryhl cc2c358d25 chore: document issue labels (#2708) 2020-07-28 15:41:43 -07:00
Jeb Rosen 51e7933c35 ci: add information to the rustfmt check, hinting at the necessary fix (#2673) 2020-07-28 13:30:33 -07:00
Blas Rodriguez IrizarandAlice Ryhl 027351dd3a macros: silence unreachable_code warning in select! (#2678)
Solves #2665 by adding #[allow(unreachable_code)] inside a branch
matching arm.

Co-authored-by: Alice Ryhl <[email protected]>
2020-07-28 13:10:07 -07:00
Alice Ryhl ff6130da65 time: interval Stream impl requires stream feature (#2695)
Fixes: #1878
2020-07-26 21:23:06 +02:00
Alice Ryhl 018e345add time: fix incorrect argument name in doc (#2691) 2020-07-26 21:22:56 +02:00
Alice Ryhl e3e7cdeaff macros: document basic_scheduler option (#2697) 2020-07-26 09:51:56 -07:00
Felix Giese 7f29acd964 time: fix resetting expired timers causing panics (#2587)
* Add Unit Test demonstrating the issue

This test demonstrates a panic that occurs when the user inserts an
item with an instant in the past, and then tries to reset the timeout
using the returned key

* Guard reset_at against removals of expired items

Trying to remove an already expired Timer Wheel entry (called by
DelayQueue.reset()) causes panics in some cases as described in (#2573)

This prevents this panic by removing the item from the expired queue and
not the wheel in these cases

Fixes: #2473
2020-07-26 09:40:29 +02:00
jean-airoldie 2d97d5ad15 net: add try_recv/from & try_send/to to UnixDatagram (#1677)
This allows nonblocking sync send & recv operations on the socket.
2020-07-25 12:34:47 +02:00
Nikhil Benesch d1744bf260 time: report correct error for timers that exceed max duration (#2023)
Closes #1953
2020-07-24 22:03:37 -07:00
Carl Lerche de7b8914a9 chore: add ci job that depends on all tests (#2690)
This makes it a bit easier to block a PR from landing without CI
passing.
2020-07-24 21:17:37 -07:00
Carl Lerche 9943acda81 chore: complete CI migration to Github Actions (#2680) 2020-07-24 16:28:24 -07:00
Alice Ryhl 4fca1974e9 net: ensure that unix sockets have both split and into_split (#2687)
The documentation build failed with errors such as

error: `[read]` public documentation for `take` links to a private item
    --> tokio/src/io/util/async_read_ext.rs:1078:9
     |
1078 | /         /// Creates an adaptor which reads at most `limit` bytes from it.
1079 | |         ///
1080 | |         /// This function returns a new instance of `AsyncRead` which will read
1081 | |         /// at most `limit` bytes, after which it will always return EOF
...    |
1103 | |         /// }
1104 | |         /// ```
     | |_______________^
     |
note: the lint level is defined here
    --> tokio/src/lib.rs:13:9
     |
13   | #![deny(intra_doc_link_resolution_failure)]
     |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     = note: the link appears in this line:

             bytes read and future calls to [`read()`][read] may succeed.
2020-07-24 12:56:38 -07:00
Blas Rodriguez Irizar 08872c55d1 doc: feature flags in README (#2682) 2020-07-24 20:51:34 +02:00
xd009642 844d9c6acb rt: document how #[tokio::main] is expanded (#2683) 2020-07-24 08:32:15 -07:00
cssivision ff7125ec7b net: introduce split on UnixDatagram (#2557) 2020-07-23 22:03:47 -07:00
Taiki Endo 7a60a0b362 io: always re-export std::io (#2606) 2020-07-23 22:00:27 -07:00
John Doneth 94b64cd70d udp: Fix UdpFramed with regards to Decode (#1445) 2020-07-23 11:27:43 -04:00
Alice Ryhl b5d2b0d05b doc: fix links to new website (#2674) 2020-07-22 20:35:02 -07:00
Sean McArthur 0e090b7ae2 io: add io::duplex() as bidirectional reader/writer (#2661)
`duplex` returns a pair of connected `DuplexStream`s.

`DuplexStream` is a bidirectional type that can be used to simulate IO,
but over an in-process piece of memory.
2020-07-22 15:07:39 -07:00
Eliza Weisman 21f726041c chore: prepare to release 0.2.22 (#2672)
# 0.2.22 (July 2!, 2020)

### Fixes
- docs: misc improvements (#2572, #2658, #2663, #2656, #2647, #2630, #2487, #2621,
  #2624, #2600, #2623, #2622, #2577, #2569, #2589, #2575, #2540, #2564, #2567,
  #2520, #2521, #2493)
- rt: allow calls to `block_on` inside calls to `block_in_place` that are
  themselves inside `block_on` (#2645)
- net: fix non-portable behavior when dropping `TcpStream` `OwnedWriteHalf` (#2597)
- io: improve stack usage by allocating large buffers on directly on the heap
  (#2634)
- io: fix unsound pin projection in `AsyncReadExt::read_buf` and
  `AsyncWriteExt::write_buf` (#2612)
- io: fix unnecessary zeroing for `AsyncRead` implementors (#2525)
- io: Fix `BufReader` not correctly forwarding `poll_write_buf` (#2654)
- io: fix panic in `AsyncReadExt::read_line` (#2541)

### Changes
- coop: returning `Poll::Pending` no longer decrements the task budget (#2549)

### Added
- io: little-endian variants of `AsyncReadExt` and `AsyncWriteExt` methods
  (#1915)
- task: add [`tracing`] instrumentation to spawned tasks (#2655)
- sync: allow unsized types in `Mutex` and `RwLock` (via `default` constructors)
  (#2615)
- net: add `ToSocketAddrs` implementation for `&[SocketAddr]` (#2604)
- fs: add `OpenOptionsExt` for `OpenOptions` (#2515)
- fs: add `DirBuilder` (#2524)

[`tracing`]: https://crates.io/crates/tracing

Signed-off-by: Eliza Weisman <[email protected]>
2020-07-21 17:52:16 -07:00
Kornel c344aac925 sync: support larger number of semaphore permits (#2607) 2020-07-21 16:51:42 -07:00
Zephyr Shannon cbb4abc8ae chore: add audit check (#2595) 2020-07-21 15:32:54 -07:00
Alice Ryhl 14723f9786 doc: update links in README.md and CONTRIBUTING.md (#2609) 2020-07-21 15:31:26 -07:00
04a2826084 provide a way to drop a runtime in an async context (#2646)
Dropping a runtime normally involves waiting for any outstanding blocking tasks
to complete. When this drop happens in an asynchronous context, we previously
would issue a cryptic panic due to trying to block in an asynchronous context.

This change improves the panic message, and adds a `shutdown_blocking()` function
which can be used to shutdown a runtime without blocking at all, as an out for
cases where this really is necessary.

Co-authored-by: Bryan Donlan <[email protected]>
Co-authored-by: Alice Ryhl <[email protected]>
2020-07-21 15:26:47 -07:00
Mikail Bagishov 28a93e6044 Update doc comments (#2572)
* Update doc comments

* Remove trailing whitespace
2020-07-20 14:50:59 -07:00
Markus Westerlind dd28831e13 io: Forward poll_write_buf on BufReader (#2654)
For some yet unknown reason using the default on a wrapped `Bufreader<TcpStream>`
causes the hyper server to sometimes fail to send the entire body in the
response.

This fixes that problem for us and ensures that hyper has a chance to
use vectored IO (making it a good change regardless of the mentioned
bug)
2020-07-20 14:49:38 -07:00
nicolaiunrein 6dcce1901a sync: remove misleading comment (#2666)
We are not returning the old value. I suppose this was once indented and this
is a leftover.
2020-07-20 14:30:28 -07:00
Blas Rodriguez Irizar 32f46d7b88 time: improve Entry field comment (#2671)
Applying a suggestion from #2617 to make the sentence more clear.
2020-07-20 14:29:25 -07:00
Alice Ryhl 356c81c977 dns: document that strings require the DNS feature (#2663) 2020-07-20 14:27:34 -07:00
Alice Ryhl d685bceb03 sync: "which kind of mutex?" section added to doc (#2658) 2020-07-20 19:15:15 +02:00
Alice Ryhl b094ee90e2 chore: fix new manual_non_exhaustive clippy lint (#2669)
Our minimum supported Rust version does not allow switching to `#[non_exhaustive]`.
2020-07-20 09:23:19 -07:00
Evan Cameron 7e4edb8963 io: add little endian variants for AsyncRead/WriteExt (#1915) 2020-07-16 07:50:43 +02:00
bdonlanandBryan Donlan fc63fa2606 rt: allow block_on inside block_in_place inside block_on (#2645)
A fast path in block_on_place was failing to call exit() in the case where we
were in a block_on call.

Fixes: #2639

Co-authored-by: Bryan Donlan <[email protected]>
2020-07-14 21:31:13 -07:00
Eliza Weisman b9e3d2edde task: add Tracing instrumentation to spawned tasks (#2655)
## Motivation

When debugging asynchronous systems, it can be very valuable to inspect
what tasks are currently active (see #2510). The [`tracing` crate] and
related libraries provide an interface for Rust libraries and
applications to emit and consume structured, contextual, and async-aware
diagnostic information. Because this diagnostic information is
structured and machine-readable, it is a better fit for the
task-tracking use case than textual logging — `tracing` spans can be
consumed to generate metrics ranging from a simple counter of active
tasks to histograms of poll durations, idle durations, and total task
lifetimes. This information is potentially valuable to both Tokio users
*and* to maintainers.

Additionally, `tracing` is maintained by the Tokio project and is
becoming widely adopted by other libraries in the "Tokio stack", such as
[`hyper`], [`h2`], and [`tonic`] and in [other] [parts] of the broader Rust
ecosystem. Therefore, it is suitable for use in Tokio itself.

[`tracing` crate]: https://github.com/tokio-rs/tracing
[`hyper`]: https://github.com/hyperium/hyper/pull/2204
[`h2`]: https://github.com/hyperium/h2/pull/475
[`tonic`]: https://github.com/hyperium/tonic/blob/570c606397e47406ec148fe1763586e87a8f5298/tonic/Cargo.toml#L48
[other]: https://github.com/rust-lang/chalk/pull/525
[parts]: https://github.com/rust-lang/compiler-team/issues/331

## Solution

This PR is an MVP for instrumenting Tokio with `tracing` spans. When the
"tracing" optional dependency is enabled, every spawned future will be
instrumented with a `tracing` span.

The generated spans are at the `TRACE` verbosity level, and have the
target "tokio::task", which may be used by consumers to filter whether
they should be recorded. They include fields for the type name of the
spawned future and for what kind of task the span corresponds to (a
standard `spawn`ed task, a local task spawned by `spawn_local`, or a
`blocking` task spawned by `spawn_blocking`). Because `tracing` has
separate concepts of "opening/closing" and "entering/exiting" a span, we
enter these spans every time the spawned task is polled. This allows
collecting data such as:

 - the total lifetime of the task from `spawn` to `drop`
 - the number of times the task was polled before it completed
 - the duration of each individual time that the span was polled (and
   therefore, aggregated metrics like histograms or averages of poll
   durations)
 - the total time a span was actively being polled, and the total time
   it was alive but **not** being polled
 - the time between when the task was `spawn`ed and the first poll

As an example, here is the output of a version of the `chat` example
instrumented with `tracing`:
![image](https://user-images.githubusercontent.com/2796466/87231927-e50f6900-c36f-11ea-8a90-6da9b93b9601.png)
And, with multiple connections actually sending messages:
![trace_example_1](https://user-images.githubusercontent.com/2796466/87231876-8d70fd80-c36f-11ea-91f1-0ad1a5b3112f.png)


I haven't added any `tracing` spans in the example, only converted the
existing `println!`s to `tracing::info` and `tracing::error` for
consistency. The span durations in the above output are generated by
`tracing-subscriber`. Of course, a Tokio-specific subscriber could
generate even more detailed statistics, but that's follow-up work once
basic tracing support has been added.

Note that the `Instrumented` type from `tracing-futures`, which attaches
a `tracing` span to a future, was reimplemented inside of Tokio to avoid
a dependency on that crate. `tracing-futures` has a feature flag that
enables an optional dependency on Tokio, and I believe that if another
crate in a dependency graph enables that feature while Tokio's `tracing`
support is also enabled, it would create a circular dependency that
Cargo wouldn't be able to handle. Also, it avoids a dependency for a
very small amount of code that is unlikely to ever change.

There is, of course, room for plenty of future work here. This might 
include:

 - instrumenting other parts of `tokio`, such as I/O resources and 
   channels (possibly via waker instrumentation)
 - instrumenting the threadpool so that the state of worker threads
   can be inspected
 - writing `tracing-subscriber` `Layer`s to collect and display
   Tokio-specific data from these traces
 - using `track_caller` (when it's stable) to record _where_ a task 
   was `spawn`ed from

However, this is intended as an MVP to get us started on that path.

Signed-off-by: Eliza Weisman <[email protected]>
2020-07-13 16:46:59 -07:00
Antoine Murat a23d2b2274 doc: fix typo from "Rust langague" to "Rust language" (#2656)
* doc: fix typo in addr

* doc: fix typo in stream

* doc: fix typo in stream/collect
2020-07-13 08:48:02 -07:00
Carl Lerche 98e7831479 net: fix OwnedWriteHalf behavior on drop (#2597)
Previously, dropping the Write handle would issue a `shutdown(Both)`. However,
shutting down the read half is not portable and not the correct action to take.

This changes the behavior of OwnedWriteHalf to only perform a `shutdown(Write)`
on drop.
2020-07-12 19:25:58 -07:00
alborq 8411a6945f example: close pending connection on proxy exemple (#2590) 2020-07-12 20:33:20 +02:00
Markus WesterlindandAlice Ryhl f69e5bfb87 fix: Update the docs of "pause" to state that time will still advance (#2647)
* doc: Update the docs of "pause" to state that time will still advance

This was changed in #2059. This had me extremely confused for some time
as my timeouts fired immediately, without the wrapped future that were
waiting on IO to actually run long enough.

I am not sure about the exact wording here but this had me very confused
for some time. Deprecating "pause" and giving it a more accurate name
may be a good idea as well.

```rust
async fn timeout_advances() {
    time::pause();

    timeout(ms(1), async {
        // Change to 1 and the this future resolve, 2 or
        // more and the timeout resolves
        for _ in 0..2 {
            tokio::task::yield_now().await
        }
    })
    .await
    .unwrap();
}

```

* Update tokio/src/time/clock.rs

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

Co-authored-by: Alice Ryhl <[email protected]>
2020-07-10 09:11:01 -07:00
Taiki Endo 2aa8751261 ci: use latest stable compiler on macOS ci (#2643) 2020-07-05 18:48:47 +02:00
htrefil be02d36a86 io: allocate buffer directly on heap (#2634) 2020-07-01 14:57:25 -07:00
GokulandAlice Ryhl cf2c05317c sync: update oneshot::Receiver::close doc link (#2630)
Co-authored-by: Alice Ryhl <[email protected]>
2020-06-25 10:43:22 -07:00
João Oliveira f0b2b708a7 test: fix new clippy lint (#2631) 2020-06-25 17:32:16 +02:00
Artem Pyanykh f75e5a7ef4 docs: BufWriter does not flush on drop (#2487)
Fixes: #2484
2020-06-18 21:42:28 +02:00
Jeb Rosen 0ab28627e2 docs: remove unneeded doc from AsyncReadExt::read_ext() (#2621)
This paragraph from `std::io::Read::read_ext()` applies to
*implementors* of `Read`. Since `AsyncReadExt` can't and shouldn't be
implemented outside of this crate, this documentation is unnecessary.
2020-06-18 21:36:06 +02:00
Alice Ryhl a43ec11daf sync: channel doc grammar change (#2624) 2020-06-18 21:22:29 +02:00
Alice Ryhl 3db22e29d1 sync: documentation for mpsc channels (#2600) 2020-06-17 22:14:09 +02:00
Craig Pastro e2adf2612d time: add example using interval to the time module (#2623) 2020-06-16 11:25:08 +02:00
s0lst1ce 2bc6bc14a8 doc: fix typo on select macro (#2622) 2020-06-15 15:30:50 +02:00
Taiki Endo d2f81b506a sync: allow unsized types in Mutex and RwLock (#2615) 2020-06-13 03:32:51 +09:00
Taiki Endo 6b6e76080a chore: reduce pin related unsafe code (#2613) 2020-06-12 19:49:39 +09:00
Taiki Endo 68b4ca9f55 ci: pin compiler version in miri tests (#2614) 2020-06-12 18:37:06 +09:00
Taiki Endo 1769f65d37 io: fix unsound pin projection in read_buf and write_buf (#2612) 2020-06-12 14:28:23 +09:00
Taiki Endo 1636910f0a net: impl ToSocketAddrs for &[SocketAddr] (#2604) 2020-06-11 11:06:15 +02:00
johnnydai0 adaa6849a5 docs: fix the link of contributing guide (#2577) 2020-06-11 10:51:49 +02:00
Alice Ryhl 0a422593f0 doc: add sleep alias to delay_for (#2589) 2020-06-10 23:30:08 +02:00
Taiki Endo d22301967b chore: fix macOS ci on github actions (#2602) 2020-06-11 04:51:24 +09:00
Taiki Endo 4010335c84 chore: fix ci failure on master (#2593)
* Fix clippy warnings
* Pin rustc version to 1.43.1 in macOS

Refs: https://github.com/rust-lang/rust/issues/73030
2020-06-07 20:38:02 +09:00
‏‏Dave be4577e22f io: fix typo on BufReader (#2569) 2020-06-02 08:49:47 +02:00
xliiv e70a1b6d64 docs: use intra-links in the docs (#2575) 2020-05-31 18:49:04 +02:00
Mikail Bagishov 9264b837d8 test: fix all clippy lints in tests (#2573) 2020-05-31 14:49:22 +02:00
Mikail Bagishov db0d6d75b3 chore: fix clippy errors (#2571) 2020-05-30 14:06:03 -07:00
xliiv f2f30d4cf6 docs: replace method links with intra-links (#2540) 2020-05-30 20:18:01 +02:00
Geoffry Song c624cb8ce3 io: update AsyncBufRead documentation (#2564) 2020-05-29 14:00:13 +02:00
Mathspy f7574d9023 net: add note about into_split's drop (#2567)
This took me a bit to catch on to because I didn't really think there was any reason to investigate the individual documentation of each half. As someone dealing with TCP streams directly for first time (without previous experience from other languages) this caught me by surprise
2020-05-28 10:11:55 +02:00
Alice Ryhl 954f2b7304 io: fix panic in read_line (#2541)
Fixes: #2532
2020-05-24 23:26:33 +02:00
Geoff Shannon d562e58871 ci: start migrating CI to Github Actions (#2531)
This migrates test_tokio, test_sub_crates, and test_integration to
GitHub Actions, as the first step in the migration from Azure Pipelines.
2020-05-25 01:46:48 +09:00
Jon Gjengset 9f63911adc coop: Undo budget decrement on Pending (#2549)
This patch updates the coop logic so that the budget is only decremented
if a future makes progress (that is, if it returns `Ready`). This is
realized by restoring the budget to its former value after
`poll_proceed` _unless_ the caller indicates that it made progress.

The thinking here is that we always want tasks to make progress when we
poll them. With the way things were, if a task polled 128 resources that
could make no progress, and just returned `Pending`, then a 129th
resource that _could_ make progress would not be polled. Worse yet, this
could manifest as a deadlock, if the first 128 resources were all
_waiting_ for the 129th resource, since it would _never_ be polled.

The downside of this change is that `Pending` resources now do not take
up any part of the budget, even though they _do_ take up time on the
executor. If a task is particularly aggressive (or unoptimized), and
polls a large number of resources that cannot make progress whenever it
is polled, then coop will allow it to run potentially much longer before
yielding than it could before. The impact of this should be relatively
contained though, because tasks that behaved in this way in the past
probably ignored `Pending` _anyway_, so whether a resource returned
`Pending` due to coop or due to lack of progress may not make a
difference to it.
2020-05-21 17:07:23 -04:00
Mikail Bagishov 1e54a35325 io: remove zeroing for AsyncRead implementors (#2525) 2020-05-21 19:42:28 +02:00
Charles Hovine 4f4f4807c3 fs: implement OpenOptionsExt for OpenOptions (#2515)
Trait OpenOptionsExt is now implemented for fs::OpenOption.

In order to access the underlying std::fs::OpenOptions wrapped in
tokio's OpenOption, an as_inner_mut method was added to OpenOption,
only visible to the parent module.

Fixes: #2366
2020-05-21 17:18:58 +02:00
Dmitri Shkurski 8fda5f1984 fs: add DirBuilder (#2524)
The initial idea was to implement  a thin wrapper  around an internally
held `std::fs::DirBuilder` instance.  This, however, didn't work due to
`std::fs::DirBuilder` not having a Copy/Clone traits implemented, which
are necessary  for constructing an instance to move-capture it  into  a
closure.

Instead,  we mirror `std::fs::DirBuilder` configuration by  storing the
`recursive` and (unix-only) `mode`  parameters locally,  which are then
used to construct an `std::fs::DirBuilder` instance on-the-fly.

This commit also mirrors the (unix-only) DirBuilderExt trait from std.

Fixes: #2369
2020-05-21 12:49:36 +02:00
Alice Ryhl 7cb5e3460c stream: update StreamExt::merge doc (#2520) 2020-05-21 11:54:52 +02:00
Alice Ryhl 9b81580be6 github: update issue templates (#2552) 2020-05-21 09:45:27 +02:00
Geoff Shannon 9b6744cc8e tokio-macros: warn about renaming the tokio dependency (#2521) 2020-05-20 21:50:41 +02:00
Ondřej Hruška 4563699838 codec: add Framed::read_buffer_mut (#2546)
Adds a method to retrieve a mutable reference to the Framed stream's read buffer.
This makes it possible to e.g. externally clear the buffer to prevent the codec from
parsing stale data.
2020-05-20 21:45:03 +02:00
Jake Goulding f48065910e doc: fix two -> to typo (#2527) 2020-05-16 16:31:47 +02:00
ZSL a5c1a7de03 sync: document maximum number of permits (#2539) 2020-05-16 12:41:45 +02:00
Sunjay Varma a343b1d180 Clarifying that Handle::current must be called on a thread managed by tokio (#2493) 2020-05-14 12:28:56 -04:00
Geoff Shannon b44ab27359 docs: improve discoverability of codec module (#2523) 2020-05-14 16:51:55 +02:00
Carl Lerche 02661ba30a chore: prepare v0.2.21 release (#2530) 2020-05-13 11:45:02 -07:00
Carl Lerche fb7dfcf432 sync: use intrusive list strategy for broadcast (#2509)
Previously, in the broadcast channel, receiver wakers were passed to the
sender via an atomic stack with allocated nodes. When a message was
sent, the stack was drained. This caused a problem when many receivers
pushed a waiter node then dropped. The waiter node remained indefinitely
in cases where no values were sent.

This patch switches broadcast to use the intrusive linked-list waiter
strategy used by `Notify` and `Semaphore.
2020-05-12 15:09:43 -07:00
Alice Ryhl a32f918671 chore: change norun to no_run (#2518)
I was building the docs and got the following documentation warning:

warning: unknown attribute `norun`. Did you mean `no_run`?
  --> tokio/src/time/throttle.rs:13:1
   |
13 | / /// Slows down a stream by enforcing a delay between items.
14 | | /// They will be produced not more often than the specified interval.
15 | | ///
16 | | /// # Example
...  |
31 | | /// # }
32 | | /// ```
   | |_______^
   |
   = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want)
2020-05-12 16:42:24 +02:00
Plecra 221f421464 codec: rewrite of codec::Framed (#2368)
Framed was designed to encapsulate both AsyncRead and AsyncWrite so
that it could wrap two-way connections. It used Fuse to manage the pinned
io object between the FramedWrite and FramedRead structs.

I replaced the Fuse struct by isolating the state used in reading and
writing, and making the code generic over that instead. This means
the FramedImpl struct now has a parameter for the state, and contains
the logic for both directions. The Framed* structs are now simply
wrappers around this type

Hopefully removing the `Pin` handling made things easier to
understand, too.
2020-05-12 13:47:38 +02:00
Jeb Rosen 1cc0168335 macros: disambiguate the built-in #[test] attribute in macro expansion (#2503)
`tokio::test` and related macros now use the absolute path
`::core::prelude::v1::test` to refer to the built-in `test` macro.

This absolute path was introduced in rust-lang/rust#62086.
2020-05-12 09:09:59 +02:00
Patrick Mooney 67220eac37 tokio: add support for illumos target (#2486)
Although very similar in many regards, illumos and Solaris have been
diverging since the end of OpenSolaris.  With the addition of illumos as
a Rust target, it must be wired into the same interfaces which it was
consuming when running under the 'solaris' target.
2020-05-11 22:23:49 +02:00
Boqin QinandAlice Ryhl 3ba818a177 io: add doc warning about concurrently calling poll_read/write_ready (#2439)
Co-authored-by: Alice Ryhl <[email protected]>
Fixes: #2429
2020-05-11 21:59:46 +02:00
Danny Browning 6aeeeff6e8 io: add mio::Ready argument to PollEvented (#2419)
Add additional methods to allow PollEvented to be created with an appropriate
mio::Ready state, so that it can be properly registered with the reactor.

Fixes #2413
2020-05-11 21:47:03 +02:00
Tom Ciborski a75fe38ba5 stream: fix documentation on filter_map (#2511) 2020-05-10 23:08:05 +02:00
Karl Voss adce911b02 doc: add link fragments to CONTRIBUTING.md (#2507)
Added GitHub style link fragments to the `[Commit Squashing]`
sections of CONTRIBUTING.md

Fixes: #2506
2020-05-08 14:40:23 +02:00
zeroed 8565a98601 docs: fix links in tokio::sync (#2491)
Fixes: #2489
2020-05-07 16:26:09 -07:00
Carl Lerche bff21aba6c rt: set task budget after block_in_place call (#2502)
In some cases, when a call to `block_in_place` completes, the runtime is
reinstated on the thread. In this case, the task budget must also be set
in order to avoid starving other tasks on the worker.
2020-05-07 16:25:04 -07:00
Adam C. Foltzer 07533a5255 rt: add Handle::spawn_blocking method (#2501)
This follows a similar pattern to `Handle::spawn` to add the
blocking spawn capabilities to `Handle`.
2020-05-07 16:24:24 -07:00
Carl Lerche 4748b2571f rt: simplify coop implementation (#2498)
Simplifies coop implementation. Prunes unused code, create a `Budget`
type to track the current budget.
2020-05-06 19:02:07 -07:00
Lucio Franco 66fef4a9bc Remove tokio-tls from master (#2497) 2020-05-06 17:30:01 -04:00
Lucio Franco 13e2a366de tls: Deprecate in favor of tokio-native-tls (#2485) 2020-05-06 16:10:57 -04:00
Carl Lerche cc8a662598 sync: simplify the broadcast channel (#2467)
Replace an ad hoc read/write lock with RwLock. Use
The parking_lot RwLock when possible.
2020-05-06 07:37:44 -07:00
Carl Lerche 264ae3bdb2 sync: move CancellationToken tests (#2477)
In preparation of work on `CancellationToken` internals, the tests are
moved into `tests/` and are updated to not depend on internals.
2020-05-03 12:35:47 -07:00
Matthias Einwag 187af2e6a3 sync: add CancellationToken (#2263)
As a first step towards structured concurrency, this change adds a
CancellationToken for graceful cancellation of tasks.

The task can be awaited by an arbitrary amount of tasks due to the usage
of an intrusive list.

The token can be cloned. In addition to this child tokens can be derived.
When the parent token gets cancelled, all child tokens will also get
cancelled.
2020-05-02 14:19:28 -07:00
zeroed 31315b9463 doc: remove reference to the Sink trait in the MPSC documentation (#2476)
The implementation of the Sink trait was removed in 8a7e5778.

Fixes: #2464
Refs: #2389
2020-05-02 13:41:40 -07:00
Eliza Weisman 20b5df9037 task: fix LocalSet having a single shared task budget (#2462)
## Motivation

Currently, an issue exists where a `LocalSet` has a single cooperative
task budget that's shared across all futures spawned on the `LocalSet`
_and_ by any future passed to `LocalSet::run_until` or
`LocalSet::block_on`. Because these methods will poll the `run_until`
future before polling spawned tasks, it is possible for that task to
_always_ deterministically starve the entire `LocalSet` so that no local
tasks can proceed. When the completion of that future _itself_ depends
on other tasks on the `LocalSet`, this will then result in a deadlock,
as in issue #2460.

A detailed description of why this is the case, taken from [this 
comment][1]:

`LocalSet` wraps each time a local task is run in `budget`:
https://github.com/tokio-rs/tokio/blob/947045b9445f15fb9314ba0892efa2251076ae73/tokio/src/task/local.rs#L406

This is identical to what tokio's other schedulers do when running
tasks, and in theory should give each task its own budget every time
it's polled. 

_However_, `LocalSet` is different from other schedulers. Unlike the
runtime schedulers, a `LocalSet` is itself a future that's run on
another scheduler, in `block_on`.  `block_on` _also_ sets a budget:
https://github.com/tokio-rs/tokio/blob/947045b9445f15fb9314ba0892efa2251076ae73/tokio/src/runtime/basic_scheduler.rs#L131

The docs for `budget` state that:
https://github.com/tokio-rs/tokio/blob/947045b9445f15fb9314ba0892efa2251076ae73/tokio/src/coop.rs#L73

This means that inside of a `LocalSet`, the calls to `budget` are
no-ops. Instead, each future polled by the `LocalSet` is subtracting
from a single global budget.

`LocalSet`'s `RunUntil` future polls the provided future before polling
any other tasks spawned on the local set:
https://github.com/tokio-rs/tokio/blob/947045b9445f15fb9314ba0892efa2251076ae73/tokio/src/task/local.rs#L525-L535

In this case, the provided future is `JoinAll`. Unfortunately, every
time a `JoinAll` is polled, it polls _every_ joined future that has not
yet completed. When the number of futures in the `JoinAll` is >= 128,
this means that the `JoinAll` immediately exhausts the task budget. This
would, in theory, be a _good_ thing --- if the `JoinAll` had a huge
number of `JoinHandle`s in it and none of them are ready, it would limit
the time we spend polling those join handles. 

However, because the `LocalSet` _actually_ has a single shared task
budget, this means polling the `JoinAll` _always_ exhausts the entire
budget. There is now no budget remaining to poll any other tasks spawned
on the `LocalSet`, and they are never able to complete.

[1]: https://github.com/tokio-rs/tokio/issues/2460#issuecomment-621403122

## Solution

This branch solves this issue by resetting the task budget when polling
a `LocalSet`. I've added a new function to `coop` for resetting the task
budget to `UNCONSTRAINED` for the duration of a closure, and thus
allowing the `budget` calls in `LocalSet` to _actually_ create a new
budget for each spawned local task. Additionally, I've changed
`LocalSet` to _also_ ensure that a separate task budget is applied to
any future passed to `block_on`/`run_until`.

Additionally, I've added a test reproducing the issue described in
#2460. This test fails prior to this change, and passes after it.

Fixes #2460

Signed-off-by: Eliza Weisman <[email protected]>
2020-04-30 15:19:17 -07:00
Carl Lerche fa9743f0d4 macros: scoped_thread_local should be private (#2470)
Do not export the `scoped_thread_local` macro outside of the Tokio
crate. This is not considered a breaking change as the macro never
worked if used from outside of the crate due to the generated code
referencing crate-private types.
2020-04-30 14:32:47 -07:00
Hanif Ariffin 7a89d66513 io: add get_mut, get_ref and into_inner to Lines (#2450) 2020-04-30 12:44:19 +02:00
Eliza Weisman 45773c5641 mutex: add OwnedMutexGuard for Arc<Mutex<T>>s (#2455)
This PR adds a new `OwnedMutexGuard` type and `lock_owned` and
`try_lock_owned` methods for `Arc<Mutex<T>>`.  This is pretty much the
same as the similar APIs added in #2421. 

I've also corrected some existing documentation that incorrectly
implied that the existing `lock` method cloned an internal `Arc` — I
think this may be a holdover from `tokio` 0.1's `Lock` type?

Signed-off-by: Eliza Weisman <[email protected]>
2020-04-29 15:48:08 -07:00
Matthijs Brobbel c52b78b792 chore: fix a typo (#2461) 2020-04-29 13:06:40 -07:00
Jonathan Foote 1d28060836 chore: add initial security policy (#2360)
Adds an initial security policy based on email discussions with @carllerche,
@hawkw, and co.
2020-04-29 12:37:09 -07:00
Thomas Whiteway 947045b944 time: notify when resetting a Delay to a time in the past (#2290)
If a Delay has been polled, then the task that polled it may be waiting
for a notification.  If the delay gets reset to a time in the past, then
it immediately becomes elapsed, so it should notify the relevant task.
2020-04-29 18:03:44 +02:00
John-John Tedro 2c53bebe56 runtime: mem::forget instead of keeping track of dropped state (#2451) 2020-04-29 08:24:55 -07:00
Carl Lerche 0f4287ac2b chore: prepare v0.2.20 release. (#2458) 2020-04-28 16:32:09 -07:00
Carl Lerche 1bf1928088 rt: fix default thread number logic (#2457)
Previously, the function picking the default number of threads for the
threaded runtime did not factor in `max_threads`. Instead, it only used
the value returned by `num_cpus`. However, if `num_cpus` returns a value
greater than `max_threads`, then the function would panic.

This patch fixes the function by limiting the default number of threads
by `max_threads`.

Fixes #2452
2020-04-28 15:04:41 -07:00
Alice Ryhl a26d3aec96 net: mention that bind sets SO_REUSEADDR (#2454) 2020-04-28 19:44:33 +02:00
Kevin Leimkuhler a819584849 sync: fix slow receivers in broadcast (#2448)
Broadcast uses a ring buffer to store values sent to the channel. In order to
deal with slow receivers, the oldest values are overwritten with new values
once the buffer wraps. A receiver should be able to calculate how many values
it has missed.

Additionally, when the broadcast closes, a final value of `None` is sent to
the channel. If the buffer has wrapped, this value overwrites the oldest
value.

This is an issue mainly in a single capacity broadcast when a value is sent
and then the sender is dropped. The original value is immediately overwritten
with `None` meaning that receivers assume they have lagged behind.

**Solution**

A value of `None` is no longer sent to the channel when the final sender has
been dropped. This solves the single capacity broadcast case by completely
removing the behavior of overwriting values when the channel is closed.

Now, when the final sender is dropped a closed bit is set on the next slot
that the channel is supposed to send to.

In the case of a fast receiver, if it finds a slot where the closed bit is
set, it knows the channel is closed without locking the tail.

In the case of a slow receiver, it must first find out if it has missed any
values. This is similar to before, but must be able to account for channel
closure.

If the channel is not closed, the oldest value may be located at index `n`. If
the channel is closed, the oldest value is located at index `n - 1`.

Knowing the index where the oldest value is located, a receiver can calculate
how many values it may have missed and starts to catch up.

Closes #2425
2020-04-27 21:04:47 -07:00
John-John Tedro 70ed3c7f04 rt: reduce usage of ManuallyDrop (#2449) 2020-04-27 14:45:39 -07:00
Carl Lerche ce9eabfdd1 chore: prepare v0.2.19 release (#2441) 2020-04-24 15:13:55 -07:00
Alice Ryhl 894eb8b83f runtime: improve runtime and handle doc (#2440)
Refs: #2437
2020-04-24 21:05:10 +02:00
Alice Ryhl 3572ba5a7b task: update doc on spawn_blocking and block_in_place (#2436) 2020-04-24 10:16:46 -04:00
Dan Burkert d8139fef7a Add Handle::block_on method (#2437) 2020-04-24 15:25:48 +03:00
Alice Ryhl 9bcb50660e docs: make it easier to discover extension traits (#2434)
Refs: #2307
2020-04-23 15:11:49 -07:00
Alice Ryhl a3aab864d7 io: track rustfmt/clippy changes (#2431)
Refs: rust-lang/rustfmt#4140
2020-04-23 13:07:53 -07:00
Mikail Bagishov 236629d1be stream: fix panic in Merge and Chain size_hint (#2430) 2020-04-23 20:19:56 +02:00
Palash Ahuja f83f6388c4 task: link to lib.rs in spawn_blocking documentation (#2426) 2020-04-23 16:04:43 +02:00
Pythonidea 13974068f9 io: fix typo on AsyncWrite doc (#2427) 2020-04-22 19:18:48 +02:00
6349efd237 sync: improve mutex documentation (#2405)
Co-authored-by: Taiki Endo <[email protected]>
Co-authored-by: Alice Ryhl <[email protected]>
2020-04-21 20:36:13 +02:00
Geoffry Song 2da15b5f24 io: remove unsafe from ReadToString (#2384) 2020-04-21 19:41:41 +02:00
Taiki Endo 7e88b56be5 test: remove unnecessary unsafe code (#2424) 2020-04-22 02:34:55 +09:00
damienrg 43bbbf61a2 Remove relative link when possible and fix invalid links (#2423)
The link to tokio::main was relative to tokio_macros crate in the source
directory. This is why it worked in local build of documentation and not
in doc.rs.

Refs: #1473
2020-04-21 13:08:07 +02:00
Jon Gjengset 282b00cbe8 Be more principled about when blocking is ok (#2410)
This enables `block_in_place` to be used in more contexts. Specifically,
it allows you to block whenever you are off the tokio runtime (like if
you are not using tokio, are in a `spawn_blocking` closure, etc.), and
in the threaded scheduler's `block_on`. Blocking in `LocalSet` and the
basic scheduler's` block_on` is still disallowed.

Fixes #2327.
Fixes #2393.
2020-04-20 19:18:47 -04:00
Alice Ryhl 5a548044d7 sync: add owned semaphore permit (#2421) 2020-04-20 22:59:25 +02:00
Alice RyhlandEliza Weisman a748da1031 io: rewrite stdin documentation (#2420)
Co-authored-by: Eliza Weisman <[email protected]>
2020-04-20 20:44:08 +02:00
Gardner Vickers 6edc64afc7 task: Ensure the visibility modifier is propagated when constructing a task local (#2416) 2020-04-20 12:40:14 -04:00
Alice Ryhl 8f3a265972 net: introduce owned split on TcpStream (#2270) 2020-04-19 19:00:44 +02:00
Alice Ryhl 800574b4e0 doc: mention CPU-bound code lib.rs (#2414) 2020-04-18 18:46:51 -07:00
Lucio Franco 19a87e090e test: Add Future and Stream impl for Spawn. (#2412) 2020-04-17 15:37:59 -04:00
Nikolai Vazquez 6f00d7158b Link PRs in CHANGELOG files (#2383)
Allows for simply clicking on the PR number to view the corresponding
changes made.
2020-04-17 11:23:13 -04:00
Jon Gjengset 67c4cc0391 Support nested block_in_place (#2409) 2020-04-16 16:40:11 -04:00
Carl Lerche 8381dff39b chore: link mini-redis in examples (#2407) 2020-04-15 15:30:03 -07:00
xliiv 9553355c27 doc: fix a few broken links (#2400) 2020-04-13 17:25:28 +02:00
Taiki Endo 770d0ec452 ci: fix FreeBSD CI (#2403) 2020-04-13 14:41:42 +02:00
Alice Ryhl 5376f9181f chore: prepare to release 0.2.18 (#2399) 2020-04-12 20:40:34 -07:00
Alice Ryhl 4fc2adae4f task: make LocalSet non-Send (#2398)
This does not count as a breaking change as it fixes a
regression and a soundness bug.
2020-04-12 14:55:37 -07:00
xliiv f39c15334e docs: replace some html links with rustdoc paths (#2381)
Included changes
- all simple references like `<type>.<name>.html` for these types
    - enum
    - fn
    - struct
    - trait
    - type
- simple references for methods, like struct.DelayQueue.html#method.poll

Refs: #1473
2020-04-12 10:25:55 -07:00
shuoandlishuo 060d22bd10 io: report error on zero-write in write_int (#2334)
* tokio-io: make write_i* same behavior as write_all when poll_write returns Ok(0)

Fixes: #2329

Co-authored-by: lishuo <[email protected]>
2020-04-12 16:05:03 +02:00
Nikita Baksalyar 8118f8f117 docs: fix incorrect documentation links & formatting (#2332)
The streams documentation referred to module-level 'split' doc which is no longer there
2020-04-12 15:59:37 +02:00
Max Inden 1e679748ec docs: remove duplicate "a listener" (#2395) 2020-04-12 15:41:14 +02:00
Eliza Weisman 3137c6f07d chore: prepare to release 0.2.17 (#2392)
# 0.2.17 (April 9, 2020)

### Fixes
- rt: bug in work-stealing queue (#2387) 

### Changes 
- rt: threadpool uses logical CPU count instead of physical by default
  (#2391)


Signed-off-by: Eliza Weisman <[email protected]>
2020-04-09 13:49:19 -07:00
Sean McArthur d294c992e7 Use logical CPUs instead of physical by default (#2391)
Some reasons to prefer logical count as the default:

- Chips reporting many logical CPUs vs physical, such as via
hyperthreading, probably know better than us about the workload the CPUs
can handle.
- The logical count (`num_cpus::get()`) takes into consideration
schedular affinity, and cgroups CPU quota, in case the user wants to
limit the amount of CPUs a process can use.

Closes #2269
2020-04-09 12:42:46 -07:00
Carl Lerche 58ba45a38c rt: fix bug in work-stealing queue (#2387)
Fixes a couple bugs in the work-stealing queue introduced as
part of #2315. First, the cursor needs to be able to represent more
values than the size of the buffer. This is to be able to track if
`tail` is ahead of `head` or if they are identical. This bug resulted in
the "overflow" path being taken before the buffer was full.

The second bug can happen when a queue is being stolen from concurrently
with stealing into. In this case, it is possible for buffer slots to be
overwritten before they are released by the stealer. This is harder to
happen in practice due to the first bug preventing the queue from
filling up 100%, but could still happen. It triggered an assertion in
`steal_into`. This bug slipped through due to a bug in loom not
correctly catching the case. The loom bug is fixed as part of
tokio-rs/loom#119.

Fixes: #2382
2020-04-09 11:35:16 -07:00
nasa de8326a5a4 doc: Sort methods on mpsc::Sender in doc (#2379) 2020-04-06 22:49:10 +02:00
Vojtech Kral d65bf3805b doc: add error explanation for UnboundedSender::send() (#2372) 2020-04-04 19:36:12 +02:00
Alice Ryhl 7c1bc460f7 test: add Send/Sync tests for all async fns (#2377)
Also updates Empty and Pending to be unconditionally Send and Sync.
2020-04-04 19:02:26 +02:00
263 changed files with 12414 additions and 4894 deletions
+2 -14
View File
@@ -1,5 +1,5 @@
freebsd_instance:
image: freebsd-12-0-release-amd64
image: freebsd-12-1-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
@@ -19,18 +19,6 @@ task:
- |
echo "~~~~ rustc --version ~~~~"
rustc --version
# Remove any existing patch statements
mv Cargo.toml Cargo.toml.bck
sed -n '/\[patch.crates-io\]/q;p' Cargo.toml.bck > Cargo.toml
# Patch all crates
cat ci/patch.toml >> Cargo.toml
# Print `Cargo.toml` for debugging
echo "~~~~ Cargo.toml ~~~~"
cat Cargo.toml
echo "~~~~~~~~~~~~~~~~~~~~"
test_script:
- . $HOME/.cargo/env
- cargo test --all
@@ -39,4 +27,4 @@ task:
# i686_test_script:
# - . $HOME/.cargo/env
# - |
# cargo test --all --exclude tokio-tls --exclude tokio-macros --target i686-unknown-freebsd
# cargo test --all --exclude tokio-macros --target i686-unknown-freebsd
-51
View File
@@ -1,51 +0,0 @@
<!--
Thank you for reporting an issue.
Please fill in as much of the template below as you're able.
-->
## Version
<!--
List the versions of all `tokio` crates you are using. The easiest way to get
this information is using `cargo-tree`.
`cargo install cargo-tree`
(see install here: https://github.com/sfackler/cargo-tree)
Then:
`cargo tree | grep tokio`
-->
## Platform
<!---
Output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows)
-->
## Subcrates
<!--
If known, please specify the affected Tokio sub crates. Otherwise, delete this
section.
-->
## Description
<!--
Enter your issue details below this comment.
One way to structure the description:
<short summary of the bug>
I tried this code:
<code sample that causes the bug>
I expected to see this happen: <explanation>
Instead, this happened: <explanation>
-->
+36
View File
@@ -0,0 +1,36 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: A-tokio, C-bug
assignees: ''
---
**Version**
List the versions of all `tokio` crates you are using. The easiest way to get
this information is using `cargo-tree`.
`cargo install cargo-tree`
(see install here: https://github.com/sfackler/cargo-tree)
Then:
`cargo tree | grep tokio`
**Platform**
The output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows)
**Description**
Enter your issue details here.
One way to structure the description:
[short summary of the bug]
I tried this code:
[code sample that causes the bug]
I expected to see this happen: [explanation]
Instead, this happened: [explanation]
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: A-tokio, C-feature-request
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+16
View File
@@ -0,0 +1,16 @@
---
name: Question
about: Please use the discussions tab for questions
title: ''
labels: ''
assignees: ''
---
Please post your question as a discussion here:
https://github.com/tokio-rs/tokio/discussions
You may also be able to find help here:
https://discord.gg/tokio
https://users.rust-lang.org/
+3
View File
@@ -5,6 +5,9 @@ the requirements below.
Bug fixes and new features should include tests.
Contributors guide: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md
The contributors guide includes instructions for running rustfmt and building the
documentation, which requires special commands beyond `cargo fmt` and `cargo doc`.
-->
## Motivation
+22
View File
@@ -0,0 +1,22 @@
name: Security Audit
on:
push:
branches:
- master
paths:
- '**/Cargo.toml'
schedule:
- cron: '0 2 * * *' # run at 2 AM UTC
jobs:
security-audit:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'ci skip')"
steps:
- uses: actions/checkout@v2
- name: Audit Check
uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
+266
View File
@@ -0,0 +1,266 @@
on:
push:
branches: ["v0.2.x"]
pull_request:
branches: ["v0.2.x"]
name: CI
env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
nightly: nightly-2020-09-21
minrust: 1.39.0
jobs:
# Depends on all action sthat are required for a "successful" CI run.
tests-pass:
name: all systems go
runs-on: ubuntu-latest
needs:
- test
- test-unstable
- miri
- cross
- features
- minrust
- fmt
# - clippy
- docs
- loom
steps:
- run: exit 0
test:
name: test tokio full
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- windows-latest
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update stable
- name: Install cargo-hack
run: cargo install cargo-hack
# Run `tokio` with `full` features. This excludes testing utilities which
# can alter the runtime behavior of Tokio.
- name: test tokio full
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
# Run integration tests for each feature
- name: test tests-integration --each-feature
run: cargo hack test --each-feature
working-directory: tests-integration
# Run macro build tests
- name: test tests-build --each-feature
run: cargo hack test --each-feature
working-directory: tests-build
test-unstable:
name: test tokio full --unstable
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- windows-latest
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update stable
# Run `tokio` with "unstable" cfg flag.
- name: test tokio full --cfg unstable
run: cargo test --features full
working-directory: tokio
env:
RUSTFLAGS: '--cfg tokio_unstable'
miri:
name: miri
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.nightly }}
override: true
- name: Install Miri
run: |
set -e
rustup component add miri
cargo miri setup
rm -rf tokio/tests
- name: miri
run: cargo miri test --features rt-core,rt-threaded,rt-util,sync task
working-directory: tokio
cross:
name: cross
runs-on: ubuntu-latest
strategy:
matrix:
target:
- i686-unknown-linux-gnu
- powerpc-unknown-linux-gnu
- powerpc64-unknown-linux-gnu
- mips-unknown-linux-gnu
- arm-linux-androideabi
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: ${{ matrix.target }}
override: true
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: check
args: --workspace --target ${{ matrix.target }}
features:
name: features
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 --each-feature
run: cargo hack check --all --each-feature -Z avoid-dev-deps
# Try with unstable feature flags
- name: check --each-feature --unstable
run: cargo hack check --all --each-feature -Z avoid-dev-deps
env:
RUSTFLAGS: --cfg tokio_unstable
minrust:
name: minrust
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.minrust }}
override: true
- 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
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update stable
- name: Install rustfmt
run: rustup component add rustfmt
# Check fmt
- name: "rustfmt --check"
# Workaround for rust-lang/cargo#7732
run: |
if ! rustfmt --check --edition 2018 $(find . -name '*.rs' -print); then
printf "Please run \`rustfmt --edition 2018 \$(find . -name '*.rs' -print)\` to fix rustfmt errors.\nSee CONTRIBUTING.md for more details.\n" >&2
exit 1
fi
# This branch no longer actively developed. Most commits to this
# branch are backporting and should not be blocked by clippy.
# clippy:
# name: clippy
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - name: Install Rust
# run: rustup update stable
# - name: Install clippy
# run: rustup component add clippy
#
# # Run clippy
# - name: "clippy --all"
# run: cargo clippy --all --tests
docs:
name: docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.nightly }}
override: true
- name: "doc --lib --all-features"
run: cargo doc --lib --no-deps --all-features
env:
RUSTDOCFLAGS: --cfg docsrs
loom:
name: loom
runs-on: ubuntu-latest
strategy:
matrix:
scope:
- --skip loom_pool
- loom_pool::group_a
- loom_pool::group_b
- loom_pool::group_c
- loom_pool::group_d
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update stable
- name: loom ${{ matrix.scope }}
run: cargo test --lib --release --features full -- --nocapture $SCOPE
working-directory: tokio
env:
RUSTFLAGS: --cfg loom --cfg tokio_unstable
LOOM_MAX_PREEMPTIONS: 2
SCOPE: ${{ matrix.scope }}
+32
View File
@@ -0,0 +1,32 @@
name: Pull Request Security Audit
on:
push:
paths:
- '**/Cargo.toml'
pull_request:
paths:
- '**/Cargo.toml'
jobs:
security-audit:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'ci skip')"
steps:
- uses: actions/checkout@v2
- name: Install cargo-audit
uses: actions-rs/cargo@v1
with:
command: install
args: cargo-audit
- name: Generate lockfile
uses: actions-rs/cargo@v1
with:
command: generate-lockfile
- name: Audit dependencies
uses: actions-rs/cargo@v1
with:
command: audit
+97 -10
View File
@@ -15,12 +15,14 @@ It should be considered a map to help you navigate the process.
The [dev channel][dev] is available for any concerns not covered in this guide, please join
us!
[dev]: https://discord.gg/6yGkFeN
[dev]: https://discord.gg/tokio
## Conduct
The Tokio project adheres to the [Rust Code of Conduct][coc]. This describes
the _minimum_ behavior expected from all contributors. Instances of violations of the Code of Conduct can be reported by contacting the project team at [[email protected]](mailto:[email protected]).
the _minimum_ behavior expected from all contributors. Instances of violations of the
Code of Conduct can be reported by contacting the project team at
[[email protected]](mailto:[email protected]).
[coc]: https://github.com/rust-lang/rust/blob/master/CODE_OF_CONDUCT.md
@@ -29,8 +31,8 @@ the _minimum_ behavior expected from all contributors. Instances of violations o
For any issue, there are fundamentally three ways an individual can contribute:
1. By opening the issue for discussion: For instance, if you believe that you
have uncovered a bug in Tokio, creating a new issue in the tokio-rs/tokio
issue tracker is the way to report it.
have discovered a bug in Tokio, creating a new issue in [the tokio-rs/tokio
issue tracker][issue] is the way to report it.
2. By helping to triage the issue: This can be done by providing
supporting details (a test case that demonstrates a bug), providing
@@ -42,21 +44,25 @@ For any issue, there are fundamentally three ways an individual can contribute:
often, by opening a Pull Request that changes some bit of something in
Tokio in a concrete and reviewable manner.
[issue]: https://github.com/tokio-rs/tokio/issues
**Anybody can participate in any stage of contribution**. We urge you to
participate in the discussion around bugs and participate in reviewing PRs.
### Asking for General Help
If you have reviewed existing documentation and still have questions or are
having problems, you can open an issue asking for help.
having problems, you can [open a discussion] asking for help.
In exchange for receiving help, we ask that you contribute back a documentation
PR that helps others avoid the problems that you encountered.
[open a discussion]: https://github.com/tokio-rs/tokio/discussions/new
### Submitting a Bug Report
When opening a new issue in the Tokio issue tracker, users will be presented
with a [basic template][template] that should be filled in. If you believe that you have
When opening a new issue in the Tokio issue tracker, you will be presented
with a basic template that should be filled in. If you believe that you have
uncovered a bug, please fill out this form, following the template to the best
of your ability. Do not worry if you cannot answer every detail, just fill in
what you can.
@@ -72,7 +78,6 @@ cases should be limited, as much as possible, to using only Tokio APIs.
See [How to create a Minimal, Complete, and Verifiable example][mcve].
[mcve]: https://stackoverflow.com/help/mcve
[template]: .github/PULL_REQUEST_TEMPLATE.md
### Triaging a Bug Report
@@ -132,8 +137,13 @@ RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
```
The `cargo fmt` command does not work on the Tokio codebase. You can use the
command below instead:
```
# Mac or Linux
rustfmt --check --edition 2018 $(find . -name '*.rs' -print)
# Powershell
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2018 $_.FullName }
```
The `--check` argument prints the things that need to be fixed. If you remove
it, `rustfmt` will update your files locally instead.
@@ -250,7 +260,7 @@ That said, if you have a number of commits that are "checkpoints" and don't
represent a single logical change, please squash those together.
Note that multiple commits often get squashed when they are landed (see the
notes about [commit squashing]).
notes about [commit squashing](#commit-squashing)).
#### Commit message guidelines
@@ -321,7 +331,7 @@ in order to evaluate whether the changes are correct and necessary.
Keep an eye out for comments from code owners to provide guidance on conflicting
feedback.
**Once the PR is open, do not rebase the commits**. See [Commit Squashing] for
**Once the PR is open, do not rebase the commits**. See [Commit Squashing](#commit-squashing) for
more details.
### Commit Squashing
@@ -415,6 +425,83 @@ _Adapted from the [Node.js contributing guide][node]_.
[hiding-a-comment]: https://help.github.com/articles/managing-disruptive-comments/#hiding-a-comment
[documentation test]: https://doc.rust-lang.org/rustdoc/documentation-tests.html
## Keeping track of issues and PRs
The Tokio GitHub repository has a lot of issues and PRs, which is not easy to
keep track of. This section explains the meaning of various labels, as well as
our [GitHub project][project]. The section is primarily targeted at maintainers.
**Area.** The area label describes the crates relevant to this issue or PR.
- **A-tokio** This issue concerns the main Tokio crate.
- **A-tokio-util** This issue concerns the `tokio-util` crate.
- **A-tokio-tls** This issue concerns the `tokio-tls` crate. Only used for
older issues, as the crate has been moved to another repository.
- **A-tokio-test** The issue concerns the `tokio-test` crate.
- **A-tokio-macros** This issue concerns the `tokio-macros` crate. Should only
be used for the procedural macros, and not `join!` or `select!`.
- **A-ci** This issue concerns our GitHub Actions setup.
**Category.** The category label describes the category.
- **C-bug** This is a bug-report. Bug-fix PRs use `C-enhancement` instead.
- **C-enhancement** This is a PR that adds a new features.
- **C-maintenance** This is an issue or PR about stuff such as documentation,
GitHub Actions or code quality.
- **C-feature-request** This is a feature request. Implementations of feature
requests use `C-enhancement` instead.
- **C-feature-accepted** If you submit a PR for this feature request, we wont
close it with the reason "we don't want this". Issues with this label should
also have the `C-feature-request` label.
- **C-musing** Stuff like tracking issues or roadmaps. "musings about a better
world"
- **C-proposal** A proposal of some kind, and a request for comments.
- **C-question** A user question. Large overlap with GitHub discussions.
- **C-request** A non-feature request, e.g. "please add deprecation notices to
`-alpha.*` versions of crates"
**Call for participation.** I don't know why it's called `E-`. Many issues are
missing a difficulty rating, and you should feel free to add one.
- **E-help-wanted** Stuff where we want help. Often seen together with `C-bug`
or `C-feature-accepted`.
- **E-easy** This is easy, ranging from quick documentation fixes to stuff you
can do after reading the tutorial on our website.
- **E-medium** This is not `E-easy` or `E-hard`.
- **E-hard** This either involves very tricky code, is something we don't know
how to solve, or is difficult for some other reason.
- **E-needs-mvce** This bug is missing a minimal complete and verifiable
example.
**Module.** A more fine groaned categorization than area.
- **M-blocking** Things relevant to `spawn_blocking`, `block_in_place`.
- **M-codec** The `tokio_util::codec` module.
- **M-compat** The `tokio_util::compat` module.
- **M-coop** Things relevant to coop.
- **M-fs** The `tokio::fs` module.
- **M-io** The `tokio::io` module.
- **M-macros** Issues about any kind of macro.
- **M-net** The `tokio::net` module.
- **M-process** The `tokio::process` module.
- **M-runtime** The `tokio::runtime` module.
- **M-signal** The `tokio::signal` module.
- **M-stream** The `tokio::stream` module.
- **M-sync** The `tokio::sync` module.
- **M-task** The `tokio::task` module.
- **M-time** The `tokio::time` module.
- **M-tracing** Tracing support in Tokio.
**Topic.** Some extra information.
- **T-docs** This is about documentation.
- **T-performance** This is about performance.
- **T-v0.1.x** This is about old Tokio.
Any label not listed here is not in active use.
[project]: https://github.com/orgs/tokio-rs/projects/1
## Releasing
Since the Tokio project consists of a number of crates, many of which depend on
-1
View File
@@ -4,7 +4,6 @@ members = [
"tokio",
"tokio-macros",
"tokio-test",
"tokio-tls",
"tokio-util",
# Internal
+20 -12
View File
@@ -20,14 +20,14 @@ the Rust programming language. It is:
[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]: LICENSE
[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
[discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord&style=flat-square
[discord-url]: https://discord.gg/tokio
[Website](https://tokio.rs) |
[Guides](https://tokio.rs/docs/overview/) |
[Guides](https://tokio.rs/tokio/tutorial) |
[API Docs](https://docs.rs/tokio/latest/tokio) |
[Roadmap](https://github.com/tokio-rs/tokio/blob/master/ROADMAP.md) |
[Chat](https://discord.gg/tokio)
@@ -90,19 +90,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
```
More examples can be found [here](examples).
More examples can be found [here][examples]. For a larger "real world" example, see the
[mini-redis] repository.
[examples]: https://github.com/tokio-rs/tokio/tree/master/examples
[mini-redis]: https://github.com/tokio-rs/mini-redis/
To see a list of the available features flags that can be enabled, check our
[docs][feature-flag-docs].
## Getting Help
First, see if the answer to your question can be found in the [Guides] or the
[API documentation]. If the answer is not there, there is an active community in
the [Tokio Discord server][chat]. We would be happy to try to answer your
question. Last, if that doesn't work, try opening an [issue] with the question.
question. You can also ask your question on [the discussions page][discussions].
[Guides]: https://tokio.rs/docs/overview/
[Guides]: https://tokio.rs/tokio/tutorial
[API documentation]: https://docs.rs/tokio/latest/tokio
[chat]: https://discord.gg/tokio
[issue]: https://github.com/tokio-rs/tokio/issues/new
[discussions]: https://github.com/tokio-rs/tokio/discussions
[feature-flag-docs]: https://docs.rs/tokio/#feature-flags
## Contributing
@@ -149,15 +157,15 @@ several other libraries, including:
## Supported Rust Versions
Tokio is built against the latest stable, nightly, and beta Rust releases. The
minimum version supported is the stable release from three months before the
current stable release version. For example, if the latest stable Rust is 1.29,
the minimum version supported is 1.26. The current Tokio version is not
guaranteed to build on Rust versions earlier than the minimum supported version.
Tokio is built against the latest stable release. The minimum supported version is 1.39.
The current Tokio version is not guaranteed to build on Rust versions earlier than the
minimum supported version.
## License
This project is licensed under the [MIT license](LICENSE).
This project is licensed under the [MIT license].
[MIT license]: https://github.com/tokio-rs/tokio/blob/master/LICENSE
### Contribution
+13
View File
@@ -0,0 +1,13 @@
## Report a security issue
The Tokio project team welcomes security reports and is committed to providing prompt attention to security issues. Security issues should be reported privately via [[email protected]](mailto:[email protected]). Security issues should not be reported via the public Github Issue tracker.
## Vulnerability coordination
Remediation of security vulnerabilities is prioritized by the project team. The project team coordinates remediation with third-party project stakeholders via [Github Security Advisories](https://help.github.com/en/github/managing-security-vulnerabilities/about-github-security-advisories). Third-party stakeholders may include the reporter of the issue, affected direct or indirect users of Tokio, and maintainers of upstream dependencies if applicable.
Downstream project maintainers and Tokio users can request participation in coordination of applicable security issues by sending your contact email address, Github username(s) and any other salient information to [[email protected]](mailto:[email protected]). Participation in security issue coordination processes is at the discretion of the Tokio team.
## Security advisories
The project team is committed to transparency in the security issue disclosure process. The Tokio team announces security issues via [project Github Release notes](https://github.com/tokio-rs/tokio/releases) and the [RustSec advisory database](https://github.com/RustSec/advisory-db) (i.e. `cargo-audit`).
-121
View File
@@ -1,121 +0,0 @@
trigger: ["master"]
pr: ["master"]
variables:
RUSTFLAGS: -Dwarnings
nightly: nightly-2020-01-25
jobs:
# Test top level crate
- template: ci/azure-test-stable.yml
parameters:
name: test_tokio
rust: stable
displayName: Test tokio
cross: true
crates:
- tokio
- tests-integration
# Test sub crates
- template: ci/azure-test-stable.yml
parameters:
name: test_linux
displayName: Test sub crates -
rust: stable
crates:
- tokio-macros
- tokio-test
- tokio-tls
- tokio-util
- examples
# Run integration tests
- template: ci/azure-test-integration.yml
parameters:
name: test_integration
displayName: Integration tests
rust: stable
# Run tests from `tests-build`. This requires a different process
- template: ci/azure-test-build.yml
parameters:
name: test_build
displayName: Test build permutations
rust: stable
# Run miri tests
- template: ci/azure-miri.yml
parameters:
name: miri
# Try cross compiling
- template: ci/azure-cross-compile.yml
parameters:
name: cross
rust: stable
# Check each feature works properly
- template: ci/azure-check-features.yml
parameters:
rust: $(nightly)
name: check_features
# This represents the minimum Rust version supported by
# Tokio. Updating this should be done in a dedicated PR and
# cannot be greater than two 0.x releases prior to the
# current stable.
#
# Tests are not run as tests may require newer versions of
# rust.
- template: ci/azure-check-minrust.yml
parameters:
name: minrust
rust: 1.39.0
# Check formatting
- template: ci/azure-rustfmt.yml
parameters:
rust: stable
name: rustfmt
# Apply clippy lints to all crates
- template: ci/azure-clippy.yml
parameters:
rust: stable
name: clippy
# Check doc generation
- template: ci/azure-check-docs.yml
parameters:
rust: $(nightly)
name: docs
# - template: ci/azure-tsan.yml
# parameters:
# name: tsan
# rust: stable
# Run loom tests
- template: ci/azure-loom.yml
parameters:
name: loom
rust: stable
- template: ci/azure-deploy-docs.yml
parameters:
rust: stable
dependsOn:
- rustfmt
- docs
- clippy
- test_tokio
- test_linux
- test_integration
- test_build
- loom
- miri
- cross
- minrust
- check_features
# - tsan
-29
View File
@@ -1,29 +0,0 @@
parameters:
noDefaultFeatures: '--no-default-features'
jobs:
- job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }}
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- template: azure-is-release.yml
- ${{ each crate in parameters.crates }}:
- ${{ each feature in crate.value }}:
- script: cargo check ${{ parameters.noDefaultFeatures }} --features ${{ feature }}
displayName: Check `${{ crate.key }}`, features = ${{ feature }}
workingDirectory: $(Build.SourcesDirectory)/${{ crate.key }}
condition: and(succeeded(), not(variables['isRelease']))
- template: azure-patch-crates.yml
- ${{ each crate in parameters.crates }}:
- ${{ each feature in crate.value }}:
- script: cargo check ${{ parameters.noDefaultFeatures }} --features ${{ feature }}
displayName: Check `${{ crate.key }}`, features = ${{ feature }}
workingDirectory: $(Build.SourcesDirectory)/${{ crate.key }}
-15
View File
@@ -1,15 +0,0 @@
jobs:
# Check docs
- job: ${{ parameters.name }}
displayName: Check docs
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- script: |
RUSTDOCFLAGS="--cfg docsrs" cargo doc --lib --no-deps --all-features
displayName: Check docs
-32
View File
@@ -1,32 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: Check features
strategy:
matrix:
Linux:
vmImage: ubuntu-16.04
MacOS:
vmImage: macos-latest
Windows:
vmImage: vs2017-win2016
pool:
vmImage: $(vmImage)
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- template: azure-patch-crates.yml
- script: cargo install cargo-hack
displayName: Install cargo-hack
# Check each feature works properly
# * --each-feature
# run for each feature which includes --no-default-features and default features of package
# * -Z avoid-dev-deps
# build without dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866
# tracking-issue: https://github.com/rust-lang/cargo/issues/5133
- script: cargo hack check --all --each-feature -Z avoid-dev-deps
displayName: cargo hack check --all --each-feature
-14
View File
@@ -1,14 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: Min supported Rust version
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- template: azure-patch-crates.yml
- script: cargo check --all
displayName: cargo check --all
-16
View File
@@ -1,16 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: Clippy
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- script: |
rustup component add clippy
cargo clippy --version
displayName: Install clippy
- script: |
cargo clippy --all --all-features
displayName: cargo clippy --all
-44
View File
@@ -1,44 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }}
strategy:
matrix:
i686:
vmImage: ubuntu-16.04
target: i686-unknown-linux-gnu
powerpc:
vmImage: ubuntu-16.04
target: powerpc-unknown-linux-gnu
powerpc64:
vmImage: ubuntu-16.04
target: powerpc64-unknown-linux-gnu
mips:
vmImage: ubuntu-16.04
target: mips-unknown-linux-gnu
arm:
vmImage: ubuntu-16.04
target: arm-linux-androideabi
pool:
vmImage: $(vmImage)
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- script: sudo apt-get update
displayName: apt-get update
- script: sudo apt-get install gcc-multilib
displayName: Install gcc-multilib
- script: cargo install cross
displayName: Install cross
# Always patch
- template: azure-patch-crates.yml
- script: cross check --all --exclude tokio-tls --target $(target)
displayName: Check source
# - script: cross check --tests --all --exclude tokio-tls --target $(target)
# displayName: Check tests
-39
View File
@@ -1,39 +0,0 @@
parameters:
dependsOn: []
jobs:
- job: documentation
displayName: 'Deploy API Documentation'
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
pool:
vmImage: 'Ubuntu 16.04'
dependsOn:
- ${{ parameters.dependsOn }}
steps:
- template: azure-install-rust.yml
parameters:
# rust_version: stable
rust_version: ${{ parameters.rust }}
- script: |
cargo doc --all --no-deps --all-features
cp -R target/doc '$(Build.BinariesDirectory)'
displayName: 'Generate Documentation'
- script: |
set -e
git --version
ls -la
git init
git config user.name 'Deployment Bot (from Azure Pipelines)'
git config user.email '[email protected]'
git config --global credential.helper 'store --file ~/.my-credentials'
printf "protocol=https\nhost=github.com\nusername=carllerche\npassword=%s\n\n" "$GITHUB_TOKEN" | git credential-store --file ~/.my-credentials store
git remote add origin https://github.com/tokio-rs/tokio
git checkout -b gh-pages
git add .
git commit -m 'Deploy Tokio API documentation'
git push -f origin gh-pages
env:
GITHUB_TOKEN: $(githubPersonalToken)
workingDirectory: '$(Build.BinariesDirectory)'
displayName: 'Deploy Documentation'
-40
View File
@@ -1,40 +0,0 @@
steps:
# Linux and macOS.
- script: |
set -e
if [ "$RUSTUP_TOOLCHAIN" == "nightly" ]; then
echo "++ getting latest miri version"
export RUSTUP_TOOLCHAIN="nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)"
echo "$RUSTUP_TOOLCHAIN"
fi
curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal --default-toolchain none
export PATH=$PATH:$HOME/.cargo/bin
rustup toolchain install $RUSTUP_TOOLCHAIN
rustup default $RUSTUP_TOOLCHAIN
echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
env:
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
displayName: "Install rust (*nix)"
condition: not(eq(variables['Agent.OS'], 'Windows_NT'))
# Windows.
- script: |
curl -sSf -o rustup-init.exe https://win.rustup.rs
rustup-init.exe -y --profile minimal --default-toolchain none
set PATH=%PATH%;%USERPROFILE%\.cargo\bin
rustup toolchain install %RUSTUP_TOOLCHAIN%
rustup default %RUSTUP_TOOLCHAIN%
echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
env:
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
displayName: "Install rust (windows)"
condition: eq(variables['Agent.OS'], 'Windows_NT')
# All platforms.
- script: |
rustup toolchain list
rustc -Vv
cargo -V
displayName: Query rust and cargo versions
-9
View File
@@ -1,9 +0,0 @@
steps:
- bash: |
set -e
if git log --no-merges -1 --format='%B' | grep -qF '[ci-release]'; then
echo "##vso[task.setvariable variable=isRelease]true"
fi
failOnStderr: true
displayName: Check if release commit
-29
View File
@@ -1,29 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: Loom tests
strategy:
matrix:
rest:
scope: --skip loom_pool
pool_group_a:
scope: loom_pool::group_a
pool_group_b:
scope: loom_pool::group_b
pool_group_c:
scope: loom_pool::group_c
pool_group_d:
scope: loom_pool::group_d
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- script: RUSTFLAGS="--cfg loom" cargo test --lib --release --features "full" -- --nocapture $(scope)
env:
LOOM_MAX_PREEMPTIONS: 2
CI: 'True'
displayName: $(scope)
workingDirectory: $(Build.SourcesDirectory)/tokio
-23
View File
@@ -1,23 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: Miri
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: nightly
- script: |
rustup component add miri
cargo miri setup
rm -rf $(Build.SourcesDirectory)/tokio/tests
displayName: Install miri
# TODO: enable all tests once they pass
- script: cargo miri test --features rt-core,rt-threaded,rt-util,sync -- -- task
env:
CI: 'True'
displayName: cargo miri test
workingDirectory: $(Build.SourcesDirectory)/tokio
-16
View File
@@ -1,16 +0,0 @@
steps:
- script: |
set -e
# Remove any existing patch statements
mv Cargo.toml Cargo.toml.bck
sed -n '/\[patch.crates-io\]/q;p' Cargo.toml.bck > Cargo.toml
# Patch all crates
cat ci/patch.toml >> Cargo.toml
# Print `Cargo.toml` for debugging
echo "~~~~ Cargo.toml ~~~~"
cat Cargo.toml
echo "~~~~~~~~~~~~~~~~~~~~"
displayName: Patch Cargo.toml
-18
View File
@@ -1,18 +0,0 @@
jobs:
# Check formatting
- job: ${{ parameters.name }}
displayName: Check rustfmt
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- script: |
rustup component add rustfmt
cargo fmt --version
displayName: Install rustfmt
- script: |
# Workaround for rust-lang/cargo#7732
rustfmt --check --edition 2018 $(find . -name '*.rs' -print)
displayName: Check formatting
-17
View File
@@ -1,17 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }}
pool:
vmImage: 'Ubuntu 16.04'
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- script: cargo install cargo-hack
displayName: Install cargo-hack
- script: cargo hack test --each-feature
displayName: cargo hack test --each-feature
workingDirectory: $(Build.SourcesDirectory)/tests-build
-28
View File
@@ -1,28 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }}
strategy:
matrix:
Linux:
vmImage: ubuntu-16.04
MacOS:
vmImage: macos-latest
Windows:
vmImage: vs2017-win2016
pool:
vmImage: $(vmImage)
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- script: cargo install cargo-hack
displayName: Install cargo-hack
# Run with all crate features
- script: cargo hack test --each-feature
env:
CI: 'True'
displayName: cargo hack test --each-feature
workingDirectory: $(Build.SourcesDirectory)/tests-integration
-19
View File
@@ -1,19 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }}
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- template: azure-patch-crates.yml
- script: cargo check --all
displayName: cargo check --all
# Check benches
- script: cargo check --benches --all
displayName: Check benchmarks
-47
View File
@@ -1,47 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }}
strategy:
matrix:
Linux:
vmImage: ubuntu-16.04
${{ if parameters.cross }}:
MacOS:
vmImage: macos-latest
Windows:
vmImage: vs2017-win2016
pool:
vmImage: $(vmImage)
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- template: azure-is-release.yml
- ${{ each crate in parameters.crates }}:
# Run with all crate features
- script: cargo test --all-features
env:
RUST_BACKTRACE: 1
CI: 'True'
displayName: ${{ crate }} - cargo test --all-features
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
# Check benches
- script: cargo check --all-features --benches
displayName: ${{ crate }} - cargo check --benches
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
- template: azure-patch-crates.yml
- ${{ each crate in parameters.crates }}:
# Run with all crate features
- script: cargo test --all-features
env:
RUST_BACKTRACE: 1
CI: 'True'
displayName: ${{ crate }} - cargo test --all-features
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
-34
View File
@@ -1,34 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: TSAN
strategy:
matrix:
Timer:
cmd: cargo test -p tokio-timer --test hammer
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- template: azure-patch-crates.yml
- script: |
set -e
# Make sure the benchmarks compile
export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0"
export TSAN_OPTIONS="suppressions=`pwd`/ci/tsan"
export RUST_BACKTRACE=1
# Run address sanitizer
RUSTFLAGS="-Z sanitizer=address" \
$(cmd) --target x86_64-unknown-linux-gnu
# Run thread sanitizer
RUSTFLAGS="-Z sanitizer=thread" \
$(cmd) --target x86_64-unknown-linux-gnu
displayName: TSAN / MSAN
env:
TSAN: yes
-8
View File
@@ -1,8 +0,0 @@
# Patch dependencies to run all tests against versions of the crate in the
# repository.
[patch.crates-io]
tokio = { path = "tokio" }
tokio-macros = { path = "tokio-macros" }
tokio-test = { path = "tokio-test" }
tokio-tls = { path = "tokio-tls" }
tokio-util = { path = "tokio-util" }
-39
View File
@@ -1,39 +0,0 @@
# TSAN suppressions file for Tokio
# TSAN does not understand fences and `Arc::drop` is implemented using a fence.
# This causes many false positives.
race:Arc*drop
race:Weak*drop
# `std` mpsc is not used in any Tokio code base. This race is triggered by some
# rust runtime logic.
race:std*mpsc_queue
race:std*lang_start
race:drop*std::thread*
# Probably more fences in std.
race:__call_tls_dtors
# The epoch-based GC uses fences.
race:crossbeam_epoch
# Push and steal operations in crossbeam-deque may cause data races, but such
# data races are safe. If a data race happens, the value read by `steal` is
# forgotten and the steal operation is then retried.
race:crossbeam_deque*push
race:crossbeam_deque*steal
# This filters out expected data race in the Treiber stack implementations.
# Treiber stacks are inherently racy. The pop operation will attempt to access
# the "next" pointer on the node it is attempting to pop. However, at this
# point it has not gained ownership of the node and another thread might beat
# it and take ownership of the node first (touching the next pointer). The
# original pop operation will fail due to the ABA guard, but tsan still picks
# up the access on the next pointer.
race:Backup::next_sleeper
race:Backup::set_next_sleeper
race:WorkerEntry::set_next_sleeper
# This ignores a false positive caused by `thread::park()`/`thread::unpark()`.
# See: https://github.com/rust-lang/rust/pull/54806#issuecomment-436193353
race:pthread_cond_destroy
+3 -1
View File
@@ -7,7 +7,9 @@ edition = "2018"
# If you copy one of the examples into a new project, you should be using
# [dependencies] instead.
[dev-dependencies]
tokio = { version = "0.2.0", path = "../tokio", features = ["full"] }
tokio = { version = "0.2.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"
futures = "0.3.0"
+4 -1
View File
@@ -13,8 +13,11 @@ A good starting point for the examples would be [`hello_world`](hello_world.rs)
and [`echo`](echo.rs). Additionally [the tokio website][tokioweb] contains
additional guides for some of the examples.
For a larger "real world" example, see the [`mini-redis`][redis] repository.
If you've got an example you'd like to see here, please feel free to open an
issue. Otherwise if you've got an example you'd like to add, please feel free
to make a PR!
[tokioweb]: https://tokio.rs/docs/overview/
[tokioweb]: https://tokio.rs/tokio/tutorial
[redis]: https://github.com/tokio-rs/mini-redis
+29 -7
View File
@@ -43,6 +43,26 @@ use std::task::{Context, Poll};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter};
// Configure a `tracing` subscriber that logs traces emitted by the chat
// server.
tracing_subscriber::fmt()
// Filter what traces are displayed based on the RUST_LOG environment
// variable.
//
// Traces emitted by the example code will always be displayed. You
// can set `RUST_LOG=tokio=trace` to enable additional traces emitted by
// Tokio itself.
.with_env_filter(EnvFilter::from_default_env().add_directive("chat=info".parse()?))
// Log events when `tracing` spans are created, entered, exited, or
// closed. When Tokio's internal tracing support is enabled (as
// described above), this can be used to track the lifecycle of spawned
// tasks on the Tokio runtime.
.with_span_events(FmtSpan::FULL)
// Set this subscriber as the default, to collect all traces emitted by
// the program.
.init();
// Create the shared state. This is how all the peers communicate.
//
// The server task will hold a handle to this. For every new client, the
@@ -59,7 +79,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Note that this is the Tokio TcpListener, which is fully async.
let mut listener = TcpListener::bind(&addr).await?;
println!("server running on {}", addr);
tracing::info!("server running on {}", addr);
loop {
// Asynchronously wait for an inbound TcpStream.
@@ -70,8 +90,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Spawn our handler to be run asynchronously.
tokio::spawn(async move {
tracing::debug!("accepted connection");
if let Err(e) = process(state, stream, addr).await {
println!("an error occurred; error = {:?}", e);
tracing::info!("an error occurred; error = {:?}", e);
}
});
}
@@ -200,7 +221,7 @@ async fn process(
Some(Ok(line)) => line,
// We didn't get a line so we return early here.
_ => {
println!("Failed to get username from {}. Client disconnected.", addr);
tracing::error!("Failed to get username from {}. Client disconnected.", addr);
return Ok(());
}
};
@@ -212,7 +233,7 @@ async fn process(
{
let mut state = state.lock().await;
let msg = format!("{} has joined the chat", username);
println!("{}", msg);
tracing::info!("{}", msg);
state.broadcast(addr, &msg).await;
}
@@ -233,9 +254,10 @@ async fn process(
peer.lines.send(&msg).await?;
}
Err(e) => {
println!(
tracing::error!(
"an error occurred while processing messages for {}; error = {:?}",
username, e
username,
e
);
}
}
@@ -248,7 +270,7 @@ async fn process(
state.peers.remove(&addr);
let msg = format!("{} has left the chat", username);
println!("{}", msg);
tracing::info!("{}", msg);
state.broadcast(addr, &msg).await;
}
-1
View File
@@ -15,7 +15,6 @@
use std::error::Error;
use std::net::SocketAddr;
use std::{env, io};
use tokio;
use tokio::net::UdpSocket;
struct Server {
-1
View File
@@ -21,7 +21,6 @@
#![warn(rust_2018_idioms)]
use tokio;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
+10 -2
View File
@@ -23,6 +23,7 @@
#![warn(rust_2018_idioms)]
use tokio::io;
use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream};
use futures::future::try_join;
@@ -63,8 +64,15 @@ async fn transfer(mut inbound: TcpStream, proxy_addr: String) -> Result<(), Box<
let (mut ri, mut wi) = inbound.split();
let (mut ro, mut wo) = outbound.split();
let client_to_server = io::copy(&mut ri, &mut wo);
let server_to_client = io::copy(&mut ro, &mut wi);
let client_to_server = async {
io::copy(&mut ri, &mut wo).await?;
wo.shutdown().await
};
let server_to_client = async {
io::copy(&mut ro, &mut wi).await?;
wi.shutdown().await
};
try_join(client_to_server, server_to_client).await?;
-1
View File
@@ -18,7 +18,6 @@ use futures::SinkExt;
use http::{header::HeaderValue, Request, Response, StatusCode};
#[macro_use]
extern crate serde_derive;
use serde_json;
use std::{env, error::Error, fmt, io};
use tokio::net::{TcpListener, TcpStream};
use tokio::stream::StreamExt;
+28 -9
View File
@@ -1,31 +1,50 @@
# 0.2.5 (February 27, 2019)
# 0.2.6 (November 11, 2020)
### Changes
- disambiguate the built-in `#[test]` attribute in macro expansion ([#2503])
- warn about renaming the Tokio dependency ([#2521])
- various documentation changes ([#2683], [#2697])
# 0.2.5 (February 27, 2020)
### Fixed
- doc improvements (#2225).
- doc improvements ([#2225]).
# 0.2.4 (January 27, 2019)
# 0.2.4 (January 27, 2020)
### Fixed
- generics on `#[tokio::main]` function (#2177).
- generics on `#[tokio::main]` function ([#2177]).
### Added
- support for `tokio::select!` (#2152).
- support for `tokio::select!` ([#2152]).
# 0.2.3 (January 7, 2019)
# 0.2.3 (January 7, 2020)
### Fixed
- Revert breaking change.
# 0.2.2 (January 7, 2019)
# 0.2.2 (January 7, 2020)
### Added
- General refactoring and inclusion of additional runtime options (#2022 and #2038)
- General refactoring and inclusion of additional runtime options ([#2022] and [#2038])
# 0.2.1 (December 18, 2019)
### Fixes
- inherit visibility when wrapping async fn (#1954).
- inherit visibility when wrapping async fn ([#1954]).
# 0.2.0 (November 26, 2019)
- Initial release
[#2697]: https://github.com/tokio-rs/tokio/pull/2697
[#2683]: https://github.com/tokio-rs/tokio/pull/2683
[#2521]: https://github.com/tokio-rs/tokio/pull/2521
[#2503]: https://github.com/tokio-rs/tokio/pull/2503
[#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
+3 -3
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.2.x" git tag.
version = "0.2.6"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-macros/0.2.5/tokio_macros"
documentation = "https://docs.rs/tokio-macros/0.2.6/tokio_macros"
description = """
Tokio's proc macros.
"""
+5 -3
View File
@@ -1,3 +1,5 @@
#![allow(clippy::unnecessary_lazy_evaluations)]
use proc_macro::TokenStream;
use quote::quote;
use std::num::NonZeroUsize;
@@ -142,7 +144,7 @@ fn parse_knobs(
let header = {
if is_test {
quote! {
#[test]
#[::core::prelude::v1::test]
}
} else {
quote! {}
@@ -334,14 +336,14 @@ pub(crate) mod old {
let result = match runtime {
Runtime::Threaded => quote! {
#[test]
#[::core::prelude::v1::test]
#(#attrs)*
#vis fn #name() #ret {
tokio::runtime::Runtime::new().unwrap().block_on(async { #body })
}
},
Runtime::Basic | Runtime::Auto => quote! {
#[test]
#[::core::prelude::v1::test]
#(#attrs)*
#vis fn #name() #ret {
tokio::runtime::Builder::new()
+163 -7
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.2.6")]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@@ -6,7 +6,7 @@
rust_2018_idioms,
unreachable_pub
)]
#![deny(intra_doc_link_resolution_failure)]
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
@@ -24,13 +24,18 @@ mod select;
use proc_macro::TokenStream;
/// Marks async function to be executed by selected runtime.
/// 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:
///
/// If you want to set the number of worker threads used for asynchronous code, use the
/// `core_threads` option.
///
/// - `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`).
///
/// ## Function arguments:
///
@@ -47,21 +52,88 @@ use proc_macro::TokenStream;
/// }
/// ```
///
/// ### Set number of core threads
/// Equivalent code not using `#[tokio::main]`
///
/// ```rust
/// #[tokio::main(core_threads = 1)]
/// fn main() {
/// tokio::runtime::Builder::new()
/// .threaded_scheduler()
/// .enable_all()
/// .build()
/// .unwrap()
/// .block_on(async {
/// println!("Hello world");
/// })
/// }
/// ```
///
/// ### Using basic scheduler
///
/// The basic scheduler is single-threaded.
///
/// ```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()
/// .enable_all()
/// .build()
/// .unwrap()
/// .block_on(async {
/// println!("Hello world");
/// })
/// }
/// ```
///
/// ### Set number of core threads
///
/// ```rust
/// #[tokio::main(core_threads = 2)]
/// async fn main() {
/// println!("Hello world");
/// }
/// ```
///
/// Equivalent code not using `#[tokio::main]`
///
/// ```rust
/// fn main() {
/// tokio::runtime::Builder::new()
/// .threaded_scheduler()
/// .core_threads(2)
/// .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_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args, item, true)
}
/// Marks async function to be executed by selected runtime.
/// 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:
///
@@ -83,6 +155,18 @@ pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
/// }
/// ```
///
/// Equivalent code not using `#[tokio::main]`
///
/// ```rust
/// fn main() {
/// tokio::runtime::Runtime::new()
/// .unwrap()
/// .block_on(async {
/// println!("Hello world");
/// })
/// }
/// ```
///
/// ### Select runtime
///
/// ```rust
@@ -91,13 +175,38 @@ pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
/// 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(args: TokenStream, item: TokenStream) -> TokenStream {
entry::old::main(args, item)
}
/// Marks async function to be executed by selected runtime.
/// 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:
///
@@ -117,6 +226,29 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
/// 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 {
@@ -149,6 +281,14 @@ pub fn main_basic(args: TokenStream, item: TokenStream) -> TokenStream {
/// assert!(true);
/// }
/// ```
///
/// ### 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_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, true)
@@ -180,6 +320,14 @@ pub fn test_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
/// assert!(true);
/// }
/// ```
///
/// ### 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(args: TokenStream, item: TokenStream) -> TokenStream {
entry::old::test(args, item)
@@ -199,6 +347,14 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
/// assert!(true);
/// }
/// ```
///
/// ### 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)
+4
View File
@@ -1,3 +1,7 @@
# 0.2.1 (April 17, 2020)
- Add `Future` and `Stream` implementations for `task::Spawn<T>`.
# 0.2.0 (November 25, 2019)
- Initial release
+2 -2
View File
@@ -7,13 +7,13 @@ name = "tokio-test"
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "v0.2.x" git tag.
version = "0.2.0"
version = "0.2.1"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-test/0.2.0/tokio_test"
documentation = "https://docs.rs/tokio-test/0.2.1/tokio_test"
description = """
Testing utilities for Tokio- and futures-based code
"""
+1 -1
View File
@@ -12,7 +12,7 @@
//!
//! # Usage
//!
//! Attempting to write data that the mock isn't expected will result in a
//! Attempting to write data that the mock isn't expecting will result in a
//! panic.
//!
//! [`AsyncRead`]: tokio::io::AsyncRead
+2 -2
View File
@@ -1,11 +1,11 @@
#![doc(html_root_url = "https://docs.rs/tokio-test/0.2.0")]
#![doc(html_root_url = "https://docs.rs/tokio-test/0.2.1")]
#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub
)]
#![deny(intra_doc_link_resolution_failure)]
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
+18 -12
View File
@@ -46,21 +46,11 @@ const SLEEP: usize = 2;
impl<T> Spawn<T> {
/// Consumes `self` returning the inner value
pub fn into_inner(mut self) -> T
pub fn into_inner(self) -> T
where
T: Unpin,
{
drop(self.task);
// Pin::into_inner is unstable, so we work around it
//
// Safety: `T` is bound by `Unpin`.
unsafe {
let ptr = Pin::get_mut(self.future.as_mut()) as *mut T;
let future = Box::from_raw(ptr);
mem::forget(self.future);
*future
}
*Pin::into_inner(self.future)
}
/// Returns `true` if the inner future has received a wake notification
@@ -116,6 +106,22 @@ impl<T: Stream> Spawn<T> {
}
}
impl<T: Future> Future for Spawn<T> {
type Output = T::Output;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.future.as_mut().poll(cx)
}
}
impl<T: Stream> Stream for Spawn<T> {
type Item = T::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.future.as_mut().poll_next(cx)
}
}
impl MockTask {
/// Creates new mock task
fn new() -> Self {
-36
View File
@@ -1,36 +0,0 @@
# 0.3.0 (November 26, 2019)
- Updates for tokio 0.2 release
# 0.3.0-alpha.6 (September 30, 2019)
- Move to `futures-*-preview 0.3.0-alpha.19`
- Move to `pin-project 0.4`
# 0.3.0-alpha.5 (September 19, 2019)
### Added
- `TlsStream::get_ref` and `TlsStream::get_mut` (#1537).
# 0.3.0-alpha.4 (August 30, 2019)
### Changed
- Track `tokio` 0.2.0-alpha.4
# 0.3.0-alpha.2 (August 17, 2019)
### Changed
- Update `futures` dependency to 0.3.0-alpha.18.
# 0.3.0-alpha.1 (August 8, 2019)
### Changed
- Switch to `async`, `await`, and `std::future`.
# 0.2.1 (January 6, 2019)
* Implement `Clone` for `TlsConnector` and `TlsAcceptor` (#777)
# 0.2.0 (August 8, 2018)
* Initial release with `tokio` support.
-63
View File
@@ -1,63 +0,0 @@
[package]
name = "tokio-tls"
# When releasing to crates.io:
# - Remove path dependencies
# - Update html_root_url.
# - Update doc url
# - Cargo.toml
# - README.md
# - Update CHANGELOG.md.
# - 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-tls/0.3.0-alpha.6/tokio_tls/"
description = """
An implementation of TLS/SSL streams for Tokio giving an implementation of TLS
for nonblocking I/O streams.
"""
categories = ["asynchronous", "network-programming"]
[badges]
travis-ci = { repository = "tokio-rs/tokio-tls" }
[dependencies]
native-tls = "0.2"
tokio = { version = "0.2.0", path = "../tokio" }
[dev-dependencies]
tokio = { version = "0.2.0", path = "../tokio", features = ["macros", "stream", "rt-core", "io-util", "net"] }
tokio-util = { version = "0.3.0", path = "../tokio-util", features = ["full"] }
cfg-if = "0.1"
env_logger = { version = "0.6", default-features = false }
futures = { version = "0.3.0", features = ["async-await"] }
[target.'cfg(all(not(target_os = "macos"), not(windows), not(target_os = "ios")))'.dev-dependencies]
openssl = "0.10"
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dev-dependencies]
security-framework = "0.2"
[target.'cfg(windows)'.dev-dependencies]
schannel = "0.1"
[target.'cfg(windows)'.dev-dependencies.winapi]
version = "0.3"
features = [
"lmcons",
"basetsd",
"minwinbase",
"minwindef",
"ntdef",
"sysinfoapi",
"timezoneapi",
"wincrypt",
"winerror",
]
[package.metadata.docs.rs]
all-features = true
-25
View File
@@ -1,25 +0,0 @@
Copyright (c) 2019 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
-14
View File
@@ -1,14 +0,0 @@
# tokio-tls
An implementation of TLS/SSL streams for Tokio built on top of the [`native-tls`
crate]
## License
This project is licensed under the [MIT license](./LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
-40
View File
@@ -1,40 +0,0 @@
// #![warn(rust_2018_idioms)]
use native_tls::TlsConnector;
use std::error::Error;
use std::net::ToSocketAddrs;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio_tls;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let addr = "www.rust-lang.org:443"
.to_socket_addrs()?
.next()
.ok_or("failed to resolve www.rust-lang.org")?;
let socket = TcpStream::connect(&addr).await?;
let cx = TlsConnector::builder().build()?;
let cx = tokio_tls::TlsConnector::from(cx);
let mut socket = cx.connect("www.rust-lang.org", socket).await?;
socket
.write_all(
"\
GET / HTTP/1.0\r\n\
Host: www.rust-lang.org\r\n\
\r\n\
"
.as_bytes(),
)
.await?;
let mut data = Vec::new();
socket.read_to_end(&mut data).await?;
// println!("data: {:?}", &data);
println!("{}", String::from_utf8_lossy(&data[..]));
Ok(())
}
Binary file not shown.
-55
View File
@@ -1,55 +0,0 @@
#![warn(rust_2018_idioms)]
// A tiny async TLS echo server with Tokio
use native_tls;
use native_tls::Identity;
use tokio;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
use tokio_tls;
/**
an example to setup a tls server.
how to test:
wget https://127.0.0.1:12345 --no-check-certificate
*/
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Bind the server's socket
let addr = "127.0.0.1:12345".to_string();
let mut tcp: TcpListener = TcpListener::bind(&addr).await?;
// Create the TLS acceptor.
let der = include_bytes!("identity.p12");
let cert = Identity::from_pkcs12(der, "mypass")?;
let tls_acceptor =
tokio_tls::TlsAcceptor::from(native_tls::TlsAcceptor::builder(cert).build()?);
loop {
// Asynchronously wait for an inbound socket.
let (socket, remote_addr) = tcp.accept().await?;
let tls_acceptor = tls_acceptor.clone();
println!("accept connection from {}", remote_addr);
tokio::spawn(async move {
// Accept the TLS connection.
let mut tls_stream = tls_acceptor.accept(socket).await.expect("accept error");
// In a loop, read data from the socket and write the data back.
let mut buf = [0; 1024];
let n = tls_stream
.read(&mut buf)
.await
.expect("failed to read data from socket");
if n == 0 {
return;
}
println!("read={}", unsafe {
String::from_utf8_unchecked(buf[0..n].into())
});
tls_stream
.write_all(&buf[0..n])
.await
.expect("failed to write data to socket");
});
}
}
-361
View File
@@ -1,361 +0,0 @@
#![doc(html_root_url = "https://docs.rs/tokio-tls/0.3.0")]
#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub
)]
#![deny(intra_doc_link_resolution_failure)]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]
//! Async TLS streams
//!
//! This library is an implementation of TLS streams using the most appropriate
//! system library by default for negotiating the connection. That is, on
//! Windows this library uses SChannel, on OSX it uses SecureTransport, and on
//! other platforms it uses OpenSSL.
//!
//! Each TLS stream implements the `Read` and `Write` traits to interact and
//! interoperate with the rest of the futures I/O ecosystem. Client connections
//! initiated from this crate verify hostnames automatically and by default.
//!
//! This crate primarily exports this ability through two newtypes,
//! `TlsConnector` and `TlsAcceptor`. These newtypes augment the
//! functionality provided by the `native-tls` crate, on which this crate is
//! built. Configuration of TLS parameters is still primarily done through the
//! `native-tls` crate.
use tokio::io::{AsyncRead, AsyncWrite};
use native_tls::{Error, HandshakeError, MidHandshakeTlsStream};
use std::fmt;
use std::future::Future;
use std::io::{self, Read, Write};
use std::marker::Unpin;
use std::mem::MaybeUninit;
use std::pin::Pin;
use std::ptr::null_mut;
use std::task::{Context, Poll};
#[derive(Debug)]
struct AllowStd<S> {
inner: S,
context: *mut (),
}
/// A wrapper around an underlying raw stream which implements the TLS or SSL
/// protocol.
///
/// A `TlsStream<S>` represents a handshake that has been completed successfully
/// and both the server and the client are ready for receiving and sending
/// data. Bytes read from a `TlsStream` are decrypted from `S` and bytes written
/// to a `TlsStream` are encrypted when passing through to `S`.
#[derive(Debug)]
pub struct TlsStream<S>(native_tls::TlsStream<AllowStd<S>>);
/// A wrapper around a `native_tls::TlsConnector`, providing an async `connect`
/// method.
#[derive(Clone)]
pub struct TlsConnector(native_tls::TlsConnector);
/// A wrapper around a `native_tls::TlsAcceptor`, providing an async `accept`
/// method.
#[derive(Clone)]
pub struct TlsAcceptor(native_tls::TlsAcceptor);
struct MidHandshake<S>(Option<MidHandshakeTlsStream<AllowStd<S>>>);
enum StartedHandshake<S> {
Done(TlsStream<S>),
Mid(MidHandshakeTlsStream<AllowStd<S>>),
}
struct StartedHandshakeFuture<F, S>(Option<StartedHandshakeFutureInner<F, S>>);
struct StartedHandshakeFutureInner<F, S> {
f: F,
stream: S,
}
struct Guard<'a, S>(&'a mut TlsStream<S>)
where
AllowStd<S>: Read + Write;
impl<S> Drop for Guard<'_, S>
where
AllowStd<S>: Read + Write,
{
fn drop(&mut self) {
(self.0).0.get_mut().context = null_mut();
}
}
// *mut () context is neither Send nor Sync
unsafe impl<S: Send> Send for AllowStd<S> {}
unsafe impl<S: Sync> Sync for AllowStd<S> {}
impl<S> AllowStd<S>
where
S: Unpin,
{
fn with_context<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut Context<'_>, Pin<&mut S>) -> R,
{
unsafe {
assert!(!self.context.is_null());
let waker = &mut *(self.context as *mut _);
f(waker, Pin::new(&mut self.inner))
}
}
}
impl<S> Read for AllowStd<S>
where
S: AsyncRead + Unpin,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.with_context(|ctx, stream| stream.poll_read(ctx, buf)) {
Poll::Ready(r) => r,
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
}
}
}
impl<S> Write for AllowStd<S>
where
S: AsyncWrite + Unpin,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self.with_context(|ctx, stream| stream.poll_write(ctx, buf)) {
Poll::Ready(r) => r,
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
}
}
fn flush(&mut self) -> io::Result<()> {
match self.with_context(|ctx, stream| stream.poll_flush(ctx)) {
Poll::Ready(r) => r,
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
}
}
}
fn cvt<T>(r: io::Result<T>) -> Poll<io::Result<T>> {
match r {
Ok(v) => Poll::Ready(Ok(v)),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending,
Err(e) => Poll::Ready(Err(e)),
}
}
impl<S> TlsStream<S> {
fn with_context<F, R>(&mut self, ctx: &mut Context<'_>, f: F) -> R
where
F: FnOnce(&mut native_tls::TlsStream<AllowStd<S>>) -> R,
AllowStd<S>: Read + Write,
{
self.0.get_mut().context = ctx as *mut _ as *mut ();
let g = Guard(self);
f(&mut (g.0).0)
}
/// Returns a shared reference to the inner stream.
pub fn get_ref(&self) -> &S
where
S: AsyncRead + AsyncWrite + Unpin,
{
&self.0.get_ref().inner
}
/// Returns a mutable reference to the inner stream.
pub fn get_mut(&mut self) -> &mut S
where
S: AsyncRead + AsyncWrite + Unpin,
{
&mut self.0.get_mut().inner
}
}
impl<S> AsyncRead for TlsStream<S>
where
S: AsyncRead + AsyncWrite + Unpin,
{
unsafe fn prepare_uninitialized_buffer(&self, _: &mut [MaybeUninit<u8>]) -> bool {
// Note that this does not forward to `S` because the buffer is
// unconditionally filled in by OpenSSL, not the actual object `S`.
// We're decrypting bytes from `S` into the buffer above!
false
}
fn poll_read(
mut self: Pin<&mut Self>,
ctx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
self.with_context(ctx, |s| cvt(s.read(buf)))
}
}
impl<S> AsyncWrite for TlsStream<S>
where
S: AsyncRead + AsyncWrite + Unpin,
{
fn poll_write(
mut self: Pin<&mut Self>,
ctx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
self.with_context(ctx, |s| cvt(s.write(buf)))
}
fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.with_context(ctx, |s| cvt(s.flush()))
}
fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
match self.with_context(ctx, |s| s.shutdown()) {
Ok(()) => Poll::Ready(Ok(())),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending,
Err(e) => Poll::Ready(Err(e)),
}
}
}
async fn handshake<F, S>(f: F, stream: S) -> Result<TlsStream<S>, Error>
where
F: FnOnce(
AllowStd<S>,
) -> Result<native_tls::TlsStream<AllowStd<S>>, HandshakeError<AllowStd<S>>>
+ Unpin,
S: AsyncRead + AsyncWrite + Unpin,
{
let start = StartedHandshakeFuture(Some(StartedHandshakeFutureInner { f, stream }));
match start.await {
Err(e) => Err(e),
Ok(StartedHandshake::Done(s)) => Ok(s),
Ok(StartedHandshake::Mid(s)) => MidHandshake(Some(s)).await,
}
}
impl<F, S> Future for StartedHandshakeFuture<F, S>
where
F: FnOnce(
AllowStd<S>,
) -> Result<native_tls::TlsStream<AllowStd<S>>, HandshakeError<AllowStd<S>>>
+ Unpin,
S: Unpin,
AllowStd<S>: Read + Write,
{
type Output = Result<StartedHandshake<S>, Error>;
fn poll(
mut self: Pin<&mut Self>,
ctx: &mut Context<'_>,
) -> Poll<Result<StartedHandshake<S>, Error>> {
let inner = self.0.take().expect("future polled after completion");
let stream = AllowStd {
inner: inner.stream,
context: ctx as *mut _ as *mut (),
};
match (inner.f)(stream) {
Ok(mut s) => {
s.get_mut().context = null_mut();
Poll::Ready(Ok(StartedHandshake::Done(TlsStream(s))))
}
Err(HandshakeError::WouldBlock(mut s)) => {
s.get_mut().context = null_mut();
Poll::Ready(Ok(StartedHandshake::Mid(s)))
}
Err(HandshakeError::Failure(e)) => Poll::Ready(Err(e)),
}
}
}
impl TlsConnector {
/// Connects the provided stream with this connector, assuming the provided
/// domain.
///
/// This function will internally call `TlsConnector::connect` to connect
/// the stream and returns a future representing the resolution of the
/// connection operation. The returned future will resolve to either
/// `TlsStream<S>` or `Error` depending if it's successful or not.
///
/// This is typically used for clients who have already established, for
/// example, a TCP connection to a remote server. That stream is then
/// provided here to perform the client half of a connection to a
/// TLS-powered server.
pub async fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, Error>
where
S: AsyncRead + AsyncWrite + Unpin,
{
handshake(move |s| self.0.connect(domain, s), stream).await
}
}
impl fmt::Debug for TlsConnector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TlsConnector").finish()
}
}
impl From<native_tls::TlsConnector> for TlsConnector {
fn from(inner: native_tls::TlsConnector) -> TlsConnector {
TlsConnector(inner)
}
}
impl TlsAcceptor {
/// Accepts a new client connection with the provided stream.
///
/// This function will internally call `TlsAcceptor::accept` to connect
/// the stream and returns a future representing the resolution of the
/// connection operation. The returned future will resolve to either
/// `TlsStream<S>` or `Error` depending if it's successful or not.
///
/// This is typically used after a new socket has been accepted from a
/// `TcpListener`. That socket is then passed to this function to perform
/// the server half of accepting a client connection.
pub async fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, Error>
where
S: AsyncRead + AsyncWrite + Unpin,
{
handshake(move |s| self.0.accept(s), stream).await
}
}
impl fmt::Debug for TlsAcceptor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TlsAcceptor").finish()
}
}
impl From<native_tls::TlsAcceptor> for TlsAcceptor {
fn from(inner: native_tls::TlsAcceptor) -> TlsAcceptor {
TlsAcceptor(inner)
}
}
impl<S: AsyncRead + AsyncWrite + Unpin> Future for MidHandshake<S> {
type Output = Result<TlsStream<S>, Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut_self = self.get_mut();
let mut s = mut_self.0.take().expect("future polled after completion");
s.get_mut().context = cx as *mut _ as *mut ();
match s.handshake() {
Ok(stream) => Poll::Ready(Ok(TlsStream(stream))),
Err(HandshakeError::Failure(e)) => Poll::Ready(Err(e)),
Err(HandshakeError::WouldBlock(mut s)) => {
s.get_mut().context = null_mut();
mut_self.0 = Some(s);
Poll::Pending
}
}
}
}
-124
View File
@@ -1,124 +0,0 @@
#![warn(rust_2018_idioms)]
use cfg_if::cfg_if;
use env_logger;
use native_tls::TlsConnector;
use std::io::{self, Error};
use std::net::ToSocketAddrs;
use tokio::net::TcpStream;
use tokio_tls;
macro_rules! t {
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
}
};
}
cfg_if! {
if #[cfg(feature = "force-rustls")] {
fn verify_failed(err: &Error, s: &str) {
let err = err.to_string();
assert!(err.contains(s), "bad error: {}", err);
}
fn assert_expired_error(err: &Error) {
verify_failed(err, "CertExpired");
}
fn assert_wrong_host(err: &Error) {
verify_failed(err, "CertNotValidForName");
}
fn assert_self_signed(err: &Error) {
verify_failed(err, "UnknownIssuer");
}
fn assert_untrusted_root(err: &Error) {
verify_failed(err, "UnknownIssuer");
}
} else if #[cfg(any(feature = "force-openssl",
all(not(target_os = "macos"),
not(target_os = "windows"),
not(target_os = "ios"))))] {
fn verify_failed(err: &Error) {
assert!(format!("{}", err).contains("certificate verify failed"))
}
use verify_failed as assert_expired_error;
use verify_failed as assert_wrong_host;
use verify_failed as assert_self_signed;
use verify_failed as assert_untrusted_root;
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
fn assert_invalid_cert_chain(err: &Error) {
assert!(format!("{}", err).contains("was not trusted."))
}
use crate::assert_invalid_cert_chain as assert_expired_error;
use crate::assert_invalid_cert_chain as assert_wrong_host;
use crate::assert_invalid_cert_chain as assert_self_signed;
use crate::assert_invalid_cert_chain as assert_untrusted_root;
} else {
fn assert_expired_error(err: &Error) {
let s = err.to_string();
assert!(s.contains("system clock"), "error = {:?}", s);
}
fn assert_wrong_host(err: &Error) {
let s = err.to_string();
assert!(s.contains("CN name"), "error = {:?}", s);
}
fn assert_self_signed(err: &Error) {
let s = err.to_string();
assert!(s.contains("root certificate which is not trusted"), "error = {:?}", s);
}
use assert_self_signed as assert_untrusted_root;
}
}
async fn get_host(host: &'static str) -> Error {
drop(env_logger::try_init());
let addr = format!("{}:443", host);
let addr = t!(addr.to_socket_addrs()).next().unwrap();
let socket = t!(TcpStream::connect(&addr).await);
let builder = TlsConnector::builder();
let cx = t!(builder.build());
let cx = tokio_tls::TlsConnector::from(cx);
let res = cx
.connect(host, socket)
.await
.map_err(|e| Error::new(io::ErrorKind::Other, e));
assert!(res.is_err());
res.err().unwrap()
}
#[tokio::test]
async fn expired() {
assert_expired_error(&get_host("expired.badssl.com").await)
}
// TODO: the OSX builders on Travis apparently fail this tests spuriously?
// passes locally though? Seems... bad!
#[tokio::test]
#[cfg_attr(all(target_os = "macos", feature = "force-openssl"), ignore)]
async fn wrong_host() {
assert_wrong_host(&get_host("wrong.host.badssl.com").await)
}
#[tokio::test]
async fn self_signed() {
assert_self_signed(&get_host("self-signed.badssl.com").await)
}
#[tokio::test]
async fn untrusted_root() {
assert_untrusted_root(&get_host("untrusted-root.badssl.com").await)
}
-102
View File
@@ -1,102 +0,0 @@
#![warn(rust_2018_idioms)]
use cfg_if::cfg_if;
use env_logger;
use native_tls;
use native_tls::TlsConnector;
use std::io;
use std::net::ToSocketAddrs;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio_tls;
macro_rules! t {
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
}
};
}
cfg_if! {
if #[cfg(feature = "force-rustls")] {
fn assert_bad_hostname_error(err: &io::Error) {
let err = err.to_string();
assert!(err.contains("CertNotValidForName"), "bad error: {}", err);
}
} else if #[cfg(any(feature = "force-openssl",
all(not(target_os = "macos"),
not(target_os = "windows"),
not(target_os = "ios"))))] {
fn assert_bad_hostname_error(err: &io::Error) {
let err = err.get_ref().unwrap();
let err = err.downcast_ref::<native_tls::Error>().unwrap();
assert!(format!("{}", err).contains("certificate verify failed"));
}
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
fn assert_bad_hostname_error(err: &io::Error) {
let err = err.get_ref().unwrap();
let err = err.downcast_ref::<native_tls::Error>().unwrap();
assert!(format!("{}", err).contains("was not trusted."));
}
} else {
fn assert_bad_hostname_error(err: &io::Error) {
let err = err.get_ref().unwrap();
let err = err.downcast_ref::<native_tls::Error>().unwrap();
assert!(format!("{}", err).contains("CN name"));
}
}
}
#[tokio::test]
async fn fetch_google() {
drop(env_logger::try_init());
// First up, resolve google.com
let addr = t!("google.com:443".to_socket_addrs()).next().unwrap();
let socket = TcpStream::connect(&addr).await.unwrap();
// Send off the request by first negotiating an SSL handshake, then writing
// of our request, then flushing, then finally read off the response.
let builder = TlsConnector::builder();
let connector = t!(builder.build());
let connector = tokio_tls::TlsConnector::from(connector);
let mut socket = t!(connector.connect("google.com", socket).await);
t!(socket.write_all(b"GET / HTTP/1.0\r\n\r\n").await);
let mut data = Vec::new();
t!(socket.read_to_end(&mut data).await);
// any response code is fine
assert!(data.starts_with(b"HTTP/1.0 "));
let data = String::from_utf8_lossy(&data);
let data = data.trim_end();
assert!(data.ends_with("</html>") || data.ends_with("</HTML>"));
}
fn native2io(e: native_tls::Error) -> io::Error {
io::Error::new(io::ErrorKind::Other, e)
}
// see comment in bad.rs for ignore reason
#[cfg_attr(all(target_os = "macos", feature = "force-openssl"), ignore)]
#[tokio::test]
async fn wrong_hostname_error() {
drop(env_logger::try_init());
let addr = t!("google.com:443".to_socket_addrs()).next().unwrap();
let socket = t!(TcpStream::connect(&addr).await);
let builder = TlsConnector::builder();
let connector = t!(builder.build());
let connector = tokio_tls::TlsConnector::from(connector);
let res = connector
.connect("rust-lang.org", socket)
.await
.map_err(native2io);
assert!(res.is_err());
assert_bad_hostname_error(&res.err().unwrap());
}
-629
View File
@@ -1,629 +0,0 @@
#![warn(rust_2018_idioms)]
use cfg_if::cfg_if;
use env_logger;
use futures::join;
use native_tls;
use native_tls::{Identity, TlsAcceptor, TlsConnector};
use std::io::Write;
use std::marker::Unpin;
use std::process::Command;
use std::ptr;
use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt, Error, ErrorKind};
use tokio::net::{TcpListener, TcpStream};
use tokio::stream::StreamExt;
use tokio_tls;
macro_rules! t {
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
}
};
}
#[allow(dead_code)]
struct Keys {
cert_der: Vec<u8>,
pkey_der: Vec<u8>,
pkcs12_der: Vec<u8>,
}
#[allow(dead_code)]
fn openssl_keys() -> &'static Keys {
static INIT: Once = Once::new();
static mut KEYS: *mut Keys = ptr::null_mut();
INIT.call_once(|| {
let path = t!(env::current_exe());
let path = path.parent().unwrap();
let keyfile = path.join("test.key");
let certfile = path.join("test.crt");
let config = path.join("openssl.config");
File::create(&config)
.unwrap()
.write_all(
b"\
[req]\n\
distinguished_name=dn\n\
[ dn ]\n\
CN=localhost\n\
[ ext ]\n\
basicConstraints=CA:FALSE,pathlen:0\n\
subjectAltName = @alt_names
extendedKeyUsage=serverAuth,clientAuth
[alt_names]
DNS.1 = localhost
",
)
.unwrap();
let subj = "/C=US/ST=Denial/L=Sprintfield/O=Dis/CN=localhost";
let output = t!(Command::new("openssl")
.arg("req")
.arg("-nodes")
.arg("-x509")
.arg("-newkey")
.arg("rsa:2048")
.arg("-config")
.arg(&config)
.arg("-extensions")
.arg("ext")
.arg("-subj")
.arg(subj)
.arg("-keyout")
.arg(&keyfile)
.arg("-out")
.arg(&certfile)
.arg("-days")
.arg("1")
.output());
assert!(output.status.success());
let crtout = t!(Command::new("openssl")
.arg("x509")
.arg("-outform")
.arg("der")
.arg("-in")
.arg(&certfile)
.output());
assert!(crtout.status.success());
let keyout = t!(Command::new("openssl")
.arg("rsa")
.arg("-outform")
.arg("der")
.arg("-in")
.arg(&keyfile)
.output());
assert!(keyout.status.success());
let pkcs12out = t!(Command::new("openssl")
.arg("pkcs12")
.arg("-export")
.arg("-nodes")
.arg("-inkey")
.arg(&keyfile)
.arg("-in")
.arg(&certfile)
.arg("-password")
.arg("pass:foobar")
.output());
assert!(pkcs12out.status.success());
let keys = Box::new(Keys {
cert_der: crtout.stdout,
pkey_der: keyout.stdout,
pkcs12_der: pkcs12out.stdout,
});
unsafe {
KEYS = Box::into_raw(keys);
}
});
unsafe { &*KEYS }
}
cfg_if! {
if #[cfg(feature = "rustls")] {
use webpki;
use untrusted;
use std::env;
use std::fs::File;
use std::process::Command;
use std::sync::Once;
use untrusted::Input;
use webpki::trust_anchor_util;
fn server_cx() -> io::Result<ServerContext> {
let mut cx = ServerContext::new();
let (cert, key) = keys();
cx.config_mut()
.set_single_cert(vec![cert.to_vec()], key.to_vec());
Ok(cx)
}
fn configure_client(cx: &mut ClientContext) {
let (cert, _key) = keys();
let cert = Input::from(cert);
let anchor = trust_anchor_util::cert_der_as_trust_anchor(cert).unwrap();
cx.config_mut().root_store.add_trust_anchors(&[anchor]);
}
// Like OpenSSL we generate certificates on the fly, but for OSX we
// also have to put them into a specific keychain. We put both the
// certificates and the keychain next to our binary.
//
// Right now I don't know of a way to programmatically create a
// self-signed certificate, so we just fork out to the `openssl` binary.
fn keys() -> (&'static [u8], &'static [u8]) {
static INIT: Once = Once::new();
static mut KEYS: *mut (Vec<u8>, Vec<u8>) = ptr::null_mut();
INIT.call_once(|| {
let (key, cert) = openssl_keys();
let path = t!(env::current_exe());
let path = path.parent().unwrap();
let keyfile = path.join("test.key");
let certfile = path.join("test.crt");
let config = path.join("openssl.config");
File::create(&config).unwrap().write_all(b"\
[req]\n\
distinguished_name=dn\n\
[ dn ]\n\
CN=localhost\n\
[ ext ]\n\
basicConstraints=CA:FALSE,pathlen:0\n\
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
").unwrap();
let subj = "/C=US/ST=Denial/L=Sprintfield/O=Dis/CN=localhost";
let output = t!(Command::new("openssl")
.arg("req")
.arg("-nodes")
.arg("-x509")
.arg("-newkey").arg("rsa:2048")
.arg("-config").arg(&config)
.arg("-extensions").arg("ext")
.arg("-subj").arg(subj)
.arg("-keyout").arg(&keyfile)
.arg("-out").arg(&certfile)
.arg("-days").arg("1")
.output());
assert!(output.status.success());
let crtout = t!(Command::new("openssl")
.arg("x509")
.arg("-outform").arg("der")
.arg("-in").arg(&certfile)
.output());
assert!(crtout.status.success());
let keyout = t!(Command::new("openssl")
.arg("rsa")
.arg("-outform").arg("der")
.arg("-in").arg(&keyfile)
.output());
assert!(keyout.status.success());
let cert = crtout.stdout;
let key = keyout.stdout;
unsafe {
KEYS = Box::into_raw(Box::new((cert, key)));
}
});
unsafe {
(&(*KEYS).0, &(*KEYS).1)
}
}
} else if #[cfg(any(feature = "force-openssl",
all(not(target_os = "macos"),
not(target_os = "windows"),
not(target_os = "ios"))))] {
use std::fs::File;
use std::env;
use std::sync::Once;
fn contexts() -> (tokio_tls::TlsAcceptor, tokio_tls::TlsConnector) {
let keys = openssl_keys();
let pkcs12 = t!(Identity::from_pkcs12(&keys.pkcs12_der, "foobar"));
let srv = TlsAcceptor::builder(pkcs12);
let cert = t!(native_tls::Certificate::from_der(&keys.cert_der));
let mut client = TlsConnector::builder();
t!(client.add_root_certificate(cert).build());
(t!(srv.build()).into(), t!(client.build()).into())
}
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
use std::env;
use std::fs::File;
use std::sync::Once;
fn contexts() -> (tokio_tls::TlsAcceptor, tokio_tls::TlsConnector) {
let keys = openssl_keys();
let pkcs12 = t!(Identity::from_pkcs12(&keys.pkcs12_der, "foobar"));
let srv = TlsAcceptor::builder(pkcs12);
let cert = native_tls::Certificate::from_der(&keys.cert_der).unwrap();
let mut client = TlsConnector::builder();
client.add_root_certificate(cert);
(t!(srv.build()).into(), t!(client.build()).into())
}
} else {
use schannel;
use winapi;
use std::env;
use std::fs::File;
use std::io;
use std::mem;
use std::sync::Once;
use schannel::cert_context::CertContext;
use schannel::cert_store::{CertStore, CertAdd, Memory};
use winapi::shared::basetsd::*;
use winapi::shared::lmcons::*;
use winapi::shared::minwindef::*;
use winapi::shared::ntdef::WCHAR;
use winapi::um::minwinbase::*;
use winapi::um::sysinfoapi::*;
use winapi::um::timezoneapi::*;
use winapi::um::wincrypt::*;
const FRIENDLY_NAME: &str = "tokio-tls localhost testing cert";
fn contexts() -> (tokio_tls::TlsAcceptor, tokio_tls::TlsConnector) {
let cert = localhost_cert();
let mut store = t!(Memory::new()).into_store();
t!(store.add_cert(&cert, CertAdd::Always));
let pkcs12_der = t!(store.export_pkcs12("foobar"));
let pkcs12 = t!(Identity::from_pkcs12(&pkcs12_der, "foobar"));
let srv = TlsAcceptor::builder(pkcs12);
let client = TlsConnector::builder();
(t!(srv.build()).into(), t!(client.build()).into())
}
// ====================================================================
// Magic!
//
// Lots of magic is happening here to wrangle certificates for running
// these tests on Windows. For more information see the test suite
// in the schannel-rs crate as this is just coyping that.
//
// The general gist of this though is that the only way to add custom
// trusted certificates is to add it to the system store of trust. To
// do that we go through the whole rigamarole here to generate a new
// self-signed certificate and then insert that into the system store.
//
// This generates some dialogs, so we print what we're doing sometimes,
// and otherwise we just manage the ephemeral certificates. Because
// they're in the system store we always ensure that they're only valid
// for a small period of time (e.g. 1 day).
fn localhost_cert() -> CertContext {
static INIT: Once = Once::new();
INIT.call_once(|| {
for cert in local_root_store().certs() {
let name = match cert.friendly_name() {
Ok(name) => name,
Err(_) => continue,
};
if name != FRIENDLY_NAME {
continue
}
if !cert.is_time_valid().unwrap() {
io::stdout().write_all(br#"
The tokio-tls test suite is about to delete an old copy of one of its
certificates from your root trust store. This certificate was only valid for one
day and it is no longer needed. The host should be "localhost" and the
description should mention "tokio-tls".
"#).unwrap();
cert.delete().unwrap();
} else {
return
}
}
install_certificate().unwrap();
});
for cert in local_root_store().certs() {
let name = match cert.friendly_name() {
Ok(name) => name,
Err(_) => continue,
};
if name == FRIENDLY_NAME {
return cert
}
}
panic!("couldn't find a cert");
}
fn local_root_store() -> CertStore {
if env::var("CI").is_ok() {
CertStore::open_local_machine("Root").unwrap()
} else {
CertStore::open_current_user("Root").unwrap()
}
}
fn install_certificate() -> io::Result<CertContext> {
unsafe {
let mut provider = 0;
let mut hkey = 0;
let mut buffer = "tokio-tls test suite".encode_utf16()
.chain(Some(0))
.collect::<Vec<_>>();
let res = CryptAcquireContextW(&mut provider,
buffer.as_ptr(),
ptr::null_mut(),
PROV_RSA_FULL,
CRYPT_MACHINE_KEYSET);
if res != TRUE {
// create a new key container (since it does not exist)
let res = CryptAcquireContextW(&mut provider,
buffer.as_ptr(),
ptr::null_mut(),
PROV_RSA_FULL,
CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET);
if res != TRUE {
return Err(Error::last_os_error())
}
}
// create a new keypair (RSA-2048)
let res = CryptGenKey(provider,
AT_SIGNATURE,
0x0800<<16 | CRYPT_EXPORTABLE,
&mut hkey);
if res != TRUE {
return Err(Error::last_os_error());
}
// start creating the certificate
let name = "CN=localhost,O=tokio-tls,OU=tokio-tls,\
G=tokio_tls".encode_utf16()
.chain(Some(0))
.collect::<Vec<_>>();
let mut cname_buffer: [WCHAR; UNLEN as usize + 1] = mem::zeroed();
let mut cname_len = cname_buffer.len() as DWORD;
let res = CertStrToNameW(X509_ASN_ENCODING,
name.as_ptr(),
CERT_X500_NAME_STR,
ptr::null_mut(),
cname_buffer.as_mut_ptr() as *mut u8,
&mut cname_len,
ptr::null_mut());
if res != TRUE {
return Err(Error::last_os_error());
}
let mut subject_issuer = CERT_NAME_BLOB {
cbData: cname_len,
pbData: cname_buffer.as_ptr() as *mut u8,
};
let mut key_provider = CRYPT_KEY_PROV_INFO {
pwszContainerName: buffer.as_mut_ptr(),
pwszProvName: ptr::null_mut(),
dwProvType: PROV_RSA_FULL,
dwFlags: CRYPT_MACHINE_KEYSET,
cProvParam: 0,
rgProvParam: ptr::null_mut(),
dwKeySpec: AT_SIGNATURE,
};
let mut sig_algorithm = CRYPT_ALGORITHM_IDENTIFIER {
pszObjId: szOID_RSA_SHA256RSA.as_ptr() as *mut _,
Parameters: mem::zeroed(),
};
let mut expiration_date: SYSTEMTIME = mem::zeroed();
GetSystemTime(&mut expiration_date);
let mut file_time: FILETIME = mem::zeroed();
let res = SystemTimeToFileTime(&expiration_date,
&mut file_time);
if res != TRUE {
return Err(Error::last_os_error());
}
let mut timestamp: u64 = file_time.dwLowDateTime as u64 |
(file_time.dwHighDateTime as u64) << 32;
// one day, timestamp unit is in 100 nanosecond intervals
timestamp += (1E9 as u64) / 100 * (60 * 60 * 24);
file_time.dwLowDateTime = timestamp as u32;
file_time.dwHighDateTime = (timestamp >> 32) as u32;
let res = FileTimeToSystemTime(&file_time,
&mut expiration_date);
if res != TRUE {
return Err(Error::last_os_error());
}
// create a self signed certificate
let cert_context = CertCreateSelfSignCertificate(
0 as ULONG_PTR,
&mut subject_issuer,
0,
&mut key_provider,
&mut sig_algorithm,
ptr::null_mut(),
&mut expiration_date,
ptr::null_mut());
if cert_context.is_null() {
return Err(Error::last_os_error());
}
// TODO: this is.. a terrible hack. Right now `schannel`
// doesn't provide a public method to go from a raw
// cert context pointer to the `CertContext` structure it
// has, so we just fake it here with a transmute. This'll
// probably break at some point, but hopefully by then
// it'll have a method to do this!
struct MyCertContext<T>(T);
impl<T> Drop for MyCertContext<T> {
fn drop(&mut self) {}
}
let cert_context = MyCertContext(cert_context);
let cert_context: CertContext = mem::transmute(cert_context);
cert_context.set_friendly_name(FRIENDLY_NAME)?;
// install the certificate to the machine's local store
io::stdout().write_all(br#"
The tokio-tls test suite is about to add a certificate to your set of root
and trusted certificates. This certificate should be for the domain "localhost"
with the description related to "tokio-tls". This certificate is only valid
for one day and will be automatically deleted if you re-run the tokio-tls
test suite later.
"#).unwrap();
local_root_store().add_cert(&cert_context,
CertAdd::ReplaceExisting)?;
Ok(cert_context)
}
}
}
}
const AMT: usize = 128 * 1024;
async fn copy_data<W: AsyncWrite + Unpin>(mut w: W) -> Result<usize, Error> {
let mut data = vec![9; AMT as usize];
let mut amt = 0;
while !data.is_empty() {
let written = w.write(&data).await?;
if written <= data.len() {
amt += written;
data.resize(data.len() - written, 0);
} else {
w.write_all(&data).await?;
amt += data.len();
break;
}
println!("remaining: {}", data.len());
}
Ok(amt)
}
#[tokio::test]
async fn client_to_server() {
drop(env_logger::try_init());
// Create a server listening on a port, then figure out what that port is
let mut srv = t!(TcpListener::bind("127.0.0.1:0").await);
let addr = t!(srv.local_addr());
let (server_cx, client_cx) = contexts();
// Create a future to accept one socket, connect the ssl stream, and then
// read all the data from it.
let server = async move {
let mut incoming = srv.incoming();
let socket = t!(incoming.next().await.unwrap());
let mut socket = t!(server_cx.accept(socket).await);
let mut data = Vec::new();
t!(socket.read_to_end(&mut data).await);
data
};
// Create a future to connect to our server, connect the ssl stream, and
// then write a bunch of data to it.
let client = async move {
let socket = t!(TcpStream::connect(&addr).await);
let socket = t!(client_cx.connect("localhost", socket).await);
copy_data(socket).await
};
// Finally, run everything!
let (data, _) = join!(server, client);
// assert_eq!(amt, AMT);
assert!(data == vec![9; AMT]);
}
#[tokio::test]
async fn server_to_client() {
drop(env_logger::try_init());
// Create a server listening on a port, then figure out what that port is
let mut srv = t!(TcpListener::bind("127.0.0.1:0").await);
let addr = t!(srv.local_addr());
let (server_cx, client_cx) = contexts();
let server = async move {
let mut incoming = srv.incoming();
let socket = t!(incoming.next().await.unwrap());
let socket = t!(server_cx.accept(socket).await);
copy_data(socket).await
};
let client = async move {
let socket = t!(TcpStream::connect(&addr).await);
let mut socket = t!(client_cx.connect("localhost", socket).await);
let mut data = Vec::new();
t!(socket.read_to_end(&mut data).await);
data
};
// Finally, run everything!
let (_, data) = join!(server, client);
// assert_eq!(amt, AMT);
assert!(data == vec![9; AMT]);
}
#[tokio::test]
async fn one_byte_at_a_time() {
const AMT: usize = 1024;
drop(env_logger::try_init());
let mut srv = t!(TcpListener::bind("127.0.0.1:0").await);
let addr = t!(srv.local_addr());
let (server_cx, client_cx) = contexts();
let server = async move {
let mut incoming = srv.incoming();
let socket = t!(incoming.next().await.unwrap());
let mut socket = t!(server_cx.accept(socket).await);
let mut amt = 0;
for b in std::iter::repeat(9).take(AMT) {
let data = [b as u8];
t!(socket.write_all(&data).await);
amt += 1;
}
amt
};
let client = async move {
let socket = t!(TcpStream::connect(&addr).await);
let mut socket = t!(client_cx.connect("localhost", socket).await);
let mut data = Vec::new();
loop {
let mut buf = [0; 1];
match socket.read_exact(&mut buf).await {
Ok(_) => data.extend_from_slice(&buf),
Err(ref err) if err.kind() == ErrorKind::UnexpectedEof => break,
Err(err) => panic!(err),
}
}
data
};
let (amt, data) = join!(server, client);
assert_eq!(amt, AMT);
assert!(data == vec![9; AMT as usize]);
}
+11 -5
View File
@@ -3,24 +3,30 @@
### Fixed
- Adjust minimum-supported Tokio version to v0.2.5 to account for an internal
dependency on features in that version of Tokio. (#2326)
dependency on features in that version of Tokio. ([#2326])
# 0.3.0 (March 4, 2020)
### Changed
- **Breaking Change**: Change `Encoder` trait to take a generic `Item` parameter, which allows
codec writers to pass references into `Framed` and `FramedWrite` types. (#1746)
codec writers to pass references into `Framed` and `FramedWrite` types. ([#1746])
### Added
- Add futures-io/tokio::io compatibility layer. (#2117)
- Add `Framed::with_capacity`. (#2215)
- Add futures-io/tokio::io compatibility layer. ([#2117])
- Add `Framed::with_capacity`. ([#2215])
### Fixed
- Use advance over split_to when data is not needed. (#2198)
- Use advance over split_to when data is not needed. ([#2198])
# 0.2.0 (November 26, 2019)
- Initial release
[#2326]: https://github.com/tokio-rs/tokio/pull/2326
[#2215]: https://github.com/tokio-rs/tokio/pull/2215
[#2198]: https://github.com/tokio-rs/tokio/pull/2198
[#2117]: https://github.com/tokio-rs/tokio/pull/2117
[#1746]: https://github.com/tokio-rs/tokio/pull/1746
+2 -1
View File
@@ -29,6 +29,7 @@ full = ["codec", "udp", "compat"]
compat = ["futures-io",]
codec = ["tokio/stream"]
udp = ["tokio/udp"]
rt = ["tokio/rt-core"]
[dependencies]
tokio = { version = "0.2.5", path = "../tokio" }
@@ -37,7 +38,7 @@ bytes = "0.5.0"
futures-core = "0.3.0"
futures-sink = "0.3.0"
futures-io = { version = "0.3.0", optional = true }
log = "0.4"
log = "0.4.6"
pin-project-lite = "0.1.4"
[dev-dependencies]
+10
View File
@@ -18,6 +18,16 @@ macro_rules! cfg_compat {
}
}
macro_rules! cfg_rt {
($($item:item)*) => {
$(
#[cfg(feature = "rt")]
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
$item
)*
}
}
macro_rules! cfg_udp {
($($item:item)*) => {
$(
+53 -161
View File
@@ -1,10 +1,9 @@
use crate::codec::decoder::Decoder;
use crate::codec::encoder::Encoder;
use crate::codec::framed_read::{framed_read2, framed_read2_with_buffer, FramedRead2};
use crate::codec::framed_write::{framed_write2, framed_write2_with_buffer, FramedWrite2};
use crate::codec::framed_impl::{FramedImpl, RWFrames, ReadFrame, WriteFrame};
use tokio::{
io::{AsyncBufRead, AsyncRead, AsyncWrite},
io::{AsyncRead, AsyncWrite},
stream::Stream,
};
@@ -12,8 +11,7 @@ use bytes::BytesMut;
use futures_sink::Sink;
use pin_project_lite::pin_project;
use std::fmt;
use std::io::{self, BufRead, Read, Write};
use std::mem::MaybeUninit;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -30,37 +28,7 @@ pin_project! {
/// [`Decoder::framed`]: crate::codec::Decoder::framed()
pub struct Framed<T, U> {
#[pin]
inner: FramedRead2<FramedWrite2<Fuse<T, U>>>,
}
}
pin_project! {
pub(crate) struct Fuse<T, U> {
#[pin]
pub(crate) io: T,
pub(crate) codec: U,
}
}
/// Abstracts over `FramedRead2` being either `FramedRead2<FramedWrite2<Fuse<T, U>>>` or
/// `FramedRead2<Fuse<T, U>>` and lets the io and codec parts be extracted in either case.
pub(crate) trait ProjectFuse {
type Io;
type Codec;
fn project(self: Pin<&mut Self>) -> Fuse<Pin<&mut Self::Io>, &mut Self::Codec>;
}
impl<T, U> ProjectFuse for Fuse<T, U> {
type Io = T;
type Codec = U;
fn project(self: Pin<&mut Self>) -> Fuse<Pin<&mut Self::Io>, &mut Self::Codec> {
let self_ = self.project();
Fuse {
io: self_.io,
codec: self_.codec,
}
inner: FramedImpl<T, U, RWFrames>
}
}
@@ -93,7 +61,11 @@ where
/// [`split`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.split
pub fn new(inner: T, codec: U) -> Framed<T, U> {
Framed {
inner: framed_read2(framed_write2(Fuse { io: inner, codec })),
inner: FramedImpl {
inner,
codec,
state: Default::default(),
},
}
}
@@ -123,10 +95,18 @@ where
/// [`split`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.split
pub fn with_capacity(inner: T, codec: U, capacity: usize) -> Framed<T, U> {
Framed {
inner: framed_read2_with_buffer(
framed_write2(Fuse { io: inner, codec }),
BytesMut::with_capacity(capacity),
),
inner: FramedImpl {
inner,
codec,
state: RWFrames {
read: ReadFrame {
eof: false,
is_readable: false,
buffer: BytesMut::with_capacity(capacity),
},
write: WriteFrame::default(),
},
},
}
}
}
@@ -161,16 +141,14 @@ impl<T, U> Framed<T, U> {
/// [`split`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.split
pub fn from_parts(parts: FramedParts<T, U>) -> Framed<T, U> {
Framed {
inner: framed_read2_with_buffer(
framed_write2_with_buffer(
Fuse {
io: parts.io,
codec: parts.codec,
},
parts.write_buf,
),
parts.read_buf,
),
inner: FramedImpl {
inner: parts.io,
codec: parts.codec,
state: RWFrames {
read: parts.read_buf.into(),
write: parts.write_buf.into(),
},
},
}
}
@@ -181,7 +159,7 @@ impl<T, U> Framed<T, U> {
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_ref(&self) -> &T {
&self.inner.get_ref().get_ref().io
&self.inner.inner
}
/// Returns a mutable reference to the underlying I/O stream wrapped by
@@ -191,7 +169,7 @@ impl<T, U> Framed<T, U> {
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner.get_mut().get_mut().io
&mut self.inner.inner
}
/// Returns a reference to the underlying codec wrapped by
@@ -200,7 +178,7 @@ impl<T, U> Framed<T, U> {
/// 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) -> &U {
&self.inner.get_ref().get_ref().codec
&self.inner.codec
}
/// Returns a mutable reference to the underlying codec wrapped by
@@ -209,12 +187,17 @@ impl<T, U> Framed<T, U> {
/// 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 U {
&mut self.inner.get_mut().get_mut().codec
&mut self.inner.codec
}
/// Returns a reference to the read buffer.
pub fn read_buffer(&self) -> &BytesMut {
self.inner.buffer()
&self.inner.state.read.buffer
}
/// Returns a mutable reference to the read buffer.
pub fn read_buffer_mut(&mut self) -> &mut BytesMut {
&mut self.inner.state.read.buffer
}
/// Consumes the `Framed`, returning its underlying I/O stream.
@@ -223,7 +206,7 @@ impl<T, U> Framed<T, U> {
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn into_inner(self) -> T {
self.inner.into_inner().into_inner().io
self.inner.inner
}
/// Consumes the `Framed`, returning its underlying I/O stream, the buffer
@@ -233,19 +216,17 @@ impl<T, U> Framed<T, U> {
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn into_parts(self) -> FramedParts<T, U> {
let (inner, read_buf) = self.inner.into_parts();
let (inner, write_buf) = inner.into_parts();
FramedParts {
io: inner.io,
codec: inner.codec,
read_buf,
write_buf,
io: self.inner.inner,
codec: self.inner.codec,
read_buf: self.inner.state.read.buffer,
write_buf: self.inner.state.write.buffer,
_priv: (),
}
}
}
// This impl just defers to the underlying FramedImpl
impl<T, U> Stream for Framed<T, U>
where
T: AsyncRead,
@@ -258,6 +239,7 @@ where
}
}
// This impl just defers to the underlying FramedImpl
impl<T, I, U> Sink<I> for Framed<T, U>
where
T: AsyncWrite,
@@ -267,19 +249,19 @@ where
type Error = U::Error;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project().inner.get_pin_mut().poll_ready(cx)
self.project().inner.poll_ready(cx)
}
fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
self.project().inner.get_pin_mut().start_send(item)
self.project().inner.start_send(item)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project().inner.get_pin_mut().poll_flush(cx)
self.project().inner.poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project().inner.get_pin_mut().poll_close(cx)
self.project().inner.poll_close(cx)
}
}
@@ -290,109 +272,19 @@ where
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Framed")
.field("io", &self.inner.get_ref().get_ref().io)
.field("codec", &self.inner.get_ref().get_ref().codec)
.field("io", self.get_ref())
.field("codec", self.codec())
.finish()
}
}
// ===== impl Fuse =====
impl<T: Read, U> Read for Fuse<T, U> {
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
self.io.read(dst)
}
}
impl<T: BufRead, U> BufRead for Fuse<T, U> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.io.fill_buf()
}
fn consume(&mut self, amt: usize) {
self.io.consume(amt)
}
}
impl<T: AsyncRead, U> AsyncRead for Fuse<T, U> {
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [MaybeUninit<u8>]) -> bool {
self.io.prepare_uninitialized_buffer(buf)
}
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<Result<usize, io::Error>> {
self.project().io.poll_read(cx, buf)
}
}
impl<T: AsyncBufRead, U> AsyncBufRead for Fuse<T, U> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
self.project().io.poll_fill_buf(cx)
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.project().io.consume(amt)
}
}
impl<T: Write, U> Write for Fuse<T, U> {
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
self.io.write(src)
}
fn flush(&mut self) -> io::Result<()> {
self.io.flush()
}
}
impl<T: AsyncWrite, U> AsyncWrite for Fuse<T, U> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
self.project().io.poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
self.project().io.poll_flush(cx)
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
self.project().io.poll_shutdown(cx)
}
}
impl<T, U: Decoder> Decoder for Fuse<T, U> {
type Item = U::Item;
type Error = U::Error;
fn decode(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
self.codec.decode(buffer)
}
fn decode_eof(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
self.codec.decode_eof(buffer)
}
}
impl<T, I, U: Encoder<I>> Encoder<I> for Fuse<T, U> {
type Error = U::Error;
fn encode(&mut self, item: I, dst: &mut BytesMut) -> Result<(), Self::Error> {
self.codec.encode(item, dst)
}
}
/// `FramedParts` contains an export of the data of a Framed transport.
/// It can be used to construct a new [`Framed`] with a different codec.
/// It contains all current buffers and the inner transport.
///
/// [`Framed`]: crate::codec::Framed
#[derive(Debug)]
#[allow(clippy::manual_non_exhaustive)]
pub struct FramedParts<T, U> {
/// The inner transport used to read bytes to and write bytes to
pub io: T,
+225
View File
@@ -0,0 +1,225 @@
use crate::codec::decoder::Decoder;
use crate::codec::encoder::Encoder;
use tokio::{
io::{AsyncRead, AsyncWrite},
stream::Stream,
};
use bytes::{Buf, BytesMut};
use futures_core::ready;
use futures_sink::Sink;
use log::trace;
use pin_project_lite::pin_project;
use std::borrow::{Borrow, BorrowMut};
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
pin_project! {
#[derive(Debug)]
pub(crate) struct FramedImpl<T, U, State> {
#[pin]
pub(crate) inner: T,
pub(crate) state: State,
pub(crate) codec: U,
}
}
const INITIAL_CAPACITY: usize = 8 * 1024;
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
pub(crate) struct ReadFrame {
pub(crate) eof: bool,
pub(crate) is_readable: bool,
pub(crate) buffer: BytesMut,
}
pub(crate) struct WriteFrame {
pub(crate) buffer: BytesMut,
}
#[derive(Default)]
pub(crate) struct RWFrames {
pub(crate) read: ReadFrame,
pub(crate) write: WriteFrame,
}
impl Default for ReadFrame {
fn default() -> Self {
Self {
eof: false,
is_readable: false,
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
}
}
}
impl Default for WriteFrame {
fn default() -> Self {
Self {
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
}
}
}
impl From<BytesMut> for ReadFrame {
fn from(mut buffer: BytesMut) -> Self {
let size = buffer.capacity();
if size < INITIAL_CAPACITY {
buffer.reserve(INITIAL_CAPACITY - size);
}
Self {
buffer,
is_readable: size > 0,
eof: false,
}
}
}
impl From<BytesMut> for WriteFrame {
fn from(mut buffer: BytesMut) -> Self {
let size = buffer.capacity();
if size < INITIAL_CAPACITY {
buffer.reserve(INITIAL_CAPACITY - size);
}
Self { buffer }
}
}
impl Borrow<ReadFrame> for RWFrames {
fn borrow(&self) -> &ReadFrame {
&self.read
}
}
impl BorrowMut<ReadFrame> for RWFrames {
fn borrow_mut(&mut self) -> &mut ReadFrame {
&mut self.read
}
}
impl Borrow<WriteFrame> for RWFrames {
fn borrow(&self) -> &WriteFrame {
&self.write
}
}
impl BorrowMut<WriteFrame> for RWFrames {
fn borrow_mut(&mut self) -> &mut WriteFrame {
&mut self.write
}
}
impl<T, U, R> Stream for FramedImpl<T, U, R>
where
T: AsyncRead,
U: Decoder,
R: BorrowMut<ReadFrame>,
{
type Item = Result<U::Item, U::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut pinned = self.project();
let state: &mut ReadFrame = pinned.state.borrow_mut();
loop {
// Repeatedly call `decode` or `decode_eof` as long as it is
// "readable". Readable is defined as not having returned `None`. If
// the upstream has returned EOF, and the decoder is no longer
// readable, it can be assumed that the decoder will never become
// readable again, at which point the stream is terminated.
if state.is_readable {
if state.eof {
let frame = pinned.codec.decode_eof(&mut state.buffer)?;
return Poll::Ready(frame.map(Ok));
}
trace!("attempting to decode a frame");
if let Some(frame) = pinned.codec.decode(&mut state.buffer)? {
trace!("frame decoded from buffer");
return Poll::Ready(Some(Ok(frame)));
}
state.is_readable = false;
}
assert!(!state.eof);
// Otherwise, try to read more data and try again. Make sure we've
// got room for at least one byte to read to ensure that we don't
// get a spurious 0 that looks like EOF
state.buffer.reserve(1);
let bytect = match pinned.inner.as_mut().poll_read_buf(cx, &mut state.buffer)? {
Poll::Ready(ct) => ct,
Poll::Pending => return Poll::Pending,
};
if bytect == 0 {
state.eof = true;
}
state.is_readable = true;
}
}
}
impl<T, I, U, W> Sink<I> for FramedImpl<T, U, W>
where
T: AsyncWrite,
U: Encoder<I>,
U::Error: From<io::Error>,
W: BorrowMut<WriteFrame>,
{
type Error = U::Error;
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
if self.state.borrow().buffer.len() >= BACKPRESSURE_BOUNDARY {
self.as_mut().poll_flush(cx)
} else {
Poll::Ready(Ok(()))
}
}
fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
let pinned = self.project();
pinned
.codec
.encode(item, &mut pinned.state.borrow_mut().buffer)?;
Ok(())
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
trace!("flushing framed transport");
let mut pinned = self.project();
while !pinned.state.borrow_mut().buffer.is_empty() {
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))?;
if n == 0 {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::WriteZero,
"failed to \
write frame to transport",
)
.into()));
}
pinned.state.borrow_mut().buffer.advance(n);
}
// Try flushing the underlying IO
ready!(pinned.inner.poll_flush(cx))?;
trace!("framed transport flushed");
Poll::Ready(Ok(()))
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
ready!(self.as_mut().poll_flush(cx))?;
ready!(self.project().inner.poll_shutdown(cx))?;
Poll::Ready(Ok(()))
}
}
+30 -178
View File
@@ -1,11 +1,10 @@
use crate::codec::framed::{Fuse, ProjectFuse};
use crate::codec::framed_impl::{FramedImpl, ReadFrame};
use crate::codec::Decoder;
use tokio::{io::AsyncRead, stream::Stream};
use bytes::BytesMut;
use futures_sink::Sink;
use log::trace;
use pin_project_lite::pin_project;
use std::fmt;
use std::pin::Pin;
@@ -18,22 +17,10 @@ pin_project! {
/// [`AsyncRead`]: tokio::io::AsyncRead
pub struct FramedRead<T, D> {
#[pin]
inner: FramedRead2<Fuse<T, D>>,
inner: FramedImpl<T, D, ReadFrame>,
}
}
pin_project! {
pub(crate) struct FramedRead2<T> {
#[pin]
inner: T,
eof: bool,
is_readable: bool,
buffer: BytesMut,
}
}
const INITIAL_CAPACITY: usize = 8 * 1024;
// ===== impl FramedRead =====
impl<T, D> FramedRead<T, D>
@@ -44,10 +31,11 @@ where
/// Creates a new `FramedRead` with the given `decoder`.
pub fn new(inner: T, decoder: D) -> FramedRead<T, D> {
FramedRead {
inner: framed_read2(Fuse {
io: inner,
inner: FramedImpl {
inner,
codec: decoder,
}),
state: Default::default(),
},
}
}
@@ -55,13 +43,15 @@ where
/// initial size.
pub fn with_capacity(inner: T, decoder: D, capacity: usize) -> FramedRead<T, D> {
FramedRead {
inner: framed_read2_with_buffer(
Fuse {
io: inner,
codec: decoder,
inner: FramedImpl {
inner,
codec: decoder,
state: ReadFrame {
eof: false,
is_readable: false,
buffer: BytesMut::with_capacity(capacity),
},
BytesMut::with_capacity(capacity),
),
},
}
}
}
@@ -74,7 +64,7 @@ impl<T, D> FramedRead<T, D> {
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_ref(&self) -> &T {
&self.inner.inner.io
&self.inner.inner
}
/// Returns a mutable reference to the underlying I/O stream wrapped by
@@ -84,7 +74,7 @@ impl<T, D> FramedRead<T, D> {
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner.inner.io
&mut self.inner.inner
}
/// Consumes the `FramedRead`, returning its underlying I/O stream.
@@ -93,25 +83,26 @@ impl<T, D> FramedRead<T, D> {
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn into_inner(self) -> T {
self.inner.inner.io
self.inner.inner
}
/// Returns a reference to the underlying decoder.
pub fn decoder(&self) -> &D {
&self.inner.inner.codec
&self.inner.codec
}
/// Returns a mutable reference to the underlying decoder.
pub fn decoder_mut(&mut self) -> &mut D {
&mut self.inner.inner.codec
&mut self.inner.codec
}
/// Returns a reference to the read buffer.
pub fn read_buffer(&self) -> &BytesMut {
&self.inner.buffer
&self.inner.state.buffer
}
}
// This impl just defers to the underlying FramedImpl
impl<T, D> Stream for FramedRead<T, D>
where
T: AsyncRead,
@@ -132,43 +123,19 @@ where
type Error = T::Error;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project()
.inner
.project()
.inner
.project()
.io
.poll_ready(cx)
self.project().inner.project().inner.poll_ready(cx)
}
fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
self.project()
.inner
.project()
.inner
.project()
.io
.start_send(item)
self.project().inner.project().inner.start_send(item)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project()
.inner
.project()
.inner
.project()
.io
.poll_flush(cx)
self.project().inner.project().inner.poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project()
.inner
.project()
.inner
.project()
.io
.poll_close(cx)
self.project().inner.project().inner.poll_close(cx)
}
}
@@ -179,126 +146,11 @@ where
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FramedRead")
.field("inner", &self.inner.inner.io)
.field("decoder", &self.inner.inner.codec)
.field("eof", &self.inner.eof)
.field("is_readable", &self.inner.is_readable)
.field("buffer", &self.inner.buffer)
.field("inner", &self.get_ref())
.field("decoder", &self.decoder())
.field("eof", &self.inner.state.eof)
.field("is_readable", &self.inner.state.is_readable)
.field("buffer", &self.read_buffer())
.finish()
}
}
// ===== impl FramedRead2 =====
pub(crate) fn framed_read2<T>(inner: T) -> FramedRead2<T> {
FramedRead2 {
inner,
eof: false,
is_readable: false,
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
}
}
pub(crate) fn framed_read2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedRead2<T> {
if buf.capacity() < INITIAL_CAPACITY {
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
buf.reserve(bytes_to_reserve);
}
FramedRead2 {
inner,
eof: false,
is_readable: !buf.is_empty(),
buffer: buf,
}
}
impl<T> FramedRead2<T> {
pub(crate) fn get_ref(&self) -> &T {
&self.inner
}
pub(crate) fn into_inner(self) -> T {
self.inner
}
pub(crate) fn into_parts(self) -> (T, BytesMut) {
(self.inner, self.buffer)
}
pub(crate) fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
pub(crate) fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
self.project().inner
}
pub(crate) fn buffer(&self) -> &BytesMut {
&self.buffer
}
}
impl<T> Stream for FramedRead2<T>
where
T: ProjectFuse + AsyncRead,
T::Codec: Decoder,
{
type Item = Result<<T::Codec as Decoder>::Item, <T::Codec as Decoder>::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut pinned = self.project();
loop {
// Repeatedly call `decode` or `decode_eof` as long as it is
// "readable". Readable is defined as not having returned `None`. If
// the upstream has returned EOF, and the decoder is no longer
// readable, it can be assumed that the decoder will never become
// readable again, at which point the stream is terminated.
if *pinned.is_readable {
if *pinned.eof {
let frame = pinned
.inner
.as_mut()
.project()
.codec
.decode_eof(&mut pinned.buffer)?;
return Poll::Ready(frame.map(Ok));
}
trace!("attempting to decode a frame");
if let Some(frame) = pinned
.inner
.as_mut()
.project()
.codec
.decode(&mut pinned.buffer)?
{
trace!("frame decoded from buffer");
return Poll::Ready(Some(Ok(frame)));
}
*pinned.is_readable = false;
}
assert!(!*pinned.eof);
// Otherwise, try to read more data and try again. Make sure we've
// got room for at least one byte to read to ensure that we don't
// get a spurious 0 that looks like EOF
pinned.buffer.reserve(1);
let bytect = match pinned
.inner
.as_mut()
.poll_read_buf(cx, &mut pinned.buffer)?
{
Poll::Ready(ct) => ct,
Poll::Pending => return Poll::Pending,
};
if bytect == 0 {
*pinned.eof = true;
}
*pinned.is_readable = true;
}
}
}
+21 -215
View File
@@ -1,20 +1,12 @@
use crate::codec::decoder::Decoder;
use crate::codec::encoder::Encoder;
use crate::codec::framed::{Fuse, ProjectFuse};
use crate::codec::framed_impl::{FramedImpl, WriteFrame};
use tokio::{
io::{AsyncBufRead, AsyncRead, AsyncWrite},
stream::Stream,
};
use tokio::{io::AsyncWrite, stream::Stream};
use bytes::{Buf, BytesMut};
use futures_core::ready;
use futures_sink::Sink;
use log::trace;
use pin_project_lite::pin_project;
use std::fmt;
use std::io::{self, BufRead, Read};
use std::mem::MaybeUninit;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -24,21 +16,10 @@ pin_project! {
/// [`Sink`]: futures_sink::Sink
pub struct FramedWrite<T, E> {
#[pin]
inner: FramedWrite2<Fuse<T, E>>,
inner: FramedImpl<T, E, WriteFrame>,
}
}
pin_project! {
pub(crate) struct FramedWrite2<T> {
#[pin]
inner: T,
buffer: BytesMut,
}
}
const INITIAL_CAPACITY: usize = 8 * 1024;
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
impl<T, E> FramedWrite<T, E>
where
T: AsyncWrite,
@@ -46,10 +27,11 @@ where
/// Creates a new `FramedWrite` with the given `encoder`.
pub fn new(inner: T, encoder: E) -> FramedWrite<T, E> {
FramedWrite {
inner: framed_write2(Fuse {
io: inner,
inner: FramedImpl {
inner,
codec: encoder,
}),
state: WriteFrame::default(),
},
}
}
}
@@ -62,7 +44,7 @@ impl<T, E> FramedWrite<T, E> {
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_ref(&self) -> &T {
&self.inner.inner.io
&self.inner.inner
}
/// Returns a mutable reference to the underlying I/O stream wrapped by
@@ -72,7 +54,7 @@ impl<T, E> FramedWrite<T, E> {
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner.inner.io
&mut self.inner.inner
}
/// Consumes the `FramedWrite`, returning its underlying I/O stream.
@@ -81,21 +63,21 @@ impl<T, E> FramedWrite<T, E> {
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn into_inner(self) -> T {
self.inner.inner.io
self.inner.inner
}
/// Returns a reference to the underlying decoder.
/// Returns a reference to the underlying encoder.
pub fn encoder(&self) -> &E {
&self.inner.inner.codec
&self.inner.codec
}
/// Returns a mutable reference to the underlying decoder.
/// Returns a mutable reference to the underlying encoder.
pub fn encoder_mut(&mut self) -> &mut E {
&mut self.inner.inner.codec
&mut self.inner.codec
}
}
// This impl just defers to the underlying FramedWrite2
// This impl just defers to the underlying FramedImpl
impl<T, I, E> Sink<I> for FramedWrite<T, E>
where
T: AsyncWrite,
@@ -121,6 +103,7 @@ where
}
}
// This impl just defers to the underlying T: Stream
impl<T, D> Stream for FramedWrite<T, D>
where
T: Stream,
@@ -128,13 +111,7 @@ where
type Item = T::Item;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project()
.inner
.project()
.inner
.project()
.io
.poll_next(cx)
self.project().inner.project().inner.poll_next(cx)
}
}
@@ -145,180 +122,9 @@ where
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FramedWrite")
.field("inner", &self.inner.get_ref().io)
.field("encoder", &self.inner.get_ref().codec)
.field("buffer", &self.inner.buffer)
.field("inner", &self.get_ref())
.field("encoder", &self.encoder())
.field("buffer", &self.inner.state.buffer)
.finish()
}
}
// ===== impl FramedWrite2 =====
pub(crate) fn framed_write2<T>(inner: T) -> FramedWrite2<T> {
FramedWrite2 {
inner,
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
}
}
pub(crate) fn framed_write2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedWrite2<T> {
if buf.capacity() < INITIAL_CAPACITY {
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
buf.reserve(bytes_to_reserve);
}
FramedWrite2 { inner, buffer: buf }
}
impl<T> FramedWrite2<T> {
pub(crate) fn get_ref(&self) -> &T {
&self.inner
}
pub(crate) fn into_inner(self) -> T {
self.inner
}
pub(crate) fn into_parts(self) -> (T, BytesMut) {
(self.inner, self.buffer)
}
pub(crate) fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<I, T> Sink<I> for FramedWrite2<T>
where
T: ProjectFuse + AsyncWrite,
T::Codec: Encoder<I>,
{
type Error = <T::Codec as Encoder<I>>::Error;
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
// If the buffer is already over 8KiB, then attempt to flush it. If after flushing it's
// *still* over 8KiB, then apply backpressure (reject the send).
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
match self.as_mut().poll_flush(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Ready(Ok(())) => (),
};
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
return Poll::Pending;
}
}
Poll::Ready(Ok(()))
}
fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
let mut pinned = self.project();
pinned
.inner
.project()
.codec
.encode(item, &mut pinned.buffer)?;
Ok(())
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
trace!("flushing framed transport");
let mut pinned = self.project();
while !pinned.buffer.is_empty() {
trace!("writing; remaining={}", pinned.buffer.len());
let buf = &pinned.buffer;
let n = ready!(pinned.inner.as_mut().poll_write(cx, &buf))?;
if n == 0 {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::WriteZero,
"failed to \
write frame to transport",
)
.into()));
}
pinned.buffer.advance(n);
}
// Try flushing the underlying IO
ready!(pinned.inner.poll_flush(cx))?;
trace!("framed transport flushed");
Poll::Ready(Ok(()))
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
ready!(self.as_mut().poll_flush(cx))?;
ready!(self.project().inner.poll_shutdown(cx))?;
Poll::Ready(Ok(()))
}
}
impl<T: Decoder> Decoder for FramedWrite2<T> {
type Item = T::Item;
type Error = T::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
self.inner.decode(src)
}
fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
self.inner.decode_eof(src)
}
}
impl<T: Read> Read for FramedWrite2<T> {
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
self.inner.read(dst)
}
}
impl<T: BufRead> BufRead for FramedWrite2<T> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.inner.fill_buf()
}
fn consume(&mut self, amt: usize) {
self.inner.consume(amt)
}
}
impl<T: AsyncRead> AsyncRead for FramedWrite2<T> {
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [MaybeUninit<u8>]) -> bool {
self.inner.prepare_uninitialized_buffer(buf)
}
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<Result<usize, io::Error>> {
self.project().inner.poll_read(cx, buf)
}
}
impl<T: AsyncBufRead> AsyncBufRead for FramedWrite2<T> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
self.project().inner.poll_fill_buf(cx)
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.project().inner.consume(amt)
}
}
impl<T> ProjectFuse for FramedWrite2<T>
where
T: ProjectFuse,
{
type Io = T::Io;
type Codec = T::Codec;
fn project(self: Pin<&mut Self>) -> Fuse<Pin<&mut Self::Io>, &mut Self::Codec> {
self.project().inner.project()
}
}
+7 -7
View File
@@ -364,13 +364,13 @@
//! +------------+--------------+
//! ```
//!
//! [`LengthDelimitedCodec::new()`]: struct.LengthDelimitedCodec.html#method.new
//! [`FramedRead`]: struct.FramedRead.html
//! [`FramedWrite`]: struct.FramedWrite.html
//! [`AsyncRead`]: ../../trait.AsyncRead.html
//! [`AsyncWrite`]: ../../trait.AsyncWrite.html
//! [`Encoder`]: ../trait.Encoder.html
//! [`BytesMut`]: https://docs.rs/bytes/0.4/bytes/struct.BytesMut.html
//! [`LengthDelimitedCodec::new()`]: method@LengthDelimitedCodec::new
//! [`FramedRead`]: struct@FramedRead
//! [`FramedWrite`]: struct@FramedWrite
//! [`AsyncRead`]: trait@tokio::io::AsyncRead
//! [`AsyncWrite`]: trait@tokio::io::AsyncWrite
//! [`Encoder`]: trait@Encoder
//! [`BytesMut`]: bytes::BytesMut
use crate::codec::{Decoder, Encoder, Framed, FramedRead, FramedWrite};
+13 -4
View File
@@ -1,8 +1,13 @@
//! Utilities for encoding and decoding frames.
//! Adaptors from AsyncRead/AsyncWrite to Stream/Sink
//!
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
//! Framed streams are also known as transports.
//! Raw I/O objects work with byte sequences, but higher-level code
//! usually wants to batch these into meaningful chunks, called
//! "frames".
//!
//! This module contains adapters to go from streams of bytes,
//! [`AsyncRead`] and [`AsyncWrite`], to framed streams implementing
//! [`Sink`] and [`Stream`]. Framed streams are also known as
//! transports.
//!
//! [`AsyncRead`]: tokio::io::AsyncRead
//! [`AsyncWrite`]: tokio::io::AsyncWrite
@@ -18,6 +23,10 @@ pub use self::decoder::Decoder;
mod encoder;
pub use self::encoder::Encoder;
mod framed_impl;
#[allow(unused_imports)]
pub(crate) use self::framed_impl::{FramedImpl, RWFrames, ReadFrame, WriteFrame};
mod framed;
pub use self::framed::{Framed, FramedParts};
+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::Handle;
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<F> {
#[pin]
inner: F,
handle: Handle,
}
}
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;
handle.enter(|| fut.poll(cx))
}
}
/// Trait extension that simplifies bundling a `Handle` with a `Future`.
pub trait HandleExt {
/// Convenience method that takes a Future and returns a `TokioContext`.
///
/// # Example: calling Tokio Runtime from a custom ThreadPool
///
/// ```no_run
/// use tokio_util::context::HandleExt;
/// use tokio::time::{delay_for, Duration};
///
/// let mut rt = tokio::runtime::Builder::new()
/// .threaded_scheduler()
/// .enable_all()
/// .build().unwrap();
///
/// let rt2 = tokio::runtime::Builder::new()
/// .threaded_scheduler()
/// .build().unwrap();
///
/// let fut = delay_for(Duration::from_millis(2));
///
/// rt.block_on(
/// rt2
/// .handle()
/// .wrap(async { delay_for(Duration::from_millis(2)).await }),
/// );
///```
fn wrap<F: Future>(&self, fut: F) -> TokioContext<F>;
}
impl HandleExt for Handle {
fn wrap<F: Future>(&self, fut: F) -> TokioContext<F> {
TokioContext {
inner: fut,
handle: self.clone(),
}
}
}
+5 -1
View File
@@ -6,7 +6,7 @@
rust_2018_idioms,
unreachable_pub
)]
#![deny(intra_doc_link_resolution_failure)]
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
@@ -35,3 +35,7 @@ cfg_udp! {
cfg_compat! {
pub mod compat;
}
cfg_rt! {
pub mod context;
}
+36 -19
View File
@@ -6,6 +6,7 @@ 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};
@@ -36,6 +37,8 @@ pub struct UdpFramed<C> {
wr: BytesMut,
out_addr: SocketAddr,
flushed: bool,
is_readable: bool,
current_addr: Option<SocketAddr>,
}
impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
@@ -46,27 +49,39 @@ impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
pin.rd.reserve(INITIAL_RD_CAPACITY);
let (_n, addr) = unsafe {
// Read into the buffer without having to initialize the memory.
//
// safety: we know tokio::net::UdpSocket never reads from the memory
// during a recv
let res = {
let bytes = &mut *(pin.rd.bytes_mut() as *mut _ as *mut [u8]);
ready!(Pin::new(&mut pin.socket).poll_recv_from(cx, bytes))
loop {
// Are there are 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
.current_addr
.expect("will always be set before this line is called");
return Poll::Ready(Some(Ok((frame, current_addr))));
}
// if this line has been reached then decode has returned `None`.
pin.is_readable = false;
pin.rd.clear();
}
// We're out of data. Try and fetch more data to decode
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 res = ready!(Pin::new(&mut pin.socket).poll_recv_from(cx, buf));
let (n, addr) = res?;
pin.rd.advance_mut(n);
addr
};
let (n, addr) = res?;
pin.rd.advance_mut(n);
(n, addr)
};
let frame_res = pin.codec.decode(&mut pin.rd);
pin.rd.clear();
let frame = frame_res?;
let result = frame.map(|frame| Ok((frame, addr))); // frame -> (frame, addr)
Poll::Ready(result)
pin.current_addr = Some(addr);
pin.is_readable = true;
}
}
}
@@ -148,6 +163,8 @@ impl<C> UdpFramed<C> {
rd: BytesMut::with_capacity(INITIAL_RD_CAPACITY),
wr: BytesMut::with_capacity(INITIAL_WR_CAPACITY),
flushed: true,
is_readable: false,
current_addr: None,
}
}
+29
View File
@@ -0,0 +1,29 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "rt")]
use tokio::runtime::Builder;
use tokio::time::*;
use tokio_util::context::HandleExt;
#[test]
fn tokio_context_with_another_runtime() {
let mut rt1 = Builder::new()
.threaded_scheduler()
.core_threads(1)
// no timer!
.build()
.unwrap();
let rt2 = Builder::new()
.threaded_scheduler()
.core_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.handle()
.wrap(async move { delay_for(Duration::from_millis(2)).await }),
);
}
+23 -2
View File
@@ -1,5 +1,5 @@
use tokio::{net::UdpSocket, stream::StreamExt};
use tokio_util::codec::{Decoder, Encoder};
use tokio_util::codec::{Decoder, Encoder, LinesCodec};
use tokio_util::udp::UdpFramed;
use bytes::{BufMut, BytesMut};
@@ -10,7 +10,7 @@ use std::io;
#[cfg_attr(any(target_os = "macos", target_os = "ios"), allow(unused_assignments))]
#[tokio::test]
async fn send_framed() -> std::io::Result<()> {
async fn send_framed_byte_codec() -> std::io::Result<()> {
let mut a_soc = UdpSocket::bind("127.0.0.1:0").await?;
let mut b_soc = UdpSocket::bind("127.0.0.1:0").await?;
@@ -77,3 +77,24 @@ impl Encoder<&[u8]> for ByteCodec {
Ok(())
}
}
#[tokio::test]
async fn send_framed_lines_codec() -> std::io::Result<()> {
let a_soc = UdpSocket::bind("127.0.0.1:0").await?;
let b_soc = UdpSocket::bind("127.0.0.1:0").await?;
let a_addr = a_soc.local_addr()?;
let b_addr = b_soc.local_addr()?;
let mut a = UdpFramed::new(a_soc, ByteCodec);
let mut b = UdpFramed::new(b_soc, LinesCodec::new());
let msg = b"1\r\n2\r\n3\r\n".to_vec();
a.send((&msg, b_addr)).await?;
assert_eq!(b.next().await.unwrap().unwrap(), ("1".to_string(), a_addr));
assert_eq!(b.next().await.unwrap().unwrap(), ("2".to_string(), a_addr));
assert_eq!(b.next().await.unwrap().unwrap(), ("3".to_string(), a_addr));
Ok(())
}
+477 -175
View File
@@ -1,123 +1,254 @@
# 0.2.25 (January 28, 2021)
### Changes
- chore: upgrade mio dependency (#3207)
- runtime: update panic messages to include version (#3460)
### Fixes
- task: add missing feature flags for `task_local` (#3236)
# 0.2.24 (December 7, 2020)
### Fixes
- sync: fix mpsc bug related to closing the channel (#3215)
# 0.2.23 (November 12, 2020)
### Fixes
- time: report correct error for timers that exceed max duration (#2023)
- time: fix resetting expired timers causing panics (#2587)
- macros: silence `unreachable_code` warning in `select!` (#2678)
- rt: fix potential leak during runtime shutdown (#2649)
- sync: fix missing notification during mpsc close (#2854)
### Changes
- io: always re-export `std::io` (#2606)
- dependencies: update `parking_lot` dependency to 0.11.0 (#2676)
- io: rewrite `read_to_end` and `read_to_string` (#2560)
- coop: reset coop budget when blocking in `block_on` (#2711)
- sync: better Debug for Mutex (#2725)
- net: make `UnixListener::poll_accept` public (#2880)
- dep: raise `lazy_static` to `1.4.0` (#3132)
- dep: raise `slab` to `0.4.2` (#3132)
### Added
- io: add `io::duplex()` as bidirectional reader/writer (#2661)
- net: introduce split and `into_split` on `UnixDatagram` (#2557)
- net: ensure that unix sockets have both `split` and `into_split` (#2687)
- net: add `try_recv`/`from` & `try_send`/`to` to UnixDatagram (#1677)
- net: Add `UdpSocket::{try_send,try_send_to}` methods (#1979)
- net: implement `ToSocketAddrs` for `(String, u16)` (#2724)
- io: add `ReaderStream` (#2714)
- sync: implement map methods (#2771)
# 0.2.22 (July 21, 2020)
### Fixes
- docs: misc improvements (#2572, #2658, #2663, #2656, #2647, #2630, #2487, #2621,
#2624, #2600, #2623, #2622, #2577, #2569, #2589, #2575, #2540, #2564, #2567,
#2520, #2521, #2493)
- rt: allow calls to `block_on` inside calls to `block_in_place` that are
themselves inside `block_on` (#2645)
- net: fix non-portable behavior when dropping `TcpStream` `OwnedWriteHalf` (#2597)
- io: improve stack usage by allocating large buffers on directly on the heap
(#2634)
- io: fix unsound pin projection in `AsyncReadExt::read_buf` and
`AsyncWriteExt::write_buf` (#2612)
- io: fix unnecessary zeroing for `AsyncRead` implementors (#2525)
- io: Fix `BufReader` not correctly forwarding `poll_write_buf` (#2654)
- io: fix panic in `AsyncReadExt::read_line` (#2541)
### Changes
- coop: returning `Poll::Pending` no longer decrements the task budget (#2549)
### Added
- io: little-endian variants of `AsyncReadExt` and `AsyncWriteExt` methods
(#1915)
- task: add [`tracing`] instrumentation to spawned tasks (#2655)
- sync: allow unsized types in `Mutex` and `RwLock` (via `default` constructors)
(#2615)
- net: add `ToSocketAddrs` implementation for `&[SocketAddr]` (#2604)
- fs: add `OpenOptionsExt` for `OpenOptions` (#2515)
- fs: add `DirBuilder` (#2524)
[`tracing`]: https://crates.io/crates/tracing
# 0.2.21 (May 13, 2020)
### Fixes
- macros: disambiguate built-in `#[test]` attribute in macro expansion (#2503)
- rt: `LocalSet` and task budgeting (#2462).
- rt: task budgeting with `block_in_place` (#2502).
- sync: release `broadcast` channel memory without sending a value (#2509).
- time: notify when resetting a `Delay` to a time in the past (#2290)
### Added
- io: `get_mut`, `get_ref`, and `into_inner` to `Lines` (#2450).
- io: `mio::Ready` argument to `PollEvented` (#2419).
- os: illumos support (#2486).
- rt: `Handle::spawn_blocking` (#2501).
- sync: `OwnedMutexGuard` for `Arc<Mutex<T>>` (#2455).
# 0.2.20 (April 28, 2020)
### Fixes
- sync: `broadcast` closing the channel no longer requires capacity (#2448).
- rt: regression when configuring runtime with `max_threads` less than number of CPUs (#2457).
# 0.2.19 (April 24, 2020)
### Fixes
- docs: misc improvements (#2400, #2405, #2414, #2420, #2423, #2426, #2427, #2434, #2436, #2440).
- rt: support `block_in_place` in more contexts (#2409, #2410).
- stream: no panic in `merge()` and `chain()` when using `size_hint()` (#2430).
- task: include visibility modifier when defining a task-local (#2416).
### Added
- rt: `runtime::Handle::block_on` (#2437).
- sync: owned `Semaphore` permit (#2421).
- tcp: owned split (#2270).
# 0.2.18 (April 12, 2020)
### Fixes
- task: `LocalSet` was incorrectly marked as `Send` (#2398)
- io: correctly report `WriteZero` failure in `write_int` (#2334)
# 0.2.17 (April 9, 2020)
### Fixes
- rt: bug in work-stealing queue (#2387)
### Changes
- rt: threadpool uses logical CPU count instead of physical by default (#2391)
# 0.2.16 (April 3, 2020)
### Fixes
- sync: fix a regression where `Mutex`, `Semaphore`, and `RwLock` futures no
longer implement `Sync` (#2375)
- fs: fix `fs::copy` not copying file permissions (#2354)
longer implement `Sync` ([#2375])
- fs: fix `fs::copy` not copying file permissions ([#2354])
### Added
- time: added `deadline` method to `delay_queue::Expired` (#2300)
- io: added `StreamReader` (#2052)
- time: added `deadline` method to `delay_queue::Expired` ([#2300])
- io: added `StreamReader` ([#2052])
# 0.2.15 (April 2, 2020)
### Fixes
- rt: fix queue regression (#2362).
- rt: fix queue regression ([#2362]).
### Added
- sync: Add disarm to `mpsc::Sender` (#2358).
- sync: Add disarm to `mpsc::Sender` ([#2358]).
# 0.2.14 (April 1, 2020)
### Fixes
- rt: concurrency bug in scheduler (#2273).
- rt: concurrency bug with shell runtime (#2333).
- test-util: correct pause/resume of time (#2253).
- time: `DelayQueue` correct wakeup after `insert` (#2285).
- rt: concurrency bug in scheduler ([#2273]).
- rt: concurrency bug with shell runtime ([#2333]).
- test-util: correct pause/resume of time ([#2253]).
- time: `DelayQueue` correct wakeup after `insert` ([#2285]).
### Added
- io: impl `RawFd`, `AsRawHandle` for std io types (#2335).
- io: impl `RawFd`, `AsRawHandle` for std io types ([#2335]).
- rt: automatic cooperative task yielding (#2160, #2343, #2349).
- sync: `RwLock::into_inner` (#2321).
- sync: `RwLock::into_inner` ([#2321]).
### Changed
- sync: semaphore, mutex internals rewritten to avoid allocations (#2325).
- sync: semaphore, mutex internals rewritten to avoid allocations ([#2325]).
# 0.2.13 (February 28, 2020)
### Fixes
- macros: unresolved import in `pin!` (#2281).
- macros: unresolved import in `pin!` ([#2281]).
# 0.2.12 (February 27, 2020)
### Fixes
- net: `UnixStream::poll_shutdown` should call `shutdown(Write)` (#2245).
- process: Wake up read and write on `EPOLLERR` (#2218).
- net: `UnixStream::poll_shutdown` should call `shutdown(Write)` ([#2245]).
- process: Wake up read and write on `EPOLLERR` ([#2218]).
- rt: potential deadlock when using `block_in_place` and shutting down the
runtime (#2119).
- rt: only detect number of CPUs if `core_threads` not specified (#2238).
- sync: reduce `watch::Receiver` struct size (#2191).
- time: succeed when setting delay of `$MAX-1` (#2184).
- time: avoid having to poll `DelayQueue` after inserting new delay (#2217).
runtime ([#2119]).
- rt: only detect number of CPUs if `core_threads` not specified ([#2238]).
- sync: reduce `watch::Receiver` struct size ([#2191]).
- time: succeed when setting delay of `$MAX-1` ([#2184]).
- time: avoid having to poll `DelayQueue` after inserting new delay ([#2217]).
### Added
- macros: `pin!` variant that assigns to identifier and pins (#2274).
- net: impl `Stream` for `Listener` types (#2275).
- macros: `pin!` variant that assigns to identifier and pins ([#2274]).
- net: impl `Stream` for `Listener` types ([#2275]).
- rt: `Runtime::shutdown_timeout` waits for runtime to shutdown for specified
duration (#2186).
duration ([#2186]).
- stream: `StreamMap` merges streams and can insert / remove streams at
runtime (#2185).
- stream: `StreamExt::skip()` skips a fixed number of items (#2204).
- stream: `StreamExt::skip_while()` skips items based on a predicate (#2205).
- sync: `Notify` provides basic `async` / `await` task notification (#2210).
- sync: `Mutex::into_inner` retrieves guarded data (#2250).
runtime ([#2185]).
- stream: `StreamExt::skip()` skips a fixed number of items ([#2204]).
- stream: `StreamExt::skip_while()` skips items based on a predicate ([#2205]).
- sync: `Notify` provides basic `async` / `await` task notification ([#2210]).
- sync: `Mutex::into_inner` retrieves guarded data ([#2250]).
- sync: `mpsc::Sender::send_timeout` sends, waiting for up to specified duration
for channel capacity (#2227).
- time: impl `Ord` and `Hash` for `Instant` (#2239).
for channel capacity ([#2227]).
- time: impl `Ord` and `Hash` for `Instant` ([#2239]).
# 0.2.11 (January 27, 2020)
### Fixes
- docs: misc fixes and tweaks (#2155, #2103, #2027, #2167, #2175).
- macros: handle generics in `#[tokio::main]` method (#2177).
- sync: `broadcast` potential lost notifications (#2135).
- rt: improve "no runtime" panic messages (#2145).
- macros: handle generics in `#[tokio::main]` method ([#2177]).
- sync: `broadcast` potential lost notifications ([#2135]).
- rt: improve "no runtime" panic messages ([#2145]).
### Added
- optional support for using `parking_lot` internally (#2164).
- fs: `fs::copy`, an async version of `std::fs::copy` (#2079).
- macros: `select!` waits for the first branch to complete (#2152).
- macros: `join!` waits for all branches to complete (#2158).
- macros: `try_join!` waits for all branches to complete or the first error (#2169).
- macros: `pin!` pins a value to the stack (#2163).
- net: `ReadHalf::poll()` and `ReadHalf::poll_peak` (#2151)
- stream: `StreamExt::timeout()` sets a per-item max duration (#2149).
- stream: `StreamExt::fold()` applies a function, producing a single value. (#2122).
- sync: impl `Eq`, `PartialEq` for `oneshot::RecvError` (#2168).
- task: methods for inspecting the `JoinError` cause (#2051).
- optional support for using `parking_lot` internally ([#2164]).
- fs: `fs::copy`, an async version of `std::fs::copy` ([#2079]).
- macros: `select!` waits for the first branch to complete ([#2152]).
- macros: `join!` waits for all branches to complete ([#2158]).
- macros: `try_join!` waits for all branches to complete or the first error ([#2169]).
- macros: `pin!` pins a value to the stack ([#2163]).
- net: `ReadHalf::poll()` and `ReadHalf::poll_peak` ([#2151])
- stream: `StreamExt::timeout()` sets a per-item max duration ([#2149]).
- stream: `StreamExt::fold()` applies a function, producing a single value. ([#2122]).
- sync: impl `Eq`, `PartialEq` for `oneshot::RecvError` ([#2168]).
- task: methods for inspecting the `JoinError` cause ([#2051]).
# 0.2.10 (January 21, 2020)
### Fixes
- `#[tokio::main]` when `rt-core` feature flag is not enabled (#2139).
- remove `AsyncBufRead` from `BufStream` impl block (#2108).
- potential undefined behavior when implementing `AsyncRead` incorrectly (#2030).
- `#[tokio::main]` when `rt-core` feature flag is not enabled ([#2139]).
- remove `AsyncBufRead` from `BufStream` impl block ([#2108]).
- potential undefined behavior when implementing `AsyncRead` incorrectly ([#2030]).
### Added
- `BufStream::with_capacity` (#2125).
- impl `From` and `Default` for `RwLock` (#2089).
- `BufStream::with_capacity` ([#2125]).
- impl `From` and `Default` for `RwLock` ([#2089]).
- `io::ReadHalf::is_pair_of` checks if provided `WriteHalf` is for the same
underlying object (#1762, #2144).
- `runtime::Handle::try_current()` returns a handle to the current runtime (#2118).
- `stream::empty()` returns an immediately ready empty stream (#2092).
- `stream::once(val)` returns a stream that yields a single value: `val` (#2094).
- `stream::pending()` returns a stream that never becomes ready (#2092).
- `StreamExt::chain()` sequences a second stream after the first completes (#2093).
- `StreamExt::collect()` transform a stream into a collection (#2109).
- `StreamExt::fuse` ends the stream after the first `None` (#2085).
- `StreamExt::merge` combines two streams, yielding values as they become ready (#2091).
- Task-local storage (#2126).
- `runtime::Handle::try_current()` returns a handle to the current runtime ([#2118]).
- `stream::empty()` returns an immediately ready empty stream ([#2092]).
- `stream::once(val)` returns a stream that yields a single value: `val` ([#2094]).
- `stream::pending()` returns a stream that never becomes ready ([#2092]).
- `StreamExt::chain()` sequences a second stream after the first completes ([#2093]).
- `StreamExt::collect()` transform a stream into a collection ([#2109]).
- `StreamExt::fuse` ends the stream after the first `None` ([#2085]).
- `StreamExt::merge` combines two streams, yielding values as they become ready ([#2091]).
- Task-local storage ([#2126]).
# 0.2.9 (January 9, 2020)
### Fixes
- `AsyncSeek` impl for `File` (#1986).
- `AsyncSeek` impl for `File` ([#1986]).
- rt: shutdown deadlock in `threaded_scheduler` (#2074, #2082).
- rt: memory ordering when dropping `JoinHandle` (#2044).
- rt: memory ordering when dropping `JoinHandle` ([#2044]).
- docs: misc API documentation fixes and improvements.
# 0.2.8 (January 7, 2020)
@@ -129,108 +260,108 @@
### Fixes
- potential deadlock when dropping `basic_scheduler` Runtime.
- calling `spawn_blocking` from within a `spawn_blocking` (#2006).
- storing a `Runtime` instance in a thread-local (#2011).
- calling `spawn_blocking` from within a `spawn_blocking` ([#2006]).
- storing a `Runtime` instance in a thread-local ([#2011]).
- miscellaneous documentation fixes.
- rt: fix `Waker::will_wake` to return true when tasks match (#2045).
- test-util: `time::advance` runs pending tasks before changing the time (#2059).
- rt: fix `Waker::will_wake` to return true when tasks match ([#2045]).
- test-util: `time::advance` runs pending tasks before changing the time ([#2059]).
### Added
- `net::lookup_host` maps a `T: ToSocketAddrs` to a stream of `SocketAddrs` (#1870).
- `process::Child` fields are made public to match `std` (#2014).
- impl `Stream` for `sync::broadcast::Receiver` (#2012).
- `sync::RwLock` provides an asynchonous read-write lock (#1699).
- `runtime::Handle::current` returns the handle for the current runtime (#2040).
- `StreamExt::filter` filters stream values according to a predicate (#2001).
- `StreamExt::filter_map` simultaneously filter and map stream values (#2001).
- `StreamExt::try_next` convenience for streams of `Result<T, E>` (#2005).
- `StreamExt::take` limits a stream to a specified number of values (#2025).
- `StreamExt::take_while` limits a stream based on a predicate (#2029).
- `StreamExt::all` tests if every element of the stream matches a predicate (#2035).
- `StreamExt::any` tests if any element of the stream matches a predicate (#2034).
- `task::LocalSet.await` runs spawned tasks until the set is idle (#1971).
- `time::DelayQueue::len` returns the number entries in the queue (#1755).
- expose runtime options from the `#[tokio::main]` and `#[tokio::test]` (#2022).
- `net::lookup_host` maps a `T: ToSocketAddrs` to a stream of `SocketAddrs` ([#1870]).
- `process::Child` fields are made public to match `std` ([#2014]).
- impl `Stream` for `sync::broadcast::Receiver` ([#2012]).
- `sync::RwLock` provides an asynchonous read-write lock ([#1699]).
- `runtime::Handle::current` returns the handle for the current runtime ([#2040]).
- `StreamExt::filter` filters stream values according to a predicate ([#2001]).
- `StreamExt::filter_map` simultaneously filter and map stream values ([#2001]).
- `StreamExt::try_next` convenience for streams of `Result<T, E>` ([#2005]).
- `StreamExt::take` limits a stream to a specified number of values ([#2025]).
- `StreamExt::take_while` limits a stream based on a predicate ([#2029]).
- `StreamExt::all` tests if every element of the stream matches a predicate ([#2035]).
- `StreamExt::any` tests if any element of the stream matches a predicate ([#2034]).
- `task::LocalSet.await` runs spawned tasks until the set is idle ([#1971]).
- `time::DelayQueue::len` returns the number entries in the queue ([#1755]).
- expose runtime options from the `#[tokio::main]` and `#[tokio::test]` ([#2022]).
# 0.2.6 (December 19, 2019)
### Fixes
- `fs::File::seek` API regression (#1991).
- `fs::File::seek` API regression ([#1991]).
# 0.2.5 (December 18, 2019)
### Added
- `io::AsyncSeek` trait (#1924).
- `Mutex::try_lock` (#1939)
- `mpsc::Receiver::try_recv` and `mpsc::UnboundedReceiver::try_recv` (#1939).
- `writev` support for `TcpStream` (#1956).
- `time::throttle` for throttling streams (#1949).
- implement `Stream` for `time::DelayQueue` (#1975).
- `sync::broadcast` provides a fan-out channel (#1943).
- `sync::Semaphore` provides an async semaphore (#1973).
- `stream::StreamExt` provides stream utilities (#1962).
- `io::AsyncSeek` trait ([#1924]).
- `Mutex::try_lock` ([#1939])
- `mpsc::Receiver::try_recv` and `mpsc::UnboundedReceiver::try_recv` ([#1939]).
- `writev` support for `TcpStream` ([#1956]).
- `time::throttle` for throttling streams ([#1949]).
- implement `Stream` for `time::DelayQueue` ([#1975]).
- `sync::broadcast` provides a fan-out channel ([#1943]).
- `sync::Semaphore` provides an async semaphore ([#1973]).
- `stream::StreamExt` provides stream utilities ([#1962]).
### Fixes
- deadlock risk while shutting down the runtime (#1972).
- panic while shutting down the runtime (#1978).
- `sync::MutexGuard` debug output (#1961).
- deadlock risk while shutting down the runtime ([#1972]).
- panic while shutting down the runtime ([#1978]).
- `sync::MutexGuard` debug output ([#1961]).
- misc doc improvements (#1933, #1934, #1940, #1942).
### Changes
- runtime threads are configured with `runtime::Builder::core_threads` and
`runtime::Builder::max_threads`. `runtime::Builder::num_threads` is
deprecated (#1977).
deprecated ([#1977]).
# 0.2.4 (December 6, 2019)
### Fixes
- `sync::Mutex` deadlock when `lock()` future is dropped early (#1898).
- `sync::Mutex` deadlock when `lock()` future is dropped early ([#1898]).
# 0.2.3 (December 6, 2019)
### Added
- read / write integers using `AsyncReadExt` and `AsyncWriteExt` (#1863).
- `read_buf` / `write_buf` for reading / writing `Buf` / `BufMut` (#1881).
- `TcpStream::poll_peek` - pollable API for performing TCP peek (#1864).
- read / write integers using `AsyncReadExt` and `AsyncWriteExt` ([#1863]).
- `read_buf` / `write_buf` for reading / writing `Buf` / `BufMut` ([#1881]).
- `TcpStream::poll_peek` - pollable API for performing TCP peek ([#1864]).
- `sync::oneshot::error::TryRecvError` provides variants to detect the error
kind (#1874).
- `LocalSet::block_on` accepts `!'static` task (#1882).
- `task::JoinError` is now `Sync` (#1888).
kind ([#1874]).
- `LocalSet::block_on` accepts `!'static` task ([#1882]).
- `task::JoinError` is now `Sync` ([#1888]).
- impl conversions between `tokio::time::Instant` and
`std::time::Instant` (#1904).
`std::time::Instant` ([#1904]).
### Fixes
- calling `spawn_blocking` after runtime shutdown (#1875).
- `LocalSet` drop inifinite loop (#1892).
- `LocalSet` hang under load (#1905).
- calling `spawn_blocking` after runtime shutdown ([#1875]).
- `LocalSet` drop inifinite loop ([#1892]).
- `LocalSet` hang under load ([#1905]).
- improved documentation (#1865, #1866, #1868, #1874, #1876, #1911).
# 0.2.2 (November 29, 2019)
### Fixes
- scheduling with `basic_scheduler` (#1861).
- update `spawn` panic message to specify that a task scheduler is required (#1839).
- API docs example for `runtime::Builder` to include a task scheduler (#1841).
- general documentation (#1834).
- building on illumos/solaris (#1772).
- panic when dropping `LocalSet` (#1843).
- API docs mention the required Cargo features for `Builder::{basic, threaded}_scheduler` (#1858).
- scheduling with `basic_scheduler` ([#1861]).
- update `spawn` panic message to specify that a task scheduler is required ([#1839]).
- API docs example for `runtime::Builder` to include a task scheduler ([#1841]).
- general documentation ([#1834]).
- building on illumos/solaris ([#1772]).
- panic when dropping `LocalSet` ([#1843]).
- API docs mention the required Cargo features for `Builder::{basic, threaded}_scheduler` ([#1858]).
### Added
- impl `Stream` for `signal::unix::Signal` (#1849).
- API docs for platform specific behavior of `signal::ctrl_c` and `signal::unix::Signal` (#1854).
- API docs for `signal::unix::Signal::{recv, poll_recv}` and `signal::windows::CtrlBreak::{recv, poll_recv}` (#1854).
- `File::into_std` and `File::try_into_std` methods (#1856).
- impl `Stream` for `signal::unix::Signal` ([#1849]).
- API docs for platform specific behavior of `signal::ctrl_c` and `signal::unix::Signal` ([#1854]).
- API docs for `signal::unix::Signal::{recv, poll_recv}` and `signal::windows::CtrlBreak::{recv, poll_recv}` ([#1854]).
- `File::into_std` and `File::try_into_std` methods ([#1856]).
# 0.2.1 (November 26, 2019)
### Fixes
- API docs for `TcpListener::incoming`, `UnixListener::incoming` (#1831).
- API docs for `TcpListener::incoming`, `UnixListener::incoming` ([#1831]).
### Added
- `tokio::task::LocalSet` provides a strategy for spawning `!Send` tasks (#1733).
- export `tokio::time::Elapsed` (#1826).
- impl `AsRawFd`, `AsRawHandle` for `tokio::fs::File` (#1827).
- `tokio::task::LocalSet` provides a strategy for spawning `!Send` tasks ([#1733]).
- export `tokio::time::Elapsed` ([#1826]).
- impl `AsRawFd`, `AsRawHandle` for `tokio::fs::File` ([#1827]).
# 0.2.0 (November 26, 2019)
@@ -256,69 +387,69 @@ another. This changelog entry contains a highlight
# 0.1.21 (May 30, 2019)
### Changed
- Bump `tokio-trace-core` version to 0.2 (#1111).
- Bump `tokio-trace-core` version to 0.2 ([#1111]).
# 0.1.20 (May 14, 2019)
### Added
- `tokio::runtime::Builder::panic_handler` allows configuring handling
panics on the runtime (#1055).
panics on the runtime ([#1055]).
# 0.1.19 (April 22, 2019)
### Added
- Re-export `tokio::sync::Mutex` primitive (#964).
- Re-export `tokio::sync::Mutex` primitive ([#964]).
# 0.1.18 (March 22, 2019)
### Added
- `TypedExecutor` re-export and implementations (#993).
- `TypedExecutor` re-export and implementations ([#993]).
# 0.1.17 (March 13, 2019)
### Added
- Propagate trace subscriber in the runtime (#966).
- Propagate trace subscriber in the runtime ([#966]).
# 0.1.16 (March 1, 2019)
### Fixed
- async-await: track latest nightly changes (#940).
- async-await: track latest nightly changes ([#940]).
### Added
- `sync::Watch`, a single value broadcast channel (#922).
- Async equivalent of read / write file helpers being added to `std` (#896).
- `sync::Watch`, a single value broadcast channel ([#922]).
- Async equivalent of read / write file helpers being added to `std` ([#896]).
# 0.1.15 (January 24, 2019)
### Added
- Re-export tokio-sync APIs (#839).
- Stream enumerate combinator (#832).
- Re-export tokio-sync APIs ([#839]).
- Stream enumerate combinator ([#832]).
# 0.1.14 (January 6, 2019)
* Use feature flags to break up the crate, allowing users to pick & choose
components (#808).
* Export `UnixDatagram` and `UnixDatagramFramed` (#772).
components ([#808]).
* Export `UnixDatagram` and `UnixDatagramFramed` ([#772]).
# 0.1.13 (November 21, 2018)
* Fix `Runtime::reactor()` when no tasks are spawned (#721).
* `runtime::Builder` no longer uses deprecated methods (#749).
* Fix `Runtime::reactor()` when no tasks are spawned ([#721]).
* `runtime::Builder` no longer uses deprecated methods ([#749]).
* Provide `after_start` and `before_stop` configuration settings for
`Runtime` (#756).
* Implement throttle stream combinator (#736).
`Runtime` ([#756]).
* Implement throttle stream combinator ([#736]).
# 0.1.12 (October 23, 2018)
* runtime: expose `keep_alive` on runtime builder (#676).
* runtime: create a reactor per worker thread (#660).
* codec: fix panic in `LengthDelimitedCodec` (#682).
* io: re-export `tokio_io::io::read` function (#689).
* runtime: check for executor re-entry in more places (#708).
* runtime: expose `keep_alive` on runtime builder ([#676]).
* runtime: create a reactor per worker thread ([#660]).
* codec: fix panic in `LengthDelimitedCodec` ([#682]).
* io: re-export `tokio_io::io::read` function ([#689]).
* runtime: check for executor re-entry in more places ([#708]).
# 0.1.11 (September 28, 2018)
* Fix `tokio-async-await` dependency (#675).
* Fix `tokio-async-await` dependency ([#675]).
# 0.1.10 (September 27, 2018)
@@ -326,65 +457,65 @@ another. This changelog entry contains a highlight
# 0.1.9 (September 27, 2018)
* Experimental async/await improvements (#661).
* Re-export `TaskExecutor` from `tokio-current-thread` (#652).
* Improve `Runtime` builder API (#645).
* Experimental async/await improvements ([#661]).
* Re-export `TaskExecutor` from `tokio-current-thread` ([#652]).
* Improve `Runtime` builder API ([#645]).
* `tokio::run` panics when called from the context of an executor
(#646).
* Introduce `StreamExt` with a `timeout` helper (#573).
* Move `length_delimited` into `tokio` (#575).
* Re-organize `tokio::net` module (#548).
([#646]).
* Introduce `StreamExt` with a `timeout` helper ([#573]).
* Move `length_delimited` into `tokio` ([#575]).
* Re-organize `tokio::net` module ([#548]).
* Re-export `tokio-current-thread::spawn` in current_thread runtime
(#579).
([#579]).
# 0.1.8 (August 23, 2018)
* Extract tokio::executor::current_thread to a sub crate (#370)
* Add `Runtime::block_on` (#398)
* Add `runtime::current_thread::block_on_all` (#477)
* Misc documentation improvements (#450)
* Implement `std::error::Error` for error types (#501)
* Extract tokio::executor::current_thread to a sub crate ([#370])
* Add `Runtime::block_on` ([#398])
* Add `runtime::current_thread::block_on_all` ([#477])
* Misc documentation improvements ([#450])
* Implement `std::error::Error` for error types ([#501])
# 0.1.7 (June 6, 2018)
* Add `Runtime::block_on` for concurrent runtime (#391).
* Add `Runtime::block_on` for concurrent runtime ([#391]).
* Provide handle to `current_thread::Runtime` that allows spawning tasks from
other threads (#340).
* Provide `clock::now()`, a configurable source of time (#381).
other threads ([#340]).
* Provide `clock::now()`, a configurable source of time ([#381]).
# 0.1.6 (May 2, 2018)
* Add asynchronous filesystem APIs (#323).
* Add "current thread" runtime variant (#308).
* Add asynchronous filesystem APIs ([#323]).
* Add "current thread" runtime variant ([#308]).
* `CurrentThread`: Expose inner `Park` instance.
* Improve fairness of `CurrentThread` executor (#313).
* Improve fairness of `CurrentThread` executor ([#313]).
# 0.1.5 (March 30, 2018)
* Provide timer API (#266)
* Provide timer API ([#266])
# 0.1.4 (March 22, 2018)
* Fix build on FreeBSD (#218)
* Shutdown the Runtime when the handle is dropped (#214)
* Set Runtime thread name prefix for worker threads (#232)
* Add builder for Runtime (#234)
* Extract TCP and UDP types into separate crates (#224)
* Fix build on FreeBSD ([#218])
* Shutdown the Runtime when the handle is dropped ([#214])
* Set Runtime thread name prefix for worker threads ([#232])
* Add builder for Runtime ([#234])
* Extract TCP and UDP types into separate crates ([#224])
* Optionally support futures 0.2.
# 0.1.3 (March 09, 2018)
* Fix `CurrentThread::turn` to block on idle (#212).
* Fix `CurrentThread::turn` to block on idle ([#212]).
# 0.1.2 (March 09, 2018)
* Introduce Tokio Runtime (#141)
* Provide `CurrentThread` for more flexible usage of current thread executor (#141).
* Add Lio for platforms that support it (#142).
* I/O resources now lazily bind to the reactor (#160).
* Extract Reactor to dedicated crate (#169)
* Add facade to sub crates and add prelude (#166).
* Switch TCP/UDP fns to poll_ -> Poll<...> style (#175)
* Introduce Tokio Runtime ([#141])
* Provide `CurrentThread` for more flexible usage of current thread executor ([#141]).
* Add Lio for platforms that support it ([#142]).
* I/O resources now lazily bind to the reactor ([#160]).
* Extract Reactor to dedicated crate ([#169])
* Add facade to sub crates and add prelude ([#166]).
* Switch TCP/UDP fns to poll_ -> Poll<...> style ([#175])
# 0.1.1 (February 09, 2018)
@@ -393,3 +524,174 @@ another. This changelog entry contains a highlight
# 0.1.0 (February 07, 2018)
* Initial crate released based on [RFC](https://github.com/tokio-rs/tokio-rfcs/pull/3).
[#2375]: https://github.com/tokio-rs/tokio/pull/2375
[#2362]: https://github.com/tokio-rs/tokio/pull/2362
[#2358]: https://github.com/tokio-rs/tokio/pull/2358
[#2354]: https://github.com/tokio-rs/tokio/pull/2354
[#2335]: https://github.com/tokio-rs/tokio/pull/2335
[#2333]: https://github.com/tokio-rs/tokio/pull/2333
[#2325]: https://github.com/tokio-rs/tokio/pull/2325
[#2321]: https://github.com/tokio-rs/tokio/pull/2321
[#2300]: https://github.com/tokio-rs/tokio/pull/2300
[#2285]: https://github.com/tokio-rs/tokio/pull/2285
[#2281]: https://github.com/tokio-rs/tokio/pull/2281
[#2275]: https://github.com/tokio-rs/tokio/pull/2275
[#2274]: https://github.com/tokio-rs/tokio/pull/2274
[#2273]: https://github.com/tokio-rs/tokio/pull/2273
[#2253]: https://github.com/tokio-rs/tokio/pull/2253
[#2250]: https://github.com/tokio-rs/tokio/pull/2250
[#2245]: https://github.com/tokio-rs/tokio/pull/2245
[#2239]: https://github.com/tokio-rs/tokio/pull/2239
[#2238]: https://github.com/tokio-rs/tokio/pull/2238
[#2227]: https://github.com/tokio-rs/tokio/pull/2227
[#2218]: https://github.com/tokio-rs/tokio/pull/2218
[#2217]: https://github.com/tokio-rs/tokio/pull/2217
[#2210]: https://github.com/tokio-rs/tokio/pull/2210
[#2205]: https://github.com/tokio-rs/tokio/pull/2205
[#2204]: https://github.com/tokio-rs/tokio/pull/2204
[#2191]: https://github.com/tokio-rs/tokio/pull/2191
[#2186]: https://github.com/tokio-rs/tokio/pull/2186
[#2185]: https://github.com/tokio-rs/tokio/pull/2185
[#2184]: https://github.com/tokio-rs/tokio/pull/2184
[#2177]: https://github.com/tokio-rs/tokio/pull/2177
[#2169]: https://github.com/tokio-rs/tokio/pull/2169
[#2168]: https://github.com/tokio-rs/tokio/pull/2168
[#2164]: https://github.com/tokio-rs/tokio/pull/2164
[#2163]: https://github.com/tokio-rs/tokio/pull/2163
[#2158]: https://github.com/tokio-rs/tokio/pull/2158
[#2152]: https://github.com/tokio-rs/tokio/pull/2152
[#2151]: https://github.com/tokio-rs/tokio/pull/2151
[#2149]: https://github.com/tokio-rs/tokio/pull/2149
[#2145]: https://github.com/tokio-rs/tokio/pull/2145
[#2139]: https://github.com/tokio-rs/tokio/pull/2139
[#2135]: https://github.com/tokio-rs/tokio/pull/2135
[#2126]: https://github.com/tokio-rs/tokio/pull/2126
[#2125]: https://github.com/tokio-rs/tokio/pull/2125
[#2122]: https://github.com/tokio-rs/tokio/pull/2122
[#2119]: https://github.com/tokio-rs/tokio/pull/2119
[#2118]: https://github.com/tokio-rs/tokio/pull/2118
[#2109]: https://github.com/tokio-rs/tokio/pull/2109
[#2108]: https://github.com/tokio-rs/tokio/pull/2108
[#2094]: https://github.com/tokio-rs/tokio/pull/2094
[#2093]: https://github.com/tokio-rs/tokio/pull/2093
[#2092]: https://github.com/tokio-rs/tokio/pull/2092
[#2091]: https://github.com/tokio-rs/tokio/pull/2091
[#2089]: https://github.com/tokio-rs/tokio/pull/2089
[#2085]: https://github.com/tokio-rs/tokio/pull/2085
[#2079]: https://github.com/tokio-rs/tokio/pull/2079
[#2059]: https://github.com/tokio-rs/tokio/pull/2059
[#2052]: https://github.com/tokio-rs/tokio/pull/2052
[#2051]: https://github.com/tokio-rs/tokio/pull/2051
[#2045]: https://github.com/tokio-rs/tokio/pull/2045
[#2044]: https://github.com/tokio-rs/tokio/pull/2044
[#2040]: https://github.com/tokio-rs/tokio/pull/2040
[#2035]: https://github.com/tokio-rs/tokio/pull/2035
[#2034]: https://github.com/tokio-rs/tokio/pull/2034
[#2030]: https://github.com/tokio-rs/tokio/pull/2030
[#2029]: https://github.com/tokio-rs/tokio/pull/2029
[#2025]: https://github.com/tokio-rs/tokio/pull/2025
[#2022]: https://github.com/tokio-rs/tokio/pull/2022
[#2014]: https://github.com/tokio-rs/tokio/pull/2014
[#2012]: https://github.com/tokio-rs/tokio/pull/2012
[#2011]: https://github.com/tokio-rs/tokio/pull/2011
[#2006]: https://github.com/tokio-rs/tokio/pull/2006
[#2005]: https://github.com/tokio-rs/tokio/pull/2005
[#2001]: https://github.com/tokio-rs/tokio/pull/2001
[#1991]: https://github.com/tokio-rs/tokio/pull/1991
[#1986]: https://github.com/tokio-rs/tokio/pull/1986
[#1978]: https://github.com/tokio-rs/tokio/pull/1978
[#1977]: https://github.com/tokio-rs/tokio/pull/1977
[#1975]: https://github.com/tokio-rs/tokio/pull/1975
[#1973]: https://github.com/tokio-rs/tokio/pull/1973
[#1972]: https://github.com/tokio-rs/tokio/pull/1972
[#1971]: https://github.com/tokio-rs/tokio/pull/1971
[#1962]: https://github.com/tokio-rs/tokio/pull/1962
[#1961]: https://github.com/tokio-rs/tokio/pull/1961
[#1956]: https://github.com/tokio-rs/tokio/pull/1956
[#1949]: https://github.com/tokio-rs/tokio/pull/1949
[#1943]: https://github.com/tokio-rs/tokio/pull/1943
[#1939]: https://github.com/tokio-rs/tokio/pull/1939
[#1924]: https://github.com/tokio-rs/tokio/pull/1924
[#1905]: https://github.com/tokio-rs/tokio/pull/1905
[#1904]: https://github.com/tokio-rs/tokio/pull/1904
[#1898]: https://github.com/tokio-rs/tokio/pull/1898
[#1892]: https://github.com/tokio-rs/tokio/pull/1892
[#1888]: https://github.com/tokio-rs/tokio/pull/1888
[#1882]: https://github.com/tokio-rs/tokio/pull/1882
[#1881]: https://github.com/tokio-rs/tokio/pull/1881
[#1875]: https://github.com/tokio-rs/tokio/pull/1875
[#1874]: https://github.com/tokio-rs/tokio/pull/1874
[#1870]: https://github.com/tokio-rs/tokio/pull/1870
[#1864]: https://github.com/tokio-rs/tokio/pull/1864
[#1863]: https://github.com/tokio-rs/tokio/pull/1863
[#1861]: https://github.com/tokio-rs/tokio/pull/1861
[#1858]: https://github.com/tokio-rs/tokio/pull/1858
[#1856]: https://github.com/tokio-rs/tokio/pull/1856
[#1854]: https://github.com/tokio-rs/tokio/pull/1854
[#1849]: https://github.com/tokio-rs/tokio/pull/1849
[#1843]: https://github.com/tokio-rs/tokio/pull/1843
[#1841]: https://github.com/tokio-rs/tokio/pull/1841
[#1839]: https://github.com/tokio-rs/tokio/pull/1839
[#1834]: https://github.com/tokio-rs/tokio/pull/1834
[#1831]: https://github.com/tokio-rs/tokio/pull/1831
[#1827]: https://github.com/tokio-rs/tokio/pull/1827
[#1826]: https://github.com/tokio-rs/tokio/pull/1826
[#1772]: https://github.com/tokio-rs/tokio/pull/1772
[#1755]: https://github.com/tokio-rs/tokio/pull/1755
[#1733]: https://github.com/tokio-rs/tokio/pull/1733
[#1699]: https://github.com/tokio-rs/tokio/pull/1699
[#1111]: https://github.com/tokio-rs/tokio/pull/1111
[#1055]: https://github.com/tokio-rs/tokio/pull/1055
[#993]: https://github.com/tokio-rs/tokio/pull/993
[#966]: https://github.com/tokio-rs/tokio/pull/966
[#964]: https://github.com/tokio-rs/tokio/pull/964
[#940]: https://github.com/tokio-rs/tokio/pull/940
[#922]: https://github.com/tokio-rs/tokio/pull/922
[#896]: https://github.com/tokio-rs/tokio/pull/896
[#839]: https://github.com/tokio-rs/tokio/pull/839
[#832]: https://github.com/tokio-rs/tokio/pull/832
[#808]: https://github.com/tokio-rs/tokio/pull/808
[#772]: https://github.com/tokio-rs/tokio/pull/772
[#756]: https://github.com/tokio-rs/tokio/pull/756
[#749]: https://github.com/tokio-rs/tokio/pull/749
[#736]: https://github.com/tokio-rs/tokio/pull/736
[#721]: https://github.com/tokio-rs/tokio/pull/721
[#708]: https://github.com/tokio-rs/tokio/pull/708
[#689]: https://github.com/tokio-rs/tokio/pull/689
[#682]: https://github.com/tokio-rs/tokio/pull/682
[#676]: https://github.com/tokio-rs/tokio/pull/676
[#675]: https://github.com/tokio-rs/tokio/pull/675
[#661]: https://github.com/tokio-rs/tokio/pull/661
[#660]: https://github.com/tokio-rs/tokio/pull/660
[#652]: https://github.com/tokio-rs/tokio/pull/652
[#646]: https://github.com/tokio-rs/tokio/pull/646
[#645]: https://github.com/tokio-rs/tokio/pull/645
[#579]: https://github.com/tokio-rs/tokio/pull/579
[#575]: https://github.com/tokio-rs/tokio/pull/575
[#573]: https://github.com/tokio-rs/tokio/pull/573
[#548]: https://github.com/tokio-rs/tokio/pull/548
[#501]: https://github.com/tokio-rs/tokio/pull/501
[#477]: https://github.com/tokio-rs/tokio/pull/477
[#450]: https://github.com/tokio-rs/tokio/pull/450
[#398]: https://github.com/tokio-rs/tokio/pull/398
[#391]: https://github.com/tokio-rs/tokio/pull/391
[#381]: https://github.com/tokio-rs/tokio/pull/381
[#370]: https://github.com/tokio-rs/tokio/pull/370
[#340]: https://github.com/tokio-rs/tokio/pull/340
[#323]: https://github.com/tokio-rs/tokio/pull/323
[#313]: https://github.com/tokio-rs/tokio/pull/313
[#308]: https://github.com/tokio-rs/tokio/pull/308
[#266]: https://github.com/tokio-rs/tokio/pull/266
[#234]: https://github.com/tokio-rs/tokio/pull/234
[#232]: https://github.com/tokio-rs/tokio/pull/232
[#224]: https://github.com/tokio-rs/tokio/pull/224
[#218]: https://github.com/tokio-rs/tokio/pull/218
[#214]: https://github.com/tokio-rs/tokio/pull/214
[#212]: https://github.com/tokio-rs/tokio/pull/212
[#175]: https://github.com/tokio-rs/tokio/pull/175
[#169]: https://github.com/tokio-rs/tokio/pull/169
[#166]: https://github.com/tokio-rs/tokio/pull/166
[#160]: https://github.com/tokio-rs/tokio/pull/160
[#142]: https://github.com/tokio-rs/tokio/pull/142
[#141]: https://github.com/tokio-rs/tokio/pull/141
+13 -13
View File
@@ -8,12 +8,12 @@ name = "tokio"
# - README.md
# - Update CHANGELOG.md.
# - Create "v0.2.x" git tag.
version = "0.2.16"
version = "0.2.25"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
readme = "README.md"
documentation = "https://docs.rs/tokio/0.2.16/tokio/"
documentation = "https://docs.rs/tokio/0.2.25/tokio/"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
description = """
@@ -67,7 +67,7 @@ process = [
"winapi/winerror",
]
# Includes basic task execution capabilities
rt-core = []
rt-core = ["slab"]
rt-util = []
rt-threaded = [
"num_cpus",
@@ -91,7 +91,7 @@ udp = ["io-driver"]
uds = ["io-driver", "mio-uds", "libc"]
[dependencies]
tokio-macros = { version = "0.2.4", path = "../tokio-macros", optional = true }
tokio-macros = { version = "0.2.6", optional = true }
bytes = "0.5.0"
pin-project-lite = "0.1.1"
@@ -99,13 +99,14 @@ pin-project-lite = "0.1.1"
# Everything else is optional...
fnv = { version = "1.0.6", 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 }
mio = { version = "0.6.23", optional = true }
iovec = { version = "0.1.4", optional = true }
num_cpus = { version = "1.8.0", optional = true }
parking_lot = { version = "0.10.0", optional = true } # Not in full
slab = { version = "0.4.1", optional = true } # Backs `DelayQueue`
parking_lot = { version = "0.11.0", optional = true } # Not in full
slab = { version = "0.4.2", optional = true } # Backs `DelayQueue`
tracing = { version = "0.1.16", default-features = false, features = ["std"], optional = true } # Not in full
[target.'cfg(unix)'.dependencies]
mio-uds = { version = "0.6.5", optional = true }
@@ -121,15 +122,14 @@ default-features = false
optional = true
[dev-dependencies]
tokio-test = { version = "0.2.0" }
tokio-test = { version = "0.2.0", path = "../tokio-test" }
futures = { version = "0.3.0", features = ["async-await"] }
futures-test = "0.3.0"
proptest = "0.9.4"
tempfile = "3.1.0"
# loom is currently not compiling on windows.
# See: https://github.com/Xudong-Huang/generator-rs/issues/19
[target.'cfg(not(windows))'.dev-dependencies]
loom = { version = "0.3.0", features = ["futures", "checkpoint"] }
[target.'cfg(loom)'.dev-dependencies]
loom = { version = "0.3.5", features = ["futures", "checkpoint"] }
[package.metadata.docs.rs]
all-features = true
+47 -29
View File
@@ -20,16 +20,17 @@ the Rust programming language. It is:
[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]: LICENSE
[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
[discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord&style=flat-square
[discord-url]: https://discord.gg/6yGkFeN
[discord-url]: https://discord.gg/tokio
[Website](https://tokio.rs) |
[Guides](https://tokio.rs/docs/) |
[API Docs](https://docs.rs/tokio/0.2/tokio) |
[Chat](https://discord.gg/6yGkFeN)
[Guides](https://tokio.rs/tokio/tutorial) |
[API Docs](https://docs.rs/tokio/latest/tokio) |
[Roadmap](https://github.com/tokio-rs/tokio/blob/master/ROADMAP.md) |
[Chat](https://discord.gg/tokio)
## Overview
@@ -45,20 +46,11 @@ level, it provides a few major components:
These components provide the runtime components necessary for building
an asynchronous application.
[net]: https://docs.rs/tokio/0.2/tokio/net/index.html
[scheduler]: https://docs.rs/tokio/0.2/tokio/runtime/index.html
[net]: https://docs.rs/tokio/latest/tokio/net/index.html
[scheduler]: https://docs.rs/tokio/latest/tokio/runtime/index.html
## Example
To get started, add the following to `Cargo.toml`.
```toml
tokio = { version = "0.2", features = ["full"] }
```
Tokio requires components to be explicitly enabled using feature flags. As a
shorthand, the `full` feature enables all components.
A basic TCP echo server with Tokio:
```rust,no_run
@@ -98,19 +90,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
```
More examples can be found [here](../examples).
More examples can be found [here][examples]. For a larger "real world" example, see the
[mini-redis] repository.
[examples]: https://github.com/tokio-rs/tokio/tree/master/examples
[mini-redis]: https://github.com/tokio-rs/mini-redis/
To see a list of the available features flags that can be enabled, check our
[docs][feature-flag-docs].
## Getting Help
First, see if the answer to your question can be found in the [Guides] or the
[API documentation]. If the answer is not there, there is an active community in
the [Tokio Discord server][chat]. We would be happy to try to answer your
question. Last, if that doesn't work, try opening an [issue] with the question.
question. You can also ask your question on [the discussions page][discussions].
[Guides]: https://tokio.rs/docs/
[API documentation]: https://docs.rs/tokio/0.2
[chat]: https://discord.gg/6yGkFeN
[issue]: https://github.com/tokio-rs/tokio/issues/new
[Guides]: https://tokio.rs/tokio/tutorial
[API documentation]: https://docs.rs/tokio/latest/tokio
[chat]: https://discord.gg/tokio
[discussions]: https://github.com/tokio-rs/tokio/discussions
[feature-flag-docs]: https://docs.rs/tokio/#feature-flags
## Contributing
@@ -118,36 +118,54 @@ question. Last, if that doesn't work, try opening an [issue] with the question.
you! We have a [contributing guide][guide] to help you get involved in the Tokio
project.
[guide]: CONTRIBUTING.md
[guide]: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md
## Related Projects
In addition to the crates in this repository, the Tokio project also maintains
several other libraries, including:
* [`hyper`]: A fast and correct HTTP/1.1 and HTTP/2 implementation for Rust.
* [`tonic`]: A gRPC over HTTP/2 implementation focused on high performance, interoperability, and flexibility.
* [`warp`]: A super-easy, composable, web server framework for warp speeds.
* [`tower`]: A library of modular and reusable components for building robust networking clients and servers.
* [`tracing`] (formerly `tokio-trace`): A framework for application-level
tracing and async-aware diagnostics.
* [`rdbc`]: A Rust database connectivity library for MySQL, Postgres and SQLite.
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers
`tokio`.
* [`bytes`]: Utilities for working with bytes, including efficient byte buffers.
* [`loom`]: A testing tool for concurrent Rust code
[`warp`]: https://github.com/seanmonstar/warp
[`hyper`]: https://github.com/hyperium/hyper
[`tonic`]: https://github.com/hyperium/tonic
[`tower`]: https://github.com/tower-rs/tower
[`loom`]: https://github.com/tokio-rs/loom
[`rdbc`]: https://github.com/tokio-rs/rdbc
[`tracing`]: https://github.com/tokio-rs/tracing
[`mio`]: https://github.com/tokio-rs/mio
[`bytes`]: https://github.com/tokio-rs/bytes
## Supported Rust Versions
Tokio is built against the latest stable, nightly, and beta Rust releases. The
minimum version supported is the stable release from three months before the
current stable release version. For example, if the latest stable Rust is 1.29,
the minimum version supported is 1.26. The current Tokio version is not
guaranteed to build on Rust versions earlier than the minimum supported version.
Tokio is built against the latest stable release. The minimum supported version is 1.39.
The current Tokio version is not guaranteed to build on Rust versions earlier than the
minimum supported version.
## License
This project is licensed under the [MIT license](LICENSE).
This project is licensed under the [MIT license].
[MIT license]: https://github.com/tokio-rs/tokio/blob/master/LICENSE
### Contribution
+224 -302
View File
@@ -1,11 +1,12 @@
//! Opt-in yield points for improved cooperative scheduling.
//!
//! A single call to [`poll`] on a top-level task may potentially do a lot of work before it
//! returns `Poll::Pending`. If a task runs for a long period of time without yielding back to the
//! executor, it can starve other tasks waiting on that executor to execute them, or drive
//! underlying resources. Since Rust does not have a runtime, it is difficult to forcibly preempt a
//! long-running task. Instead, this module provides an opt-in mechanism for futures to collaborate
//! with the executor to avoid starvation.
//! A single call to [`poll`] on a top-level task may potentially do a lot of
//! work before it returns `Poll::Pending`. If a task runs for a long period of
//! time without yielding back to the executor, it can starve other tasks
//! waiting on that executor to execute them, or drive underlying resources.
//! Since Rust does not have a runtime, it is difficult to forcibly preempt a
//! long-running task. Instead, this module provides an opt-in mechanism for
//! futures to collaborate with the executor to avoid starvation.
//!
//! Consider a future like this one:
//!
@@ -16,9 +17,10 @@
//! }
//! ```
//!
//! It may look harmless, but consider what happens under heavy load if the input stream is
//! _always_ ready. If we spawn `drop_all`, the task will never yield, and will starve other tasks
//! and resources on the same executor. With opt-in yield points, this problem is alleviated:
//! It may look harmless, but consider what happens under heavy load if the
//! input stream is _always_ ready. If we spawn `drop_all`, the task will never
//! yield, and will starve other tasks and resources on the same executor. With
//! opt-in yield points, this problem is alleviated:
//!
//! ```ignore
//! # use tokio::stream::{Stream, StreamExt};
@@ -29,71 +31,99 @@
//! }
//! ```
//!
//! The `proceed` future will coordinate with the executor to make sure that every so often control
//! is yielded back to the executor so it can run other tasks.
//! The `proceed` future will coordinate with the executor to make sure that
//! every so often control is yielded back to the executor so it can run other
//! tasks.
//!
//! # Placing yield points
//!
//! Voluntary yield points should be placed _after_ at least some work has been done. If they are
//! not, a future sufficiently deep in the task hierarchy may end up _never_ getting to run because
//! of the number of yield points that inevitably appear before it is reached. In general, you will
//! want yield points to only appear in "leaf" futures -- those that do not themselves poll other
//! futures. By doing this, you avoid double-counting each iteration of the outer future against
//! the cooperating budget.
//! Voluntary yield points should be placed _after_ at least some work has been
//! done. If they are not, a future sufficiently deep in the task hierarchy may
//! end up _never_ getting to run because of the number of yield points that
//! inevitably appear before it is reached. In general, you will want yield
//! points to only appear in "leaf" futures -- those that do not themselves poll
//! other futures. By doing this, you avoid double-counting each iteration of
//! the outer future against the cooperating budget.
//!
//! [`poll`]: https://doc.rust-lang.org/std/future/trait.Future.html#tymethod.poll
//! [`poll`]: method@std::future::Future::poll
// NOTE: The doctests in this module are ignored since the whole module is (currently) private.
use std::cell::Cell;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
/// Constant used to determine how much "work" a task is allowed to do without yielding.
///
/// The value itself is chosen somewhat arbitrarily. It needs to be high enough to amortize wakeup
/// and scheduling costs, but low enough that we do not starve other tasks for too long. The value
/// also needs to be high enough that particularly deep tasks are able to do at least some useful
/// work at all.
///
/// Note that as more yield points are added in the ecosystem, this value will probably also have
/// to be raised.
const BUDGET: usize = 128;
/// Constant used to determine if budgeting has been disabled.
const UNCONSTRAINED: usize = usize::max_value();
thread_local! {
static HITS: Cell<usize> = Cell::new(UNCONSTRAINED);
static CURRENT: Cell<Budget> = Cell::new(Budget::unconstrained());
}
/// Run the given closure with a cooperative task budget.
///
/// Enabling budgeting when it is already enabled is a no-op.
/// Opaque type tracking the amount of "work" a task may still do before
/// yielding back to the scheduler.
#[derive(Debug, Copy, Clone)]
pub(crate) struct Budget(Option<u8>);
impl Budget {
/// Budget assigned to a task on each poll.
///
/// The value itself is chosen somewhat arbitrarily. It needs to be high
/// enough to amortize wakeup and scheduling costs, but low enough that we
/// do not starve other tasks for too long. The value also needs to be high
/// enough that particularly deep tasks are able to do at least some useful
/// work at all.
///
/// Note that as more yield points are added in the ecosystem, this value
/// will probably also have to be raised.
const fn initial() -> Budget {
Budget(Some(128))
}
/// Returns an unconstrained budget. Operations will not be limited.
const fn unconstrained() -> Budget {
Budget(None)
}
}
cfg_rt_threaded! {
impl Budget {
fn has_remaining(self) -> bool {
self.0.map(|budget| budget > 0).unwrap_or(true)
}
}
}
/// Run the given closure with a cooperative task budget. When the function
/// returns, the budget is reset to the value prior to calling the function.
#[inline(always)]
pub(crate) fn budget<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
HITS.with(move |hits| {
if hits.get() != UNCONSTRAINED {
// We are already being budgeted.
//
// Arguably this should be an error, but it can happen "correctly"
// such as with block_on + LocalSet, so we make it a no-op.
return f();
}
pub(crate) fn budget<R>(f: impl FnOnce() -> R) -> R {
with_budget(Budget::initial(), f)
}
struct Guard<'a>(&'a Cell<usize>);
impl<'a> Drop for Guard<'a> {
fn drop(&mut self) {
self.0.set(UNCONSTRAINED);
}
}
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> {
cell: &'a Cell<Budget>,
prev: Budget,
}
impl<'a> Drop for ResetGuard<'a> {
fn drop(&mut self) {
self.cell.set(self.prev);
}
}
CURRENT.with(move |cell| {
let prev = cell.get();
cell.set(budget);
let _guard = ResetGuard { cell, prev };
hits.set(BUDGET);
let _guard = Guard(hits);
f()
})
}
@@ -101,279 +131,171 @@ where
cfg_rt_threaded! {
#[inline(always)]
pub(crate) fn has_budget_remaining() -> bool {
HITS.with(|hits| hits.get() > 0)
CURRENT.with(|cell| cell.get().has_remaining())
}
}
cfg_blocking_impl! {
/// Forcibly remove the budgeting constraints early.
pub(crate) fn stop() {
HITS.with(|hits| {
hits.set(UNCONSTRAINED);
});
///
/// Returns the remaining budget
pub(crate) fn stop() -> Budget {
CURRENT.with(|cell| {
let prev = cell.get();
cell.set(Budget::unconstrained());
prev
})
}
}
/// Invoke `f` with a subset of the remaining budget.
///
/// This is useful if you have sub-futures that you need to poll, but that you want to restrict
/// from using up your entire budget. For example, imagine the following future:
///
/// ```rust
/// # use std::{future::Future, pin::Pin, task::{Context, Poll}};
/// use futures::stream::FuturesUnordered;
/// struct MyFuture<F1, F2> {
/// big: FuturesUnordered<F1>,
/// small: F2,
/// }
///
/// use tokio::stream::Stream;
/// impl<F1, F2> Future for MyFuture<F1, F2>
/// where F1: Future, F2: Future
/// # , F1: Unpin, F2: Unpin
/// {
/// type Output = F2::Output;
///
/// // fn poll(...)
/// # fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F2::Output> {
/// # let this = &mut *self;
/// let mut big = // something to pin self.big
/// # Pin::new(&mut this.big);
/// let small = // something to pin self.small
/// # Pin::new(&mut this.small);
///
/// // see if any of the big futures have finished
/// while let Some(e) = futures::ready!(big.as_mut().poll_next(cx)) {
/// // do something with e
/// # let _ = e;
/// }
///
/// // see if the small future has finished
/// small.poll(cx)
/// }
/// # }
/// ```
///
/// It could be that every time `poll` gets called, `big` ends up spending the entire budget, and
/// `small` never gets polled. That would be sad. If you want to stick up for the little future,
/// that's what `limit` is for. It lets you portion out a smaller part of the yield budget to a
/// particular segment of your code. In the code above, you would write
///
/// ```rust,ignore
/// # use std::{future::Future, pin::Pin, task::{Context, Poll}};
/// # use futures::stream::FuturesUnordered;
/// # struct MyFuture<F1, F2> {
/// # big: FuturesUnordered<F1>,
/// # small: F2,
/// # }
/// #
/// # use tokio::stream::Stream;
/// # impl<F1, F2> Future for MyFuture<F1, F2>
/// # where F1: Future, F2: Future
/// # , F1: Unpin, F2: Unpin
/// # {
/// # type Output = F2::Output;
/// # fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F2::Output> {
/// # let this = &mut *self;
/// # let mut big = Pin::new(&mut this.big);
/// # let small = Pin::new(&mut this.small);
/// #
/// // see if any of the big futures have finished
/// while let Some(e) = futures::ready!(tokio::coop::limit(64, || big.as_mut().poll_next(cx))) {
/// # // do something with e
/// # let _ = e;
/// # }
/// # small.poll(cx)
/// # }
/// # }
/// ```
///
/// Now, even if `big` spends its entire budget, `small` will likely be left with some budget left
/// to also do useful work. In particular, if the remaining budget was `N` at the start of `poll`,
/// `small` will have at least a budget of `N - 64`. It may be more if `big` did not spend its
/// entire budget.
///
/// Note that you cannot _increase_ your budget by calling `limit`. The budget provided to the code
/// inside the buget is the _minimum_ of the _current_ budget and the bound.
///
#[allow(unreachable_pub, dead_code)]
pub fn limit<R>(bound: usize, f: impl FnOnce() -> R) -> R {
HITS.with(|hits| {
let budget = hits.get();
// with_bound cannot _increase_ the remaining budget
let bound = std::cmp::min(budget, bound);
// When f() exits, how much should we add to what is left?
let floor = budget.saturating_sub(bound);
// Make sure we restore the remaining budget even on panic
struct RestoreBudget<'a>(&'a Cell<usize>, usize);
impl<'a> Drop for RestoreBudget<'a> {
fn drop(&mut self) {
let left = self.0.get();
self.0.set(self.1 + left);
cfg_coop! {
use std::task::{Context, Poll};
#[must_use]
pub(crate) struct RestoreOnPending(Cell<Budget>);
impl RestoreOnPending {
pub(crate) fn made_progress(&self) {
self.0.set(Budget::unconstrained());
}
}
impl Drop for RestoreOnPending {
fn drop(&mut self) {
// Don't reset if budget was unconstrained or if we made progress.
// They are both represented as the remembered budget being unconstrained.
let budget = self.0.get();
if !budget.is_unconstrained() {
CURRENT.with(|cell| {
cell.set(budget);
});
}
}
// Time to restrict!
hits.set(bound);
let _restore = RestoreBudget(&hits, floor);
f()
})
}
}
/// Returns `Poll::Pending` if the current task has exceeded its budget and should yield.
#[allow(unreachable_pub, dead_code)]
#[inline]
pub fn poll_proceed(cx: &mut Context<'_>) -> Poll<()> {
HITS.with(|hits| {
let n = hits.get();
if n == UNCONSTRAINED {
// opted out of budgeting
Poll::Ready(())
} else if n == 0 {
cx.waker().wake_by_ref();
Poll::Pending
} else {
hits.set(n.saturating_sub(1));
Poll::Ready(())
/// Returns `Poll::Pending` if the current task has exceeded its budget and should yield.
///
/// When you call this method, the current budget is decremented. However, to ensure that
/// progress is made every time a task is polled, the budget is automatically restored to its
/// former value if the returned `RestoreOnPending` is dropped. It is the caller's
/// responsibility to call `RestoreOnPending::made_progress` if it made progress, to ensure
/// that the budget empties appropriately.
///
/// Note that `RestoreOnPending` restores the budget **as it was before `poll_proceed`**.
/// Therefore, if the budget is _further_ adjusted between when `poll_proceed` returns and
/// `RestRestoreOnPending` is dropped, those adjustments are erased unless the caller indicates
/// that progress was made.
#[inline]
pub(crate) fn poll_proceed(cx: &mut Context<'_>) -> Poll<RestoreOnPending> {
CURRENT.with(|cell| {
let mut budget = cell.get();
if budget.decrement() {
let restore = RestoreOnPending(Cell::new(cell.get()));
cell.set(budget);
Poll::Ready(restore)
} else {
cx.waker().wake_by_ref();
Poll::Pending
}
})
}
impl Budget {
/// Decrement the budget. Returns `true` if successful. Decrementing fails
/// when there is not enough remaining budget.
fn decrement(&mut self) -> bool {
if let Some(num) = &mut self.0 {
if *num > 0 {
*num -= 1;
true
} else {
false
}
} else {
true
}
}
})
}
/// Resolves immediately unless the current task has already exceeded its budget.
///
/// This should be placed after at least some work has been done. Otherwise a future sufficiently
/// deep in the task hierarchy may end up never getting to run because of the number of yield
/// points that inevitably appear before it is even reached. For example:
///
/// ```ignore
/// # use tokio::stream::{Stream, StreamExt};
/// async fn drop_all<I: Stream + Unpin>(mut input: I) {
/// while let Some(_) = input.next().await {
/// tokio::coop::proceed().await;
/// }
/// }
/// ```
#[allow(unreachable_pub, dead_code)]
#[inline]
pub async fn proceed() {
use crate::future::poll_fn;
poll_fn(|cx| poll_proceed(cx)).await;
}
pin_project_lite::pin_project! {
/// A future that cooperatively yields to the task scheduler when polling,
/// if the task's budget is exhausted.
///
/// Internally, this is simply a future combinator which calls
/// [`poll_proceed`] in its `poll` implementation before polling the wrapped
/// future.
///
/// # Examples
///
/// ```rust,ignore
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::coop::CoopFutureExt;
///
/// async { /* ... */ }
/// .cooperate()
/// .await;
/// # }
/// ```
///
/// [`poll_proceed`]: fn.poll_proceed.html
#[derive(Debug)]
#[allow(unreachable_pub, dead_code)]
pub struct CoopFuture<F> {
#[pin]
future: F,
}
}
impl<F: Future> Future for CoopFuture<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
ready!(poll_proceed(cx));
self.project().future.poll(cx)
}
}
impl<F: Future> CoopFuture<F> {
/// Returns a new `CoopFuture` wrapping the given future.
///
#[allow(unreachable_pub, dead_code)]
pub fn new(future: F) -> Self {
Self { future }
}
}
// Currently only used by `tokio::sync`; and if we make this combinator public,
// it should probably be on the `FutureExt` trait instead.
cfg_sync! {
/// Extension trait providing `Future::cooperate` extension method.
///
/// Note: if/when the co-op API becomes public, this method should probably be
/// provided by `FutureExt`, instead.
pub(crate) trait CoopFutureExt: Future {
/// Wrap `self` to cooperatively yield to the scheduler when polling, if the
/// task's budget is exhausted.
fn cooperate(self) -> CoopFuture<Self>
where
Self: Sized,
{
CoopFuture::new(self)
fn is_unconstrained(self) -> bool {
self.0.is_none()
}
}
impl<F> CoopFutureExt for F where F: Future {}
}
#[cfg(all(test, not(loom)))]
mod test {
use super::*;
fn get() -> usize {
HITS.with(|hits| hits.get())
fn get() -> Budget {
CURRENT.with(|cell| cell.get())
}
#[test]
fn bugeting() {
use futures::future::poll_fn;
use tokio_test::*;
assert_eq!(get(), UNCONSTRAINED);
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get(), UNCONSTRAINED);
budget(|| {
assert_eq!(get(), BUDGET);
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get(), BUDGET - 1);
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get(), BUDGET - 2);
});
assert_eq!(get(), UNCONSTRAINED);
assert!(get().0.is_none());
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert!(get().0.is_none());
drop(coop);
assert!(get().0.is_none());
budget(|| {
limit(3, || {
assert_eq!(get(), 3);
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get(), 2);
limit(4, || {
assert_eq!(get(), 2);
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get(), 1);
});
assert_eq!(get(), 1);
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get(), 0);
assert_pending!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get(), 0);
assert_pending!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get(), 0);
assert_eq!(get().0, Budget::initial().0);
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 1);
drop(coop);
// we didn't make progress
assert_eq!(get().0, Budget::initial().0);
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 1);
coop.made_progress();
drop(coop);
// we _did_ make progress
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 1);
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 2);
coop.made_progress();
drop(coop);
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 2);
budget(|| {
assert_eq!(get().0, Budget::initial().0);
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 1);
coop.made_progress();
drop(coop);
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 1);
});
assert_eq!(get(), BUDGET - 3);
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
assert_eq!(get(), BUDGET - 4);
assert_ready!(task::spawn(proceed()).poll());
assert_eq!(get(), BUDGET - 5);
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 2);
});
assert!(get().0.is_none());
budget(|| {
let n = get().0.unwrap();
for _ in 0..n {
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
coop.made_progress();
}
let mut task = task::spawn(poll_fn(|cx| {
let coop = ready!(poll_proceed(cx));
coop.made_progress();
Poll::Ready(())
}));
assert_pending!(task.poll());
});
}
}
+117
View File
@@ -0,0 +1,117 @@
use crate::fs::asyncify;
use std::io;
use std::path::Path;
/// A builder for creating directories in various manners.
///
/// Additional Unix-specific options are available via importing the
/// [`DirBuilderExt`] trait.
///
/// This is a specialized version of [`std::fs::DirBuilder`] for usage on
/// the Tokio runtime.
///
/// [std::fs::DirBuilder]: std::fs::DirBuilder
/// [`DirBuilderExt`]: crate::fs::os::unix::DirBuilderExt
#[derive(Debug, Default)]
pub struct DirBuilder {
/// Indicates whether to create parent directories if they are missing.
recursive: bool,
/// Set the Unix mode for newly created directories.
#[cfg(unix)]
pub(super) mode: Option<u32>,
}
impl DirBuilder {
/// Creates a new set of options with default mode/security settings for all
/// platforms and also non-recursive.
///
/// This is an async version of [`std::fs::DirBuilder::new`][std]
///
/// [std]: std::fs::DirBuilder::new
///
/// # Examples
///
/// ```no_run
/// use tokio::fs::DirBuilder;
///
/// let builder = DirBuilder::new();
/// ```
pub fn new() -> Self {
Default::default()
}
/// Indicates whether to create directories recursively (including all parent directories).
/// Parents that do not exist are created with the same security and permissions settings.
///
/// This option defaults to `false`.
///
/// This is an async version of [`std::fs::DirBuilder::recursive`][std]
///
/// [std]: std::fs::DirBuilder::recursive
///
/// # Examples
///
/// ```no_run
/// use tokio::fs::DirBuilder;
///
/// let mut builder = DirBuilder::new();
/// builder.recursive(true);
/// ```
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
self.recursive = recursive;
self
}
/// Creates the specified directory with the configured options.
///
/// It is considered an error if the directory already exists unless
/// recursive mode is enabled.
///
/// This is an async version of [`std::fs::DirBuilder::create`][std]
///
/// [std]: std::fs::DirBuilder::create
///
/// # Errors
///
/// An error will be returned under the following circumstances:
///
/// * Path already points to an existing file.
/// * Path already points to an existing directory and the mode is
/// non-recursive.
/// * The calling process doesn't have permissions to create the directory
/// or its missing parents.
/// * Other I/O error occurred.
///
/// # Examples
///
/// ```no_run
/// use tokio::fs::DirBuilder;
/// use std::io;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// DirBuilder::new()
/// .recursive(true)
/// .create("/tmp/foo/bar/baz")
/// .await?;
///
/// Ok(())
/// }
/// ```
pub async fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
let path = path.as_ref().to_owned();
let mut builder = std::fs::DirBuilder::new();
builder.recursive(self.recursive);
#[cfg(unix)]
{
if let Some(mode) = self.mode {
std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode);
}
}
asyncify(move || builder.create(path)).await
}
}
+57 -6
View File
@@ -24,12 +24,28 @@ use std::task::Poll::*;
/// Tokio runtime.
///
/// An instance of a `File` can be read and/or written depending on what options
/// it was opened with. Files also implement Seek to alter the logical cursor
/// that the file contains internally.
/// it was opened with. Files also implement [`AsyncSeek`] to alter the logical
/// cursor that the file contains internally.
///
/// Files are automatically closed when they go out of scope.
/// A file will not be closed immediately when it goes out of scope if there
/// are any IO operations that have not yet completed. To ensure that a file is
/// closed immediately when it is dropped, you should call [`flush`] before
/// dropping it. Note that this does not ensure that the file has been fully
/// written to disk; the operating system might keep the changes around in an
/// in-memory buffer. See the [`sync_all`] method for telling the OS to write
/// the data to disk.
///
/// [std]: std::fs::File
/// Reading and writing to a `File` is usually done using the convenience
/// methods found on the [`AsyncReadExt`] and [`AsyncWriteExt`] traits. Examples
/// import these traits through [the prelude].
///
/// [std]: struct@std::fs::File
/// [`AsyncSeek`]: trait@crate::io::AsyncSeek
/// [`flush`]: fn@crate::io::AsyncWriteExt::flush
/// [`sync_all`]: fn@crate::fs::File::sync_all
/// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
/// [the prelude]: crate::prelude
///
/// # Examples
///
@@ -37,7 +53,7 @@ use std::task::Poll::*;
///
/// ```no_run
/// use tokio::fs::File;
/// use tokio::prelude::*;
/// use tokio::prelude::*; // for write_all()
///
/// # async fn dox() -> std::io::Result<()> {
/// let mut file = File::create("foo.txt").await?;
@@ -50,7 +66,7 @@ use std::task::Poll::*;
///
/// ```no_run
/// use tokio::fs::File;
/// use tokio::prelude::*;
/// use tokio::prelude::*; // for read_to_end()
///
/// # async fn dox() -> std::io::Result<()> {
/// let mut file = File::open("foo.txt").await?;
@@ -114,6 +130,11 @@ impl File {
/// # Ok(())
/// # }
/// ```
///
/// The [`read_to_end`] method is defined on the [`AsyncReadExt`] trait.
///
/// [`read_to_end`]: fn@crate::io::AsyncReadExt::read_to_end
/// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt
pub async fn open(path: impl AsRef<Path>) -> io::Result<File> {
let path = path.as_ref().to_owned();
let std = asyncify(|| sys::File::open(path)).await?;
@@ -149,6 +170,11 @@ impl File {
/// # Ok(())
/// # }
/// ```
///
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
///
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
pub async fn create(path: impl AsRef<Path>) -> io::Result<File> {
let path = path.as_ref().to_owned();
let std_file = asyncify(move || sys::File::create(path)).await?;
@@ -195,6 +221,11 @@ impl File {
/// # 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;
@@ -251,6 +282,11 @@ impl File {
/// # Ok(())
/// # }
/// ```
///
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
///
/// [`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;
@@ -280,6 +316,11 @@ impl File {
/// # Ok(())
/// # }
/// ```
///
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
///
/// [`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;
@@ -312,6 +353,11 @@ impl File {
/// # Ok(())
/// # }
/// ```
///
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
///
/// [`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;
@@ -491,6 +537,11 @@ impl File {
}
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>,
cx: &mut Context<'_>,
+3
View File
@@ -33,6 +33,9 @@ pub use self::create_dir::create_dir;
mod create_dir_all;
pub use self::create_dir_all::create_dir_all;
mod dir_builder;
pub use self::dir_builder::DirBuilder;
mod file;
pub use self::file::File;
+6
View File
@@ -382,6 +382,12 @@ impl OpenOptions {
let std = asyncify(move || opts.open(path)).await?;
Ok(File::from_std(std))
}
/// Returns a mutable reference to the the underlying std::fs::OpenOptions
#[cfg(unix)]
pub(super) fn as_inner_mut(&mut self) -> &mut std::fs::OpenOptions {
&mut self.0
}
}
impl From<std::fs::OpenOptions> for OpenOptions {
+29
View File
@@ -0,0 +1,29 @@
use crate::fs::dir_builder::DirBuilder;
/// Unix-specific extensions to [`DirBuilder`].
///
/// [`DirBuilder`]: crate::fs::DirBuilder
pub trait DirBuilderExt {
/// Sets the mode to create new directories with.
///
/// This option defaults to 0o777.
///
/// # Examples
///
///
/// ```no_run
/// use tokio::fs::DirBuilder;
/// use tokio::fs::os::unix::DirBuilderExt;
///
/// let mut builder = DirBuilder::new();
/// builder.mode(0o775);
/// ```
fn mode(&mut self, mode: u32) -> &mut Self;
}
impl DirBuilderExt for DirBuilder {
fn mode(&mut self, mode: u32) -> &mut Self {
self.mode = Some(mode);
self
}
}
+6
View File
@@ -2,3 +2,9 @@
mod symlink;
pub use self::symlink::symlink;
mod open_options_ext;
pub use self::open_options_ext::OpenOptionsExt;
mod dir_builder_ext;
pub use self::dir_builder_ext::DirBuilderExt;
+79
View File
@@ -0,0 +1,79 @@
use crate::fs::open_options::OpenOptions;
use std::os::unix::fs::OpenOptionsExt as StdOpenOptionsExt;
/// 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 {
/// 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
/// specified `mode` will be used as the permission bits for the new file.
/// If no `mode` is set, the default of `0o666` will be used.
/// The operating system masks out bits with the system's `umask`, to produce
/// the final permissions.
///
/// # Examples
///
/// ```no_run
/// use tokio::fs::OpenOptions;
/// use tokio::fs::os::unix::OpenOptionsExt;
/// use std::io;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut options = OpenOptions::new();
/// options.mode(0o644); // Give read/write for owner and read for others.
/// let file = options.open("foo.txt").await?;
///
/// Ok(())
/// }
/// ```
fn mode(&mut self, mode: u32) -> &mut Self;
/// Pass custom flags to the `flags` argument of `open`.
///
/// The bits that define the access mode are masked out with `O_ACCMODE`, to
/// ensure they do not interfere with the access mode set by Rusts options.
///
/// Custom flags can only set flags, not remove flags set by Rusts options.
/// This options overwrites any previously set custom flags.
///
/// # Examples
///
/// ```no_run
/// use libc;
/// use tokio::fs::OpenOptions;
/// use tokio::fs::os::unix::OpenOptionsExt;
/// use std::io;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut options = OpenOptions::new();
/// options.write(true);
/// if cfg!(unix) {
/// options.custom_flags(libc::O_NOFOLLOW);
/// }
/// let file = options.open("foo.txt").await?;
///
/// Ok(())
/// }
/// ```
fn custom_flags(&mut self, flags: i32) -> &mut Self;
}
impl OpenOptionsExt for OpenOptions {
fn mode(&mut self, mode: u32) -> &mut OpenOptions {
self.as_inner_mut().mode(mode);
self
}
fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
self.as_inner_mut().custom_flags(flags);
self
}
}
+1 -1
View File
@@ -99,7 +99,7 @@ impl crate::stream::Stream for ReadDir {
/// Entries returned by the [`ReadDir`] stream.
///
/// [`ReadDir`]: struct.ReadDir.html
/// [`ReadDir`]: struct@ReadDir
///
/// This is a specialized version of [`std::fs::DirEntry`] for usage from the
/// Tokio runtime.
+1 -1
View File
@@ -7,7 +7,7 @@ use std::path::Path;
///
/// This is an async version of [`std::fs::remove_dir_all`][std]
///
/// [std]: https://doc.rust-lang.org/std/fs/fn.remove_dir_all.html
/// [std]: fn@std::fs::remove_dir_all
pub async fn remove_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
let path = path.as_ref().to_owned();
asyncify(move || std::fs::remove_dir_all(path)).await
+1 -1
View File
@@ -8,7 +8,7 @@ use std::path::Path;
///
/// This is an async version of [`std::fs::set_permissions`][std]
///
/// [std]: https://doc.rust-lang.org/std/fs/fn.set_permissions.html
/// [std]: fn@std::fs::set_permissions
pub async fn set_permissions(path: impl AsRef<Path>, perm: Permissions) -> io::Result<()> {
let path = path.as_ref().to_owned();
asyncify(|| std::fs::set_permissions(path, perm)).await
+1 -1
View File
@@ -8,7 +8,7 @@ use std::path::Path;
///
/// This is an async version of [`std::fs::symlink_metadata`][std]
///
/// [std]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html
/// [std]: fn@std::fs::symlink_metadata
pub async fn symlink_metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
let path = path.as_ref().to_owned();
asyncify(|| std::fs::symlink_metadata(path)).await
+1 -1
View File
@@ -2,7 +2,7 @@ use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
/// Future for the [`ready`](ready()) function.
/// Future for the [`ok`](ok()) function.
///
/// `pub` in order to use the future as an associated type in a sealed trait.
#[derive(Debug)]
+9 -7
View File
@@ -7,14 +7,18 @@ use std::task::{Context, Poll};
/// Reads bytes asynchronously.
///
/// This trait inherits from [`std::io::BufRead`] and indicates that an I/O object is
/// **non-blocking**. All non-blocking I/O objects must return an error when
/// bytes are unavailable instead of blocking the current thread.
/// This trait is analogous to [`std::io::BufRead`], but integrates with
/// the asynchronous task system. In particular, the [`poll_fill_buf`] method,
/// unlike [`BufRead::fill_buf`], will automatically queue the current task for wakeup
/// and return if data is not yet available, rather than blocking the calling
/// thread.
///
/// Utilities for working with `AsyncBufRead` values are provided by
/// [`AsyncBufReadExt`].
///
/// [`std::io::BufRead`]: std::io::BufRead
/// [`poll_fill_buf`]: AsyncBufRead::poll_fill_buf
/// [`BufRead::fill_buf`]: std::io::BufRead::fill_buf
/// [`AsyncBufReadExt`]: crate::io::AsyncBufReadExt
pub trait AsyncBufRead: AsyncRead {
/// Attempts to return the contents of the internal buffer, filling it with more data
@@ -60,16 +64,14 @@ pub trait AsyncBufRead: AsyncRead {
macro_rules! deref_async_buf_read {
() => {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>)
-> Poll<io::Result<&[u8]>>
{
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
Pin::new(&mut **self.get_mut()).poll_fill_buf(cx)
}
fn consume(mut self: Pin<&mut Self>, amt: usize) {
Pin::new(&mut **self).consume(amt)
}
}
};
}
impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for Box<T> {
+8 -6
View File
@@ -73,10 +73,10 @@ pub trait AsyncRead {
/// that they did not write to.
///
/// [`io::Read`]: std::io::Read
/// [`poll_read_buf`]: #method.poll_read_buf
/// [`poll_read_buf`]: method@Self::poll_read_buf
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [MaybeUninit<u8>]) -> bool {
for x in buf {
*x.as_mut_ptr() = 0;
*x = MaybeUninit::new(0);
}
true
@@ -140,12 +140,14 @@ macro_rules! deref_async_read {
(**self).prepare_uninitialized_buffer(buf)
}
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8])
-> Poll<io::Result<usize>>
{
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut **self).poll_read(cx, buf)
}
}
};
}
impl<T: ?Sized + AsyncRead + Unpin> AsyncRead for Box<T> {
+2 -5
View File
@@ -55,13 +55,10 @@ macro_rules! deref_async_seek {
Pin::new(&mut **self).start_seek(cx, pos)
}
fn poll_complete(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<io::Result<u64>> {
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
Pin::new(&mut **self).poll_complete(cx)
}
}
};
}
impl<T: ?Sized + AsyncSeek + Unpin> AsyncSeek for Box<T> {
+7 -5
View File
@@ -51,7 +51,7 @@ pub trait AsyncWrite {
/// If the object is not ready for writing, the method returns
/// `Poll::Pending` and arranges for the current task (via
/// `cx.waker()`) to receive a notification when the object becomes
/// readable or is closed.
/// writable or is closed.
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
@@ -153,9 +153,11 @@ pub trait AsyncWrite {
macro_rules! deref_async_write {
() => {
fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8])
-> Poll<io::Result<usize>>
{
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut **self).poll_write(cx, buf)
}
@@ -166,7 +168,7 @@ macro_rules! deref_async_write {
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut **self).poll_shutdown(cx)
}
}
};
}
impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> {
+19 -12
View File
@@ -181,6 +181,8 @@ impl Park for Driver {
self.turn(Some(duration))?;
Ok(())
}
fn shutdown(&mut self) {}
}
impl fmt::Debug for Driver {
@@ -198,8 +200,9 @@ impl Handle {
///
/// This function panics if there is no current reactor set.
pub(super) fn current() -> Self {
context::io_handle()
.expect("there is no reactor running, must be called from the context of Tokio runtime")
context::io_handle().expect(
"there is no reactor running, must be called from the context of a Tokio 0.2.x runtime",
)
}
/// Forces a reactor blocked in a call to `turn` to wakeup, or otherwise
@@ -237,10 +240,14 @@ impl fmt::Debug for Handle {
// ===== impl Inner =====
impl Inner {
/// Registers an I/O resource with the reactor.
/// Registers an I/O resource with the reactor for a given `mio::Ready` state.
///
/// The registration token is returned.
pub(super) fn add_source(&self, source: &dyn Evented) -> io::Result<Address> {
pub(super) fn add_source(
&self,
source: &dyn Evented,
ready: mio::Ready,
) -> io::Result<Address> {
let address = self.io_dispatch.alloc().ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
@@ -253,7 +260,7 @@ impl Inner {
self.io.register(
source,
mio::Token(address.to_usize()),
mio::Ready::all(),
ready,
mio::PollOpt::edge(),
)?;
@@ -339,12 +346,12 @@ mod tests {
let inner = reactor.inner;
let inner2 = inner.clone();
let token_1 = inner.add_source(&NotEvented).unwrap();
let token_1 = inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
let thread = thread::spawn(move || {
inner2.drop_source(token_1);
});
let token_2 = inner.add_source(&NotEvented).unwrap();
let token_2 = inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
thread.join().unwrap();
assert!(token_1 != token_2);
@@ -360,15 +367,15 @@ mod tests {
// add sources to fill up the first page so that the dropped index
// may be reused.
for _ in 0..31 {
inner.add_source(&NotEvented).unwrap();
inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
}
let token_1 = inner.add_source(&NotEvented).unwrap();
let token_1 = inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
let thread = thread::spawn(move || {
inner2.drop_source(token_1);
});
let token_2 = inner.add_source(&NotEvented).unwrap();
let token_2 = inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
thread.join().unwrap();
assert!(token_1 != token_2);
@@ -383,11 +390,11 @@ mod tests {
let inner2 = inner.clone();
let thread = thread::spawn(move || {
let token_2 = inner2.add_source(&NotEvented).unwrap();
let token_2 = inner2.add_source(&NotEvented, mio::Ready::all()).unwrap();
token_2
});
let token_1 = inner.add_source(&NotEvented).unwrap();
let token_1 = inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
let token_2 = thread.join().unwrap();
assert!(token_1 != token_2);
+56 -29
View File
@@ -15,19 +15,19 @@
//! type will _yield_ to the Tokio scheduler when IO is not ready, rather than
//! blocking. This allows other tasks to run while waiting on IO.
//!
//! Another difference is that [`AsyncRead`] and [`AsyncWrite`] only contain
//! Another difference is that `AsyncRead` and `AsyncWrite` only contain
//! core methods needed to provide asynchronous reading and writing
//! functionality. Instead, utility methods are defined in the [`AsyncReadExt`]
//! and [`AsyncWriteExt`] extension traits. These traits are automatically
//! implemented for all values that implement [`AsyncRead`] and [`AsyncWrite`]
//! implemented for all values that implement `AsyncRead` and `AsyncWrite`
//! respectively.
//!
//! End users will rarely interact directly with [`AsyncRead`] and
//! [`AsyncWrite`]. Instead, they will use the async functions defined in the
//! extension traits. Library authors are expected to implement [`AsyncRead`]
//! and [`AsyncWrite`] in order to provide types that behave like byte streams.
//! End users will rarely interact directly with `AsyncRead` and
//! `AsyncWrite`. Instead, they will use the async functions defined in the
//! extension traits. Library authors are expected to implement `AsyncRead`
//! and `AsyncWrite` in order to provide types that behave like byte streams.
//!
//! Even with these differences, Tokio's [`AsyncRead`] and [`AsyncWrite`] traits
//! Even with these differences, Tokio's `AsyncRead` and `AsyncWrite` traits
//! can be used in almost exactly the same manner as the standard library's
//! `Read` and `Write`. Most types in the standard library that implement `Read`
//! and `Write` have asynchronous equivalents in `tokio` that implement
@@ -57,7 +57,7 @@
//! [`File`]: crate::fs::File
//! [`TcpStream`]: crate::net::TcpStream
//! [`std::fs::File`]: std::fs::File
//! [std_example]: https://doc.rust-lang.org/std/io/index.html#read-and-write
//! [std_example]: std::io#read-and-write
//!
//! ## Buffered Readers and Writers
//!
@@ -93,7 +93,8 @@
//! ```
//!
//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call
//! to [`write`](crate::io::AsyncWriteExt::write):
//! to [`write`](crate::io::AsyncWriteExt::write). However, you **must** flush
//! [`BufWriter`] to ensure that any buffered data is written.
//!
//! ```no_run
//! use tokio::io::{self, BufWriter, AsyncWriteExt};
@@ -105,16 +106,19 @@
//! {
//! let mut writer = BufWriter::new(f);
//!
//! // write a byte to the buffer
//! // Write a byte to the buffer.
//! writer.write(&[42u8]).await?;
//!
//! } // the buffer is flushed once writer goes out of scope
//! // Flush the buffer before it goes out of scope.
//! writer.flush().await?;
//!
//! } // Unless flushed or shut down, the contents of the buffer is discarded on drop.
//!
//! Ok(())
//! }
//! ```
//!
//! [stdbuf]: https://doc.rust-lang.org/std/io/index.html#bufreader-and-bufwriter
//! [stdbuf]: std::io#bufreader-and-bufwriter
//! [`std::io::BufRead`]: std::io::BufRead
//! [`AsyncBufRead`]: crate::io::AsyncBufRead
//! [`BufReader`]: crate::io::BufReader
@@ -122,12 +126,26 @@
//!
//! ## Implementing AsyncRead and AsyncWrite
//!
//! Because they are traits, we can implement `AsyncRead` and `AsyncWrite` for
//! Because they are traits, we can implement [`AsyncRead`] and [`AsyncWrite`] for
//! our own types, as well. Note that these traits must only be implemented for
//! non-blocking I/O types that integrate with the futures type system. In
//! other words, these types must never block the thread, and instead the
//! current task is notified when the I/O resource is ready.
//!
//! ## Conversion to and from Sink/Stream
//!
//! It is often convenient to encapsulate the reading and writing of
//! bytes and instead work with a [`Sink`] or [`Stream`] of some data
//! type that is encoded as bytes and/or decoded from bytes. Tokio
//! provides some utility traits in the [tokio-util] crate that
//! abstract the asynchronous buffering that is required and allows
//! you to write [`Encoder`] and [`Decoder`] functions working with a
//! buffer of bytes, and then use that ["codec"] to transform anything
//! that implements [`AsyncRead`] and [`AsyncWrite`] into a `Sink`/`Stream` of
//! your structured data.
//!
//! [tokio-util]: https://docs.rs/tokio-util/0.3/tokio_util/codec/index.html
//!
//! # Standard input and output
//!
//! Tokio provides asynchronous APIs to standard [input], [output], and [error].
@@ -138,21 +156,29 @@
//! context of the Tokio runtime, as they require Tokio-specific features to
//! function. Calling these functions outside of a Tokio runtime will panic.
//!
//! [input]: fn.stdin.html
//! [output]: fn.stdout.html
//! [error]: fn.stderr.html
//! [input]: fn@stdin
//! [output]: fn@stdout
//! [error]: fn@stderr
//!
//! # `std` re-exports
//!
//! Additionally, [`Error`], [`ErrorKind`], and [`Result`] are re-exported
//! from `std::io` for ease of use.
//! Additionally, [`Error`], [`ErrorKind`], [`Result`], and [`SeekFrom`] are
//! re-exported from `std::io` for ease of use.
//!
//! [`AsyncRead`]: trait.AsyncRead.html
//! [`AsyncWrite`]: trait.AsyncWrite.html
//! [`Error`]: struct.Error.html
//! [`ErrorKind`]: enum.ErrorKind.html
//! [`Result`]: type.Result.html
//! [`AsyncRead`]: trait@AsyncRead
//! [`AsyncWrite`]: trait@AsyncWrite
//! [`AsyncReadExt`]: trait@AsyncReadExt
//! [`AsyncWriteExt`]: trait@AsyncWriteExt
//! ["codec"]: https://docs.rs/tokio-util/0.3/tokio_util/codec/index.html
//! [`Encoder`]: https://docs.rs/tokio-util/0.3/tokio_util/codec/trait.Encoder.html
//! [`Decoder`]: https://docs.rs/tokio-util/0.3/tokio_util/codec/trait.Decoder.html
//! [`Error`]: struct@Error
//! [`ErrorKind`]: enum@ErrorKind
//! [`Result`]: type@Result
//! [`Read`]: std::io::Read
//! [`SeekFrom`]: enum@SeekFrom
//! [`Sink`]: https://docs.rs/futures/0.3/futures/sink/trait.Sink.html
//! [`Stream`]: crate::stream::Stream
//! [`Write`]: std::io::Write
cfg_io_blocking! {
pub(crate) mod blocking;
@@ -170,6 +196,10 @@ pub use self::async_seek::AsyncSeek;
mod async_write;
pub use self::async_write::AsyncWrite;
// Re-export some types from `std::io` so that users don't have to deal
// with conflicts when `use`ing `tokio::io` and `std::io`.
pub use std::io::{Error, ErrorKind, Result, SeekFrom};
cfg_io_driver! {
pub(crate) mod driver;
@@ -200,17 +230,14 @@ cfg_io_util! {
pub(crate) mod util;
pub use util::{
copy, empty, repeat, sink, AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt,
BufReader, BufStream, BufWriter, Copy, Empty, Lines, Repeat, Sink, Split, Take,
copy, duplex, empty, repeat, sink, AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt,
BufReader, BufStream, BufWriter, DuplexStream, Copy, Empty, Lines, Repeat, Sink, Split, Take,
};
cfg_stream! {
pub use util::{stream_reader, StreamReader};
pub use util::{reader_stream, ReaderStream};
}
// Re-export io::Error so that users don't have to deal with conflicts when
// `use`ing `tokio::io` and `std::io`.
pub use std::io::{Error, ErrorKind, Result};
}
cfg_not_io_util! {
+52 -14
View File
@@ -90,17 +90,17 @@ cfg_io_driver! {
/// These events are included as part of the read readiness event stream. The
/// write readiness event stream is only for `Ready::writable()` events.
///
/// [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
/// [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
/// [`AsyncRead`]: ../io/trait.AsyncRead.html
/// [`AsyncWrite`]: ../io/trait.AsyncWrite.html
/// [`mio::Evented`]: https://docs.rs/mio/0.6/mio/trait.Evented.html
/// [`Registration`]: struct.Registration.html
/// [`TcpListener`]: ../net/struct.TcpListener.html
/// [`clear_read_ready`]: #method.clear_read_ready
/// [`clear_write_ready`]: #method.clear_write_ready
/// [`poll_read_ready`]: #method.poll_read_ready
/// [`poll_write_ready`]: #method.poll_write_ready
/// [`std::io::Read`]: trait@std::io::Read
/// [`std::io::Write`]: trait@std::io::Write
/// [`AsyncRead`]: trait@AsyncRead
/// [`AsyncWrite`]: trait@AsyncWrite
/// [`mio::Evented`]: trait@mio::Evented
/// [`Registration`]: struct@Registration
/// [`TcpListener`]: struct@crate::net::TcpListener
/// [`clear_read_ready`]: method@Self::clear_read_ready
/// [`clear_write_ready`]: method@Self::clear_write_ready
/// [`poll_read_ready`]: method@Self::poll_read_ready
/// [`poll_write_ready`]: method@Self::poll_write_ready
pub struct PollEvented<E: Evented> {
io: Option<E>,
inner: Inner,
@@ -175,7 +175,35 @@ where
/// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
pub fn new(io: E) -> io::Result<Self> {
let registration = Registration::new(&io)?;
PollEvented::new_with_ready(io, mio::Ready::all())
}
/// Creates a new `PollEvented` associated with the default reactor, for specific `mio::Ready`
/// state. `new_with_ready` should be used over `new` when you need control over the readiness
/// state, such as when a file descriptor only allows reads. This does not add `hup` or `error`
/// so if you are interested in those states, you will need to add them to the readiness state
/// passed to this function.
///
/// An example to listen to read only
///
/// ```rust
/// ##[cfg(unix)]
/// mio::Ready::from_usize(
/// mio::Ready::readable().as_usize()
/// | mio::unix::UnixReady::error().as_usize()
/// | mio::unix::UnixReady::hup().as_usize()
/// );
/// ```
///
/// # Panics
///
/// This function panics if thread-local runtime is not set.
///
/// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
pub fn new_with_ready(io: E, ready: mio::Ready) -> io::Result<Self> {
let registration = Registration::new_with_ready(&io, ready)?;
Ok(Self {
io: Some(io),
inner: Inner {
@@ -225,7 +253,7 @@ where
/// The I/O resource will remain in a read-ready state until readiness is
/// cleared by calling [`clear_read_ready`].
///
/// [`clear_read_ready`]: #method.clear_read_ready
/// [`clear_read_ready`]: method@Self::clear_read_ready
///
/// # Panics
///
@@ -233,6 +261,11 @@ where
///
/// * `ready` includes writable.
/// * called from outside of a task context.
///
/// # Warning
///
/// This method may not be called concurrently. It takes `&self` to allow
/// calling it concurrently with `poll_write_ready`.
pub fn poll_read_ready(
&self,
cx: &mut Context<'_>,
@@ -291,7 +324,7 @@ where
/// The I/O resource will remain in a write-ready state until readiness is
/// cleared by calling [`clear_write_ready`].
///
/// [`clear_write_ready`]: #method.clear_write_ready
/// [`clear_write_ready`]: method@Self::clear_write_ready
///
/// # Panics
///
@@ -299,6 +332,11 @@ where
///
/// * `ready` contains bits besides `writable` and `hup`.
/// * called from outside of a task context.
///
/// # Warning
///
/// This method may not be called concurrently. It takes `&self` to allow
/// calling it concurrently with `poll_read_ready`.
pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<mio::Ready>> {
poll_ready!(
self,
+65 -24
View File
@@ -34,9 +34,9 @@ cfg_io_driver! {
/// stream. The write readiness event stream is only for `Ready::writable()`
/// events.
///
/// [`new`]: #method.new
/// [`poll_read_ready`]: #method.poll_read_ready`]
/// [`poll_write_ready`]: #method.poll_write_ready`]
/// [`new`]: method@Self::new
/// [`poll_read_ready`]: method@Self::poll_read_ready`
/// [`poll_write_ready`]: method@Self::poll_write_ready`
#[derive(Debug)]
pub struct Registration {
handle: Handle,
@@ -63,12 +63,49 @@ impl Registration {
/// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
pub fn new<T>(io: &T) -> io::Result<Registration>
where
T: Evented,
{
Registration::new_with_ready(io, mio::Ready::all())
}
/// Registers the I/O resource with the default reactor, for a specific `mio::Ready` state.
/// `new_with_ready` should be used over `new` when you need control over the readiness state,
/// such as when a file descriptor only allows reads. This does not add `hup` or `error` so if
/// you are interested in those states, you will need to add them to the readiness state passed
/// to this function.
///
/// An example to listen to read only
///
/// ```rust
/// ##[cfg(unix)]
/// mio::Ready::from_usize(
/// mio::Ready::readable().as_usize()
/// | mio::unix::UnixReady::error().as_usize()
/// | mio::unix::UnixReady::hup().as_usize()
/// );
/// ```
///
/// # Return
///
/// - `Ok` if the registration happened successfully
/// - `Err` if an error was encountered during registration
///
///
/// # Panics
///
/// This function panics if thread-local runtime is not set.
///
/// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
pub fn new_with_ready<T>(io: &T, ready: mio::Ready) -> io::Result<Registration>
where
T: Evented,
{
let handle = Handle::current();
let address = if let Some(inner) = handle.inner() {
inner.add_source(io)?
inner.add_source(io, ready)?
} else {
return Err(io::Error::new(
io::ErrorKind::Other,
@@ -116,8 +153,6 @@ impl Registration {
/// the function will always return `Ready(HUP)`. This should be treated as
/// the end of the readiness stream.
///
/// Ensure that [`register`] has been called first.
///
/// # Return value
///
/// There are several possible return values:
@@ -129,22 +164,26 @@ impl Registration {
/// since the last call to `poll_read_ready`.
///
/// * `Poll::Ready(Err(err))` means that the registration has encountered an
/// error. This error either represents a permanent internal error **or**
/// the fact that [`register`] was not called first.
/// error. This could represent a permanent internal error for example.
///
/// [`register`]: #method.register
/// [edge-triggered]: https://docs.rs/mio/0.6/mio/struct.Poll.html#edge-triggered-and-level-triggered
/// [edge-triggered]: struct@mio::Poll#edge-triggered-and-level-triggered
///
/// # Panics
///
/// This function will panic if called from outside of a task context.
pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<mio::Ready>> {
// Keep track of task budget
ready!(crate::coop::poll_proceed(cx));
let coop = ready!(crate::coop::poll_proceed(cx));
let v = self.poll_ready(Direction::Read, Some(cx))?;
let v = self.poll_ready(Direction::Read, Some(cx)).map_err(|e| {
coop.made_progress();
e
})?;
match v {
Some(v) => Poll::Ready(Ok(v)),
Some(v) => {
coop.made_progress();
Poll::Ready(Ok(v))
}
None => Poll::Pending,
}
}
@@ -155,7 +194,7 @@ impl Registration {
/// will not notify the current task when a new event is received. As such,
/// it is safe to call this function from outside of a task context.
///
/// [`poll_read_ready`]: #method.poll_read_ready
/// [`poll_read_ready`]: method@Self::poll_read_ready
pub fn take_read_ready(&self) -> io::Result<Option<mio::Ready>> {
self.poll_ready(Direction::Read, None)
}
@@ -170,8 +209,6 @@ impl Registration {
/// the function will always return `Ready(HUP)`. This should be treated as
/// the end of the readiness stream.
///
/// Ensure that [`register`] has been called first.
///
/// # Return value
///
/// There are several possible return values:
@@ -183,22 +220,26 @@ impl Registration {
/// since the last call to `poll_write_ready`.
///
/// * `Poll::Ready(Err(err))` means that the registration has encountered an
/// error. This error either represents a permanent internal error **or**
/// the fact that [`register`] was not called first.
/// error. This could represent a permanent internal error for example.
///
/// [`register`]: #method.register
/// [edge-triggered]: https://docs.rs/mio/0.6/mio/struct.Poll.html#edge-triggered-and-level-triggered
/// [edge-triggered]: struct@mio::Poll#edge-triggered-and-level-triggered
///
/// # Panics
///
/// This function will panic if called from outside of a task context.
pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<mio::Ready>> {
// Keep track of task budget
ready!(crate::coop::poll_proceed(cx));
let coop = ready!(crate::coop::poll_proceed(cx));
let v = self.poll_ready(Direction::Write, Some(cx))?;
let v = self.poll_ready(Direction::Write, Some(cx)).map_err(|e| {
coop.made_progress();
e
})?;
match v {
Some(v) => Poll::Ready(Ok(v)),
Some(v) => {
coop.made_progress();
Poll::Ready(Ok(v))
}
None => Poll::Pending,
}
}
@@ -209,7 +250,7 @@ impl Registration {
/// will not notify the current task when a new event is received. As such,
/// it is safe to call this function from outside of a task context.
///
/// [`poll_write_ready`]: #method.poll_write_ready
/// [`poll_write_ready`]: method@Self::poll_write_ready
pub fn take_write_ready(&self) -> io::Result<Option<mio::Ready>> {
self.poll_ready(Direction::Write, None)
}
+22 -14
View File
@@ -12,16 +12,19 @@ cfg_io_std! {
/// The handle implements the [`AsyncRead`] trait, but beware that concurrent
/// reads of `Stdin` must be executed with care.
///
/// As an additional caveat, reading from the handle may block the calling
/// future indefinitely if there is not enough data available. This makes this
/// handle unsuitable for use in any circumstance where immediate reaction to
/// available data is required, e.g. interactive use or when implementing a
/// subprocess driven by requests on the standard input.
/// This handle is best used for non-interactive uses, such as when a file
/// is piped into the application. For technical reasons, `stdin` is
/// implemented by using an ordinary blocking read on a separate thread, and
/// it is impossible to cancel that read. This can make shutdown of the
/// runtime hang until the user presses enter.
///
/// For interactive uses, it is recommended to spawn a thread dedicated to
/// user input and use blocking IO directly in that thread.
///
/// Created by the [`stdin`] function.
///
/// [`stdin`]: fn.stdin.html
/// [`AsyncRead`]: trait.AsyncRead.html
/// [`stdin`]: fn@stdin
/// [`AsyncRead`]: trait@AsyncRead
#[derive(Debug)]
pub struct Stdin {
std: Blocking<std::io::Stdin>,
@@ -29,14 +32,14 @@ cfg_io_std! {
/// Constructs a new handle to the standard input of the current process.
///
/// The returned handle allows reading from standard input from the within the
/// Tokio runtime.
/// This handle is best used for non-interactive uses, such as when a file
/// is piped into the application. For technical reasons, `stdin` is
/// implemented by using an ordinary blocking read on a separate thread, and
/// it is impossible to cancel that read. This can make shutdown of the
/// runtime hang until the user presses enter.
///
/// As an additional caveat, reading from the handle may block the calling
/// future indefinitely if there is not enough data available. This makes this
/// handle unsuitable for use in any circumstance where immediate reaction to
/// available data is required, e.g. interactive use or when implementing a
/// subprocess driven by requests on the standard input.
/// For interactive uses, it is recommended to spawn a thread dedicated to
/// user input and use blocking IO directly in that thread.
pub fn stdin() -> Stdin {
let std = io::stdin();
Stdin {
@@ -60,6 +63,11 @@ impl std::os::windows::io::AsRawHandle for Stdin {
}
impl AsyncRead for Stdin {
unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [std::mem::MaybeUninit<u8>]) -> bool {
// https://github.com/rust-lang/rust/blob/09c817eeb29e764cfc12d0a8d94841e3ffe34023/src/libstd/io/stdio.rs#L97
false
}
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,

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