mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
109
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07203408de | ||
|
|
8bf2e9aeb0 | ||
|
|
cf184eb326 | ||
|
|
d822b721b4 | ||
|
|
c66b56c3fb | ||
|
|
89639ec48b | ||
|
|
2b1b0ac858 | ||
|
|
6b84c73f12 | ||
|
|
767b370c21 | ||
|
|
28010b5962 | ||
|
|
2e343f9e42 | ||
|
|
31f71dedee | ||
|
|
989262fe6e | ||
|
|
d91c775f36 | ||
|
|
96b556fbff | ||
|
|
fd36054ae4 | ||
|
|
89d6bfc5cb | ||
|
|
decc83e959 | ||
|
|
afcfefd7e3 | ||
|
|
c89b0b4c8c | ||
|
|
6b1e4ab0a3 | ||
|
|
4153cc4076 | ||
|
|
5304557d1d | ||
|
|
fdb2f61357 | ||
|
|
e964c4136c | ||
|
|
0490280d66 | ||
|
|
0f76470172 | ||
|
|
9352249c3e | ||
|
|
e5b2681513 | ||
|
|
629c9f0698 | ||
|
|
5d0d2a2e12 | ||
|
|
ad4693a18f | ||
|
|
c85bde3170 | ||
|
|
1e90e27720 | ||
|
|
f212a2ab9d | ||
|
|
84db325628 | ||
|
|
365efec24a | ||
|
|
491f15827b | ||
|
|
c17ecb53e7 | ||
|
|
6ba8e7621d | ||
|
|
39c95d6206 | ||
|
|
78b6bd4ca5 | ||
|
|
b3ff9e315c | ||
|
|
990186ec9d | ||
|
|
19da6ff59a | ||
|
|
36c817f0c3 | ||
|
|
35123f7ae4 | ||
|
|
54b7c1b10d | ||
|
|
e6fc3d209d | ||
|
|
f98b81e527 | ||
|
|
dc7202cfa9 | ||
|
|
f1a7caea3f | ||
|
|
b019532bc2 | ||
|
|
7fb579c667 | ||
|
|
dbefa67058 | ||
|
|
24d99c029e | ||
|
|
3fecd0154c | ||
|
|
0440343a11 | ||
|
|
3cf56b7bfa | ||
|
|
7153d8d6ce | ||
|
|
ecfe2f6a05 | ||
|
|
5753553ba3 | ||
|
|
04a4bfd455 | ||
|
|
b2f77dcebe | ||
|
|
85cf47de86 | ||
|
|
45bcea6c4f | ||
|
|
3fac7ce68c | ||
|
|
71c8f561e3 | ||
|
|
011ebf44eb | ||
|
|
c25ea78ec9 | ||
|
|
2e0cd292d2 | ||
|
|
4ebaf18c27 | ||
|
|
ab07733d66 | ||
|
|
d1f825ca13 | ||
|
|
4cf7d73b22 | ||
|
|
2cd854c2c7 | ||
|
|
ba05c39d65 | ||
|
|
64b8884911 | ||
|
|
d391e63418 | ||
|
|
8d8c895a1c | ||
|
|
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 |
+6
-8
@@ -15,7 +15,7 @@ matrix:
|
||||
# This represents the minimum Rust version supported by Tokio. Updating this
|
||||
# should be done in a dedicated PR and cannot be greater than two 0.x
|
||||
# releases prior to the current stable.
|
||||
- rust: 1.21.0
|
||||
- rust: 1.25.0
|
||||
- rust: stable
|
||||
- rust: beta
|
||||
- rust: nightly
|
||||
@@ -29,14 +29,12 @@ 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"
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
# === tokio-timer ====
|
||||
|
||||
@@ -52,19 +50,19 @@ script:
|
||||
|
||||
# Run address sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test -p tokio-threadpool --tests
|
||||
cargo test -p tokio-threadpool --tests --target x86_64-unknown-linux-gnu
|
||||
|
||||
# Run thread sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
cargo test -p tokio-threadpool --tests
|
||||
cargo test -p tokio-threadpool --tests --target x86_64-unknown-linux-gnu
|
||||
fi
|
||||
- |
|
||||
set -e
|
||||
if [[ "$TARGET" ]]
|
||||
then
|
||||
rustup target add $TARGET
|
||||
cargo check --all --target $TARGET
|
||||
cargo check --tests --all --target $TARGET
|
||||
cargo check --all --exclude tokio-tls --target $TARGET
|
||||
cargo check --tests --all --exclude tokio-tls --target $TARGET
|
||||
else
|
||||
cargo test --all
|
||||
# Disable these tests for now as they are buggy
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
This changelog only applies to the `tokio` crate proper. Each sub crate
|
||||
maintains its own changelog tracking changes made in each respective sub crate.
|
||||
|
||||
# 0.1.8 (August 23, 2018)
|
||||
|
||||
* Extract tokio::executor::current_thread to a sub crate (#370)
|
||||
* Add `Runtime::block_on` (#398)
|
||||
* Add `runtime::current_thread::block_on_all` (#477)
|
||||
* Misc documentation improvements (#450)
|
||||
* Implement `std::error::Error` for error types (#501)
|
||||
|
||||
# 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).
|
||||
|
||||
+16
-7
@@ -4,14 +4,15 @@ name = "tokio"
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.6"
|
||||
version = "0.1.8"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio/0.1.8/tokio/"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio/0.1"
|
||||
description = """
|
||||
An event-driven, non-blocking I/O platform for writing asynchronous I/O
|
||||
backed applications.
|
||||
@@ -23,6 +24,8 @@ keywords = ["io", "async", "non-blocking", "futures"]
|
||||
|
||||
members = [
|
||||
"./",
|
||||
"tokio-codec",
|
||||
"tokio-current-thread",
|
||||
"tokio-executor",
|
||||
"tokio-fs",
|
||||
"tokio-io",
|
||||
@@ -30,8 +33,9 @@ members = [
|
||||
"tokio-threadpool",
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
"tokio-tls",
|
||||
"tokio-udp",
|
||||
"futures2",
|
||||
"tokio-uds",
|
||||
]
|
||||
|
||||
[badges]
|
||||
@@ -39,23 +43,28 @@ travis-ci = { repository = "tokio-rs/tokio" }
|
||||
appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" }
|
||||
|
||||
[dependencies]
|
||||
tokio-codec = { version = "0.1.0", path = "tokio-codec" }
|
||||
tokio-current-thread = { version = "0.1.1", path = "tokio-current-thread" }
|
||||
tokio-io = { version = "0.1.6", path = "tokio-io" }
|
||||
tokio-executor = { version = "0.1.2", path = "tokio-executor" }
|
||||
tokio-reactor = { version = "0.1.1", path = "tokio-reactor" }
|
||||
tokio-threadpool = { version = "0.1.2", 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.1", path = "tokio-timer" }
|
||||
tokio-fs = { version = "0.1.0", path = "tokio-fs" }
|
||||
tokio-timer = { version = "0.2.6", path = "tokio-timer" }
|
||||
tokio-fs = { version = "0.1.3", path = "tokio-fs" }
|
||||
|
||||
futures = "0.1.20"
|
||||
|
||||
# Needed until `reactor` is removed from `tokio`.
|
||||
mio = "0.6.14"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
tokio-uds = { version = "0.2.0", path = "tokio-uds" }
|
||||
|
||||
[dev-dependencies]
|
||||
bytes = "0.4"
|
||||
env_logger = { version = "0.4", default-features = false }
|
||||
env_logger = { version = "0.5", default-features = false }
|
||||
flate2 = { version = "1", features = ["tokio"] }
|
||||
futures-cpupool = "0.1"
|
||||
http = "0.1"
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -49,7 +53,7 @@ These components provide the runtime components necessary for building
|
||||
an asynchronous application.
|
||||
|
||||
[net]: https://docs.rs/tokio/0.1/tokio/net/index.html
|
||||
[reactor]: https://docs.rs/tokio/0.1.1/tokio/reactor/index.html
|
||||
[reactor]: https://docs.rs/tokio/0.1/tokio/reactor/index.html
|
||||
[scheduler]: https://tokio-rs.github.io/tokio/tokio/runtime/index.html
|
||||
|
||||
## Example
|
||||
@@ -107,6 +111,11 @@ have greater guarantees of stability.
|
||||
|
||||
The crates included as part of Tokio are:
|
||||
|
||||
* [`tokio-codec`]: Utilities for encoding and decoding protocol frames.
|
||||
|
||||
* [`tokio-current-thread`]: Schedule the execution of futures on the current
|
||||
thread.
|
||||
|
||||
* [`tokio-executor`]: Task execution related traits and utilities.
|
||||
|
||||
* [`tokio-fs`]: Filesystem (and standard in / out) APIs.
|
||||
@@ -125,6 +134,11 @@ The crates included as part of Tokio are:
|
||||
|
||||
* [`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-codec`]: tokio-codec
|
||||
[`tokio-current-thread`]: tokio-current-thread
|
||||
[`tokio-executor`]: tokio-executor
|
||||
[`tokio-fs`]: tokio-fs
|
||||
[`tokio-io`]: tokio-io
|
||||
@@ -133,6 +147,7 @@ The crates included as part of Tokio are:
|
||||
[`tokio-threadpool`]: tokio-threadpool
|
||||
[`tokio-timer`]: tokio-timer
|
||||
[`tokio-udp`]: tokio-udp
|
||||
[`tokio-uds`]: tokio-uds
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ mod prelude {
|
||||
pub use futures::*;
|
||||
pub use tokio::reactor::Reactor;
|
||||
pub use tokio::net::{TcpListener, TcpStream};
|
||||
pub use tokio::executor::current_thread;
|
||||
pub use tokio_io::io::read_to_end;
|
||||
|
||||
pub use test::{self, Bencher};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# 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
|
||||
race:Weak*drop
|
||||
|
||||
# `std` mpsc is not used in any Tokio code base. This race is triggered by some
|
||||
# rust runtime logic.
|
||||
@@ -12,15 +12,14 @@ race:std*mpsc_queue
|
||||
# Probably more fences in std.
|
||||
race:__call_tls_dtors
|
||||
|
||||
# The crossbeam deque uses fences.
|
||||
race:crossbeam_deque
|
||||
# The epoch-based GC uses fences.
|
||||
race:crossbeam_epoch
|
||||
|
||||
# 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
|
||||
# Push and steal operations in crossbeam-deque may cause data races, but such
|
||||
# data races are safe. If a data race happens, the value read by `steal` is
|
||||
# forgotten and the steal operation is then retried.
|
||||
race:crossbeam_deque*push
|
||||
race:crossbeam_deque*steal
|
||||
|
||||
# This filters out expected data race in the treiber stack implementations.
|
||||
# Treiber stacks are inherently racy. The pop operation will attempt to access
|
||||
|
||||
+3
-1
@@ -38,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.
|
||||
@@ -53,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!
|
||||
|
||||
+3
-3
@@ -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.
|
||||
@@ -290,7 +290,7 @@ impl Lines {
|
||||
fn poll_flush(&mut self) -> Poll<(), io::Error> {
|
||||
// As long as there is buffered data to write, try to write it.
|
||||
while !self.wr.is_empty() {
|
||||
// Try to read some bytes from the socket
|
||||
// Try to write some bytes to the socket
|
||||
let n = try_ready!(self.socket.poll_write(&self.wr));
|
||||
|
||||
// As long as the wr is not empty, a successful write should
|
||||
|
||||
+3
-2
@@ -82,7 +82,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.
|
||||
///
|
||||
@@ -122,6 +122,7 @@ mod tcp {
|
||||
use tokio;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
use tokio::codec::Decoder;
|
||||
|
||||
use bytes::BytesMut;
|
||||
use codec::Bytes;
|
||||
@@ -151,7 +152,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,86 @@
|
||||
//! 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_current_thread;
|
||||
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_current_thread::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 = tokio_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 tokio_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)
|
||||
tokio_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();
|
||||
}
|
||||
@@ -55,11 +55,12 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_codec;
|
||||
|
||||
use tokio_io::codec::BytesCodec;
|
||||
use tokio_codec::BytesCodec;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
use tokio::codec::Decoder;
|
||||
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
@@ -99,8 +100,8 @@ fn main() {
|
||||
// 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-io/0.1/src/tokio_io/codec/bytes_codec.rs.html
|
||||
let framed = socket.framed(BytesCodec::new());
|
||||
// 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
|
||||
|
||||
+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.
|
||||
//!
|
||||
|
||||
@@ -28,8 +28,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 +54,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"
|
||||
@@ -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`][n] 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;
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
//! Execute many tasks concurrently on the current thread.
|
||||
//!
|
||||
//! [`CurrentThread`] is an executor that keeps tasks on the same thread that
|
||||
@@ -102,69 +104,24 @@
|
||||
//! [`CurrentThread`]: struct.CurrentThread.html
|
||||
//! [`Future::poll`]: https://docs.rs/futures/0.1/futures/future/trait.Future.html#tymethod.poll
|
||||
|
||||
#![allow(deprecated)]
|
||||
pub use tokio_current_thread::{
|
||||
BlockError,
|
||||
CurrentThread,
|
||||
Entered,
|
||||
Handle,
|
||||
RunError,
|
||||
RunTimeoutError,
|
||||
TaskExecutor,
|
||||
Turn,
|
||||
TurnError,
|
||||
block_on_all,
|
||||
spawn,
|
||||
};
|
||||
|
||||
mod scheduler;
|
||||
use self::scheduler::Scheduler;
|
||||
|
||||
use tokio_executor::{self, Enter, SpawnError};
|
||||
use tokio_executor::park::{Park, Unpark, ParkThread};
|
||||
|
||||
use futures::{executor, Async, Future};
|
||||
use futures::future::{self, Executor, ExecuteError, ExecuteErrorKind};
|
||||
|
||||
use std::fmt;
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Executes tasks on the current thread
|
||||
pub struct CurrentThread<P: Park = ParkThread> {
|
||||
/// Execute futures and receive unpark notifications.
|
||||
scheduler: Scheduler<P::Unpark>,
|
||||
|
||||
/// Current number of futures being executed
|
||||
num_futures: usize,
|
||||
|
||||
/// Thread park handle
|
||||
park: P,
|
||||
}
|
||||
|
||||
/// Executes futures on the current thread.
|
||||
///
|
||||
/// All futures executed using this executor will be executed on the current
|
||||
/// thread. As such, `run` will wait for these futures to complete before
|
||||
/// returning.
|
||||
///
|
||||
/// For more details, see the [module level](index.html) documentation.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TaskExecutor {
|
||||
// Prevent the handle from moving across threads.
|
||||
_p: ::std::marker::PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
/// Returned by the `turn` function.
|
||||
#[derive(Debug)]
|
||||
pub struct Turn {
|
||||
polled: bool
|
||||
}
|
||||
|
||||
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 conext.
|
||||
pub struct Entered<'a, P: Park + 'a> {
|
||||
executor: &'a mut CurrentThread<P>,
|
||||
enter: &'a mut Enter,
|
||||
}
|
||||
use futures::future::{self};
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "use block_on_all instead")]
|
||||
#[doc(hidden)]
|
||||
@@ -174,54 +131,17 @@ pub struct Context<'a> {
|
||||
_p: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
/// Error returned by the `run` function.
|
||||
#[derive(Debug)]
|
||||
pub struct RunError {
|
||||
_p: (),
|
||||
impl<'a> Context<'a> {
|
||||
/// Cancels *all* executing futures.
|
||||
pub fn cancel_all_spawned(&self) {
|
||||
self.cancel.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned by the `run_timeout` function.
|
||||
#[derive(Debug)]
|
||||
pub struct RunTimeoutError {
|
||||
timeout: bool,
|
||||
}
|
||||
|
||||
/// Error returned by the `turn` function.
|
||||
#[derive(Debug)]
|
||||
pub struct TurnError {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
/// Error returned by the `block_on` function.
|
||||
#[derive(Debug)]
|
||||
pub struct BlockError<T> {
|
||||
inner: Option<T>,
|
||||
}
|
||||
|
||||
/// This is mostly split out to make the borrow checker happy.
|
||||
struct Borrow<'a, U: 'a> {
|
||||
scheduler: &'a mut Scheduler<U>,
|
||||
num_futures: &'a mut usize,
|
||||
}
|
||||
|
||||
trait SpawnLocal {
|
||||
fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>);
|
||||
}
|
||||
|
||||
struct CurrentRunner {
|
||||
spawn: Cell<Option<*mut SpawnLocal>>,
|
||||
}
|
||||
|
||||
/// Current thread's task runner. This is set in `TaskRunner::with`
|
||||
thread_local!(static CURRENT: CurrentRunner = CurrentRunner {
|
||||
spawn: Cell::new(None),
|
||||
});
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "use block_on_all instead")]
|
||||
#[doc(hidden)]
|
||||
#[allow(deprecated)]
|
||||
pub fn run<F, R>(f: F) -> R
|
||||
where F: FnOnce(&mut Context) -> R
|
||||
where F: FnOnce(&mut Context) -> R
|
||||
{
|
||||
let mut context = Context {
|
||||
cancel: Cell::new(false),
|
||||
@@ -242,520 +162,9 @@ where F: FnOnce(&mut Context) -> R
|
||||
ret
|
||||
}
|
||||
|
||||
/// Run the executor bootstrapping the execution with the provided future.
|
||||
///
|
||||
/// This creates a new [`CurrentThread`] executor, spawns the provided future,
|
||||
/// 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,
|
||||
/// `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.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [`CurrentThread`]: struct.CurrentThread.html
|
||||
/// [mod]: index.html
|
||||
pub fn block_on_all<F>(future: F) -> Result<F::Item, F::Error>
|
||||
where F: Future,
|
||||
{
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
let ret = current_thread.block_on(future);
|
||||
current_thread.run().unwrap();
|
||||
|
||||
ret.map_err(|e| e.into_inner().expect("unexpected execution error"))
|
||||
}
|
||||
|
||||
/// Executes a future on the current thread.
|
||||
///
|
||||
/// The provided future must complete or be canceled before `run` will return.
|
||||
///
|
||||
/// Unlike [`tokio::spawn`], this function will always spawn on a
|
||||
/// `CurrentThread` executor and is able to spawn futures that are not `Send`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function can only be invoked from the context of a `run` call; any
|
||||
/// other use will result in a panic.
|
||||
///
|
||||
/// [`tokio::spawn`]: ../fn.spawn.html
|
||||
pub fn spawn<F>(future: F)
|
||||
where F: Future<Item = (), Error = ()> + 'static
|
||||
{
|
||||
TaskExecutor::current()
|
||||
.spawn_local(Box::new(future))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// ===== impl CurrentThread =====
|
||||
|
||||
impl CurrentThread<ParkThread> {
|
||||
/// Create a new instance of `CurrentThread`.
|
||||
pub fn new() -> Self {
|
||||
CurrentThread::new_with_park(ParkThread::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Park> CurrentThread<P> {
|
||||
/// Create a new instance of `CurrentThread` backed by the given park
|
||||
/// handle.
|
||||
pub fn new_with_park(park: P) -> Self {
|
||||
let unpark = park.unpark();
|
||||
|
||||
CurrentThread {
|
||||
scheduler: Scheduler::new(unpark),
|
||||
num_futures: 0,
|
||||
park,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the executor is currently idle.
|
||||
///
|
||||
/// An idle executor is defined by not currently having any spawned tasks.
|
||||
pub fn is_idle(&self) -> bool {
|
||||
self.num_futures == 0
|
||||
}
|
||||
|
||||
/// Spawn the future on the executor.
|
||||
///
|
||||
/// This internally queues the future to be executed once `run` is called.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where F: Future<Item = (), Error = ()> + 'static,
|
||||
{
|
||||
self.borrow().spawn_local(Box::new(future));
|
||||
self
|
||||
}
|
||||
|
||||
/// Synchronously waits for the provided `future` to complete.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution.
|
||||
pub fn block_on<F>(&mut self, future: F)
|
||||
-> Result<F::Item, BlockError<F::Error>>
|
||||
where F: Future
|
||||
{
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
self.enter(&mut enter).block_on(future)
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until **all**
|
||||
/// spawned futures have completed.
|
||||
pub fn run(&mut self) -> Result<(), RunError> {
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
self.enter(&mut enter).run()
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until all
|
||||
/// spawned futures have completed **or** `duration` time has elapsed.
|
||||
pub fn run_timeout(&mut self, duration: Duration)
|
||||
-> Result<(), RunTimeoutError>
|
||||
{
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
self.enter(&mut enter).run_timeout(duration)
|
||||
}
|
||||
|
||||
/// Perform a single iteration of the event loop.
|
||||
///
|
||||
/// This function blocks the current thread even if the executor is idle.
|
||||
pub fn turn(&mut self, duration: Option<Duration>)
|
||||
-> Result<Turn, TurnError>
|
||||
{
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
self.enter(&mut enter).turn(duration)
|
||||
}
|
||||
|
||||
/// Bind `CurrentThread` instance with an execution context.
|
||||
pub fn enter<'a>(&'a mut self, enter: &'a mut Enter) -> Entered<'a, P> {
|
||||
Entered {
|
||||
executor: self,
|
||||
enter,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_executor::Executor for CurrentThread {
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
self.borrow().spawn_local(future);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
fn spawn2(&mut self, _future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
|
||||
-> Result<(), futures2::executor::SpawnError>
|
||||
{
|
||||
panic!("Futures 0.2 integration is not available for current_thread");
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Park> fmt::Debug for CurrentThread<P> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("CurrentThread")
|
||||
.field("scheduler", &self.scheduler)
|
||||
.field("num_futures", &self.num_futures)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Entered =====
|
||||
|
||||
impl<'a, P: Park> Entered<'a, P> {
|
||||
/// Spawn the future on the executor.
|
||||
///
|
||||
/// This internally queues the future to be executed once `run` is called.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where F: Future<Item = (), Error = ()> + 'static,
|
||||
{
|
||||
self.executor.borrow().spawn_local(Box::new(future));
|
||||
self
|
||||
}
|
||||
|
||||
/// Synchronously waits for the provided `future` to complete.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution.
|
||||
pub fn block_on<F>(&mut self, future: F)
|
||||
-> Result<F::Item, BlockError<F::Error>>
|
||||
where F: Future
|
||||
{
|
||||
let mut future = executor::spawn(future);
|
||||
let notify = self.executor.scheduler.notify();
|
||||
|
||||
loop {
|
||||
let res = self.executor.borrow().enter(self.enter, || {
|
||||
future.poll_future_notify(¬ify, 0)
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(Async::Ready(e)) => return Ok(e),
|
||||
Err(e) => return Err(BlockError { inner: Some(e) }),
|
||||
Ok(Async::NotReady) => {}
|
||||
}
|
||||
|
||||
self.tick();
|
||||
|
||||
if let Err(_) = self.executor.park.park() {
|
||||
return Err(BlockError { inner: None });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until **all**
|
||||
/// spawned futures have completed.
|
||||
pub fn run(&mut self) -> Result<(), RunError> {
|
||||
self.run_timeout2(None)
|
||||
.map_err(|_| RunError { _p: () })
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until all
|
||||
/// spawned futures have completed **or** `duration` time has elapsed.
|
||||
pub fn run_timeout(&mut self, duration: Duration)
|
||||
-> Result<(), RunTimeoutError>
|
||||
{
|
||||
self.run_timeout2(Some(duration))
|
||||
}
|
||||
|
||||
/// Perform a single iteration of the event loop.
|
||||
///
|
||||
/// This function blocks the current thread even if the executor is idle.
|
||||
pub fn turn(&mut self, duration: Option<Duration>)
|
||||
-> Result<Turn, TurnError>
|
||||
{
|
||||
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: () });
|
||||
}
|
||||
|
||||
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>)
|
||||
-> Result<(), RunTimeoutError>
|
||||
{
|
||||
if self.executor.is_idle() {
|
||||
// Nothing to do
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut time = dur.map(|dur| (Instant::now() + dur, dur));
|
||||
|
||||
loop {
|
||||
self.tick();
|
||||
|
||||
if self.executor.is_idle() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match time {
|
||||
Some((until, rem)) => {
|
||||
if let Err(_) = self.executor.park.park_timeout(rem) {
|
||||
return Err(RunTimeoutError::new(false));
|
||||
}
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
if now >= until {
|
||||
return Err(RunTimeoutError::new(true));
|
||||
}
|
||||
|
||||
time = Some((until, until - now));
|
||||
}
|
||||
None => {
|
||||
if let Err(_) = self.executor.park.park() {
|
||||
return Err(RunTimeoutError::new(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if any futures were processed
|
||||
fn tick(&mut self) -> bool {
|
||||
self.executor.scheduler.tick(
|
||||
&mut *self.enter,
|
||||
&mut self.executor.num_futures)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: Park> fmt::Debug for Entered<'a, P> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Entered")
|
||||
.field("executor", &self.executor)
|
||||
.field("enter", &self.enter)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl TaskExecutor =====
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "use TaskExecutor::current instead")]
|
||||
#[doc(hidden)]
|
||||
pub fn task_executor() -> TaskExecutor {
|
||||
TaskExecutor {
|
||||
_p: ::std::marker::PhantomData,
|
||||
}
|
||||
TaskExecutor::current()
|
||||
}
|
||||
|
||||
impl TaskExecutor {
|
||||
/// Returns an executor that executes futures on the current thread.
|
||||
///
|
||||
/// The user of `TaskExecutor` must ensure that when a future is submitted,
|
||||
/// that it is done within the context of a call to `run`.
|
||||
///
|
||||
/// For more details, see the [module level](index.html) documentation.
|
||||
pub fn current() -> TaskExecutor {
|
||||
TaskExecutor {
|
||||
_p: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a future onto the current `CurrentThread` instance.
|
||||
pub fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
CURRENT.with(|current| {
|
||||
match current.spawn.get() {
|
||||
Some(spawn) => {
|
||||
unsafe { (*spawn).spawn_local(future) };
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_executor::Executor for TaskExecutor {
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
self.spawn_local(future)
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
fn spawn2(&mut self, _future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
|
||||
-> Result<(), futures2::executor::SpawnError>
|
||||
{
|
||||
panic!("Futures 0.2 integration is not available for current_thread");
|
||||
}
|
||||
|
||||
fn status(&self) -> Result<(), SpawnError> {
|
||||
CURRENT.with(|current| {
|
||||
if current.spawn.get().is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Executor<F> for TaskExecutor
|
||||
where F: Future<Item = (), Error = ()> + 'static
|
||||
{
|
||||
fn execute(&self, future: F) -> Result<(), ExecuteError<F>> {
|
||||
CURRENT.with(|current| {
|
||||
match current.spawn.get() {
|
||||
Some(spawn) => {
|
||||
unsafe { (*spawn).spawn_local(Box::new(future)) };
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
Err(ExecuteError::new(ExecuteErrorKind::Shutdown, future))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Context =====
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
/// Cancels *all* executing futures.
|
||||
pub fn cancel_all_spawned(&self) {
|
||||
self.cancel.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Borrow =====
|
||||
|
||||
impl<'a, U: Unpark> Borrow<'a, U> {
|
||||
fn enter<F, R>(&mut self, _: &mut Enter, f: F) -> R
|
||||
where F: FnOnce() -> R,
|
||||
{
|
||||
CURRENT.with(|current| {
|
||||
current.set_spawn(self, || {
|
||||
f()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> SpawnLocal for Borrow<'a, U> {
|
||||
fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>) {
|
||||
*self.num_futures += 1;
|
||||
self.scheduler.schedule(future);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl CurrentRunner =====
|
||||
|
||||
impl CurrentRunner {
|
||||
fn set_spawn<F, R>(&self, spawn: &mut SpawnLocal, f: F) -> R
|
||||
where F: FnOnce() -> R
|
||||
{
|
||||
struct Reset<'a>(&'a CurrentRunner);
|
||||
|
||||
impl<'a> Drop for Reset<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.0.spawn.set(None);
|
||||
}
|
||||
}
|
||||
|
||||
let _reset = Reset(self);
|
||||
|
||||
let spawn = unsafe { hide_lt(spawn as *mut SpawnLocal) };
|
||||
self.spawn.set(Some(spawn));
|
||||
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn hide_lt<'a>(p: *mut (SpawnLocal + 'a)) -> *mut (SpawnLocal + 'static) {
|
||||
use std::mem;
|
||||
mem::transmute(p)
|
||||
}
|
||||
|
||||
// ===== impl RunTimeoutError =====
|
||||
|
||||
impl RunTimeoutError {
|
||||
fn new(timeout: bool) -> Self {
|
||||
RunTimeoutError { timeout }
|
||||
}
|
||||
|
||||
/// Returns `true` if the error was caused by the operation timeing out.
|
||||
pub fn is_timeout(&self) -> bool {
|
||||
self.timeout
|
||||
}
|
||||
}
|
||||
|
||||
impl From<tokio_executor::EnterError> for RunTimeoutError {
|
||||
fn from(_: tokio_executor::EnterError) -> Self {
|
||||
RunTimeoutError::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl BlockError =====
|
||||
|
||||
impl<T> BlockError<T> {
|
||||
/// Returns the error yielded by the future being blocked on
|
||||
pub fn into_inner(self) -> Option<T> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<tokio_executor::EnterError> for BlockError<T> {
|
||||
fn from(_: tokio_executor::EnterError) -> Self {
|
||||
BlockError { inner: None }
|
||||
}
|
||||
}
|
||||
|
||||
+16
-114
@@ -5,7 +5,7 @@
|
||||
//! the future must be submitted to an executor. A future that is submitted to
|
||||
//! an executor is called a "task".
|
||||
//!
|
||||
//! The executor executor is responsible for ensuring that [`Future::poll`] is
|
||||
//! The executor is responsible for ensuring that [`Future::poll`] is
|
||||
//! called whenever the task is [notified]. Notification happens when the
|
||||
//! internal state of a task transitions from "not ready" to ready. For
|
||||
//! example, a socket might have received data and a call to `read` will now be
|
||||
@@ -13,16 +13,8 @@
|
||||
//!
|
||||
//! The specific strategy used to manage the tasks is left up to the
|
||||
//! executor. There are two main flavors of executors: single-threaded and
|
||||
//! multithreaded. This module provides both.
|
||||
//!
|
||||
//! * **[`current_thread`]**: A single-threaded executor that support spawning
|
||||
//! tasks that are not `Send`. It guarantees that tasks will be executed on
|
||||
//! the same thread from which they are spawned.
|
||||
//!
|
||||
//! * **[`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
|
||||
//! spread across the available threads.
|
||||
//! multi-threaded. Tokio provides implementation for both of these in the
|
||||
//! [`runtime`] module.
|
||||
//!
|
||||
//! # `Executor` trait.
|
||||
//!
|
||||
@@ -36,93 +28,26 @@
|
||||
//! executor. This value will often be set to the executor itself, but it is
|
||||
//! possible that the default executor might be set to a different executor.
|
||||
//!
|
||||
//! For example, the [`current_thread`] executor might set the default executor
|
||||
//! to a thread pool instead of itself, allowing futures to spawn new tasks onto
|
||||
//! the thread pool when those tasks are `Send`.
|
||||
//! For example, a single threaded executor might set the default executor to a
|
||||
//! thread pool instead of itself, allowing futures to spawn new tasks onto the
|
||||
//! thread pool when those tasks are `Send`.
|
||||
//!
|
||||
//! [`Future::poll`]: https://docs.rs/futures/0.1/futures/future/trait.Future.html#tymethod.poll
|
||||
//! [notified]: https://docs.rs/futures/0.1/futures/executor/trait.Notify.html#tymethod.notify
|
||||
//! [`current_thread`]: current_thread/index.html
|
||||
//! [`thread_pool`]: thread_pool/index.html
|
||||
//! [work-stealing]: https://en.wikipedia.org/wiki/Work_stealing
|
||||
//! [`tokio-executor`]: #
|
||||
//! [`Executor`]: #
|
||||
//! [`spawn`]: #
|
||||
//! [`runtime`]: ../runtime/index.html
|
||||
//! [`tokio-executor`]: https://docs.rs/tokio-executor/0.1
|
||||
//! [`Executor`]: trait.Executor.html
|
||||
//! [`spawn`]: fn.spawn.html
|
||||
|
||||
#[deprecated(since = "0.1.8", note = "use tokio-current-thread crate instead")]
|
||||
#[doc(hidden)]
|
||||
pub mod current_thread;
|
||||
|
||||
#[deprecated(since = "0.1.8", note = "use tokio-threadpool crate instead")]
|
||||
/// Re-exports of [`tokio-threadpool`], deprecated in favor of the crate.
|
||||
///
|
||||
/// [`tokio-threadpool`]: https://docs.rs/tokio-threadpool/0.1
|
||||
pub mod thread_pool {
|
||||
//! Maintains a pool of threads across which the set of spawned tasks are
|
||||
//! executed.
|
||||
//!
|
||||
//! [`ThreadPool`] is an executor that uses a thread pool for executing
|
||||
//! tasks concurrently across multiple cores. It uses a thread pool that is
|
||||
//! optimized for use cases that involve multiplexing large number of
|
||||
//! independent tasks that perform short(ish) amounts of computation and are
|
||||
//! mainly waiting on I/O, i.e. the Tokio use case.
|
||||
//!
|
||||
//! Usually, users of [`ThreadPool`] will not create pool instances.
|
||||
//! Instead, they will create a [`Runtime`] instance, which comes with a
|
||||
//! pre-configured thread pool.
|
||||
//!
|
||||
//! At the core, [`ThreadPool`] uses a work-stealing based scheduling
|
||||
//! strategy. When spawning a task while *external* to the thread pool
|
||||
//! (i.e., from a thread that is not part of the thread pool), the task is
|
||||
//! randomly assigned to a worker thread. When spawning a task while
|
||||
//! *internal* to the thread pool, the task is assigned to the current
|
||||
//! worker.
|
||||
//!
|
||||
//! Each worker maintains its own queue and first focuses on processing all
|
||||
//! tasks in its queue. When the worker's queue is empty, the worker will
|
||||
//! attempt to *steal* tasks from other worker queues. This strategy helps
|
||||
//! ensure that work is evenly distributed across threads while minimizing
|
||||
//! synchronization between worker threads.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! Thread pool instances are created using [`ThreadPool::new`] or
|
||||
//! [`Builder::new`]. The first option returns a thread pool with default
|
||||
//! configuration values. The second option allows configuring the thread
|
||||
//! pool before instantiating it.
|
||||
//!
|
||||
//! Once an instance is obtained, futures may be spawned onto it using the
|
||||
//! [`spawn`] function.
|
||||
//!
|
||||
//! A handle to the thread pool is obtained using [`ThreadPool::sender`].
|
||||
//! This handle is **only** able to spawn futures onto the thread pool. It
|
||||
//! is unable to affect the lifecycle of the thread pool in any way. This
|
||||
//! handle can be passed into functions or stored in structs as a way to
|
||||
//! grant the capability of spawning futures.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! # extern crate tokio;
|
||||
//! # extern crate futures;
|
||||
//! # use tokio::executor::thread_pool::ThreadPool;
|
||||
//! use futures::future::{Future, lazy};
|
||||
//!
|
||||
//! # pub fn main() {
|
||||
//! // Create a thread pool with default configuration values
|
||||
//! let thread_pool = ThreadPool::new();
|
||||
//!
|
||||
//! thread_pool.spawn(lazy(|| {
|
||||
//! println!("called from a worker thread");
|
||||
//! Ok(())
|
||||
//! }));
|
||||
//!
|
||||
//! // Gracefully shutdown the threadpool
|
||||
//! thread_pool.shutdown().wait().unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! [`ThreadPool`]: struct.ThreadPool.html
|
||||
//! [`ThreadPool::new`]: struct.ThreadPool.html#method.new
|
||||
//! [`ThreadPool::sender`]: struct.ThreadPool.html#method.sender
|
||||
//! [`spawn`]: struct.ThreadPool.html#method.spawn
|
||||
//! [`Builder::new`]: struct.Builder.html#method.new
|
||||
//! [`Runtime`]: ../../runtime/struct.Runtime.html
|
||||
|
||||
pub use tokio_threadpool::{
|
||||
Builder,
|
||||
Sender,
|
||||
@@ -136,9 +61,6 @@ pub use tokio_executor::{Executor, DefaultExecutor, SpawnError};
|
||||
use futures::{Future, IntoFuture};
|
||||
use futures::future::{self, FutureResult};
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Return value from the `spawn` function.
|
||||
///
|
||||
/// Currently this value doesn't actually provide any functionality. However, it
|
||||
@@ -208,15 +130,6 @@ where F: Future<Item = (), Error = ()> + 'static + Send
|
||||
Spawn(())
|
||||
}
|
||||
|
||||
/// Like `spawn`, but compatible with futures 0.2
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub fn spawn2<F>(f: F) -> Spawn
|
||||
where F: futures2::Future<Item = (), Error = futures2::Never> + 'static + Send
|
||||
{
|
||||
::tokio_executor::spawn2(f);
|
||||
Spawn(())
|
||||
}
|
||||
|
||||
impl IntoFuture for Spawn {
|
||||
type Future = FutureResult<(), ()>;
|
||||
type Item = ();
|
||||
@@ -226,14 +139,3 @@ impl IntoFuture for Spawn {
|
||||
future::ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
impl futures2::IntoFuture for Spawn {
|
||||
type Future = futures2::future::FutureResult<(), ()>;
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn into_future(self) -> Self::Future {
|
||||
futures2::future::ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//! the context of the Tokio runtime as they require Tokio specific features to
|
||||
//! function.
|
||||
|
||||
pub use tokio_fs::{
|
||||
file,
|
||||
File,
|
||||
};
|
||||
pub use tokio_fs::{create_dir, create_dir_all, file, hard_link, metadata, os, read_dir, read_link};
|
||||
pub use tokio_fs::{remove_dir, remove_file, rename, set_permissions, symlink_metadata, File};
|
||||
pub use tokio_fs::OpenOptions;
|
||||
|
||||
+32
-7
@@ -5,7 +5,7 @@
|
||||
//! provides a few major components:
|
||||
//!
|
||||
//! * A multi threaded, work-stealing based task [scheduler][runtime].
|
||||
//! * A [reactor][reactor] backed by the operating system's event queue (epoll, kqueue,
|
||||
//! * A [reactor] backed by the operating system's event queue (epoll, kqueue,
|
||||
//! IOCP, etc...).
|
||||
//! * Asynchronous [TCP and UDP][net] sockets.
|
||||
//! * Asynchronous [filesystem][fs] operations.
|
||||
@@ -17,7 +17,7 @@
|
||||
//! Guide level documentation is found on the [website].
|
||||
//!
|
||||
//! [website]: https://tokio.rs/docs/getting-started/hello-world/
|
||||
//! [futures]: http://docs.rs/futures
|
||||
//! [futures]: http://docs.rs/futures/0.1
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
@@ -70,8 +70,10 @@
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate mio;
|
||||
extern crate tokio_current_thread;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_fs;
|
||||
extern crate tokio_reactor;
|
||||
extern crate tokio_threadpool;
|
||||
@@ -79,9 +81,10 @@ extern crate tokio_timer;
|
||||
extern crate tokio_tcp;
|
||||
extern crate tokio_udp;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
extern crate futures2;
|
||||
#[cfg(unix)]
|
||||
extern crate tokio_uds;
|
||||
|
||||
pub mod clock;
|
||||
pub mod executor;
|
||||
pub mod fs;
|
||||
pub mod net;
|
||||
@@ -91,11 +94,33 @@ pub mod timer;
|
||||
pub mod util;
|
||||
|
||||
pub use executor::spawn;
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub use executor::spawn2;
|
||||
|
||||
pub use runtime::run;
|
||||
|
||||
pub mod codec {
|
||||
//! Utilities for encoding and decoding frames.
|
||||
//!
|
||||
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
|
||||
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
|
||||
//! Framed streams are also known as [transports].
|
||||
//!
|
||||
//! [`AsyncRead`]: ../io/trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: ../io/trait.AsyncWrite.html
|
||||
//! [`Sink`]: https://docs.rs/futures/0.1/futures/sink/trait.Sink.html
|
||||
//! [`Stream`]: https://docs.rs/futures/0.1/futures/stream/trait.Stream.html
|
||||
//! [transports]: https://tokio.rs/docs/going-deeper/frames/
|
||||
|
||||
pub use tokio_codec::{
|
||||
Decoder,
|
||||
Encoder,
|
||||
Framed,
|
||||
FramedParts,
|
||||
FramedRead,
|
||||
FramedWrite,
|
||||
BytesCodec,
|
||||
LinesCodec,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod io {
|
||||
//! Asynchronous I/O.
|
||||
//!
|
||||
|
||||
+14
-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
|
||||
@@ -39,3 +39,16 @@
|
||||
pub use tokio_tcp::{TcpStream, ConnectFuture};
|
||||
pub use tokio_tcp::{TcpListener, Incoming};
|
||||
pub use tokio_udp::{UdpSocket, UdpFramed, SendDgram, RecvDgram};
|
||||
|
||||
#[cfg(unix)]
|
||||
pub mod unix {
|
||||
//! Unix domain socket bindings for `tokio`.
|
||||
|
||||
pub use tokio_uds::{
|
||||
ConnectFuture, Incoming, RecvDgram, SendDgram, UCred, UnixDatagram, UnixListener,
|
||||
UnixStream,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub use self::unix::{UnixListener, UnixStream};
|
||||
|
||||
+5
-5
@@ -81,7 +81,7 @@
|
||||
//! ## Implementation
|
||||
//!
|
||||
//! The reactor implementation uses [`mio`] to interface with the operating
|
||||
//! system's event queue. A call to [`Reactor::poll`] results in in a single
|
||||
//! system's event queue. A call to [`Reactor::poll`] results in a single
|
||||
//! call to [`Poll::poll`] which in turn results in a single call to the
|
||||
//! operating system's selector.
|
||||
//!
|
||||
@@ -107,8 +107,8 @@
|
||||
//! There are a couple of ways to do this.
|
||||
//!
|
||||
//! If the custom I/O resource implements [`mio::Evented`] and implements
|
||||
//! [`std::Read`] and / or [`std::Write`], then [`PollEvented`] is the most
|
||||
//! suited.
|
||||
//! [`std::io::Read`] and / or [`std::io::Write`], then [`PollEvented`] is the
|
||||
//! most suited.
|
||||
//!
|
||||
//! Otherwise, [`Registration`] can be used directly. This provides the lowest
|
||||
//! level primitive needed for integrating with the reactor: a stream of
|
||||
@@ -132,8 +132,8 @@
|
||||
//! [`Poll::poll`]: https://docs.rs/mio/0.6/mio/struct.Poll.html#method.poll
|
||||
//! [`mio::Evented`]: https://docs.rs/mio/0.6/mio/trait.Evented.html
|
||||
//! [`PollEvented`]: struct.PollEvented.html
|
||||
//! [`std::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
||||
//! [`std::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
|
||||
//! [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
||||
//! [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
|
||||
|
||||
pub use tokio_reactor::{
|
||||
Reactor,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+25
-6
@@ -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.
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,9 @@
|
||||
//!
|
||||
//! # Spawning from other threads
|
||||
//!
|
||||
//! By default, [`current_thread::Runtime`][rt] does not provide a way to spawn
|
||||
//! tasks from other threads. However, this can be accomplished by using a
|
||||
//! [`mpsc::channel`][chan]. To do so, create a channel to send the task, then
|
||||
//! spawn a task on [`current_thread::Runtime`][rt] that consumes the channel
|
||||
//! messages and spawns new tasks for them.
|
||||
//! 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:
|
||||
//!
|
||||
@@ -30,17 +28,15 @@
|
||||
//! # extern crate futures;
|
||||
//! use tokio::runtime::current_thread::Runtime;
|
||||
//! use tokio::prelude::*;
|
||||
//! use futures::sync::mpsc;
|
||||
//! use std::thread;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let mut runtime = Runtime::new().unwrap();
|
||||
//! let (tx, rx) = mpsc::channel(128);
|
||||
//! # tx.send(future::ok(()));
|
||||
//! let handle = runtime.handle();
|
||||
//!
|
||||
//! runtime.spawn(rx.for_each(|task| {
|
||||
//! tokio::spawn(task);
|
||||
//! Ok(())
|
||||
//! }).map_err(|e| panic!("channel error")));
|
||||
//! thread::spawn(move || {
|
||||
//! handle.spawn(future::ok(()));
|
||||
//! }).join().unwrap();
|
||||
//!
|
||||
//! # /*
|
||||
//! runtime.run().unwrap();
|
||||
@@ -66,7 +62,29 @@
|
||||
//! [rt]: struct.Runtime.html
|
||||
//! [concurrent-rt]: ../struct.Runtime.html
|
||||
//! [chan]: https://docs.rs/futures/0.1/futures/sync/mpsc/fn.channel.html
|
||||
//! [reactor]: ../../reactor/struct.Reactor.html
|
||||
//! [executor]: https://tokio.rs/docs/getting-started/runtime-model/#executors
|
||||
//! [timer]: ../../timer/index.html
|
||||
|
||||
mod builder;
|
||||
mod runtime;
|
||||
|
||||
pub use self::runtime::Runtime;
|
||||
pub use self::builder::Builder;
|
||||
pub use self::runtime::{Runtime, Handle};
|
||||
|
||||
use futures::Future;
|
||||
|
||||
/// Run the provided future to completion using a runtime running on the current thread.
|
||||
///
|
||||
/// This first creates a new [`Runtime`], and calls [`Runtime::block_on`] with the provided future,
|
||||
/// which blocks the current thread until the provided future completes. It then calls
|
||||
/// [`Runtime::run`] to wait for any other spawned futures to resolve.
|
||||
pub fn block_on_all<F>(future: F) -> Result<F::Item, F::Error>
|
||||
where
|
||||
F: Future,
|
||||
{
|
||||
let mut r = Runtime::new().expect("failed to start runtime on current thread");
|
||||
let v = r.block_on(future)?;
|
||||
r.run().expect("failed to resolve remaining futures");
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
use executor::current_thread::{self, CurrentThread};
|
||||
use tokio_current_thread::{self as current_thread, CurrentThread};
|
||||
use tokio_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::fmt;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
|
||||
/// Single-threaded runtime provides a way to start reactor
|
||||
@@ -18,34 +23,74 @@ use std::io;
|
||||
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 fmt::Display for RunError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for RunError {
|
||||
fn description(&self) -> &str {
|
||||
self.inner.description()
|
||||
}
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
self.inner.cause()
|
||||
}
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
/// Returns a new runtime initialized with default configuration values.
|
||||
pub fn new() -> io::Result<Runtime> {
|
||||
// We need a reactor to receive events about IO objects from kernel
|
||||
let reactor = Reactor::new()?;
|
||||
let reactor_handle = reactor.handle();
|
||||
Builder::new().build()
|
||||
}
|
||||
|
||||
// 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();
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 { reactor_handle, timer_handle, executor };
|
||||
Ok(runtime)
|
||||
/// 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.
|
||||
@@ -124,7 +169,13 @@ impl Runtime {
|
||||
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 mut executor } = *self;
|
||||
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");
|
||||
@@ -132,16 +183,18 @@ impl Runtime {
|
||||
// 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);
|
||||
f(&mut executor)
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+46
-24
@@ -127,9 +127,8 @@ use std::io;
|
||||
|
||||
use tokio_threadpool as threadpool;
|
||||
|
||||
use futures;
|
||||
use futures::future::Future;
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Handle to the Tokio runtime.
|
||||
///
|
||||
@@ -214,18 +213,6 @@ where F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
|
||||
/// Start the Tokio runtime using the supplied future to bootstrap execution.
|
||||
///
|
||||
/// Identical to `run` but works with futures 0.2-style futures.
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub fn run2<F>(future: F)
|
||||
where F: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
|
||||
{
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
runtime.spawn2(future);
|
||||
runtime.shutdown_on_idle().wait().unwrap();
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
/// Create a new runtime instance with default configuration values.
|
||||
///
|
||||
@@ -234,7 +221,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.
|
||||
///
|
||||
@@ -352,17 +339,52 @@ impl Runtime {
|
||||
self
|
||||
}
|
||||
|
||||
/// Spawn a futures 0.2-style future onto the Tokio runtime.
|
||||
/// Run a future to completion on the Tokio runtime.
|
||||
///
|
||||
/// Otherwise identical to `spawn`
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub fn spawn2<F>(&mut self, future: F) -> &mut Self
|
||||
where F: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
|
||||
/// 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,
|
||||
{
|
||||
futures2::executor::Executor::spawn(
|
||||
self.inner_mut().pool.sender_mut(), Box::new(future)
|
||||
).unwrap();
|
||||
self
|
||||
let (tx, rx) = futures::sync::oneshot::channel();
|
||||
self.spawn(future.then(move |r| tx.send(r).map_err(|_| unreachable!())));
|
||||
rx.wait().unwrap()
|
||||
}
|
||||
|
||||
/// Run a future to completion on the Tokio runtime, then wait for all
|
||||
/// background futures to complete too.
|
||||
///
|
||||
/// This runs the given future on the runtime, blocking until it is
|
||||
/// complete, waiting for background futures to complete, and yielding
|
||||
/// its resolved result. Any tasks or timers which the future spawns
|
||||
/// internally will be executed on the runtime and waited for completion.
|
||||
///
|
||||
/// This method should not be called from an 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_all<F, R, E>(mut self, future: F) -> Result<R, E>
|
||||
where
|
||||
F: Send + 'static + Future<Item = R, Error = E>,
|
||||
R: Send + 'static,
|
||||
E: Send + 'static,
|
||||
{
|
||||
let res = self.block_on(future);
|
||||
self.shutdown_on_idle().wait().unwrap();
|
||||
res
|
||||
}
|
||||
|
||||
/// Signals the runtime to shutdown once it becomes idle.
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
use tokio_threadpool::Sender;
|
||||
|
||||
use futures::future::{self, Future};
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Executes futures on the runtime
|
||||
///
|
||||
@@ -74,25 +72,4 @@ impl ::executor::Executor for TaskExecutor {
|
||||
{
|
||||
self.inner.spawn(future)
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
fn spawn2(&mut self, future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
|
||||
-> Result<(), futures2::executor::SpawnError>
|
||||
{
|
||||
self.inner.spawn2(future)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
type Task2 = Box<futures2::Future<Item = (), Error = futures2::Never> + Send>;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
impl futures2::executor::Executor for TaskExecutor {
|
||||
fn spawn(&mut self, f: Task2) -> Result<(), futures2::executor::SpawnError> {
|
||||
futures2::executor::Executor::spawn(&mut self.inner, f)
|
||||
}
|
||||
|
||||
fn status(&self) -> Result<(), futures2::executor::SpawnError> {
|
||||
futures2::executor::Executor::status(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
+26
-10
@@ -10,9 +10,12 @@
|
||||
//! is initialized with a `Duration` and repeatedly yields each time the
|
||||
//! duration elapses.
|
||||
//!
|
||||
//! * [`Deadline`][Deadline] wraps a future, requiring that it completes before
|
||||
//! a specified `Instant` in time. If the future does not complete in time,
|
||||
//! then it is canceled and an error is returned.
|
||||
//! * [`Timeout`][Timeeout]: Wraps a future or stream, setting an upper bound to the
|
||||
//! amount of time it is allowed to execute. If the future or stream does not
|
||||
//! completee in time, then it is canceled and an error is returned.
|
||||
//!
|
||||
//! * [`DelayQueue`]: A queue where items are returned once the requested delay
|
||||
//! has expired.
|
||||
//!
|
||||
//! These types are sufficient for handling a large number of scenarios
|
||||
//! involving time.
|
||||
@@ -45,7 +48,7 @@
|
||||
//! ```
|
||||
//!
|
||||
//! Require that an operation takes no more than 300ms. Note that this uses the
|
||||
//! [`deadline`][ext] function on the [`FutureExt`][ext] trait. This trait is
|
||||
//! [`timeout`][ext] function on the [`FutureExt`][ext] trait. This trait is
|
||||
//! included in the prelude.
|
||||
//!
|
||||
//! ```
|
||||
@@ -61,11 +64,9 @@
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let when = Instant::now() + Duration::from_millis(300);
|
||||
//!
|
||||
//! tokio::run({
|
||||
//! long_op()
|
||||
//! .deadline(when)
|
||||
//! .timeout(Duration::from_millis(300))
|
||||
//! .map_err(|e| {
|
||||
//! println!("operation timed out");
|
||||
//! })
|
||||
@@ -75,12 +76,27 @@
|
||||
//!
|
||||
//! [runtime]: ../runtime/struct.Runtime.html
|
||||
//! [tokio-timer]: https://docs.rs/tokio-timer
|
||||
//! [ext]: ../util/trait.FutureExt.html#method.deadline
|
||||
//! [ext]: ../util/trait.FutureExt.html#method.timeout
|
||||
//! [Timeout]: struct.Timeout.html
|
||||
//! [Delay]: struct.Delay.html
|
||||
//! [Interval]: struct.Interval.html
|
||||
//! [`DelayQueue`]: struct.DelayQueue.html
|
||||
|
||||
pub use tokio_timer::{
|
||||
Deadline,
|
||||
DeadlineError,
|
||||
delay_queue,
|
||||
DelayQueue,
|
||||
Error,
|
||||
Interval,
|
||||
Delay,
|
||||
Timeout,
|
||||
timeout,
|
||||
};
|
||||
|
||||
#[deprecated(since = "0.1.8", note = "use Timeout instead")]
|
||||
#[allow(deprecated)]
|
||||
#[doc(hidden)]
|
||||
pub type Deadline<T> = ::tokio_timer::Deadline<T>;
|
||||
#[deprecated(since = "0.1.8", note = "use Timeout instead")]
|
||||
#[allow(deprecated)]
|
||||
#[doc(hidden)]
|
||||
pub type DeadlineError<T> = ::tokio_timer::DeadlineError<T>;
|
||||
|
||||
+21
-11
@@ -1,14 +1,16 @@
|
||||
#[allow(deprecated)]
|
||||
use tokio_timer::Deadline;
|
||||
use tokio_timer::Timeout;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
use std::time::Instant;
|
||||
use std::time::{Instant, Duration};
|
||||
|
||||
|
||||
/// An extension trait for `Future` that provides a variety of convenient
|
||||
/// combinator functions.
|
||||
///
|
||||
/// Currently, there only is a [`deadline`] function, but this will increase
|
||||
/// Currently, there only is a [`timeout`] function, but this will increase
|
||||
/// over time.
|
||||
///
|
||||
/// Users are not expected to implement this trait. All types that implement
|
||||
@@ -17,18 +19,17 @@ use std::time::Instant;
|
||||
/// This trait can be imported directly or via the Tokio prelude: `use
|
||||
/// tokio::prelude::*`.
|
||||
///
|
||||
/// [`deadline`]: #method.deadline
|
||||
/// [`timeout`]: #method.timeout
|
||||
pub trait FutureExt: Future {
|
||||
|
||||
/// Creates a new future which allows `self` until `deadline`.
|
||||
/// Creates a new future which allows `self` until `timeout`.
|
||||
///
|
||||
/// 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.
|
||||
/// with a timeout. The returned future is allowed to execute until it
|
||||
/// completes or `timeout` has elapsed, 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
|
||||
/// `deadline` is reached.
|
||||
/// If the future completes before `timeout` then the future will resolve
|
||||
/// with that item. Otherwise the future will resolve to an error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -36,7 +37,7 @@ pub trait FutureExt: Future {
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate futures;
|
||||
/// use tokio::prelude::*;
|
||||
/// use std::time::{Duration, Instant};
|
||||
/// use std::time::Duration;
|
||||
/// # use futures::future::{self, FutureResult};
|
||||
///
|
||||
/// # fn long_future() -> FutureResult<(), ()> {
|
||||
@@ -45,12 +46,21 @@ pub trait FutureExt: Future {
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let future = long_future()
|
||||
/// .deadline(Instant::now() + Duration::from_secs(1))
|
||||
/// .timeout(Duration::from_secs(1))
|
||||
/// .map_err(|e| println!("error = {:?}", e));
|
||||
///
|
||||
/// tokio::run(future);
|
||||
/// # }
|
||||
/// ```
|
||||
fn timeout(self, timeout: Duration) -> Timeout<Self>
|
||||
where Self: Sized,
|
||||
{
|
||||
Timeout::new(self, timeout)
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.8", note = "use `timeout` instead")]
|
||||
#[allow(deprecated)]
|
||||
#[doc(hidden)]
|
||||
fn deadline(self, deadline: Instant) -> Deadline<Self>
|
||||
where Self: Sized,
|
||||
{
|
||||
|
||||
+3
-2
@@ -1,8 +1,9 @@
|
||||
//! Utilities for working with Tokio.
|
||||
//!
|
||||
//! This module contains utilities that are useful for working with Tokio.
|
||||
//! Currently, this only includes [`FutureExt`][FutureExt]. However, this will
|
||||
//! include over time.
|
||||
//! Currently, this only includes [`FutureExt`], but this may grow over time.
|
||||
//!
|
||||
//! [`FutureExt`]: trait.FutureExt.html
|
||||
|
||||
mod future;
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ macro_rules! t {
|
||||
#[test]
|
||||
fn echo_server() {
|
||||
const N: usize = 1024;
|
||||
drop(env_logger::init());
|
||||
drop(env_logger::try_init());
|
||||
|
||||
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
@@ -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::try_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::try_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();
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#![cfg(feature = "unstable-futures")]
|
||||
|
||||
// This test is the same as `echo.rs`, but ported to futures 0.2
|
||||
|
||||
extern crate env_logger;
|
||||
extern crate futures2;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use std::thread;
|
||||
|
||||
use futures2::prelude::*;
|
||||
use futures2::executor::block_on;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn echo_server() {
|
||||
drop(env_logger::init());
|
||||
|
||||
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let msg = "foo bar baz";
|
||||
let t = thread::spawn(move || {
|
||||
let mut s = TcpStream::connect(&addr).unwrap();
|
||||
|
||||
for _i in 0..1024 {
|
||||
assert_eq!(t!(s.write(msg.as_bytes())), msg.len());
|
||||
let mut buf = [0; 1024];
|
||||
assert_eq!(t!(s.read(&mut buf)), msg.len());
|
||||
assert_eq!(&buf[..msg.len()], msg.as_bytes());
|
||||
}
|
||||
});
|
||||
|
||||
let clients = srv.incoming();
|
||||
let client = clients.next().map(|e| e.0.unwrap()).map_err(|e| e.0);
|
||||
let halves = client.map(|s| s.split());
|
||||
let copied = halves.and_then(|(a, b)| a.copy_into(b));
|
||||
|
||||
let (amt, _, _) = t!(block_on(copied));
|
||||
t.join().unwrap();
|
||||
|
||||
assert_eq!(amt, msg.len() as u64 * 1024);
|
||||
}
|
||||
+2
-2
@@ -21,7 +21,7 @@ macro_rules! t {
|
||||
|
||||
#[test]
|
||||
fn hammer_old() {
|
||||
let _ = env_logger::init();
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let threads = (0..10).map(|_| {
|
||||
thread::spawn(|| {
|
||||
@@ -77,7 +77,7 @@ fn hammer_split() {
|
||||
const N: usize = 100;
|
||||
const ITER: usize = 10;
|
||||
|
||||
let _ = env_logger::init();
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
for _ in 0..ITER {
|
||||
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
#![cfg(feature = "unstable-futures")]
|
||||
|
||||
// This test is the same as `global.rs`, but ported to futures 0.2
|
||||
|
||||
extern crate futures;
|
||||
extern crate futures2;
|
||||
extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::{io, thread};
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures2::prelude::*;
|
||||
use futures2::executor::block_on;
|
||||
use futures2::task;
|
||||
|
||||
use tokio::net::{TcpStream, TcpListener};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hammer() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let threads = (0..10).map(|_| {
|
||||
thread::spawn(|| {
|
||||
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(srv.local_addr());
|
||||
let mine = TcpStream::connect(&addr);
|
||||
let theirs = srv.incoming().next()
|
||||
.map(|(s, _)| s.unwrap())
|
||||
.map_err(|(s, _)| s);
|
||||
let (mine, theirs) = t!(block_on(mine.join(theirs)));
|
||||
|
||||
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
|
||||
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
|
||||
})
|
||||
}).collect::<Vec<_>>();
|
||||
for thread in threads {
|
||||
thread.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
struct Rd(Arc<TcpStream>);
|
||||
struct Wr(Arc<TcpStream>);
|
||||
|
||||
impl AsyncRead for Rd {
|
||||
fn poll_read(&mut self, cx: &mut task::Context, dst: &mut [u8]) -> Poll<usize, io::Error> {
|
||||
<&TcpStream>::poll_read(&mut &*self.0, cx, dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Wr {
|
||||
fn poll_write(&mut self, cx: &mut task::Context, src: &[u8]) -> Poll<usize, io::Error> {
|
||||
<&TcpStream>::poll_write(&mut &*self.0, cx, src)
|
||||
}
|
||||
|
||||
fn poll_flush(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
fn poll_close(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hammer_split() {
|
||||
const N: usize = 100;
|
||||
|
||||
let _ = env_logger::init();
|
||||
|
||||
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let mut rt = Runtime::new().unwrap();
|
||||
|
||||
fn split(socket: TcpStream) {
|
||||
let socket = Arc::new(socket);
|
||||
let rd = Rd(socket.clone());
|
||||
let wr = Wr(socket);
|
||||
|
||||
let rd = rd.read(vec![0; 1])
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("read error = {:?}", e));
|
||||
|
||||
let wr = wr.write_all(b"1")
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("write error = {:?}", e));
|
||||
|
||||
tokio::spawn2(rd);
|
||||
tokio::spawn2(wr);
|
||||
}
|
||||
|
||||
rt.spawn2({
|
||||
srv.incoming()
|
||||
.map_err(|e| panic!("accept error = {:?}", e))
|
||||
.take(N as u64)
|
||||
.for_each(|socket| {
|
||||
split(socket);
|
||||
Ok(())
|
||||
})
|
||||
.map(|_| ())
|
||||
});
|
||||
|
||||
for _ in 0..N {
|
||||
rt.spawn2({
|
||||
TcpStream::connect(&addr)
|
||||
.map_err(|e| panic!("connect error = {:?}", e))
|
||||
.map(|socket| split(socket))
|
||||
});
|
||||
}
|
||||
|
||||
futures::Future::wait(rt.shutdown_on_idle()).unwrap();
|
||||
}
|
||||
@@ -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;
|
||||
@@ -51,7 +51,7 @@ impl Encoder for LineCodec {
|
||||
|
||||
#[test]
|
||||
fn echo() {
|
||||
drop(env_logger::init());
|
||||
drop(env_logger::try_init());
|
||||
|
||||
let pool = Builder::new()
|
||||
.pool_size(1)
|
||||
@@ -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(())
|
||||
});
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ impl Evented for MyFile {
|
||||
|
||||
#[test]
|
||||
fn hup() {
|
||||
drop(env_logger::init());
|
||||
drop(env_logger::try_init());
|
||||
|
||||
let handle = Handle::default();
|
||||
unsafe {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
extern crate futures;
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_reactor;
|
||||
extern crate tokio_tcp;
|
||||
|
||||
use tokio_reactor::Reactor;
|
||||
use tokio_tcp::TcpListener;
|
||||
|
||||
use futures::{Future, Stream};
|
||||
use futures::executor::{spawn, Notify, Spawn};
|
||||
|
||||
use std::mem;
|
||||
use std::net::TcpStream;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[test]
|
||||
fn test_drop_on_notify() {
|
||||
// When the reactor receives a kernel notification, it notifies the
|
||||
// task that holds the associated socket. If this notification results in
|
||||
// the task being dropped, the socket will also be dropped.
|
||||
//
|
||||
// Previously, there was a deadlock scenario where the reactor, while
|
||||
// notifying, held a lock and the task being dropped attempted to acquire
|
||||
// that same lock in order to clean up state.
|
||||
//
|
||||
// To simulate this case, we create a fake executor that does nothing when
|
||||
// the task is notified. This simulates an executor in the process of
|
||||
// shutting down. Then, when the task handle is dropped, the task itself is
|
||||
// dropped.
|
||||
|
||||
struct MyNotify;
|
||||
|
||||
type Task = Mutex<Spawn<Box<Future<Item = (), Error = ()>>>>;
|
||||
|
||||
impl Notify for MyNotify {
|
||||
fn notify(&self, _: usize) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
fn clone_id(&self, id: usize) -> usize {
|
||||
let ptr = id as *const Task;
|
||||
let task = unsafe { Arc::from_raw(ptr) };
|
||||
|
||||
mem::forget(task.clone());
|
||||
mem::forget(task);
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
fn drop_id(&self, id: usize) {
|
||||
let ptr = id as *const Task;
|
||||
let _ = unsafe { Arc::from_raw(ptr) };
|
||||
}
|
||||
}
|
||||
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
let mut reactor = Reactor::new().unwrap();
|
||||
|
||||
// Create a listener
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
// Define a task that just drains the listener
|
||||
let task = Box::new({
|
||||
listener.incoming()
|
||||
.for_each(|_| Ok(()))
|
||||
.map_err(|_| panic!())
|
||||
}) as Box<Future<Item = (), Error = ()>>;
|
||||
|
||||
let task = Arc::new(Mutex::new(spawn(task)));
|
||||
let notify = Arc::new(MyNotify);
|
||||
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
|
||||
tokio_reactor::with_default(&reactor.handle(), &mut enter, |_| {
|
||||
let id = &*task as *const Task as usize;
|
||||
|
||||
task.lock().unwrap()
|
||||
.poll_future_notify(¬ify, id)
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
drop(task);
|
||||
|
||||
// Establish a connection to the acceptor
|
||||
let _s = TcpStream::connect(&addr).unwrap();
|
||||
|
||||
reactor.turn(None).unwrap();
|
||||
}
|
||||
+205
-3
@@ -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 {
|
||||
@@ -44,14 +50,14 @@ fn create_client_server_future() -> Box<Future<Item=(), Error=()> + Send> {
|
||||
|
||||
#[test]
|
||||
fn runtime_tokio_run() {
|
||||
let _ = env_logger::init();
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
tokio::run(create_client_server_future());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_single_threaded() {
|
||||
let _ = env_logger::init();
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let mut runtime = tokio::runtime::current_thread::Runtime::new()
|
||||
.unwrap();
|
||||
@@ -59,9 +65,77 @@ fn runtime_single_threaded() {
|
||||
runtime.run().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_single_threaded_block_on() {
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
tokio::runtime::current_thread::block_on_all(create_client_server_future()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_single_threaded_block_on_all() {
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let msg = tokio::runtime::current_thread::block_on_all(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();
|
||||
|
||||
assert_eq!(2, *cnt.lock().unwrap());
|
||||
assert_eq!(msg, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_single_threaded_racy_spawn() {
|
||||
let (trigger, exit) = futures::sync::oneshot::channel();
|
||||
let (handle_tx, handle_rx) = ::std::sync::mpsc::channel();
|
||||
let jh = ::std::thread::spawn(move || {
|
||||
let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
|
||||
handle_tx.send(rt.handle()).unwrap();
|
||||
|
||||
// don't exit until we are told to
|
||||
rt.block_on(exit.map_err(|_| ())).unwrap();
|
||||
|
||||
// run until all spawned futures (incl. the "exit" signal future) have completed.
|
||||
rt.run().unwrap();
|
||||
});
|
||||
|
||||
let (tx, rx) = futures::sync::oneshot::channel();
|
||||
|
||||
let handle = handle_rx.recv().unwrap();
|
||||
handle
|
||||
.spawn(futures::future::lazy(move || {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
// signal runtime thread to exit
|
||||
trigger.send(()).unwrap();
|
||||
|
||||
// wait for runtime thread to exit
|
||||
jh.join().unwrap();
|
||||
|
||||
assert_eq!(rx.wait().unwrap(), ());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_multi_threaded() {
|
||||
let _ = env_logger::init();
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let mut runtime = tokio::runtime::Builder::new()
|
||||
.build()
|
||||
@@ -69,3 +143,131 @@ fn runtime_multi_threaded() {
|
||||
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());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_from_block_on_all() {
|
||||
let cnt = Arc::new(Mutex::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let runtime = Runtime::new().unwrap();
|
||||
let msg = runtime
|
||||
.block_on_all(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
|
||||
// Spawn!
|
||||
tokio::spawn(lazy(move || {
|
||||
{
|
||||
let mut x = c.lock().unwrap();
|
||||
*x = 1 + *x;
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
|
||||
Ok::<_, ()>("hello")
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(2, *cnt.lock().unwrap());
|
||||
assert_eq!(msg, "hello");
|
||||
}
|
||||
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
#![cfg(feature = "unstable-futures")]
|
||||
|
||||
// This test is the same as `tcp.rs`, but ported to futures 0.2
|
||||
|
||||
extern crate env_logger;
|
||||
extern crate tokio;
|
||||
extern crate mio;
|
||||
extern crate futures2;
|
||||
|
||||
use std::{net, thread};
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use futures2::executor::block_on;
|
||||
use futures2::prelude::*;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn connect() {
|
||||
drop(env_logger::init());
|
||||
let srv = t!(net::TcpListener::bind("127.0.0.1:0"));
|
||||
let addr = t!(srv.local_addr());
|
||||
let t = thread::spawn(move || {
|
||||
t!(srv.accept()).0
|
||||
});
|
||||
|
||||
let stream = TcpStream::connect(&addr);
|
||||
let mine = t!(block_on(stream));
|
||||
let theirs = t.join().unwrap();
|
||||
|
||||
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
|
||||
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept() {
|
||||
drop(env_logger::init());
|
||||
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let client = srv.incoming().map(move |t| {
|
||||
tx.send(()).unwrap();
|
||||
t
|
||||
}).next().map_err(|e| e.0);
|
||||
assert!(rx.try_recv().is_err());
|
||||
let t = thread::spawn(move || {
|
||||
net::TcpStream::connect(&addr).unwrap()
|
||||
});
|
||||
|
||||
let (mine, _remaining) = t!(block_on(client));
|
||||
let mine = mine.unwrap();
|
||||
let theirs = t.join().unwrap();
|
||||
|
||||
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
|
||||
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept2() {
|
||||
drop(env_logger::init());
|
||||
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
net::TcpStream::connect(&addr).unwrap()
|
||||
});
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let client = srv.incoming().map(move |t| {
|
||||
tx.send(()).unwrap();
|
||||
t
|
||||
}).next().map_err(|e| e.0);
|
||||
assert!(rx.try_recv().is_err());
|
||||
|
||||
let (mine, _remaining) = t!(block_on(client));
|
||||
mine.unwrap();
|
||||
t.join().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix {
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use env_logger;
|
||||
use futures2::future;
|
||||
use futures2::executor::block_on;
|
||||
use futures2::io::AsyncRead;
|
||||
use mio::unix::UnixReady;
|
||||
|
||||
use std::{net, thread};
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn poll_hup() {
|
||||
drop(env_logger::init());
|
||||
|
||||
let srv = t!(net::TcpListener::bind("127.0.0.1:0"));
|
||||
let addr = t!(srv.local_addr());
|
||||
let t = thread::spawn(move || {
|
||||
let mut client = t!(srv.accept()).0;
|
||||
client.write(b"hello world").unwrap();
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
});
|
||||
|
||||
let mut stream = t!(block_on(TcpStream::connect(&addr)));
|
||||
|
||||
// Poll for HUP before reading.
|
||||
block_on(future::poll_fn(|cx| {
|
||||
stream.poll_read_ready2(cx, UnixReady::hup().into())
|
||||
})).unwrap();
|
||||
|
||||
// Same for write half
|
||||
block_on(future::poll_fn(|cx| {
|
||||
stream.poll_write_ready2(cx)
|
||||
})).unwrap();
|
||||
|
||||
let mut buf = vec![0; 11];
|
||||
|
||||
// Read the data
|
||||
block_on(future::poll_fn(|cx| {
|
||||
stream.poll_read(cx, &mut buf)
|
||||
})).unwrap();
|
||||
|
||||
assert_eq!(b"hello world", &buf[..]);
|
||||
|
||||
t.join().unwrap();
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -11,7 +11,7 @@ use std::time::{Duration, Instant};
|
||||
|
||||
#[test]
|
||||
fn timer_with_runtime() {
|
||||
let _ = env_logger::init();
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let when = Instant::now() + Duration::from_millis(100);
|
||||
let (tx, rx) = mpsc::channel();
|
||||
@@ -33,7 +33,7 @@ fn timer_with_runtime() {
|
||||
fn starving() {
|
||||
use futures::{task, Poll, Async};
|
||||
|
||||
let _ = env_logger::init();
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
struct Starve(Delay, u64);
|
||||
|
||||
@@ -75,7 +75,7 @@ fn starving() {
|
||||
fn deadline() {
|
||||
use futures::future;
|
||||
|
||||
let _ = env_logger::init();
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let when = Instant::now() + Duration::from_millis(20);
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# # 0.1.0 (June 13, 2018)
|
||||
|
||||
* 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.7", 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() {
|
||||
@@ -57,20 +57,12 @@ fn lines_decoder() {
|
||||
|
||||
#[test]
|
||||
fn lines_encoder() {
|
||||
let mut codec = BytesCodec::new();
|
||||
|
||||
// Default capacity of BytesMut
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const INLINE_CAP: usize = 4 * 8 - 1;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const INLINE_CAP: usize = 4 * 4 - 1;
|
||||
|
||||
let mut codec = LinesCodec::new();
|
||||
let mut buf = BytesMut::new();
|
||||
codec.encode(Bytes::from_static(&[b'a'; INLINE_CAP + 1]), &mut buf).unwrap();
|
||||
|
||||
// Default capacity of Framed Read
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
codec.encode(String::from("line 1"), &mut buf).unwrap();
|
||||
assert_eq!("line 1\n", buf);
|
||||
|
||||
let mut buf = BytesMut::with_capacity(INITIAL_CAPACITY);
|
||||
codec.encode(Bytes::from_static(&[b'a'; INITIAL_CAPACITY + 1]), &mut buf).unwrap();
|
||||
codec.encode(String::from("line 2"), &mut buf).unwrap();
|
||||
assert_eq!("line 1\nline 2\n", buf);
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -38,6 +40,7 @@ impl Encoder for U32Codec {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,31 +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,11 +1,12 @@
|
||||
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 bytes::{BytesMut, Buf, IntoBuf};
|
||||
use futures::Stream;
|
||||
use futures::Async::{Ready, NotReady};
|
||||
|
||||
@@ -31,7 +32,7 @@ impl Decoder for U32Decoder {
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
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};
|
||||
use bytes::{BytesMut, BufMut};
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::collections::VecDeque;
|
||||
@@ -0,0 +1,8 @@
|
||||
# 0.1.1 (August 6, 2018)
|
||||
|
||||
* Implement `std::Error` for misc error types (#501)
|
||||
* bugfix: Track tasks pending in spawn queue (#478)
|
||||
|
||||
# 0.1.0 (June 13, 2018)
|
||||
|
||||
* Extract `tokio::executor::current_thread` to a tokio-current-thread crate (#356)
|
||||
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "tokio-current-thread"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.1"
|
||||
documentation = "https://docs.rs/tokio-current-thread"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://github.com/tokio-rs/tokio"
|
||||
license = "MIT"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
description = """
|
||||
Single threaded executor which manage many tasks concurrently on the current thread.
|
||||
"""
|
||||
keywords = ["futures", "tokio"]
|
||||
categories = ["concurrency", "asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
tokio-executor = { version = "0.1.2", path = "../tokio-executor" }
|
||||
futures = "0.1.19"
|
||||
@@ -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-current-thread
|
||||
|
||||
Single threaded executor for Tokio.
|
||||
|
||||
[Documentation](https://tokio-rs.github.io/tokio/tokio_current_thread/)
|
||||
|
||||
## Overview
|
||||
|
||||
This crate provides the single threaded executor which execute many tasks concurrently.
|
||||
|
||||
## 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,797 @@
|
||||
//! A single-threaded executor which executes tasks on the same thread from which
|
||||
//! they are spawned.
|
||||
//!
|
||||
//!
|
||||
//! The crate provides:
|
||||
//!
|
||||
//! * [`CurrentThread`] is the main type of this crate. It executes tasks on the current thread.
|
||||
//! The easiest way to start a new [`CurrentThread`] executor is to call
|
||||
//! [`block_on_all`] with an initial task to seed the executor.
|
||||
//! All tasks that are being managed by a [`CurrentThread`] executor are able to
|
||||
//! spawn additional tasks by calling [`spawn`].
|
||||
//!
|
||||
//!
|
||||
//! Application authors will not use this crate directly. Instead, they will use the
|
||||
//! `tokio` crate. Library authors should only depend on `tokio-current-thread` if they
|
||||
//! are building a custom task executor.
|
||||
//!
|
||||
//! For more details, see [executor module] documentation in the Tokio crate.
|
||||
//!
|
||||
//! [`CurrentThread`]: struct.CurrentThread.html
|
||||
//! [`spawn`]: fn.spawn.html
|
||||
//! [`block_on_all`]: fn.block_on_all.html
|
||||
//! [executor module]: https://docs.rs/tokio/0.1/tokio/executor/index.html
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-current-thread/0.1.1")]
|
||||
#![deny(warnings, missing_docs, missing_debug_implementations)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_executor;
|
||||
|
||||
mod scheduler;
|
||||
|
||||
use self::scheduler::Scheduler;
|
||||
|
||||
use tokio_executor::{Enter, SpawnError};
|
||||
use tokio_executor::park::{Park, Unpark, ParkThread};
|
||||
|
||||
use futures::{executor, Async, Future};
|
||||
use futures::future::{Executor, ExecuteError, ExecuteErrorKind};
|
||||
|
||||
use std::fmt;
|
||||
use std::cell::Cell;
|
||||
use std::error::Error;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{atomic, mpsc, Arc};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// Executes tasks on the current thread
|
||||
pub struct CurrentThread<P: Park = ParkThread> {
|
||||
/// Execute futures and receive unpark notifications.
|
||||
scheduler: Scheduler<P::Unpark>,
|
||||
|
||||
/// Current number of futures being executed.
|
||||
///
|
||||
/// The LSB is used to indicate that the runtime is preparing to shut down.
|
||||
/// Thus, to get the actual number of pending futures, `>>1`.
|
||||
num_futures: Arc<atomic::AtomicUsize>,
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// All futures executed using this executor will be executed on the current
|
||||
/// thread. As such, `run` will wait for these futures to complete before
|
||||
/// returning.
|
||||
///
|
||||
/// For more details, see the [module level](index.html) documentation.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TaskExecutor {
|
||||
// Prevent the handle from moving across threads.
|
||||
_p: ::std::marker::PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
/// Returned by the `turn` function.
|
||||
#[derive(Debug)]
|
||||
pub struct Turn {
|
||||
polled: bool
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
/// Error returned by the `run` function.
|
||||
#[derive(Debug)]
|
||||
pub struct RunError {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
impl fmt::Display for RunError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for RunError {
|
||||
fn description(&self) -> &str {
|
||||
"Run error"
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned by the `run_timeout` function.
|
||||
#[derive(Debug)]
|
||||
pub struct RunTimeoutError {
|
||||
timeout: bool,
|
||||
}
|
||||
|
||||
impl fmt::Display for RunTimeoutError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for RunTimeoutError {
|
||||
fn description(&self) -> &str {
|
||||
if self.timeout {
|
||||
"Run timeout error (timeout)"
|
||||
} else {
|
||||
"Run timeout error (not timeout)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned by the `turn` function.
|
||||
#[derive(Debug)]
|
||||
pub struct TurnError {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
impl fmt::Display for TurnError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for TurnError {
|
||||
fn description(&self) -> &str {
|
||||
"Turn error"
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned by the `block_on` function.
|
||||
#[derive(Debug)]
|
||||
pub struct BlockError<T> {
|
||||
inner: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for BlockError<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "Block error")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> Error for BlockError<T> {
|
||||
fn description(&self) -> &str {
|
||||
"Block error"
|
||||
}
|
||||
}
|
||||
|
||||
/// This is mostly split out to make the borrow checker happy.
|
||||
struct Borrow<'a, U: 'a> {
|
||||
scheduler: &'a mut Scheduler<U>,
|
||||
num_futures: &'a atomic::AtomicUsize,
|
||||
}
|
||||
|
||||
trait SpawnLocal {
|
||||
fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>, already_counted: bool);
|
||||
}
|
||||
|
||||
struct CurrentRunner {
|
||||
spawn: Cell<Option<*mut SpawnLocal>>,
|
||||
}
|
||||
|
||||
/// Current thread's task runner. This is set in `TaskRunner::with`
|
||||
thread_local!(static CURRENT: CurrentRunner = CurrentRunner {
|
||||
spawn: Cell::new(None),
|
||||
});
|
||||
|
||||
/// Run the executor bootstrapping the execution with the provided future.
|
||||
///
|
||||
/// This creates a new [`CurrentThread`] executor, spawns the provided future,
|
||||
/// and blocks the current thread until the provided future and **all**
|
||||
/// subsequently spawned futures complete. In other words:
|
||||
///
|
||||
/// * 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.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [`CurrentThread`]: struct.CurrentThread.html
|
||||
/// [mod]: index.html
|
||||
pub fn block_on_all<F>(future: F) -> Result<F::Item, F::Error>
|
||||
where F: Future,
|
||||
{
|
||||
let mut current_thread = CurrentThread::new();
|
||||
|
||||
let ret = current_thread.block_on(future);
|
||||
current_thread.run().unwrap();
|
||||
|
||||
ret.map_err(|e| e.into_inner().expect("unexpected execution error"))
|
||||
}
|
||||
|
||||
/// Executes a future on the current thread.
|
||||
///
|
||||
/// The provided future must complete or be canceled before `run` will return.
|
||||
///
|
||||
/// Unlike [`tokio::spawn`], this function will always spawn on a
|
||||
/// `CurrentThread` executor and is able to spawn futures that are not `Send`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function can only be invoked from the context of a `run` call; any
|
||||
/// other use will result in a panic.
|
||||
///
|
||||
/// [`tokio::spawn`]: ../fn.spawn.html
|
||||
pub fn spawn<F>(future: F)
|
||||
where F: Future<Item = (), Error = ()> + 'static
|
||||
{
|
||||
TaskExecutor::current()
|
||||
.spawn_local(Box::new(future))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// ===== impl CurrentThread =====
|
||||
|
||||
impl CurrentThread<ParkThread> {
|
||||
/// Create a new instance of `CurrentThread`.
|
||||
pub fn new() -> Self {
|
||||
CurrentThread::new_with_park(ParkThread::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Park> CurrentThread<P> {
|
||||
/// Create a new instance of `CurrentThread` backed by the given park
|
||||
/// handle.
|
||||
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();
|
||||
|
||||
let num_futures = Arc::new(atomic::AtomicUsize::new(0));
|
||||
|
||||
CurrentThread {
|
||||
scheduler: scheduler,
|
||||
num_futures: num_futures.clone(),
|
||||
park,
|
||||
spawn_handle: Handle {
|
||||
sender: spawn_sender,
|
||||
num_futures: num_futures,
|
||||
notify: notify,
|
||||
shut_down: Cell::new(false),
|
||||
},
|
||||
spawn_receiver: spawn_receiver,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the executor is currently idle.
|
||||
///
|
||||
/// An idle executor is defined by not currently having any spawned tasks.
|
||||
///
|
||||
/// Note that this method is inherently racy -- if a future is spawned from a remote `Handle`,
|
||||
/// this method may return `true` even though there are more futures to be executed.
|
||||
pub fn is_idle(&self) -> bool {
|
||||
self.num_futures.load(atomic::Ordering::SeqCst) <= 1
|
||||
}
|
||||
|
||||
/// Spawn the future on the executor.
|
||||
///
|
||||
/// This internally queues the future to be executed once `run` is called.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where F: Future<Item = (), Error = ()> + 'static,
|
||||
{
|
||||
self.borrow().spawn_local(Box::new(future), false);
|
||||
self
|
||||
}
|
||||
|
||||
/// Synchronously waits for the provided `future` to complete.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution.
|
||||
pub fn block_on<F>(&mut self, future: F)
|
||||
-> Result<F::Item, BlockError<F::Error>>
|
||||
where F: Future
|
||||
{
|
||||
let mut enter = tokio_executor::enter()
|
||||
.expect("failed to start `current_thread::Runtime`");
|
||||
self.enter(&mut enter).block_on(future)
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until **all**
|
||||
/// spawned futures have completed.
|
||||
pub fn run(&mut self) -> Result<(), RunError> {
|
||||
let mut enter = tokio_executor::enter()
|
||||
.expect("failed to start `current_thread::Runtime`");
|
||||
self.enter(&mut enter).run()
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until all
|
||||
/// spawned futures have completed **or** `duration` time has elapsed.
|
||||
pub fn run_timeout(&mut self, duration: Duration)
|
||||
-> Result<(), RunTimeoutError>
|
||||
{
|
||||
let mut enter = tokio_executor::enter()
|
||||
.expect("failed to start `current_thread::Runtime`");
|
||||
self.enter(&mut enter).run_timeout(duration)
|
||||
}
|
||||
|
||||
/// Perform a single iteration of the event loop.
|
||||
///
|
||||
/// This function blocks the current thread even if the executor is idle.
|
||||
pub fn turn(&mut self, duration: Option<Duration>)
|
||||
-> Result<Turn, TurnError>
|
||||
{
|
||||
let mut enter = tokio_executor::enter()
|
||||
.expect("failed to start `current_thread::Runtime`");
|
||||
self.enter(&mut enter).turn(duration)
|
||||
}
|
||||
|
||||
/// Bind `CurrentThread` instance with an execution context.
|
||||
pub fn enter<'a>(&'a mut self, enter: &'a mut Enter) -> Entered<'a, P> {
|
||||
Entered {
|
||||
executor: self,
|
||||
enter,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: &*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<P: Park> Drop for CurrentThread<P> {
|
||||
fn drop(&mut self) {
|
||||
// Signal to Handles that no more futures can be spawned by setting LSB.
|
||||
//
|
||||
// NOTE: this isn't technically necessary since the send on the mpsc will fail once the
|
||||
// receiver is dropped, but it's useful to illustrate how clean shutdown will be
|
||||
// implemented (e.g., by setting the LSB).
|
||||
let pending = self.num_futures.fetch_add(1, atomic::Ordering::SeqCst);
|
||||
|
||||
// TODO: We currently ignore any pending futures at the time we shut down.
|
||||
//
|
||||
// The "proper" fix for this is to have an explicit shutdown phase (`shutdown_on_idle`)
|
||||
// which sets LSB (as above) do make Handle::spawn stop working, and then runs until
|
||||
// num_futures.load() == 1.
|
||||
let _ = pending;
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_executor::Executor for CurrentThread {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<Future<Item = (), Error = ()> + Send>,
|
||||
) -> Result<(), SpawnError> {
|
||||
self.borrow().spawn_local(future, false);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Park> fmt::Debug for CurrentThread<P> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("CurrentThread")
|
||||
.field("scheduler", &self.scheduler)
|
||||
.field("num_futures", &self.num_futures.load(atomic::Ordering::SeqCst))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Entered =====
|
||||
|
||||
impl<'a, P: Park> Entered<'a, P> {
|
||||
/// Spawn the future on the executor.
|
||||
///
|
||||
/// This internally queues the future to be executed once `run` is called.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where F: Future<Item = (), Error = ()> + 'static,
|
||||
{
|
||||
self.executor.borrow().spawn_local(Box::new(future), false);
|
||||
self
|
||||
}
|
||||
|
||||
/// Synchronously waits for the provided `future` to complete.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution.
|
||||
pub fn block_on<F>(&mut self, future: F)
|
||||
-> Result<F::Item, BlockError<F::Error>>
|
||||
where F: Future
|
||||
{
|
||||
let mut future = executor::spawn(future);
|
||||
let notify = self.executor.scheduler.notify();
|
||||
|
||||
loop {
|
||||
let res = self.executor.borrow().enter(self.enter, || {
|
||||
future.poll_future_notify(¬ify, 0)
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(Async::Ready(e)) => return Ok(e),
|
||||
Err(e) => return Err(BlockError { inner: Some(e) }),
|
||||
Ok(Async::NotReady) => {}
|
||||
}
|
||||
|
||||
self.tick();
|
||||
|
||||
if let Err(_) = self.executor.park.park() {
|
||||
return Err(BlockError { inner: None });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until **all**
|
||||
/// spawned futures have completed.
|
||||
pub fn run(&mut self) -> Result<(), RunError> {
|
||||
self.run_timeout2(None)
|
||||
.map_err(|_| RunError { _p: () })
|
||||
}
|
||||
|
||||
/// Run the executor to completion, blocking the thread until all
|
||||
/// spawned futures have completed **or** `duration` time has elapsed.
|
||||
pub fn run_timeout(&mut self, duration: Duration)
|
||||
-> Result<(), RunTimeoutError>
|
||||
{
|
||||
self.run_timeout2(Some(duration))
|
||||
}
|
||||
|
||||
/// Perform a single iteration of the event loop.
|
||||
///
|
||||
/// This function blocks the current thread even if the executor is idle.
|
||||
pub fn turn(&mut self, duration: Option<Duration>)
|
||||
-> Result<Turn, TurnError>
|
||||
{
|
||||
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: () });
|
||||
}
|
||||
|
||||
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>)
|
||||
-> Result<(), RunTimeoutError>
|
||||
{
|
||||
if self.executor.is_idle() {
|
||||
// Nothing to do
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut time = dur.map(|dur| (Instant::now() + dur, dur));
|
||||
|
||||
loop {
|
||||
self.tick();
|
||||
|
||||
if self.executor.is_idle() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match time {
|
||||
Some((until, rem)) => {
|
||||
if let Err(_) = self.executor.park.park_timeout(rem) {
|
||||
return Err(RunTimeoutError::new(false));
|
||||
}
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
if now >= until {
|
||||
return Err(RunTimeoutError::new(true));
|
||||
}
|
||||
|
||||
time = Some((until, until - now));
|
||||
}
|
||||
None => {
|
||||
if let Err(_) = self.executor.park.park() {
|
||||
return Err(RunTimeoutError::new(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if any futures were processed
|
||||
fn tick(&mut self) -> bool {
|
||||
// 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: &*self.executor.num_futures,
|
||||
},
|
||||
&mut self.executor.spawn_receiver,
|
||||
);
|
||||
|
||||
while let Ok(future) = spawn_receiver.try_recv() {
|
||||
borrow.spawn_local(future, true);
|
||||
}
|
||||
|
||||
// After any pending futures were scheduled, do the actual tick
|
||||
borrow.scheduler.tick(
|
||||
&mut *self.enter,
|
||||
borrow.num_futures)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: Park> fmt::Debug for Entered<'a, P> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Entered")
|
||||
.field("executor", &self.executor)
|
||||
.field("enter", &self.enter)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 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>>,
|
||||
num_futures: Arc<atomic::AtomicUsize>,
|
||||
shut_down: Cell<bool>,
|
||||
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")
|
||||
.field("shut_down", &self.shut_down.get())
|
||||
.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,
|
||||
{
|
||||
if self.shut_down.get() {
|
||||
return Err(SpawnError::shutdown());
|
||||
}
|
||||
|
||||
// NOTE: += 2 since LSB is the shutdown bit
|
||||
let pending = self.num_futures.fetch_add(2, atomic::Ordering::SeqCst);
|
||||
if pending % 2 == 1 {
|
||||
// Bring the count back so we still know when the Runtime is idle.
|
||||
self.num_futures.fetch_sub(2, atomic::Ordering::SeqCst);
|
||||
|
||||
// Once the Runtime is shutting down, we know it won't come back.
|
||||
self.shut_down.set(true);
|
||||
|
||||
return Err(SpawnError::shutdown());
|
||||
}
|
||||
|
||||
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 =====
|
||||
|
||||
impl TaskExecutor {
|
||||
/// Returns an executor that executes futures on the current thread.
|
||||
///
|
||||
/// The user of `TaskExecutor` must ensure that when a future is submitted,
|
||||
/// that it is done within the context of a call to `run`.
|
||||
///
|
||||
/// For more details, see the [module level](index.html) documentation.
|
||||
pub fn current() -> TaskExecutor {
|
||||
TaskExecutor {
|
||||
_p: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a future onto the current `CurrentThread` instance.
|
||||
pub fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
CURRENT.with(|current| {
|
||||
match current.spawn.get() {
|
||||
Some(spawn) => {
|
||||
unsafe { (*spawn).spawn_local(future, false) };
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_executor::Executor for TaskExecutor {
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
self.spawn_local(future)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Executor<F> for TaskExecutor
|
||||
where F: Future<Item = (), Error = ()> + 'static
|
||||
{
|
||||
fn execute(&self, future: F) -> Result<(), ExecuteError<F>> {
|
||||
CURRENT.with(|current| {
|
||||
match current.spawn.get() {
|
||||
Some(spawn) => {
|
||||
unsafe { (*spawn).spawn_local(Box::new(future), false) };
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
Err(ExecuteError::new(ExecuteErrorKind::Shutdown, future))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Borrow =====
|
||||
|
||||
impl<'a, U: Unpark> Borrow<'a, U> {
|
||||
fn enter<F, R>(&mut self, _: &mut Enter, f: F) -> R
|
||||
where F: FnOnce() -> R,
|
||||
{
|
||||
CURRENT.with(|current| {
|
||||
current.set_spawn(self, || {
|
||||
f()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> SpawnLocal for Borrow<'a, U> {
|
||||
fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>, already_counted: bool) {
|
||||
if !already_counted {
|
||||
// NOTE: we have a borrow of the Runtime, so we know that it isn't shut down.
|
||||
// NOTE: += 2 since LSB is the shutdown bit
|
||||
self.num_futures.fetch_add(2, atomic::Ordering::SeqCst);
|
||||
}
|
||||
self.scheduler.schedule(future);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl CurrentRunner =====
|
||||
|
||||
impl CurrentRunner {
|
||||
fn set_spawn<F, R>(&self, spawn: &mut SpawnLocal, f: F) -> R
|
||||
where F: FnOnce() -> R
|
||||
{
|
||||
struct Reset<'a>(&'a CurrentRunner);
|
||||
|
||||
impl<'a> Drop for Reset<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.0.spawn.set(None);
|
||||
}
|
||||
}
|
||||
|
||||
let _reset = Reset(self);
|
||||
|
||||
let spawn = unsafe { hide_lt(spawn as *mut SpawnLocal) };
|
||||
self.spawn.set(Some(spawn));
|
||||
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn hide_lt<'a>(p: *mut (SpawnLocal + 'a)) -> *mut (SpawnLocal + 'static) {
|
||||
use std::mem;
|
||||
mem::transmute(p)
|
||||
}
|
||||
|
||||
// ===== impl RunTimeoutError =====
|
||||
|
||||
impl RunTimeoutError {
|
||||
fn new(timeout: bool) -> Self {
|
||||
RunTimeoutError { timeout }
|
||||
}
|
||||
|
||||
/// Returns `true` if the error was caused by the operation timing out.
|
||||
pub fn is_timeout(&self) -> bool {
|
||||
self.timeout
|
||||
}
|
||||
}
|
||||
|
||||
impl From<tokio_executor::EnterError> for RunTimeoutError {
|
||||
fn from(_: tokio_executor::EnterError) -> Self {
|
||||
RunTimeoutError::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl BlockError =====
|
||||
|
||||
impl<T> BlockError<T> {
|
||||
/// Returns the error yielded by the future being blocked on
|
||||
pub fn into_inner(self) -> Option<T> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<tokio_executor::EnterError> for BlockError<T> {
|
||||
fn from(_: tokio_executor::EnterError) -> Self {
|
||||
BlockError { inner: None }
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ use std::fmt::{self, Debug};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::Ordering::{Relaxed, SeqCst, Acquire, Release, AcqRel};
|
||||
use std::sync::atomic::{AtomicPtr, AtomicBool, AtomicUsize};
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::usize;
|
||||
use std::thread;
|
||||
@@ -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
|
||||
@@ -210,7 +210,7 @@ where U: Unpark,
|
||||
///
|
||||
/// This function should be called whenever the caller is notified via a
|
||||
/// wakeup.
|
||||
pub fn tick(&mut self, enter: &mut Enter, num_futures: &mut usize) -> bool
|
||||
pub fn tick(&mut self, enter: &mut Enter, num_futures: &AtomicUsize) -> bool
|
||||
{
|
||||
let mut ret = false;
|
||||
let tick = self.inner.tick_num.fetch_add(1, SeqCst)
|
||||
@@ -330,7 +330,8 @@ where U: Unpark,
|
||||
};
|
||||
|
||||
if borrow.enter(enter, || scheduled.tick()) {
|
||||
*borrow.num_futures -= 1;
|
||||
// we have a borrow of the Runtime, so we know it's not shut down
|
||||
borrow.num_futures.fetch_sub(2, SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,7 +643,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,10 +1,8 @@
|
||||
#![cfg(not(feature = "unstable-futures"))]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate tokio_current_thread;
|
||||
extern crate tokio_executor;
|
||||
extern crate futures;
|
||||
|
||||
use tokio::executor::current_thread::{self, block_on_all, CurrentThread};
|
||||
use tokio_current_thread::{block_on_all, CurrentThread};
|
||||
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
@@ -22,11 +20,11 @@ fn spawn_from_block_on_all() {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let msg = current_thread::block_on_all(lazy(move || {
|
||||
let msg = tokio_current_thread::block_on_all(lazy(move || {
|
||||
c.set(1 + c.get());
|
||||
|
||||
// Spawn!
|
||||
current_thread::spawn(lazy(move || {
|
||||
tokio_current_thread::spawn(lazy(move || {
|
||||
c.set(1 + c.get());
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
@@ -63,17 +61,17 @@ fn spawn_many() {
|
||||
const ITER: usize = 200;
|
||||
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let mut current_thread = CurrentThread::new();
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
for _ in 0..ITER {
|
||||
let cnt = cnt.clone();
|
||||
current_thread.spawn(lazy(move || {
|
||||
tokio_current_thread.spawn(lazy(move || {
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
}
|
||||
|
||||
current_thread.run().unwrap();
|
||||
tokio_current_thread.run().unwrap();
|
||||
|
||||
assert_eq!(cnt.get(), ITER);
|
||||
}
|
||||
@@ -95,12 +93,12 @@ fn does_not_set_global_executor_by_default() {
|
||||
fn spawn_from_block_on_future() {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
|
||||
let mut current_thread = CurrentThread::new();
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
current_thread.block_on(lazy(|| {
|
||||
tokio_current_thread.block_on(lazy(|| {
|
||||
let cnt = cnt.clone();
|
||||
|
||||
current_thread::spawn(lazy(move || {
|
||||
tokio_current_thread::spawn(lazy(move || {
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok(())
|
||||
}));
|
||||
@@ -108,7 +106,7 @@ fn spawn_from_block_on_future() {
|
||||
Ok::<_, ()>(())
|
||||
})).unwrap();
|
||||
|
||||
current_thread.run().unwrap();
|
||||
tokio_current_thread.run().unwrap();
|
||||
|
||||
assert_eq!(1, cnt.get());
|
||||
}
|
||||
@@ -128,10 +126,10 @@ impl Future for Never {
|
||||
fn outstanding_tasks_are_dropped_when_executor_is_dropped() {
|
||||
let mut rc = Rc::new(());
|
||||
|
||||
let mut current_thread = CurrentThread::new();
|
||||
current_thread.spawn(Never(rc.clone()));
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
tokio_current_thread.spawn(Never(rc.clone()));
|
||||
|
||||
drop(current_thread);
|
||||
drop(tokio_current_thread);
|
||||
|
||||
// Ensure the daemon is dropped
|
||||
assert!(Rc::get_mut(&mut rc).is_some());
|
||||
@@ -140,14 +138,14 @@ fn outstanding_tasks_are_dropped_when_executor_is_dropped() {
|
||||
|
||||
let mut rc = Rc::new(());
|
||||
|
||||
let mut current_thread = CurrentThread::new();
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
current_thread.block_on(lazy(|| {
|
||||
current_thread::spawn(Never(rc.clone()));
|
||||
tokio_current_thread.block_on(lazy(|| {
|
||||
tokio_current_thread::spawn(Never(rc.clone()));
|
||||
Ok::<_, ()>(())
|
||||
})).unwrap();
|
||||
|
||||
drop(current_thread);
|
||||
drop(tokio_current_thread);
|
||||
|
||||
// Ensure the daemon is dropped
|
||||
assert!(Rc::get_mut(&mut rc).is_some());
|
||||
@@ -169,7 +167,7 @@ fn nesting_run() {
|
||||
#[should_panic]
|
||||
fn run_in_future() {
|
||||
block_on_all(lazy(|| {
|
||||
current_thread::spawn(lazy(|| {
|
||||
tokio_current_thread::spawn(lazy(|| {
|
||||
block_on_all(lazy(|| {
|
||||
ok()
|
||||
})).unwrap();
|
||||
@@ -246,12 +244,12 @@ fn tasks_are_scheduled_fairly() {
|
||||
}
|
||||
|
||||
block_on_all(lazy(|| {
|
||||
current_thread::spawn(Spin {
|
||||
tokio_current_thread::spawn(Spin {
|
||||
state: state.clone(),
|
||||
idx: 0,
|
||||
});
|
||||
|
||||
current_thread::spawn(Spin {
|
||||
tokio_current_thread::spawn(Spin {
|
||||
state: state,
|
||||
idx: 1,
|
||||
});
|
||||
@@ -265,21 +263,21 @@ fn spawn_and_turn() {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let mut current_thread = CurrentThread::new();
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
// Spawn a basic task to get the executor to turn
|
||||
current_thread.spawn(lazy(move || {
|
||||
tokio_current_thread.spawn(lazy(move || {
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
// Turn once...
|
||||
current_thread.turn(None).unwrap();
|
||||
tokio_current_thread.turn(None).unwrap();
|
||||
|
||||
current_thread.spawn(lazy(move || {
|
||||
tokio_current_thread.spawn(lazy(move || {
|
||||
c.set(1 + c.get());
|
||||
|
||||
// Spawn!
|
||||
current_thread::spawn(lazy(move || {
|
||||
tokio_current_thread::spawn(lazy(move || {
|
||||
c.set(1 + c.get());
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
@@ -288,21 +286,21 @@ fn spawn_and_turn() {
|
||||
}));
|
||||
|
||||
// This does not run the newly spawned thread
|
||||
current_thread.turn(None).unwrap();
|
||||
tokio_current_thread.turn(None).unwrap();
|
||||
assert_eq!(1, cnt.get());
|
||||
|
||||
// This runs the newly spawned thread
|
||||
current_thread.turn(None).unwrap();
|
||||
tokio_current_thread.turn(None).unwrap();
|
||||
assert_eq!(2, cnt.get());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_in_drop() {
|
||||
let mut current_thread = CurrentThread::new();
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
current_thread.spawn({
|
||||
tokio_current_thread.spawn({
|
||||
struct OnDrop<F: FnOnce()>(Option<F>);
|
||||
|
||||
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||
@@ -326,7 +324,7 @@ fn spawn_in_drop() {
|
||||
|
||||
MyFuture {
|
||||
_data: Box::new(OnDrop(Some(move || {
|
||||
current_thread::spawn(lazy(move || {
|
||||
tokio_current_thread::spawn(lazy(move || {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
}));
|
||||
@@ -334,8 +332,8 @@ fn spawn_in_drop() {
|
||||
}
|
||||
});
|
||||
|
||||
current_thread.block_on(rx).unwrap();
|
||||
current_thread.run().unwrap();
|
||||
tokio_current_thread.block_on(rx).unwrap();
|
||||
tokio_current_thread.run().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -352,11 +350,11 @@ fn hammer_turn() {
|
||||
// Add some jitter
|
||||
for _ in 0..THREADS {
|
||||
let th = thread::spawn(|| {
|
||||
let mut current_thread = CurrentThread::new();
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
|
||||
current_thread.spawn({
|
||||
tokio_current_thread.spawn({
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
@@ -378,8 +376,8 @@ fn hammer_turn() {
|
||||
}
|
||||
});
|
||||
|
||||
while !current_thread.is_idle() {
|
||||
current_thread.turn(None).unwrap();
|
||||
while !tokio_current_thread.is_idle() {
|
||||
tokio_current_thread.turn(None).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -394,20 +392,20 @@ fn hammer_turn() {
|
||||
|
||||
#[test]
|
||||
fn turn_has_polled() {
|
||||
let mut current_thread = CurrentThread::new();
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
// Spawn oneshot receiver
|
||||
let (sender, receiver) = oneshot::channel::<()>();
|
||||
current_thread.spawn(receiver.then(|_| Ok(())));
|
||||
tokio_current_thread.spawn(receiver.then(|_| Ok(())));
|
||||
|
||||
// Turn once...
|
||||
let res = current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
let res = tokio_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();
|
||||
let res = tokio_current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
|
||||
// Should've polled nothing, the receiver is not ready yet
|
||||
assert!(!res.has_polled());
|
||||
@@ -416,14 +414,14 @@ fn turn_has_polled() {
|
||||
sender.send(()).unwrap();
|
||||
|
||||
// Turn another time
|
||||
let res = current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
let res = tokio_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();
|
||||
assert!(tokio_current_thread.is_idle());
|
||||
let res = tokio_current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
|
||||
// So should've polled nothing
|
||||
assert!(!res.has_polled());
|
||||
@@ -478,14 +476,14 @@ fn turn_fair() {
|
||||
send_now: send_now.clone(),
|
||||
};
|
||||
|
||||
let mut current_thread = CurrentThread::new_with_park(my_park);
|
||||
let mut tokio_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
|
||||
tokio_current_thread.spawn(receiver
|
||||
.map_err(|_| unreachable!())
|
||||
.and_then(move |_| {
|
||||
sender_2.send(()).unwrap();
|
||||
@@ -498,7 +496,7 @@ fn turn_fair() {
|
||||
let receiver_2_done = Rc::new(Cell::new(false));
|
||||
let receiver_2_done_clone = receiver_2_done.clone();
|
||||
|
||||
current_thread.spawn(receiver_2
|
||||
tokio_current_thread.spawn(receiver_2
|
||||
.map_err(|_| unreachable!())
|
||||
.and_then(move |_| {
|
||||
receiver_2_done_clone.set(true);
|
||||
@@ -511,7 +509,7 @@ fn turn_fair() {
|
||||
let receiver_3_done = Rc::new(Cell::new(false));
|
||||
let receiver_3_done_clone = receiver_3_done.clone();
|
||||
|
||||
current_thread.spawn(receiver_3
|
||||
tokio_current_thread.spawn(receiver_3
|
||||
.map_err(|_| unreachable!())
|
||||
.and_then(move |_| {
|
||||
receiver_3_done_clone.set(true);
|
||||
@@ -520,11 +518,11 @@ fn turn_fair() {
|
||||
);
|
||||
|
||||
// First turn should've polled both and considered them not ready
|
||||
let res = current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
let res = tokio_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();
|
||||
let res = tokio_current_thread.turn(Some(Duration::from_millis(0))).unwrap();
|
||||
assert!(!res.has_polled());
|
||||
|
||||
assert!(!receiver_1_done.get());
|
||||
@@ -537,7 +535,7 @@ fn turn_fair() {
|
||||
|
||||
// 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();
|
||||
let res = tokio_current_thread.turn(None).unwrap();
|
||||
assert!(res.has_polled());
|
||||
|
||||
assert!(receiver_1_done.get());
|
||||
@@ -551,7 +549,7 @@ fn turn_fair() {
|
||||
// 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();
|
||||
let res = tokio_current_thread.turn(None).unwrap();
|
||||
assert!(res.has_polled());
|
||||
|
||||
assert!(receiver_1_done.get());
|
||||
@@ -562,11 +560,61 @@ fn turn_fair() {
|
||||
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!(tokio_current_thread.is_idle());
|
||||
let res = tokio_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,3 +1,15 @@
|
||||
# 0.1.4 (August 23, 2018)
|
||||
|
||||
* Implement `std::error::Error` for error types (#511).
|
||||
|
||||
# 0.1.3 (August 6, 2018)
|
||||
|
||||
* Implement `Executor` for `Box<E: Executor>` (#420).
|
||||
* Improve `EnterError` debug message (#410).
|
||||
* Implement `status`, `Send`, and `Sync` for `DefaultExecutor` (#463, #472).
|
||||
* Fix race in `ParkThread` (#507).
|
||||
* Handle recursive calls into `DefaultExecutor` (#473).
|
||||
|
||||
# 0.1.2 (March 30, 2018)
|
||||
|
||||
* Implement `Unpark` for `Box<Unpark>`.
|
||||
|
||||
@@ -4,9 +4,10 @@ name = "tokio-executor"
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.2"
|
||||
documentation = "https://docs.rs/tokio-executor"
|
||||
version = "0.1.4"
|
||||
documentation = "https://docs.rs/tokio-executor/0.1.4/tokio_executor"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://github.com/tokio-rs/tokio"
|
||||
license = "MIT"
|
||||
|
||||
+21
-10
@@ -1,10 +1,8 @@
|
||||
use std::prelude::v1::*;
|
||||
use std::cell::Cell;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
thread_local!(static ENTERED: Cell<bool> = Cell::new(false));
|
||||
|
||||
/// Represents an executor context.
|
||||
@@ -13,18 +11,34 @@ thread_local!(static ENTERED: Cell<bool> = Cell::new(false));
|
||||
pub struct Enter {
|
||||
on_exit: Vec<Box<Callback>>,
|
||||
permanent: bool,
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
_enter2: futures2::executor::Enter,
|
||||
}
|
||||
|
||||
/// An error returned by `enter` if an execution scope has already been
|
||||
/// entered.
|
||||
#[derive(Debug)]
|
||||
pub struct EnterError {
|
||||
_a: (),
|
||||
}
|
||||
|
||||
impl fmt::Debug for EnterError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("EnterError")
|
||||
.field("reason", &self.description())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EnterError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for EnterError {
|
||||
fn description(&self) -> &str {
|
||||
"attempted to run an executor while another executor is already running"
|
||||
}
|
||||
}
|
||||
|
||||
/// Marks the current thread as being within the dynamic extent of an
|
||||
/// executor.
|
||||
///
|
||||
@@ -46,9 +60,6 @@ pub fn enter() -> Result<Enter, EnterError> {
|
||||
Ok(Enter {
|
||||
on_exit: Vec::new(),
|
||||
permanent: false,
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
_enter2: futures2::executor::enter().unwrap(),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,11 +3,6 @@ use super::{Executor, Enter, SpawnError};
|
||||
use futures::Future;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
use futures2;
|
||||
|
||||
/// Executes futures on the default executor for the current execution context.
|
||||
///
|
||||
@@ -21,8 +16,7 @@ use futures2;
|
||||
/// variable and is set using `tokio_executor::with_default`
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DefaultExecutor {
|
||||
// Prevent the handle from moving across threads.
|
||||
_p: PhantomData<Rc<()>>,
|
||||
_dummy: (),
|
||||
}
|
||||
|
||||
impl DefaultExecutor {
|
||||
@@ -34,15 +28,44 @@ impl DefaultExecutor {
|
||||
/// the default **at the time `spawn` is called**. This enables
|
||||
/// `DefaultExecutor::current()` to be called before an execution context is
|
||||
/// setup, then passed **into** an execution context before it is used.
|
||||
///
|
||||
/// This is also true for sending the handle across threads, so calling
|
||||
/// `DefaultExecutor::current()` on thread A and then sending the result to
|
||||
/// thread B will _not_ reference the default executor that was set on thread A.
|
||||
pub fn current() -> DefaultExecutor {
|
||||
DefaultExecutor {
|
||||
_p: PhantomData,
|
||||
_dummy: (),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_current<F: FnOnce(&mut Executor) -> R, R>(f: F) -> Option<R> {
|
||||
EXECUTOR.with(|current_executor| {
|
||||
match current_executor.replace(State::Active) {
|
||||
State::Ready(executor_ptr) => {
|
||||
let executor = unsafe { &mut *executor_ptr };
|
||||
let result = f(executor);
|
||||
current_executor.set(State::Ready(executor_ptr));
|
||||
Some(result)
|
||||
},
|
||||
State::Empty | State::Active => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum State {
|
||||
// default executor not defined
|
||||
Empty,
|
||||
// default executor is defined and ready to be used
|
||||
Ready(*mut Executor),
|
||||
// default executor is currently active (used to detect recursive calls)
|
||||
Active
|
||||
}
|
||||
|
||||
/// Thread-local tracking the current executor
|
||||
thread_local!(static EXECUTOR: Cell<Option<*mut Executor>> = Cell::new(None));
|
||||
thread_local!(static EXECUTOR: Cell<State> = Cell::new(State::Empty));
|
||||
|
||||
// ===== impl DefaultExecutor =====
|
||||
|
||||
@@ -50,34 +73,13 @@ impl super::Executor for DefaultExecutor {
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
EXECUTOR.with(|current_executor| {
|
||||
match current_executor.get() {
|
||||
Some(executor) => {
|
||||
let executor = unsafe { &mut *executor };
|
||||
executor.spawn(future)
|
||||
}
|
||||
None => {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
})
|
||||
DefaultExecutor::with_current(|executor| executor.spawn(future))
|
||||
.unwrap_or_else(|| Err(SpawnError::shutdown()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
fn spawn2(&mut self, future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
|
||||
-> Result<(), futures2::executor::SpawnError>
|
||||
{
|
||||
EXECUTOR.with(|current_executor| {
|
||||
match current_executor.get() {
|
||||
Some(executor) => {
|
||||
let executor = unsafe { &mut *executor };
|
||||
executor.spawn2(future)
|
||||
}
|
||||
None => {
|
||||
Err(futures2::executor::SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
})
|
||||
fn status(&self) -> Result<(), SpawnError> {
|
||||
DefaultExecutor::with_current(|executor| executor.status())
|
||||
.unwrap_or_else(|| Err(SpawnError::shutdown()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,15 +131,6 @@ pub fn spawn<T>(future: T)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Like `spawn` but compatible with futures 0.2
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub fn spawn2<T>(future: T)
|
||||
where T: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
|
||||
{
|
||||
DefaultExecutor::current().spawn2(Box::new(future))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Set the default executor for the duration of the closure
|
||||
///
|
||||
/// # Panics
|
||||
@@ -148,15 +141,19 @@ where T: Executor,
|
||||
F: FnOnce(&mut Enter) -> R
|
||||
{
|
||||
EXECUTOR.with(|cell| {
|
||||
assert!(cell.get().is_none(), "default executor already set for execution context");
|
||||
match cell.get() {
|
||||
State::Ready(_) | State::Active =>
|
||||
panic!("default executor already set for execution context"),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Ensure that the executor is removed from the thread-local context
|
||||
// when leaving the scope. This handles cases that involve panicking.
|
||||
struct Reset<'a>(&'a Cell<Option<*mut Executor>>);
|
||||
struct Reset<'a>(&'a Cell<State>);
|
||||
|
||||
impl<'a> Drop for Reset<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.0.set(None);
|
||||
self.0.set(State::Empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +168,7 @@ where T: Executor,
|
||||
// cells require.
|
||||
let executor = unsafe { hide_lt(executor as &mut _ as *mut _) };
|
||||
|
||||
cell.set(Some(executor));
|
||||
cell.set(State::Ready(executor));
|
||||
|
||||
f(enter)
|
||||
})
|
||||
@@ -181,3 +178,27 @@ unsafe fn hide_lt<'a>(p: *mut (Executor + 'a)) -> *mut (Executor + 'static) {
|
||||
use std::mem;
|
||||
mem::transmute(p)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Executor, DefaultExecutor, with_default};
|
||||
|
||||
#[test]
|
||||
fn default_executor_is_send_and_sync() {
|
||||
fn assert_send_sync<T: Send + Sync>() {}
|
||||
|
||||
assert_send_sync::<DefaultExecutor>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_default_executor_status() {
|
||||
let mut enter = super::super::enter().unwrap();
|
||||
let mut executor = DefaultExecutor::current();
|
||||
|
||||
let result = with_default(&mut executor, &mut enter, |_| {
|
||||
DefaultExecutor::current().status()
|
||||
});
|
||||
|
||||
assert!(result.err().unwrap().is_shutdown())
|
||||
}
|
||||
}
|
||||
|
||||
+35
-17
@@ -1,3 +1,6 @@
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.4")]
|
||||
|
||||
//! Task execution related traits and utilities.
|
||||
//!
|
||||
//! In the Tokio execution model, futures are lazy. When a future is created, no
|
||||
@@ -6,9 +9,10 @@
|
||||
//! an executor is called a "task".
|
||||
//!
|
||||
//! The executor is responsible for ensuring that [`Future::poll`] is called
|
||||
//! whenever the task is [notified]. Notification happens when the internal
|
||||
//! state of a task transitions from "not ready" to ready. For example, a socket
|
||||
//! might have received data and a call to `read` will now be able to succeed.
|
||||
//! whenever the task is notified. Notification happens when the internal
|
||||
//! state of a task transitions from *not ready* to *ready*. For example, a
|
||||
//! socket might have received data and a call to `read` will now be able to
|
||||
//! succeed.
|
||||
//!
|
||||
//! This crate provides traits and utilities that are necessary for building an
|
||||
//! executor, including:
|
||||
@@ -29,15 +33,10 @@
|
||||
//! [`enter`]: fn.enter.html
|
||||
//! [`DefaultExecutor`]: struct.DefaultExecutor.html
|
||||
//! [`Park`]: park/index.html
|
||||
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.2")]
|
||||
//! [`Future::poll`]: https://docs.rs/futures/0.1/futures/future/trait.Future.html#tymethod.poll
|
||||
|
||||
extern crate futures;
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
extern crate futures2;
|
||||
|
||||
mod enter;
|
||||
mod global;
|
||||
pub mod park;
|
||||
@@ -45,11 +44,11 @@ pub mod park;
|
||||
pub use enter::{enter, Enter, EnterError};
|
||||
pub use global::{spawn, with_default, DefaultExecutor};
|
||||
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
pub use global::spawn2;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
/// A value that executes futures.
|
||||
///
|
||||
/// The [`spawn`] function is used to submit a future to an executor. Once
|
||||
@@ -137,11 +136,6 @@ pub trait Executor {
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), SpawnError>;
|
||||
|
||||
/// Like `spawn`, but compatible with futures 0.2
|
||||
#[cfg(feature = "unstable-futures")]
|
||||
fn spawn2(&mut self, future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
|
||||
-> Result<(), futures2::executor::SpawnError>;
|
||||
|
||||
/// Provides a best effort **hint** to whether or not `spawn` will succeed.
|
||||
///
|
||||
/// This function may return both false positives **and** false negatives.
|
||||
@@ -182,6 +176,18 @@ pub trait Executor {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Executor + ?Sized> Executor for Box<E> {
|
||||
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
|
||||
-> Result<(), SpawnError>
|
||||
{
|
||||
(**self).spawn(future)
|
||||
}
|
||||
|
||||
fn status(&self) -> Result<(), SpawnError> {
|
||||
(**self).status()
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors returned by `Executor::spawn`.
|
||||
///
|
||||
/// Spawn errors should represent relatively rare scenarios. Currently, the two
|
||||
@@ -217,3 +223,15 @@ impl SpawnError {
|
||||
!self.is_shutdown
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SpawnError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SpawnError {
|
||||
fn description(&self) -> &str {
|
||||
"attempted to spawn task while the executor is at capacity or shut down"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -42,7 +42,7 @@
|
||||
//! [`park_timeout`]: trait.Park.html#tymethod.park_timeout
|
||||
//! [`unpark`]: trait.Unpark.html#tymethod.unpark
|
||||
//! [up]: trait.Unpark.html
|
||||
//! [mio]: https://docs.rs/mio/0.6.13/mio/struct.Poll.html
|
||||
//! [mio]: https://docs.rs/mio/0.6/mio/struct.Poll.html
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
@@ -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);
|
||||
|
||||
@@ -288,10 +288,12 @@ impl Inner {
|
||||
// The other half is sleeping, this requires a lock
|
||||
let _m = self.mutex.lock().unwrap();
|
||||
|
||||
// Transition from SLEEP -> NOTIFY
|
||||
match self.state.compare_and_swap(SLEEP, NOTIFY, Ordering::SeqCst) {
|
||||
// Transition to NOTIFY
|
||||
match self.state.swap(NOTIFY, Ordering::SeqCst) {
|
||||
SLEEP => {}
|
||||
_ => return,
|
||||
NOTIFY => return,
|
||||
IDLE => return,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
// Wakeup the sleeper
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
# 0.1.3 (August 6, 2018)
|
||||
|
||||
* Add async equivalents to most of `std::fs` (#494).
|
||||
|
||||
# 0.1.2 (July 11, 2018)
|
||||
|
||||
* Add `metadata` and `File::metadata` ([#433](https://github.com/tokio-rs/tokio/pull/433), [#385](https://github.com/tokio-rs/tokio/pull/385))
|
||||
* Add `File::seek` ([#434](https://github.com/tokio-rs/tokio/pull/434))
|
||||
|
||||
# 0.1.1 (June 13, 2018)
|
||||
|
||||
* Add `OpenOptions` ([#390](https://github.com/tokio-rs/tokio/pull/390))
|
||||
* Add `into_std` to `File` ([#403](https://github.com/tokio-rs/tokio/pull/403))
|
||||
* Use `tokio-codec` in examples
|
||||
|
||||
# 0.1.0 (May 2, 2018)
|
||||
|
||||
* Initial release
|
||||
|
||||
+6
-5
@@ -5,7 +5,7 @@ name = "tokio-fs"
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.0"
|
||||
version = "0.1.3"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
@@ -20,11 +20,12 @@ categories = ["asynchronous", "network-programming", "filesystem"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.21"
|
||||
# TODO: Set real version
|
||||
tokio-threadpool = { version = "0.1.1", path = "../tokio-threadpool" }
|
||||
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"
|
||||
rand = "0.5"
|
||||
tempfile = "3"
|
||||
tokio-io = { version = "0.1.6", path = "../tokio-io" }
|
||||
tokio-codec = { version = "0.1.0", path = "../tokio-codec" }
|
||||
tokio = { version = "0.1.7", path = ".." }
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
//! Echo everything received on STDIN to STDOUT.
|
||||
#![deny(deprecated, warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_fs;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_codec;
|
||||
extern crate tokio_threadpool;
|
||||
|
||||
use tokio_fs::{stdin, stdout, stderr};
|
||||
use tokio_io::codec::{FramedRead, FramedWrite, LinesCodec};
|
||||
use tokio_codec::{FramedRead, FramedWrite, LinesCodec};
|
||||
use tokio_threadpool::Builder;
|
||||
|
||||
use futures::{Future, Stream, Sink};
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Creates a new, empty directory at the provided path
|
||||
///
|
||||
/// This is an async version of [`std::fs::create_dir`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.create_dir.html
|
||||
pub fn create_dir<P: AsRef<Path>>(path: P) -> CreateDirFuture<P> {
|
||||
CreateDirFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `create_dir`.
|
||||
#[derive(Debug)]
|
||||
pub struct CreateDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> CreateDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
fn new(path: P) -> CreateDirFuture<P> {
|
||||
CreateDirFuture {
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for CreateDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::create_dir(&self.path) )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Recursively create a directory and all of its parent components if they
|
||||
/// are missing.
|
||||
///
|
||||
/// This is an async version of [`std::fs::create_dir_all`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.create_dir_all.html
|
||||
pub fn create_dir_all<P: AsRef<Path>>(path: P) -> CreateDirAllFuture<P> {
|
||||
CreateDirAllFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `create_dir_all`.
|
||||
#[derive(Debug)]
|
||||
pub struct CreateDirAllFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> CreateDirAllFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
fn new(path: P) -> CreateDirAllFuture<P> {
|
||||
CreateDirAllFuture {
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for CreateDirAllFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::create_dir_all(&self.path) )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
use super::File;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::fs::File as StdFile;
|
||||
use std::fs::Metadata;
|
||||
use std::io;
|
||||
|
||||
const POLL_AFTER_RESOLVE: &str = "Cannot poll MetadataFuture after it resolves";
|
||||
|
||||
/// Future returned by `File::metadata` and resolves to a `(Metadata, File)` instance.
|
||||
#[derive(Debug)]
|
||||
pub struct MetadataFuture {
|
||||
file: Option<File>,
|
||||
}
|
||||
|
||||
impl MetadataFuture {
|
||||
pub(crate) fn new(file: File) -> Self {
|
||||
MetadataFuture { file: Some(file) }
|
||||
}
|
||||
|
||||
fn std(&mut self) -> &mut StdFile {
|
||||
self.file.as_mut().expect(POLL_AFTER_RESOLVE).std()
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for MetadataFuture {
|
||||
type Item = (File, Metadata);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let metadata = try_ready!(::blocking_io(|| {
|
||||
StdFile::metadata(self.std())
|
||||
}));
|
||||
|
||||
let file = self.file.take().expect(POLL_AFTER_RESOLVE);
|
||||
Ok((file, metadata).into())
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
//! Types for working with [`File`].
|
||||
//!
|
||||
//! [`File`]: struct.File.html
|
||||
//! [`File`]: file/struct.File.html
|
||||
|
||||
mod create;
|
||||
mod metadata;
|
||||
mod open;
|
||||
mod open_options;
|
||||
mod seek;
|
||||
|
||||
pub use self::create::CreateFuture;
|
||||
pub use self::metadata::MetadataFuture;
|
||||
pub use self::open::OpenFuture;
|
||||
pub use self::open_options::OpenOptions;
|
||||
pub use self::seek::SeekFuture;
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
@@ -36,16 +42,20 @@ pub struct File {
|
||||
impl File {
|
||||
/// Attempts to open a file in read-only mode.
|
||||
///
|
||||
/// See [`OpenOptions`] for more details.
|
||||
///
|
||||
/// [`OpenOptions`]: struct.OpenOptions.html
|
||||
///
|
||||
/// # 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
|
||||
/// [`open`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.open
|
||||
pub fn open<P>(path: P) -> OpenFuture<P>
|
||||
where P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
OpenFuture::new(path)
|
||||
OpenOptions::new().read(true).open(path)
|
||||
}
|
||||
|
||||
/// Opens a file in write-only mode.
|
||||
@@ -53,10 +63,16 @@ impl File {
|
||||
/// This function will create a file if it does not exist, and will truncate
|
||||
/// it if it does.
|
||||
///
|
||||
/// See [`OpenOptions`] for more details.
|
||||
///
|
||||
/// [`OpenOptions`]: struct.OpenOptions.html
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// `CreateFuture` results in an error if called from outside of the Tokio
|
||||
/// runtime or if the underlying [`create`] call results in an error.
|
||||
///
|
||||
/// [`open`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create
|
||||
/// [`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,
|
||||
{
|
||||
@@ -86,6 +102,16 @@ impl File {
|
||||
::blocking_io(|| self.std().seek(pos))
|
||||
}
|
||||
|
||||
/// Seek to an offset, in bytes, in a stream.
|
||||
///
|
||||
/// Similar to `poll_seek`, but returning a `Future`.
|
||||
///
|
||||
/// This method consumes the `File` and returns it back when the future
|
||||
/// completes.
|
||||
pub fn seek(self, pos: io::SeekFrom) -> SeekFuture {
|
||||
SeekFuture::new(self, pos)
|
||||
}
|
||||
|
||||
/// Attempts to sync all OS-internal metadata to disk.
|
||||
///
|
||||
/// This function will attempt to ensure that all in-core data reaches the
|
||||
@@ -121,6 +147,11 @@ impl File {
|
||||
::blocking_io(|| self.std().set_len(size))
|
||||
}
|
||||
|
||||
/// Queries metadata about the underlying file.
|
||||
pub fn metadata(self) -> MetadataFuture {
|
||||
MetadataFuture::new(self)
|
||||
}
|
||||
|
||||
/// Queries metadata about the underlying file.
|
||||
pub fn poll_metadata(&mut self) -> Poll<Metadata, io::Error> {
|
||||
::blocking_io(|| self.std().metadata())
|
||||
@@ -155,6 +186,17 @@ impl File {
|
||||
::blocking_io(|| self.std().set_permissions(perm))
|
||||
}
|
||||
|
||||
/// Destructures the `tokio_fs::File` into a [`std::fs::File`][std].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if `shutdown` has been called.
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
|
||||
pub fn into_std(mut self) -> StdFile {
|
||||
self.std.take().expect("`File` instance already shutdown")
|
||||
}
|
||||
|
||||
fn std(&mut self) -> &mut StdFile {
|
||||
self.std.as_mut().expect("`File` instance already shutdown")
|
||||
}
|
||||
|
||||
@@ -2,21 +2,22 @@ use super::File;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::fs::File as StdFile;
|
||||
use std::fs::OpenOptions as StdOpenOptions;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Future returned by `File::open` and resolves to a `File` instance.
|
||||
#[derive(Debug)]
|
||||
pub struct OpenFuture<P> {
|
||||
options: StdOpenOptions,
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> OpenFuture<P>
|
||||
where P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
pub(crate) fn new(path: P) -> Self {
|
||||
OpenFuture { path }
|
||||
pub(crate) fn new(options: StdOpenOptions, path: P) -> Self {
|
||||
OpenFuture { options, path }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +29,7 @@ where P: AsRef<Path> + Send + 'static,
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let std = try_ready!(::blocking_io(|| {
|
||||
StdFile::open(&self.path)
|
||||
self.options.open(&self.path)
|
||||
}));
|
||||
|
||||
let file = File::from_std(std);
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
use super::OpenFuture;
|
||||
|
||||
use std::convert::From;
|
||||
use std::fs::OpenOptions as StdOpenOptions;
|
||||
use std::path::Path;
|
||||
|
||||
/// Options and flags which can be used to configure how a file is opened.
|
||||
///
|
||||
/// This is a specialized version of [`std::fs::OpenOptions`] for usage from
|
||||
/// the Tokio runtime.
|
||||
///
|
||||
/// `From<std::fs::OpenOptions>` is implemented for more advanced configuration
|
||||
/// than the methods provided here.
|
||||
///
|
||||
/// [`std::fs::OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OpenOptions(StdOpenOptions);
|
||||
|
||||
impl OpenOptions {
|
||||
/// Creates a blank new set of options ready for configuration.
|
||||
///
|
||||
/// All options are initially set to `false`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// use tokio::fs::OpenOptions;
|
||||
///
|
||||
/// let mut options = OpenOptions::new();
|
||||
/// let future = options.read(true).open("foo.txt");
|
||||
/// ```
|
||||
pub fn new() -> OpenOptions {
|
||||
OpenOptions(StdOpenOptions::new())
|
||||
}
|
||||
|
||||
/// See the underlying [`read`] call for details.
|
||||
///
|
||||
/// [`read`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.read
|
||||
pub fn read(&mut self, read: bool) -> &mut OpenOptions {
|
||||
self.0.read(read);
|
||||
self
|
||||
}
|
||||
|
||||
/// See the underlying [`write`] call for details.
|
||||
///
|
||||
/// [`write`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.write
|
||||
pub fn write(&mut self, write: bool) -> &mut OpenOptions {
|
||||
self.0.write(write);
|
||||
self
|
||||
}
|
||||
|
||||
/// See the underlying [`append`] call for details.
|
||||
///
|
||||
/// [`append`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.append
|
||||
pub fn append(&mut self, append: bool) -> &mut OpenOptions {
|
||||
self.0.append(append);
|
||||
self
|
||||
}
|
||||
|
||||
/// See the underlying [`truncate`] call for details.
|
||||
///
|
||||
/// [`truncate`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.truncate
|
||||
pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
|
||||
self.0.truncate(truncate);
|
||||
self
|
||||
}
|
||||
|
||||
/// See the underlying [`create`] call for details.
|
||||
///
|
||||
/// [`create`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create
|
||||
pub fn create(&mut self, create: bool) -> &mut OpenOptions {
|
||||
self.0.create(create);
|
||||
self
|
||||
}
|
||||
|
||||
/// See the underlying [`create_new`] call for details.
|
||||
///
|
||||
/// [`create_new`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new
|
||||
pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
|
||||
self.0.create_new(create_new);
|
||||
self
|
||||
}
|
||||
|
||||
/// Opens a file at `path` with the options specified by `self`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// `OpenOptionsFuture` 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>(&self, path: P) -> OpenFuture<P>
|
||||
where P: AsRef<Path> + Send + 'static
|
||||
{
|
||||
OpenFuture::new(self.0.clone(), path)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StdOpenOptions> for OpenOptions {
|
||||
fn from(options: StdOpenOptions) -> OpenOptions {
|
||||
OpenOptions(options)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
use super::File;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::io;
|
||||
|
||||
/// Future returned by `File::seek`.
|
||||
#[derive(Debug)]
|
||||
pub struct SeekFuture {
|
||||
inner: Option<File>,
|
||||
pos: io::SeekFrom,
|
||||
}
|
||||
|
||||
impl SeekFuture {
|
||||
pub(crate) fn new(file: File, pos: io::SeekFrom) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
inner: Some(file),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for SeekFuture {
|
||||
type Item = (File, u64);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let pos = try_ready!(
|
||||
self.inner
|
||||
.as_mut()
|
||||
.expect("Cannot poll `SeekFuture` after it resolves")
|
||||
.poll_seek(self.pos)
|
||||
);
|
||||
let inner = self.inner.take().unwrap();
|
||||
Ok((inner, pos).into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Creates a new hard link on the filesystem.
|
||||
///
|
||||
/// The `dst` path will be a link pointing to the `src` path. Note that systems
|
||||
/// often require these two paths to both be located on the same filesystem.
|
||||
///
|
||||
/// This is an async version of [`std::fs::hard_link`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.hard_link.html
|
||||
pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> HardLinkFuture<P, Q> {
|
||||
HardLinkFuture::new(src, dst)
|
||||
}
|
||||
|
||||
/// Future returned by `hard_link`.
|
||||
#[derive(Debug)]
|
||||
pub struct HardLinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
src: P,
|
||||
dst: Q,
|
||||
}
|
||||
|
||||
impl<P, Q> HardLinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
fn new(src: P, dst: Q) -> HardLinkFuture<P, Q> {
|
||||
HardLinkFuture {
|
||||
src: src,
|
||||
dst: dst,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, Q> Future for HardLinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::hard_link(&self.src, &self.dst) )
|
||||
}
|
||||
}
|
||||
+51
-11
@@ -1,31 +1,71 @@
|
||||
//! Asynchronous filesystem manipulation operations (and stdin, stdout, stderr).
|
||||
//! Asynchronous file and standard stream adaptation.
|
||||
//!
|
||||
//! This module contains basic methods and types for manipulating the contents
|
||||
//! of the local filesystem from within the context of the Tokio runtime.
|
||||
//! This module contains utility methods and adapter types for input/output to
|
||||
//! files or standard streams (`Stdin`, `Stdout`, `Stderr`), and
|
||||
//! filesystem manipulation, for use within (and only within) a 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.
|
||||
//! Tasks run by *worker* threads should not block, as this could delay
|
||||
//! servicing reactor events. Portable filesystem operations are blocking,
|
||||
//! however. This module offers adapters which use a [`blocking`] annotation
|
||||
//! to inform the runtime that a blocking operation is required. When
|
||||
//! necessary, this allows the runtime to convert the current thread from a
|
||||
//! *worker* to a *backup* thread, where blocking is acceptable.
|
||||
//!
|
||||
//! [blocking]: https://docs.rs/tokio-threadpool/0.1/tokio_threadpool/fn.blocking.html
|
||||
//! ## Usage
|
||||
//!
|
||||
//! Where possible, users should prefer the provided asynchronous-specific
|
||||
//! traits such as [`AsyncRead`], or methods returning a `Future` or `Poll`
|
||||
//! type. Adaptions also extend to traits like `std::io::Read` where methods
|
||||
//! return `std::io::Result`. Be warned that these adapted methods may return
|
||||
//! `std::io::ErrorKind::WouldBlock` if a *worker* thread can not be converted
|
||||
//! to a *backup* thread immediately. See [tokio-threadpool] for more details
|
||||
//! of the threading model and [`blocking`].
|
||||
//!
|
||||
//! [`blocking`]: https://docs.rs/tokio-threadpool/0.1/tokio_threadpool/fn.blocking.html
|
||||
//! [`AsyncRead`]: https://docs.rs/tokio-io/0.1/tokio_io/trait.AsyncRead.html
|
||||
//! [tokio-threadpool]: https://docs.rs/tokio-threadpool/0.1/tokio_threadpool
|
||||
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-fs/0.1.3")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_threadpool;
|
||||
|
||||
mod create_dir;
|
||||
mod create_dir_all;
|
||||
pub mod file;
|
||||
mod hard_link;
|
||||
mod metadata;
|
||||
pub mod os;
|
||||
mod read_dir;
|
||||
mod read_link;
|
||||
mod remove_dir;
|
||||
mod remove_file;
|
||||
mod rename;
|
||||
mod set_permissions;
|
||||
mod stdin;
|
||||
mod stdout;
|
||||
mod stderr;
|
||||
mod symlink_metadata;
|
||||
|
||||
pub use create_dir::{create_dir, CreateDirFuture};
|
||||
pub use create_dir_all::{create_dir_all, CreateDirAllFuture};
|
||||
pub use file::File;
|
||||
pub use file::OpenOptions;
|
||||
pub use hard_link::{hard_link, HardLinkFuture};
|
||||
pub use metadata::{metadata, MetadataFuture};
|
||||
pub use read_dir::{read_dir, ReadDirFuture, ReadDir, DirEntry};
|
||||
pub use read_link::{read_link, ReadLinkFuture};
|
||||
pub use remove_dir::{remove_dir, RemoveDirFuture};
|
||||
pub use remove_file::{remove_file, RemoveFileFuture};
|
||||
pub use rename::{rename, RenameFuture};
|
||||
pub use set_permissions::{set_permissions, SetPermissionsFuture};
|
||||
pub use stdin::{stdin, Stdin};
|
||||
pub use stdout::{stdout, Stdout};
|
||||
pub use stderr::{stderr, Stderr};
|
||||
pub use symlink_metadata::{symlink_metadata, SymlinkMetadataFuture};
|
||||
|
||||
use futures::Poll;
|
||||
use futures::Async::*;
|
||||
@@ -59,6 +99,6 @@ where F: FnOnce() -> io::Result<T>,
|
||||
}
|
||||
|
||||
fn blocking_err() -> io::Error {
|
||||
io::Error::new(Other, "tokio-fs::File::open must be called \
|
||||
io::Error::new(Other, "`blocking` annotated I/O must be called \
|
||||
from the context of the Tokio runtime.")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
use super::blocking_io;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::fs::{self, Metadata};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Queries the file system metadata for a path.
|
||||
pub fn metadata<P>(path: P) -> MetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
MetadataFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `metadata`.
|
||||
#[derive(Debug)]
|
||||
pub struct MetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> MetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
pub(crate) fn new(path: P) -> Self {
|
||||
Self { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for MetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
type Item = Metadata;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
blocking_io(|| fs::metadata(&self.path))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
//! OS-specific functionality.
|
||||
|
||||
#[cfg(unix)]
|
||||
pub mod unix;
|
||||
#[cfg(windows)]
|
||||
pub mod windows;
|
||||
@@ -0,0 +1,55 @@
|
||||
//! Unix-specific extensions to primitives in the `tokio_fs` module.
|
||||
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::os::unix::fs;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Creates a new symbolic link on the filesystem.
|
||||
///
|
||||
/// The `dst` path will be a symbolic link pointing to the `src` path.
|
||||
///
|
||||
/// This is an async version of [`std::os::unix::fs::symlink`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html
|
||||
pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> SymlinkFuture<P, Q> {
|
||||
SymlinkFuture::new(src, dst)
|
||||
}
|
||||
|
||||
/// Future returned by `symlink`.
|
||||
#[derive(Debug)]
|
||||
pub struct SymlinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
src: P,
|
||||
dst: Q,
|
||||
}
|
||||
|
||||
impl<P, Q> SymlinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
fn new(src: P, dst: Q) -> SymlinkFuture<P, Q> {
|
||||
SymlinkFuture {
|
||||
src: src,
|
||||
dst: dst,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, Q> Future for SymlinkFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::symlink(&self.src, &self.dst) )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
//! Windows-specific extensions for the primitives in the `tokio_fs` module.
|
||||
|
||||
mod symlink_dir;
|
||||
mod symlink_file;
|
||||
|
||||
pub use self::symlink_dir::{symlink_dir, SymlinkDirFuture};
|
||||
pub use self::symlink_file::{symlink_file, SymlinkFileFuture};
|
||||
@@ -0,0 +1,54 @@
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::os::windows::fs;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Creates a new directory symlink on the filesystem.
|
||||
///
|
||||
/// The `dst` path will be a directory symbolic link pointing to the `src`
|
||||
/// path.
|
||||
///
|
||||
/// This is an async version of [`std::os::windows::fs::symlink_dir`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
|
||||
pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> SymlinkDirFuture<P, Q> {
|
||||
SymlinkDirFuture::new(src, dst)
|
||||
}
|
||||
|
||||
/// Future returned by `symlink_dir`.
|
||||
#[derive(Debug)]
|
||||
pub struct SymlinkDirFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
src: P,
|
||||
dst: Q,
|
||||
}
|
||||
|
||||
impl<P, Q> SymlinkDirFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
fn new(src: P, dst: Q) -> SymlinkDirFuture<P, Q> {
|
||||
SymlinkDirFuture {
|
||||
src: src,
|
||||
dst: dst,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, Q> Future for SymlinkDirFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::symlink_dir(&self.src, &self.dst) )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::os::windows::fs;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Creates a new file symbolic link on the filesystem.
|
||||
///
|
||||
/// The `dst` path will be a file symbolic link pointing to the `src`
|
||||
/// path.
|
||||
///
|
||||
/// This is an async version of [`std::os::windows::fs::symlink_file`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
|
||||
pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> SymlinkFileFuture<P, Q> {
|
||||
SymlinkFileFuture::new(src, dst)
|
||||
}
|
||||
|
||||
/// Future returned by `symlink_file`.
|
||||
#[derive(Debug)]
|
||||
pub struct SymlinkFileFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
src: P,
|
||||
dst: Q,
|
||||
}
|
||||
|
||||
impl<P, Q> SymlinkFileFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
fn new(src: P, dst: Q) -> SymlinkFileFuture<P, Q> {
|
||||
SymlinkFileFuture {
|
||||
src: src,
|
||||
dst: dst,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, Q> Future for SymlinkFileFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::symlink_file(&self.src, &self.dst) )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{self, DirEntry as StdDirEntry, ReadDir as StdReadDir, FileType, Metadata};
|
||||
use std::io;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::DirEntryExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use futures::{Future, Poll, Stream};
|
||||
|
||||
/// Returns a stream over the entries within a directory.
|
||||
///
|
||||
/// This is an async version of [`std::fs::read_dir`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.read_dir.html
|
||||
pub fn read_dir<P>(path: P) -> ReadDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
ReadDirFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `read_dir`.
|
||||
#[derive(Debug)]
|
||||
pub struct ReadDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> ReadDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static
|
||||
{
|
||||
fn new(path: P) -> ReadDirFuture<P> {
|
||||
ReadDirFuture {
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for ReadDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
type Item = ReadDir;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, io::Error> {
|
||||
::blocking_io(|| Ok(ReadDir(fs::read_dir(&self.path)?)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Stream of the entries in a directory.
|
||||
///
|
||||
/// This stream is returned from the [`read_dir`] function of this module and
|
||||
/// will yield instances of [`DirEntry`]. Through a [`DirEntry`]
|
||||
/// information like the entry's path and possibly other metadata can be
|
||||
/// learned.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This [`Stream`] will return an [`Err`] if there's some sort of intermittent
|
||||
/// IO error during iteration.
|
||||
///
|
||||
/// [`read_dir`]: fn.read_dir.html
|
||||
/// [`DirEntry`]: struct.DirEntry.html
|
||||
/// [`Stream`]: ../futures/stream/trait.Stream.html
|
||||
/// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err
|
||||
#[derive(Debug)]
|
||||
pub struct ReadDir(StdReadDir);
|
||||
|
||||
impl Stream for ReadDir {
|
||||
type Item = DirEntry;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
::blocking_io(|| {
|
||||
match self.0.next() {
|
||||
Some(Err(err)) => Err(err),
|
||||
Some(Ok(item)) => Ok(Some(DirEntry(item))),
|
||||
None => Ok(None)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Entries returned by the [`ReadDir`] stream.
|
||||
///
|
||||
/// [`ReadDir`]: struct.ReadDir.html
|
||||
///
|
||||
/// This is a specialized version of [`std::fs::DirEntry`][std] for usage from the
|
||||
/// Tokio runtime.
|
||||
///
|
||||
/// An instance of `DirEntry` represents an entry inside of a directory on the
|
||||
/// filesystem. Each entry can be inspected via methods to learn about the full
|
||||
/// path or possibly other metadata through per-platform extension traits.
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html
|
||||
#[derive(Debug)]
|
||||
pub struct DirEntry(StdDirEntry);
|
||||
|
||||
impl DirEntry {
|
||||
/// Destructures the `tokio_fs::DirEntry` into a [`std::fs::DirEntry`][std].
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html
|
||||
pub fn into_std(self) -> StdDirEntry {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns the full path to the file that this entry represents.
|
||||
///
|
||||
/// The full path is created by joining the original path to `read_dir`
|
||||
/// with the filename of this entry.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate tokio_fs;
|
||||
/// use futures::{Future, Stream};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let fut = tokio_fs::read_dir(".").flatten_stream().for_each(|dir| {
|
||||
/// println!("{:?}", dir.path());
|
||||
/// Ok(())
|
||||
/// }).map_err(|err| { eprintln!("Error: {:?}", err); () });
|
||||
/// tokio::run(fut);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This prints output like:
|
||||
///
|
||||
/// ```text
|
||||
/// "./whatever.txt"
|
||||
/// "./foo.html"
|
||||
/// "./hello_world.rs"
|
||||
/// ```
|
||||
///
|
||||
/// The exact text, of course, depends on what files you have in `.`.
|
||||
pub fn path(&self) -> PathBuf {
|
||||
self.0.path()
|
||||
}
|
||||
|
||||
/// Returns the bare file name of this directory entry without any other
|
||||
/// leading path component.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate tokio_fs;
|
||||
/// use futures::{Future, Stream};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let fut = tokio_fs::read_dir(".").flatten_stream().for_each(|dir| {
|
||||
/// // Here, `dir` is a `DirEntry`.
|
||||
/// println!("{:?}", dir.file_name());
|
||||
/// Ok(())
|
||||
/// }).map_err(|err| { eprintln!("Error: {:?}", err); () });
|
||||
/// tokio::run(fut);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn file_name(&self) -> OsString {
|
||||
self.0.file_name()
|
||||
}
|
||||
|
||||
/// Return the metadata for the file that this entry points at.
|
||||
///
|
||||
/// This function will not traverse symlinks if this entry points at a
|
||||
/// symlink.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// On Windows this function is cheap to call (no extra system calls
|
||||
/// needed), but on Unix platforms this function is the equivalent of
|
||||
/// calling `symlink_metadata` on the path.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate tokio_fs;
|
||||
/// use futures::{Future, Stream};
|
||||
/// use futures::future::poll_fn;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let fut = tokio_fs::read_dir(".").flatten_stream().for_each(|dir| {
|
||||
/// // Here, `dir` is a `DirEntry`.
|
||||
/// let path = dir.path();
|
||||
/// poll_fn(move || dir.poll_metadata()).map(move |metadata| {
|
||||
/// println!("{:?}: {:?}", path, metadata.permissions());
|
||||
/// })
|
||||
/// }).map_err(|err| { eprintln!("Error: {:?}", err); () });
|
||||
/// tokio::run(fut);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn poll_metadata(&self) -> Poll<Metadata, io::Error> {
|
||||
::blocking_io(|| self.0.metadata())
|
||||
}
|
||||
|
||||
/// Return the file type for the file that this entry points at.
|
||||
///
|
||||
/// This function will not traverse symlinks if this entry points at a
|
||||
/// symlink.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// On Windows and most Unix platforms this function is free (no extra
|
||||
/// system calls needed), but some Unix platforms may require the equivalent
|
||||
/// call to `symlink_metadata` to learn about the target file type.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate futures;
|
||||
/// # extern crate tokio;
|
||||
/// # extern crate tokio_fs;
|
||||
/// use futures::{Future, Stream};
|
||||
/// use futures::future::poll_fn;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let fut = tokio_fs::read_dir(".").flatten_stream().for_each(|dir| {
|
||||
/// // Here, `dir` is a `DirEntry`.
|
||||
/// let path = dir.path();
|
||||
/// poll_fn(move || dir.poll_file_type()).map(move |file_type| {
|
||||
/// // Now let's show our entry's file type!
|
||||
/// println!("{:?}: {:?}", path, file_type);
|
||||
/// })
|
||||
/// }).map_err(|err| { eprintln!("Error: {:?}", err); () });
|
||||
/// tokio::run(fut);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn poll_file_type(&self) -> Poll<FileType, io::Error> {
|
||||
::blocking_io(|| self.0.file_type())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl DirEntryExt for DirEntry {
|
||||
fn ino(&self) -> u64 {
|
||||
self.0.ino()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Reads a symbolic link, returning the file that the link points to.
|
||||
///
|
||||
/// This is an async version of [`std::fs::read_link`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.read_link.html
|
||||
pub fn read_link<P: AsRef<Path>>(path: P) -> ReadLinkFuture<P> {
|
||||
ReadLinkFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `read_link`.
|
||||
#[derive(Debug)]
|
||||
pub struct ReadLinkFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> ReadLinkFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
fn new(path: P) -> ReadLinkFuture<P> {
|
||||
ReadLinkFuture {
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for ReadLinkFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
type Item = PathBuf;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::read_link(&self.path) )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Removes an existing, empty directory.
|
||||
///
|
||||
/// This is an async version of [`std::fs::remove_dir`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.remove_dir.html
|
||||
pub fn remove_dir<P: AsRef<Path>>(path: P) -> RemoveDirFuture<P> {
|
||||
RemoveDirFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `remove_dir`.
|
||||
#[derive(Debug)]
|
||||
pub struct RemoveDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> RemoveDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
fn new(path: P) -> RemoveDirFuture<P> {
|
||||
RemoveDirFuture {
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for RemoveDirFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::remove_dir(&self.path) )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Removes a file from the filesystem.
|
||||
///
|
||||
/// Note that there is no
|
||||
/// guarantee that the file is immediately deleted (e.g. depending on
|
||||
/// platform, other open file descriptors may prevent immediate removal).
|
||||
///
|
||||
/// This is an async version of [`std::fs::remove_file`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.remove_file.html
|
||||
pub fn remove_file<P: AsRef<Path>>(path: P) -> RemoveFileFuture<P> {
|
||||
RemoveFileFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `remove_file`.
|
||||
#[derive(Debug)]
|
||||
pub struct RemoveFileFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> RemoveFileFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
fn new(path: P) -> RemoveFileFuture<P> {
|
||||
RemoveFileFuture {
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for RemoveFileFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::remove_file(&self.path) )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Rename a file or directory to a new name, replacing the original file if
|
||||
/// `to` already exists.
|
||||
///
|
||||
/// This will not work if the new name is on a different mount point.
|
||||
///
|
||||
/// This is an async version of [`std::fs::rename`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.rename.html
|
||||
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> RenameFuture<P, Q> {
|
||||
RenameFuture::new(from, to)
|
||||
}
|
||||
|
||||
/// Future returned by `rename`.
|
||||
#[derive(Debug)]
|
||||
pub struct RenameFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
from: P,
|
||||
to: Q,
|
||||
}
|
||||
|
||||
impl<P, Q> RenameFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
fn new(from: P, to: Q) -> RenameFuture<P, Q> {
|
||||
RenameFuture {
|
||||
from: from,
|
||||
to: to,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, Q> Future for RenameFuture<P, Q>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>
|
||||
{
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::rename(&self.from, &self.to) )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Changes the permissions found on a file or a directory.
|
||||
///
|
||||
/// This is an async version of [`std::fs::set_permissions`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.set_permissions.html
|
||||
pub fn set_permissions<P: AsRef<Path>>(path: P, perm: fs::Permissions) -> SetPermissionsFuture<P> {
|
||||
SetPermissionsFuture::new(path, perm)
|
||||
}
|
||||
|
||||
/// Future returned by `set_permissions`.
|
||||
#[derive(Debug)]
|
||||
pub struct SetPermissionsFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
path: P,
|
||||
perm: fs::Permissions,
|
||||
}
|
||||
|
||||
impl<P> SetPermissionsFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
fn new(path: P, perm: fs::Permissions) -> SetPermissionsFuture<P> {
|
||||
SetPermissionsFuture {
|
||||
path: path,
|
||||
perm: perm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for SetPermissionsFuture<P>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
::blocking_io(|| fs::set_permissions(&self.path, self.perm.clone()) )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
use super::blocking_io;
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::fs::{self, Metadata};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Queries the file system metadata for a path.
|
||||
///
|
||||
/// This is an async version of [`std::fs::symlink_metadata`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html
|
||||
pub fn symlink_metadata<P>(path: P) -> SymlinkMetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
SymlinkMetadataFuture::new(path)
|
||||
}
|
||||
|
||||
/// Future returned by `symlink_metadata`.
|
||||
#[derive(Debug)]
|
||||
pub struct SymlinkMetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
path: P,
|
||||
}
|
||||
|
||||
impl<P> SymlinkMetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
pub(crate) fn new(path: P) -> Self {
|
||||
Self { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Future for SymlinkMetadataFuture<P>
|
||||
where
|
||||
P: AsRef<Path> + Send + 'static,
|
||||
{
|
||||
type Item = Metadata;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
blocking_io(|| fs::symlink_metadata(&self.path))
|
||||
}
|
||||
}
|
||||
+78
-5
@@ -1,6 +1,6 @@
|
||||
extern crate futures;
|
||||
extern crate rand;
|
||||
extern crate tempdir;
|
||||
extern crate tempfile;
|
||||
extern crate tokio_fs;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_threadpool;
|
||||
@@ -13,16 +13,16 @@ use futures::Future;
|
||||
use futures::future::poll_fn;
|
||||
use futures::sync::oneshot;
|
||||
use rand::{thread_rng, Rng};
|
||||
use tempdir::TempDir;
|
||||
use tempfile::Builder as TmpBuilder;
|
||||
|
||||
use std::fs::File as StdFile;
|
||||
use std::io::Read;
|
||||
use std::io::{Read, SeekFrom};
|
||||
|
||||
#[test]
|
||||
fn read_write() {
|
||||
const NUM_CHARS: usize = 16 * 1_024;
|
||||
|
||||
let dir = TempDir::new("tokio-fs-tests").unwrap();
|
||||
let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
|
||||
let file_path = dir.path().join("read_write.txt");
|
||||
|
||||
let contents: Vec<u8> = thread_rng().gen_ascii_chars()
|
||||
@@ -41,7 +41,9 @@ fn read_write() {
|
||||
let contents = contents.clone();
|
||||
|
||||
File::create(file_path)
|
||||
.and_then(move |file| io::write_all(file, contents))
|
||||
.and_then(|file| file.metadata())
|
||||
.inspect(|&(_, ref metadata)| assert!(metadata.is_file()))
|
||||
.and_then(move |(file, _)| io::write_all(file, contents))
|
||||
.and_then(|(mut file, _)| {
|
||||
poll_fn(move || file.poll_sync_all())
|
||||
})
|
||||
@@ -61,13 +63,84 @@ fn read_write() {
|
||||
|
||||
assert_eq!(dst, contents);
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
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);
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
rx.wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metadata() {
|
||||
let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
|
||||
let file_path = dir.path().join("metadata.txt");
|
||||
|
||||
let pool = Builder::new().pool_size(1).build();
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
pool.spawn({
|
||||
let file_path = file_path.clone();
|
||||
let file_path2 = file_path.clone();
|
||||
let file_path3 = file_path.clone();
|
||||
|
||||
tokio_fs::metadata(file_path)
|
||||
.then(|r| {
|
||||
let _ = r.err().unwrap();
|
||||
Ok(())
|
||||
})
|
||||
.and_then(|_| File::create(file_path2))
|
||||
.and_then(|_| tokio_fs::metadata(file_path3))
|
||||
.then(|r| {
|
||||
assert!(r.unwrap().is_file());
|
||||
tx.send(())
|
||||
})
|
||||
});
|
||||
|
||||
rx.wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek() {
|
||||
let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
|
||||
let file_path = dir.path().join("seek.txt");
|
||||
|
||||
let pool = Builder::new().pool_size(1).build();
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
pool.spawn(
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(file_path)
|
||||
.and_then(|file| io::write_all(file, "Hello, world!"))
|
||||
.and_then(|(file, _)| file.seek(SeekFrom::End(-6)))
|
||||
.and_then(|(file, _)| io::read_exact(file, vec![0; 5]))
|
||||
.and_then(|(file, buf)| {
|
||||
assert_eq!(buf, b"world");
|
||||
file.seek(SeekFrom::Start(0))
|
||||
})
|
||||
.and_then(|(file, _)| io::read_exact(file, vec![0; 5]))
|
||||
.and_then(|(_, buf)| {
|
||||
assert_eq!(buf, b"Hello");
|
||||
Ok(())
|
||||
})
|
||||
.then(|r| {
|
||||
let _ = r.unwrap();
|
||||
tx.send(())
|
||||
}),
|
||||
);
|
||||
|
||||
rx.wait().unwrap();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
# 0.1.8 (August 23, 2018)
|
||||
|
||||
* Documentation improvements
|
||||
|
||||
# 0.1.7 (June 13, 2018)
|
||||
|
||||
* Move `codec::{Encode, Decode, Framed*}` into `tokio-codec` (#353)
|
||||
|
||||
# 0.1.6 (March 09, 2018)
|
||||
|
||||
* Add native endian builder fn to length_delimited (#144)
|
||||
|
||||
+4
-3
@@ -4,13 +4,14 @@ name = "tokio-io"
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.6"
|
||||
version = "0.1.8"
|
||||
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"
|
||||
documentation = "https://docs.rs/tokio-io/0.1.8/tokio_io"
|
||||
description = """
|
||||
Core I/O primitives for asynchronous I/O in Rust.
|
||||
"""
|
||||
|
||||
@@ -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;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user