mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
192
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b117fc1d65 | ||
|
|
272e09d349 | ||
|
|
3235749006 | ||
|
|
9c037044c4 | ||
|
|
3658e10045 | ||
|
|
ed3ece266b | ||
|
|
9b1a45cc6a | ||
|
|
477fa5580a | ||
|
|
bb6cca8ff0 | ||
|
|
e166c4d912 | ||
|
|
b7506cf663 | ||
|
|
dc4a29359f | ||
|
|
d3dca4552b | ||
|
|
42a0df1ea4 | ||
|
|
a98eab6eff | ||
|
|
5a5dde70b3 | ||
|
|
d0963774a3 | ||
|
|
c83355235c | ||
|
|
33a216e4c1 | ||
|
|
09f2ac85bf | ||
|
|
32a152630f | ||
|
|
9153067d66 | ||
|
|
d246964bdf | ||
|
|
e700607554 | ||
|
|
5321550534 | ||
|
|
32e1cafb57 | ||
|
|
49bc4025dd | ||
|
|
51e36e41bc | ||
|
|
d011b92b9a | ||
|
|
f929576f0e | ||
|
|
b0f001a05a | ||
|
|
2291ba9d0d | ||
|
|
7f84f6b4ca | ||
|
|
5f61bd5252 | ||
|
|
bffa3ed558 | ||
|
|
7b5ef61aeb | ||
|
|
753336de8e | ||
|
|
65aea16ad1 | ||
|
|
796fee6364 | ||
|
|
adb0ba71d4 | ||
|
|
bfa6766f3c | ||
|
|
a2f457fa48 | ||
|
|
1879bc49ce | ||
|
|
678f6382b8 | ||
|
|
e27b0a46ba | ||
|
|
d35d0518f5 | ||
|
|
886511c0a6 | ||
|
|
d06bd6b216 | ||
|
|
2c85cd0991 | ||
|
|
1e45237a28 | ||
|
|
3a88d85538 | ||
|
|
b47ad24268 | ||
|
|
cab9a44e01 | ||
|
|
964afb2ce3 | ||
|
|
2f690d30bc | ||
|
|
331a88cee6 | ||
|
|
46353737e7 | ||
|
|
ffd73a64e7 | ||
|
|
1119d572ee | ||
|
|
20ca59114a | ||
|
|
3dd95a9ff1 | ||
|
|
be67eda117 | ||
|
|
e267a1922d | ||
|
|
9b456f48d9 | ||
|
|
0ca973a7eb | ||
|
|
df6acf0c2a | ||
|
|
98d23b8b29 | ||
|
|
85f8522536 | ||
|
|
d275341fb2 | ||
|
|
d735e5d527 | ||
|
|
4019198706 | ||
|
|
24dc85dc5e | ||
|
|
aaa5adb7fd | ||
|
|
5f68b3aaa1 | ||
|
|
2f69acbe9f | ||
|
|
7e12f5c39e | ||
|
|
462882b356 | ||
|
|
35687f1d18 | ||
|
|
e7dc3a1091 | ||
|
|
b594e240f9 | ||
|
|
605708dca6 | ||
|
|
cc40a4e7f0 | ||
|
|
4ae6c997ee | ||
|
|
0f44adf5f6 | ||
|
|
19d5565442 | ||
|
|
98e76d9bc6 | ||
|
|
b7f5bc95fe | ||
|
|
90ea2f6c5b | ||
|
|
837c3934d5 | ||
|
|
bcd42d11d9 | ||
|
|
8ad66d296f | ||
|
|
d9edc26e97 | ||
|
|
f0ac62151b | ||
|
|
214722a296 | ||
|
|
b8f8145b62 | ||
|
|
32d3e0e1f9 | ||
|
|
266919add6 | ||
|
|
c9ffd98b1e | ||
|
|
2d4bfa1485 | ||
|
|
b6ecfa251c | ||
|
|
3a81d7746a | ||
|
|
6e1a833825 | ||
|
|
3f80953dee | ||
|
|
4374f5be70 | ||
|
|
b23ab94cd5 | ||
|
|
40c77bd17e | ||
|
|
31b51004f2 | ||
|
|
9a4e4f2308 | ||
|
|
484fda7a23 | ||
|
|
45ba6e2652 | ||
|
|
e73b8a0cc9 | ||
|
|
209232befd | ||
|
|
f759e4d70f | ||
|
|
1fdff707b8 | ||
|
|
e97e8cb7fe | ||
|
|
2848df9b6c | ||
|
|
7a24ed7509 | ||
|
|
9c9760cfbb | ||
|
|
5ecd929b1a | ||
|
|
8ddebf4309 | ||
|
|
0df1882f21 | ||
|
|
4fa1b2b58c | ||
|
|
a4895fe364 | ||
|
|
7ab97f99c8 | ||
|
|
9bf3228f73 | ||
|
|
d41c60e21d | ||
|
|
934c596133 | ||
|
|
72e2209bd8 | ||
|
|
6a7092b9f7 | ||
|
|
1c893ef6d3 | ||
|
|
20e7598e8d | ||
|
|
3db92496f6 | ||
|
|
da47cfbd58 | ||
|
|
f5eadc74f1 | ||
|
|
175f9afea9 | ||
|
|
48eda3fe2f | ||
|
|
010c2223ca | ||
|
|
36b58d8fa8 | ||
|
|
c601f68c9f | ||
|
|
78ca103f3a | ||
|
|
7da00f3832 | ||
|
|
edba77e8df | ||
|
|
cf1afd2d90 | ||
|
|
8e58a9d8d4 | ||
|
|
4afef9391a | ||
|
|
b6bacc1ca3 | ||
|
|
955cd2836d | ||
|
|
70e4ed67ad | ||
|
|
699b9ab89e | ||
|
|
ba0921a01d | ||
|
|
367cb56e02 | ||
|
|
04d949c380 | ||
|
|
ed4359bb26 | ||
|
|
4142dc2fae | ||
|
|
468b037e4e | ||
|
|
b33ae3cdd6 | ||
|
|
4519ac8e17 | ||
|
|
92b93ee176 | ||
|
|
635149e3ab | ||
|
|
e28c350e31 | ||
|
|
1a122018a2 | ||
|
|
f3f8ee431e | ||
|
|
61c4047c6a | ||
|
|
3486a61a0f | ||
|
|
8291c3d462 | ||
|
|
1b6893b6f6 | ||
|
|
eca7f0760f | ||
|
|
89d969d518 | ||
|
|
16664189c1 | ||
|
|
6828870608 | ||
|
|
89d0cda2e2 | ||
|
|
8052a9b348 | ||
|
|
a5ac6c8b72 | ||
|
|
3a59526523 | ||
|
|
322a94f72f | ||
|
|
c03b23355b | ||
|
|
bc91bc5022 | ||
|
|
a7b053372f | ||
|
|
673fdb5cb3 | ||
|
|
97618746de | ||
|
|
d8f8b59df9 | ||
|
|
0745a9b88a | ||
|
|
a7f5ba28ba | ||
|
|
cc3b6af7a3 | ||
|
|
07e30ae923 | ||
|
|
69d90ac7ee | ||
|
|
d16032cf06 | ||
|
|
b479ce78d3 | ||
|
|
6e45e0ac61 | ||
|
|
82c5baa09b | ||
|
|
7dc6404726 | ||
|
|
2e88e29fe9 |
+3
-1
@@ -1,3 +1,4 @@
|
||||
image: Visual Studio 2017
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
@@ -9,6 +10,7 @@ install:
|
||||
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- rustup-init.exe -y --default-host %TARGET%
|
||||
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
|
||||
- set RUST_BACKTRACE=1
|
||||
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
@@ -16,4 +18,4 @@ install:
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --all --target %TARGET%
|
||||
- cargo test --all --no-fail-fast --target %TARGET%
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<!--
|
||||
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>
|
||||
-->
|
||||
@@ -0,0 +1,23 @@
|
||||
<!--
|
||||
Thank you for your Pull Request. Please provide a description above and review
|
||||
the requirements below.
|
||||
|
||||
Bug fixes and new features should include tests.
|
||||
|
||||
Contributors guide: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md
|
||||
-->
|
||||
|
||||
## Motivation
|
||||
|
||||
<!--
|
||||
Explain the context and why you're making that change. What is the problem
|
||||
you're trying to solve? In some cases there is not a problem and this can be
|
||||
thought of as being the motivation for your change.
|
||||
-->
|
||||
|
||||
## Solution
|
||||
|
||||
<!--
|
||||
Summarize the solution and provide any necessary context needed to understand
|
||||
the code change.
|
||||
-->
|
||||
+21
-4
@@ -15,19 +15,36 @@ matrix:
|
||||
# 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.
|
||||
- rust: 1.25.0
|
||||
- rust: 1.26.0
|
||||
- rust: stable
|
||||
- rust: beta
|
||||
- rust: nightly
|
||||
env: ALLOW_FAILURES=true
|
||||
- os: osx
|
||||
- env: TARGET=x86_64-unknown-freebsd
|
||||
- env: TARGET=i686-unknown-freebsd
|
||||
- env: TARGET=i686-unknown-linux-gnu
|
||||
|
||||
# Test the async / await preview. We don't want to block PRs on this failing
|
||||
# though.
|
||||
- rust: nightly
|
||||
env: ALLOW_FAILURES=true
|
||||
script: |
|
||||
cd tokio-async-await
|
||||
cargo check --all
|
||||
|
||||
# This runs TSAN against nightly and allows failures to propagate up.
|
||||
- rust: nightly-2018-11-18
|
||||
env: TSAN=yes
|
||||
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
env: ALLOW_FAILURES=true
|
||||
|
||||
script:
|
||||
- |
|
||||
set -e
|
||||
if [[ "$TRAVIS_RUST_VERSION" == nightly ]]
|
||||
if [[ "$TRAVIS_RUST_VERSION" == nightly && "$TSAN" == yes ]]
|
||||
then
|
||||
# Make sure the benchmarks compile
|
||||
cargo build --benches --all
|
||||
@@ -64,7 +81,7 @@ script:
|
||||
cargo check --all --exclude tokio-tls --target $TARGET
|
||||
cargo check --tests --all --exclude tokio-tls --target $TARGET
|
||||
else
|
||||
cargo test --all
|
||||
cargo test --all --no-fail-fast
|
||||
# Disable these tests for now as they are buggy
|
||||
#
|
||||
# cargo test --features unstable-futures
|
||||
@@ -85,7 +102,7 @@ deploy:
|
||||
branch: master
|
||||
repo: tokio-rs/tokio
|
||||
rust: stable
|
||||
condition: $TRAVIS_OS_NAME = linux
|
||||
condition: $TRAVIS_OS_NAME = "linux" && $TARGET = ""
|
||||
|
||||
env:
|
||||
global:
|
||||
|
||||
@@ -1,6 +1,43 @@
|
||||
This changelog only applies to the `tokio` crate proper. Each sub crate
|
||||
maintains its own changelog tracking changes made in each respective sub crate.
|
||||
|
||||
# 0.1.13 (November 21, 2018)
|
||||
|
||||
* 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).
|
||||
|
||||
# 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).
|
||||
|
||||
# 0.1.11 (September 28, 2018)
|
||||
|
||||
* Fix `tokio-async-await` dependency (#675).
|
||||
|
||||
# 0.1.10 (September 27, 2018)
|
||||
|
||||
* Fix minimal versions
|
||||
|
||||
# 0.1.9 (September 27, 2018)
|
||||
|
||||
* 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).
|
||||
* Re-export `tokio-current-thread::spawn` in current_thread runtime
|
||||
(#579).
|
||||
|
||||
# 0.1.8 (August 23, 2018)
|
||||
|
||||
* Extract tokio::executor::current_thread to a sub crate (#370)
|
||||
|
||||
+387
@@ -0,0 +1,387 @@
|
||||
# Contributing to Tokio
|
||||
|
||||
:balloon: Thanks for your help improving the project! We are so happy to have
|
||||
you!
|
||||
|
||||
There are opportunities to contribute to Tokio at any level. It doesn't matter if
|
||||
you are just getting started with Rust or are the most weathered expert, we can
|
||||
use your help.
|
||||
|
||||
**No contribution is too small and all contributions are valued.**
|
||||
|
||||
This guide will help you get started. **Do not let this guide intimidate you**.
|
||||
It should be considered a map to help you navigate the process.
|
||||
|
||||
You may also get help with contributing in the [dev channel][dev], please join
|
||||
us!
|
||||
|
||||
[dev]: https://gitter.im/tokio-rs/dev
|
||||
|
||||
## Conduct
|
||||
|
||||
The Tokio project adheres to the [Rust Code of Conduct][coc]. This describes
|
||||
the _minimum_ behavior expected from all contributors.
|
||||
|
||||
[coc]: https://github.com/rust-lang/rust/blob/master/CODE_OF_CONDUCT.md
|
||||
|
||||
## Contributing in Issues
|
||||
|
||||
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.
|
||||
|
||||
2. By helping to triage the issue: This can be done by providing
|
||||
supporting details (a test case that demonstrates a bug), providing
|
||||
suggestions on how to address the issue, or ensuring that the issue is tagged
|
||||
correctly.
|
||||
|
||||
3. By helping to resolve the issue: Typically this is done either in the form of
|
||||
demonstrating that the issue reported is not a problem after all, or more
|
||||
often, by opening a Pull Request that changes some bit of something in
|
||||
Tokio in a concrete and reviewable manner.
|
||||
|
||||
**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.
|
||||
|
||||
In exchange for receiving help, we ask that you contribute back a documentation
|
||||
PR that helps others avoid the problems that you encountered.
|
||||
|
||||
### 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
|
||||
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.
|
||||
|
||||
The two most important pieces of information we need in order to properly
|
||||
evaluate the report is a description of the behavior you are seeing and a simple
|
||||
test case we can use to recreate the problem on our own. If we cannot recreate
|
||||
the issue, it becomes impossible for us to fix.
|
||||
|
||||
In order to rule out the possibility of bugs introduced by userland code, test
|
||||
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
|
||||
|
||||
Once an issue has been opened, it is not uncommon for there to be discussion
|
||||
around it. Some contributors may have differing opinions about the issue,
|
||||
including whether the behavior being seen is a bug or a feature. This discussion
|
||||
is part of the process and should be kept focused, helpful, and professional.
|
||||
|
||||
Short, clipped responses—that provide neither additional context nor supporting
|
||||
detail—are not helpful or professional. To many, such responses are simply
|
||||
annoying and unfriendly.
|
||||
|
||||
Contributors are encouraged to help one another make forward progress as much as
|
||||
possible, empowering one another to solve issues collaboratively. If you choose
|
||||
to comment on an issue that you feel either is not a problem that needs to be
|
||||
fixed, or if you encounter information in an issue that you feel is incorrect,
|
||||
explain why you feel that way with additional supporting context, and be willing
|
||||
to be convinced that you may be wrong. By doing so, we can often reach the
|
||||
correct outcome much faster.
|
||||
|
||||
### Resolving a Bug Report
|
||||
|
||||
In the majority of cases, issues are resolved by opening a Pull Request. The
|
||||
process for opening and reviewing a Pull Request is similar to that of opening
|
||||
and triaging issues, but carries with it a necessary review and approval
|
||||
workflow that ensures that the proposed changes meet the minimal quality and
|
||||
functional guidelines of the Tokio project.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Pull Requests are the way concrete changes are made to the code, documentation,
|
||||
and dependencies in the Tokio repository.
|
||||
|
||||
Even tiny pull requests (e.g., one character pull request fixing a typo in API
|
||||
documentation) are greatly appreciated. Before making a large change, it is
|
||||
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.
|
||||
|
||||
### Tests
|
||||
|
||||
If the change being proposed alters code (as opposed to only documentation for
|
||||
example), it is either adding new functionality to Tokio or it is fixing
|
||||
existing, broken functionality. In both of these cases, the pull request should
|
||||
include one or more tests to ensure that Tokio does not regress in the future.
|
||||
There are two ways to write tests: integration tests and documentation tests
|
||||
(Tokio avoids unit tests as much as possible).
|
||||
|
||||
#### Integration tests
|
||||
|
||||
Integration tests go in the same crate as the code they are testing. Each sub
|
||||
crate should have a `dev-dependency` on `tokio` itself. This makes all Tokio
|
||||
utilities available to use in tests, no matter the crate being tested.
|
||||
|
||||
The best strategy for writing a new integration test is to look at existing
|
||||
integration tests in the crate and follow the style.
|
||||
|
||||
#### Documentation tests
|
||||
|
||||
Ideally, every API has at least one [documentation test] that demonstrates how to
|
||||
use the API. Documentation tests are run with `cargo test --doc`. This ensures
|
||||
that the example is correct and provides additional test coverage.
|
||||
|
||||
The trick to documentation tests is striking a balance between being succinct
|
||||
for a reader to understand and actually testing the API.
|
||||
|
||||
Same as with integration tests, when writing a documentation test, the full
|
||||
`tokio` crate is available. This is especially useful for getting access to the
|
||||
runtime to run the example.
|
||||
|
||||
The documentation tests will be visible from both the crate specific
|
||||
documentation **and** the `tokio` facade documentation via the re-export. The
|
||||
example should be written from the point of view of a user that is using the
|
||||
`tokio` crate. As such, the example should use the API via the facade and not by
|
||||
directly referencing the crate.
|
||||
|
||||
The type level example for `tokio_timer::Timeout` provides a good example of a
|
||||
documentation test:
|
||||
|
||||
```
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio;
|
||||
/// // import the `timeout` function, usually this is done
|
||||
/// // with `use tokio::prelude::*`
|
||||
/// use tokio::prelude::FutureExt;
|
||||
/// use futures::Stream;
|
||||
/// use futures::sync::mpsc;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let (tx, rx) = mpsc::unbounded();
|
||||
/// # tx.unbounded_send(()).unwrap();
|
||||
/// # drop(tx);
|
||||
///
|
||||
/// let process = rx.for_each(|item| {
|
||||
/// // do something with `item`
|
||||
/// # drop(item);
|
||||
/// # Ok(())
|
||||
/// });
|
||||
///
|
||||
/// # tokio::runtime::current_thread::block_on_all(
|
||||
/// // Wrap the future with a `Timeout` set to expire in 10 milliseconds.
|
||||
/// process.timeout(Duration::from_millis(10))
|
||||
/// # ).unwrap();
|
||||
/// # }
|
||||
```
|
||||
|
||||
Given that this is a *type* level documentation test and the primary way users
|
||||
of `tokio` will create an instance of `Timeout` is by using
|
||||
`FutureExt::timeout`, this is how the documentation test is structured.
|
||||
|
||||
Lines that start with `/// #` are removed when the documentation is generated.
|
||||
They are only there to get the test to run. The `block_on_all` function is the
|
||||
easiest way to execute a future from a test.
|
||||
|
||||
If this were a documentation test for the `Timeout::new` function, then the
|
||||
example would explicitly use `Timeout::new`. For example:
|
||||
|
||||
```
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio;
|
||||
/// use tokio::timer::Timeout;
|
||||
/// use futures::Future;
|
||||
/// use futures::sync::oneshot;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let (tx, rx) = oneshot::channel();
|
||||
/// # tx.send(()).unwrap();
|
||||
///
|
||||
/// # tokio::runtime::current_thread::block_on_all(
|
||||
/// // Wrap the future with a `Timeout` set to expire in 10 milliseconds.
|
||||
/// Timeout::new(rx, Duration::from_millis(10))
|
||||
/// # ).unwrap();
|
||||
/// # }
|
||||
```
|
||||
|
||||
### Commits
|
||||
|
||||
It is a recommended best practice to keep your changes as logically grouped as
|
||||
possible within individual commits. There is no limit to the number of commits
|
||||
any single Pull Request may have, and many contributors find it easier to review
|
||||
changes that are split across multiple commits.
|
||||
|
||||
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]).
|
||||
|
||||
#### Commit message guidelines
|
||||
|
||||
A good commit message should describe what changed and why.
|
||||
|
||||
1. The first line should:
|
||||
|
||||
* contain a short description of the change (preferably 50 characters or less,
|
||||
and no more than 72 characters)
|
||||
* be entirely in lowercase with the exception of proper nouns, acronyms, and
|
||||
the words that refer to code, like function/variable names
|
||||
* be prefixed with the name of the sub crate being changed (without the `tokio-`
|
||||
prefix) and start with an imperative verb. If modifying `tokio` proper,
|
||||
omit the crate prefix.
|
||||
|
||||
Examples:
|
||||
|
||||
* timer: introduce `Timeout` and deprecate `Deadline`
|
||||
* export `Encoder`, `Decoder`, `Framed*` from tokio_codec
|
||||
|
||||
2. Keep the second line blank.
|
||||
3. Wrap all other lines at 72 columns (except for long URLs).
|
||||
4. If your patch fixes an open issue, you can add a reference to it at the end
|
||||
of the log. Use the `Fixes: #` prefix and the issue number. For other
|
||||
references use `Refs: #`. `Refs` may include multiple issues, separated by a
|
||||
comma.
|
||||
|
||||
Examples:
|
||||
|
||||
- `Fixes: #1337`
|
||||
- `Refs: #1234`
|
||||
|
||||
Sample complete commit message:
|
||||
|
||||
```txt
|
||||
subcrate: explain the commit in one line
|
||||
|
||||
Body of commit message is a few lines of text, explaining things
|
||||
in more detail, possibly giving some background about the issue
|
||||
being fixed, etc.
|
||||
|
||||
The body of the commit message can be several paragraphs, and
|
||||
please do proper word-wrap and keep columns shorter than about
|
||||
72 characters or so. That way, `git log` will show things
|
||||
nicely even when it is indented.
|
||||
|
||||
Fixes: #1337
|
||||
Refs: #453, #154
|
||||
```
|
||||
|
||||
### Opening the Pull Request
|
||||
|
||||
From within GitHub, opening a new Pull Request will present you with a
|
||||
[template] that should be filled out. Please try to do your best at filling out
|
||||
the details, but feel free to skip parts if you're not sure what to put.
|
||||
|
||||
[template]: .github/PULL_REQUEST_TEMPLATE.md
|
||||
|
||||
### Discuss and update
|
||||
|
||||
You will probably get feedback or requests for changes to your Pull Request.
|
||||
This is a big part of the submission process so don't be discouraged! Some
|
||||
contributors may sign off on the Pull Request right away, others may have
|
||||
more detailed comments or feedback. This is a necessary part of the process
|
||||
in order to evaluate whether the changes are correct and necessary.
|
||||
|
||||
**Any community member can review a PR and you might get conflicting feedback**.
|
||||
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
|
||||
more details.
|
||||
|
||||
### Commit Squashing
|
||||
|
||||
In most cases, **do not squash commits that you add to your Pull Request during
|
||||
the review process**. When the commits in your Pull Request land, they may be
|
||||
squashed into one commit per logical change. Metadata will be added to the
|
||||
commit message (including links to the Pull Request, links to relevant issues,
|
||||
and the names of the reviewers). The commit history of your Pull Request,
|
||||
however, will stay intact on the Pull Request page.
|
||||
|
||||
## Reviewing Pull Requests
|
||||
|
||||
**Any Tokio community member is welcome to review any pull request**.
|
||||
|
||||
All Tokio contributors who choose to review and provide feedback on Pull
|
||||
Requests have a responsibility to both the project and the individual making the
|
||||
contribution. Reviews and feedback must be helpful, insightful, and geared
|
||||
towards improving the contribution as opposed to simply blocking it. If there
|
||||
are reasons why you feel the PR should not land, explain what those are. Do not
|
||||
expect to be able to block a Pull Request from advancing simply because you say
|
||||
"No" without giving an explanation. Be open to having your mind changed. Be open
|
||||
to working with the contributor to make the Pull Request better.
|
||||
|
||||
Reviews that are dismissive or disrespectful of the contributor or any other
|
||||
reviewers are strictly counter to the Code of Conduct.
|
||||
|
||||
When reviewing a Pull Request, the primary goals are for the codebase to improve
|
||||
and for the person submitting the request to succeed. **Even if a Pull Request
|
||||
does not land, the submitters should come away from the experience feeling like
|
||||
their effort was not wasted or unappreciated**. Every Pull Request from a new
|
||||
contributor is an opportunity to grow the community.
|
||||
|
||||
### Review a bit at a time.
|
||||
|
||||
Do not overwhelm new contributors.
|
||||
|
||||
It is tempting to micro-optimize and make everything about relative performance,
|
||||
perfect grammar, or exact style matches. Do not succumb to that temptation.
|
||||
|
||||
Focus first on the most significant aspects of the change:
|
||||
|
||||
1. Does this change make sense for Tokio?
|
||||
2. Does this change make Tokio better, even if only incrementally?
|
||||
3. Are there clear bugs or larger scale issues that need attending to?
|
||||
4. Is the commit message readable and correct? If it contains a breaking change
|
||||
is it clear enough?
|
||||
|
||||
Note that only **incremental** improvement is needed to land a PR. This means
|
||||
that the PR does not need to be perfect, only better than the status quo. Follow
|
||||
up PRs may be opened to continue iterating.
|
||||
|
||||
When changes are necessary, *request* them, do not *demand* them, and **do not
|
||||
assume that the submitter already knows how to add a test or run a benchmark**.
|
||||
|
||||
Specific performance optimization techniques, coding styles and conventions
|
||||
change over time. The first impression you give to a new contributor never does.
|
||||
|
||||
Nits (requests for small changes that are not essential) are fine, but try to
|
||||
avoid stalling the Pull Request. Most nits can typically be fixed by the Tokio
|
||||
Collaborator landing the Pull Request but they can also be an opportunity for
|
||||
the contributor to learn a bit more about the project.
|
||||
|
||||
It is always good to clearly indicate nits when you comment: e.g.
|
||||
`Nit: change foo() to bar(). But this is not blocking.`
|
||||
|
||||
If your comments were addressed but were not folded automatically after new
|
||||
commits or if they proved to be mistaken, please, [hide them][hiding-a-comment]
|
||||
with the appropriate reason to keep the conversation flow concise and relevant.
|
||||
|
||||
### Be aware of the person behind the code
|
||||
|
||||
Be aware that *how* you communicate requests and reviews in your feedback can
|
||||
have a significant impact on the success of the Pull Request. Yes, we may land
|
||||
a particular change that makes Tokio better, but the individual might just not
|
||||
want to have anything to do with Tokio ever again. The goal is not just having
|
||||
good code.
|
||||
|
||||
### Abandoned or Stalled Pull Requests
|
||||
|
||||
If a Pull Request appears to be abandoned or stalled, it is polite to first
|
||||
check with the contributor to see if they intend to continue the work before
|
||||
checking if they would mind if you took it over (especially if it just has nits
|
||||
left). When doing so, it is courteous to give the original contributor credit
|
||||
for the work they started (either by preserving their name and email address in
|
||||
the commit log, or by using an `Author: ` meta-data tag in the commit.
|
||||
|
||||
_Adapted from the [Node.js contributing guide][node]_.
|
||||
|
||||
[node]: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md
|
||||
[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
|
||||
+39
-7
@@ -6,11 +6,11 @@ name = "tokio"
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.8"
|
||||
version = "0.1.13"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio/0.1.8/tokio/"
|
||||
documentation = "https://docs.rs/tokio/0.1.13/tokio/"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
@@ -24,12 +24,16 @@ keywords = ["io", "async", "non-blocking", "futures"]
|
||||
|
||||
members = [
|
||||
"./",
|
||||
"tokio-async-await",
|
||||
"tokio-buf",
|
||||
"tokio-channel",
|
||||
"tokio-codec",
|
||||
"tokio-current-thread",
|
||||
"tokio-executor",
|
||||
"tokio-fs",
|
||||
"tokio-io",
|
||||
"tokio-reactor",
|
||||
"tokio-signal",
|
||||
"tokio-threadpool",
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
@@ -38,20 +42,29 @@ members = [
|
||||
"tokio-uds",
|
||||
]
|
||||
|
||||
[features]
|
||||
# This feature comes with no promise of stability. Things will
|
||||
# break with each patch release. Use at your own risk.
|
||||
async-await-preview = [
|
||||
"tokio-async-await/async-await-preview",
|
||||
]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "tokio-rs/tokio" }
|
||||
appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" }
|
||||
|
||||
[dependencies]
|
||||
bytes = "0.4"
|
||||
num_cpus = "1.8.0"
|
||||
tokio-codec = { version = "0.1.0", path = "tokio-codec" }
|
||||
tokio-current-thread = { version = "0.1.1", path = "tokio-current-thread" }
|
||||
tokio-current-thread = { version = "0.1.3", path = "tokio-current-thread" }
|
||||
tokio-io = { version = "0.1.6", path = "tokio-io" }
|
||||
tokio-executor = { version = "0.1.2", path = "tokio-executor" }
|
||||
tokio-executor = { version = "0.1.5", path = "tokio-executor" }
|
||||
tokio-reactor = { version = "0.1.1", path = "tokio-reactor" }
|
||||
tokio-threadpool = { version = "0.1.4", path = "tokio-threadpool" }
|
||||
tokio-tcp = { version = "0.1.0", path = "tokio-tcp" }
|
||||
tokio-udp = { version = "0.1.0", path = "tokio-udp" }
|
||||
tokio-timer = { version = "0.2.6", path = "tokio-timer" }
|
||||
tokio-timer = { version = "0.2.8", path = "tokio-timer" }
|
||||
tokio-fs = { version = "0.1.3", path = "tokio-fs" }
|
||||
|
||||
futures = "0.1.20"
|
||||
@@ -59,11 +72,13 @@ futures = "0.1.20"
|
||||
# Needed until `reactor` is removed from `tokio`.
|
||||
mio = "0.6.14"
|
||||
|
||||
# Needed for async/await preview support
|
||||
tokio-async-await = { version = "0.1.0", path = "tokio-async-await", optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
tokio-uds = { version = "0.2.0", path = "tokio-uds" }
|
||||
tokio-uds = { version = "0.2.1", path = "tokio-uds" }
|
||||
|
||||
[dev-dependencies]
|
||||
bytes = "0.4"
|
||||
env_logger = { version = "0.5", default-features = false }
|
||||
flate2 = { version = "1", features = ["tokio"] }
|
||||
futures-cpupool = "0.1"
|
||||
@@ -75,3 +90,20 @@ serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
time = "0.1"
|
||||
|
||||
[patch.crates-io]
|
||||
tokio = { path = "." }
|
||||
tokio-async-await = { path = "./tokio-async-await" }
|
||||
tokio-codec = { path = "./tokio-codec" }
|
||||
tokio-current-thread = { path = "./tokio-current-thread" }
|
||||
tokio-executor = { path = "./tokio-executor" }
|
||||
tokio-fs = { path = "./tokio-fs" }
|
||||
tokio-io = { path = "./tokio-io" }
|
||||
tokio-reactor = { path = "./tokio-reactor" }
|
||||
tokio-signal = { path = "./tokio-signal" }
|
||||
tokio-tcp = { path = "./tokio-tcp" }
|
||||
tokio-threadpool = { path = "./tokio-threadpool" }
|
||||
tokio-timer = { path = "./tokio-timer" }
|
||||
tokio-tls = { path = "./tokio-tls" }
|
||||
tokio-udp = { path = "./tokio-udp" }
|
||||
tokio-uds = { path = "./tokio-uds" }
|
||||
|
||||
@@ -103,6 +103,24 @@ fn main() {
|
||||
|
||||
More examples can be found [here](examples).
|
||||
|
||||
## 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 Gitter channel][chat]. We would be happy to try to answer your
|
||||
question. Last, if that doesn't work, try opening an [issue] with the question.
|
||||
|
||||
[chat]: https://gitter.im/tokio-rs/tokio
|
||||
[issue]: https://github.com/tokio-rs/tokio/issues/new
|
||||
|
||||
## Contributing
|
||||
|
||||
:balloon: Thanks for your help improving the project! We are so happy to have
|
||||
you! We have a [contributing guide][guide] to help you get involved in the Tokio
|
||||
project.
|
||||
|
||||
[guide]: CONTRIBUTING.md
|
||||
|
||||
## Project layout
|
||||
|
||||
The `tokio` crate, found at the root, is primarily intended for use by
|
||||
@@ -111,6 +129,8 @@ have greater guarantees of stability.
|
||||
|
||||
The crates included as part of Tokio are:
|
||||
|
||||
* [`tokio-async-await`]: Experimental `async` / `await` support.
|
||||
|
||||
* [`tokio-codec`]: Utilities for encoding and decoding protocol frames.
|
||||
|
||||
* [`tokio-current-thread`]: Schedule the execution of futures on the current
|
||||
@@ -137,6 +157,7 @@ The crates included as part of Tokio are:
|
||||
* [`tokio-uds`]: Unix Domain Socket bindings for use with `tokio-io` and
|
||||
`tokio-reactor`.
|
||||
|
||||
[`tokio-async-await`]: tokio-async-await
|
||||
[`tokio-codec`]: tokio-codec
|
||||
[`tokio-current-thread`]: tokio-current-thread
|
||||
[`tokio-executor`]: tokio-executor
|
||||
@@ -149,6 +170,14 @@ The crates included as part of Tokio are:
|
||||
[`tokio-udp`]: tokio-udp
|
||||
[`tokio-uds`]: tokio-uds
|
||||
|
||||
## 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.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
|
||||
@@ -21,7 +21,7 @@ race:crossbeam_epoch
|
||||
race:crossbeam_deque*push
|
||||
race:crossbeam_deque*steal
|
||||
|
||||
# This filters out expected data race in the treiber stack implementations.
|
||||
# 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
|
||||
@@ -29,4 +29,9 @@ race:crossbeam_deque*steal
|
||||
# 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
|
||||
|
||||
@@ -34,12 +34,12 @@ use std::env;
|
||||
use std::io::{BufReader};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// Create the TCP listener we'll accept connections on.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse().unwrap();
|
||||
let addr = addr.parse()?;
|
||||
|
||||
let socket = TcpListener::bind(&addr).unwrap();
|
||||
let socket = TcpListener::bind(&addr)?;
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// This is running on the Tokio runtime, so it will be multi-threaded. The
|
||||
@@ -49,10 +49,10 @@ fn main() {
|
||||
// The server task asynchronously iterates over and processes each incoming
|
||||
// connection.
|
||||
let srv = socket.incoming()
|
||||
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
|
||||
.map_err(|e| {println!("failed to accept socket; error = {:?}", e); e})
|
||||
.for_each(move |stream| {
|
||||
// The client's socket address
|
||||
let addr = stream.peer_addr().unwrap();
|
||||
let addr = stream.peer_addr()?;
|
||||
|
||||
println!("New Connection: {}", addr);
|
||||
|
||||
@@ -143,8 +143,10 @@ fn main() {
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
});
|
||||
})
|
||||
.map_err(|err| println!("error occurred: {:?}", err));
|
||||
|
||||
// execute server
|
||||
tokio::run(srv);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+4
-3
@@ -426,7 +426,7 @@ fn process(socket: TcpStream, state: Arc<Mutex<Shared>>) {
|
||||
tokio::spawn(connection);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
pub fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// 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
|
||||
@@ -434,12 +434,12 @@ pub fn main() {
|
||||
// client connection.
|
||||
let state = Arc::new(Mutex::new(Shared::new()));
|
||||
|
||||
let addr = "127.0.0.1:6142".parse().unwrap();
|
||||
let addr = "127.0.0.1:6142".parse()?;
|
||||
|
||||
// Bind a TCP listener to the socket address.
|
||||
//
|
||||
// Note that this is the Tokio TcpListener, which is fully async.
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
let listener = TcpListener::bind(&addr)?;
|
||||
|
||||
// The server task asynchronously iterates over and processes each
|
||||
// incoming connection.
|
||||
@@ -471,4 +471,5 @@ pub fn main() {
|
||||
// In our example, we have not defined a shutdown strategy, so this will
|
||||
// block until `ctrl-c` is pressed at the terminal.
|
||||
tokio::run(server);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+28
-20
@@ -29,7 +29,7 @@ use std::thread;
|
||||
use tokio::prelude::*;
|
||||
use futures::sync::mpsc;
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// Determine if we're going to run in TCP or UDP mode
|
||||
let mut args = env::args().skip(1).collect::<Vec<_>>();
|
||||
let tcp = match args.iter().position(|a| a == "--udp") {
|
||||
@@ -41,10 +41,11 @@ fn main() {
|
||||
};
|
||||
|
||||
// Parse what address we're going to connect to
|
||||
let addr = args.first().unwrap_or_else(|| {
|
||||
panic!("this program requires at least one argument")
|
||||
});
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
let addr = match args.first() {
|
||||
Some(addr) => addr,
|
||||
None => Err("this program requires at least one argument")?,
|
||||
};
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
|
||||
// Right now Tokio doesn't support a handle to stdin running on the event
|
||||
// loop, so we farm out that work to a separate thread. This thread will
|
||||
@@ -52,15 +53,15 @@ fn main() {
|
||||
// loop over a standard futures channel.
|
||||
let (stdin_tx, stdin_rx) = mpsc::channel(0);
|
||||
thread::spawn(|| read_stdin(stdin_tx));
|
||||
let stdin_rx = stdin_rx.map_err(|_| panic!()); // errors not possible on rx
|
||||
let stdin_rx = stdin_rx.map_err(|_| panic!("errors not possible on rx"));
|
||||
|
||||
// Now that we've got our stdin read we either set up our TCP connection or
|
||||
// our UDP connection to get a stream of bytes we're going to emit to
|
||||
// stdout.
|
||||
let stdout = if tcp {
|
||||
tcp::connect(&addr, Box::new(stdin_rx))
|
||||
tcp::connect(&addr, Box::new(stdin_rx))?
|
||||
} else {
|
||||
udp::connect(&addr, Box::new(stdin_rx))
|
||||
udp::connect(&addr, Box::new(stdin_rx))?
|
||||
};
|
||||
|
||||
// And now with our stream of bytes to write to stdout, we execute that in
|
||||
@@ -77,6 +78,7 @@ fn main() {
|
||||
})
|
||||
.map_err(|e| println!("error reading stdout; error = {:?}", e))
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod codec {
|
||||
@@ -127,12 +129,13 @@ mod tcp {
|
||||
use bytes::BytesMut;
|
||||
use codec::Bytes;
|
||||
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub fn connect(addr: &SocketAddr,
|
||||
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>)
|
||||
-> Box<Stream<Item = BytesMut, Error = io::Error> + Send>
|
||||
-> Result<Box<Stream<Item = BytesMut, Error = io::Error> + Send>, Box<Error>>
|
||||
{
|
||||
let tcp = TcpStream::connect(addr);
|
||||
|
||||
@@ -151,22 +154,24 @@ mod tcp {
|
||||
// You'll also note that we *spawn* the work to read stdin and write it
|
||||
// to the TCP stream. This is done to ensure that happens concurrently
|
||||
// with us reading data from the stream.
|
||||
Box::new(tcp.map(move |stream| {
|
||||
let stream = Box::new(tcp.map(move |stream| {
|
||||
let (sink, stream) = Bytes.framed(stream).split();
|
||||
|
||||
tokio::spawn(stdin.forward(sink).then(|result| {
|
||||
if let Err(e) = result {
|
||||
panic!("failed to write to socket: {}", e)
|
||||
println!("failed to write to socket: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
stream
|
||||
}).flatten_stream())
|
||||
}).flatten_stream());
|
||||
Ok(stream)
|
||||
}
|
||||
}
|
||||
|
||||
mod udp {
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
@@ -179,17 +184,19 @@ mod udp {
|
||||
|
||||
pub fn connect(&addr: &SocketAddr,
|
||||
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>)
|
||||
-> Box<Stream<Item = BytesMut, Error = io::Error> + Send>
|
||||
-> Result<Box<Stream<Item = BytesMut, Error = io::Error> + Send>, Box<Error>>
|
||||
{
|
||||
// We'll bind our UDP socket to a local IP/port, but for now we
|
||||
// basically let the OS pick both of those.
|
||||
let addr_to_bind = if addr.ip().is_ipv4() {
|
||||
"0.0.0.0:0".parse().unwrap()
|
||||
"0.0.0.0:0".parse()?
|
||||
} else {
|
||||
"[::]:0".parse().unwrap()
|
||||
"[::]:0".parse()?
|
||||
};
|
||||
let udp = match UdpSocket::bind(&addr_to_bind) {
|
||||
Ok(udp) => udp,
|
||||
Err(_) => Err("failed to bind socket")?,
|
||||
};
|
||||
let udp = UdpSocket::bind(&addr_to_bind)
|
||||
.expect("failed to bind socket");
|
||||
|
||||
// Like above with TCP we use an instance of `Bytes` codec to transform
|
||||
// this UDP socket into a framed sink/stream which operates over
|
||||
@@ -203,7 +210,7 @@ mod udp {
|
||||
(chunk, addr)
|
||||
}).forward(sink).then(|result| {
|
||||
if let Err(e) = result {
|
||||
panic!("failed to write to socket: {}", e)
|
||||
println!("failed to write to socket: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
@@ -218,10 +225,11 @@ mod udp {
|
||||
}
|
||||
});
|
||||
|
||||
Box::new(future::lazy(|| {
|
||||
let stream = Box::new(future::lazy(|| {
|
||||
tokio::spawn(forward_stdin);
|
||||
future::ok(receive)
|
||||
}).flatten_stream())
|
||||
}).flatten_stream());
|
||||
Ok(stream)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! An UDP echo server that just sends back everything that it receives.
|
||||
//!
|
||||
//! If you're on unix you can test this out by in one terminal executing:
|
||||
//! If you're on Unix you can test this out by in one terminal executing:
|
||||
//!
|
||||
//! cargo run --example echo-udp
|
||||
//!
|
||||
@@ -50,12 +50,12 @@ impl Future for Server {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
|
||||
let socket = UdpSocket::bind(&addr).unwrap();
|
||||
println!("Listening on: {}", socket.local_addr().unwrap());
|
||||
let socket = UdpSocket::bind(&addr)?;
|
||||
println!("Listening on: {}", socket.local_addr()?);
|
||||
|
||||
let server = Server {
|
||||
socket: socket,
|
||||
@@ -70,4 +70,5 @@ fn main() {
|
||||
//
|
||||
// `tokio::run` spawns the task on the Tokio runtime and starts running.
|
||||
tokio::run(server.map_err(|e| println!("server error = {:?}", e)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+4
-3
@@ -30,19 +30,19 @@ use tokio::prelude::*;
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// Allow passing an address to listen on as the first argument of this
|
||||
// program, but otherwise we'll just set up our TCP listener on
|
||||
// 127.0.0.1:8080 for connections.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
|
||||
// Next up we create a TCP listener which will listen for incoming
|
||||
// connections. This TCP listener is bound to the address we determined
|
||||
// above and must be associated with an event loop, so we pass in a handle
|
||||
// to our event loop. After the socket's created we inform that we're ready
|
||||
// to go and start accepting connections.
|
||||
let socket = TcpListener::bind(&addr).unwrap();
|
||||
let socket = TcpListener::bind(&addr)?;
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// Here we convert the `TcpListener` to a stream of incoming connections
|
||||
@@ -111,4 +111,5 @@ fn main() {
|
||||
// never completes (it just keeps accepting sockets), `tokio::run` blocks
|
||||
// forever (until ctrl-c is pressed).
|
||||
tokio::run(done);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+20
-33
@@ -1,59 +1,45 @@
|
||||
//! Hello world server.
|
||||
//!
|
||||
//! A simple server that accepts connections, writes "hello world\n", and closes
|
||||
//! A simple client that opens a TCP stream, writes "hello world\n", and closes
|
||||
//! the connection.
|
||||
//!
|
||||
//! You can test this out by running:
|
||||
//!
|
||||
//! cargo run --example hello_world
|
||||
//! ncat -l 6142
|
||||
//!
|
||||
//! And then in another terminal run:
|
||||
//!
|
||||
//! telnet localhost 6142
|
||||
//!
|
||||
//! cargo run --example hello_world
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
|
||||
pub fn main() {
|
||||
let addr = "127.0.0.1:6142".parse().unwrap();
|
||||
pub fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let addr = "127.0.0.1:6142".parse()?;
|
||||
|
||||
// Bind a TCP listener to the socket address.
|
||||
// Open a TCP stream to the socket address.
|
||||
//
|
||||
// Note that this is the Tokio TcpListener, which is fully async.
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
|
||||
// The server task asynchronously iterates over and processes each
|
||||
// incoming connection.
|
||||
let server = listener.incoming().for_each(|socket| {
|
||||
println!("accepted socket; addr={:?}", socket.peer_addr().unwrap());
|
||||
|
||||
let connection = io::write_all(socket, "hello world\n")
|
||||
.then(|res| {
|
||||
println!("wrote message; success={:?}", res.is_ok());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// Spawn a new task that processes the socket:
|
||||
tokio::spawn(connection);
|
||||
|
||||
Ok(())
|
||||
// Note that this is the Tokio TcpStream, which is fully async.
|
||||
let client = TcpStream::connect(&addr).and_then(|stream| {
|
||||
println!("created stream");
|
||||
io::write_all(stream, "hello world\n").then(|result| {
|
||||
println!("wrote to stream; success={:?}", result.is_ok());
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.map_err(|err| {
|
||||
// All tasks must have an `Error` type of `()`. This forces error
|
||||
// handling and helps avoid silencing failures.
|
||||
//
|
||||
// In our example, we are only going to log the error to STDOUT.
|
||||
println!("accept error = {:?}", err);
|
||||
println!("connection error = {:?}", err);
|
||||
});
|
||||
|
||||
println!("server running on localhost:6142");
|
||||
|
||||
// Start the Tokio runtime.
|
||||
//
|
||||
// The Tokio is a pre-configured "out of the box" runtime for building
|
||||
@@ -63,8 +49,9 @@ pub fn main() {
|
||||
// This function blocks until the runtime reaches an idle state. Idle is
|
||||
// defined as all spawned tasks have completed and all I/O resources (TCP
|
||||
// sockets in our case) have been dropped.
|
||||
//
|
||||
// In our example, we have not defined a shutdown strategy, so this will
|
||||
// block until `ctrl-c` is pressed at the terminal.
|
||||
tokio::run(server);
|
||||
println!("About to create the stream and write to it...");
|
||||
tokio::run(client);
|
||||
println!("Stream has been created and written to.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ fn run<F: Future<Item = (), Error = ()>>(f: F) -> Result<(), IoError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
run(future::lazy(|| {
|
||||
// Here comes the application logic. It can spawn further tasks by tokio_current_thread::spawn().
|
||||
// It also can use the default reactor and create timeouts.
|
||||
@@ -82,5 +82,6 @@ fn main() {
|
||||
// We can spawn on the default executor, which is also the local one.
|
||||
tokio::executor::spawn(deadline);
|
||||
Ok(())
|
||||
})).unwrap();
|
||||
}))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -65,19 +65,19 @@ use tokio::codec::Decoder;
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// Allow passing an address to listen on as the first argument of this
|
||||
// program, but otherwise we'll just set up our TCP listener on
|
||||
// 127.0.0.1:8080 for connections.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
|
||||
// Next up we create a TCP listener which will listen for incoming
|
||||
// connections. This TCP listener is bound to the address we determined
|
||||
// above and must be associated with an event loop, so we pass in a handle
|
||||
// to our event loop. After the socket's created we inform that we're ready
|
||||
// to go and start accepting connections.
|
||||
let socket = TcpListener::bind(&addr).unwrap();
|
||||
let socket = TcpListener::bind(&addr)?;
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// Here we convert the `TcpListener` to a stream of incoming connections
|
||||
@@ -146,4 +146,5 @@ fn main() {
|
||||
// never completes (it just keeps accepting sockets), `tokio::run` blocks
|
||||
// forever (until ctrl-c is pressed).
|
||||
tokio::run(done);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+5
-4
@@ -33,15 +33,15 @@ use tokio::io::{copy, shutdown};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let listen_addr = env::args().nth(1).unwrap_or("127.0.0.1:8081".to_string());
|
||||
let listen_addr = listen_addr.parse::<SocketAddr>().unwrap();
|
||||
let listen_addr = listen_addr.parse::<SocketAddr>()?;
|
||||
|
||||
let server_addr = env::args().nth(2).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let server_addr = server_addr.parse::<SocketAddr>().unwrap();
|
||||
let server_addr = server_addr.parse::<SocketAddr>()?;
|
||||
|
||||
// Create a TCP listener which will listen for incoming connections.
|
||||
let socket = TcpListener::bind(&listen_addr).unwrap();
|
||||
let socket = TcpListener::bind(&listen_addr)?;
|
||||
println!("Listening on: {}", listen_addr);
|
||||
println!("Proxying to: {}", server_addr);
|
||||
|
||||
@@ -94,6 +94,7 @@ fn main() {
|
||||
});
|
||||
|
||||
tokio::run(done);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// This is a custom type used to have a custom implementation of the
|
||||
|
||||
+5
-4
@@ -56,7 +56,7 @@ use tokio::prelude::*;
|
||||
/// The in-memory database shared amongst all clients.
|
||||
///
|
||||
/// This database will be shared via `Arc`, so to mutate the internal map we're
|
||||
/// also going to use a `RefCell` for interior mutability.
|
||||
/// going to use a `Mutex` for interior mutability.
|
||||
struct Database {
|
||||
map: Mutex<HashMap<String, String>>,
|
||||
}
|
||||
@@ -74,12 +74,12 @@ enum Response {
|
||||
Error { msg: String },
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// Parse the address we're going to run this server on
|
||||
// and set up our TCP listener to accept connections.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
let listener = TcpListener::bind(&addr).expect("failed to bind");
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
let listener = TcpListener::bind(&addr).map_err(|_| "failed to bind")?;
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// Create the shared state of this server that will be shared amongst all
|
||||
@@ -156,6 +156,7 @@ fn main() {
|
||||
});
|
||||
|
||||
tokio::run(done);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Request {
|
||||
|
||||
+29
-24
@@ -34,13 +34,13 @@ use bytes::BytesMut;
|
||||
use http::header::HeaderValue;
|
||||
use http::{Request, Response, StatusCode};
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// Parse the arguments, bind the TCP socket we'll be listening to, spin up
|
||||
// our worker threads, and start shipping sockets to those worker threads.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
|
||||
let listener = TcpListener::bind(&addr).expect("failed to bind");
|
||||
let listener = TcpListener::bind(&addr)?;
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
tokio::run({
|
||||
@@ -51,6 +51,7 @@ fn main() {
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process(socket: TcpStream) {
|
||||
@@ -84,28 +85,32 @@ fn process(socket: TcpStream) {
|
||||
fn respond(req: Request<()>)
|
||||
-> Box<Future<Item = Response<String>, Error = io::Error> + Send>
|
||||
{
|
||||
let mut ret = Response::builder();
|
||||
let body = match req.uri().path() {
|
||||
"/plaintext" => {
|
||||
ret.header("Content-Type", "text/plain");
|
||||
"Hello, World!".to_string()
|
||||
}
|
||||
"/json" => {
|
||||
ret.header("Content-Type", "application/json");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Message {
|
||||
message: &'static str,
|
||||
let f = future::lazy(move || {
|
||||
let mut response = Response::builder();
|
||||
let body = match req.uri().path() {
|
||||
"/plaintext" => {
|
||||
response.header("Content-Type", "text/plain");
|
||||
"Hello, World!".to_string()
|
||||
}
|
||||
serde_json::to_string(&Message { message: "Hello, World!" })
|
||||
.unwrap()
|
||||
}
|
||||
_ => {
|
||||
ret.status(StatusCode::NOT_FOUND);
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
Box::new(future::ok(ret.body(body).unwrap()))
|
||||
"/json" => {
|
||||
response.header("Content-Type", "application/json");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Message {
|
||||
message: &'static str,
|
||||
}
|
||||
serde_json::to_string(&Message { message: "Hello, World!" })?
|
||||
}
|
||||
_ => {
|
||||
response.status(StatusCode::NOT_FOUND);
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
let response = response.body(body).map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
||||
Ok(response)
|
||||
});
|
||||
|
||||
Box::new(f)
|
||||
}
|
||||
|
||||
struct Http;
|
||||
|
||||
+11
-16
@@ -35,29 +35,27 @@ use std::net::SocketAddr;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::prelude::*;
|
||||
|
||||
fn get_stdin_data() -> Vec<u8> {
|
||||
fn get_stdin_data() -> Result<Vec<u8>, Box<std::error::Error>> {
|
||||
let mut buf = Vec::new();
|
||||
stdin().read_to_end(&mut buf).unwrap();
|
||||
buf
|
||||
stdin().read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let remote_addr: SocketAddr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("127.0.0.1:8080".into())
|
||||
.parse()
|
||||
.unwrap();
|
||||
.parse()?;
|
||||
// We use port 0 to let the operating system allocate an available port for us.
|
||||
let local_addr: SocketAddr = if remote_addr.is_ipv4() {
|
||||
"0.0.0.0:0"
|
||||
} else {
|
||||
"[::]:0"
|
||||
}.parse()
|
||||
.unwrap();
|
||||
let socket = UdpSocket::bind(&local_addr).unwrap();
|
||||
}.parse()?;
|
||||
let socket = UdpSocket::bind(&local_addr)?;
|
||||
const MAX_DATAGRAM_SIZE: usize = 65_507;
|
||||
let processing = socket
|
||||
.send_dgram(get_stdin_data(), &remote_addr)
|
||||
socket
|
||||
.send_dgram(get_stdin_data()?, &remote_addr)
|
||||
.and_then(|(socket, _)| socket.recv_dgram(vec![0u8; MAX_DATAGRAM_SIZE]))
|
||||
.map(|(_, data, len, _)| {
|
||||
println!(
|
||||
@@ -66,9 +64,6 @@ fn main() {
|
||||
String::from_utf8_lossy(&data[..len])
|
||||
)
|
||||
})
|
||||
.wait();
|
||||
match processing {
|
||||
Ok(_) => {}
|
||||
Err(e) => eprintln!("Encountered an error: {}", e),
|
||||
}
|
||||
.wait()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -19,15 +19,15 @@ use tokio::prelude::*;
|
||||
use tokio::net::{UdpSocket, UdpFramed};
|
||||
use tokio_codec::BytesCodec;
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
|
||||
let addr: SocketAddr = "127.0.0.1:0".parse()?;
|
||||
|
||||
// Bind both our sockets and then figure out what ports we got.
|
||||
let a = UdpSocket::bind(&addr).unwrap();
|
||||
let b = UdpSocket::bind(&addr).unwrap();
|
||||
let b_addr = b.local_addr().unwrap();
|
||||
let a = UdpSocket::bind(&addr)?;
|
||||
let b = UdpSocket::bind(&addr)?;
|
||||
let b_addr = b.local_addr()?;
|
||||
|
||||
// We're parsing each socket with the `BytesCodec` included in `tokio_io`, and then we
|
||||
// `split` each codec into the sink/stream halves.
|
||||
@@ -61,4 +61,5 @@ fn main() {
|
||||
.map(|_| ())
|
||||
.map_err(|e| println!("error = {:?}", e))
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
use std::future::{Future as StdFuture};
|
||||
|
||||
async fn map_ok<T: StdFuture>(future: T) -> Result<(), ()> {
|
||||
let _ = await!(future);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Like `tokio::run`, but takes an `async` block
|
||||
pub fn run_async<F>(future: F)
|
||||
where F: StdFuture<Output = ()> + Send + 'static,
|
||||
{
|
||||
use tokio_async_await::compat::backward;
|
||||
let future = backward::Compat::new(map_ok(future));
|
||||
|
||||
::run(future);
|
||||
}
|
||||
|
||||
/// Like `tokio::spawn`, but takes an `async` block
|
||||
pub fn spawn_async<F>(future: F)
|
||||
where F: StdFuture<Output = ()> + Send + 'static,
|
||||
{
|
||||
use tokio_async_await::compat::backward;
|
||||
let future = backward::Compat::new(map_ok(future));
|
||||
|
||||
::spawn(future);
|
||||
}
|
||||
@@ -0,0 +1,975 @@
|
||||
//! Frame a stream of bytes based on a length prefix
|
||||
//!
|
||||
//! Many protocols delimit their frames by prefacing frame data with a
|
||||
//! frame head that specifies the length of the frame. The
|
||||
//! `length_delimited` module provides utilities for handling the length
|
||||
//! based framing. This allows the consumer to work with entire frames
|
||||
//! without having to worry about buffering or other framing logic.
|
||||
//!
|
||||
//! # Getting started
|
||||
//!
|
||||
//! If implementing a protocol from scratch, using length delimited framing
|
||||
//! is an easy way to get started. [`Codec::new()`] will return a length
|
||||
//! delimited codec using default configuration values. This can then be
|
||||
//! used to construct a framer to adapt a full-duplex byte stream into a
|
||||
//! stream of frames.
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! use tokio::io::{AsyncRead, AsyncWrite};
|
||||
//! use tokio::codec::*;
|
||||
//!
|
||||
//! fn bind_transport<T: AsyncRead + AsyncWrite>(io: T)
|
||||
//! -> Framed<T, LengthDelimitedCodec>
|
||||
//! {
|
||||
//! Framed::new(io, LengthDelimitedCodec::new())
|
||||
//! }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! The returned transport implements `Sink + Stream` for `BytesMut`. It
|
||||
//! encodes the frame with a big-endian `u32` header denoting the frame
|
||||
//! payload length:
|
||||
//!
|
||||
//! ```text
|
||||
//! +----------+--------------------------------+
|
||||
//! | len: u32 | frame payload |
|
||||
//! +----------+--------------------------------+
|
||||
//! ```
|
||||
//!
|
||||
//! Specifically, given the following:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # extern crate bytes;
|
||||
//! # extern crate futures;
|
||||
//! #
|
||||
//! use tokio::io::{AsyncRead, AsyncWrite};
|
||||
//! use tokio::codec::*;
|
||||
//! use bytes::Bytes;
|
||||
//! use futures::{Sink, Future};
|
||||
//!
|
||||
//! fn write_frame<T: AsyncRead + AsyncWrite>(io: T) {
|
||||
//! let mut transport = Framed::new(io, LengthDelimitedCodec::new());
|
||||
//! let frame = Bytes::from("hello world");
|
||||
//!
|
||||
//! transport.send(frame).wait().unwrap();
|
||||
//! }
|
||||
//! #
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! The encoded frame will look like this:
|
||||
//!
|
||||
//! ```text
|
||||
//! +---- len: u32 ----+---- data ----+
|
||||
//! | \x00\x00\x00\x0b | hello world |
|
||||
//! +------------------+--------------+
|
||||
//! ```
|
||||
//!
|
||||
//! # Decoding
|
||||
//!
|
||||
//! [`FramedRead`] adapts an [`AsyncRead`] into a `Stream` of [`BytesMut`],
|
||||
//! such that each yielded [`BytesMut`] value contains the contents of an
|
||||
//! entire frame. There are many configuration parameters enabling
|
||||
//! [`FramedRead`] to handle a wide range of protocols. Here are some
|
||||
//! examples that will cover the various options at a high level.
|
||||
//!
|
||||
//! ## Example 1
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 0, including the
|
||||
//! frame head in the yielded `BytesMut`.
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # use tokio::io::AsyncRead;
|
||||
//! # use tokio::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(0) // default value
|
||||
//! .num_skip(0) // Do not strip frame header
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT DECODED
|
||||
//! +-- len ---+--- Payload ---+ +-- len ---+--- Payload ---+
|
||||
//! | \x00\x0B | Hello world | --> | \x00\x0B | Hello world |
|
||||
//! +----------+---------------+ +----------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! The value of the length field is 11 (`\x0B`) which represents the length
|
||||
//! of the payload, `hello world`. By default, [`FramedRead`] assumes that
|
||||
//! the length field represents the number of bytes that **follows** the
|
||||
//! length field. Thus, the entire frame has a length of 13: 2 bytes for the
|
||||
//! frame head + 11 bytes for the payload.
|
||||
//!
|
||||
//! ## Example 2
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 0, omitting the
|
||||
//! frame head in the yielded `BytesMut`.
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # use tokio::io::AsyncRead;
|
||||
//! # use tokio::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(0) // default value
|
||||
//! // `num_skip` is not needed, the default is to skip
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT DECODED
|
||||
//! +-- len ---+--- Payload ---+ +--- Payload ---+
|
||||
//! | \x00\x0B | Hello world | --> | Hello world |
|
||||
//! +----------+---------------+ +---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! This is similar to the first example, the only difference is that the
|
||||
//! frame head is **not** included in the yielded `BytesMut` value.
|
||||
//!
|
||||
//! ## Example 3
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 0, including the
|
||||
//! frame head in the yielded `BytesMut`. In this case, the length field
|
||||
//! **includes** the frame head length.
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # use tokio::io::AsyncRead;
|
||||
//! # use tokio::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(-2) // size of head
|
||||
//! .num_skip(0)
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT DECODED
|
||||
//! +-- len ---+--- Payload ---+ +-- len ---+--- Payload ---+
|
||||
//! | \x00\x0D | Hello world | --> | \x00\x0D | Hello world |
|
||||
//! +----------+---------------+ +----------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! In most cases, the length field represents the length of the payload
|
||||
//! only, as shown in the previous examples. However, in some protocols the
|
||||
//! length field represents the length of the whole frame, including the
|
||||
//! head. In such cases, we specify a negative `length_adjustment` to adjust
|
||||
//! the value provided in the frame head to represent the payload length.
|
||||
//!
|
||||
//! ## Example 4
|
||||
//!
|
||||
//! The following will parse a 3 byte length field at offset 0 in a 5 byte
|
||||
//! frame head, including the frame head in the yielded `BytesMut`.
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # use tokio::io::AsyncRead;
|
||||
//! # use tokio::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(3)
|
||||
//! .length_adjustment(2) // remaining head
|
||||
//! .num_skip(0)
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT
|
||||
//! +---- len -----+- head -+--- Payload ---+
|
||||
//! | \x00\x00\x0B | \xCAFE | Hello world |
|
||||
//! +--------------+--------+---------------+
|
||||
//!
|
||||
//! DECODED
|
||||
//! +---- len -----+- head -+--- Payload ---+
|
||||
//! | \x00\x00\x0B | \xCAFE | Hello world |
|
||||
//! +--------------+--------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! A more advanced example that shows a case where there is extra frame
|
||||
//! head data between the length field and the payload. In such cases, it is
|
||||
//! usually desirable to include the frame head as part of the yielded
|
||||
//! `BytesMut`. This lets consumers of the length delimited framer to
|
||||
//! process the frame head as needed.
|
||||
//!
|
||||
//! The positive `length_adjustment` value lets `FramedRead` factor in the
|
||||
//! additional head into the frame length calculation.
|
||||
//!
|
||||
//! ## Example 5
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 1 of a 4 byte
|
||||
//! frame head. The first byte and the length field will be omitted from the
|
||||
//! yielded `BytesMut`, but the trailing 2 bytes of the frame head will be
|
||||
//! included.
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # use tokio::io::AsyncRead;
|
||||
//! # use tokio::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(1) // length of hdr1
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(1) // length of hdr2
|
||||
//! .num_skip(3) // length of hdr1 + LEN
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT
|
||||
//! +- hdr1 -+-- len ---+- hdr2 -+--- Payload ---+
|
||||
//! | \xCA | \x00\x0B | \xFE | Hello world |
|
||||
//! +--------+----------+--------+---------------+
|
||||
//!
|
||||
//! DECODED
|
||||
//! +- hdr2 -+--- Payload ---+
|
||||
//! | \xFE | Hello world |
|
||||
//! +--------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! The length field is situated in the middle of the frame head. In this
|
||||
//! case, the first byte in the frame head could be a version or some other
|
||||
//! identifier that is not needed for processing. On the other hand, the
|
||||
//! second half of the head is needed.
|
||||
//!
|
||||
//! `length_field_offset` indicates how many bytes to skip before starting
|
||||
//! to read the length field. `length_adjustment` is the number of bytes to
|
||||
//! skip starting at the end of the length field. In this case, it is the
|
||||
//! second half of the head.
|
||||
//!
|
||||
//! ## Example 6
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 1 of a 4 byte
|
||||
//! frame head. The first byte and the length field will be omitted from the
|
||||
//! yielded `BytesMut`, but the trailing 2 bytes of the frame head will be
|
||||
//! included. In this case, the length field **includes** the frame head
|
||||
//! length.
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # use tokio::io::AsyncRead;
|
||||
//! # use tokio::codec::length_delimited;
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(1) // length of hdr1
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(-3) // length of hdr1 + LEN, negative
|
||||
//! .num_skip(3)
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT
|
||||
//! +- hdr1 -+-- len ---+- hdr2 -+--- Payload ---+
|
||||
//! | \xCA | \x00\x0F | \xFE | Hello world |
|
||||
//! +--------+----------+--------+---------------+
|
||||
//!
|
||||
//! DECODED
|
||||
//! +- hdr2 -+--- Payload ---+
|
||||
//! | \xFE | Hello world |
|
||||
//! +--------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! Similar to the example above, the difference is that the length field
|
||||
//! represents the length of the entire frame instead of just the payload.
|
||||
//! The length of `hdr1` and `len` must be counted in `length_adjustment`.
|
||||
//! Note that the length of `hdr2` does **not** need to be explicitly set
|
||||
//! anywhere because it already is factored into the total frame length that
|
||||
//! is read from the byte stream.
|
||||
//!
|
||||
//! # Encoding
|
||||
//!
|
||||
//! [`FramedWrite`] adapts an [`AsyncWrite`] into a `Sink` of [`BytesMut`],
|
||||
//! such that each submitted [`BytesMut`] is prefaced by a length field.
|
||||
//! There are fewer configuration options than [`FramedRead`]. Given
|
||||
//! protocols that have more complex frame heads, an encoder should probably
|
||||
//! be written by hand using [`Encoder`].
|
||||
//!
|
||||
//! Here is a simple example, given a `FramedWrite` with the following
|
||||
//! configuration:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # extern crate bytes;
|
||||
//! # use tokio::io::AsyncWrite;
|
||||
//! # use tokio::codec::length_delimited;
|
||||
//! # use bytes::BytesMut;
|
||||
//! # fn write_frame<T: AsyncWrite>(io: T) {
|
||||
//! # let _ =
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_length(2)
|
||||
//! .new_write(io);
|
||||
//! # }
|
||||
//! # pub fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! A payload of `hello world` will be encoded as:
|
||||
//!
|
||||
//! ```text
|
||||
//! +- len: u16 -+---- data ----+
|
||||
//! | \x00\x0b | hello world |
|
||||
//! +------------+--------------+
|
||||
//! ```
|
||||
//!
|
||||
//! [`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
|
||||
|
||||
use {
|
||||
codec::{
|
||||
Decoder, Encoder, FramedRead, FramedWrite, Framed
|
||||
},
|
||||
io::{
|
||||
AsyncRead, AsyncWrite
|
||||
},
|
||||
};
|
||||
|
||||
use bytes::{Buf, BufMut, Bytes, BytesMut, IntoBuf};
|
||||
|
||||
use std::{cmp, fmt};
|
||||
use std::error::Error as StdError;
|
||||
use std::io::{self, Cursor};
|
||||
|
||||
/// Configure length delimited `LengthDelimitedCodec`s.
|
||||
///
|
||||
/// `Builder` enables constructing configured length delimited codecs. Note
|
||||
/// that not all configuration settings apply to both encoding and decoding. See
|
||||
/// the documentation for specific methods for more detail.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Builder {
|
||||
// Maximum frame length
|
||||
max_frame_len: usize,
|
||||
|
||||
// Number of bytes representing the field length
|
||||
length_field_len: usize,
|
||||
|
||||
// Number of bytes in the header before the length field
|
||||
length_field_offset: usize,
|
||||
|
||||
// Adjust the length specified in the header field by this amount
|
||||
length_adjustment: isize,
|
||||
|
||||
// Total number of bytes to skip before reading the payload, if not set,
|
||||
// `length_field_len + length_field_offset`
|
||||
num_skip: Option<usize>,
|
||||
|
||||
// Length field byte order (little or big endian)
|
||||
length_field_is_big_endian: bool,
|
||||
}
|
||||
|
||||
/// An error when the number of bytes read is more than max frame length.
|
||||
pub struct FrameTooBig {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
/// A codec for frames delimited by a frame head specifying their lengths.
|
||||
///
|
||||
/// This allows the consumer to work with entire frames without having to worry
|
||||
/// about buffering or other framing logic.
|
||||
///
|
||||
/// See [module level] documentation for more detail.
|
||||
///
|
||||
/// [module level]: index.html
|
||||
#[derive(Debug)]
|
||||
pub struct LengthDelimitedCodec {
|
||||
// Configuration values
|
||||
builder: Builder,
|
||||
|
||||
// Read state
|
||||
state: DecodeState,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum DecodeState {
|
||||
Head,
|
||||
Data(usize),
|
||||
}
|
||||
|
||||
// ===== impl LengthDelimitedCodec ======
|
||||
|
||||
impl LengthDelimitedCodec {
|
||||
/// Creates a new `LengthDelimitedCodec` with the default configuration values.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
builder: Builder::new(),
|
||||
state: DecodeState::Head,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current max frame setting
|
||||
///
|
||||
/// This is the largest size this codec will accept from the wire. Larger
|
||||
/// frames will be rejected.
|
||||
pub fn max_frame_length(&self) -> usize {
|
||||
self.builder.max_frame_len
|
||||
}
|
||||
|
||||
/// Updates the max frame setting.
|
||||
///
|
||||
/// The change takes effect the next time a frame is decoded. In other
|
||||
/// words, if a frame is currently in process of being decoded with a frame
|
||||
/// size greater than `val` but less than the max frame length in effect
|
||||
/// before calling this function, then the frame will be allowed.
|
||||
pub fn set_max_frame_length(&mut self, val: usize) {
|
||||
self.builder.max_frame_length(val);
|
||||
}
|
||||
|
||||
fn decode_head(&mut self, src: &mut BytesMut) -> io::Result<Option<usize>> {
|
||||
let head_len = self.builder.num_head_bytes();
|
||||
let field_len = self.builder.length_field_len;
|
||||
|
||||
if src.len() < head_len {
|
||||
// Not enough data
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let n = {
|
||||
let mut src = Cursor::new(&mut *src);
|
||||
|
||||
// Skip the required bytes
|
||||
src.advance(self.builder.length_field_offset);
|
||||
|
||||
// match endianess
|
||||
let n = if self.builder.length_field_is_big_endian {
|
||||
src.get_uint_be(field_len)
|
||||
} else {
|
||||
src.get_uint_le(field_len)
|
||||
};
|
||||
|
||||
if n > self.builder.max_frame_len as u64 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, FrameTooBig {
|
||||
_priv: (),
|
||||
}));
|
||||
}
|
||||
|
||||
// The check above ensures there is no overflow
|
||||
let n = n as usize;
|
||||
|
||||
// Adjust `n` with bounds checking
|
||||
let n = if self.builder.length_adjustment < 0 {
|
||||
n.checked_sub(-self.builder.length_adjustment as usize)
|
||||
} else {
|
||||
n.checked_add(self.builder.length_adjustment as usize)
|
||||
};
|
||||
|
||||
// Error handling
|
||||
match n {
|
||||
Some(n) => n,
|
||||
None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "provided length would overflow after adjustment")),
|
||||
}
|
||||
};
|
||||
|
||||
let num_skip = self.builder.get_num_skip();
|
||||
|
||||
if num_skip > 0 {
|
||||
let _ = src.split_to(num_skip);
|
||||
}
|
||||
|
||||
// Ensure that the buffer has enough space to read the incoming
|
||||
// payload
|
||||
src.reserve(n);
|
||||
|
||||
return Ok(Some(n));
|
||||
}
|
||||
|
||||
fn decode_data(&self, n: usize, src: &mut BytesMut) -> io::Result<Option<BytesMut>> {
|
||||
// At this point, the buffer has already had the required capacity
|
||||
// reserved. All there is to do is read.
|
||||
if src.len() < n {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(Some(src.split_to(n)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for LengthDelimitedCodec {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> io::Result<Option<BytesMut>> {
|
||||
let n = match self.state {
|
||||
DecodeState::Head => {
|
||||
match try!(self.decode_head(src)) {
|
||||
Some(n) => {
|
||||
self.state = DecodeState::Data(n);
|
||||
n
|
||||
}
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
DecodeState::Data(n) => n,
|
||||
};
|
||||
|
||||
match try!(self.decode_data(n, src)) {
|
||||
Some(data) => {
|
||||
// Update the decode state
|
||||
self.state = DecodeState::Head;
|
||||
|
||||
// Make sure the buffer has enough space to read the next head
|
||||
src.reserve(self.builder.num_head_bytes());
|
||||
|
||||
Ok(Some(data))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for LengthDelimitedCodec {
|
||||
type Item = Bytes;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, data: Bytes, dst: &mut BytesMut) -> Result<(), io::Error> {
|
||||
let n = (&data).into_buf().remaining();
|
||||
|
||||
if n > self.builder.max_frame_len {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, FrameTooBig {
|
||||
_priv: (),
|
||||
}));
|
||||
}
|
||||
|
||||
// Adjust `n` with bounds checking
|
||||
let n = if self.builder.length_adjustment < 0 {
|
||||
n.checked_add(-self.builder.length_adjustment as usize)
|
||||
} else {
|
||||
n.checked_sub(self.builder.length_adjustment as usize)
|
||||
};
|
||||
|
||||
let n = n.ok_or_else(|| io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"provided length would overflow after adjustment",
|
||||
))?;
|
||||
|
||||
// Reserve capacity in the destination buffer to fit the frame and
|
||||
// length field (plus adjustment).
|
||||
dst.reserve(self.builder.length_field_len + n);
|
||||
|
||||
if self.builder.length_field_is_big_endian {
|
||||
dst.put_uint_be(n as u64, self.builder.length_field_len);
|
||||
} else {
|
||||
dst.put_uint_le(n as u64, self.builder.length_field_len);
|
||||
}
|
||||
|
||||
// Write the frame to the buffer
|
||||
dst.extend_from_slice(&data[..]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Builder =====
|
||||
|
||||
impl Builder {
|
||||
/// Creates a new length delimited codec builder with default configuration
|
||||
/// values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_offset(0)
|
||||
/// .length_field_length(2)
|
||||
/// .length_adjustment(0)
|
||||
/// .num_skip(0)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn new() -> Builder {
|
||||
Builder {
|
||||
// Default max frame length of 8MB
|
||||
max_frame_len: 8 * 1_024 * 1_024,
|
||||
|
||||
// Default byte length of 4
|
||||
length_field_len: 4,
|
||||
|
||||
// Default to the header field being at the start of the header.
|
||||
length_field_offset: 0,
|
||||
|
||||
length_adjustment: 0,
|
||||
|
||||
// Total number of bytes to skip before reading the payload, if not set,
|
||||
// `length_field_len + length_field_offset`
|
||||
num_skip: None,
|
||||
|
||||
// Default to reading the length field in network (big) endian.
|
||||
length_field_is_big_endian: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the length field as a big endian integer
|
||||
///
|
||||
/// This is the default setting.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .big_endian()
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn big_endian(&mut self) -> &mut Self {
|
||||
self.length_field_is_big_endian = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Read the length field as a little endian integer
|
||||
///
|
||||
/// The default setting is big endian.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .little_endian()
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn little_endian(&mut self) -> &mut Self {
|
||||
self.length_field_is_big_endian = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Read the length field as a native endian integer
|
||||
///
|
||||
/// The default setting is big endian.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .native_endian()
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn native_endian(&mut self) -> &mut Self {
|
||||
if cfg!(target_endian = "big") {
|
||||
self.big_endian()
|
||||
} else {
|
||||
self.little_endian()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the max frame length
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding. The
|
||||
/// default value is 8MB.
|
||||
///
|
||||
/// When decoding, the length field read from the byte stream is checked
|
||||
/// against this setting **before** any adjustments are applied. When
|
||||
/// encoding, the length of the submitted payload is checked against this
|
||||
/// setting.
|
||||
///
|
||||
/// When frames exceed the max length, an `io::Error` with the custom value
|
||||
/// of the `FrameTooBig` type will be returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .max_frame_length(8 * 1024)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn max_frame_length(&mut self, val: usize) -> &mut Self {
|
||||
self.max_frame_len = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the number of bytes used to represent the length field
|
||||
///
|
||||
/// The default value is `4`. The max value is `8`.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_length(4)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn length_field_length(&mut self, val: usize) -> &mut Self {
|
||||
assert!(val > 0 && val <= 8, "invalid length field length");
|
||||
self.length_field_len = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the number of bytes in the header before the length field
|
||||
///
|
||||
/// This configuration option only applies to decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_offset(1)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn length_field_offset(&mut self, val: usize) -> &mut Self {
|
||||
self.length_field_offset = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Delta between the payload length specified in the header and the real
|
||||
/// payload length
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_adjustment(-2)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn length_adjustment(&mut self, val: isize) -> &mut Self {
|
||||
self.length_adjustment = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the number of bytes to skip before reading the payload
|
||||
///
|
||||
/// Default value is `length_field_len + length_field_offset`
|
||||
///
|
||||
/// This configuration option only applies to decoding
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .num_skip(4)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn num_skip(&mut self, val: usize) -> &mut Self {
|
||||
self.num_skip = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a configured length delimited `LengthDelimitedCodec`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
/// # pub fn main() {
|
||||
/// Builder::new()
|
||||
/// .length_field_offset(0)
|
||||
/// .length_field_length(2)
|
||||
/// .length_adjustment(0)
|
||||
/// .num_skip(0)
|
||||
/// .new_codec();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new_codec(&self) -> LengthDelimitedCodec {
|
||||
LengthDelimitedCodec {
|
||||
builder: *self,
|
||||
state: DecodeState::Head,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a configured length delimited `FramedRead`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::io::AsyncRead;
|
||||
/// use tokio::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_offset(0)
|
||||
/// .length_field_length(2)
|
||||
/// .length_adjustment(0)
|
||||
/// .num_skip(0)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn new_read<T>(&self, upstream: T) -> FramedRead<T, LengthDelimitedCodec>
|
||||
where T: AsyncRead,
|
||||
{
|
||||
FramedRead::new(upstream, self.new_codec())
|
||||
}
|
||||
|
||||
/// Create a configured length delimited `FramedWrite`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate bytes;
|
||||
/// # use tokio::io::AsyncWrite;
|
||||
/// # use tokio::codec::length_delimited;
|
||||
/// # use bytes::BytesMut;
|
||||
/// # fn write_frame<T: AsyncWrite>(io: T) {
|
||||
/// length_delimited::Builder::new()
|
||||
/// .length_field_length(2)
|
||||
/// .new_write(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn new_write<T>(&self, inner: T) -> FramedWrite<T, LengthDelimitedCodec>
|
||||
where T: AsyncWrite,
|
||||
{
|
||||
FramedWrite::new(inner, self.new_codec())
|
||||
}
|
||||
|
||||
/// Create a configured length delimited `Framed`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate bytes;
|
||||
/// # use tokio::io::{AsyncRead, AsyncWrite};
|
||||
/// # use tokio::codec::length_delimited;
|
||||
/// # use bytes::BytesMut;
|
||||
/// # fn write_frame<T: AsyncRead + AsyncWrite>(io: T) {
|
||||
/// # let _ =
|
||||
/// length_delimited::Builder::new()
|
||||
/// .length_field_length(2)
|
||||
/// .new_framed(io);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn new_framed<T>(&self, inner: T) -> Framed<T, LengthDelimitedCodec>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
{
|
||||
Framed::new(inner, self.new_codec())
|
||||
}
|
||||
|
||||
fn num_head_bytes(&self) -> usize {
|
||||
let num = self.length_field_offset + self.length_field_len;
|
||||
cmp::max(num, self.num_skip.unwrap_or(0))
|
||||
}
|
||||
|
||||
fn get_num_skip(&self) -> usize {
|
||||
self.num_skip.unwrap_or(self.length_field_offset + self.length_field_len)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===== impl FrameTooBig =====
|
||||
|
||||
impl fmt::Debug for FrameTooBig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("FrameTooBig")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FrameTooBig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for FrameTooBig {
|
||||
fn description(&self) -> &str {
|
||||
"frame size too big"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//! Utilities for encoding and decoding frames.
|
||||
//!
|
||||
//! 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`]: ../io/trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: ../io/trait.AsyncWrite.html
|
||||
//! [`Sink`]: https://docs.rs/futures/0.1/futures/sink/trait.Sink.html
|
||||
//! [`Stream`]: https://docs.rs/futures/0.1/futures/stream/trait.Stream.html
|
||||
//! [transports]: https://tokio.rs/docs/going-deeper/frames/
|
||||
|
||||
pub use tokio_codec::{
|
||||
Decoder,
|
||||
Encoder,
|
||||
Framed,
|
||||
FramedParts,
|
||||
FramedRead,
|
||||
FramedWrite,
|
||||
BytesCodec,
|
||||
LinesCodec,
|
||||
};
|
||||
|
||||
pub mod length_delimited;
|
||||
|
||||
pub use self::length_delimited::LengthDelimitedCodec;
|
||||
+5
-1
@@ -39,11 +39,15 @@
|
||||
//! [`Executor`]: trait.Executor.html
|
||||
//! [`spawn`]: fn.spawn.html
|
||||
|
||||
#[deprecated(since = "0.1.8", note = "use tokio-current-thread crate instead")]
|
||||
#[deprecated(
|
||||
since = "0.1.8",
|
||||
note = "use tokio-current-thread crate or functions in tokio::runtime::current_thread instead",
|
||||
)]
|
||||
#[doc(hidden)]
|
||||
pub mod current_thread;
|
||||
|
||||
#[deprecated(since = "0.1.8", note = "use tokio-threadpool crate instead")]
|
||||
#[doc(hidden)]
|
||||
/// Re-exports of [`tokio-threadpool`], deprecated in favor of the crate.
|
||||
///
|
||||
/// [`tokio-threadpool`]: https://docs.rs/tokio-threadpool/0.1
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
//! Asynchronous I/O.
|
||||
//!
|
||||
//! This module is the asynchronous version of `std::io`. Primarily, it
|
||||
//! defines two traits, [`AsyncRead`] and [`AsyncWrite`], which extend the
|
||||
//! `Read` and `Write` traits of the standard library.
|
||||
//!
|
||||
//! # AsyncRead and AsyncWrite
|
||||
//!
|
||||
//! [`AsyncRead`] and [`AsyncWrite`] must only be implemented for
|
||||
//! non-blocking I/O types that integrate with the futures type system. In
|
||||
//! other words, these types must never block the thread, and instead the
|
||||
//! current task is notified when the I/O resource is ready.
|
||||
//!
|
||||
//! # Standard input and output
|
||||
//!
|
||||
//! Tokio provides asynchronous APIs to standard [input], [output], and [error].
|
||||
//! These APIs are very similar to the ones provided by `std`, but they also
|
||||
//! implement [`AsyncRead`] and [`AsyncWrite`].
|
||||
//!
|
||||
//! Unlike *most* other Tokio APIs, the standard input / output APIs
|
||||
//! **must** be used from the context of the Tokio runtime as they require
|
||||
//! Tokio specific features to function.
|
||||
//!
|
||||
//! [input]: fn.stdin.html
|
||||
//! [output]: fn.stdout.html
|
||||
//! [error]: fn.stderr.html
|
||||
//!
|
||||
//! # Utility functions
|
||||
//!
|
||||
//! Utilities functions are provided for working with [`AsyncRead`] /
|
||||
//! [`AsyncWrite`] types. For example, [`copy`] asynchronously copies all
|
||||
//! data from a source to a destination.
|
||||
//!
|
||||
//! # `std` re-exports
|
||||
//!
|
||||
//! Additionally, [`Read`], [`Write`], [`Error`], [`ErrorKind`], and
|
||||
//! [`Result`] are re-exported from `std::io` for ease of use.
|
||||
//!
|
||||
//! [`AsyncRead`]: trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: trait.AsyncWrite.html
|
||||
//! [`copy`]: fn.copy.html
|
||||
//! [`Read`]: trait.Read.html
|
||||
//! [`Write`]: trait.Write.html
|
||||
//! [`Error`]: struct.Error.html
|
||||
//! [`ErrorKind`]: enum.ErrorKind.html
|
||||
//! [`Result`]: type.Result.html
|
||||
|
||||
pub use tokio_io::{
|
||||
AsyncRead,
|
||||
AsyncWrite,
|
||||
};
|
||||
|
||||
// standard input, output, and error
|
||||
pub use tokio_fs::{
|
||||
stdin,
|
||||
Stdin,
|
||||
stdout,
|
||||
Stdout,
|
||||
stderr,
|
||||
Stderr,
|
||||
};
|
||||
|
||||
// Utils
|
||||
pub use tokio_io::io::{
|
||||
copy,
|
||||
Copy,
|
||||
flush,
|
||||
Flush,
|
||||
lines,
|
||||
Lines,
|
||||
read,
|
||||
read_exact,
|
||||
ReadExact,
|
||||
read_to_end,
|
||||
ReadToEnd,
|
||||
read_until,
|
||||
ReadUntil,
|
||||
ReadHalf,
|
||||
shutdown,
|
||||
Shutdown,
|
||||
write_all,
|
||||
WriteAll,
|
||||
WriteHalf,
|
||||
};
|
||||
|
||||
// Re-export io::Error so that users don't have to deal
|
||||
// with conflicts when `use`ing `futures::io` and `std::io`.
|
||||
pub use ::std::io::{
|
||||
Error,
|
||||
ErrorKind,
|
||||
Result,
|
||||
Read,
|
||||
Write,
|
||||
};
|
||||
+23
-161
@@ -1,3 +1,11 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio/0.1.13")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
#![cfg_attr(feature = "async-await-preview", feature(
|
||||
async_await,
|
||||
await_macro,
|
||||
futures_api,
|
||||
))]
|
||||
|
||||
//! A runtime for writing reliable, asynchronous, and slim applications.
|
||||
//!
|
||||
//! Tokio is an event-driven, non-blocking I/O platform for writing asynchronous
|
||||
@@ -64,12 +72,11 @@
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/tokio/0.1.5")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
|
||||
extern crate bytes;
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate mio;
|
||||
extern crate num_cpus;
|
||||
extern crate tokio_current_thread;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_executor;
|
||||
@@ -81,13 +88,19 @@ extern crate tokio_timer;
|
||||
extern crate tokio_tcp;
|
||||
extern crate tokio_udp;
|
||||
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
extern crate tokio_async_await;
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate tokio_uds;
|
||||
|
||||
pub mod clock;
|
||||
pub mod codec;
|
||||
pub mod executor;
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod net;
|
||||
pub mod prelude;
|
||||
pub mod reactor;
|
||||
pub mod runtime;
|
||||
pub mod timer;
|
||||
@@ -96,164 +109,13 @@ pub mod util;
|
||||
pub use executor::spawn;
|
||||
pub use runtime::run;
|
||||
|
||||
pub mod codec {
|
||||
//! Utilities for encoding and decoding frames.
|
||||
//!
|
||||
//! 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`]: ../io/trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: ../io/trait.AsyncWrite.html
|
||||
//! [`Sink`]: https://docs.rs/futures/0.1/futures/sink/trait.Sink.html
|
||||
//! [`Stream`]: https://docs.rs/futures/0.1/futures/stream/trait.Stream.html
|
||||
//! [transports]: https://tokio.rs/docs/going-deeper/frames/
|
||||
// ===== Experimental async/await support =====
|
||||
|
||||
pub use tokio_codec::{
|
||||
Decoder,
|
||||
Encoder,
|
||||
Framed,
|
||||
FramedParts,
|
||||
FramedRead,
|
||||
FramedWrite,
|
||||
BytesCodec,
|
||||
LinesCodec,
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
mod async_await;
|
||||
|
||||
pub mod io {
|
||||
//! Asynchronous I/O.
|
||||
//!
|
||||
//! This module is the asynchronous version of `std::io`. Primarily, it
|
||||
//! defines two traits, [`AsyncRead`] and [`AsyncWrite`], which extend the
|
||||
//! `Read` and `Write` traits of the standard library.
|
||||
//!
|
||||
//! # AsyncRead and AsyncWrite
|
||||
//!
|
||||
//! [`AsyncRead`] and [`AsyncWrite`] must only be implemented for
|
||||
//! non-blocking I/O types that integrate with the futures type system. In
|
||||
//! other words, these types must never block the thread, and instead the
|
||||
//! current task is notified when the I/O resource is ready.
|
||||
//!
|
||||
//! # Standard input and output
|
||||
//!
|
||||
//! Tokio provides asynchronous APIs to standard [input], [output], and [error].
|
||||
//! These APIs are very similar to the ones provided by `std`, but they also
|
||||
//! implement [`AsyncRead`] and [`AsyncWrite`].
|
||||
//!
|
||||
//! Unlike *most* other Tokio APIs, the standard input / output APIs
|
||||
//! **must** be used from the context of the Tokio runtime as they require
|
||||
//! Tokio specific features to function.
|
||||
//!
|
||||
//! [input]: fn.stdin.html
|
||||
//! [output]: fn.stdout.html
|
||||
//! [error]: fn.stderr.html
|
||||
//!
|
||||
//! # Utility functions
|
||||
//!
|
||||
//! Utilities functions are provided for working with [`AsyncRead`] /
|
||||
//! [`AsyncWrite`] types. For example, [`copy`] asynchronously copies all
|
||||
//! data from a source to a destination.
|
||||
//!
|
||||
//! # `std` re-exports
|
||||
//!
|
||||
//! Additionally, [`Read`], [`Write`], [`Error`], [`ErrorKind`], and
|
||||
//! [`Result`] are re-exported from `std::io` for ease of use.
|
||||
//!
|
||||
//! [`AsyncRead`]: trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: trait.AsyncWrite.html
|
||||
//! [`copy`]: fn.copy.html
|
||||
//! [`Read`]: trait.Read.html
|
||||
//! [`Write`]: trait.Write.html
|
||||
//! [`Error`]: struct.Error.html
|
||||
//! [`ErrorKind`]: enum.ErrorKind.html
|
||||
//! [`Result`]: type.Result.html
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
pub use async_await::{run_async, spawn_async};
|
||||
|
||||
pub use tokio_io::{
|
||||
AsyncRead,
|
||||
AsyncWrite,
|
||||
};
|
||||
|
||||
// standard input, output, and error
|
||||
pub use tokio_fs::{
|
||||
stdin,
|
||||
Stdin,
|
||||
stdout,
|
||||
Stdout,
|
||||
stderr,
|
||||
Stderr,
|
||||
};
|
||||
|
||||
// Utils
|
||||
pub use tokio_io::io::{
|
||||
copy,
|
||||
Copy,
|
||||
flush,
|
||||
Flush,
|
||||
lines,
|
||||
Lines,
|
||||
read_exact,
|
||||
ReadExact,
|
||||
read_to_end,
|
||||
ReadToEnd,
|
||||
read_until,
|
||||
ReadUntil,
|
||||
ReadHalf,
|
||||
shutdown,
|
||||
Shutdown,
|
||||
write_all,
|
||||
WriteAll,
|
||||
WriteHalf,
|
||||
};
|
||||
|
||||
// Re-export io::Error so that users don't have to deal
|
||||
// with conflicts when `use`ing `futures::io` and `std::io`.
|
||||
pub use ::std::io::{
|
||||
Error,
|
||||
ErrorKind,
|
||||
Result,
|
||||
Read,
|
||||
Write,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
//! A "prelude" for users of the `tokio` crate.
|
||||
//!
|
||||
//! This prelude is similar to the standard library's prelude in that you'll
|
||||
//! almost always want to import its entire contents, but unlike the standard
|
||||
//! library's prelude you'll have to do so manually:
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio::prelude::*;
|
||||
//! ```
|
||||
//!
|
||||
//! The prelude may grow over time as additional items see ubiquitous use.
|
||||
|
||||
pub use tokio_io::{
|
||||
AsyncRead,
|
||||
AsyncWrite,
|
||||
};
|
||||
|
||||
pub use util::{
|
||||
FutureExt,
|
||||
};
|
||||
|
||||
pub use ::std::io::{
|
||||
Read,
|
||||
Write,
|
||||
};
|
||||
|
||||
pub use futures::{
|
||||
Future,
|
||||
future,
|
||||
Stream,
|
||||
stream,
|
||||
Sink,
|
||||
IntoFuture,
|
||||
Async,
|
||||
AsyncSink,
|
||||
Poll,
|
||||
task,
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
pub use tokio_async_await::await;
|
||||
|
||||
+65
-34
@@ -1,54 +1,85 @@
|
||||
//! TCP/UDP bindings for `tokio`.
|
||||
//! TCP/UDP/Unix bindings for `tokio`.
|
||||
//!
|
||||
//! This module contains the TCP/UDP networking types, similar to the standard
|
||||
//! This module contains the TCP/UDP/Unix networking types, similar to the standard
|
||||
//! library, which can be used to implement networking protocols.
|
||||
//!
|
||||
//! # TCP
|
||||
//! # Organization
|
||||
//!
|
||||
//! Connecting to an address, via TCP, can be done using [`TcpStream`]'s
|
||||
//! [`connect`] method, which returns [`ConnectFuture`]. `ConnectFuture`
|
||||
//! implements a future which returns a `TcpStream`.
|
||||
//! * [`TcpListener`] and [`TcpStream`] provide functionality for communication over TCP
|
||||
//! * [`UdpSocket`] and [`UdpFramed`] provide functionality for communication over UDP
|
||||
//! * [`UnixListener`] and [`UnixStream`] provide functionality for communication over a
|
||||
//! Unix Domain Socket **(available on Unix only)**
|
||||
//!
|
||||
//! To listen on an address [`TcpListener`] can be used. `TcpListener`'s
|
||||
//! [`incoming`][incoming_method] method can be used to accept new connections.
|
||||
//! It return the [`Incoming`] struct, which implements a stream which returns
|
||||
//! `TcpStream`s.
|
||||
//!
|
||||
//! [`TcpStream`]: struct.TcpStream.html
|
||||
//! [`connect`]: struct.TcpStream.html#method.connect
|
||||
//! [`ConnectFuture`]: struct.ConnectFuture.html
|
||||
//! [`TcpListener`]: struct.TcpListener.html
|
||||
//! [incoming_method]: struct.TcpListener.html#method.incoming
|
||||
//! [`Incoming`]: struct.Incoming.html
|
||||
//!
|
||||
//! # UDP
|
||||
//!
|
||||
//! The main struct for UDP is the [`UdpSocket`], which represents a UDP socket.
|
||||
//! Reading and writing to it can be done using futures, which return the
|
||||
//! [`RecvDgram`] and [`SendDgram`] structs respectively.
|
||||
//!
|
||||
//! For convenience it's also possible to convert raw datagrams into higher-level
|
||||
//! frames.
|
||||
//!
|
||||
//! [`TcpStream`]: struct.TcpStream.html
|
||||
//! [`UdpSocket`]: struct.UdpSocket.html
|
||||
//! [`RecvDgram`]: struct.RecvDgram.html
|
||||
//! [`SendDgram`]: struct.SendDgram.html
|
||||
//! [`UdpFramed`]: struct.UdpFramed.html
|
||||
//! [`framed`]: struct.UdpSocket.html#method.framed
|
||||
//! [`UnixListener`]: struct.UnixListener.html
|
||||
//! [`UnixStream`]: struct.UnixStream.html
|
||||
|
||||
pub use tokio_tcp::{TcpStream, ConnectFuture};
|
||||
pub use tokio_tcp::{TcpListener, Incoming};
|
||||
pub use tokio_udp::{UdpSocket, UdpFramed, SendDgram, RecvDgram};
|
||||
pub mod tcp {
|
||||
//! TCP bindings for `tokio`.
|
||||
//!
|
||||
//! Connecting to an address, via TCP, can be done using [`TcpStream`]'s
|
||||
//! [`connect`] method, which returns [`ConnectFuture`]. `ConnectFuture`
|
||||
//! implements a future which returns a `TcpStream`.
|
||||
//!
|
||||
//! To listen on an address [`TcpListener`] can be used. `TcpListener`'s
|
||||
//! [`incoming`][incoming_method] method can be used to accept new connections.
|
||||
//! It return the [`Incoming`] struct, which implements a stream which returns
|
||||
//! `TcpStream`s.
|
||||
//!
|
||||
//! [`TcpStream`]: struct.TcpStream.html
|
||||
//! [`connect`]: struct.TcpStream.html#method.connect
|
||||
//! [`ConnectFuture`]: struct.ConnectFuture.html
|
||||
//! [`TcpListener`]: struct.TcpListener.html
|
||||
//! [incoming_method]: struct.TcpListener.html#method.incoming
|
||||
//! [`Incoming`]: struct.Incoming.html
|
||||
pub use tokio_tcp::{ConnectFuture, Incoming, TcpListener, TcpStream};
|
||||
}
|
||||
pub use self::tcp::{TcpListener, TcpStream};
|
||||
|
||||
#[deprecated(note = "use `tokio::net::tcp::ConnectFuture` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type ConnectFuture = self::tcp::ConnectFuture;
|
||||
#[deprecated(note = "use `tokio::net::tcp::Incoming` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type Incoming = self::tcp::Incoming;
|
||||
|
||||
pub mod udp {
|
||||
//! UDP bindings for `tokio`.
|
||||
//!
|
||||
//! The main struct for UDP is the [`UdpSocket`], which represents a UDP socket.
|
||||
//! Reading and writing to it can be done using futures, which return the
|
||||
//! [`RecvDgram`] and [`SendDgram`] structs respectively.
|
||||
//!
|
||||
//! For convenience it's also possible to convert raw datagrams into higher-level
|
||||
//! frames.
|
||||
//!
|
||||
//! [`UdpSocket`]: struct.UdpSocket.html
|
||||
//! [`RecvDgram`]: struct.RecvDgram.html
|
||||
//! [`SendDgram`]: struct.SendDgram.html
|
||||
//! [`UdpFramed`]: struct.UdpFramed.html
|
||||
//! [`framed`]: struct.UdpSocket.html#method.framed
|
||||
pub use tokio_udp::{RecvDgram, SendDgram, UdpFramed, UdpSocket};
|
||||
}
|
||||
pub use self::udp::{UdpFramed, UdpSocket};
|
||||
|
||||
#[deprecated(note = "use `tokio::net::udp::RecvDgram` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type RecvDgram<T> = self::udp::RecvDgram<T>;
|
||||
#[deprecated(note = "use `tokio::net::udp::SendDgram` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type SendDgram<T> = self::udp::SendDgram<T>;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub mod unix {
|
||||
//! Unix domain socket bindings for `tokio`.
|
||||
//! Unix domain socket bindings for `tokio` (only available on unix systems).
|
||||
|
||||
pub use tokio_uds::{
|
||||
ConnectFuture, Incoming, RecvDgram, SendDgram, UCred, UnixDatagram, UnixListener,
|
||||
UnixStream,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub use self::unix::{UnixListener, UnixStream};
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
//! A "prelude" for users of the `tokio` crate.
|
||||
//!
|
||||
//! This prelude is similar to the standard library's prelude in that you'll
|
||||
//! almost always want to import its entire contents, but unlike the standard
|
||||
//! library's prelude you'll have to do so manually:
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio::prelude::*;
|
||||
//! ```
|
||||
//!
|
||||
//! The prelude may grow over time as additional items see ubiquitous use.
|
||||
|
||||
pub use tokio_io::{
|
||||
AsyncRead,
|
||||
AsyncWrite,
|
||||
};
|
||||
|
||||
pub use util::{
|
||||
FutureExt,
|
||||
StreamExt,
|
||||
};
|
||||
|
||||
pub use ::std::io::{
|
||||
Read,
|
||||
Write,
|
||||
};
|
||||
|
||||
pub use futures::{
|
||||
Future,
|
||||
future,
|
||||
Stream,
|
||||
stream,
|
||||
Sink,
|
||||
IntoFuture,
|
||||
Async,
|
||||
AsyncSink,
|
||||
Poll,
|
||||
task,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
#[doc(inline)]
|
||||
pub use tokio_async_await::{
|
||||
io::{
|
||||
AsyncReadExt,
|
||||
AsyncWriteExt,
|
||||
},
|
||||
sink::{
|
||||
SinkExt,
|
||||
},
|
||||
stream::{
|
||||
StreamExt as StreamAsyncExt,
|
||||
},
|
||||
};
|
||||
+263
-43
@@ -3,10 +3,12 @@ use runtime::{Inner, Runtime};
|
||||
use reactor::Reactor;
|
||||
|
||||
use std::io;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use num_cpus;
|
||||
use tokio_reactor;
|
||||
use tokio_threadpool::Builder as ThreadPoolBuilder;
|
||||
use tokio_threadpool::park::DefaultPark;
|
||||
use tokio_timer::clock::{self, Clock};
|
||||
use tokio_timer::timer::{self, Timer};
|
||||
|
||||
@@ -26,30 +28,37 @@ use tokio_timer::timer::{self, Timer};
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate tokio_threadpool;
|
||||
/// # use tokio::runtime::Builder;
|
||||
/// extern crate tokio;
|
||||
/// extern crate tokio_timer;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// // create and configure ThreadPool
|
||||
/// let mut threadpool_builder = tokio_threadpool::Builder::new();
|
||||
/// threadpool_builder
|
||||
/// .name_prefix("my-runtime-worker-")
|
||||
/// .pool_size(4);
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// // build Runtime
|
||||
/// let runtime = Builder::new()
|
||||
/// .threadpool_builder(threadpool_builder)
|
||||
/// .build();
|
||||
/// // ... call runtime.run(...)
|
||||
/// # let _ = runtime;
|
||||
/// # }
|
||||
/// use tokio::runtime::Builder;
|
||||
/// use tokio_timer::clock::Clock;
|
||||
///
|
||||
/// fn main() {
|
||||
/// // build Runtime
|
||||
/// let mut runtime = Builder::new()
|
||||
/// .blocking_threads(4)
|
||||
/// .clock(Clock::system())
|
||||
/// .core_threads(4)
|
||||
/// .keep_alive(Some(Duration::from_secs(60)))
|
||||
/// .name_prefix("my-custom-name-")
|
||||
/// .stack_size(3 * 1024 * 1024)
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // use runtime ...
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
/// Thread pool specific builder
|
||||
threadpool_builder: ThreadPoolBuilder,
|
||||
|
||||
/// The number of worker threads
|
||||
core_threads: usize,
|
||||
|
||||
/// The clock to use
|
||||
clock: Clock,
|
||||
}
|
||||
@@ -60,11 +69,15 @@ impl Builder {
|
||||
///
|
||||
/// Configuration methods can be chained on the return value.
|
||||
pub fn new() -> Builder {
|
||||
let core_threads = num_cpus::get().max(1);
|
||||
|
||||
let mut threadpool_builder = ThreadPoolBuilder::new();
|
||||
threadpool_builder.name_prefix("tokio-runtime-worker-");
|
||||
threadpool_builder.pool_size(core_threads);
|
||||
|
||||
Builder {
|
||||
threadpool_builder,
|
||||
core_threads,
|
||||
clock: Clock::new(),
|
||||
}
|
||||
}
|
||||
@@ -76,11 +89,210 @@ impl Builder {
|
||||
}
|
||||
|
||||
/// Set builder to set up the thread pool instance.
|
||||
#[deprecated(
|
||||
since="0.1.9",
|
||||
note="use the `core_threads`, `blocking_threads`, `name_prefix`, \
|
||||
`keep_alive`, and `stack_size` functions on `runtime::Builder`, \
|
||||
instead")]
|
||||
#[doc(hidden)]
|
||||
pub fn threadpool_builder(&mut self, val: ThreadPoolBuilder) -> &mut Self {
|
||||
self.threadpool_builder = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the maximum number of worker threads for the `Runtime`'s thread pool.
|
||||
///
|
||||
/// This must be a number between 1 and 32,768 though it is advised to keep
|
||||
/// this value on the smaller side.
|
||||
///
|
||||
/// The default value is the number of cores available to the system.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let mut rt = runtime::Builder::new()
|
||||
/// .core_threads(4)
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn core_threads(&mut self, val: usize) -> &mut Self {
|
||||
self.core_threads = val;
|
||||
self.threadpool_builder.pool_size(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the maximum number of concurrent blocking sections in the `Runtime`'s
|
||||
/// thread pool.
|
||||
///
|
||||
/// When the maximum concurrent `blocking` calls is reached, any further
|
||||
/// calls to `blocking` will return `NotReady` and the task is notified once
|
||||
/// previously in-flight calls to `blocking` return.
|
||||
///
|
||||
/// This must be a number between 1 and 32,768 though it is advised to keep
|
||||
/// this value on the smaller side.
|
||||
///
|
||||
/// The default value is 100.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let mut rt = runtime::Builder::new()
|
||||
/// .blocking_threads(200)
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn blocking_threads(&mut self, val: usize) -> &mut Self {
|
||||
self.threadpool_builder.max_blocking(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the worker thread keep alive duration for threads in the `Runtime`'s
|
||||
/// thread pool.
|
||||
///
|
||||
/// If set, a worker thread will wait for up to the specified duration for
|
||||
/// work, at which point the thread will shutdown. When work becomes
|
||||
/// available, a new thread will eventually be spawned to replace the one
|
||||
/// that shut down.
|
||||
///
|
||||
/// When the value is `None`, the thread will wait for work forever.
|
||||
///
|
||||
/// The default value is `None`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let mut rt = runtime::Builder::new()
|
||||
/// .keep_alive(Some(Duration::from_secs(30)))
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn keep_alive(&mut self, val: Option<Duration>) -> &mut Self {
|
||||
self.threadpool_builder.keep_alive(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set name prefix of threads spawned by the `Runtime`'s thread pool.
|
||||
///
|
||||
/// Thread name prefix is used for generating thread names. For example, if
|
||||
/// prefix is `my-pool-`, then threads in the pool will get names like
|
||||
/// `my-pool-1` etc.
|
||||
///
|
||||
/// The default prefix is "tokio-runtime-worker-".
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let mut rt = runtime::Builder::new()
|
||||
/// .name_prefix("my-pool-")
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn name_prefix<S: Into<String>>(&mut self, val: S) -> &mut Self {
|
||||
self.threadpool_builder.name_prefix(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the stack size (in bytes) for worker threads.
|
||||
///
|
||||
/// The actual stack size may be greater than this value if the platform
|
||||
/// specifies minimal stack size.
|
||||
///
|
||||
/// The default stack size for spawned threads is 2 MiB, though this
|
||||
/// particular stack size is subject to change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let mut rt = runtime::Builder::new()
|
||||
/// .stack_size(32 * 1024)
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn stack_size(&mut self, val: usize) -> &mut Self {
|
||||
self.threadpool_builder.stack_size(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute function `f` after each thread is started but before it starts
|
||||
/// doing work.
|
||||
///
|
||||
/// This is intended for bookkeeping and monitoring use cases.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let thread_pool = runtime::Builder::new()
|
||||
/// .after_start(|| {
|
||||
/// println!("thread started");
|
||||
/// })
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn after_start<F>(&mut self, f: F) -> &mut Self
|
||||
where F: Fn() + Send + Sync + 'static
|
||||
{
|
||||
self.threadpool_builder.after_start(f);
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute function `f` before each thread stops.
|
||||
///
|
||||
/// This is intended for bookkeeping and monitoring use cases.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio::runtime;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// let thread_pool = runtime::Builder::new()
|
||||
/// .before_stop(|| {
|
||||
/// println!("thread stopping");
|
||||
/// })
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn before_stop<F>(&mut self, f: F) -> &mut Self
|
||||
where F: Fn() + Send + Sync + 'static
|
||||
{
|
||||
self.threadpool_builder.before_stop(f);
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the configured `Runtime`.
|
||||
///
|
||||
/// The returned `ThreadPool` instance is ready to spawn tasks.
|
||||
@@ -97,50 +309,58 @@ impl Builder {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn build(&mut self) -> io::Result<Runtime> {
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
// TODO(stjepang): Once we remove the `threadpool_builder` method, remove this line too.
|
||||
self.threadpool_builder.pool_size(self.core_threads);
|
||||
|
||||
let mut reactor_handles = Vec::new();
|
||||
let mut timer_handles = Vec::new();
|
||||
let mut timers = Vec::new();
|
||||
|
||||
for _ in 0..self.core_threads {
|
||||
// Create a new reactor.
|
||||
let reactor = Reactor::new()?;
|
||||
reactor_handles.push(reactor.handle());
|
||||
|
||||
// Create a new timer.
|
||||
let timer = Timer::new_with_now(reactor, self.clock.clone());
|
||||
timer_handles.push(timer.handle());
|
||||
timers.push(Mutex::new(Some(timer)));
|
||||
}
|
||||
|
||||
// Get a handle to the clock for the runtime.
|
||||
let clock1 = self.clock.clone();
|
||||
let clock2 = clock1.clone();
|
||||
|
||||
let timers = Arc::new(Mutex::new(HashMap::<_, timer::Handle>::new()));
|
||||
let t1 = timers.clone();
|
||||
|
||||
// Spawn a reactor on a background thread.
|
||||
let reactor = Reactor::new()?.background()?;
|
||||
|
||||
// Get a handle to the reactor.
|
||||
let reactor_handle = reactor.handle().clone();
|
||||
let clock = self.clock.clone();
|
||||
|
||||
let pool = self.threadpool_builder
|
||||
.around_worker(move |w, enter| {
|
||||
let timer_handle = t1.lock().unwrap()
|
||||
.get(w.id()).unwrap()
|
||||
.clone();
|
||||
let index = w.id().to_usize();
|
||||
|
||||
tokio_reactor::with_default(&reactor_handle, enter, |enter| {
|
||||
clock::with_default(&clock1, enter, |enter| {
|
||||
timer::with_default(&timer_handle, enter, |_| {
|
||||
tokio_reactor::with_default(&reactor_handles[index], enter, |enter| {
|
||||
clock::with_default(&clock, enter, |enter| {
|
||||
timer::with_default(&timer_handles[index], enter, |_| {
|
||||
w.run();
|
||||
});
|
||||
})
|
||||
});
|
||||
})
|
||||
.custom_park(move |worker_id| {
|
||||
// Create a new timer
|
||||
let timer = Timer::new_with_now(DefaultPark::new(), clock2.clone());
|
||||
let index = worker_id.to_usize();
|
||||
|
||||
timers.lock().unwrap()
|
||||
.insert(worker_id.clone(), timer.handle());
|
||||
|
||||
timer
|
||||
timers[index]
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap()
|
||||
})
|
||||
.build();
|
||||
|
||||
// To support deprecated `reactor()` function
|
||||
let reactor = Reactor::new()?;
|
||||
let reactor_handle = reactor.handle();
|
||||
|
||||
Ok(Runtime {
|
||||
inner: Some(Inner {
|
||||
reactor,
|
||||
reactor_handle,
|
||||
reactor: Mutex::new(Some(reactor)),
|
||||
pool,
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -71,6 +71,8 @@ mod runtime;
|
||||
|
||||
pub use self::builder::Builder;
|
||||
pub use self::runtime::{Runtime, Handle};
|
||||
pub use tokio_current_thread::spawn;
|
||||
pub use tokio_current_thread::TaskExecutor;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use tokio_timer::clock::{self, Clock};
|
||||
use tokio_timer::timer::{self, Timer};
|
||||
use tokio_executor;
|
||||
|
||||
use futures::Future;
|
||||
use futures::{future, Future};
|
||||
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
@@ -42,6 +42,38 @@ impl Handle {
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static {
|
||||
self.0.spawn(future)
|
||||
}
|
||||
|
||||
/// Provides a best effort **hint** to whether or not `spawn` will succeed.
|
||||
///
|
||||
/// This function may return both false positives **and** false negatives.
|
||||
/// If `status` returns `Ok`, then a call to `spawn` will *probably*
|
||||
/// succeed, but may fail. If `status` returns `Err`, a call to `spawn` will
|
||||
/// *probably* fail, but may succeed.
|
||||
///
|
||||
/// This allows a caller to avoid creating the task if the call to `spawn`
|
||||
/// has a high likelihood of failing.
|
||||
pub fn status(&self) -> Result<(), tokio_executor::SpawnError> {
|
||||
self.0.status()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> future::Executor<T> for Handle
|
||||
where T: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
fn execute(&self, future: T) -> Result<(), future::ExecuteError<T>> {
|
||||
if let Err(e) = self.status() {
|
||||
let kind = if e.is_at_capacity() {
|
||||
future::ExecuteErrorKind::NoCapacity
|
||||
} else {
|
||||
future::ExecuteErrorKind::Shutdown
|
||||
};
|
||||
|
||||
return Err(future::ExecuteError::new(kind, future));
|
||||
}
|
||||
|
||||
let _ = self.spawn(future);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned by the `run` function.
|
||||
|
||||
+39
-22
@@ -121,10 +121,12 @@ pub use self::builder::Builder;
|
||||
pub use self::shutdown::Shutdown;
|
||||
pub use self::task_executor::TaskExecutor;
|
||||
|
||||
use reactor::{Background, Handle};
|
||||
use reactor::{Handle, Reactor};
|
||||
|
||||
use std::io;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use tokio_executor::enter;
|
||||
use tokio_threadpool as threadpool;
|
||||
|
||||
use futures;
|
||||
@@ -151,8 +153,11 @@ pub struct Runtime {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
/// Reactor running on a background thread.
|
||||
reactor: Background,
|
||||
/// A handle to the reactor in the background thread.
|
||||
reactor_handle: Handle,
|
||||
|
||||
// TODO: This should go away in 0.2
|
||||
reactor: Mutex<Option<Reactor>>,
|
||||
|
||||
/// Task execution pool.
|
||||
pool: threadpool::ThreadPool,
|
||||
@@ -208,9 +213,13 @@ struct Inner {
|
||||
pub fn run<F>(future: F)
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
// Check enter before creating a new Runtime...
|
||||
let mut entered = enter().expect("nested tokio::run");
|
||||
let mut runtime = Runtime::new().expect("failed to start new Runtime");
|
||||
runtime.spawn(future);
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
entered
|
||||
.block_on(runtime.shutdown_on_idle())
|
||||
.expect("shutdown cannot error")
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
@@ -251,6 +260,7 @@ impl Runtime {
|
||||
#[deprecated(since = "0.1.5", note = "use `reactor` instead")]
|
||||
#[doc(hidden)]
|
||||
pub fn handle(&self) -> &Handle {
|
||||
#[allow(deprecated)]
|
||||
self.reactor()
|
||||
}
|
||||
|
||||
@@ -272,8 +282,16 @@ impl Runtime {
|
||||
///
|
||||
/// // use `reactor_handle`
|
||||
/// ```
|
||||
#[deprecated(since = "0.1.11", note = "there is now a reactor per worker thread")]
|
||||
pub fn reactor(&self) -> &Handle {
|
||||
self.inner().reactor.handle()
|
||||
let mut reactor = self.inner().reactor.lock().unwrap();
|
||||
if let Some(reactor) = reactor.take() {
|
||||
if let Ok(background) = reactor.background() {
|
||||
background.forget();
|
||||
}
|
||||
}
|
||||
|
||||
&self.inner().reactor_handle
|
||||
}
|
||||
|
||||
/// Return a handle to the runtime's executor.
|
||||
@@ -345,7 +363,7 @@ impl Runtime {
|
||||
/// complete, and yielding its resolved result. Any tasks or timers which
|
||||
/// the future spawns internally will be executed on the runtime.
|
||||
///
|
||||
/// This method should not be called from an asynchrounous context.
|
||||
/// This method should not be called from an asynchronous context.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
@@ -357,9 +375,10 @@ impl Runtime {
|
||||
R: Send + 'static,
|
||||
E: Send + 'static,
|
||||
{
|
||||
let mut entered = enter().expect("nested block_on");
|
||||
let (tx, rx) = futures::sync::oneshot::channel();
|
||||
self.spawn(future.then(move |r| tx.send(r).map_err(|_| unreachable!())));
|
||||
rx.wait().unwrap()
|
||||
entered.block_on(rx).unwrap()
|
||||
}
|
||||
|
||||
/// Run a future to completion on the Tokio runtime, then wait for all
|
||||
@@ -370,7 +389,7 @@ impl Runtime {
|
||||
/// its resolved result. Any tasks or timers which the future spawns
|
||||
/// internally will be executed on the runtime and waited for completion.
|
||||
///
|
||||
/// This method should not be called from an asynchrounous context.
|
||||
/// This method should not be called from an asynchronous context.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
@@ -382,9 +401,16 @@ impl Runtime {
|
||||
R: Send + 'static,
|
||||
E: Send + 'static,
|
||||
{
|
||||
let res = self.block_on(future);
|
||||
self.shutdown_on_idle().wait().unwrap();
|
||||
res
|
||||
let mut entered = enter().expect("nested block_on_all");
|
||||
let (tx, rx) = futures::sync::oneshot::channel();
|
||||
self.spawn(future.then(move |r| tx.send(r).map_err(|_| unreachable!())));
|
||||
let block = rx
|
||||
.map_err(|_| unreachable!())
|
||||
.and_then(move |r| {
|
||||
self.shutdown_on_idle()
|
||||
.map(move |()| r)
|
||||
});
|
||||
entered.block_on(block).unwrap()
|
||||
}
|
||||
|
||||
/// Signals the runtime to shutdown once it becomes idle.
|
||||
@@ -421,16 +447,7 @@ impl Runtime {
|
||||
/// [mod]: index.html
|
||||
pub fn shutdown_on_idle(mut self) -> Shutdown {
|
||||
let inner = self.inner.take().unwrap();
|
||||
|
||||
let inner = Box::new({
|
||||
let pool = inner.pool;
|
||||
let reactor = inner.reactor;
|
||||
|
||||
pool.shutdown_on_idle().and_then(|_| {
|
||||
reactor.shutdown_on_idle()
|
||||
})
|
||||
});
|
||||
|
||||
let inner = inner.pool.shutdown_on_idle();
|
||||
Shutdown { inner }
|
||||
}
|
||||
|
||||
|
||||
+3
-13
@@ -1,4 +1,5 @@
|
||||
use runtime::Inner;
|
||||
use tokio_threadpool as threadpool;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
@@ -6,23 +7,12 @@ use futures::{Future, Poll};
|
||||
|
||||
/// A future that resolves when the Tokio `Runtime` is shut down.
|
||||
pub struct Shutdown {
|
||||
pub(super) inner: Box<Future<Item = (), Error = ()> + Send>,
|
||||
pub(super) inner: threadpool::Shutdown,
|
||||
}
|
||||
|
||||
impl Shutdown {
|
||||
pub(super) fn shutdown_now(inner: Inner) -> Self {
|
||||
let inner = Box::new({
|
||||
let pool = inner.pool;
|
||||
let reactor = inner.reactor;
|
||||
|
||||
pool.shutdown_now().and_then(|_| {
|
||||
reactor.shutdown_now()
|
||||
.then(|_| {
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
let inner = inner.pool.shutdown_now();
|
||||
Shutdown { inner }
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,9 +10,9 @@
|
||||
//! is initialized with a `Duration` and repeatedly yields each time the
|
||||
//! duration elapses.
|
||||
//!
|
||||
//! * [`Timeout`][Timeeout]: Wraps a future or stream, setting an upper bound to the
|
||||
//! * [`Timeout`][Timeout]: Wraps a future or stream, setting an upper bound to the
|
||||
//! amount of time it is allowed to execute. If the future or stream does not
|
||||
//! completee in time, then it is canceled and an error is returned.
|
||||
//! complete in time, then it is canceled and an error is returned.
|
||||
//!
|
||||
//! * [`DelayQueue`]: A queue where items are returned once the requested delay
|
||||
//! has expired.
|
||||
|
||||
@@ -31,6 +31,9 @@ pub trait FutureExt: Future {
|
||||
/// If the future completes before `timeout` then the future will resolve
|
||||
/// with that item. Otherwise the future will resolve to an error.
|
||||
///
|
||||
/// The future is guaranteed to be polled at least once, even if `timeout`
|
||||
/// is set to zero.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -69,3 +72,16 @@ pub trait FutureExt: Future {
|
||||
}
|
||||
|
||||
impl<T: ?Sized> FutureExt for T where T: Future {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use prelude::future;
|
||||
|
||||
#[test]
|
||||
fn timeout_polls_at_least_once() {
|
||||
let base_future = future::result::<(), ()>(Ok(()));
|
||||
let timeouted_future = base_future.timeout(Duration::new(0, 0));
|
||||
assert!(timeouted_future.wait().is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -1,10 +1,14 @@
|
||||
//! Utilities for working with Tokio.
|
||||
//!
|
||||
//! This module contains utilities that are useful for working with Tokio.
|
||||
//! Currently, this only includes [`FutureExt`], but this may grow over time.
|
||||
//! Currently, this only includes [`FutureExt`] and [`StreamExt`], but this
|
||||
//! may grow over time.
|
||||
//!
|
||||
//! [`FutureExt`]: trait.FutureExt.html
|
||||
//! [`StreamExt`]: trait.StreamExt.html
|
||||
|
||||
mod future;
|
||||
mod stream;
|
||||
|
||||
pub use self::future::FutureExt;
|
||||
pub use self::stream::StreamExt;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
use tokio_timer::{
|
||||
throttle::Throttle,
|
||||
Timeout,
|
||||
};
|
||||
|
||||
use futures::Stream;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
/// An extension trait for `Stream` that provides a variety of convenient
|
||||
/// combinator functions.
|
||||
///
|
||||
/// Currently, there only is a [`timeout`] function, but this will increase
|
||||
/// over time.
|
||||
///
|
||||
/// Users are not expected to implement this trait. All types that implement
|
||||
/// `Stream` already implement `StreamExt`.
|
||||
///
|
||||
/// This trait can be imported directly or via the Tokio prelude: `use
|
||||
/// tokio::prelude::*`.
|
||||
///
|
||||
/// [`timeout`]: #method.timeout
|
||||
pub trait StreamExt: Stream {
|
||||
/// Throttle down the stream by enforcing a fixed delay between items.
|
||||
///
|
||||
/// Errors are also delayed.
|
||||
fn throttle(self, duration: Duration) -> Throttle<Self>
|
||||
where Self: Sized
|
||||
{
|
||||
Throttle::new(self, duration)
|
||||
}
|
||||
|
||||
/// Creates a new stream which allows `self` until `timeout`.
|
||||
///
|
||||
/// This combinator creates a new stream which wraps the receiving stream
|
||||
/// with a timeout. For each item, the returned stream is allowed to execute
|
||||
/// until it completes or `timeout` has elapsed, whichever happens first.
|
||||
///
|
||||
/// If an item completes before `timeout` then the stream will yield
|
||||
/// with that item. Otherwise the stream will yield to an error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// use tokio::prelude::*;
|
||||
/// use std::time::Duration;
|
||||
/// # use futures::future::{self, FutureResult};
|
||||
///
|
||||
/// # fn long_future() -> FutureResult<(), ()> {
|
||||
/// # future::ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let stream = long_future()
|
||||
/// .into_stream()
|
||||
/// .timeout(Duration::from_secs(1))
|
||||
/// .for_each(|i| future::ok(println!("item = {:?}", i)))
|
||||
/// .map_err(|e| println!("error = {:?}", e));
|
||||
///
|
||||
/// tokio::run(stream);
|
||||
/// # }
|
||||
/// ```
|
||||
fn timeout(self, timeout: Duration) -> Timeout<Self>
|
||||
where Self: Sized,
|
||||
{
|
||||
Timeout::new(self, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> StreamExt for T where T: Stream {}
|
||||
@@ -0,0 +1,580 @@
|
||||
extern crate tokio;
|
||||
extern crate futures;
|
||||
extern crate bytes;
|
||||
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio::codec::*;
|
||||
|
||||
use bytes::{Bytes, BytesMut, BufMut};
|
||||
use futures::{Stream, Sink, Poll};
|
||||
use futures::Async::*;
|
||||
|
||||
use std::io;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
macro_rules! mock {
|
||||
($($x:expr,)*) => {{
|
||||
let mut v = VecDeque::new();
|
||||
v.extend(vec![$($x),*]);
|
||||
Mock { calls: v }
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn read_empty_io_yields_nothing() {
|
||||
let mut io = FramedRead::new(mock!(), LengthDelimitedCodec::new());
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_one_packet() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_one_packet_little_endian() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.little_endian()
|
||||
.new_read(mock! {
|
||||
Ok(b"\x09\x00\x00\x00abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_one_packet_native_endian() {
|
||||
let data = if cfg!(target_endian = "big") {
|
||||
b"\x00\x00\x00\x09abcdefghi"
|
||||
} else {
|
||||
b"\x09\x00\x00\x00abcdefghi"
|
||||
};
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.native_endian()
|
||||
.new_read(mock! {
|
||||
Ok(data[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_multi_frame_one_packet() {
|
||||
let mut data: Vec<u8> = vec![];
|
||||
data.extend_from_slice(b"\x00\x00\x00\x09abcdefghi");
|
||||
data.extend_from_slice(b"\x00\x00\x00\x03123");
|
||||
data.extend_from_slice(b"\x00\x00\x00\x0bhello world");
|
||||
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(data.into()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_multi_packet() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_multi_frame_multi_packet() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x0312"[..].into()),
|
||||
Ok(b"3\x00\x00\x00\x0bhello world"[..].into()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_multi_packet_wait() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
Err(would_block()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_multi_frame_multi_packet_wait() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x00\x00\x0312"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"3\x00\x00\x00\x0bhello world"[..].into()),
|
||||
Err(would_block()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_incomplete_head() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert!(io.poll().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_incomplete_head_multi() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Err(would_block()),
|
||||
Ok(b"\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert!(io.poll().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_incomplete_payload() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09ab"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"cd"[..].into()),
|
||||
Err(would_block()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert!(io.poll().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_max_frame_len() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.max_frame_length(5)
|
||||
.new_read(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap_err().kind(), io::ErrorKind::InvalidData);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_update_max_frame_len_at_rest() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.new_read(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
io.decoder_mut().set_max_frame_length(5);
|
||||
assert_eq!(io.poll().unwrap_err().kind(), io::ErrorKind::InvalidData);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_update_max_frame_len_in_flight() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.new_read(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcd"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"efghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
io.decoder_mut().set_max_frame_length(5);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap_err().kind(), io::ErrorKind::InvalidData);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_one_byte_length_field() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.length_field_length(1)
|
||||
.new_read(mock! {
|
||||
Ok(b"\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_header_offset() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.length_field_length(2)
|
||||
.length_field_offset(4)
|
||||
.new_read(mock! {
|
||||
Ok(b"zzzz\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_multi_frame_one_packet_skip_none_adjusted() {
|
||||
let mut data: Vec<u8> = vec![];
|
||||
data.extend_from_slice(b"xx\x00\x09abcdefghi");
|
||||
data.extend_from_slice(b"yy\x00\x03123");
|
||||
data.extend_from_slice(b"zz\x00\x0bhello world");
|
||||
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.length_field_length(2)
|
||||
.length_field_offset(2)
|
||||
.num_skip(0)
|
||||
.length_adjustment(4)
|
||||
.new_read(mock! {
|
||||
Ok(data.into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"xx\x00\x09abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"yy\x00\x03123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"zz\x00\x0bhello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_multi_frame_one_packet_length_includes_head() {
|
||||
let mut data: Vec<u8> = vec![];
|
||||
data.extend_from_slice(b"\x00\x0babcdefghi");
|
||||
data.extend_from_slice(b"\x00\x05123");
|
||||
data.extend_from_slice(b"\x00\x0dhello world");
|
||||
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.length_field_length(2)
|
||||
.length_adjustment(-2)
|
||||
.new_read(mock! {
|
||||
Ok(data.into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_length_adjusted() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.length_adjustment(-2)
|
||||
.new_write(mock! {
|
||||
Ok(b"\x00\x00\x00\x0b"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
assert!(io.start_send(Bytes::from("abcdefghi")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_nothing_yields_nothing() {
|
||||
let mut io = FramedWrite::new(
|
||||
mock!(),
|
||||
LengthDelimitedCodec::new()
|
||||
);
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_one_packet() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdefghi")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_multi_frame_one_packet() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x03"[..].into()),
|
||||
Ok(b"123"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x0b"[..].into()),
|
||||
Ok(b"hello world"[..].into()),
|
||||
Ok(Flush),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdefghi")).unwrap().is_ready());
|
||||
assert!(io.start_send(Bytes::from("123")).unwrap().is_ready());
|
||||
assert!(io.start_send(Bytes::from("hello world")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_multi_frame_multi_packet() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
Ok(b"\x00\x00\x00\x03"[..].into()),
|
||||
Ok(b"123"[..].into()),
|
||||
Ok(Flush),
|
||||
Ok(b"\x00\x00\x00\x0b"[..].into()),
|
||||
Ok(b"hello world"[..].into()),
|
||||
Ok(Flush),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdefghi")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.start_send(Bytes::from("123")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.start_send(Bytes::from("hello world")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_would_block() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
}, LengthDelimitedCodec::new());
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdefghi")).unwrap().is_ready());
|
||||
assert!(!io.poll_complete().unwrap().is_ready());
|
||||
assert!(!io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_little_endian() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.little_endian()
|
||||
.new_write(mock! {
|
||||
Ok(b"\x09\x00\x00\x00"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdefghi")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_with_short_length_field() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.length_field_length(1)
|
||||
.new_write(mock! {
|
||||
Ok(b"\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdefghi")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_max_frame_len() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.max_frame_length(5)
|
||||
.new_write(mock! { });
|
||||
|
||||
assert_eq!(io.start_send(Bytes::from("abcdef")).unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_update_max_frame_len_at_rest() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.new_write(mock! {
|
||||
Ok(b"\x00\x00\x00\x06"[..].into()),
|
||||
Ok(b"abcdef"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdef")).unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
io.encoder_mut().set_max_frame_length(5);
|
||||
assert_eq!(io.start_send(Bytes::from("abcdef")).unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_update_max_frame_len_in_flight() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.new_write(mock! {
|
||||
Ok(b"\x00\x00\x00\x06"[..].into()),
|
||||
Ok(b"ab"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"cdef"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdef")).unwrap().is_ready());
|
||||
assert!(!io.poll_complete().unwrap().is_ready());
|
||||
io.encoder_mut().set_max_frame_length(5);
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert_eq!(io.start_send(Bytes::from("abcdef")).unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_zero() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.new_write(mock! { });
|
||||
|
||||
assert!(io.start_send(Bytes::from("abcdef")).unwrap().is_ready());
|
||||
assert_eq!(io.poll_complete().unwrap_err().kind(), io::ErrorKind::WriteZero);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_overflow() {
|
||||
// Test reproducing tokio-rs/tokio#681.
|
||||
let mut codec = length_delimited::Builder::new().new_codec();
|
||||
let mut buf = BytesMut::with_capacity(1024);
|
||||
|
||||
// Put some data into the buffer without resizing it to hold more.
|
||||
let some_as = std::iter::repeat(b'a')
|
||||
.take(1024)
|
||||
.collect::<Vec<_>>();
|
||||
buf.put_slice(&some_as[..]);
|
||||
|
||||
// Trying to encode the length header should resize the buffer if it won't fit.
|
||||
codec.encode(Bytes::from("hello"), &mut buf).unwrap();
|
||||
}
|
||||
|
||||
// ===== Test utils =====
|
||||
|
||||
fn would_block() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::WouldBlock, "would block")
|
||||
}
|
||||
|
||||
struct Mock {
|
||||
calls: VecDeque<io::Result<Op>>,
|
||||
}
|
||||
|
||||
enum Op {
|
||||
Data(Vec<u8>),
|
||||
Flush,
|
||||
}
|
||||
|
||||
use self::Op::*;
|
||||
|
||||
impl io::Read for Mock {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(Op::Data(data))) => {
|
||||
debug_assert!(dst.len() >= data.len());
|
||||
dst[..data.len()].copy_from_slice(&data[..]);
|
||||
Ok(data.len())
|
||||
}
|
||||
Some(Ok(_)) => panic!(),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Mock {
|
||||
}
|
||||
|
||||
impl io::Write for Mock {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(Op::Data(data))) => {
|
||||
let len = data.len();
|
||||
assert!(src.len() >= len, "expect={:?}; actual={:?}", data, src);
|
||||
assert_eq!(&data[..], &src[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
Some(Ok(_)) => panic!(),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(Op::Flush)) => {
|
||||
Ok(())
|
||||
}
|
||||
Some(Ok(_)) => panic!(),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Mock {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for Op {
|
||||
fn from(src: &'a [u8]) -> Op {
|
||||
Op::Data(src.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Op {
|
||||
fn from(src: Vec<u8>) -> Op {
|
||||
Op::Data(src)
|
||||
}
|
||||
}
|
||||
+354
-111
@@ -3,7 +3,7 @@ extern crate env_logger;
|
||||
extern crate futures;
|
||||
|
||||
use futures::sync::oneshot;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{Arc, Mutex, atomic};
|
||||
use std::thread;
|
||||
use tokio::io;
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
@@ -11,6 +11,11 @@ use tokio::prelude::future::lazy;
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
// this import is used in all child modules that have it in scope
|
||||
// from importing super::*, but the compiler doesn't realise that
|
||||
// and warns about it.
|
||||
pub use futures::future::Executor;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
@@ -72,78 +77,130 @@ fn runtime_single_threaded_block_on() {
|
||||
tokio::runtime::current_thread::block_on_all(create_client_server_future()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_single_threaded_block_on_all() {
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
mod runtime_single_threaded_block_on_all {
|
||||
use super::*;
|
||||
|
||||
let msg = tokio::runtime::current_thread::block_on_all(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
fn test<F>(spawn: F)
|
||||
where
|
||||
F: Fn(Box<Future<Item=(), Error=()> + Send>),
|
||||
{
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
// Spawn!
|
||||
tokio::spawn(lazy(move || {
|
||||
let msg = tokio::runtime::current_thread::block_on_all(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
|
||||
Ok::<_, ()>("hello")
|
||||
})).unwrap();
|
||||
// Spawn!
|
||||
spawn(Box::new(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
})));
|
||||
|
||||
assert_eq!(2, *cnt.lock().unwrap());
|
||||
assert_eq!(msg, "hello");
|
||||
Ok::<_, ()>("hello")
|
||||
})).unwrap();
|
||||
|
||||
assert_eq!(2, *cnt.lock().unwrap());
|
||||
assert_eq!(msg, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
test(|f| { tokio::spawn(f); })
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|f| {
|
||||
tokio::executor::DefaultExecutor::current()
|
||||
.execute(f)
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_single_threaded_racy_spawn() {
|
||||
let (trigger, exit) = futures::sync::oneshot::channel();
|
||||
let (handle_tx, handle_rx) = ::std::sync::mpsc::channel();
|
||||
let jh = ::std::thread::spawn(move || {
|
||||
let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
|
||||
handle_tx.send(rt.handle()).unwrap();
|
||||
mod runtime_single_threaded_racy {
|
||||
use super::*;
|
||||
fn test<F>(spawn: F)
|
||||
where
|
||||
F: Fn(
|
||||
tokio::runtime::current_thread::Handle,
|
||||
Box<Future<Item=(), Error=()> + Send>,
|
||||
),
|
||||
{
|
||||
let (trigger, exit) = futures::sync::oneshot::channel();
|
||||
let (handle_tx, handle_rx) = ::std::sync::mpsc::channel();
|
||||
let jh = ::std::thread::spawn(move || {
|
||||
let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
|
||||
handle_tx.send(rt.handle()).unwrap();
|
||||
|
||||
// don't exit until we are told to
|
||||
rt.block_on(exit.map_err(|_| ())).unwrap();
|
||||
// don't exit until we are told to
|
||||
rt.block_on(exit.map_err(|_| ())).unwrap();
|
||||
|
||||
// run until all spawned futures (incl. the "exit" signal future) have completed.
|
||||
rt.run().unwrap();
|
||||
});
|
||||
// run until all spawned futures (incl. the "exit" signal future) have completed.
|
||||
rt.run().unwrap();
|
||||
});
|
||||
|
||||
let (tx, rx) = futures::sync::oneshot::channel();
|
||||
let (tx, rx) = futures::sync::oneshot::channel();
|
||||
|
||||
let handle = handle_rx.recv().unwrap();
|
||||
handle
|
||||
.spawn(futures::future::lazy(move || {
|
||||
let handle = handle_rx.recv().unwrap();
|
||||
spawn(handle, Box::new(futures::future::lazy(move || {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
}))
|
||||
.unwrap();
|
||||
})));
|
||||
|
||||
// signal runtime thread to exit
|
||||
trigger.send(()).unwrap();
|
||||
// signal runtime thread to exit
|
||||
trigger.send(()).unwrap();
|
||||
|
||||
// wait for runtime thread to exit
|
||||
jh.join().unwrap();
|
||||
// wait for runtime thread to exit
|
||||
jh.join().unwrap();
|
||||
|
||||
assert_eq!(rx.wait().unwrap(), ());
|
||||
assert_eq!(rx.wait().unwrap(), ());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
test(|handle, f| { handle.spawn(f).unwrap(); })
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|handle, f| { handle.execute(f).unwrap(); })
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_multi_threaded() {
|
||||
let _ = env_logger::try_init();
|
||||
mod runtime_multi_threaded {
|
||||
use super::*;
|
||||
fn test<F>(spawn: F)
|
||||
where
|
||||
F: Fn(&mut Runtime) + Send + 'static,
|
||||
{
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let mut runtime = tokio::runtime::Builder::new()
|
||||
.build()
|
||||
.unwrap();
|
||||
runtime.spawn(create_client_server_future());
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
let mut runtime = tokio::runtime::Builder::new()
|
||||
.build()
|
||||
.unwrap();
|
||||
spawn(&mut runtime);
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
test(|rt| { rt.spawn(create_client_server_future()); });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|rt| { rt.executor().execute(create_client_server_future()).unwrap(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn block_on_timer() {
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -161,35 +218,57 @@ fn block_on_timer() {
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_from_block_on() {
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
mod from_block_on {
|
||||
use super::*;
|
||||
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
let msg = runtime
|
||||
.block_on(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
fn test<F>(spawn: F)
|
||||
where
|
||||
F: Fn(Box<Future<Item=(), Error=()> + Send>) + Send + 'static,
|
||||
{
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
// Spawn!
|
||||
tokio::spawn(lazy(move || {
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
let msg = runtime
|
||||
.block_on(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
|
||||
Ok::<_, ()>("hello")
|
||||
}))
|
||||
.unwrap();
|
||||
// Spawn!
|
||||
spawn(Box::new(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
})));
|
||||
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
assert_eq!(2, *cnt.lock().unwrap());
|
||||
assert_eq!(msg, "hello");
|
||||
Ok::<_, ()>("hello")
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
assert_eq!(2, *cnt.lock().unwrap());
|
||||
assert_eq!(msg, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|f| {
|
||||
tokio::executor::DefaultExecutor::current()
|
||||
.execute(f)
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
test(|f| {
|
||||
tokio::spawn(f);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -220,54 +299,218 @@ fn block_waits() {
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_many() {
|
||||
mod many {
|
||||
use super::*;
|
||||
|
||||
const ITER: usize = 200;
|
||||
fn test<F>(spawn: F)
|
||||
where
|
||||
F: Fn(&mut Runtime, Box<Future<Item=(), Error=()> + Send>),
|
||||
{
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
|
||||
for _ in 0..ITER {
|
||||
let c = cnt.clone();
|
||||
runtime.spawn(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
}
|
||||
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
assert_eq!(ITER, *cnt.lock().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_from_block_on_all() {
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let runtime = Runtime::new().unwrap();
|
||||
let msg = runtime
|
||||
.block_on_all(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
|
||||
// Spawn!
|
||||
tokio::spawn(lazy(move || {
|
||||
for _ in 0..ITER {
|
||||
let c = cnt.clone();
|
||||
spawn(&mut runtime, Box::new(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
})));
|
||||
}
|
||||
|
||||
Ok::<_, ()>("hello")
|
||||
}))
|
||||
.unwrap();
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
assert_eq!(ITER, *cnt.lock().unwrap());
|
||||
}
|
||||
|
||||
assert_eq!(2, *cnt.lock().unwrap());
|
||||
assert_eq!(msg, "hello");
|
||||
#[test]
|
||||
fn spawn() {
|
||||
test(|rt, f| { rt.spawn(f); })
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|rt, f| {
|
||||
rt.executor()
|
||||
.execute(f)
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mod from_block_on_all {
|
||||
use super::*;
|
||||
|
||||
fn test<F>(spawn: F)
|
||||
where
|
||||
F: Fn(Box<Future<Item=(), Error=()> + Send>) + Send + 'static,
|
||||
{
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let runtime = Runtime::new().unwrap();
|
||||
let msg = runtime
|
||||
.block_on_all(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
|
||||
// Spawn!
|
||||
spawn(Box::new(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
})));
|
||||
|
||||
Ok::<_, ()>("hello")
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(2, *cnt.lock().unwrap());
|
||||
assert_eq!(msg, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|f| {
|
||||
tokio::executor::DefaultExecutor::current()
|
||||
.execute(f)
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
test(|f| { tokio::spawn(f); })
|
||||
}
|
||||
}
|
||||
|
||||
mod nested_enter {
|
||||
use super::*;
|
||||
use tokio::runtime::current_thread;
|
||||
use std::panic;
|
||||
|
||||
fn test<F1, F2>(first: F1, nested: F2)
|
||||
where
|
||||
F1: Fn(Box<Future<Item=(), Error=()> + Send>) + Send + 'static,
|
||||
F2: Fn(Box<Future<Item=(), Error=()> + Send>) + panic::UnwindSafe + Send + 'static,
|
||||
{
|
||||
let panicked = Arc::new(Mutex::new(false));
|
||||
let panicked2 = panicked.clone();
|
||||
|
||||
// Since this is testing panics in other threads, printing about panics
|
||||
// is noisy and can give the impression that the test is ignoring panics.
|
||||
//
|
||||
// It *is* ignoring them, but on purpose.
|
||||
let prev_hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|info| {
|
||||
let s = info.to_string();
|
||||
if s.starts_with("panicked at 'nested ")
|
||||
|| s.starts_with("panicked at 'Multiple executors at once")
|
||||
{
|
||||
// expected, noop
|
||||
} else {
|
||||
println!("{}", s);
|
||||
}
|
||||
}));
|
||||
|
||||
first(Box::new(lazy(move || {
|
||||
panic::catch_unwind(move || {
|
||||
nested(Box::new(lazy(|| { Ok::<(), ()>(()) })))
|
||||
}).expect_err("nested should panic");
|
||||
*panicked2.lock().unwrap() = true;
|
||||
Ok::<(), ()>(())
|
||||
})));
|
||||
|
||||
panic::set_hook(prev_hook);
|
||||
|
||||
assert!(*panicked.lock().unwrap(), "nested call should have panicked");
|
||||
}
|
||||
|
||||
fn threadpool_new() -> Runtime {
|
||||
Runtime::new().expect("rt new")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_in_run() {
|
||||
test(tokio::run, tokio::run);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn threadpool_block_on_in_run() {
|
||||
test(tokio::run, |fut| {
|
||||
let mut rt = threadpool_new();
|
||||
rt.block_on(fut).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn threadpool_block_on_all_in_run() {
|
||||
test(tokio::run, |fut| {
|
||||
let rt = threadpool_new();
|
||||
rt.block_on_all(fut).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_thread_block_on_all_in_run() {
|
||||
test(tokio::run, |fut| {
|
||||
current_thread::block_on_all(fut).unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_reactor_handle() {
|
||||
#![allow(deprecated)]
|
||||
|
||||
use futures::Stream;
|
||||
use std::net::{
|
||||
TcpListener as StdListener,
|
||||
TcpStream as StdStream,
|
||||
};
|
||||
|
||||
let rt = Runtime::new().unwrap();
|
||||
|
||||
let std_listener = StdListener::bind("127.0.0.1:0").unwrap();
|
||||
let tk_listener = TcpListener::from_std(std_listener, rt.handle()).unwrap();
|
||||
|
||||
let addr = tk_listener.local_addr().unwrap();
|
||||
|
||||
// Spawn a thread since we are avoiding the runtime
|
||||
let th = thread::spawn(|| {
|
||||
for _ in tk_listener.incoming().take(1).wait() {
|
||||
}
|
||||
});
|
||||
|
||||
let _ = StdStream::connect(&addr).unwrap();
|
||||
|
||||
th.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn after_start_and_before_stop_is_called() {
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let after_start = Arc::new(atomic::AtomicUsize::new(0));
|
||||
let before_stop = Arc::new(atomic::AtomicUsize::new(0));
|
||||
|
||||
let after_inner = after_start.clone();
|
||||
let before_inner = before_stop.clone();
|
||||
let runtime = tokio::runtime::Builder::new()
|
||||
.after_start(move || { after_inner.clone().fetch_add(1, atomic::Ordering::Relaxed); })
|
||||
.before_stop(move || { before_inner.clone().fetch_add(1, atomic::Ordering::Relaxed); })
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
runtime.block_on_all(create_client_server_future()).unwrap();
|
||||
|
||||
assert!(after_start.load(atomic::Ordering::Relaxed) > 0);
|
||||
assert!(before_stop.load(atomic::Ordering::Relaxed) > 0);
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ fn deadline() {
|
||||
let when = Instant::now() + Duration::from_millis(20);
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
#[allow(deprecated)]
|
||||
tokio::run({
|
||||
future::empty::<(), ()>()
|
||||
.deadline(when)
|
||||
@@ -92,3 +93,24 @@ fn deadline() {
|
||||
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timeout() {
|
||||
use futures::future;
|
||||
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
tokio::run({
|
||||
future::empty::<(), ()>()
|
||||
.timeout(Duration::from_millis(20))
|
||||
.then(move |res| {
|
||||
assert!(res.is_err());
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "tokio-async-await"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
version = "0.1.4"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-async-await/0.1.3"
|
||||
description = """
|
||||
Experimental async/await support for Tokio
|
||||
"""
|
||||
categories = ["asynchronous"]
|
||||
|
||||
[features]
|
||||
# This feature comes with no promise of stability. Things will
|
||||
# break with each patch release. Use at your own risk.
|
||||
async-await-preview = ["futures/nightly"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.23"
|
||||
tokio-io = { version = "0.1.7", path = "../tokio-io" }
|
||||
|
||||
[dev-dependencies]
|
||||
bytes = "0.4.9"
|
||||
tokio = { version = "0.1.8", path = ".." }
|
||||
# tokio-codec = { version = "0.1.0", path = "../tokio-codec" }
|
||||
hyper = "0.12.8"
|
||||
@@ -0,0 +1,52 @@
|
||||
Copyright (c) 2018 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.
|
||||
|
||||
Copyright (c) 2016 futures-rs authors
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Tokio async/await preview
|
||||
|
||||
This crate provides a preview of Tokio with async / await support. It is a shim
|
||||
layer on top of `tokio`.
|
||||
|
||||
**This crate requires Rust nightly and does not provide API stability
|
||||
guarantees. You are living on the edge here.**
|
||||
|
||||
## Usage
|
||||
|
||||
To use this crate, you need to start with a Rust 2018 edition crate.
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# In the `[packages]` section
|
||||
edition = "2018"
|
||||
|
||||
# In the `[dependencies]` section
|
||||
tokio = {version = "0.1.0", features = ["async-await-preview"]}
|
||||
```
|
||||
|
||||
Then, get started. In your application, add:
|
||||
|
||||
```rust
|
||||
// The nightly features that are commonly needed with async / await
|
||||
#![feature(await_macro, async_await, futures_api)]
|
||||
|
||||
// This pulls in the `tokio-async-await` crate. While Rust 2018 doesn't require
|
||||
// `extern crate`, we need to pull in the macros.
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
|
||||
fn main() {
|
||||
// And we are async...
|
||||
tokio::run_async(async {
|
||||
println!("Hello");
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Because nightly is required, run the app with `cargo +nightly run`
|
||||
|
||||
Check the [examples](examples) directory for more.
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target-dir = "../../target"
|
||||
@@ -0,0 +1,49 @@
|
||||
[package]
|
||||
name = "examples"
|
||||
edition = "2018"
|
||||
version = "0.1.0"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
|
||||
# Break out of the parent workspace
|
||||
[workspace]
|
||||
|
||||
[[bin]]
|
||||
name = "chat"
|
||||
path = "src/chat.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "echo_client"
|
||||
path = "src/echo_client.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "echo_server"
|
||||
path = "src/echo_server.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "hyper"
|
||||
path = "src/hyper.rs"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.1.0", path = "../..", features = ["async-await-preview"] }
|
||||
futures = "0.1.23"
|
||||
bytes = "0.4.9"
|
||||
hyper = "0.12.8"
|
||||
|
||||
# Avoid using crates.io for Tokio dependencies
|
||||
[patch.crates-io]
|
||||
tokio = { path = "../.." }
|
||||
tokio-async-await = { path = "../" }
|
||||
tokio-codec = { path = "../../tokio-codec" }
|
||||
tokio-current-thread = { path = "../../tokio-current-thread" }
|
||||
tokio-executor = { path = "../../tokio-executor" }
|
||||
tokio-fs = { path = "../../tokio-fs" }
|
||||
tokio-io = { path = "../../tokio-io" }
|
||||
tokio-reactor = { path = "../../tokio-reactor" }
|
||||
tokio-signal = { path = "../../tokio-signal" }
|
||||
tokio-tcp = { path = "../../tokio-tcp" }
|
||||
tokio-threadpool = { path = "../../tokio-threadpool" }
|
||||
tokio-timer = { path = "../../tokio-timer" }
|
||||
tokio-tls = { path = "../../tokio-tls" }
|
||||
tokio-udp = { path = "../../tokio-udp" }
|
||||
tokio-uds = { path = "../../tokio-uds" }
|
||||
@@ -0,0 +1,5 @@
|
||||
# Tokio async/await examples
|
||||
|
||||
These are a separate crate in order to work around some cargo bugs. It also
|
||||
allows `[patch]` to be used in `Cargo.toml` to ensure the correct lib versions
|
||||
are being pulled in.
|
||||
@@ -0,0 +1,135 @@
|
||||
#![feature(await_macro, async_await, futures_api)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
extern crate futures; // v0.1
|
||||
|
||||
use tokio::codec::{LinesCodec, Decoder};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
|
||||
use futures::sync::mpsc;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Shorthand for the transmit half of the message channel.
|
||||
type Tx = mpsc::UnboundedSender<String>;
|
||||
|
||||
struct Shared {
|
||||
peers: HashMap<SocketAddr, Tx>,
|
||||
}
|
||||
|
||||
impl Shared {
|
||||
/// Create a new, empty, instance of `Shared`.
|
||||
fn new() -> Self {
|
||||
Shared {
|
||||
peers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn process(stream: TcpStream, state: Arc<Mutex<Shared>>) -> io::Result<()> {
|
||||
let addr = stream.peer_addr().unwrap();
|
||||
let mut lines = LinesCodec::new().framed(stream);
|
||||
|
||||
// Extract the peer's name
|
||||
let name = match await!(lines.next()) {
|
||||
Some(name) => name?,
|
||||
None => {
|
||||
// Disconnected early
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
println!("`{}` is joining the chat", name);
|
||||
|
||||
let (tx, mut rx) = mpsc::unbounded();
|
||||
|
||||
// Register the socket
|
||||
state.lock().unwrap()
|
||||
.peers.insert(addr, tx);
|
||||
|
||||
// Split the `lines` handle into send and recv handles. This allows spawning
|
||||
// separate tasks.
|
||||
let (mut lines_tx, mut lines_rx) = lines.split();
|
||||
|
||||
// Spawn a task that receives all lines broadcasted to us from other peers
|
||||
// and writes it to the client.
|
||||
tokio::spawn_async(async move {
|
||||
while let Some(line) = await!(rx.next()) {
|
||||
let line = line.unwrap();
|
||||
await!(lines_tx.send_async(line));
|
||||
}
|
||||
});
|
||||
|
||||
// Use the current task to read lines from the socket and broadcast them to
|
||||
// other peers.
|
||||
while let Some(message) = await!(lines_rx.next()) {
|
||||
// TODO: Error handling
|
||||
let message = message.unwrap();
|
||||
|
||||
let mut line = name.clone();
|
||||
line.push_str(": ");
|
||||
line.push_str(&message);
|
||||
line.push_str("\r\n");
|
||||
|
||||
let state = state.lock().unwrap();
|
||||
|
||||
for (peer_addr, tx) in &state.peers {
|
||||
if *peer_addr != addr {
|
||||
// TODO: Error handling
|
||||
tx.unbounded_send(line.clone()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the client from the shared state. Doing so will also result in the
|
||||
// tx task to terminate.
|
||||
state.lock().unwrap()
|
||||
.peers.remove(&addr)
|
||||
.expect("bug");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// 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
|
||||
// `state` handle is cloned and passed into the task that processes the
|
||||
// client connection.
|
||||
let state = Arc::new(Mutex::new(Shared::new()));
|
||||
|
||||
let addr = "127.0.0.1:6142".parse().unwrap();
|
||||
|
||||
// Bind a TCP listener to the socket address.
|
||||
//
|
||||
// Note that this is the Tokio TcpListener, which is fully async.
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
|
||||
println!("server running on localhost:6142");
|
||||
|
||||
// Start the Tokio runtime.
|
||||
tokio::run_async(async move {
|
||||
let mut incoming = listener.incoming();
|
||||
|
||||
while let Some(stream) = await!(incoming.next()) {
|
||||
let stream = match stream {
|
||||
Ok(stream) => stream,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let state = state.clone();
|
||||
|
||||
tokio::spawn_async(async move {
|
||||
if let Err(_) = await!(process(stream, state)) {
|
||||
eprintln!("failed to process connection");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#![feature(await_macro, async_await, futures_api)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
const MESSAGES: &[&str] = &[
|
||||
"hello",
|
||||
"world",
|
||||
"one two three",
|
||||
];
|
||||
|
||||
async fn run_client(addr: &SocketAddr) -> io::Result<()> {
|
||||
let mut stream = await!(TcpStream::connect(addr))?;
|
||||
|
||||
// Buffer to read into
|
||||
let mut buf = [0; 128];
|
||||
|
||||
for msg in MESSAGES {
|
||||
println!(" > write = {:?}", msg);
|
||||
|
||||
// Write the message to the server
|
||||
await!(stream.write_all_async(msg.as_bytes()))?;
|
||||
|
||||
// Read the message back from the server
|
||||
await!(stream.read_exact_async(&mut buf[..msg.len()]))?;
|
||||
|
||||
assert_eq!(&buf[..msg.len()], msg.as_bytes());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use std::env;
|
||||
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
// Connect to the echo serveer
|
||||
|
||||
tokio::run_async(async move {
|
||||
match await!(run_client(&addr)) {
|
||||
Ok(_) => println!("done."),
|
||||
Err(e) => eprintln!("echo client failed; error = {:?}", e),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
fn handle(mut stream: TcpStream) {
|
||||
tokio::spawn_async(async move {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
loop {
|
||||
match await!(stream.read_async(&mut buf)).unwrap() {
|
||||
0 => break, // Socket closed
|
||||
n => {
|
||||
// Send the data back
|
||||
await!(stream.write_all_async(&buf[0..n])).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use std::env;
|
||||
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
// Bind the TCP listener
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
tokio::run_async(async {
|
||||
let mut incoming = listener.incoming();
|
||||
|
||||
while let Some(stream) = await!(incoming.next()) {
|
||||
let stream = stream.unwrap();
|
||||
handle(stream);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#![feature(await_macro, async_await, futures_api)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
extern crate hyper;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use hyper::Client;
|
||||
|
||||
use std::time::Duration;
|
||||
use std::str;
|
||||
|
||||
pub fn main() {
|
||||
tokio::run_async(async {
|
||||
let client = Client::new();
|
||||
|
||||
let uri = "http://httpbin.org/ip".parse().unwrap();
|
||||
|
||||
let response = await!({
|
||||
client.get(uri)
|
||||
.timeout(Duration::from_secs(10))
|
||||
}).unwrap();
|
||||
|
||||
println!("Response: {}", response.status());
|
||||
|
||||
let mut body = response.into_body();
|
||||
|
||||
while let Some(chunk) = await!(body.next()) {
|
||||
let chunk = chunk.unwrap();
|
||||
println!("chunk = {}", str::from_utf8(&chunk[..]).unwrap());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/// Wait for a future to complete.
|
||||
#[macro_export]
|
||||
macro_rules! await {
|
||||
($e:expr) => {{
|
||||
use $crate::std_await;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::compat::forward::IntoAwaitable as IntoAwaitableForward;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::compat::backward::IntoAwaitable as IntoAwaitableBackward;
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut e = $e;
|
||||
let e = e.into_awaitable();
|
||||
std_await!(e)
|
||||
}}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::future::{
|
||||
Future as StdFuture,
|
||||
};
|
||||
use std::ptr::NonNull;
|
||||
use std::task::{
|
||||
LocalWaker,
|
||||
Poll as StdPoll,
|
||||
UnsafeWake,
|
||||
Waker,
|
||||
};
|
||||
|
||||
/// Convert an 0.3 `Future` to an 0.1 `Future`.
|
||||
#[derive(Debug)]
|
||||
pub struct Compat<T>(Pin<Box<T>>);
|
||||
|
||||
impl<T> Compat<T> {
|
||||
/// Create a new `Compat` backed by `future`.
|
||||
pub fn new(future: T) -> Compat<T> {
|
||||
Compat(Box::pinned(future))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a value into one that can be used with `await!`.
|
||||
pub trait IntoAwaitable {
|
||||
type Awaitable;
|
||||
|
||||
fn into_awaitable(self) -> Self::Awaitable;
|
||||
}
|
||||
|
||||
impl<T> IntoAwaitable for T
|
||||
where T: StdFuture,
|
||||
{
|
||||
type Awaitable = Self;
|
||||
|
||||
fn into_awaitable(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Item, Error> Future for Compat<T>
|
||||
where T: StdFuture<Output = Result<Item, Error>>,
|
||||
{
|
||||
type Item = Item;
|
||||
type Error = Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Item, Error> {
|
||||
use futures::Async::*;
|
||||
|
||||
let local_waker = noop_local_waker();
|
||||
|
||||
let res = self.0.as_mut().poll(&local_waker);
|
||||
|
||||
match res {
|
||||
StdPoll::Ready(Ok(val)) => Ok(Ready(val)),
|
||||
StdPoll::Ready(Err(err)) => Err(err),
|
||||
StdPoll::Pending => Ok(NotReady),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== NoopWaker =====
|
||||
|
||||
struct NoopWaker;
|
||||
|
||||
fn noop_local_waker() -> LocalWaker {
|
||||
let w: NonNull<NoopWaker> = NonNull::dangling();
|
||||
unsafe { LocalWaker::new(w) }
|
||||
}
|
||||
|
||||
fn noop_waker() -> Waker {
|
||||
let w: NonNull<NoopWaker> = NonNull::dangling();
|
||||
unsafe { Waker::new(w) }
|
||||
}
|
||||
|
||||
unsafe impl UnsafeWake for NoopWaker {
|
||||
unsafe fn clone_raw(&self) -> Waker {
|
||||
noop_waker()
|
||||
}
|
||||
|
||||
unsafe fn drop_raw(&self) {
|
||||
}
|
||||
|
||||
unsafe fn wake(&self) {
|
||||
panic!("NoopWake cannot wake");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
use futures::{Future, Async};
|
||||
|
||||
use std::marker::Unpin;
|
||||
use std::future::Future as StdFuture;
|
||||
use std::pin::Pin;
|
||||
use std::task::{LocalWaker, Poll as StdPoll};
|
||||
|
||||
/// Converts an 0.1 `Future` into an 0.3 `Future`.
|
||||
#[derive(Debug)]
|
||||
pub struct Compat<T>(T);
|
||||
|
||||
pub(crate) fn convert_poll<T, E>(poll: Result<Async<T>, E>) -> StdPoll<Result<T, E>> {
|
||||
use futures::Async::{Ready, NotReady};
|
||||
|
||||
match poll {
|
||||
Ok(Ready(val)) => StdPoll::Ready(Ok(val)),
|
||||
Ok(NotReady) => StdPoll::Pending,
|
||||
Err(err) => StdPoll::Ready(Err(err)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn convert_poll_stream<T, E>(
|
||||
poll: Result<Async<Option<T>>, E>) -> StdPoll<Option<Result<T, E>>>
|
||||
{
|
||||
use futures::Async::{Ready, NotReady};
|
||||
|
||||
match poll {
|
||||
Ok(Ready(Some(val))) => StdPoll::Ready(Some(Ok(val))),
|
||||
Ok(Ready(None)) => StdPoll::Ready(None),
|
||||
Ok(NotReady) => StdPoll::Pending,
|
||||
Err(err) => StdPoll::Ready(Some(Err(err))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a value into one that can be used with `await!`.
|
||||
pub trait IntoAwaitable {
|
||||
type Awaitable;
|
||||
|
||||
/// Convert `self` into a value that can be used with `await!`.
|
||||
fn into_awaitable(self) -> Self::Awaitable;
|
||||
}
|
||||
|
||||
impl<T: Future + Unpin> IntoAwaitable for T {
|
||||
type Awaitable = Compat<T>;
|
||||
|
||||
fn into_awaitable(self) -> Self::Awaitable {
|
||||
Compat(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> StdFuture for Compat<T>
|
||||
where T: Future + Unpin
|
||||
{
|
||||
type Output = Result<T::Item, T::Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _lw: &LocalWaker) -> StdPoll<Self::Output> {
|
||||
use futures::Async::{Ready, NotReady};
|
||||
|
||||
// TODO: wire in cx
|
||||
|
||||
match self.0.poll() {
|
||||
Ok(Ready(val)) => StdPoll::Ready(Ok(val)),
|
||||
Ok(NotReady) => StdPoll::Pending,
|
||||
Err(e) => StdPoll::Ready(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#![doc(hidden)]
|
||||
|
||||
pub mod forward;
|
||||
pub mod backward;
|
||||
@@ -0,0 +1,32 @@
|
||||
use tokio_io::AsyncWrite;
|
||||
|
||||
|
||||
use std::io;
|
||||
use std::future::Future;
|
||||
use std::marker::Unpin;
|
||||
use std::pin::Pin;
|
||||
use std::task::{LocalWaker, Poll};
|
||||
|
||||
/// A future used to fully flush an I/O object.
|
||||
#[derive(Debug)]
|
||||
pub struct Flush<'a, T: ?Sized + 'a> {
|
||||
writer: &'a mut T,
|
||||
}
|
||||
|
||||
// Pin is never projected to fields
|
||||
impl<'a, T: ?Sized> Unpin for Flush<'a, T> {}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> Flush<'a, T> {
|
||||
pub(super) fn new(writer: &'a mut T) -> Flush<'a, T> {
|
||||
Flush { writer }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> Future for Flush<'a, T> {
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _wx: &LocalWaker) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
convert_poll(self.writer.poll_flush())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
//! Use I/O with `async` / `await`.
|
||||
|
||||
mod flush;
|
||||
mod read;
|
||||
mod read_exact;
|
||||
mod write;
|
||||
mod write_all;
|
||||
|
||||
pub use self::flush::Flush;
|
||||
pub use self::read::Read;
|
||||
pub use self::read_exact::ReadExact;
|
||||
pub use self::write::Write;
|
||||
pub use self::write_all::WriteAll;
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
/// An extension trait which adds utility methods to `AsyncRead` types.
|
||||
pub trait AsyncReadExt: AsyncRead {
|
||||
/// Tries to read some bytes directly into the given `buf` in an
|
||||
/// asynchronous manner, returning a future.
|
||||
///
|
||||
/// The returned future will resolve to the number of bytes read once the read
|
||||
/// operation is completed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncReadExt;
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut reader = Cursor::new([1, 2, 3, 4]);
|
||||
/// let mut output = [0u8; 5];
|
||||
///
|
||||
/// let bytes = await!(reader.read_async(&mut output[..])).unwrap();
|
||||
///
|
||||
/// // This is only guaranteed to be 4 because `&[u8]` is a synchronous
|
||||
/// // reader. In a real system you could get anywhere from 1 to
|
||||
/// // `output.len()` bytes in a single read.
|
||||
/// assert_eq!(bytes, 4);
|
||||
/// assert_eq!(output, [1, 2, 3, 4, 0]);
|
||||
/// });
|
||||
/// ```
|
||||
fn read_async<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self> {
|
||||
Read::new(self, buf)
|
||||
}
|
||||
|
||||
/// Creates a future which will read exactly enough bytes to fill `buf`,
|
||||
/// returning an error if end of file (EOF) is hit sooner.
|
||||
///
|
||||
/// The returned future will resolve once the read operation is completed.
|
||||
///
|
||||
/// In the case of an error the buffer and the object will be discarded, with
|
||||
/// the error yielded.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncReadExt;
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut reader = Cursor::new([1, 2, 3, 4]);
|
||||
/// let mut output = [0u8; 4];
|
||||
///
|
||||
/// await!(reader.read_exact_async(&mut output)).unwrap();
|
||||
///
|
||||
/// assert_eq!(output, [1, 2, 3, 4]);
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// ## EOF is hit before `buf` is filled
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncReadExt;
|
||||
/// use std::io::{self, Cursor};
|
||||
///
|
||||
/// let mut reader = Cursor::new([1, 2, 3, 4]);
|
||||
/// let mut output = [0u8; 5];
|
||||
///
|
||||
/// let result = await!(reader.read_exact_async(&mut output));
|
||||
///
|
||||
/// assert_eq!(result.unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
/// });
|
||||
/// ```
|
||||
fn read_exact_async<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self> {
|
||||
ReadExact::new(self, buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// An extension trait which adds utility methods to `AsyncWrite` types.
|
||||
pub trait AsyncWriteExt: AsyncWrite {
|
||||
/// Write data into this object.
|
||||
///
|
||||
/// Creates a future that will write the entire contents of the buffer `buf` into
|
||||
/// this `AsyncWrite`.
|
||||
///
|
||||
/// The returned future will not complete until all the data has been written.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncWriteExt;
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buf = [0u8; 5];
|
||||
/// let mut writer = Cursor::new(&mut buf[..]);
|
||||
///
|
||||
/// let n = await!(writer.write_async(&[1, 2, 3, 4])).unwrap();
|
||||
///
|
||||
/// assert_eq!(writer.into_inner()[..n], [1, 2, 3, 4, 0][..n]);
|
||||
/// });
|
||||
/// ```
|
||||
fn write_async<'a>(&'a mut self, buf: &'a [u8]) -> Write<'a, Self> {
|
||||
Write::new(self, buf)
|
||||
}
|
||||
|
||||
/// Write an entire buffer into this object.
|
||||
///
|
||||
/// Creates a future that will write the entire contents of the buffer `buf` into
|
||||
/// this `AsyncWrite`.
|
||||
///
|
||||
/// The returned future will not complete until all the data has been written.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncWriteExt;
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buf = [0u8; 5];
|
||||
/// let mut writer = Cursor::new(&mut buf[..]);
|
||||
///
|
||||
/// await!(writer.write_all_async(&[1, 2, 3, 4])).unwrap();
|
||||
///
|
||||
/// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]);
|
||||
/// });
|
||||
/// ```
|
||||
fn write_all_async<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> {
|
||||
WriteAll::new(self, buf)
|
||||
}
|
||||
|
||||
/// Creates a future which will entirely flush this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncWriteExt;
|
||||
/// use std::io::{BufWriter, Cursor};
|
||||
///
|
||||
/// let mut output = [0u8; 5];
|
||||
///
|
||||
/// {
|
||||
/// let mut writer = Cursor::new(&mut output[..]);
|
||||
/// let mut buffered = BufWriter::new(writer);
|
||||
/// await!(buffered.write_all_async(&[1, 2])).unwrap();
|
||||
/// await!(buffered.write_all_async(&[3, 4])).unwrap();
|
||||
/// await!(buffered.flush_async()).unwrap();
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(output, [1, 2, 3, 4, 0]);
|
||||
/// });
|
||||
/// ```
|
||||
fn flush_async<'a>(&mut self) -> Flush<Self> {
|
||||
Flush::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + ?Sized> AsyncReadExt for T {}
|
||||
impl<T: AsyncWrite + ?Sized> AsyncWriteExt for T {}
|
||||
@@ -0,0 +1,38 @@
|
||||
use tokio_io::AsyncRead;
|
||||
|
||||
use std::future::Future;
|
||||
use std::task::{self, Poll};
|
||||
|
||||
use std::io;
|
||||
use std::marker::Unpin;
|
||||
use std::pin::Pin;
|
||||
|
||||
/// A future which can be used to read bytes.
|
||||
#[derive(Debug)]
|
||||
pub struct Read<'a, T: ?Sized + 'a> {
|
||||
reader: &'a mut T,
|
||||
buf: &'a mut [u8],
|
||||
}
|
||||
|
||||
// Pinning is never projected to fields
|
||||
impl<'a, T: ?Sized> Unpin for Read<'a, T> {}
|
||||
|
||||
impl<'a, T: AsyncRead + ?Sized> Read<'a, T> {
|
||||
pub(super) fn new(reader: &'a mut T, buf: &'a mut [u8]) -> Read<'a, T> {
|
||||
Read {
|
||||
reader,
|
||||
buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: AsyncRead + ?Sized> Future for Read<'a, T> {
|
||||
type Output = io::Result<usize>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _lw: &task::LocalWaker) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
convert_poll(this.reader.poll_read(this.buf))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
use tokio_io::AsyncRead;
|
||||
|
||||
use std::future::Future;
|
||||
use std::task::{self, Poll};
|
||||
|
||||
use std::io;
|
||||
use std::marker::Unpin;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
|
||||
/// A future which can be used to read exactly enough bytes to fill a buffer.
|
||||
#[derive(Debug)]
|
||||
pub struct ReadExact<'a, T: ?Sized + 'a> {
|
||||
reader: &'a mut T,
|
||||
buf: &'a mut [u8],
|
||||
}
|
||||
|
||||
// Pinning is never projected to fields
|
||||
impl<'a, T: ?Sized> Unpin for ReadExact<'a, T> {}
|
||||
|
||||
impl<'a, T: AsyncRead + ?Sized> ReadExact<'a, T> {
|
||||
pub(super) fn new(reader: &'a mut T, buf: &'a mut [u8]) -> ReadExact<'a, T> {
|
||||
ReadExact {
|
||||
reader,
|
||||
buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eof() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::UnexpectedEof, "early eof")
|
||||
}
|
||||
|
||||
impl<'a, T: AsyncRead + ?Sized> Future for ReadExact<'a, T> {
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _lw: &task::LocalWaker) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
|
||||
while !this.buf.is_empty() {
|
||||
let n = try_ready!(convert_poll(this.reader.poll_read(this.buf)));
|
||||
|
||||
{
|
||||
let (_, rest) = mem::replace(&mut this.buf, &mut []).split_at_mut(n);
|
||||
this.buf = rest;
|
||||
}
|
||||
if n == 0 {
|
||||
return Poll::Ready(Err(eof()))
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
use tokio_io::AsyncWrite;
|
||||
|
||||
use std::future::Future;
|
||||
use std::task::{self, Poll};
|
||||
|
||||
use std::io;
|
||||
use std::marker::Unpin;
|
||||
use std::pin::Pin;
|
||||
|
||||
/// A future used to write data.
|
||||
#[derive(Debug)]
|
||||
pub struct Write<'a, T: 'a + ?Sized> {
|
||||
writer: &'a mut T,
|
||||
buf: &'a [u8],
|
||||
}
|
||||
|
||||
// Pinning is never projected to fields
|
||||
impl<'a, T: ?Sized> Unpin for Write<'a, T> {}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> Write<'a, T> {
|
||||
pub(super) fn new(writer: &'a mut T, buf: &'a [u8]) -> Write<'a, T> {
|
||||
Write {
|
||||
writer,
|
||||
buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> Future for Write<'a, T> {
|
||||
type Output = io::Result<usize>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _lw: &task::LocalWaker) -> Poll<io::Result<usize>> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
convert_poll(this.writer.poll_write(this.buf))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
use tokio_io::AsyncWrite;
|
||||
|
||||
use std::future::Future;
|
||||
use std::task::{self, Poll};
|
||||
|
||||
use std::io;
|
||||
use std::marker::Unpin;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
|
||||
/// A future used to write the entire contents of a buffer.
|
||||
#[derive(Debug)]
|
||||
pub struct WriteAll<'a, T: ?Sized + 'a> {
|
||||
writer: &'a mut T,
|
||||
buf: &'a [u8],
|
||||
}
|
||||
|
||||
// Pinning is never projected to fields
|
||||
impl<'a, T: ?Sized> Unpin for WriteAll<'a, T> {}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> WriteAll<'a, T> {
|
||||
pub(super) fn new(writer: &'a mut T, buf: &'a [u8]) -> WriteAll<'a, T> {
|
||||
WriteAll {
|
||||
writer,
|
||||
buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn zero_write() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::WriteZero, "zero-length write")
|
||||
}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> Future for WriteAll<'a, T> {
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _lw: &task::LocalWaker) -> Poll<io::Result<()>> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
|
||||
while !this.buf.is_empty() {
|
||||
let n = try_ready!(convert_poll(this.writer.poll_write(this.buf)));
|
||||
|
||||
{
|
||||
let (_, rest) = mem::replace(&mut this.buf, &[]).split_at(n);
|
||||
this.buf = rest;
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return Poll::Ready(Err(zero_write()))
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
#![cfg(feature = "async-await-preview")]
|
||||
#![feature(
|
||||
rust_2018_preview,
|
||||
arbitrary_self_types,
|
||||
async_await,
|
||||
await_macro,
|
||||
futures_api,
|
||||
pin,
|
||||
)]
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-async-await/0.1.4")]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! A preview of Tokio w/ `async` / `await` support.
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_io;
|
||||
|
||||
/// Extracts the successful type of a `Poll<Result<T, E>>`.
|
||||
///
|
||||
/// This macro bakes in propagation of `Pending` and `Err` signals by returning early.
|
||||
macro_rules! try_ready {
|
||||
($x:expr) => {
|
||||
match $x {
|
||||
std::task::Poll::Ready(Ok(x)) => x,
|
||||
std::task::Poll::Ready(Err(e)) =>
|
||||
return std::task::Poll::Ready(Err(e.into())),
|
||||
std::task::Poll::Pending =>
|
||||
return std::task::Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
mod await;
|
||||
pub mod compat;
|
||||
pub mod io;
|
||||
pub mod sink;
|
||||
pub mod stream;
|
||||
|
||||
/*
|
||||
pub mod prelude {
|
||||
//! A "prelude" for users of the `tokio` crate.
|
||||
//!
|
||||
//! This prelude is similar to the standard library's prelude in that you'll
|
||||
//! almost always want to import its entire contents, but unlike the standard
|
||||
//! library's prelude you'll have to do so manually:
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio::prelude::*;
|
||||
//! ```
|
||||
//!
|
||||
//! The prelude may grow over time as additional items see ubiquitous use.
|
||||
|
||||
pub use tokio_main::prelude::*;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::async_await::{
|
||||
io::{
|
||||
AsyncReadExt,
|
||||
AsyncWriteExt,
|
||||
},
|
||||
sink::{
|
||||
SinkExt,
|
||||
},
|
||||
stream::{
|
||||
StreamExt,
|
||||
},
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
||||
// Rename the `await` macro in `std`. This is used by the redefined
|
||||
// `await` macro in this crate.
|
||||
#[doc(hidden)]
|
||||
pub use std::await as std_await;
|
||||
|
||||
/*
|
||||
use std::future::{Future as StdFuture};
|
||||
|
||||
fn run<T: futures::Future<Item = (), Error = ()>>(t: T) {
|
||||
drop(t);
|
||||
}
|
||||
|
||||
async fn map_ok<T: StdFuture>(future: T) -> Result<(), ()> {
|
||||
let _ = await!(future);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Like `tokio::run`, but takes an `async` block
|
||||
pub fn run_async<F>(future: F)
|
||||
where F: StdFuture<Output = ()> + Send + 'static,
|
||||
{
|
||||
use async_await::compat::backward;
|
||||
let future = backward::Compat::new(map_ok(future));
|
||||
|
||||
run(future);
|
||||
unimplemented!();
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
/// Like `tokio::spawn`, but takes an `async` block
|
||||
pub fn spawn_async<F>(future: F)
|
||||
where F: StdFuture<Output = ()> + Send + 'static,
|
||||
{
|
||||
use crate::async_await::compat::backward;
|
||||
|
||||
spawn(backward::Compat::new(async || {
|
||||
let _ = await!(future);
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,26 @@
|
||||
//! Use sinks with `async` / `await`.
|
||||
|
||||
mod send;
|
||||
|
||||
pub use self::send::Send;
|
||||
|
||||
use futures::Sink;
|
||||
|
||||
use std::marker::Unpin;
|
||||
|
||||
/// An extension trait which adds utility methods to `Sink` types.
|
||||
pub trait SinkExt: Sink {
|
||||
/// Send an item into the sink.
|
||||
///
|
||||
/// Note that, **because of the flushing requirement, it is usually better
|
||||
/// to batch together items to send via `send_all`, rather than flushing
|
||||
/// between each item.**
|
||||
fn send_async(&mut self, item: Self::SinkItem) -> Send<Self>
|
||||
where
|
||||
Self: Sized + Unpin,
|
||||
{
|
||||
Send::new(self, item)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sink> SinkExt for T {}
|
||||
@@ -0,0 +1,54 @@
|
||||
use futures::Sink;
|
||||
|
||||
use std::future::Future;
|
||||
use std::task::{self, Poll};
|
||||
|
||||
use std::marker::Unpin;
|
||||
use std::pin::Pin;
|
||||
|
||||
/// Future for the `SinkExt::send_async` combinator, which sends a value to a
|
||||
/// sink and then waits until the sink has fully flushed.
|
||||
#[derive(Debug)]
|
||||
pub struct Send<'a, T: Sink + 'a + ?Sized> {
|
||||
sink: &'a mut T,
|
||||
item: Option<T::SinkItem>,
|
||||
}
|
||||
|
||||
impl<T: Sink + Unpin + ?Sized> Unpin for Send<'_, T> {}
|
||||
|
||||
impl<'a, T: Sink + Unpin + ?Sized> Send<'a, T> {
|
||||
pub(super) fn new(sink: &'a mut T, item: T::SinkItem) -> Self {
|
||||
Send {
|
||||
sink,
|
||||
item: Some(item),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sink + Unpin + ?Sized> Future for Send<'_, T> {
|
||||
type Output = Result<(), T::SinkError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _lw: &task::LocalWaker) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
use futures::AsyncSink::{Ready, NotReady};
|
||||
|
||||
if let Some(item) = self.item.take() {
|
||||
match self.sink.start_send(item) {
|
||||
Ok(Ready) => {}
|
||||
Ok(NotReady(val)) => {
|
||||
self.item = Some(val);
|
||||
return Poll::Pending;
|
||||
}
|
||||
Err(err) => {
|
||||
return Poll::Ready(Err(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we're done sending the item, but want to block on flushing the
|
||||
// sink
|
||||
try_ready!(convert_poll(self.sink.poll_complete()));
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//! Use streams with `async` / `await`.
|
||||
|
||||
mod next;
|
||||
|
||||
pub use self::next::Next;
|
||||
|
||||
use futures::Stream;
|
||||
|
||||
use std::marker::Unpin;
|
||||
|
||||
/// An extension trait which adds utility methods to `Stream` types.
|
||||
pub trait StreamExt: Stream {
|
||||
/// Creates a future that resolves to the next item in the stream.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(await_macro, async_await)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::{stream, StreamExt};
|
||||
///
|
||||
/// let mut stream = stream::iter_ok::<_, ()>(1..3);
|
||||
///
|
||||
/// assert_eq!(await!(stream.next()), Some(Ok(1)));
|
||||
/// assert_eq!(await!(stream.next()), Some(Ok(2)));
|
||||
/// assert_eq!(await!(stream.next()), Some(Ok(3)));
|
||||
/// assert_eq!(await!(stream.next()), None);
|
||||
/// });
|
||||
/// ```
|
||||
fn next(&mut self) -> Next<Self>
|
||||
where
|
||||
Self: Sized + Unpin,
|
||||
{
|
||||
Next::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stream> StreamExt for T {}
|
||||
@@ -0,0 +1,30 @@
|
||||
use futures::Stream;
|
||||
|
||||
use std::future::Future;
|
||||
use std::marker::Unpin;
|
||||
use std::pin::Pin;
|
||||
use std::task::{LocalWaker, Poll};
|
||||
|
||||
/// A future of the next element of a stream.
|
||||
#[derive(Debug)]
|
||||
pub struct Next<'a, T: 'a> {
|
||||
stream: &'a mut T,
|
||||
}
|
||||
|
||||
impl<'a, T: Stream + Unpin> Unpin for Next<'a, T> {}
|
||||
|
||||
impl<'a, T: Stream + Unpin> Next<'a, T> {
|
||||
pub(super) fn new(stream: &'a mut T) -> Next<'a, T> {
|
||||
Next { stream }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Stream + Unpin> Future for Next<'a, T> {
|
||||
type Output = Option<Result<T::Item, T::Error>>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _lw: &LocalWaker) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll_stream;
|
||||
|
||||
convert_poll_stream(self.stream.poll())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# 0.1.0 (unreleased)
|
||||
|
||||
* Initial release
|
||||
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "tokio-buf"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.0"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-buf/0.1.0"
|
||||
description = """
|
||||
Asynchronous stream of byte buffers
|
||||
"""
|
||||
categories = ["asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
bytes = { version = "0.4.10", features = [ "either" ] }
|
||||
either = "1.5"
|
||||
futures = "0.1.23"
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2018 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.
|
||||
@@ -0,0 +1,70 @@
|
||||
use BufStream;
|
||||
use buf_stream::errors::internal::Never;
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::Poll;
|
||||
|
||||
use std::io;
|
||||
|
||||
impl BufStream for Vec<u8> {
|
||||
type Item = io::Cursor<Vec<u8>>;
|
||||
type Error = Never;
|
||||
|
||||
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
if self.is_empty() {
|
||||
return Ok(None.into());
|
||||
}
|
||||
|
||||
poll_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl BufStream for &'static [u8] {
|
||||
type Item = io::Cursor<&'static [u8]>;
|
||||
type Error = Never;
|
||||
|
||||
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
if self.is_empty() {
|
||||
return Ok(None.into());
|
||||
}
|
||||
|
||||
poll_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl BufStream for Bytes {
|
||||
type Item = io::Cursor<Bytes>;
|
||||
type Error = Never;
|
||||
|
||||
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
if self.is_empty() {
|
||||
return Ok(None.into());
|
||||
}
|
||||
|
||||
poll_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl BufStream for BytesMut {
|
||||
type Item = io::Cursor<BytesMut>;
|
||||
type Error = Never;
|
||||
|
||||
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
if self.is_empty() {
|
||||
return Ok(None.into());
|
||||
}
|
||||
|
||||
poll_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_bytes<T: Default>(buf: &mut T)
|
||||
-> Poll<Option<io::Cursor<T>>, Never>
|
||||
{
|
||||
use std::mem;
|
||||
|
||||
let bytes = mem::replace(buf, Default::default());
|
||||
let buf = io::Cursor::new(bytes);
|
||||
|
||||
Ok(Some(buf).into())
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use super::{BufStream, SizeHint};
|
||||
|
||||
use either::Either;
|
||||
use futures::Poll;
|
||||
|
||||
/// A buf stream that sequences two buf streams together.
|
||||
///
|
||||
/// `Chain` values are produced by the `chain` function on `BufStream`.
|
||||
#[derive(Debug)]
|
||||
pub struct Chain<T, U> {
|
||||
left: Option<T>,
|
||||
right: U,
|
||||
}
|
||||
|
||||
impl<T, U> Chain<T, U> {
|
||||
pub(crate) fn new(left: T, right: U) -> Chain<T, U> {
|
||||
Chain {
|
||||
left: Some(left),
|
||||
right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> BufStream for Chain<T, U>
|
||||
where
|
||||
T: BufStream,
|
||||
U: BufStream<Error = T::Error>,
|
||||
{
|
||||
type Item = Either<T::Item, U::Item>;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
if let Some(ref mut stream) = self.left {
|
||||
let res = try_ready!(stream.poll_buf());
|
||||
|
||||
if res.is_some() {
|
||||
return Ok(res.map(Either::Left).into());
|
||||
}
|
||||
}
|
||||
|
||||
self.left = None;
|
||||
|
||||
let res = try_ready!(self.right.poll_buf());
|
||||
Ok(res.map(Either::Right).into())
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
// TODO: Implement
|
||||
SizeHint::default()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
use super::{BufStream, FromBufStream};
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Consumes a buf stream, collecting the data into a single byte container.
|
||||
///
|
||||
/// `Collect` values are produced by `BufStream::collect`.
|
||||
#[derive(Debug)]
|
||||
pub struct Collect<T, U>
|
||||
where
|
||||
T: BufStream,
|
||||
U: FromBufStream<T::Item>,
|
||||
{
|
||||
stream: T,
|
||||
builder: Option<U::Builder>,
|
||||
}
|
||||
|
||||
/// Errors returned from `Collect` future.
|
||||
#[derive(Debug)]
|
||||
pub struct CollectError<T, U> {
|
||||
inner: Error<T, U>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error<T, U> {
|
||||
Stream(T),
|
||||
Collect(U),
|
||||
}
|
||||
|
||||
impl<T, U> Collect<T, U>
|
||||
where
|
||||
T: BufStream,
|
||||
U: FromBufStream<T::Item>,
|
||||
{
|
||||
pub(crate) fn new(stream: T) -> Collect<T, U> {
|
||||
let builder = U::builder(&stream.size_hint());
|
||||
|
||||
Collect {
|
||||
stream,
|
||||
builder: Some(builder),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Future for Collect<T, U>
|
||||
where
|
||||
T: BufStream,
|
||||
U: FromBufStream<T::Item>,
|
||||
{
|
||||
type Item = U;
|
||||
type Error = CollectError<T::Error, U::Error>;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
let res = self.stream.poll_buf()
|
||||
.map_err(|err| {
|
||||
let inner = Error::Stream(err);
|
||||
CollectError { inner }
|
||||
});
|
||||
|
||||
match try_ready!(res) {
|
||||
Some(mut buf) => {
|
||||
let builder = self.builder.as_mut().expect("cannot poll after done");
|
||||
|
||||
U::extend(builder, &mut buf, &self.stream.size_hint())
|
||||
.map_err(|err| {
|
||||
let inner = Error::Collect(err);
|
||||
CollectError { inner }
|
||||
})?;
|
||||
}
|
||||
None => {
|
||||
let builder = self.builder.take().expect("cannot poll after done");
|
||||
let value = U::build(builder)
|
||||
.map_err(|err| {
|
||||
let inner = Error::Collect(err);
|
||||
CollectError { inner }
|
||||
})?;
|
||||
return Ok(value.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl CollectError =====
|
||||
|
||||
impl<T, U> CollectError<T, U> {
|
||||
/// Returns `true` if the error was caused by polling the stream.
|
||||
pub fn is_stream_err(&self) -> bool {
|
||||
match self.inner {
|
||||
Error::Stream(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the error happened while collecting the data.
|
||||
pub fn is_collect_err(&self) -> bool {
|
||||
match self.inner {
|
||||
Error::Collect(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//! Error types
|
||||
|
||||
pub use super::collect::CollectError;
|
||||
pub use super::from::CollectVecError;
|
||||
pub use super::limit::LimitError;
|
||||
|
||||
// Being crate-private, we should be able to swap the type out in a
|
||||
// backwards compatible way.
|
||||
pub(crate) mod internal {
|
||||
use std::{error, fmt};
|
||||
|
||||
/// An error that can never occur
|
||||
pub enum Never {}
|
||||
|
||||
impl fmt::Debug for Never {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Never {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Never {
|
||||
fn description(&self) -> &str {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
use super::SizeHint;
|
||||
|
||||
use bytes::{Buf, BufMut};
|
||||
|
||||
use std::usize;
|
||||
|
||||
/// Conversion from a `BufStream`.
|
||||
///
|
||||
/// By implementing `FromBufStream` for a type, you define how it will be
|
||||
/// created from a buf stream. This is common for types which describe byte
|
||||
/// storage of some kind.
|
||||
///
|
||||
/// `FromBufStream` is rarely called explicitly, and it is instead used through
|
||||
/// `BufStream`'s `collect` method.
|
||||
pub trait FromBufStream<T: Buf>: Sized {
|
||||
/// Type that is used to build `Self` while the `BufStream` is being
|
||||
/// consumed.
|
||||
type Builder;
|
||||
|
||||
/// Error that might happen on conversion.
|
||||
type Error;
|
||||
|
||||
/// Create a new, empty, builder. The provided `hint` can be used to inform
|
||||
/// reserving capacity.
|
||||
fn builder(hint: &SizeHint) -> Self::Builder;
|
||||
|
||||
/// Extend the builder with the `Buf`.
|
||||
///
|
||||
/// This method is called whenever a new `Buf` value is obtained from the
|
||||
/// buf stream.
|
||||
///
|
||||
/// The provided size hint represents the state of the stream **after**
|
||||
/// `buf` has been yielded. The lower bound represents the minimum amount of
|
||||
/// data that will be provided after this call to `extend` returns.
|
||||
fn extend(builder: &mut Self::Builder, buf: &mut T, hint: &SizeHint)
|
||||
-> Result<(), Self::Error>;
|
||||
|
||||
/// Finalize the building of `Self`.
|
||||
///
|
||||
/// Called once the buf stream is fully consumed.
|
||||
fn build(builder: Self::Builder) -> Result<Self, Self::Error>;
|
||||
}
|
||||
|
||||
/// Error returned from collecting into a `Vec<u8>`
|
||||
#[derive(Debug)]
|
||||
pub struct CollectVecError { _p: () }
|
||||
|
||||
impl<T: Buf> FromBufStream<T> for Vec<u8> {
|
||||
type Builder = Vec<u8>;
|
||||
type Error = CollectVecError;
|
||||
|
||||
fn builder(_hint: &SizeHint) -> Vec<u8> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn extend(builder: &mut Self, buf: &mut T, hint: &SizeHint) -> Result<(), Self::Error> {
|
||||
let lower = hint.lower();
|
||||
|
||||
// If the lower bound is greater than `usize::MAX` then we have a
|
||||
// problem
|
||||
if lower > usize::MAX as u64 {
|
||||
return Err(CollectVecError { _p: () });
|
||||
}
|
||||
|
||||
let mut reserve = lower as usize;
|
||||
|
||||
// If `upper` is set, use this value if it is less than or equal to 64.
|
||||
// This only really impacts the first iteration.
|
||||
match hint.upper() {
|
||||
Some(upper) if upper <= 64 => {
|
||||
reserve = upper as usize;
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
|
||||
// hint.lower() represents the minimum amount of data that will be
|
||||
// received *after* this function call. We reserve this amount on top of
|
||||
// the amount of data in `buf`.
|
||||
reserve = match reserve.checked_add(buf.remaining()) {
|
||||
Some(n) => n,
|
||||
None => return Err(CollectVecError { _p: () }),
|
||||
};
|
||||
|
||||
// Always reserve 64 bytes the first time, unless `upper` is set and is
|
||||
// less than 64.
|
||||
if builder.is_empty() {
|
||||
reserve = reserve.max(match hint.upper() {
|
||||
Some(upper) if upper < 64 => upper as usize,
|
||||
_ => 64,
|
||||
});
|
||||
}
|
||||
|
||||
// Make sure overflow won't happen when reserving
|
||||
if reserve.checked_add(builder.len()).is_none() {
|
||||
return Err(CollectVecError { _p: () });
|
||||
}
|
||||
|
||||
// Reserve space
|
||||
builder.reserve(reserve);
|
||||
|
||||
// Copy the data
|
||||
builder.put(buf);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build(builder: Self) -> Result<Self, Self::Error> {
|
||||
Ok(builder)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
use super::{BufStream, SizeHint};
|
||||
|
||||
use bytes::Buf;
|
||||
use futures::Poll;
|
||||
|
||||
/// Limits the stream to a maximum amount of data.
|
||||
#[derive(Debug)]
|
||||
pub struct Limit<T> {
|
||||
stream: T,
|
||||
remaining: u64,
|
||||
}
|
||||
|
||||
/// Errors returned from `Limit`.
|
||||
#[derive(Debug)]
|
||||
pub struct LimitError<T> {
|
||||
/// When `None`, limit was reached
|
||||
inner: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> Limit<T> {
|
||||
pub(crate) fn new(stream: T, amount: u64) -> Limit<T> {
|
||||
Limit {
|
||||
stream,
|
||||
remaining: amount,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> BufStream for Limit<T>
|
||||
where
|
||||
T: BufStream,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = LimitError<T::Error>;
|
||||
|
||||
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
use futures::Async::Ready;
|
||||
|
||||
if self.stream.size_hint().lower() > self.remaining {
|
||||
return Err(LimitError { inner: None });
|
||||
}
|
||||
|
||||
let res = self.stream.poll_buf()
|
||||
.map_err(|err| {
|
||||
LimitError { inner: Some(err) }
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(Ready(Some(ref buf))) => {
|
||||
if buf.remaining() as u64 > self.remaining {
|
||||
self.remaining = 0;
|
||||
return Err(LimitError { inner: None });
|
||||
}
|
||||
|
||||
self.remaining -= buf.remaining() as u64;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
let mut hint = self.stream.size_hint();
|
||||
|
||||
let upper = hint.upper()
|
||||
.map(|upper| upper.min(self.remaining))
|
||||
.unwrap_or(self.remaining);
|
||||
|
||||
hint.set_upper(upper);
|
||||
hint
|
||||
}
|
||||
|
||||
fn consume_hint(&mut self, amount: usize) {
|
||||
// TODO: Should this be capped by `self.remaining`?
|
||||
self.stream.consume_hint(amount)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl LimitError =====
|
||||
|
||||
impl<T> LimitError<T> {
|
||||
/// Returns `true` if the error was caused by polling the stream.
|
||||
pub fn is_stream_err(&self) -> bool {
|
||||
self.inner.is_some()
|
||||
}
|
||||
|
||||
/// Returns `true` if the stream reached its limit.
|
||||
pub fn is_limit_err(&self) -> bool {
|
||||
self.inner.is_none()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
//! Types and utilities for working with `BufStream`.
|
||||
|
||||
mod bytes;
|
||||
mod chain;
|
||||
mod collect;
|
||||
pub mod errors;
|
||||
mod from;
|
||||
mod limit;
|
||||
mod size_hint;
|
||||
mod str;
|
||||
|
||||
pub use self::chain::Chain;
|
||||
pub use self::collect::Collect;
|
||||
pub use self::from::FromBufStream;
|
||||
pub use self::limit::Limit;
|
||||
pub use self::size_hint::SizeHint;
|
||||
|
||||
use bytes::Buf;
|
||||
use futures::Poll;
|
||||
|
||||
/// An asynchronous stream of bytes.
|
||||
///
|
||||
/// `BufStream` asynchronously yields values implementing `Buf`, i.e. byte
|
||||
/// buffers.
|
||||
pub trait BufStream {
|
||||
/// Values yielded by the `BufStream`.
|
||||
///
|
||||
/// Each item is a sequence of bytes representing a chunk of the total
|
||||
/// `ByteStream`.
|
||||
type Item: Buf;
|
||||
|
||||
/// The error type this `BufStream` might generate.
|
||||
type Error;
|
||||
|
||||
/// Attempt to pull out the next buffer of this stream, registering the
|
||||
/// current task for wakeup if the value is not yet available, and returning
|
||||
/// `None` if the stream is exhausted.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// There are several possible return values, each indicating a distinct
|
||||
/// stream state:
|
||||
///
|
||||
/// - `Ok(Async::NotReady)` means that this stream's next value is not ready
|
||||
/// yet. Implementations will ensure that the current task will be notified
|
||||
/// when the next value may be ready.
|
||||
///
|
||||
/// - `Ok(Async::Ready(Some(buf)))` means that the stream has successfully
|
||||
/// produced a value, `buf`, and may produce further values on subsequent
|
||||
/// `poll_buf` calls.
|
||||
///
|
||||
/// - `Ok(Async::Ready(None))` means that the stream has terminated, and
|
||||
/// `poll_buf` should not be invoked again.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Once a stream is finished, i.e. `Ready(None)` has been returned, further
|
||||
/// calls to `poll_buf` may result in a panic or other "bad behavior".
|
||||
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error>;
|
||||
|
||||
/// Returns the bounds on the remaining length of the stream.
|
||||
///
|
||||
/// The size hint allows the caller to perform certain optimizations that
|
||||
/// are dependent on the byte stream size. For example, `collect` uses the
|
||||
/// size hint to pre-allocate enough capacity to store the entirety of the
|
||||
/// data received from the byte stream.
|
||||
///
|
||||
/// When `SizeHint::upper()` returns `Some` with a value equal to
|
||||
/// `SizeHint::lower()`, this represents the exact number of bytes that will
|
||||
/// be yielded by the `BufStream`.
|
||||
///
|
||||
/// # Implementation notes
|
||||
///
|
||||
/// While not enforced, implementations are expected to respect the values
|
||||
/// returned from `SizeHint`. Any deviation is considered an implementation
|
||||
/// bug. Consumers may rely on correctness in order to use the value as part
|
||||
/// of protocol impelmentations. For example, an HTTP library may use the
|
||||
/// size hint to set the `content-length` header.
|
||||
///
|
||||
/// However, `size_hint` must not be trusted to omit bounds checks in unsafe
|
||||
/// code. An incorrect implementation of `size_hint()` must not lead to
|
||||
/// memory safety violations.
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
SizeHint::default()
|
||||
}
|
||||
|
||||
/// Indicates to the `BufStream` how much data the consumer is currently
|
||||
/// able to process.
|
||||
///
|
||||
/// The consume hint allows the stream to perform certain optimizations that
|
||||
/// are dependent on the consumer's readiness. For example, the consume hint
|
||||
/// may be used to request a remote peer to start sending up to `amount`
|
||||
/// data.
|
||||
///
|
||||
/// Calling `consume_hint` is not a requirement. If `consume_hint` is never
|
||||
/// called, the stream should assume a default behavior. When `consume_hint`
|
||||
/// is called, the stream should make a best effort to honor by the request.
|
||||
///
|
||||
/// `amount` represents the number of bytes that the caller would like to
|
||||
/// receive at the time the function is called. For example, if
|
||||
/// `consume_hint` is called with 20, the consumer requests 20 bytes. The
|
||||
/// stream may yield less than that. If the next call to `poll_buf` returns
|
||||
/// 5 bytes, the consumer still has 15 bytes requested. At this point,
|
||||
/// invoking `consume_hint` again with 20 resets the amount requested back
|
||||
/// to 20 bytes.
|
||||
///
|
||||
/// Calling `consume_hint` with 0 as the argument informs the stream that
|
||||
/// the caller does not intend to call `poll_buf`. If `poll_buf` **is**
|
||||
/// called, the stream may, but is not obligated to, return `NotReady` even
|
||||
/// if it could produce data at that point. If it chooses to return
|
||||
/// `NotReady`, when `consume_hint` is called with a non-zero argument, the
|
||||
/// task must be notified in order to respect the `poll_buf` contract.
|
||||
fn consume_hint(&mut self, amount: usize) {
|
||||
// By default, this function does nothing
|
||||
drop(amount);
|
||||
}
|
||||
|
||||
/// Takes two buf streams and creates a new buf stream over both in
|
||||
/// sequence.
|
||||
///
|
||||
/// `chain()` returns a new `BufStream` value which will first yield all
|
||||
/// data from `self` then all data from `other`.
|
||||
///
|
||||
/// In other words, it links two buf streams together, in a chain.
|
||||
fn chain<T>(self, other: T) -> Chain<Self, T>
|
||||
where
|
||||
Self: Sized,
|
||||
T: BufStream<Error = Self::Error>,
|
||||
{
|
||||
Chain::new(self, other)
|
||||
}
|
||||
|
||||
/// Consumes all data from `self`, storing it in byte storage of type `T`.
|
||||
///
|
||||
/// `collect()` returns a future that buffers all data yielded from `self`
|
||||
/// into storage of type of `T`. The future completes once `self` yield
|
||||
/// `None`, returning the buffered data.
|
||||
///
|
||||
/// The collect future will yield an error if `self` yields an error or if
|
||||
/// the collect operation errors. The collect error cases are dependent on
|
||||
/// the target storage type.
|
||||
fn collect<T>(self) -> Collect<Self, T>
|
||||
where
|
||||
Self: Sized,
|
||||
T: FromBufStream<Self::Item>,
|
||||
{
|
||||
Collect::new(self)
|
||||
}
|
||||
|
||||
/// Limit the number of bytes that the stream can yield.
|
||||
///
|
||||
/// `limit()` returns a new `BufStream` value which yields all the data from
|
||||
/// `self` while ensuring that at most `amount` bytes are yielded.
|
||||
///
|
||||
/// If `self` can yield greater than `amount` bytes, the returned stream
|
||||
/// will yield an error.
|
||||
fn limit(self, amount: u64) -> Limit<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Limit::new(self, amount)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
use std::u64;
|
||||
|
||||
/// A `BufStream` size hint
|
||||
///
|
||||
/// The default implementation returns:
|
||||
///
|
||||
/// * 0 for `available`
|
||||
/// * 0 for `lower`
|
||||
/// * `None` for `upper`.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct SizeHint {
|
||||
lower: u64,
|
||||
upper: Option<u64>,
|
||||
}
|
||||
|
||||
impl SizeHint {
|
||||
/// Returns a new `SizeHint` with default values
|
||||
pub fn new() -> SizeHint {
|
||||
SizeHint::default()
|
||||
}
|
||||
|
||||
/// Returns the lower bound of data that the `BufStream` will yield before
|
||||
/// completing.
|
||||
pub fn lower(&self) -> u64 {
|
||||
self.lower
|
||||
}
|
||||
|
||||
/// Set the value of the `lower` hint.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The function panics if `value` is less than `upper`.
|
||||
pub fn set_lower(&mut self, value: u64) {
|
||||
assert!(value <= self.upper.unwrap_or(u64::MAX));
|
||||
self.lower = value;
|
||||
}
|
||||
|
||||
/// Returns the upper bound of data the `BufStream` will yield before
|
||||
/// completing, or `None` if the value is unknown.
|
||||
pub fn upper(&self) -> Option<u64> {
|
||||
self.upper
|
||||
}
|
||||
|
||||
/// Set the value of the `upper` hint value.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if `value` is less than `lower`.
|
||||
pub fn set_upper(&mut self, value: u64) {
|
||||
// There is no need to check `available` as that is guaranteed to be
|
||||
// less than or equal to `lower`.
|
||||
assert!(value >= self.lower, "`value` is less than than `lower`");
|
||||
|
||||
self.upper = Some(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
use BufStream;
|
||||
use buf_stream::errors::internal::Never;
|
||||
|
||||
use futures::Poll;
|
||||
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
impl BufStream for String {
|
||||
type Item = io::Cursor<Vec<u8>>;
|
||||
type Error = Never;
|
||||
|
||||
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
if self.is_empty() {
|
||||
return Ok(None.into());
|
||||
}
|
||||
|
||||
let bytes = mem::replace(self, Default::default()).into_bytes();
|
||||
let buf = io::Cursor::new(bytes);
|
||||
|
||||
Ok(Some(buf).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl BufStream for &'static str {
|
||||
type Item = io::Cursor<&'static [u8]>;
|
||||
type Error = Never;
|
||||
|
||||
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
if self.is_empty() {
|
||||
return Ok(None.into());
|
||||
}
|
||||
|
||||
let bytes = mem::replace(self, Default::default()).as_bytes();
|
||||
let buf = io::Cursor::new(bytes);
|
||||
|
||||
Ok(Some(buf).into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-buf/0.1.0")]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! Asynchronous stream of bytes.
|
||||
//!
|
||||
//! This crate contains the `BufStream` trait and a number of combinators for
|
||||
//! this trait. The trait is similar to `Stream` in the `futures` library, but
|
||||
//! instead of yielding arbitrary values, it only yields types that implement
|
||||
//! `Buf` (i.e, byte collections).
|
||||
|
||||
extern crate bytes;
|
||||
extern crate either;
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
|
||||
pub mod buf_stream;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use buf_stream::BufStream;
|
||||
@@ -0,0 +1,329 @@
|
||||
extern crate tokio_buf;
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
|
||||
use tokio_buf::buf_stream::{BufStream, SizeHint};
|
||||
use bytes::Buf;
|
||||
use futures::{Future, Poll};
|
||||
use futures::Async::*;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::io::Cursor;
|
||||
|
||||
macro_rules! assert_buf_eq {
|
||||
($actual:expr, $expect:expr) => {{
|
||||
match $actual {
|
||||
Ok(Ready(Some(val))) => {
|
||||
assert_eq!(val.remaining(), val.bytes().len());
|
||||
assert_eq!(val.bytes(), $expect.as_bytes());
|
||||
}
|
||||
Ok(Ready(None)) => panic!("expected value; BufStream yielded None"),
|
||||
Ok(NotReady) => panic!("expected value; BufStream is not ready"),
|
||||
Err(e) => panic!("expected value; got error = {:?}", e),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! assert_none {
|
||||
($actual:expr) => {
|
||||
match $actual {
|
||||
Ok(Ready(None)) => {}
|
||||
actual => panic!("expected None; actual = {:?}", actual),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! assert_not_ready {
|
||||
($actual:expr) => {
|
||||
match $actual {
|
||||
Ok(NotReady) => {}
|
||||
actual => panic!("expected NotReady; actual = {:?}", actual),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== test `SizeHint` =====
|
||||
|
||||
#[test]
|
||||
fn size_hint() {
|
||||
let hint = SizeHint::new();
|
||||
assert_eq!(hint.lower(), 0);
|
||||
assert!(hint.upper().is_none());
|
||||
|
||||
let mut hint = SizeHint::new();
|
||||
hint.set_lower(100);
|
||||
assert_eq!(hint.lower(), 100);
|
||||
assert!(hint.upper().is_none());
|
||||
|
||||
let mut hint = SizeHint::new();
|
||||
hint.set_upper(200);
|
||||
assert_eq!(hint.lower(), 0);
|
||||
assert_eq!(hint.upper(), Some(200));
|
||||
|
||||
let mut hint = SizeHint::new();
|
||||
hint.set_lower(100);
|
||||
hint.set_upper(100);
|
||||
assert_eq!(hint.lower(), 100);
|
||||
assert_eq!(hint.upper(), Some(100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn size_hint_lower_bigger_than_upper() {
|
||||
let mut hint = SizeHint::new();
|
||||
hint.set_upper(100);
|
||||
hint.set_lower(200);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn size_hint_upper_less_than_lower() {
|
||||
let mut hint = SizeHint::new();
|
||||
hint.set_lower(200);
|
||||
hint.set_upper(100);
|
||||
}
|
||||
|
||||
// ===== test `chain()` =====
|
||||
|
||||
#[test]
|
||||
fn chain() {
|
||||
// Chain one with one
|
||||
//
|
||||
let mut bs = one("hello").chain(one("world"));
|
||||
|
||||
assert_buf_eq!(bs.poll_buf(), "hello");
|
||||
assert_buf_eq!(bs.poll_buf(), "world");
|
||||
assert_none!(bs.poll_buf());
|
||||
|
||||
// Chain multi with multi
|
||||
let mut bs = list(&["foo", "bar"])
|
||||
.chain(list(&["baz", "bok"]));
|
||||
|
||||
assert_buf_eq!(bs.poll_buf(), "foo");
|
||||
assert_buf_eq!(bs.poll_buf(), "bar");
|
||||
assert_buf_eq!(bs.poll_buf(), "baz");
|
||||
assert_buf_eq!(bs.poll_buf(), "bok");
|
||||
assert_none!(bs.poll_buf());
|
||||
|
||||
// Chain includes a not ready call
|
||||
//
|
||||
let mut bs = new_mock(&[
|
||||
Ok(Ready("foo")),
|
||||
Ok(NotReady),
|
||||
Ok(Ready("bar"))
|
||||
]).chain(one("baz"));
|
||||
|
||||
assert_buf_eq!(bs.poll_buf(), "foo");
|
||||
assert_not_ready!(bs.poll_buf());
|
||||
assert_buf_eq!(bs.poll_buf(), "bar");
|
||||
assert_buf_eq!(bs.poll_buf(), "baz");
|
||||
assert_none!(bs.poll_buf());
|
||||
}
|
||||
|
||||
// ===== Test `collect()` =====
|
||||
|
||||
#[test]
|
||||
fn collect_vec() {
|
||||
// While unfortunate, this test makes some assumptions on vec's resizing
|
||||
// behavior.
|
||||
//
|
||||
// Collect one
|
||||
//
|
||||
let bs = one("hello world");
|
||||
|
||||
let vec: Vec<u8> = bs.collect()
|
||||
.wait().unwrap();
|
||||
|
||||
assert_eq!(vec, b"hello world");
|
||||
assert_eq!(vec.capacity(), 64);
|
||||
|
||||
// Collect one, with size hint
|
||||
//
|
||||
let mut bs = one("hello world");
|
||||
bs.size_hint.set_lower(11);
|
||||
|
||||
let vec: Vec<u8> = bs.collect()
|
||||
.wait().unwrap();
|
||||
|
||||
assert_eq!(vec, b"hello world");
|
||||
assert_eq!(vec.capacity(), 64);
|
||||
|
||||
// Collect one, with size hint
|
||||
//
|
||||
let mut bs = one("hello world");
|
||||
bs.size_hint.set_lower(10);
|
||||
|
||||
let vec: Vec<u8> = bs.collect()
|
||||
.wait().unwrap();
|
||||
|
||||
assert_eq!(vec, b"hello world");
|
||||
assert_eq!(vec.capacity(), 64);
|
||||
|
||||
// Collect many
|
||||
//
|
||||
let bs = list(&["hello", " ", "world", ", one two three"]);
|
||||
|
||||
let vec: Vec<u8> = bs.collect()
|
||||
.wait().unwrap();
|
||||
|
||||
assert_eq!(vec, b"hello world, one two three");
|
||||
}
|
||||
|
||||
// ===== Test limit() =====
|
||||
|
||||
#[test]
|
||||
fn limit() {
|
||||
// Not limited
|
||||
|
||||
let res = one("hello world")
|
||||
.limit(100)
|
||||
.collect::<Vec<_>>()
|
||||
.wait().unwrap();
|
||||
|
||||
assert_eq!(res, b"hello world");
|
||||
|
||||
let res = list(&["hello", " ", "world"])
|
||||
.limit(100)
|
||||
.collect::<Vec<_>>()
|
||||
.wait().unwrap();
|
||||
|
||||
assert_eq!(res, b"hello world");
|
||||
|
||||
let res = list(&["hello", " ", "world"])
|
||||
.limit(11)
|
||||
.collect::<Vec<_>>()
|
||||
.wait().unwrap();
|
||||
|
||||
assert_eq!(res, b"hello world");
|
||||
|
||||
// Limited
|
||||
|
||||
let res = one("hello world")
|
||||
.limit(5)
|
||||
.collect::<Vec<_>>()
|
||||
.wait();
|
||||
|
||||
assert!(res.is_err());
|
||||
|
||||
let res = one("hello world")
|
||||
.limit(10)
|
||||
.collect::<Vec<_>>()
|
||||
.wait();
|
||||
|
||||
assert!(res.is_err());
|
||||
|
||||
let mut bs = list(&["hello", " ", "world"])
|
||||
.limit(9);
|
||||
|
||||
assert_buf_eq!(bs.poll_buf(), "hello");
|
||||
assert_buf_eq!(bs.poll_buf(), " ");
|
||||
assert!(bs.poll_buf().is_err());
|
||||
|
||||
let mut bs = list(&["hello", " ", "world"]);
|
||||
bs.size_hint.set_lower(11);
|
||||
let mut bs = bs.limit(9);
|
||||
|
||||
assert!(bs.poll_buf().is_err());
|
||||
}
|
||||
|
||||
// ===== BufStream impelmentations for misc types =====
|
||||
|
||||
#[test]
|
||||
fn str_buf_stream() {
|
||||
let mut bs = "hello world".to_string();
|
||||
assert_buf_eq!(bs.poll_buf(), "hello world");
|
||||
assert!(bs.is_empty());
|
||||
assert_none!(bs.poll_buf());
|
||||
|
||||
let mut bs = "hello world";
|
||||
assert_buf_eq!(bs.poll_buf(), "hello world");
|
||||
assert!(bs.is_empty());
|
||||
assert_none!(bs.poll_buf());
|
||||
}
|
||||
|
||||
// ===== Test utils =====
|
||||
|
||||
fn one(buf: &'static str) -> Mock {
|
||||
list(&[buf])
|
||||
}
|
||||
|
||||
fn list(bufs: &[&'static str]) -> Mock {
|
||||
let mut polls = VecDeque::new();
|
||||
|
||||
for &buf in bufs {
|
||||
polls.push_back(Ok(Ready(buf.as_bytes())));
|
||||
}
|
||||
|
||||
Mock {
|
||||
polls,
|
||||
size_hint: SizeHint::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_mock(values: &[Poll<&'static str, ()>]) -> Mock {
|
||||
let mut polls = VecDeque::new();
|
||||
|
||||
for &v in values {
|
||||
polls.push_back(match v {
|
||||
Ok(Ready(v)) => Ok(Ready(v.as_bytes())),
|
||||
Ok(NotReady) => Ok(NotReady),
|
||||
Err(e) => Err(e),
|
||||
});
|
||||
}
|
||||
|
||||
Mock {
|
||||
polls,
|
||||
size_hint: SizeHint::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Mock {
|
||||
polls: VecDeque<Poll<&'static [u8], ()>>,
|
||||
size_hint: SizeHint,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MockBuf {
|
||||
data: Cursor<&'static [u8]>,
|
||||
}
|
||||
|
||||
impl BufStream for Mock {
|
||||
type Item = MockBuf;
|
||||
type Error = ();
|
||||
|
||||
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
match self.polls.pop_front() {
|
||||
Some(Ok(Ready(value))) => Ok(Ready(Some(MockBuf::new(value)))),
|
||||
Some(Ok(NotReady)) => Ok(NotReady),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(Ready(None)),
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
self.size_hint.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl MockBuf {
|
||||
fn new(data: &'static [u8]) -> MockBuf {
|
||||
MockBuf {
|
||||
data: Cursor::new(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Buf for MockBuf {
|
||||
fn remaining(&self) -> usize {
|
||||
self.data.remaining()
|
||||
}
|
||||
|
||||
fn bytes(&self) -> &[u8] {
|
||||
self.data.bytes()
|
||||
}
|
||||
|
||||
fn advance(&mut self, cnt: usize) {
|
||||
self.data.advance(cnt)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "tokio-channel"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.0"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-channel/0.1.0"
|
||||
description = """
|
||||
Channels for asynchronous communication using Tokio.
|
||||
"""
|
||||
categories = ["asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.23"
|
||||
@@ -0,0 +1,51 @@
|
||||
Copyright (c) 2018 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.
|
||||
|
||||
Copyright (c) 2016 futures-rs 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.
|
||||
@@ -0,0 +1,14 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-channel/0.1.0")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
|
||||
//! Asynchronous channels.
|
||||
//!
|
||||
//! This crate provides channels that can be used to communicate between
|
||||
//! asynchronous tasks.
|
||||
|
||||
extern crate futures;
|
||||
|
||||
pub mod mpsc;
|
||||
pub mod oneshot;
|
||||
|
||||
mod lock;
|
||||
@@ -0,0 +1,105 @@
|
||||
//! A "mutex" which only supports `try_lock`
|
||||
//!
|
||||
//! As a futures library the eventual call to an event loop should be the only
|
||||
//! thing that ever blocks, so this is assisted with a fast user-space
|
||||
//! implementation of a lock that can only have a `try_lock` operation.
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
/// A "mutex" around a value, similar to `std::sync::Mutex<T>`.
|
||||
///
|
||||
/// This lock only supports the `try_lock` operation, however, and does not
|
||||
/// implement poisoning.
|
||||
#[derive(Debug)]
|
||||
pub struct Lock<T> {
|
||||
locked: AtomicBool,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
/// Sentinel representing an acquired lock through which the data can be
|
||||
/// accessed.
|
||||
pub struct TryLock<'a, T: 'a> {
|
||||
__ptr: &'a Lock<T>,
|
||||
}
|
||||
|
||||
// The `Lock` structure is basically just a `Mutex<T>`, and these two impls are
|
||||
// intended to mirror the standard library's corresponding impls for `Mutex<T>`.
|
||||
//
|
||||
// If a `T` is sendable across threads, so is the lock, and `T` must be sendable
|
||||
// across threads to be `Sync` because it allows mutable access from multiple
|
||||
// threads.
|
||||
unsafe impl<T: Send> Send for Lock<T> {}
|
||||
unsafe impl<T: Send> Sync for Lock<T> {}
|
||||
|
||||
impl<T> Lock<T> {
|
||||
/// Creates a new lock around the given value.
|
||||
pub fn new(t: T) -> Lock<T> {
|
||||
Lock {
|
||||
locked: AtomicBool::new(false),
|
||||
data: UnsafeCell::new(t),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to acquire this lock, returning whether the lock was acquired or
|
||||
/// not.
|
||||
///
|
||||
/// If `Some` is returned then the data this lock protects can be accessed
|
||||
/// through the sentinel. This sentinel allows both mutable and immutable
|
||||
/// access.
|
||||
///
|
||||
/// If `None` is returned then the lock is already locked, either elsewhere
|
||||
/// on this thread or on another thread.
|
||||
pub fn try_lock(&self) -> Option<TryLock<T>> {
|
||||
if !self.locked.swap(true, SeqCst) {
|
||||
Some(TryLock { __ptr: self })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for TryLock<'a, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
// The existence of `TryLock` represents that we own the lock, so we
|
||||
// can safely access the data here.
|
||||
unsafe { &*self.__ptr.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for TryLock<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
// The existence of `TryLock` represents that we own the lock, so we
|
||||
// can safely access the data here.
|
||||
//
|
||||
// Additionally, we're the *only* `TryLock` in existence so mutable
|
||||
// access should be ok.
|
||||
unsafe { &mut *self.__ptr.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for TryLock<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
self.__ptr.locked.store(false, SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Lock;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let a = Lock::new(1);
|
||||
let mut a1 = a.try_lock().unwrap();
|
||||
assert!(a.try_lock().is_none());
|
||||
assert_eq!(*a1, 1);
|
||||
*a1 = 2;
|
||||
drop(a1);
|
||||
assert_eq!(*a.try_lock().unwrap(), 2);
|
||||
assert_eq!(*a.try_lock().unwrap(), 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,989 @@
|
||||
//! A multi-producer, single-consumer, futures-aware, FIFO queue with back pressure.
|
||||
//!
|
||||
//! A channel can be used as a communication primitive between tasks running on
|
||||
//! `futures-rs` executors. Channel creation provides `Receiver` and `Sender`
|
||||
//! handles. `Receiver` implements `Stream` and allows a task to read values
|
||||
//! out of the channel. If there is no message to read from the channel, the
|
||||
//! current task will be notified when a new value is sent. `Sender` implements
|
||||
//! the `Sink` trait and allows a task to send messages into the channel. If
|
||||
//! the channel is at capacity, then send will be rejected and the task will be
|
||||
//! notified when additional capacity is available.
|
||||
//!
|
||||
//! # Disconnection
|
||||
//!
|
||||
//! When all `Sender` handles have been dropped, it is no longer possible to
|
||||
//! send values into the channel. This is considered the termination event of
|
||||
//! the stream. As such, `Sender::poll` will return `Ok(Ready(None))`.
|
||||
//!
|
||||
//! If the receiver handle is dropped, then messages can no longer be read out
|
||||
//! of the channel. In this case, a `send` will result in an error.
|
||||
//!
|
||||
//! # Clean Shutdown
|
||||
//!
|
||||
//! If the `Receiver` is simply dropped, then it is possible for there to be
|
||||
//! messages still in the channel that will not be processed. As such, it is
|
||||
//! usually desirable to perform a "clean" shutdown. To do this, the receiver
|
||||
//! will first call `close`, which will prevent any further messages to be sent
|
||||
//! into the channel. Then, the receiver consumes the channel to completion, at
|
||||
//! which point the receiver can be dropped.
|
||||
|
||||
// At the core, the channel uses an atomic FIFO queue for message passing. This
|
||||
// queue is used as the primary coordination primitive. In order to enforce
|
||||
// capacity limits and handle back pressure, a secondary FIFO queue is used to
|
||||
// send parked task handles.
|
||||
//
|
||||
// The general idea is that the channel is created with a `buffer` size of `n`.
|
||||
// The channel capacity is `n + num-senders`. Each sender gets one "guaranteed"
|
||||
// slot to hold a message. This allows `Sender` to know for a fact that a send
|
||||
// will succeed *before* starting to do the actual work of sending the value.
|
||||
// Since most of this work is lock-free, once the work starts, it is impossible
|
||||
// to safely revert.
|
||||
//
|
||||
// If the sender is unable to process a send operation, then the current
|
||||
// task is parked and the handle is sent on the parked task queue.
|
||||
//
|
||||
// Note that the implementation guarantees that the channel capacity will never
|
||||
// exceed the configured limit, however there is no *strict* guarantee that the
|
||||
// receiver will wake up a parked task *immediately* when a slot becomes
|
||||
// available. However, it will almost always unpark a task when a slot becomes
|
||||
// available and it is *guaranteed* that a sender will be unparked when the
|
||||
// message that caused the sender to become parked is read out of the channel.
|
||||
//
|
||||
// The steps for sending a message are roughly:
|
||||
//
|
||||
// 1) Increment the channel message count
|
||||
// 2) If the channel is at capacity, push the task handle onto the wait queue
|
||||
// 3) Push the message onto the message queue.
|
||||
//
|
||||
// The steps for receiving a message are roughly:
|
||||
//
|
||||
// 1) Pop a message from the message queue
|
||||
// 2) Pop a task handle from the wait queue
|
||||
// 3) Decrement the channel message count.
|
||||
//
|
||||
// It's important for the order of operations on lock-free structures to happen
|
||||
// in reverse order between the sender and receiver. This makes the message
|
||||
// queue the primary coordination structure and establishes the necessary
|
||||
// happens-before semantics required for the acquire / release semantics used
|
||||
// by the queue structure.
|
||||
|
||||
|
||||
|
||||
use mpsc::queue::{Queue, PopResult};
|
||||
|
||||
use futures::task::{self, Task};
|
||||
use futures::{Async, AsyncSink, Poll, StartSend, Sink, Stream};
|
||||
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
use std::any::Any;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::usize;
|
||||
|
||||
mod queue;
|
||||
|
||||
/// The transmission end of a channel which is used to send values.
|
||||
///
|
||||
/// This is created by the `channel` method.
|
||||
#[derive(Debug)]
|
||||
pub struct Sender<T> {
|
||||
// Channel state shared between the sender and receiver.
|
||||
inner: Arc<Inner<T>>,
|
||||
|
||||
// Handle to the task that is blocked on this sender. This handle is sent
|
||||
// to the receiver half in order to be notified when the sender becomes
|
||||
// unblocked.
|
||||
sender_task: Arc<Mutex<SenderTask>>,
|
||||
|
||||
// True if the sender might be blocked. This is an optimization to avoid
|
||||
// having to lock the mutex most of the time.
|
||||
maybe_parked: bool,
|
||||
}
|
||||
|
||||
/// The transmission end of a channel which is used to send values.
|
||||
///
|
||||
/// This is created by the `unbounded` method.
|
||||
#[derive(Debug)]
|
||||
pub struct UnboundedSender<T>(Sender<T>);
|
||||
|
||||
trait AssertKinds: Send + Sync + Clone {}
|
||||
impl AssertKinds for UnboundedSender<u32> {}
|
||||
|
||||
|
||||
/// The receiving end of a channel which implements the `Stream` trait.
|
||||
///
|
||||
/// This is a concrete implementation of a stream which can be used to represent
|
||||
/// a stream of values being computed elsewhere. This is created by the
|
||||
/// `channel` method.
|
||||
#[derive(Debug)]
|
||||
pub struct Receiver<T> {
|
||||
inner: Arc<Inner<T>>,
|
||||
}
|
||||
|
||||
/// Error type for sending, used when the receiving end of a channel is
|
||||
/// dropped
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct SendError<T>(T);
|
||||
|
||||
/// Error type returned from `try_send`
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct TrySendError<T> {
|
||||
kind: TrySendErrorKind<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
enum TrySendErrorKind<T> {
|
||||
Full(T),
|
||||
Disconnected(T),
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for SendError<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_tuple("SendError")
|
||||
.field(&"...")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for SendError<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "send failed because receiver is gone")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Any> Error for SendError<T>
|
||||
{
|
||||
fn description(&self) -> &str {
|
||||
"send failed because receiver is gone"
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SendError<T> {
|
||||
/// Returns the message that was attempted to be sent but failed.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for TrySendError<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_tuple("TrySendError")
|
||||
.field(&"...")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for TrySendError<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.is_full() {
|
||||
write!(fmt, "send failed because channel is full")
|
||||
} else {
|
||||
write!(fmt, "send failed because receiver is gone")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Any> Error for TrySendError<T> {
|
||||
fn description(&self) -> &str {
|
||||
if self.is_full() {
|
||||
"send failed because channel is full"
|
||||
} else {
|
||||
"send failed because receiver is gone"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TrySendError<T> {
|
||||
/// Returns true if this error is a result of the channel being full
|
||||
pub fn is_full(&self) -> bool {
|
||||
use self::TrySendErrorKind::*;
|
||||
|
||||
match self.kind {
|
||||
Full(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this error is a result of the receiver being dropped
|
||||
pub fn is_disconnected(&self) -> bool {
|
||||
use self::TrySendErrorKind::*;
|
||||
|
||||
match self.kind {
|
||||
Disconnected(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the message that was attempted to be sent but failed.
|
||||
pub fn into_inner(self) -> T {
|
||||
use self::TrySendErrorKind::*;
|
||||
|
||||
match self.kind {
|
||||
Full(v) | Disconnected(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner<T> {
|
||||
// Max buffer size of the channel. If `None` then the channel is unbounded.
|
||||
buffer: Option<usize>,
|
||||
|
||||
// Internal channel state. Consists of the number of messages stored in the
|
||||
// channel as well as a flag signalling that the channel is closed.
|
||||
state: AtomicUsize,
|
||||
|
||||
// Atomic, FIFO queue used to send messages to the receiver
|
||||
message_queue: Queue<Option<T>>,
|
||||
|
||||
// Atomic, FIFO queue used to send parked task handles to the receiver.
|
||||
parked_queue: Queue<Arc<Mutex<SenderTask>>>,
|
||||
|
||||
// Number of senders in existence
|
||||
num_senders: AtomicUsize,
|
||||
|
||||
// Handle to the receiver's task.
|
||||
recv_task: Mutex<ReceiverTask>,
|
||||
}
|
||||
|
||||
// Struct representation of `Inner::state`.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct State {
|
||||
// `true` when the channel is open
|
||||
is_open: bool,
|
||||
|
||||
// Number of messages in the channel
|
||||
num_messages: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReceiverTask {
|
||||
unparked: bool,
|
||||
task: Option<Task>,
|
||||
}
|
||||
|
||||
// Returned from Receiver::try_park()
|
||||
enum TryPark {
|
||||
Parked,
|
||||
Closed,
|
||||
NotEmpty,
|
||||
}
|
||||
|
||||
// The `is_open` flag is stored in the left-most bit of `Inner::state`
|
||||
const OPEN_MASK: usize = usize::MAX - (usize::MAX >> 1);
|
||||
|
||||
// When a new channel is created, it is created in the open state with no
|
||||
// pending messages.
|
||||
const INIT_STATE: usize = OPEN_MASK;
|
||||
|
||||
// The maximum number of messages that a channel can track is `usize::MAX >> 1`
|
||||
const MAX_CAPACITY: usize = !(OPEN_MASK);
|
||||
|
||||
// The maximum requested buffer size must be less than the maximum capacity of
|
||||
// a channel. This is because each sender gets a guaranteed slot.
|
||||
const MAX_BUFFER: usize = MAX_CAPACITY >> 1;
|
||||
|
||||
// Sent to the consumer to wake up blocked producers
|
||||
#[derive(Debug)]
|
||||
struct SenderTask {
|
||||
task: Option<Task>,
|
||||
is_parked: bool,
|
||||
}
|
||||
|
||||
impl SenderTask {
|
||||
fn new() -> Self {
|
||||
SenderTask {
|
||||
task: None,
|
||||
is_parked: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn notify(&mut self) {
|
||||
self.is_parked = false;
|
||||
|
||||
if let Some(task) = self.task.take() {
|
||||
task.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an in-memory channel implementation of the `Stream` trait with
|
||||
/// bounded capacity.
|
||||
///
|
||||
/// This method creates a concrete implementation of the `Stream` trait which
|
||||
/// can be used to send values across threads in a streaming fashion. This
|
||||
/// channel is unique in that it implements back pressure to ensure that the
|
||||
/// sender never outpaces the receiver. The channel capacity is equal to
|
||||
/// `buffer + num-senders`. In other words, each sender gets a guaranteed slot
|
||||
/// in the channel capacity, and on top of that there are `buffer` "first come,
|
||||
/// first serve" slots available to all senders.
|
||||
///
|
||||
/// The `Receiver` returned implements the `Stream` trait and has access to any
|
||||
/// number of the associated combinators for transforming the result.
|
||||
pub fn channel<T>(buffer: usize) -> (Sender<T>, Receiver<T>) {
|
||||
// Check that the requested buffer size does not exceed the maximum buffer
|
||||
// size permitted by the system.
|
||||
assert!(buffer < MAX_BUFFER, "requested buffer size too large");
|
||||
channel2(Some(buffer))
|
||||
}
|
||||
|
||||
/// Creates an in-memory channel implementation of the `Stream` trait with
|
||||
/// unbounded capacity.
|
||||
///
|
||||
/// This method creates a concrete implementation of the `Stream` trait which
|
||||
/// can be used to send values across threads in a streaming fashion. A `send`
|
||||
/// on this channel will always succeed as long as the receive half has not
|
||||
/// been closed. If the receiver falls behind, messages will be buffered
|
||||
/// internally.
|
||||
///
|
||||
/// **Note** that the amount of available system memory is an implicit bound to
|
||||
/// the channel. Using an `unbounded` channel has the ability of causing the
|
||||
/// process to run out of memory. In this case, the process will be aborted.
|
||||
pub fn unbounded<T>() -> (UnboundedSender<T>, Receiver<T>) {
|
||||
let (tx, rx) = channel2(None);
|
||||
(UnboundedSender(tx), rx)
|
||||
}
|
||||
|
||||
fn channel2<T>(buffer: Option<usize>) -> (Sender<T>, Receiver<T>) {
|
||||
let inner = Arc::new(Inner {
|
||||
buffer: buffer,
|
||||
state: AtomicUsize::new(INIT_STATE),
|
||||
message_queue: Queue::new(),
|
||||
parked_queue: Queue::new(),
|
||||
num_senders: AtomicUsize::new(1),
|
||||
recv_task: Mutex::new(ReceiverTask {
|
||||
unparked: false,
|
||||
task: None,
|
||||
}),
|
||||
});
|
||||
|
||||
let tx = Sender {
|
||||
inner: inner.clone(),
|
||||
sender_task: Arc::new(Mutex::new(SenderTask::new())),
|
||||
maybe_parked: false,
|
||||
};
|
||||
|
||||
let rx = Receiver {
|
||||
inner: inner,
|
||||
};
|
||||
|
||||
(tx, rx)
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* ===== impl Sender =====
|
||||
*
|
||||
*/
|
||||
|
||||
impl<T> Sender<T> {
|
||||
/// Attempts to send a message on this `Sender<T>` without blocking.
|
||||
///
|
||||
/// This function, unlike `start_send`, is safe to call whether it's being
|
||||
/// called on a task or not. Note that this function, however, will *not*
|
||||
/// attempt to block the current task if the message cannot be sent.
|
||||
///
|
||||
/// It is not recommended to call this function from inside of a future,
|
||||
/// only from an external thread where you've otherwise arranged to be
|
||||
/// notified when the channel is no longer full.
|
||||
pub fn try_send(&mut self, msg: T) -> Result<(), TrySendError<T>> {
|
||||
// If the sender is currently blocked, reject the message
|
||||
if !self.poll_unparked(false).is_ready() {
|
||||
return Err(TrySendError {
|
||||
kind: TrySendErrorKind::Full(msg),
|
||||
});
|
||||
}
|
||||
|
||||
// The channel has capacity to accept the message, so send it
|
||||
self.do_send(Some(msg), false)
|
||||
.map_err(|SendError(v)| {
|
||||
TrySendError {
|
||||
kind: TrySendErrorKind::Disconnected(v),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Do the send without failing
|
||||
// None means close
|
||||
fn do_send(&mut self, msg: Option<T>, do_park: bool) -> Result<(), SendError<T>> {
|
||||
// First, increment the number of messages contained by the channel.
|
||||
// This operation will also atomically determine if the sender task
|
||||
// should be parked.
|
||||
//
|
||||
// None is returned in the case that the channel has been closed by the
|
||||
// receiver. This happens when `Receiver::close` is called or the
|
||||
// receiver is dropped.
|
||||
let park_self = match self.inc_num_messages(msg.is_none()) {
|
||||
Some(park_self) => park_self,
|
||||
None => {
|
||||
// The receiver has closed the channel. Only abort if actually
|
||||
// sending a message. It is important that the stream
|
||||
// termination (None) is always sent. This technically means
|
||||
// that it is possible for the queue to contain the following
|
||||
// number of messages:
|
||||
//
|
||||
// num-senders + buffer + 1
|
||||
//
|
||||
if let Some(msg) = msg {
|
||||
return Err(SendError(msg));
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If the channel has reached capacity, then the sender task needs to
|
||||
// be parked. This will send the task handle on the parked task queue.
|
||||
//
|
||||
// However, when `do_send` is called while dropping the `Sender`,
|
||||
// `task::current()` can't be called safely. In this case, in order to
|
||||
// maintain internal consistency, a blank message is pushed onto the
|
||||
// parked task queue.
|
||||
if park_self {
|
||||
self.park(do_park);
|
||||
}
|
||||
|
||||
self.queue_push_and_signal(msg);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Do the send without parking current task.
|
||||
//
|
||||
// To be called from unbounded sender.
|
||||
fn do_send_nb(&self, msg: T) -> Result<(), SendError<T>> {
|
||||
match self.inc_num_messages(false) {
|
||||
Some(park_self) => assert!(!park_self),
|
||||
None => return Err(SendError(msg)),
|
||||
};
|
||||
|
||||
self.queue_push_and_signal(Some(msg));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Push message to the queue and signal to the receiver
|
||||
fn queue_push_and_signal(&self, msg: Option<T>) {
|
||||
// Push the message onto the message queue
|
||||
self.inner.message_queue.push(msg);
|
||||
|
||||
// Signal to the receiver that a message has been enqueued. If the
|
||||
// receiver is parked, this will unpark the task.
|
||||
self.signal();
|
||||
}
|
||||
|
||||
// Increment the number of queued messages. Returns if the sender should
|
||||
// block.
|
||||
fn inc_num_messages(&self, close: bool) -> Option<bool> {
|
||||
let mut curr = self.inner.state.load(SeqCst);
|
||||
|
||||
loop {
|
||||
let mut state = decode_state(curr);
|
||||
|
||||
// The receiver end closed the channel.
|
||||
if !state.is_open {
|
||||
return None;
|
||||
}
|
||||
|
||||
// This probably is never hit? Odds are the process will run out of
|
||||
// memory first. It may be worth to return something else in this
|
||||
// case?
|
||||
assert!(state.num_messages < MAX_CAPACITY, "buffer space exhausted; \
|
||||
sending this messages would overflow the state");
|
||||
|
||||
state.num_messages += 1;
|
||||
|
||||
// The channel is closed by all sender handles being dropped.
|
||||
if close {
|
||||
state.is_open = false;
|
||||
}
|
||||
|
||||
let next = encode_state(&state);
|
||||
match self.inner.state.compare_exchange(curr, next, SeqCst, SeqCst) {
|
||||
Ok(_) => {
|
||||
// Block if the current number of pending messages has exceeded
|
||||
// the configured buffer size
|
||||
let park_self = match self.inner.buffer {
|
||||
Some(buffer) => state.num_messages > buffer,
|
||||
None => false,
|
||||
};
|
||||
|
||||
return Some(park_self)
|
||||
}
|
||||
Err(actual) => curr = actual,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Signal to the receiver task that a message has been enqueued
|
||||
fn signal(&self) {
|
||||
// TODO
|
||||
// This logic can probably be improved by guarding the lock with an
|
||||
// atomic.
|
||||
//
|
||||
// Do this step first so that the lock is dropped when
|
||||
// `unpark` is called
|
||||
let task = {
|
||||
let mut recv_task = self.inner.recv_task.lock().unwrap();
|
||||
|
||||
// If the receiver has already been unparked, then there is nothing
|
||||
// more to do
|
||||
if recv_task.unparked {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setting this flag enables the receiving end to detect that
|
||||
// an unpark event happened in order to avoid unnecessarily
|
||||
// parking.
|
||||
recv_task.unparked = true;
|
||||
recv_task.task.take()
|
||||
};
|
||||
|
||||
if let Some(task) = task {
|
||||
task.notify();
|
||||
}
|
||||
}
|
||||
|
||||
fn park(&mut self, can_park: bool) {
|
||||
// TODO: clean up internal state if the task::current will fail
|
||||
|
||||
let task = if can_park {
|
||||
Some(task::current())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
{
|
||||
let mut sender = self.sender_task.lock().unwrap();
|
||||
sender.task = task;
|
||||
sender.is_parked = true;
|
||||
}
|
||||
|
||||
// Send handle over queue
|
||||
let t = self.sender_task.clone();
|
||||
self.inner.parked_queue.push(t);
|
||||
|
||||
// Check to make sure we weren't closed after we sent our task on the
|
||||
// queue
|
||||
let state = decode_state(self.inner.state.load(SeqCst));
|
||||
self.maybe_parked = state.is_open;
|
||||
}
|
||||
|
||||
/// Polls the channel to determine if there is guaranteed to be capacity to send at least one
|
||||
/// item without waiting.
|
||||
///
|
||||
/// Returns `Ok(Async::Ready(_))` if there is sufficient capacity, or returns
|
||||
/// `Ok(Async::NotReady)` if the channel is not guaranteed to have capacity. Returns
|
||||
/// `Err(SendError(_))` if the receiver has been dropped.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if called from outside the context of a task or future.
|
||||
pub fn poll_ready(&mut self) -> Poll<(), SendError<()>> {
|
||||
let state = decode_state(self.inner.state.load(SeqCst));
|
||||
if !state.is_open {
|
||||
return Err(SendError(()));
|
||||
}
|
||||
|
||||
Ok(self.poll_unparked(true))
|
||||
}
|
||||
|
||||
fn poll_unparked(&mut self, do_park: bool) -> Async<()> {
|
||||
// First check the `maybe_parked` variable. This avoids acquiring the
|
||||
// lock in most cases
|
||||
if self.maybe_parked {
|
||||
// Get a lock on the task handle
|
||||
let mut task = self.sender_task.lock().unwrap();
|
||||
|
||||
if !task.is_parked {
|
||||
self.maybe_parked = false;
|
||||
return Async::Ready(())
|
||||
}
|
||||
|
||||
// At this point, an unpark request is pending, so there will be an
|
||||
// unpark sometime in the future. We just need to make sure that
|
||||
// the correct task will be notified.
|
||||
//
|
||||
// Update the task in case the `Sender` has been moved to another
|
||||
// task
|
||||
task.task = if do_park {
|
||||
Some(task::current())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Async::NotReady
|
||||
} else {
|
||||
Async::Ready(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sink for Sender<T> {
|
||||
type SinkItem = T;
|
||||
type SinkError = SendError<T>;
|
||||
|
||||
fn start_send(&mut self, msg: T) -> StartSend<T, SendError<T>> {
|
||||
// If the sender is currently blocked, reject the message before doing
|
||||
// any work.
|
||||
if !self.poll_unparked(true).is_ready() {
|
||||
return Ok(AsyncSink::NotReady(msg));
|
||||
}
|
||||
|
||||
// The channel has capacity to accept the message, so send it.
|
||||
self.do_send(Some(msg), true)?;
|
||||
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), SendError<T>> {
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), SendError<T>> {
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UnboundedSender<T> {
|
||||
/// Sends the provided message along this channel.
|
||||
///
|
||||
/// This is an unbounded sender, so this function differs from `Sink::send`
|
||||
/// by ensuring the return type reflects that the channel is always ready to
|
||||
/// receive messages.
|
||||
#[deprecated(note = "renamed to `unbounded_send`")]
|
||||
#[doc(hidden)]
|
||||
pub fn send(&self, msg: T) -> Result<(), SendError<T>> {
|
||||
self.unbounded_send(msg)
|
||||
}
|
||||
|
||||
/// Sends the provided message along this channel.
|
||||
///
|
||||
/// This is an unbounded sender, so this function differs from `Sink::send`
|
||||
/// by ensuring the return type reflects that the channel is always ready to
|
||||
/// receive messages.
|
||||
pub fn unbounded_send(&self, msg: T) -> Result<(), SendError<T>> {
|
||||
self.0.do_send_nb(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sink for UnboundedSender<T> {
|
||||
type SinkItem = T;
|
||||
type SinkError = SendError<T>;
|
||||
|
||||
fn start_send(&mut self, msg: T) -> StartSend<T, SendError<T>> {
|
||||
self.0.start_send(msg)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), SendError<T>> {
|
||||
self.0.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), SendError<T>> {
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Sink for &'a UnboundedSender<T> {
|
||||
type SinkItem = T;
|
||||
type SinkError = SendError<T>;
|
||||
|
||||
fn start_send(&mut self, msg: T) -> StartSend<T, SendError<T>> {
|
||||
self.0.do_send_nb(msg)?;
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), SendError<T>> {
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), SendError<T>> {
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for UnboundedSender<T> {
|
||||
fn clone(&self) -> UnboundedSender<T> {
|
||||
UnboundedSender(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T> Clone for Sender<T> {
|
||||
fn clone(&self) -> Sender<T> {
|
||||
// Since this atomic op isn't actually guarding any memory and we don't
|
||||
// care about any orderings besides the ordering on the single atomic
|
||||
// variable, a relaxed ordering is acceptable.
|
||||
let mut curr = self.inner.num_senders.load(SeqCst);
|
||||
|
||||
loop {
|
||||
// If the maximum number of senders has been reached, then fail
|
||||
if curr == self.inner.max_senders() {
|
||||
panic!("cannot clone `Sender` -- too many outstanding senders");
|
||||
}
|
||||
|
||||
debug_assert!(curr < self.inner.max_senders());
|
||||
|
||||
let next = curr + 1;
|
||||
let actual = self.inner.num_senders.compare_and_swap(curr, next, SeqCst);
|
||||
|
||||
// The ABA problem doesn't matter here. We only care that the
|
||||
// number of senders never exceeds the maximum.
|
||||
if actual == curr {
|
||||
return Sender {
|
||||
inner: self.inner.clone(),
|
||||
sender_task: Arc::new(Mutex::new(SenderTask::new())),
|
||||
maybe_parked: false,
|
||||
};
|
||||
}
|
||||
|
||||
curr = actual;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Sender<T> {
|
||||
fn drop(&mut self) {
|
||||
// Ordering between variables don't matter here
|
||||
let prev = self.inner.num_senders.fetch_sub(1, SeqCst);
|
||||
|
||||
if prev == 1 {
|
||||
let _ = self.do_send(None, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* ===== impl Receiver =====
|
||||
*
|
||||
*/
|
||||
|
||||
impl<T> Receiver<T> {
|
||||
/// Closes the receiving half
|
||||
///
|
||||
/// This prevents any further messages from being sent on the channel while
|
||||
/// still enabling the receiver to drain messages that are buffered.
|
||||
pub fn close(&mut self) {
|
||||
let mut curr = self.inner.state.load(SeqCst);
|
||||
|
||||
loop {
|
||||
let mut state = decode_state(curr);
|
||||
|
||||
if !state.is_open {
|
||||
break
|
||||
}
|
||||
|
||||
state.is_open = false;
|
||||
|
||||
let next = encode_state(&state);
|
||||
match self.inner.state.compare_exchange(curr, next, SeqCst, SeqCst) {
|
||||
Ok(_) => break,
|
||||
Err(actual) => curr = actual,
|
||||
}
|
||||
}
|
||||
|
||||
// Wake up any threads waiting as they'll see that we've closed the
|
||||
// channel and will continue on their merry way.
|
||||
loop {
|
||||
match unsafe { self.inner.parked_queue.pop() } {
|
||||
PopResult::Data(task) => {
|
||||
task.lock().unwrap().notify();
|
||||
}
|
||||
PopResult::Empty => break,
|
||||
PopResult::Inconsistent => thread::yield_now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_message(&mut self) -> Async<Option<T>> {
|
||||
// Pop off a message
|
||||
loop {
|
||||
match unsafe { self.inner.message_queue.pop() } {
|
||||
PopResult::Data(msg) => {
|
||||
return Async::Ready(msg);
|
||||
}
|
||||
PopResult::Empty => {
|
||||
// The queue is empty, return NotReady
|
||||
return Async::NotReady;
|
||||
}
|
||||
PopResult::Inconsistent => {
|
||||
// Inconsistent means that there will be a message to pop
|
||||
// in a short time. This branch can only be reached if
|
||||
// values are being produced from another thread, so there
|
||||
// are a few ways that we can deal with this:
|
||||
//
|
||||
// 1) Spin
|
||||
// 2) thread::yield_now()
|
||||
// 3) task::current().unwrap() & return NotReady
|
||||
//
|
||||
// For now, thread::yield_now() is used, but it would
|
||||
// probably be better to spin a few times then yield.
|
||||
thread::yield_now();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unpark a single task handle if there is one pending in the parked queue
|
||||
fn unpark_one(&mut self) {
|
||||
loop {
|
||||
match unsafe { self.inner.parked_queue.pop() } {
|
||||
PopResult::Data(task) => {
|
||||
task.lock().unwrap().notify();
|
||||
return;
|
||||
}
|
||||
PopResult::Empty => {
|
||||
// Queue empty, no task to wake up.
|
||||
return;
|
||||
}
|
||||
PopResult::Inconsistent => {
|
||||
// Same as above
|
||||
thread::yield_now();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to park the receiver task
|
||||
fn try_park(&self) -> TryPark {
|
||||
let curr = self.inner.state.load(SeqCst);
|
||||
let state = decode_state(curr);
|
||||
|
||||
// If the channel is closed, then there is no need to park.
|
||||
if !state.is_open && state.num_messages == 0 {
|
||||
return TryPark::Closed;
|
||||
}
|
||||
|
||||
// First, track the task in the `recv_task` slot
|
||||
let mut recv_task = self.inner.recv_task.lock().unwrap();
|
||||
|
||||
if recv_task.unparked {
|
||||
// Consume the `unpark` signal without actually parking
|
||||
recv_task.unparked = false;
|
||||
return TryPark::NotEmpty;
|
||||
}
|
||||
|
||||
recv_task.task = Some(task::current());
|
||||
TryPark::Parked
|
||||
}
|
||||
|
||||
fn dec_num_messages(&self) {
|
||||
let mut curr = self.inner.state.load(SeqCst);
|
||||
|
||||
loop {
|
||||
let mut state = decode_state(curr);
|
||||
|
||||
state.num_messages -= 1;
|
||||
|
||||
let next = encode_state(&state);
|
||||
match self.inner.state.compare_exchange(curr, next, SeqCst, SeqCst) {
|
||||
Ok(_) => break,
|
||||
Err(actual) => curr = actual,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for Receiver<T> {
|
||||
type Item = T;
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<T>, ()> {
|
||||
loop {
|
||||
// Try to read a message off of the message queue.
|
||||
let msg = match self.next_message() {
|
||||
Async::Ready(msg) => msg,
|
||||
Async::NotReady => {
|
||||
// There are no messages to read, in this case, attempt to
|
||||
// park. The act of parking will verify that the channel is
|
||||
// still empty after the park operation has completed.
|
||||
match self.try_park() {
|
||||
TryPark::Parked => {
|
||||
// The task was parked, and the channel is still
|
||||
// empty, return NotReady.
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
TryPark::Closed => {
|
||||
// The channel is closed, there will be no further
|
||||
// messages.
|
||||
return Ok(Async::Ready(None));
|
||||
}
|
||||
TryPark::NotEmpty => {
|
||||
// A message has been sent while attempting to
|
||||
// park. Loop again, the next iteration is
|
||||
// guaranteed to get the message.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If there are any parked task handles in the parked queue, pop
|
||||
// one and unpark it.
|
||||
self.unpark_one();
|
||||
|
||||
// Decrement number of messages
|
||||
self.dec_num_messages();
|
||||
|
||||
// Return the message
|
||||
return Ok(Async::Ready(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Receiver<T> {
|
||||
fn drop(&mut self) {
|
||||
// Drain the channel of all pending messages
|
||||
self.close();
|
||||
while self.next_message().is_ready() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* ===== impl Inner =====
|
||||
*
|
||||
*/
|
||||
|
||||
impl<T> Inner<T> {
|
||||
// The return value is such that the total number of messages that can be
|
||||
// enqueued into the channel will never exceed MAX_CAPACITY
|
||||
fn max_senders(&self) -> usize {
|
||||
match self.buffer {
|
||||
Some(buffer) => MAX_CAPACITY - buffer,
|
||||
None => MAX_BUFFER,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for Inner<T> {}
|
||||
unsafe impl<T: Send> Sync for Inner<T> {}
|
||||
|
||||
/*
|
||||
*
|
||||
* ===== Helpers =====
|
||||
*
|
||||
*/
|
||||
|
||||
fn decode_state(num: usize) -> State {
|
||||
State {
|
||||
is_open: num & OPEN_MASK == OPEN_MASK,
|
||||
num_messages: num & MAX_CAPACITY,
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_state(state: &State) -> usize {
|
||||
let mut num = state.num_messages;
|
||||
|
||||
if state.is_open {
|
||||
num |= OPEN_MASK;
|
||||
}
|
||||
|
||||
num
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are
|
||||
* those of the authors and should not be interpreted as representing official
|
||||
* policies, either expressed or implied, of Dmitry Vyukov.
|
||||
*/
|
||||
|
||||
//! A mostly lock-free multi-producer, single consumer queue.
|
||||
//!
|
||||
//! This module contains an implementation of a concurrent MPSC queue. This
|
||||
//! queue can be used to share data between threads, and is also used as the
|
||||
//! building block of channels in rust.
|
||||
//!
|
||||
//! Note that the current implementation of this queue has a caveat of the `pop`
|
||||
//! method, and see the method for more information about it. Due to this
|
||||
//! caveat, this queue may not be appropriate for all use-cases.
|
||||
|
||||
// http://www.1024cores.net/home/lock-free-algorithms
|
||||
// /queues/non-intrusive-mpsc-node-based-queue
|
||||
|
||||
// NOTE: this implementation is lifted from the standard library and only
|
||||
// slightly modified
|
||||
|
||||
pub use self::PopResult::*;
|
||||
use std::prelude::v1::*;
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
||||
/// A result of the `pop` function.
|
||||
pub enum PopResult<T> {
|
||||
/// Some data has been popped
|
||||
Data(T),
|
||||
/// The queue is empty
|
||||
Empty,
|
||||
/// The queue is in an inconsistent state. Popping data should succeed, but
|
||||
/// some pushers have yet to make enough progress in order allow a pop to
|
||||
/// succeed. It is recommended that a pop() occur "in the near future" in
|
||||
/// order to see if the sender has made progress or not
|
||||
Inconsistent,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Node<T> {
|
||||
next: AtomicPtr<Node<T>>,
|
||||
value: Option<T>,
|
||||
}
|
||||
|
||||
/// The multi-producer single-consumer structure. This is not cloneable, but it
|
||||
/// may be safely shared so long as it is guaranteed that there is only one
|
||||
/// popper at a time (many pushers are allowed).
|
||||
#[derive(Debug)]
|
||||
pub struct Queue<T> {
|
||||
head: AtomicPtr<Node<T>>,
|
||||
tail: UnsafeCell<*mut Node<T>>,
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for Queue<T> { }
|
||||
unsafe impl<T: Send> Sync for Queue<T> { }
|
||||
|
||||
impl<T> Node<T> {
|
||||
unsafe fn new(v: Option<T>) -> *mut Node<T> {
|
||||
Box::into_raw(Box::new(Node {
|
||||
next: AtomicPtr::new(ptr::null_mut()),
|
||||
value: v,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Queue<T> {
|
||||
/// Creates a new queue that is safe to share among multiple producers and
|
||||
/// one consumer.
|
||||
pub fn new() -> Queue<T> {
|
||||
let stub = unsafe { Node::new(None) };
|
||||
Queue {
|
||||
head: AtomicPtr::new(stub),
|
||||
tail: UnsafeCell::new(stub),
|
||||
}
|
||||
}
|
||||
|
||||
/// Pushes a new value onto this queue.
|
||||
pub fn push(&self, t: T) {
|
||||
unsafe {
|
||||
let n = Node::new(Some(t));
|
||||
let prev = self.head.swap(n, Ordering::AcqRel);
|
||||
(*prev).next.store(n, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// Pops some data from this queue.
|
||||
///
|
||||
/// Note that the current implementation means that this function cannot
|
||||
/// return `Option<T>`. It is possible for this queue to be in an
|
||||
/// inconsistent state where many pushes have succeeded and completely
|
||||
/// finished, but pops cannot return `Some(t)`. This inconsistent state
|
||||
/// happens when a pusher is preempted at an inopportune moment.
|
||||
///
|
||||
/// This inconsistent state means that this queue does indeed have data, but
|
||||
/// it does not currently have access to it at this time.
|
||||
///
|
||||
/// This function is unsafe because only one thread can call it at a time.
|
||||
pub unsafe fn pop(&self) -> PopResult<T> {
|
||||
let tail = *self.tail.get();
|
||||
let next = (*tail).next.load(Ordering::Acquire);
|
||||
|
||||
if !next.is_null() {
|
||||
*self.tail.get() = next;
|
||||
assert!((*tail).value.is_none());
|
||||
assert!((*next).value.is_some());
|
||||
let ret = (*next).value.take().unwrap();
|
||||
drop(Box::from_raw(tail));
|
||||
return Data(ret);
|
||||
}
|
||||
|
||||
if self.head.load(Ordering::Acquire) == tail {Empty} else {Inconsistent}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Queue<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let mut cur = *self.tail.get();
|
||||
while !cur.is_null() {
|
||||
let next = (*cur).next.load(Ordering::Relaxed);
|
||||
drop(Box::from_raw(cur));
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
//! A one-shot, futures-aware channel
|
||||
|
||||
use lock::Lock;
|
||||
|
||||
use futures::{Future, Poll, Async};
|
||||
use futures::task::{self, Task};
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
/// A future representing the completion of a computation happening elsewhere in
|
||||
/// memory.
|
||||
///
|
||||
/// This is created by the `oneshot::channel` function.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct Receiver<T> {
|
||||
inner: Arc<Inner<T>>,
|
||||
}
|
||||
|
||||
/// Represents the completion half of a oneshot through which the result of a
|
||||
/// computation is signaled.
|
||||
///
|
||||
/// This is created by the `oneshot::channel` function.
|
||||
#[derive(Debug)]
|
||||
pub struct Sender<T> {
|
||||
inner: Arc<Inner<T>>,
|
||||
}
|
||||
|
||||
/// Internal state of the `Receiver`/`Sender` pair above. This is all used as
|
||||
/// the internal synchronization between the two for send/recv operations.
|
||||
#[derive(Debug)]
|
||||
struct Inner<T> {
|
||||
/// Indicates whether this oneshot is complete yet. This is filled in both
|
||||
/// by `Sender::drop` and by `Receiver::drop`, and both sides interpret it
|
||||
/// appropriately.
|
||||
///
|
||||
/// For `Receiver`, if this is `true`, then it's guaranteed that `data` is
|
||||
/// unlocked and ready to be inspected.
|
||||
///
|
||||
/// For `Sender` if this is `true` then the oneshot has gone away and it
|
||||
/// can return ready from `poll_cancel`.
|
||||
complete: AtomicBool,
|
||||
|
||||
/// The actual data being transferred as part of this `Receiver`. This is
|
||||
/// filled in by `Sender::complete` and read by `Receiver::poll`.
|
||||
///
|
||||
/// Note that this is protected by `Lock`, but it is in theory safe to
|
||||
/// replace with an `UnsafeCell` as it's actually protected by `complete`
|
||||
/// above. I wouldn't recommend doing this, however, unless someone is
|
||||
/// supremely confident in the various atomic orderings here and there.
|
||||
data: Lock<Option<T>>,
|
||||
|
||||
/// Field to store the task which is blocked in `Receiver::poll`.
|
||||
///
|
||||
/// This is filled in when a oneshot is polled but not ready yet. Note that
|
||||
/// the `Lock` here, unlike in `data` above, is important to resolve races.
|
||||
/// Both the `Receiver` and the `Sender` halves understand that if they
|
||||
/// can't acquire the lock then some important interference is happening.
|
||||
rx_task: Lock<Option<Task>>,
|
||||
|
||||
/// Like `rx_task` above, except for the task blocked in
|
||||
/// `Sender::poll_cancel`. Additionally, `Lock` cannot be `UnsafeCell`.
|
||||
tx_task: Lock<Option<Task>>,
|
||||
}
|
||||
|
||||
/// Creates a new futures-aware, one-shot channel.
|
||||
///
|
||||
/// This function is similar to Rust's channels found in the standard library.
|
||||
/// Two halves are returned, the first of which is a `Sender` handle, used to
|
||||
/// signal the end of a computation and provide its value. The second half is a
|
||||
/// `Receiver` which implements the `Future` trait, resolving to the value that
|
||||
/// was given to the `Sender` handle.
|
||||
///
|
||||
/// Each half can be separately owned and sent across threads/tasks.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// extern crate tokio_channel;
|
||||
/// extern crate futures;
|
||||
///
|
||||
/// use tokio_channel::oneshot;
|
||||
/// use futures::*;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let (p, c) = oneshot::channel::<i32>();
|
||||
///
|
||||
/// thread::spawn(|| {
|
||||
/// c.map(|i| {
|
||||
/// println!("got: {}", i);
|
||||
/// }).wait();
|
||||
/// });
|
||||
///
|
||||
/// p.send(3).unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
|
||||
let inner = Arc::new(Inner::new());
|
||||
let receiver = Receiver {
|
||||
inner: inner.clone(),
|
||||
};
|
||||
let sender = Sender {
|
||||
inner: inner,
|
||||
};
|
||||
(sender, receiver)
|
||||
}
|
||||
|
||||
impl<T> Inner<T> {
|
||||
fn new() -> Inner<T> {
|
||||
Inner {
|
||||
complete: AtomicBool::new(false),
|
||||
data: Lock::new(None),
|
||||
rx_task: Lock::new(None),
|
||||
tx_task: Lock::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn send(&self, t: T) -> Result<(), T> {
|
||||
if self.complete.load(SeqCst) {
|
||||
return Err(t)
|
||||
}
|
||||
|
||||
// Note that this lock acquisition may fail if the receiver
|
||||
// is closed and sets the `complete` flag to true, whereupon
|
||||
// the receiver may call `poll()`.
|
||||
if let Some(mut slot) = self.data.try_lock() {
|
||||
assert!(slot.is_none());
|
||||
*slot = Some(t);
|
||||
drop(slot);
|
||||
|
||||
// If the receiver called `close()` between the check at the
|
||||
// start of the function, and the lock being released, then
|
||||
// the receiver may not be around to receive it, so try to
|
||||
// pull it back out.
|
||||
if self.complete.load(SeqCst) {
|
||||
// If lock acquisition fails, then receiver is actually
|
||||
// receiving it, so we're good.
|
||||
if let Some(mut slot) = self.data.try_lock() {
|
||||
if let Some(t) = slot.take() {
|
||||
return Err(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
// Must have been closed
|
||||
Err(t)
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_cancel(&self) -> Poll<(), ()> {
|
||||
// Fast path up first, just read the flag and see if our other half is
|
||||
// gone. This flag is set both in our destructor and the oneshot
|
||||
// destructor, but our destructor hasn't run yet so if it's set then the
|
||||
// oneshot is gone.
|
||||
if self.complete.load(SeqCst) {
|
||||
return Ok(Async::Ready(()))
|
||||
}
|
||||
|
||||
// If our other half is not gone then we need to park our current task
|
||||
// and move it into the `notify_cancel` slot to get notified when it's
|
||||
// actually gone.
|
||||
//
|
||||
// If `try_lock` fails, then the `Receiver` is in the process of using
|
||||
// it, so we can deduce that it's now in the process of going away and
|
||||
// hence we're canceled. If it succeeds then we just store our handle.
|
||||
//
|
||||
// Crucially we then check `oneshot_gone` *again* before we return.
|
||||
// While we were storing our handle inside `notify_cancel` the `Receiver`
|
||||
// may have been dropped. The first thing it does is set the flag, and
|
||||
// if it fails to acquire the lock it assumes that we'll see the flag
|
||||
// later on. So... we then try to see the flag later on!
|
||||
let handle = task::current();
|
||||
match self.tx_task.try_lock() {
|
||||
Some(mut p) => *p = Some(handle),
|
||||
None => return Ok(Async::Ready(())),
|
||||
}
|
||||
if self.complete.load(SeqCst) {
|
||||
Ok(Async::Ready(()))
|
||||
} else {
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_canceled(&self) -> bool {
|
||||
self.complete.load(SeqCst)
|
||||
}
|
||||
|
||||
fn drop_tx(&self) {
|
||||
// Flag that we're a completed `Sender` and try to wake up a receiver.
|
||||
// Whether or not we actually stored any data will get picked up and
|
||||
// translated to either an item or cancellation.
|
||||
//
|
||||
// Note that if we fail to acquire the `rx_task` lock then that means
|
||||
// we're in one of two situations:
|
||||
//
|
||||
// 1. The receiver is trying to block in `poll`
|
||||
// 2. The receiver is being dropped
|
||||
//
|
||||
// In the first case it'll check the `complete` flag after it's done
|
||||
// blocking to see if it succeeded. In the latter case we don't need to
|
||||
// wake up anyone anyway. So in both cases it's ok to ignore the `None`
|
||||
// case of `try_lock` and bail out.
|
||||
//
|
||||
// The first case crucially depends on `Lock` using `SeqCst` ordering
|
||||
// under the hood. If it instead used `Release` / `Acquire` ordering,
|
||||
// then it would not necessarily synchronize with `inner.complete`
|
||||
// and deadlock might be possible, as was observed in
|
||||
// https://github.com/rust-lang-nursery/futures-rs/pull/219.
|
||||
self.complete.store(true, SeqCst);
|
||||
if let Some(mut slot) = self.rx_task.try_lock() {
|
||||
if let Some(task) = slot.take() {
|
||||
drop(slot);
|
||||
task.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn close_rx(&self) {
|
||||
// Flag our completion and then attempt to wake up the sender if it's
|
||||
// blocked. See comments in `drop` below for more info
|
||||
self.complete.store(true, SeqCst);
|
||||
if let Some(mut handle) = self.tx_task.try_lock() {
|
||||
if let Some(task) = handle.take() {
|
||||
drop(handle);
|
||||
task.notify()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn recv(&self) -> Poll<T, Canceled> {
|
||||
let mut done = false;
|
||||
|
||||
// Check to see if some data has arrived. If it hasn't then we need to
|
||||
// block our task.
|
||||
//
|
||||
// Note that the acquisition of the `rx_task` lock might fail below, but
|
||||
// the only situation where this can happen is during `Sender::drop`
|
||||
// when we are indeed completed already. If that's happening then we
|
||||
// know we're completed so keep going.
|
||||
if self.complete.load(SeqCst) {
|
||||
done = true;
|
||||
} else {
|
||||
let task = task::current();
|
||||
match self.rx_task.try_lock() {
|
||||
Some(mut slot) => *slot = Some(task),
|
||||
None => done = true,
|
||||
}
|
||||
}
|
||||
|
||||
// If we're `done` via one of the paths above, then look at the data and
|
||||
// figure out what the answer is. If, however, we stored `rx_task`
|
||||
// successfully above we need to check again if we're completed in case
|
||||
// a message was sent while `rx_task` was locked and couldn't notify us
|
||||
// otherwise.
|
||||
//
|
||||
// If we're not done, and we're not complete, though, then we've
|
||||
// successfully blocked our task and we return `NotReady`.
|
||||
if done || self.complete.load(SeqCst) {
|
||||
// If taking the lock fails, the sender will realise that the we're
|
||||
// `done` when it checks the `complete` flag on the way out, and will
|
||||
// treat the send as a failure.
|
||||
if let Some(mut slot) = self.data.try_lock() {
|
||||
if let Some(data) = slot.take() {
|
||||
return Ok(data.into());
|
||||
}
|
||||
}
|
||||
Err(Canceled)
|
||||
} else {
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
fn drop_rx(&self) {
|
||||
// Indicate to the `Sender` that we're done, so any future calls to
|
||||
// `poll_cancel` are weeded out.
|
||||
self.complete.store(true, SeqCst);
|
||||
|
||||
// If we've blocked a task then there's no need for it to stick around,
|
||||
// so we need to drop it. If this lock acquisition fails, though, then
|
||||
// it's just because our `Sender` is trying to take the task, so we
|
||||
// let them take care of that.
|
||||
if let Some(mut slot) = self.rx_task.try_lock() {
|
||||
let task = slot.take();
|
||||
drop(slot);
|
||||
drop(task);
|
||||
}
|
||||
|
||||
// Finally, if our `Sender` wants to get notified of us going away, it
|
||||
// would have stored something in `tx_task`. Here we try to peel that
|
||||
// out and unpark it.
|
||||
//
|
||||
// Note that the `try_lock` here may fail, but only if the `Sender` is
|
||||
// in the process of filling in the task. If that happens then we
|
||||
// already flagged `complete` and they'll pick that up above.
|
||||
if let Some(mut handle) = self.tx_task.try_lock() {
|
||||
if let Some(task) = handle.take() {
|
||||
drop(handle);
|
||||
task.notify()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sender<T> {
|
||||
#[deprecated(note = "renamed to `send`", since = "0.1.11")]
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "with-deprecated")]
|
||||
pub fn complete(self, t: T) {
|
||||
drop(self.send(t));
|
||||
}
|
||||
|
||||
/// Completes this oneshot with a successful result.
|
||||
///
|
||||
/// This function will consume `self` and indicate to the other end, the
|
||||
/// `Receiver`, that the value provided is the result of the computation this
|
||||
/// represents.
|
||||
///
|
||||
/// If the value is successfully enqueued for the remote end to receive,
|
||||
/// then `Ok(())` is returned. If the receiving end was deallocated before
|
||||
/// this function was called, however, then `Err` is returned with the value
|
||||
/// provided.
|
||||
pub fn send(self, t: T) -> Result<(), T> {
|
||||
self.inner.send(t)
|
||||
}
|
||||
|
||||
/// Polls this `Sender` half to detect whether the `Receiver` this has
|
||||
/// paired with has gone away.
|
||||
///
|
||||
/// This function can be used to learn about when the `Receiver` (consumer)
|
||||
/// half has gone away and nothing will be able to receive a message sent
|
||||
/// from `send`.
|
||||
///
|
||||
/// If `Ready` is returned then it means that the `Receiver` has disappeared
|
||||
/// and the result this `Sender` would otherwise produce should no longer
|
||||
/// be produced.
|
||||
///
|
||||
/// If `NotReady` is returned then the `Receiver` is still alive and may be
|
||||
/// able to receive a message if sent. The current task, however, is
|
||||
/// scheduled to receive a notification if the corresponding `Receiver` goes
|
||||
/// away.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Like `Future::poll`, this function will panic if it's not called from
|
||||
/// within the context of a task. In other words, this should only ever be
|
||||
/// called from inside another future.
|
||||
///
|
||||
/// If you're calling this function from a context that does not have a
|
||||
/// task, then you can use the `is_canceled` API instead.
|
||||
pub fn poll_cancel(&mut self) -> Poll<(), ()> {
|
||||
self.inner.poll_cancel()
|
||||
}
|
||||
|
||||
/// Tests to see whether this `Sender`'s corresponding `Receiver`
|
||||
/// has gone away.
|
||||
///
|
||||
/// This function can be used to learn about when the `Receiver` (consumer)
|
||||
/// half has gone away and nothing will be able to receive a message sent
|
||||
/// from `send`.
|
||||
///
|
||||
/// Note that this function is intended to *not* be used in the context of a
|
||||
/// future. If you're implementing a future you probably want to call the
|
||||
/// `poll_cancel` function which will block the current task if the
|
||||
/// cancellation hasn't happened yet. This can be useful when working on a
|
||||
/// non-futures related thread, though, which would otherwise panic if
|
||||
/// `poll_cancel` were called.
|
||||
pub fn is_canceled(&self) -> bool {
|
||||
self.inner.is_canceled()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Sender<T> {
|
||||
fn drop(&mut self) {
|
||||
self.inner.drop_tx()
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned from a `Receiver<T>` whenever the corresponding `Sender<T>`
|
||||
/// is dropped.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct Canceled;
|
||||
|
||||
impl fmt::Display for Canceled {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "oneshot canceled")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for Canceled {
|
||||
fn description(&self) -> &str {
|
||||
"oneshot canceled"
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Receiver<T> {
|
||||
/// Gracefully close this receiver, preventing sending any future messages.
|
||||
///
|
||||
/// Any `send` operation which happens after this method returns is
|
||||
/// guaranteed to fail. Once this method is called the normal `poll` method
|
||||
/// can be used to determine whether a message was actually sent or not. If
|
||||
/// `Canceled` is returned from `poll` then no message was sent.
|
||||
pub fn close(&mut self) {
|
||||
self.inner.close_rx()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Future for Receiver<T> {
|
||||
type Item = T;
|
||||
type Error = Canceled;
|
||||
|
||||
fn poll(&mut self) -> Poll<T, Canceled> {
|
||||
self.inner.recv()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Receiver<T> {
|
||||
fn drop(&mut self) {
|
||||
self.inner.drop_rx()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
extern crate tokio_channel;
|
||||
extern crate futures;
|
||||
|
||||
|
||||
use tokio_channel::mpsc::*;
|
||||
use futures::prelude::*;
|
||||
use std::thread;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let (mut sender, receiver) = channel(1);
|
||||
|
||||
let t = thread::spawn(move ||{
|
||||
while let Ok(s) = sender.send(42).wait() {
|
||||
sender = s;
|
||||
}
|
||||
});
|
||||
|
||||
receiver.take(3).for_each(|_| Ok(())).wait().unwrap();
|
||||
|
||||
t.join().unwrap()
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
extern crate tokio_channel;
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
|
||||
mod support;
|
||||
use support::*;
|
||||
|
||||
use tokio_channel::mpsc;
|
||||
use tokio_channel::oneshot;
|
||||
|
||||
use futures::prelude::*;
|
||||
use futures::future::lazy;
|
||||
|
||||
use std::thread;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
trait AssertSend: Send {}
|
||||
impl AssertSend for mpsc::Sender<i32> {}
|
||||
impl AssertSend for mpsc::Receiver<i32> {}
|
||||
|
||||
#[test]
|
||||
fn send_recv() {
|
||||
let (tx, rx) = mpsc::channel::<i32>(16);
|
||||
let mut rx = rx.wait();
|
||||
|
||||
tx.send(1).wait().unwrap();
|
||||
|
||||
assert_eq!(rx.next().unwrap(), Ok(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_recv_no_buffer() {
|
||||
let (mut tx, mut rx) = mpsc::channel::<i32>(0);
|
||||
|
||||
// Run on a task context
|
||||
lazy(move || {
|
||||
assert!(tx.poll_complete().unwrap().is_ready());
|
||||
assert!(tx.poll_ready().unwrap().is_ready());
|
||||
|
||||
// Send first message
|
||||
let res = tx.start_send(1).unwrap();
|
||||
assert!(is_ready(&res));
|
||||
assert!(tx.poll_ready().unwrap().is_not_ready());
|
||||
|
||||
// Send second message
|
||||
let res = tx.start_send(2).unwrap();
|
||||
assert!(!is_ready(&res));
|
||||
|
||||
// Take the value
|
||||
assert_eq!(rx.poll().unwrap(), Async::Ready(Some(1)));
|
||||
assert!(tx.poll_ready().unwrap().is_ready());
|
||||
|
||||
let res = tx.start_send(2).unwrap();
|
||||
assert!(is_ready(&res));
|
||||
assert!(tx.poll_ready().unwrap().is_not_ready());
|
||||
|
||||
// Take the value
|
||||
assert_eq!(rx.poll().unwrap(), Async::Ready(Some(2)));
|
||||
assert!(tx.poll_ready().unwrap().is_ready());
|
||||
|
||||
Ok::<(), ()>(())
|
||||
}).wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_shared_recv() {
|
||||
let (tx1, rx) = mpsc::channel::<i32>(16);
|
||||
let tx2 = tx1.clone();
|
||||
let mut rx = rx.wait();
|
||||
|
||||
tx1.send(1).wait().unwrap();
|
||||
assert_eq!(rx.next().unwrap(), Ok(1));
|
||||
|
||||
tx2.send(2).wait().unwrap();
|
||||
assert_eq!(rx.next().unwrap(), Ok(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_recv_threads() {
|
||||
let (tx, rx) = mpsc::channel::<i32>(16);
|
||||
let mut rx = rx.wait();
|
||||
|
||||
thread::spawn(move|| {
|
||||
tx.send(1).wait().unwrap();
|
||||
});
|
||||
|
||||
assert_eq!(rx.next().unwrap(), Ok(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_recv_threads_no_capacity() {
|
||||
let (tx, rx) = mpsc::channel::<i32>(0);
|
||||
let mut rx = rx.wait();
|
||||
|
||||
let (readytx, readyrx) = mpsc::channel::<()>(2);
|
||||
let mut readyrx = readyrx.wait();
|
||||
let t = thread::spawn(move|| {
|
||||
let readytx = readytx.sink_map_err(|_| panic!());
|
||||
let (a, b) = tx.send(1).join(readytx.send(())).wait().unwrap();
|
||||
a.send(2).join(b.send(())).wait().unwrap();
|
||||
});
|
||||
|
||||
drop(readyrx.next().unwrap());
|
||||
assert_eq!(rx.next().unwrap(), Ok(1));
|
||||
drop(readyrx.next().unwrap());
|
||||
assert_eq!(rx.next().unwrap(), Ok(2));
|
||||
|
||||
t.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recv_close_gets_none() {
|
||||
let (mut tx, mut rx) = mpsc::channel::<i32>(10);
|
||||
|
||||
// Run on a task context
|
||||
lazy(move || {
|
||||
rx.close();
|
||||
|
||||
assert_eq!(rx.poll(), Ok(Async::Ready(None)));
|
||||
assert!(tx.poll_ready().is_err());
|
||||
|
||||
drop(tx);
|
||||
|
||||
Ok::<(), ()>(())
|
||||
}).wait().unwrap();
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn tx_close_gets_none() {
|
||||
let (_, mut rx) = mpsc::channel::<i32>(10);
|
||||
|
||||
// Run on a task context
|
||||
lazy(move || {
|
||||
assert_eq!(rx.poll(), Ok(Async::Ready(None)));
|
||||
assert_eq!(rx.poll(), Ok(Async::Ready(None)));
|
||||
|
||||
Ok::<(), ()>(())
|
||||
}).wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stress_shared_unbounded() {
|
||||
const AMT: u32 = 10000;
|
||||
const NTHREADS: u32 = 8;
|
||||
let (tx, rx) = mpsc::unbounded::<i32>();
|
||||
let mut rx = rx.wait();
|
||||
|
||||
let t = thread::spawn(move|| {
|
||||
for _ in 0..AMT * NTHREADS {
|
||||
assert_eq!(rx.next().unwrap(), Ok(1));
|
||||
}
|
||||
|
||||
if rx.next().is_some() {
|
||||
panic!();
|
||||
}
|
||||
});
|
||||
|
||||
for _ in 0..NTHREADS {
|
||||
let tx = tx.clone();
|
||||
|
||||
thread::spawn(move|| {
|
||||
for _ in 0..AMT {
|
||||
tx.unbounded_send(1).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
drop(tx);
|
||||
|
||||
t.join().ok().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stress_shared_bounded_hard() {
|
||||
const AMT: u32 = 10000;
|
||||
const NTHREADS: u32 = 8;
|
||||
let (tx, rx) = mpsc::channel::<i32>(0);
|
||||
let mut rx = rx.wait();
|
||||
|
||||
let t = thread::spawn(move|| {
|
||||
for _ in 0..AMT * NTHREADS {
|
||||
assert_eq!(rx.next().unwrap(), Ok(1));
|
||||
}
|
||||
|
||||
if rx.next().is_some() {
|
||||
panic!();
|
||||
}
|
||||
});
|
||||
|
||||
for _ in 0..NTHREADS {
|
||||
let mut tx = tx.clone();
|
||||
|
||||
thread::spawn(move|| {
|
||||
for _ in 0..AMT {
|
||||
tx = tx.send(1).wait().unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
drop(tx);
|
||||
|
||||
t.join().ok().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stress_receiver_multi_task_bounded_hard() {
|
||||
const AMT: usize = 10_000;
|
||||
const NTHREADS: u32 = 2;
|
||||
|
||||
let (mut tx, rx) = mpsc::channel::<usize>(0);
|
||||
let rx = Arc::new(Mutex::new(Some(rx)));
|
||||
let n = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
let mut th = vec![];
|
||||
|
||||
for _ in 0..NTHREADS {
|
||||
let rx = rx.clone();
|
||||
let n = n.clone();
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
let mut i = 0;
|
||||
|
||||
loop {
|
||||
i += 1;
|
||||
let mut lock = rx.lock().ok().unwrap();
|
||||
|
||||
match lock.take() {
|
||||
Some(mut rx) => {
|
||||
if i % 5 == 0 {
|
||||
let (item, rest) = rx.into_future().wait().ok().unwrap();
|
||||
|
||||
if item.is_none() {
|
||||
break;
|
||||
}
|
||||
|
||||
n.fetch_add(1, Ordering::Relaxed);
|
||||
*lock = Some(rest);
|
||||
} else {
|
||||
// Just poll
|
||||
let n = n.clone();
|
||||
let r = lazy(move || {
|
||||
let r = match rx.poll().unwrap() {
|
||||
Async::Ready(Some(_)) => {
|
||||
n.fetch_add(1, Ordering::Relaxed);
|
||||
*lock = Some(rx);
|
||||
false
|
||||
}
|
||||
Async::Ready(None) => {
|
||||
true
|
||||
}
|
||||
Async::NotReady => {
|
||||
*lock = Some(rx);
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
Ok::<bool, ()>(r)
|
||||
}).wait().unwrap();
|
||||
|
||||
if r {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
th.push(t);
|
||||
}
|
||||
|
||||
for i in 0..AMT {
|
||||
tx = tx.send(i).wait().unwrap();
|
||||
}
|
||||
|
||||
drop(tx);
|
||||
|
||||
for t in th {
|
||||
t.join().unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(AMT, n.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
/// Stress test that receiver properly receives all the messages
|
||||
/// after sender dropped.
|
||||
#[test]
|
||||
fn stress_drop_sender() {
|
||||
fn list() -> Box<Stream<Item=i32, Error=u32>> {
|
||||
let (tx, rx) = mpsc::channel(1);
|
||||
tx.send(Ok(1))
|
||||
.and_then(|tx| tx.send(Ok(2)))
|
||||
.and_then(|tx| tx.send(Ok(3)))
|
||||
.forget();
|
||||
Box::new(rx.then(|r| r.unwrap()))
|
||||
}
|
||||
|
||||
for _ in 0..10000 {
|
||||
assert_eq!(list().wait().collect::<Result<Vec<_>, _>>(),
|
||||
Ok(vec![1, 2, 3]));
|
||||
}
|
||||
}
|
||||
|
||||
/// Stress test that after receiver dropped,
|
||||
/// no messages are lost.
|
||||
fn stress_close_receiver_iter() {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
let (unwritten_tx, unwritten_rx) = std::sync::mpsc::channel();
|
||||
let th = thread::spawn(move || {
|
||||
for i in 1.. {
|
||||
if let Err(_) = tx.unbounded_send(i) {
|
||||
unwritten_tx.send(i).expect("unwritten_tx");
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut rx = rx.wait();
|
||||
|
||||
// Read one message to make sure thread effectively started
|
||||
assert_eq!(Some(Ok(1)), rx.next());
|
||||
|
||||
rx.get_mut().close();
|
||||
|
||||
for i in 2.. {
|
||||
match rx.next() {
|
||||
Some(Ok(r)) => assert!(i == r),
|
||||
Some(Err(_)) => unreachable!(),
|
||||
None => {
|
||||
let unwritten = unwritten_rx.recv().expect("unwritten_rx");
|
||||
assert_eq!(unwritten, i);
|
||||
th.join().unwrap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stress_close_receiver() {
|
||||
for _ in 0..10000 {
|
||||
stress_close_receiver_iter();
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that after `poll_ready` indicates capacity a channel can always send without waiting.
|
||||
#[test]
|
||||
fn stress_poll_ready() {
|
||||
// A task which checks channel capacity using poll_ready, and pushes items onto the channel when
|
||||
// ready.
|
||||
struct SenderTask {
|
||||
sender: mpsc::Sender<u32>,
|
||||
count: u32,
|
||||
}
|
||||
impl Future for SenderTask {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
// In a loop, check if the channel is ready. If so, push an item onto the channel
|
||||
// (asserting that it doesn't attempt to block).
|
||||
while self.count > 0 {
|
||||
try_ready!(self.sender.poll_ready().map_err(|_| ()));
|
||||
assert!(self.sender.start_send(self.count).unwrap().is_ready());
|
||||
self.count -= 1;
|
||||
}
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
const AMT: u32 = 1000;
|
||||
const NTHREADS: u32 = 8;
|
||||
|
||||
/// Run a stress test using the specified channel capacity.
|
||||
fn stress(capacity: usize) {
|
||||
let (tx, rx) = mpsc::channel(capacity);
|
||||
let mut threads = Vec::new();
|
||||
for _ in 0..NTHREADS {
|
||||
let sender = tx.clone();
|
||||
threads.push(thread::spawn(move || {
|
||||
SenderTask {
|
||||
sender: sender,
|
||||
count: AMT,
|
||||
}.wait()
|
||||
}));
|
||||
}
|
||||
drop(tx);
|
||||
|
||||
let mut rx = rx.wait();
|
||||
for _ in 0..AMT * NTHREADS {
|
||||
assert!(rx.next().is_some());
|
||||
}
|
||||
|
||||
assert!(rx.next().is_none());
|
||||
|
||||
for thread in threads {
|
||||
thread.join().unwrap().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
stress(0);
|
||||
stress(1);
|
||||
stress(8);
|
||||
stress(16);
|
||||
}
|
||||
|
||||
fn is_ready<T>(res: &AsyncSink<T>) -> bool {
|
||||
match *res {
|
||||
AsyncSink::Ready => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_send_1() {
|
||||
const N: usize = 3000;
|
||||
let (mut tx, rx) = mpsc::channel(0);
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
for i in 0..N {
|
||||
loop {
|
||||
if tx.try_send(i).is_ok() {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
for (i, j) in rx.wait().enumerate() {
|
||||
assert_eq!(i, j.unwrap());
|
||||
}
|
||||
t.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_send_2() {
|
||||
let (mut tx, rx) = mpsc::channel(0);
|
||||
|
||||
tx.try_send("hello").unwrap();
|
||||
|
||||
let (readytx, readyrx) = oneshot::channel::<()>();
|
||||
|
||||
let th = thread::spawn(|| {
|
||||
lazy(|| {
|
||||
assert!(tx.start_send("fail").unwrap().is_not_ready());
|
||||
Ok::<_, ()>(())
|
||||
}).wait().unwrap();
|
||||
|
||||
drop(readytx);
|
||||
tx.send("goodbye").wait().unwrap();
|
||||
});
|
||||
|
||||
let mut rx = rx.wait();
|
||||
|
||||
drop(readyrx.wait());
|
||||
assert_eq!(rx.next(), Some(Ok("hello")));
|
||||
assert_eq!(rx.next(), Some(Ok("goodbye")));
|
||||
assert!(rx.next().is_none());
|
||||
|
||||
th.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_send_fail() {
|
||||
let (mut tx, rx) = mpsc::channel(0);
|
||||
let mut rx = rx.wait();
|
||||
|
||||
tx.try_send("hello").unwrap();
|
||||
|
||||
// This should fail
|
||||
assert!(tx.try_send("fail").is_err());
|
||||
|
||||
assert_eq!(rx.next(), Some(Ok("hello")));
|
||||
|
||||
tx.try_send("goodbye").unwrap();
|
||||
drop(tx);
|
||||
|
||||
assert_eq!(rx.next(), Some(Ok("goodbye")));
|
||||
assert!(rx.next().is_none());
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
extern crate tokio_channel;
|
||||
extern crate futures;
|
||||
|
||||
mod support;
|
||||
use support::*;
|
||||
|
||||
use tokio_channel::oneshot::*;
|
||||
|
||||
use futures::prelude::*;
|
||||
use futures::future::{lazy, ok};
|
||||
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
#[test]
|
||||
fn smoke_poll() {
|
||||
let (mut tx, rx) = channel::<u32>();
|
||||
|
||||
lazy(|| {
|
||||
assert!(tx.poll_cancel().unwrap().is_not_ready());
|
||||
assert!(tx.poll_cancel().unwrap().is_not_ready());
|
||||
drop(rx);
|
||||
assert!(tx.poll_cancel().unwrap().is_ready());
|
||||
assert!(tx.poll_cancel().unwrap().is_ready());
|
||||
ok::<(), ()>(())
|
||||
}).wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_notifies() {
|
||||
let (tx, rx) = channel::<u32>();
|
||||
let (tx2, rx2) = mpsc::channel();
|
||||
|
||||
WaitForCancel { tx: tx }.then(move |v| tx2.send(v)).forget();
|
||||
drop(rx);
|
||||
rx2.recv().unwrap().unwrap();
|
||||
}
|
||||
|
||||
struct WaitForCancel {
|
||||
tx: Sender<u32>,
|
||||
}
|
||||
|
||||
impl Future for WaitForCancel {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
self.tx.poll_cancel()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_lots() {
|
||||
let (tx, rx) = mpsc::channel::<(Sender<_>, mpsc::Sender<_>)>();
|
||||
let t = thread::spawn(move || {
|
||||
for (tx, tx2) in rx {
|
||||
WaitForCancel { tx: tx }.then(move |v| tx2.send(v)).forget();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
for _ in 0..20000 {
|
||||
let (otx, orx) = channel::<u32>();
|
||||
let (tx2, rx2) = mpsc::channel();
|
||||
tx.send((otx, tx2)).unwrap();
|
||||
drop(orx);
|
||||
rx2.recv().unwrap().unwrap();
|
||||
}
|
||||
drop(tx);
|
||||
|
||||
t.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn close() {
|
||||
let (mut tx, mut rx) = channel::<u32>();
|
||||
rx.close();
|
||||
assert!(rx.poll().is_err());
|
||||
assert!(tx.poll_cancel().unwrap().is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn close_wakes() {
|
||||
let (tx, mut rx) = channel::<u32>();
|
||||
let (tx2, rx2) = mpsc::channel();
|
||||
let t = thread::spawn(move || {
|
||||
rx.close();
|
||||
rx2.recv().unwrap();
|
||||
});
|
||||
WaitForCancel { tx: tx }.wait().unwrap();
|
||||
tx2.send(()).unwrap();
|
||||
t.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_canceled() {
|
||||
let (tx, rx) = channel::<u32>();
|
||||
assert!(!tx.is_canceled());
|
||||
drop(rx);
|
||||
assert!(tx.is_canceled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_sends() {
|
||||
let (tx, rx) = mpsc::channel::<Sender<_>>();
|
||||
let t = thread::spawn(move || {
|
||||
for otx in rx {
|
||||
let _ = otx.send(42);
|
||||
}
|
||||
});
|
||||
|
||||
for _ in 0..20000 {
|
||||
let (otx, mut orx) = channel::<u32>();
|
||||
tx.send(otx).unwrap();
|
||||
|
||||
orx.close();
|
||||
// Not necessary to wrap in a task because the implementation of oneshot
|
||||
// never calls `task::current()` if the channel has been closed already.
|
||||
let _ = orx.poll();
|
||||
}
|
||||
|
||||
drop(tx);
|
||||
t.join().unwrap();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
use futures::Future;
|
||||
|
||||
pub trait ForgetExt {
|
||||
fn forget(self);
|
||||
}
|
||||
|
||||
impl<F> ForgetExt for F
|
||||
where F: Future + Sized + Send + 'static,
|
||||
F::Item: Send,
|
||||
F::Error: Send
|
||||
{
|
||||
fn forget(self) {
|
||||
use std::thread;
|
||||
thread::spawn(|| self.wait());
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
# # 0.1.0 (June 13, 2018)
|
||||
# 0.1.1 (September 26, 2018)
|
||||
|
||||
* Allow setting max line length with `LinesCodec` (#632)
|
||||
|
||||
# 0.1.0 (June 13, 2018)
|
||||
|
||||
* Initial release (#353)
|
||||
|
||||
@@ -3,14 +3,15 @@ name = "tokio-codec"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update doc URL.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
authors = ["Carl Lerche <[email protected]>", "Bryan Burgers <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-codec/0.1"
|
||||
documentation = "https://docs.rs/tokio-codec/0.1.1/tokio_codec"
|
||||
description = """
|
||||
Utilities for encoding and decoding frames.
|
||||
"""
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-codec/0.1.1")]
|
||||
|
||||
//! Utilities for encoding and decoding frames.
|
||||
//!
|
||||
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
|
||||
@@ -10,9 +13,6 @@
|
||||
//! [`Stream`]: #
|
||||
//! [transports]: #
|
||||
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-codec/0.1.0")]
|
||||
|
||||
extern crate bytes;
|
||||
extern crate tokio_io;
|
||||
|
||||
|
||||
+123
-15
@@ -1,6 +1,6 @@
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use tokio_io::_tokio_codec::{Encoder, Decoder};
|
||||
use std::{io, str};
|
||||
use std::{cmp, io, str, usize};
|
||||
|
||||
/// A simple `Codec` implementation that splits up data into lines.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
@@ -12,12 +12,93 @@ pub struct LinesCodec {
|
||||
// The next time `decode` is called with `abcde\n`, the method will
|
||||
// only look at `de\n` before returning.
|
||||
next_index: usize,
|
||||
|
||||
/// The maximum length for a given line. If `usize::MAX`, lines will be
|
||||
/// read until a `\n` character is reached.
|
||||
max_length: usize,
|
||||
|
||||
/// Are we currently discarding the remainder of a line which was over
|
||||
/// the length limit?
|
||||
is_discarding: bool,
|
||||
}
|
||||
|
||||
impl LinesCodec {
|
||||
/// Returns a `LinesCodec` for splitting up data into lines.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The returned `LinesCodec` will not have an upper bound on the length
|
||||
/// 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
|
||||
pub fn new() -> LinesCodec {
|
||||
LinesCodec { next_index: 0 }
|
||||
LinesCodec {
|
||||
next_index: 0,
|
||||
max_length: usize::MAX,
|
||||
is_discarding: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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
|
||||
/// function as normal.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Setting a length limit is highly recommended for any `LinesCodec` which
|
||||
/// will be exposed to untrusted input. Otherwise, the size of the buffer
|
||||
/// that holds the line currently being read is unbounded. An attacker could
|
||||
/// exploit this unbounded buffer by sending an unbounded amount of input
|
||||
/// without any `\n` characters, causing unbounded memory consumption.
|
||||
///
|
||||
/// [`LengthError`]: ../struct.LengthError
|
||||
pub fn new_with_max_length(max_length: usize) -> Self {
|
||||
LinesCodec {
|
||||
max_length,
|
||||
..LinesCodec::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the maximum line length when decoding.
|
||||
///
|
||||
/// ```
|
||||
/// use std::usize;
|
||||
/// use tokio_codec::LinesCodec;
|
||||
///
|
||||
/// let codec = LinesCodec::new();
|
||||
/// assert_eq!(codec.max_length(), usize::MAX);
|
||||
/// ```
|
||||
/// ```
|
||||
/// use tokio_codec::LinesCodec;
|
||||
///
|
||||
/// let codec = LinesCodec::new_with_max_length(256);
|
||||
/// assert_eq!(codec.max_length(), 256);
|
||||
/// ```
|
||||
pub fn max_length(&self) -> usize {
|
||||
self.max_length
|
||||
}
|
||||
|
||||
fn discard(&mut self, newline_offset: Option<usize>, read_to: usize, buf: &mut BytesMut) {
|
||||
let discard_to = if let Some(offset) = newline_offset {
|
||||
// If we found a newline, discard up to that offset and
|
||||
// then stop discarding. On the next iteration, we'll try
|
||||
// to read a line normally.
|
||||
self.is_discarding = false;
|
||||
offset + self.next_index + 1
|
||||
} else {
|
||||
// Otherwise, we didn't find a newline, so we'll discard
|
||||
// everything we read. On the next iteration, we'll continue
|
||||
// discarding up to max_len bytes unless we find a newline.
|
||||
read_to
|
||||
};
|
||||
buf.advance(discard_to);
|
||||
self.next_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,22 +119,49 @@ fn without_carriage_return(s: &[u8]) -> &[u8] {
|
||||
|
||||
impl Decoder for LinesCodec {
|
||||
type Item = String;
|
||||
// TODO: in the next breaking change, this should be changed to a custom
|
||||
// error type that indicates the "max length exceeded" condition better.
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<String>, io::Error> {
|
||||
if let Some(newline_offset) =
|
||||
buf[self.next_index..].iter().position(|b| *b == b'\n')
|
||||
{
|
||||
let newline_index = newline_offset + self.next_index;
|
||||
let line = buf.split_to(newline_index + 1);
|
||||
let line = &line[..line.len()-1];
|
||||
let line = without_carriage_return(line);
|
||||
let line = utf8(line)?;
|
||||
self.next_index = 0;
|
||||
Ok(Some(line.to_string()))
|
||||
} else {
|
||||
self.next_index = buf.len();
|
||||
Ok(None)
|
||||
loop {
|
||||
// Determine how far into the buffer we'll search for a newline. If
|
||||
// there's no max_length set, we'll read to the end of the buffer.
|
||||
let read_to = cmp::min(self.max_length.saturating_add(1), buf.len());
|
||||
|
||||
let newline_offset = buf[self.next_index..read_to]
|
||||
.iter()
|
||||
.position(|b| *b == b'\n');
|
||||
|
||||
if self.is_discarding {
|
||||
self.discard(newline_offset, read_to, buf);
|
||||
} else {
|
||||
return if let Some(offset) = newline_offset {
|
||||
// Found a line!
|
||||
let newline_index = offset + self.next_index;
|
||||
self.next_index = 0;
|
||||
let line = buf.split_to(newline_index + 1);
|
||||
let line = &line[..line.len() - 1];
|
||||
let line = without_carriage_return(line);
|
||||
let line = utf8(line)?;
|
||||
|
||||
Ok(Some(line.to_string()))
|
||||
} else if buf.len() > self.max_length {
|
||||
// Reached the maximum length without finding a
|
||||
// newline, return an error and start discarding on the
|
||||
// next call.
|
||||
self.is_discarding = true;
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"line length limit exceeded"
|
||||
))
|
||||
} else {
|
||||
// We didn't find a line or reach the length limit, so the next
|
||||
// call will resume searching at the current offset.
|
||||
self.next_index = read_to;
|
||||
Ok(None)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,116 @@ fn lines_decoder() {
|
||||
assert_eq!(None, codec.decode_eof(buf).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines_decoder_max_length() {
|
||||
const MAX_LENGTH: usize = 6;
|
||||
|
||||
let mut codec = LinesCodec::new_with_max_length(MAX_LENGTH);
|
||||
let buf = &mut BytesMut::new();
|
||||
|
||||
buf.reserve(200);
|
||||
buf.put("line 1 is too long\nline 2\nline 3\r\nline 4\n\r\n\r");
|
||||
|
||||
assert!(codec.decode(buf).is_err());
|
||||
|
||||
let line = codec.decode(buf).unwrap().unwrap();
|
||||
assert!(line.len() <= MAX_LENGTH, "{:?}.len() <= {:?}", line, MAX_LENGTH);
|
||||
assert_eq!("line 2", line);
|
||||
|
||||
assert!(codec.decode(buf).is_err());
|
||||
|
||||
let line = codec.decode(buf).unwrap().unwrap();
|
||||
assert!(line.len() <= MAX_LENGTH, "{:?}.len() <= {:?}", line, MAX_LENGTH);
|
||||
assert_eq!("line 4", line);
|
||||
|
||||
let line = codec.decode(buf).unwrap().unwrap();
|
||||
assert!(line.len() <= MAX_LENGTH, "{:?}.len() <= {:?}", line, MAX_LENGTH);
|
||||
assert_eq!("", line);
|
||||
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
assert_eq!(None, codec.decode_eof(buf).unwrap());
|
||||
buf.put("k");
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
|
||||
let line = codec.decode_eof(buf).unwrap().unwrap();
|
||||
assert!(line.len() <= MAX_LENGTH, "{:?}.len() <= {:?}", line, MAX_LENGTH);
|
||||
assert_eq!("\rk", line);
|
||||
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
assert_eq!(None, codec.decode_eof(buf).unwrap());
|
||||
|
||||
// Line that's one character too long. This could cause an out of bounds
|
||||
// error if we peek at the next characters using slice indexing.
|
||||
// buf.put("aaabbbc");
|
||||
// assert!(codec.decode(buf).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines_decoder_max_length_underrun() {
|
||||
const MAX_LENGTH: usize = 6;
|
||||
|
||||
let mut codec = LinesCodec::new_with_max_length(MAX_LENGTH);
|
||||
let buf = &mut BytesMut::new();
|
||||
|
||||
buf.reserve(200);
|
||||
buf.put("line ");
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
buf.put("too l");
|
||||
assert!(codec.decode(buf).is_err());
|
||||
buf.put("ong\n");
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
|
||||
buf.put("line 2");
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
buf.put("\n");
|
||||
assert_eq!("line 2", codec.decode(buf).unwrap().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines_decoder_max_length_bursts() {
|
||||
const MAX_LENGTH: usize = 10;
|
||||
|
||||
let mut codec = LinesCodec::new_with_max_length(MAX_LENGTH);
|
||||
let buf = &mut BytesMut::new();
|
||||
|
||||
buf.reserve(200);
|
||||
buf.put("line ");
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
buf.put("too l");
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
buf.put("ong\n");
|
||||
assert!(codec.decode(buf).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines_decoder_max_length_big_burst() {
|
||||
const MAX_LENGTH: usize = 10;
|
||||
|
||||
let mut codec = LinesCodec::new_with_max_length(MAX_LENGTH);
|
||||
let buf = &mut BytesMut::new();
|
||||
|
||||
buf.reserve(200);
|
||||
buf.put("line ");
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
buf.put("too long!\n");
|
||||
assert!(codec.decode(buf).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines_decoder_max_length_newline_between_decodes() {
|
||||
const MAX_LENGTH: usize = 5;
|
||||
|
||||
let mut codec = LinesCodec::new_with_max_length(MAX_LENGTH);
|
||||
let buf = &mut BytesMut::new();
|
||||
|
||||
buf.reserve(200);
|
||||
buf.put("hello");
|
||||
assert_eq!(None, codec.decode(buf).unwrap());
|
||||
|
||||
buf.put("\nworld");
|
||||
assert_eq!("hello", codec.decode(buf).unwrap().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines_encoder() {
|
||||
let mut codec = LinesCodec::new();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user