Compare commits

...
Author SHA1 Message Date
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
Eliza Weisman d883ac0fa0 chore: prepare tokio 0.2.16 release
# 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)

### Added

- time: added `deadline` method to `delay_queue::Expired` (#2300)
- io: added `StreamReader` (#2052) 

Signed-off-by: Eliza Weisman <[email protected]>
2020-04-03 17:00:42 -07:00
Eliza Weisman 1121a8eb23 sync: ensure Mutex, RwLock, and Semaphore futures are Send + Sync (#2375)
Previously, the `Mutex::lock`, `RwLock::{read, write}`, and
`Semaphore::acquire` futures in `tokio::sync` implemented `Send + Sync`
automatically. This was by virtue of being implemented using a `poll_fn`
that only closed over `Send + Sync` types. However, this broke in
PR #2325, which rewrote those types using the new `batch_semaphore`.
Now, they await an `Acquire` future, which contains a `Waiter`, which
internally contains an `UnsafeCell`, and thus does not implement `Sync`.

Since removing previously implemented traits breaks existing code, this
inadvertantly caused a breaking change. There were tests ensuring that
the `Mutex`, `RwLock`, and `Semaphore` types themselves were `Send +
Sync`, but no tests that the _futures they return_ implemented those
traits.

I've fixed this by adding an explicit impl of `Sync` for the
`batch_semaphore::Acquire` future. Since the `Waiter` type held by this
struct is only accessed when borrowed mutably, it is safe for it to
implement `Sync`.

Additionally, I've added to the bounds checks for the effected
`tokio::sync` types to ensure that returned futures continue to
implement `Send + Sync` in the future.
2020-04-03 15:45:29 -07:00
nasa 6fa40b6e20 doc: Fix readme link (#2370) 2020-04-03 09:00:18 -04:00
Alice Ryhl e10471dc3a io: Add StreamReader (#2052)
Allow conversion from a stream of chunks of bytes to an `AsyncRead`.
2020-04-02 14:18:52 -07:00
Alice Ryhl 0245515e4d examples: add comment about dependency gotcha (#2355) 2020-04-02 23:16:00 +02:00
MOZGIII 03cb3b6ca1 Expose time::deplay_queue::Expired::deadline (#2300)
* Expose time::deplay_queue::Expired::deadline

* Return by value
2020-04-02 17:11:17 -04:00
Kevin Leimkuhler 3eaa1885c3 fs: Copy file permissions (#2354)
Signed-off-by: Kevin Leimkuhler <[email protected]>
2020-04-02 17:10:44 -04:00
Benjamin Halsted cf4cbc142b test: Added read_error() and write_error() (#2337)
Enable testing of edge cases caused by io errors.
2020-04-02 17:10:12 -04:00
Benjamin Halsted 215d7d4c5f util: documentation example for LengthDelimitedCodec (#2339)
There is a gap in examples for Builder::num_skip() that shows how to
move past unused bytes between the length and payload.
2020-04-02 17:09:56 -04:00
Lucio Franco 2a8d917d2c chore: Prepare 0.2.15 release (#2365)
Signed-off-by: Lucio Franco <[email protected]>
2020-04-02 12:40:04 -04:00
Jon Gjengset 7fb1698e8d sync: Add disarm to mpsc::Sender (#2358)
Fixes #898.
2020-04-02 11:27:37 -04:00
Carl Lerche fa4fe9ef6f rt: fix queue regression (#2362)
The new queue uses `u8` to track offsets. Cursors are expected to wrap.
An operation was performed with `+` instead of `wrapping_add`. This was
not _obviously_ issue before as it is difficult to wrap a `usize` on
64bit platforms, but wrapping a `u8` is trivial.

The fix is to use `wrapping_add` instead of `+`. A new test is added
that catches the issue.

Fixes #2361
2020-04-02 07:52:02 -07:00
Carl Lerche f01136b5c0 chore: prepare tokio v0.2.14 release (#2356) 2020-04-01 13:54:11 -07:00
Carl Lerche caa7e180e4 rt: cap fifo scheduler slot to avoid starvation (#2349)
The work-stealing scheduler includes an optimization where each worker
includes a single slot to store the **last** scheduled task. Tasks in
scheduler's LIFO slot are executed next. This speeds up and reduces
latency with message passing patterns.

Previously, this optimization was susceptible to starving other tasks in
certain cases. If two tasks ping-ping between each other without ever
yielding, the worker would never execute other tasks.

An early PR (#2160) introduced a form of pre-emption. Each task is
allocated a per-poll operation budget. Tokio resources will return ready
until the budget is depleted, at which point, Tokio resources will
always return `Pending`.

This patch leverages the operation budget to limit the LIFO scheduler
optimization. When executing tasks from the LIFO slot, the budget is
**not** reset. Once the budget goes to zero, the task in the LIFO slot
is pushed to the back of the queue.
2020-03-28 13:55:12 -07:00
Alice Ryhl 7b2438e744 sync: fix notified link (#2351) 2020-03-28 13:20:51 -07:00
Eliza Weisman 00725f6876 sync: fix possible dangling pointer in semaphore (#2340)
## Motivation

When cancelling futures which are waiting to acquire semaphore permits,
there is a possible dangling pointer if notified futures are dropped
after the notified wakers have been split into a separate list. Because
these futures' wait queue nodes are no longer in the main list guarded
by the lock, their `Drop` impls will complete immediately, and they may
be dropped while still in the list of tasks to notify.

## Solution

This branch fixes this by popping from the wait list inside the lock.
The wakers of popped nodes are temporarily stored in a stack array,
so that they can be notified after the lock is released. Since the
size of the stack array is fixed, we may in some cases have to loop
multiple times, acquiring and releasing the lock, until all permits
have been released. This may also have the possible side advantage of
preventing a thread releasing a very large number of permits from
starving other threads that need to enqueue waiters.

I've also added a loom test that can reliably reproduce a segfault
on master, but passes on this branch (after a lot of iterations).

Signed-off-by: Eliza Weisman <[email protected]>
2020-03-27 16:14:07 -07:00
kalcutter 5c71268bb8 sync: broadcast, revert "Keep lock until sender notified" (#2348)
This reverts commit 826fc21abf.

The code was intentional. Holding the lock while notifying is
unnecessary. Also change the code to use `drop` so clippy doesn't
confuse people against their will.
2020-03-27 14:22:16 -07:00
Carl Lerche 8020b02bd0 fs: add coop test (#2344) 2020-03-26 22:17:56 -07:00
Carl Lerche 11acfbbea4 rt: add task join coop test (#2345)
Add test verifying that joining on a task consumes the caller's budget.
2020-03-26 22:17:48 -07:00
Carl Lerche f2005a78ca timer: fix loom test (#2346)
Fixes a test from a PR that was written before the recent loom upgrade.
A change in the details how loom executes models resulted in the test to
start failing. The fix is to reduce the number of iterations performed
by the test.
2020-03-26 15:23:33 -07:00
Brian L. Troutwine 3fb213a861 timer: improve memory ordering in Inner's increment (#2107)
This commit improves the memory ordering in the implementation of
Inner's increment function. The former code did a sequentially
consistent load of self.num, then entered a loop with a sequentially
consistent compare and swap on the same, bailing out with and Err only
if the loaded value was MAX_TIMEOUTS. The use of SeqCst means that all
threads must observe all relevant memory operations in the same order,
implying synchronization between all CPUs.

This commit adjusts the implementation in two key ways. First, the
initial load of self.num is now down with Relaxed ordering. If two
threads entered this code simultaneously, formerly, tokio required
that one proceed before the other, negating their parallelism. Now,
either thread may proceed without coordination. Second, the SeqCst
compare_and_swap is changed to a Release, Relaxed
compare_exchange_weak. The first memory ordering referrs to success:
if the value is swapped the load of that value for comparison will be
Relaxed and the store will be Release. The second memory ordering
referrs to failure: if the value is not swapped the load is
Relaxed. The _weak variant may spuriously fail but will generate
better code.

These changes mean that it is possible for more loops to be taken per
call than strictly necessary but with greater parallelism available on
this operation, improved energy consumption as CPUs don't have to
coordinate as much.
2020-03-26 13:04:08 -07:00
Christofer Nolander 6cf1a5b6b8 time: fix DelayQueue rewriting delay on insert after Poll::Ready (#2285)
When the queue was polled and yielded an index from the wheel, the delay
until the next item was never updated. As a result, when one item was
yielded from `poll_idx` the following insert erronously updated the
delay to the instant of the inserted item.

Fixes: #1700
2020-03-26 12:54:56 -07:00
Carl Lerche 1cb1e291c1 rt: track loom changes + tweak queue (#2315)
Loom is having a big refresh to improve performance and tighten up the
concurrency model. This diff tracks those changes.

Included in the changes is the removal of `CausalCell` deferred checks.
This is due to it technically being undefined behavior in the C++11
memory model. To address this, the work-stealing queue is updated to
avoid needing this behavior. This is done by limiting the queue to have
one concurrent stealer.
2020-03-26 12:23:12 -07:00
Carl Lerche 186196b911 stream: iter() should yield every so often. (#2343) 2020-03-25 14:56:24 -07:00
Tudor Sidea 57ba37c978 time: fix repeated pause/resume of time (#2253)
The resume function was breaking the guarantee that Instants should
never be less than any previously measured Instants when created.

Altered the pause and resume function such that they will not break this
guarantee. After resume, the time should continue from where it left
off.

Created test to prove that the advanced function still works as
expected.

Added additional tests for the pause/advance/resume functions.
2020-03-23 22:20:07 -07:00
Eliza Weisman acf8a7da7a sync: new internal semaphore based on intrusive lists (#2325)
## Motivation

Many of Tokio's synchronization primitives (`RwLock`, `Mutex`,
`Semaphore`, and the bounded MPSC channel) are based on the internal
semaphore implementation, called `semaphore_ll`. This semaphore type
provides a lower-level internal API for the semaphore implementation
than the public `Semaphore` type, and supports "batch" operations, where
waiters may acquire more than one permit at a time, and batches of
permits may be released back to the semaphore.

Currently, `semaphore_ll` uses an atomic singly-linked list for the
waiter queue. The linked list implementation is specific to the
semaphore. This implementation therefore requires a heap allocation for
every waiter in the queue. These allocations are owned by the semaphore,
rather than by the task awaiting permits from the semaphore. Critically,
they are only _deallocated_ when permits are released back to the
semaphore, at which point it dequeues as many waiters from the front of
the queue as can be satisfied with the released permits. If a task
attempts to acquire permits from the semaphore and is cancelled (such as
by timing out), their waiter nodes remain in the list until they are
dequeued while releasing permits. In cases where large numbers of tasks
are cancelled while waiting for permits, this results in extremely high
memory use for the semaphore (see #2237).

## Solution

@Matthias247 has proposed that Tokio adopt the approach used in his
`futures-intrusive` crate: using an _intrusive_ linked list to store the
wakers of tasks waiting on a synchronization primitive. In an intrusive
list, each list node is stored as part of the entry that node
represents, rather than in a heap allocation that owns the entry.
Because futures must be pinned in order to be polled, the necessary
invariant of such a list --- that entries may not move while in the list
--- may be upheld by making the waiter node `!Unpin`. In this approach,
the waiter node can be stored inline in the future, rather than
requiring  separate heap allocation, and cancelled futures may remove
their nodes from the list.

This branch adds a new semaphore implementation that uses the intrusive
list added to Tokio in #2210. The implementation is essentially a hybrid
of the old `semaphore_ll` and the semaphore used in `futures-intrusive`:
while a `Mutex` around the wait list is necessary, since the intrusive
list is not thread-safe, the permit state is stored outside of the mutex
and updated atomically. 

The mutex is acquired only when accessing the wait list — if a task 
can acquire sufficient permits without waiting, it does not need to
acquire the lock. When releasing permits, we iterate over the wait
list from the end of the queue until we run out of permits to release,
and split off all the nodes that received enough permits to wake up
into a separate list. Then, we can drain the new list and notify those
wakers *after* releasing the lock. Because the split operation only
modifies the pointers on the head node of the split-off list and the
new tail node of the old list, it is O(1) and does not require an
allocation to return a variable length number of waiters to notify.


Because of the intrusive list invariants, the API provided by the new
`batch_semaphore` is somewhat different than that of `semaphore_ll`. In
particular, the `Permit` type has been removed. This type was primarily
intended allow the reuse of a wait list node allocated on the heap.
Since the intrusive list means we can avoid heap-allocating waiters,
this is no longer necessary. Instead, acquiring permits is done by
polling an `Acquire` future returned by the `Semaphore` type. The use of
a future here ensures that the waiter node is always pinned while
waiting to acquire permits, and that a reference to the semaphore is
available to remove the waiter if the future is cancelled.
Unfortunately, the current implementation of the bounded MPSC requires a
`poll_acquire` operation, and has methods that call it while outside of
a pinned context. Therefore, I've left the old `semaphore_ll`
implementation in place to be used by the bounded MPSC, and updated the
`Mutex`, `RwLock`, and `Semaphore` APIs to use the new implementation.
Hopefully, a subsequent change can update the bounded MPSC to use the
new semaphore as well.

Fixes #2237

Signed-off-by: Eliza Weisman <[email protected]>
2020-03-23 13:45:48 -07:00
MarinPostma 2258de5147 io: impl as RawFd / AsRawHandle for stdio (#2335)
Fixes: #2311
2020-03-22 22:25:49 -07:00
Carl Lerche dd27f1a259 rt: remove unsafe from shell runtime. (#2333)
Since the original shell runtime was implemented, utilities have been
added to encapsulate `unsafe`. The shell runtime is now able to use
those utilities and not include its own `unsafe` code.
2020-03-20 21:06:50 -07:00
Nikhil Benesch 5fd1b8f67c util: Prepare 0.3.1 release (#2330) 2020-03-18 20:05:55 -04:00
Nikhil Benesch 9e58b37ad5 tokio-util: fix minimum supported version of tokio (#2326)
tokio-util uses tokio::stream::StreamExt, which was not introduced until
tokio v0.2.5. The current dependency specification is incorrect, and
breaks with cargo update -Z minimal-versions.
2020-03-18 18:18:53 -04:00
Daniel Müller c3b830110a sync: Add RwLock::into_inner method (#2321)
Add RwLock::into_inner method that consumes the lock and returns
the wrapped value.

Fixes: #2320
2020-03-18 11:52:11 -07:00
Alice Ryhl 602ad2e0ba runtime: update the documentation around Handle (#2328)
This PR was prompted by having encountered a few cases of people not noticing that Runtime::handle can be cloned, and therefore not realizing it could be moved to another thread.
2020-03-17 22:59:26 +01:00
Jon Gjengset 06a4d895ec Add cooperative task yielding (#2160)
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.
See for example rust-lang/futures-rs#2047, rust-lang/futures-rs#1957,
and rust-lang/futures-rs#869. Since Rust does not have a runtime, it is
difficult to forcibly preempt a long-running task.

Consider a future like this one:

```rust
use tokio::stream::StreamExt;
async fn drop_all<I: Stream>(input: I) {
    while let Some(_) = input.next().await {}
}
```

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.

This patch adds a `coop` module that provides an opt-in mechanism for
futures to cooperate with the executor to avoid starvation. This
alleviates the problem above:

```
use tokio::stream::StreamExt;
async fn drop_all<I: Stream>(input: I) {
    while let Some(_) = input.next().await {
        tokio::coop::proceed().await;
    }
}
```

The call to [`proceed`] 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 implementation uses a thread-local counter that simply counts how
many "cooperation points" we have passed since the task was first
polled. Once the "budget" has been spent, any subsequent points will
return `Poll::Pending`, eventually making the top-level task yield. When
it finally does yield, the executor resets the budget before
running the next task.

The budget per task poll is currently hard-coded to 128. Eventually, we
may want to make it dynamic as more cooperation points are added. The
number 128 was chosen more or less arbitrarily to balance the cost of
yielding unnecessarily against the time an executor may be "held up".

At the moment, all the tokio leaf futures ("resources") call into coop,
but external futures have no way of doing so. We probably want to
continue limiting coop points to leaf futures in the future, but may
want to also enable third-party leaf futures to cooperate to benefit the
ecosystem as a whole. This is reflected in the methods marked as `pub`
in `mod coop` (even though the module is only `pub(crate)`). We will
likely also eventually want to expose `coop::limit`, which enables
sub-executors and manual `impl Future` blocks to avoid one sub-task
spending all of their poll budget.

Benchmarks (see tokio-rs/tokio#2160) suggest that the overhead of `coop`
is marginal.
2020-03-16 17:17:56 -04:00
Alice Ryhl fce6845f2b Document common cargo commands (#2293)
* Document common cargo commands
* Add loom command
2020-03-15 13:02:39 +01:00
th0114nd 826fc21abf Keep lock until sender notified (#2302) 2020-03-08 20:46:19 -07:00
Thomas Whiteway bc8dcdeb58 Add foo.txt and bar.txt to .gitignore (#2294) 2020-03-06 13:00:33 -05:00
Carl Lerche a78b1c65cc rt: cleanup and simplify scheduler (scheduler v2.5) (#2273)
A refactor of the scheduler internals focusing on simplifying and
reducing unsafety. There are no fundamental logic changes.

* The state transitions of the core task component are refined and
reduced.
* `basic_scheduler` has most unsafety removed.
* `local_set` has most unsafety removed.
* `threaded_scheduler` limits most unsafety to its queue implementation.
2020-03-05 10:31:37 -08:00
5ede2e4d6b util: Prepare 0.3.0 release (#2296)
Signed-off-by: Lucio Franco <[email protected]>
Co-authored-by: David Barsky <[email protected]>
Co-authored-by: Eliza Weisman <[email protected]>
2020-03-04 17:27:18 -05:00
Lucio FrancoandMarkus Westerlind 9d4d076189 codec: change Encoder to take &Item (#1746)
Co-authored-by: Markus Westerlind <[email protected]>
2020-03-04 15:54:41 -05:00
Jean-Christophe BEGUE 1eb6131321 codec: Add Framed::with_capacity (#2215) 2020-03-02 11:40:54 -05:00
Jeffrey Czyz 17a6f6fabf macros: fix select! documentation formatting (#2283) 2020-02-29 07:17:49 -08:00
Carl Lerche ecc23c084a chore: prepare v0.2.13 release (#2282)
Includes a quick bug fix
2020-02-28 13:51:24 -08:00
Hiro Saito 937ae11f37 macros: fix unresolved import in pin! (#2281) 2020-02-28 09:36:27 -08:00
Alice Ryhl f0d18653a6 runtime: add threaded_scheduler to examples (#2277)
It can be pretty confusing when the core_threads example does not call
threaded_scheduler, when actually building such a runtime results in
panics if you try to spawn something on it:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=866b3af344b0d6aad170ac9cbc9d57ed
2020-02-27 14:03:18 -08:00
Carl Lerche 06bcbe8dcf sync: refactor intrusive linked list (#2279)
Allow storing the intrusive linked-list pointers in an arbitrary
location in the node. This is in preparation for using the linked list
in the scheduler.

In order to make using the intrusive linked list more flexible, a trait
is introduced to abstract mapping an entry to raw pointers and the next
/ prev pointers. This also pushes more unsafety onto the user.
2020-02-27 13:45:28 -08:00
Carl Lerche bfdfb46fcd chore: delete unused file (#2280)
The `blocking` module is not referenced in code. The file is left over
from an earlier refactor.
2020-02-27 11:57:35 -08:00
Carl Lerche c6fc1db698 chore: prepare v0.2.12 release (#2278)
Also includes `tokio-macros` v0.2.5.
2020-02-27 10:18:01 -08:00
Akshay Narayan d44ce338af tcp: Update listener docs (#2276)
* Update listener docs
* re-wrap text and add links
2020-02-26 21:16:13 +01:00
Carl Lerche 8b7ea0ff5c sync: adds Notify for basic task notification (#2210)
`Notify` provides a synchronization primitive similar to thread park /
unpark, except for tasks.
2020-02-26 11:40:10 -08:00
Thomas Whiteway 7207bf355e time: avoid needing to poll DelayQueue after insertion (#2217)
If an entry is inserted in the queue before the next deadline, the
DelayQueue needs to update the Delay tracking the next time to poll.

If there is an existing Delay, reset that rather than replacing it as if
it's already been polled the task will be waiting for a notification
before it will poll again, and dropping the Delay means that that
notification will never be performed.
2020-02-26 10:55:08 -08:00
David Kellum a4c4ac254b docs: macros doc(cfg) workarounds (#2225)
This is a workaround for the fact that the doc(cfg) from outer cfg_*
macros doesn't get applied correctly. Its included in the rt-threaded
branch only, which is what is used for doc.rs via all-features.
2020-02-26 10:38:54 -08:00
Akshay Narayan 0589acc9ff Implement Stream for Listener types (#2275)
The Incoming types currently don't take ownership of the listener, but
in most cases, users who want to use the Listener as a stream will only
want to use the stream from that point on. So, implement Stream directly
on the Listener types.
2020-02-26 13:38:41 -05:00
Carl Lerche 1dadc701c0 macros: add assignment form to pin! (#2274)
Allows combining assignment to a binding and pinning it.
2020-02-25 20:06:54 -08:00
Kevin Leimkuhler 10f1507cf4 process: Wake up read and write on EPOLLERR (#2218)
## Motivation

#2174

On epoll platforms, the read end of a pipe closing is signaled to the write end
through the `EPOLLERR` event [[1](http://man7.org/linux/man-pages/man2/epoll_ctl.2.html)]. If readiness is not registered for this
event, it will silently pass through `epoll_wait` calls.

Additionally, this specific case that `EPOLLERR` is triggered leaves the write
end of the pipe (parent process) waiting for a wakeup that never occurs.

## Solution

Similar to the `HUP` event on Unix platforms, errors are now always masked
through registrations so that both read and write ends of a connection are made
aware of errors.

In cases where pipes are used and the read end closes, write ends that are
waiting for a wakeup are properly notified and try to write again. This allows
a client to observe `BrokenPipe` and go through the proper cleanup and/or
restablishment of connection.

Closes #2174

Signed-off-by: Kevin Leimkuhler <[email protected]>
2020-02-25 13:27:44 -08:00
Jake b9cc032d3b mpsc: add Sender::send_timeout (#2227) 2020-02-25 09:06:02 -08:00
Thomas 4213b79461 tokio: fix broken contributing guide link (#2267)
The link to the contributing guide in the tokio sub crate was
referencing a non-existent file. This updates the link to reference
the repo root's CONTRIBUTING.md file.

Fixes: #2266
2020-02-22 21:00:58 -08:00
Matt Butcher 7b8ce356c3 sync: fixed a small typo in sync docs (#2262)
Signed-off-by: Matt Butcher <[email protected]>
2020-02-20 11:38:28 -05:00
Alice Ryhl 0605abacfc sync: Use yield rather than block on read method (#2258) 2020-02-20 11:36:08 -05:00
Eliza Weisman b37e4a4380 sync: improve RwLock API docs (#2252)
Currently, the documentation for `tokio::sync::RwLock` states that it
has an unspecified priority policy dependent on the operating system.
This is incorrect: Tokio's `RwLock` is fairly queued. The incorrect
documentation appears to have been copied from the `std::sync::RwLock`
docs, for which this *is* the case.

This commit corrects the documentation to describe the actual priority
policy.

Signed-off-by: Eliza Weisman <[email protected]>
2020-02-18 12:43:29 -08:00
Tudor Sidea 41576e6c48 Added Ord and Hash as derived traits for tokio::time::Instant (#2239)
The added derived traits mirror the traits of std::time::Instant. As
tokio::time::Instant is just a wrapper around std::time::Instant, it
should also derive all the traits std::time::Instant derives.
2020-02-17 11:18:19 -05:00
Waffle Lapkin 09b5f47381 Add Mutex::into_inner method (#2250)
Add `Mutex::into_inner` method that consumes mutex
and return underlying value.

Fixes: #2211
2020-02-17 11:16:48 -05:00
Jon Gjengset fc65951731 UnixStream::poll_shutdown is not a no-op (#2245) 2020-02-14 12:22:29 -05:00
Jon Gjengset 564da5c128 Test some more mpsc behavior with loom (#2246) 2020-02-14 12:03:57 -05:00
Luca Bruno 466dd4a851 rt: lazily detect number of CPUs (#2238)
This tweaks the runtime builder default to defer and lazily
auto-detect the number of CPUs. This is done in order to
avoid performing useless operations which may be expensive
on some platforms (e.g. Linux, where it is coupled to CPU
frequency probing).

Ref: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7d5905dc14a87805a59f3c5bf70173aac2bb18f8
2020-02-13 10:07:06 -08:00
Jon Gjengset 2eed6d00f5 Avoid race in tcp_accept::no_extra_poll (#2236) 2020-02-12 15:53:56 -05:00
Jon Gjengset c1232a6520 io: avoid unnecessary wake in registration (#2221)
See discussion in #2222. This wake/notify call has been there in one
form or another since the very early days of tokio. Currently though, it
is not clear that it is needed; the contract for polling is that you
must keep polling until you get `Pending`, so doing a wakeup when we are
about to return `Ready` is premature.
2020-02-12 11:09:44 -08:00
lord 5e75b0446d Fix doc comment spelling in lib.rs (#2233) 2020-02-11 16:09:40 -05:00
Christian Vallentin d49e6ae1b3 Fixed typos in examples (#2231) 2020-02-11 10:56:32 -05:00
Wade Mealing 874264a4ef Correct link to the guide on tokio.rs (#2229)
The current link to tokio.rs/docs 404's.  This change redirects the "guides" link to the docs overview (which may be what was intended).
2020-02-11 10:56:13 -05:00
Jon GjengsetandCarl Lerche 55b5e1b6ad Fix #2119 and failing state assertion (#2212)
Add a test for #2119 and failing state assertion,
and a fix to go with it.

Co-authored-by: Carl Lerche <[email protected]>
2020-02-04 11:27:18 -05:00
Tore Pettersen 513671f8de stream: add StreamExt::skip_while (#2205)
async version of Iterator::skip_while

Refs: #2104
2020-02-02 11:36:41 -08:00
Alice Ryhl 79e4514283 util: add links to tokio-util + example to BytesCodec (#2207) 2020-02-01 14:04:58 -08:00
Daniel Müller 64e75ad1b0 time: Add comment about cancelation of timed out futures (#2206)
While the module documentation explains that a timed out future (as
created through the tokio::time::timeout function) is canceled, the
function's actual documentation doesn't mention that at all. This change
adds this relevant information to the documentation.
2020-02-01 14:04:17 -08:00
Tore Pettersen 1a5de2c79d stream: add StreamExt::skip (#2204)
skip version of take

Refs: #2104
2020-02-01 14:03:34 -08:00
Carl Lerche ab24a655ad stream: provide StreamMap utility (#2185)
`StreamMap` is similar to `StreamExt::merge` in that it combines source
streams into a single merged stream that yields values in the order that
they arrive from the source streams. However, `StreamMap` has a lot more
flexibility in usage patterns.

`StreamMap` can:

- Merge an arbitrary number of streams.
- Track which source stream the value was received from.
- Handle inserting and removing streams from the set of managed streams
  at any point during iteration.

All source streams held by `StreamMap` are indexed using a key. This key
is included with the value when a source stream yields a value. The key
is also used to remove the stream from the `StreamMap` before the stream
has completed streaming.

Because the `StreamMap` API moves streams during runtime, both streams
and keys must be `Unpin`. In order to insert a `!Unpin` stream into a
`StreamMap`, use `pin!` to pin the stream to the stack or `Box::pin` to
pin the stream in the heap.
2020-01-31 21:18:11 -08:00
Markus Westerlind c3d56b85c3 codec: use advance over split_to when data is not needed (#2198) 2020-01-30 11:26:19 -08:00
roignpar 1eee6508fc sync: fix broadcast link in API docs (#2197) 2020-01-30 11:25:51 -08:00
Sean McArthur 116a18b849 sync: reduce memory size of watch::Receiver (#2191)
This reduces the `mem::size_of::<watch::Receiver>()` from 4 words to 2.

- The `id` is now the pointer of the `Arc<WatchInner>`.
- The `ver` is moved into the `WatchInner`.
2020-01-29 12:22:21 -08:00
Carl Lerche 9d6b99494b rt: add Runtime::shutdown_timeout (#2186)
Provides an API for forcing a runtime to shutdown even if there are
still running tasks.
2020-01-29 12:00:40 -08:00
Tomasz Miąsko 560d0fa548 rt: read join waker conditionally to avoid data race (#2096)
The previous implementation would perform a load that might be part of a
data race. The value read would be used only when race did not occur.
This would be well defined in a memory model where a load that is a part
of race merely returns an undefined value, the Rust memory model on the
other hand defines it to be undefined behaviour.

Perform read conditionally to avoid data race.

Covered by existing loom tests after changing casualty check to be
immediate rather than deferred.

Fixes: #2087
2020-01-29 11:44:15 -08:00
Carl Lerche 6232c74724 macros: correctly feature gate join/try_join (#2196)
The `macros` feature flag was ommitted despite the fact that these
macros require the feature flag to function. The macros are now scoped
by the `macros` feature flag.

This is *not* a breaking change due to the fact that the macros were
broken without the `macros` feature flag in the first place.
2020-01-29 11:16:58 -08:00
Eliza WeismanandWim Looman be832f20cb util: add futures-io/tokio::io compatibility layer (#2117)
* util: add futures-io/tokio::io compatibility layer

This PR adds a compatibility layer with conversions between the
`tokio::io` and `futures-io` versions of the `AsyncRead` and
`AsyncWrite` traits.

I initially opened this PR against `tokio-compat`, but we decided that
a compatibility layer for current versions of the `tokio` and
`futures-io` crates (rather than for compatibility with legacy code)
ought to go in `tokio-util` instead. See:
https://github.com/tokio-rs/tokio-compat/pull/2#issuecomment-551310953

This is based on code originally written by @Nemo157 as part of the
`futures-tokio-compat` crate, and is contributed on behalf of the
original author:
https://github.com/Nemo157/futures-tokio-compat/issues/2#issuecomment-544118866

Closes tokio-rs/tokio-compat#2

Co-authored-by: Wim Looman <[email protected]>
Signed-off-by: Eliza Weisman <[email protected]>
2020-01-29 11:14:24 -08:00
Avery Harnish 326f724978 chore: fix typos in ROADMAP.md (#2190) 2020-01-29 11:12:35 -08:00
Avery Harnish 81c20d8454 chore: improve discoverability of CoC (#2180) 2020-01-29 10:56:11 -08:00
Jon Gjengset b70f1ce3c0 rt: enable task state assertions under loom (#2192) 2020-01-29 10:50:52 -08:00
Vitor Enes 64e4bd1b2f docs: minor fixes to TcpStream API docs (#2183) 2020-01-28 14:07:51 -08:00
Tore 8ed209b612 docs: fix stream::pending() example (#2189) 2020-01-28 11:07:29 -08:00
Lucio Franco 4a24c7063b sync: add mpsc benchmark (#2166) 2020-01-27 20:48:35 -08:00
Juan Alvarez e2230f3392 timer: fix out of bounds error (#2184) 2020-01-27 20:46:52 -08:00
369 changed files with 23380 additions and 11208 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 }}
+246
View File
@@ -0,0 +1,246 @@
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
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
+7
View File
@@ -0,0 +1,7 @@
# Code of Conduct
The Tokio project adheres to the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct). This describes the minimum behavior expected from all contributors.
## Enforcement
Instances of violations of the Code of Conduct can be reported by contacting the project team at [[email protected]](mailto:[email protected]).
+130 -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.
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.
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
@@ -112,6 +117,44 @@ usually a good idea to first open an issue describing the change to solicit
feedback and guidance. This will increase the likelihood of the PR getting
merged.
### Cargo Commands
Due to the extensive use of features in Tokio, you will often need to add extra
arguments to many common cargo commands. This section lists some commonly needed
commands.
Some commands just need the `--all-features` argument:
```
cargo build --all-features
cargo check --all-features
cargo test --all-features
```
When building documentation normally, the markers that list the features
required for various parts of Tokio are missing. To build the documentation
correctly, use this command:
```
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
```
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.
You can run loom tests with
```
cd tokio # tokio crate in workspace
LOOM_MAX_PREEMPTIONS=1 RUSTFLAGS="--cfg loom" \
cargo test --lib --release --features full -- --test-threads=1 --nocapture
```
### Tests
If the change being proposed alters code (as opposed to only documentation for
@@ -217,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
@@ -288,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
@@ -382,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
+22 -14
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/) |
[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/
[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
@@ -110,7 +118,7 @@ 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
@@ -127,7 +135,7 @@ several other libraries, including:
* [`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
@@ -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
+2 -2
View File
@@ -26,13 +26,13 @@ The Tokio 1.0 release will be **no later** than Q3 2020. It will also come with
When Tokio 1.0 is released in Q3 2020, on-going support, security fixes, and
critical bug fixes are guaranteed until **at least** Q3 2025. Tokio 2.0 will not
be released until **at least** Q3 2023 (though, ideally there will never been a
be released until **at least** Q3 2023 (though, ideally there will never be a
Tokio 2.0 release).
### How to get there
While Tokio 0.1 probably should have been a 1.0, Tokio 0.2 will be a **true**
0.2 release. There will breaking change releases every 2 ~ 3 months until 1.0.
0.2 release. There will be breaking change releases every 2 ~ 3 months until 1.0.
These changes will be **much** smaller than going from 0.1 -> 0.2. It is
expected that the 1.0 release will look a lot like 0.2.
+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`).
-115
View File
@@ -1,115 +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 loom tests
- template: ci/azure-loom.yml
parameters:
name: loom
rust: stable
crates:
- tokio
# 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
- template: ci/azure-deploy-docs.yml
parameters:
rust: stable
dependsOn:
- rustfmt
- clippy
- test_tokio
- test_linux
- test_build
- loom
- cross
- minrust
- check_features
# - tsan
+21
View File
@@ -12,3 +12,24 @@ bencher = "0.1.5"
name = "spawn"
path = "spawn.rs"
harness = false
[[bench]]
name = "mpsc"
path = "mpsc.rs"
harness = false
[[bench]]
name = "scheduler"
path = "scheduler.rs"
harness = false
[[bench]]
name = "sync_rwlock"
path = "sync_rwlock.rs"
harness = false
[[bench]]
name = "sync_semaphore"
path = "sync_semaphore.rs"
harness = false
+188
View File
@@ -0,0 +1,188 @@
use bencher::{black_box, Bencher};
use tokio::sync::mpsc;
type Medium = [usize; 64];
type Large = [Medium; 64];
fn create_1_medium(b: &mut Bencher) {
b.iter(|| {
black_box(&mpsc::channel::<Medium>(1));
});
}
fn create_100_medium(b: &mut Bencher) {
b.iter(|| {
black_box(&mpsc::channel::<Medium>(100));
});
}
fn create_100_000_medium(b: &mut Bencher) {
b.iter(|| {
black_box(&mpsc::channel::<Medium>(100_000));
});
}
fn send_medium(b: &mut Bencher) {
b.iter(|| {
let (mut tx, mut rx) = mpsc::channel::<Medium>(1000);
let _ = tx.try_send([0; 64]);
rx.try_recv().unwrap();
});
}
fn send_large(b: &mut Bencher) {
b.iter(|| {
let (mut tx, mut rx) = mpsc::channel::<Large>(1000);
let _ = tx.try_send([[0; 64]; 64]);
rx.try_recv().unwrap();
});
}
fn contention_bounded(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for _ in 0..5 {
let mut tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
for _ in 0..1_000 * 5 {
let _ = rx.recv().await;
}
})
});
}
fn contention_bounded_full(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(100);
for _ in 0..5 {
let mut tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
for _ in 0..1_000 * 5 {
let _ = rx.recv().await;
}
})
});
}
fn contention_unbounded(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).unwrap();
}
});
}
for _ in 0..1_000 * 5 {
let _ = rx.recv().await;
}
})
});
}
fn uncontented_bounded(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
b.iter(|| {
rt.block_on(async move {
let (mut tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for i in 0..5000 {
tx.send(i).await.unwrap();
}
for _ in 0..5_000 {
let _ = rx.recv().await;
}
})
});
}
fn uncontented_unbounded(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
for i in 0..5000 {
tx.send(i).unwrap();
}
for _ in 0..5_000 {
let _ = rx.recv().await;
}
})
});
}
bencher::benchmark_group!(
create,
create_1_medium,
create_100_medium,
create_100_000_medium
);
bencher::benchmark_group!(send, send_medium, send_large);
bencher::benchmark_group!(
contention,
contention_bounded,
contention_bounded_full,
contention_unbounded,
uncontented_bounded,
uncontented_unbounded
);
bencher::benchmark_main!(create, send, contention);
+152
View File
@@ -0,0 +1,152 @@
//! Benchmark implementation details of the theaded scheduler. These benches are
//! intended to be used as a form of regression testing and not as a general
//! purpose benchmark demonstrating real-world performance.
use tokio::runtime::{self, Runtime};
use tokio::sync::oneshot;
use bencher::{benchmark_group, benchmark_main, Bencher};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::{mpsc, Arc};
fn spawn_many(b: &mut Bencher) {
const NUM_SPAWN: usize = 10_000;
let mut rt = rt();
let (tx, rx) = mpsc::sync_channel(1000);
let rem = Arc::new(AtomicUsize::new(0));
b.iter(|| {
rem.store(NUM_SPAWN, Relaxed);
rt.block_on(async {
for _ in 0..NUM_SPAWN {
let tx = tx.clone();
let rem = rem.clone();
tokio::spawn(async move {
if 1 == rem.fetch_sub(1, Relaxed) {
tx.send(()).unwrap();
}
});
}
let _ = rx.recv().unwrap();
});
});
}
fn yield_many(b: &mut Bencher) {
const NUM_YIELD: usize = 1_000;
const TASKS: usize = 200;
let rt = rt();
let (tx, rx) = mpsc::sync_channel(TASKS);
b.iter(move || {
for _ in 0..TASKS {
let tx = tx.clone();
rt.spawn(async move {
for _ in 0..NUM_YIELD {
tokio::task::yield_now().await;
}
tx.send(()).unwrap();
});
}
for _ in 0..TASKS {
let _ = rx.recv().unwrap();
}
});
}
fn ping_pong(b: &mut Bencher) {
const NUM_PINGS: usize = 1_000;
let mut rt = rt();
let (done_tx, done_rx) = mpsc::sync_channel(1000);
let rem = Arc::new(AtomicUsize::new(0));
b.iter(|| {
let done_tx = done_tx.clone();
let rem = rem.clone();
rem.store(NUM_PINGS, Relaxed);
rt.block_on(async {
tokio::spawn(async move {
for _ in 0..NUM_PINGS {
let rem = rem.clone();
let done_tx = done_tx.clone();
tokio::spawn(async move {
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
tokio::spawn(async move {
rx1.await.unwrap();
tx2.send(()).unwrap();
});
tx1.send(()).unwrap();
rx2.await.unwrap();
if 1 == rem.fetch_sub(1, Relaxed) {
done_tx.send(()).unwrap();
}
});
}
});
done_rx.recv().unwrap();
});
});
}
fn chained_spawn(b: &mut Bencher) {
const ITER: usize = 1_000;
let mut rt = rt();
fn iter(done_tx: mpsc::SyncSender<()>, n: usize) {
if n == 0 {
done_tx.send(()).unwrap();
} else {
tokio::spawn(async move {
iter(done_tx, n - 1);
});
}
}
let (done_tx, done_rx) = mpsc::sync_channel(1000);
b.iter(move || {
let done_tx = done_tx.clone();
rt.block_on(async {
tokio::spawn(async move {
iter(done_tx, ITER);
});
done_rx.recv().unwrap();
});
});
}
fn rt() -> Runtime {
runtime::Builder::new()
.threaded_scheduler()
.core_threads(4)
.enable_all()
.build()
.unwrap()
}
benchmark_group!(scheduler, spawn_many, ping_pong, yield_many, chained_spawn,);
benchmark_main!(scheduler);
+2 -2
View File
@@ -60,11 +60,11 @@ fn threaded_scheduler_remote_spawn(bench: &mut Bencher) {
}
bencher::benchmark_group!(
benches,
spawn,
basic_scheduler_local_spawn,
threaded_scheduler_local_spawn,
basic_scheduler_remote_spawn,
threaded_scheduler_remote_spawn
);
bencher::benchmark_main!(benches);
bencher::benchmark_main!(spawn);
+147
View File
@@ -0,0 +1,147 @@
use bencher::{black_box, Bencher};
use std::sync::Arc;
use tokio::{sync::RwLock, task};
fn read_uncontended(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
let lock = Arc::new(RwLock::new(()));
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
for _ in 0..6 {
let read = lock.read().await;
black_box(read);
}
})
});
}
fn read_concurrent_uncontended_multi(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
async fn task(lock: Arc<RwLock<()>>) {
let read = lock.read().await;
black_box(read);
}
let lock = Arc::new(RwLock::new(()));
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
let j = tokio::try_join! {
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone()))
};
j.unwrap();
})
});
}
fn read_concurrent_uncontended(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
.build()
.unwrap();
async fn task(lock: Arc<RwLock<()>>) {
let read = lock.read().await;
black_box(read);
}
let lock = Arc::new(RwLock::new(()));
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
tokio::join! {
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone())
};
})
});
}
fn read_concurrent_contended_multi(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
async fn task(lock: Arc<RwLock<()>>) {
let read = lock.read().await;
black_box(read);
}
let lock = Arc::new(RwLock::new(()));
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
let write = lock.write().await;
let j = tokio::try_join! {
async move { drop(write); Ok(()) },
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())),
};
j.unwrap();
})
});
}
fn read_concurrent_contended(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
.build()
.unwrap();
async fn task(lock: Arc<RwLock<()>>) {
let read = lock.read().await;
black_box(read);
}
let lock = Arc::new(RwLock::new(()));
b.iter(|| {
let lock = lock.clone();
rt.block_on(async move {
let write = lock.write().await;
tokio::join! {
async move { drop(write) },
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
task(lock.clone()),
};
})
});
}
bencher::benchmark_group!(
sync_rwlock,
read_uncontended,
read_concurrent_uncontended,
read_concurrent_uncontended_multi,
read_concurrent_contended,
read_concurrent_contended_multi
);
bencher::benchmark_main!(sync_rwlock);
+130
View File
@@ -0,0 +1,130 @@
use bencher::Bencher;
use std::sync::Arc;
use tokio::{sync::Semaphore, task};
fn uncontended(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
let s = Arc::new(Semaphore::new(10));
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
for _ in 0..6 {
let permit = s.acquire().await;
drop(permit);
}
})
});
}
async fn task(s: Arc<Semaphore>) {
let permit = s.acquire().await;
drop(permit);
}
fn uncontended_concurrent_multi(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
let s = Arc::new(Semaphore::new(10));
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
let j = tokio::try_join! {
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone()))
};
j.unwrap();
})
});
}
fn uncontended_concurrent_single(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
.build()
.unwrap();
let s = Arc::new(Semaphore::new(10));
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
tokio::join! {
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone())
};
})
});
}
fn contended_concurrent_multi(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.core_threads(6)
.threaded_scheduler()
.build()
.unwrap();
let s = Arc::new(Semaphore::new(5));
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
let j = tokio::try_join! {
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone())),
task::spawn(task(s.clone()))
};
j.unwrap();
})
});
}
fn contended_concurrent_single(b: &mut Bencher) {
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
.build()
.unwrap();
let s = Arc::new(Semaphore::new(5));
b.iter(|| {
let s = s.clone();
rt.block_on(async move {
tokio::join! {
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone()),
task(s.clone())
};
})
});
}
bencher::benchmark_group!(
sync_semaphore,
uncontended,
uncontended_concurrent_multi,
uncontended_concurrent_single,
contended_concurrent_multi,
contended_concurrent_single
);
bencher::benchmark_main!(sync_semaphore);
-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-10.13
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'
-33
View File
@@ -1,33 +0,0 @@
steps:
# Linux and macOS.
- script: |
set -e
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
-18
View File
@@ -1,18 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: Loom tests
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust }}
- ${{ each crate in parameters.crates }}:
- script: RUSTFLAGS="--cfg loom" cargo test --lib --release --features "full" -- --test-threads=1 --nocapture
env:
LOOM_MAX_PREEMPTIONS: 1
CI: 'True'
displayName: test ${{ crate }}
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
-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-10.13
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-10.13
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:
LOOM_MAX_PREEMPTIONS: 2
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:
LOOM_MAX_PREEMPTIONS: 2
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
+6 -2
View File
@@ -4,9 +4,13 @@ version = "0.0.0"
publish = false
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-util = { version = "0.2.0", path = "../tokio-util", 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"
http = "0.2"
+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
+33 -13
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 occured; error = {:?}", e);
tracing::info!("an error occurred; error = {:?}", e);
}
});
}
@@ -175,7 +196,7 @@ impl Stream for Peer {
// We've received a message we should broadcast to others.
Some(Ok(message)) => Some(Ok(Message::Broadcast(message))),
// An error occured.
// An error occurred.
Some(Err(e)) => Some(Err(e)),
// The stream has been exhausted.
@@ -193,16 +214,14 @@ async fn process(
let mut lines = Framed::new(stream, LinesCodec::new());
// Send a prompt to the client to enter their username.
lines
.send(String::from("Please enter your username:"))
.await?;
lines.send("Please enter your username:").await?;
// Read the first line from the `LineCodec` stream to get the username.
let username = match lines.next().await {
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(());
}
};
@@ -214,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;
}
@@ -232,12 +251,13 @@ async fn process(
// A message was received from a peer. Send it to the
// current user.
Ok(Message::Received(msg)) => {
peer.lines.send(msg).await?;
peer.lines.send(&msg).await?;
}
Err(e) => {
println!(
"an error occured while processing messages for {}; error = {:?}",
username, e
tracing::error!(
"an error occurred while processing messages for {}; error = {:?}",
username,
e
);
}
}
@@ -250,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 -1
View File
@@ -130,7 +130,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
let response = response.serialize();
if let Err(e) = lines.send(response).await {
if let Err(e) = lines.send(response.as_str()).await {
println!("error on sending response; error = {:?}", e);
}
}
+1 -3
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;
@@ -96,8 +95,7 @@ struct Http;
/// Implementation of encoding an HTTP response into a `BytesMut`, basically
/// just writing out an HTTP/1.1 response.
impl Encoder for Http {
type Item = Response<String>;
impl Encoder<Response<String>> for Http {
type Error = io::Error;
fn encode(&mut self, item: Response<String>, dst: &mut BytesMut) -> io::Result<()> {
+1 -1
View File
@@ -46,7 +46,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Run both futures simultaneously of `a` and `b` sending messages back and forth.
match futures::future::try_join(a, b).await {
Err(e) => println!("an error occured; error = {:?}", e),
Err(e) => println!("an error occurred; error = {:?}", e),
_ => println!("done!"),
}
+1
View File
@@ -15,6 +15,7 @@ full = [
"tokio-test"
]
macros = ["tokio/macros"]
sync = ["tokio/sync"]
rt-core = ["tokio/rt-core"]
rt-threaded = ["rt-core", "tokio/rt-threaded"]
+32
View File
@@ -0,0 +1,32 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "sync")]
use tokio::runtime;
use tokio::sync::oneshot;
use std::sync::mpsc;
use std::thread;
#[test]
fn basic_shell_rt() {
let (feed_tx, feed_rx) = mpsc::channel::<oneshot::Sender<()>>();
let th = thread::spawn(move || {
for tx in feed_rx.iter() {
tx.send(()).unwrap();
}
});
for _ in 0..1_000 {
let mut rt = runtime::Builder::new().build().unwrap();
let (tx, rx) = oneshot::channel();
feed_tx.send(tx).unwrap();
rt.block_on(rx).unwrap();
}
drop(feed_tx);
th.join().unwrap();
}
+31 -7
View File
@@ -1,26 +1,50 @@
# 0.2.4 (January 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
- generics on `#[tokio::main]` function (#2177).
- doc improvements ([#2225]).
# 0.2.4 (January 27, 2020)
### Fixed
- 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.4"
# - 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.3/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()
+201 -44
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio-macros/0.2.3")]
#![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,12 +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:
///
/// - `core_threads=n` - Sets core threads to `n`.
/// - `max_threads=n` - Sets max threads to `n`.
/// 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:
///
@@ -46,26 +52,93 @@ use proc_macro::TokenStream;
/// }
/// ```
///
/// Equivalent code not using `#[tokio::main]`
///
/// ```rust
/// 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 = 1)]
/// #[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:
///
/// - `basic_scheduler` - All tasks are executed on the current thread.
/// - `threaded_scheduler` - Uses the multi-threaded scheduler. Used by default.
/// - `threaded_scheduler` - Uses the multi-threaded scheduler. Used by default (requires `rt-threaded` feature).
///
/// ## Function arguments:
///
@@ -82,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
@@ -90,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:
///
@@ -116,49 +226,41 @@ 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 {
entry::main(args, item, false)
}
/// Marks async function to be executed by runtime, suitable to test enviornment
/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `basic_scheduler` - All tasks are executed on the current thread. Used by default.
/// - `threaded_scheduler` - Use multi-threaded scheduler.
///
/// ## Usage
///
/// ### Select runtime
///
/// ```no_run
/// #[tokio::test(threaded_scheduler)]
/// async fn my_test() {
/// assert!(true);
/// }
/// ```
///
/// ### Using default
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
/// assert!(true);
/// }
/// ```
#[proc_macro_attribute]
pub fn test_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, true)
}
/// Marks async function to be executed by runtime, suitable to test enviornment
///
/// ## Options:
///
/// - `core_threads=n` - Sets core threads to `n`.
/// - `max_threads=n` - Sets max threads to `n`.
/// - `core_threads=n` - Sets core threads to `n` (requires `rt-threaded` feature).
/// - `max_threads=n` - Sets max threads to `n` (requires `rt-core` or `rt-threaded` feature).
///
/// ## Usage
///
@@ -179,12 +281,59 @@ 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_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args, item, true)
}
/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `basic_scheduler` - All tasks are executed on the current thread. Used by default.
/// - `threaded_scheduler` - Use multi-threaded scheduler (requires `rt-threaded` feature).
///
/// ## Usage
///
/// ### Select runtime
///
/// ```no_run
/// #[tokio::test(threaded_scheduler)]
/// async fn my_test() {
/// assert!(true);
/// }
/// ```
///
/// ### Using default
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
/// 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)
}
/// Marks async function to be executed by runtime, suitable to test enviornment
/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
@@ -198,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
"""
+65 -3
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
@@ -27,6 +27,7 @@ use futures_core::ready;
use std::collections::VecDeque;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{self, Poll, Waker};
use std::{cmp, io};
@@ -57,6 +58,10 @@ enum Action {
Read(Vec<u8>),
Write(Vec<u8>),
Wait(Duration),
// Wrapped in Arc so that Builder can be cloned and Send.
// Mock is not cloned as does not need to check Rc for ref counts.
ReadError(Option<Arc<io::Error>>),
WriteError(Option<Arc<io::Error>>),
}
#[derive(Debug)]
@@ -83,6 +88,16 @@ impl Builder {
self
}
/// Sequence a `read` operation that produces an error.
///
/// The next operation in the mock's script will be to expect a `read` call
/// and return `error`.
pub fn read_error(&mut self, error: io::Error) -> &mut Self {
let error = Some(error.into());
self.actions.push_back(Action::ReadError(error));
self
}
/// Sequence a `write` operation.
///
/// The next operation in the mock's script will be to expect a `write`
@@ -92,6 +107,16 @@ impl Builder {
self
}
/// Sequence a `write` operation that produces an error.
///
/// The next operation in the mock's script will be to expect a `write`
/// call that provides `error`.
pub fn write_error(&mut self, error: io::Error) -> &mut Self {
let error = Some(error.into());
self.actions.push_back(Action::WriteError(error));
self
}
/// Sequence a wait.
///
/// The next operation in the mock's script will be to wait without doing so
@@ -128,6 +153,16 @@ impl Handle {
self
}
/// Sequence a `read` operation error.
///
/// The next operation in the mock's script will be to expect a `read` call
/// and return `error`.
pub fn read_error(&mut self, error: io::Error) -> &mut Self {
let error = Some(error.into());
self.tx.send(Action::ReadError(error)).unwrap();
self
}
/// Sequence a `write` operation.
///
/// The next operation in the mock's script will be to expect a `write`
@@ -136,6 +171,16 @@ impl Handle {
self.tx.send(Action::Write(buf.into())).unwrap();
self
}
/// Sequence a `write` operation error.
///
/// The next operation in the mock's script will be to expect a `write`
/// call error.
pub fn write_error(&mut self, error: io::Error) -> &mut Self {
let error = Some(error.into());
self.tx.send(Action::WriteError(error)).unwrap();
self
}
}
impl Inner {
@@ -174,6 +219,12 @@ impl Inner {
// Return the number of bytes read
Ok(n)
}
Some(&mut Action::ReadError(ref mut err)) => {
// As the
let err = err.take().expect("Should have been removed from actions.");
let err = Arc::try_unwrap(err).expect("There are no other references.");
Err(err)
}
Some(_) => {
// Either waiting or expecting a write
Err(io::ErrorKind::WouldBlock.into())
@@ -193,6 +244,12 @@ impl Inner {
return Err(io::ErrorKind::WouldBlock.into());
}
if let Some(&mut Action::WriteError(ref mut err)) = self.action() {
let err = err.take().expect("Should have been removed from actions.");
let err = Arc::try_unwrap(err).expect("There are no other references.");
return Err(err);
}
for i in 0..self.actions.len() {
match self.actions[i] {
Action::Write(ref mut expect) => {
@@ -210,7 +267,7 @@ impl Inner {
return Ok(ret);
}
}
Action::Wait(..) => {
Action::Wait(..) | Action::WriteError(..) => {
break;
}
_ => {}
@@ -258,6 +315,11 @@ impl Inner {
break;
}
}
Action::ReadError(ref mut error) | Action::WriteError(ref mut error) => {
if error.is_some() {
break;
}
}
}
let _action = self.actions.pop_front();
@@ -272,7 +334,7 @@ impl Inner {
impl Mock {
fn maybe_wakeup_reader(&mut self) {
match self.inner.action() {
Some(&mut Action::Read(_)) | None => {
Some(&mut Action::Read(_)) | Some(&mut Action::ReadError(_)) | None => {
if let Some(waker) = self.inner.read_wait.take() {
waker.wake();
}
+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 {
+47
View File
@@ -1,5 +1,6 @@
#![warn(rust_2018_idioms)]
use std::io;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio_test::io::Builder;
@@ -16,6 +17,31 @@ async fn read() {
assert_eq!(&buf[..n], b"world!");
}
#[tokio::test]
async fn read_error() {
let error = io::Error::new(io::ErrorKind::Other, "cruel");
let mut mock = Builder::new()
.read(b"hello ")
.read_error(error)
.read(b"world!")
.build();
let mut buf = [0; 256];
let n = mock.read(&mut buf).await.expect("read 1");
assert_eq!(&buf[..n], b"hello ");
match mock.read(&mut buf).await {
Err(error) => {
assert_eq!(error.kind(), io::ErrorKind::Other);
assert_eq!("cruel", format!("{}", error));
}
Ok(_) => panic!("error not received"),
}
let n = mock.read(&mut buf).await.expect("read 1");
assert_eq!(&buf[..n], b"world!");
}
#[tokio::test]
async fn write() {
let mut mock = Builder::new().write(b"hello ").write(b"world!").build();
@@ -23,3 +49,24 @@ async fn write() {
mock.write_all(b"hello ").await.expect("write 1");
mock.write_all(b"world!").await.expect("write 2");
}
#[tokio::test]
async fn write_error() {
let error = io::Error::new(io::ErrorKind::Other, "cruel");
let mut mock = Builder::new()
.write(b"hello ")
.write_error(error)
.write(b"world!")
.build();
mock.write_all(b"hello ").await.expect("write 1");
match mock.write_all(b"whoa").await {
Err(error) => {
assert_eq!(error.kind(), io::ErrorKind::Other);
assert_eq!("cruel", format!("{}", error));
}
Ok(_) => panic!("error not received"),
}
mock.write_all(b"world!").await.expect("write 2");
}
-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.2.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]);
}
+29
View File
@@ -1,3 +1,32 @@
# 0.3.1 (March 18, 2020)
### Fixed
- Adjust minimum-supported Tokio version to v0.2.5 to account for an internal
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])
### Added
- 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])
# 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
+8 -5
View File
@@ -7,13 +7,13 @@ name = "tokio-util"
# - Cargo.toml
# - Update CHANGELOG.md.
# - Create "v0.2.x" git tag.
version = "0.2.0"
version = "0.3.1"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-util/0.2.0/tokio_util"
documentation = "https://docs.rs/tokio-util/0.3.1/tokio_util"
description = """
Additional utilities for working with Tokio.
"""
@@ -24,19 +24,22 @@ categories = ["asynchronous"]
default = []
# Shorthand for enabling everything
full = ["codec", "udp"]
full = ["codec", "udp", "compat"]
compat = ["futures-io",]
codec = ["tokio/stream"]
udp = ["tokio/udp"]
rt = ["tokio/rt-core"]
[dependencies]
tokio = { version = "0.2.0", path = "../tokio" }
tokio = { version = "0.2.5", path = "../tokio" }
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"
pin-project-lite = "0.1.1"
pin-project-lite = "0.1.4"
[dev-dependencies]
tokio = { version = "0.2.0", path = "../tokio", features = ["full"] }
+20
View File
@@ -8,6 +8,26 @@ macro_rules! cfg_codec {
}
}
macro_rules! cfg_compat {
($($item:item)*) => {
$(
#[cfg(feature = "compat")]
#[cfg_attr(docsrs, doc(cfg(feature = "compat")))]
$item
)*
}
}
macro_rules! cfg_rt {
($($item:item)*) => {
$(
#[cfg(feature = "rt")]
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
$item
)*
}
}
macro_rules! cfg_udp {
($($item:item)*) => {
$(
+38 -3
View File
@@ -4,7 +4,43 @@ use crate::codec::encoder::Encoder;
use bytes::{BufMut, Bytes, BytesMut};
use std::io;
/// A simple `Codec` implementation that just ships bytes around.
/// A simple [`Decoder`] and [`Encoder`] implementation that just ships bytes around.
///
/// [`Decoder`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
///
/// # Example
///
/// Turn an [`AsyncRead`] into a stream of `Result<`[`BytesMut`]`, `[`Error`]`>`.
///
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`BytesMut`]: bytes::BytesMut
/// [`Error`]: std::io::Error
///
/// ```
/// # mod hidden {
/// # #[allow(unused_imports)]
/// use tokio::fs::File;
/// # }
/// use tokio::io::AsyncRead;
/// use tokio_util::codec::{FramedRead, BytesCodec};
///
/// # enum File {}
/// # impl File {
/// # async fn open(_name: &str) -> Result<impl AsyncRead, std::io::Error> {
/// # use std::io::Cursor;
/// # Ok(Cursor::new(vec![0, 1, 2, 3, 4, 5]))
/// # }
/// # }
/// #
/// # #[tokio::main(core_threads = 1)]
/// # async fn main() -> Result<(), std::io::Error> {
/// let my_async_read = File::open("filename.txt").await?;
/// let my_stream_of_bytes = FramedRead::new(my_async_read, BytesCodec::new());
/// # Ok(())
/// # }
/// ```
///
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct BytesCodec(());
@@ -29,8 +65,7 @@ impl Decoder for BytesCodec {
}
}
impl Encoder for BytesCodec {
type Item = Bytes;
impl Encoder<Bytes> for BytesCodec {
type Error = io::Error;
fn encode(&mut self, data: Bytes, buf: &mut BytesMut) -> Result<(), io::Error> {
+21 -10
View File
@@ -1,4 +1,3 @@
use crate::codec::encoder::Encoder;
use crate::codec::Framed;
use tokio::io::{AsyncRead, AsyncWrite};
@@ -8,14 +7,17 @@ use std::io;
/// Decoding of frames via buffers.
///
/// This trait is used when constructing an instance of `Framed` or
/// `FramedRead`. An implementation of `Decoder` takes a byte stream that has
/// This trait is used when constructing an instance of [`Framed`] or
/// [`FramedRead`]. An implementation of `Decoder` takes a byte stream that has
/// already been buffered in `src` and decodes the data into a stream of
/// `Self::Item` frames.
///
/// Implementations are able to track state on `self`, which enables
/// implementing stateful streaming parsers. In many cases, though, this type
/// will simply be a unit struct (e.g. `struct HttpDecoder`).
///
/// [`Framed`]: crate::codec::Framed
/// [`FramedRead`]: crate::codec::FramedRead
pub trait Decoder {
/// The type of decoded frames.
type Item;
@@ -27,17 +29,19 @@ pub trait Decoder {
/// useful to report the failure as an `Item`.
///
/// `From<io::Error>` is required in the interest of making `Error` suitable
/// for returning directly from a `FramedRead`, and to enable the default
/// for returning directly from a [`FramedRead`], and to enable the default
/// implementation of `decode_eof` to yield an `io::Error` when the decoder
/// fails to consume all available data.
///
/// Note that implementors of this trait can simply indicate `type Error =
/// io::Error` to use I/O errors as this type.
///
/// [`FramedRead`]: crate::codec::FramedRead
type Error: From<io::Error>;
/// Attempts to decode a frame from the provided buffer of bytes.
///
/// This method is called by `FramedRead` whenever bytes are ready to be
/// This method is called by [`FramedRead`] whenever bytes are ready to be
/// parsed. The provided buffer of bytes is what's been read so far, and
/// this instance of `Decode` can determine whether an entire frame is in
/// the buffer and is ready to be returned.
@@ -49,7 +53,7 @@ pub trait Decoder {
/// most circumstances.
///
/// If the bytes look valid, but a frame isn't fully available yet, then
/// `Ok(None)` is returned. This indicates to the `Framed` instance that
/// `Ok(None)` is returned. This indicates to the [`Framed`] instance that
/// it needs to read some more bytes before calling this method again.
///
/// Note that the bytes provided may be empty. If a previous call to
@@ -58,9 +62,12 @@ pub trait Decoder {
/// be read.
///
/// Finally, if the bytes in the buffer are malformed then an error is
/// returned indicating why. This informs `Framed` that the stream is now
/// returned indicating why. This informs [`Framed`] that the stream is now
/// corrupt and should be terminated.
///
/// [`Framed`]: crate::codec::Framed
/// [`FramedRead`]: crate::codec::FramedRead
///
/// # Buffer management
///
/// Before returning from the function, implementations should ensure that
@@ -128,7 +135,7 @@ pub trait Decoder {
}
}
/// Provides a `Stream` and `Sink` interface for reading and writing to this
/// Provides a [`Stream`] and [`Sink`] interface for reading and writing to this
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
@@ -143,11 +150,15 @@ pub trait Decoder {
/// underlying object.
///
/// If you want to work more directly with the streams and sink, consider
/// calling `split` on the `Framed` returned by this method, which will
/// calling `split` on the [`Framed`] returned by this method, which will
/// break them into separate objects, allowing them to interact more easily.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Framed`]: crate::codec::Framed
fn framed<T: AsyncRead + AsyncWrite + Sized>(self, io: T) -> Framed<T, Self>
where
Self: Encoder + Sized,
Self: Sized,
{
Framed::new(io, self)
}
+11 -8
View File
@@ -2,21 +2,24 @@ use bytes::BytesMut;
use std::io;
/// Trait of helper objects to write out messages as bytes, for use with
/// `FramedWrite`.
pub trait Encoder {
/// The type of items consumed by the `Encoder`
type Item;
/// [`FramedWrite`].
///
/// [`FramedWrite`]: crate::codec::FramedWrite
pub trait Encoder<Item> {
/// The type of encoding errors.
///
/// `FramedWrite` requires `Encoder`s errors to implement `From<io::Error>`
/// [`FramedWrite`] requires `Encoder`s errors to implement `From<io::Error>`
/// in the interest letting it return `Error`s directly.
///
/// [`FramedWrite`]: crate::codec::FramedWrite
type Error: From<io::Error>;
/// Encodes a frame into the buffer provided.
///
/// This method will encode `item` into the byte buffer provided by `dst`.
/// The `dst` provided is an internal buffer of the `Framed` instance and
/// The `dst` provided is an internal buffer of the [`FramedWrite`] instance and
/// will be written out when possible.
fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error>;
///
/// [`FramedWrite`]: crate::codec::FramedWrite
fn encode(&mut self, item: Item, dst: &mut BytesMut) -> Result<(), Self::Error>;
}
+129 -182
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,84 +11,109 @@ 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};
pin_project! {
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
/// A unified [`Stream`] and [`Sink`] interface to an underlying I/O object, using
/// the `Encoder` and `Decoder` traits to encode and decode frames.
///
/// You can create a `Framed` instance by using the `AsyncRead::framed` adapter.
/// You can create a `Framed` instance by using the [`Decoder::framed`] adapter, or
/// by using the `new` function seen below.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`AsyncRead`]: tokio::io::AsyncRead
/// [`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>
}
}
impl<T, U> Framed<T, U>
where
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder,
{
/// Provides a `Stream` and `Sink` interface for reading and writing to this
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
/// Provides a [`Stream`] and [`Sink`] interface for reading and writing to this
/// I/O object, using [`Decoder`] and [`Encoder`] to read and write the raw data.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
/// wants to batch these into meaningful chunks, called "frames". This
/// method layers framing on top of an I/O object, by using the `Codec`
/// method layers framing on top of an I/O object, by using the codec
/// traits to handle encoding and decoding of messages frames. Note that
/// the incoming and outgoing frame types may be distinct.
///
/// This function returns a *single* object that is both `Stream` and
/// `Sink`; grouping this into a single object is often useful for layering
/// This function returns a *single* object that is both [`Stream`] and
/// [`Sink`]; grouping this into a single object is often useful for layering
/// things like gzip or TLS, which require both read and write access to the
/// underlying object.
///
/// If you want to work more directly with the streams and sink, consider
/// calling `split` on the `Framed` returned by this method, which will
/// calling [`split`] on the `Framed` returned by this method, which will
/// break them into separate objects, allowing them to interact more easily.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Decode`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
/// [`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(),
},
}
}
/// Provides a [`Stream`] and [`Sink`] interface for reading and writing to this
/// I/O object, using [`Decoder`] and [`Encoder`] to read and write the raw data,
/// with a specific read buffer initial capacity.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
/// wants to batch these into meaningful chunks, called "frames". This
/// method layers framing on top of an I/O object, by using the codec
/// traits to handle encoding and decoding of messages frames. Note that
/// the incoming and outgoing frame types may be distinct.
///
/// This function returns a *single* object that is both [`Stream`] and
/// [`Sink`]; grouping this into a single object is often useful for layering
/// things like gzip or TLS, which require both read and write access to the
/// underlying object.
///
/// If you want to work more directly with the streams and sink, consider
/// calling [`split`] on the `Framed` returned by this method, which will
/// break them into separate objects, allowing them to interact more easily.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Decode`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
/// [`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: FramedImpl {
inner,
codec,
state: RWFrames {
read: ReadFrame {
eof: false,
is_readable: false,
buffer: BytesMut::with_capacity(capacity),
},
write: WriteFrame::default(),
},
},
}
}
}
impl<T, U> Framed<T, U> {
/// Provides a `Stream` and `Sink` interface for reading and writing to this
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
/// Provides a [`Stream`] and [`Sink`] interface for reading and writing to this
/// I/O object, using [`Decoder`] and [`Encoder`] to read and write the raw data.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
/// wants to batch these into meaningful chunks, called "frames". This
@@ -97,104 +121,112 @@ impl<T, U> Framed<T, U> {
/// traits to handle encoding and decoding of messages frames. Note that
/// the incoming and outgoing frame types may be distinct.
///
/// This function returns a *single* object that is both `Stream` and
/// `Sink`; grouping this into a single object is often useful for layering
/// This function returns a *single* object that is both [`Stream`] and
/// [`Sink`]; grouping this into a single object is often useful for layering
/// things like gzip or TLS, which require both read and write access to the
/// underlying object.
///
/// This objects takes a stream and a readbuffer and a writebuffer. These field
/// can be obtained from an existing `Framed` with the `into_parts` method.
/// can be obtained from an existing `Framed` with the [`into_parts`] method.
///
/// If you want to work more directly with the streams and sink, consider
/// calling `split` on the `Framed` returned by this method, which will
/// calling [`split`] on the `Framed` returned by this method, which will
/// break them into separate objects, allowing them to interact more easily.
///
/// [`Stream`]: tokio::stream::Stream
/// [`Sink`]: futures_sink::Sink
/// [`Decoder`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
/// [`into_parts`]: crate::codec::Framed::into_parts()
/// [`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(),
},
},
}
}
/// Returns a reference to the underlying I/O stream wrapped by
/// `Frame`.
/// `Framed`.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_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
/// `Frame`.
/// `Framed`.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_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
/// `Frame`.
/// `Framed`.
///
/// Note that care should be taken to not tamper with the underlying codec
/// as it may corrupt the stream of frames otherwise being worked with.
pub fn codec(&self) -> &U {
&self.inner.get_ref().get_ref().codec
&self.inner.codec
}
/// Returns a mutable reference to the underlying codec wrapped by
/// `Frame`.
/// `Framed`.
///
/// Note that care should be taken to not tamper with the underlying codec
/// as it may corrupt the stream of frames otherwise being worked with.
pub fn codec_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
}
/// Consumes the `Frame`, returning its underlying I/O stream.
/// 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.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn into_inner(self) -> T {
self.inner.into_inner().into_inner().io
self.inner.inner
}
/// Consumes the `Frame`, returning its underlying I/O stream, the buffer
/// Consumes the `Framed`, returning its underlying I/O stream, the buffer
/// with unprocessed data, and the codec.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn 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,
@@ -207,28 +239,29 @@ where
}
}
// This impl just defers to the underlying FramedImpl
impl<T, I, U> Sink<I> for Framed<T, U>
where
T: AsyncWrite,
U: Encoder<Item = I>,
U: Encoder<I>,
U::Error: From<io::Error>,
{
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)
}
}
@@ -239,108 +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, U: Encoder> Encoder for Fuse<T, U> {
type Item = U::Item;
type Error = U::Error;
fn encode(&mut self, item: Self::Item, 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 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,
@@ -361,7 +305,10 @@ pub struct FramedParts<T, U> {
impl<T, U> FramedParts<T, U> {
/// Create a new, default, `FramedParts`
pub fn new(io: T, codec: U) -> FramedParts<T, U> {
pub fn new<I>(io: T, codec: U) -> FramedParts<T, U>
where
U: Encoder<I>,
{
FramedParts {
io,
codec,
+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(()))
}
}
+42 -173
View File
@@ -1,36 +1,26 @@
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;
use std::task::{Context, Poll};
pin_project! {
/// A `Stream` of messages decoded from an `AsyncRead`.
/// A [`Stream`] of messages decoded from an [`AsyncRead`].
///
/// [`Stream`]: tokio::stream::Stream
/// [`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>
@@ -41,10 +31,27 @@ 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(),
},
}
}
/// Creates a new `FramedRead` with the given `decoder` and a buffer of `capacity`
/// initial size.
pub fn with_capacity(inner: T, decoder: D, capacity: usize) -> FramedRead<T, D> {
FramedRead {
inner: FramedImpl {
inner,
codec: decoder,
state: ReadFrame {
eof: false,
is_readable: false,
buffer: BytesMut::with_capacity(capacity),
},
},
}
}
}
@@ -57,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
@@ -67,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.
@@ -76,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,
@@ -115,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)
}
}
@@ -162,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;
}
}
}
+25 -219
View File
@@ -1,54 +1,37 @@
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::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};
pin_project! {
/// A `Sink` of frames encoded to an `AsyncWrite`.
/// A [`Sink`] of frames encoded to an `AsyncWrite`.
///
/// [`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,
E: Encoder,
{
/// 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(),
},
}
}
}
@@ -61,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
@@ -71,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.
@@ -80,25 +63,25 @@ 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,
E: Encoder<Item = I>,
E: Encoder<I>,
E::Error: From<io::Error>,
{
type Error = E::Error;
@@ -120,6 +103,7 @@ where
}
}
// This impl just defers to the underlying T: Stream
impl<T, D> Stream for FramedWrite<T, D>
where
T: Stream,
@@ -127,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)
}
}
@@ -144,181 +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<Item = I>,
{
type Error = <T::Codec as Encoder>::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()));
}
// TODO: Add a way to `bytes` to do this w/o returning the drained data.
let _ = pinned.buffer.split_to(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()
}
}
+41 -11
View File
@@ -302,6 +302,37 @@
//! anywhere because it already is factored into the total frame length that
//! is read from the byte stream.
//!
//! ## Example 7
//!
//! The following will parse a 3 byte length field at offset 0 in a 4 byte
//! frame head, excluding the 4th byte from the yielded `BytesMut`.
//!
//! ```
//! # use tokio::io::AsyncRead;
//! # use tokio_util::codec::LengthDelimitedCodec;
//! # fn bind_read<T: AsyncRead>(io: T) {
//! LengthDelimitedCodec::builder()
//! .length_field_offset(0) // default value
//! .length_field_length(3)
//! .length_adjustment(0) // default value
//! .num_skip(4) // skip the first 4 bytes
//! .new_read(io);
//! # }
//! # pub fn main() {}
//! ```
//!
//! The following frame will be decoded as such:
//!
//! ```text
//! INPUT DECODED
//! +------- len ------+--- Payload ---+ +--- Payload ---+
//! | \x00\x00\x0B\xFF | Hello world | => | Hello world |
//! +------------------+---------------+ +---------------+
//! ```
//!
//! A simple example where there are unused bytes between the length field
//! and the payload.
//!
//! # Encoding
//!
//! [`FramedWrite`] adapts an [`AsyncWrite`] into a `Sink` of [`BytesMut`],
@@ -333,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};
@@ -494,7 +525,7 @@ impl LengthDelimitedCodec {
let num_skip = self.builder.get_num_skip();
if num_skip > 0 {
let _ = src.split_to(num_skip);
src.advance(num_skip);
}
// Ensure that the buffer has enough space to read the incoming
@@ -546,12 +577,11 @@ impl Decoder for LengthDelimitedCodec {
}
}
impl Encoder for LengthDelimitedCodec {
type Item = Bytes;
impl Encoder<Bytes> for LengthDelimitedCodec {
type Error = io::Error;
fn encode(&mut self, data: Bytes, dst: &mut BytesMut) -> Result<(), io::Error> {
let n = (&data).remaining();
let n = data.len();
if n > self.builder.max_frame_len {
return Err(io::Error::new(
+13 -7
View File
@@ -4,7 +4,10 @@ use crate::codec::encoder::Encoder;
use bytes::{Buf, BufMut, BytesMut};
use std::{cmp, fmt, io, str, usize};
/// A simple `Codec` implementation that splits up data into lines.
/// A simple [`Decoder`] and [`Encoder`] implementation that splits up data into lines.
///
/// [`Decoder`]: crate::codec::Decoder
/// [`Encoder`]: crate::codec::Encoder
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct LinesCodec {
// Stored index of the next index to examine for a `\n` character.
@@ -33,7 +36,7 @@ impl LinesCodec {
/// of a buffered line. See the documentation for [`new_with_max_length`]
/// for information on why this could be a potential security risk.
///
/// [`new_with_max_length`]: #method.new_with_max_length
/// [`new_with_max_length`]: crate::codec::LinesCodec::new_with_max_length()
pub fn new() -> LinesCodec {
LinesCodec {
next_index: 0,
@@ -45,7 +48,7 @@ impl LinesCodec {
/// Returns a `LinesCodec` with a maximum line length limit.
///
/// If this is set, calls to `LinesCodec::decode` will return a
/// [`LengthError`] when a line exceeds the length limit. Subsequent calls
/// [`LinesCodecError`] when a line exceeds the length limit. Subsequent calls
/// will discard up to `limit` bytes from that line until a newline
/// character is reached, returning `None` until the line over the limit
/// has been fully discarded. After that point, calls to `decode` will
@@ -59,7 +62,7 @@ impl LinesCodec {
/// exploit this unbounded buffer by sending an unbounded amount of input
/// without any `\n` characters, causing unbounded memory consumption.
///
/// [`LengthError`]: ../struct.LengthError
/// [`LinesCodecError`]: crate::codec::LinesCodecError
pub fn new_with_max_length(max_length: usize) -> Self {
LinesCodec {
max_length,
@@ -179,11 +182,14 @@ impl Decoder for LinesCodec {
}
}
impl Encoder for LinesCodec {
type Item = String;
impl<T> Encoder<T> for LinesCodec
where
T: AsRef<str>,
{
type Error = LinesCodecError;
fn encode(&mut self, line: String, buf: &mut BytesMut) -> Result<(), LinesCodecError> {
fn encode(&mut self, line: T, buf: &mut BytesMut) -> Result<(), LinesCodecError> {
let line = line.as_ref();
buf.reserve(line.len() + 1);
buf.put(line.as_bytes());
buf.put_u8(b'\n');
+17 -8
View File
@@ -1,13 +1,18 @@
//! 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".
//!
//! [`AsyncRead`]: https://docs.rs/tokio/*/tokio/io/trait.AsyncRead.html
//! [`AsyncWrite`]: https://docs.rs/tokio/*/tokio/io/trait.AsyncWrite.html
//! [`Stream`]: https://docs.rs/tokio/*/tokio/stream/trait.Stream.html
//! [`Sink`]: https://docs.rs/futures-sink/*/futures_sink/trait.Sink.html
//! 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
//! [`Stream`]: tokio::stream::Stream
//! [`Sink`]: futures_sink::Sink
mod bytes_codec;
pub use self::bytes_codec::BytesCodec;
@@ -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};
+201
View File
@@ -0,0 +1,201 @@
//! Compatibility between the `tokio::io` and `futures-io` versions of the
//! `AsyncRead` and `AsyncWrite` traits.
use pin_project_lite::pin_project;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
pin_project! {
/// A compatibility layer that allows conversion between the
/// `tokio::io` and `futures-io` `AsyncRead` and `AsyncWrite` traits.
#[derive(Copy, Clone, Debug)]
pub struct Compat<T> {
#[pin]
inner: T,
}
}
/// Extension trait that allows converting a type implementing
/// `futures_io::AsyncRead` to implement `tokio::io::AsyncRead`.
pub trait FuturesAsyncReadCompatExt: futures_io::AsyncRead {
/// Wraps `self` with a compatibility layer that implements
/// `tokio_io::AsyncWrite`.
fn compat(self) -> Compat<Self>
where
Self: Sized,
{
Compat::new(self)
}
}
impl<T: futures_io::AsyncRead> FuturesAsyncReadCompatExt for T {}
/// Extension trait that allows converting a type implementing
/// `futures_io::AsyncWrite` to implement `tokio::io::AsyncWrite`.
pub trait FuturesAsyncWriteCompatExt: futures_io::AsyncWrite {
/// Wraps `self` with a compatibility layer that implements
/// `tokio::io::AsyncWrite`.
fn compat_write(self) -> Compat<Self>
where
Self: Sized,
{
Compat::new(self)
}
}
impl<T: futures_io::AsyncWrite> FuturesAsyncWriteCompatExt for T {}
/// Extension trait that allows converting a type implementing
/// `tokio::io::AsyncRead` to implement `futures_io::AsyncRead`.
pub trait Tokio02AsyncReadCompatExt: tokio::io::AsyncRead {
/// Wraps `self` with a compatibility layer that implements
/// `futures_io::AsyncRead`.
fn compat(self) -> Compat<Self>
where
Self: Sized,
{
Compat::new(self)
}
}
impl<T: tokio::io::AsyncRead> Tokio02AsyncReadCompatExt for T {}
/// Extension trait that allows converting a type implementing
/// `tokio::io::AsyncWrite` to implement `futures_io::AsyncWrite`.
pub trait Tokio02AsyncWriteCompatExt: tokio::io::AsyncWrite {
/// Wraps `self` with a compatibility layer that implements
/// `futures_io::AsyncWrite`.
fn compat_write(self) -> Compat<Self>
where
Self: Sized,
{
Compat::new(self)
}
}
impl<T: tokio::io::AsyncWrite> Tokio02AsyncWriteCompatExt for T {}
// === impl Compat ===
impl<T> Compat<T> {
fn new(inner: T) -> Self {
Self { inner }
}
/// Get a reference to the `Future`, `Stream`, `AsyncRead`, or `AsyncWrite` object
/// contained within.
pub fn get_ref(&self) -> &T {
&self.inner
}
/// Get a mutable reference to the `Future`, `Stream`, `AsyncRead`, or `AsyncWrite` object
/// contained within.
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
/// Returns the wrapped item.
pub fn into_inner(self) -> T {
self.inner
}
}
impl<T> tokio::io::AsyncRead for Compat<T>
where
T: futures_io::AsyncRead,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
futures_io::AsyncRead::poll_read(self.project().inner, cx, buf)
}
}
impl<T> futures_io::AsyncRead for Compat<T>
where
T: tokio::io::AsyncRead,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
tokio::io::AsyncRead::poll_read(self.project().inner, cx, buf)
}
}
impl<T> tokio::io::AsyncBufRead for Compat<T>
where
T: futures_io::AsyncBufRead,
{
fn poll_fill_buf<'a>(
self: Pin<&'a mut Self>,
cx: &mut Context<'_>,
) -> Poll<io::Result<&'a [u8]>> {
futures_io::AsyncBufRead::poll_fill_buf(self.project().inner, cx)
}
fn consume(self: Pin<&mut Self>, amt: usize) {
futures_io::AsyncBufRead::consume(self.project().inner, amt)
}
}
impl<T> futures_io::AsyncBufRead for Compat<T>
where
T: tokio::io::AsyncBufRead,
{
fn poll_fill_buf<'a>(
self: Pin<&'a mut Self>,
cx: &mut Context<'_>,
) -> Poll<io::Result<&'a [u8]>> {
tokio::io::AsyncBufRead::poll_fill_buf(self.project().inner, cx)
}
fn consume(self: Pin<&mut Self>, amt: usize) {
tokio::io::AsyncBufRead::consume(self.project().inner, amt)
}
}
impl<T> tokio::io::AsyncWrite for Compat<T>
where
T: futures_io::AsyncWrite,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
futures_io::AsyncWrite::poll_write(self.project().inner, cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
futures_io::AsyncWrite::poll_flush(self.project().inner, cx)
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
futures_io::AsyncWrite::poll_close(self.project().inner, cx)
}
}
impl<T> futures_io::AsyncWrite for Compat<T>
where
T: tokio::io::AsyncWrite,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
tokio::io::AsyncWrite::poll_write(self.project().inner, cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
tokio::io::AsyncWrite::poll_flush(self.project().inner, cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
tokio::io::AsyncWrite::poll_shutdown(self.project().inner, cx)
}
}
+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(),
}
}
}
+16 -2
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio-util/0.2.0")]
#![doc(html_root_url = "https://docs.rs/tokio-util/0.3.1")]
#![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))
@@ -14,6 +14,12 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
//! Utilities for working with Tokio.
//!
//! This crate is not versioned in lockstep with the core
//! [`tokio`] crate. However, `tokio-util` _will_ respect Rust's
//! semantic versioning policy, especially with regard to breaking changes.
//!
//! [`tokio`]: https://docs.rs/tokio
#[macro_use]
mod cfg;
@@ -25,3 +31,11 @@ cfg_codec! {
cfg_udp! {
pub mod udp;
}
cfg_compat! {
pub mod compat;
}
cfg_rt! {
pub mod context;
}
+38 -21
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,31 +49,43 @@ 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;
}
}
}
impl<C: Encoder + Unpin> Sink<(C::Item, SocketAddr)> for UdpFramed<C> {
impl<I, C: Encoder<I> + Unpin> Sink<(I, SocketAddr)> for UdpFramed<C> {
type Error = C::Error;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@@ -84,7 +99,7 @@ impl<C: Encoder + Unpin> Sink<(C::Item, SocketAddr)> for UdpFramed<C> {
Poll::Ready(Ok(()))
}
fn start_send(self: Pin<&mut Self>, item: (C::Item, SocketAddr)) -> Result<(), Self::Error> {
fn start_send(self: Pin<&mut Self>, item: (I, SocketAddr)) -> Result<(), Self::Error> {
let (frame, out_addr) = item;
let pin = self.get_mut();
@@ -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,
}
}
+2 -2
View File
@@ -209,9 +209,9 @@ fn lines_encoder() {
let mut codec = LinesCodec::new();
let mut buf = BytesMut::new();
codec.encode(String::from("line 1"), &mut buf).unwrap();
codec.encode("line 1", &mut buf).unwrap();
assert_eq!("line 1\n", buf);
codec.encode(String::from("line 2"), &mut buf).unwrap();
codec.encode("line 2", &mut buf).unwrap();
assert_eq!("line 1\nline 2\n", buf);
}
+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 }),
);
}
+1 -2
View File
@@ -28,8 +28,7 @@ impl Decoder for U32Codec {
}
}
impl Encoder for U32Codec {
type Item = u32;
impl Encoder<u32> for U32Codec {
type Error = io::Error;
fn encode(&mut self, item: u32, dst: &mut BytesMut) -> io::Result<()> {
+1 -2
View File
@@ -28,8 +28,7 @@ macro_rules! pin {
struct U32Encoder;
impl Encoder for U32Encoder {
type Item = u32;
impl Encoder<u32> for U32Encoder {
type Error = io::Error;
fn encode(&mut self, item: u32, dst: &mut BytesMut) -> io::Result<()> {
+19
View File
@@ -372,6 +372,25 @@ fn read_single_multi_frame_one_packet_skip_none_adjusted() {
assert_done!(io);
}
#[test]
fn read_single_frame_length_adjusted() {
let mut d: Vec<u8> = vec![];
d.extend_from_slice(b"\x00\x00\x0b\x0cHello world");
let io = length_delimited::Builder::new()
.length_field_offset(0)
.length_field_length(3)
.length_adjustment(0)
.num_skip(4)
.new_read(mock! {
data(&d),
});
pin_mut!(io);
assert_next_eq!(io, b"Hello world");
assert_done!(io);
}
#[test]
fn read_single_multi_frame_one_packet_length_includes_head() {
let mut d: Vec<u8> = vec![];
+34 -12
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};
@@ -8,8 +8,9 @@ use futures::future::FutureExt;
use futures::sink::SinkExt;
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?;
@@ -21,33 +22,34 @@ async fn send_framed() -> std::io::Result<()> {
let mut a = UdpFramed::new(a_soc, ByteCodec);
let mut b = UdpFramed::new(b_soc, ByteCodec);
let msg = b"4567".to_vec();
let msg = b"4567";
let send = a.send((msg.clone(), b_addr));
let send = a.send((msg, b_addr));
let recv = b.next().map(|e| e.unwrap());
let (_, received) = try_join(send, recv).await.unwrap();
let (data, addr) = received;
assert_eq!(msg, data);
assert_eq!(msg, &*data);
assert_eq!(a_addr, addr);
a_soc = a.into_inner();
b_soc = b.into_inner();
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
// test sending & receiving an empty message
{
let mut a = UdpFramed::new(a_soc, ByteCodec);
let mut b = UdpFramed::new(b_soc, ByteCodec);
let msg = b"".to_vec();
let msg = b"";
let send = a.send((msg.clone(), b_addr));
let send = a.send((msg, b_addr));
let recv = b.next().map(|e| e.unwrap());
let (_, received) = try_join(send, recv).await.unwrap();
let (data, addr) = received;
assert_eq!(msg, data);
assert_eq!(msg, &*data);
assert_eq!(a_addr, addr);
}
@@ -66,13 +68,33 @@ impl Decoder for ByteCodec {
}
}
impl Encoder for ByteCodec {
type Item = Vec<u8>;
impl Encoder<&[u8]> for ByteCodec {
type Error = io::Error;
fn encode(&mut self, data: Vec<u8>, buf: &mut BytesMut) -> Result<(), io::Error> {
fn encode(&mut self, data: &[u8], buf: &mut BytesMut) -> Result<(), io::Error> {
buf.reserve(data.len());
buf.put_slice(&data);
buf.put_slice(data);
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(())
}
+473 -149
View File
@@ -1,165 +1,318 @@
# 0.2.11 (January 27, 2019)
# 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])
### Added
- 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]).
### Added
- 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]).
### Added
- io: impl `RawFd`, `AsRawHandle` for std io types ([#2335]).
- rt: automatic cooperative task yielding (#2160, #2343, #2349).
- sync: `RwLock::into_inner` ([#2321]).
### Changed
- sync: semaphore, mutex internals rewritten to avoid allocations ([#2325]).
# 0.2.13 (February 28, 2020)
### Fixes
- 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]).
- 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]).
### Added
- 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]).
- 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]).
- sync: `mpsc::Sender::send_timeout` sends, waiting for up to specified duration
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, 2019)
# 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, 2019)
# 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, 2019)
# 0.2.8 (January 7, 2020)
### Fixes
- depend on new version of `tokio-macros`.
# 0.2.7 (January 7, 2019)
# 0.2.7 (January 7, 2020)
### 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)
@@ -185,69 +338,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)
@@ -255,65 +408,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)
@@ -322,3 +475,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
+9 -9
View File
@@ -8,12 +8,12 @@ name = "tokio"
# - README.md
# - Update CHANGELOG.md.
# - Create "v0.2.x" git tag.
version = "0.2.11"
version = "0.2.22"
edition = "2018"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
readme = "README.md"
documentation = "https://docs.rs/tokio/0.2.11/tokio/"
documentation = "https://docs.rs/tokio/0.2.22/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",
@@ -104,8 +104,9 @@ memchr = { version = "2.2", optional = true }
mio = { version = "0.6.20", 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
parking_lot = { version = "0.11.0", optional = true } # Not in full
slab = { version = "0.4.1", optional = true } # Backs `DelayQueue`
tracing = { version = "0.1.16", default-features = false, features = ["std"], optional = true } # Not in full
[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.2.13", 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
-11
View File
@@ -1,11 +0,0 @@
#![cfg_attr(not(feature = "blocking"), allow(dead_code, unused_imports))]
//! Perform blocking operations from an asynchronous context.
cfg_blocking_impl! {
mod pool;
pub(crate) use pool::{spawn_blocking, BlockingPool, Spawner};
mod schedule;
mod task;
}

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