mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
116
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
987ccfc8ac | ||
|
|
1bc6d75543 | ||
|
|
27148d6110 | ||
|
|
a1871b1480 | ||
|
|
acd08eb23d | ||
|
|
90b1a01010 | ||
|
|
676824988e | ||
|
|
46149f031e | ||
|
|
5510ba6dba | ||
|
|
b8f63308d7 | ||
|
|
4313d65b38 | ||
|
|
e780fccce4 | ||
|
|
b01e71b3d8 | ||
|
|
7f911b6b70 | ||
|
|
d88aba8d1c | ||
|
|
6fbef0a528 | ||
|
|
9be5f3f9ff | ||
|
|
e28856cffe | ||
|
|
85e3bd34af | ||
|
|
db4019d84a | ||
|
|
195c4b0496 | ||
|
|
619d3b163b | ||
|
|
5ff6e37c59 | ||
|
|
43d69d77e2 | ||
|
|
dbb04e310c | ||
|
|
0e2e07812a | ||
|
|
047d0b821c | ||
|
|
70f4fc481c | ||
|
|
7039f02bb2 | ||
|
|
4985e0c608 | ||
|
|
fd22090df8 | ||
|
|
02a5091885 | ||
|
|
80162306e7 | ||
|
|
ab595d0825 | ||
|
|
41a2245b85 | ||
|
|
7ca4f3ec4b | ||
|
|
0da649727c | ||
|
|
1cf5f73651 | ||
|
|
beb639a030 | ||
|
|
75ab7c9e9b | ||
|
|
cec9efeb7a | ||
|
|
f9345f99bb | ||
|
|
ab206b976c | ||
|
|
3d787b16c7 | ||
|
|
f513558076 | ||
|
|
d0cdcff8aa | ||
|
|
e3115231dd | ||
|
|
2d5aa82341 | ||
|
|
dd66096ea0 | ||
|
|
c08e73c8d4 | ||
|
|
d1d72dc1c8 | ||
|
|
27a42b980c | ||
|
|
7a50e09495 | ||
|
|
d7a556fe8b | ||
|
|
860ca79d62 | ||
|
|
7b98bf7da3 | ||
|
|
49774f6af1 | ||
|
|
ec22fb9843 | ||
|
|
ce2147d2b6 | ||
|
|
fca41d4e73 | ||
|
|
a69aca850c | ||
|
|
13c96187f8 | ||
|
|
61d4aa98e4 | ||
|
|
9d6d142bed | ||
|
|
95b0eec8af | ||
|
|
e1a07ce50c | ||
|
|
11e2af66a8 | ||
|
|
a4aae1459c | ||
|
|
12546d1d9c | ||
|
|
fbad6297c5 | ||
|
|
0ec8986b0b | ||
|
|
c6f8bdb249 | ||
|
|
c6f9a069a5 | ||
|
|
9f356d6244 | ||
|
|
13083153aa | ||
|
|
91f20e33a4 | ||
|
|
983e9d1b67 | ||
|
|
4c8f274db9 | ||
|
|
c980837581 | ||
|
|
eec370cae8 | ||
|
|
733d432b80 | ||
|
|
74c473d68f | ||
|
|
d95c697781 | ||
|
|
25e835c5b7 | ||
|
|
961aae41c4 | ||
|
|
74c73b218e | ||
|
|
7a49ebb65e | ||
|
|
a687922746 | ||
|
|
df299ced45 | ||
|
|
78d1fe0eb0 | ||
|
|
fc8cde383a | ||
|
|
76198f63d7 | ||
|
|
39dc5706b7 | ||
|
|
cbecb87797 | ||
|
|
f0bdf1980c | ||
|
|
5e2d93f060 | ||
|
|
9a8d087c69 | ||
|
|
30f59670c8 | ||
|
|
9e4ddaeaf3 | ||
|
|
03e2e864f3 | ||
|
|
c8a990eda4 | ||
|
|
1a5026324f | ||
|
|
fdf4aba621 | ||
|
|
201b6ce53a | ||
|
|
db69275202 | ||
|
|
af85cb3430 | ||
|
|
36f1a19ac8 | ||
|
|
6aa990ea75 | ||
|
|
760a7667d6 | ||
|
|
2283b63e9e | ||
|
|
8263e5f18d | ||
|
|
b3e57b60d0 | ||
|
|
1cd0ebfc5e | ||
|
|
4797d79950 | ||
|
|
e7d9ba7e51 | ||
|
|
527dc0a66f |
@@ -1,21 +0,0 @@
|
||||
image: Visual Studio 2017
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
platform: x64
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
platform: x86
|
||||
|
||||
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
|
||||
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --all --no-fail-fast --target %TARGET%
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
freebsd_instance:
|
||||
image: freebsd-12-0-release-amd64
|
||||
|
||||
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
|
||||
# same VM. The binary will be built in 32-bit mode, but will execute on a
|
||||
# 64-bit kernel and in a 64-bit environment. Our tests don't execute any of
|
||||
# the system's binaries, so the environment shouldn't matter.
|
||||
task:
|
||||
name: FreeBSD 12.0
|
||||
env:
|
||||
LOOM_MAX_DURATION: 10
|
||||
setup_script:
|
||||
- pkg install -y curl
|
||||
- curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
- sh rustup.sh -y
|
||||
- . $HOME/.cargo/env
|
||||
- rustup target add i686-unknown-freebsd
|
||||
cargo_cache:
|
||||
folder: $HOME/.cargo/registry
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all --no-fail-fast
|
||||
- cargo doc --all
|
||||
i686_test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all --exclude tokio-tls --no-fail-fast --target i686-unknown-freebsd
|
||||
before_cache_script:
|
||||
- rm -rf $HOME/.cargo/registry/index
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
---
|
||||
language: rust
|
||||
sudo: false
|
||||
cache:
|
||||
- apt
|
||||
- cargo
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
# to x-compile miniz-sys from sources
|
||||
- gcc-multilib
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# 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.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 && "$TSAN" == yes ]]
|
||||
then
|
||||
# Make sure the benchmarks compile
|
||||
cargo build --benches --all
|
||||
|
||||
export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0"
|
||||
export TSAN_OPTIONS="suppressions=`pwd`/ci/tsan"
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
# === tokio-timer ====
|
||||
|
||||
# Run address sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test -p tokio-timer --test hammer --target x86_64-unknown-linux-gnu
|
||||
|
||||
# Run thread sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
cargo test -p tokio-timer --test hammer --target x86_64-unknown-linux-gnu
|
||||
|
||||
# === tokio-threadpool ====
|
||||
|
||||
# Run address sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test -p tokio-threadpool --tests --target x86_64-unknown-linux-gnu
|
||||
|
||||
# Run thread sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
cargo test -p tokio-threadpool --tests --target x86_64-unknown-linux-gnu
|
||||
fi
|
||||
- |
|
||||
set -e
|
||||
if [[ "$TARGET" ]]
|
||||
then
|
||||
rustup target add $TARGET
|
||||
cargo check --all --exclude tokio-tls --target $TARGET
|
||||
cargo check --tests --all --exclude tokio-tls --target $TARGET
|
||||
else
|
||||
cargo test --all --no-fail-fast
|
||||
# Disable these tests for now as they are buggy
|
||||
#
|
||||
# cargo test --features unstable-futures
|
||||
# cargo test --manifest-path tokio-threadpool/Cargo.toml --features unstable-futures
|
||||
# cargo test --manifest-path tokio-reactor/Cargo.toml --features unstable-futures
|
||||
fi
|
||||
|
||||
before_deploy:
|
||||
- cargo doc --all --no-deps
|
||||
|
||||
deploy:
|
||||
provider: pages
|
||||
skip_cleanup: true
|
||||
github_token: $GH_TOKEN
|
||||
target_branch: gh-pages
|
||||
local_dir: target/doc
|
||||
on:
|
||||
branch: master
|
||||
repo: tokio-rs/tokio
|
||||
rust: stable
|
||||
condition: $TRAVIS_OS_NAME = "linux" && $TARGET = ""
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: iwlN1zfUCp/5BAAheqIRSFIqiM9zSwfIGcVDw/V7jHveqXyNzmCs7H58/cd90WLqonqpPX0t5GF66oTjms4v0DFjgXr/k4358qeSZaV082V3baNrVpCDHeCQV0SvKsfiYxDDJGSUL1WIUP+tqqDm4+ksZQP3LnwZojkABjWz5CBNt4kX+Wz5ZbYqtQoxyuZba5UyPY2CXJtubvCVPGMJULuUpklYxXZ4dWM2olzGgVJ8rE8udhSZ4ER4JgxB0KUx3/5TwHHzgyPEsWR4bKN6JzBjIczQofXUcUXXdoZBs23H/VhCpzKcn3/oJ8btVYPzwtdj5FmVB1aVR/gjPo2bSGi/sofq+LwL/1HJXkM+kjl8m2dLLcDBKqNYNERtVA1++LhkMWAFRgGYe8v8Ryxjiue1NF5LgAIA/fjK0uI1DELTzTf/TKrM+AtPDNTvhOft4/YD+hoImjwk6nv6PBb2TiTYnc79Qf4AZ65tv1qtsAUPuw4plLaccHQAO4ldYVXn4u9c+iisJwvovs6jo06bF3U3qtdI5gXsrI9+T25TrXvYb+IREo0MHzYEM0KlPFnscEArzC3eajuSd36ARFP3lDc+gp2RPs89iJjowms0eRyepp7Cu6XO3Cd2pfAX8AqvnmttZf4Nm51ONeiBPXPXItUkJm49MCpMJywU1IZcWZg=
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
@@ -1,6 +1,32 @@
|
||||
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.17 (March 13, 2019)
|
||||
|
||||
### Added
|
||||
- Propagate trace subscriber in the runtime (#966).
|
||||
|
||||
# 0.1.16 (March 1, 2019)
|
||||
|
||||
### Fixed
|
||||
- async-await: track latest nightly changes (#940).
|
||||
|
||||
### Added
|
||||
- `sync::Watch`, a single value broadcast channel (#922).
|
||||
- Async equivalent of read / write file helpers being added to `std` (#896).
|
||||
|
||||
# 0.1.15 (January 24, 2019)
|
||||
|
||||
### Added
|
||||
- Re-export tokio-sync APIs (#839).
|
||||
- Stream enumerate combinator (#832).
|
||||
|
||||
# 0.1.14 (January 6, 2019)
|
||||
|
||||
* Use feature flags to break up the crate, allowing users to pick & choose
|
||||
components (#808).
|
||||
* Export `UnixDatagram` and `UnixDatagramFramed` (#772).
|
||||
|
||||
# 0.1.13 (November 21, 2018)
|
||||
|
||||
* Fix `Runtime::reactor()` when no tasks are spawned (#721).
|
||||
|
||||
+59
-37
@@ -1,16 +1,17 @@
|
||||
[package]
|
||||
name = "tokio"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.13"
|
||||
version = "0.1.17"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio/0.1.13/tokio/"
|
||||
documentation = "https://docs.rs/tokio/0.1.17/tokio/"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
@@ -26,7 +27,6 @@ members = [
|
||||
"./",
|
||||
"tokio-async-await",
|
||||
"tokio-buf",
|
||||
"tokio-channel",
|
||||
"tokio-codec",
|
||||
"tokio-current-thread",
|
||||
"tokio-executor",
|
||||
@@ -34,15 +34,50 @@ members = [
|
||||
"tokio-io",
|
||||
"tokio-reactor",
|
||||
"tokio-signal",
|
||||
"tokio-sync",
|
||||
"tokio-threadpool",
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
"tokio-tls",
|
||||
"tokio-trace",
|
||||
"tokio-trace/tokio-trace-core",
|
||||
"tokio-udp",
|
||||
"tokio-uds",
|
||||
]
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"codec",
|
||||
"fs",
|
||||
"io",
|
||||
"reactor",
|
||||
"rt-full",
|
||||
"sync",
|
||||
"tcp",
|
||||
"timer",
|
||||
"udp",
|
||||
"uds",
|
||||
]
|
||||
|
||||
codec = ["io", "tokio-codec"]
|
||||
fs = ["tokio-fs"]
|
||||
io = ["bytes", "tokio-io"]
|
||||
reactor = ["io", "mio", "tokio-reactor"]
|
||||
rt-full = [
|
||||
"num_cpus",
|
||||
"reactor",
|
||||
"timer",
|
||||
"tokio-current-thread",
|
||||
"tokio-executor",
|
||||
"tokio-threadpool",
|
||||
"tokio-trace-core",
|
||||
]
|
||||
sync = ["tokio-sync"]
|
||||
tcp = ["tokio-tcp"]
|
||||
timer = ["tokio-timer"]
|
||||
udp = ["tokio-udp"]
|
||||
uds = ["tokio-uds"]
|
||||
|
||||
# This feature comes with no promise of stability. Things will
|
||||
# break with each patch release. Use at your own risk.
|
||||
async-await-preview = [
|
||||
@@ -54,29 +89,33 @@ 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.3", path = "tokio-current-thread" }
|
||||
tokio-io = { version = "0.1.6", path = "tokio-io" }
|
||||
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.8", path = "tokio-timer" }
|
||||
tokio-fs = { version = "0.1.3", path = "tokio-fs" }
|
||||
|
||||
# Only non-optional dependency...
|
||||
futures = "0.1.20"
|
||||
|
||||
# Everything else is optional...
|
||||
bytes = { version = "0.4", optional = true }
|
||||
num_cpus = { version = "1.8.0", optional = true }
|
||||
tokio-codec = { version = "0.1.0", path = "tokio-codec", optional = true }
|
||||
tokio-current-thread = { version = "0.1.3", path = "tokio-current-thread", optional = true }
|
||||
tokio-fs = { version = "0.1.6", path = "tokio-fs", optional = true }
|
||||
tokio-io = { version = "0.1.6", path = "tokio-io", optional = true }
|
||||
tokio-executor = { version = "0.1.5", path = "tokio-executor", optional = true }
|
||||
tokio-reactor = { version = "0.1.1", path = "tokio-reactor", optional = true }
|
||||
tokio-sync = { version = "0.1.3", path = "tokio-sync", optional = true }
|
||||
tokio-threadpool = { version = "0.1.8", path = "tokio-threadpool", optional = true }
|
||||
tokio-tcp = { version = "0.1.0", path = "tokio-tcp", optional = true }
|
||||
tokio-udp = { version = "0.1.0", path = "tokio-udp", optional = true }
|
||||
tokio-timer = { version = "0.2.8", path = "tokio-timer", optional = true }
|
||||
tokio-trace-core = { version = "0.1", path = "tokio-trace/tokio-trace-core", optional = true }
|
||||
|
||||
# Needed until `reactor` is removed from `tokio`.
|
||||
mio = "0.6.14"
|
||||
mio = { version = "0.6.14", optional = true }
|
||||
|
||||
# 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.1", path = "tokio-uds" }
|
||||
tokio-uds = { version = "0.2.1", path = "tokio-uds", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { version = "0.5", default-features = false }
|
||||
@@ -90,20 +129,3 @@ 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" }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018 Tokio Contributors
|
||||
Copyright (c) 2019 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
|
||||
@@ -14,29 +14,26 @@ the Rust programming language. It is:
|
||||
|
||||
[![Crates.io][crates-badge]][crates-url]
|
||||
[![MIT licensed][mit-badge]][mit-url]
|
||||
[![Travis Build Status][travis-badge]][travis-url]
|
||||
[![Appveyor Build Status][appveyor-badge]][appveyor-url]
|
||||
[![Build Status][azure-badge]][azure-url]
|
||||
[![Gitter chat][gitter-badge]][gitter-url]
|
||||
|
||||
[crates-badge]: https://img.shields.io/crates/v/tokio.svg
|
||||
[crates-url]: https://crates.io/crates/tokio
|
||||
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[mit-url]: LICENSE-MIT
|
||||
[travis-badge]: https://travis-ci.org/tokio-rs/tokio.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/tokio-rs/tokio
|
||||
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/s83yxhy9qeb58va7/branch/master?svg=true
|
||||
[appveyor-url]: https://ci.appveyor.com/project/carllerche/tokio/branch/master
|
||||
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.tokio?branchName=master
|
||||
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=1&branchName=master
|
||||
[gitter-badge]: https://img.shields.io/gitter/room/tokio-rs/tokio.svg
|
||||
[gitter-url]: https://gitter.im/tokio-rs/tokio
|
||||
|
||||
[Website](https://tokio.rs) |
|
||||
[Guides](https://tokio.rs/docs/getting-started/hello-world/) |
|
||||
[API Docs](https://docs.rs/tokio) |
|
||||
[API Docs](https://docs.rs/tokio/0.1.17/tokio) |
|
||||
[Chat](https://gitter.im/tokio-rs/tokio)
|
||||
|
||||
The API docs for the master branch are published [here][master-dox].
|
||||
|
||||
[master-dox]: https://tokio-rs.github.io/tokio/tokio/
|
||||
[master-dox]: https://tokio-rs.github.io/tokio/doc/tokio/
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
trigger: ["master"]
|
||||
pr: ["master"]
|
||||
|
||||
jobs:
|
||||
# Check formatting
|
||||
- template: ci/azure-rustfmt.yml
|
||||
parameters:
|
||||
name: rustfmt
|
||||
|
||||
# Test top level crate
|
||||
- template: ci/azure-test-stable.yml
|
||||
parameters:
|
||||
name: test_tokio
|
||||
displayName: Test tokio
|
||||
cross: true
|
||||
crates:
|
||||
- tokio
|
||||
|
||||
# Test crates that are platform specific
|
||||
- template: ci/azure-test-stable.yml
|
||||
parameters:
|
||||
name: test_sub_cross
|
||||
displayName: Test sub crates -
|
||||
cross: true
|
||||
crates:
|
||||
- tokio-fs
|
||||
- tokio-reactor
|
||||
- tokio-signal
|
||||
- tokio-tcp
|
||||
- tokio-tls
|
||||
- tokio-udp
|
||||
- tokio-uds
|
||||
|
||||
# Test crates that are NOT platform specific
|
||||
- template: ci/azure-test-stable.yml
|
||||
parameters:
|
||||
name: test_linux
|
||||
displayName: Test sub crates - Any
|
||||
crates:
|
||||
- tokio-buf
|
||||
- tokio-codec
|
||||
- tokio-current-thread
|
||||
- tokio-executor
|
||||
- tokio-io
|
||||
- tokio-sync
|
||||
- tokio-threadpool
|
||||
- tokio-timer
|
||||
- tokio-trace
|
||||
- tokio-trace/tokio-trace-core
|
||||
|
||||
- template: ci/azure-cargo-check.yml
|
||||
parameters:
|
||||
name: features
|
||||
displayName: Check feature permtuations
|
||||
rust: stable
|
||||
crates:
|
||||
tokio:
|
||||
- codec
|
||||
- fs
|
||||
- io
|
||||
- reactor
|
||||
- rt-full
|
||||
- tcp
|
||||
- timer
|
||||
- udp
|
||||
- uds
|
||||
tokio-buf:
|
||||
- util
|
||||
|
||||
# Check async / await
|
||||
- template: ci/azure-cargo-check.yml
|
||||
parameters:
|
||||
name: async_await
|
||||
displayName: Async / Await
|
||||
rust: nightly-2019-02-28
|
||||
noDefaultFeatures: ''
|
||||
benches: true
|
||||
crates:
|
||||
tokio:
|
||||
- async-await-preview
|
||||
|
||||
# Try cross compiling
|
||||
- template: ci/azure-cross-compile.yml
|
||||
parameters:
|
||||
name: cross_32bit_linux
|
||||
target: i686-unknown-linux-gnu
|
||||
|
||||
# This represents the minimum Rust version supported by
|
||||
# Tokio. Updating this should be done in a dedicated PR and
|
||||
# cannot be greater than two 0.x releases prior to the
|
||||
# current stable.
|
||||
#
|
||||
# Tests are not run as tests may require newer versions of
|
||||
# rust.
|
||||
- template: ci/azure-check-minrust.yml
|
||||
parameters:
|
||||
name: minrust
|
||||
rust_version: 1.26.0
|
||||
|
||||
- template: ci/azure-tsan.yml
|
||||
parameters:
|
||||
name: tsan
|
||||
|
||||
- template: ci/azure-deploy-docs.yml
|
||||
parameters:
|
||||
dependsOn:
|
||||
- rustfmt
|
||||
- test_tokio
|
||||
- test_sub_cross
|
||||
- test_linux
|
||||
- features
|
||||
- async_await
|
||||
- cross_32bit_linux
|
||||
- minrust
|
||||
- tsan
|
||||
+1
-3
@@ -10,8 +10,8 @@ use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::thread;
|
||||
|
||||
use futures::sync::oneshot;
|
||||
use futures::sync::mpsc;
|
||||
use futures::sync::oneshot;
|
||||
use futures::{Future, Poll, Sink, Stream};
|
||||
use test::Bencher;
|
||||
use tokio::net::UdpSocket;
|
||||
@@ -57,7 +57,6 @@ fn udp_echo_latency(b: &mut Bencher) {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
let child = thread::spawn(move || {
|
||||
|
||||
let socket = tokio::net::UdpSocket::bind(&any_addr).unwrap();
|
||||
tx.send(socket.local_addr().unwrap()).unwrap();
|
||||
|
||||
@@ -67,7 +66,6 @@ fn udp_echo_latency(b: &mut Bencher) {
|
||||
server.wait().unwrap();
|
||||
});
|
||||
|
||||
|
||||
let client = std::net::UdpSocket::bind(&any_addr).unwrap();
|
||||
|
||||
let server_addr = rx.wait().unwrap();
|
||||
|
||||
+8
-9
@@ -3,14 +3,13 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate test;
|
||||
extern crate mio;
|
||||
extern crate test;
|
||||
|
||||
use test::Bencher;
|
||||
|
||||
use mio::tcp::TcpListener;
|
||||
use mio::{Token, Ready, PollOpt};
|
||||
|
||||
use mio::{PollOpt, Ready, Token};
|
||||
|
||||
#[bench]
|
||||
fn mio_register_deregister(b: &mut Bencher) {
|
||||
@@ -22,8 +21,8 @@ fn mio_register_deregister(b: &mut Bencher) {
|
||||
const CLIENT: Token = Token(1);
|
||||
|
||||
b.iter(|| {
|
||||
poll.register(&sock, CLIENT, Ready::readable(),
|
||||
PollOpt::edge()).unwrap();
|
||||
poll.register(&sock, CLIENT, Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
poll.deregister(&sock).unwrap();
|
||||
});
|
||||
}
|
||||
@@ -36,12 +35,12 @@ fn mio_reregister(b: &mut Bencher) {
|
||||
let poll = mio::Poll::new().unwrap();
|
||||
|
||||
const CLIENT: Token = Token(1);
|
||||
poll.register(&sock, CLIENT, Ready::readable(),
|
||||
PollOpt::edge()).unwrap();
|
||||
poll.register(&sock, CLIENT, Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
poll.reregister(&sock, CLIENT, Ready::readable(),
|
||||
PollOpt::edge()).unwrap();
|
||||
poll.reregister(&sock, CLIENT, Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
});
|
||||
poll.deregister(&sock).unwrap();
|
||||
}
|
||||
|
||||
+62
-49
@@ -11,18 +11,18 @@ pub extern crate test;
|
||||
|
||||
mod prelude {
|
||||
pub use futures::*;
|
||||
pub use tokio::reactor::Reactor;
|
||||
pub use tokio::net::{TcpListener, TcpStream};
|
||||
pub use tokio::reactor::Reactor;
|
||||
pub use tokio_io::io::read_to_end;
|
||||
|
||||
pub use test::{self, Bencher};
|
||||
pub use std::io::{self, Read, Write};
|
||||
pub use std::thread;
|
||||
pub use std::time::Duration;
|
||||
pub use std::io::{self, Read, Write};
|
||||
pub use test::{self, Bencher};
|
||||
}
|
||||
|
||||
mod connect_churn {
|
||||
use ::prelude::*;
|
||||
use prelude::*;
|
||||
|
||||
const NUM: usize = 300;
|
||||
const CONCURRENT: usize = 8;
|
||||
@@ -36,25 +36,29 @@ mod connect_churn {
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
// Spawn a single future that accepts & drops connections
|
||||
let serve_incomings = listener.incoming()
|
||||
let serve_incomings = listener
|
||||
.incoming()
|
||||
.map_err(|e| panic!("server err: {:?}", e))
|
||||
.for_each(|_| Ok(()));
|
||||
|
||||
let connects = stream::iter_result((0..NUM).map(|_| {
|
||||
Ok(TcpStream::connect(&addr)
|
||||
.and_then(|sock| {
|
||||
sock.set_linger(Some(Duration::from_secs(0))).unwrap();
|
||||
read_to_end(sock, vec![])
|
||||
}))
|
||||
Ok(TcpStream::connect(&addr).and_then(|sock| {
|
||||
sock.set_linger(Some(Duration::from_secs(0))).unwrap();
|
||||
read_to_end(sock, vec![])
|
||||
}))
|
||||
}));
|
||||
|
||||
let connects_concurrent = connects.buffer_unordered(CONCURRENT)
|
||||
let connects_concurrent = connects
|
||||
.buffer_unordered(CONCURRENT)
|
||||
.map_err(|e| panic!("client err: {:?}", e))
|
||||
.for_each(|_| Ok(()));
|
||||
|
||||
serve_incomings.select(connects_concurrent)
|
||||
.map(|_| ()).map_err(|_| ())
|
||||
.wait().unwrap();
|
||||
serve_incomings
|
||||
.select(connects_concurrent)
|
||||
.map(|_| ())
|
||||
.map_err(|_| ())
|
||||
.wait()
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,8 +69,7 @@ mod connect_churn {
|
||||
// Spawn reactor thread
|
||||
let server_thread = thread::spawn(move || {
|
||||
// Bind the TCP listener
|
||||
let listener = TcpListener::bind(
|
||||
&"127.0.0.1:0".parse().unwrap()).unwrap();
|
||||
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
|
||||
|
||||
// Get the address being listened on.
|
||||
let addr = listener.local_addr().unwrap();
|
||||
@@ -75,47 +78,56 @@ mod connect_churn {
|
||||
addr_tx.send(addr).unwrap();
|
||||
|
||||
// Spawn a single future that accepts & drops connections
|
||||
let serve_incomings = listener.incoming()
|
||||
let serve_incomings = listener
|
||||
.incoming()
|
||||
.map_err(|e| panic!("server err: {:?}", e))
|
||||
.for_each(|_| Ok(()));
|
||||
|
||||
// Run server
|
||||
serve_incomings.select(shutdown_rx)
|
||||
.map(|_| ()).map_err(|_| ())
|
||||
.wait().unwrap();
|
||||
serve_incomings
|
||||
.select(shutdown_rx)
|
||||
.map(|_| ())
|
||||
.map_err(|_| ())
|
||||
.wait()
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
// Get the bind addr of the server
|
||||
let addr = addr_rx.wait().unwrap();
|
||||
|
||||
b.iter(move || {
|
||||
use std::sync::{Barrier, Arc};
|
||||
use std::sync::{Arc, Barrier};
|
||||
|
||||
// Create a barrier to coordinate threads
|
||||
let barrier = Arc::new(Barrier::new(n + 1));
|
||||
|
||||
// Spawn worker threads
|
||||
let threads: Vec<_> = (0..n).map(|_| {
|
||||
let barrier = barrier.clone();
|
||||
let addr = addr.clone();
|
||||
let threads: Vec<_> = (0..n)
|
||||
.map(|_| {
|
||||
let barrier = barrier.clone();
|
||||
let addr = addr.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
let connects = stream::iter_result((0..(NUM / n)).map(|_| {
|
||||
Ok(TcpStream::connect(&addr)
|
||||
.map_err(|e| panic!("connect err: {:?}", e))
|
||||
.and_then(|sock| {
|
||||
sock.set_linger(Some(Duration::from_secs(0))).unwrap();
|
||||
read_to_end(sock, vec![])
|
||||
}))
|
||||
}));
|
||||
thread::spawn(move || {
|
||||
let connects = stream::iter_result((0..(NUM / n)).map(|_| {
|
||||
Ok(TcpStream::connect(&addr)
|
||||
.map_err(|e| panic!("connect err: {:?}", e))
|
||||
.and_then(|sock| {
|
||||
sock.set_linger(Some(Duration::from_secs(0))).unwrap();
|
||||
read_to_end(sock, vec![])
|
||||
}))
|
||||
}));
|
||||
|
||||
barrier.wait();
|
||||
barrier.wait();
|
||||
|
||||
connects.buffer_unordered(CONCURRENT)
|
||||
.map_err(|e| panic!("client err: {:?}", e))
|
||||
.for_each(|_| Ok(())).wait().unwrap();
|
||||
connects
|
||||
.buffer_unordered(CONCURRENT)
|
||||
.map_err(|e| panic!("client err: {:?}", e))
|
||||
.for_each(|_| Ok(()))
|
||||
.wait()
|
||||
.unwrap();
|
||||
})
|
||||
})
|
||||
}).collect();
|
||||
.collect();
|
||||
|
||||
barrier.wait();
|
||||
|
||||
@@ -141,7 +153,7 @@ mod connect_churn {
|
||||
}
|
||||
|
||||
mod transfer {
|
||||
use ::prelude::*;
|
||||
use prelude::*;
|
||||
use std::{cmp, mem};
|
||||
|
||||
const MB: usize = 3 * 1024 * 1024;
|
||||
@@ -200,7 +212,8 @@ mod transfer {
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
// Spawn a single future that accepts 1 connection, Drain it and drops
|
||||
let server = listener.incoming()
|
||||
let server = listener
|
||||
.incoming()
|
||||
.into_future() // take the first connection
|
||||
.map_err(|(e, _other_incomings)| e)
|
||||
.map(|(connection, _other_incomings)| connection.unwrap())
|
||||
@@ -210,17 +223,17 @@ mod transfer {
|
||||
sock: sock,
|
||||
chunk: read_size,
|
||||
};
|
||||
drain.map(|_| ()).map_err(|e| panic!("server error: {:?}", e))
|
||||
drain
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("server error: {:?}", e))
|
||||
})
|
||||
.map_err(|e| panic!("server err: {:?}", e));
|
||||
|
||||
let client = TcpStream::connect(&addr)
|
||||
.and_then(move |sock| {
|
||||
Transfer {
|
||||
sock: sock,
|
||||
rem: MB,
|
||||
chunk: write_size,
|
||||
}
|
||||
.and_then(move |sock| Transfer {
|
||||
sock: sock,
|
||||
rem: MB,
|
||||
chunk: write_size,
|
||||
})
|
||||
.map_err(|e| panic!("client err: {:?}", e));
|
||||
|
||||
@@ -229,7 +242,7 @@ mod transfer {
|
||||
}
|
||||
|
||||
mod small_chunks {
|
||||
use ::prelude::*;
|
||||
use prelude::*;
|
||||
|
||||
#[bench]
|
||||
fn one_thread(b: &mut Bencher) {
|
||||
@@ -238,7 +251,7 @@ mod transfer {
|
||||
}
|
||||
|
||||
mod big_chunks {
|
||||
use ::prelude::*;
|
||||
use prelude::*;
|
||||
|
||||
#[bench]
|
||||
fn one_thread(b: &mut Bencher) {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
parameters:
|
||||
noDefaultFeatures: '--no-default-features'
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
- ${{ each feature in crate.value }}:
|
||||
- ${{ if eq(crate.key, 'tokio') }}:
|
||||
- script: cargo check ${{ parameters.noDefaultFeatures }} --features ${{ feature }}
|
||||
displayName: Check features = ${{ feature }}
|
||||
|
||||
- ${{ if not(eq(crate.key, 'tokio')) }}:
|
||||
- script: cargo check ${{ parameters.noDefaultFeatures }} --features ${{ feature }}
|
||||
displayName: Check `${{ crate.key }}`, features = ${{ feature }}
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate.key }}
|
||||
|
||||
- ${{ if parameters.benches }}:
|
||||
- script: cargo check --benches --all
|
||||
displayName: Check benchmarks
|
||||
@@ -0,0 +1,12 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Min supported Rust version
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust_version }}
|
||||
|
||||
- script: cargo check --all
|
||||
displayName: cargo check --all
|
||||
@@ -0,0 +1,21 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: stable
|
||||
|
||||
- script: sudo apt-get install gcc-multilib
|
||||
displayName: "Install gcc-multilib"
|
||||
|
||||
- script: rustup target add ${{ parameters.target }}
|
||||
displayName: "Add target"
|
||||
|
||||
- script: cargo check --all --exclude tokio-tls --target ${{ parameters.target }}
|
||||
displayName: Check source
|
||||
|
||||
- script: cargo check --tests --all --exclude tokio-tls --target ${{ parameters.target }}
|
||||
displayName: Check tests
|
||||
@@ -0,0 +1,38 @@
|
||||
parameters:
|
||||
dependsOn: []
|
||||
|
||||
jobs:
|
||||
- job: documentation
|
||||
displayName: 'Deploy API Documentation'
|
||||
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
dependsOn:
|
||||
- ${{ parameters.dependsOn }}
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: stable
|
||||
- script: |
|
||||
cargo doc --all --no-deps
|
||||
cp -R target/doc '$(Build.BinariesDirectory)'
|
||||
displayName: 'Generate Documentation'
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
git --version
|
||||
ls -la
|
||||
git init
|
||||
git config user.name 'Deployment Bot (from Azure Pipelines)'
|
||||
git config user.email '[email protected]'
|
||||
git config --global credential.helper 'store --file ~/.my-credentials'
|
||||
printf "protocol=https\nhost=github.com\nusername=carllerche\npassword=%s\n\n" "$GITHUB_TOKEN" | git credential-store --file ~/.my-credentials store
|
||||
git remote add origin https://github.com/tokio-rs/tokio
|
||||
git checkout -b gh-pages
|
||||
git add .
|
||||
git commit -m 'Deploy Tokio API documentation'
|
||||
git push -f origin gh-pages
|
||||
env:
|
||||
GITHUB_TOKEN: $(githubPersonalToken)
|
||||
workingDirectory: '$(Build.BinariesDirectory)'
|
||||
displayName: 'Deploy Documentation'
|
||||
@@ -0,0 +1,27 @@
|
||||
steps:
|
||||
# Linux and macOS.
|
||||
- script: |
|
||||
set -e
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN
|
||||
echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
|
||||
displayName: "Install rust (*nix)"
|
||||
condition: not(eq(variables['Agent.OS'], 'Windows_NT'))
|
||||
|
||||
# Windows.
|
||||
- script: |
|
||||
curl -sSf -o rustup-init.exe https://win.rustup.rs
|
||||
rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN%
|
||||
set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
|
||||
displayName: "Install rust (windows)"
|
||||
condition: eq(variables['Agent.OS'], 'Windows_NT')
|
||||
|
||||
# All platforms.
|
||||
- script: |
|
||||
rustc -Vv
|
||||
cargo -V
|
||||
displayName: Query rust and cargo versions
|
||||
@@ -0,0 +1,16 @@
|
||||
jobs:
|
||||
# Check formatting
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Check rustfmt
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: stable
|
||||
- script: |
|
||||
rustup component add rustfmt
|
||||
displayName: Install rustfmt
|
||||
- script: |
|
||||
cargo fmt --all -- --check
|
||||
displayName: Check formatting
|
||||
@@ -0,0 +1,36 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
vmImage: ubuntu-16.04
|
||||
|
||||
${{ if parameters.cross }}:
|
||||
MacOS:
|
||||
vmImage: macOS-10.13
|
||||
Windows:
|
||||
vmImage: vs2017-win2016
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: stable
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
- ${{ if eq(crate, 'tokio') }}:
|
||||
- script: cargo test
|
||||
env:
|
||||
LOOM_MAX_DURATION: 10
|
||||
CI: 'True'
|
||||
displayName: cargo test
|
||||
|
||||
- ${{ if not(eq(crate, 'tokio')) }}:
|
||||
- script: cargo test
|
||||
env:
|
||||
LOOM_MAX_DURATION: 10
|
||||
CI: 'True'
|
||||
displayName: cargo test -p ${{ crate }}
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
@@ -0,0 +1,34 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: TSAN
|
||||
strategy:
|
||||
matrix:
|
||||
Timer:
|
||||
cmd: cargo test -p tokio-timer --test hammer
|
||||
Threadpool:
|
||||
cmd: cargo test -p tokio-threadpool --tests
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: nightly-2018-11-18
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
# Make sure the benchmarks compile
|
||||
export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0"
|
||||
export TSAN_OPTIONS="suppressions=`pwd`/ci/tsan"
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
# Run address sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
$(cmd) --target x86_64-unknown-linux-gnu
|
||||
|
||||
# Run thread sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
$(cmd) --target x86_64-unknown-linux-gnu
|
||||
displayName: TSAN / MSAN
|
||||
env:
|
||||
TSAN: yes
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
//! A chat server that broadcasts a message to all connections.
|
||||
//!
|
||||
//! This is a line-based server which accepts connections, reads lines from
|
||||
//! those connections, and broadcasts the lines to all other connected clients.
|
||||
//!
|
||||
//! This example is similar to chat.rs, but uses combinators and a much more
|
||||
//! functional style.
|
||||
//!
|
||||
//! Because we are here running the reactor/executor on the same thread instead
|
||||
//! of a threadpool, we can avoid full synchronization with Arc + Mutex and use
|
||||
//! Rc + RefCell instead. The max performance is however limited to a CPU HW
|
||||
//! thread.
|
||||
//!
|
||||
//! You can test this out by running:
|
||||
//!
|
||||
//! cargo run --example chat-combinator-current-thread
|
||||
//!
|
||||
//! And then in another window run:
|
||||
//!
|
||||
//! cargo run --example connect 127.0.0.1:8080
|
||||
//!
|
||||
//! You can run the second command in multiple windows and then chat between the
|
||||
//! two, seeing the messages from the other client as they're received. For all
|
||||
//! connected clients they'll all join the same room and see everyone else's
|
||||
//! messages.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::current_thread::{Runtime, TaskExecutor};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::BufReader;
|
||||
use std::iter;
|
||||
use std::rc::Rc;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
|
||||
// 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()?;
|
||||
|
||||
let socket = TcpListener::bind(&addr)?;
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// This is running on the Tokio current_thread runtime, so it will be single-
|
||||
// threaded. The `Rc<RefCell<...>>` allows state to be shared across the tasks.
|
||||
let connections = Rc::new(RefCell::new(HashMap::new()));
|
||||
|
||||
// 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);
|
||||
e
|
||||
})
|
||||
.for_each(move |stream| {
|
||||
// The client's socket address
|
||||
let addr = stream.peer_addr()?;
|
||||
|
||||
println!("New Connection: {}", addr);
|
||||
|
||||
// Split the TcpStream into two separate handles. One handle for reading
|
||||
// and one handle for writing. This lets us use separate tasks for
|
||||
// reading and writing.
|
||||
let (reader, writer) = stream.split();
|
||||
|
||||
// Create a channel for our stream, which other sockets will use to
|
||||
// send us messages. Then register our address with the stream to send
|
||||
// data to us.
|
||||
let (tx, rx) = futures::sync::mpsc::unbounded();
|
||||
let mut conns = connections.borrow_mut();
|
||||
conns.insert(addr, tx);
|
||||
|
||||
// Define here what we do for the actual I/O. That is, read a bunch of
|
||||
// lines from the socket and dispatch them while we also write any lines
|
||||
// from other sockets.
|
||||
let connections_inner = connections.clone();
|
||||
let reader = BufReader::new(reader);
|
||||
|
||||
// Model the read portion of this socket by mapping an infinite
|
||||
// iterator to each line off the socket. This "loop" is then
|
||||
// terminated with an error once we hit EOF on the socket.
|
||||
let iter = stream::iter_ok::<_, io::Error>(iter::repeat(()));
|
||||
|
||||
let socket_reader = iter.fold(reader, move |reader, _| {
|
||||
// Read a line off the socket, failing if we're at EOF
|
||||
let line = io::read_until(reader, b'\n', Vec::new());
|
||||
let line = line.and_then(|(reader, vec)| {
|
||||
if vec.len() == 0 {
|
||||
Err(io::Error::new(io::ErrorKind::BrokenPipe, "broken pipe"))
|
||||
} else {
|
||||
Ok((reader, vec))
|
||||
}
|
||||
});
|
||||
|
||||
// Convert the bytes we read into a string, and then send that
|
||||
// string to all other connected clients.
|
||||
let line = line.map(|(reader, vec)| (reader, String::from_utf8(vec)));
|
||||
|
||||
// Move the connection state into the closure below.
|
||||
let connections = connections_inner.clone();
|
||||
|
||||
line.map(move |(reader, message)| {
|
||||
println!("{}: {:?}", addr, message);
|
||||
let mut conns = connections.borrow_mut();
|
||||
|
||||
if let Ok(msg) = message {
|
||||
// For each open connection except the sender, send the
|
||||
// string via the channel.
|
||||
let iter = conns
|
||||
.iter_mut()
|
||||
.filter(|&(&k, _)| k != addr)
|
||||
.map(|(_, v)| v);
|
||||
for tx in iter {
|
||||
tx.unbounded_send(format!("{}: {}", addr, msg)).unwrap();
|
||||
}
|
||||
} else {
|
||||
let tx = conns.get_mut(&addr).unwrap();
|
||||
tx.unbounded_send("You didn't send valid UTF-8.".to_string())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
reader
|
||||
})
|
||||
});
|
||||
|
||||
// Whenever we receive a string on the Receiver, we write it to
|
||||
// `WriteHalf<TcpStream>`.
|
||||
let socket_writer = rx.fold(writer, |writer, msg| {
|
||||
let amt = io::write_all(writer, msg.into_bytes());
|
||||
let amt = amt.map(|(writer, _)| writer);
|
||||
amt.map_err(|_| ())
|
||||
});
|
||||
|
||||
// Now that we've got futures representing each half of the socket, we
|
||||
// use the `select` combinator to wait for either half to be done to
|
||||
// tear down the other. Then we spawn off the result.
|
||||
let connections = connections.clone();
|
||||
let socket_reader = socket_reader.map_err(|_| ());
|
||||
let connection = socket_reader.map(|_| ()).select(socket_writer.map(|_| ()));
|
||||
|
||||
// Spawn locally a task to process the connection
|
||||
TaskExecutor::current()
|
||||
.spawn_local(Box::new(connection.then(move |_| {
|
||||
let mut conns = connections.borrow_mut();
|
||||
conns.remove(&addr);
|
||||
println!("Connection {} closed.", addr);
|
||||
Ok(())
|
||||
})))
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| println!("error occurred: {:?}", err));
|
||||
|
||||
// Spawn srv itself
|
||||
runtime.spawn(srv);
|
||||
|
||||
// Execute server
|
||||
runtime.run().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
+16
-12
@@ -21,17 +21,17 @@
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::io;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::iter;
|
||||
use std::env;
|
||||
use std::io::{BufReader};
|
||||
use std::io::BufReader;
|
||||
use std::iter;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
@@ -48,8 +48,12 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
|
||||
// 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); e})
|
||||
let srv = socket
|
||||
.incoming()
|
||||
.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()?;
|
||||
@@ -91,9 +95,7 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
|
||||
// Convert the bytes we read into a string, and then send that
|
||||
// string to all other connected clients.
|
||||
let line = line.map(|(reader, vec)| {
|
||||
(reader, String::from_utf8(vec))
|
||||
});
|
||||
let line = line.map(|(reader, vec)| (reader, String::from_utf8(vec)));
|
||||
|
||||
// Move the connection state into the closure below.
|
||||
let connections = connections_inner.clone();
|
||||
@@ -105,15 +107,17 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
if let Ok(msg) = message {
|
||||
// For each open connection except the sender, send the
|
||||
// string via the channel.
|
||||
let iter = conns.iter_mut()
|
||||
.filter(|&(&k, _)| k != addr)
|
||||
.map(|(_, v)| v);
|
||||
let iter = conns
|
||||
.iter_mut()
|
||||
.filter(|&(&k, _)| k != addr)
|
||||
.map(|(_, v)| v);
|
||||
for tx in iter {
|
||||
tx.unbounded_send(format!("{}: {}", addr, msg)).unwrap();
|
||||
}
|
||||
} else {
|
||||
let tx = conns.get_mut(&addr).unwrap();
|
||||
tx.unbounded_send("You didn't send valid UTF-8.".to_string()).unwrap();
|
||||
tx.unbounded_send("You didn't send valid UTF-8.".to_string())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
reader
|
||||
|
||||
+28
-30
@@ -31,12 +31,12 @@ extern crate tokio;
|
||||
extern crate futures;
|
||||
extern crate bytes;
|
||||
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use futures::future::{self, Either};
|
||||
use futures::sync::mpsc;
|
||||
use tokio::io;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
use futures::sync::mpsc;
|
||||
use futures::future::{self, Either};
|
||||
use bytes::{BytesMut, Bytes, BufMut};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
@@ -131,10 +131,7 @@ impl Shared {
|
||||
|
||||
impl Peer {
|
||||
/// Create a new instance of `Peer`.
|
||||
fn new(name: BytesMut,
|
||||
state: Arc<Mutex<Shared>>,
|
||||
lines: Lines) -> Peer
|
||||
{
|
||||
fn new(name: BytesMut, state: Arc<Mutex<Shared>>, lines: Lines) -> Peer {
|
||||
// Get the client socket address
|
||||
let addr = lines.socket.peer_addr().unwrap();
|
||||
|
||||
@@ -142,8 +139,7 @@ impl Peer {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
|
||||
// Add an entry for this `Peer` in the shared state map.
|
||||
state.lock().unwrap()
|
||||
.peers.insert(addr, tx);
|
||||
state.lock().unwrap().peers.insert(addr, tx);
|
||||
|
||||
Peer {
|
||||
name,
|
||||
@@ -198,7 +194,7 @@ impl Future for Peer {
|
||||
// though there could still be lines to read. Because we did
|
||||
// not reach `Async::NotReady`, we have to notify ourselves
|
||||
// in order to tell the executor to schedule the task again.
|
||||
if i+1 == LINES_PER_TICK {
|
||||
if i + 1 == LINES_PER_TICK {
|
||||
task::current().notify();
|
||||
}
|
||||
}
|
||||
@@ -256,8 +252,7 @@ impl Future for Peer {
|
||||
|
||||
impl Drop for Peer {
|
||||
fn drop(&mut self) {
|
||||
self.state.lock().unwrap().peers
|
||||
.remove(&self.addr);
|
||||
self.state.lock().unwrap().peers.remove(&self.addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +328,10 @@ impl Stream for Lines {
|
||||
let sock_closed = self.fill_read_buf()?.is_ready();
|
||||
|
||||
// Now, try finding lines
|
||||
let pos = self.rd.windows(2).enumerate()
|
||||
let pos = self
|
||||
.rd
|
||||
.windows(2)
|
||||
.enumerate()
|
||||
.find(|&(_, bytes)| bytes == b"\r\n")
|
||||
.map(|(i, _)| i);
|
||||
|
||||
@@ -373,7 +371,8 @@ fn process(socket: TcpStream, state: Arc<Mutex<Shared>>) {
|
||||
// We use the `into_future` combinator to extract the first item from the
|
||||
// lines stream. `into_future` takes a `Stream` and converts it to a future
|
||||
// of `(first, rest)` where `rest` is the original stream instance.
|
||||
let connection = lines.into_future()
|
||||
let connection = lines
|
||||
.into_future()
|
||||
// `into_future` doesn't have the right error type, so map the error to
|
||||
// make it work.
|
||||
.map_err(|(e, _)| e)
|
||||
@@ -408,10 +407,7 @@ fn process(socket: TcpStream, state: Arc<Mutex<Shared>>) {
|
||||
//
|
||||
// This is also a future that processes the connection, only
|
||||
// completing when the socket closes.
|
||||
let peer = Peer::new(
|
||||
name,
|
||||
state,
|
||||
lines);
|
||||
let peer = Peer::new(name, state, lines);
|
||||
|
||||
// Wrap `peer` with `Either::B` to make the return type fit.
|
||||
Either::B(peer)
|
||||
@@ -443,18 +439,20 @@ pub fn main() -> Result<(), Box<std::error::Error>> {
|
||||
|
||||
// The server task asynchronously iterates over and processes each
|
||||
// incoming connection.
|
||||
let server = listener.incoming().for_each(move |socket| {
|
||||
// Spawn a task to process the connection
|
||||
process(socket, state.clone());
|
||||
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);
|
||||
});
|
||||
let server = listener
|
||||
.incoming()
|
||||
.for_each(move |socket| {
|
||||
// Spawn a task to process the connection
|
||||
process(socket, state.clone());
|
||||
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!("server running on localhost:6142");
|
||||
|
||||
|
||||
+48
-44
@@ -16,18 +16,18 @@
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate futures;
|
||||
extern crate bytes;
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::SocketAddr;
|
||||
use std::thread;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use futures::sync::mpsc;
|
||||
use tokio::prelude::*;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// Determine if we're going to run in TCP or UDP mode
|
||||
@@ -73,18 +73,16 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
|
||||
tokio::run({
|
||||
stdout
|
||||
.for_each(move |chunk| {
|
||||
out.write_all(&chunk)
|
||||
})
|
||||
.for_each(move |chunk| out.write_all(&chunk))
|
||||
.map_err(|e| println!("error reading stdout; error = {:?}", e))
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod codec {
|
||||
use std::io;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use tokio::codec::{Encoder, Decoder};
|
||||
use std::io;
|
||||
use tokio::codec::{Decoder, Encoder};
|
||||
|
||||
/// A simple `Codec` implementation that just ships bytes around.
|
||||
///
|
||||
@@ -122,9 +120,9 @@ mod codec {
|
||||
|
||||
mod tcp {
|
||||
use tokio;
|
||||
use tokio::codec::Decoder;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
use tokio::codec::Decoder;
|
||||
|
||||
use bytes::BytesMut;
|
||||
use codec::Bytes;
|
||||
@@ -133,10 +131,10 @@ mod tcp {
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub fn connect(addr: &SocketAddr,
|
||||
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>)
|
||||
-> Result<Box<Stream<Item = BytesMut, Error = io::Error> + Send>, Box<Error>>
|
||||
{
|
||||
pub fn connect(
|
||||
addr: &SocketAddr,
|
||||
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>,
|
||||
) -> Result<Box<Stream<Item = BytesMut, Error = io::Error> + Send>, Box<Error>> {
|
||||
let tcp = TcpStream::connect(addr);
|
||||
|
||||
// After the TCP connection has been established, we set up our client
|
||||
@@ -154,18 +152,21 @@ 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.
|
||||
let stream = Box::new(tcp.map(move |stream| {
|
||||
let (sink, stream) = Bytes.framed(stream).split();
|
||||
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 {
|
||||
println!("failed to write to socket: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
}));
|
||||
tokio::spawn(stdin.forward(sink).then(|result| {
|
||||
if let Err(e) = result {
|
||||
println!("failed to write to socket: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
stream
|
||||
}).flatten_stream());
|
||||
stream
|
||||
})
|
||||
.flatten_stream(),
|
||||
);
|
||||
Ok(stream)
|
||||
}
|
||||
}
|
||||
@@ -175,17 +176,17 @@ mod udp {
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tokio;
|
||||
use tokio::net::{UdpSocket, UdpFramed};
|
||||
use tokio::prelude::*;
|
||||
use bytes::BytesMut;
|
||||
use tokio;
|
||||
use tokio::net::{UdpFramed, UdpSocket};
|
||||
use tokio::prelude::*;
|
||||
|
||||
use codec::Bytes;
|
||||
|
||||
pub fn connect(&addr: &SocketAddr,
|
||||
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>)
|
||||
-> Result<Box<Stream<Item = BytesMut, Error = io::Error> + Send>, Box<Error>>
|
||||
{
|
||||
pub fn connect(
|
||||
&addr: &SocketAddr,
|
||||
stdin: Box<Stream<Item = Vec<u8>, 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() {
|
||||
@@ -206,14 +207,15 @@ mod udp {
|
||||
|
||||
// All bytes from `stdin` will go to the `addr` specified in our
|
||||
// argument list. Like with TCP this is spawned concurrently
|
||||
let forward_stdin = stdin.map(move |chunk| {
|
||||
(chunk, addr)
|
||||
}).forward(sink).then(|result| {
|
||||
if let Err(e) = result {
|
||||
println!("failed to write to socket: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
let forward_stdin = stdin
|
||||
.map(move |chunk| (chunk, addr))
|
||||
.forward(sink)
|
||||
.then(|result| {
|
||||
if let Err(e) = result {
|
||||
println!("failed to write to socket: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// With UDP we could receive data from any source, so filter out
|
||||
// anything coming from a different address
|
||||
@@ -225,10 +227,13 @@ mod udp {
|
||||
}
|
||||
});
|
||||
|
||||
let stream = Box::new(future::lazy(|| {
|
||||
tokio::spawn(forward_stdin);
|
||||
future::ok(receive)
|
||||
}).flatten_stream());
|
||||
let stream = Box::new(
|
||||
future::lazy(|| {
|
||||
tokio::spawn(forward_stdin);
|
||||
future::ok(receive)
|
||||
})
|
||||
.flatten_stream(),
|
||||
);
|
||||
Ok(stream)
|
||||
}
|
||||
}
|
||||
@@ -240,8 +245,7 @@ fn read_stdin(mut tx: mpsc::Sender<Vec<u8>>) {
|
||||
loop {
|
||||
let mut buf = vec![0; 1024];
|
||||
let n = match stdin.read(&mut buf) {
|
||||
Err(_) |
|
||||
Ok(0) => break,
|
||||
Err(_) | Ok(0) => break,
|
||||
Ok(n) => n,
|
||||
};
|
||||
buf.truncate(n);
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use std::{env, io};
|
||||
use std::net::SocketAddr;
|
||||
use std::{env, io};
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::prelude::*;
|
||||
|
||||
struct Server {
|
||||
socket: UdpSocket,
|
||||
|
||||
+2
-2
@@ -54,7 +54,8 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// connections made to the server). The return value of the `for_each`
|
||||
// method is itself a future representing processing the entire stream of
|
||||
// connections, and ends up being our server.
|
||||
let done = socket.incoming()
|
||||
let done = socket
|
||||
.incoming()
|
||||
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
|
||||
.for_each(move |socket| {
|
||||
// Once we're inside this closure this represents an accepted client
|
||||
@@ -89,7 +90,6 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
Ok(())
|
||||
});
|
||||
|
||||
|
||||
// And this is where much of the magic of this server happens. We
|
||||
// crucially want all clients to make progress concurrently, rather than
|
||||
// blocking one on completion of another. To achieve this we use the
|
||||
|
||||
+14
-13
@@ -25,20 +25,21 @@ pub fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// Open a TCP stream to the socket address.
|
||||
//
|
||||
// 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(())
|
||||
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!("connection error = {:?}", err);
|
||||
});
|
||||
.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!("connection error = {:?}", err);
|
||||
});
|
||||
|
||||
// Start the Tokio runtime.
|
||||
//
|
||||
|
||||
@@ -57,10 +57,10 @@
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
|
||||
use tokio_codec::BytesCodec;
|
||||
use tokio::codec::Decoder;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
use tokio::codec::Decoder;
|
||||
use tokio_codec::BytesCodec;
|
||||
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
+17
-16
@@ -24,10 +24,10 @@
|
||||
|
||||
extern crate tokio;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::env;
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use tokio::io::{copy, shutdown};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
@@ -45,7 +45,8 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
println!("Listening on: {}", listen_addr);
|
||||
println!("Proxying to: {}", server_addr);
|
||||
|
||||
let done = socket.incoming()
|
||||
let done = socket
|
||||
.incoming()
|
||||
.map_err(|e| println!("error accepting socket; error = {:?}", e))
|
||||
.for_each(move |client| {
|
||||
let server = TcpStream::connect(&server_addr);
|
||||
@@ -68,25 +69,25 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
// After the copy is done we indicate to the remote side that we've
|
||||
// finished by shutting down the connection.
|
||||
let client_to_server = copy(client_reader, server_writer)
|
||||
.and_then(|(n, _, server_writer)| {
|
||||
shutdown(server_writer).map(move |_| n)
|
||||
});
|
||||
.and_then(|(n, _, server_writer)| shutdown(server_writer).map(move |_| n));
|
||||
|
||||
let server_to_client = copy(server_reader, client_writer)
|
||||
.and_then(|(n, _, client_writer)| {
|
||||
shutdown(client_writer).map(move |_| n)
|
||||
});
|
||||
.and_then(|(n, _, client_writer)| shutdown(client_writer).map(move |_| n));
|
||||
|
||||
client_to_server.join(server_to_client)
|
||||
});
|
||||
|
||||
let msg = amounts.map(move |(from_client, from_server)| {
|
||||
println!("client wrote {} bytes and received {} bytes",
|
||||
from_client, from_server);
|
||||
}).map_err(|e| {
|
||||
// Don't panic. Maybe the client just disconnected too soon.
|
||||
println!("error: {}", e);
|
||||
});
|
||||
let msg = amounts
|
||||
.map(move |(from_client, from_server)| {
|
||||
println!(
|
||||
"client wrote {} bytes and received {} bytes",
|
||||
from_client, from_server
|
||||
);
|
||||
})
|
||||
.map_err(|e| {
|
||||
// Don't panic. Maybe the client just disconnected too soon.
|
||||
println!("error: {}", e);
|
||||
});
|
||||
|
||||
tokio::spawn(msg);
|
||||
|
||||
|
||||
+44
-24
@@ -44,8 +44,8 @@
|
||||
extern crate tokio;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::BufReader;
|
||||
use std::env;
|
||||
use std::io::BufReader;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@@ -69,9 +69,18 @@ enum Request {
|
||||
|
||||
/// Responses to the `Request` commands above
|
||||
enum Response {
|
||||
Value { key: String, value: String },
|
||||
Set { key: String, value: String, previous: Option<String> },
|
||||
Error { msg: String },
|
||||
Value {
|
||||
key: String,
|
||||
value: String,
|
||||
},
|
||||
Set {
|
||||
key: String,
|
||||
value: String,
|
||||
previous: Option<String>,
|
||||
},
|
||||
Error {
|
||||
msg: String,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
@@ -93,7 +102,8 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
map: Mutex::new(initial_db),
|
||||
});
|
||||
|
||||
let done = listener.incoming()
|
||||
let done = listener
|
||||
.incoming()
|
||||
.map_err(|e| println!("error accepting socket; error = {:?}", e))
|
||||
.for_each(move |socket| {
|
||||
// As with many other small examples, the first thing we'll do is
|
||||
@@ -124,15 +134,22 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
|
||||
let mut db = db.map.lock().unwrap();
|
||||
match request {
|
||||
Request::Get { key } => {
|
||||
match db.get(&key) {
|
||||
Some(value) => Response::Value { key, value: value.clone() },
|
||||
None => Response::Error { msg: format!("no key {}", key) },
|
||||
}
|
||||
}
|
||||
Request::Get { key } => match db.get(&key) {
|
||||
Some(value) => Response::Value {
|
||||
key,
|
||||
value: value.clone(),
|
||||
},
|
||||
None => Response::Error {
|
||||
msg: format!("no key {}", key),
|
||||
},
|
||||
},
|
||||
Request::Set { key, value } => {
|
||||
let previous = db.insert(key.clone(), value.clone());
|
||||
Response::Set { key, value, previous }
|
||||
Response::Set {
|
||||
key,
|
||||
value,
|
||||
previous,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -169,9 +186,11 @@ impl Request {
|
||||
None => return Err(format!("GET must be followed by a key")),
|
||||
};
|
||||
if parts.next().is_some() {
|
||||
return Err(format!("GET's key must not be followed by anything"))
|
||||
return Err(format!("GET's key must not be followed by anything"));
|
||||
}
|
||||
Ok(Request::Get { key: key.to_string() })
|
||||
Ok(Request::Get {
|
||||
key: key.to_string(),
|
||||
})
|
||||
}
|
||||
Some("SET") => {
|
||||
let key = match parts.next() {
|
||||
@@ -182,7 +201,10 @@ impl Request {
|
||||
Some(value) => value,
|
||||
None => return Err(format!("SET needs a value")),
|
||||
};
|
||||
Ok(Request::Set { key: key.to_string(), value: value.to_string() })
|
||||
Ok(Request::Set {
|
||||
key: key.to_string(),
|
||||
value: value.to_string(),
|
||||
})
|
||||
}
|
||||
Some(cmd) => Err(format!("unknown command: {}", cmd)),
|
||||
None => Err(format!("empty input")),
|
||||
@@ -193,15 +215,13 @@ impl Request {
|
||||
impl Response {
|
||||
fn serialize(&self) -> String {
|
||||
match *self {
|
||||
Response::Value { ref key, ref value } => {
|
||||
format!("{} = {}", key, value)
|
||||
}
|
||||
Response::Set { ref key, ref value, ref previous } => {
|
||||
format!("set {} = `{}`, previous: {:?}", key, value, previous)
|
||||
}
|
||||
Response::Error { ref msg } => {
|
||||
format!("error: {}", msg)
|
||||
}
|
||||
Response::Value { ref key, ref value } => format!("{} = {}", key, value),
|
||||
Response::Set {
|
||||
ref key,
|
||||
ref value,
|
||||
ref previous,
|
||||
} => format!("set {} = `{}`, previous: {:?}", key, value, previous),
|
||||
Response::Error { ref msg } => format!("error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+47
-35
@@ -1,9 +1,9 @@
|
||||
//! A "tiny" example of HTTP request/response handling using just tokio-core
|
||||
//! A "tiny" example of HTTP request/response handling using transports.
|
||||
//!
|
||||
//! This example is intended for *learning purposes* to see how various pieces
|
||||
//! hook up together and how HTTP can get up and running. Note that this example
|
||||
//! is written with the restriction that it *can't* use any "big" library other
|
||||
//! than tokio-core, if you'd like a "real world" HTTP library you likely want a
|
||||
//! than Tokio, if you'd like a "real world" HTTP library you likely want a
|
||||
//! crate like Hyper.
|
||||
//!
|
||||
//! Code here is based on the `echo-threads` example and implements two paths,
|
||||
@@ -23,12 +23,12 @@ extern crate time;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
|
||||
use std::{env, fmt, io};
|
||||
use std::net::SocketAddr;
|
||||
use std::{env, fmt, io};
|
||||
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
use tokio::codec::{Decoder, Encoder};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
use tokio::codec::{Encoder, Decoder};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use http::header::HeaderValue;
|
||||
@@ -44,7 +44,8 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
tokio::run({
|
||||
listener.incoming()
|
||||
listener
|
||||
.incoming()
|
||||
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
|
||||
.for_each(|socket| {
|
||||
process(socket);
|
||||
@@ -64,14 +65,13 @@ fn process(socket: TcpStream) {
|
||||
.split();
|
||||
|
||||
// Map all requests into responses and send them back to the client.
|
||||
let task = tx.send_all(rx.and_then(respond))
|
||||
.then(|res| {
|
||||
if let Err(e) = res {
|
||||
println!("failed to process connection; error = {:?}", e);
|
||||
}
|
||||
let task = tx.send_all(rx.and_then(respond)).then(|res| {
|
||||
if let Err(e) = res {
|
||||
println!("failed to process connection; error = {:?}", e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// Spawn the task that handles the connection.
|
||||
tokio::spawn(task);
|
||||
@@ -82,9 +82,7 @@ fn process(socket: TcpStream) {
|
||||
/// This function is a map from and HTTP request to a future of a response and
|
||||
/// represents the various handling a server might do. Currently the contents
|
||||
/// here are pretty uninteresting.
|
||||
fn respond(req: Request<()>)
|
||||
-> Box<Future<Item = Response<String>, Error = io::Error> + Send>
|
||||
{
|
||||
fn respond(req: Request<()>) -> Box<Future<Item = Response<String>, Error = io::Error> + Send> {
|
||||
let f = future::lazy(move || {
|
||||
let mut response = Response::builder();
|
||||
let body = match req.uri().path() {
|
||||
@@ -99,14 +97,18 @@ fn respond(req: Request<()>)
|
||||
struct Message {
|
||||
message: &'static str,
|
||||
}
|
||||
serde_json::to_string(&Message { message: "Hello, World!" })?
|
||||
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))?;
|
||||
let response = response
|
||||
.body(body)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
||||
Ok(response)
|
||||
});
|
||||
|
||||
@@ -124,12 +126,19 @@ impl Encoder for Http {
|
||||
fn encode(&mut self, item: Response<String>, dst: &mut BytesMut) -> io::Result<()> {
|
||||
use std::fmt::Write;
|
||||
|
||||
write!(BytesWrite(dst), "\
|
||||
HTTP/1.1 {}\r\n\
|
||||
Server: Example\r\n\
|
||||
Content-Length: {}\r\n\
|
||||
Date: {}\r\n\
|
||||
", item.status(), item.body().len(), date::now()).unwrap();
|
||||
write!(
|
||||
BytesWrite(dst),
|
||||
"\
|
||||
HTTP/1.1 {}\r\n\
|
||||
Server: Example\r\n\
|
||||
Content-Length: {}\r\n\
|
||||
Date: {}\r\n\
|
||||
",
|
||||
item.status(),
|
||||
item.body().len(),
|
||||
date::now()
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for (k, v) in item.headers() {
|
||||
dst.extend_from_slice(k.as_str().as_bytes());
|
||||
@@ -198,13 +207,18 @@ impl Decoder for Http {
|
||||
headers[i] = Some((k, v));
|
||||
}
|
||||
|
||||
(toslice(r.method.unwrap().as_bytes()),
|
||||
toslice(r.path.unwrap().as_bytes()),
|
||||
r.version.unwrap(),
|
||||
amt)
|
||||
(
|
||||
toslice(r.method.unwrap().as_bytes()),
|
||||
toslice(r.path.unwrap().as_bytes()),
|
||||
r.version.unwrap(),
|
||||
amt,
|
||||
)
|
||||
};
|
||||
if version != 1 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "only HTTP/1.1 accepted"))
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"only HTTP/1.1 accepted",
|
||||
));
|
||||
}
|
||||
let data = src.split_to(amt).freeze();
|
||||
let mut ret = Request::builder();
|
||||
@@ -216,15 +230,13 @@ impl Decoder for Http {
|
||||
Some((ref k, ref v)) => (k, v),
|
||||
None => break,
|
||||
};
|
||||
let value = unsafe {
|
||||
HeaderValue::from_shared_unchecked(data.slice(v.0, v.1))
|
||||
};
|
||||
let value = unsafe { HeaderValue::from_shared_unchecked(data.slice(v.0, v.1)) };
|
||||
ret.header(&data[k.0..k.1], value);
|
||||
}
|
||||
|
||||
let req = ret.body(()).map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, e)
|
||||
})?;
|
||||
let req = ret
|
||||
.body(())
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
Ok(Some(req))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
"0.0.0.0:0"
|
||||
} else {
|
||||
"[::]:0"
|
||||
}.parse()?;
|
||||
}
|
||||
.parse()?;
|
||||
let socket = UdpSocket::bind(&local_addr)?;
|
||||
const MAX_DATAGRAM_SIZE: usize = 65_507;
|
||||
socket
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate env_logger;
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tokio::net::{UdpFramed, UdpSocket};
|
||||
use tokio::prelude::*;
|
||||
use tokio::net::{UdpSocket, UdpFramed};
|
||||
use tokio_codec::BytesCodec;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
|
||||
+28
-6
@@ -1,13 +1,34 @@
|
||||
use std::future::{Future as StdFuture};
|
||||
use std::future::Future as StdFuture;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Poll, Waker};
|
||||
|
||||
async fn map_ok<T: StdFuture>(future: T) -> Result<(), ()> {
|
||||
let _ = await!(future);
|
||||
Ok(())
|
||||
fn map_ok<T: StdFuture>(future: T) -> impl StdFuture<Output = Result<(), ()>> {
|
||||
MapOk(future)
|
||||
}
|
||||
|
||||
struct MapOk<T>(T);
|
||||
|
||||
impl<T> MapOk<T> {
|
||||
fn future<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut T> {
|
||||
unsafe { Pin::map_unchecked_mut(self, |x| &mut x.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StdFuture> StdFuture for MapOk<T> {
|
||||
type Output = Result<(), ()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, waker: &Waker) -> Poll<Self::Output> {
|
||||
match self.future().poll(waker) {
|
||||
Poll::Ready(_) => Poll::Ready(Ok(())),
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `tokio::run`, but takes an `async` block
|
||||
pub fn run_async<F>(future: F)
|
||||
where F: StdFuture<Output = ()> + Send + 'static,
|
||||
where
|
||||
F: StdFuture<Output = ()> + Send + 'static,
|
||||
{
|
||||
use tokio_async_await::compat::backward;
|
||||
let future = backward::Compat::new(map_ok(future));
|
||||
@@ -17,7 +38,8 @@ where F: StdFuture<Output = ()> + Send + 'static,
|
||||
|
||||
/// Like `tokio::spawn`, but takes an `async` block
|
||||
pub fn spawn_async<F>(future: F)
|
||||
where F: StdFuture<Output = ()> + Send + 'static,
|
||||
where
|
||||
F: StdFuture<Output = ()> + Send + 'static,
|
||||
{
|
||||
use tokio_async_await::compat::backward;
|
||||
let future = backward::Compat::new(map_ok(future));
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
//! # 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.
|
||||
//! is an easy way to get started. [`LengthDelimitedCodec::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;
|
||||
@@ -49,11 +49,12 @@
|
||||
//! use bytes::Bytes;
|
||||
//! use futures::{Sink, Future};
|
||||
//!
|
||||
//! fn write_frame<T: AsyncRead + AsyncWrite>(io: T) {
|
||||
//! fn write_frame<T: AsyncRead + AsyncWrite>(io: T) -> Result<(), Box<std::error::Error>> {
|
||||
//! let mut transport = Framed::new(io, LengthDelimitedCodec::new());
|
||||
//! let frame = Bytes::from("hello world");
|
||||
//!
|
||||
//! transport.send(frame).wait().unwrap();
|
||||
//! transport.send(frame).wait()?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! #
|
||||
//! # pub fn main() {}
|
||||
@@ -345,6 +346,7 @@
|
||||
//! +------------+--------------+
|
||||
//! ```
|
||||
//!
|
||||
//! [`LengthDelimitedCodec::new()`]: struct.LengthDelimitedCodec.html#method.new
|
||||
//! [`FramedRead`]: struct.FramedRead.html
|
||||
//! [`FramedWrite`]: struct.FramedWrite.html
|
||||
//! [`AsyncRead`]: ../../trait.AsyncRead.html
|
||||
@@ -353,19 +355,15 @@
|
||||
//! [`BytesMut`]: https://docs.rs/bytes/0.4/bytes/struct.BytesMut.html
|
||||
|
||||
use {
|
||||
codec::{
|
||||
Decoder, Encoder, FramedRead, FramedWrite, Framed
|
||||
},
|
||||
io::{
|
||||
AsyncRead, AsyncWrite
|
||||
},
|
||||
codec::{Decoder, Encoder, Framed, FramedRead, FramedWrite},
|
||||
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};
|
||||
use std::{cmp, fmt};
|
||||
|
||||
/// Configure length delimited `LengthDelimitedCodec`s.
|
||||
///
|
||||
@@ -474,9 +472,10 @@ impl LengthDelimitedCodec {
|
||||
};
|
||||
|
||||
if n > self.builder.max_frame_len as u64 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, FrameTooBig {
|
||||
_priv: (),
|
||||
}));
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
FrameTooBig { _priv: () },
|
||||
));
|
||||
}
|
||||
|
||||
// The check above ensures there is no overflow
|
||||
@@ -492,7 +491,12 @@ impl LengthDelimitedCodec {
|
||||
// Error handling
|
||||
match n {
|
||||
Some(n) => n,
|
||||
None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "provided length would overflow after adjustment")),
|
||||
None => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"provided length would overflow after adjustment",
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -526,15 +530,13 @@ impl Decoder for LengthDelimitedCodec {
|
||||
|
||||
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::Head => match try!(self.decode_head(src)) {
|
||||
Some(n) => {
|
||||
self.state = DecodeState::Data(n);
|
||||
n
|
||||
}
|
||||
}
|
||||
None => return Ok(None),
|
||||
},
|
||||
DecodeState::Data(n) => n,
|
||||
};
|
||||
|
||||
@@ -561,9 +563,10 @@ impl Encoder for LengthDelimitedCodec {
|
||||
let n = (&data).into_buf().remaining();
|
||||
|
||||
if n > self.builder.max_frame_len {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, FrameTooBig {
|
||||
_priv: (),
|
||||
}));
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
FrameTooBig { _priv: () },
|
||||
));
|
||||
}
|
||||
|
||||
// Adjust `n` with bounds checking
|
||||
@@ -573,10 +576,12 @@ impl Encoder for LengthDelimitedCodec {
|
||||
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",
|
||||
))?;
|
||||
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).
|
||||
@@ -890,7 +895,8 @@ impl Builder {
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn new_read<T>(&self, upstream: T) -> FramedRead<T, LengthDelimitedCodec>
|
||||
where T: AsyncRead,
|
||||
where
|
||||
T: AsyncRead,
|
||||
{
|
||||
FramedRead::new(upstream, self.new_codec())
|
||||
}
|
||||
@@ -913,7 +919,8 @@ impl Builder {
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn new_write<T>(&self, inner: T) -> FramedWrite<T, LengthDelimitedCodec>
|
||||
where T: AsyncWrite,
|
||||
where
|
||||
T: AsyncWrite,
|
||||
{
|
||||
FramedWrite::new(inner, self.new_codec())
|
||||
}
|
||||
@@ -937,7 +944,8 @@ impl Builder {
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
pub fn new_framed<T>(&self, inner: T) -> Framed<T, LengthDelimitedCodec>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
where
|
||||
T: AsyncRead + AsyncWrite,
|
||||
{
|
||||
Framed::new(inner, self.new_codec())
|
||||
}
|
||||
@@ -948,17 +956,16 @@ impl Builder {
|
||||
}
|
||||
|
||||
fn get_num_skip(&self) -> usize {
|
||||
self.num_skip.unwrap_or(self.length_field_offset + self.length_field_len)
|
||||
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()
|
||||
f.debug_struct("FrameTooBig").finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-8
@@ -11,14 +11,7 @@
|
||||
//! [transports]: https://tokio.rs/docs/going-deeper/frames/
|
||||
|
||||
pub use tokio_codec::{
|
||||
Decoder,
|
||||
Encoder,
|
||||
Framed,
|
||||
FramedParts,
|
||||
FramedRead,
|
||||
FramedWrite,
|
||||
BytesCodec,
|
||||
LinesCodec,
|
||||
BytesCodec, Decoder, Encoder, Framed, FramedParts, FramedRead, FramedWrite, LinesCodec,
|
||||
};
|
||||
|
||||
pub mod length_delimited;
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
//! the context of the Tokio runtime as they require Tokio specific features to
|
||||
//! function.
|
||||
|
||||
pub use tokio_fs::{create_dir, create_dir_all, file, hard_link, metadata, os, read_dir, read_link};
|
||||
pub use tokio_fs::{remove_dir, remove_file, rename, set_permissions, symlink_metadata, File};
|
||||
pub use tokio_fs::OpenOptions;
|
||||
pub use tokio_fs::{
|
||||
create_dir, create_dir_all, file, hard_link, metadata, os, read_dir, read_link,
|
||||
};
|
||||
pub use tokio_fs::{read, write, ReadFile, WriteFile};
|
||||
pub use tokio_fs::{remove_dir, remove_file, rename, set_permissions, symlink_metadata, File};
|
||||
|
||||
@@ -45,50 +45,18 @@
|
||||
//! [`ErrorKind`]: enum.ErrorKind.html
|
||||
//! [`Result`]: type.Result.html
|
||||
|
||||
pub use tokio_io::{
|
||||
AsyncRead,
|
||||
AsyncWrite,
|
||||
};
|
||||
pub use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
// standard input, output, and error
|
||||
pub use tokio_fs::{
|
||||
stdin,
|
||||
Stdin,
|
||||
stdout,
|
||||
Stdout,
|
||||
stderr,
|
||||
Stderr,
|
||||
};
|
||||
#[cfg(feature = "fs")]
|
||||
pub use tokio_fs::{stderr, stdin, stdout, Stderr, Stdin, Stdout};
|
||||
|
||||
// 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,
|
||||
copy, flush, lines, read, read_exact, read_to_end, read_until, shutdown, write_all, Copy,
|
||||
Flush, Lines, ReadExact, ReadHalf, ReadToEnd, ReadUntil, Shutdown, 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 use std::io::{Error, ErrorKind, Read, Result, Write};
|
||||
|
||||
+51
-17
@@ -1,10 +1,9 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio/0.1.13")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio/0.1.17")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
#![cfg_attr(feature = "async-await-preview", feature(
|
||||
async_await,
|
||||
await_macro,
|
||||
futures_api,
|
||||
))]
|
||||
#![cfg_attr(
|
||||
feature = "async-await-preview",
|
||||
feature(async_await, await_macro, futures_api,)
|
||||
)]
|
||||
|
||||
//! A runtime for writing reliable, asynchronous, and slim applications.
|
||||
//!
|
||||
@@ -72,42 +71,77 @@
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
extern crate bytes;
|
||||
macro_rules! if_runtime {
|
||||
($($i:item)*) => ($(
|
||||
#[cfg(any(feature = "rt-full"))]
|
||||
$i
|
||||
)*)
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
|
||||
#[cfg(feature = "io")]
|
||||
extern crate bytes;
|
||||
#[cfg(feature = "reactor")]
|
||||
extern crate mio;
|
||||
#[cfg(feature = "rt-full")]
|
||||
extern crate num_cpus;
|
||||
extern crate tokio_current_thread;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_executor;
|
||||
#[cfg(feature = "codec")]
|
||||
extern crate tokio_codec;
|
||||
#[cfg(feature = "rt-full")]
|
||||
extern crate tokio_current_thread;
|
||||
#[cfg(feature = "fs")]
|
||||
extern crate tokio_fs;
|
||||
#[cfg(feature = "io")]
|
||||
extern crate tokio_io;
|
||||
#[cfg(feature = "reactor")]
|
||||
extern crate tokio_reactor;
|
||||
extern crate tokio_threadpool;
|
||||
extern crate tokio_timer;
|
||||
#[cfg(feature = "sync")]
|
||||
extern crate tokio_sync;
|
||||
#[cfg(feature = "tcp")]
|
||||
extern crate tokio_tcp;
|
||||
#[cfg(feature = "rt-full")]
|
||||
extern crate tokio_threadpool;
|
||||
#[cfg(feature = "timer")]
|
||||
extern crate tokio_timer;
|
||||
#[cfg(feature = "udp")]
|
||||
extern crate tokio_udp;
|
||||
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
extern crate tokio_async_await;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, feature = "uds"))]
|
||||
extern crate tokio_uds;
|
||||
|
||||
#[cfg(feature = "timer")]
|
||||
pub mod clock;
|
||||
#[cfg(feature = "codec")]
|
||||
pub mod codec;
|
||||
pub mod executor;
|
||||
#[cfg(feature = "fs")]
|
||||
pub mod fs;
|
||||
#[cfg(feature = "io")]
|
||||
pub mod io;
|
||||
#[cfg(any(feature = "tcp", feature = "udp", feature = "uds"))]
|
||||
pub mod net;
|
||||
pub mod prelude;
|
||||
#[cfg(feature = "reactor")]
|
||||
pub mod reactor;
|
||||
pub mod runtime;
|
||||
#[cfg(feature = "sync")]
|
||||
pub mod sync;
|
||||
#[cfg(feature = "timer")]
|
||||
pub mod timer;
|
||||
pub mod util;
|
||||
|
||||
pub use executor::spawn;
|
||||
pub use runtime::run;
|
||||
if_runtime! {
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_trace_core;
|
||||
pub mod executor;
|
||||
pub mod runtime;
|
||||
|
||||
pub use executor::spawn;
|
||||
pub use runtime::run;
|
||||
}
|
||||
|
||||
// ===== Experimental async/await support =====
|
||||
|
||||
|
||||
+19
-6
@@ -8,7 +8,10 @@
|
||||
//! * [`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)**
|
||||
//! Unix Domain Stream Socket **(available on Unix only)**
|
||||
//! * [`UnixDatagram`] and [`UnixDatagramFramed`] provide functionality for communication
|
||||
//! over Unix Domain Datagram Socket **(available on Unix only)**
|
||||
|
||||
//!
|
||||
//! [`TcpListener`]: struct.TcpListener.html
|
||||
//! [`TcpStream`]: struct.TcpStream.html
|
||||
@@ -16,7 +19,10 @@
|
||||
//! [`UdpFramed`]: struct.UdpFramed.html
|
||||
//! [`UnixListener`]: struct.UnixListener.html
|
||||
//! [`UnixStream`]: struct.UnixStream.html
|
||||
//! [`UnixDatagram`]: struct.UnixDatagram.html
|
||||
//! [`UnixDatagramFramed`]: struct.UnixDatagramFramed.html
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
pub mod tcp {
|
||||
//! TCP bindings for `tokio`.
|
||||
//!
|
||||
@@ -37,15 +43,19 @@ pub mod tcp {
|
||||
//! [`Incoming`]: struct.Incoming.html
|
||||
pub use tokio_tcp::{ConnectFuture, Incoming, TcpListener, TcpStream};
|
||||
}
|
||||
#[cfg(feature = "tcp")]
|
||||
pub use self::tcp::{TcpListener, TcpStream};
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
#[deprecated(note = "use `tokio::net::tcp::ConnectFuture` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type ConnectFuture = self::tcp::ConnectFuture;
|
||||
#[cfg(feature = "tcp")]
|
||||
#[deprecated(note = "use `tokio::net::tcp::Incoming` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type Incoming = self::tcp::Incoming;
|
||||
|
||||
#[cfg(feature = "udp")]
|
||||
pub mod udp {
|
||||
//! UDP bindings for `tokio`.
|
||||
//!
|
||||
@@ -63,23 +73,26 @@ pub mod udp {
|
||||
//! [`framed`]: struct.UdpSocket.html#method.framed
|
||||
pub use tokio_udp::{RecvDgram, SendDgram, UdpFramed, UdpSocket};
|
||||
}
|
||||
#[cfg(feature = "udp")]
|
||||
pub use self::udp::{UdpFramed, UdpSocket};
|
||||
|
||||
#[cfg(feature = "udp")]
|
||||
#[deprecated(note = "use `tokio::net::udp::RecvDgram` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type RecvDgram<T> = self::udp::RecvDgram<T>;
|
||||
#[cfg(feature = "udp")]
|
||||
#[deprecated(note = "use `tokio::net::udp::SendDgram` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type SendDgram<T> = self::udp::SendDgram<T>;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, feature = "uds"))]
|
||||
pub mod unix {
|
||||
//! Unix domain socket bindings for `tokio` (only available on unix systems).
|
||||
|
||||
pub use tokio_uds::{
|
||||
ConnectFuture, Incoming, RecvDgram, SendDgram, UCred, UnixDatagram, UnixListener,
|
||||
UnixStream,
|
||||
ConnectFuture, Incoming, RecvDgram, SendDgram, UCred, UnixDatagram, UnixDatagramFramed,
|
||||
UnixListener, UnixStream,
|
||||
};
|
||||
}
|
||||
#[cfg(unix)]
|
||||
pub use self::unix::{UnixListener, UnixStream};
|
||||
#[cfg(all(unix, feature = "uds"))]
|
||||
pub use self::unix::{UnixDatagram, UnixDatagramFramed, UnixListener, UnixStream};
|
||||
|
||||
+8
-34
@@ -10,45 +10,19 @@
|
||||
//!
|
||||
//! The prelude may grow over time as additional items see ubiquitous use.
|
||||
|
||||
pub use tokio_io::{
|
||||
AsyncRead,
|
||||
AsyncWrite,
|
||||
};
|
||||
#[cfg(feature = "io")]
|
||||
pub use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
pub use util::{
|
||||
FutureExt,
|
||||
StreamExt,
|
||||
};
|
||||
pub use util::{FutureExt, StreamExt};
|
||||
|
||||
pub use ::std::io::{
|
||||
Read,
|
||||
Write,
|
||||
};
|
||||
pub use std::io::{Read, Write};
|
||||
|
||||
pub use futures::{
|
||||
Future,
|
||||
future,
|
||||
Stream,
|
||||
stream,
|
||||
Sink,
|
||||
IntoFuture,
|
||||
Async,
|
||||
AsyncSink,
|
||||
Poll,
|
||||
task,
|
||||
};
|
||||
pub use futures::{future, stream, task, Async, AsyncSink, Future, IntoFuture, Poll, Sink, Stream};
|
||||
|
||||
#[cfg(feature = "async-await-preview")]
|
||||
#[doc(inline)]
|
||||
pub use tokio_async_await::{
|
||||
io::{
|
||||
AsyncReadExt,
|
||||
AsyncWriteExt,
|
||||
},
|
||||
sink::{
|
||||
SinkExt,
|
||||
},
|
||||
stream::{
|
||||
StreamExt as StreamAsyncExt,
|
||||
},
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
sink::SinkExt,
|
||||
stream::StreamExt as StreamAsyncExt,
|
||||
};
|
||||
|
||||
+1
-6
@@ -136,12 +136,7 @@
|
||||
//! [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
|
||||
|
||||
pub use tokio_reactor::{
|
||||
Reactor,
|
||||
Handle,
|
||||
Background,
|
||||
Turn,
|
||||
Registration,
|
||||
PollEvented as PollEvented2,
|
||||
Background, Handle, PollEvented as PollEvented2, Reactor, Registration, Turn,
|
||||
};
|
||||
|
||||
mod poll_evented;
|
||||
|
||||
+30
-22
@@ -10,9 +10,9 @@
|
||||
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use futures::{task, Async, Poll};
|
||||
use mio::event::Evented;
|
||||
@@ -41,9 +41,7 @@ struct Inner {
|
||||
|
||||
impl<E: fmt::Debug> fmt::Debug for PollEvented<E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("PollEvented")
|
||||
.field("io", &self.io)
|
||||
.finish()
|
||||
f.debug_struct("PollEvented").field("io", &self.io).finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +49,8 @@ impl<E> PollEvented<E> {
|
||||
/// Creates a new readiness stream associated with the provided
|
||||
/// `loop_handle` and for the given `source`.
|
||||
pub fn new(io: E, handle: &Handle) -> io::Result<PollEvented<E>>
|
||||
where E: Evented,
|
||||
where
|
||||
E: Evented,
|
||||
{
|
||||
let registration = Registration::new();
|
||||
registration.register(&io)?;
|
||||
@@ -153,7 +152,9 @@ impl<E> PollEvented<E> {
|
||||
};
|
||||
|
||||
// Cache the value
|
||||
self.inner.write_readiness.store(ready2usize(ready), Relaxed);
|
||||
self.inner
|
||||
.write_readiness
|
||||
.store(ready2usize(ready), Relaxed);
|
||||
|
||||
().into()
|
||||
}
|
||||
@@ -334,17 +335,17 @@ impl<E> PollEvented<E> {
|
||||
/// method is called, and will likely return an error if this `PollEvented`
|
||||
/// was created on a separate event loop from the `handle` specified.
|
||||
pub fn deregister(&self) -> io::Result<()>
|
||||
where E: Evented,
|
||||
where
|
||||
E: Evented,
|
||||
{
|
||||
self.inner.registration.lock().unwrap()
|
||||
.deregister(&self.io)
|
||||
self.inner.registration.lock().unwrap().deregister(&self.io)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Read> Read for PollEvented<E> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
if let Async::NotReady = self.poll_read() {
|
||||
return Err(io::ErrorKind::WouldBlock.into())
|
||||
return Err(io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
|
||||
let r = self.get_mut().read(buf);
|
||||
@@ -353,14 +354,14 @@ impl<E: Read> Read for PollEvented<E> {
|
||||
self.need_read()?;
|
||||
}
|
||||
|
||||
return r
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Write> Write for PollEvented<E> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if let Async::NotReady = self.poll_write() {
|
||||
return Err(io::ErrorKind::WouldBlock.into())
|
||||
return Err(io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
|
||||
let r = self.get_mut().write(buf);
|
||||
@@ -369,12 +370,12 @@ impl<E: Write> Write for PollEvented<E> {
|
||||
self.need_write()?;
|
||||
}
|
||||
|
||||
return r
|
||||
return r;
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
if let Async::NotReady = self.poll_write() {
|
||||
return Err(io::ErrorKind::WouldBlock.into())
|
||||
return Err(io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
|
||||
let r = self.get_mut().flush();
|
||||
@@ -383,12 +384,11 @@ impl<E: Write> Write for PollEvented<E> {
|
||||
self.need_write()?;
|
||||
}
|
||||
|
||||
return r
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Read> AsyncRead for PollEvented<E> {
|
||||
}
|
||||
impl<E: Read> AsyncRead for PollEvented<E> {}
|
||||
|
||||
impl<E: Write> AsyncWrite for PollEvented<E> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
@@ -430,8 +430,8 @@ fn usize2ready(bits: usize) -> Ready {
|
||||
|
||||
#[cfg(unix)]
|
||||
mod platform {
|
||||
use mio::Ready;
|
||||
use mio::unix::UnixReady;
|
||||
use mio::Ready;
|
||||
|
||||
const HUP: usize = 1 << 2;
|
||||
const ERROR: usize = 1 << 3;
|
||||
@@ -476,14 +476,22 @@ mod platform {
|
||||
bits
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios",
|
||||
target_os = "macos"))]
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos"
|
||||
))]
|
||||
fn usize2ready_aio(ready: &mut UnixReady) {
|
||||
ready.insert(UnixReady::aio());
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "dragonfly",
|
||||
target_os = "freebsd", target_os = "ios", target_os = "macos")))]
|
||||
#[cfg(not(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos"
|
||||
)))]
|
||||
fn usize2ready_aio(_ready: &mut UnixReady) {
|
||||
// aio not available here → empty
|
||||
}
|
||||
|
||||
@@ -90,3 +90,18 @@ where
|
||||
r.run().expect("failed to resolve remaining futures");
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Start a current-thread runtime using the supplied future to bootstrap execution.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if called from the context of an executor.
|
||||
pub fn run<F>(future: F)
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + 'static,
|
||||
{
|
||||
|
||||
let mut r = Runtime::new().expect("failed to start runtime on current thread");
|
||||
r.spawn(future);
|
||||
r.run().expect("failed to resolve remaining futures");
|
||||
}
|
||||
|
||||
@@ -92,6 +92,10 @@ impl Error for RunError {
|
||||
fn description(&self) -> &str {
|
||||
self.inner.description()
|
||||
}
|
||||
|
||||
// FIXME(taiki-e): When the minimum support version of tokio reaches Rust 1.30,
|
||||
// replace this with Error::source.
|
||||
#[allow(deprecated)]
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
self.inner.cause()
|
||||
}
|
||||
|
||||
+9
-394
@@ -106,405 +106,20 @@
|
||||
//! [timer]: ../timer/index.html
|
||||
//! [`Runtime`]: struct.Runtime.html
|
||||
//! [`Reactor`]: ../reactor/struct.Reactor.html
|
||||
//! [`ThreadPool`]: ../executor/thread_pool/struct.ThreadPool.html
|
||||
//! [`ThreadPool`]: https://docs.rs/tokio-threadpool/0.1/tokio_threadpool/struct.ThreadPool.html
|
||||
//! [`run`]: fn.run.html
|
||||
//! [idle]: struct.Runtime.html#method.shutdown_on_idle
|
||||
//! [`tokio::spawn`]: ../executor/fn.spawn.html
|
||||
//! [`Timer`]: https://docs.rs/tokio-timer/0.2/tokio_timer/timer/struct.Timer.html
|
||||
|
||||
mod builder;
|
||||
pub mod current_thread;
|
||||
mod shutdown;
|
||||
mod task_executor;
|
||||
mod threadpool;
|
||||
|
||||
pub use self::builder::Builder;
|
||||
pub use self::shutdown::Shutdown;
|
||||
pub use self::task_executor::TaskExecutor;
|
||||
pub use self::threadpool::{
|
||||
Builder,
|
||||
Runtime,
|
||||
Shutdown,
|
||||
TaskExecutor,
|
||||
run,
|
||||
};
|
||||
|
||||
use reactor::{Handle, Reactor};
|
||||
|
||||
use std::io;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use tokio_executor::enter;
|
||||
use tokio_threadpool as threadpool;
|
||||
|
||||
use futures;
|
||||
use futures::future::Future;
|
||||
|
||||
/// Handle to the Tokio runtime.
|
||||
///
|
||||
/// The Tokio runtime includes a reactor as well as an executor for running
|
||||
/// tasks.
|
||||
///
|
||||
/// Instances of `Runtime` can be created using [`new`] or [`Builder`]. However,
|
||||
/// most users will use [`tokio::run`], which uses a `Runtime` internally.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
/// [`new`]: #method.new
|
||||
/// [`Builder`]: struct.Builder.html
|
||||
/// [`tokio::run`]: fn.run.html
|
||||
#[derive(Debug)]
|
||||
pub struct Runtime {
|
||||
inner: Option<Inner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
/// 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,
|
||||
}
|
||||
|
||||
// ===== impl Runtime =====
|
||||
|
||||
/// Start the Tokio runtime using the supplied future to bootstrap execution.
|
||||
///
|
||||
/// This function is used to bootstrap the execution of a Tokio application. It
|
||||
/// does the following:
|
||||
///
|
||||
/// * Start the Tokio runtime using a default configuration.
|
||||
/// * Spawn the given future onto the thread pool.
|
||||
/// * Block the current thread until the runtime shuts down.
|
||||
///
|
||||
/// Note that the function will not return immediately once `future` has
|
||||
/// completed. Instead it waits for the entire runtime to become idle.
|
||||
///
|
||||
/// See the [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use futures::{Future, Stream};
|
||||
/// use tokio::net::TcpListener;
|
||||
///
|
||||
/// # fn process<T>(_: T) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
/// # unimplemented!();
|
||||
/// # }
|
||||
/// # fn dox() {
|
||||
/// # let addr = "127.0.0.1:8080".parse().unwrap();
|
||||
/// let listener = TcpListener::bind(&addr).unwrap();
|
||||
///
|
||||
/// let server = listener.incoming()
|
||||
/// .map_err(|e| println!("error = {:?}", e))
|
||||
/// .for_each(|socket| {
|
||||
/// tokio::spawn(process(socket))
|
||||
/// });
|
||||
///
|
||||
/// tokio::run(server);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if called from the context of an executor.
|
||||
///
|
||||
/// [mod]: ../index.html
|
||||
pub fn run<F>(future: F)
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
// 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);
|
||||
entered
|
||||
.block_on(runtime.shutdown_on_idle())
|
||||
.expect("shutdown cannot error")
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
/// Create a new runtime instance with default configuration values.
|
||||
///
|
||||
/// This results in a reactor, thread pool, and timer being initialized. The
|
||||
/// thread pool will not spawn any worker threads until it needs to, i.e.
|
||||
/// tasks are scheduled to run.
|
||||
///
|
||||
/// Most users will not need to call this function directly, instead they
|
||||
/// will use [`tokio::run`](fn.run.html).
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a new `Runtime` with default configuration values.
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
/// use tokio::prelude::*;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // Use the runtime...
|
||||
///
|
||||
/// // Shutdown the runtime
|
||||
/// rt.shutdown_now()
|
||||
/// .wait().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [mod]: index.html
|
||||
pub fn new() -> io::Result<Self> {
|
||||
Builder::new().build()
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.5", note = "use `reactor` instead")]
|
||||
#[doc(hidden)]
|
||||
pub fn handle(&self) -> &Handle {
|
||||
#[allow(deprecated)]
|
||||
self.reactor()
|
||||
}
|
||||
|
||||
/// Return a reference to the reactor handle for this runtime instance.
|
||||
///
|
||||
/// The returned handle reference can be cloned in order to get an owned
|
||||
/// value of the handle. This handle can be used to initialize I/O resources
|
||||
/// (like TCP or UDP sockets) that will not be used on the runtime.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// let reactor_handle = rt.reactor().clone();
|
||||
///
|
||||
/// // use `reactor_handle`
|
||||
/// ```
|
||||
#[deprecated(since = "0.1.11", note = "there is now a reactor per worker thread")]
|
||||
pub fn reactor(&self) -> &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.
|
||||
///
|
||||
/// The returned handle can be used to spawn tasks that run on this runtime.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// let executor_handle = rt.executor();
|
||||
///
|
||||
/// // use `executor_handle`
|
||||
/// ```
|
||||
pub fn executor(&self) -> TaskExecutor {
|
||||
let inner = self.inner().pool.sender().clone();
|
||||
TaskExecutor { inner }
|
||||
}
|
||||
|
||||
/// Spawn a future onto the Tokio runtime.
|
||||
///
|
||||
/// This spawns the given future onto the runtime's executor, usually a
|
||||
/// thread pool. The thread pool is then responsible for polling the future
|
||||
/// until it completes.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use futures::{future, Future, Stream};
|
||||
/// use tokio::runtime::Runtime;
|
||||
///
|
||||
/// # fn dox() {
|
||||
/// // Create the runtime
|
||||
/// let mut rt = Runtime::new().unwrap();
|
||||
///
|
||||
/// // Spawn a future onto the runtime
|
||||
/// rt.spawn(future::lazy(|| {
|
||||
/// println!("now running on a worker thread");
|
||||
/// Ok(())
|
||||
/// }));
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the spawn fails. Failure occurs if the executor
|
||||
/// is currently at capacity and is unable to spawn a new future.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
self.inner_mut().pool.sender().spawn(future).unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
/// Run a future to completion on the Tokio runtime.
|
||||
///
|
||||
/// This runs the given future on the runtime, blocking until it is
|
||||
/// 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 asynchronous context.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the executor is at capacity, if the provided
|
||||
/// future panics, or if called within an asynchronous execution context.
|
||||
pub fn block_on<F, R, E>(&mut self, future: F) -> Result<R, E>
|
||||
where
|
||||
F: Send + 'static + Future<Item = R, Error = E>,
|
||||
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!())));
|
||||
entered.block_on(rx).unwrap()
|
||||
}
|
||||
|
||||
/// Run a future to completion on the Tokio runtime, then wait for all
|
||||
/// background futures to complete too.
|
||||
///
|
||||
/// This runs the given future on the runtime, blocking until it is
|
||||
/// complete, waiting for background futures to complete, and yielding
|
||||
/// 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 asynchronous context.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the executor is at capacity, if the provided
|
||||
/// future panics, or if called within an asynchronous execution context.
|
||||
pub fn block_on_all<F, R, E>(mut self, future: F) -> Result<R, E>
|
||||
where
|
||||
F: Send + 'static + Future<Item = R, Error = E>,
|
||||
R: Send + 'static,
|
||||
E: Send + 'static,
|
||||
{
|
||||
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.
|
||||
///
|
||||
/// Returns a future that completes once the shutdown operation has
|
||||
/// completed.
|
||||
///
|
||||
/// This function can be used to perform a graceful shutdown of the runtime.
|
||||
///
|
||||
/// The runtime enters an idle state once **all** of the following occur.
|
||||
///
|
||||
/// * The thread pool has no tasks to execute, i.e., all tasks that were
|
||||
/// spawned have completed.
|
||||
/// * The reactor is not managing any I/O resources.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
/// use tokio::prelude::*;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // Use the runtime...
|
||||
///
|
||||
/// // Shutdown the runtime
|
||||
/// rt.shutdown_on_idle()
|
||||
/// .wait().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [mod]: index.html
|
||||
pub fn shutdown_on_idle(mut self) -> Shutdown {
|
||||
let inner = self.inner.take().unwrap();
|
||||
let inner = inner.pool.shutdown_on_idle();
|
||||
Shutdown { inner }
|
||||
}
|
||||
|
||||
/// Signals the runtime to shutdown immediately.
|
||||
///
|
||||
/// Returns a future that completes once the shutdown operation has
|
||||
/// completed.
|
||||
///
|
||||
/// This function will forcibly shutdown the runtime, causing any
|
||||
/// in-progress work to become canceled. The shutdown steps are:
|
||||
///
|
||||
/// * Drain any scheduled work queues.
|
||||
/// * Drop any futures that have not yet completed.
|
||||
/// * Drop the reactor.
|
||||
///
|
||||
/// Once the reactor has dropped, any outstanding I/O resources bound to
|
||||
/// that reactor will no longer function. Calling any method on them will
|
||||
/// result in an error.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
/// use tokio::prelude::*;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // Use the runtime...
|
||||
///
|
||||
/// // Shutdown the runtime
|
||||
/// rt.shutdown_now()
|
||||
/// .wait().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [mod]: index.html
|
||||
pub fn shutdown_now(mut self) -> Shutdown {
|
||||
let inner = self.inner.take().unwrap();
|
||||
Shutdown::shutdown_now(inner)
|
||||
}
|
||||
|
||||
fn inner(&self) -> &Inner {
|
||||
self.inner.as_ref().unwrap()
|
||||
}
|
||||
|
||||
fn inner_mut(&mut self) -> &mut Inner {
|
||||
self.inner.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Runtime {
|
||||
fn drop(&mut self) {
|
||||
if let Some(inner) = self.inner.take() {
|
||||
let shutdown = Shutdown::shutdown_now(inner);
|
||||
let _ = shutdown.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use runtime::{Inner, Runtime};
|
||||
use super::{Inner, Runtime};
|
||||
|
||||
use reactor::Reactor;
|
||||
|
||||
@@ -11,6 +11,7 @@ use tokio_reactor;
|
||||
use tokio_threadpool::Builder as ThreadPoolBuilder;
|
||||
use tokio_timer::clock::{self, Clock};
|
||||
use tokio_timer::timer::{self, Timer};
|
||||
use tokio_trace_core as trace;
|
||||
|
||||
/// Builds Tokio Runtime with custom configuration values.
|
||||
///
|
||||
@@ -90,10 +91,10 @@ 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")]
|
||||
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;
|
||||
@@ -330,14 +331,23 @@ impl Builder {
|
||||
// Get a handle to the clock for the runtime.
|
||||
let clock = self.clock.clone();
|
||||
|
||||
let pool = self.threadpool_builder
|
||||
// Get the current trace dispatcher.
|
||||
// TODO(eliza): when `tokio-trace-core` is stable enough to take a
|
||||
// public API dependency, we should allow users to set a custom
|
||||
// subscriber for the runtime.
|
||||
let dispatch = trace::dispatcher::get_default(trace::Dispatch::clone);
|
||||
|
||||
let pool = self
|
||||
.threadpool_builder
|
||||
.around_worker(move |w, enter| {
|
||||
let index = w.id().to_usize();
|
||||
|
||||
tokio_reactor::with_default(&reactor_handles[index], enter, |enter| {
|
||||
clock::with_default(&clock, enter, |enter| {
|
||||
timer::with_default(&timer_handles[index], enter, |_| {
|
||||
w.run();
|
||||
trace::dispatcher::with_default(&dispatch, || {
|
||||
w.run();
|
||||
})
|
||||
});
|
||||
})
|
||||
});
|
||||
@@ -0,0 +1,395 @@
|
||||
mod builder;
|
||||
mod shutdown;
|
||||
mod task_executor;
|
||||
|
||||
pub use self::builder::Builder;
|
||||
pub use self::shutdown::Shutdown;
|
||||
pub use self::task_executor::TaskExecutor;
|
||||
|
||||
use reactor::{Handle, Reactor};
|
||||
|
||||
use std::io;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use tokio_executor::enter;
|
||||
use tokio_threadpool as threadpool;
|
||||
|
||||
use futures;
|
||||
use futures::future::Future;
|
||||
|
||||
/// Handle to the Tokio runtime.
|
||||
///
|
||||
/// The Tokio runtime includes a reactor as well as an executor for running
|
||||
/// tasks.
|
||||
///
|
||||
/// Instances of `Runtime` can be created using [`new`] or [`Builder`]. However,
|
||||
/// most users will use [`tokio::run`], which uses a `Runtime` internally.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
/// [`new`]: #method.new
|
||||
/// [`Builder`]: struct.Builder.html
|
||||
/// [`tokio::run`]: fn.run.html
|
||||
#[derive(Debug)]
|
||||
pub struct Runtime {
|
||||
inner: Option<Inner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
/// 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,
|
||||
}
|
||||
|
||||
// ===== impl Runtime =====
|
||||
|
||||
/// Start the Tokio runtime using the supplied future to bootstrap execution.
|
||||
///
|
||||
/// This function is used to bootstrap the execution of a Tokio application. It
|
||||
/// does the following:
|
||||
///
|
||||
/// * Start the Tokio runtime using a default configuration.
|
||||
/// * Spawn the given future onto the thread pool.
|
||||
/// * Block the current thread until the runtime shuts down.
|
||||
///
|
||||
/// Note that the function will not return immediately once `future` has
|
||||
/// completed. Instead it waits for the entire runtime to become idle.
|
||||
///
|
||||
/// See the [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use futures::{Future, Stream};
|
||||
/// use tokio::net::TcpListener;
|
||||
///
|
||||
/// # fn process<T>(_: T) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
/// # unimplemented!();
|
||||
/// # }
|
||||
/// # fn dox() {
|
||||
/// # let addr = "127.0.0.1:8080".parse().unwrap();
|
||||
/// let listener = TcpListener::bind(&addr).unwrap();
|
||||
///
|
||||
/// let server = listener.incoming()
|
||||
/// .map_err(|e| println!("error = {:?}", e))
|
||||
/// .for_each(|socket| {
|
||||
/// tokio::spawn(process(socket))
|
||||
/// });
|
||||
///
|
||||
/// tokio::run(server);
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if called from the context of an executor.
|
||||
///
|
||||
/// [mod]: ../index.html
|
||||
pub fn run<F>(future: F)
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
// 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);
|
||||
entered
|
||||
.block_on(runtime.shutdown_on_idle())
|
||||
.expect("shutdown cannot error")
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
/// Create a new runtime instance with default configuration values.
|
||||
///
|
||||
/// This results in a reactor, thread pool, and timer being initialized. The
|
||||
/// thread pool will not spawn any worker threads until it needs to, i.e.
|
||||
/// tasks are scheduled to run.
|
||||
///
|
||||
/// Most users will not need to call this function directly, instead they
|
||||
/// will use [`tokio::run`](fn.run.html).
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a new `Runtime` with default configuration values.
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
/// use tokio::prelude::*;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // Use the runtime...
|
||||
///
|
||||
/// // Shutdown the runtime
|
||||
/// rt.shutdown_now()
|
||||
/// .wait().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [mod]: index.html
|
||||
pub fn new() -> io::Result<Self> {
|
||||
Builder::new().build()
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.5", note = "use `reactor` instead")]
|
||||
#[doc(hidden)]
|
||||
pub fn handle(&self) -> &Handle {
|
||||
#[allow(deprecated)]
|
||||
self.reactor()
|
||||
}
|
||||
|
||||
/// Return a reference to the reactor handle for this runtime instance.
|
||||
///
|
||||
/// The returned handle reference can be cloned in order to get an owned
|
||||
/// value of the handle. This handle can be used to initialize I/O resources
|
||||
/// (like TCP or UDP sockets) that will not be used on the runtime.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// let reactor_handle = rt.reactor().clone();
|
||||
///
|
||||
/// // use `reactor_handle`
|
||||
/// ```
|
||||
#[deprecated(since = "0.1.11", note = "there is now a reactor per worker thread")]
|
||||
pub fn reactor(&self) -> &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.
|
||||
///
|
||||
/// The returned handle can be used to spawn tasks that run on this runtime.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// let executor_handle = rt.executor();
|
||||
///
|
||||
/// // use `executor_handle`
|
||||
/// ```
|
||||
pub fn executor(&self) -> TaskExecutor {
|
||||
let inner = self.inner().pool.sender().clone();
|
||||
TaskExecutor { inner }
|
||||
}
|
||||
|
||||
/// Spawn a future onto the Tokio runtime.
|
||||
///
|
||||
/// This spawns the given future onto the runtime's executor, usually a
|
||||
/// thread pool. The thread pool is then responsible for polling the future
|
||||
/// until it completes.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// # use futures::{future, Future, Stream};
|
||||
/// use tokio::runtime::Runtime;
|
||||
///
|
||||
/// # fn dox() {
|
||||
/// // Create the runtime
|
||||
/// let mut rt = Runtime::new().unwrap();
|
||||
///
|
||||
/// // Spawn a future onto the runtime
|
||||
/// rt.spawn(future::lazy(|| {
|
||||
/// println!("now running on a worker thread");
|
||||
/// Ok(())
|
||||
/// }));
|
||||
/// # }
|
||||
/// # pub fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the spawn fails. Failure occurs if the executor
|
||||
/// is currently at capacity and is unable to spawn a new future.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
self.inner_mut().pool.sender().spawn(future).unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
/// Run a future to completion on the Tokio runtime.
|
||||
///
|
||||
/// This runs the given future on the runtime, blocking until it is
|
||||
/// 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 asynchronous context.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the executor is at capacity, if the provided
|
||||
/// future panics, or if called within an asynchronous execution context.
|
||||
pub fn block_on<F, R, E>(&mut self, future: F) -> Result<R, E>
|
||||
where
|
||||
F: Send + 'static + Future<Item = R, Error = E>,
|
||||
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!())));
|
||||
entered.block_on(rx).unwrap()
|
||||
}
|
||||
|
||||
/// Run a future to completion on the Tokio runtime, then wait for all
|
||||
/// background futures to complete too.
|
||||
///
|
||||
/// This runs the given future on the runtime, blocking until it is
|
||||
/// complete, waiting for background futures to complete, and yielding
|
||||
/// 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 asynchronous context.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the executor is at capacity, if the provided
|
||||
/// future panics, or if called within an asynchronous execution context.
|
||||
pub fn block_on_all<F, R, E>(mut self, future: F) -> Result<R, E>
|
||||
where
|
||||
F: Send + 'static + Future<Item = R, Error = E>,
|
||||
R: Send + 'static,
|
||||
E: Send + 'static,
|
||||
{
|
||||
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.
|
||||
///
|
||||
/// Returns a future that completes once the shutdown operation has
|
||||
/// completed.
|
||||
///
|
||||
/// This function can be used to perform a graceful shutdown of the runtime.
|
||||
///
|
||||
/// The runtime enters an idle state once **all** of the following occur.
|
||||
///
|
||||
/// * The thread pool has no tasks to execute, i.e., all tasks that were
|
||||
/// spawned have completed.
|
||||
/// * The reactor is not managing any I/O resources.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
/// use tokio::prelude::*;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // Use the runtime...
|
||||
///
|
||||
/// // Shutdown the runtime
|
||||
/// rt.shutdown_on_idle()
|
||||
/// .wait().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [mod]: index.html
|
||||
pub fn shutdown_on_idle(mut self) -> Shutdown {
|
||||
let inner = self.inner.take().unwrap();
|
||||
let inner = inner.pool.shutdown_on_idle();
|
||||
Shutdown { inner }
|
||||
}
|
||||
|
||||
/// Signals the runtime to shutdown immediately.
|
||||
///
|
||||
/// Returns a future that completes once the shutdown operation has
|
||||
/// completed.
|
||||
///
|
||||
/// This function will forcibly shutdown the runtime, causing any
|
||||
/// in-progress work to become canceled. The shutdown steps are:
|
||||
///
|
||||
/// * Drain any scheduled work queues.
|
||||
/// * Drop any futures that have not yet completed.
|
||||
/// * Drop the reactor.
|
||||
///
|
||||
/// Once the reactor has dropped, any outstanding I/O resources bound to
|
||||
/// that reactor will no longer function. Calling any method on them will
|
||||
/// result in an error.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::runtime::Runtime;
|
||||
/// use tokio::prelude::*;
|
||||
///
|
||||
/// let rt = Runtime::new()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // Use the runtime...
|
||||
///
|
||||
/// // Shutdown the runtime
|
||||
/// rt.shutdown_now()
|
||||
/// .wait().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [mod]: index.html
|
||||
pub fn shutdown_now(mut self) -> Shutdown {
|
||||
let inner = self.inner.take().unwrap();
|
||||
Shutdown::shutdown_now(inner)
|
||||
}
|
||||
|
||||
fn inner(&self) -> &Inner {
|
||||
self.inner.as_ref().unwrap()
|
||||
}
|
||||
|
||||
fn inner_mut(&mut self) -> &mut Inner {
|
||||
self.inner.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Runtime {
|
||||
fn drop(&mut self) {
|
||||
if let Some(inner) = self.inner.take() {
|
||||
let shutdown = Shutdown::shutdown_now(inner);
|
||||
let _ = shutdown.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use runtime::Inner;
|
||||
use super::Inner;
|
||||
use tokio_threadpool as threadpool;
|
||||
|
||||
use std::fmt;
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
//! Future-aware synchronization
|
||||
//!
|
||||
//! This module is enabled with the **`sync`** feature flag.
|
||||
//!
|
||||
//! Tasks sometimes need to communicate with each other. This module contains
|
||||
//! two basic abstractions for doing so:
|
||||
//!
|
||||
//! - [oneshot](oneshot/index.html), a way of sending a single value
|
||||
//! from one task to another.
|
||||
//! - [mpsc](mpsc/index.html), a multi-producer, single-consumer channel for
|
||||
//! sending values between tasks.
|
||||
//! - [watch](watch/index.html), a single-producer, multi-consumer channel that
|
||||
//! only stores the **most recently** sent value.
|
||||
|
||||
pub use tokio_sync::{mpsc, oneshot, watch};
|
||||
+1
-9
@@ -82,15 +82,7 @@
|
||||
//! [Interval]: struct.Interval.html
|
||||
//! [`DelayQueue`]: struct.DelayQueue.html
|
||||
|
||||
pub use tokio_timer::{
|
||||
delay_queue,
|
||||
DelayQueue,
|
||||
Error,
|
||||
Interval,
|
||||
Delay,
|
||||
Timeout,
|
||||
timeout,
|
||||
};
|
||||
pub use tokio_timer::{delay_queue, timeout, Delay, DelayQueue, Error, Interval, Timeout};
|
||||
|
||||
#[deprecated(since = "0.1.8", note = "use Timeout instead")]
|
||||
#[allow(deprecated)]
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
use futures::{Async, Poll, Sink, StartSend, Stream};
|
||||
|
||||
/// A stream combinator which combines the yields the current item
|
||||
/// plus its count starting from 0.
|
||||
///
|
||||
/// This structure is produced by the `Stream::enumerate` method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "Does nothing unless polled"]
|
||||
pub struct Enumerate<T> {
|
||||
inner: T,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl<T> Enumerate<T> {
|
||||
pub(crate) fn new(stream: T) -> Self {
|
||||
Self {
|
||||
inner: stream,
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Acquires a reference to the underlying stream that this combinator is
|
||||
/// pulling from.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Acquires a mutable reference to the underlying stream that this
|
||||
/// combinator is pulling from.
|
||||
///
|
||||
/// Note that care must be taken to avoid tampering with the state of the
|
||||
/// stream which may otherwise confuse this combinator.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Consumes this combinator, returning the underlying stream.
|
||||
///
|
||||
/// Note that this may discard intermediate state of this combinator, so
|
||||
/// care should be taken to avoid losing resources when this is called.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for Enumerate<T>
|
||||
where
|
||||
T: Stream,
|
||||
{
|
||||
type Item = (usize, T::Item);
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, T::Error> {
|
||||
match try_ready!(self.inner.poll()) {
|
||||
Some(item) => {
|
||||
let ret = Some((self.count, item));
|
||||
self.count += 1;
|
||||
Ok(Async::Ready(ret))
|
||||
}
|
||||
None => return Ok(Async::Ready(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forwarding impl of Sink from the underlying stream
|
||||
impl<T> Sink for Enumerate<T>
|
||||
where
|
||||
T: Sink,
|
||||
{
|
||||
type SinkItem = T::SinkItem;
|
||||
type SinkError = T::SinkError;
|
||||
|
||||
fn start_send(&mut self, item: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), T::SinkError> {
|
||||
self.inner.close()
|
||||
}
|
||||
}
|
||||
+11
-5
@@ -1,11 +1,13 @@
|
||||
#[cfg(feature = "timer")]
|
||||
#[allow(deprecated)]
|
||||
use tokio_timer::Deadline;
|
||||
#[cfg(feature = "timer")]
|
||||
use tokio_timer::Timeout;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
use std::time::{Instant, Duration};
|
||||
|
||||
#[cfg(feature = "timer")]
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// An extension trait for `Future` that provides a variety of convenient
|
||||
/// combinator functions.
|
||||
@@ -21,7 +23,6 @@ use std::time::{Instant, Duration};
|
||||
///
|
||||
/// [`timeout`]: #method.timeout
|
||||
pub trait FutureExt: Future {
|
||||
|
||||
/// Creates a new future which allows `self` until `timeout`.
|
||||
///
|
||||
/// This combinator creates a new future which wraps the receiving future
|
||||
@@ -55,17 +56,21 @@ pub trait FutureExt: Future {
|
||||
/// tokio::run(future);
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "timer")]
|
||||
fn timeout(self, timeout: Duration) -> Timeout<Self>
|
||||
where Self: Sized,
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Timeout::new(self, timeout)
|
||||
}
|
||||
|
||||
#[cfg(feature = "timer")]
|
||||
#[deprecated(since = "0.1.8", note = "use `timeout` instead")]
|
||||
#[allow(deprecated)]
|
||||
#[doc(hidden)]
|
||||
fn deadline(self, deadline: Instant) -> Deadline<Self>
|
||||
where Self: Sized,
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Deadline::new(self, deadline)
|
||||
}
|
||||
@@ -78,6 +83,7 @@ mod test {
|
||||
use super::*;
|
||||
use prelude::future;
|
||||
|
||||
#[cfg(feature = "timer")]
|
||||
#[test]
|
||||
fn timeout_polls_at_least_once() {
|
||||
let base_future = future::result::<(), ()>(Ok(()));
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//! [`FutureExt`]: trait.FutureExt.html
|
||||
//! [`StreamExt`]: trait.StreamExt.html
|
||||
|
||||
mod enumerate;
|
||||
mod future;
|
||||
mod stream;
|
||||
|
||||
|
||||
+31
-9
@@ -1,18 +1,17 @@
|
||||
use tokio_timer::{
|
||||
throttle::Throttle,
|
||||
Timeout,
|
||||
};
|
||||
#[cfg(feature = "timer")]
|
||||
use tokio_timer::{throttle::Throttle, Timeout};
|
||||
|
||||
use futures::Stream;
|
||||
|
||||
#[cfg(feature = "timer")]
|
||||
use std::time::Duration;
|
||||
|
||||
pub use util::enumerate::Enumerate;
|
||||
|
||||
/// 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.
|
||||
/// Currently, there are only [`timeout`] and [`throttle`] functions, but
|
||||
/// this will increase over time.
|
||||
///
|
||||
/// Users are not expected to implement this trait. All types that implement
|
||||
/// `Stream` already implement `StreamExt`.
|
||||
@@ -25,12 +24,33 @@ pub trait StreamExt: Stream {
|
||||
/// Throttle down the stream by enforcing a fixed delay between items.
|
||||
///
|
||||
/// Errors are also delayed.
|
||||
#[cfg(feature = "timer")]
|
||||
fn throttle(self, duration: Duration) -> Throttle<Self>
|
||||
where Self: Sized
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Throttle::new(self, duration)
|
||||
}
|
||||
|
||||
/// Creates a new stream which gives the current iteration count as well
|
||||
/// as the next value.
|
||||
///
|
||||
/// The stream returned yields pairs `(i, val)`, where `i` is the
|
||||
/// current index of iteration and `val` is the value returned by the
|
||||
/// iterator.
|
||||
///
|
||||
/// # Overflow Behavior
|
||||
///
|
||||
/// The method does no guarding against overflows, so counting elements of
|
||||
/// an iterator with more than [`std::usize::MAX`] elements either produces the
|
||||
/// wrong result or panics.
|
||||
fn enumerate(self) -> Enumerate<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Enumerate::new(self)
|
||||
}
|
||||
|
||||
/// Creates a new stream which allows `self` until `timeout`.
|
||||
///
|
||||
/// This combinator creates a new stream which wraps the receiving stream
|
||||
@@ -63,8 +83,10 @@ pub trait StreamExt: Stream {
|
||||
/// tokio::run(stream);
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "timer")]
|
||||
fn timeout(self, timeout: Duration) -> Timeout<Self>
|
||||
where Self: Sized,
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Timeout::new(self, timeout)
|
||||
}
|
||||
|
||||
+9
-7
@@ -3,20 +3,22 @@ extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
|
||||
use std::io::{BufReader, BufWriter, Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use std::thread;
|
||||
use std::io::{Read, Write, BufReader, BufWriter};
|
||||
|
||||
use futures::Future;
|
||||
use futures::stream::Stream;
|
||||
use tokio_io::io::copy;
|
||||
use futures::Future;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_io::io::copy;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+5
-10
@@ -1,7 +1,7 @@
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_timer;
|
||||
extern crate env_logger;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::{self, current_thread};
|
||||
@@ -26,10 +26,7 @@ fn clock_and_timer_concurrent() {
|
||||
let when = Instant::now() + Duration::from_millis(5_000);
|
||||
let clock = Clock::new_with_now(MockNow(when));
|
||||
|
||||
let mut rt = runtime::Builder::new()
|
||||
.clock(clock)
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut rt = runtime::Builder::new().clock(clock).build().unwrap();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
@@ -53,10 +50,7 @@ fn clock_and_timer_single_threaded() {
|
||||
let when = Instant::now() + Duration::from_millis(5_000);
|
||||
let clock = Clock::new_with_now(MockNow(when));
|
||||
|
||||
let mut rt = current_thread::Builder::new()
|
||||
.clock(clock)
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut rt = current_thread::Builder::new().clock(clock).build().unwrap();
|
||||
|
||||
rt.block_on({
|
||||
Delay::new(when)
|
||||
@@ -65,5 +59,6 @@ fn clock_and_timer_single_threaded() {
|
||||
assert!(Instant::now() < when);
|
||||
Ok(())
|
||||
})
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
extern crate tokio;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use std::thread;
|
||||
use std::net;
|
||||
use std::thread;
|
||||
|
||||
use futures::future;
|
||||
use futures::prelude::*;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_timer;
|
||||
|
||||
use futures::sync::mpsc;
|
||||
use tokio::util::StreamExt;
|
||||
|
||||
#[test]
|
||||
fn enumerate() {
|
||||
use futures::*;
|
||||
|
||||
let (mut tx, rx) = mpsc::channel(1);
|
||||
|
||||
std::thread::spawn(|| {
|
||||
for i in 0..5 {
|
||||
tx = tx.send(i * 2).wait().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
let result = rx.enumerate().collect();
|
||||
assert_eq!(
|
||||
result.wait(),
|
||||
Ok(vec![(0, 0), (1, 2), (2, 4), (3, 6), (4, 8)])
|
||||
);
|
||||
}
|
||||
+27
-22
@@ -1,42 +1,48 @@
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::{io, thread};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
|
||||
use futures::prelude::*;
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hammer_old() {
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let threads = (0..10).map(|_| {
|
||||
thread::spawn(|| {
|
||||
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(srv.local_addr());
|
||||
let mine = TcpStream::connect(&addr);
|
||||
let theirs = srv.incoming().into_future()
|
||||
.map(|(s, _)| s.unwrap())
|
||||
.map_err(|(s, _)| s);
|
||||
let (mine, theirs) = t!(mine.join(theirs).wait());
|
||||
let threads = (0..10)
|
||||
.map(|_| {
|
||||
thread::spawn(|| {
|
||||
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(srv.local_addr());
|
||||
let mine = TcpStream::connect(&addr);
|
||||
let theirs = srv
|
||||
.incoming()
|
||||
.into_future()
|
||||
.map(|(s, _)| s.unwrap())
|
||||
.map_err(|(s, _)| s);
|
||||
let (mine, theirs) = t!(mine.join(theirs).wait());
|
||||
|
||||
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
|
||||
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
|
||||
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
|
||||
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
|
||||
})
|
||||
})
|
||||
}).collect::<Vec<_>>();
|
||||
.collect::<Vec<_>>();
|
||||
for thread in threads {
|
||||
thread.join().unwrap();
|
||||
}
|
||||
@@ -51,8 +57,7 @@ impl io::Read for Rd {
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_io::AsyncRead for Rd {
|
||||
}
|
||||
impl tokio_io::AsyncRead for Rd {}
|
||||
|
||||
impl io::Write for Wr {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
|
||||
+190
-143
@@ -1,16 +1,16 @@
|
||||
extern crate tokio;
|
||||
extern crate futures;
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio::codec::*;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use bytes::{Bytes, BytesMut, BufMut};
|
||||
use futures::{Stream, Sink, Poll};
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use futures::Async::*;
|
||||
use futures::{Poll, Sink, Stream};
|
||||
|
||||
use std::io;
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
|
||||
macro_rules! mock {
|
||||
($($x:expr,)*) => {{
|
||||
@@ -20,7 +20,6 @@ macro_rules! mock {
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn read_empty_io_yields_nothing() {
|
||||
let mut io = FramedRead::new(mock!(), LengthDelimitedCodec::new());
|
||||
@@ -30,9 +29,12 @@ fn read_empty_io_yields_nothing() {
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_one_packet() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
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));
|
||||
@@ -74,9 +76,12 @@ fn read_single_multi_frame_one_packet() {
|
||||
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());
|
||||
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())));
|
||||
@@ -86,11 +91,14 @@ fn read_single_multi_frame_one_packet() {
|
||||
|
||||
#[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());
|
||||
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));
|
||||
@@ -98,13 +106,16 @@ fn read_single_frame_multi_packet() {
|
||||
|
||||
#[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());
|
||||
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())));
|
||||
@@ -114,14 +125,17 @@ fn read_multi_frame_multi_packet() {
|
||||
|
||||
#[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());
|
||||
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);
|
||||
@@ -132,19 +146,21 @@ fn read_single_frame_multi_packet_wait() {
|
||||
|
||||
#[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());
|
||||
|
||||
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);
|
||||
@@ -159,20 +175,26 @@ fn read_multi_frame_multi_packet_wait() {
|
||||
|
||||
#[test]
|
||||
fn read_incomplete_head() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
}, LengthDelimitedCodec::new());
|
||||
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());
|
||||
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);
|
||||
@@ -181,12 +203,15 @@ fn read_incomplete_head_multi() {
|
||||
|
||||
#[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());
|
||||
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);
|
||||
@@ -206,11 +231,10 @@ fn read_max_frame_len() {
|
||||
|
||||
#[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()),
|
||||
});
|
||||
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);
|
||||
@@ -219,13 +243,12 @@ fn read_update_max_frame_len_at_rest() {
|
||||
|
||||
#[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()),
|
||||
});
|
||||
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);
|
||||
@@ -274,9 +297,15 @@ fn read_single_multi_frame_one_packet_skip_none_adjusted() {
|
||||
Ok(data.into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"xx\x00\x09abcdefghi"[..].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(Some(b"zz\x00\x0bhello world"[..].into()))
|
||||
);
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
@@ -316,20 +345,20 @@ fn write_single_frame_length_adjusted() {
|
||||
|
||||
#[test]
|
||||
fn write_nothing_yields_nothing() {
|
||||
let mut io = FramedWrite::new(
|
||||
mock!(),
|
||||
LengthDelimitedCodec::new()
|
||||
);
|
||||
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());
|
||||
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());
|
||||
@@ -338,56 +367,71 @@ fn write_single_frame_one_packet() {
|
||||
|
||||
#[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());
|
||||
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
|
||||
.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());
|
||||
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
|
||||
.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());
|
||||
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());
|
||||
@@ -412,7 +456,6 @@ fn write_single_frame_little_endian() {
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_with_short_length_field() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
@@ -432,54 +475,63 @@ fn write_single_frame_with_short_length_field() {
|
||||
fn write_max_frame_len() {
|
||||
let mut io = length_delimited::Builder::new()
|
||||
.max_frame_length(5)
|
||||
.new_write(mock! { });
|
||||
.new_write(mock! {});
|
||||
|
||||
assert_eq!(io.start_send(Bytes::from("abcdef")).unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||
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),
|
||||
});
|
||||
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_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),
|
||||
});
|
||||
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_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! { });
|
||||
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_eq!(
|
||||
io.poll_complete().unwrap_err().kind(),
|
||||
io::ErrorKind::WriteZero
|
||||
);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
@@ -490,9 +542,7 @@ fn encode_overflow() {
|
||||
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<_>>();
|
||||
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.
|
||||
@@ -531,8 +581,7 @@ impl io::Read for Mock {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Mock {
|
||||
}
|
||||
impl AsyncRead for Mock {}
|
||||
|
||||
impl io::Write for Mock {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
@@ -551,9 +600,7 @@ impl io::Write for Mock {
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(Op::Flush)) => {
|
||||
Ok(())
|
||||
}
|
||||
Some(Ok(Op::Flush)) => Ok(()),
|
||||
Some(Ok(_)) => panic!(),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(()),
|
||||
|
||||
+12
-10
@@ -1,19 +1,19 @@
|
||||
extern crate bytes;
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_threadpool;
|
||||
extern crate bytes;
|
||||
|
||||
use std::io;
|
||||
use std::net::Shutdown;
|
||||
|
||||
use bytes::{BytesMut, BufMut};
|
||||
use futures::{Future, Stream, Sink};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::{Future, Sink, Stream};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_codec::{Encoder, Decoder};
|
||||
use tokio_io::io::{write_all, read};
|
||||
use tokio_codec::{Decoder, Encoder};
|
||||
use tokio_io::io::{read, write_all};
|
||||
use tokio_threadpool::Builder;
|
||||
|
||||
pub struct LineCodec;
|
||||
@@ -53,20 +53,22 @@ impl Encoder for LineCodec {
|
||||
fn echo() {
|
||||
drop(env_logger::try_init());
|
||||
|
||||
let pool = Builder::new()
|
||||
.pool_size(1)
|
||||
.build();
|
||||
let pool = Builder::new().pool_size(1).build();
|
||||
|
||||
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
let sender = pool.sender().clone();
|
||||
let srv = listener.incoming().for_each(move |socket| {
|
||||
let (sink, stream) = LineCodec.framed(socket).split();
|
||||
sender.spawn(sink.send_all(stream).map(|_| ()).map_err(|_| ())).unwrap();
|
||||
sender
|
||||
.spawn(sink.send_all(stream).map(|_| ()).map_err(|_| ()))
|
||||
.unwrap();
|
||||
Ok(())
|
||||
});
|
||||
|
||||
pool.sender().spawn(srv.map_err(|e| panic!("srv error: {}", e))).unwrap();
|
||||
pool.sender()
|
||||
.spawn(srv.map_err(|e| panic!("srv error: {}", e)))
|
||||
.unwrap();
|
||||
|
||||
let client = TcpStream::connect(&addr);
|
||||
let client = client.wait().unwrap();
|
||||
|
||||
+27
-12
@@ -13,18 +13,20 @@ use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::Future;
|
||||
use mio::event::Evented;
|
||||
use mio::unix::{UnixReady, EventedFd};
|
||||
use mio::unix::{EventedFd, UnixReady};
|
||||
use mio::{PollOpt, Ready, Token};
|
||||
use tokio::reactor::{Handle, PollEvented2};
|
||||
use tokio_io::io::read_to_end;
|
||||
use futures::Future;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct MyFile(File);
|
||||
@@ -46,13 +48,23 @@ impl io::Read for MyFile {
|
||||
}
|
||||
|
||||
impl Evented for MyFile {
|
||||
fn register(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt)
|
||||
-> io::Result<()> {
|
||||
fn register(
|
||||
&self,
|
||||
poll: &mio::Poll,
|
||||
token: Token,
|
||||
interest: Ready,
|
||||
opts: PollOpt,
|
||||
) -> io::Result<()> {
|
||||
let hup: Ready = UnixReady::hup().into();
|
||||
EventedFd(&self.0.as_raw_fd()).register(poll, token, interest | hup, opts)
|
||||
}
|
||||
fn reregister(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt)
|
||||
-> io::Result<()> {
|
||||
fn reregister(
|
||||
&self,
|
||||
poll: &mio::Poll,
|
||||
token: Token,
|
||||
interest: Ready,
|
||||
opts: PollOpt,
|
||||
) -> io::Result<()> {
|
||||
let hup: Ready = UnixReady::hup().into();
|
||||
EventedFd(&self.0.as_raw_fd()).reregister(poll, token, interest | hup, opts)
|
||||
}
|
||||
@@ -68,8 +80,11 @@ fn hup() {
|
||||
let handle = Handle::default();
|
||||
unsafe {
|
||||
let mut pipes = [0; 2];
|
||||
assert!(libc::pipe(pipes.as_mut_ptr()) != -1,
|
||||
"pipe error: {}", io::Error::last_os_error());
|
||||
assert!(
|
||||
libc::pipe(pipes.as_mut_ptr()) != -1,
|
||||
"pipe error: {}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
let read = File::from_raw_fd(pipes[0]);
|
||||
let mut write = File::from_raw_fd(pipes[1]);
|
||||
let t = thread::spawn(move || {
|
||||
|
||||
+5
-3
@@ -6,8 +6,8 @@ extern crate tokio_tcp;
|
||||
use tokio_reactor::Reactor;
|
||||
use tokio_tcp::TcpListener;
|
||||
|
||||
use futures::{Future, Stream};
|
||||
use futures::executor::{spawn, Notify, Spawn};
|
||||
use futures::{Future, Stream};
|
||||
|
||||
use std::mem;
|
||||
use std::net::TcpStream;
|
||||
@@ -62,7 +62,8 @@ fn test_drop_on_notify() {
|
||||
|
||||
// Define a task that just drains the listener
|
||||
let task = Box::new({
|
||||
listener.incoming()
|
||||
listener
|
||||
.incoming()
|
||||
.for_each(|_| Ok(()))
|
||||
.map_err(|_| panic!())
|
||||
}) as Box<Future<Item = (), Error = ()>>;
|
||||
@@ -75,7 +76,8 @@ fn test_drop_on_notify() {
|
||||
tokio_reactor::with_default(&reactor.handle(), &mut enter, |_| {
|
||||
let id = &*task as *const Task as usize;
|
||||
|
||||
task.lock().unwrap()
|
||||
task.lock()
|
||||
.unwrap()
|
||||
.poll_future_notify(¬ify, id)
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
+83
-67
@@ -1,12 +1,12 @@
|
||||
extern crate tokio;
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
use futures::sync::oneshot;
|
||||
use std::sync::{Arc, Mutex, atomic};
|
||||
use std::sync::{atomic, Arc, Mutex};
|
||||
use std::thread;
|
||||
use tokio::io;
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::future::lazy;
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::Runtime;
|
||||
@@ -17,18 +17,22 @@ use tokio::runtime::Runtime;
|
||||
pub use futures::future::Executor;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn create_client_server_future() -> Box<Future<Item=(), Error=()> + Send> {
|
||||
fn create_client_server_future() -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
let server = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(server.local_addr());
|
||||
let client = TcpStream::connect(&addr);
|
||||
|
||||
let server = server.incoming().take(1)
|
||||
let server = server
|
||||
.incoming()
|
||||
.take(1)
|
||||
.map_err(|e| panic!("accept err = {:?}", e))
|
||||
.for_each(|socket| {
|
||||
tokio::spawn({
|
||||
@@ -48,8 +52,7 @@ fn create_client_server_future() -> Box<Future<Item=(), Error=()> + Send> {
|
||||
.map_err(|e| panic!("read err = {:?}", e))
|
||||
});
|
||||
|
||||
let future = server.join(client)
|
||||
.map(|_| ());
|
||||
let future = server.join(client).map(|_| ());
|
||||
Box::new(future)
|
||||
}
|
||||
|
||||
@@ -64,8 +67,7 @@ fn runtime_tokio_run() {
|
||||
fn runtime_single_threaded() {
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let mut runtime = tokio::runtime::current_thread::Runtime::new()
|
||||
.unwrap();
|
||||
let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap();
|
||||
runtime.block_on(create_client_server_future()).unwrap();
|
||||
runtime.run().unwrap();
|
||||
}
|
||||
@@ -82,7 +84,7 @@ mod runtime_single_threaded_block_on_all {
|
||||
|
||||
fn test<F>(spawn: F)
|
||||
where
|
||||
F: Fn(Box<Future<Item=(), Error=()> + Send>),
|
||||
F: Fn(Box<Future<Item = (), Error = ()> + Send>),
|
||||
{
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
@@ -103,7 +105,8 @@ mod runtime_single_threaded_block_on_all {
|
||||
})));
|
||||
|
||||
Ok::<_, ()>("hello")
|
||||
})).unwrap();
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(2, *cnt.lock().unwrap());
|
||||
assert_eq!(msg, "hello");
|
||||
@@ -111,7 +114,9 @@ mod runtime_single_threaded_block_on_all {
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
test(|f| { tokio::spawn(f); })
|
||||
test(|f| {
|
||||
tokio::spawn(f);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -128,10 +133,7 @@ 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>,
|
||||
),
|
||||
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();
|
||||
@@ -149,10 +151,13 @@ mod runtime_single_threaded_racy {
|
||||
let (tx, rx) = futures::sync::oneshot::channel();
|
||||
|
||||
let handle = handle_rx.recv().unwrap();
|
||||
spawn(handle, Box::new(futures::future::lazy(move || {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})));
|
||||
spawn(
|
||||
handle,
|
||||
Box::new(futures::future::lazy(move || {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})),
|
||||
);
|
||||
|
||||
// signal runtime thread to exit
|
||||
trigger.send(()).unwrap();
|
||||
@@ -165,12 +170,16 @@ mod runtime_single_threaded_racy {
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
test(|handle, f| { handle.spawn(f).unwrap(); })
|
||||
test(|handle, f| {
|
||||
handle.spawn(f).unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|handle, f| { handle.execute(f).unwrap(); })
|
||||
test(|handle, f| {
|
||||
handle.execute(f).unwrap();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,25 +191,28 @@ mod runtime_multi_threaded {
|
||||
{
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let mut runtime = tokio::runtime::Builder::new()
|
||||
.build()
|
||||
.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(|rt| {
|
||||
rt.spawn(create_client_server_future());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|rt| { rt.executor().execute(create_client_server_future()).unwrap(); });
|
||||
test(|rt| {
|
||||
rt.executor()
|
||||
.execute(create_client_server_future())
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn block_on_timer() {
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -223,7 +235,7 @@ mod from_block_on {
|
||||
|
||||
fn test<F>(spawn: F)
|
||||
where
|
||||
F: Fn(Box<Future<Item=(), Error=()> + Send>) + Send + 'static,
|
||||
F: Fn(Box<Future<Item = (), Error = ()> + Send>) + Send + 'static,
|
||||
{
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
@@ -305,20 +317,23 @@ mod many {
|
||||
const ITER: usize = 200;
|
||||
fn test<F>(spawn: F)
|
||||
where
|
||||
F: Fn(&mut Runtime, Box<Future<Item=(), Error=()> + Send>),
|
||||
F: Fn(&mut Runtime, Box<Future<Item = (), Error = ()> + Send>),
|
||||
{
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
|
||||
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::<(), ()>(())
|
||||
})));
|
||||
spawn(
|
||||
&mut runtime,
|
||||
Box::new(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
@@ -327,26 +342,25 @@ mod many {
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
test(|rt, f| { rt.spawn(f); })
|
||||
test(|rt, f| {
|
||||
rt.spawn(f);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|rt, f| {
|
||||
rt.executor()
|
||||
.execute(f)
|
||||
.unwrap();
|
||||
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,
|
||||
F: Fn(Box<Future<Item = (), Error = ()> + Send>) + Send + 'static,
|
||||
{
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
@@ -387,19 +401,21 @@ mod from_block_on_all {
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
test(|f| { tokio::spawn(f); })
|
||||
test(|f| {
|
||||
tokio::spawn(f);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
mod nested_enter {
|
||||
use super::*;
|
||||
use tokio::runtime::current_thread;
|
||||
use std::panic;
|
||||
use tokio::runtime::current_thread;
|
||||
|
||||
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,
|
||||
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();
|
||||
@@ -421,16 +437,18 @@ mod nested_enter {
|
||||
}));
|
||||
|
||||
first(Box::new(lazy(move || {
|
||||
panic::catch_unwind(move || {
|
||||
nested(Box::new(lazy(|| { Ok::<(), ()>(()) })))
|
||||
}).expect_err("nested should panic");
|
||||
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");
|
||||
assert!(
|
||||
*panicked.lock().unwrap(),
|
||||
"nested call should have panicked"
|
||||
);
|
||||
}
|
||||
|
||||
fn threadpool_new() -> Runtime {
|
||||
@@ -471,10 +489,7 @@ fn runtime_reactor_handle() {
|
||||
#![allow(deprecated)]
|
||||
|
||||
use futures::Stream;
|
||||
use std::net::{
|
||||
TcpListener as StdListener,
|
||||
TcpStream as StdStream,
|
||||
};
|
||||
use std::net::{TcpListener as StdListener, TcpStream as StdStream};
|
||||
|
||||
let rt = Runtime::new().unwrap();
|
||||
|
||||
@@ -484,10 +499,7 @@ fn runtime_reactor_handle() {
|
||||
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 th = thread::spawn(|| for _ in tk_listener.incoming().take(1).wait() {});
|
||||
|
||||
let _ = StdStream::connect(&addr).unwrap();
|
||||
|
||||
@@ -504,10 +516,14 @@ fn after_start_and_before_stop_is_called() {
|
||||
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();
|
||||
.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();
|
||||
|
||||
|
||||
+12
-15
@@ -1,7 +1,7 @@
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate env_logger;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::timer::*;
|
||||
@@ -31,7 +31,7 @@ fn timer_with_runtime() {
|
||||
|
||||
#[test]
|
||||
fn starving() {
|
||||
use futures::{task, Poll, Async};
|
||||
use futures::{task, Async, Poll};
|
||||
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
@@ -60,12 +60,11 @@ fn starving() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
tokio::run({
|
||||
starve
|
||||
.and_then(move |_ticks| {
|
||||
assert!(Instant::now() >= when);
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
starve.and_then(move |_ticks| {
|
||||
assert!(Instant::now() >= when);
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
rx.recv().unwrap();
|
||||
@@ -82,13 +81,11 @@ fn deadline() {
|
||||
|
||||
#[allow(deprecated)]
|
||||
tokio::run({
|
||||
future::empty::<(), ()>()
|
||||
.deadline(when)
|
||||
.then(move |res| {
|
||||
assert!(res.is_err());
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
future::empty::<(), ()>().deadline(when).then(move |res| {
|
||||
assert!(res.is_err());
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
rx.recv().unwrap();
|
||||
|
||||
@@ -3,12 +3,12 @@ name = "tokio-async-await"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
version = "0.1.4"
|
||||
version = "0.1.6"
|
||||
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"
|
||||
documentation = "https://docs.rs/tokio-async-await/0.1.6"
|
||||
description = """
|
||||
Experimental async/await support for Tokio
|
||||
"""
|
||||
@@ -26,5 +26,4 @@ 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"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018 Tokio Contributors
|
||||
Copyright (c) 2019 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
|
||||
@@ -8,7 +8,8 @@ guarantees. You are living on the edge here.**
|
||||
|
||||
## Usage
|
||||
|
||||
To use this crate, you need to start with a Rust 2018 edition crate.
|
||||
To use this crate, you need to start with a Rust 2018 edition crate, with rustc
|
||||
1.34.0-nightly or later.
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
@@ -17,7 +18,7 @@ Add this to your `Cargo.toml`:
|
||||
edition = "2018"
|
||||
|
||||
# In the `[dependencies]` section
|
||||
tokio = {version = "0.1.0", features = ["async-await-preview"]}
|
||||
tokio = {version = "0.1.15", features = ["async-await-preview"]}
|
||||
```
|
||||
|
||||
Then, get started. In your application, add:
|
||||
|
||||
@@ -61,7 +61,7 @@ async fn process(stream: TcpStream, state: Arc<Mutex<Shared>>) -> io::Result<()>
|
||||
tokio::spawn_async(async move {
|
||||
while let Some(line) = await!(rx.next()) {
|
||||
let line = line.unwrap();
|
||||
await!(lines_tx.send_async(line));
|
||||
await!(lines_tx.send_async(line)).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![feature(await_macro, async_await)]
|
||||
#![feature(await_macro, async_await, futures_api)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
#[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_imports)]
|
||||
use $crate::compat::forward::IntoAwaitable as IntoAwaitableForward;
|
||||
use $crate::std_await;
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut e = $e;
|
||||
let e = e.into_awaitable();
|
||||
std_await!(e)
|
||||
}}
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::future::Future as StdFuture;
|
||||
use std::pin::Pin;
|
||||
use std::future::{
|
||||
Future as StdFuture,
|
||||
};
|
||||
use std::ptr::NonNull;
|
||||
use std::task::{
|
||||
LocalWaker,
|
||||
Poll as StdPoll,
|
||||
UnsafeWake,
|
||||
Waker,
|
||||
};
|
||||
use std::ptr;
|
||||
use std::task::{Poll as StdPoll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
/// Convert an 0.3 `Future` to an 0.1 `Future`.
|
||||
#[derive(Debug)]
|
||||
@@ -19,7 +12,7 @@ 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))
|
||||
Compat(Box::pin(future))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +24,8 @@ pub trait IntoAwaitable {
|
||||
}
|
||||
|
||||
impl<T> IntoAwaitable for T
|
||||
where T: StdFuture,
|
||||
where
|
||||
T: StdFuture,
|
||||
{
|
||||
type Awaitable = Self;
|
||||
|
||||
@@ -41,7 +35,8 @@ where T: StdFuture,
|
||||
}
|
||||
|
||||
impl<T, Item, Error> Future for Compat<T>
|
||||
where T: StdFuture<Output = Result<Item, Error>>,
|
||||
where
|
||||
T: StdFuture<Output = Result<Item, Error>>,
|
||||
{
|
||||
type Item = Item;
|
||||
type Error = Error;
|
||||
@@ -49,9 +44,9 @@ where T: StdFuture<Output = Result<Item, Error>>,
|
||||
fn poll(&mut self) -> Poll<Item, Error> {
|
||||
use futures::Async::*;
|
||||
|
||||
let local_waker = noop_local_waker();
|
||||
let waker = noop_waker();
|
||||
|
||||
let res = self.0.as_mut().poll(&local_waker);
|
||||
let res = self.0.as_mut().poll(&waker);
|
||||
|
||||
match res {
|
||||
StdPoll::Ready(Ok(val)) => Ok(Ready(val)),
|
||||
@@ -63,27 +58,26 @@ where T: StdFuture<Output = Result<Item, Error>>,
|
||||
|
||||
// ===== NoopWaker =====
|
||||
|
||||
struct NoopWaker;
|
||||
|
||||
fn noop_local_waker() -> LocalWaker {
|
||||
let w: NonNull<NoopWaker> = NonNull::dangling();
|
||||
unsafe { LocalWaker::new(w) }
|
||||
fn noop_raw_waker() -> RawWaker {
|
||||
RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE)
|
||||
}
|
||||
|
||||
fn noop_waker() -> Waker {
|
||||
let w: NonNull<NoopWaker> = NonNull::dangling();
|
||||
unsafe { Waker::new(w) }
|
||||
unsafe { Waker::new_unchecked(noop_raw_waker()) }
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
unsafe fn clone_raw(_data: *const ()) -> RawWaker {
|
||||
noop_raw_waker()
|
||||
}
|
||||
|
||||
unsafe fn drop_raw(_data: *const ()) {}
|
||||
|
||||
unsafe fn wake(_data: *const ()) {
|
||||
unimplemented!("async-await-preview currently only supports futures 0.1. Use the compatibility layer of futures 0.3 instead, if you want to use futures 0.3.");
|
||||
}
|
||||
|
||||
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable {
|
||||
clone: clone_raw,
|
||||
drop: drop_raw,
|
||||
wake,
|
||||
};
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
use futures::{Async, Future};
|
||||
|
||||
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};
|
||||
use std::task::{Poll as StdPoll, Waker};
|
||||
|
||||
/// 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};
|
||||
use futures::Async::{NotReady, Ready};
|
||||
|
||||
match poll {
|
||||
Ok(Ready(val)) => StdPoll::Ready(Ok(val)),
|
||||
@@ -21,9 +19,9 @@ pub(crate) fn convert_poll<T, E>(poll: Result<Async<T>, E>) -> StdPoll<Result<T,
|
||||
}
|
||||
|
||||
pub(crate) fn convert_poll_stream<T, E>(
|
||||
poll: Result<Async<Option<T>>, E>) -> StdPoll<Option<Result<T, E>>>
|
||||
{
|
||||
use futures::Async::{Ready, NotReady};
|
||||
poll: Result<Async<Option<T>>, E>,
|
||||
) -> StdPoll<Option<Result<T, E>>> {
|
||||
use futures::Async::{NotReady, Ready};
|
||||
|
||||
match poll {
|
||||
Ok(Ready(Some(val))) => StdPoll::Ready(Some(Ok(val))),
|
||||
@@ -50,12 +48,13 @@ impl<T: Future + Unpin> IntoAwaitable for T {
|
||||
}
|
||||
|
||||
impl<T> StdFuture for Compat<T>
|
||||
where T: Future + Unpin
|
||||
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};
|
||||
fn poll(mut self: Pin<&mut Self>, _waker: &Waker) -> StdPoll<Self::Output> {
|
||||
use futures::Async::{NotReady, Ready};
|
||||
|
||||
// TODO: wire in cx
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![doc(hidden)]
|
||||
|
||||
pub mod forward;
|
||||
pub mod backward;
|
||||
pub mod forward;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use tokio_io::AsyncWrite;
|
||||
|
||||
|
||||
use std::io;
|
||||
use std::future::Future;
|
||||
use std::marker::Unpin;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{LocalWaker, Poll};
|
||||
use std::task::{Poll, Waker};
|
||||
|
||||
/// A future used to fully flush an I/O object.
|
||||
#[derive(Debug)]
|
||||
@@ -25,7 +23,7 @@ impl<'a, T: AsyncWrite + ?Sized> Flush<'a, T> {
|
||||
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> {
|
||||
fn poll(mut self: Pin<&mut Self>, _wx: &Waker) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
convert_poll(self.writer.poll_flush())
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ 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.
|
||||
@@ -19,17 +18,14 @@ 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,
|
||||
}
|
||||
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> {
|
||||
fn poll(mut self: Pin<&mut Self>, _waker: &task::Waker) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::future::Future;
|
||||
use std::task::{self, Poll};
|
||||
|
||||
use std::io;
|
||||
use std::marker::Unpin;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
|
||||
@@ -20,10 +19,7 @@ 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,
|
||||
}
|
||||
ReadExact { reader, buf }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +30,7 @@ fn eof() -> io::Error {
|
||||
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> {
|
||||
fn poll(mut self: Pin<&mut Self>, _waker: &task::Waker) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
@@ -47,7 +43,7 @@ impl<'a, T: AsyncRead + ?Sized> Future for ReadExact<'a, T> {
|
||||
this.buf = rest;
|
||||
}
|
||||
if n == 0 {
|
||||
return Poll::Ready(Err(eof()))
|
||||
return Poll::Ready(Err(eof()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ 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.
|
||||
@@ -19,17 +18,14 @@ 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,
|
||||
}
|
||||
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>> {
|
||||
fn poll(mut self: Pin<&mut Self>, _waker: &task::Waker) -> Poll<io::Result<usize>> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::future::Future;
|
||||
use std::task::{self, Poll};
|
||||
|
||||
use std::io;
|
||||
use std::marker::Unpin;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
|
||||
@@ -20,10 +19,7 @@ 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,
|
||||
}
|
||||
WriteAll { writer, buf }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +30,7 @@ fn zero_write() -> io::Error {
|
||||
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<()>> {
|
||||
fn poll(mut self: Pin<&mut Self>, _waker: &task::Waker) -> Poll<io::Result<()>> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
@@ -48,7 +44,7 @@ impl<'a, T: AsyncWrite + ?Sized> Future for WriteAll<'a, T> {
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return Poll::Ready(Err(zero_write()))
|
||||
return Poll::Ready(Err(zero_write()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
#![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")]
|
||||
#![feature(rust_2018_preview, async_await, await_macro, futures_api)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-async-await/0.1.6")]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
@@ -24,12 +16,10 @@ 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,
|
||||
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]
|
||||
@@ -39,77 +29,7 @@ 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(())
|
||||
}));
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -6,8 +6,6 @@ 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.
|
||||
|
||||
@@ -3,7 +3,6 @@ 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
|
||||
@@ -28,9 +27,9 @@ impl<'a, T: Sink + Unpin + ?Sized> Send<'a, T> {
|
||||
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> {
|
||||
fn poll(mut self: Pin<&mut Self>, _waker: &task::Waker) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
use futures::AsyncSink::{Ready, NotReady};
|
||||
use futures::AsyncSink::{NotReady, Ready};
|
||||
|
||||
if let Some(item) = self.item.take() {
|
||||
match self.sink.start_send(item) {
|
||||
|
||||
@@ -6,8 +6,6 @@ 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.
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use futures::Stream;
|
||||
|
||||
use std::future::Future;
|
||||
use std::marker::Unpin;
|
||||
use std::pin::Pin;
|
||||
use std::task::{LocalWaker, Poll};
|
||||
use std::task::{Poll, Waker};
|
||||
|
||||
/// A future of the next element of a stream.
|
||||
#[derive(Debug)]
|
||||
@@ -22,7 +21,7 @@ impl<'a, T: Stream + Unpin> Next<'a, T> {
|
||||
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> {
|
||||
fn poll(mut self: Pin<&mut Self>, _waker: &Waker) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll_stream;
|
||||
|
||||
convert_poll_stream(self.stream.poll())
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# 0.1.0 (unreleased)
|
||||
# 0.1.0 (February 23, 2019)
|
||||
|
||||
* Initial release
|
||||
|
||||
+10
-3
@@ -3,6 +3,9 @@ name = "tokio-buf"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.0"
|
||||
@@ -10,13 +13,17 @@ 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"
|
||||
documentation = "https://docs.rs/tokio-buf/0.1.0/tokio_buf"
|
||||
description = """
|
||||
Asynchronous stream of byte buffers
|
||||
"""
|
||||
categories = ["asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
bytes = { version = "0.4.10", features = [ "either" ] }
|
||||
either = "1.5"
|
||||
bytes = "0.4.10"
|
||||
either = { version = "1.5", optional = true}
|
||||
futures = "0.1.23"
|
||||
|
||||
[features]
|
||||
default = ["util"]
|
||||
util = ["bytes/either", "either"]
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018 Tokio Contributors
|
||||
Copyright (c) 2019 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# tokio-buf
|
||||
|
||||
Asynchronous stream of byte buffers
|
||||
|
||||
[Documenation](https://docs.rs/tokio-buf)
|
||||
|
||||
## Usage
|
||||
|
||||
First, add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio-buf = "0.1.0"
|
||||
```
|
||||
|
||||
Next, add this to your crate:
|
||||
|
||||
```rust
|
||||
extern crate tokio_buf;
|
||||
```
|
||||
|
||||
You can find extensive documentation and examples about how to use this crate
|
||||
online at [https://tokio.rs](https://tokio.rs). The [API
|
||||
documentation](https://docs.rs/tokio-buf) is also a great place to get started
|
||||
for the nitty-gritty.
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
//! 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 {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
//! 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)
|
||||
}
|
||||
}
|
||||
+82
-3
@@ -1,5 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-buf/0.1.0")]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![deny(missing_docs, missing_debug_implementations, unreachable_pub)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! Asynchronous stream of bytes.
|
||||
@@ -10,11 +10,90 @@
|
||||
//! `Buf` (i.e, byte collections).
|
||||
|
||||
extern crate bytes;
|
||||
#[cfg(feature = "util")]
|
||||
extern crate either;
|
||||
#[allow(unused)]
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
|
||||
pub mod buf_stream;
|
||||
mod never;
|
||||
mod size_hint;
|
||||
mod str;
|
||||
mod u8;
|
||||
#[cfg(feature = "util")]
|
||||
pub mod util;
|
||||
|
||||
pub use self::size_hint::SizeHint;
|
||||
#[doc(inline)]
|
||||
pub use buf_stream::BufStream;
|
||||
#[cfg(feature = "util")]
|
||||
pub use util::BufStreamExt;
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use never::Never;
|
||||
use BufStream;
|
||||
use buf_stream::errors::internal::Never;
|
||||
|
||||
use futures::Poll;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use BufStream;
|
||||
use buf_stream::errors::internal::Never;
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::Poll;
|
||||
|
||||
use never::Never;
|
||||
use std::io;
|
||||
use BufStream;
|
||||
|
||||
impl BufStream for Vec<u8> {
|
||||
type Item = io::Cursor<Vec<u8>>;
|
||||
@@ -58,9 +56,7 @@ impl BufStream for BytesMut {
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_bytes<T: Default>(buf: &mut T)
|
||||
-> Poll<Option<io::Cursor<T>>, Never>
|
||||
{
|
||||
fn poll_bytes<T: Default>(buf: &mut T) -> Poll<Option<io::Cursor<T>>, Never> {
|
||||
use std::mem;
|
||||
|
||||
let bytes = mem::replace(buf, Default::default());
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{BufStream, SizeHint};
|
||||
use BufStream;
|
||||
|
||||
use either::Either;
|
||||
use futures::Poll;
|
||||
@@ -43,9 +43,4 @@ where
|
||||
let res = try_ready!(self.right.poll_buf());
|
||||
Ok(res.map(Either::Right).into())
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
// TODO: Implement
|
||||
SizeHint::default()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::{BufStream, FromBufStream};
|
||||
use super::FromBufStream;
|
||||
use BufStream;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
@@ -52,29 +53,26 @@ where
|
||||
|
||||
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 }
|
||||
});
|
||||
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 }
|
||||
})?;
|
||||
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 }
|
||||
})?;
|
||||
let value = U::build(builder).map_err(|err| {
|
||||
let inner = Error::Collect(err);
|
||||
CollectError { inner }
|
||||
})?;
|
||||
return Ok(value.into());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::SizeHint;
|
||||
use SizeHint;
|
||||
|
||||
use bytes::{Buf, BufMut};
|
||||
|
||||
@@ -43,7 +43,9 @@ pub trait FromBufStream<T: Buf>: Sized {
|
||||
|
||||
/// Error returned from collecting into a `Vec<u8>`
|
||||
#[derive(Debug)]
|
||||
pub struct CollectVecError { _p: () }
|
||||
pub struct CollectVecError {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
impl<T: Buf> FromBufStream<T> for Vec<u8> {
|
||||
type Builder = Vec<u8>;
|
||||
@@ -70,7 +72,7 @@ impl<T: Buf> FromBufStream<T> for Vec<u8> {
|
||||
Some(upper) if upper <= 64 => {
|
||||
reserve = upper as usize;
|
||||
}
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// hint.lower() represents the minimum amount of data that will be
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{BufStream, SizeHint};
|
||||
use BufStream;
|
||||
|
||||
use bytes::Buf;
|
||||
use futures::Poll;
|
||||
@@ -40,10 +40,10 @@ where
|
||||
return Err(LimitError { inner: None });
|
||||
}
|
||||
|
||||
let res = self.stream.poll_buf()
|
||||
.map_err(|err| {
|
||||
LimitError { inner: Some(err) }
|
||||
});
|
||||
let res = self
|
||||
.stream
|
||||
.poll_buf()
|
||||
.map_err(|err| LimitError { inner: Some(err) });
|
||||
|
||||
match res {
|
||||
Ok(Ready(Some(ref buf))) => {
|
||||
@@ -59,22 +59,6 @@ where
|
||||
|
||||
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 =====
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user