mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
61
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dba5c27296 | ||
|
|
db620b42ec | ||
|
|
9013ed9bd4 | ||
|
|
06325fa63b | ||
|
|
0d41ba7a08 | ||
|
|
c07a7b26d3 | ||
|
|
f723d10087 | ||
|
|
3d7263d3a0 | ||
|
|
9caec1c15d | ||
|
|
703f07ca17 | ||
|
|
db9371126d | ||
|
|
eb1cf8fc9b | ||
|
|
4af6109398 | ||
|
|
96f3ec903c | ||
|
|
8c791fd0bf | ||
|
|
c0747a5fc1 | ||
|
|
c8e710d39e | ||
|
|
e281e4f4cb | ||
|
|
6598334021 | ||
|
|
35f3351c97 | ||
|
|
1f5bb121e2 | ||
|
|
88801bb613 | ||
|
|
a850063211 | ||
|
|
14ec268b8a | ||
|
|
363b207f2b | ||
|
|
06b2c40222 | ||
|
|
68b82f5721 | ||
|
|
7cca6499a9 | ||
|
|
8235eefbf0 | ||
|
|
14b31bdba5 | ||
|
|
f768163982 | ||
|
|
7a2b5db15c | ||
|
|
2465483845 | ||
|
|
6a0ecef81a | ||
|
|
6defeeb2ba | ||
|
|
b36a73059d | ||
|
|
d1d4fe4d07 | ||
|
|
9aaa8f06d1 | ||
|
|
6ea00162b9 | ||
|
|
61d635e8ad | ||
|
|
372400ed34 | ||
|
|
ba9d849ef0 | ||
|
|
5b677934fe | ||
|
|
dbcd8353b0 | ||
|
|
3be6b69e1b | ||
|
|
0bcf9b0ae6 | ||
|
|
c715739599 | ||
|
|
6aea9c43e8 | ||
|
|
82f6a52d1a | ||
|
|
a6b307cfbe | ||
|
|
dcb20b289c | ||
|
|
79afc7ee68 | ||
|
|
3ba5595233 | ||
|
|
7232ba6d55 | ||
|
|
a14de909eb | ||
|
|
d8789cd379 | ||
|
|
8d4be0361e | ||
|
|
3f2710397d | ||
|
|
8895a7d3ab | ||
|
|
10cb9dd468 | ||
|
|
2ca214bd2c |
+5
-2
@@ -1,11 +1,14 @@
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
platform: x64
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
platform: x86
|
||||
|
||||
install:
|
||||
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- rustup-init.exe -y --default-host x86_64-pc-windows-msvc
|
||||
- rustup-init.exe -y --default-host %TARGET%
|
||||
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
|
||||
- if NOT "%TARGET%" == "x86_64-pc-windows-msvc" rustup target add %TARGET%
|
||||
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
|
||||
+26
-6
@@ -1,6 +1,14 @@
|
||||
---
|
||||
language: rust
|
||||
sudo: false
|
||||
cache:
|
||||
- apt
|
||||
- cargo
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
# to x-compile miniz-sys from sources
|
||||
- gcc-multilib
|
||||
|
||||
matrix:
|
||||
include:
|
||||
@@ -9,31 +17,43 @@ matrix:
|
||||
# releases prior to the current stable.
|
||||
- rust: 1.21.0
|
||||
- rust: stable
|
||||
- os: osx
|
||||
- rust: beta
|
||||
- rust: nightly
|
||||
- os: osx
|
||||
- env: TARGET=x86_64-unknown-freebsd
|
||||
- env: TARGET=i686-unknown-freebsd
|
||||
- env: TARGET=i686-unknown-linux-gnu
|
||||
|
||||
script:
|
||||
- |
|
||||
set -e
|
||||
if [[ "$TRAVIS_RUST_VERSION" == nightly ]]
|
||||
then
|
||||
# Pin the nightly version until rust-lang/rust#49436 is resolved.
|
||||
rustup override set nightly-2018-03-26
|
||||
|
||||
# Make sure the benchmarks compile
|
||||
cargo build --benches --all
|
||||
|
||||
export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0"
|
||||
export TSAN_OPTIONS="suppressions=`pwd`/ci/tsan"
|
||||
|
||||
# === tokio-timer ====
|
||||
|
||||
# Run address sanitizer
|
||||
ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0" \
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test -p tokio-timer --test hammer --target x86_64-unknown-linux-gnu
|
||||
|
||||
# Run thread sanitizer
|
||||
TSAN_OPTIONS="suppressions=`pwd`/ci/tsan" \
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
cargo test -p tokio-timer --test hammer --target x86_64-unknown-linux-gnu
|
||||
|
||||
# === tokio-threadpool ====
|
||||
|
||||
# Run address sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test -p tokio-threadpool --tests
|
||||
|
||||
# Run thread sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
cargo test -p tokio-threadpool --tests
|
||||
fi
|
||||
- |
|
||||
set -e
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# 0.1.7 (June 6, 2018)
|
||||
|
||||
* Add `Runtime::block_on` for concurrent runtime (#391).
|
||||
* Provide handle to `current_thread::Runtime` that allows spawning tasks from
|
||||
other threads (#340).
|
||||
* Provide `clock::now()`, a configurable source of time (#381).
|
||||
|
||||
# 0.1.6 (May 2, 2018)
|
||||
|
||||
* Add asynchronous filesystem APIs (#323).
|
||||
* Add "current thread" runtime variant (#308).
|
||||
* `CurrentThread`: Expose inner `Park` instance.
|
||||
* Improve fairness of `CurrentThread` executor (#313).
|
||||
|
||||
# 0.1.5 (March 30, 2018)
|
||||
|
||||
* Provide timer API (#266)
|
||||
|
||||
+11
-24
@@ -5,7 +5,7 @@ name = "tokio"
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.4"
|
||||
version = "0.1.7"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
@@ -23,37 +23,38 @@ keywords = ["io", "async", "non-blocking", "futures"]
|
||||
|
||||
members = [
|
||||
"./",
|
||||
"tokio-codec",
|
||||
"tokio-executor",
|
||||
"tokio-fs",
|
||||
"tokio-io",
|
||||
"tokio-reactor",
|
||||
"tokio-threadpool",
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
"tokio-udp",
|
||||
"futures2",
|
||||
"tokio-uds",
|
||||
]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "tokio-rs/tokio" }
|
||||
appveyor = { repository = "carllerche/tokio" }
|
||||
appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" }
|
||||
|
||||
[dependencies]
|
||||
tokio-codec = { version = "0.1.0", path = "tokio-codec" }
|
||||
tokio-io = { version = "0.1.6", path = "tokio-io" }
|
||||
tokio-executor = { version = "0.1.1", path = "tokio-executor" }
|
||||
tokio-executor = { version = "0.1.2", path = "tokio-executor" }
|
||||
tokio-reactor = { version = "0.1.1", path = "tokio-reactor" }
|
||||
tokio-threadpool = { version = "0.1.1", path = "tokio-threadpool" }
|
||||
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.0", path = "tokio-timer" }
|
||||
tokio-timer = { version = "0.2.4", path = "tokio-timer" }
|
||||
tokio-fs = { version = "0.1.0", path = "tokio-fs" }
|
||||
|
||||
futures = "0.1.19"
|
||||
futures = "0.1.20"
|
||||
|
||||
# Needed until `reactor` is removed from `tokio`.
|
||||
mio = "0.6.14"
|
||||
|
||||
# Futures 0.2 integration
|
||||
futures2 = { version = "0.1.0", path = "futures2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
bytes = "0.4"
|
||||
env_logger = { version = "0.4", default-features = false }
|
||||
@@ -67,17 +68,3 @@ serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
time = "0.1"
|
||||
|
||||
[patch.crates-io]
|
||||
tokio-io = { path = "tokio-io" }
|
||||
|
||||
[features]
|
||||
unstable-futures = [
|
||||
"futures2",
|
||||
"tokio-reactor/unstable-futures",
|
||||
"tokio-threadpool/unstable-futures",
|
||||
"tokio-executor/unstable-futures",
|
||||
"tokio-tcp/unstable-futures",
|
||||
"tokio-udp/unstable-futures"
|
||||
]
|
||||
default = []
|
||||
|
||||
@@ -16,6 +16,7 @@ the Rust programming language. It is:
|
||||
[![MIT licensed][mit-badge]][mit-url]
|
||||
[![Travis Build Status][travis-badge]][travis-url]
|
||||
[![Appveyor Build Status][appveyor-badge]][appveyor-url]
|
||||
[![Gitter chat][gitter-badge]][gitter-url]
|
||||
|
||||
[crates-badge]: https://img.shields.io/crates/v/tokio.svg
|
||||
[crates-url]: https://crates.io/crates/tokio
|
||||
@@ -25,10 +26,13 @@ the Rust programming language. It is:
|
||||
[travis-url]: https://travis-ci.org/tokio-rs/tokio
|
||||
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/s83yxhy9qeb58va7/branch/master?svg=true
|
||||
[appveyor-url]: https://ci.appveyor.com/project/carllerche/tokio/branch/master
|
||||
[gitter-badge]: https://img.shields.io/gitter/room/tokio-rs/tokio.svg
|
||||
[gitter-url]: https://gitter.im/tokio-rs/tokio
|
||||
|
||||
[Website](https://tokio.rs) |
|
||||
[Guides](https://tokio.rs/docs/getting-started/hello-world/) |
|
||||
[API Docs](https://docs.rs/tokio)
|
||||
[API Docs](https://docs.rs/tokio) |
|
||||
[Chat](https://gitter.im/tokio-rs/tokio)
|
||||
|
||||
The API docs for the master branch are published [here][master-dox].
|
||||
|
||||
@@ -109,24 +113,34 @@ The crates included as part of Tokio are:
|
||||
|
||||
* [`tokio-executor`]: Task execution related traits and utilities.
|
||||
|
||||
* [`tokio-fs`]: Filesystem (and standard in / out) APIs.
|
||||
|
||||
* [`tokio-io`]: Asynchronous I/O related traits and utilities.
|
||||
|
||||
* [`tokio-reactor`]: Event loop that drives I/O resources (like TCP and UDP
|
||||
sockets).
|
||||
|
||||
* [`tokio-tcp`]: TCP bindings for use with `tokio-io` and `tokio-reactor`.
|
||||
|
||||
* [`tokio-threadpool`]: Schedules the execution of futures across a pool of
|
||||
threads.
|
||||
|
||||
* [`tokio-tcp`]: TCP bindings for use with `tokio-io` and `tokio-reactor`.
|
||||
* [ `tokio-timer`]: Time related APIs.
|
||||
|
||||
* [`tokio-udp`]: UDP bindings for use with `tokio-io` and `tokio-reactor`.
|
||||
|
||||
* [`tokio-uds`]: Unix Domain Socket bindings for use with `tokio-io` and
|
||||
`tokio-reactor`.
|
||||
|
||||
[`tokio-executor`]: tokio-executor
|
||||
[`tokio-fs`]: tokio-fs
|
||||
[`tokio-io`]: tokio-io
|
||||
[`tokio-reactor`]: tokio-reactor
|
||||
[`tokio-threadpool`]: tokio-threadpool
|
||||
[`tokio-tcp`]: tokio-tcp
|
||||
[`tokio-threadpool`]: tokio-threadpool
|
||||
[`tokio-timer`]: tokio-timer
|
||||
[`tokio-udp`]: tokio-udp
|
||||
[`tokio-uds`]: tokio-uds
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -3,3 +3,31 @@
|
||||
# TSAN does not understand fences and `Arc::drop` is implemented using a fence.
|
||||
# This causes many false positives.
|
||||
race:Arc*drop
|
||||
race:arc*Weak*drop
|
||||
|
||||
# `std` mpsc is not used in any Tokio code base. This race is triggered by some
|
||||
# rust runtime logic.
|
||||
race:std*mpsc_queue
|
||||
|
||||
# Probably more fences in std.
|
||||
race:__call_tls_dtors
|
||||
|
||||
# The crossbeam deque uses fences.
|
||||
race:crossbeam_deque
|
||||
|
||||
# This is excluded as this race shows up due to using the stealing features of
|
||||
# the deque. Unfortunately, the implementation uses a fence, which makes tsan
|
||||
# unhappy.
|
||||
#
|
||||
# TODO: It would be nice to not have to filter this out.
|
||||
race:try_steal_task
|
||||
|
||||
# This filters out expected data race in the treiber stack implementations.
|
||||
# Treiber stacks are inherently racy. The pop operation will attempt to access
|
||||
# the "next" pointer on the node it is attempting to pop. However, at this
|
||||
# point it has not gained ownership of the node and another thread might beat
|
||||
# it and take ownership of the node first (touching the next pointer). The
|
||||
# original pop operation will fail due to the ABA guard, but tsan still picks
|
||||
# up the access on the next pointer.
|
||||
race:Backup::next_sleeper
|
||||
race:WorkerEntry::set_next_sleeper
|
||||
|
||||
+7
-1
@@ -19,6 +19,10 @@ A high level description of each example is:
|
||||
connections and then echos back any contents that are read from each connected
|
||||
client.
|
||||
|
||||
* [`print_each_packet`](print_each_packet.rs) - this server will create a TCP
|
||||
listener, accept connections in a loop, and put down in the stdout everything
|
||||
that's read off of each TCP connection.
|
||||
|
||||
* [`echo-udp`](echo-udp.rs) - again your standard "echo server", except for UDP
|
||||
instead of TCP. This will echo back any packets received to the original
|
||||
sender.
|
||||
@@ -34,7 +38,7 @@ A high level description of each example is:
|
||||
in multiple terminals and use it to chat between the terminals.
|
||||
|
||||
* [`chat-combinator`](chat-combinator.rs) - Similar to `chat`, but this uses a
|
||||
much more functional programming approch using combinators.
|
||||
much more functional programming approach using combinators.
|
||||
|
||||
* [`proxy`](proxy.rs) - an example proxy server that will forward all connected
|
||||
TCP clients to the remote address specified when starting the program.
|
||||
@@ -49,6 +53,8 @@ A high level description of each example is:
|
||||
|
||||
* [`udp-client`](udp-client.rs) - a simple `send_dgram`/`recv_dgram` example.
|
||||
|
||||
* [`manual-runtime`](manual-runtime.rs) - manually composing a runtime.
|
||||
|
||||
If you've got an example you'd like to see here, please feel free to open an
|
||||
issue. Otherwise if you've got an example you'd like to add, please feel free
|
||||
to make a PR!
|
||||
|
||||
+5
-5
@@ -4,7 +4,7 @@
|
||||
//! illustrate more concepts.
|
||||
//!
|
||||
//! A chat server for telnet clients. After a telnet client connects, the first
|
||||
//! line should contain the client's name. After that, all lines send by a
|
||||
//! line should contain the client's name. After that, all lines sent by a
|
||||
//! client are broadcasted to all other connected clients.
|
||||
//!
|
||||
//! Because the client is telnet, lines are delimited by "\r\n".
|
||||
@@ -157,7 +157,7 @@ impl Peer {
|
||||
|
||||
/// This is where a connected client is managed.
|
||||
///
|
||||
/// A `Peer` is also a future representing completly processing the client.
|
||||
/// A `Peer` is also a future representing completely processing the client.
|
||||
///
|
||||
/// When a `Peer` is created, the first line (representing the client's name)
|
||||
/// has already been read. When the socket closes, the `Peer` future completes.
|
||||
@@ -216,9 +216,9 @@ impl Future for Peer {
|
||||
if let Some(message) = line {
|
||||
// Append the peer's name to the front of the line:
|
||||
let mut line = self.name.clone();
|
||||
line.put(": ");
|
||||
line.put(&message);
|
||||
line.put("\r\n");
|
||||
line.extend_from_slice(b": ");
|
||||
line.extend_from_slice(&message);
|
||||
line.extend_from_slice(b"\r\n");
|
||||
|
||||
// We're using `Bytes`, which allows zero-copy clones (by
|
||||
// storing the data in an Arc internally).
|
||||
|
||||
+4
-2
@@ -17,6 +17,7 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
extern crate futures;
|
||||
extern crate bytes;
|
||||
@@ -82,7 +83,7 @@ fn main() {
|
||||
mod codec {
|
||||
use std::io;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
use tokio_codec::{Encoder, Decoder};
|
||||
|
||||
/// A simple `Codec` implementation that just ships bytes around.
|
||||
///
|
||||
@@ -120,6 +121,7 @@ mod codec {
|
||||
|
||||
mod tcp {
|
||||
use tokio;
|
||||
use tokio_codec::Decoder;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
|
||||
@@ -151,7 +153,7 @@ mod tcp {
|
||||
// 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 (sink, stream) = stream.framed(Bytes).split();
|
||||
let (sink, stream) = Bytes.framed(stream).split();
|
||||
|
||||
tokio::spawn(stdin.forward(sink).then(|result| {
|
||||
if let Err(e) = result {
|
||||
|
||||
@@ -68,6 +68,6 @@ fn main() {
|
||||
// `map_err` handles the error by logging it and maps the future to a type
|
||||
// that can be spawned.
|
||||
//
|
||||
// `tokio::run` spanws the task on the Tokio runtime and starts running.
|
||||
// `tokio::run` spawns the task on the Tokio runtime and starts running.
|
||||
tokio::run(server.map_err(|e| println!("server error = {:?}", e)));
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
//! This server will create a TCP listener, accept connections in a loop, and
|
||||
//! write back everything that's read off of each TCP connection.
|
||||
//!
|
||||
//! Because the Tokio runtime uses a thread poool, each TCP connection is
|
||||
//! Because the Tokio runtime uses a thread pool, each TCP connection is
|
||||
//! processed concurrently with all other TCP connections across multiple
|
||||
//! threads.
|
||||
//!
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
//! An example how to manually assemble a runtime and run some tasks on it.
|
||||
//!
|
||||
//! This is closer to the single-threaded runtime than the default tokio one, as it is simpler to
|
||||
//! grasp. There are conceptually similar, but the multi-threaded one would be more code. If you
|
||||
//! just want to *use* a single-threaded runtime, use the one provided by tokio directly
|
||||
//! (`tokio::runtime::current_thread::Runtime::new()`. This is a demonstration only.
|
||||
//!
|
||||
//! Note that the error handling is a bit left out. Also, the `run` could be modified to return the
|
||||
//! result of the provided future.
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_reactor;
|
||||
extern crate tokio_timer;
|
||||
|
||||
use std::io::Error as IoError;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use futures::{future, Future};
|
||||
use tokio::executor::current_thread::{self, CurrentThread};
|
||||
use tokio_reactor::Reactor;
|
||||
use tokio_timer::timer::{self, Timer};
|
||||
|
||||
/// Creates a „runtime“.
|
||||
///
|
||||
/// This is similar to running `tokio::runtime::current_thread::Runtime::new()`.
|
||||
fn run<F: Future<Item = (), Error = ()>>(f: F) -> Result<(), IoError> {
|
||||
// We need a reactor to receive events about IO objects from kernel
|
||||
let reactor = Reactor::new()?;
|
||||
let reactor_handle = reactor.handle();
|
||||
// Place a timer wheel on top of the reactor. If there are no timeouts to fire, it'll let the
|
||||
// reactor pick up some new external events.
|
||||
let timer = Timer::new(reactor);
|
||||
let timer_handle = timer.handle();
|
||||
// And now put a single-threaded executor on top of the timer. When there are no futures ready
|
||||
// to do something, it'll let the timer or the reactor generate some new stimuli for the
|
||||
// futures to continue in their life.
|
||||
let mut executor = CurrentThread::new_with_park(timer);
|
||||
// Binds an executor to this thread
|
||||
let mut enter = tokio_executor::enter().expect("Multiple executors at once");
|
||||
// This will set the default handle and timer to use inside the closure and run the future.
|
||||
tokio_reactor::with_default(&reactor_handle, &mut enter, |enter| {
|
||||
timer::with_default(&timer_handle, enter, |enter| {
|
||||
// The TaskExecutor is a fake executor that looks into the current single-threaded
|
||||
// executor when used. This is a trick, because we need two mutable references to the
|
||||
// executor (one to run the provided future, another to install as the default one). We
|
||||
// use the fake one here as the default one.
|
||||
let mut default_executor = current_thread::TaskExecutor::current();
|
||||
tokio_executor::with_default(&mut default_executor, enter, |enter| {
|
||||
let mut executor = executor.enter(enter);
|
||||
// Run the provided future
|
||||
executor.block_on(f).unwrap();
|
||||
// Run all the other futures that are still left in the executor
|
||||
executor.run().unwrap();
|
||||
});
|
||||
});
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
run(future::lazy(|| {
|
||||
// Here comes the application logic. It can spawn further tasks by current_thread::spawn().
|
||||
// It also can use the default reactor and create timeouts.
|
||||
|
||||
// Connect somewhere. And then do nothing with it. Yes, useless.
|
||||
//
|
||||
// This will use the default reactor which runs in the current thread.
|
||||
let connect = tokio::net::TcpStream::connect(&"127.0.0.1:53".parse().unwrap())
|
||||
.map(|_| println!("Connected"))
|
||||
.map_err(|e| println!("Failed to connect: {}", e));
|
||||
// We can spawn it without requiring Send. This would panic if we run it outside of the
|
||||
// `run` (or outside of anything else)
|
||||
current_thread::spawn(connect);
|
||||
|
||||
// We can also create timeouts.
|
||||
let deadline = tokio::timer::Delay::new(Instant::now() + Duration::from_secs(5))
|
||||
.map(|()| println!("5 seconds are over"))
|
||||
.map_err(|e| println!("Failed to wait: {}", e));
|
||||
// We can spawn on the default executor, which is also the local one.
|
||||
tokio::executor::spawn(deadline);
|
||||
Ok(())
|
||||
})).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
//! A "print-each-packet" server with Tokio
|
||||
//!
|
||||
//! This server will create a TCP listener, accept connections in a loop, and
|
||||
//! put down in the stdout everything that's read off of each TCP connection.
|
||||
//!
|
||||
//! Because the Tokio runtime uses a thread pool, each TCP connection is
|
||||
//! processed concurrently with all other TCP connections across multiple
|
||||
//! threads.
|
||||
//!
|
||||
//! To see this server in action, you can run this in one terminal:
|
||||
//!
|
||||
//! cargo run --example print\_each\_packet
|
||||
//!
|
||||
//! and in another terminal you can run:
|
||||
//!
|
||||
//! cargo run --example connect 127.0.0.1:8080
|
||||
//!
|
||||
//! Each line you type in to the `connect` terminal should be written to terminal!
|
||||
//!
|
||||
//! Minimal js example:
|
||||
//!
|
||||
//! ```js
|
||||
//! var net = require("net");
|
||||
//!
|
||||
//! var listenPort = 8080;
|
||||
//!
|
||||
//! var server = net.createServer(function (socket) {
|
||||
//! socket.on("data", function (bytes) {
|
||||
//! console.log("bytes", bytes);
|
||||
//! });
|
||||
//!
|
||||
//! socket.on("end", function() {
|
||||
//! console.log("Socket received FIN packet and closed connection");
|
||||
//! });
|
||||
//! socket.on("error", function (error) {
|
||||
//! console.log("Socket closed with error", error);
|
||||
//! });
|
||||
//!
|
||||
//! socket.on("close", function (with_error) {
|
||||
//! if (with_error) {
|
||||
//! console.log("Socket closed with result: Err(SomeError)");
|
||||
//! } else {
|
||||
//! console.log("Socket closed with result: Ok(())");
|
||||
//! }
|
||||
//! });
|
||||
//!
|
||||
//! });
|
||||
//!
|
||||
//! server.listen(listenPort);
|
||||
//!
|
||||
//! console.log("Listening on:", listenPort);
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
|
||||
use tokio_codec::{Decoder, BytesCodec};
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
fn main() {
|
||||
// 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();
|
||||
|
||||
// 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();
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
// Here we convert the `TcpListener` to a stream of incoming connections
|
||||
// with the `incoming` method. We then define how to process each element in
|
||||
// the stream with the `for_each` method.
|
||||
//
|
||||
// This combinator, defined on the `Stream` trait, will allow us to define a
|
||||
// computation to happen for all items on the stream (in this case TCP
|
||||
// connections made to the server). The return value of the `for_each`
|
||||
// method is itself a future representing processing the entire stream of
|
||||
// connections, and ends up being our server.
|
||||
let done = socket
|
||||
.incoming()
|
||||
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
|
||||
.for_each(move |socket| {
|
||||
// Once we're inside this closure this represents an accepted client
|
||||
// from our server. The `socket` is the client connection (similar to
|
||||
// how the standard library operates).
|
||||
//
|
||||
// We're parsing each socket with the `BytesCodec` included in `tokio_io`,
|
||||
// and then we `split` each codec into the reader/writer halves.
|
||||
//
|
||||
// See https://docs.rs/tokio-codec/0.1/src/tokio_codec/bytes_codec.rs.html
|
||||
let framed = BytesCodec::new().framed(socket);
|
||||
let (_writer, reader) = framed.split();
|
||||
|
||||
let processor = reader
|
||||
.for_each(|bytes| {
|
||||
println!("bytes: {:?}", bytes);
|
||||
Ok(())
|
||||
})
|
||||
// After our copy operation is complete we just print out some helpful
|
||||
// information.
|
||||
.and_then(|()| {
|
||||
println!("Socket received FIN packet and closed connection");
|
||||
Ok(())
|
||||
})
|
||||
.or_else(|err| {
|
||||
println!("Socket closed with error: {:?}", err);
|
||||
// We have to return the error to catch it in the next ``.then` call
|
||||
Err(err)
|
||||
})
|
||||
.then(|result| {
|
||||
println!("Socket closed with result: {:?}", result);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// And this is where much of the magic of this server happens. We
|
||||
// crucially want all clients to make progress concurrently, rather than
|
||||
// blocking one on completion of another. To achieve this we use the
|
||||
// `tokio::spawn` function to execute the work in the background.
|
||||
//
|
||||
// This function will transfer ownership of the future (`msg` in this
|
||||
// case) to the Tokio runtime thread pool that. The thread pool will
|
||||
// drive the future to completion.
|
||||
//
|
||||
// Essentially here we're executing a new task to run concurrently,
|
||||
// which will allow all of our clients to be processed concurrently.
|
||||
tokio::spawn(processor)
|
||||
});
|
||||
|
||||
// And finally now that we've define what our server is, we run it!
|
||||
//
|
||||
// This starts the Tokio runtime, spawns the server task, and blocks the
|
||||
// current thread until all tasks complete execution. Since the `done` task
|
||||
// never completes (it just keeps accepting sockets), `tokio::run` blocks
|
||||
// forever (until ctrl-c is pressed).
|
||||
tokio::run(done);
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
//! A proxy that forwards data to another server and forwards that server's
|
||||
//! responses back to clients.
|
||||
//!
|
||||
//! Because the Tokio runtime uses a thread poool, each TCP connection is
|
||||
//! Because the Tokio runtime uses a thread pool, each TCP connection is
|
||||
//! processed concurrently with all other TCP connections across multiple
|
||||
//! threads.
|
||||
//!
|
||||
|
||||
@@ -21,6 +21,7 @@ extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
extern crate time;
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
|
||||
use std::{env, fmt, io};
|
||||
@@ -29,7 +30,7 @@ use std::net::SocketAddr;
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
use tokio::prelude::*;
|
||||
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
use tokio_codec::{Encoder, Decoder};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use http::header::HeaderValue;
|
||||
@@ -55,10 +56,10 @@ fn main() {
|
||||
}
|
||||
|
||||
fn process(socket: TcpStream) {
|
||||
let (tx, rx) = socket
|
||||
let (tx, rx) =
|
||||
// Frame the socket using the `Http` protocol. This maps the TCP socket
|
||||
// to a Stream + Sink of HTTP frames.
|
||||
.framed(Http)
|
||||
Http.framed(socket)
|
||||
// This splits a single `Stream + Sink` value into two separate handles
|
||||
// that can be used independently (even on different tasks or threads).
|
||||
.split();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
extern crate env_logger;
|
||||
|
||||
@@ -16,7 +17,7 @@ use std::net::SocketAddr;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::net::{UdpSocket, UdpFramed};
|
||||
use tokio_io::codec::BytesCodec;
|
||||
use tokio_codec::BytesCodec;
|
||||
|
||||
fn main() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "futures2"
|
||||
|
||||
version = "0.1.0"
|
||||
authors = ["Aaron Turon <[email protected]>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
Enables depending on futures 0.2 and futures 0.1 in the same crate.
|
||||
"""
|
||||
|
||||
[dependencies]
|
||||
futures = "=0.2.0-beta"
|
||||
@@ -1,2 +0,0 @@
|
||||
extern crate futures;
|
||||
pub use futures::*;
|
||||
@@ -0,0 +1,15 @@
|
||||
//! A configurable source of time.
|
||||
//!
|
||||
//! This module provides the [`now`][n] function, which returns an `Instant`
|
||||
//! representing "now". The source of time used by this function is configurable
|
||||
//! (via the [`tokio-timer`] crate) and allows mocking out the source of time in
|
||||
//! tests or performing caching operations to reduce the number of syscalls.
|
||||
//!
|
||||
//! Note that, because the source of time is configurable, it is possible to
|
||||
//! observe non-monotonic behavior when calling [`now`] from different
|
||||
//! executors.
|
||||
//!
|
||||
//! [n]: fn.now.html
|
||||
//! [`tokio-timer`]: https://docs.rs/tokio-timer/0.2/tokio_timer/clock/index.html
|
||||
|
||||
pub use tokio_timer::clock::now;
|
||||
@@ -118,6 +118,7 @@ use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::sync::mpsc;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
@@ -132,6 +133,12 @@ pub struct CurrentThread<P: Park = ParkThread> {
|
||||
|
||||
/// Thread park handle
|
||||
park: P,
|
||||
|
||||
/// Handle for spawning new futures from other threads
|
||||
spawn_handle: Handle,
|
||||
|
||||
/// Receiver for futures spawned from other threads
|
||||
spawn_receiver: mpsc::Receiver<Box<Future<Item = (), Error = ()> + Send + 'static>>,
|
||||
}
|
||||
|
||||
/// Executes futures on the current thread.
|
||||
@@ -147,11 +154,20 @@ pub struct TaskExecutor {
|
||||
_p: ::std::marker::PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
/// Returned by the `turn` function
|
||||
/// Returned by the `turn` function.
|
||||
#[derive(Debug)]
|
||||
pub struct Turn(());
|
||||
pub struct Turn {
|
||||
polled: bool
|
||||
}
|
||||
|
||||
/// A `CurrentThread` instance bound to a supplied execution conext.
|
||||
impl Turn {
|
||||
/// `true` if any futures were polled at all and `false` otherwise.
|
||||
pub fn has_polled(&self) -> bool {
|
||||
self.polled
|
||||
}
|
||||
}
|
||||
|
||||
/// A `CurrentThread` instance bound to a supplied execution context.
|
||||
pub struct Entered<'a, P: Park + 'a> {
|
||||
executor: &'a mut CurrentThread<P>,
|
||||
enter: &'a mut Enter,
|
||||
@@ -239,7 +255,7 @@ where F: FnOnce(&mut Context) -> R
|
||||
/// and blocks the current thread until the provided future and **all**
|
||||
/// subsequently spawned futures complete. In other words:
|
||||
///
|
||||
/// * If the provided boostrap future does **not** spawn any additional tasks,
|
||||
/// * If the provided bootstrap future does **not** spawn any additional tasks,
|
||||
/// `block_on_all` returns once `future` completes.
|
||||
/// * If the provided bootstrap future **does** spawn additional tasks, then
|
||||
/// `block_on_all` returns once **all** spawned futures complete.
|
||||
@@ -295,10 +311,17 @@ impl<P: Park> CurrentThread<P> {
|
||||
pub fn new_with_park(park: P) -> Self {
|
||||
let unpark = park.unpark();
|
||||
|
||||
let (spawn_sender, spawn_receiver) = mpsc::channel();
|
||||
|
||||
let scheduler = Scheduler::new(unpark);
|
||||
let notify = scheduler.notify();
|
||||
|
||||
CurrentThread {
|
||||
scheduler: Scheduler::new(unpark),
|
||||
scheduler: scheduler,
|
||||
num_futures: 0,
|
||||
park,
|
||||
spawn_handle: Handle { sender: spawn_sender, notify: notify },
|
||||
spawn_receiver: spawn_receiver,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,12 +397,30 @@ impl<P: Park> CurrentThread<P> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying `Park` instance.
|
||||
pub fn get_park(&self) -> &P {
|
||||
&self.park
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying `Park` instance.
|
||||
pub fn get_park_mut(&mut self) -> &mut P {
|
||||
&mut self.park
|
||||
}
|
||||
|
||||
fn borrow(&mut self) -> Borrow<P::Unpark> {
|
||||
Borrow {
|
||||
scheduler: &mut self.scheduler,
|
||||
num_futures: &mut self.num_futures,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a new handle to spawn futures on the executor
|
||||
///
|
||||
/// Different to the executor itself, the handle can be sent to different
|
||||
/// threads and can be used to spawn futures on the executor.
|
||||
pub fn handle(&self) -> Handle {
|
||||
self.spawn_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_executor::Executor for CurrentThread {
|
||||
@@ -480,20 +521,32 @@ impl<'a, P: Park> Entered<'a, P> {
|
||||
pub fn turn(&mut self, duration: Option<Duration>)
|
||||
-> Result<Turn, TurnError>
|
||||
{
|
||||
if !self.tick() {
|
||||
let res = match duration {
|
||||
let res = if self.executor.scheduler.has_pending_futures() {
|
||||
self.executor.park.park_timeout(Duration::from_millis(0))
|
||||
} else {
|
||||
match duration {
|
||||
Some(duration) => self.executor.park.park_timeout(duration),
|
||||
None => self.executor.park.park(),
|
||||
};
|
||||
|
||||
if res.is_err() {
|
||||
return Err(TurnError { _p: () });
|
||||
}
|
||||
};
|
||||
|
||||
self.tick();
|
||||
if res.is_err() {
|
||||
return Err(TurnError { _p: () });
|
||||
}
|
||||
|
||||
Ok(Turn(()))
|
||||
let polled = self.tick();
|
||||
|
||||
Ok(Turn { polled })
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying `Park` instance.
|
||||
pub fn get_park(&self) -> &P {
|
||||
&self.executor.park
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying `Park` instance.
|
||||
pub fn get_park_mut(&mut self) -> &mut P {
|
||||
&mut self.executor.park
|
||||
}
|
||||
|
||||
fn run_timeout2(&mut self, dur: Option<Duration>)
|
||||
@@ -538,9 +591,26 @@ impl<'a, P: Park> Entered<'a, P> {
|
||||
|
||||
/// Returns `true` if any futures were processed
|
||||
fn tick(&mut self) -> bool {
|
||||
self.executor.scheduler.tick(
|
||||
// Spawn any futures that were spawned from other threads by manually
|
||||
// looping over the receiver stream
|
||||
|
||||
// FIXME: Slightly ugly but needed to make the borrow checker happy
|
||||
let (mut borrow, spawn_receiver) = (
|
||||
Borrow {
|
||||
scheduler: &mut self.executor.scheduler,
|
||||
num_futures: &mut self.executor.num_futures,
|
||||
},
|
||||
&mut self.executor.spawn_receiver,
|
||||
);
|
||||
|
||||
while let Ok(future) = spawn_receiver.try_recv() {
|
||||
borrow.spawn_local(future);
|
||||
}
|
||||
|
||||
// After any pending futures were scheduled, do the actual tick
|
||||
borrow.scheduler.tick(
|
||||
&mut *self.enter,
|
||||
&mut self.executor.num_futures)
|
||||
borrow.num_futures)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,6 +623,41 @@ impl<'a, P: Park> fmt::Debug for Entered<'a, P> {
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Handle =====
|
||||
|
||||
/// Handle to spawn a future on the corresponding `CurrentThread` instance
|
||||
#[derive(Clone)]
|
||||
pub struct Handle {
|
||||
sender: mpsc::Sender<Box<Future<Item = (), Error = ()> + Send + 'static>>,
|
||||
notify: executor::NotifyHandle,
|
||||
}
|
||||
|
||||
// Manual implementation because the Sender does not implement Debug
|
||||
impl fmt::Debug for Handle {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Handle")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
/// Spawn a future onto the `CurrentThread` instance corresponding to this handle
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the spawn fails. Failure occurs if the `CurrentThread`
|
||||
/// instance of the `Handle` does not exist anymore.
|
||||
pub fn spawn<F>(&self, future: F) -> Result<(), SpawnError>
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static {
|
||||
self.sender.send(Box::new(future))
|
||||
.expect("CurrentThread does not exist anymore");
|
||||
// use 0 for the id, CurrentThread does not make use of it
|
||||
self.notify.notify(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl TaskExecutor =====
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "use TaskExecutor::current instead")]
|
||||
@@ -702,7 +807,7 @@ impl RunTimeoutError {
|
||||
RunTimeoutError { timeout }
|
||||
}
|
||||
|
||||
/// Returns `true` if the error was caused by the operation timeing out.
|
||||
/// Returns `true` if the error was caused by the operation timing out.
|
||||
pub fn is_timeout(&self) -> bool {
|
||||
self.timeout
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ struct List<U> {
|
||||
// Specifically, when a node is stored in at least one of the two lists
|
||||
// described above, this represents a logical `Arc` handle. This is how
|
||||
// `Scheduler` maintains its reference to all nodes it manages. Each
|
||||
// `NotifyHande` instance is an `Arc<Node>` as well.
|
||||
// `NotifyHandle` instance is an `Arc<Node>` as well.
|
||||
//
|
||||
// When `Scheduler` drops, it clears the linked list of all nodes that it
|
||||
// manages. When doing so, it must attempt to decrement the reference count (by
|
||||
@@ -196,6 +196,15 @@ where U: Unpark,
|
||||
self.inner.enqueue(ptr);
|
||||
}
|
||||
|
||||
/// Returns `true` if there are currently any pending futures
|
||||
pub fn has_pending_futures(&mut self) -> bool {
|
||||
// See function definition for why the unsafe is needed and
|
||||
// correctly used here
|
||||
unsafe {
|
||||
self.inner.has_pending_futures()
|
||||
}
|
||||
}
|
||||
|
||||
/// Advance the scheduler state, returning `true` if any futures were
|
||||
/// processed.
|
||||
///
|
||||
@@ -439,6 +448,22 @@ impl<U> Inner<U> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if there are currently any pending futures
|
||||
///
|
||||
/// See `dequeue` for an explanation why this function is unsafe.
|
||||
unsafe fn has_pending_futures(&self) -> bool {
|
||||
let tail = *self.tail_readiness.get();
|
||||
let next = (*tail).next_readiness.load(Acquire);
|
||||
|
||||
if tail == self.stub() {
|
||||
if next.is_null() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// The dequeue function from the 1024cores intrusive MPSC queue algorithm
|
||||
///
|
||||
/// Note that this unsafe as it required mutual exclusion (only one thread
|
||||
@@ -617,7 +642,7 @@ impl<'a, U> Clone for Notify<'a, U> {
|
||||
|
||||
impl<'a, U> fmt::Debug for Notify<'a, U> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Notiy").finish()
|
||||
fmt.debug_struct("Notify").finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
//!
|
||||
//! * **[`thread_pool`]**: A multi-threaded executor that maintains a pool of
|
||||
//! threads. Tasks are spawned to one of the threads in the pool and executed.
|
||||
//! The pool employes a [work-stealing] strategy for optimizing how tasks get
|
||||
//! The pool employs a [work-stealing] strategy for optimizing how tasks get
|
||||
//! spread across the available threads.
|
||||
//!
|
||||
//! # `Executor` trait.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//! Asynchronous filesystem manipulation operations.
|
||||
//!
|
||||
//! This module contains basic methods and types for manipulating the contents
|
||||
//! of the local filesystem from within the context of the Tokio runtime.
|
||||
//!
|
||||
//! Unlike *most* other Tokio APIs, the filesystem APIs **must** be used from
|
||||
//! the context of the Tokio runtime as they require Tokio specific features to
|
||||
//! function.
|
||||
|
||||
pub use tokio_fs::{
|
||||
file,
|
||||
File,
|
||||
};
|
||||
+37
-1
@@ -8,6 +8,7 @@
|
||||
//! * A [reactor][reactor] backed by the operating system's event queue (epoll, kqueue,
|
||||
//! IOCP, etc...).
|
||||
//! * Asynchronous [TCP and UDP][net] sockets.
|
||||
//! * Asynchronous [filesystem][fs] operations.
|
||||
//! * [Timer][timer] API for scheduling work in the future.
|
||||
//!
|
||||
//! Tokio is built using [futures] as the abstraction for managing the
|
||||
@@ -63,7 +64,7 @@
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/tokio/0.1.4")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio/0.1.5")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
|
||||
#[macro_use]
|
||||
@@ -71,6 +72,7 @@ extern crate futures;
|
||||
extern crate mio;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_fs;
|
||||
extern crate tokio_reactor;
|
||||
extern crate tokio_threadpool;
|
||||
extern crate tokio_timer;
|
||||
@@ -80,7 +82,9 @@ extern crate tokio_udp;
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
extern crate futures2;
|
||||
|
||||
pub mod clock;
|
||||
pub mod executor;
|
||||
pub mod fs;
|
||||
pub mod net;
|
||||
pub mod reactor;
|
||||
pub mod runtime;
|
||||
@@ -100,15 +104,35 @@ pub mod io {
|
||||
//! defines two traits, [`AsyncRead`] and [`AsyncWrite`], which extend the
|
||||
//! `Read` and `Write` traits of the standard library.
|
||||
//!
|
||||
//! # AsyncRead and AsyncWrite
|
||||
//!
|
||||
//! [`AsyncRead`] and [`AsyncWrite`] must only be implemented for
|
||||
//! non-blocking I/O types that integrate with the futures type system. In
|
||||
//! other words, these types must never block the thread, and instead the
|
||||
//! current task is notified when the I/O resource is ready.
|
||||
//!
|
||||
//! # Standard input and output
|
||||
//!
|
||||
//! Tokio provides asynchronous APIs to standard [input], [output], and [error].
|
||||
//! These APIs are very similar to the ones provided by `std`, but they also
|
||||
//! implement [`AsyncRead`] and [`AsyncWrite`].
|
||||
//!
|
||||
//! Unlike *most* other Tokio APIs, the standard input / output APIs
|
||||
//! **must** be used from the context of the Tokio runtime as they require
|
||||
//! Tokio specific features to function.
|
||||
//!
|
||||
//! [input]: fn.stdin.html
|
||||
//! [output]: fn.stdout.html
|
||||
//! [error]: fn.stderr.html
|
||||
//!
|
||||
//! # Utility functions
|
||||
//!
|
||||
//! Utilities functions are provided for working with [`AsyncRead`] /
|
||||
//! [`AsyncWrite`] types. For example, [`copy`] asynchronously copies all
|
||||
//! data from a source to a destination.
|
||||
//!
|
||||
//! # `std` re-exports
|
||||
//!
|
||||
//! Additionally, [`Read`], [`Write`], [`Error`], [`ErrorKind`], and
|
||||
//! [`Result`] are re-exported from `std::io` for ease of use.
|
||||
//!
|
||||
@@ -126,6 +150,16 @@ pub mod io {
|
||||
AsyncWrite,
|
||||
};
|
||||
|
||||
// standard input, output, and error
|
||||
pub use tokio_fs::{
|
||||
stdin,
|
||||
Stdin,
|
||||
stdout,
|
||||
Stdout,
|
||||
stderr,
|
||||
Stderr,
|
||||
};
|
||||
|
||||
// Utils
|
||||
pub use tokio_io::io::{
|
||||
copy,
|
||||
@@ -140,10 +174,12 @@ pub mod io {
|
||||
ReadToEnd,
|
||||
read_until,
|
||||
ReadUntil,
|
||||
ReadHalf,
|
||||
shutdown,
|
||||
Shutdown,
|
||||
write_all,
|
||||
WriteAll,
|
||||
WriteHalf,
|
||||
};
|
||||
|
||||
// Re-export io::Error so that users don't have to deal
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
//! Reading and writing to it can be done using futures, which return the
|
||||
//! [`RecvDgram`] and [`SendDgram`] structs respectively.
|
||||
//!
|
||||
//! For convience it's also possible to convert raw datagrams into higher-level
|
||||
//! For convenience it's also possible to convert raw datagrams into higher-level
|
||||
//! frames.
|
||||
//!
|
||||
//! [`UdpSocket`]: struct.UdpSocket.html
|
||||
|
||||
@@ -428,7 +428,7 @@ fn usize2ready(bits: usize) -> Ready {
|
||||
ready | platform::usize2ready(bits)
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
#[cfg(unix)]
|
||||
mod platform {
|
||||
use mio::Ready;
|
||||
use mio::unix::UnixReady;
|
||||
@@ -516,7 +516,7 @@ mod platform {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(windows, target_os = "fuchsia"))]
|
||||
#[cfg(windows)]
|
||||
mod platform {
|
||||
use mio::Ready;
|
||||
|
||||
|
||||
+26
-7
@@ -7,11 +7,12 @@ use std::io;
|
||||
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};
|
||||
|
||||
/// Builds Tokio Runtime with custom configuration values.
|
||||
///
|
||||
/// Methods can be chanined in order to set the configuration values. The
|
||||
/// Methods can be chained in order to set the configuration values. The
|
||||
/// Runtime is constructed by calling [`build`].
|
||||
///
|
||||
/// New instances of `Builder` are obtained via [`Builder::new`].
|
||||
@@ -48,6 +49,9 @@ use tokio_timer::timer::{self, Timer};
|
||||
pub struct Builder {
|
||||
/// Thread pool specific builder
|
||||
threadpool_builder: ThreadPoolBuilder,
|
||||
|
||||
/// The clock to use
|
||||
clock: Clock,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
@@ -59,7 +63,16 @@ impl Builder {
|
||||
let mut threadpool_builder = ThreadPoolBuilder::new();
|
||||
threadpool_builder.name_prefix("tokio-runtime-worker-");
|
||||
|
||||
Builder { threadpool_builder }
|
||||
Builder {
|
||||
threadpool_builder,
|
||||
clock: Clock::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the `Clock` instance that will be used by the runtime.
|
||||
pub fn clock(&mut self, clock: Clock) -> &mut Self {
|
||||
self.clock = clock;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set builder to set up the thread pool instance.
|
||||
@@ -78,7 +91,7 @@ impl Builder {
|
||||
/// # extern crate tokio;
|
||||
/// # use tokio::runtime::Builder;
|
||||
/// # pub fn main() {
|
||||
/// let runtime = Builder::new().build();
|
||||
/// let runtime = Builder::new().build().unwrap();
|
||||
/// // ... call runtime.run(...)
|
||||
/// # let _ = runtime;
|
||||
/// # }
|
||||
@@ -87,6 +100,10 @@ impl Builder {
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
// 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();
|
||||
|
||||
@@ -103,14 +120,16 @@ impl Builder {
|
||||
.clone();
|
||||
|
||||
tokio_reactor::with_default(&reactor_handle, enter, |enter| {
|
||||
timer::with_default(&timer_handle, enter, |_| {
|
||||
w.run();
|
||||
});
|
||||
clock::with_default(&clock1, enter, |enter| {
|
||||
timer::with_default(&timer_handle, enter, |_| {
|
||||
w.run();
|
||||
});
|
||||
})
|
||||
});
|
||||
})
|
||||
.custom_park(move |worker_id| {
|
||||
// Create a new timer
|
||||
let timer = Timer::new(DefaultPark::new());
|
||||
let timer = Timer::new_with_now(DefaultPark::new(), clock2.clone());
|
||||
|
||||
timers.lock().unwrap()
|
||||
.insert(worker_id.clone(), timer.handle());
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
use executor::current_thread::CurrentThread;
|
||||
use runtime::current_thread::Runtime;
|
||||
|
||||
use tokio_reactor::Reactor;
|
||||
use tokio_timer::clock::Clock;
|
||||
use tokio_timer::timer::Timer;
|
||||
|
||||
use std::io;
|
||||
|
||||
/// Builds a Single-threaded runtime with custom configuration values.
|
||||
///
|
||||
/// Methods can be chained in order to set the configuration values. The
|
||||
/// Runtime is constructed by calling [`build`].
|
||||
///
|
||||
/// New instances of `Builder` are obtained via [`Builder::new`].
|
||||
///
|
||||
/// See function level documentation for details on the various configuration
|
||||
/// settings.
|
||||
///
|
||||
/// [`build`]: #method.build
|
||||
/// [`Builder::new`]: #method.new
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// extern crate tokio;
|
||||
/// extern crate tokio_timer;
|
||||
///
|
||||
/// use tokio::runtime::current_thread::Builder;
|
||||
/// use tokio_timer::clock::Clock;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// // build Runtime
|
||||
/// let runtime = Builder::new()
|
||||
/// .clock(Clock::new())
|
||||
/// .build();
|
||||
/// // ... call runtime.run(...)
|
||||
/// # let _ = runtime;
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
/// The clock to use
|
||||
clock: Clock,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Returns a new runtime builder initialized with default configuration
|
||||
/// values.
|
||||
///
|
||||
/// Configuration methods can be chained on the return value.
|
||||
pub fn new() -> Builder {
|
||||
Builder {
|
||||
clock: Clock::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the `Clock` instance that will be used by the runtime.
|
||||
pub fn clock(&mut self, clock: Clock) -> &mut Self {
|
||||
self.clock = clock;
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the configured `Runtime`.
|
||||
pub fn build(&mut self) -> io::Result<Runtime> {
|
||||
// We need a reactor to receive events about IO objects from kernel
|
||||
let reactor = Reactor::new()?;
|
||||
let reactor_handle = reactor.handle();
|
||||
|
||||
// Place a timer wheel on top of the reactor. If there are no timeouts to fire, it'll let the
|
||||
// reactor pick up some new external events.
|
||||
let timer = Timer::new_with_now(reactor, self.clock.clone());
|
||||
let timer_handle = timer.handle();
|
||||
|
||||
// And now put a single-threaded executor on top of the timer. When there are no futures ready
|
||||
// to do something, it'll let the timer or the reactor to generate some new stimuli for the
|
||||
// futures to continue in their life.
|
||||
let executor = CurrentThread::new_with_park(timer);
|
||||
|
||||
let runtime = Runtime::new2(
|
||||
reactor_handle,
|
||||
timer_handle,
|
||||
self.clock.clone(),
|
||||
executor);
|
||||
|
||||
Ok(runtime)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
//! A runtime implementation that runs everything on the current thread.
|
||||
//!
|
||||
//! [`current_thread::Runtime`][rt] is similar to the primary
|
||||
//! [`Runtime`][concurrent-rt] except that it runs all components on the current
|
||||
//! thread instead of using a thread pool. This means that it is able to spawn
|
||||
//! futures that do not implement `Send`.
|
||||
//!
|
||||
//! Same as the default [`Runtime`][concurrent-rt], the
|
||||
//! [`current_thread::Runtime`][rt] includes:
|
||||
//!
|
||||
//! * A [reactor] to drive I/O resources.
|
||||
//! * An [executor] to execute tasks that use these I/O resources.
|
||||
//! * A [timer] for scheduling work to run after a set period of time.
|
||||
//!
|
||||
//! Note that [`current_thread::Runtime`][rt] does not implement `Send` itself
|
||||
//! and cannot be safely moved to other threads.
|
||||
//!
|
||||
//! # Spawning from other threads
|
||||
//!
|
||||
//! While [`current_thread::Runtime`][rt] does not implement `Send` and cannot
|
||||
//! safely be moved to other threads, it provides a `Handle` that can be sent
|
||||
//! to other threads and allows to spawn new tasks from there.
|
||||
//!
|
||||
//! For example:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate tokio;
|
||||
//! # extern crate futures;
|
||||
//! use tokio::runtime::current_thread::Runtime;
|
||||
//! use tokio::prelude::*;
|
||||
//! use std::thread;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let mut runtime = Runtime::new().unwrap();
|
||||
//! let handle = runtime.handle();
|
||||
//!
|
||||
//! thread::spawn(move || {
|
||||
//! handle.spawn(future::ok(()));
|
||||
//! }).join().unwrap();
|
||||
//!
|
||||
//! # /*
|
||||
//! runtime.run().unwrap();
|
||||
//! # */
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Creating a new `Runtime` and running a future `f` until its completion and
|
||||
//! returning its result.
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio::runtime::current_thread::Runtime;
|
||||
//! use tokio::prelude::*;
|
||||
//!
|
||||
//! let mut runtime = Runtime::new().unwrap();
|
||||
//!
|
||||
//! // Use the runtime...
|
||||
//! // runtime.block_on(f); // where f is a future
|
||||
//! ```
|
||||
//!
|
||||
//! [rt]: struct.Runtime.html
|
||||
//! [concurrent-rt]: ../struct.Runtime.html
|
||||
//! [chan]: https://docs.rs/futures/0.1/futures/sync/mpsc/fn.channel.html
|
||||
|
||||
mod builder;
|
||||
mod runtime;
|
||||
|
||||
pub use self::builder::Builder;
|
||||
pub use self::runtime::{Runtime, Handle};
|
||||
@@ -0,0 +1,185 @@
|
||||
use executor::current_thread::{self, CurrentThread};
|
||||
use executor::current_thread::Handle as ExecutorHandle;
|
||||
use runtime::current_thread::Builder;
|
||||
|
||||
use tokio_reactor::{self, Reactor};
|
||||
use tokio_timer::clock::{self, Clock};
|
||||
use tokio_timer::timer::{self, Timer};
|
||||
use tokio_executor;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
use std::io;
|
||||
|
||||
/// Single-threaded runtime provides a way to start reactor
|
||||
/// and executor on the current thread.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
#[derive(Debug)]
|
||||
pub struct Runtime {
|
||||
reactor_handle: tokio_reactor::Handle,
|
||||
timer_handle: timer::Handle,
|
||||
clock: Clock,
|
||||
executor: CurrentThread<Timer<Reactor>>,
|
||||
}
|
||||
|
||||
/// Handle to spawn a future on the corresponding `CurrentThread` runtime instance
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Handle(ExecutorHandle);
|
||||
|
||||
impl Handle {
|
||||
/// Spawn a future onto the `CurrentThread` runtime instance corresponding to this handle
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the spawn fails. Failure occurs if the `CurrentThread`
|
||||
/// instance of the `Handle` does not exist anymore.
|
||||
pub fn spawn<F>(&self, future: F) -> Result<(), tokio_executor::SpawnError>
|
||||
where F: Future<Item = (), Error = ()> + Send + 'static {
|
||||
self.0.spawn(future)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned by the `run` function.
|
||||
#[derive(Debug)]
|
||||
pub struct RunError {
|
||||
inner: current_thread::RunError,
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
/// Returns a new runtime initialized with default configuration values.
|
||||
pub fn new() -> io::Result<Runtime> {
|
||||
Builder::new().build()
|
||||
}
|
||||
|
||||
pub(super) fn new2(
|
||||
reactor_handle: tokio_reactor::Handle,
|
||||
timer_handle: timer::Handle,
|
||||
clock: Clock,
|
||||
executor: CurrentThread<Timer<Reactor>>) -> Runtime
|
||||
{
|
||||
Runtime {
|
||||
reactor_handle,
|
||||
timer_handle,
|
||||
clock,
|
||||
executor,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a new handle to spawn futures on the single-threaded Tokio runtime
|
||||
///
|
||||
/// Different to the runtime itself, the handle can be sent to different
|
||||
/// threads.
|
||||
pub fn handle(&self) -> Handle {
|
||||
Handle(self.executor.handle().clone())
|
||||
}
|
||||
|
||||
/// Spawn a future onto the single-threaded Tokio runtime.
|
||||
///
|
||||
/// 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::current_thread::Runtime;
|
||||
///
|
||||
/// # fn dox() {
|
||||
/// // Create the runtime
|
||||
/// let mut rt = Runtime::new().unwrap();
|
||||
///
|
||||
/// // Spawn a future onto the runtime
|
||||
/// rt.spawn(future::lazy(|| {
|
||||
/// println!("running on the runtime");
|
||||
/// 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 = ()> + 'static,
|
||||
{
|
||||
self.executor.spawn(future);
|
||||
self
|
||||
}
|
||||
|
||||
/// Runs the provided future, blocking the current thread until the future
|
||||
/// completes.
|
||||
///
|
||||
/// This function can be used to synchronously block the current thread
|
||||
/// until the provided `future` has resolved either successfully or with an
|
||||
/// error. The result of the future is then returned from this function
|
||||
/// call.
|
||||
///
|
||||
/// Note that this function will **also** execute any spawned futures on the
|
||||
/// current thread, but will **not** block until these other spawned futures
|
||||
/// have completed. Once the function returns, any uncompleted futures
|
||||
/// remain pending in the `Runtime` instance. These futures will not run
|
||||
/// until `block_on` or `run` is called again.
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution by calling `block_on` or `run`.
|
||||
pub fn block_on<F>(&mut self, f: F) -> Result<F::Item, F::Error>
|
||||
where F: Future
|
||||
{
|
||||
self.enter(|executor| {
|
||||
// Run the provided future
|
||||
let ret = executor.block_on(f);
|
||||
ret.map_err(|e| e.into_inner().expect("unexpected execution error"))
|
||||
})
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until **all**
|
||||
/// spawned futures have completed.
|
||||
pub fn run(&mut self) -> Result<(), RunError> {
|
||||
self.enter(|executor| executor.run())
|
||||
.map_err(|e| RunError {
|
||||
inner: e,
|
||||
})
|
||||
}
|
||||
|
||||
fn enter<F, R>(&mut self, f: F) -> R
|
||||
where F: FnOnce(&mut current_thread::Entered<Timer<Reactor>>) -> R
|
||||
{
|
||||
let Runtime {
|
||||
ref reactor_handle,
|
||||
ref timer_handle,
|
||||
ref clock,
|
||||
ref mut executor,
|
||||
..
|
||||
} = *self;
|
||||
|
||||
// Binds an executor to this thread
|
||||
let mut enter = tokio_executor::enter().expect("Multiple executors at once");
|
||||
|
||||
// This will set the default handle and timer to use inside the closure
|
||||
// and run the future.
|
||||
tokio_reactor::with_default(&reactor_handle, &mut enter, |enter| {
|
||||
clock::with_default(clock, enter, |enter| {
|
||||
timer::with_default(&timer_handle, enter, |enter| {
|
||||
// The TaskExecutor is a fake executor that looks into the
|
||||
// current single-threaded executor when used. This is a trick,
|
||||
// because we need two mutable references to the executor (one
|
||||
// to run the provided future, another to install as the default
|
||||
// one). We use the fake one here as the default one.
|
||||
let mut default_executor = current_thread::TaskExecutor::current();
|
||||
tokio_executor::with_default(&mut default_executor, enter, |enter| {
|
||||
let mut executor = executor.enter(enter);
|
||||
f(&mut executor)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
+26
-1
@@ -113,6 +113,7 @@
|
||||
//! [`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;
|
||||
|
||||
@@ -126,6 +127,7 @@ use std::io;
|
||||
|
||||
use tokio_threadpool as threadpool;
|
||||
|
||||
use futures;
|
||||
use futures::future::Future;
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
@@ -233,7 +235,7 @@ impl Runtime {
|
||||
/// tasks are scheduled to run.
|
||||
///
|
||||
/// Most users will not need to call this function directly, instead they
|
||||
/// will use [`tokio::run`][fn.run.html].
|
||||
/// will use [`tokio::run`](fn.run.html).
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
@@ -364,6 +366,29 @@ impl Runtime {
|
||||
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 asynchrounous 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()
|
||||
}
|
||||
|
||||
/// Signals the runtime to shutdown once it becomes idle.
|
||||
///
|
||||
/// Returns a future that completes once the shutdown operation has
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
pub use tokio_timer::{
|
||||
Deadline,
|
||||
DeadlineError,
|
||||
Error,
|
||||
Interval,
|
||||
Delay,
|
||||
};
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ pub trait FutureExt: Future {
|
||||
///
|
||||
/// This combinator creates a new future which wraps the receiving future
|
||||
/// with a deadline. The returned future is allowed to execute until it
|
||||
/// completes or `deadline` is reached, whicheever happens first.
|
||||
/// completes or `deadline` is reached, whichever happens first.
|
||||
///
|
||||
/// If the future completes before `deadline` then the future will resolve
|
||||
/// with that item. Otherwise the future will resolve to an error once
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_timer;
|
||||
extern crate env_logger;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::{self, current_thread};
|
||||
use tokio::timer::*;
|
||||
use tokio_timer::clock::Clock;
|
||||
|
||||
use std::sync::mpsc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
struct MockNow(Instant);
|
||||
|
||||
impl tokio_timer::clock::Now for MockNow {
|
||||
fn now(&self) -> Instant {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clock_and_timer_concurrent() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let when = Instant::now() + Duration::from_millis(5_000);
|
||||
let clock = Clock::new_with_now(MockNow(when));
|
||||
|
||||
let mut rt = runtime::Builder::new()
|
||||
.clock(clock)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
rt.spawn({
|
||||
Delay::new(when)
|
||||
.map_err(|e| panic!("unexpected error; err={:?}", e))
|
||||
.and_then(move |_| {
|
||||
assert!(Instant::now() < when);
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clock_and_timer_single_threaded() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let when = Instant::now() + Duration::from_millis(5_000);
|
||||
let clock = Clock::new_with_now(MockNow(when));
|
||||
|
||||
let mut rt = current_thread::Builder::new()
|
||||
.clock(clock)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
rt.block_on({
|
||||
Delay::new(when)
|
||||
.map_err(|e| panic!("unexpected error; err={:?}", e))
|
||||
.and_then(move |_| {
|
||||
assert!(Instant::now() < when);
|
||||
Ok(())
|
||||
})
|
||||
}).unwrap();
|
||||
}
|
||||
@@ -392,6 +392,231 @@ fn hammer_turn() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_has_polled() {
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
// Spawn oneshot receiver
|
||||
let (sender, receiver) = oneshot::channel::<()>();
|
||||
current_thread.spawn(receiver.then(|_| Ok(())));
|
||||
|
||||
// Turn once...
|
||||
let res = current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
|
||||
// Should've polled the receiver once, but considered it not ready
|
||||
assert!(res.has_polled());
|
||||
|
||||
// Turn another time
|
||||
let res = current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
|
||||
// Should've polled nothing, the receiver is not ready yet
|
||||
assert!(!res.has_polled());
|
||||
|
||||
// Make the receiver ready
|
||||
sender.send(()).unwrap();
|
||||
|
||||
// Turn another time
|
||||
let res = current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
|
||||
// Should've polled the receiver, it's ready now
|
||||
assert!(res.has_polled());
|
||||
|
||||
// Now the executor should be empty
|
||||
assert!(current_thread.is_idle());
|
||||
let res = current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
|
||||
// So should've polled nothing
|
||||
assert!(!res.has_polled());
|
||||
}
|
||||
|
||||
// Our own mock Park that is never really waiting and the only
|
||||
// thing it does is to send, on request, something (once) to a onshot
|
||||
// channel
|
||||
struct MyPark {
|
||||
sender: Option<oneshot::Sender<()>>,
|
||||
send_now: Rc<Cell<bool>>,
|
||||
}
|
||||
|
||||
struct MyUnpark;
|
||||
|
||||
impl tokio_executor::park::Park for MyPark {
|
||||
type Unpark = MyUnpark;
|
||||
type Error = ();
|
||||
|
||||
fn unpark(&self) -> Self::Unpark {
|
||||
MyUnpark
|
||||
}
|
||||
|
||||
fn park(&mut self) -> Result<(), Self::Error> {
|
||||
// If called twice with send_now, this will intentionally panic
|
||||
if self.send_now.get() {
|
||||
self.sender.take().unwrap().send(()).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn park_timeout(&mut self, _duration: Duration) -> Result<(), Self::Error> {
|
||||
self.park()
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_executor::park::Unpark for MyUnpark {
|
||||
fn unpark(&self) {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_fair() {
|
||||
let send_now = Rc::new(Cell::new(false));
|
||||
|
||||
let (sender, receiver) = oneshot::channel::<()>();
|
||||
let (sender_2, receiver_2) = oneshot::channel::<()>();
|
||||
let (sender_3, receiver_3) = oneshot::channel::<()>();
|
||||
|
||||
let my_park = MyPark {
|
||||
sender: Some(sender_3),
|
||||
send_now: send_now.clone(),
|
||||
};
|
||||
|
||||
let mut current_thread = CurrentThread::new_with_park(my_park);
|
||||
|
||||
let receiver_1_done = Rc::new(Cell::new(false));
|
||||
let receiver_1_done_clone = receiver_1_done.clone();
|
||||
|
||||
// Once an item is received on the oneshot channel, it will immediately
|
||||
// immediately make the second oneshot channel ready
|
||||
current_thread.spawn(receiver
|
||||
.map_err(|_| unreachable!())
|
||||
.and_then(move |_| {
|
||||
sender_2.send(()).unwrap();
|
||||
receiver_1_done_clone.set(true);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
);
|
||||
|
||||
let receiver_2_done = Rc::new(Cell::new(false));
|
||||
let receiver_2_done_clone = receiver_2_done.clone();
|
||||
|
||||
current_thread.spawn(receiver_2
|
||||
.map_err(|_| unreachable!())
|
||||
.and_then(move |_| {
|
||||
receiver_2_done_clone.set(true);
|
||||
Ok(())
|
||||
})
|
||||
);
|
||||
|
||||
// The third receiver is only woken up from our Park implementation, it simulates
|
||||
// e.g. a socket that first has to be polled to know if it is ready now
|
||||
let receiver_3_done = Rc::new(Cell::new(false));
|
||||
let receiver_3_done_clone = receiver_3_done.clone();
|
||||
|
||||
current_thread.spawn(receiver_3
|
||||
.map_err(|_| unreachable!())
|
||||
.and_then(move |_| {
|
||||
receiver_3_done_clone.set(true);
|
||||
Ok(())
|
||||
})
|
||||
);
|
||||
|
||||
// First turn should've polled both and considered them not ready
|
||||
let res = current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
assert!(res.has_polled());
|
||||
|
||||
// Next turn should've polled nothing
|
||||
let res = current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
assert!(!res.has_polled());
|
||||
|
||||
assert!(!receiver_1_done.get());
|
||||
assert!(!receiver_2_done.get());
|
||||
assert!(!receiver_3_done.get());
|
||||
|
||||
// After this the receiver future will wake up the second receiver future,
|
||||
// so there are pending futures again
|
||||
sender.send(()).unwrap();
|
||||
|
||||
// Now the first receiver should be done, the second receiver should be ready
|
||||
// to be polled again and the socket not yet
|
||||
let res = current_thread.turn(None).unwrap();
|
||||
assert!(res.has_polled());
|
||||
|
||||
assert!(receiver_1_done.get());
|
||||
assert!(!receiver_2_done.get());
|
||||
assert!(!receiver_3_done.get());
|
||||
|
||||
// Now let our park implementation know that it should send something to sender 3
|
||||
send_now.set(true);
|
||||
|
||||
// This should resolve the second receiver directly, but also poll the socket
|
||||
// and read the packet from it. If it didn't do both here, we would handle
|
||||
// futures that are woken up from the reactor and directly unfairly and would
|
||||
// favour the ones that are woken up directly.
|
||||
let res = current_thread.turn(None).unwrap();
|
||||
assert!(res.has_polled());
|
||||
|
||||
assert!(receiver_1_done.get());
|
||||
assert!(receiver_2_done.get());
|
||||
assert!(receiver_3_done.get());
|
||||
|
||||
// Don't send again
|
||||
send_now.set(false);
|
||||
|
||||
// Now we should be idle and turning should not poll anything
|
||||
assert!(current_thread.is_idle());
|
||||
let res = current_thread.turn(None).unwrap();
|
||||
assert!(!res.has_polled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_from_other_thread() {
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
let handle = current_thread.handle();
|
||||
let (sender, receiver) = oneshot::channel::<()>();
|
||||
|
||||
thread::spawn(move || {
|
||||
handle.spawn(lazy(move || {
|
||||
sender.send(()).unwrap();
|
||||
Ok(())
|
||||
})).unwrap();
|
||||
});
|
||||
|
||||
let _ = current_thread.block_on(receiver).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_from_other_thread_unpark() {
|
||||
use std::sync::mpsc::channel as mpsc_channel;
|
||||
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
let handle = current_thread.handle();
|
||||
let (sender_1, receiver_1) = oneshot::channel::<()>();
|
||||
let (sender_2, receiver_2) = mpsc_channel::<()>();
|
||||
|
||||
thread::spawn(move || {
|
||||
let _ = receiver_2.recv().unwrap();
|
||||
|
||||
handle.spawn(lazy(move || {
|
||||
sender_1.send(()).unwrap();
|
||||
Ok(())
|
||||
})).unwrap();
|
||||
});
|
||||
|
||||
// Ensure that unparking the executor works correctly. It will first
|
||||
// check if there are new futures (there are none), then execute the
|
||||
// lazy future below which will cause the future to be spawned from
|
||||
// the other thread. Then the executor will park but should be woken
|
||||
// up because *now* we have a new future to schedule
|
||||
let _ = current_thread.block_on(
|
||||
lazy(move || {
|
||||
sender_2.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
.and_then(|_| receiver_1)
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
fn ok() -> future::FutureResult<(), ()> {
|
||||
future::ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_threadpool;
|
||||
extern crate bytes;
|
||||
@@ -11,9 +12,8 @@ use std::net::Shutdown;
|
||||
use bytes::{BytesMut, BufMut};
|
||||
use futures::{Future, Stream, Sink};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
use tokio_codec::{Encoder, Decoder};
|
||||
use tokio_io::io::{write_all, read};
|
||||
use tokio_io::AsyncRead;
|
||||
use tokio_threadpool::Builder;
|
||||
|
||||
pub struct LineCodec;
|
||||
@@ -61,7 +61,7 @@ fn echo() {
|
||||
let addr = listener.local_addr().unwrap();
|
||||
let sender = pool.sender().clone();
|
||||
let srv = listener.incoming().for_each(move |socket| {
|
||||
let (sink, stream) = socket.framed(LineCodec).split();
|
||||
let (sink, stream) = LineCodec.framed(socket).split();
|
||||
sender.spawn(sink.send_all(stream).map(|_| ()).map_err(|_| ())).unwrap();
|
||||
Ok(())
|
||||
});
|
||||
|
||||
+157
-29
@@ -1,9 +1,15 @@
|
||||
extern crate tokio;
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
|
||||
use futures::sync::oneshot;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use tokio::io;
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
use tokio::prelude::future::lazy;
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
@@ -12,36 +18,158 @@ macro_rules! t {
|
||||
})
|
||||
}
|
||||
|
||||
fn create_client_server_future() -> Box<Future<Item=(), Error=()> + Send> {
|
||||
let server = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(server.local_addr());
|
||||
let client = TcpStream::connect(&addr);
|
||||
|
||||
let server = server.incoming().take(1)
|
||||
.map_err(|e| panic!("accept err = {:?}", e))
|
||||
.for_each(|socket| {
|
||||
tokio::spawn({
|
||||
io::write_all(socket, b"hello")
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("write err = {:?}", e))
|
||||
})
|
||||
})
|
||||
.map(|_| ());
|
||||
|
||||
let client = client
|
||||
.map_err(|e| panic!("connect err = {:?}", e))
|
||||
.and_then(|client| {
|
||||
// Read all
|
||||
io::read_to_end(client, vec![])
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("read err = {:?}", e))
|
||||
});
|
||||
|
||||
let future = server.join(client)
|
||||
.map(|_| ());
|
||||
Box::new(future)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_runtime_usage() {
|
||||
fn runtime_tokio_run() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
tokio::run({
|
||||
let server = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(server.local_addr());
|
||||
let client = TcpStream::connect(&addr);
|
||||
|
||||
let server = server.incoming().take(1)
|
||||
.map_err(|e| panic!("accept err = {:?}", e))
|
||||
.for_each(|socket| {
|
||||
tokio::spawn({
|
||||
io::write_all(socket, b"hello")
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("write err = {:?}", e))
|
||||
})
|
||||
})
|
||||
.map(|_| ());
|
||||
|
||||
let client = client
|
||||
.map_err(|e| panic!("connect err = {:?}", e))
|
||||
.and_then(|client| {
|
||||
// Read all
|
||||
io::read_to_end(client, vec![])
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("read err = {:?}", e))
|
||||
});
|
||||
|
||||
server.join(client)
|
||||
.map(|_| ())
|
||||
});
|
||||
tokio::run(create_client_server_future());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_single_threaded() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let mut runtime = tokio::runtime::current_thread::Runtime::new()
|
||||
.unwrap();
|
||||
runtime.block_on(create_client_server_future()).unwrap();
|
||||
runtime.run().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_multi_threaded() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let mut runtime = tokio::runtime::Builder::new()
|
||||
.build()
|
||||
.unwrap();
|
||||
runtime.spawn(create_client_server_future());
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_on_timer() {
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::timer::{Delay, Error};
|
||||
|
||||
fn after_1s<T>(x: T) -> Box<Future<Item = T, Error = Error> + Send>
|
||||
where
|
||||
T: Send + 'static,
|
||||
{
|
||||
Box::new(Delay::new(Instant::now() + Duration::from_millis(100)).map(move |_| x))
|
||||
}
|
||||
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
assert_eq!(runtime.block_on(after_1s(42)).unwrap(), 42);
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_from_block_on() {
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
let msg = runtime
|
||||
.block_on(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
|
||||
// Spawn!
|
||||
tokio::spawn(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
|
||||
Ok::<_, ()>("hello")
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
assert_eq!(2, *cnt.lock().unwrap());
|
||||
assert_eq!(msg, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_waits() {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
thread::spawn(|| {
|
||||
use std::time::Duration;
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
runtime
|
||||
.block_on(rx.then(move |_| {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<_, ()>(())
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, *cnt.lock().unwrap());
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_many() {
|
||||
const ITER: usize = 200;
|
||||
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
|
||||
for _ in 0..ITER {
|
||||
let c = cnt.clone();
|
||||
runtime.spawn(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
}
|
||||
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
assert_eq!(ITER, *cnt.lock().unwrap());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Unreleased
|
||||
|
||||
* Initial release (#353)
|
||||
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "tokio-codec"
|
||||
|
||||
# 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]>", "Bryan Burgers <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-codec/0.1"
|
||||
description = """
|
||||
Utilities for encoding and decoding frames.
|
||||
"""
|
||||
categories = ["asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
tokio-io = { version = "0.1.6", path = "../tokio-io" }
|
||||
bytes = "0.4.7"
|
||||
futures = "0.1.18"
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2018 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,35 @@
|
||||
# tokio-codec
|
||||
|
||||
Utilities for encoding and decoding frames.
|
||||
|
||||
[Documentation](https://docs.rs/tokio-codec)
|
||||
|
||||
## Usage
|
||||
|
||||
First, add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio-codec = "0.1"
|
||||
```
|
||||
|
||||
Next, add this to your crate:
|
||||
|
||||
```rust
|
||||
extern crate tokio_codec;
|
||||
```
|
||||
|
||||
You can find extensive documentation and examples about how to use this crate
|
||||
online at [https://tokio.rs](https://tokio.rs). The [API
|
||||
documentation](https://docs.rs/tokio-codec) is also a great place to get started
|
||||
for the nitty-gritty.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
|
||||
terms or conditions.
|
||||
@@ -0,0 +1,37 @@
|
||||
use bytes::{Bytes, BufMut, BytesMut};
|
||||
use tokio_io::_tokio_codec::{Encoder, Decoder};
|
||||
use std::io;
|
||||
|
||||
/// A simple `Codec` implementation that just ships bytes around.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct BytesCodec(());
|
||||
|
||||
impl BytesCodec {
|
||||
/// Creates a new `BytesCodec` for shipping around raw bytes.
|
||||
pub fn new() -> BytesCodec { BytesCodec(()) }
|
||||
}
|
||||
|
||||
impl Decoder for BytesCodec {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<BytesMut>, io::Error> {
|
||||
if buf.len() > 0 {
|
||||
let len = buf.len();
|
||||
Ok(Some(buf.split_to(len)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for BytesCodec {
|
||||
type Item = Bytes;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, data: Bytes, buf: &mut BytesMut) -> Result<(), io::Error> {
|
||||
buf.reserve(data.len());
|
||||
buf.put(data);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//! Utilities for encoding and decoding frames.
|
||||
//!
|
||||
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
|
||||
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
|
||||
//! Framed streams are also known as [transports].
|
||||
//!
|
||||
//! [`AsyncRead`]: #
|
||||
//! [`AsyncWrite`]: #
|
||||
//! [`Sink`]: #
|
||||
//! [`Stream`]: #
|
||||
//! [transports]: #
|
||||
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-codec/0.1.0")]
|
||||
|
||||
extern crate bytes;
|
||||
extern crate tokio_io;
|
||||
|
||||
mod bytes_codec;
|
||||
mod lines_codec;
|
||||
|
||||
pub use tokio_io::_tokio_codec::{
|
||||
Decoder,
|
||||
Encoder,
|
||||
Framed,
|
||||
FramedParts,
|
||||
FramedRead,
|
||||
FramedWrite,
|
||||
};
|
||||
|
||||
pub use bytes_codec::BytesCodec;
|
||||
pub use lines_codec::LinesCodec;
|
||||
@@ -0,0 +1,89 @@
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use tokio_io::_tokio_codec::{Encoder, Decoder};
|
||||
use std::{io, str};
|
||||
|
||||
/// A simple `Codec` implementation that splits up data into lines.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct LinesCodec {
|
||||
// Stored index of the next index to examine for a `\n` character.
|
||||
// This is used to optimize searching.
|
||||
// For example, if `decode` was called with `abc`, it would hold `3`,
|
||||
// because that is the next index to examine.
|
||||
// The next time `decode` is called with `abcde\n`, the method will
|
||||
// only look at `de\n` before returning.
|
||||
next_index: usize,
|
||||
}
|
||||
|
||||
impl LinesCodec {
|
||||
/// Returns a `LinesCodec` for splitting up data into lines.
|
||||
pub fn new() -> LinesCodec {
|
||||
LinesCodec { next_index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
fn utf8(buf: &[u8]) -> Result<&str, io::Error> {
|
||||
str::from_utf8(buf).map_err(|_|
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Unable to decode input as UTF8"))
|
||||
}
|
||||
|
||||
fn without_carriage_return(s: &[u8]) -> &[u8] {
|
||||
if let Some(&b'\r') = s.last() {
|
||||
&s[..s.len() - 1]
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for LinesCodec {
|
||||
type Item = String;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<String>, io::Error> {
|
||||
if let Some(newline_offset) =
|
||||
buf[self.next_index..].iter().position(|b| *b == b'\n')
|
||||
{
|
||||
let newline_index = newline_offset + self.next_index;
|
||||
let line = buf.split_to(newline_index + 1);
|
||||
let line = &line[..line.len()-1];
|
||||
let line = without_carriage_return(line);
|
||||
let line = utf8(line)?;
|
||||
self.next_index = 0;
|
||||
Ok(Some(line.to_string()))
|
||||
} else {
|
||||
self.next_index = buf.len();
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<String>, io::Error> {
|
||||
Ok(match self.decode(buf)? {
|
||||
Some(frame) => Some(frame),
|
||||
None => {
|
||||
// No terminating newline - return remaining data, if any
|
||||
if buf.is_empty() || buf == &b"\r"[..] {
|
||||
None
|
||||
} else {
|
||||
let line = buf.take();
|
||||
let line = without_carriage_return(&line);
|
||||
let line = utf8(line)?;
|
||||
self.next_index = 0;
|
||||
Some(line.to_string())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for LinesCodec {
|
||||
type Item = String;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, line: String, buf: &mut BytesMut) -> Result<(), io::Error> {
|
||||
buf.reserve(line.len() + 1);
|
||||
buf.put(line);
|
||||
buf.put_u8(b'\n');
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_codec;
|
||||
extern crate bytes;
|
||||
|
||||
use bytes::{BytesMut, Bytes, BufMut};
|
||||
use tokio_io::codec::{BytesCodec, LinesCodec, Decoder, Encoder};
|
||||
use tokio_codec::{BytesCodec, LinesCodec, Decoder, Encoder};
|
||||
|
||||
#[test]
|
||||
fn bytes_decoder() {
|
||||
@@ -1,15 +1,17 @@
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
|
||||
use futures::{Stream, Future};
|
||||
use std::io::{self, Read};
|
||||
use tokio_io::codec::{Framed, FramedParts, Decoder, Encoder};
|
||||
use tokio_codec::{Framed, FramedParts, Decoder, Encoder};
|
||||
use tokio_io::AsyncRead;
|
||||
use bytes::{BytesMut, Buf, BufMut, IntoBuf, BigEndian};
|
||||
use bytes::{BytesMut, Buf, BufMut, IntoBuf};
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
|
||||
/// Encode and decode u32 values.
|
||||
struct U32Codec;
|
||||
|
||||
impl Decoder for U32Codec {
|
||||
@@ -21,7 +23,7 @@ impl Decoder for U32Codec {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let n = buf.split_to(4).into_buf().get_u32::<BigEndian>();
|
||||
let n = buf.split_to(4).into_buf().get_u32_be();
|
||||
Ok(Some(n))
|
||||
}
|
||||
}
|
||||
@@ -33,11 +35,12 @@ impl Encoder for U32Codec {
|
||||
fn encode(&mut self, item: u32, dst: &mut BytesMut) -> io::Result<()> {
|
||||
// Reserve space
|
||||
dst.reserve(4);
|
||||
dst.put_u32::<BigEndian>(item);
|
||||
dst.put_u32_be(item);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This value should never be used
|
||||
struct DontReadIntoThis;
|
||||
|
||||
impl Read for DontReadIntoThis {
|
||||
@@ -51,12 +54,10 @@ impl AsyncRead for DontReadIntoThis {}
|
||||
|
||||
#[test]
|
||||
fn can_read_from_existing_buf() {
|
||||
let parts = FramedParts {
|
||||
inner: DontReadIntoThis,
|
||||
readbuf: vec![0, 0, 0, 42].into(),
|
||||
writebuf: BytesMut::with_capacity(0),
|
||||
};
|
||||
let framed = Framed::from_parts(parts, U32Codec);
|
||||
let mut parts = FramedParts::new(DontReadIntoThis, U32Codec);
|
||||
parts.read_buf = vec![0, 0, 0, 42].into();
|
||||
|
||||
let framed = Framed::from_parts(parts);
|
||||
|
||||
let num = framed
|
||||
.into_future()
|
||||
@@ -66,32 +67,28 @@ fn can_read_from_existing_buf() {
|
||||
.wait()
|
||||
.map_err(|e| e.0)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(num, 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_buf_grows_to_init() {
|
||||
let parts = FramedParts {
|
||||
inner: DontReadIntoThis,
|
||||
readbuf: vec![0, 0, 0, 42].into(),
|
||||
writebuf: BytesMut::with_capacity(0),
|
||||
};
|
||||
let framed = Framed::from_parts(parts, U32Codec);
|
||||
let FramedParts { readbuf, .. } = framed.into_parts();
|
||||
let mut parts = FramedParts::new(DontReadIntoThis, U32Codec);
|
||||
parts.read_buf = vec![0, 0, 0, 42].into();
|
||||
|
||||
assert_eq!(readbuf.capacity(), INITIAL_CAPACITY);
|
||||
let framed = Framed::from_parts(parts);
|
||||
let FramedParts { read_buf, .. } = framed.into_parts();
|
||||
|
||||
assert_eq!(read_buf.capacity(), INITIAL_CAPACITY);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_buf_does_not_shrink() {
|
||||
let parts = FramedParts {
|
||||
inner: DontReadIntoThis,
|
||||
readbuf: vec![0; INITIAL_CAPACITY * 2].into(),
|
||||
writebuf: BytesMut::with_capacity(0),
|
||||
};
|
||||
let framed = Framed::from_parts(parts, U32Codec);
|
||||
let FramedParts { readbuf, .. } = framed.into_parts();
|
||||
let mut parts = FramedParts::new(DontReadIntoThis, U32Codec);
|
||||
parts.read_buf = vec![0; INITIAL_CAPACITY * 2].into();
|
||||
|
||||
assert_eq!(readbuf.capacity(), INITIAL_CAPACITY * 2);
|
||||
let framed = Framed::from_parts(parts);
|
||||
let FramedParts { read_buf, .. } = framed.into_parts();
|
||||
|
||||
assert_eq!(read_buf.capacity(), INITIAL_CAPACITY * 2);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
|
||||
use tokio_io::AsyncRead;
|
||||
use tokio_io::codec::{FramedRead, Decoder};
|
||||
use tokio_codec::{FramedRead, Decoder};
|
||||
|
||||
use bytes::{BytesMut, Buf, IntoBuf, BigEndian};
|
||||
use futures::Stream;
|
||||
@@ -1,9 +1,10 @@
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_io;
|
||||
extern crate bytes;
|
||||
extern crate futures;
|
||||
|
||||
use tokio_io::AsyncWrite;
|
||||
use tokio_io::codec::{Encoder, FramedWrite};
|
||||
use tokio_codec::{Encoder, FramedWrite};
|
||||
|
||||
use futures::{Sink, Poll};
|
||||
use bytes::{BytesMut, BufMut, BigEndian};
|
||||
@@ -28,7 +29,7 @@ impl Encoder for U32Encoder {
|
||||
fn encode(&mut self, item: u32, dst: &mut BytesMut) -> io::Result<()> {
|
||||
// Reserve space
|
||||
dst.reserve(4);
|
||||
dst.put_u32::<BigEndian>(item);
|
||||
dst.put_u32_be(item);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -65,7 +66,7 @@ fn write_hits_backpressure() {
|
||||
|
||||
for i in 0..(ITER + 1) {
|
||||
let mut b = BytesMut::with_capacity(4);
|
||||
b.put_u32::<BigEndian>(i as u32);
|
||||
b.put_u32_be(i as u32);
|
||||
|
||||
// Append to the end
|
||||
match mock.calls.back_mut().unwrap() {
|
||||
@@ -19,10 +19,3 @@ categories = ["concurrency", "asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.19"
|
||||
|
||||
# Futures 0.2 integration
|
||||
futures2 = { version = "0.1.0", path = "../futures2", optional = true }
|
||||
|
||||
[features]
|
||||
unstable-futures = ["futures2"]
|
||||
default = []
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
//!
|
||||
//! * If [`unpark`] is called before [`park`], the next call to [`park`] will
|
||||
//! **not** block the thread.
|
||||
//! * **Spurious** wakeups are permited, i.e., the [`park`] method may unblock
|
||||
//! * **Spurious** wakeups are permitted, i.e., the [`park`] method may unblock
|
||||
//! even if [`unpark`] was not called.
|
||||
//! * [`park_timeout`] does the same as [`park`] but allows specifying a maximum
|
||||
//! time to block the thread for.
|
||||
@@ -75,7 +75,7 @@ pub trait Park {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function **should** not panic, but ultimiately, panics are left as
|
||||
/// This function **should** not panic, but ultimately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Park` implementation
|
||||
///
|
||||
@@ -95,7 +95,7 @@ pub trait Park {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function **should** not panic, but ultimiately, panics are left as
|
||||
/// This function **should** not panic, but ultimately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Park` implementation
|
||||
///
|
||||
@@ -119,7 +119,7 @@ pub trait Unpark: Sync + Send + 'static {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function **should** not panic, but ultimiately, panics are left as
|
||||
/// This function **should** not panic, but ultimately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Unpark` implementation
|
||||
///
|
||||
@@ -264,7 +264,7 @@ impl Inner {
|
||||
None => self.condvar.wait(m).unwrap(),
|
||||
};
|
||||
|
||||
// Transition back to idle. If the state has transitione dto `NOTIFY`,
|
||||
// Transition back to idle. If the state has transitioned to `NOTIFY`,
|
||||
// this will consume that notification
|
||||
self.state.store(IDLE, Ordering::SeqCst);
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Unreleased
|
||||
|
||||
* Use `tokio-codec` in examples
|
||||
|
||||
# 0.1.0 (May 2, 2018)
|
||||
|
||||
* Initial release
|
||||
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "tokio-fs"
|
||||
|
||||
# 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"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-fs/0.1"
|
||||
description = """
|
||||
Filesystem API for Tokio.
|
||||
"""
|
||||
keywords = ["tokio", "futures", "fs", "file", "async"]
|
||||
categories = ["asynchronous", "network-programming", "filesystem"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.21"
|
||||
tokio-threadpool = { version = "0.1.3", path = "../tokio-threadpool" }
|
||||
tokio-io = { version = "0.1.6", path = "../tokio-io" }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.4.2"
|
||||
tempdir = "0.3.7"
|
||||
tokio-io = { version = "0.1.6", path = "../tokio-io" }
|
||||
tokio-codec = { version = "0.1.0", path = "../tokio-codec" }
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2018 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Tokio FS
|
||||
|
||||
Asynchronous filesystem manipulation operations (and stdin, stdout, stderr).
|
||||
|
||||
[Documentation](https://tokio-rs.github.io/tokio/tokio_fs/)
|
||||
|
||||
## Overview
|
||||
|
||||
This crate provides filesystem manipulation facilities for usage with Tokio.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
|
||||
terms or conditions.
|
||||
@@ -0,0 +1,48 @@
|
||||
//! Echo everything received on STDIN to STDOUT.
|
||||
#![deny(deprecated, warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_fs;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_threadpool;
|
||||
|
||||
use tokio_fs::{stdin, stdout, stderr};
|
||||
use tokio_codec::{FramedRead, FramedWrite, LinesCodec};
|
||||
use tokio_threadpool::Builder;
|
||||
|
||||
use futures::{Future, Stream, Sink};
|
||||
|
||||
use std::io;
|
||||
|
||||
pub fn main() {
|
||||
let pool = Builder::new()
|
||||
.pool_size(1)
|
||||
.build();
|
||||
|
||||
pool.spawn({
|
||||
let input = FramedRead::new(stdin(), LinesCodec::new());
|
||||
|
||||
let output = FramedWrite::new(stdout(), LinesCodec::new())
|
||||
.with(|line: String| {
|
||||
let mut out = "OUT: ".to_string();
|
||||
out.push_str(&line);
|
||||
Ok::<_, io::Error>(out)
|
||||
});
|
||||
|
||||
let error = FramedWrite::new(stderr(), LinesCodec::new())
|
||||
.with(|line: String| {
|
||||
let mut out = "ERR: ".to_string();
|
||||
out.push_str(&line);
|
||||
Ok::<_, io::Error>(out)
|
||||
});
|
||||
|
||||
let dst = output.fanout(error);
|
||||
|
||||
input
|
||||
.forward(dst)
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("io error = {:?}", e))
|
||||
});
|
||||
|
||||
pool.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
use super::File;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::fs::File as StdFile;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Future returned by `File::create` and resolves to a `File` instance.
|
||||
#[derive(Debug)]
|
||||
pub struct CreateFuture<P> {
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> CreateFuture<P>
|
||||
where P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
pub(crate) fn new(path: P) -> Self {
|
||||
CreateFuture { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for CreateFuture<P>
|
||||
where P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
type Item = File;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let std = try_ready!(::blocking_io(|| {
|
||||
StdFile::create(&self.path)
|
||||
}));
|
||||
|
||||
let file = File::from_std(std);
|
||||
Ok(file.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
//! Types for working with [`File`].
|
||||
//!
|
||||
//! [`File`]: file/struct.File.html
|
||||
|
||||
mod create;
|
||||
mod open;
|
||||
|
||||
pub use self::create::CreateFuture;
|
||||
pub use self::open::OpenFuture;
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use futures::Poll;
|
||||
|
||||
use std::fs::{File as StdFile, Metadata, Permissions};
|
||||
use std::io::{self, Read, Write, Seek};
|
||||
use std::path::Path;
|
||||
|
||||
/// A reference to an open file on the filesystem.
|
||||
///
|
||||
/// This is a specialized version of [`std::fs::File`][std] for usage from the
|
||||
/// Tokio runtime.
|
||||
///
|
||||
/// An instance of a `File` can be read and/or written depending on what options
|
||||
/// it was opened with. Files also implement Seek to alter the logical cursor
|
||||
/// that the file contains internally.
|
||||
///
|
||||
/// Files are automatically closed when they go out of scope.
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
|
||||
#[derive(Debug)]
|
||||
pub struct File {
|
||||
std: Option<StdFile>,
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// Attempts to open a file in read-only mode.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// `OpenFuture` results in an error if called from outside of the Tokio
|
||||
/// runtime or if the underlying [`open`] call results in an error.
|
||||
///
|
||||
/// [`open`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open
|
||||
pub fn open<P>(path: P) -> OpenFuture<P>
|
||||
where P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
OpenFuture::new(path)
|
||||
}
|
||||
|
||||
/// Opens a file in write-only mode.
|
||||
///
|
||||
/// This function will create a file if it does not exist, and will truncate
|
||||
/// it if it does.
|
||||
///
|
||||
/// `CreateFuture` results in an error if called from outside of the Tokio
|
||||
/// runtime or if the underlying [`create`] call results in an error.
|
||||
///
|
||||
/// [`create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create
|
||||
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`.
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
|
||||
pub(crate) fn from_std(std: StdFile) -> File {
|
||||
File { std: Some(std) }
|
||||
}
|
||||
|
||||
/// Seek to an offset, in bytes, in a stream.
|
||||
///
|
||||
/// A seek beyond the end of a stream is allowed, but implementation
|
||||
/// defined.
|
||||
///
|
||||
/// If the seek operation completed successfully, this method returns the
|
||||
/// new position from the start of the stream. That position can be used
|
||||
/// later with `SeekFrom::Start`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Seeking to a negative offset is considered an error.
|
||||
pub fn poll_seek(&mut self, pos: io::SeekFrom) -> Poll<u64, io::Error> {
|
||||
::blocking_io(|| self.std().seek(pos))
|
||||
}
|
||||
|
||||
/// Attempts to sync all OS-internal metadata to disk.
|
||||
///
|
||||
/// This function will attempt to ensure that all in-core data reaches the
|
||||
/// filesystem before returning.
|
||||
pub fn poll_sync_all(&mut self) -> Poll<(), io::Error> {
|
||||
::blocking_io(|| self.std().sync_all())
|
||||
}
|
||||
|
||||
/// This function is similar to `poll_sync_all`, except that it may not
|
||||
/// synchronize file metadata to the filesystem.
|
||||
///
|
||||
/// This is intended for use cases that must synchronize content, but don't
|
||||
/// need the metadata on disk. The goal of this method is to reduce disk
|
||||
/// operations.
|
||||
///
|
||||
/// Note that some platforms may simply implement this in terms of `poll_sync_all`.
|
||||
pub fn poll_sync_data(&mut self) -> Poll<(), io::Error> {
|
||||
::blocking_io(|| self.std().sync_data())
|
||||
}
|
||||
|
||||
/// Truncates or extends the underlying file, updating the size of this file to become size.
|
||||
///
|
||||
/// If the size is less than the current file's size, then the file will be
|
||||
/// shrunk. If it is greater than the current file's size, then the file
|
||||
/// will be extended to size and have all of the intermediate data filled in
|
||||
/// with 0s.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the file is not opened for
|
||||
/// writing.
|
||||
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.
|
||||
pub fn poll_metadata(&mut self) -> Poll<Metadata, io::Error> {
|
||||
::blocking_io(|| self.std().metadata())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn poll_try_clone(&mut self) -> Poll<File, io::Error> {
|
||||
::blocking_io(|| {
|
||||
let std = self.std().try_clone()?;
|
||||
Ok(File::from_std(std))
|
||||
})
|
||||
}
|
||||
|
||||
/// Changes the permissions on the underlying file.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `fchmod` function on Unix and
|
||||
/// the `SetFileInformationByHandle` function on Windows. Note that, this
|
||||
/// [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// 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.
|
||||
pub fn poll_set_permissions(&mut self, perm: Permissions) -> Poll<(), io::Error> {
|
||||
::blocking_io(|| self.std().set_permissions(perm))
|
||||
}
|
||||
|
||||
fn std(&mut self) -> &mut StdFile {
|
||||
self.std.as_mut().expect("`File` instance already shutdown")
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for File {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
::would_block(|| self.std().read(buf))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for File {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for File {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
::would_block(|| self.std().write(buf))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
::would_block(|| self.std().flush())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for File {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
::blocking_io(|| {
|
||||
self.std = None;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for File {
|
||||
fn drop(&mut self) {
|
||||
if let Some(_std) = self.std.take() {
|
||||
// This is probably fine as closing a file *shouldn't* be a blocking
|
||||
// operation. That said, ideally `shutdown` is called first.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
use super::File;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::fs::File as StdFile;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Future returned by `File::open` and resolves to a `File` instance.
|
||||
#[derive(Debug)]
|
||||
pub struct OpenFuture<P> {
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> OpenFuture<P>
|
||||
where P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
pub(crate) fn new(path: P) -> Self {
|
||||
OpenFuture { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for OpenFuture<P>
|
||||
where P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
type Item = File;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let std = try_ready!(::blocking_io(|| {
|
||||
StdFile::open(&self.path)
|
||||
}));
|
||||
|
||||
let file = File::from_std(std);
|
||||
Ok(file.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//! Asynchronous filesystem manipulation operations (and stdin, stdout, stderr).
|
||||
//!
|
||||
//! This module contains basic methods and types for manipulating the contents
|
||||
//! of the local filesystem from within the context of the Tokio runtime.
|
||||
//!
|
||||
//! Tasks running on the Tokio runtime are expected to be asynchronous, i.e.,
|
||||
//! they will not block the thread of execution. Filesystem operations do not
|
||||
//! satisfy this requirement. In order to perform filesystem operations
|
||||
//! asynchronously, this library uses the [`blocking`][blocking] annotation
|
||||
//! to signal to the runtime that a blocking operation is being performed. This
|
||||
//! allows the runtime to compensate.
|
||||
//!
|
||||
//! [blocking]: https://docs.rs/tokio-threadpool/0.1/tokio_threadpool/fn.blocking.html
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_threadpool;
|
||||
|
||||
pub mod file;
|
||||
mod stdin;
|
||||
mod stdout;
|
||||
mod stderr;
|
||||
|
||||
pub use file::File;
|
||||
pub use stdin::{stdin, Stdin};
|
||||
pub use stdout::{stdout, Stdout};
|
||||
pub use stderr::{stderr, Stderr};
|
||||
|
||||
use futures::Poll;
|
||||
use futures::Async::*;
|
||||
|
||||
use std::io;
|
||||
use std::io::ErrorKind::{Other, WouldBlock};
|
||||
|
||||
fn blocking_io<F, T>(f: F) -> Poll<T, io::Error>
|
||||
where F: FnOnce() -> io::Result<T>,
|
||||
{
|
||||
match tokio_threadpool::blocking(f) {
|
||||
Ok(Ready(Ok(v))) => Ok(v.into()),
|
||||
Ok(Ready(Err(err))) => Err(err),
|
||||
Ok(NotReady) => Ok(NotReady),
|
||||
Err(_) => Err(blocking_err()),
|
||||
}
|
||||
}
|
||||
|
||||
fn would_block<F, T>(f: F) -> io::Result<T>
|
||||
where F: FnOnce() -> io::Result<T>,
|
||||
{
|
||||
match tokio_threadpool::blocking(f) {
|
||||
Ok(Ready(Ok(v))) => Ok(v),
|
||||
Ok(Ready(Err(err))) => {
|
||||
debug_assert_ne!(err.kind(), WouldBlock);
|
||||
Err(err)
|
||||
}
|
||||
Ok(NotReady) => Err(WouldBlock.into()),
|
||||
Err(_) => Err(blocking_err()),
|
||||
}
|
||||
}
|
||||
|
||||
fn blocking_err() -> io::Error {
|
||||
io::Error::new(Other, "`blocking` annotated I/O must be called \
|
||||
from the context of the Tokio runtime.")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use tokio_io::{AsyncWrite};
|
||||
|
||||
use futures::Poll;
|
||||
|
||||
use std::io::{self, Write, Stderr as StdStderr};
|
||||
|
||||
/// A handle to the standard error stream of a process.
|
||||
///
|
||||
/// The handle implements the [`AsyncWrite`] trait, but beware that concurrent
|
||||
/// writes to `Stderr` must be executed with care.
|
||||
///
|
||||
/// Created by the [`stderr`] function.
|
||||
///
|
||||
/// [`stderr`]: fn.stderr.html
|
||||
/// [`AsyncWrite`]: trait.AsyncWrite.html
|
||||
#[derive(Debug)]
|
||||
pub struct Stderr {
|
||||
std: StdStderr,
|
||||
}
|
||||
|
||||
/// Constructs a new handle to the standard error of the current process.
|
||||
///
|
||||
/// The returned handle allows writing to standard error from the within the
|
||||
/// Tokio runtime.
|
||||
pub fn stderr() -> Stderr {
|
||||
let std = io::stderr();
|
||||
Stderr { std }
|
||||
}
|
||||
|
||||
impl Write for Stderr {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
::would_block(|| self.std.write(buf))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
::would_block(|| self.std.flush())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Stderr {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
use tokio_io::{AsyncRead};
|
||||
|
||||
use std::io::{self, Read, Stdin as StdStdin};
|
||||
|
||||
/// A handle to the standard input stream of a process.
|
||||
///
|
||||
/// The handle implements the [`AsyncRead`] trait, but beware that concurrent
|
||||
/// reads of `Stdin` must be executed with care.
|
||||
///
|
||||
/// Created by the [`stdin`] function.
|
||||
///
|
||||
/// [`stdin`]: fn.stdin.html
|
||||
/// [`AsyncRead`]: trait.AsyncRead.html
|
||||
#[derive(Debug)]
|
||||
pub struct Stdin {
|
||||
std: StdStdin,
|
||||
}
|
||||
|
||||
/// Constructs a new handle to the standard input of the current process.
|
||||
///
|
||||
/// The returned handle allows reading from standard input from the within the
|
||||
/// Tokio runtime.
|
||||
pub fn stdin() -> Stdin {
|
||||
let std = io::stdin();
|
||||
Stdin { std }
|
||||
}
|
||||
|
||||
impl Read for Stdin {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
::would_block(|| self.std.read(buf))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Stdin {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use tokio_io::{AsyncWrite};
|
||||
|
||||
use futures::Poll;
|
||||
|
||||
use std::io::{self, Write, Stdout as StdStdout};
|
||||
|
||||
/// A handle to the standard output stream of a process.
|
||||
///
|
||||
/// The handle implements the [`AsyncWrite`] trait, but beware that concurrent
|
||||
/// writes to `Stdout` must be executed with care.
|
||||
///
|
||||
/// Created by the [`stdout`] function.
|
||||
///
|
||||
/// [`stdout`]: fn.stdout.html
|
||||
/// [`AsyncWrite`]: trait.AsyncWrite.html
|
||||
#[derive(Debug)]
|
||||
pub struct Stdout {
|
||||
std: StdStdout,
|
||||
}
|
||||
|
||||
/// Constructs a new handle to the standard output of the current process.
|
||||
///
|
||||
/// The returned handle allows writing to standard out from the within the Tokio
|
||||
/// runtime.
|
||||
pub fn stdout() -> Stdout {
|
||||
let std = io::stdout();
|
||||
Stdout { std }
|
||||
}
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
::would_block(|| self.std.write(buf))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
::would_block(|| self.std.flush())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Stdout {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
extern crate futures;
|
||||
extern crate rand;
|
||||
extern crate tempdir;
|
||||
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 tempdir::TempDir;
|
||||
|
||||
use std::fs::File as StdFile;
|
||||
use std::io::Read;
|
||||
|
||||
#[test]
|
||||
fn read_write() {
|
||||
const NUM_CHARS: usize = 16 * 1_024;
|
||||
|
||||
let dir = TempDir::new("tokio-fs-tests").unwrap();
|
||||
let file_path = dir.path().join("read_write.txt");
|
||||
|
||||
let contents: Vec<u8> = thread_rng().gen_ascii_chars()
|
||||
.take(NUM_CHARS)
|
||||
.collect::<String>()
|
||||
.into();
|
||||
|
||||
let pool = Builder::new()
|
||||
.pool_size(1)
|
||||
.build();
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
pool.spawn({
|
||||
let file_path = file_path.clone();
|
||||
let contents = contents.clone();
|
||||
|
||||
File::create(file_path)
|
||||
.and_then(move |file| io::write_all(file, contents))
|
||||
.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![];
|
||||
file.read_to_end(&mut dst).unwrap();
|
||||
|
||||
assert_eq!(dst, contents);
|
||||
|
||||
pool.spawn({
|
||||
File::open(file_path)
|
||||
.and_then(|file| io::read_to_end(file, vec![]))
|
||||
.then(move |res| {
|
||||
let (_, buf) = res.unwrap();
|
||||
assert_eq!(buf, contents);
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
# Unreleased
|
||||
|
||||
* Move `codec::{Encode, Decode, Framed*}` into `tokio-codec` (#353)
|
||||
|
||||
# 0.1.6 (March 09, 2018)
|
||||
|
||||
* Add native endian builder fn to length_delimited (#144)
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ name = "tokio-io"
|
||||
version = "0.1.6"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio-io"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-io/0.1"
|
||||
description = """
|
||||
@@ -17,6 +17,6 @@ Core I/O primitives for asynchronous I/O in Rust.
|
||||
categories = ["asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
bytes = "0.4.1"
|
||||
bytes = "0.4.7"
|
||||
futures = "0.1.18"
|
||||
log = "0.4"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// For now, we need to keep the implmentation of Encoder in tokio_io.
|
||||
|
||||
pub use codec::Decoder;
|
||||
@@ -0,0 +1,3 @@
|
||||
// For now, we need to keep the implmentation of Encoder in tokio_io.
|
||||
|
||||
pub use codec::Encoder;
|
||||
@@ -0,0 +1,262 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::fmt;
|
||||
|
||||
use {AsyncRead, AsyncWrite};
|
||||
use codec::{Decoder, Encoder};
|
||||
use super::framed_read::{framed_read2, framed_read2_with_buffer, FramedRead2};
|
||||
use super::framed_write::{framed_write2, framed_write2_with_buffer, FramedWrite2};
|
||||
|
||||
use futures::{Stream, Sink, StartSend, Poll};
|
||||
use bytes::{BytesMut};
|
||||
|
||||
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
|
||||
/// the `Encoder` and `Decoder` traits to encode and decode frames.
|
||||
///
|
||||
/// You can create a `Framed` instance by using the `AsyncRead::framed` adapter.
|
||||
pub struct Framed<T, U> {
|
||||
inner: FramedRead2<FramedWrite2<Fuse<T, U>>>,
|
||||
}
|
||||
|
||||
pub struct Fuse<T, U>(pub T, pub U);
|
||||
|
||||
impl<T, U> Framed<T, U>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
U: Decoder + Encoder,
|
||||
{
|
||||
/// Provides a `Stream` and `Sink` interface for reading and writing to this
|
||||
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Codec`
|
||||
/// traits to handle encoding and decoding of messages frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
///
|
||||
/// This function returns a *single* object that is both `Stream` and
|
||||
/// `Sink`; grouping this into a single object is often useful for layering
|
||||
/// things like gzip or TLS, which require both read and write access to the
|
||||
/// underlying object.
|
||||
///
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
pub fn new(inner: T, codec: U) -> Framed<T, U> {
|
||||
Framed {
|
||||
inner: framed_read2(framed_write2(Fuse(inner, codec))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Framed<T, U> {
|
||||
/// Provides a `Stream` and `Sink` interface for reading and writing to this
|
||||
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Codec`
|
||||
/// traits to handle encoding and decoding of messages frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
///
|
||||
/// This function returns a *single* object that is both `Stream` and
|
||||
/// `Sink`; grouping this into a single object is often useful for layering
|
||||
/// things like gzip or TLS, which require both read and write access to the
|
||||
/// underlying object.
|
||||
///
|
||||
/// This objects takes a stream and a readbuffer and a writebuffer. These field
|
||||
/// can be obtained from an existing `Framed` with the `into_parts` method.
|
||||
///
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
pub fn from_parts(parts: FramedParts<T, U>) -> Framed<T, U>
|
||||
{
|
||||
Framed {
|
||||
inner: framed_read2_with_buffer(framed_write2_with_buffer(Fuse(parts.io, parts.codec), parts.write_buf), parts.read_buf),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `Frame`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.get_ref().get_ref().0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `Frame`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.get_mut().get_mut().0
|
||||
}
|
||||
|
||||
/// Consumes the `Frame`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.into_inner().into_inner().0
|
||||
}
|
||||
|
||||
/// Consumes the `Frame`, returning its underlying I/O stream, the buffer
|
||||
/// with unprocessed data, and the codec.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_parts(self) -> FramedParts<T, U> {
|
||||
let (inner, read_buf) = self.inner.into_parts();
|
||||
let (inner, write_buf) = inner.into_parts();
|
||||
|
||||
FramedParts {
|
||||
io: inner.0,
|
||||
codec: inner.1,
|
||||
read_buf: read_buf,
|
||||
write_buf: write_buf,
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Stream for Framed<T, U>
|
||||
where T: AsyncRead,
|
||||
U: Decoder,
|
||||
{
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Sink for Framed<T, U>
|
||||
where T: AsyncWrite,
|
||||
U: Encoder,
|
||||
U::Error: From<io::Error>,
|
||||
{
|
||||
type SinkItem = U::Item;
|
||||
type SinkError = U::Error;
|
||||
|
||||
fn start_send(&mut self,
|
||||
item: Self::SinkItem)
|
||||
-> StartSend<Self::SinkItem, Self::SinkError>
|
||||
{
|
||||
self.inner.get_mut().start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.get_mut().poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.get_mut().close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> fmt::Debug for Framed<T, U>
|
||||
where T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Framed")
|
||||
.field("io", &self.inner.get_ref().get_ref().0)
|
||||
.field("codec", &self.inner.get_ref().get_ref().1)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Fuse =====
|
||||
|
||||
impl<T: Read, U> Read for Fuse<T, U> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead, U> AsyncRead for Fuse<T, U> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.0.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write, U> Write for Fuse<T, U> {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(src)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, U> AsyncWrite for Fuse<T, U> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
self.0.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Decoder> Decoder for Fuse<T, U> {
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn decode(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.1.decode(buffer)
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.1.decode_eof(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Encoder> Encoder for Fuse<T, U> {
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
self.1.encode(item, dst)
|
||||
}
|
||||
}
|
||||
|
||||
/// `FramedParts` contains an export of the data of a Framed transport.
|
||||
/// It can be used to construct a new `Framed` with a different codec.
|
||||
/// It contains all current buffers and the inner transport.
|
||||
#[derive(Debug)]
|
||||
pub struct FramedParts<T, U> {
|
||||
/// The inner transport used to read bytes to and write bytes to
|
||||
pub io: T,
|
||||
|
||||
/// The codec
|
||||
pub codec: U,
|
||||
|
||||
/// The buffer with read but unprocessed data.
|
||||
pub read_buf: BytesMut,
|
||||
|
||||
/// A buffer with unprocessed data which are not written yet.
|
||||
pub write_buf: BytesMut,
|
||||
|
||||
/// This private field allows us to add additional fields in the future in a
|
||||
/// backwards compatible way.
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl<T, U> FramedParts<T, U> {
|
||||
/// Create a new, default, `FramedParts`
|
||||
pub fn new(io: T, codec: U) -> FramedParts<T, U> {
|
||||
FramedParts {
|
||||
io,
|
||||
codec,
|
||||
read_buf: BytesMut::new(),
|
||||
write_buf: BytesMut::new(),
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use AsyncRead;
|
||||
use codec::Decoder;
|
||||
use super::framed::Fuse;
|
||||
|
||||
use futures::{Async, Poll, Stream, Sink, StartSend};
|
||||
use bytes::BytesMut;
|
||||
|
||||
/// A `Stream` of messages decoded from an `AsyncRead`.
|
||||
pub struct FramedRead<T, D> {
|
||||
inner: FramedRead2<Fuse<T, D>>,
|
||||
}
|
||||
|
||||
pub struct FramedRead2<T> {
|
||||
inner: T,
|
||||
eof: bool,
|
||||
is_readable: bool,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
|
||||
// ===== impl FramedRead =====
|
||||
|
||||
impl<T, D> FramedRead<T, D>
|
||||
where T: AsyncRead,
|
||||
D: Decoder,
|
||||
{
|
||||
/// Creates a new `FramedRead` with the given `decoder`.
|
||||
pub fn new(inner: T, decoder: D) -> FramedRead<T, D> {
|
||||
FramedRead {
|
||||
inner: framed_read2(Fuse(inner, decoder)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> FramedRead<T, D> {
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Consumes the `FramedRead`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying decoder.
|
||||
pub fn decoder(&self) -> &D {
|
||||
&self.inner.inner.1
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying decoder.
|
||||
pub fn decoder_mut(&mut self) -> &mut D {
|
||||
&mut self.inner.inner.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Stream for FramedRead<T, D>
|
||||
where T: AsyncRead,
|
||||
D: Decoder,
|
||||
{
|
||||
type Item = D::Item;
|
||||
type Error = D::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Sink for FramedRead<T, D>
|
||||
where T: Sink,
|
||||
{
|
||||
type SinkItem = T::SinkItem;
|
||||
type SinkError = T::SinkError;
|
||||
|
||||
fn start_send(&mut self,
|
||||
item: Self::SinkItem)
|
||||
-> StartSend<Self::SinkItem, Self::SinkError>
|
||||
{
|
||||
self.inner.inner.0.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.inner.0.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.inner.0.close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> fmt::Debug for FramedRead<T, D>
|
||||
where T: fmt::Debug,
|
||||
D: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("FramedRead")
|
||||
.field("inner", &self.inner.inner.0)
|
||||
.field("decoder", &self.inner.inner.1)
|
||||
.field("eof", &self.inner.eof)
|
||||
.field("is_readable", &self.inner.is_readable)
|
||||
.field("buffer", &self.inner.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedRead2 =====
|
||||
|
||||
pub fn framed_read2<T>(inner: T) -> FramedRead2<T> {
|
||||
FramedRead2 {
|
||||
inner: inner,
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn framed_read2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedRead2<T> {
|
||||
if buf.capacity() < INITIAL_CAPACITY {
|
||||
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
|
||||
buf.reserve(bytes_to_reserve);
|
||||
}
|
||||
FramedRead2 {
|
||||
inner: inner,
|
||||
eof: false,
|
||||
is_readable: buf.len() > 0,
|
||||
buffer: buf,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedRead2<T> {
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (T, BytesMut) {
|
||||
(self.inner, self.buffer)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for FramedRead2<T>
|
||||
where T: AsyncRead + Decoder,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
loop {
|
||||
// Repeatedly call `decode` or `decode_eof` as long as it is
|
||||
// "readable". Readable is defined as not having returned `None`. If
|
||||
// the upstream has returned EOF, and the decoder is no longer
|
||||
// readable, it can be assumed that the decoder will never become
|
||||
// readable again, at which point the stream is terminated.
|
||||
if self.is_readable {
|
||||
if self.eof {
|
||||
let frame = try!(self.inner.decode_eof(&mut self.buffer));
|
||||
return Ok(Async::Ready(frame));
|
||||
}
|
||||
|
||||
trace!("attempting to decode a frame");
|
||||
|
||||
if let Some(frame) = try!(self.inner.decode(&mut self.buffer)) {
|
||||
trace!("frame decoded from buffer");
|
||||
return Ok(Async::Ready(Some(frame)));
|
||||
}
|
||||
|
||||
self.is_readable = false;
|
||||
}
|
||||
|
||||
assert!(!self.eof);
|
||||
|
||||
// Otherwise, try to read more data and try again. Make sure we've
|
||||
// got room for at least one byte to read to ensure that we don't
|
||||
// get a spurious 0 that looks like EOF
|
||||
self.buffer.reserve(1);
|
||||
if 0 == try_ready!(self.inner.read_buf(&mut self.buffer)) {
|
||||
self.eof = true;
|
||||
}
|
||||
|
||||
self.is_readable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::io::{self, Read};
|
||||
use std::fmt;
|
||||
|
||||
use {AsyncRead, AsyncWrite};
|
||||
use codec::{Decoder, Encoder};
|
||||
use super::framed::Fuse;
|
||||
|
||||
use futures::{Async, AsyncSink, Poll, Stream, Sink, StartSend};
|
||||
use bytes::BytesMut;
|
||||
|
||||
/// A `Sink` of frames encoded to an `AsyncWrite`.
|
||||
pub struct FramedWrite<T, E> {
|
||||
inner: FramedWrite2<Fuse<T, E>>,
|
||||
}
|
||||
|
||||
pub struct FramedWrite2<T> {
|
||||
inner: T,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
|
||||
|
||||
impl<T, E> FramedWrite<T, E>
|
||||
where T: AsyncWrite,
|
||||
E: Encoder,
|
||||
{
|
||||
/// Creates a new `FramedWrite` with the given `encoder`.
|
||||
pub fn new(inner: T, encoder: E) -> FramedWrite<T, E> {
|
||||
FramedWrite {
|
||||
inner: framed_write2(Fuse(inner, encoder)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> FramedWrite<T, E> {
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Consumes the `FramedWrite`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying decoder.
|
||||
pub fn encoder(&self) -> &E {
|
||||
&self.inner.inner.1
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying decoder.
|
||||
pub fn encoder_mut(&mut self) -> &mut E {
|
||||
&mut self.inner.inner.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Sink for FramedWrite<T, E>
|
||||
where T: AsyncWrite,
|
||||
E: Encoder,
|
||||
{
|
||||
type SinkItem = E::Item;
|
||||
type SinkError = E::Error;
|
||||
|
||||
fn start_send(&mut self, item: E::Item) -> StartSend<E::Item, E::Error> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
Ok(try!(self.inner.close()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Stream for FramedWrite<T, D>
|
||||
where T: Stream,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.inner.0.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> fmt::Debug for FramedWrite<T, U>
|
||||
where T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("FramedWrite")
|
||||
.field("inner", &self.inner.get_ref().0)
|
||||
.field("encoder", &self.inner.get_ref().1)
|
||||
.field("buffer", &self.inner.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedWrite2 =====
|
||||
|
||||
pub fn framed_write2<T>(inner: T) -> FramedWrite2<T> {
|
||||
FramedWrite2 {
|
||||
inner: inner,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn framed_write2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedWrite2<T> {
|
||||
if buf.capacity() < INITIAL_CAPACITY {
|
||||
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
|
||||
buf.reserve(bytes_to_reserve);
|
||||
}
|
||||
FramedWrite2 {
|
||||
inner: inner,
|
||||
buffer: buf,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedWrite2<T> {
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (T, BytesMut) {
|
||||
(self.inner, self.buffer)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sink for FramedWrite2<T>
|
||||
where T: AsyncWrite + Encoder,
|
||||
{
|
||||
type SinkItem = T::Item;
|
||||
type SinkError = T::Error;
|
||||
|
||||
fn start_send(&mut self, item: T::Item) -> StartSend<T::Item, T::Error> {
|
||||
// If the buffer is already over 8KiB, then attempt to flush it. If after flushing it's
|
||||
// *still* over 8KiB, then apply backpressure (reject the send).
|
||||
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
try!(self.poll_complete());
|
||||
|
||||
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
return Ok(AsyncSink::NotReady(item));
|
||||
}
|
||||
}
|
||||
|
||||
try!(self.inner.encode(item, &mut self.buffer));
|
||||
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
trace!("flushing framed transport");
|
||||
|
||||
while !self.buffer.is_empty() {
|
||||
trace!("writing; remaining={}", self.buffer.len());
|
||||
|
||||
let n = try_ready!(self.inner.poll_write(&self.buffer));
|
||||
|
||||
if n == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::WriteZero, "failed to
|
||||
write frame to transport").into());
|
||||
}
|
||||
|
||||
// TODO: Add a way to `bytes` to do this w/o returning the drained
|
||||
// data.
|
||||
let _ = self.buffer.split_to(n);
|
||||
}
|
||||
|
||||
// Try flushing the underlying IO
|
||||
try_ready!(self.inner.poll_flush());
|
||||
|
||||
trace!("framed transport flushed");
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
try_ready!(self.poll_complete());
|
||||
Ok(try!(self.inner.shutdown()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decoder> Decoder for FramedWrite2<T> {
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
|
||||
self.inner.decode(src)
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
|
||||
self.inner.decode_eof(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read> Read for FramedWrite2<T> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for FramedWrite2<T> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.inner.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//! Utilities for encoding and decoding frames.
|
||||
//!
|
||||
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
|
||||
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
|
||||
//! Framed streams are also known as [transports].
|
||||
//!
|
||||
//! [`AsyncRead`]: #
|
||||
//! [`AsyncWrite`]: #
|
||||
//! [`Sink`]: #
|
||||
//! [`Stream`]: #
|
||||
//! [transports]: #
|
||||
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(hidden, html_root_url = "https://docs.rs/tokio-codec/0.1.0")]
|
||||
|
||||
// _tokio_codec are the items that belong in the `tokio_codec` crate. However, because we need to
|
||||
// maintain backward compatibility until the next major breaking change, they are defined here.
|
||||
// When the next breaking change comes, they should be moved to the `tokio_codec` crate and become
|
||||
// independent.
|
||||
//
|
||||
// The primary reason we can't move these to `tokio-codec` now is because, again for backward
|
||||
// compatibility reasons, we need to keep `Decoder` and `Encoder` in tokio_io::codec. And `Decoder`
|
||||
// and `Encoder` needs to reference `Framed`. So they all still need to still be in the same
|
||||
// module.
|
||||
|
||||
mod decoder;
|
||||
mod encoder;
|
||||
mod framed;
|
||||
mod framed_read;
|
||||
mod framed_write;
|
||||
|
||||
pub use self::decoder::Decoder;
|
||||
pub use self::encoder::Encoder;
|
||||
pub use self::framed::{Framed, FramedParts};
|
||||
pub use self::framed_read::FramedRead;
|
||||
pub use self::framed_write::FramedWrite;
|
||||
@@ -76,6 +76,6 @@ impl<T> io::Read for AllowStdIo<T> where T: io::Read {
|
||||
}
|
||||
|
||||
impl<T> AsyncRead for AllowStdIo<T> where T: io::Read {
|
||||
// TODO: override prepare_unitialized_buffer once `Read::initializer` is stable.
|
||||
// TODO: override prepare_uninitialized_buffer once `Read::initializer` is stable.
|
||||
// See rust-lang/rust #42788
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use bytes::BufMut;
|
||||
use futures::{Async, Poll};
|
||||
|
||||
use {framed, split, AsyncWrite};
|
||||
#[allow(deprecated)]
|
||||
use codec::{Decoder, Encoder, Framed};
|
||||
use split::{ReadHalf, WriteHalf};
|
||||
|
||||
@@ -129,6 +130,8 @@ pub trait AsyncRead: std_io::Read {
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
#[deprecated(since = "0.1.7", note = "Use tokio_codec::Decoder::framed instead")]
|
||||
#[allow(deprecated)]
|
||||
fn framed<T: Encoder + Decoder>(self, codec: T) -> Framed<Self, T>
|
||||
where Self: AsyncWrite + Sized,
|
||||
{
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use bytes::{Bytes, BufMut, BytesMut};
|
||||
use codec::{Encoder, Decoder};
|
||||
use std::io;
|
||||
|
||||
/// A simple `Codec` implementation that just ships bytes around.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
pub struct BytesCodec(());
|
||||
|
||||
impl BytesCodec {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
use std::io;
|
||||
use bytes::BytesMut;
|
||||
|
||||
use {AsyncWrite, AsyncRead};
|
||||
use super::encoder::Encoder;
|
||||
|
||||
use ::_tokio_codec::Framed;
|
||||
|
||||
/// Decoding of frames via buffers.
|
||||
///
|
||||
/// This trait is used when constructing an instance of `Framed` or
|
||||
@@ -11,6 +16,9 @@ use bytes::BytesMut;
|
||||
/// Implementations are able to track state on `self`, which enables
|
||||
/// implementing stateful streaming parsers. In many cases, though, this type
|
||||
/// will simply be a unit struct (e.g. `struct HttpDecoder`).
|
||||
|
||||
// Note: We can't deprecate this trait, because the deprecation carries through to tokio-codec, and
|
||||
// there doesn't seem to be a way to un-deprecate the re-export.
|
||||
pub trait Decoder {
|
||||
/// The type of decoded frames.
|
||||
type Item;
|
||||
@@ -83,4 +91,27 @@ pub trait Decoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a `Stream` and `Sink` interface for reading and writing to this
|
||||
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Codec`
|
||||
/// traits to handle encoding and decoding of messages frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
///
|
||||
/// This function returns a *single* object that is both `Stream` and
|
||||
/// `Sink`; grouping this into a single object is often useful for layering
|
||||
/// things like gzip or TLS, which require both read and write access to the
|
||||
/// underlying object.
|
||||
///
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
fn framed<T: AsyncRead + AsyncWrite + Sized>(self, io: T) -> Framed<T, Self>
|
||||
where Self: Encoder + Sized,
|
||||
{
|
||||
Framed::new(io, self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ use bytes::BytesMut;
|
||||
|
||||
/// Trait of helper objects to write out messages as bytes, for use with
|
||||
/// `FramedWrite`.
|
||||
|
||||
// Note: We can't deprecate this trait, because the deprecation carries through to tokio-codec, and
|
||||
// there doesn't seem to be a way to un-deprecate the re-export.
|
||||
pub trait Encoder {
|
||||
/// The type of items consumed by the `Encoder`
|
||||
type Item;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use codec::{Encoder, Decoder};
|
||||
use std::{io, str};
|
||||
|
||||
/// A simple `Codec` implementation that splits up data into lines.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
pub struct LinesCodec {
|
||||
// Stored index of the next index to examine for a `\n` character.
|
||||
// This is used to optimize searching.
|
||||
|
||||
@@ -10,6 +10,14 @@
|
||||
//! [`Stream`]: #
|
||||
//! [transports]: #
|
||||
|
||||
// tokio_io::codec originally held all codec-related helpers. This is now intended to be in
|
||||
// tokio_codec instead. However, for backward compatibility, this remains here. When the next major
|
||||
// breaking change comes, `Encoder` and `Decoder` need to be moved to `tokio_codec`, and the rest
|
||||
// of this module should be removed.
|
||||
|
||||
#![doc(hidden)]
|
||||
#![allow(deprecated)]
|
||||
|
||||
mod decoder;
|
||||
mod encoder;
|
||||
mod bytes_codec;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::fmt;
|
||||
|
||||
@@ -13,10 +15,14 @@ use bytes::{BytesMut};
|
||||
/// the `Encoder` and `Decoder` traits to encode and decode frames.
|
||||
///
|
||||
/// You can create a `Framed` instance by using the `AsyncRead::framed` adapter.
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct Framed<T, U> {
|
||||
inner: FramedRead2<FramedWrite2<Fuse<T, U>>>,
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct Fuse<T, U>(pub T, pub U);
|
||||
|
||||
pub fn framed<T, U>(inner: T, codec: U) -> Framed<T, U>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use AsyncRead;
|
||||
@@ -8,10 +10,14 @@ use futures::{Async, Poll, Stream, Sink, StartSend};
|
||||
use bytes::BytesMut;
|
||||
|
||||
/// A `Stream` of messages decoded from an `AsyncRead`.
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct FramedRead<T, D> {
|
||||
inner: FramedRead2<Fuse<T, D>>,
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct FramedRead2<T> {
|
||||
inner: T,
|
||||
eof: bool,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::io::{self, Read};
|
||||
use std::fmt;
|
||||
|
||||
@@ -9,10 +11,14 @@ use futures::{Async, AsyncSink, Poll, Stream, Sink, StartSend};
|
||||
use bytes::BytesMut;
|
||||
|
||||
/// A `Sink` of frames encoded to an `AsyncWrite`.
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct FramedWrite<T, E> {
|
||||
inner: FramedWrite2<Fuse<T, E>>,
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct FramedWrite2<T> {
|
||||
inner: T,
|
||||
buffer: BytesMut,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use {codec, AsyncRead, AsyncWrite};
|
||||
|
||||
use bytes::{Buf, BufMut, BytesMut, IntoBuf, BigEndian, LittleEndian};
|
||||
use bytes::{Buf, BufMut, BytesMut, IntoBuf};
|
||||
use bytes::buf::Chain;
|
||||
|
||||
use futures::{Async, AsyncSink, Stream, Sink, StartSend, Poll};
|
||||
@@ -291,9 +293,9 @@ impl Decoder {
|
||||
|
||||
// match endianess
|
||||
let n = if self.builder.length_field_is_big_endian {
|
||||
src.get_uint::<BigEndian>(field_len)
|
||||
src.get_uint_be(field_len)
|
||||
} else {
|
||||
src.get_uint::<LittleEndian>(field_len)
|
||||
src.get_uint_le(field_len)
|
||||
};
|
||||
|
||||
if n > self.builder.max_frame_len as u64 {
|
||||
@@ -479,9 +481,9 @@ impl<T: AsyncWrite, B: IntoBuf> FramedWrite<T, B> {
|
||||
};
|
||||
|
||||
if self.builder.length_field_is_big_endian {
|
||||
head.put_uint::<BigEndian>(n as u64, self.builder.length_field_len);
|
||||
head.put_uint_be(n as u64, self.builder.length_field_len);
|
||||
} else {
|
||||
head.put_uint::<LittleEndian>(n as u64, self.builder.length_field_len);
|
||||
head.put_uint_le(n as u64, self.builder.length_field_len);
|
||||
}
|
||||
|
||||
debug_assert!(self.frame.is_none());
|
||||
|
||||
@@ -56,6 +56,7 @@ mod length_delimited;
|
||||
mod lines;
|
||||
mod split;
|
||||
mod window;
|
||||
pub mod _tokio_codec;
|
||||
|
||||
pub use self::async_read::AsyncRead;
|
||||
pub use self::async_write::AsyncWrite;
|
||||
|
||||
@@ -24,13 +24,3 @@ mio = "0.6.14"
|
||||
slab = "0.4.0"
|
||||
tokio-executor = { version = "0.1.1", path = "../tokio-executor" }
|
||||
tokio-io = { version = "0.1.6", path = "../tokio-io" }
|
||||
|
||||
# Futures 0.2 integration
|
||||
futures2 = { version = "0.1", path = "../futures2", optional = true }
|
||||
|
||||
[features]
|
||||
unstable-futures = [
|
||||
"futures2",
|
||||
"tokio-executor/unstable-futures",
|
||||
]
|
||||
default = []
|
||||
|
||||
+107
-42
@@ -94,8 +94,17 @@ pub struct Reactor {
|
||||
/// A `Handle` is used for associating I/O objects with an event loop
|
||||
/// explicitly. Typically though you won't end up using a `Handle` that often
|
||||
/// and will instead use the default reactor for the execution context.
|
||||
///
|
||||
/// By default, most components bind lazily to reactors.
|
||||
/// To get this behavior when manually passing a `Handle`, use `default()`.
|
||||
#[derive(Clone)]
|
||||
pub struct Handle {
|
||||
inner: Option<HandlePriv>,
|
||||
}
|
||||
|
||||
/// Like `Handle`, but never `None`.
|
||||
#[derive(Clone)]
|
||||
struct HandlePriv {
|
||||
inner: Weak<Inner>,
|
||||
}
|
||||
|
||||
@@ -116,6 +125,12 @@ pub struct SetFallbackError(());
|
||||
#[doc(hidden)]
|
||||
pub type SetDefaultError = SetFallbackError;
|
||||
|
||||
#[test]
|
||||
fn test_handle_size() {
|
||||
use std::mem;
|
||||
assert_eq!(mem::size_of::<Handle>(), mem::size_of::<HandlePriv>());
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
/// The underlying system event queue.
|
||||
io: mio::Poll,
|
||||
@@ -147,7 +162,7 @@ pub(crate) enum Direction {
|
||||
static HANDLE_FALLBACK: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
/// Tracks the reactor for the current execution context.
|
||||
thread_local!(static CURRENT_REACTOR: RefCell<Option<Handle>> = RefCell::new(None));
|
||||
thread_local!(static CURRENT_REACTOR: RefCell<Option<HandlePriv>> = RefCell::new(None));
|
||||
|
||||
const TOKEN_SHIFT: usize = 22;
|
||||
|
||||
@@ -199,8 +214,17 @@ where F: FnOnce(&mut Enter) -> R
|
||||
CURRENT_REACTOR.with(|current| {
|
||||
{
|
||||
let mut current = current.borrow_mut();
|
||||
|
||||
assert!(current.is_none(), "default Tokio reactor already set \
|
||||
for execution context");
|
||||
|
||||
let handle = match handle.as_priv() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
panic!("`handle` does not reference a reactor");
|
||||
}
|
||||
};
|
||||
|
||||
*current = Some(handle.clone());
|
||||
}
|
||||
|
||||
@@ -240,7 +264,9 @@ impl Reactor {
|
||||
/// to bind them to this event loop.
|
||||
pub fn handle(&self) -> Handle {
|
||||
Handle {
|
||||
inner: Arc::downgrade(&self.inner),
|
||||
inner: Some(HandlePriv {
|
||||
inner: Arc::downgrade(&self.inner),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +294,7 @@ impl Reactor {
|
||||
/// then this function will also return an error. (aka if `Handle::default`
|
||||
/// has been called previously in this program).
|
||||
pub fn set_fallback(&self) -> Result<(), SetFallbackError> {
|
||||
set_fallback(self.handle())
|
||||
set_fallback(self.handle().into_priv().unwrap())
|
||||
}
|
||||
|
||||
/// Performs one iteration of the event loop, blocking on waiting for events
|
||||
@@ -416,24 +442,84 @@ impl fmt::Debug for Reactor {
|
||||
impl Handle {
|
||||
/// Returns a handle to the current reactor.
|
||||
pub fn current() -> Handle {
|
||||
Handle::try_current()
|
||||
.unwrap_or(Handle { inner: Weak::new() })
|
||||
// TODO: Should this panic on error?
|
||||
HandlePriv::try_current()
|
||||
.map(|handle| Handle {
|
||||
inner: Some(handle),
|
||||
})
|
||||
.unwrap_or(Handle {
|
||||
inner: Some(HandlePriv {
|
||||
inner: Weak::new(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn as_priv(&self) -> Option<&HandlePriv> {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
|
||||
fn into_priv(self) -> Option<HandlePriv> {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn wakeup(&self) {
|
||||
if let Some(handle) = self.as_priv() {
|
||||
handle.wakeup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Unpark for Handle {
|
||||
fn unpark(&self) {
|
||||
if let Some(ref h) = self.inner {
|
||||
h.wakeup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Handle {
|
||||
/// Returns a "default" handle, i.e., a handle that lazily binds to a reactor.
|
||||
fn default() -> Handle {
|
||||
Handle { inner: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Handle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Handle")
|
||||
}
|
||||
}
|
||||
|
||||
fn set_fallback(handle: HandlePriv) -> Result<(), SetFallbackError> {
|
||||
unsafe {
|
||||
let val = handle.into_usize();
|
||||
match HANDLE_FALLBACK.compare_exchange(0, val, SeqCst, SeqCst) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => {
|
||||
drop(HandlePriv::from_usize(val));
|
||||
Err(SetFallbackError(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl HandlePriv =====
|
||||
|
||||
impl HandlePriv {
|
||||
/// Try to get a handle to the current reactor.
|
||||
///
|
||||
/// Returns `Err` if no handle is found.
|
||||
pub(crate) fn try_current() -> io::Result<Handle> {
|
||||
pub(crate) fn try_current() -> io::Result<HandlePriv> {
|
||||
CURRENT_REACTOR.with(|current| {
|
||||
match *current.borrow() {
|
||||
Some(ref handle) => Ok(handle.clone()),
|
||||
None => Handle::fallback(),
|
||||
None => HandlePriv::fallback(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a handle to the fallback reactor.
|
||||
fn fallback() -> io::Result<Handle> {
|
||||
fn fallback() -> io::Result<HandlePriv> {
|
||||
let mut fallback = HANDLE_FALLBACK.load(SeqCst);
|
||||
|
||||
// If the fallback hasn't been previously initialized then let's spin
|
||||
@@ -454,8 +540,8 @@ impl Handle {
|
||||
// that someone was racing with this call to `Handle::default`.
|
||||
// They ended up winning so we'll destroy our helper thread (which
|
||||
// shuts down the thread) and reload the fallback.
|
||||
if set_fallback(reactor.handle().clone()).is_ok() {
|
||||
let ret = reactor.handle().clone();
|
||||
if set_fallback(reactor.handle().into_priv().unwrap()).is_ok() {
|
||||
let ret = reactor.handle().into_priv().unwrap();
|
||||
|
||||
match reactor.background() {
|
||||
Ok(bg) => bg.forget(),
|
||||
@@ -476,9 +562,13 @@ impl Handle {
|
||||
assert!(fallback != 0);
|
||||
|
||||
let ret = unsafe {
|
||||
let handle = Handle::from_usize(fallback);
|
||||
let handle = HandlePriv::from_usize(fallback);
|
||||
let ret = handle.clone();
|
||||
|
||||
// This prevents `handle` from being dropped and having the ref
|
||||
// count decremented.
|
||||
drop(handle.into_usize());
|
||||
|
||||
ret
|
||||
};
|
||||
|
||||
@@ -506,9 +596,9 @@ impl Handle {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn from_usize(val: usize) -> Handle {
|
||||
unsafe fn from_usize(val: usize) -> HandlePriv {
|
||||
let inner = mem::transmute::<usize, Weak<Inner>>(val);;
|
||||
Handle { inner }
|
||||
HandlePriv { inner }
|
||||
}
|
||||
|
||||
fn inner(&self) -> Option<Arc<Inner>> {
|
||||
@@ -516,34 +606,9 @@ impl Handle {
|
||||
}
|
||||
}
|
||||
|
||||
impl Unpark for Handle {
|
||||
fn unpark(&self) {
|
||||
self.wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Handle {
|
||||
fn default() -> Handle {
|
||||
Handle::current()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Handle {
|
||||
impl fmt::Debug for HandlePriv {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Handle")
|
||||
}
|
||||
}
|
||||
|
||||
fn set_fallback(handle: Handle) -> Result<(), SetFallbackError> {
|
||||
unsafe {
|
||||
let val = handle.into_usize();
|
||||
match HANDLE_FALLBACK.compare_exchange(0, val, SeqCst, SeqCst) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => {
|
||||
drop(Handle::from_usize(val));
|
||||
Err(SetFallbackError(()))
|
||||
}
|
||||
}
|
||||
write!(f, "HandlePriv")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,7 +712,7 @@ impl Task {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
#[cfg(unix)]
|
||||
mod platform {
|
||||
use mio::Ready;
|
||||
use mio::unix::UnixReady;
|
||||
@@ -661,7 +726,7 @@ mod platform {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(windows, target_os = "fuchsia"))]
|
||||
#[cfg(windows)]
|
||||
mod platform {
|
||||
use mio::Ready;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ use std::sync::atomic::Ordering::Relaxed;
|
||||
///
|
||||
/// **Note**: While `PollEvented` is `Sync` (if the underlying I/O type is
|
||||
/// `Sync`), the caller must ensure that there are at most two tasks that use a
|
||||
/// `PollEvented` instance concurrenty. One for reading and one for writing.
|
||||
/// `PollEvented` instance concurrently. One for reading and one for writing.
|
||||
/// While violating this requirement is "safe" from a Rust memory model point of
|
||||
/// view, it will result in unexpected behavior in the form of lost
|
||||
/// notifications and tasks hanging.
|
||||
@@ -50,7 +50,7 @@ use std::sync::atomic::Ordering::Relaxed;
|
||||
/// [`clear_write_ready`]. This clears the readiness state until a new readiness
|
||||
/// event is received.
|
||||
///
|
||||
/// This allows the caller to implement additional funcitons. For example,
|
||||
/// This allows the caller to implement additional functions. For example,
|
||||
/// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and
|
||||
/// [`clear_write_ready`].
|
||||
///
|
||||
@@ -160,7 +160,12 @@ where E: Evented
|
||||
/// Creates a new `PollEvented` associated with the specified reactor.
|
||||
pub fn new_with_handle(io: E, handle: &Handle) -> io::Result<Self> {
|
||||
let ret = PollEvented::new(io);
|
||||
ret.inner.registration.register_with(ret.io.as_ref().unwrap(), handle)?;
|
||||
|
||||
if let Some(handle) = handle.as_priv() {
|
||||
ret.inner.registration
|
||||
.register_with_priv(ret.io.as_ref().unwrap(), handle)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use {Handle, Direction, Task};
|
||||
use {Handle, HandlePriv, Direction, Task};
|
||||
|
||||
use futures::{Async, Poll, task};
|
||||
use mio::{self, Evented};
|
||||
@@ -59,7 +59,7 @@ pub struct Registration {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
handle: Handle,
|
||||
handle: HandlePriv,
|
||||
token: usize,
|
||||
}
|
||||
|
||||
@@ -117,10 +117,10 @@ impl Registration {
|
||||
pub fn register<T>(&self, io: &T) -> io::Result<bool>
|
||||
where T: Evented,
|
||||
{
|
||||
self.register2(io, || Handle::try_current())
|
||||
self.register2(io, || HandlePriv::try_current())
|
||||
}
|
||||
|
||||
/// Deregister the I/O resource from the reactor it is associatd with.
|
||||
/// Deregister the I/O resource from the reactor it is associated with.
|
||||
///
|
||||
/// This function must be called before the I/O resource associated with the
|
||||
/// registration is dropped.
|
||||
@@ -163,13 +163,24 @@ impl Registration {
|
||||
/// If an error is encountered during registration, `Err` is returned.
|
||||
pub fn register_with<T>(&self, io: &T, handle: &Handle) -> io::Result<bool>
|
||||
where T: Evented,
|
||||
{
|
||||
self.register2(io, || {
|
||||
match handle.as_priv() {
|
||||
Some(handle) => Ok(handle.clone()),
|
||||
None => HandlePriv::try_current(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn register_with_priv<T>(&self, io: &T, handle: &HandlePriv) -> io::Result<bool>
|
||||
where T: Evented,
|
||||
{
|
||||
self.register2(io, || Ok(handle.clone()))
|
||||
}
|
||||
|
||||
fn register2<T, F>(&self, io: &T, f: F) -> io::Result<bool>
|
||||
where T: Evented,
|
||||
F: Fn() -> io::Result<Handle>,
|
||||
F: Fn() -> io::Result<HandlePriv>,
|
||||
{
|
||||
let mut state = self.state.load(SeqCst);
|
||||
|
||||
@@ -434,7 +445,7 @@ unsafe impl Sync for Registration {}
|
||||
// ===== impl Inner =====
|
||||
|
||||
impl Inner {
|
||||
fn new<T>(io: &T, handle: Handle) -> (Self, io::Result<()>)
|
||||
fn new<T>(io: &T, handle: HandlePriv) -> (Self, io::Result<()>)
|
||||
where T: Evented,
|
||||
{
|
||||
let mut res = Ok(());
|
||||
|
||||
@@ -24,15 +24,5 @@ mio = "0.6.14"
|
||||
iovec = "0.1"
|
||||
futures = "0.1.19"
|
||||
|
||||
# Futures 0.2 integration
|
||||
futures2 = { version = "0.1", path = "../futures2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { version = "0.4", default-features = false }
|
||||
|
||||
[features]
|
||||
unstable-futures = [
|
||||
"futures2",
|
||||
"tokio-reactor/unstable-futures",
|
||||
]
|
||||
default = []
|
||||
|
||||
@@ -96,7 +96,7 @@ impl TcpListener {
|
||||
///
|
||||
/// This function is the same as `accept` above except that it returns a
|
||||
/// `std::net::TcpStream` instead of a `tokio::net::TcpStream`. This in turn
|
||||
/// can then allow for the TCP stream to be assoiated with a different
|
||||
/// can then allow for the TCP stream to be associated with a different
|
||||
/// reactor than the one this `TcpListener` is associated with.
|
||||
///
|
||||
/// # Return
|
||||
@@ -159,6 +159,7 @@ impl TcpListener {
|
||||
///
|
||||
/// Finally, the `handle` argument is the event loop that this listener will
|
||||
/// be bound to.
|
||||
/// Use `Handle::default()` to lazily bind to an event loop, just like `bind` does.
|
||||
///
|
||||
/// The platform specific behavior of this function looks like:
|
||||
///
|
||||
@@ -233,7 +234,7 @@ impl fmt::Debug for TcpListener {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
#[cfg(unix)]
|
||||
mod sys {
|
||||
use std::os::unix::prelude::*;
|
||||
use super::TcpListener;
|
||||
|
||||
@@ -69,8 +69,7 @@ impl TcpStream {
|
||||
///
|
||||
/// This function will convert a TCP stream created by the standard library
|
||||
/// to a TCP stream ready to be used with the provided event loop handle.
|
||||
/// The stream returned is associated with the event loop and ready to
|
||||
/// perform I/O.
|
||||
/// Use `Handle::default()` to lazily bind to an event loop, just like `connect` does.
|
||||
pub fn from_std(stream: net::TcpStream, handle: &Handle)
|
||||
-> io::Result<TcpStream>
|
||||
{
|
||||
@@ -618,7 +617,7 @@ impl<'a> futures2::io::AsyncWrite for &'a TcpStream {
|
||||
Ok(futures2::Async::Ready(n))
|
||||
}
|
||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
self.io.clear_write_ready()?;
|
||||
self.io.clear_write_ready2(cx)?;
|
||||
Ok(futures2::Async::Pending)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
@@ -718,7 +717,7 @@ impl futures2::Future for ConnectFutureState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
#[cfg(unix)]
|
||||
mod sys {
|
||||
use std::os::unix::prelude::*;
|
||||
use super::TcpStream;
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
# 0.1.4 (June 6, 2018)
|
||||
|
||||
* Fix bug that can occur with multiple pools in a process (#375).
|
||||
|
||||
# 0.1.3 (May 2, 2018)
|
||||
|
||||
* Add `blocking` annotation (#317).
|
||||
|
||||
# 0.1.2 (March 30, 2018)
|
||||
|
||||
* Add the ability to specify a custom thread parker.
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
[package]
|
||||
name = "tokio-threadpool"
|
||||
version = "0.1.2"
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.4"
|
||||
documentation = "https://docs.rs/tokio-threadpool"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://github.com/tokio-rs/tokio"
|
||||
@@ -13,24 +17,17 @@ keywords = ["futures", "tokio"]
|
||||
categories = ["concurrency", "asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
tokio-executor = { version = "0.1.1", path = "../tokio-executor" }
|
||||
tokio-executor = { version = "0.1.2", path = "../tokio-executor" }
|
||||
futures = "0.1.19"
|
||||
crossbeam-deque = "0.3"
|
||||
num_cpus = "1.2"
|
||||
rand = "0.4"
|
||||
log = "0.4"
|
||||
|
||||
# Futures 0.2 integration
|
||||
futures2 = { version = "0.1", path = "../futures2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-timer = "0.1"
|
||||
env_logger = "0.4"
|
||||
futures-cpupool = "0.1.7"
|
||||
|
||||
[features]
|
||||
unstable-futures = [
|
||||
"futures2",
|
||||
"tokio-executor/unstable-futures",
|
||||
]
|
||||
default = []
|
||||
# For comparison benchmarks
|
||||
futures-cpupool = "0.1.7"
|
||||
threadpool = "1.7.1"
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
A library for scheduling execution of futures concurrently across a pool of
|
||||
threads.
|
||||
|
||||
**Note**: This library isn't quite ready for use.
|
||||
|
||||
### Why not Rayon?
|
||||
|
||||
Rayon is designed to handle parallelizing single computations by breaking them
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate rand;
|
||||
extern crate tokio_threadpool;
|
||||
extern crate threadpool;
|
||||
extern crate test;
|
||||
|
||||
const ITER: usize = 1_000;
|
||||
|
||||
mod blocking {
|
||||
use super::*;
|
||||
|
||||
use futures::future::*;
|
||||
use tokio_threadpool::{Builder, blocking};
|
||||
|
||||
#[bench]
|
||||
fn cpu_bound(b: &mut test::Bencher) {
|
||||
let pool = Builder::new()
|
||||
.pool_size(2)
|
||||
.max_blocking(20)
|
||||
.build();
|
||||
|
||||
b.iter(|| {
|
||||
let count_down = Arc::new(CountDown::new(::ITER));
|
||||
|
||||
for _ in 0..::ITER {
|
||||
let count_down = count_down.clone();
|
||||
|
||||
pool.spawn(lazy(move || {
|
||||
poll_fn(|| {
|
||||
blocking(|| {
|
||||
perform_complex_computation()
|
||||
})
|
||||
.map_err(|_| panic!())
|
||||
})
|
||||
.and_then(move |_| {
|
||||
// Do something with the value
|
||||
count_down.dec();
|
||||
Ok(())
|
||||
})
|
||||
}));
|
||||
}
|
||||
|
||||
count_down.wait();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
mod message_passing {
|
||||
use super::*;
|
||||
|
||||
use futures::future::*;
|
||||
use futures::sync::oneshot;
|
||||
use tokio_threadpool::Builder;
|
||||
|
||||
#[bench]
|
||||
fn cpu_bound(b: &mut test::Bencher) {
|
||||
let pool = Builder::new()
|
||||
.pool_size(2)
|
||||
.max_blocking(20)
|
||||
.build();
|
||||
|
||||
let blocking = threadpool::ThreadPool::new(20);
|
||||
|
||||
b.iter(|| {
|
||||
let count_down = Arc::new(CountDown::new(::ITER));
|
||||
|
||||
for _ in 0..::ITER {
|
||||
let count_down = count_down.clone();
|
||||
let blocking = blocking.clone();
|
||||
|
||||
pool.spawn(lazy(move || {
|
||||
// Create a channel to receive the return value.
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
// Spawn a task on the blocking thread pool to process the
|
||||
// computation.
|
||||
blocking.execute(move || {
|
||||
let res = perform_complex_computation();
|
||||
tx.send(res).unwrap();
|
||||
});
|
||||
|
||||
rx.and_then(move |_| {
|
||||
count_down.dec();
|
||||
Ok(())
|
||||
}).map_err(|_| panic!())
|
||||
}));
|
||||
}
|
||||
|
||||
count_down.wait();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_complex_computation() -> usize {
|
||||
use rand::*;
|
||||
|
||||
// Simulate a CPU heavy computation
|
||||
let mut rng = rand::thread_rng();
|
||||
rng.gen()
|
||||
}
|
||||
|
||||
// Util for waiting until the tasks complete
|
||||
|
||||
use std::sync::*;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::*;
|
||||
|
||||
struct CountDown {
|
||||
rem: AtomicUsize,
|
||||
mutex: Mutex<()>,
|
||||
condvar: Condvar,
|
||||
}
|
||||
|
||||
impl CountDown {
|
||||
fn new(rem: usize) -> Self {
|
||||
CountDown {
|
||||
rem: AtomicUsize::new(rem),
|
||||
mutex: Mutex::new(()),
|
||||
condvar: Condvar::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn dec(&self) {
|
||||
let prev = self.rem.fetch_sub(1, AcqRel);
|
||||
|
||||
if prev != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
let _lock = self.mutex.lock().unwrap();
|
||||
self.condvar.notify_all();
|
||||
}
|
||||
|
||||
fn wait(&self) {
|
||||
let mut lock = self.mutex.lock().unwrap();
|
||||
|
||||
loop {
|
||||
if self.rem.load(Acquire) == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
lock = self.condvar.wait(lock).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
use worker::Worker;
|
||||
|
||||
use futures::Poll;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
/// Error raised by `blocking`.
|
||||
#[derive(Debug)]
|
||||
pub struct BlockingError {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
/// Enter a blocking section of code.
|
||||
///
|
||||
/// The `blocking` function annotates a section of code that performs a blocking
|
||||
/// operation, either by issuing a blocking syscall or by performing a long
|
||||
/// running CPU-bound computation.
|
||||
///
|
||||
/// When the `blocking` function enters, it hands off the responsibility of
|
||||
/// processing the current work queue to another thread. Then, it calls the
|
||||
/// supplied closure. The closure is permitted to block indefinitely.
|
||||
///
|
||||
/// If the maximum number of concurrent `blocking` calls has been reached, then
|
||||
/// `NotReady` is returned and the task is notified once existing `blocking`
|
||||
/// calls complete. The maximum value is specified when creating a thread pool
|
||||
/// using [`Builder::max_blocking`][build]
|
||||
///
|
||||
/// [build]: struct.Builder.html#method.max_blocking
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// When the blocking closure is executed, `Ok(T)` is returned, where `T` is the
|
||||
/// closure's return value.
|
||||
///
|
||||
/// If the thread pool has shutdown, `Err` is returned.
|
||||
///
|
||||
/// If the number of concurrent `blocking` calls has reached the maximum,
|
||||
/// `Ok(NotReady)` is returned and the current task is notified when a call to
|
||||
/// `blocking` will succeed.
|
||||
///
|
||||
/// If `blocking` is called from outside the context of a Tokio thread pool,
|
||||
/// `Err` is returned.
|
||||
///
|
||||
/// # Background
|
||||
///
|
||||
/// By default, the Tokio thread pool expects that tasks will only run for short
|
||||
/// periods at a time before yielding back to the thread pool. This is the basic
|
||||
/// premise of cooperative multitasking.
|
||||
///
|
||||
/// However, it is common to want to perform a blocking operation while
|
||||
/// processing an asynchronous computation. Examples of blocking operation
|
||||
/// include:
|
||||
///
|
||||
/// * Performing synchronous file operations (reading and writing).
|
||||
/// * Blocking on acquiring a mutex.
|
||||
/// * Performing a CPU bound computation, like cryptographic encryption or
|
||||
/// decryption.
|
||||
///
|
||||
/// One option for dealing with blocking operations in an asynchronous context
|
||||
/// is to use a thread pool dedicated to performing these operations. This not
|
||||
/// ideal as it requires bidirectional message passing as well as a channel to
|
||||
/// communicate which adds a level of buffering.
|
||||
///
|
||||
/// Instead, `blocking` hands off the responsibility of processing the work queue
|
||||
/// to another thread. This hand off is light compared to a channel and does not
|
||||
/// require buffering.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Block on receiving a message from a `std` channel. This example is a little
|
||||
/// silly as using the non-blocking channel from the `futures` crate would make
|
||||
/// more sense. The blocking receive can be replaced with any blocking operation
|
||||
/// that needs to be performed.
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio_threadpool;
|
||||
///
|
||||
/// use tokio_threadpool::{ThreadPool, blocking};
|
||||
///
|
||||
/// use futures::Future;
|
||||
/// use futures::future::{lazy, poll_fn};
|
||||
///
|
||||
/// use std::sync::mpsc;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// pub fn main() {
|
||||
/// // This is a *blocking* channel
|
||||
/// let (tx, rx) = mpsc::channel();
|
||||
///
|
||||
/// // Spawn a thread to send a message
|
||||
/// thread::spawn(move || {
|
||||
/// thread::sleep(Duration::from_millis(500));
|
||||
/// tx.send("hello").unwrap();
|
||||
/// });
|
||||
///
|
||||
/// let pool = ThreadPool::new();
|
||||
///
|
||||
/// pool.spawn(lazy(move || {
|
||||
/// // Because `blocking` returns `Poll`, it is intended to be used
|
||||
/// // from the context of a `Future` implementation. Since we don't
|
||||
/// // have a complicated requirement, we can use `poll_fn` in this
|
||||
/// // case.
|
||||
/// poll_fn(move || {
|
||||
/// blocking(|| {
|
||||
/// let msg = rx.recv().unwrap();
|
||||
/// println!("message = {}", msg);
|
||||
/// }).map_err(|_| panic!("the threadpool shut down"))
|
||||
/// })
|
||||
/// }));
|
||||
///
|
||||
/// // Wait for the task we just spawned to complete.
|
||||
/// pool.shutdown_on_idle().wait().unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn blocking<F, T>(f: F) -> Poll<T, BlockingError>
|
||||
where F: FnOnce() -> T,
|
||||
{
|
||||
let res = Worker::with_current(|worker| {
|
||||
let worker = match worker {
|
||||
Some(worker) => worker,
|
||||
None => {
|
||||
return Err(BlockingError { _p: () });
|
||||
}
|
||||
};
|
||||
|
||||
// Transition the worker state to blocking. This will exit the fn early
|
||||
// with `NotReady` if the pool does not have enough capacity to enter
|
||||
// blocking mode.
|
||||
worker.transition_to_blocking()
|
||||
});
|
||||
|
||||
// If the transition cannot happen, exit early
|
||||
try_ready!(res);
|
||||
|
||||
// Currently in blocking mode, so call the inner closure
|
||||
let ret = f();
|
||||
|
||||
// Try to transition out of blocking mode. This is a fast path that takes
|
||||
// back ownership of the worker if the worker handoff didn't complete yet.
|
||||
Worker::with_current(|worker| {
|
||||
// Worker must be set since it was above.
|
||||
worker.unwrap()
|
||||
.transition_from_blocking();
|
||||
});
|
||||
|
||||
// Return the result
|
||||
Ok(ret.into())
|
||||
}
|
||||
|
||||
impl fmt::Display for BlockingError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for BlockingError {
|
||||
fn description(&self) -> &str {
|
||||
"`blocking` annotation used from outside the context of a thread pool"
|
||||
}
|
||||
}
|
||||
+108
-30
@@ -2,31 +2,25 @@ use callback::Callback;
|
||||
use config::{Config, MAX_WORKERS};
|
||||
use park::{BoxPark, BoxedPark, DefaultPark};
|
||||
use sender::Sender;
|
||||
use shutdown_task::ShutdownTask;
|
||||
use sleep_stack::SleepStack;
|
||||
use state::State;
|
||||
use pool::{Pool, MAX_BACKUP};
|
||||
use thread_pool::ThreadPool;
|
||||
use inner::Inner;
|
||||
use worker::{Worker, WorkerId};
|
||||
use worker_entry::WorkerEntry;
|
||||
use worker::{self, Worker, WorkerId};
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::time::Duration;
|
||||
|
||||
use num_cpus;
|
||||
use tokio_executor::Enter;
|
||||
use tokio_executor::park::Park;
|
||||
use futures::task::AtomicTask;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Builds a thread pool with custom configuration values.
|
||||
///
|
||||
/// Methods can be chanined in order to set the configuration values. The thread
|
||||
/// Methods can be chained in order to set the configuration values. The thread
|
||||
/// pool is constructed by calling [`build`].
|
||||
///
|
||||
/// New instances of `Builder` are obtained via [`Builder::new`].
|
||||
@@ -69,6 +63,10 @@ pub struct Builder {
|
||||
/// Number of workers to spawn
|
||||
pool_size: usize,
|
||||
|
||||
/// Maximum number of futures that can be in a blocking section
|
||||
/// concurrently.
|
||||
max_blocking: usize,
|
||||
|
||||
/// Generates the `Park` instances
|
||||
new_park: Box<Fn(&WorkerId) -> BoxPark>,
|
||||
}
|
||||
@@ -105,11 +103,14 @@ impl Builder {
|
||||
|
||||
Builder {
|
||||
pool_size: num_cpus,
|
||||
max_blocking: 100,
|
||||
config: Config {
|
||||
keep_alive: None,
|
||||
name_prefix: None,
|
||||
stack_size: None,
|
||||
around_worker: None,
|
||||
after_start: None,
|
||||
before_stop: None,
|
||||
},
|
||||
new_park,
|
||||
}
|
||||
@@ -144,6 +145,37 @@ impl Builder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the maximum number of concurrent blocking sections.
|
||||
///
|
||||
/// When the maximum concurrent `blocking` calls is reached, any further
|
||||
/// calls to `blocking` will return `NotReady` and the task is notified once
|
||||
/// previously in-flight calls to `blocking` return.
|
||||
///
|
||||
/// This must be a number between 1 and 32,768 though it is advised to keep
|
||||
/// this value on the smaller side.
|
||||
///
|
||||
/// The default value is 100.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio_threadpool;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio_threadpool::Builder;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// // Create a thread pool with default configuration values
|
||||
/// let thread_pool = Builder::new()
|
||||
/// .max_blocking(200)
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn max_blocking(&mut self, val: usize) -> &mut Self {
|
||||
assert!(val <= MAX_BACKUP, "max value is {}", MAX_BACKUP);
|
||||
self.max_blocking = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the worker thread keep alive duration
|
||||
///
|
||||
/// If set, a worker thread will wait for up to the specified duration for
|
||||
@@ -261,6 +293,61 @@ 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_threadpool;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio_threadpool::Builder;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// // Create a thread pool with default configuration values
|
||||
/// let thread_pool = 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.config.after_start = Some(Arc::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute function `f` before each thread stops.
|
||||
///
|
||||
/// This is intended for bookkeeping and monitoring use cases.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate tokio_threadpool;
|
||||
/// # extern crate futures;
|
||||
/// # use tokio_threadpool::Builder;
|
||||
///
|
||||
/// # pub fn main() {
|
||||
/// // Create a thread pool with default configuration values
|
||||
/// let thread_pool = 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.config.before_stop = Some(Arc::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
/// Customize the `park` instance used by each worker thread.
|
||||
///
|
||||
/// The provided closure `f` is called once per worker and returns a `Park`
|
||||
@@ -285,7 +372,7 @@ impl Builder {
|
||||
/// let park = DefaultPark::new();
|
||||
///
|
||||
/// // Decorate the `park` instance, allowing us to customize work
|
||||
/// // that happens when a worker therad goes to sleep.
|
||||
/// // that happens when a worker thread goes to sleep.
|
||||
/// decorate(park)
|
||||
/// })
|
||||
/// .build();
|
||||
@@ -330,30 +417,21 @@ impl Builder {
|
||||
let park = (self.new_park)(&id);
|
||||
let unpark = park.unpark();
|
||||
|
||||
workers.push(WorkerEntry::new(park, unpark));
|
||||
workers.push(worker::Entry::new(park, unpark));
|
||||
}
|
||||
|
||||
let inner = Arc::new(Inner {
|
||||
state: AtomicUsize::new(State::new().into()),
|
||||
sleep_stack: AtomicUsize::new(SleepStack::new().into()),
|
||||
num_workers: AtomicUsize::new(self.pool_size),
|
||||
next_thread_id: AtomicUsize::new(0),
|
||||
workers: workers.into_boxed_slice(),
|
||||
shutdown_task: ShutdownTask {
|
||||
task1: AtomicTask::new(),
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
task2: futures2::task::AtomicWaker::new(),
|
||||
},
|
||||
config: self.config.clone(),
|
||||
// Create the pool
|
||||
let inner = Arc::new(
|
||||
Pool::new(
|
||||
workers.into_boxed_slice(),
|
||||
self.max_blocking,
|
||||
self.config.clone()));
|
||||
|
||||
// Wrap with `Sender`
|
||||
let inner = Some(Sender {
|
||||
inner
|
||||
});
|
||||
|
||||
// Now, we prime the sleeper stack
|
||||
for i in 0..self.pool_size {
|
||||
inner.push_sleeper(i).unwrap();
|
||||
}
|
||||
|
||||
let inner = Some(Sender { inner });
|
||||
|
||||
ThreadPool { inner }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,32 @@
|
||||
use callback::Callback;
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
/// Thread pool specific configuration values
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Config {
|
||||
pub keep_alive: Option<Duration>,
|
||||
// Used to configure a worker thread
|
||||
pub name_prefix: Option<String>,
|
||||
pub stack_size: Option<usize>,
|
||||
pub around_worker: Option<Callback>,
|
||||
pub after_start: Option<Arc<Fn() + Send + Sync>>,
|
||||
pub before_stop: Option<Arc<Fn() + Send + Sync>>,
|
||||
}
|
||||
|
||||
/// Max number of workers that can be part of a pool. This is the most that can
|
||||
/// fit in the scheduler state. Note, that this is the max number of **active**
|
||||
/// threads. There can be more standby threads.
|
||||
pub(crate) const MAX_WORKERS: usize = 1 << 15;
|
||||
|
||||
impl fmt::Debug for Config {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Config")
|
||||
.field("keep_alive", &self.keep_alive)
|
||||
.field("name_prefix", &self.name_prefix)
|
||||
.field("stack_size", &self.stack_size)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use inner::Inner;
|
||||
use inner::Pool;
|
||||
use notifier::Notifier;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
@@ -14,7 +14,7 @@ pub(crate) struct Futures2Wake {
|
||||
}
|
||||
|
||||
impl Futures2Wake {
|
||||
pub(crate) fn new(id: usize, inner: &Arc<Inner>) -> Futures2Wake {
|
||||
pub(crate) fn new(id: usize, inner: &Arc<Pool>) -> Futures2Wake {
|
||||
let notifier = Arc::new(Notifier {
|
||||
inner: Arc::downgrade(inner),
|
||||
});
|
||||
|
||||
@@ -1,430 +0,0 @@
|
||||
use config::{Config, MAX_WORKERS};
|
||||
use sleep_stack::{
|
||||
SleepStack,
|
||||
EMPTY,
|
||||
TERMINATED,
|
||||
};
|
||||
use shutdown_task::ShutdownTask;
|
||||
use state::{State, SHUTDOWN_ON_IDLE, SHUTDOWN_NOW};
|
||||
use task::Task;
|
||||
use worker::{Worker, WorkerId};
|
||||
use worker_entry::WorkerEntry;
|
||||
use worker_state::{
|
||||
WorkerState,
|
||||
PUSHED_MASK,
|
||||
WORKER_SHUTDOWN,
|
||||
WORKER_RUNNING,
|
||||
WORKER_SLEEPING,
|
||||
WORKER_NOTIFIED,
|
||||
WORKER_SIGNALED,
|
||||
};
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::sync::atomic::Ordering::{Acquire, AcqRel, Release, Relaxed};
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rand::{Rng, SeedableRng, XorShiftRng};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Inner {
|
||||
// ThreadPool state
|
||||
pub state: AtomicUsize,
|
||||
|
||||
// Stack tracking sleeping workers.
|
||||
pub sleep_stack: AtomicUsize,
|
||||
|
||||
// Number of workers who haven't reached the final state of shutdown
|
||||
//
|
||||
// This is only used to know when to single `shutdown_task` once the
|
||||
// shutdown process has completed.
|
||||
pub num_workers: AtomicUsize,
|
||||
|
||||
// Used to generate a thread local RNG seed
|
||||
pub next_thread_id: AtomicUsize,
|
||||
|
||||
// Storage for workers
|
||||
//
|
||||
// This will *usually* be a small number
|
||||
pub workers: Box<[WorkerEntry]>,
|
||||
|
||||
// Task notified when the worker shuts down
|
||||
pub shutdown_task: ShutdownTask,
|
||||
|
||||
// Configuration
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
/// Start shutting down the pool. This means that no new futures will be
|
||||
/// accepted.
|
||||
pub fn shutdown(&self, now: bool, purge_queue: bool) {
|
||||
let mut state: State = self.state.load(Acquire).into();
|
||||
|
||||
trace!("shutdown; state={:?}", state);
|
||||
|
||||
// For now, this must be true
|
||||
debug_assert!(!purge_queue || now);
|
||||
|
||||
// Start by setting the SHUTDOWN flag
|
||||
loop {
|
||||
let mut next = state;
|
||||
|
||||
let num_futures = next.num_futures();
|
||||
|
||||
if next.lifecycle() >= SHUTDOWN_NOW {
|
||||
// Already transitioned to shutting down state
|
||||
|
||||
if !purge_queue || num_futures == 0 {
|
||||
// Nothing more to do
|
||||
return;
|
||||
}
|
||||
|
||||
// The queue must be purged
|
||||
debug_assert!(purge_queue);
|
||||
next.clear_num_futures();
|
||||
} else {
|
||||
next.set_lifecycle(if now || num_futures == 0 {
|
||||
// If already idle, always transition to shutdown now.
|
||||
SHUTDOWN_NOW
|
||||
} else {
|
||||
SHUTDOWN_ON_IDLE
|
||||
});
|
||||
|
||||
if purge_queue {
|
||||
next.clear_num_futures();
|
||||
}
|
||||
}
|
||||
|
||||
let actual = self.state.compare_and_swap(
|
||||
state.into(), next.into(), AcqRel).into();
|
||||
|
||||
if state == actual {
|
||||
state = next;
|
||||
break;
|
||||
}
|
||||
|
||||
state = actual;
|
||||
}
|
||||
|
||||
trace!(" -> transitioned to shutdown");
|
||||
|
||||
// Only transition to terminate if there are no futures currently on the
|
||||
// pool
|
||||
if state.num_futures() != 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.terminate_sleeping_workers();
|
||||
}
|
||||
|
||||
pub fn terminate_sleeping_workers(&self) {
|
||||
trace!(" -> shutting down workers");
|
||||
// Wakeup all sleeping workers. They will wake up, see the state
|
||||
// transition, and terminate.
|
||||
while let Some((idx, worker_state)) = self.pop_sleeper(WORKER_SIGNALED, TERMINATED) {
|
||||
trace!(" -> shutdown worker; idx={:?}; state={:?}", idx, worker_state);
|
||||
self.signal_stop(idx, worker_state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Signals to the worker that it should stop
|
||||
fn signal_stop(&self, idx: usize, mut state: WorkerState) {
|
||||
let worker = &self.workers[idx];
|
||||
|
||||
// Transition the worker state to signaled
|
||||
loop {
|
||||
let mut next = state;
|
||||
|
||||
match state.lifecycle() {
|
||||
WORKER_SHUTDOWN => {
|
||||
trace!("signal_stop -- WORKER_SHUTDOWN; idx={}", idx);
|
||||
// If the worker is in the shutdown state, then it will never be
|
||||
// started again.
|
||||
self.worker_terminated();
|
||||
|
||||
return;
|
||||
}
|
||||
WORKER_RUNNING | WORKER_SLEEPING => {}
|
||||
_ => {
|
||||
trace!("signal_stop -- skipping; idx={}; state={:?}", idx, state);
|
||||
// All other states will naturally converge to a state of
|
||||
// shutdown.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
next.set_lifecycle(WORKER_SIGNALED);
|
||||
|
||||
let actual = worker.state.compare_and_swap(
|
||||
state.into(), next.into(), AcqRel).into();
|
||||
|
||||
if actual == state {
|
||||
break;
|
||||
}
|
||||
|
||||
state = actual;
|
||||
}
|
||||
|
||||
// Wakeup the worker
|
||||
worker.wakeup();
|
||||
}
|
||||
|
||||
pub fn worker_terminated(&self) {
|
||||
let prev = self.num_workers.fetch_sub(1, AcqRel);
|
||||
|
||||
trace!("worker_terminated; num_workers={}", prev - 1);
|
||||
|
||||
if 1 == prev {
|
||||
trace!("notifying shutdown task");
|
||||
self.shutdown_task.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/// Submit a task to the scheduler.
|
||||
///
|
||||
/// Called from either inside or outside of the scheduler. If currently on
|
||||
/// the scheduler, then a fast path is taken.
|
||||
pub fn submit(&self, task: Task, inner: &Arc<Inner>) {
|
||||
Worker::with_current(|worker| {
|
||||
match worker {
|
||||
Some(worker) => {
|
||||
let idx = worker.id.idx;
|
||||
|
||||
trace!(" -> submit internal; idx={}", idx);
|
||||
|
||||
worker.inner.workers[idx].submit_internal(task);
|
||||
worker.inner.signal_work(inner);
|
||||
}
|
||||
None => {
|
||||
self.submit_external(task, inner);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Submit a task to the scheduler from off worker
|
||||
///
|
||||
/// Called from outside of the scheduler, this function is how new tasks
|
||||
/// enter the system.
|
||||
fn submit_external(&self, task: Task, inner: &Arc<Inner>) {
|
||||
// First try to get a handle to a sleeping worker. This ensures that
|
||||
// sleeping tasks get woken up
|
||||
if let Some((idx, state)) = self.pop_sleeper(WORKER_NOTIFIED, EMPTY) {
|
||||
trace!("submit to existing worker; idx={}; state={:?}", idx, state);
|
||||
self.submit_to_external(idx, task, state, inner);
|
||||
return;
|
||||
}
|
||||
|
||||
// All workers are active, so pick a random worker and submit the
|
||||
// task to it.
|
||||
let len = self.workers.len();
|
||||
let idx = self.rand_usize() % len;
|
||||
|
||||
trace!(" -> submitting to random; idx={}", idx);
|
||||
|
||||
let state: WorkerState = self.workers[idx].state.load(Acquire).into();
|
||||
self.submit_to_external(idx, task, state, inner);
|
||||
}
|
||||
|
||||
fn submit_to_external(&self,
|
||||
idx: usize,
|
||||
task: Task,
|
||||
state: WorkerState,
|
||||
inner: &Arc<Inner>)
|
||||
{
|
||||
let entry = &self.workers[idx];
|
||||
|
||||
if !entry.submit_external(task, state) {
|
||||
Worker::spawn(WorkerId::new(idx), inner);
|
||||
}
|
||||
}
|
||||
|
||||
/// If there are any other workers currently relaxing, signal them that work
|
||||
/// is available so that they can try to find more work to process.
|
||||
pub fn signal_work(&self, inner: &Arc<Inner>) {
|
||||
if let Some((idx, mut state)) = self.pop_sleeper(WORKER_SIGNALED, EMPTY) {
|
||||
let entry = &self.workers[idx];
|
||||
|
||||
// Transition the worker state to signaled
|
||||
loop {
|
||||
let mut next = state;
|
||||
|
||||
// pop_sleeper should skip these
|
||||
debug_assert!(state.lifecycle() != WORKER_SIGNALED);
|
||||
next.set_lifecycle(WORKER_SIGNALED);
|
||||
|
||||
let actual = entry.state.compare_and_swap(
|
||||
state.into(), next.into(), AcqRel).into();
|
||||
|
||||
if actual == state {
|
||||
break;
|
||||
}
|
||||
|
||||
state = actual;
|
||||
}
|
||||
|
||||
// The state has been transitioned to signal, now we need to wake up
|
||||
// the worker if necessary.
|
||||
match state.lifecycle() {
|
||||
WORKER_SLEEPING => {
|
||||
trace!("signal_work -- wakeup; idx={}", idx);
|
||||
self.workers[idx].wakeup();
|
||||
}
|
||||
WORKER_SHUTDOWN => {
|
||||
trace!("signal_work -- spawn; idx={}", idx);
|
||||
Worker::spawn(WorkerId::new(idx), inner);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Push a worker on the sleep stack
|
||||
///
|
||||
/// Returns `Err` if the pool has been terminated
|
||||
pub fn push_sleeper(&self, idx: usize) -> Result<(), ()> {
|
||||
let mut state: SleepStack = self.sleep_stack.load(Acquire).into();
|
||||
|
||||
debug_assert!(WorkerState::from(self.workers[idx].state.load(Relaxed)).is_pushed());
|
||||
|
||||
loop {
|
||||
let mut next = state;
|
||||
|
||||
let head = state.head();
|
||||
|
||||
if head == TERMINATED {
|
||||
// The pool is terminated, cannot push the sleeper.
|
||||
return Err(());
|
||||
}
|
||||
|
||||
self.workers[idx].set_next_sleeper(head);
|
||||
next.set_head(idx);
|
||||
|
||||
let actual = self.sleep_stack.compare_and_swap(
|
||||
state.into(), next.into(), AcqRel).into();
|
||||
|
||||
if state == actual {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
state = actual;
|
||||
}
|
||||
}
|
||||
|
||||
/// Pop a worker from the sleep stack
|
||||
fn pop_sleeper(&self, max_lifecycle: usize, terminal: usize)
|
||||
-> Option<(usize, WorkerState)>
|
||||
{
|
||||
debug_assert!(terminal == EMPTY || terminal == TERMINATED);
|
||||
|
||||
let mut state: SleepStack = self.sleep_stack.load(Acquire).into();
|
||||
|
||||
loop {
|
||||
let head = state.head();
|
||||
|
||||
if head == EMPTY {
|
||||
let mut next = state;
|
||||
next.set_head(terminal);
|
||||
|
||||
if next == state {
|
||||
debug_assert!(terminal == EMPTY);
|
||||
return None;
|
||||
}
|
||||
|
||||
let actual = self.sleep_stack.compare_and_swap(
|
||||
state.into(), next.into(), AcqRel).into();
|
||||
|
||||
if actual != state {
|
||||
state = actual;
|
||||
continue;
|
||||
}
|
||||
|
||||
return None;
|
||||
} else if head == TERMINATED {
|
||||
return None;
|
||||
}
|
||||
|
||||
debug_assert!(head < MAX_WORKERS);
|
||||
|
||||
let mut next = state;
|
||||
|
||||
let next_head = self.workers[head].next_sleeper();
|
||||
|
||||
// TERMINATED can never be set as the "next pointer" on a worker.
|
||||
debug_assert!(next_head != TERMINATED);
|
||||
|
||||
if next_head == EMPTY {
|
||||
next.set_head(terminal);
|
||||
} else {
|
||||
next.set_head(next_head);
|
||||
}
|
||||
|
||||
let actual = self.sleep_stack.compare_and_swap(
|
||||
state.into(), next.into(), AcqRel).into();
|
||||
|
||||
if actual == state {
|
||||
// The worker has been removed from the stack, so the pushed bit
|
||||
// can be unset. Release ordering is used to ensure that this
|
||||
// operation happens after actually popping the task.
|
||||
debug_assert_eq!(1, PUSHED_MASK);
|
||||
|
||||
// Unset the PUSHED flag and get the current state.
|
||||
let state: WorkerState = self.workers[head].state
|
||||
.fetch_sub(PUSHED_MASK, Release).into();
|
||||
|
||||
if state.lifecycle() >= max_lifecycle {
|
||||
// If the worker has already been notified, then it is
|
||||
// warming up to do more work. In this case, try to pop
|
||||
// another thread that might be in a relaxed state.
|
||||
continue;
|
||||
}
|
||||
|
||||
return Some((head, state));
|
||||
}
|
||||
|
||||
state = actual;
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a random number
|
||||
///
|
||||
/// Uses a thread-local seeded XorShift.
|
||||
pub fn rand_usize(&self) -> usize {
|
||||
// Use a thread-local random number generator. If the thread does not
|
||||
// have one yet, then seed a new one
|
||||
thread_local!(static THREAD_RNG_KEY: UnsafeCell<Option<XorShiftRng>> = UnsafeCell::new(None));
|
||||
|
||||
THREAD_RNG_KEY.with(|t| {
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
fn new_rng(thread_id: usize) -> XorShiftRng {
|
||||
XorShiftRng::from_seed([
|
||||
thread_id as u32,
|
||||
0x00000000,
|
||||
0xa8a7d469,
|
||||
0x97830e05])
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
fn new_rng(thread_id: usize) -> XorShiftRng {
|
||||
XorShiftRng::from_seed([
|
||||
thread_id as u32,
|
||||
(thread_id >> 32) as u32,
|
||||
0xa8a7d469,
|
||||
0x97830e05])
|
||||
}
|
||||
|
||||
let thread_id = self.next_thread_id.fetch_add(1, Relaxed);
|
||||
let rng = unsafe { &mut *t.get() };
|
||||
|
||||
if rng.is_none() {
|
||||
*rng = Some(new_rng(thread_id));
|
||||
}
|
||||
|
||||
rng.as_mut().unwrap().next_u32() as usize
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Inner {}
|
||||
unsafe impl Sync for Inner {}
|
||||
@@ -1,11 +1,13 @@
|
||||
//! A work-stealing based thread pool for executing futures.
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-threadpool/0.1.2")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-threadpool/0.1.4")]
|
||||
#![deny(warnings, missing_docs, missing_debug_implementations)]
|
||||
|
||||
extern crate tokio_executor;
|
||||
extern crate futures;
|
||||
|
||||
extern crate crossbeam_deque as deque;
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate num_cpus;
|
||||
extern crate rand;
|
||||
|
||||
@@ -17,24 +19,22 @@ extern crate futures2;
|
||||
|
||||
pub mod park;
|
||||
|
||||
mod blocking;
|
||||
mod builder;
|
||||
mod callback;
|
||||
mod config;
|
||||
mod inner;
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
mod futures2_wake;
|
||||
mod notifier;
|
||||
mod pool;
|
||||
mod sender;
|
||||
mod shutdown;
|
||||
mod shutdown_task;
|
||||
mod sleep_stack;
|
||||
mod state;
|
||||
mod task;
|
||||
mod thread_pool;
|
||||
mod worker;
|
||||
mod worker_entry;
|
||||
mod worker_state;
|
||||
|
||||
pub use blocking::{blocking, BlockingError};
|
||||
pub use builder::Builder;
|
||||
pub use sender::Sender;
|
||||
pub use shutdown::Shutdown;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user