Compare commits

...
Author SHA1 Message Date
Carl Lerche 25e835c5b7 tcp: specify version for tokio dev dependency
This is required for publishing to crates.io
2019-01-06 23:31:24 -08:00
Carl Lerche 961aae41c4 Bump version to 0.1.14. (#836)
Also bumps:

* tokio-async-await (0.1.5)
* tokio-executor (0.1.6)
* tokio-fs (0.1.5)
* tokio-io (0.1.11)
* tokio-reactor (0.1.8)
* tokio-tcp (0.1.3)
* tokio-threadpool (0.1.10)
* tokio-tls (0.2.1)
* tokio-uds (0.2.5)

...and updates LICENSE files to 2019.
2019-01-06 23:25:55 -08:00
Carl Lerche 74c73b218e Revert "util: implement stream debounce combinator (#747)" (#834)
This reverts commit 7a49ebb65e.

The commit conflicted with another change that was merged, causing CI to fail. The public API
also requires a bit more refinement (#833) and Tokio crates need to be released.
2019-01-06 16:56:49 -08:00
Moritz Gunz 7a49ebb65e util: implement stream debounce combinator (#747) 2019-01-05 11:08:12 -05:00
Stjepan Glavina a687922746 tcp: deprecate TcpStream::try_clone() (#824) 2019-01-05 10:55:58 -05:00
Stjepan Glavina df299ced45 threadpool: panic if a worker thread cannot be spawned (#826) 2019-01-05 10:53:38 -05:00
Carl Lerche 78d1fe0eb0 ci: limit min rust version to cargo check (#829) 2019-01-05 10:52:26 -05:00
Ryan Huang fc8cde383a docs: fix link to ThreadPool (#830) 2019-01-05 10:51:17 -05:00
Sean McArthur 76198f63d7 Provide optional features on tokio crate (#808)
Disabling all features means the only dependency is `futures`.

Relevant pieces of the API can then be enabled with the following features:

- `codec`
- `fs`
- `io`
- `reactor`
- `tcp`
- `timer`
- `udp`
- `uds`

This also introduces the beginnings of enabling only certain pieces of the `Runtime`. As a start, the entire default runtime API is enabled via the `rt-full` feature.
2019-01-04 11:42:33 -08:00
Carl Lerche 39dc5706b7 travis: remove commented out code. (#828)
The commented out lines are no longer relevant and will not be brought
back.
2019-01-03 22:04:09 -08:00
Carl Lerche cbecb87797 executor: fix build (#825)
Two unrelated PRs to the same file resulted in a broken build. This
patch fixes the build by including `Arc`.
2019-01-03 11:26:58 -08:00
Carl Lerche f0bdf1980c threadpool: remove unused fn (#822)
The unused lint on nightly has discovered a new unused fn.
2019-01-03 09:34:37 -08:00
Stjepan Glavina 5e2d93f060 Use Crossbeam's Parker/Unparker (#528) 2019-01-02 21:51:22 -08:00
Taiki Endo 9a8d087c69 Allow deprecated Error::cause (#818)
Error::cause is deprecated in Rust 1.33, but this allows Error::cause
until the minimum supported version of tokio is Rust 1.30.

When the minimum support version of tokio reaches Rust 1.30,
replace Error::cause with Error::source.

Fixes: #817
2019-01-02 14:12:11 -08:00
gralpli 30f59670c8 Clarify what NoopWaker does (#819) 2019-01-02 12:29:30 -08:00
jq-rs 9e4ddaeaf3 examples: single-threaded chat combinator example (#794) 2018-12-29 10:16:30 -05:00
Balthild Ires 03e2e864f3 Stablize pin feature (#814)
Box::pinned has been renamed to Box::pin. Meanwhile, the pin feature
no longer requires an attribute to enable.

Fixes: #813
2018-12-28 12:12:31 -08:00
Sean McArthur c8a990eda4 tokio-reactor: deprecates Handle::current() (#805)
The side effects of calling `Handle::current()` from outside of a
runtime could be very surprising, since it would start up a background
reactor.
2018-12-28 12:09:35 -08:00
Pavel Strakhov 1a5026324f executor: impl Unpark for Arc<Unpark> (#802) 2018-12-28 14:40:04 -05:00
Stjepan Glavina fdf4aba621 threadpool: introduce a global task queue (#798) 2018-12-28 14:34:54 -05:00
Roman 201b6ce53a ci: remove ALLOW_FAILURES=false in travis for nightly cargo doc (#816) 2018-12-28 10:06:00 -05:00
Roman db69275202 docs: fix warnings for nightly docs (#792) 2018-12-17 15:20:46 -05:00
Roman af85cb3430 ci: improve travis run times (#793) 2018-12-17 15:18:27 -05:00
Christian Bourjau 36f1a19ac8 Minor change in documentation of Decoder::decode (#797)
`None` -> `Ok(None)`
2018-12-13 11:14:38 -08:00
Stjepan Glavina 6aa990ea75 threadpool: fix semaphore deadlock (#795) 2018-12-12 16:42:18 -05:00
Simon Farnsworth 760a7667d6 threadpool: improve the documentation of blocking (#789) 2018-12-05 15:20:19 -05:00
Matt Gathu 2283b63e9e fs: added usage examples/doctests to File (#786) 2018-12-01 21:07:48 -05:00
Felix Obenhuber 8263e5f18d uds: fix WouldBlock case in UnixDatagram send methods (#782) 2018-11-30 19:40:03 -05:00
Carl Lerche b3e57b60d0 examples: remove reference to tokio-core (#780) 2018-11-28 14:55:31 -05:00
Matt Gathu 1cd0ebfc5e tcp: add usage examples to TcpListener and TcpStream (#775)
Refs: https://github.com/rust-lang-nursery/wg-net/issues/54
2018-11-28 13:05:35 -05:00
David Kellum 4797d79950 reactor: update to parking_lot 0.7 (#778) 2018-11-28 09:29:31 -08:00
Steven Fackler e7d9ba7e51 tls: make TlsConnector and TlsAcceptor derive Clone (#777) 2018-11-27 07:52:17 -05:00
luben karavelov 527dc0a66f net: export UnixDatagram and UnixDatagramFramed (#772) 2018-11-23 08:41:32 -05:00
Carl Lerche b117fc1d65 Bump version to v0.1.13 (#771)
This also bumps the following sub crate versions:

* tokio-current-thread (0.1.4)
* tokio-reactor (0.1.7)
* tokio-signal (0.2.7)
* tokio-threadpool (0.1.9)
* tokio-timer (0.2.8)
* tokio-udp (0.1.3)
* tokio-uds (0.2.4)
2018-11-21 17:11:31 -08:00
Felix Obenhuber 272e09d349 threadpool: remove smoke example (#764) (#770) 2018-11-21 14:23:36 -08:00
Stjepan Glavina 3235749006 threadpool: refactor pool shutdown (#769) 2018-11-20 21:43:23 +01:00
Stjepan Glavina 9c037044c4 threadpool: rename inner to something more descriptive (#768)
`inner` is a fitting name for variables of type named `Inner`, but in other cases I find them confusing - sometimes `inner` refers to a `Pool`, sometimes to a `Sender`. I renamed a bunch of variables named `inner` to be more descriptive.

This PR is the first step in an effort of splitting https://github.com/tokio-rs/tokio/pull/722#issuecomment-439552671 into multiple PRs.
2018-11-20 20:05:14 +01:00
Patrick Barrett 3658e10045 uds: implement UnixDatagramFramed (#453)
Implement `Stream + Sink` layer on top of unix domain sockets
using codecs.
2018-11-20 09:19:34 -08:00
Carl Lerche ed3ece266b current-thread: fix shutdown on idle (#763)
When spawning using `Handle` while on the executor, tasks were being
double counted. This prevented the number of active tasks to reach zero,
thus preventing the executor from shutting down.

This changes `spawn` to check if being called from the executor
**before** incrementing the number of active tasks.

Fixes #760
2018-11-20 09:17:07 -08:00
Liran Ringel 9b1a45cc6a tests: handle errors properly in examples (#748) 2018-11-20 11:10:36 -05:00
Carl Lerche 477fa5580a ci: Don't deploy docs if $TARGET is set (#762) 2018-11-19 21:22:28 -08:00
Toby Lawrence bb6cca8ff0 tests: switch to Windows Server 2016 for AppVeyor builds. (#761)
Should hopefully fix the underlying bug that was causing tokio-tls tests to occasionally fail on Windows.

Signed-off-by: Toby Lawrence <[email protected]>
2018-11-19 20:18:37 -05:00
Moritz Gunz e166c4d912 Implement throttle combinator (#736)
Throttle down a stream by enforcing a fixed delay between items.
2018-11-19 15:04:55 -08:00
Toby Lawrence b7506cf663 Allow nightly builds to fail. (#743)
* tests: allow nightly builds to fail

Signed-off-by: Toby Lawrence <[email protected]>
2018-11-19 17:13:56 -05:00
Carl Lerche dc4a29359f io: allow deprecated code in length_delimited test (#759)
This file is testing deprecated code, so it should be permitted to
access deprecated code.
2018-11-19 14:11:46 -08:00
Bastian Köcher d3dca4552b Expose after_start and before_stop in runtime::Builder (#756)
Closes #705
2018-11-19 09:04:58 -08:00
andoks 42a0df1ea4 Fix async await README example (#758)
* async-await: fix README example dependencies

As per commit "async-await: track nightly changes (#661)" ( commit
2f690d30bc)

> The `tokio-async-await` crate is no longer a facade. Instead, the
> `tokio` crate provides a feature flag to enable async/await support.

Ensure the example in the async-await README file also works by
correctly declaring this updated dependency

* async-await: remove unnecessary 'edition' declaration from README

As the "edition" feature was stabilized in rust v1.30 and async-await
specifies that the nightly toolchain must be used, remove the use of the
"edition" feature gate since it is enabled by default.
2018-11-17 20:58:19 -08:00
Brian Myers a98eab6eff rt: fix Builder docs to no longer use deprecated methods (#749) 2018-11-16 14:58:08 -08:00
Ivan Petkov 5a5dde70b3 signal: miscellaneous tweaks and improvements (#751)
* Minimize allocation needed for channels

* Use a newtype for signal ids

* We can just cast the raw pointer to a `usize` and still perform a
simple identity check, without incurring any implications of storing a
raw pointer (e.g. previously Signal was !Sync and had an unsafe impl of
Send, and now it is naturally Sync+Send)

* Broadcast with `try_send` instead of `start_send`

The `Stream::start_send` method uses backpressure and schedules the
current task to be notified whenever the channel has additional room,
which means we'll generate a lot of unnecessary wakeups whenever a
channel gets full

By changing to `try_send` and handling any errors, we ensure the
Driver's task won't get woken up when a Signal finally consumes its
notification, since we're coalescing things anyway
2018-11-16 14:56:43 -08:00
Alex Gaynor d0963774a3 chore: bump rand dependency to 0.6 (#753) 2018-11-16 14:54:14 -08:00
Felix Obenhuber c83355235c uds: minor doc fix in UnixStream and UnixDatagram (#754) 2018-11-16 14:53:19 -08:00
Kazuyoshi Kato 33a216e4c1 fs: add more tests (#755)
Fixes #704.
2018-11-16 14:50:06 -08:00
Toralf Wittner 09f2ac85bf udp: add into_parts to RecvDgram (#710)
* udp: add `into_parts` to `RecvDgram`

If `RecvDgram` can not be driven to completion it may become necessary to get back the `UdpSocket` it contains which is currently not possible.

This adds`into_parts` to get the socket as well as the buffer back. Both methods consume `RecvDgram`.

Note that after the future has completed, `into_parts` must not be used, or else a panic will happen.
2018-11-15 10:55:34 -05:00
Ohad Ravid 32a152630f uds: added solaris support in the ucred module (#733) 2018-11-15 10:30:37 -05:00
Kazuyoshi Kato 9153067d66 fs: add tests for directory-related functions (#704) (#724)
This change adds a few tests around directory-related functions.
2018-11-13 18:28:52 -05:00
Kazuyoshi Kato d246964bdf fs: gen_ascii_chars has been deprecated (#735)
Use sample_iter() instead.
2018-11-10 21:35:38 -05:00
Alex Gaynor e700607554 Bumped crossbeam-utils version (#746)
## Motivation

tokio depends on an out of date version of crossbeam-utils, which results in multiple versions of that package being linked in binaries which use other popular libraries.

## Solution

Bump the version; there's no API changes and tests still pass.
2018-11-10 10:39:09 +01:00
Benjamin Saunders 5321550534 Derive Clone for delay_queue::Key (#730)
Improves API ergonomics with minimal forwards-compatibility hazard.
2018-11-09 15:11:17 -08:00
Stjepan Glavina 32e1cafb57 fix tsan errors (#745) 2018-11-09 15:06:46 -08:00
Josh Leverette 49bc4025dd reactor: reduce log level of loop process (#734) 2018-11-07 16:53:53 -05:00
Carl Lerche 51e36e41bc Add tokio-buf and a BufStream trait (#611)
The `BufStream` trait provides an improved API for working with
asynchronous streams of bytes compared to `Stream<Item = [u8]>`
2018-10-29 13:43:48 -07:00
Carl Lerche d011b92b9a rt: fix Runtime::reactor() as used by tokio-core (#721)
* rt: fix `Runtime::reactor()` as used by tokio-core

Up until Tokio v0.1.11, the handle returned by `Runtime::reactor()`
pointed to a reactor instance running in a background thread. The thread
was eagerly spawned.

As of v0.1.12, a reactor instance is created per runtime worker thread.
`Runtime::reactor()` was deprecated and updated to point to the reactor
for one of the worker threads.

A problem occurs when attempting to use the reactor before spawning a
task. Worker threads are spawned lazily, which means that the reactor
referenced by `Runtime::reactor()` is not yet running.

This patch changes `Runtime::reactor` back to a dedicated reactor
running on a background thread. However, the background thread is now
spawned lazily when the deprecated function is first called.

Fixes #720

* Fix comment

Co-Authored-By: carllerche <[email protected]>
2018-10-25 11:23:54 +02:00
Carl Lerche f929576f0e Bump version to 0.1.12 (#718)
Also bumps the following sub-crates:

* tokio-fs (0.1.4)
* tokio-io (0.1.10)
* tokio-signal (0.2.6)
* tokio-threadpool (0.1.8)
* tokio-uds (0.2.3)
2018-10-23 22:00:49 -07:00
Ivan Petkov b0f001a05a signal: Bump version to 0.2.6 (#714)
* Also Update the CHANGELOG to match the rest of the project
2018-10-23 20:39:19 -07:00
Iku Iwasa 2291ba9d0d uds: add NetBSD support (#715) 2018-10-23 20:15:42 -04:00
Name 7f84f6b4ca contributing: fix an invalid link (#716)
Just move a dot to the right place.
2018-10-21 16:56:01 +00:00
Andrew Audibert 5f61bd5252 fix a typo in the contributing guide (#711) 2018-10-19 09:25:11 -07:00
Ryan Dahl bffa3ed558 fs: expose fs::File::from_std() (#696) 2018-10-17 19:51:46 -04:00
Sean McArthur 7b5ef61aeb runtime: check Enter in more places when blocking (#708)
- `tokio::run` checks Enter before creating a new threadpool and
  spawning the main future.
- `Runtime::block_on` now checks Enter
- `Runtime::block_on_all` now checks Enter
2018-10-17 15:25:40 -07:00
Stjepan Glavina 753336de8e threadpool: Arc instead of Inner in Notifier (#702) 2018-10-15 13:24:00 -07:00
Ryan Levick 65aea16ad1 tokio: change hello world to new, simpler example (#690) 2018-10-12 12:42:19 -04:00
nickelc 796fee6364 fs: fix minor documentation error for MetadataFuture (#698) 2018-10-12 12:41:19 -04:00
Stjepan Glavina adb0ba71d4 threadpool: worker threads shouldn't respect keep_alive (#692)
<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md
-->

## Motivation

Now that each worker thread drives its own reactor, reactors have to be driven until the threadpool shuts down. We mustn't use the `keep_alive` setting to shut down a worker thread if it doesn't receive an event from the reactor for a certain duration of time.

<!--
Explain the context and why you're making that change. What is the problem
you're trying to solve? In some cases there is not a problem and this can be
thought of as being the motivation for your change.
-->

## Solution

Just ignore the `keep_alive` setting when parking in `Worker::sleep`.

<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->
2018-10-10 09:05:36 +02:00
David Ross bfa6766f3c re-export tokio_io::read in tokio::io (#689)
Fixes: #688
2018-10-09 19:50:03 -07:00
Nikolay Kim a2f457fa48 io: expose underlying codec (#686) 2018-10-06 19:23:05 -04:00
Eliza Weisman 1879bc49ce codec: Fix panic in LengthDelimitedCodec::encode (#682)
Fixes: #681 

## Motivation

Currently, a potential panic exists in `LengthDelimitedCodec::encode`.
Writing the length field to the `dst` buffer can exceed the buffer
capacity, as `BufMut::put_uint_{le,be}` doesn't reserve more capacity. 

## Solution

This branch adds a call to `dst.reserve` to ensure that there's 
sufficient remaining buffer capacity to hold the length field and
the frame, prior to writing the length field. Previously, capacity
was only reserved later in the function, when writing the frame
to the buffer, and we never reserved capacity for the length field.

I've also added a test that reproduces the issue. The test panics on
master, but passes after making this change.

Signed-off-by: Eliza Weisman <[email protected]>
2018-10-04 12:46:57 -07:00
Sven Marnach 678f6382b8 io: implement prepare_uninitialized_buffer for Take and Chain (#678) 2018-10-04 11:03:43 -07:00
Stjepan Glavina e27b0a46ba threadpool: spawn new tasks onto a random worker (#683)
* threadpool: submit new tasks to a random worker

* Revert unnecessary version bumps
2018-10-03 23:09:20 +02:00
Stjepan Glavina d35d0518f5 runtime: create reactor per worker (#660) 2018-10-02 18:19:27 -07:00
Sven Marnach 886511c0a6 io: fix minor documentation errors for Async{Read,Write} (#677) 2018-10-01 19:34:24 -04:00
Steven Fackler d06bd6b216 Expose keep_alive on the Runtime builder (#676)
This was overlooked when delegating the rest of the threadpool builder
methods from Runtime's builder.
2018-09-28 21:00:50 -07:00
Carl Lerche 2c85cd0991 Bump version to v0.1.11 (#675)
This fixes the dependency on `tokio-async-await` to not be scoped to
unix platforms.

Fixes #673
2018-09-28 11:32:52 -07:00
Carl Lerche 1e45237a28 Bump tokio-uds to v0.2.2 2018-09-27 20:05:23 -07:00
Sean McArthur 3a88d85538 ads: fix UdsStream::read_buf to clear read (not write) readiness (#672) 2018-09-27 20:02:29 -07:00
Carl Lerche b47ad24268 Bump version to v0.1.10, fixing minimal versions (#671)
Some minimal versions were not correctly updated.

Also updates:

* tokio-current-thread (v0.1.3).
2018-09-27 13:00:53 -07:00
167 changed files with 5022 additions and 1847 deletions
+1
View File
@@ -1,3 +1,4 @@
image: Visual Studio 2017
environment:
matrix:
- TARGET: x86_64-pc-windows-msvc
+52 -33
View File
@@ -1,9 +1,6 @@
---
language: rust
sudo: false
cache:
- apt
- cargo
addons:
apt:
packages:
@@ -12,18 +9,40 @@ addons:
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
# 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.
- rust: 1.26.0
script: |
cargo check --all
# Test combinations of enabled features.
- rust: stable
script: |
shopt -s expand_aliases
alias check="cargo check --no-default-features"
check
check --features codec
check --features fs
check --features io
check --features reactor
check --features rt-full
check --features tcp
check --features timer
check --features udp
check --features uds
# Test the async / await preview. We don't want to block PRs on this failing
# though.
- rust: nightly
@@ -31,16 +50,12 @@ matrix:
script: |
cd tokio-async-await
cargo check --all
allow_failures:
- rust: nightly
env: ALLOW_FAILURES=true
script:
- |
set -e
if [[ "$TRAVIS_RUST_VERSION" == nightly ]]
then
# This runs TSAN against nightly and allows failures to propagate up.
- rust: nightly-2018-11-18
env: TSAN=yes
script: |
set -e
# Make sure the benchmarks compile
cargo build --benches --all
@@ -67,22 +82,26 @@ script:
# 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
# This runs cargo +nightly doc
- name: nightly_docs
rust: nightly
script: cargo doc
allow_failures:
- rust: nightly
env: ALLOW_FAILURES=true
script: |
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
fi
before_deploy:
- cargo doc --all --no-deps
@@ -97,7 +116,7 @@ deploy:
branch: master
repo: tokio-rs/tokio
rust: stable
condition: $TRAVIS_OS_NAME = linux
condition: $TRAVIS_OS_NAME = "linux" && $TARGET = ""
env:
global:
+30
View File
@@ -1,6 +1,36 @@
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.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).
* `runtime::Builder` no longer uses deprecated methods (#749).
* Provide `after_start` and `before_stop` configuration settings for
`Runtime` (#756).
* Implement throttle stream combinator (#736).
# 0.1.12 (October 23, 2018)
* runtime: expose `keep_alive` on runtime builder (#676).
* runtime: create a reactor per worker thread (#660).
* codec: fix panic in `LengthDelimitedCodec` (#682).
* io: re-export `tokio_io::io::read` function (#689).
* runtime: check for executor re-entry in more places (#708).
# 0.1.11 (September 28, 2018)
* Fix `tokio-async-await` dependency (#675).
# 0.1.10 (September 27, 2018)
* Fix minimal versions
# 0.1.9 (September 27, 2018)
* Experimental async/await improvements (#661).
+3 -3
View File
@@ -109,7 +109,7 @@ and dependencies in the Tokio repository.
Even tiny pull requests (e.g., one character pull request fixing a typo in API
documentation) are greatly appreciated. Before making a large change, it is
usually a good idea to first open an issue describing the change to solicit
feedback and guidance. This will increasethe likelihood of the PR getting
feedback and guidance. This will increase the likelihood of the PR getting
merged.
### Tests
@@ -380,8 +380,8 @@ left). When doing so, it is courteous to give the original contributor credit
for the work they started (either by preserving their name and email address in
the commit log, or by using an `Author: ` meta-data tag in the commit.
_Adapted from the [Node.js contributing guide][node]_
_Adapted from the [Node.js contributing guide][node]_.
[node]: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md.
[node]: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md
[hiding-a-comment]: https://help.github.com/articles/managing-disruptive-comments/#hiding-a-comment
[documentation test]: https://doc.rust-lang.org/rustdoc/documentation-tests.html
+68 -18
View File
@@ -6,11 +6,11 @@ name = "tokio"
# - Update CHANGELOG.md.
# - Update doc URL.
# - Create "v0.1.x" git tag.
version = "0.1.9"
version = "0.1.14"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT"
readme = "README.md"
documentation = "https://docs.rs/tokio/0.1.9/tokio/"
documentation = "https://docs.rs/tokio/0.1.14/tokio/"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
description = """
@@ -25,6 +25,7 @@ keywords = ["io", "async", "non-blocking", "futures"]
members = [
"./",
"tokio-async-await",
"tokio-buf",
"tokio-channel",
"tokio-codec",
"tokio-current-thread",
@@ -42,6 +43,35 @@ members = [
]
[features]
default = [
"codec",
"fs",
"io",
"reactor",
"rt-full",
"tcp",
"timer",
"udp",
"uds",
]
codec = ["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",
]
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 = [
@@ -53,29 +83,32 @@ travis-ci = { repository = "tokio-rs/tokio" }
appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" }
[dependencies]
bytes = "0.4"
tokio-codec = { version = "0.1.0", path = "tokio-codec" }
tokio-current-thread = { version = "0.1.1", path = "tokio-current-thread" }
tokio-io = { version = "0.1.6", path = "tokio-io" }
tokio-executor = { version = "0.1.2", path = "tokio-executor" }
tokio-reactor = { version = "0.1.1", path = "tokio-reactor" }
tokio-threadpool = { version = "0.1.4", path = "tokio-threadpool" }
tokio-tcp = { version = "0.1.0", path = "tokio-tcp" }
tokio-udp = { version = "0.1.0", path = "tokio-udp" }
tokio-timer = { version = "0.2.6", path = "tokio-timer" }
tokio-fs = { version = "0.1.3", path = "tokio-fs" }
# Only non-optional dependency...
futures = "0.1.20"
# Needed until `reactor` is removed from `tokio`.
mio = "0.6.14"
# 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.3", 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-threadpool = { version = "0.1.4", 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 }
[target.'cfg(unix)'.dependencies]
tokio-uds = { version = "0.2.1", path = "tokio-uds" }
# Needed until `reactor` is removed from `tokio`.
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", optional = true }
[dev-dependencies]
env_logger = { version = "0.5", default-features = false }
flate2 = { version = "1", features = ["tokio"] }
@@ -88,3 +121,20 @@ serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
time = "0.1"
[patch.crates-io]
#tokio = { path = "." }
#tokio-async-await = { path = "./tokio-async-await" }
#tokio-codec = { path = "./tokio-codec" }
#tokio-current-thread = { path = "./tokio-current-thread" }
#tokio-executor = { path = "./tokio-executor" }
#tokio-fs = { path = "./tokio-fs" }
#tokio-io = { path = "./tokio-io" }
#tokio-reactor = { path = "./tokio-reactor" }
#tokio-signal = { path = "./tokio-signal" }
#tokio-tcp = { path = "./tokio-tcp" }
#tokio-threadpool = { path = "./tokio-threadpool" }
#tokio-timer = { path = "./tokio-timer" }
#tokio-tls = { path = "./tokio-tls" }
#tokio-udp = { path = "./tokio-udp" }
#tokio-uds = { path = "./tokio-uds" }
+1 -1
View File
@@ -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
+1 -1
View File
@@ -31,7 +31,7 @@ the Rust programming language. It is:
[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.14/tokio) |
[Chat](https://gitter.im/tokio-rs/tokio)
The API docs for the master branch are published [here][master-dox].
+4
View File
@@ -31,3 +31,7 @@ race:crossbeam_deque*steal
race:Backup::next_sleeper
race:Backup::set_next_sleeper
race:WorkerEntry::set_next_sleeper
# This ignores a false positive caused by `thread::park()`/`thread::unpark()`.
# See: https://github.com/rust-lang/rust/pull/54806#issuecomment-436193353
race:pthread_cond_destroy
+167
View File
@@ -0,0 +1,167 @@
//! 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 tokio;
extern crate futures;
use tokio::io;
use tokio::net::TcpListener;
use tokio::prelude::*;
use tokio::runtime::current_thread::{Runtime, TaskExecutor};
use std::collections::HashMap;
use std::iter;
use std::env;
use std::io::{BufReader};
use std::rc::Rc;
use std::cell::RefCell;
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(())
}
+8 -6
View File
@@ -34,12 +34,12 @@ use std::env;
use std::io::{BufReader};
use std::sync::{Arc, Mutex};
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
// Create the TCP listener we'll accept connections on.
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
let addr = addr.parse().unwrap();
let addr = addr.parse()?;
let socket = TcpListener::bind(&addr).unwrap();
let socket = TcpListener::bind(&addr)?;
println!("Listening on: {}", addr);
// This is running on the Tokio runtime, so it will be multi-threaded. The
@@ -49,10 +49,10 @@ fn main() {
// The server task asynchronously iterates over and processes each incoming
// connection.
let srv = socket.incoming()
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
.map_err(|e| {println!("failed to accept socket; error = {:?}", e); e})
.for_each(move |stream| {
// The client's socket address
let addr = stream.peer_addr().unwrap();
let addr = stream.peer_addr()?;
println!("New Connection: {}", addr);
@@ -143,8 +143,10 @@ fn main() {
}));
Ok(())
});
})
.map_err(|err| println!("error occurred: {:?}", err));
// execute server
tokio::run(srv);
Ok(())
}
+4 -3
View File
@@ -426,7 +426,7 @@ fn process(socket: TcpStream, state: Arc<Mutex<Shared>>) {
tokio::spawn(connection);
}
pub fn main() {
pub fn main() -> Result<(), Box<std::error::Error>> {
// Create the shared state. This is how all the peers communicate.
//
// The server task will hold a handle to this. For every new client, the
@@ -434,12 +434,12 @@ pub fn main() {
// client connection.
let state = Arc::new(Mutex::new(Shared::new()));
let addr = "127.0.0.1:6142".parse().unwrap();
let addr = "127.0.0.1:6142".parse()?;
// Bind a TCP listener to the socket address.
//
// Note that this is the Tokio TcpListener, which is fully async.
let listener = TcpListener::bind(&addr).unwrap();
let listener = TcpListener::bind(&addr)?;
// The server task asynchronously iterates over and processes each
// incoming connection.
@@ -471,4 +471,5 @@ pub fn main() {
// In our example, we have not defined a shutdown strategy, so this will
// block until `ctrl-c` is pressed at the terminal.
tokio::run(server);
Ok(())
}
+28 -20
View File
@@ -29,7 +29,7 @@ use std::thread;
use tokio::prelude::*;
use futures::sync::mpsc;
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
// Determine if we're going to run in TCP or UDP mode
let mut args = env::args().skip(1).collect::<Vec<_>>();
let tcp = match args.iter().position(|a| a == "--udp") {
@@ -41,10 +41,11 @@ fn main() {
};
// Parse what address we're going to connect to
let addr = args.first().unwrap_or_else(|| {
panic!("this program requires at least one argument")
});
let addr = addr.parse::<SocketAddr>().unwrap();
let addr = match args.first() {
Some(addr) => addr,
None => Err("this program requires at least one argument")?,
};
let addr = addr.parse::<SocketAddr>()?;
// Right now Tokio doesn't support a handle to stdin running on the event
// loop, so we farm out that work to a separate thread. This thread will
@@ -52,15 +53,15 @@ fn main() {
// loop over a standard futures channel.
let (stdin_tx, stdin_rx) = mpsc::channel(0);
thread::spawn(|| read_stdin(stdin_tx));
let stdin_rx = stdin_rx.map_err(|_| panic!()); // errors not possible on rx
let stdin_rx = stdin_rx.map_err(|_| panic!("errors not possible on rx"));
// Now that we've got our stdin read we either set up our TCP connection or
// our UDP connection to get a stream of bytes we're going to emit to
// stdout.
let stdout = if tcp {
tcp::connect(&addr, Box::new(stdin_rx))
tcp::connect(&addr, Box::new(stdin_rx))?
} else {
udp::connect(&addr, Box::new(stdin_rx))
udp::connect(&addr, Box::new(stdin_rx))?
};
// And now with our stream of bytes to write to stdout, we execute that in
@@ -77,6 +78,7 @@ fn main() {
})
.map_err(|e| println!("error reading stdout; error = {:?}", e))
});
Ok(())
}
mod codec {
@@ -127,12 +129,13 @@ mod tcp {
use bytes::BytesMut;
use codec::Bytes;
use std::error::Error;
use std::io;
use std::net::SocketAddr;
pub fn connect(addr: &SocketAddr,
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>)
-> Box<Stream<Item = BytesMut, Error = io::Error> + Send>
-> Result<Box<Stream<Item = BytesMut, Error = io::Error> + Send>, Box<Error>>
{
let tcp = TcpStream::connect(addr);
@@ -151,22 +154,24 @@ mod tcp {
// You'll also note that we *spawn* the work to read stdin and write it
// to the TCP stream. This is done to ensure that happens concurrently
// with us reading data from the stream.
Box::new(tcp.map(move |stream| {
let stream = Box::new(tcp.map(move |stream| {
let (sink, stream) = Bytes.framed(stream).split();
tokio::spawn(stdin.forward(sink).then(|result| {
if let Err(e) = result {
panic!("failed to write to socket: {}", e)
println!("failed to write to socket: {}", e)
}
Ok(())
}));
stream
}).flatten_stream())
}).flatten_stream());
Ok(stream)
}
}
mod udp {
use std::error::Error;
use std::io;
use std::net::SocketAddr;
@@ -179,17 +184,19 @@ mod udp {
pub fn connect(&addr: &SocketAddr,
stdin: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send>)
-> Box<Stream<Item = BytesMut, Error = io::Error> + Send>
-> Result<Box<Stream<Item = BytesMut, Error = io::Error> + Send>, Box<Error>>
{
// We'll bind our UDP socket to a local IP/port, but for now we
// basically let the OS pick both of those.
let addr_to_bind = if addr.ip().is_ipv4() {
"0.0.0.0:0".parse().unwrap()
"0.0.0.0:0".parse()?
} else {
"[::]:0".parse().unwrap()
"[::]:0".parse()?
};
let udp = match UdpSocket::bind(&addr_to_bind) {
Ok(udp) => udp,
Err(_) => Err("failed to bind socket")?,
};
let udp = UdpSocket::bind(&addr_to_bind)
.expect("failed to bind socket");
// Like above with TCP we use an instance of `Bytes` codec to transform
// this UDP socket into a framed sink/stream which operates over
@@ -203,7 +210,7 @@ mod udp {
(chunk, addr)
}).forward(sink).then(|result| {
if let Err(e) = result {
panic!("failed to write to socket: {}", e)
println!("failed to write to socket: {}", e)
}
Ok(())
});
@@ -218,10 +225,11 @@ mod udp {
}
});
Box::new(future::lazy(|| {
let stream = Box::new(future::lazy(|| {
tokio::spawn(forward_stdin);
future::ok(receive)
}).flatten_stream())
}).flatten_stream());
Ok(stream)
}
}
+5 -4
View File
@@ -50,12 +50,12 @@ impl Future for Server {
}
}
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
let addr = addr.parse::<SocketAddr>().unwrap();
let addr = addr.parse::<SocketAddr>()?;
let socket = UdpSocket::bind(&addr).unwrap();
println!("Listening on: {}", socket.local_addr().unwrap());
let socket = UdpSocket::bind(&addr)?;
println!("Listening on: {}", socket.local_addr()?);
let server = Server {
socket: socket,
@@ -70,4 +70,5 @@ fn main() {
//
// `tokio::run` spawns the task on the Tokio runtime and starts running.
tokio::run(server.map_err(|e| println!("server error = {:?}", e)));
Ok(())
}
+4 -3
View File
@@ -30,19 +30,19 @@ use tokio::prelude::*;
use std::env;
use std::net::SocketAddr;
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
// Allow passing an address to listen on as the first argument of this
// program, but otherwise we'll just set up our TCP listener on
// 127.0.0.1:8080 for connections.
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
let addr = addr.parse::<SocketAddr>().unwrap();
let addr = addr.parse::<SocketAddr>()?;
// Next up we create a TCP listener which will listen for incoming
// connections. This TCP listener is bound to the address we determined
// above and must be associated with an event loop, so we pass in a handle
// to our event loop. After the socket's created we inform that we're ready
// to go and start accepting connections.
let socket = TcpListener::bind(&addr).unwrap();
let socket = TcpListener::bind(&addr)?;
println!("Listening on: {}", addr);
// Here we convert the `TcpListener` to a stream of incoming connections
@@ -111,4 +111,5 @@ fn main() {
// never completes (it just keeps accepting sockets), `tokio::run` blocks
// forever (until ctrl-c is pressed).
tokio::run(done);
Ok(())
}
+20 -33
View File
@@ -1,59 +1,45 @@
//! Hello world server.
//!
//! A simple server that accepts connections, writes "hello world\n", and closes
//! A simple client that opens a TCP stream, writes "hello world\n", and closes
//! the connection.
//!
//! You can test this out by running:
//!
//! cargo run --example hello_world
//! ncat -l 6142
//!
//! And then in another terminal run:
//!
//! telnet localhost 6142
//!
//! cargo run --example hello_world
#![deny(warnings)]
extern crate tokio;
use tokio::io;
use tokio::net::TcpListener;
use tokio::net::TcpStream;
use tokio::prelude::*;
pub fn main() {
let addr = "127.0.0.1:6142".parse().unwrap();
pub fn main() -> Result<(), Box<std::error::Error>> {
let addr = "127.0.0.1:6142".parse()?;
// Bind a TCP listener to the socket address.
// Open a TCP stream to the socket address.
//
// Note that this is the Tokio TcpListener, which is fully async.
let listener = TcpListener::bind(&addr).unwrap();
// The server task asynchronously iterates over and processes each
// incoming connection.
let server = listener.incoming().for_each(|socket| {
println!("accepted socket; addr={:?}", socket.peer_addr().unwrap());
let connection = io::write_all(socket, "hello world\n")
.then(|res| {
println!("wrote message; success={:?}", res.is_ok());
Ok(())
});
// Spawn a new task that processes the socket:
tokio::spawn(connection);
Ok(())
// Note that this is the Tokio TcpStream, which is fully async.
let client = TcpStream::connect(&addr).and_then(|stream| {
println!("created stream");
io::write_all(stream, "hello world\n").then(|result| {
println!("wrote to stream; success={:?}", result.is_ok());
Ok(())
})
})
.map_err(|err| {
// All tasks must have an `Error` type of `()`. This forces error
// handling and helps avoid silencing failures.
//
// In our example, we are only going to log the error to STDOUT.
println!("accept error = {:?}", err);
println!("connection error = {:?}", err);
});
println!("server running on localhost:6142");
// Start the Tokio runtime.
//
// The Tokio is a pre-configured "out of the box" runtime for building
@@ -63,8 +49,9 @@ pub fn main() {
// This function blocks until the runtime reaches an idle state. Idle is
// defined as all spawned tasks have completed and all I/O resources (TCP
// sockets in our case) have been dropped.
//
// In our example, we have not defined a shutdown strategy, so this will
// block until `ctrl-c` is pressed at the terminal.
tokio::run(server);
println!("About to create the stream and write to it...");
tokio::run(client);
println!("Stream has been created and written to.");
Ok(())
}
+3 -2
View File
@@ -60,7 +60,7 @@ fn run<F: Future<Item = (), Error = ()>>(f: F) -> Result<(), IoError> {
Ok(())
}
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
run(future::lazy(|| {
// Here comes the application logic. It can spawn further tasks by tokio_current_thread::spawn().
// It also can use the default reactor and create timeouts.
@@ -82,5 +82,6 @@ fn main() {
// We can spawn on the default executor, which is also the local one.
tokio::executor::spawn(deadline);
Ok(())
})).unwrap();
}))?;
Ok(())
}
+4 -3
View File
@@ -65,19 +65,19 @@ use tokio::codec::Decoder;
use std::env;
use std::net::SocketAddr;
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
// Allow passing an address to listen on as the first argument of this
// program, but otherwise we'll just set up our TCP listener on
// 127.0.0.1:8080 for connections.
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
let addr = addr.parse::<SocketAddr>().unwrap();
let addr = addr.parse::<SocketAddr>()?;
// Next up we create a TCP listener which will listen for incoming
// connections. This TCP listener is bound to the address we determined
// above and must be associated with an event loop, so we pass in a handle
// to our event loop. After the socket's created we inform that we're ready
// to go and start accepting connections.
let socket = TcpListener::bind(&addr).unwrap();
let socket = TcpListener::bind(&addr)?;
println!("Listening on: {}", addr);
// Here we convert the `TcpListener` to a stream of incoming connections
@@ -146,4 +146,5 @@ fn main() {
// never completes (it just keeps accepting sockets), `tokio::run` blocks
// forever (until ctrl-c is pressed).
tokio::run(done);
Ok(())
}
+5 -4
View File
@@ -33,15 +33,15 @@ use tokio::io::{copy, shutdown};
use tokio::net::{TcpListener, TcpStream};
use tokio::prelude::*;
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
let listen_addr = env::args().nth(1).unwrap_or("127.0.0.1:8081".to_string());
let listen_addr = listen_addr.parse::<SocketAddr>().unwrap();
let listen_addr = listen_addr.parse::<SocketAddr>()?;
let server_addr = env::args().nth(2).unwrap_or("127.0.0.1:8080".to_string());
let server_addr = server_addr.parse::<SocketAddr>().unwrap();
let server_addr = server_addr.parse::<SocketAddr>()?;
// Create a TCP listener which will listen for incoming connections.
let socket = TcpListener::bind(&listen_addr).unwrap();
let socket = TcpListener::bind(&listen_addr)?;
println!("Listening on: {}", listen_addr);
println!("Proxying to: {}", server_addr);
@@ -94,6 +94,7 @@ fn main() {
});
tokio::run(done);
Ok(())
}
// This is a custom type used to have a custom implementation of the
+4 -3
View File
@@ -74,12 +74,12 @@ enum Response {
Error { msg: String },
}
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
// Parse the address we're going to run this server on
// and set up our TCP listener to accept connections.
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
let addr = addr.parse::<SocketAddr>().unwrap();
let listener = TcpListener::bind(&addr).expect("failed to bind");
let addr = addr.parse::<SocketAddr>()?;
let listener = TcpListener::bind(&addr).map_err(|_| "failed to bind")?;
println!("Listening on: {}", addr);
// Create the shared state of this server that will be shared amongst all
@@ -156,6 +156,7 @@ fn main() {
});
tokio::run(done);
Ok(())
}
impl Request {
+31 -26
View File
@@ -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,
@@ -34,13 +34,13 @@ use bytes::BytesMut;
use http::header::HeaderValue;
use http::{Request, Response, StatusCode};
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
// Parse the arguments, bind the TCP socket we'll be listening to, spin up
// our worker threads, and start shipping sockets to those worker threads.
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
let addr = addr.parse::<SocketAddr>().unwrap();
let addr = addr.parse::<SocketAddr>()?;
let listener = TcpListener::bind(&addr).expect("failed to bind");
let listener = TcpListener::bind(&addr)?;
println!("Listening on: {}", addr);
tokio::run({
@@ -51,6 +51,7 @@ fn main() {
Ok(())
})
});
Ok(())
}
fn process(socket: TcpStream) {
@@ -84,28 +85,32 @@ fn process(socket: TcpStream) {
fn respond(req: Request<()>)
-> Box<Future<Item = Response<String>, Error = io::Error> + Send>
{
let mut ret = Response::builder();
let body = match req.uri().path() {
"/plaintext" => {
ret.header("Content-Type", "text/plain");
"Hello, World!".to_string()
}
"/json" => {
ret.header("Content-Type", "application/json");
#[derive(Serialize)]
struct Message {
message: &'static str,
let f = future::lazy(move || {
let mut response = Response::builder();
let body = match req.uri().path() {
"/plaintext" => {
response.header("Content-Type", "text/plain");
"Hello, World!".to_string()
}
serde_json::to_string(&Message { message: "Hello, World!" })
.unwrap()
}
_ => {
ret.status(StatusCode::NOT_FOUND);
String::new()
}
};
Box::new(future::ok(ret.body(body).unwrap()))
"/json" => {
response.header("Content-Type", "application/json");
#[derive(Serialize)]
struct Message {
message: &'static str,
}
serde_json::to_string(&Message { message: "Hello, World!" })?
}
_ => {
response.status(StatusCode::NOT_FOUND);
String::new()
}
};
let response = response.body(body).map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
Ok(response)
});
Box::new(f)
}
struct Http;
+11 -16
View File
@@ -35,29 +35,27 @@ use std::net::SocketAddr;
use tokio::net::UdpSocket;
use tokio::prelude::*;
fn get_stdin_data() -> Vec<u8> {
fn get_stdin_data() -> Result<Vec<u8>, Box<std::error::Error>> {
let mut buf = Vec::new();
stdin().read_to_end(&mut buf).unwrap();
buf
stdin().read_to_end(&mut buf)?;
Ok(buf)
}
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
let remote_addr: SocketAddr = env::args()
.nth(1)
.unwrap_or("127.0.0.1:8080".into())
.parse()
.unwrap();
.parse()?;
// We use port 0 to let the operating system allocate an available port for us.
let local_addr: SocketAddr = if remote_addr.is_ipv4() {
"0.0.0.0:0"
} else {
"[::]:0"
}.parse()
.unwrap();
let socket = UdpSocket::bind(&local_addr).unwrap();
}.parse()?;
let socket = UdpSocket::bind(&local_addr)?;
const MAX_DATAGRAM_SIZE: usize = 65_507;
let processing = socket
.send_dgram(get_stdin_data(), &remote_addr)
socket
.send_dgram(get_stdin_data()?, &remote_addr)
.and_then(|(socket, _)| socket.recv_dgram(vec![0u8; MAX_DATAGRAM_SIZE]))
.map(|(_, data, len, _)| {
println!(
@@ -66,9 +64,6 @@ fn main() {
String::from_utf8_lossy(&data[..len])
)
})
.wait();
match processing {
Ok(_) => {}
Err(e) => eprintln!("Encountered an error: {}", e),
}
.wait()?;
Ok(())
}
+6 -5
View File
@@ -19,15 +19,15 @@ use tokio::prelude::*;
use tokio::net::{UdpSocket, UdpFramed};
use tokio_codec::BytesCodec;
fn main() {
fn main() -> Result<(), Box<std::error::Error>> {
let _ = env_logger::init();
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let addr: SocketAddr = "127.0.0.1:0".parse()?;
// Bind both our sockets and then figure out what ports we got.
let a = UdpSocket::bind(&addr).unwrap();
let b = UdpSocket::bind(&addr).unwrap();
let b_addr = b.local_addr().unwrap();
let a = UdpSocket::bind(&addr)?;
let b = UdpSocket::bind(&addr)?;
let b_addr = b.local_addr()?;
// We're parsing each socket with the `BytesCodec` included in `tokio_io`, and then we
// `split` each codec into the sink/stream halves.
@@ -61,4 +61,5 @@ fn main() {
.map(|_| ())
.map_err(|e| println!("error = {:?}", e))
});
Ok(())
}
+9 -4
View File
@@ -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;
@@ -345,6 +345,7 @@
//! +------------+--------------+
//! ```
//!
//! [`LengthDelimitedCodec::new()`]: struct.LengthDelimitedCodec.html#method.new
//! [`FramedRead`]: struct.FramedRead.html
//! [`FramedWrite`]: struct.FramedWrite.html
//! [`AsyncRead`]: ../../trait.AsyncRead.html
@@ -578,6 +579,10 @@ impl Encoder for LengthDelimitedCodec {
"provided length would overflow after adjustment",
))?;
// Reserve capacity in the destination buffer to fit the frame and
// length field (plus adjustment).
dst.reserve(self.builder.length_field_len + n);
if self.builder.length_field_is_big_endian {
dst.put_uint_be(n as u64, self.builder.length_field_len);
} else {
+2
View File
@@ -51,6 +51,7 @@ pub use tokio_io::{
};
// standard input, output, and error
#[cfg(feature = "fs")]
pub use tokio_fs::{
stdin,
Stdin,
@@ -68,6 +69,7 @@ pub use tokio_io::io::{
Flush,
lines,
Lines,
read,
read_exact,
ReadExact,
read_to_end,
+40 -9
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio/0.1.9")]
#![doc(html_root_url = "https://docs.rs/tokio/0.1.14")]
#![deny(missing_docs, warnings, missing_debug_implementations)]
#![cfg_attr(feature = "async-await-preview", feature(
async_await,
@@ -72,41 +72,72 @@
//! }
//! ```
extern crate bytes;
#[macro_use]
macro_rules! if_runtime {
($($i:item)*) => ($(
#[cfg(any(feature = "rt-full"))]
$i
)*)
}
#[cfg_attr(feature = "rt-full", 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;
#[cfg(feature = "rt-full")]
extern crate tokio_current_thread;
#[cfg(feature = "io")]
extern crate tokio_io;
extern crate tokio_executor;
#[cfg(feature = "codec")]
extern crate tokio_codec;
#[cfg(feature = "fs")]
extern crate tokio_fs;
#[cfg(feature = "reactor")]
extern crate tokio_reactor;
#[cfg(feature = "rt-full")]
extern crate tokio_threadpool;
#[cfg(feature = "timer")]
extern crate tokio_timer;
#[cfg(feature = "tcp")]
extern crate tokio_tcp;
#[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 = "timer")]
pub mod timer;
pub mod util;
pub use executor::spawn;
pub use runtime::run;
if_runtime! {
extern crate tokio_executor;
pub mod executor;
pub mod runtime;
pub use executor::spawn;
pub use runtime::run;
}
// ===== Experimental async/await support =====
+19 -6
View File
@@ -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};
+1
View File
@@ -10,6 +10,7 @@
//!
//! The prelude may grow over time as additional items see ubiquitous use.
#[cfg(feature = "io")]
pub use tokio_io::{
AsyncRead,
AsyncWrite,
+15
View File
@@ -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");
}
+4
View File
@@ -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 -380
View File
@@ -106,391 +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::{Background, Handle};
use std::io;
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 {
/// Reactor running on a background thread.
reactor: Background,
/// 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,
{
let mut runtime = Runtime::new().unwrap();
runtime.spawn(future);
enter().expect("nested tokio::run")
.block_on(runtime.shutdown_on_idle())
.unwrap();
}
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 {
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`
/// ```
pub fn reactor(&self) -> &Handle {
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 (tx, rx) = futures::sync::oneshot::channel();
self.spawn(future.then(move |r| tx.send(r).map_err(|_| unreachable!())));
rx.wait().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 res = self.block_on(future);
self.shutdown_on_idle().wait().unwrap();
res
}
/// 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 = Box::new({
let pool = inner.pool;
let reactor = inner.reactor;
pool.shutdown_on_idle().and_then(|_| {
reactor.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,12 +1,14 @@
use runtime::{Inner, Runtime};
use super::{Inner, Runtime};
use reactor::Reactor;
use std::io;
use std::sync::Mutex;
use std::time::Duration;
use num_cpus;
use tokio_reactor;
use tokio_threadpool::Builder as ThreadPoolBuilder;
use tokio_threadpool::park::DefaultPark;
use tokio_timer::clock::{self, Clock};
use tokio_timer::timer::{self, Timer};
@@ -26,30 +28,37 @@ use tokio_timer::timer::{self, Timer};
/// # Examples
///
/// ```
/// # extern crate tokio;
/// # extern crate tokio_threadpool;
/// # use tokio::runtime::Builder;
/// extern crate tokio;
/// extern crate tokio_timer;
///
/// # pub fn main() {
/// // create and configure ThreadPool
/// let mut threadpool_builder = tokio_threadpool::Builder::new();
/// threadpool_builder
/// .name_prefix("my-runtime-worker-")
/// .pool_size(4);
/// use std::time::Duration;
///
/// // build Runtime
/// let runtime = Builder::new()
/// .threadpool_builder(threadpool_builder)
/// .build();
/// // ... call runtime.run(...)
/// # let _ = runtime;
/// # }
/// use tokio::runtime::Builder;
/// use tokio_timer::clock::Clock;
///
/// fn main() {
/// // build Runtime
/// let mut runtime = Builder::new()
/// .blocking_threads(4)
/// .clock(Clock::system())
/// .core_threads(4)
/// .keep_alive(Some(Duration::from_secs(60)))
/// .name_prefix("my-custom-name-")
/// .stack_size(3 * 1024 * 1024)
/// .build()
/// .unwrap();
///
/// // use runtime ...
/// }
/// ```
#[derive(Debug)]
pub struct Builder {
/// Thread pool specific builder
threadpool_builder: ThreadPoolBuilder,
/// The number of worker threads
core_threads: usize,
/// The clock to use
clock: Clock,
}
@@ -60,11 +69,15 @@ impl Builder {
///
/// Configuration methods can be chained on the return value.
pub fn new() -> Builder {
let core_threads = num_cpus::get().max(1);
let mut threadpool_builder = ThreadPoolBuilder::new();
threadpool_builder.name_prefix("tokio-runtime-worker-");
threadpool_builder.pool_size(core_threads);
Builder {
threadpool_builder,
core_threads,
clock: Clock::new(),
}
}
@@ -79,7 +92,8 @@ impl Builder {
#[deprecated(
since="0.1.9",
note="use the `core_threads`, `blocking_threads`, `name_prefix`, \
and `stack_size` functions on `runtime::Builder`, instead")]
`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;
@@ -108,6 +122,7 @@ impl Builder {
/// # }
/// ```
pub fn core_threads(&mut self, val: usize) -> &mut Self {
self.core_threads = val;
self.threadpool_builder.pool_size(val);
self
}
@@ -142,6 +157,37 @@ impl Builder {
self
}
/// Set the worker thread keep alive duration for threads in the `Runtime`'s
/// thread pool.
///
/// If set, a worker thread will wait for up to the specified duration for
/// work, at which point the thread will shutdown. When work becomes
/// available, a new thread will eventually be spawned to replace the one
/// that shut down.
///
/// When the value is `None`, the thread will wait for work forever.
///
/// The default value is `None`.
///
/// # Examples
///
/// ```
/// # extern crate tokio;
/// # extern crate futures;
/// # use tokio::runtime;
/// use std::time::Duration;
///
/// # pub fn main() {
/// let mut rt = runtime::Builder::new()
/// .keep_alive(Some(Duration::from_secs(30)))
/// .build();
/// # }
/// ```
pub fn keep_alive(&mut self, val: Option<Duration>) -> &mut Self {
self.threadpool_builder.keep_alive(val);
self
}
/// Set name prefix of threads spawned by the `Runtime`'s thread pool.
///
/// Thread name prefix is used for generating thread names. For example, if
@@ -194,6 +240,59 @@ impl Builder {
self
}
/// Execute function `f` after each thread is started but before it starts
/// doing work.
///
/// This is intended for bookkeeping and monitoring use cases.
///
/// # Examples
///
/// ```
/// # extern crate tokio;
/// # extern crate futures;
/// # use tokio::runtime;
///
/// # pub fn main() {
/// let thread_pool = runtime::Builder::new()
/// .after_start(|| {
/// println!("thread started");
/// })
/// .build();
/// # }
/// ```
pub fn after_start<F>(&mut self, f: F) -> &mut Self
where F: Fn() + Send + Sync + 'static
{
self.threadpool_builder.after_start(f);
self
}
/// Execute function `f` before each thread stops.
///
/// This is intended for bookkeeping and monitoring use cases.
///
/// # Examples
///
/// ```
/// # extern crate tokio;
/// # extern crate futures;
/// # use tokio::runtime;
///
/// # pub fn main() {
/// let thread_pool = runtime::Builder::new()
/// .before_stop(|| {
/// println!("thread stopping");
/// })
/// .build();
/// # }
/// ```
pub fn before_stop<F>(&mut self, f: F) -> &mut Self
where F: Fn() + Send + Sync + 'static
{
self.threadpool_builder.before_stop(f);
self
}
/// Create the configured `Runtime`.
///
/// The returned `ThreadPool` instance is ready to spawn tasks.
@@ -210,50 +309,58 @@ impl Builder {
/// # }
/// ```
pub fn build(&mut self) -> io::Result<Runtime> {
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
// TODO(stjepang): Once we remove the `threadpool_builder` method, remove this line too.
self.threadpool_builder.pool_size(self.core_threads);
let mut reactor_handles = Vec::new();
let mut timer_handles = Vec::new();
let mut timers = Vec::new();
for _ in 0..self.core_threads {
// Create a new reactor.
let reactor = Reactor::new()?;
reactor_handles.push(reactor.handle());
// Create a new timer.
let timer = Timer::new_with_now(reactor, self.clock.clone());
timer_handles.push(timer.handle());
timers.push(Mutex::new(Some(timer)));
}
// Get a handle to the clock for the runtime.
let clock1 = self.clock.clone();
let clock2 = clock1.clone();
let timers = Arc::new(Mutex::new(HashMap::<_, timer::Handle>::new()));
let t1 = timers.clone();
// Spawn a reactor on a background thread.
let reactor = Reactor::new()?.background()?;
// Get a handle to the reactor.
let reactor_handle = reactor.handle().clone();
let clock = self.clock.clone();
let pool = self.threadpool_builder
.around_worker(move |w, enter| {
let timer_handle = t1.lock().unwrap()
.get(w.id()).unwrap()
.clone();
let index = w.id().to_usize();
tokio_reactor::with_default(&reactor_handle, enter, |enter| {
clock::with_default(&clock1, enter, |enter| {
timer::with_default(&timer_handle, enter, |_| {
tokio_reactor::with_default(&reactor_handles[index], enter, |enter| {
clock::with_default(&clock, enter, |enter| {
timer::with_default(&timer_handles[index], enter, |_| {
w.run();
});
})
});
})
.custom_park(move |worker_id| {
// Create a new timer
let timer = Timer::new_with_now(DefaultPark::new(), clock2.clone());
let index = worker_id.to_usize();
timers.lock().unwrap()
.insert(worker_id.clone(), timer.handle());
timer
timers[index]
.lock()
.unwrap()
.take()
.unwrap()
})
.build();
// To support deprecated `reactor()` function
let reactor = Reactor::new()?;
let reactor_handle = reactor.handle();
Ok(Runtime {
inner: Some(Inner {
reactor,
reactor_handle,
reactor: Mutex::new(Some(reactor)),
pool,
}),
})
+395
View File
@@ -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,5 @@
use runtime::Inner;
use super::Inner;
use tokio_threadpool as threadpool;
use std::fmt;
@@ -6,23 +7,12 @@ use futures::{Future, Poll};
/// A future that resolves when the Tokio `Runtime` is shut down.
pub struct Shutdown {
pub(super) inner: Box<Future<Item = (), Error = ()> + Send>,
pub(super) inner: threadpool::Shutdown,
}
impl Shutdown {
pub(super) fn shutdown_now(inner: Inner) -> Self {
let inner = Box::new({
let pool = inner.pool;
let reactor = inner.reactor;
pool.shutdown_now().and_then(|_| {
reactor.shutdown_now()
.then(|_| {
Ok(())
})
})
});
let inner = inner.pool.shutdown_now();
Shutdown { inner }
}
}
+6
View File
@@ -1,9 +1,12 @@
#[cfg(feature = "timer")]
#[allow(deprecated)]
use tokio_timer::Deadline;
#[cfg(feature = "timer")]
use tokio_timer::Timeout;
use futures::Future;
#[cfg(feature = "timer")]
use std::time::{Instant, Duration};
@@ -55,12 +58,14 @@ pub trait FutureExt: Future {
/// tokio::run(future);
/// # }
/// ```
#[cfg(feature = "timer")]
fn timeout(self, timeout: Duration) -> Timeout<Self>
where Self: Sized,
{
Timeout::new(self, timeout)
}
#[cfg(feature = "timer")]
#[deprecated(since = "0.1.8", note = "use `timeout` instead")]
#[allow(deprecated)]
#[doc(hidden)]
@@ -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(()));
+16 -1
View File
@@ -1,7 +1,12 @@
use tokio_timer::Timeout;
#[cfg(feature = "timer")]
use tokio_timer::{
throttle::Throttle,
Timeout,
};
use futures::Stream;
#[cfg(feature = "timer")]
use std::time::Duration;
@@ -19,6 +24,15 @@ use std::time::Duration;
///
/// [`timeout`]: #method.timeout
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
{
Throttle::new(self, duration)
}
/// Creates a new stream which allows `self` until `timeout`.
///
@@ -52,6 +66,7 @@ pub trait StreamExt: Stream {
/// tokio::run(stream);
/// # }
/// ```
#[cfg(feature = "timer")]
fn timeout(self, timeout: Duration) -> Timeout<Self>
where Self: Sized,
{
+17 -1
View File
@@ -5,7 +5,7 @@ extern crate bytes;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::codec::*;
use bytes::Bytes;
use bytes::{Bytes, BytesMut, BufMut};
use futures::{Stream, Sink, Poll};
use futures::Async::*;
@@ -483,6 +483,22 @@ fn write_zero() {
assert!(io.get_ref().calls.is_empty());
}
#[test]
fn encode_overflow() {
// Test reproducing tokio-rs/tokio#681.
let mut codec = length_delimited::Builder::new().new_codec();
let mut buf = BytesMut::with_capacity(1024);
// Put some data into the buffer without resizing it to hold more.
let some_as = std::iter::repeat(b'a')
.take(1024)
.collect::<Vec<_>>();
buf.put_slice(&some_as[..]);
// Trying to encode the length header should resize the buffer if it won't fit.
codec.encode(Bytes::from("hello"), &mut buf).unwrap();
}
// ===== Test utils =====
fn would_block() -> io::Error {
+121 -9
View File
@@ -3,7 +3,7 @@ extern crate env_logger;
extern crate futures;
use futures::sync::oneshot;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex, atomic};
use std::thread;
use tokio::io;
use tokio::net::{TcpStream, TcpListener};
@@ -391,14 +391,126 @@ mod from_block_on_all {
}
}
#[test]
fn run_in_run() {
mod nested_enter {
use super::*;
use tokio::runtime::current_thread;
use std::panic;
tokio::run(lazy(|| {
panic::catch_unwind(|| {
tokio::run(lazy(|| { Ok::<(), ()>(()) }))
}).unwrap_err();
Ok::<(), ()>(())
}));
fn test<F1, F2>(first: F1, nested: F2)
where
F1: Fn(Box<Future<Item=(), Error=()> + Send>) + Send + 'static,
F2: Fn(Box<Future<Item=(), Error=()> + Send>) + panic::UnwindSafe + Send + 'static,
{
let panicked = Arc::new(Mutex::new(false));
let panicked2 = panicked.clone();
// Since this is testing panics in other threads, printing about panics
// is noisy and can give the impression that the test is ignoring panics.
//
// It *is* ignoring them, but on purpose.
let prev_hook = panic::take_hook();
panic::set_hook(Box::new(|info| {
let s = info.to_string();
if s.starts_with("panicked at 'nested ")
|| s.starts_with("panicked at 'Multiple executors at once")
{
// expected, noop
} else {
println!("{}", s);
}
}));
first(Box::new(lazy(move || {
panic::catch_unwind(move || {
nested(Box::new(lazy(|| { Ok::<(), ()>(()) })))
}).expect_err("nested should panic");
*panicked2.lock().unwrap() = true;
Ok::<(), ()>(())
})));
panic::set_hook(prev_hook);
assert!(*panicked.lock().unwrap(), "nested call should have panicked");
}
fn threadpool_new() -> Runtime {
Runtime::new().expect("rt new")
}
#[test]
fn run_in_run() {
test(tokio::run, tokio::run);
}
#[test]
fn threadpool_block_on_in_run() {
test(tokio::run, |fut| {
let mut rt = threadpool_new();
rt.block_on(fut).unwrap();
});
}
#[test]
fn threadpool_block_on_all_in_run() {
test(tokio::run, |fut| {
let rt = threadpool_new();
rt.block_on_all(fut).unwrap();
});
}
#[test]
fn current_thread_block_on_all_in_run() {
test(tokio::run, |fut| {
current_thread::block_on_all(fut).unwrap();
});
}
}
#[test]
fn runtime_reactor_handle() {
#![allow(deprecated)]
use futures::Stream;
use std::net::{
TcpListener as StdListener,
TcpStream as StdStream,
};
let rt = Runtime::new().unwrap();
let std_listener = StdListener::bind("127.0.0.1:0").unwrap();
let tk_listener = TcpListener::from_std(std_listener, rt.handle()).unwrap();
let addr = tk_listener.local_addr().unwrap();
// Spawn a thread since we are avoiding the runtime
let th = thread::spawn(|| {
for _ in tk_listener.incoming().take(1).wait() {
}
});
let _ = StdStream::connect(&addr).unwrap();
th.join().unwrap();
}
#[test]
fn after_start_and_before_stop_is_called() {
let _ = env_logger::try_init();
let after_start = Arc::new(atomic::AtomicUsize::new(0));
let before_stop = Arc::new(atomic::AtomicUsize::new(0));
let after_inner = after_start.clone();
let before_inner = before_stop.clone();
let runtime = tokio::runtime::Builder::new()
.after_start(move || { after_inner.clone().fetch_add(1, atomic::Ordering::Relaxed); })
.before_stop(move || { before_inner.clone().fetch_add(1, atomic::Ordering::Relaxed); })
.build()
.unwrap();
runtime.block_on_all(create_client_server_future()).unwrap();
assert!(after_start.load(atomic::Ordering::Relaxed) > 0);
assert!(before_stop.load(atomic::Ordering::Relaxed) > 0);
}
+1 -2
View File
@@ -3,7 +3,7 @@ name = "tokio-async-await"
# When releasing to crates.io:
# - Update html_root_url.
version = "0.1.4"
version = "0.1.5"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/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 -1
View File
@@ -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
+3 -5
View File
@@ -8,19 +8,17 @@ 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.33.0-nightly or later.
Add this to your `Cargo.toml`:
```toml
# At the very top of the file
cargo-features = ["edition"]
# In the `[packages]` section
edition = "2018"
# In the `[dependencies]` section
tokio-async-await = "0.1.0"
tokio = {version = "0.1.0", features = ["async-await-preview"]}
```
Then, get started. In your application, add:
+2 -2
View File
@@ -19,7 +19,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))
}
}
@@ -84,6 +84,6 @@ unsafe impl UnsafeWake for NoopWaker {
}
unsafe fn wake(&self) {
panic!("NoopWake cannot wake");
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.");
}
}
+1 -2
View File
@@ -5,10 +5,9 @@
async_await,
await_macro,
futures_api,
pin,
)]
#![doc(html_root_url = "https://docs.rs/tokio-async-await/0.1.4")]
#![doc(html_root_url = "https://docs.rs/tokio-async-await/0.1.5")]
#![deny(missing_docs, missing_debug_implementations)]
#![cfg_attr(test, deny(warnings))]
+3
View File
@@ -0,0 +1,3 @@
# 0.1.0 (unreleased)
* Initial release
+22
View File
@@ -0,0 +1,22 @@
[package]
name = "tokio-buf"
# When releasing to crates.io:
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Create "v0.1.x" git tag.
version = "0.1.0"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-buf/0.1.0"
description = """
Asynchronous stream of byte buffers
"""
categories = ["asynchronous"]
[dependencies]
bytes = { version = "0.4.10", features = [ "either" ] }
either = "1.5"
futures = "0.1.23"
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2019 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
View File
+70
View File
@@ -0,0 +1,70 @@
use BufStream;
use buf_stream::errors::internal::Never;
use bytes::{Bytes, BytesMut};
use futures::Poll;
use std::io;
impl BufStream for Vec<u8> {
type Item = io::Cursor<Vec<u8>>;
type Error = Never;
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.is_empty() {
return Ok(None.into());
}
poll_bytes(self)
}
}
impl BufStream for &'static [u8] {
type Item = io::Cursor<&'static [u8]>;
type Error = Never;
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.is_empty() {
return Ok(None.into());
}
poll_bytes(self)
}
}
impl BufStream for Bytes {
type Item = io::Cursor<Bytes>;
type Error = Never;
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.is_empty() {
return Ok(None.into());
}
poll_bytes(self)
}
}
impl BufStream for BytesMut {
type Item = io::Cursor<BytesMut>;
type Error = Never;
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.is_empty() {
return Ok(None.into());
}
poll_bytes(self)
}
}
fn poll_bytes<T: Default>(buf: &mut T)
-> Poll<Option<io::Cursor<T>>, Never>
{
use std::mem;
let bytes = mem::replace(buf, Default::default());
let buf = io::Cursor::new(bytes);
Ok(Some(buf).into())
}
+51
View File
@@ -0,0 +1,51 @@
use super::{BufStream, SizeHint};
use either::Either;
use futures::Poll;
/// A buf stream that sequences two buf streams together.
///
/// `Chain` values are produced by the `chain` function on `BufStream`.
#[derive(Debug)]
pub struct Chain<T, U> {
left: Option<T>,
right: U,
}
impl<T, U> Chain<T, U> {
pub(crate) fn new(left: T, right: U) -> Chain<T, U> {
Chain {
left: Some(left),
right,
}
}
}
impl<T, U> BufStream for Chain<T, U>
where
T: BufStream,
U: BufStream<Error = T::Error>,
{
type Item = Either<T::Item, U::Item>;
type Error = T::Error;
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if let Some(ref mut stream) = self.left {
let res = try_ready!(stream.poll_buf());
if res.is_some() {
return Ok(res.map(Either::Left).into());
}
}
self.left = None;
let res = try_ready!(self.right.poll_buf());
Ok(res.map(Either::Right).into())
}
fn size_hint(&self) -> SizeHint {
// TODO: Implement
SizeHint::default()
}
}
+103
View File
@@ -0,0 +1,103 @@
use super::{BufStream, FromBufStream};
use futures::{Future, Poll};
/// Consumes a buf stream, collecting the data into a single byte container.
///
/// `Collect` values are produced by `BufStream::collect`.
#[derive(Debug)]
pub struct Collect<T, U>
where
T: BufStream,
U: FromBufStream<T::Item>,
{
stream: T,
builder: Option<U::Builder>,
}
/// Errors returned from `Collect` future.
#[derive(Debug)]
pub struct CollectError<T, U> {
inner: Error<T, U>,
}
#[derive(Debug)]
enum Error<T, U> {
Stream(T),
Collect(U),
}
impl<T, U> Collect<T, U>
where
T: BufStream,
U: FromBufStream<T::Item>,
{
pub(crate) fn new(stream: T) -> Collect<T, U> {
let builder = U::builder(&stream.size_hint());
Collect {
stream,
builder: Some(builder),
}
}
}
impl<T, U> Future for Collect<T, U>
where
T: BufStream,
U: FromBufStream<T::Item>,
{
type Item = U;
type Error = CollectError<T::Error, U::Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
let res = self.stream.poll_buf()
.map_err(|err| {
let inner = Error::Stream(err);
CollectError { inner }
});
match try_ready!(res) {
Some(mut buf) => {
let builder = self.builder.as_mut().expect("cannot poll after done");
U::extend(builder, &mut buf, &self.stream.size_hint())
.map_err(|err| {
let inner = Error::Collect(err);
CollectError { inner }
})?;
}
None => {
let builder = self.builder.take().expect("cannot poll after done");
let value = U::build(builder)
.map_err(|err| {
let inner = Error::Collect(err);
CollectError { inner }
})?;
return Ok(value.into());
}
}
}
}
}
// ===== impl CollectError =====
impl<T, U> CollectError<T, U> {
/// Returns `true` if the error was caused by polling the stream.
pub fn is_stream_err(&self) -> bool {
match self.inner {
Error::Stream(_) => true,
_ => false,
}
}
/// Returns `true` if the error happened while collecting the data.
pub fn is_collect_err(&self) -> bool {
match self.inner {
Error::Collect(_) => true,
_ => false,
}
}
}
+32
View File
@@ -0,0 +1,32 @@
//! Error types
pub use super::collect::CollectError;
pub use super::from::CollectVecError;
pub use super::limit::LimitError;
// Being crate-private, we should be able to swap the type out in a
// backwards compatible way.
pub(crate) mod internal {
use std::{error, fmt};
/// An error that can never occur
pub enum Never {}
impl fmt::Debug for Never {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match *self {}
}
}
impl fmt::Display for Never {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match *self {}
}
}
impl error::Error for Never {
fn description(&self) -> &str {
match *self {}
}
}
}
+110
View File
@@ -0,0 +1,110 @@
use super::SizeHint;
use bytes::{Buf, BufMut};
use std::usize;
/// Conversion from a `BufStream`.
///
/// By implementing `FromBufStream` for a type, you define how it will be
/// created from a buf stream. This is common for types which describe byte
/// storage of some kind.
///
/// `FromBufStream` is rarely called explicitly, and it is instead used through
/// `BufStream`'s `collect` method.
pub trait FromBufStream<T: Buf>: Sized {
/// Type that is used to build `Self` while the `BufStream` is being
/// consumed.
type Builder;
/// Error that might happen on conversion.
type Error;
/// Create a new, empty, builder. The provided `hint` can be used to inform
/// reserving capacity.
fn builder(hint: &SizeHint) -> Self::Builder;
/// Extend the builder with the `Buf`.
///
/// This method is called whenever a new `Buf` value is obtained from the
/// buf stream.
///
/// The provided size hint represents the state of the stream **after**
/// `buf` has been yielded. The lower bound represents the minimum amount of
/// data that will be provided after this call to `extend` returns.
fn extend(builder: &mut Self::Builder, buf: &mut T, hint: &SizeHint)
-> Result<(), Self::Error>;
/// Finalize the building of `Self`.
///
/// Called once the buf stream is fully consumed.
fn build(builder: Self::Builder) -> Result<Self, Self::Error>;
}
/// Error returned from collecting into a `Vec<u8>`
#[derive(Debug)]
pub struct CollectVecError { _p: () }
impl<T: Buf> FromBufStream<T> for Vec<u8> {
type Builder = Vec<u8>;
type Error = CollectVecError;
fn builder(_hint: &SizeHint) -> Vec<u8> {
Vec::new()
}
fn extend(builder: &mut Self, buf: &mut T, hint: &SizeHint) -> Result<(), Self::Error> {
let lower = hint.lower();
// If the lower bound is greater than `usize::MAX` then we have a
// problem
if lower > usize::MAX as u64 {
return Err(CollectVecError { _p: () });
}
let mut reserve = lower as usize;
// If `upper` is set, use this value if it is less than or equal to 64.
// This only really impacts the first iteration.
match hint.upper() {
Some(upper) if upper <= 64 => {
reserve = upper as usize;
}
_ => {},
}
// hint.lower() represents the minimum amount of data that will be
// received *after* this function call. We reserve this amount on top of
// the amount of data in `buf`.
reserve = match reserve.checked_add(buf.remaining()) {
Some(n) => n,
None => return Err(CollectVecError { _p: () }),
};
// Always reserve 64 bytes the first time, unless `upper` is set and is
// less than 64.
if builder.is_empty() {
reserve = reserve.max(match hint.upper() {
Some(upper) if upper < 64 => upper as usize,
_ => 64,
});
}
// Make sure overflow won't happen when reserving
if reserve.checked_add(builder.len()).is_none() {
return Err(CollectVecError { _p: () });
}
// Reserve space
builder.reserve(reserve);
// Copy the data
builder.put(buf);
Ok(())
}
fn build(builder: Self) -> Result<Self, Self::Error> {
Ok(builder)
}
}
+92
View File
@@ -0,0 +1,92 @@
use super::{BufStream, SizeHint};
use bytes::Buf;
use futures::Poll;
/// Limits the stream to a maximum amount of data.
#[derive(Debug)]
pub struct Limit<T> {
stream: T,
remaining: u64,
}
/// Errors returned from `Limit`.
#[derive(Debug)]
pub struct LimitError<T> {
/// When `None`, limit was reached
inner: Option<T>,
}
impl<T> Limit<T> {
pub(crate) fn new(stream: T, amount: u64) -> Limit<T> {
Limit {
stream,
remaining: amount,
}
}
}
impl<T> BufStream for Limit<T>
where
T: BufStream,
{
type Item = T::Item;
type Error = LimitError<T::Error>;
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
use futures::Async::Ready;
if self.stream.size_hint().lower() > self.remaining {
return Err(LimitError { inner: None });
}
let res = self.stream.poll_buf()
.map_err(|err| {
LimitError { inner: Some(err) }
});
match res {
Ok(Ready(Some(ref buf))) => {
if buf.remaining() as u64 > self.remaining {
self.remaining = 0;
return Err(LimitError { inner: None });
}
self.remaining -= buf.remaining() as u64;
}
_ => {}
}
res
}
fn size_hint(&self) -> SizeHint {
let mut hint = self.stream.size_hint();
let upper = hint.upper()
.map(|upper| upper.min(self.remaining))
.unwrap_or(self.remaining);
hint.set_upper(upper);
hint
}
fn consume_hint(&mut self, amount: usize) {
// TODO: Should this be capped by `self.remaining`?
self.stream.consume_hint(amount)
}
}
// ===== impl LimitError =====
impl<T> LimitError<T> {
/// Returns `true` if the error was caused by polling the stream.
pub fn is_stream_err(&self) -> bool {
self.inner.is_some()
}
/// Returns `true` if the stream reached its limit.
pub fn is_limit_err(&self) -> bool {
self.inner.is_none()
}
}
+163
View File
@@ -0,0 +1,163 @@
//! Types and utilities for working with `BufStream`.
mod bytes;
mod chain;
mod collect;
pub mod errors;
mod from;
mod limit;
mod size_hint;
mod str;
pub use self::chain::Chain;
pub use self::collect::Collect;
pub use self::from::FromBufStream;
pub use self::limit::Limit;
pub use self::size_hint::SizeHint;
use bytes::Buf;
use futures::Poll;
/// An asynchronous stream of bytes.
///
/// `BufStream` asynchronously yields values implementing `Buf`, i.e. byte
/// buffers.
pub trait BufStream {
/// Values yielded by the `BufStream`.
///
/// Each item is a sequence of bytes representing a chunk of the total
/// `ByteStream`.
type Item: Buf;
/// The error type this `BufStream` might generate.
type Error;
/// Attempt to pull out the next buffer of this stream, registering the
/// current task for wakeup if the value is not yet available, and returning
/// `None` if the stream is exhausted.
///
/// # Return value
///
/// There are several possible return values, each indicating a distinct
/// stream state:
///
/// - `Ok(Async::NotReady)` means that this stream's next value is not ready
/// yet. Implementations will ensure that the current task will be notified
/// when the next value may be ready.
///
/// - `Ok(Async::Ready(Some(buf)))` means that the stream has successfully
/// produced a value, `buf`, and may produce further values on subsequent
/// `poll_buf` calls.
///
/// - `Ok(Async::Ready(None))` means that the stream has terminated, and
/// `poll_buf` should not be invoked again.
///
/// # Panics
///
/// Once a stream is finished, i.e. `Ready(None)` has been returned, further
/// calls to `poll_buf` may result in a panic or other "bad behavior".
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error>;
/// Returns the bounds on the remaining length of the stream.
///
/// The size hint allows the caller to perform certain optimizations that
/// are dependent on the byte stream size. For example, `collect` uses the
/// size hint to pre-allocate enough capacity to store the entirety of the
/// data received from the byte stream.
///
/// When `SizeHint::upper()` returns `Some` with a value equal to
/// `SizeHint::lower()`, this represents the exact number of bytes that will
/// be yielded by the `BufStream`.
///
/// # Implementation notes
///
/// While not enforced, implementations are expected to respect the values
/// returned from `SizeHint`. Any deviation is considered an implementation
/// bug. Consumers may rely on correctness in order to use the value as part
/// of protocol impelmentations. For example, an HTTP library may use the
/// size hint to set the `content-length` header.
///
/// However, `size_hint` must not be trusted to omit bounds checks in unsafe
/// code. An incorrect implementation of `size_hint()` must not lead to
/// memory safety violations.
fn size_hint(&self) -> SizeHint {
SizeHint::default()
}
/// Indicates to the `BufStream` how much data the consumer is currently
/// able to process.
///
/// The consume hint allows the stream to perform certain optimizations that
/// are dependent on the consumer's readiness. For example, the consume hint
/// may be used to request a remote peer to start sending up to `amount`
/// data.
///
/// Calling `consume_hint` is not a requirement. If `consume_hint` is never
/// called, the stream should assume a default behavior. When `consume_hint`
/// is called, the stream should make a best effort to honor by the request.
///
/// `amount` represents the number of bytes that the caller would like to
/// receive at the time the function is called. For example, if
/// `consume_hint` is called with 20, the consumer requests 20 bytes. The
/// stream may yield less than that. If the next call to `poll_buf` returns
/// 5 bytes, the consumer still has 15 bytes requested. At this point,
/// invoking `consume_hint` again with 20 resets the amount requested back
/// to 20 bytes.
///
/// Calling `consume_hint` with 0 as the argument informs the stream that
/// the caller does not intend to call `poll_buf`. If `poll_buf` **is**
/// called, the stream may, but is not obligated to, return `NotReady` even
/// if it could produce data at that point. If it chooses to return
/// `NotReady`, when `consume_hint` is called with a non-zero argument, the
/// task must be notified in order to respect the `poll_buf` contract.
fn consume_hint(&mut self, amount: usize) {
// By default, this function does nothing
drop(amount);
}
/// Takes two buf streams and creates a new buf stream over both in
/// sequence.
///
/// `chain()` returns a new `BufStream` value which will first yield all
/// data from `self` then all data from `other`.
///
/// In other words, it links two buf streams together, in a chain.
fn chain<T>(self, other: T) -> Chain<Self, T>
where
Self: Sized,
T: BufStream<Error = Self::Error>,
{
Chain::new(self, other)
}
/// Consumes all data from `self`, storing it in byte storage of type `T`.
///
/// `collect()` returns a future that buffers all data yielded from `self`
/// into storage of type of `T`. The future completes once `self` yield
/// `None`, returning the buffered data.
///
/// The collect future will yield an error if `self` yields an error or if
/// the collect operation errors. The collect error cases are dependent on
/// the target storage type.
fn collect<T>(self) -> Collect<Self, T>
where
Self: Sized,
T: FromBufStream<Self::Item>,
{
Collect::new(self)
}
/// Limit the number of bytes that the stream can yield.
///
/// `limit()` returns a new `BufStream` value which yields all the data from
/// `self` while ensuring that at most `amount` bytes are yielded.
///
/// If `self` can yield greater than `amount` bytes, the returned stream
/// will yield an error.
fn limit(self, amount: u64) -> Limit<Self>
where
Self: Sized,
{
Limit::new(self, amount)
}
}
+56
View File
@@ -0,0 +1,56 @@
use std::u64;
/// A `BufStream` size hint
///
/// The default implementation returns:
///
/// * 0 for `available`
/// * 0 for `lower`
/// * `None` for `upper`.
#[derive(Debug, Default, Clone)]
pub struct SizeHint {
lower: u64,
upper: Option<u64>,
}
impl SizeHint {
/// Returns a new `SizeHint` with default values
pub fn new() -> SizeHint {
SizeHint::default()
}
/// Returns the lower bound of data that the `BufStream` will yield before
/// completing.
pub fn lower(&self) -> u64 {
self.lower
}
/// Set the value of the `lower` hint.
///
/// # Panics
///
/// The function panics if `value` is less than `upper`.
pub fn set_lower(&mut self, value: u64) {
assert!(value <= self.upper.unwrap_or(u64::MAX));
self.lower = value;
}
/// Returns the upper bound of data the `BufStream` will yield before
/// completing, or `None` if the value is unknown.
pub fn upper(&self) -> Option<u64> {
self.upper
}
/// Set the value of the `upper` hint value.
///
/// # Panics
///
/// This function panics if `value` is less than `lower`.
pub fn set_upper(&mut self, value: u64) {
// There is no need to check `available` as that is guaranteed to be
// less than or equal to `lower`.
assert!(value >= self.lower, "`value` is less than than `lower`");
self.upper = Some(value);
}
}
+39
View File
@@ -0,0 +1,39 @@
use BufStream;
use buf_stream::errors::internal::Never;
use futures::Poll;
use std::io;
use std::mem;
impl BufStream for String {
type Item = io::Cursor<Vec<u8>>;
type Error = Never;
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.is_empty() {
return Ok(None.into());
}
let bytes = mem::replace(self, Default::default()).into_bytes();
let buf = io::Cursor::new(bytes);
Ok(Some(buf).into())
}
}
impl BufStream for &'static str {
type Item = io::Cursor<&'static [u8]>;
type Error = Never;
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.is_empty() {
return Ok(None.into());
}
let bytes = mem::replace(self, Default::default()).as_bytes();
let buf = io::Cursor::new(bytes);
Ok(Some(buf).into())
}
}
+20
View File
@@ -0,0 +1,20 @@
#![doc(html_root_url = "https://docs.rs/tokio-buf/0.1.0")]
#![deny(missing_docs, missing_debug_implementations)]
#![cfg_attr(test, deny(warnings))]
//! Asynchronous stream of bytes.
//!
//! This crate contains the `BufStream` trait and a number of combinators for
//! this trait. The trait is similar to `Stream` in the `futures` library, but
//! instead of yielding arbitrary values, it only yields types that implement
//! `Buf` (i.e, byte collections).
extern crate bytes;
extern crate either;
#[macro_use]
extern crate futures;
pub mod buf_stream;
#[doc(inline)]
pub use buf_stream::BufStream;
+329
View File
@@ -0,0 +1,329 @@
extern crate tokio_buf;
extern crate bytes;
extern crate futures;
use tokio_buf::buf_stream::{BufStream, SizeHint};
use bytes::Buf;
use futures::{Future, Poll};
use futures::Async::*;
use std::collections::VecDeque;
use std::io::Cursor;
macro_rules! assert_buf_eq {
($actual:expr, $expect:expr) => {{
match $actual {
Ok(Ready(Some(val))) => {
assert_eq!(val.remaining(), val.bytes().len());
assert_eq!(val.bytes(), $expect.as_bytes());
}
Ok(Ready(None)) => panic!("expected value; BufStream yielded None"),
Ok(NotReady) => panic!("expected value; BufStream is not ready"),
Err(e) => panic!("expected value; got error = {:?}", e),
}
}};
}
macro_rules! assert_none {
($actual:expr) => {
match $actual {
Ok(Ready(None)) => {}
actual => panic!("expected None; actual = {:?}", actual),
}
}
}
macro_rules! assert_not_ready {
($actual:expr) => {
match $actual {
Ok(NotReady) => {}
actual => panic!("expected NotReady; actual = {:?}", actual),
}
}
}
// ===== test `SizeHint` =====
#[test]
fn size_hint() {
let hint = SizeHint::new();
assert_eq!(hint.lower(), 0);
assert!(hint.upper().is_none());
let mut hint = SizeHint::new();
hint.set_lower(100);
assert_eq!(hint.lower(), 100);
assert!(hint.upper().is_none());
let mut hint = SizeHint::new();
hint.set_upper(200);
assert_eq!(hint.lower(), 0);
assert_eq!(hint.upper(), Some(200));
let mut hint = SizeHint::new();
hint.set_lower(100);
hint.set_upper(100);
assert_eq!(hint.lower(), 100);
assert_eq!(hint.upper(), Some(100));
}
#[test]
#[should_panic]
fn size_hint_lower_bigger_than_upper() {
let mut hint = SizeHint::new();
hint.set_upper(100);
hint.set_lower(200);
}
#[test]
#[should_panic]
fn size_hint_upper_less_than_lower() {
let mut hint = SizeHint::new();
hint.set_lower(200);
hint.set_upper(100);
}
// ===== test `chain()` =====
#[test]
fn chain() {
// Chain one with one
//
let mut bs = one("hello").chain(one("world"));
assert_buf_eq!(bs.poll_buf(), "hello");
assert_buf_eq!(bs.poll_buf(), "world");
assert_none!(bs.poll_buf());
// Chain multi with multi
let mut bs = list(&["foo", "bar"])
.chain(list(&["baz", "bok"]));
assert_buf_eq!(bs.poll_buf(), "foo");
assert_buf_eq!(bs.poll_buf(), "bar");
assert_buf_eq!(bs.poll_buf(), "baz");
assert_buf_eq!(bs.poll_buf(), "bok");
assert_none!(bs.poll_buf());
// Chain includes a not ready call
//
let mut bs = new_mock(&[
Ok(Ready("foo")),
Ok(NotReady),
Ok(Ready("bar"))
]).chain(one("baz"));
assert_buf_eq!(bs.poll_buf(), "foo");
assert_not_ready!(bs.poll_buf());
assert_buf_eq!(bs.poll_buf(), "bar");
assert_buf_eq!(bs.poll_buf(), "baz");
assert_none!(bs.poll_buf());
}
// ===== Test `collect()` =====
#[test]
fn collect_vec() {
// While unfortunate, this test makes some assumptions on vec's resizing
// behavior.
//
// Collect one
//
let bs = one("hello world");
let vec: Vec<u8> = bs.collect()
.wait().unwrap();
assert_eq!(vec, b"hello world");
assert_eq!(vec.capacity(), 64);
// Collect one, with size hint
//
let mut bs = one("hello world");
bs.size_hint.set_lower(11);
let vec: Vec<u8> = bs.collect()
.wait().unwrap();
assert_eq!(vec, b"hello world");
assert_eq!(vec.capacity(), 64);
// Collect one, with size hint
//
let mut bs = one("hello world");
bs.size_hint.set_lower(10);
let vec: Vec<u8> = bs.collect()
.wait().unwrap();
assert_eq!(vec, b"hello world");
assert_eq!(vec.capacity(), 64);
// Collect many
//
let bs = list(&["hello", " ", "world", ", one two three"]);
let vec: Vec<u8> = bs.collect()
.wait().unwrap();
assert_eq!(vec, b"hello world, one two three");
}
// ===== Test limit() =====
#[test]
fn limit() {
// Not limited
let res = one("hello world")
.limit(100)
.collect::<Vec<_>>()
.wait().unwrap();
assert_eq!(res, b"hello world");
let res = list(&["hello", " ", "world"])
.limit(100)
.collect::<Vec<_>>()
.wait().unwrap();
assert_eq!(res, b"hello world");
let res = list(&["hello", " ", "world"])
.limit(11)
.collect::<Vec<_>>()
.wait().unwrap();
assert_eq!(res, b"hello world");
// Limited
let res = one("hello world")
.limit(5)
.collect::<Vec<_>>()
.wait();
assert!(res.is_err());
let res = one("hello world")
.limit(10)
.collect::<Vec<_>>()
.wait();
assert!(res.is_err());
let mut bs = list(&["hello", " ", "world"])
.limit(9);
assert_buf_eq!(bs.poll_buf(), "hello");
assert_buf_eq!(bs.poll_buf(), " ");
assert!(bs.poll_buf().is_err());
let mut bs = list(&["hello", " ", "world"]);
bs.size_hint.set_lower(11);
let mut bs = bs.limit(9);
assert!(bs.poll_buf().is_err());
}
// ===== BufStream impelmentations for misc types =====
#[test]
fn str_buf_stream() {
let mut bs = "hello world".to_string();
assert_buf_eq!(bs.poll_buf(), "hello world");
assert!(bs.is_empty());
assert_none!(bs.poll_buf());
let mut bs = "hello world";
assert_buf_eq!(bs.poll_buf(), "hello world");
assert!(bs.is_empty());
assert_none!(bs.poll_buf());
}
// ===== Test utils =====
fn one(buf: &'static str) -> Mock {
list(&[buf])
}
fn list(bufs: &[&'static str]) -> Mock {
let mut polls = VecDeque::new();
for &buf in bufs {
polls.push_back(Ok(Ready(buf.as_bytes())));
}
Mock {
polls,
size_hint: SizeHint::default(),
}
}
fn new_mock(values: &[Poll<&'static str, ()>]) -> Mock {
let mut polls = VecDeque::new();
for &v in values {
polls.push_back(match v {
Ok(Ready(v)) => Ok(Ready(v.as_bytes())),
Ok(NotReady) => Ok(NotReady),
Err(e) => Err(e),
});
}
Mock {
polls,
size_hint: SizeHint::default(),
}
}
#[derive(Debug)]
struct Mock {
polls: VecDeque<Poll<&'static [u8], ()>>,
size_hint: SizeHint,
}
#[derive(Debug)]
struct MockBuf {
data: Cursor<&'static [u8]>,
}
impl BufStream for Mock {
type Item = MockBuf;
type Error = ();
fn poll_buf(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
match self.polls.pop_front() {
Some(Ok(Ready(value))) => Ok(Ready(Some(MockBuf::new(value)))),
Some(Ok(NotReady)) => Ok(NotReady),
Some(Err(e)) => Err(e),
None => Ok(Ready(None)),
}
}
fn size_hint(&self) -> SizeHint {
self.size_hint.clone()
}
}
impl MockBuf {
fn new(data: &'static [u8]) -> MockBuf {
MockBuf {
data: Cursor::new(data),
}
}
}
impl Buf for MockBuf {
fn remaining(&self) -> usize {
self.data.remaining()
}
fn bytes(&self) -> &[u8] {
self.data.bytes()
}
fn advance(&mut self, cnt: usize) {
self.data.advance(cnt)
}
}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -1,3 +1,11 @@
# 0.1.4 (November 21, 2018)
* Fix shutdown on idle (#763).
# 0.1.3 (September 27, 2018)
* Fix minimal versions
# 0.1.2 (September 26, 2018)
* Implement `futures::Executor` for executor types (#563)
+3 -3
View File
@@ -6,8 +6,8 @@ name = "tokio-current-thread"
# - Update doc URL
# - Update CHANGELOG.md.
# - Create "v0.1.x" git tag.
version = "0.1.2"
documentation = "https://docs.rs/tokio-current-thread/0.1.2/tokio_current_thread"
version = "0.1.4"
documentation = "https://docs.rs/tokio-current-thread/0.1.4/tokio_current_thread"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://github.com/tokio-rs/tokio"
license = "MIT"
@@ -19,5 +19,5 @@ keywords = ["futures", "tokio"]
categories = ["concurrency", "asynchronous"]
[dependencies]
tokio-executor = { version = "0.1.2", path = "../tokio-executor" }
tokio-executor = { version = "0.1.5", path = "../tokio-executor" }
futures = "0.1.19"
+1 -1
View File
@@ -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 -8
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio-current-thread/0.1.2")]
#![doc(html_root_url = "https://docs.rs/tokio-current-thread/0.1.4")]
#![deny(warnings, missing_docs, missing_debug_implementations)]
//! A single-threaded executor which executes tasks on the same thread from which
@@ -653,6 +653,13 @@ impl Handle {
where
F: Future<Item = (), Error = ()> + Send + 'static,
{
if thread::current().id() == self.thread {
let mut e = TaskExecutor::current();
if e.id() == Some(self.id) {
return e.spawn_local(Box::new(future));
}
}
if self.shut_down.get() {
return Err(SpawnError::shutdown());
}
@@ -669,13 +676,6 @@ impl Handle {
return Err(SpawnError::shutdown());
}
if thread::current().id() == self.thread {
let mut e = TaskExecutor::current();
if e.id() == Some(self.id) {
return e.spawn_local(Box::new(future));
}
}
self.sender.send(Box::new(future))
.expect("CurrentThread does not exist anymore");
// use 0 for the id, CurrentThread does not make use of it
@@ -778,6 +778,25 @@ fn spawn_from_other_thread_unpark() {
).unwrap();
}
#[test]
fn spawn_from_executor_with_handle() {
let mut current_thread = CurrentThread::new();
let handle = current_thread.handle();
let (tx, rx) = oneshot::channel();
current_thread.spawn(lazy(move || {
handle.spawn(lazy(move || {
tx.send(()).unwrap();
Ok(())
})).unwrap();
Ok::<_, ()>(())
}));
current_thread.run();
rx.wait().unwrap();
}
fn ok() -> future::FutureResult<(), ()> {
future::ok(())
}
+5
View File
@@ -1,3 +1,8 @@
# 0.1.6 (January 6, 2019)
* Implement `Unpark` for `Arc<Unpark>` (#802).
* Switch to crossbeam's Parker / Unparker (#528).
# 0.1.5 (September 26, 2018)
* Implement `futures::Executor` for `DefaultExecutor` (#563).
+5 -2
View File
@@ -5,9 +5,11 @@ name = "tokio-executor"
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Update doc URL.
# - Cargo.toml
# - README.md
# - Create "v0.1.x" git tag.
version = "0.1.5"
documentation = "https://docs.rs/tokio-executor/0.1.5/tokio_executor"
version = "0.1.6"
documentation = "https://docs.rs/tokio-executor/0.1.6/tokio_executor"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://github.com/tokio-rs/tokio"
license = "MIT"
@@ -19,4 +21,5 @@ keywords = ["futures", "tokio"]
categories = ["concurrency", "asynchronous"]
[dependencies]
crossbeam-utils = "0.6.2"
futures = "0.1.19"
+1 -1
View File
@@ -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
+1 -1
View File
@@ -2,7 +2,7 @@
Task execution related traits and utilities.
[Documentation](https://tokio-rs.github.io/tokio/tokio_executor/)
[Documentation](https://docs.rs/tokio-executor/0.1.6/tokio_executor)
## Overview
+2 -1
View File
@@ -1,5 +1,5 @@
#![deny(missing_docs, missing_debug_implementations, warnings)]
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.5")]
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.6")]
//! Task execution related traits and utilities.
//!
@@ -35,6 +35,7 @@
//! [`Park`]: park/index.html
//! [`Future::poll`]: https://docs.rs/futures/0.1/futures/future/trait.Future.html#tymethod.poll
extern crate crossbeam_utils;
extern crate futures;
mod enter;
+18 -95
View File
@@ -46,10 +46,11 @@
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::{Arc, Mutex, Condvar};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use crossbeam_utils::sync::{Parker, Unparker};
/// Block the current thread.
///
/// See [module documentation][mod] for more details.
@@ -133,6 +134,12 @@ impl Unpark for Box<Unpark> {
}
}
impl Unpark for Arc<Unpark> {
fn unpark(&self) {
(**self).unpark()
}
}
/// Blocks the current thread using a condition variable.
///
/// Implements the [`Park`] functionality by using a condition variable. An
@@ -161,26 +168,11 @@ pub struct ParkError {
/// Unblocks a thread that was blocked by `ParkThread`.
#[derive(Clone, Debug)]
pub struct UnparkThread {
inner: Arc<Inner>,
inner: Unparker,
}
#[derive(Debug)]
struct Inner {
state: AtomicUsize,
mutex: Mutex<()>,
condvar: Condvar,
}
const IDLE: usize = 0;
const NOTIFY: usize = 1;
const SLEEP: usize = 2;
thread_local! {
static CURRENT_PARK_THREAD: Arc<Inner> = Arc::new(Inner {
state: AtomicUsize::new(IDLE),
mutex: Mutex::new(()),
condvar: Condvar::new(),
});
static CURRENT_PARKER: Parker = Parker::new();
}
// ===== impl ParkThread =====
@@ -198,9 +190,9 @@ impl ParkThread {
/// Get a reference to the `ParkThread` handle for this thread.
fn with_current<F, R>(&self, f: F) -> R
where F: FnOnce(&Arc<Inner>) -> R,
where F: FnOnce(&Parker) -> R,
{
CURRENT_PARK_THREAD.with(|inner| f(inner))
CURRENT_PARKER.with(|inner| f(inner))
}
}
@@ -209,16 +201,18 @@ impl Park for ParkThread {
type Error = ParkError;
fn unpark(&self) -> Self::Unpark {
let inner = self.with_current(|inner| inner.clone());
let inner = self.with_current(|inner| inner.unparker().clone());
UnparkThread { inner }
}
fn park(&mut self) -> Result<(), Self::Error> {
self.with_current(|inner| inner.park(None))
self.with_current(|inner| inner.park());
Ok(())
}
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
self.with_current(|inner| inner.park(Some(duration)))
self.with_current(|inner| inner.park_timeout(duration));
Ok(())
}
}
@@ -229,74 +223,3 @@ impl Unpark for UnparkThread {
self.inner.unpark();
}
}
// ===== impl Inner =====
impl Inner {
/// Park the current thread for at most `dur`.
fn park(&self, timeout: Option<Duration>) -> Result<(), ParkError> {
// If currently notified, then we skip sleeping. This is checked outside
// of the lock to avoid acquiring a mutex if not necessary.
match self.state.compare_and_swap(NOTIFY, IDLE, Ordering::SeqCst) {
NOTIFY => return Ok(()),
IDLE => {},
_ => unreachable!(),
}
// The state is currently idle, so obtain the lock and then try to
// transition to a sleeping state.
let mut m = self.mutex.lock().unwrap();
// Transition to sleeping
match self.state.compare_and_swap(IDLE, SLEEP, Ordering::SeqCst) {
NOTIFY => {
// Notified before we could sleep, consume the notification and
// exit
self.state.store(IDLE, Ordering::SeqCst);
return Ok(());
}
IDLE => {},
_ => unreachable!(),
}
m = match timeout {
Some(timeout) => self.condvar.wait_timeout(m, timeout).unwrap().0,
None => self.condvar.wait(m).unwrap(),
};
// Transition back to idle. If the state has transitioned to `NOTIFY`,
// this will consume that notification
self.state.store(IDLE, Ordering::SeqCst);
// Explicitly drop the mutex guard. There is no real point in doing it
// except that I find it helpful to make it explicit where we want the
// mutex to unlock.
drop(m);
Ok(())
}
fn unpark(&self) {
// First, try transitioning from IDLE -> NOTIFY, this does not require a
// lock.
match self.state.compare_and_swap(IDLE, NOTIFY, Ordering::SeqCst) {
IDLE | NOTIFY => return,
SLEEP => {}
_ => unreachable!(),
}
// The other half is sleeping, this requires a lock
let _m = self.mutex.lock().unwrap();
// Transition to NOTIFY
match self.state.swap(NOTIFY, Ordering::SeqCst) {
SLEEP => {}
NOTIFY => return,
IDLE => return,
_ => unreachable!(),
}
// Wakeup the sleeper
self.condvar.notify_one();
}
}
+8
View File
@@ -1,3 +1,11 @@
# 0.1.5 (January 6, 2019)
* Add examples to `File` API docs (#786).
# 0.1.4 (October 23, 2018)
* Provide `File::from_std` (#696).
# 0.1.3 (August 6, 2018)
* Add async equivalents to most of `std::fs` (#494).
+7 -3
View File
@@ -4,14 +4,17 @@ name = "tokio-fs"
# When releasing to crates.io:
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Update doc URL.
# - Cargo.toml
# - README.md
# - Create "v0.1.x" git tag.
version = "0.1.3"
version = "0.1.5"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-fs/0.1"
documentation = "https://docs.rs/tokio-fs/0.1.5/tokio_fs"
description = """
Filesystem API for Tokio.
"""
@@ -24,8 +27,9 @@ tokio-threadpool = { version = "0.1.3", path = "../tokio-threadpool" }
tokio-io = { version = "0.1.6", path = "../tokio-io" }
[dev-dependencies]
rand = "0.5"
rand = "0.6"
tempfile = "3"
tempdir = "0.3"
tokio-io = { version = "0.1.6", path = "../tokio-io" }
tokio-codec = { version = "0.1.0", path = "../tokio-codec" }
tokio = { version = "0.1.7", path = ".." }
+1 -1
View File
@@ -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
+1 -1
View File
@@ -2,7 +2,7 @@
Asynchronous filesystem manipulation operations (and stdin, stdout, stderr).
[Documentation](https://tokio-rs.github.io/tokio/tokio_fs/)
[Documentation](https://docs.rs/tokio-fs/0.1.5/tokio_fs)
## Overview
+3 -2
View File
@@ -14,7 +14,7 @@ use futures::{Future, Stream, Sink};
use std::io;
pub fn main() {
pub fn main() -> Result<(), Box<std::error::Error>> {
let pool = Builder::new()
.pool_size(1)
.build();
@@ -44,5 +44,6 @@ pub fn main() {
.map_err(|e| panic!("io error = {:?}", e))
});
pool.shutdown_on_idle().wait().unwrap();
pool.shutdown_on_idle().wait().map_err(|_| "failed to shutdown the thread pool")?;
Ok(())
}
+1 -1
View File
@@ -8,7 +8,7 @@ use std::io;
const POLL_AFTER_RESOLVE: &str = "Cannot poll MetadataFuture after it resolves";
/// Future returned by `File::metadata` and resolves to a `(Metadata, File)` instance.
/// Future returned by `File::metadata` and resolves to a `(File, Metadata)` instance.
#[derive(Debug)]
pub struct MetadataFuture {
file: Option<File>,
+282 -2
View File
@@ -34,6 +34,46 @@ use std::path::Path;
/// Files are automatically closed when they go out of scope.
///
/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
///
/// # Examples
///
/// Create a new file and asynchronously write bytes to it:
///
/// ```no_run
/// extern crate tokio;
///
/// use tokio::prelude::{AsyncWrite, Future};
///
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .and_then(|mut file| file.poll_write(b"hello, world!"))
/// .map(|res| {
/// println!("{:?}", res);
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
///
/// Read the contents of a file into a buffer
///
/// ```no_run
/// extern crate tokio;
///
/// use tokio::prelude::{AsyncRead, Future};
///
/// fn main() {
/// let task = tokio::fs::File::open("foo.txt")
/// .and_then(|mut file| {
/// let mut contents = vec![];
/// file.read_buf(&mut contents)
/// .map(|res| {
/// println!("{:?}", res);
/// })
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
/// tokio::run(task);
/// }
/// ```
#[derive(Debug)]
pub struct File {
std: Option<StdFile>,
@@ -52,6 +92,23 @@ impl File {
/// runtime or if the underlying [`open`] call results in an error.
///
/// [`open`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.open
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
/// fn main() {
/// let task = tokio::fs::File::open("foo.txt").and_then(|file| {
/// // do something with the file ...
/// file.metadata().map(|md| println!("{:?}", md))
/// }).map_err(|e| {
/// // handle errors
/// eprintln!("IO error: {:?}", e);
/// });
/// tokio::run(task);
/// }
/// ```
pub fn open<P>(path: P) -> OpenFuture<P>
where P: AsRef<Path> + Send + 'static,
{
@@ -73,16 +130,46 @@ impl File {
/// runtime or if the underlying [`create`] call results in an error.
///
/// [`create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .and_then(|file| {
/// // do something with the created file ...
/// file.metadata().map(|md| println!("{:?}", md))
/// }).map_err(|e| {
/// // handle errors
/// eprintln!("IO error: {:?}", e);
/// });
/// tokio::run(task);
/// }
/// ```
pub fn create<P>(path: P) -> CreateFuture<P>
where P: AsRef<Path> + Send + 'static,
{
CreateFuture::new(path)
}
/// Convert a [`std::fs::File`][std] to a `tokio_fs::File`.
/// Convert a [`std::fs::File`][std] to a [`tokio_fs::File`][file].
///
/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
pub(crate) fn from_std(std: StdFile) -> File {
/// [file]: struct.File.html
///
/// Examples
/// ```no_run
/// # extern crate tokio;
/// use std::fs::File;
///
/// fn main() {
/// let std_file = File::open("foo.txt").unwrap();
/// let file = tokio::fs::File::from_std(std_file);
/// }
/// ```
pub fn from_std(std: StdFile) -> File {
File { std: Some(std) }
}
@@ -98,6 +185,25 @@ impl File {
/// # Errors
///
/// Seeking to a negative offset is considered an error.
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
/// use std::io::SeekFrom;
///
/// fn main() {
/// let task = tokio::fs::File::open("foo.txt")
/// // move cursor 6 bytes from the start of the file
/// .and_then(|mut file| file.poll_seek(SeekFrom::Start(6)))
/// .map(|res| {
/// println!("{:?}", res);
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
pub fn poll_seek(&mut self, pos: io::SeekFrom) -> Poll<u64, io::Error> {
::blocking_io(|| self.std().seek(pos))
}
@@ -108,6 +214,25 @@ impl File {
///
/// This method consumes the `File` and returns it back when the future
/// completes.
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
/// use std::io::SeekFrom;
///
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .and_then(|file| file.seek(SeekFrom::Start(6)))
/// .map(|file| {
/// // handle returned file ..
/// # println!("{:?}", file);
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
pub fn seek(self, pos: io::SeekFrom) -> SeekFuture {
SeekFuture::new(self, pos)
}
@@ -116,6 +241,27 @@ impl File {
///
/// This function will attempt to ensure that all in-core data reaches the
/// filesystem before returning.
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::{AsyncWrite, Future};
///
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .and_then(|mut file| {
/// file.poll_write(b"hello, world!")?;
/// file.poll_sync_all()
/// })
/// .map(|res| {
/// // handle returned result ..
/// # println!("{:?}", res);
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
pub fn poll_sync_all(&mut self) -> Poll<(), io::Error> {
::blocking_io(|| self.std().sync_all())
}
@@ -128,6 +274,27 @@ impl File {
/// operations.
///
/// Note that some platforms may simply implement this in terms of `poll_sync_all`.
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::{AsyncWrite, Future};
///
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .and_then(|mut file| {
/// file.poll_write(b"hello, world!")?;
/// file.poll_sync_data()
/// })
/// .map(|res| {
/// // handle returned result ..
/// # println!("{:?}", res);
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
pub fn poll_sync_data(&mut self) -> Poll<(), io::Error> {
::blocking_io(|| self.std().sync_data())
}
@@ -143,16 +310,71 @@ impl File {
///
/// This function will return an error if the file is not opened for
/// writing.
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
///
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .and_then(|mut file| {
/// file.poll_set_len(10)
/// })
/// .map(|res| {
/// // handle returned result ..
/// # println!("{:?}", res);
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
pub fn poll_set_len(&mut self, size: u64) -> Poll<(), io::Error> {
::blocking_io(|| self.std().set_len(size))
}
/// Queries metadata about the underlying file.
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
///
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .and_then(|file| file.metadata())
/// .map(|metadata| {
/// println!("{:?}", metadata);
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
pub fn metadata(self) -> MetadataFuture {
MetadataFuture::new(self)
}
/// Queries metadata about the underlying file.
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
///
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .and_then(|mut file| file.poll_metadata())
/// .map(|metadata| {
/// // metadata is of type Async::Ready<Metadata>
/// println!("{:?}", metadata);
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
pub fn poll_metadata(&mut self) -> Poll<Metadata, io::Error> {
::blocking_io(|| self.std().metadata())
}
@@ -160,6 +382,24 @@ impl File {
/// Create a new `File` instance that shares the same underlying file handle
/// as the existing `File` instance. Reads, writes, and seeks will affect both
/// File instances simultaneously.
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
///
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .and_then(|mut file| file.poll_try_clone())
/// .map(|clone| {
/// // do something with the clone
/// # println!("{:?}", clone);
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
pub fn poll_try_clone(&mut self) -> Poll<File, io::Error> {
::blocking_io(|| {
let std = self.std().try_clone()?;
@@ -182,6 +422,28 @@ impl File {
/// This function will return an error if the user lacks permission change
/// attributes on the underlying file. It may also return an error in other
/// os-specific unspecified cases.
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
///
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .and_then(|file| file.metadata())
/// .map(|(mut file, metadata)| {
/// let mut perms = metadata.permissions();
/// perms.set_readonly(true);
/// match file.poll_set_permissions(perms) {
/// Err(e) => eprintln!("{}", e),
/// _ => println!("permissions set!"),
/// }
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
pub fn poll_set_permissions(&mut self, perm: Permissions) -> Poll<(), io::Error> {
::blocking_io(|| self.std().set_permissions(perm))
}
@@ -193,6 +455,24 @@ impl File {
/// This function will panic if `shutdown` has been called.
///
/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
///
/// fn main() {
/// let task = tokio::fs::File::create("foo.txt")
/// .map(|file| {
/// let std_file = file.into_std();
/// // do something with the std::fs::File
/// # println!("{:?}", std_file);
/// }).map_err(|err| eprintln!("IO error: {:?}", err));
///
/// tokio::run(task);
/// }
/// ```
pub fn into_std(mut self) -> StdFile {
self.std.take().expect("`File` instance already shutdown")
}
+3 -3
View File
@@ -1,3 +1,6 @@
#![deny(missing_docs, missing_debug_implementations, warnings)]
#![doc(html_root_url = "https://docs.rs/tokio-fs/0.1.5")]
//! Asynchronous file and standard stream adaptation.
//!
//! This module contains utility methods and adapter types for input/output to
@@ -25,9 +28,6 @@
//! [`AsyncRead`]: https://docs.rs/tokio-io/0.1/tokio_io/trait.AsyncRead.html
//! [tokio-threadpool]: https://docs.rs/tokio-threadpool/0.1/tokio_threadpool
#![deny(missing_docs, missing_debug_implementations, warnings)]
#![doc(html_root_url = "https://docs.rs/tokio-fs/0.1.3")]
#[macro_use]
extern crate futures;
extern crate tokio_io;
+72
View File
@@ -0,0 +1,72 @@
extern crate futures;
extern crate tempdir;
extern crate tokio_fs;
use futures::{Future, Stream};
use std::fs;
use std::sync::{Arc, Mutex};
use tempdir::TempDir;
use tokio_fs::*;
mod pool;
#[test]
fn create() {
let base_dir = TempDir::new("base").unwrap();
let new_dir = base_dir.path().join("foo");
pool::run({ create_dir(new_dir.clone()) });
assert!(new_dir.is_dir());
}
#[test]
fn create_all() {
let base_dir = TempDir::new("base").unwrap();
let new_dir = base_dir.path().join("foo").join("bar");
pool::run({ create_dir_all(new_dir.clone()) });
assert!(new_dir.is_dir());
}
#[test]
fn remove() {
let base_dir = TempDir::new("base").unwrap();
let new_dir = base_dir.path().join("foo");
fs::create_dir(new_dir.clone()).unwrap();
pool::run({ remove_dir(new_dir.clone()) });
assert!(!new_dir.exists());
}
#[test]
fn read() {
let base_dir = TempDir::new("base").unwrap();
let p = base_dir.path();
fs::create_dir(p.join("aa")).unwrap();
fs::create_dir(p.join("bb")).unwrap();
fs::create_dir(p.join("cc")).unwrap();
let files = Arc::new(Mutex::new(Vec::new()));
let f = files.clone();
let p = p.to_path_buf();
pool::run({
read_dir(p).flatten_stream().for_each(move |e| {
let s = e.file_name().to_str().unwrap().to_string();
f.lock().unwrap().push(s);
Ok(())
})
});
let mut files = files.lock().unwrap();
files.sort(); // because the order is not guaranteed
assert_eq!(
*files,
vec!["aa".to_string(), "bb".to_string(), "cc".to_string()]
);
}
+27 -46
View File
@@ -3,40 +3,37 @@ extern crate rand;
extern crate tempfile;
extern crate tokio_fs;
extern crate tokio_io;
extern crate tokio_threadpool;
use tokio_fs::*;
use tokio_io::io;
use tokio_threadpool::*;
use futures::Future;
use futures::future::poll_fn;
use futures::sync::oneshot;
use rand::{thread_rng, Rng};
use futures::Future;
use rand::{distributions, thread_rng, Rng};
use tempfile::Builder as TmpBuilder;
use std::fs::File as StdFile;
use std::io::{Read, SeekFrom};
mod pool;
#[test]
fn read_write() {
const NUM_CHARS: usize = 16 * 1_024;
let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
let dir = TmpBuilder::new()
.prefix("tokio-fs-tests")
.tempdir()
.unwrap();
let file_path = dir.path().join("read_write.txt");
let contents: Vec<u8> = thread_rng().gen_ascii_chars()
let contents: Vec<u8> = thread_rng()
.sample_iter(&distributions::Alphanumeric)
.take(NUM_CHARS)
.collect::<String>()
.into();
let pool = Builder::new()
.pool_size(1)
.build();
let (tx, rx) = oneshot::channel();
pool.spawn({
pool::run({
let file_path = file_path.clone();
let contents = contents.clone();
@@ -44,18 +41,13 @@ fn read_write() {
.and_then(|file| file.metadata())
.inspect(|&(_, ref metadata)| assert!(metadata.is_file()))
.and_then(move |(file, _)| io::write_all(file, contents))
.and_then(|(mut file, _)| {
poll_fn(move || file.poll_sync_all())
})
.and_then(|(mut file, _)| poll_fn(move || file.poll_sync_all()))
.then(|res| {
let _ = res.unwrap();
tx.send(()).unwrap();
Ok(())
})
});
rx.wait().unwrap();
let mut file = StdFile::open(&file_path).unwrap();
let mut dst = vec![];
@@ -63,32 +55,26 @@ fn read_write() {
assert_eq!(dst, contents);
let (tx, rx) = oneshot::channel();
pool.spawn({
pool::run({
File::open(file_path)
.and_then(|file| io::read_to_end(file, vec![]))
.then(move |res| {
let (_, buf) = res.unwrap();
assert_eq!(buf, contents);
tx.send(()).unwrap();
Ok(())
})
});
rx.wait().unwrap();
}
#[test]
fn metadata() {
let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
let dir = TmpBuilder::new()
.prefix("tokio-fs-tests")
.tempdir()
.unwrap();
let file_path = dir.path().join("metadata.txt");
let pool = Builder::new().pool_size(1).build();
let (tx, rx) = oneshot::channel();
pool.spawn({
pool::run({
let file_path = file_path.clone();
let file_path2 = file_path.clone();
let file_path3 = file_path.clone();
@@ -102,23 +88,20 @@ fn metadata() {
.and_then(|_| tokio_fs::metadata(file_path3))
.then(|r| {
assert!(r.unwrap().is_file());
tx.send(())
Ok(())
})
});
rx.wait().unwrap();
}
#[test]
fn seek() {
let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
let dir = TmpBuilder::new()
.prefix("tokio-fs-tests")
.tempdir()
.unwrap();
let file_path = dir.path().join("seek.txt");
let pool = Builder::new().pool_size(1).build();
let (tx, rx) = oneshot::channel();
pool.spawn(
pool::run({
OpenOptions::new()
.create(true)
.read(true)
@@ -138,9 +121,7 @@ fn seek() {
})
.then(|r| {
let _ = r.unwrap();
tx.send(())
}),
);
rx.wait().unwrap();
Ok(())
})
});
}
+64
View File
@@ -0,0 +1,64 @@
extern crate futures;
extern crate tempdir;
extern crate tokio_fs;
use futures::Future;
use std::fs;
use std::io::prelude::*;
use std::io::BufReader;
use tempdir::TempDir;
use tokio_fs::*;
mod pool;
#[test]
fn test_hard_link() {
let dir = TempDir::new("base").unwrap();
let src = dir.path().join("src.txt");
let dst = dir.path().join("dst.txt");
{
let mut file = fs::File::create(&src).unwrap();
file.write_all(b"hello").unwrap();
}
pool::run({ hard_link(src, dst.clone()) });
let mut content = String::new();
{
let file = fs::File::open(dst).unwrap();
let mut reader = BufReader::new(file);
reader.read_to_string(&mut content).unwrap();
}
assert!(content == "hello");
}
#[cfg(unix)]
#[test]
fn test_symlink() {
let dir = TempDir::new("base").unwrap();
let src = dir.path().join("src.txt");
let dst = dir.path().join("dst.txt");
{
let mut file = fs::File::create(&src).unwrap();
file.write_all(b"hello").unwrap();
}
pool::run({ os::unix::symlink(src.clone(), dst.clone()) });
let mut content = String::new();
{
let file = fs::File::open(dst.clone()).unwrap();
let mut reader = BufReader::new(file);
reader.read_to_string(&mut content).unwrap();
}
assert!(content == "hello");
pool::run({ read_link(dst.clone()).map(move |x| assert!(x == src)) });
pool::run({ symlink_metadata(dst.clone()).map(move |x| assert!(x.file_type().is_symlink())) });
}
+17
View File
@@ -0,0 +1,17 @@
extern crate futures;
extern crate tokio_threadpool;
use self::tokio_threadpool::Builder;
use futures::sync::oneshot;
use futures::Future;
use std::io;
pub fn run<F>(f: F)
where
F: Future<Item = (), Error = io::Error> + Send + 'static,
{
let pool = Builder::new().pool_size(1).build();
let (tx, rx) = oneshot::channel::<()>();
pool.spawn(f.then(|_| tx.send(())));
rx.wait().unwrap()
}
+9
View File
@@ -1,3 +1,12 @@
# 0.1.11 (January 6, 2019)
* Fix minor error in Decoder::decode API documentation (#797).
# 0.1.10 (October 23, 2018)
* Expose inner codec from `Framed` (#686).
* Implement AsyncRead::prepare_uninitialized_buffer for Take and Chain (#678).
# 0.1.9 (September 27, 2018)
* Fix bug in `AsyncRead::split()` (#655).
+4 -2
View File
@@ -5,13 +5,15 @@ name = "tokio-io"
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Update doc URL.
# - Cargo.toml
# - Readme.md
# - Create "v0.1.x" git tag.
version = "0.1.9"
version = "0.1.11"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-io/0.1.9/tokio_io"
documentation = "https://docs.rs/tokio-io/0.1.11/tokio_io"
description = """
Core I/O primitives for asynchronous I/O in Rust.
"""
+1 -1
View File
@@ -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
+1 -1
View File
@@ -4,7 +4,7 @@ Core I/O abstractions for the Tokio stack.
[![Build Status](https://travis-ci.org/tokio-rs/tokio-io.svg?branch=master)](https://travis-ci.org/tokio-rs/tokio-io)
[Documentation](https://docs.rs/tokio-io)
[Documentation](https://docs.rs/tokio-io/0.1.11/tokio_io)
## Usage
+18
View File
@@ -97,6 +97,24 @@ impl<T, U> Framed<T, U> {
&mut self.inner.get_mut().get_mut().0
}
/// Returns a reference to the underlying codec wrapped by
/// `Frame`.
///
/// Note that care should be taken to not tamper with the underlying codec
/// as it may corrupt the stream of frames otherwise being worked with.
pub fn codec(&self) -> &U {
&self.inner.get_ref().get_ref().1
}
/// Returns a mutable reference to the underlying codec wrapped by
/// `Frame`.
///
/// Note that care should be taken to not tamper with the underlying codec
/// as it may corrupt the stream of frames otherwise being worked with.
pub fn codec_mut(&mut self) -> &mut U {
&mut self.inner.get_mut().get_mut().1
}
/// Consumes the `Frame`, returning its underlying I/O stream.
///
/// Note that care should be taken to not tamper with the underlying stream
+11 -11
View File
@@ -13,18 +13,18 @@ use split::{ReadHalf, WriteHalf};
/// **non-blocking**. All non-blocking I/O objects must return an error when
/// bytes are unavailable instead of blocking the current thread.
///
/// Specifically, this means that the `read` function will return one of the
/// following:
/// Specifically, this means that the `poll_read` function will return one of
/// the following:
///
/// * `Ok(Async::Ready(n))` means that `n` bytes of data was immediately read
/// and placed into the output buffer, where `n` == 0 implies that EOF has
/// been reached.
///
/// * `Ok(Async::NotReady)` means that no data was read into the buffer
/// * `Ok(Async::NotReady)` means that no data was read into the buffer
/// provided. The I/O object is not currently readable but may become readable
/// in the future. Most importantly, **the current future's task is scheduled
/// to get unparked when the object is readable**. This means that like
/// `Future::poll` you'll receive a notification when the I/O object is
/// to get unparked when the object is readable**. This means that like
/// `Future::poll` you'll receive a notification when the I/O object is
/// readable again.
///
/// * `Err(e)` for other errors are standard I/O errors coming from the
@@ -43,13 +43,13 @@ pub trait AsyncRead: std_io::Read {
/// zero out buffers can be expensive.
///
/// This function does any necessary work to prepare an uninitialized buffer
/// to be safe to pass to `read`. If `read` guarantees to never attempt read
/// data out of the supplied buffer, then `prepare_uninitialized_buffer`
/// to be safe to pass to `read`. If `read` guarantees to never attempt to
/// read data out of the supplied buffer, then `prepare_uninitialized_buffer`
/// doesn't need to do any work.
///
/// If this function returns `true`, then the memory has been zeroed out.
/// This allows implementations of `AsyncRead` which are composed of
/// multiple sub implementations to efficiently implement
/// multiple subimplementations to efficiently implement
/// `prepare_uninitialized_buffer`.
///
/// This function isn't actually `unsafe` to call but `unsafe` to implement.
@@ -74,7 +74,7 @@ pub trait AsyncRead: std_io::Read {
/// On success, returns `Ok(Async::Ready(num_bytes_read))`.
///
/// If no data is available for reading, the method returns
/// `Ok(Async::Pending)` and arranges for the current task (via
/// `Ok(Async::NotReady)` and arranges for the current task (via
/// `cx.waker()`) to receive a notification when the object becomes
/// readable or is closed.
fn poll_read(&mut self, buf: &mut [u8]) -> Poll<usize, std_io::Error> {
@@ -87,7 +87,7 @@ pub trait AsyncRead: std_io::Read {
}
}
/// Pull some bytes from this source into the specified `Buf`, returning
/// Pull some bytes from this source into the specified `BufMut`, returning
/// how many bytes were read.
///
/// The `buf` provided will have bytes read into it and the internal cursor
@@ -115,7 +115,7 @@ pub trait AsyncRead: std_io::Read {
}
/// Provides a `Stream` and `Sink` interface for reading and writing to this
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
/// I/O object, using `Decode` and `Encode` to read and write the raw data.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
/// wants to batch these into meaningful chunks, called "frames". This
+14 -9
View File
@@ -10,8 +10,8 @@ use AsyncRead;
/// **nonblocking**. All non-blocking I/O objects must return an error when
/// bytes cannot be written instead of blocking the current thread.
///
/// Specifically, this means that the `write` function will return one of the
/// following:
/// Specifically, this means that the `poll_write` function will return one of
/// the following:
///
/// * `Ok(Async::Ready(n))` means that `n` bytes of data was immediately
/// written.
@@ -19,7 +19,7 @@ use AsyncRead;
/// * `Ok(Async::NotReady)` means that no data was written from the buffer
/// provided. The I/O object is not currently writable but may become writable
/// in the future. Most importantly, **the current future's task is scheduled
/// to get unparked when the object is readable**. This means that like
/// to get unparked when the object is writable**. This means that like
/// `Future::poll` you'll receive a notification when the I/O object is
/// writable again.
///
@@ -40,7 +40,7 @@ pub trait AsyncWrite: std_io::Write {
/// On success, returns `Ok(Async::Ready(num_bytes_written))`.
///
/// If the object is not ready for writing, the method returns
/// `Ok(Async::Pending)` and arranges for the current task (via
/// `Ok(Async::NotReady)` and arranges for the current task (via
/// `cx.waker()`) to receive a notification when the object becomes
/// readable or is closed.
fn poll_write(&mut self, buf: &[u8]) -> Poll<usize, std_io::Error> {
@@ -59,7 +59,7 @@ pub trait AsyncWrite: std_io::Write {
/// On success, returns `Ok(Async::Ready(()))`.
///
/// If flushing cannot immediately complete, this method returns
/// `Ok(Async::Pending)` and arranges for the current task (via
/// `Ok(Async::NotReady)` and arranges for the current task (via
/// `cx.waker()`) to receive a notification when the object can make
/// progress towards flushing.
fn poll_flush(&mut self) -> Poll<(), std_io::Error> {
@@ -172,17 +172,22 @@ impl AsyncWrite for std_io::Sink {
}
}
// TODO: Implement `prepare_uninitialized_buffer` for `io::Take`.
// This is blocked on rust-lang/rust#27269
impl<T: AsyncRead> AsyncRead for std_io::Take<T> {
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
self.get_ref().prepare_uninitialized_buffer(buf)
}
}
// TODO: Implement `prepare_uninitialized_buffer` when upstream exposes inner
// parts
impl<T, U> AsyncRead for std_io::Chain<T, U>
where T: AsyncRead,
U: AsyncRead,
{
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
let (t, u) = self.get_ref();
// We don't need to execute the second initializer if the first one
// already zeroed the buffer out.
t.prepare_uninitialized_buffer(buf) || u.prepare_uninitialized_buffer(buf)
}
}
impl<T: AsyncWrite> AsyncWrite for std_io::BufWriter<T> {
+1 -1
View File
@@ -57,7 +57,7 @@ pub trait Decoder {
///
/// Note that the bytes provided may be empty. If a previous call to
/// `decode` consumed all the bytes in the buffer then `decode` will be
/// called again until it returns `None`, indicating that more bytes need to
/// called again until it returns `Ok(None)`, indicating that more bytes need to
/// be read.
///
/// Finally, if the bytes in the buffer are malformed then an error is
+1 -1
View File
@@ -46,7 +46,7 @@ pub mod length_delimited {
//! # Getting started
//!
//! If implementing a protocol from scratch, using length delimited framing
//! is an easy way to get started. [`Framed::new()`] will adapt a
//! is an easy way to get started. [`Framed::new()`](length_delimited::Framed::new) will adapt a
//! full-duplex byte stream with a length delimited framer using default
//! configuration values.
//!
+1 -1
View File
@@ -1,5 +1,5 @@
#![deny(missing_docs, missing_debug_implementations, warnings)]
#![doc(html_root_url = "https://docs.rs/tokio-io/0.1.9")]
#![doc(html_root_url = "https://docs.rs/tokio-io/0.1.11")]
//! Core I/O traits and combinators when working with Tokio.
//!
+3
View File
@@ -1,3 +1,6 @@
// This file is testing deprecated code.
#![allow(deprecated)]
extern crate tokio_io;
extern crate futures;
+10
View File
@@ -1,3 +1,13 @@
# 0.1.8 (January 6, 2019)
* Update to `parking_lot` 0.7 (#778).
* Deprecate `Handle::current()` (#805).
# 0.1.7 (November 21, 2018)
* Reduce log level to trace (#734).
* Bump internal dependency versions (#746).
# 0.1.6 (September 27, 2018)
* Fix panic when reactor is stored in a thread-local (#628).
+7 -4
View File
@@ -5,27 +5,29 @@ name = "tokio-reactor"
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Update doc URL.
# - Cargo.toml
# - README.md
# - Create "v0.1.x" git tag.
version = "0.1.6"
version = "0.1.8"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-reactor/0.1.6/tokio_reactor"
documentation = "https://docs.rs/tokio-reactor/0.1.8/tokio_reactor"
description = """
Event loop that drives Tokio I/O resources.
"""
categories = ["asynchronous", "network-programming"]
[dependencies]
crossbeam-utils = "0.5.0"
crossbeam-utils = "0.6.0"
futures = "0.1.19"
lazy_static = "1.0.2"
log = "0.4.1"
mio = "0.6.14"
num_cpus = "1.8.0"
parking_lot = "0.6.3"
parking_lot = "0.7.0"
slab = "0.4.0"
tokio-executor = { version = "0.1.1", path = "../tokio-executor" }
tokio-io = { version = "0.1.6", path = "../tokio-io" }
@@ -33,3 +35,4 @@ tokio-io = { version = "0.1.6", path = "../tokio-io" }
[dev-dependencies]
num_cpus = "1.8.0"
tokio = { version = "0.1.7", path = ".." }
tokio-io-pool = "0.1.4"
+1 -1
View File
@@ -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
+1 -1
View File
@@ -2,7 +2,7 @@
Event loop that drives Tokio I/O resources.
[Documentation](https://tokio-rs.github.io/tokio/tokio_reactor/)
[Documentation](https://docs.rs/tokio-reactor/0.1.8/tokio_reactor)
## Overview
+117 -39
View File
@@ -6,56 +6,134 @@ extern crate mio;
extern crate num_cpus;
extern crate test;
extern crate tokio;
extern crate tokio_io_pool;
extern crate tokio_reactor;
use std::sync::mpsc;
const NUM_YIELD: usize = 500;
const TASKS_PER_CPU: usize = 100;
use futures::{future, Async};
use self::test::Bencher;
use tokio_reactor::Registration;
mod threadpool {
use super::*;
use std::sync::mpsc;
const NUM_YIELD: usize = 1_000;
const TASKS_PER_CPU: usize = 50;
use test::Bencher;
use futures::{future, Async};
use tokio_reactor::Registration;
use tokio::runtime::Runtime;
#[bench]
fn notify_many(b: &mut Bencher) {
let mut rt = tokio::runtime::Runtime::new().unwrap();
#[bench]
fn notify_many(b: &mut Bencher) {
let mut rt = Runtime::new().unwrap();
let tasks = TASKS_PER_CPU * num_cpus::get();
let tasks = TASKS_PER_CPU * num_cpus::get();
let (tx, rx) = mpsc::channel();
b.iter(|| {
let (tx, rx) = mpsc::channel();
b.iter(|| {
for _ in 0..tasks {
let (r, s) = mio::Registration::new2();
let registration = Registration::new();
registration.register(&r).unwrap();
rt.block_on::<_, (), ()>(future::lazy(move || {
for _ in 0..tasks {
let tx = tx.clone();
let mut rem = NUM_YIELD;
let mut r = Some(r);
let tx = tx.clone();
tokio::spawn(future::lazy(move || {
let (r, s) = mio::Registration::new2();
let registration = Registration::new();
registration.register(&r).unwrap();
rt.spawn(future::poll_fn(move || {
loop {
let is_ready = registration.poll_read_ready().unwrap().is_ready();
let mut rem = NUM_YIELD;
let mut r = Some(r);
let tx = tx.clone();
if is_ready {
rem -= 1;
tokio::spawn(future::poll_fn(move || {
loop {
let is_ready = registration.poll_read_ready().unwrap().is_ready();
if rem == 0 {
r.take().unwrap();
tx.send(()).unwrap();
return Ok(Async::Ready(()));
}
} else {
s.set_readiness(mio::Ready::readable()).unwrap();
return Ok(Async::NotReady);
}
if is_ready {
rem -= 1;
if rem == 0 {
r.take().unwrap();
tx.send(()).unwrap();
return Ok(Async::Ready(()));
}
} else {
s.set_readiness(mio::Ready::readable()).unwrap();
return Ok(Async::NotReady);
}
}
}));
Ok(())
}));
}
}));
}
for _ in 0..tasks {
rx.recv().unwrap();
}
});
Ok(())
})).unwrap();
for _ in 0..tasks {
rx.recv().unwrap();
}
})
}
}
mod io_pool {
use super::*;
use std::sync::mpsc;
use futures::{future, Async};
use test::Bencher;
use tokio_io_pool::Runtime;
use tokio_reactor::Registration;
#[bench]
fn notify_many(b: &mut Bencher) {
let mut rt = Runtime::new();
let tasks = TASKS_PER_CPU * num_cpus::get();
b.iter(|| {
let (tx, rx) = mpsc::channel();
rt.block_on::<_, (), ()>(future::lazy(move || {
for _ in 0..tasks {
let tx = tx.clone();
tokio::spawn(future::lazy(move || {
let (r, s) = mio::Registration::new2();
let registration = Registration::new();
registration.register(&r).unwrap();
let mut rem = NUM_YIELD;
let mut r = Some(r);
let tx = tx.clone();
tokio::spawn(future::poll_fn(move || {
loop {
let is_ready = registration.poll_read_ready().unwrap().is_ready();
if is_ready {
rem -= 1;
if rem == 0 {
r.take().unwrap();
tx.send(()).unwrap();
return Ok(Async::Ready(()));
}
} else {
s.set_readiness(mio::Ready::readable()).unwrap();
return Ok(Async::NotReady);
}
}
}));
Ok(())
}));
}
Ok(())
})).unwrap();
for _ in 0..tasks {
rx.recv().unwrap();
}
})
}
}
+4 -3
View File
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/tokio-reactor/0.1.6")]
#![doc(html_root_url = "https://docs.rs/tokio-reactor/0.1.8")]
#![deny(missing_docs, warnings, missing_debug_implementations)]
//! Event loop that drives Tokio I/O resources.
@@ -376,7 +376,7 @@ impl Reactor {
if let Some(start) = start {
let dur = start.elapsed();
debug!("loop process - {} events, {}.{:03}s",
trace!("loop process - {} events, {}.{:03}s",
events,
dur.as_secs(),
dur.subsec_nanos() / 1_000_000);
@@ -455,7 +455,8 @@ impl fmt::Debug for Reactor {
// ===== impl Handle =====
impl Handle {
/// Returns a handle to the current reactor.
#[doc(hidden)]
#[deprecated(note = "semantics were sometimes surprising, use Handle::default()")]
pub fn current() -> Handle {
// TODO: Should this panic on error?
HandlePriv::try_current()

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