mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-25 00:00:18 +02:00
Update Tokio to use std::future. (#1120)
A first pass at updating Tokio to use `std::future`. Implementations of `Future` from the futures crate are updated to implement `Future` from std. Implementations of `Stream` are moved to a feature flag. This commits disables a number of crates that have not yet been updated.
This commit is contained in:
+7
-6
@@ -12,7 +12,7 @@ task:
|
||||
setup_script:
|
||||
- pkg install -y curl
|
||||
- curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
- sh rustup.sh -y
|
||||
- sh rustup.sh -y --default-toolchain nightly
|
||||
- . $HOME/.cargo/env
|
||||
- rustup target add i686-unknown-freebsd
|
||||
- |
|
||||
@@ -31,13 +31,14 @@ task:
|
||||
folder: $HOME/.cargo/registry
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all
|
||||
- cargo test --all --lib && cargo test --all --tests
|
||||
- (cd tokio-trace/test-log-support && cargo test)
|
||||
- (cd tokio-trace/test_static_max_level_features && cargo test)
|
||||
- cargo doc --all
|
||||
i686_test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- |
|
||||
cargo test --all --exclude tokio-tls --exclude tokio-macros --target i686-unknown-freebsd
|
||||
# TODO: Re-enable
|
||||
# i686_test_script:
|
||||
# - . $HOME/.cargo/env
|
||||
# - |
|
||||
# cargo test --all --exclude tokio-tls --exclude tokio-macros --target i686-unknown-freebsd
|
||||
before_cache_script:
|
||||
- rm -rf $HOME/.cargo/registry/index
|
||||
|
||||
+11
-11
@@ -2,24 +2,24 @@
|
||||
|
||||
members = [
|
||||
"tokio",
|
||||
"tokio-buf",
|
||||
"tokio-codec",
|
||||
# "tokio-buf",
|
||||
# "tokio-codec",
|
||||
"tokio-current-thread",
|
||||
"tokio-executor",
|
||||
"tokio-fs",
|
||||
# "tokio-fs",
|
||||
"tokio-futures",
|
||||
"tokio-io",
|
||||
"tokio-macros",
|
||||
# "tokio-macros",
|
||||
"tokio-reactor",
|
||||
"tokio-signal",
|
||||
# "tokio-signal",
|
||||
"tokio-sync",
|
||||
"tokio-test",
|
||||
"tokio-threadpool",
|
||||
# "tokio-threadpool",
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
"tokio-tls",
|
||||
"tokio-trace",
|
||||
"tokio-trace/tokio-trace-core",
|
||||
"tokio-udp",
|
||||
"tokio-uds",
|
||||
# "tokio-tls",
|
||||
# "tokio-trace",
|
||||
# "tokio-trace/tokio-trace-core",
|
||||
# "tokio-udp",
|
||||
# "tokio-uds",
|
||||
]
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[build]
|
||||
target-dir = "../target"
|
||||
@@ -1,31 +0,0 @@
|
||||
[package]
|
||||
name = "examples"
|
||||
edition = "2018"
|
||||
version = "0.1.0"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
|
||||
# Break out of the parent workspace
|
||||
[workspace]
|
||||
|
||||
[[bin]]
|
||||
name = "chat"
|
||||
path = "src/chat.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "echo_client"
|
||||
path = "src/echo_client.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "echo_server"
|
||||
path = "src/echo_server.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "hyper"
|
||||
path = "src/hyper.rs"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.2.0", features = ["async-await-preview"], path = "../tokio" }
|
||||
futures = "0.1.23"
|
||||
bytes = "0.4.9"
|
||||
hyper = "0.12.8"
|
||||
@@ -1,5 +0,0 @@
|
||||
# Tokio async/await examples
|
||||
|
||||
These are a separate crate in order to work around some cargo bugs. It also
|
||||
allows `[patch]` to be used in `Cargo.toml` to ensure the correct lib versions
|
||||
are being pulled in.
|
||||
@@ -1,131 +0,0 @@
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use tokio::async_wait;
|
||||
use tokio::codec::{LinesCodec, Decoder};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
|
||||
use futures::sync::mpsc;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Shorthand for the transmit half of the message channel.
|
||||
type Tx = mpsc::UnboundedSender<String>;
|
||||
|
||||
struct Shared {
|
||||
peers: HashMap<SocketAddr, Tx>,
|
||||
}
|
||||
|
||||
impl Shared {
|
||||
/// Create a new, empty, instance of `Shared`.
|
||||
fn new() -> Self {
|
||||
Shared {
|
||||
peers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn process(stream: TcpStream, state: Arc<Mutex<Shared>>) -> io::Result<()> {
|
||||
let addr = stream.peer_addr().unwrap();
|
||||
let mut lines = LinesCodec::new().framed(stream);
|
||||
|
||||
// Extract the peer's name
|
||||
let name = match async_wait!(lines.next()) {
|
||||
Some(name) => name?,
|
||||
None => {
|
||||
// Disconnected early
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
println!("`{}` is joining the chat", name);
|
||||
|
||||
let (tx, mut rx) = mpsc::unbounded();
|
||||
|
||||
// Register the socket
|
||||
state.lock().unwrap()
|
||||
.peers.insert(addr, tx);
|
||||
|
||||
// Split the `lines` handle into send and recv handles. This allows spawning
|
||||
// separate tasks.
|
||||
let (mut lines_tx, mut lines_rx) = lines.split();
|
||||
|
||||
// Spawn a task that receives all lines broadcasted to us from other peers
|
||||
// and writes it to the client.
|
||||
tokio::spawn_async(async move {
|
||||
while let Some(line) = async_wait!(rx.next()) {
|
||||
let line = line.unwrap();
|
||||
async_wait!(lines_tx.send_async(line)).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
// Use the current task to read lines from the socket and broadcast them to
|
||||
// other peers.
|
||||
while let Some(message) = async_wait!(lines_rx.next()) {
|
||||
// TODO: Error handling
|
||||
let message = message.unwrap();
|
||||
|
||||
let mut line = name.clone();
|
||||
line.push_str(": ");
|
||||
line.push_str(&message);
|
||||
line.push_str("\r\n");
|
||||
|
||||
let state = state.lock().unwrap();
|
||||
|
||||
for (peer_addr, tx) in &state.peers {
|
||||
if *peer_addr != addr {
|
||||
// TODO: Error handling
|
||||
tx.unbounded_send(line.clone()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the client from the shared state. Doing so will also result in the
|
||||
// tx task to terminate.
|
||||
state.lock().unwrap()
|
||||
.peers.remove(&addr)
|
||||
.expect("bug");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Create the shared state. This is how all the peers communicate.
|
||||
//
|
||||
// The server task will hold a handle to this. For every new client, the
|
||||
// `state` handle is cloned and passed into the task that processes the
|
||||
// client connection.
|
||||
let state = Arc::new(Mutex::new(Shared::new()));
|
||||
|
||||
let addr = "127.0.0.1:6142".parse().unwrap();
|
||||
|
||||
// Bind a TCP listener to the socket address.
|
||||
//
|
||||
// Note that this is the Tokio TcpListener, which is fully async.
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
|
||||
println!("server running on localhost:6142");
|
||||
|
||||
// Start the Tokio runtime.
|
||||
let mut incoming = listener.incoming();
|
||||
|
||||
while let Some(stream) = async_wait!(incoming.next()) {
|
||||
let stream = match stream {
|
||||
Ok(stream) => stream,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let state = state.clone();
|
||||
|
||||
tokio::spawn_async(async move {
|
||||
if let Err(_) = async_wait!(process(stream, state)) {
|
||||
eprintln!("failed to process connection");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use tokio::async_wait;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
const MESSAGES: &[&str] = &[
|
||||
"hello",
|
||||
"world",
|
||||
"one two three",
|
||||
];
|
||||
|
||||
async fn run_client(addr: &SocketAddr) -> io::Result<()> {
|
||||
let mut stream = async_wait!(TcpStream::connect(addr))?;
|
||||
|
||||
// Buffer to read into
|
||||
let mut buf = [0; 128];
|
||||
|
||||
for msg in MESSAGES {
|
||||
println!(" > write = {:?}", msg);
|
||||
|
||||
// Write the message to the server
|
||||
async_wait!(stream.write_all_async(msg.as_bytes()))?;
|
||||
|
||||
// Read the message back from the server
|
||||
async_wait!(stream.read_exact_async(&mut buf[..msg.len()]))?;
|
||||
|
||||
assert_eq!(&buf[..msg.len()], msg.as_bytes());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
use std::env;
|
||||
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
// Connect to the echo serveer
|
||||
|
||||
match async_wait!(run_client(&addr)) {
|
||||
Ok(_) => println!("done."),
|
||||
Err(e) => eprintln!("echo client failed; error = {:?}", e),
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use tokio::async_wait;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
fn handle(mut stream: TcpStream) {
|
||||
tokio::spawn_async(async move {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
loop {
|
||||
match async_wait!(stream.read_async(&mut buf)).unwrap() {
|
||||
0 => break, // Socket closed
|
||||
n => {
|
||||
// Send the data back
|
||||
async_wait!(stream.write_all_async(&buf[0..n])).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
use std::env;
|
||||
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
// Bind the TCP listener
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
let mut incoming = listener.incoming();
|
||||
|
||||
while let Some(stream) = async_wait!(incoming.next()) {
|
||||
let stream = stream.unwrap();
|
||||
handle(stream);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use tokio::async_wait;
|
||||
use tokio::prelude::*;
|
||||
use hyper::Client;
|
||||
|
||||
use std::time::Duration;
|
||||
use std::str;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let client = Client::new();
|
||||
|
||||
let uri = "http://httpbin.org/ip".parse().unwrap();
|
||||
|
||||
let response = async_wait!({
|
||||
client.get(uri)
|
||||
.timeout(Duration::from_secs(10))
|
||||
}).unwrap();
|
||||
|
||||
println!("Response: {}", response.status());
|
||||
|
||||
let mut body = response.into_body();
|
||||
|
||||
while let Some(chunk) = async_wait!(body.next()) {
|
||||
let chunk = chunk.unwrap();
|
||||
println!("chunk = {}", str::from_utf8(&chunk[..]).unwrap());
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use tokio::async_wait;
|
||||
use tokio::timer::Delay;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[tokio::test]
|
||||
async fn success_no_async() {
|
||||
assert!(true);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic]
|
||||
async fn fail_no_async() {
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn use_timer() {
|
||||
let when = Instant::now() + Duration::from_millis(10);
|
||||
async_wait!(Delay::new(when));
|
||||
}
|
||||
+93
-91
@@ -1,118 +1,120 @@
|
||||
trigger: ["master"]
|
||||
pr: ["master"]
|
||||
trigger: ["master", "std-future"]
|
||||
pr: ["master", "std-future"]
|
||||
|
||||
variables:
|
||||
nightly: nightly-2019-05-09
|
||||
nightly: nightly-2019-06-10
|
||||
|
||||
jobs:
|
||||
# Check formatting
|
||||
- template: ci/azure-rustfmt.yml
|
||||
parameters:
|
||||
name: rustfmt
|
||||
# # Check formatting
|
||||
# - template: ci/azure-rustfmt.yml
|
||||
# parameters:
|
||||
# name: rustfmt
|
||||
|
||||
# Test top level crate
|
||||
- template: ci/azure-test-stable.yml
|
||||
parameters:
|
||||
name: test_tokio
|
||||
displayName: Test tokio
|
||||
cross: true
|
||||
crates:
|
||||
- tokio
|
||||
# - template: ci/azure-test-stable.yml
|
||||
# parameters:
|
||||
# name: test_tokio
|
||||
# displayName: Test tokio
|
||||
# cross: true
|
||||
# crates:
|
||||
# - tokio
|
||||
|
||||
# Test crates that are platform specific
|
||||
- template: ci/azure-test-stable.yml
|
||||
parameters:
|
||||
name: test_sub_cross
|
||||
displayName: Test sub crates -
|
||||
displayName: Test sub crates (cross) -
|
||||
cross: true
|
||||
rust: $(nightly)
|
||||
crates:
|
||||
- tokio-fs
|
||||
# - tokio-fs
|
||||
- tokio-reactor
|
||||
- tokio-signal
|
||||
- tokio-tcp
|
||||
- tokio-tls
|
||||
- tokio-udp
|
||||
- tokio-uds
|
||||
# - tokio-signal
|
||||
# - tokio-tcp
|
||||
# - tokio-tls
|
||||
# - tokio-udp
|
||||
# - tokio-uds
|
||||
|
||||
# Test crates that are NOT platform specific
|
||||
- template: ci/azure-test-stable.yml
|
||||
parameters:
|
||||
name: test_linux
|
||||
displayName: Test sub crates -
|
||||
rust: $(nightly)
|
||||
crates:
|
||||
- tokio-buf
|
||||
- tokio-codec
|
||||
# - tokio-buf
|
||||
# - tokio-codec
|
||||
- tokio-current-thread
|
||||
- tokio-executor
|
||||
- tokio-io
|
||||
- tokio-sync
|
||||
- tokio-threadpool
|
||||
- tokio-timer
|
||||
- tokio-test
|
||||
- tokio-trace
|
||||
- tokio-trace/tokio-trace-core
|
||||
- tokio-trace/test-log-support
|
||||
- tokio-trace/test_static_max_level_features
|
||||
# - tokio-threadpool
|
||||
# - tokio-timer
|
||||
# - tokio-test
|
||||
# - tokio-trace
|
||||
# - tokio-trace/tokio-trace-core
|
||||
# - tokio-trace/test-log-support
|
||||
# - tokio-trace/test_static_max_level_features
|
||||
|
||||
- template: ci/azure-cargo-check.yml
|
||||
parameters:
|
||||
name: features
|
||||
displayName: Check feature permtuations
|
||||
rust: stable
|
||||
crates:
|
||||
tokio:
|
||||
- codec
|
||||
- fs
|
||||
- io
|
||||
- reactor
|
||||
- rt-full
|
||||
- tcp
|
||||
- timer
|
||||
- udp
|
||||
- uds
|
||||
- sync
|
||||
tokio-buf:
|
||||
- util
|
||||
|
||||
# Run async-await tests
|
||||
- template: ci/azure-test-nightly.yml
|
||||
parameters:
|
||||
name: test_nightly
|
||||
displayName: Test Async / Await
|
||||
rust: $(nightly)
|
||||
|
||||
# Try cross compiling
|
||||
- template: ci/azure-cross-compile.yml
|
||||
parameters:
|
||||
name: cross_32bit_linux
|
||||
target: i686-unknown-linux-gnu
|
||||
|
||||
# This represents the minimum Rust version supported by
|
||||
# Tokio. Updating this should be done in a dedicated PR and
|
||||
# cannot be greater than two 0.x releases prior to the
|
||||
# current stable.
|
||||
# - template: ci/azure-cargo-check.yml
|
||||
# parameters:
|
||||
# name: features
|
||||
# displayName: Check feature permtuations
|
||||
# rust: stable
|
||||
# crates:
|
||||
# tokio:
|
||||
# - codec
|
||||
# - fs
|
||||
# - io
|
||||
# - reactor
|
||||
# - rt-full
|
||||
# - tcp
|
||||
# - timer
|
||||
# - udp
|
||||
# - uds
|
||||
# - sync
|
||||
# tokio-buf:
|
||||
# - util
|
||||
#
|
||||
# Tests are not run as tests may require newer versions of
|
||||
# rust.
|
||||
- template: ci/azure-check-minrust.yml
|
||||
parameters:
|
||||
name: minrust
|
||||
rust_version: 1.34.0
|
||||
|
||||
- template: ci/azure-tsan.yml
|
||||
parameters:
|
||||
name: tsan
|
||||
rust: $(nightly)
|
||||
|
||||
- template: ci/azure-deploy-docs.yml
|
||||
parameters:
|
||||
dependsOn:
|
||||
- rustfmt
|
||||
- test_tokio
|
||||
- test_sub_cross
|
||||
- test_linux
|
||||
- features
|
||||
- test_nightly
|
||||
- cross_32bit_linux
|
||||
- minrust
|
||||
- tsan
|
||||
# # Run async-await tests
|
||||
# - template: ci/azure-test-nightly.yml
|
||||
# parameters:
|
||||
# name: test_nightly
|
||||
# displayName: Test Async / Await
|
||||
# rust: $(nightly)
|
||||
#
|
||||
# # Try cross compiling
|
||||
# - template: ci/azure-cross-compile.yml
|
||||
# parameters:
|
||||
# name: cross_32bit_linux
|
||||
# target: i686-unknown-linux-gnu
|
||||
#
|
||||
# # This represents the minimum Rust version supported by
|
||||
# # Tokio. Updating this should be done in a dedicated PR and
|
||||
# # cannot be greater than two 0.x releases prior to the
|
||||
# # current stable.
|
||||
# #
|
||||
# # Tests are not run as tests may require newer versions of
|
||||
# # rust.
|
||||
# - template: ci/azure-check-minrust.yml
|
||||
# parameters:
|
||||
# name: minrust
|
||||
# rust_version: 1.34.0
|
||||
#
|
||||
# - template: ci/azure-tsan.yml
|
||||
# parameters:
|
||||
# name: tsan
|
||||
# rust: $(nightly)
|
||||
#
|
||||
# - template: ci/azure-deploy-docs.yml
|
||||
# parameters:
|
||||
# dependsOn:
|
||||
# - rustfmt
|
||||
# - test_tokio
|
||||
# - test_sub_cross
|
||||
# - test_linux
|
||||
# - features
|
||||
# - test_nightly
|
||||
# - cross_32bit_linux
|
||||
# - minrust
|
||||
# - tsan
|
||||
|
||||
@@ -27,6 +27,9 @@ steps:
|
||||
|
||||
# All platforms.
|
||||
- script: |
|
||||
rustup toolchain install nightly
|
||||
rustup update
|
||||
rustup toolchain list
|
||||
rustc -Vv
|
||||
cargo -V
|
||||
displayName: Query rust and cargo versions
|
||||
|
||||
@@ -10,6 +10,7 @@ jobs:
|
||||
rust_version: stable
|
||||
- script: |
|
||||
rustup component add rustfmt
|
||||
cargo fmt --version
|
||||
displayName: Install rustfmt
|
||||
- script: |
|
||||
cargo fmt --all -- --check
|
||||
|
||||
+13
-12
@@ -17,23 +17,24 @@ jobs:
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: stable
|
||||
# rust_version: stable
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-is-release.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
- script: cargo test
|
||||
env:
|
||||
LOOM_MAX_DURATION: 10
|
||||
CI: 'True'
|
||||
displayName: cargo test -p ${{ crate }}
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
condition: and(succeeded(), ne(variables['isRelease'], 'true'))
|
||||
# - template: azure-is-release.yml
|
||||
#
|
||||
# - ${{ each crate in parameters.crates }}:
|
||||
# - script: cargo test
|
||||
# env:
|
||||
# LOOM_MAX_DURATION: 10
|
||||
# CI: 'True'
|
||||
# displayName: cargo test -p ${{ crate }}
|
||||
# workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
# condition: and(succeeded(), ne(variables['isRelease'], 'true'))
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
- script: cargo test
|
||||
- script: cargo test --lib && cargo test --tests
|
||||
env:
|
||||
LOOM_MAX_DURATION: 10
|
||||
CI: 'True'
|
||||
|
||||
@@ -24,4 +24,6 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
tokio-executor = { version = "0.2.0", path = "../tokio-executor" }
|
||||
futures = "0.1.19"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-sync = { version = "0.2.0", path = "../tokio-sync" }
|
||||
|
||||
@@ -30,13 +30,14 @@
|
||||
mod scheduler;
|
||||
|
||||
use crate::scheduler::Scheduler;
|
||||
use futures::future::{ExecuteError, ExecuteErrorKind, Executor};
|
||||
use futures::{executor, Async, Future};
|
||||
use std::cell::Cell;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{atomic, mpsc, Arc};
|
||||
use std::task::{Context, Poll, Waker};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio_executor::park::{Park, ParkThread, Unpark};
|
||||
@@ -60,7 +61,7 @@ pub struct CurrentThread<P: Park = ParkThread> {
|
||||
spawn_handle: Handle,
|
||||
|
||||
/// Receiver for futures spawned from other threads
|
||||
spawn_receiver: mpsc::Receiver<Box<dyn Future<Item = (), Error = ()> + Send + 'static>>,
|
||||
spawn_receiver: mpsc::Receiver<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
|
||||
|
||||
/// The thread-local ID assigned to this executor.
|
||||
id: u64,
|
||||
@@ -182,11 +183,7 @@ struct Borrow<'a, U> {
|
||||
}
|
||||
|
||||
trait SpawnLocal {
|
||||
fn spawn_local(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()>>,
|
||||
already_counted: bool,
|
||||
);
|
||||
fn spawn_local(&mut self, future: Pin<Box<dyn Future<Output = ()>>>, already_counted: bool);
|
||||
}
|
||||
|
||||
struct CurrentRunner {
|
||||
@@ -225,7 +222,7 @@ thread_local! {
|
||||
///
|
||||
/// [`CurrentThread`]: struct.CurrentThread.html
|
||||
/// [mod]: index.html
|
||||
pub fn block_on_all<F>(future: F) -> Result<F::Item, F::Error>
|
||||
pub fn block_on_all<F>(future: F) -> F::Output
|
||||
where
|
||||
F: Future,
|
||||
{
|
||||
@@ -233,8 +230,7 @@ where
|
||||
|
||||
let ret = current_thread.block_on(future);
|
||||
current_thread.run().unwrap();
|
||||
|
||||
ret.map_err(|e| e.into_inner().expect("unexpected execution error"))
|
||||
ret
|
||||
}
|
||||
|
||||
/// Executes a future on the current thread.
|
||||
@@ -252,10 +248,10 @@ where
|
||||
/// [`tokio::spawn`]: ../fn.spawn.html
|
||||
pub fn spawn<F>(future: F)
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + 'static,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
TaskExecutor::current()
|
||||
.spawn_local(Box::new(future))
|
||||
.spawn_local(Box::pin(future))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -283,7 +279,7 @@ impl<P: Park> CurrentThread<P> {
|
||||
});
|
||||
|
||||
let scheduler = Scheduler::new(unpark);
|
||||
let notify = scheduler.notify();
|
||||
let waker = scheduler.waker();
|
||||
|
||||
let num_futures = Arc::new(atomic::AtomicUsize::new(0));
|
||||
|
||||
@@ -294,10 +290,10 @@ impl<P: Park> CurrentThread<P> {
|
||||
id,
|
||||
spawn_handle: Handle {
|
||||
sender: spawn_sender,
|
||||
num_futures: num_futures,
|
||||
notify: notify,
|
||||
num_futures,
|
||||
waker,
|
||||
shut_down: Cell::new(false),
|
||||
thread: thread,
|
||||
thread,
|
||||
id,
|
||||
},
|
||||
spawn_receiver: spawn_receiver,
|
||||
@@ -319,9 +315,9 @@ impl<P: Park> CurrentThread<P> {
|
||||
/// 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,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
self.borrow().spawn_local(Box::new(future), false);
|
||||
self.borrow().spawn_local(Box::pin(future), false);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -338,7 +334,7 @@ impl<P: Park> CurrentThread<P> {
|
||||
///
|
||||
/// 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>>
|
||||
pub fn block_on<F>(&mut self, future: F) -> F::Output
|
||||
where
|
||||
F: Future,
|
||||
{
|
||||
@@ -424,7 +420,7 @@ impl<P: Park> Drop for CurrentThread<P> {
|
||||
impl tokio_executor::Executor for CurrentThread {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
future: Pin<Box<dyn Future<Output = ()> + Send>>,
|
||||
) -> Result<(), SpawnError> {
|
||||
self.borrow().spawn_local(future, false);
|
||||
Ok(())
|
||||
@@ -433,10 +429,10 @@ impl tokio_executor::Executor for CurrentThread {
|
||||
|
||||
impl<T> tokio_executor::TypedExecutor<T> for CurrentThread
|
||||
where
|
||||
T: Future<Item = (), Error = ()> + 'static,
|
||||
T: Future<Output = ()> + 'static,
|
||||
{
|
||||
fn spawn(&mut self, future: T) -> Result<(), SpawnError> {
|
||||
self.borrow().spawn_local(Box::new(future), false);
|
||||
self.borrow().spawn_local(Box::pin(future), false);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -461,9 +457,9 @@ impl<'a, P: Park> Entered<'a, P> {
|
||||
/// 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,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
self.executor.borrow().spawn_local(Box::new(future), false);
|
||||
self.executor.borrow().spawn_local(Box::pin(future), false);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -480,29 +476,35 @@ impl<'a, P: Park> Entered<'a, P> {
|
||||
///
|
||||
/// 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>>
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the `Park` call returns an error.
|
||||
pub fn block_on<F>(&mut self, mut future: F) -> F::Output
|
||||
where
|
||||
F: Future,
|
||||
{
|
||||
let mut future = executor::spawn(future);
|
||||
let notify = self.executor.scheduler.notify();
|
||||
// Safety: we shadow the original `future`, so it will never move
|
||||
// again.
|
||||
let mut future = unsafe { Pin::new_unchecked(&mut future) };
|
||||
let waker = self.executor.scheduler.waker();
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
loop {
|
||||
let res = self
|
||||
.executor
|
||||
.borrow()
|
||||
.enter(self.enter, || future.poll_future_notify(¬ify, 0));
|
||||
.enter(self.enter, || future.as_mut().poll(&mut cx));
|
||||
|
||||
match res {
|
||||
Ok(Async::Ready(e)) => return Ok(e),
|
||||
Err(e) => return Err(BlockError { inner: Some(e) }),
|
||||
Ok(Async::NotReady) => {}
|
||||
Poll::Ready(e) => return e,
|
||||
Poll::Pending => {}
|
||||
}
|
||||
|
||||
self.tick();
|
||||
|
||||
if let Err(_) = self.executor.park.park() {
|
||||
return Err(BlockError { inner: None });
|
||||
panic!("block_on park failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -629,10 +631,11 @@ impl<'a, P: Park> fmt::Debug for Entered<'a, P> {
|
||||
/// Handle to spawn a future on the corresponding `CurrentThread` instance
|
||||
#[derive(Clone)]
|
||||
pub struct Handle {
|
||||
sender: mpsc::Sender<Box<dyn Future<Item = (), Error = ()> + Send + 'static>>,
|
||||
sender: mpsc::Sender<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
|
||||
num_futures: Arc<atomic::AtomicUsize>,
|
||||
shut_down: Cell<bool>,
|
||||
notify: executor::NotifyHandle,
|
||||
/// Waker to the Scheduler
|
||||
waker: Waker,
|
||||
thread: thread::ThreadId,
|
||||
|
||||
/// The thread-local ID assigned to this Handle's executor.
|
||||
@@ -657,12 +660,12 @@ impl Handle {
|
||||
/// instance of the `Handle` does not exist anymore.
|
||||
pub fn spawn<F>(&self, future: F) -> Result<(), SpawnError>
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
F: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
if thread::current().id() == self.thread {
|
||||
let mut e = TaskExecutor::current();
|
||||
if e.id() == Some(self.id) {
|
||||
return e.spawn_local(Box::new(future));
|
||||
return e.spawn_local(Box::pin(future));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,10 +686,9 @@ impl Handle {
|
||||
}
|
||||
|
||||
self.sender
|
||||
.send(Box::new(future))
|
||||
.send(Box::pin(future))
|
||||
.expect("CurrentThread does not exist anymore");
|
||||
// use 0 for the id, CurrentThread does not make use of it
|
||||
self.notify.notify(0);
|
||||
self.waker.wake_by_ref();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -731,7 +733,7 @@ impl TaskExecutor {
|
||||
/// Spawn a future onto the current `CurrentThread` instance.
|
||||
pub fn spawn_local(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()>>,
|
||||
future: Pin<Box<dyn Future<Output = ()>>>,
|
||||
) -> Result<(), SpawnError> {
|
||||
CURRENT.with(|current| match current.spawn.get() {
|
||||
Some(spawn) => {
|
||||
@@ -746,7 +748,7 @@ impl TaskExecutor {
|
||||
impl tokio_executor::Executor for TaskExecutor {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
future: Pin<Box<dyn Future<Output = ()> + Send>>,
|
||||
) -> Result<(), SpawnError> {
|
||||
self.spawn_local(future)
|
||||
}
|
||||
@@ -754,25 +756,10 @@ impl tokio_executor::Executor for TaskExecutor {
|
||||
|
||||
impl<F> tokio_executor::TypedExecutor<F> for TaskExecutor
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + 'static,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
fn spawn(&mut self, future: F) -> Result<(), SpawnError> {
|
||||
self.spawn_local(Box::new(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)),
|
||||
})
|
||||
self.spawn_local(Box::pin(future))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,11 +778,7 @@ impl<'a, U: Unpark> Borrow<'a, U> {
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> SpawnLocal for Borrow<'a, U> {
|
||||
fn spawn_local(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()>>,
|
||||
already_counted: bool,
|
||||
) {
|
||||
fn spawn_local(&mut self, future: Pin<Box<dyn Future<Output = ()>>>, 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
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use crate::Borrow;
|
||||
use futures::executor::{self, NotifyHandle, Spawn, UnsafeNotify};
|
||||
use futures::{Async, Future};
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::marker::PhantomData;
|
||||
use std::future::Future;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst};
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
use std::thread;
|
||||
use std::usize;
|
||||
use tokio_executor::park::Unpark;
|
||||
@@ -22,8 +22,6 @@ pub struct Scheduler<U> {
|
||||
nodes: List<U>,
|
||||
}
|
||||
|
||||
pub struct Notify<'a, U>(&'a Arc<Node<U>>);
|
||||
|
||||
// A linked-list of nodes
|
||||
struct List<U> {
|
||||
len: usize,
|
||||
@@ -78,12 +76,6 @@ struct Inner<U> {
|
||||
unsafe impl<U: Sync + Send> Send for Inner<U> {}
|
||||
unsafe impl<U: Sync + Send> Sync for Inner<U> {}
|
||||
|
||||
impl<U: Unpark> executor::Notify for Inner<U> {
|
||||
fn notify(&self, _: usize) {
|
||||
self.unpark.unpark();
|
||||
}
|
||||
}
|
||||
|
||||
struct Node<U> {
|
||||
// The item
|
||||
item: UnsafeCell<Option<Task>>,
|
||||
@@ -123,12 +115,12 @@ enum Dequeue<U> {
|
||||
}
|
||||
|
||||
/// Wraps a spawned boxed future
|
||||
struct Task(Spawn<Box<dyn Future<Item = (), Error = ()>>>);
|
||||
struct Task(Pin<Box<dyn Future<Output = ()>>>);
|
||||
|
||||
/// A task that is scheduled. `turn` must be called
|
||||
pub struct Scheduled<'a, U> {
|
||||
task: &'a mut Task,
|
||||
notify: &'a Notify<'a, U>,
|
||||
node: &'a Arc<Node<U>>,
|
||||
done: &'a mut bool,
|
||||
}
|
||||
|
||||
@@ -165,11 +157,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify(&self) -> NotifyHandle {
|
||||
self.inner.clone().into()
|
||||
pub fn waker(&self) -> Waker {
|
||||
waker_inner(self.inner.clone())
|
||||
}
|
||||
|
||||
pub fn schedule(&mut self, item: Box<dyn Future<Item = (), Error = ()>>) {
|
||||
pub fn schedule(&mut self, item: Pin<Box<dyn Future<Output = ()>>>) {
|
||||
// Get the current scheduler tick
|
||||
let tick_num = self.inner.tick_num.load(SeqCst);
|
||||
|
||||
@@ -317,11 +309,10 @@ where
|
||||
// deallocating the node if need be.
|
||||
let borrow = &mut *bomb.borrow;
|
||||
let enter = &mut *bomb.enter;
|
||||
let notify = Notify(bomb.node.as_ref().unwrap());
|
||||
|
||||
let mut scheduled = Scheduled {
|
||||
task: item,
|
||||
notify: ¬ify,
|
||||
node: bomb.node.as_ref().unwrap(),
|
||||
done: &mut done,
|
||||
};
|
||||
|
||||
@@ -345,10 +336,15 @@ where
|
||||
impl<'a, U: Unpark> Scheduled<'a, U> {
|
||||
/// Polls the task, returns `true` if the task has completed.
|
||||
pub fn tick(&mut self) -> bool {
|
||||
// Tick the future
|
||||
let ret = match self.task.0.poll_future_notify(self.notify, 0) {
|
||||
Ok(Async::Ready(_)) | Err(_) => true,
|
||||
Ok(Async::NotReady) => false,
|
||||
let waker = unsafe {
|
||||
// Safety: we don't hold this waker ref longer than
|
||||
// this `tick` function
|
||||
waker_ref(self.node)
|
||||
};
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
let ret = match self.task.0.as_mut().poll(&mut cx) {
|
||||
Poll::Ready(()) => true,
|
||||
Poll::Pending => false,
|
||||
};
|
||||
|
||||
*self.done = ret;
|
||||
@@ -357,8 +353,8 @@ impl<'a, U: Unpark> Scheduled<'a, U> {
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn new(future: Box<dyn Future<Item = (), Error = ()> + 'static>) -> Self {
|
||||
Task(executor::spawn(future))
|
||||
pub fn new(future: Pin<Box<dyn Future<Output = ()> + 'static>>) -> Self {
|
||||
Task(future)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,63 +626,101 @@ impl<U> List<U> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U> Clone for Notify<'a, U> {
|
||||
fn clone(&self) -> Self {
|
||||
Notify(self.0)
|
||||
}
|
||||
unsafe fn noop(_: *const ()) {}
|
||||
|
||||
// ===== Raw Waker Inner<U> ======
|
||||
|
||||
fn waker_inner<U: Unpark>(inner: Arc<Inner<U>>) -> Waker {
|
||||
let ptr = Arc::into_raw(inner) as *const ();
|
||||
let vtable = &RawWakerVTable::new(
|
||||
clone_inner::<U>,
|
||||
wake_inner::<U>,
|
||||
wake_by_ref_inner::<U>,
|
||||
drop_inner::<U>,
|
||||
);
|
||||
|
||||
unsafe { Waker::from_raw(RawWaker::new(ptr, vtable)) }
|
||||
}
|
||||
|
||||
impl<'a, U> fmt::Debug for Notify<'a, U> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("Notify").finish()
|
||||
}
|
||||
unsafe fn clone_inner<U: Unpark>(data: *const ()) -> RawWaker {
|
||||
let arc: Arc<Inner<U>> = Arc::from_raw(data as *const Inner<U>);
|
||||
let clone = arc.clone();
|
||||
// forget both Arcs so the refcounts don't get decremented
|
||||
mem::forget(arc);
|
||||
mem::forget(clone);
|
||||
|
||||
let vtable = &RawWakerVTable::new(
|
||||
clone_inner::<U>,
|
||||
wake_inner::<U>,
|
||||
wake_by_ref_inner::<U>,
|
||||
drop_inner::<U>,
|
||||
);
|
||||
RawWaker::new(data, vtable)
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> From<Notify<'a, U>> for NotifyHandle {
|
||||
fn from(handle: Notify<'a, U>) -> NotifyHandle {
|
||||
unsafe {
|
||||
let ptr = handle.0.clone();
|
||||
let ptr = mem::transmute::<Arc<Node<U>>, *mut ArcNode<U>>(ptr);
|
||||
NotifyHandle::new(hide_lt(ptr))
|
||||
}
|
||||
}
|
||||
unsafe fn wake_inner<U: Unpark>(data: *const ()) {
|
||||
let arc: Arc<Inner<U>> = Arc::from_raw(data as *const Inner<U>);
|
||||
arc.unpark.unpark();
|
||||
}
|
||||
|
||||
struct ArcNode<U>(PhantomData<U>);
|
||||
|
||||
// We should never touch `Task` on any thread other than the one owning
|
||||
// `Scheduler`, so this should be a safe operation.
|
||||
unsafe impl<U: Sync + Send> Send for ArcNode<U> {}
|
||||
unsafe impl<U: Sync + Send> Sync for ArcNode<U> {}
|
||||
|
||||
impl<U: Unpark> executor::Notify for ArcNode<U> {
|
||||
fn notify(&self, _id: usize) {
|
||||
unsafe {
|
||||
let me: *const ArcNode<U> = self;
|
||||
let me: *const *const ArcNode<U> = &me;
|
||||
let me = me as *const Arc<Node<U>>;
|
||||
Node::notify(&*me)
|
||||
}
|
||||
}
|
||||
unsafe fn wake_by_ref_inner<U: Unpark>(data: *const ()) {
|
||||
let arc: Arc<Inner<U>> = Arc::from_raw(data as *const Inner<U>);
|
||||
arc.unpark.unpark();
|
||||
// by_ref means we don't own the Node, so forget the Arc
|
||||
mem::forget(arc);
|
||||
}
|
||||
|
||||
unsafe impl<U: Unpark> UnsafeNotify for ArcNode<U> {
|
||||
unsafe fn clone_raw(&self) -> NotifyHandle {
|
||||
let me: *const ArcNode<U> = self;
|
||||
let me: *const *const ArcNode<U> = &me;
|
||||
let me = &*(me as *const Arc<Node<U>>);
|
||||
Notify(me).into()
|
||||
}
|
||||
unsafe fn drop_inner<U>(data: *const ()) {
|
||||
drop(Arc::<Inner<U>>::from_raw(data as *const Inner<U>));
|
||||
}
|
||||
// ===== Raw Waker Node<U> ======
|
||||
|
||||
unsafe fn drop_raw(&self) {
|
||||
let mut me: *const ArcNode<U> = self;
|
||||
let me = &mut me as *mut *const ArcNode<U> as *mut Arc<Node<U>>;
|
||||
ptr::drop_in_place(me);
|
||||
}
|
||||
unsafe fn waker_ref<U: Unpark>(node: &Arc<Node<U>>) -> Waker {
|
||||
let ptr = &*node as &Node<U> as *const Node<U> as *const ();
|
||||
let vtable = &RawWakerVTable::new(
|
||||
clone_node::<U>,
|
||||
wake_unreachable,
|
||||
wake_by_ref_node::<U>,
|
||||
noop,
|
||||
);
|
||||
|
||||
Waker::from_raw(RawWaker::new(ptr, vtable))
|
||||
}
|
||||
|
||||
unsafe fn hide_lt<U: Unpark>(p: *mut ArcNode<U>) -> *mut dyn UnsafeNotify {
|
||||
mem::transmute(p as *mut dyn UnsafeNotify)
|
||||
unsafe fn wake_unreachable(_data: *const ()) {
|
||||
unreachable!("waker_ref::wake()");
|
||||
}
|
||||
|
||||
unsafe fn clone_node<U: Unpark>(data: *const ()) -> RawWaker {
|
||||
let arc: Arc<Node<U>> = Arc::from_raw(data as *const Node<U>);
|
||||
let clone = arc.clone();
|
||||
// forget both Arcs so the refcounts don't get decremented
|
||||
mem::forget(arc);
|
||||
mem::forget(clone);
|
||||
|
||||
let vtable = &RawWakerVTable::new(
|
||||
clone_node::<U>,
|
||||
wake_node::<U>,
|
||||
wake_by_ref_node::<U>,
|
||||
drop_node::<U>,
|
||||
);
|
||||
RawWaker::new(data, vtable)
|
||||
}
|
||||
|
||||
unsafe fn wake_node<U: Unpark>(data: *const ()) {
|
||||
let arc: Arc<Node<U>> = Arc::from_raw(data as *const Node<U>);
|
||||
Node::<U>::notify(&arc);
|
||||
}
|
||||
|
||||
unsafe fn wake_by_ref_node<U: Unpark>(data: *const ()) {
|
||||
let arc: Arc<Node<U>> = Arc::from_raw(data as *const Node<U>);
|
||||
Node::<U>::notify(&arc);
|
||||
// by_ref means we don't own the Node, so forget the Arc
|
||||
mem::forget(arc);
|
||||
}
|
||||
|
||||
unsafe fn drop_node<U>(data: *const ()) {
|
||||
drop(Arc::<Node<U>>::from_raw(data as *const Node<U>));
|
||||
}
|
||||
|
||||
impl<U: Unpark> Node<U> {
|
||||
|
||||
@@ -1,39 +1,34 @@
|
||||
#![deny(warnings, rust_2018_idioms)]
|
||||
#![feature(async_await)]
|
||||
|
||||
use futures::future::{self, lazy};
|
||||
// This is not actually unused --- we need this trait to be in scope for
|
||||
// the tests that sue TaskExecutor::current().execute(). The compiler
|
||||
// doesn't realise that.
|
||||
#[allow(unused_imports)]
|
||||
use futures::future::Executor;
|
||||
use futures::prelude::*;
|
||||
use futures::sync::oneshot;
|
||||
use futures::task;
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use tokio_current_thread::{block_on_all, CurrentThread};
|
||||
use tokio_executor::TypedExecutor;
|
||||
use tokio_sync::oneshot;
|
||||
|
||||
mod from_block_on_all {
|
||||
use super::*;
|
||||
fn test<F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static>(spawn: F) {
|
||||
fn test<F: Fn(Pin<Box<dyn Future<Output = ()>>>) + 'static>(spawn: F) {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let msg = tokio_current_thread::block_on_all(lazy(move || {
|
||||
let msg = tokio_current_thread::block_on_all(async move {
|
||||
c.set(1 + c.get());
|
||||
|
||||
// Spawn!
|
||||
spawn(Box::new(lazy(move || {
|
||||
spawn(Box::pin(async move {
|
||||
c.set(1 + c.get());
|
||||
Ok::<(), ()>(())
|
||||
})));
|
||||
}));
|
||||
|
||||
Ok::<_, ()>("hello")
|
||||
}))
|
||||
.unwrap();
|
||||
"hello"
|
||||
});
|
||||
|
||||
assert_eq!(2, cnt.get());
|
||||
assert_eq!(msg, "hello");
|
||||
@@ -48,7 +43,7 @@ mod from_block_on_all {
|
||||
fn execute() {
|
||||
test(|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
@@ -66,11 +61,10 @@ fn block_waits() {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let cnt2 = cnt.clone();
|
||||
|
||||
block_on_all(rx.then(move |_| {
|
||||
block_on_all(async move {
|
||||
rx.await.unwrap();
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok::<_, ()>(())
|
||||
}))
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
assert_eq!(1, cnt2.get());
|
||||
}
|
||||
@@ -84,10 +78,9 @@ fn spawn_many() {
|
||||
|
||||
for _ in 0..ITER {
|
||||
let cnt = cnt.clone();
|
||||
tokio_current_thread.spawn(lazy(move || {
|
||||
tokio_current_thread.spawn(async move {
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
tokio_current_thread.run().unwrap();
|
||||
@@ -98,48 +91,36 @@ fn spawn_many() {
|
||||
mod does_not_set_global_executor_by_default {
|
||||
use super::*;
|
||||
|
||||
fn test<F: Fn(Box<dyn Future<Item = (), Error = ()> + Send>) -> Result<(), E> + 'static, E>(
|
||||
fn test<F: Fn(Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<(), E> + 'static, E>(
|
||||
spawn: F,
|
||||
) {
|
||||
block_on_all(lazy(|| {
|
||||
spawn(Box::new(lazy(|| ok()))).unwrap_err();
|
||||
ok()
|
||||
}))
|
||||
.unwrap()
|
||||
block_on_all(async {
|
||||
spawn(Box::pin(async {})).unwrap_err();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
use tokio_executor::Executor;
|
||||
test(|f| tokio_executor::DefaultExecutor::current().spawn(f))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|f| tokio_executor::DefaultExecutor::current().execute(f))
|
||||
}
|
||||
}
|
||||
|
||||
mod from_block_on_future {
|
||||
use super::*;
|
||||
|
||||
fn test<F: Fn(Box<dyn Future<Item = (), Error = ()>>)>(spawn: F) {
|
||||
fn test<F: Fn(Pin<Box<dyn Future<Output = ()>>>)>(spawn: F) {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let cnt2 = cnt.clone();
|
||||
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
tokio_current_thread
|
||||
.block_on(lazy(|| {
|
||||
let cnt = cnt.clone();
|
||||
tokio_current_thread.block_on(async move {
|
||||
let cnt3 = cnt2.clone();
|
||||
|
||||
spawn(Box::new(lazy(move || {
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok(())
|
||||
})));
|
||||
|
||||
Ok::<_, ()>(())
|
||||
}))
|
||||
.unwrap();
|
||||
spawn(Box::pin(async move {
|
||||
cnt3.set(1 + cnt3.get());
|
||||
}));
|
||||
});
|
||||
|
||||
tokio_current_thread.run().unwrap();
|
||||
|
||||
@@ -155,35 +136,30 @@ mod from_block_on_future {
|
||||
fn execute() {
|
||||
test(|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Never(Rc<()>);
|
||||
|
||||
impl Future for Never {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
mod outstanding_tasks_are_dropped_when_executor_is_dropped {
|
||||
use super::*;
|
||||
|
||||
async fn never(_rc: Rc<()>) {
|
||||
loop {
|
||||
yield_once().await;
|
||||
}
|
||||
}
|
||||
|
||||
fn test<F, G>(spawn: F, dotspawn: G)
|
||||
where
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<dyn Future<Item = (), Error = ()>>),
|
||||
F: Fn(Pin<Box<dyn Future<Output = ()>>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Pin<Box<dyn Future<Output = ()>>>),
|
||||
{
|
||||
let mut rc = Rc::new(());
|
||||
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
dotspawn(&mut tokio_current_thread, Box::new(Never(rc.clone())));
|
||||
dotspawn(&mut tokio_current_thread, Box::pin(never(rc.clone())));
|
||||
|
||||
drop(tokio_current_thread);
|
||||
|
||||
@@ -193,15 +169,13 @@ mod outstanding_tasks_are_dropped_when_executor_is_dropped {
|
||||
// Using the global spawn fn
|
||||
|
||||
let mut rc = Rc::new(());
|
||||
let rc2 = rc.clone();
|
||||
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
tokio_current_thread
|
||||
.block_on(lazy(|| {
|
||||
spawn(Box::new(Never(rc.clone())));
|
||||
Ok::<_, ()>(())
|
||||
}))
|
||||
.unwrap();
|
||||
tokio_current_thread.block_on(async move {
|
||||
spawn(Box::pin(never(rc2)));
|
||||
});
|
||||
|
||||
drop(tokio_current_thread);
|
||||
|
||||
@@ -221,7 +195,7 @@ mod outstanding_tasks_are_dropped_when_executor_is_dropped {
|
||||
test(
|
||||
|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
},
|
||||
// Note: `CurrentThread` doesn't currently implement
|
||||
@@ -238,12 +212,9 @@ mod outstanding_tasks_are_dropped_when_executor_is_dropped {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn nesting_run() {
|
||||
block_on_all(lazy(|| {
|
||||
block_on_all(lazy(|| ok())).unwrap();
|
||||
|
||||
ok()
|
||||
}))
|
||||
.unwrap();
|
||||
block_on_all(async {
|
||||
block_on_all(async {});
|
||||
});
|
||||
}
|
||||
|
||||
mod run_in_future {
|
||||
@@ -252,29 +223,23 @@ mod run_in_future {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn spawn() {
|
||||
block_on_all(lazy(|| {
|
||||
tokio_current_thread::spawn(lazy(|| {
|
||||
block_on_all(lazy(|| ok())).unwrap();
|
||||
ok()
|
||||
}));
|
||||
ok()
|
||||
}))
|
||||
.unwrap();
|
||||
block_on_all(async {
|
||||
tokio_current_thread::spawn(async {
|
||||
block_on_all(async {});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn execute() {
|
||||
block_on_all(lazy(|| {
|
||||
block_on_all(async {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(lazy(|| {
|
||||
block_on_all(lazy(|| ok())).unwrap();
|
||||
ok()
|
||||
}))
|
||||
.spawn(async {
|
||||
block_on_all(async {});
|
||||
})
|
||||
.unwrap();
|
||||
ok()
|
||||
}))
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,23 +247,15 @@ mod run_in_future {
|
||||
fn tick_on_infini_future() {
|
||||
let num = Rc::new(Cell::new(0));
|
||||
|
||||
struct Infini {
|
||||
num: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl Future for Infini {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
self.num.set(1 + self.num.get());
|
||||
task::current().notify();
|
||||
Ok(Async::NotReady)
|
||||
async fn infini(num: Rc<Cell<usize>>) {
|
||||
loop {
|
||||
num.set(1 + num.get());
|
||||
yield_once().await
|
||||
}
|
||||
}
|
||||
|
||||
CurrentThread::new()
|
||||
.spawn(Infini { num: num.clone() })
|
||||
.spawn(infini(num.clone()))
|
||||
.turn(None)
|
||||
.unwrap();
|
||||
|
||||
@@ -307,56 +264,41 @@ fn tick_on_infini_future() {
|
||||
|
||||
mod tasks_are_scheduled_fairly {
|
||||
use super::*;
|
||||
struct Spin {
|
||||
state: Rc<RefCell<[i32; 2]>>,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl Future for Spin {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
async fn spin(state: Rc<RefCell<[i32; 2]>>, idx: usize) {
|
||||
loop {
|
||||
// borrow_mut scope
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
let mut state = self.state.borrow_mut();
|
||||
if idx == 0 {
|
||||
let diff = state[0] - state[1];
|
||||
|
||||
if self.idx == 0 {
|
||||
let diff = state[0] - state[1];
|
||||
assert!(diff.abs() <= 1);
|
||||
|
||||
assert!(diff.abs() <= 1);
|
||||
if state[0] >= 50 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if state[0] >= 50 {
|
||||
return Ok(().into());
|
||||
state[idx] += 1;
|
||||
|
||||
if state[idx] >= 100 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state[self.idx] += 1;
|
||||
|
||||
if state[self.idx] >= 100 {
|
||||
return Ok(().into());
|
||||
}
|
||||
|
||||
task::current().notify();
|
||||
Ok(Async::NotReady)
|
||||
yield_once().await;
|
||||
}
|
||||
}
|
||||
|
||||
fn test<F: Fn(Spin)>(spawn: F) {
|
||||
fn test<F: Fn(Pin<Box<dyn Future<Output = ()>>>)>(spawn: F) {
|
||||
let state = Rc::new(RefCell::new([0, 0]));
|
||||
|
||||
block_on_all(lazy(|| {
|
||||
spawn(Spin {
|
||||
state: state.clone(),
|
||||
idx: 0,
|
||||
});
|
||||
|
||||
spawn(Spin {
|
||||
state: state,
|
||||
idx: 1,
|
||||
});
|
||||
|
||||
ok()
|
||||
}))
|
||||
.unwrap();
|
||||
block_on_all(async move {
|
||||
spawn(Box::pin(spin(state.clone(), 0)));
|
||||
spawn(Box::pin(spin(state, 1)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -368,7 +310,7 @@ mod tasks_are_scheduled_fairly {
|
||||
fn execute() {
|
||||
test(|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
@@ -379,8 +321,8 @@ mod and_turn {
|
||||
|
||||
fn test<F, G>(spawn: F, dotspawn: G)
|
||||
where
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<dyn Future<Item = (), Error = ()>>),
|
||||
F: Fn(Pin<Box<dyn Future<Output = ()>>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Pin<Box<dyn Future<Output = ()>>>),
|
||||
{
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
@@ -388,24 +330,21 @@ mod and_turn {
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
// Spawn a basic task to get the executor to turn
|
||||
dotspawn(&mut tokio_current_thread, Box::new(lazy(move || Ok(()))));
|
||||
dotspawn(&mut tokio_current_thread, Box::pin(async {}));
|
||||
|
||||
// Turn once...
|
||||
tokio_current_thread.turn(None).unwrap();
|
||||
|
||||
dotspawn(
|
||||
&mut tokio_current_thread,
|
||||
Box::new(lazy(move || {
|
||||
Box::pin(async move {
|
||||
c.set(1 + c.get());
|
||||
|
||||
// Spawn!
|
||||
spawn(Box::new(lazy(move || {
|
||||
spawn(Box::pin(async move {
|
||||
c.set(1 + c.get());
|
||||
Ok::<(), ()>(())
|
||||
})));
|
||||
|
||||
Ok(())
|
||||
})),
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
// This does not run the newly spawned thread
|
||||
@@ -429,7 +368,7 @@ mod and_turn {
|
||||
test(
|
||||
|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
},
|
||||
// Note: `CurrentThread` doesn't currently implement
|
||||
@@ -454,23 +393,12 @@ mod in_drop {
|
||||
}
|
||||
}
|
||||
|
||||
struct MyFuture {
|
||||
_data: Box<dyn Any>,
|
||||
}
|
||||
|
||||
impl Future for MyFuture {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
async fn noop(_data: Box<dyn Any>) {}
|
||||
|
||||
fn test<F, G>(spawn: F, dotspawn: G)
|
||||
where
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<dyn Future<Item = (), Error = ()>>),
|
||||
F: Fn(Pin<Box<dyn Future<Output = ()>>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Pin<Box<dyn Future<Output = ()>>>),
|
||||
{
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
@@ -478,14 +406,11 @@ mod in_drop {
|
||||
|
||||
dotspawn(
|
||||
&mut tokio_current_thread,
|
||||
Box::new(MyFuture {
|
||||
_data: Box::new(OnDrop(Some(move || {
|
||||
spawn(Box::new(lazy(move || {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})));
|
||||
}))),
|
||||
}),
|
||||
Box::pin(noop(Box::new(OnDrop(Some(move || {
|
||||
spawn(Box::pin(async move {
|
||||
tx.send(()).unwrap();
|
||||
}));
|
||||
}))))),
|
||||
);
|
||||
|
||||
tokio_current_thread.block_on(rx).unwrap();
|
||||
@@ -504,7 +429,7 @@ mod in_drop {
|
||||
test(
|
||||
|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
},
|
||||
// Note: `CurrentThread` doesn't currently implement
|
||||
@@ -519,6 +444,7 @@ mod in_drop {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn hammer_turn() {
|
||||
use futures::sync::mpsc;
|
||||
@@ -572,6 +498,7 @@ fn hammer_turn() {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn turn_has_polled() {
|
||||
@@ -579,7 +506,9 @@ fn turn_has_polled() {
|
||||
|
||||
// Spawn oneshot receiver
|
||||
let (sender, receiver) = oneshot::channel::<()>();
|
||||
tokio_current_thread.spawn(receiver.then(|_| Ok(())));
|
||||
tokio_current_thread.spawn(async move {
|
||||
let _ = receiver.await;
|
||||
});
|
||||
|
||||
// Turn once...
|
||||
let res = tokio_current_thread
|
||||
@@ -674,30 +603,30 @@ fn turn_fair() {
|
||||
|
||||
// Once an item is received on the oneshot channel, it will immediately
|
||||
// immediately make the second oneshot channel ready
|
||||
tokio_current_thread.spawn(receiver.map_err(|_| unreachable!()).and_then(move |_| {
|
||||
|
||||
tokio_current_thread.spawn(async move {
|
||||
receiver.await.unwrap();
|
||||
sender_2.send(()).unwrap();
|
||||
receiver_1_done_clone.set(true);
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
});
|
||||
|
||||
let receiver_2_done = Rc::new(Cell::new(false));
|
||||
let receiver_2_done_clone = receiver_2_done.clone();
|
||||
|
||||
tokio_current_thread.spawn(receiver_2.map_err(|_| unreachable!()).and_then(move |_| {
|
||||
tokio_current_thread.spawn(async move {
|
||||
receiver_2.await.unwrap();
|
||||
receiver_2_done_clone.set(true);
|
||||
Ok(())
|
||||
}));
|
||||
});
|
||||
|
||||
// The third receiver is only woken up from our Park implementation, it simulates
|
||||
// e.g. a socket that first has to be polled to know if it is ready now
|
||||
let receiver_3_done = Rc::new(Cell::new(false));
|
||||
let receiver_3_done_clone = receiver_3_done.clone();
|
||||
|
||||
tokio_current_thread.spawn(receiver_3.map_err(|_| unreachable!()).and_then(move |_| {
|
||||
tokio_current_thread.spawn(async move {
|
||||
receiver_3.await.unwrap();
|
||||
receiver_3_done_clone.set(true);
|
||||
Ok(())
|
||||
}));
|
||||
});
|
||||
|
||||
// First turn should've polled both and considered them not ready
|
||||
let res = tokio_current_thread
|
||||
@@ -760,10 +689,9 @@ fn spawn_from_other_thread() {
|
||||
|
||||
thread::spawn(move || {
|
||||
handle
|
||||
.spawn(lazy(move || {
|
||||
.spawn(async move {
|
||||
sender.send(()).unwrap();
|
||||
Ok(())
|
||||
}))
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
@@ -784,10 +712,9 @@ fn spawn_from_other_thread_unpark() {
|
||||
let _ = receiver_2.recv().unwrap();
|
||||
|
||||
handle
|
||||
.spawn(lazy(move || {
|
||||
.spawn(async move {
|
||||
sender_1.send(()).unwrap();
|
||||
Ok(())
|
||||
}))
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
@@ -796,15 +723,14 @@ fn spawn_from_other_thread_unpark() {
|
||||
// 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();
|
||||
let _ = current_thread.block_on(async move {
|
||||
// inlined 'lazy'
|
||||
async move {
|
||||
sender_2.send(()).unwrap();
|
||||
}
|
||||
.await;
|
||||
receiver_1.await.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -813,21 +739,34 @@ fn spawn_from_executor_with_handle() {
|
||||
let handle = current_thread.handle();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
current_thread.spawn(lazy(move || {
|
||||
current_thread.spawn(async move {
|
||||
handle
|
||||
.spawn(lazy(move || {
|
||||
.spawn(async move {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
}))
|
||||
})
|
||||
.unwrap();
|
||||
Ok::<_, ()>(())
|
||||
}));
|
||||
});
|
||||
|
||||
current_thread.run().unwrap();
|
||||
|
||||
rx.wait().unwrap();
|
||||
current_thread.block_on(rx).unwrap();
|
||||
}
|
||||
|
||||
fn ok() -> future::FutureResult<(), ()> {
|
||||
future::ok(())
|
||||
async fn yield_once() {
|
||||
YieldOnce(false).await
|
||||
}
|
||||
|
||||
struct YieldOnce(bool);
|
||||
|
||||
impl Future for YieldOnce {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||
if self.0 {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
self.0 = true;
|
||||
// Push to the back of the executor's queue
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ categories = ["concurrency", "asynchronous"]
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
crossbeam-utils = "0.6.2"
|
||||
futures = "0.1.19"
|
||||
# crossbeam-utils = "0.6.2"
|
||||
crossbeam-utils = { git = "https://github.com/stjepang/crossbeam", branch = "raw-parker" }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "0.2.0", path = "../tokio" }
|
||||
# tokio = { version = "0.2.0", path = "../tokio" }
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use futures::{self, Future};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::prelude::v1::*;
|
||||
|
||||
thread_local!(static ENTERED: Cell<bool> = Cell::new(false));
|
||||
|
||||
@@ -65,8 +64,25 @@ pub fn enter() -> Result<Enter, EnterError> {
|
||||
impl Enter {
|
||||
/// Blocks the thread on the specified future, returning the value with
|
||||
/// which that future completes.
|
||||
pub fn block_on<F: Future>(&mut self, f: F) -> Result<F::Item, F::Error> {
|
||||
futures::executor::spawn(f).wait_future()
|
||||
pub fn block_on<F: Future>(&mut self, mut f: F) -> F::Output {
|
||||
use crate::park::{Park, ParkThread};
|
||||
use std::pin::Pin;
|
||||
use std::task::Context;
|
||||
use std::task::Poll::Ready;
|
||||
|
||||
let park = ParkThread::new();
|
||||
let waker = park.unpark().into_waker();
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
// `block_on` takes ownership of `f`. Once it is pinned here, the original `f` binding can
|
||||
// no longer be accessed, making the pinning safe.
|
||||
let mut f = unsafe { Pin::new_unchecked(&mut f) };
|
||||
|
||||
loop {
|
||||
if let Ready(v) = f.as_mut().poll(&mut cx) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::SpawnError;
|
||||
use futures::Future;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
/// A value that executes futures.
|
||||
///
|
||||
@@ -82,16 +83,14 @@ pub trait Executor {
|
||||
/// use futures::future::lazy;
|
||||
///
|
||||
/// # fn docs(my_executor: &mut dyn Executor) {
|
||||
/// my_executor.spawn(Box::new(lazy(|| {
|
||||
/// my_executor.spawn(Box::pin(lazy(|| {
|
||||
/// println!("running on the executor");
|
||||
/// Ok(())
|
||||
/// }))).unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
) -> Result<(), SpawnError>;
|
||||
fn spawn(&mut self, future: Pin<Box<dyn Future<Output = ()> + Send>>)
|
||||
-> Result<(), SpawnError>;
|
||||
|
||||
/// Provides a best effort **hint** to whether or not `spawn` will succeed.
|
||||
///
|
||||
@@ -116,7 +115,7 @@ pub trait Executor {
|
||||
///
|
||||
/// # fn docs(my_executor: &mut dyn Executor) {
|
||||
/// if my_executor.status().is_ok() {
|
||||
/// my_executor.spawn(Box::new(lazy(|| {
|
||||
/// my_executor.spawn(Box::pin(lazy(|| {
|
||||
/// println!("running on the executor");
|
||||
/// Ok(())
|
||||
/// }))).unwrap();
|
||||
@@ -133,7 +132,7 @@ pub trait Executor {
|
||||
impl<E: Executor + ?Sized> Executor for Box<E> {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
future: Pin<Box<dyn Future<Output = ()> + Send>>,
|
||||
) -> Result<(), SpawnError> {
|
||||
(**self).spawn(future)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::{Enter, Executor, SpawnError};
|
||||
use futures::{future, Future};
|
||||
use std::cell::Cell;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
/// Executes futures on the default executor for the current execution context.
|
||||
///
|
||||
@@ -70,7 +71,7 @@ thread_local! {
|
||||
impl super::Executor for DefaultExecutor {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
future: Pin<Box<dyn Future<Output = ()> + Send>>,
|
||||
) -> Result<(), SpawnError> {
|
||||
DefaultExecutor::with_current(|executor| executor.spawn(future))
|
||||
.unwrap_or_else(|| Err(SpawnError::shutdown()))
|
||||
@@ -84,10 +85,10 @@ impl super::Executor for DefaultExecutor {
|
||||
|
||||
impl<T> super::TypedExecutor<T> for DefaultExecutor
|
||||
where
|
||||
T: Future<Item = (), Error = ()> + Send + 'static,
|
||||
T: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
fn spawn(&mut self, future: T) -> Result<(), SpawnError> {
|
||||
super::Executor::spawn(self, Box::new(future))
|
||||
super::Executor::spawn(self, Box::pin(future))
|
||||
}
|
||||
|
||||
fn status(&self) -> Result<(), SpawnError> {
|
||||
@@ -95,26 +96,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> future::Executor<T> for DefaultExecutor
|
||||
where
|
||||
T: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
fn execute(&self, future: T) -> Result<(), future::ExecuteError<T>> {
|
||||
if let Err(e) = super::Executor::status(self) {
|
||||
let kind = if e.is_at_capacity() {
|
||||
future::ExecuteErrorKind::NoCapacity
|
||||
} else {
|
||||
future::ExecuteErrorKind::Shutdown
|
||||
};
|
||||
|
||||
return Err(future::ExecuteError::new(kind, future));
|
||||
}
|
||||
|
||||
let _ = DefaultExecutor::with_current(|executor| executor.spawn(Box::new(future)));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ===== global spawn fns =====
|
||||
|
||||
/// Submits a future for execution on the default executor -- usually a
|
||||
@@ -153,9 +134,9 @@ where
|
||||
/// ```
|
||||
pub fn spawn<T>(future: T)
|
||||
where
|
||||
T: Future<Item = (), Error = ()> + Send + 'static,
|
||||
T: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
DefaultExecutor::current().spawn(Box::new(future)).unwrap()
|
||||
DefaultExecutor::current().spawn(Box::pin(future)).unwrap()
|
||||
}
|
||||
|
||||
/// Set the default executor for the duration of the closure
|
||||
|
||||
@@ -46,8 +46,10 @@
|
||||
|
||||
use crossbeam_utils::sync::{Parker, Unparker};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::task::{RawWaker, RawWakerVTable, Waker};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Block the current thread.
|
||||
@@ -223,3 +225,44 @@ impl Unpark for UnparkThread {
|
||||
self.inner.unpark();
|
||||
}
|
||||
}
|
||||
|
||||
static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
|
||||
|
||||
impl UnparkThread {
|
||||
pub(crate) fn into_waker(self) -> Waker {
|
||||
unsafe {
|
||||
let raw = unparker_to_raw_waker(self.inner);
|
||||
Waker::from_raw(raw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn unparker_to_raw_waker(unparker: Unparker) -> RawWaker {
|
||||
RawWaker::new(Unparker::into_raw(unparker), &VTABLE)
|
||||
}
|
||||
|
||||
unsafe fn clone(raw: *const ()) -> RawWaker {
|
||||
let unparker = Unparker::from_raw(raw);
|
||||
|
||||
// Increment the ref count
|
||||
mem::forget(unparker.clone());
|
||||
|
||||
unparker_to_raw_waker(unparker)
|
||||
}
|
||||
|
||||
unsafe fn wake(raw: *const ()) {
|
||||
let unparker = Unparker::from_raw(raw);
|
||||
unparker.unpark();
|
||||
}
|
||||
|
||||
unsafe fn wake_by_ref(raw: *const ()) {
|
||||
let unparker = Unparker::from_raw(raw);
|
||||
unparker.unpark();
|
||||
|
||||
// We don't actually own a reference to the unparker
|
||||
mem::forget(unparker);
|
||||
}
|
||||
|
||||
unsafe fn drop(raw: *const ()) {
|
||||
let _ = Unparker::from_raw(raw);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#![deny(warnings, rust_2018_idioms)]
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
#[test]
|
||||
fn block_on_ready() {
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
let val = enter.block_on(async { 123 });
|
||||
|
||||
assert_eq!(val, 123);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_on_pending() {
|
||||
let mut enter = tokio_executor::enter().unwrap();
|
||||
let val = enter.block_on(async { 123 });
|
||||
|
||||
assert_eq!(val, 123);
|
||||
}
|
||||
@@ -1,17 +1,20 @@
|
||||
#![deny(warnings, rust_2018_idioms)]
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use futures::{self, future::lazy, Future};
|
||||
use tokio_executor::{self, DefaultExecutor};
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
mod out_of_executor_context {
|
||||
use super::*;
|
||||
use tokio_executor::Executor;
|
||||
|
||||
fn test<F, E>(spawn: F)
|
||||
where
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()> + Send>) -> Result<(), E>,
|
||||
F: Fn(Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<(), E>,
|
||||
{
|
||||
let res = spawn(Box::new(lazy(|| Ok(()))));
|
||||
let res = spawn(Box::pin(async {}));
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
@@ -19,10 +22,4 @@ mod out_of_executor_context {
|
||||
fn spawn() {
|
||||
test(|f| DefaultExecutor::current().spawn(f));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
use futures::future::Executor as FuturesExecutor;
|
||||
test(|f| DefaultExecutor::current().execute(f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,21 +11,16 @@ repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-futures/0.1.0"
|
||||
description = """
|
||||
Experimental std::future::Future and async/await support for Tokio
|
||||
Utilities for working with futures, async, and await.
|
||||
"""
|
||||
categories = ["asynchronous"]
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
# This feature comes with no promise of stability. Things will
|
||||
# break with each patch release. Use at your own risk.
|
||||
async-await-preview = ["futures/nightly"]
|
||||
all = []
|
||||
default = [
|
||||
"all",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.23"
|
||||
tokio-io = { version = "0.2.0", path = "../tokio-io" }
|
||||
|
||||
[dev-dependencies]
|
||||
bytes = "0.4.9"
|
||||
hyper = "0.12.8"
|
||||
tokio = { version = "0.2.0", path = "../tokio" }
|
||||
futures-core-preview = "0.3.0-alpha.16"
|
||||
|
||||
+2
-39
@@ -1,43 +1,6 @@
|
||||
# Tokio async/await preview
|
||||
# Tokio Futures
|
||||
|
||||
This crate provides a preview of Tokio with async / await support. It is a shim
|
||||
layer on top of `tokio`.
|
||||
|
||||
**This crate requires Rust nightly and does not provide API stability
|
||||
guarantees. You are living on the edge here.**
|
||||
|
||||
## Usage
|
||||
|
||||
To use this crate, you need to start with a Rust 2018 edition crate, with rustc
|
||||
1.35.0-nightly or later.
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# In the `[packages]` section
|
||||
edition = "2018"
|
||||
|
||||
# In the `[dependencies]` section
|
||||
tokio = {version = "0.2.0", features = ["async-await-preview"]}
|
||||
```
|
||||
|
||||
Then, get started. In your application, add:
|
||||
|
||||
```rust
|
||||
// The nightly features that are commonly needed with async / await
|
||||
#![feature(async_await)]
|
||||
|
||||
fn main() {
|
||||
// And we are async...
|
||||
tokio::run_async(async {
|
||||
println!("Hello");
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Because nightly is required, run the app with `cargo +nightly run`
|
||||
|
||||
Check the [examples](/async-await) directory for more.
|
||||
Asynchronous abstractions for the Tokio stack.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/// Wait for a future to complete.
|
||||
#[macro_export]
|
||||
macro_rules! async_wait {
|
||||
($e:expr) => {{
|
||||
#[allow(unused_imports)]
|
||||
use $crate::compat::backward::IntoAwaitable as IntoAwaitableBackward;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::compat::forward::IntoAwaitable as IntoAwaitableForward;
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut e = $e;
|
||||
let e = e.into_awaitable();
|
||||
e.await
|
||||
}};
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
//! Converts a `std::future::Future` into an 0.1 `Future.
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::future::Future as StdFuture;
|
||||
use std::pin::Pin;
|
||||
use std::ptr;
|
||||
use std::task::{Context, Poll as StdPoll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
/// Converts a `std::future::Future` into an 0.1 `Future.
|
||||
#[derive(Debug)]
|
||||
pub struct Compat<T>(Pin<Box<T>>);
|
||||
|
||||
impl<T> Compat<T> {
|
||||
/// Create a new `Compat` backed by `future`.
|
||||
pub(crate) fn new(future: T) -> Compat<T> {
|
||||
Compat(Box::pin(future))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait IntoAwaitable {
|
||||
type Awaitable;
|
||||
|
||||
fn into_awaitable(self) -> Self::Awaitable;
|
||||
}
|
||||
|
||||
impl<T> IntoAwaitable for T
|
||||
where
|
||||
T: StdFuture,
|
||||
{
|
||||
type Awaitable = Self;
|
||||
|
||||
fn into_awaitable(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Item, Error> Future for Compat<T>
|
||||
where
|
||||
T: StdFuture<Output = Result<Item, Error>>,
|
||||
{
|
||||
type Item = Item;
|
||||
type Error = Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Item, Error> {
|
||||
use futures::Async::*;
|
||||
|
||||
let waker = noop_waker();
|
||||
let mut context = Context::from_waker(&waker);
|
||||
|
||||
let res = self.0.as_mut().poll(&mut context);
|
||||
|
||||
match res {
|
||||
StdPoll::Ready(Ok(val)) => Ok(Ready(val)),
|
||||
StdPoll::Ready(Err(err)) => Err(err),
|
||||
StdPoll::Pending => Ok(NotReady),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== NoopWaker =====
|
||||
|
||||
fn noop_raw_waker() -> RawWaker {
|
||||
RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE)
|
||||
}
|
||||
|
||||
fn noop_waker() -> Waker {
|
||||
unsafe { Waker::from_raw(noop_raw_waker()) }
|
||||
}
|
||||
|
||||
unsafe fn clone_raw(_data: *const ()) -> RawWaker {
|
||||
noop_raw_waker()
|
||||
}
|
||||
|
||||
unsafe fn drop_raw(_data: *const ()) {}
|
||||
|
||||
unsafe fn wake(_data: *const ()) {
|
||||
unimplemented!(
|
||||
"async-await-preview currently only supports futures 0.1. Use \
|
||||
the compatibility layer of futures 0.3 instead, if you want \
|
||||
to use futures 0.3."
|
||||
);
|
||||
}
|
||||
|
||||
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(clone_raw, wake, wake, drop_raw);
|
||||
@@ -1,69 +0,0 @@
|
||||
//! Converts an 0.1 `Future` into a `std::future::Future`.
|
||||
//!
|
||||
use futures::{Async, Future};
|
||||
|
||||
use std::future::Future as StdFuture;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll as StdPoll};
|
||||
|
||||
/// Converts an 0.1 `Future` into a `std::future::Future`.
|
||||
#[derive(Debug)]
|
||||
pub struct Compat<T>(T);
|
||||
|
||||
pub(crate) fn convert_poll<T, E>(poll: Result<Async<T>, E>) -> StdPoll<Result<T, E>> {
|
||||
use futures::Async::{NotReady, Ready};
|
||||
|
||||
match poll {
|
||||
Ok(Ready(val)) => StdPoll::Ready(Ok(val)),
|
||||
Ok(NotReady) => StdPoll::Pending,
|
||||
Err(err) => StdPoll::Ready(Err(err)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn convert_poll_stream<T, E>(
|
||||
poll: Result<Async<Option<T>>, E>,
|
||||
) -> StdPoll<Option<Result<T, E>>> {
|
||||
use futures::Async::{NotReady, Ready};
|
||||
|
||||
match poll {
|
||||
Ok(Ready(Some(val))) => StdPoll::Ready(Some(Ok(val))),
|
||||
Ok(Ready(None)) => StdPoll::Ready(None),
|
||||
Ok(NotReady) => StdPoll::Pending,
|
||||
Err(err) => StdPoll::Ready(Some(Err(err))),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait IntoAwaitable {
|
||||
type Awaitable;
|
||||
|
||||
/// Convert `self` into a value that can be used with `await!`.
|
||||
fn into_awaitable(self) -> Self::Awaitable;
|
||||
}
|
||||
|
||||
impl<T: Future + Unpin> IntoAwaitable for T {
|
||||
type Awaitable = Compat<T>;
|
||||
|
||||
fn into_awaitable(self) -> Self::Awaitable {
|
||||
Compat(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> StdFuture for Compat<T>
|
||||
where
|
||||
T: Future + Unpin,
|
||||
{
|
||||
type Output = Result<T::Item, T::Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _context: &mut Context<'_>) -> StdPoll<Self::Output> {
|
||||
use futures::Async::{NotReady, Ready};
|
||||
|
||||
// TODO: wire in cx
|
||||
|
||||
match self.0.poll() {
|
||||
Ok(Ready(val)) => StdPoll::Ready(Ok(val)),
|
||||
Ok(NotReady) => StdPoll::Pending,
|
||||
Err(e) => StdPoll::Ready(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
//! Compatibility layer between futures 0.1 and `std`.
|
||||
|
||||
pub mod backward;
|
||||
pub mod forward;
|
||||
|
||||
/// Convert a `std::future::Future` yielding `Result` into an 0.1 `Future`.
|
||||
pub fn into_01<T, Item, Error>(future: T) -> backward::Compat<T>
|
||||
where
|
||||
T: std::future::Future<Output = Result<Item, Error>>,
|
||||
{
|
||||
backward::Compat::new(future)
|
||||
}
|
||||
|
||||
/// Convert a `std::future::Future` into an 0.1 `Future` with unit error.
|
||||
pub fn infallible_into_01<T>(future: T) -> impl futures::Future<Item = T::Output, Error = ()>
|
||||
where
|
||||
T: std::future::Future,
|
||||
{
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pub struct Map<T>(T);
|
||||
|
||||
impl<T> Map<T> {
|
||||
fn future<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut T> {
|
||||
unsafe { Pin::map_unchecked_mut(self, |x| &mut x.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::future::Future> std::future::Future for Map<T> {
|
||||
type Output = Result<T::Output, ()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match self.future().poll(cx) {
|
||||
Poll::Ready(v) => Poll::Ready(Ok(v)),
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
into_01(Map(future))
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
//! Futures
|
||||
|
||||
pub use core::future::Future;
|
||||
@@ -1,29 +0,0 @@
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio_io::AsyncWrite;
|
||||
|
||||
/// A future used to fully flush an I/O object.
|
||||
#[derive(Debug)]
|
||||
pub struct Flush<'a, T: ?Sized> {
|
||||
writer: &'a mut T,
|
||||
}
|
||||
|
||||
// Pin is never projected to fields
|
||||
impl<'a, T: ?Sized> Unpin for Flush<'a, T> {}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> Flush<'a, T> {
|
||||
pub(super) fn new(writer: &'a mut T) -> Flush<'a, T> {
|
||||
Flush { writer }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> Future for Flush<'a, T> {
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _context: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
convert_poll(self.writer.poll_flush())
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
//! Use I/O with `async` / `await`.
|
||||
|
||||
mod flush;
|
||||
mod read;
|
||||
mod read_exact;
|
||||
mod write;
|
||||
mod write_all;
|
||||
|
||||
pub use self::flush::Flush;
|
||||
pub use self::read::Read;
|
||||
pub use self::read_exact::ReadExact;
|
||||
pub use self::write::Write;
|
||||
pub use self::write_all::WriteAll;
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
/// An extension trait which adds utility methods to `AsyncRead` types.
|
||||
pub trait AsyncReadExt: AsyncRead {
|
||||
/// Tries to read some bytes directly into the given `buf` in an
|
||||
/// asynchronous manner, returning a future.
|
||||
///
|
||||
/// The returned future will resolve to the number of bytes read once the read
|
||||
/// operation is completed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncReadExt;
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut reader = Cursor::new([1, 2, 3, 4]);
|
||||
/// let mut output = [0u8; 5];
|
||||
///
|
||||
/// let bytes = reader.read_async(&mut output[..]).await.unwrap();
|
||||
///
|
||||
/// // This is only guaranteed to be 4 because `&[u8]` is a synchronous
|
||||
/// // reader. In a real system you could get anywhere from 1 to
|
||||
/// // `output.len()` bytes in a single read.
|
||||
/// assert_eq!(bytes, 4);
|
||||
/// assert_eq!(output, [1, 2, 3, 4, 0]);
|
||||
/// });
|
||||
/// ```
|
||||
fn read_async<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self> {
|
||||
Read::new(self, buf)
|
||||
}
|
||||
|
||||
/// Creates a future which will read exactly enough bytes to fill `buf`,
|
||||
/// returning an error if end of file (EOF) is hit sooner.
|
||||
///
|
||||
/// The returned future will resolve once the read operation is completed.
|
||||
///
|
||||
/// In the case of an error the buffer and the object will be discarded, with
|
||||
/// the error yielded.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncReadExt;
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut reader = Cursor::new([1, 2, 3, 4]);
|
||||
/// let mut output = [0u8; 4];
|
||||
///
|
||||
/// reader.read_exact_async(&mut output).await.unwrap();
|
||||
///
|
||||
/// assert_eq!(output, [1, 2, 3, 4]);
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// ## EOF is hit before `buf` is filled
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncReadExt;
|
||||
/// use std::io::{self, Cursor};
|
||||
///
|
||||
/// let mut reader = Cursor::new([1, 2, 3, 4]);
|
||||
/// let mut output = [0u8; 5];
|
||||
///
|
||||
/// let result = reader.read_exact_async(&mut output).await;
|
||||
///
|
||||
/// assert_eq!(result.unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
/// });
|
||||
/// ```
|
||||
fn read_exact_async<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self> {
|
||||
ReadExact::new(self, buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// An extension trait which adds utility methods to `AsyncWrite` types.
|
||||
pub trait AsyncWriteExt: AsyncWrite {
|
||||
/// Write data into this object.
|
||||
///
|
||||
/// Creates a future that will write the entire contents of the buffer `buf` into
|
||||
/// this `AsyncWrite`.
|
||||
///
|
||||
/// The returned future will not complete until all the data has been written.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncWriteExt;
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buf = [0u8; 5];
|
||||
/// let mut writer = Cursor::new(&mut buf[..]);
|
||||
///
|
||||
/// let n = writer.write_async(&[1, 2, 3, 4]).await.unwrap();
|
||||
///
|
||||
/// assert_eq!(writer.into_inner()[..n], [1, 2, 3, 4, 0][..n]);
|
||||
/// });
|
||||
/// ```
|
||||
fn write_async<'a>(&'a mut self, buf: &'a [u8]) -> Write<'a, Self> {
|
||||
Write::new(self, buf)
|
||||
}
|
||||
|
||||
/// Write an entire buffer into this object.
|
||||
///
|
||||
/// Creates a future that will write the entire contents of the buffer `buf` into
|
||||
/// this `AsyncWrite`.
|
||||
///
|
||||
/// The returned future will not complete until all the data has been written.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncWriteExt;
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buf = [0u8; 5];
|
||||
/// let mut writer = Cursor::new(&mut buf[..]);
|
||||
///
|
||||
/// writer.write_all_async(&[1, 2, 3, 4]).await.unwrap();
|
||||
///
|
||||
/// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]);
|
||||
/// });
|
||||
/// ```
|
||||
fn write_all_async<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> {
|
||||
WriteAll::new(self, buf)
|
||||
}
|
||||
|
||||
/// Creates a future which will entirely flush this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::AsyncWriteExt;
|
||||
/// use std::io::{BufWriter, Cursor};
|
||||
///
|
||||
/// let mut output = [0u8; 5];
|
||||
///
|
||||
/// {
|
||||
/// let mut writer = Cursor::new(&mut output[..]);
|
||||
/// let mut buffered = BufWriter::new(writer);
|
||||
/// buffered.write_all_async(&[1, 2]).await.unwrap();
|
||||
/// buffered.write_all_async(&[3, 4]).await.unwrap();
|
||||
/// buffered.flush_async().await.unwrap();
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(output, [1, 2, 3, 4, 0]);
|
||||
/// });
|
||||
/// ```
|
||||
fn flush_async<'a>(&mut self) -> Flush<'_, Self> {
|
||||
Flush::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + ?Sized> AsyncReadExt for T {}
|
||||
impl<T: AsyncWrite + ?Sized> AsyncWriteExt for T {}
|
||||
@@ -1,32 +0,0 @@
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{self, Poll};
|
||||
use tokio_io::AsyncRead;
|
||||
|
||||
/// A future which can be used to read bytes.
|
||||
#[derive(Debug)]
|
||||
pub struct Read<'a, T: ?Sized> {
|
||||
reader: &'a mut T,
|
||||
buf: &'a mut [u8],
|
||||
}
|
||||
|
||||
// Pinning is never projected to fields
|
||||
impl<'a, T: ?Sized> Unpin for Read<'a, T> {}
|
||||
|
||||
impl<'a, T: AsyncRead + ?Sized> Read<'a, T> {
|
||||
pub(super) fn new(reader: &'a mut T, buf: &'a mut [u8]) -> Read<'a, T> {
|
||||
Read { reader, buf }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: AsyncRead + ?Sized> Future for Read<'a, T> {
|
||||
type Output = io::Result<usize>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _context: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
convert_poll(this.reader.poll_read(this.buf))
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
use std::task::{self, Poll};
|
||||
use tokio_io::AsyncRead;
|
||||
|
||||
/// A future which can be used to read exactly enough bytes to fill a buffer.
|
||||
#[derive(Debug)]
|
||||
pub struct ReadExact<'a, T: ?Sized> {
|
||||
reader: &'a mut T,
|
||||
buf: &'a mut [u8],
|
||||
}
|
||||
|
||||
// Pinning is never projected to fields
|
||||
impl<'a, T: ?Sized> Unpin for ReadExact<'a, T> {}
|
||||
|
||||
impl<'a, T: AsyncRead + ?Sized> ReadExact<'a, T> {
|
||||
pub(super) fn new(reader: &'a mut T, buf: &'a mut [u8]) -> ReadExact<'a, T> {
|
||||
ReadExact { reader, buf }
|
||||
}
|
||||
}
|
||||
|
||||
fn eof() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::UnexpectedEof, "early eof")
|
||||
}
|
||||
|
||||
impl<'a, T: AsyncRead + ?Sized> Future for ReadExact<'a, T> {
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _context: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
|
||||
while !this.buf.is_empty() {
|
||||
let n = try_ready!(convert_poll(this.reader.poll_read(this.buf)));
|
||||
|
||||
{
|
||||
let (_, rest) = mem::replace(&mut this.buf, &mut []).split_at_mut(n);
|
||||
this.buf = rest;
|
||||
}
|
||||
if n == 0 {
|
||||
return Poll::Ready(Err(eof()));
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{self, Poll};
|
||||
use tokio_io::AsyncWrite;
|
||||
|
||||
/// A future used to write data.
|
||||
#[derive(Debug)]
|
||||
pub struct Write<'a, T: ?Sized> {
|
||||
writer: &'a mut T,
|
||||
buf: &'a [u8],
|
||||
}
|
||||
|
||||
// Pinning is never projected to fields
|
||||
impl<'a, T: ?Sized> Unpin for Write<'a, T> {}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> Write<'a, T> {
|
||||
pub(super) fn new(writer: &'a mut T, buf: &'a [u8]) -> Write<'a, T> {
|
||||
Write { writer, buf }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> Future for Write<'a, T> {
|
||||
type Output = io::Result<usize>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _context: &mut task::Context<'_>) -> Poll<io::Result<usize>> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
convert_poll(this.writer.poll_write(this.buf))
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
use std::task::{self, Poll};
|
||||
use tokio_io::AsyncWrite;
|
||||
|
||||
/// A future used to write the entire contents of a buffer.
|
||||
#[derive(Debug)]
|
||||
pub struct WriteAll<'a, T: ?Sized> {
|
||||
writer: &'a mut T,
|
||||
buf: &'a [u8],
|
||||
}
|
||||
|
||||
// Pinning is never projected to fields
|
||||
impl<'a, T: ?Sized> Unpin for WriteAll<'a, T> {}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> WriteAll<'a, T> {
|
||||
pub(super) fn new(writer: &'a mut T, buf: &'a [u8]) -> WriteAll<'a, T> {
|
||||
WriteAll { writer, buf }
|
||||
}
|
||||
}
|
||||
|
||||
fn zero_write() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::WriteZero, "zero-length write")
|
||||
}
|
||||
|
||||
impl<'a, T: AsyncWrite + ?Sized> Future for WriteAll<'a, T> {
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _context: &mut task::Context<'_>) -> Poll<io::Result<()>> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
|
||||
let this = &mut *self;
|
||||
|
||||
while !this.buf.is_empty() {
|
||||
let n = try_ready!(convert_poll(this.writer.poll_write(this.buf)));
|
||||
|
||||
{
|
||||
let (_, rest) = mem::replace(&mut this.buf, &[]).split_at(n);
|
||||
this.buf = rest;
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return Poll::Ready(Err(zero_write()));
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
+10
-22
@@ -1,28 +1,16 @@
|
||||
#![cfg(feature = "async-await-preview")]
|
||||
#![feature(async_await, await_macro)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-futures/0.1.0")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-futures/0.2.0")]
|
||||
#![cfg(feature = "all")]
|
||||
#![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]
|
||||
|
||||
//! A preview of Tokio w/ `async` / `await` support.
|
||||
//! Futures
|
||||
|
||||
/// Extracts the successful type of a `Poll<Result<T, E>>`.
|
||||
///
|
||||
/// This macro bakes in propagation of `Pending` and `Err` signals by returning early.
|
||||
macro_rules! try_ready {
|
||||
($x:expr) => {
|
||||
match $x {
|
||||
std::task::Poll::Ready(Ok(x)) => x,
|
||||
std::task::Poll::Ready(Err(e)) => return std::task::Poll::Ready(Err(e.into())),
|
||||
std::task::Poll::Pending => return std::task::Poll::Pending,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
mod async_wait;
|
||||
pub mod compat;
|
||||
pub mod io;
|
||||
pub mod future;
|
||||
pub mod sink;
|
||||
pub mod stream;
|
||||
|
||||
mod macros;
|
||||
|
||||
pub use crate::future::Future;
|
||||
pub use crate::sink::Sink;
|
||||
pub use crate::stream::Stream;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/// Unwrap a ready value or propagate `Async::Pending`.
|
||||
#[macro_export]
|
||||
macro_rules! ready {
|
||||
($e:expr) => {{
|
||||
use std::task::Poll::{Pending, Ready};
|
||||
|
||||
match $e {
|
||||
Ready(v) => v,
|
||||
Pending => return Pending,
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//! Sinks
|
||||
|
||||
use core::marker::Unpin;
|
||||
use core::ops::DerefMut;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
/// Asynchronously send values
|
||||
pub trait Sink<T> {
|
||||
/// TODO: Dox
|
||||
type Error;
|
||||
|
||||
/// TODO: Dox
|
||||
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
|
||||
|
||||
/// TODO: Dox
|
||||
fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), Self::Error>;
|
||||
|
||||
/// TODO: Dox
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
|
||||
|
||||
/// TODO: Dox
|
||||
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
|
||||
}
|
||||
|
||||
impl<T, S: ?Sized + Sink<T> + Unpin> Sink<T> for &mut S {
|
||||
type Error = S::Error;
|
||||
|
||||
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Pin::new(&mut **self).poll_ready(cx)
|
||||
}
|
||||
|
||||
fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> {
|
||||
Pin::new(&mut **self).start_send(item)
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Pin::new(&mut **self).poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Pin::new(&mut **self).poll_close(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Sink<T> for Pin<S>
|
||||
where
|
||||
S: DerefMut + Unpin,
|
||||
S::Target: Sink<T>,
|
||||
{
|
||||
type Error = <S::Target as Sink<T>>::Error;
|
||||
|
||||
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Pin::get_mut(self).as_mut().poll_ready(cx)
|
||||
}
|
||||
|
||||
fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> {
|
||||
Pin::get_mut(self).as_mut().start_send(item)
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Pin::get_mut(self).as_mut().poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Pin::get_mut(self).as_mut().poll_close(cx)
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
//! Use sinks with `async` / `await`.
|
||||
|
||||
mod send;
|
||||
|
||||
pub use self::send::Send;
|
||||
|
||||
use futures::Sink;
|
||||
|
||||
/// An extension trait which adds utility methods to `Sink` types.
|
||||
pub trait SinkExt: Sink {
|
||||
/// Send an item into the sink.
|
||||
///
|
||||
/// Note that, **because of the flushing requirement, it is usually better
|
||||
/// to batch together items to send via `send_all`, rather than flushing
|
||||
/// between each item.**
|
||||
fn send_async(&mut self, item: Self::SinkItem) -> Send<'_, Self>
|
||||
where
|
||||
Self: Sized + Unpin,
|
||||
{
|
||||
Send::new(self, item)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sink> SinkExt for T {}
|
||||
@@ -1,51 +0,0 @@
|
||||
use futures::Sink;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{self, Poll};
|
||||
|
||||
/// Future for the `SinkExt::send_async` combinator, which sends a value to a
|
||||
/// sink and then waits until the sink has fully flushed.
|
||||
#[derive(Debug)]
|
||||
pub struct Send<'a, T: Sink + ?Sized> {
|
||||
sink: &'a mut T,
|
||||
item: Option<T::SinkItem>,
|
||||
}
|
||||
|
||||
impl<T: Sink + Unpin + ?Sized> Unpin for Send<'_, T> {}
|
||||
|
||||
impl<'a, T: Sink + Unpin + ?Sized> Send<'a, T> {
|
||||
pub(super) fn new(sink: &'a mut T, item: T::SinkItem) -> Self {
|
||||
Send {
|
||||
sink,
|
||||
item: Some(item),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sink + Unpin + ?Sized> Future for Send<'_, T> {
|
||||
type Output = Result<(), T::SinkError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _context: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll;
|
||||
use futures::AsyncSink::{NotReady, Ready};
|
||||
|
||||
if let Some(item) = self.item.take() {
|
||||
match self.sink.start_send(item) {
|
||||
Ok(Ready) => {}
|
||||
Ok(NotReady(val)) => {
|
||||
self.item = Some(val);
|
||||
return Poll::Pending;
|
||||
}
|
||||
Err(err) => {
|
||||
return Poll::Ready(Err(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we're done sending the item, but want to block on flushing the
|
||||
// sink
|
||||
try_ready!(convert_poll(self.sink.poll_complete()));
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
//! Streams
|
||||
|
||||
pub use futures_core::stream::Stream;
|
||||
@@ -1,38 +0,0 @@
|
||||
//! Use streams with `async` / `await`.
|
||||
|
||||
mod next;
|
||||
|
||||
pub use self::next::Next;
|
||||
|
||||
use futures::Stream;
|
||||
|
||||
/// An extension trait which adds utility methods to `Stream` types.
|
||||
pub trait StreamExt: Stream {
|
||||
/// Creates a future that resolves to the next item in the stream.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
/// use tokio::prelude::{stream, StreamAsyncExt};
|
||||
///
|
||||
/// let mut stream = stream::iter_ok::<_, ()>(1..3);
|
||||
///
|
||||
/// assert_eq!(stream.next().await, Some(Ok(1)));
|
||||
/// assert_eq!(stream.next().await, Some(Ok(2)));
|
||||
/// assert_eq!(stream.next().await, Some(Ok(3)));
|
||||
/// assert_eq!(stream.next().await, None);
|
||||
/// });
|
||||
/// ```
|
||||
fn next(&mut self) -> Next<'_, Self>
|
||||
where
|
||||
Self: Sized + Unpin,
|
||||
{
|
||||
Next::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stream> StreamExt for T {}
|
||||
@@ -1,28 +0,0 @@
|
||||
use futures::Stream;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// A future of the next element of a stream.
|
||||
#[derive(Debug)]
|
||||
pub struct Next<'a, T> {
|
||||
stream: &'a mut T,
|
||||
}
|
||||
|
||||
impl<'a, T: Stream + Unpin> Unpin for Next<'a, T> {}
|
||||
|
||||
impl<'a, T: Stream + Unpin> Next<'a, T> {
|
||||
pub(super) fn new(stream: &'a mut T) -> Next<'a, T> {
|
||||
Next { stream }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Stream + Unpin> Future for Next<'a, T> {
|
||||
type Output = Option<Result<T::Item, T::Error>>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _context: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
use crate::compat::forward::convert_poll_stream;
|
||||
|
||||
convert_poll_stream(self.stream.poll())
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -23,8 +23,8 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
bytes = "0.4.7"
|
||||
futures = "0.1.18"
|
||||
log = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-current-thread = { version = "0.2.0", path = "../tokio-current-thread" }
|
||||
pin-utils = "0.1.0-alpha.4"
|
||||
tokio-test = { version = "0.2.0", path = "../tokio-test" }
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// For now, we need to keep the implementation of Encoder in tokio_io.
|
||||
|
||||
pub use crate::codec::Decoder;
|
||||
@@ -1,3 +0,0 @@
|
||||
// For now, we need to keep the implementation of Encoder in tokio_io.
|
||||
|
||||
pub use crate::codec::Encoder;
|
||||
@@ -1,281 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use super::framed_read::{framed_read2, framed_read2_with_buffer, FramedRead2};
|
||||
use super::framed_write::{framed_write2, framed_write2_with_buffer, FramedWrite2};
|
||||
use crate::codec::{Decoder, Encoder};
|
||||
use crate::{AsyncRead, AsyncWrite};
|
||||
use bytes::BytesMut;
|
||||
use futures::{Poll, Sink, StartSend, Stream};
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
|
||||
/// the `Encoder` and `Decoder` traits to encode and decode frames.
|
||||
///
|
||||
/// You can create a `Framed` instance by using the `AsyncRead::framed` adapter.
|
||||
pub struct Framed<T, U> {
|
||||
inner: FramedRead2<FramedWrite2<Fuse<T, U>>>,
|
||||
}
|
||||
|
||||
pub struct Fuse<T, U>(pub T, pub U);
|
||||
|
||||
impl<T, U> Framed<T, U>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite,
|
||||
U: Decoder + Encoder,
|
||||
{
|
||||
/// Provides a `Stream` and `Sink` interface for reading and writing to this
|
||||
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Codec`
|
||||
/// traits to handle encoding and decoding of messages frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
///
|
||||
/// This function returns a *single* object that is both `Stream` and
|
||||
/// `Sink`; grouping this into a single object is often useful for layering
|
||||
/// things like gzip or TLS, which require both read and write access to the
|
||||
/// underlying object.
|
||||
///
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
pub fn new(inner: T, codec: U) -> Framed<T, U> {
|
||||
Framed {
|
||||
inner: framed_read2(framed_write2(Fuse(inner, codec))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Framed<T, U> {
|
||||
/// Provides a `Stream` and `Sink` interface for reading and writing to this
|
||||
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Codec`
|
||||
/// traits to handle encoding and decoding of messages frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
///
|
||||
/// This function returns a *single* object that is both `Stream` and
|
||||
/// `Sink`; grouping this into a single object is often useful for layering
|
||||
/// things like gzip or TLS, which require both read and write access to the
|
||||
/// underlying object.
|
||||
///
|
||||
/// This objects takes a stream and a readbuffer and a writebuffer. These field
|
||||
/// can be obtained from an existing `Framed` with the `into_parts` method.
|
||||
///
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
pub fn from_parts(parts: FramedParts<T, U>) -> Framed<T, U> {
|
||||
Framed {
|
||||
inner: framed_read2_with_buffer(
|
||||
framed_write2_with_buffer(Fuse(parts.io, parts.codec), parts.write_buf),
|
||||
parts.read_buf,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `Frame`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.get_ref().get_ref().0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `Frame`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.get_mut().get_mut().0
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying codec wrapped by
|
||||
/// `Frame`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying codec
|
||||
/// as it may corrupt the stream of frames otherwise being worked with.
|
||||
pub fn codec(&self) -> &U {
|
||||
&self.inner.get_ref().get_ref().1
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying codec wrapped by
|
||||
/// `Frame`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying codec
|
||||
/// as it may corrupt the stream of frames otherwise being worked with.
|
||||
pub fn codec_mut(&mut self) -> &mut U {
|
||||
&mut self.inner.get_mut().get_mut().1
|
||||
}
|
||||
|
||||
/// Consumes the `Frame`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.into_inner().into_inner().0
|
||||
}
|
||||
|
||||
/// Consumes the `Frame`, returning its underlying I/O stream, the buffer
|
||||
/// with unprocessed data, and the codec.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_parts(self) -> FramedParts<T, U> {
|
||||
let (inner, read_buf) = self.inner.into_parts();
|
||||
let (inner, write_buf) = inner.into_parts();
|
||||
|
||||
FramedParts {
|
||||
io: inner.0,
|
||||
codec: inner.1,
|
||||
read_buf: read_buf,
|
||||
write_buf: write_buf,
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Stream for Framed<T, U>
|
||||
where
|
||||
T: AsyncRead,
|
||||
U: Decoder,
|
||||
{
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Sink for Framed<T, U>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
U: Encoder,
|
||||
U::Error: From<io::Error>,
|
||||
{
|
||||
type SinkItem = U::Item;
|
||||
type SinkError = U::Error;
|
||||
|
||||
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
|
||||
self.inner.get_mut().start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.get_mut().poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.get_mut().close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> fmt::Debug for Framed<T, U>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Framed")
|
||||
.field("io", &self.inner.get_ref().get_ref().0)
|
||||
.field("codec", &self.inner.get_ref().get_ref().1)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Fuse =====
|
||||
|
||||
impl<T: Read, U> Read for Fuse<T, U> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead, U> AsyncRead for Fuse<T, U> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.0.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write, U> Write for Fuse<T, U> {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(src)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, U> AsyncWrite for Fuse<T, U> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
self.0.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Decoder> Decoder for Fuse<T, U> {
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn decode(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.1.decode(buffer)
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.1.decode_eof(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Encoder> Encoder for Fuse<T, U> {
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
self.1.encode(item, dst)
|
||||
}
|
||||
}
|
||||
|
||||
/// `FramedParts` contains an export of the data of a Framed transport.
|
||||
/// It can be used to construct a new `Framed` with a different codec.
|
||||
/// It contains all current buffers and the inner transport.
|
||||
#[derive(Debug)]
|
||||
pub struct FramedParts<T, U> {
|
||||
/// The inner transport used to read bytes to and write bytes to
|
||||
pub io: T,
|
||||
|
||||
/// The codec
|
||||
pub codec: U,
|
||||
|
||||
/// The buffer with read but unprocessed data.
|
||||
pub read_buf: BytesMut,
|
||||
|
||||
/// A buffer with unprocessed data which are not written yet.
|
||||
pub write_buf: BytesMut,
|
||||
|
||||
/// This private field allows us to add additional fields in the future in a
|
||||
/// backwards compatible way.
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl<T, U> FramedParts<T, U> {
|
||||
/// Create a new, default, `FramedParts`
|
||||
pub fn new(io: T, codec: U) -> FramedParts<T, U> {
|
||||
FramedParts {
|
||||
io,
|
||||
codec,
|
||||
read_buf: BytesMut::new(),
|
||||
write_buf: BytesMut::new(),
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use super::framed::Fuse;
|
||||
use crate::codec::Decoder;
|
||||
use crate::AsyncRead;
|
||||
use bytes::BytesMut;
|
||||
use futures::{try_ready, Async, Poll, Sink, StartSend, Stream};
|
||||
use log::trace;
|
||||
use std::fmt;
|
||||
|
||||
/// A `Stream` of messages decoded from an `AsyncRead`.
|
||||
pub struct FramedRead<T, D> {
|
||||
inner: FramedRead2<Fuse<T, D>>,
|
||||
}
|
||||
|
||||
pub struct FramedRead2<T> {
|
||||
inner: T,
|
||||
eof: bool,
|
||||
is_readable: bool,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
|
||||
// ===== impl FramedRead =====
|
||||
|
||||
impl<T, D> FramedRead<T, D>
|
||||
where
|
||||
T: AsyncRead,
|
||||
D: Decoder,
|
||||
{
|
||||
/// Creates a new `FramedRead` with the given `decoder`.
|
||||
pub fn new(inner: T, decoder: D) -> FramedRead<T, D> {
|
||||
FramedRead {
|
||||
inner: framed_read2(Fuse(inner, decoder)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> FramedRead<T, D> {
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Consumes the `FramedRead`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying decoder.
|
||||
pub fn decoder(&self) -> &D {
|
||||
&self.inner.inner.1
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying decoder.
|
||||
pub fn decoder_mut(&mut self) -> &mut D {
|
||||
&mut self.inner.inner.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Stream for FramedRead<T, D>
|
||||
where
|
||||
T: AsyncRead,
|
||||
D: Decoder,
|
||||
{
|
||||
type Item = D::Item;
|
||||
type Error = D::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Sink for FramedRead<T, D>
|
||||
where
|
||||
T: Sink,
|
||||
{
|
||||
type SinkItem = T::SinkItem;
|
||||
type SinkError = T::SinkError;
|
||||
|
||||
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
|
||||
self.inner.inner.0.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.inner.0.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.inner.0.close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> fmt::Debug for FramedRead<T, D>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
D: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FramedRead")
|
||||
.field("inner", &self.inner.inner.0)
|
||||
.field("decoder", &self.inner.inner.1)
|
||||
.field("eof", &self.inner.eof)
|
||||
.field("is_readable", &self.inner.is_readable)
|
||||
.field("buffer", &self.inner.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedRead2 =====
|
||||
|
||||
pub fn framed_read2<T>(inner: T) -> FramedRead2<T> {
|
||||
FramedRead2 {
|
||||
inner: inner,
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn framed_read2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedRead2<T> {
|
||||
if buf.capacity() < INITIAL_CAPACITY {
|
||||
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
|
||||
buf.reserve(bytes_to_reserve);
|
||||
}
|
||||
FramedRead2 {
|
||||
inner: inner,
|
||||
eof: false,
|
||||
is_readable: buf.len() > 0,
|
||||
buffer: buf,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedRead2<T> {
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (T, BytesMut) {
|
||||
(self.inner, self.buffer)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for FramedRead2<T>
|
||||
where
|
||||
T: AsyncRead + Decoder,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
loop {
|
||||
// Repeatedly call `decode` or `decode_eof` as long as it is
|
||||
// "readable". Readable is defined as not having returned `None`. If
|
||||
// the upstream has returned EOF, and the decoder is no longer
|
||||
// readable, it can be assumed that the decoder will never become
|
||||
// readable again, at which point the stream is terminated.
|
||||
if self.is_readable {
|
||||
if self.eof {
|
||||
let frame = self.inner.decode_eof(&mut self.buffer)?;
|
||||
return Ok(Async::Ready(frame));
|
||||
}
|
||||
|
||||
trace!("attempting to decode a frame");
|
||||
|
||||
if let Some(frame) = self.inner.decode(&mut self.buffer)? {
|
||||
trace!("frame decoded from buffer");
|
||||
return Ok(Async::Ready(Some(frame)));
|
||||
}
|
||||
|
||||
self.is_readable = false;
|
||||
}
|
||||
|
||||
assert!(!self.eof);
|
||||
|
||||
// Otherwise, try to read more data and try again. Make sure we've
|
||||
// got room for at least one byte to read to ensure that we don't
|
||||
// get a spurious 0 that looks like EOF
|
||||
self.buffer.reserve(1);
|
||||
if 0 == try_ready!(self.inner.read_buf(&mut self.buffer)) {
|
||||
self.eof = true;
|
||||
}
|
||||
|
||||
self.is_readable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use super::framed::Fuse;
|
||||
use crate::codec::{Decoder, Encoder};
|
||||
use crate::{AsyncRead, AsyncWrite};
|
||||
use bytes::BytesMut;
|
||||
use futures::{try_ready, Async, AsyncSink, Poll, Sink, StartSend, Stream};
|
||||
use log::trace;
|
||||
use std::fmt;
|
||||
use std::io::{self, Read};
|
||||
|
||||
/// A `Sink` of frames encoded to an `AsyncWrite`.
|
||||
pub struct FramedWrite<T, E> {
|
||||
inner: FramedWrite2<Fuse<T, E>>,
|
||||
}
|
||||
|
||||
pub struct FramedWrite2<T> {
|
||||
inner: T,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
|
||||
|
||||
impl<T, E> FramedWrite<T, E>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
E: Encoder,
|
||||
{
|
||||
/// Creates a new `FramedWrite` with the given `encoder`.
|
||||
pub fn new(inner: T, encoder: E) -> FramedWrite<T, E> {
|
||||
FramedWrite {
|
||||
inner: framed_write2(Fuse(inner, encoder)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> FramedWrite<T, E> {
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Consumes the `FramedWrite`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying decoder.
|
||||
pub fn encoder(&self) -> &E {
|
||||
&self.inner.inner.1
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying decoder.
|
||||
pub fn encoder_mut(&mut self) -> &mut E {
|
||||
&mut self.inner.inner.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Sink for FramedWrite<T, E>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
E: Encoder,
|
||||
{
|
||||
type SinkItem = E::Item;
|
||||
type SinkError = E::Error;
|
||||
|
||||
fn start_send(&mut self, item: E::Item) -> StartSend<E::Item, E::Error> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
Ok(self.inner.close()?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Stream for FramedWrite<T, D>
|
||||
where
|
||||
T: Stream,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.inner.0.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> fmt::Debug for FramedWrite<T, U>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FramedWrite")
|
||||
.field("inner", &self.inner.get_ref().0)
|
||||
.field("encoder", &self.inner.get_ref().1)
|
||||
.field("buffer", &self.inner.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedWrite2 =====
|
||||
|
||||
pub fn framed_write2<T>(inner: T) -> FramedWrite2<T> {
|
||||
FramedWrite2 {
|
||||
inner: inner,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn framed_write2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedWrite2<T> {
|
||||
if buf.capacity() < INITIAL_CAPACITY {
|
||||
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
|
||||
buf.reserve(bytes_to_reserve);
|
||||
}
|
||||
FramedWrite2 {
|
||||
inner: inner,
|
||||
buffer: buf,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedWrite2<T> {
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (T, BytesMut) {
|
||||
(self.inner, self.buffer)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sink for FramedWrite2<T>
|
||||
where
|
||||
T: AsyncWrite + Encoder,
|
||||
{
|
||||
type SinkItem = T::Item;
|
||||
type SinkError = T::Error;
|
||||
|
||||
fn start_send(&mut self, item: T::Item) -> StartSend<T::Item, T::Error> {
|
||||
// If the buffer is already over 8KiB, then attempt to flush it. If after flushing it's
|
||||
// *still* over 8KiB, then apply backpressure (reject the send).
|
||||
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
self.poll_complete()?;
|
||||
|
||||
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
return Ok(AsyncSink::NotReady(item));
|
||||
}
|
||||
}
|
||||
|
||||
self.inner.encode(item, &mut self.buffer)?;
|
||||
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
trace!("flushing framed transport");
|
||||
|
||||
while !self.buffer.is_empty() {
|
||||
trace!("writing; remaining={}", self.buffer.len());
|
||||
|
||||
let n = try_ready!(self.inner.poll_write(&self.buffer));
|
||||
|
||||
if n == 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
"failed to \
|
||||
write frame to transport",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
// TODO: Add a way to `bytes` to do this w/o returning the drained
|
||||
// data.
|
||||
let _ = self.buffer.split_to(n);
|
||||
}
|
||||
|
||||
// Try flushing the underlying IO
|
||||
try_ready!(self.inner.poll_flush());
|
||||
|
||||
trace!("framed transport flushed");
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
try_ready!(self.poll_complete());
|
||||
Ok(self.inner.shutdown()?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decoder> Decoder for FramedWrite2<T> {
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
|
||||
self.inner.decode(src)
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
|
||||
self.inner.decode_eof(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read> Read for FramedWrite2<T> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for FramedWrite2<T> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.inner.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
//! 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]: #
|
||||
|
||||
#![doc(hidden, html_root_url = "https://docs.rs/tokio-codec/0.1.0")]
|
||||
|
||||
// _tokio_codec are the items that belong in the `tokio_codec` crate. However, because we need to
|
||||
// maintain backward compatibility until the next major breaking change, they are defined here.
|
||||
// When the next breaking change comes, they should be moved to the `tokio_codec` crate and become
|
||||
// independent.
|
||||
//
|
||||
// The primary reason we can't move these to `tokio-codec` now is because, again for backward
|
||||
// compatibility reasons, we need to keep `Decoder` and `Encoder` in tokio_io::codec. And `Decoder`
|
||||
// and `Encoder` needs to reference `Framed`. So they all still need to still be in the same
|
||||
// module.
|
||||
|
||||
mod decoder;
|
||||
mod encoder;
|
||||
mod framed;
|
||||
mod framed_read;
|
||||
mod framed_write;
|
||||
|
||||
pub use self::decoder::Decoder;
|
||||
pub use self::encoder::Encoder;
|
||||
pub use self::framed::{Framed, FramedParts};
|
||||
pub use self::framed_read::FramedRead;
|
||||
pub use self::framed_write::FramedWrite;
|
||||
@@ -1,93 +0,0 @@
|
||||
use crate::{AsyncRead, AsyncWrite};
|
||||
use futures::{Async, Poll};
|
||||
use std::{fmt, io};
|
||||
|
||||
/// A simple wrapper type which allows types that only implement
|
||||
/// `std::io::Read` or `std::io::Write` to be used in contexts which expect
|
||||
/// an `AsyncRead` or `AsyncWrite`.
|
||||
///
|
||||
/// If these types issue an error with the kind `io::ErrorKind::WouldBlock`,
|
||||
/// it is expected that they will notify the current task on readiness.
|
||||
/// Synchronous `std` types should not issue errors of this kind and
|
||||
/// are safe to use in this context. However, using these types with
|
||||
/// `AllowStdIo` will cause the event loop to block, so they should be used
|
||||
/// with care.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct AllowStdIo<T>(T);
|
||||
|
||||
impl<T> AllowStdIo<T> {
|
||||
/// Creates a new `AllowStdIo` from an existing IO object.
|
||||
pub fn new(io: T) -> Self {
|
||||
AllowStdIo(io)
|
||||
}
|
||||
|
||||
/// Returns a reference to the contained IO object.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the contained IO object.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
/// Consumes self and returns the contained IO object.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> io::Write for AllowStdIo<T>
|
||||
where
|
||||
T: io::Write,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
self.0.write_all(buf)
|
||||
}
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
self.0.write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsyncWrite for AllowStdIo<T>
|
||||
where
|
||||
T: io::Write,
|
||||
{
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> io::Read for AllowStdIo<T>
|
||||
where
|
||||
T: io::Read,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
// TODO: implement the `initializer` fn when it stabilizes.
|
||||
// See rust-lang/rust #42788
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
self.0.read_to_end(buf)
|
||||
}
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
self.0.read_to_string(buf)
|
||||
}
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
self.0.read_exact(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsyncRead for AllowStdIo<T>
|
||||
where
|
||||
T: io::Read,
|
||||
{
|
||||
// TODO: override prepare_uninitialized_buffer once `Read::initializer` is stable.
|
||||
// See rust-lang/rust #42788
|
||||
}
|
||||
+55
-71
@@ -1,10 +1,10 @@
|
||||
#[allow(deprecated)]
|
||||
use crate::codec::{Decoder, Encoder, Framed};
|
||||
use crate::split::{ReadHalf, WriteHalf};
|
||||
use crate::{framed, split, AsyncWrite};
|
||||
//use crate::split::{ReadHalf, WriteHalf};
|
||||
//use crate::{framed, split, AsyncWrite};
|
||||
use bytes::BufMut;
|
||||
use futures::{try_ready, Async, Poll};
|
||||
use std::io as std_io;
|
||||
use std::io;
|
||||
use std::ops::DerefMut;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Read bytes asynchronously.
|
||||
///
|
||||
@@ -15,23 +15,23 @@ use std::io as std_io;
|
||||
/// Specifically, this means that the `poll_read` function will return one of
|
||||
/// the following:
|
||||
///
|
||||
/// * `Ok(Async::Ready(n))` means that `n` bytes of data was immediately read
|
||||
/// * `Poll::Ready(Ok(n))` means that `n` bytes of data was immediately read
|
||||
/// and placed into the output buffer, where `n` == 0 implies that EOF has
|
||||
/// been reached.
|
||||
///
|
||||
/// * `Ok(Async::NotReady)` means that no data was read into the buffer
|
||||
/// * `Poll::Pending` means that no data was read into the buffer
|
||||
/// provided. The I/O object is not currently readable but may become readable
|
||||
/// in the future. Most importantly, **the current future's task is scheduled
|
||||
/// to get unparked when the object is readable**. This means that like
|
||||
/// `Future::poll` you'll receive a notification when the I/O object is
|
||||
/// readable again.
|
||||
///
|
||||
/// * `Err(e)` for other errors are standard I/O errors coming from the
|
||||
/// * `Poll::Ready(Err(e))` for other errors are standard I/O errors coming from the
|
||||
/// underlying object.
|
||||
///
|
||||
/// This trait importantly means that the `read` method only works in the
|
||||
/// context of a future's task. The object may panic if used outside of a task.
|
||||
pub trait AsyncRead: std_io::Read {
|
||||
pub trait AsyncRead {
|
||||
/// Prepares an uninitialized buffer to be safe to pass to `read`. Returns
|
||||
/// `true` if the supplied buffer was zeroed out.
|
||||
///
|
||||
@@ -70,19 +70,17 @@ pub trait AsyncRead: std_io::Read {
|
||||
|
||||
/// Attempt to read from the `AsyncRead` into `buf`.
|
||||
///
|
||||
/// On success, returns `Ok(Async::Ready(num_bytes_read))`.
|
||||
/// On success, returns `Poll::Ready(Ok(num_bytes_read))`.
|
||||
///
|
||||
/// If no data is available for reading, the method returns
|
||||
/// `Ok(Async::NotReady)` and arranges for the current task (via
|
||||
/// `Poll::Pending` and arranges for the current task (via
|
||||
/// `cx.waker()`) to receive a notification when the object becomes
|
||||
/// readable or is closed.
|
||||
fn poll_read(&mut self, buf: &mut [u8]) -> Poll<usize, std_io::Error> {
|
||||
match self.read(buf) {
|
||||
Ok(t) => Ok(Async::Ready(t)),
|
||||
Err(ref e) if e.kind() == std_io::ErrorKind::WouldBlock => return Ok(Async::NotReady),
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>>;
|
||||
|
||||
/// Pull some bytes from this source into the specified `BufMut`, returning
|
||||
/// how many bytes were read.
|
||||
@@ -90,12 +88,16 @@ pub trait AsyncRead: std_io::Read {
|
||||
/// The `buf` provided will have bytes read into it and the internal cursor
|
||||
/// will be advanced if any bytes were read. Note that this method typically
|
||||
/// will not reallocate the buffer provided.
|
||||
fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, std_io::Error>
|
||||
fn poll_read_buf<B: BufMut>(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut B,
|
||||
) -> Poll<io::Result<usize>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if !buf.has_remaining_mut() {
|
||||
return Ok(Async::Ready(0));
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
@@ -104,69 +106,51 @@ pub trait AsyncRead: std_io::Read {
|
||||
|
||||
self.prepare_uninitialized_buffer(b);
|
||||
|
||||
try_ready!(self.poll_read(b))
|
||||
ready!(self.poll_read(cx, b))?
|
||||
};
|
||||
|
||||
buf.advance_mut(n);
|
||||
Ok(Async::Ready(n))
|
||||
Poll::Ready(Ok(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a `Stream` and `Sink` interface for reading and writing to this
|
||||
/// I/O object, using `Decode` and `Encode` to read and write the raw data.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Codec`
|
||||
/// traits to handle encoding and decoding of messages frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
///
|
||||
/// This function returns a *single* object that is both `Stream` and
|
||||
/// `Sink`; grouping this into a single object is often useful for layering
|
||||
/// things like gzip or TLS, which require both read and write access to the
|
||||
/// underlying object.
|
||||
///
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
#[deprecated(since = "0.1.7", note = "Use tokio_codec::Decoder::framed instead")]
|
||||
#[allow(deprecated)]
|
||||
fn framed<T: Encoder + Decoder>(self, codec: T) -> Framed<Self, T>
|
||||
where
|
||||
Self: AsyncWrite + Sized,
|
||||
{
|
||||
framed::framed(self, codec)
|
||||
}
|
||||
macro_rules! deref_async_read {
|
||||
() => {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
(**self).prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
|
||||
/// Helper method for splitting this read/write object into two halves.
|
||||
///
|
||||
/// The two halves returned implement the `Read` and `Write` traits,
|
||||
/// respectively.
|
||||
///
|
||||
/// To restore this read/write object from its `ReadHalf` and `WriteHalf`
|
||||
/// use `unsplit`.
|
||||
fn split(self) -> (ReadHalf<Self>, WriteHalf<Self>)
|
||||
where
|
||||
Self: AsyncWrite + Sized,
|
||||
{
|
||||
split::split(self)
|
||||
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8])
|
||||
-> Poll<io::Result<usize>>
|
||||
{
|
||||
Pin::new(&mut **self).poll_read(cx, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncRead> AsyncRead for Box<T> {
|
||||
impl<T: ?Sized + AsyncRead + Unpin> AsyncRead for Box<T> {
|
||||
deref_async_read!();
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncRead + Unpin> AsyncRead for &mut T {
|
||||
deref_async_read!();
|
||||
}
|
||||
|
||||
impl<P> AsyncRead for Pin<P>
|
||||
where
|
||||
P: DerefMut + Unpin,
|
||||
P::Target: AsyncRead,
|
||||
{
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
(**self).prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + AsyncRead> AsyncRead for &'a mut T {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
(**self).prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsyncRead for &'a [u8] {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [u8]) -> bool {
|
||||
false
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
self.get_mut().as_mut().poll_read(cx, buf)
|
||||
}
|
||||
}
|
||||
|
||||
+56
-86
@@ -1,7 +1,9 @@
|
||||
use crate::AsyncRead;
|
||||
//use crate::AsyncRead;
|
||||
use bytes::Buf;
|
||||
use futures::{try_ready, Async, Poll};
|
||||
use std::io as std_io;
|
||||
use std::io;
|
||||
use std::ops::DerefMut;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Writes bytes asynchronously.
|
||||
///
|
||||
@@ -33,7 +35,7 @@ use std::io as std_io;
|
||||
/// writer has successfully been flushed, a "would block" error means that the
|
||||
/// current task is ready to receive a notification when flushing can make more
|
||||
/// progress, and otherwise normal errors can happen as well.
|
||||
pub trait AsyncWrite: std_io::Write {
|
||||
pub trait AsyncWrite {
|
||||
/// Attempt to write bytes from `buf` into the object.
|
||||
///
|
||||
/// On success, returns `Ok(Async::Ready(num_bytes_written))`.
|
||||
@@ -42,13 +44,11 @@ pub trait AsyncWrite: std_io::Write {
|
||||
/// `Ok(Async::NotReady)` and arranges for the current task (via
|
||||
/// `cx.waker()`) to receive a notification when the object becomes
|
||||
/// readable or is closed.
|
||||
fn poll_write(&mut self, buf: &[u8]) -> Poll<usize, std_io::Error> {
|
||||
match self.write(buf) {
|
||||
Ok(t) => Ok(Async::Ready(t)),
|
||||
Err(ref e) if e.kind() == std_io::ErrorKind::WouldBlock => return Ok(Async::NotReady),
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, io::Error>>;
|
||||
|
||||
/// Attempt to flush the object, ensuring that any buffered data reach
|
||||
/// their destination.
|
||||
@@ -59,13 +59,7 @@ pub trait AsyncWrite: std_io::Write {
|
||||
/// `Ok(Async::NotReady)` and arranges for the current task (via
|
||||
/// `cx.waker()`) to receive a notification when the object can make
|
||||
/// progress towards flushing.
|
||||
fn poll_flush(&mut self) -> Poll<(), std_io::Error> {
|
||||
match self.flush() {
|
||||
Ok(t) => Ok(Async::Ready(t)),
|
||||
Err(ref e) if e.kind() == std_io::ErrorKind::WouldBlock => return Ok(Async::NotReady),
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>>;
|
||||
|
||||
/// Initiates or attempts to shut down this writer, returning success when
|
||||
/// the I/O connection has completely shut down.
|
||||
@@ -125,97 +119,73 @@ pub trait AsyncWrite: std_io::Write {
|
||||
///
|
||||
/// This function will panic if not called within the context of a future's
|
||||
/// task.
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error>;
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>)
|
||||
-> Poll<Result<(), io::Error>>;
|
||||
|
||||
/// Write a `Buf` into this value, returning how many bytes were written.
|
||||
///
|
||||
/// Note that this method will advance the `buf` provided automatically by
|
||||
/// the number of bytes written.
|
||||
fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, std_io::Error>
|
||||
fn poll_write_buf<B: Buf>(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut B,
|
||||
) -> Poll<Result<usize, io::Error>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if !buf.has_remaining() {
|
||||
return Ok(Async::Ready(0));
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
|
||||
let n = try_ready!(self.poll_write(buf.bytes()));
|
||||
let n = ready!(self.poll_write(cx, buf.bytes()))?;
|
||||
buf.advance(n);
|
||||
Ok(Async::Ready(n))
|
||||
Poll::Ready(Ok(n))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncWrite> AsyncWrite for Box<T> {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
(**self).shutdown()
|
||||
}
|
||||
}
|
||||
impl<'a, T: ?Sized + AsyncWrite> AsyncWrite for &'a mut T {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
(**self).shutdown()
|
||||
macro_rules! deref_async_write {
|
||||
() => {
|
||||
fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8])
|
||||
-> Poll<io::Result<usize>>
|
||||
{
|
||||
Pin::new(&mut **self).poll_write(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Pin::new(&mut **self).poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Pin::new(&mut **self).poll_shutdown(cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for std_io::Repeat {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
|
||||
false
|
||||
}
|
||||
impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> {
|
||||
deref_async_write!();
|
||||
}
|
||||
|
||||
impl AsyncWrite for std_io::Sink {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for &mut T {
|
||||
deref_async_write!();
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for std_io::Take<T> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.get_ref().prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> AsyncRead for std_io::Chain<T, U>
|
||||
impl<P> AsyncWrite for Pin<P>
|
||||
where
|
||||
T: AsyncRead,
|
||||
U: AsyncRead,
|
||||
P: DerefMut + Unpin,
|
||||
P::Target: AsyncWrite,
|
||||
{
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
let (t, u) = self.get_ref();
|
||||
// We don't need to execute the second initializer if the first one
|
||||
// already zeroed the buffer out.
|
||||
t.prepare_uninitialized_buffer(buf) || u.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite> AsyncWrite for std_io::BufWriter<T> {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
try_ready!(self.poll_flush());
|
||||
self.get_mut().shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for std_io::BufReader<T> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.get_ref().prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> AsyncRead for std_io::Cursor<T> {}
|
||||
|
||||
impl<'a> AsyncWrite for std_io::Cursor<&'a mut [u8]> {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for std_io::Cursor<Vec<u8>> {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for std_io::Cursor<Box<[u8]>> {
|
||||
fn shutdown(&mut self) -> Poll<(), std_io::Error> {
|
||||
Ok(().into())
|
||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8])
|
||||
-> Poll<io::Result<usize>>
|
||||
{
|
||||
self.get_mut().as_mut().poll_write(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.get_mut().as_mut().poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.get_mut().as_mut().poll_shutdown(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::codec::{Decoder, Encoder};
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use std::io;
|
||||
|
||||
/// A simple `Codec` implementation that just ships bytes around.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
pub struct BytesCodec(());
|
||||
|
||||
impl BytesCodec {
|
||||
/// 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(())
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
use super::encoder::Encoder;
|
||||
use crate::_tokio_codec::Framed;
|
||||
use crate::{AsyncRead, AsyncWrite};
|
||||
use bytes::BytesMut;
|
||||
use std::io;
|
||||
|
||||
/// Decoding of frames via buffers.
|
||||
///
|
||||
/// This trait is used when constructing an instance of `Framed` or
|
||||
/// `FramedRead`. An implementation of `Decoder` takes a byte stream that has
|
||||
/// already been buffered in `src` and decodes the data into a stream of
|
||||
/// `Self::Item` frames.
|
||||
///
|
||||
/// Implementations are able to track state on `self`, which enables
|
||||
/// implementing stateful streaming parsers. In many cases, though, this type
|
||||
/// will simply be a unit struct (e.g. `struct HttpDecoder`).
|
||||
|
||||
// Note: We can't deprecate this trait, because the deprecation carries through to tokio-codec, and
|
||||
// there doesn't seem to be a way to un-deprecate the re-export.
|
||||
pub trait Decoder {
|
||||
/// The type of decoded frames.
|
||||
type Item;
|
||||
|
||||
/// The type of unrecoverable frame decoding errors.
|
||||
///
|
||||
/// If an individual message is ill-formed but can be ignored without
|
||||
/// interfering with the processing of future messages, it may be more
|
||||
/// useful to report the failure as an `Item`.
|
||||
///
|
||||
/// `From<io::Error>` is required in the interest of making `Error` suitable
|
||||
/// for returning directly from a `FramedRead`, and to enable the default
|
||||
/// implementation of `decode_eof` to yield an `io::Error` when the decoder
|
||||
/// fails to consume all available data.
|
||||
///
|
||||
/// Note that implementors of this trait can simply indicate `type Error =
|
||||
/// io::Error` to use I/O errors as this type.
|
||||
type Error: From<io::Error>;
|
||||
|
||||
/// Attempts to decode a frame from the provided buffer of bytes.
|
||||
///
|
||||
/// This method is called by `FramedRead` whenever bytes are ready to be
|
||||
/// parsed. The provided buffer of bytes is what's been read so far, and
|
||||
/// this instance of `Decode` can determine whether an entire frame is in
|
||||
/// the buffer and is ready to be returned.
|
||||
///
|
||||
/// If an entire frame is available, then this instance will remove those
|
||||
/// bytes from the buffer provided and return them as a decoded
|
||||
/// frame. Note that removing bytes from the provided buffer doesn't always
|
||||
/// necessarily copy the bytes, so this should be an efficient operation in
|
||||
/// most circumstances.
|
||||
///
|
||||
/// If the bytes look valid, but a frame isn't fully available yet, then
|
||||
/// `Ok(None)` is returned. This indicates to the `Framed` instance that
|
||||
/// it needs to read some more bytes before calling this method again.
|
||||
///
|
||||
/// Note that the bytes provided may be empty. If a previous call to
|
||||
/// `decode` consumed all the bytes in the buffer then `decode` will be
|
||||
/// called again until it returns `Ok(None)`, indicating that more bytes need to
|
||||
/// be read.
|
||||
///
|
||||
/// Finally, if the bytes in the buffer are malformed then an error is
|
||||
/// returned indicating why. This informs `Framed` that the stream is now
|
||||
/// corrupt and should be terminated.
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error>;
|
||||
|
||||
/// A default method available to be called when there are no more bytes
|
||||
/// available to be read from the underlying I/O.
|
||||
///
|
||||
/// This method defaults to calling `decode` and returns an error if
|
||||
/// `Ok(None)` is returned while there is unconsumed data in `buf`.
|
||||
/// Typically this doesn't need to be implemented unless the framing
|
||||
/// protocol differs near the end of the stream.
|
||||
///
|
||||
/// Note that the `buf` argument may be empty. If a previous call to
|
||||
/// `decode_eof` consumed all the bytes in the buffer, `decode_eof` will be
|
||||
/// called again until it returns `None`, indicating that there are no more
|
||||
/// frames to yield. This behavior enables returning finalization frames
|
||||
/// that may not be based on inbound data.
|
||||
fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match self.decode(buf)? {
|
||||
Some(frame) => Ok(Some(frame)),
|
||||
None => {
|
||||
if buf.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "bytes remaining on stream").into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a `Stream` and `Sink` interface for reading and writing to this
|
||||
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Codec`
|
||||
/// traits to handle encoding and decoding of messages frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
///
|
||||
/// This function returns a *single* object that is both `Stream` and
|
||||
/// `Sink`; grouping this into a single object is often useful for layering
|
||||
/// things like gzip or TLS, which require both read and write access to the
|
||||
/// underlying object.
|
||||
///
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
fn framed<T: AsyncRead + AsyncWrite + Sized>(self, io: T) -> Framed<T, Self>
|
||||
where
|
||||
Self: Encoder + Sized,
|
||||
{
|
||||
Framed::new(io, self)
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
use bytes::BytesMut;
|
||||
use std::io;
|
||||
|
||||
/// Trait of helper objects to write out messages as bytes, for use with
|
||||
/// `FramedWrite`.
|
||||
|
||||
// Note: We can't deprecate this trait, because the deprecation carries through to tokio-codec, and
|
||||
// there doesn't seem to be a way to un-deprecate the re-export.
|
||||
pub trait Encoder {
|
||||
/// The type of items consumed by the `Encoder`
|
||||
type Item;
|
||||
|
||||
/// The type of encoding errors.
|
||||
///
|
||||
/// `FramedWrite` requires `Encoder`s errors to implement `From<io::Error>`
|
||||
/// in the interest letting it return `Error`s directly.
|
||||
type Error: From<io::Error>;
|
||||
|
||||
/// Encodes a frame into the buffer provided.
|
||||
///
|
||||
/// This method will encode `item` into the byte buffer provided by `dst`.
|
||||
/// The `dst` provided is an internal buffer of the `Framed` instance and
|
||||
/// will be written out when possible.
|
||||
fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error>;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::codec::{Decoder, Encoder};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use std::{io, str};
|
||||
|
||||
/// A simple `Codec` implementation that splits up data into lines.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
pub struct LinesCodec {
|
||||
// Stored index of the next index to examine for a `\n` character.
|
||||
// This is used to optimize searching.
|
||||
// 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,375 +0,0 @@
|
||||
//! 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]: #
|
||||
|
||||
// tokio_io::codec originally held all codec-related helpers. This is now intended to be in
|
||||
// tokio_codec instead. However, for backward compatibility, this remains here. When the next major
|
||||
// breaking change comes, `Encoder` and `Decoder` need to be moved to `tokio_codec`, and the rest
|
||||
// of this module should be removed.
|
||||
|
||||
#![doc(hidden)]
|
||||
#![allow(deprecated)]
|
||||
|
||||
mod bytes_codec;
|
||||
mod decoder;
|
||||
mod encoder;
|
||||
mod lines_codec;
|
||||
|
||||
pub use self::bytes_codec::BytesCodec;
|
||||
pub use self::decoder::Decoder;
|
||||
pub use self::encoder::Encoder;
|
||||
pub use self::lines_codec::LinesCodec;
|
||||
pub use crate::framed::{Framed, FramedParts};
|
||||
pub use crate::framed_read::FramedRead;
|
||||
pub use crate::framed_write::FramedWrite;
|
||||
|
||||
#[deprecated(since = "0.1.8", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub mod length_delimited {
|
||||
//! Frame a stream of bytes based on a length prefix
|
||||
//!
|
||||
//! Many protocols delimit their frames by prefacing frame data with a
|
||||
//! frame head that specifies the length of the frame. The
|
||||
//! `length_delimited` module provides utilities for handling the length
|
||||
//! based framing. This allows the consumer to work with entire frames
|
||||
//! without having to worry about buffering or other framing logic.
|
||||
//!
|
||||
//! # Getting started
|
||||
//!
|
||||
//! If implementing a protocol from scratch, using length delimited framing
|
||||
//! is an easy way to get started. [`Framed::new()`](length_delimited::Framed::new) will adapt a
|
||||
//! full-duplex byte stream with a length delimited framer using default
|
||||
//! configuration values.
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_io::{AsyncRead, AsyncWrite};
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//!
|
||||
//! fn bind_transport<T: AsyncRead + AsyncWrite>(io: T)
|
||||
//! -> length_delimited::Framed<T>
|
||||
//! {
|
||||
//! length_delimited::Framed::new(io)
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The returned transport implements `Sink + Stream` for `BytesMut`. It
|
||||
//! encodes the frame with a big-endian `u32` header denoting the frame
|
||||
//! payload length:
|
||||
//!
|
||||
//! ```text
|
||||
//! +----------+--------------------------------+
|
||||
//! | len: u32 | frame payload |
|
||||
//! +----------+--------------------------------+
|
||||
//! ```
|
||||
//!
|
||||
//! Specifically, given the following:
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_io::{AsyncRead, AsyncWrite};
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//! use bytes::BytesMut;
|
||||
//! use futures::{Sink, Future};
|
||||
//!
|
||||
//! fn write_frame<T: AsyncRead + AsyncWrite>(io: T) {
|
||||
//! let mut transport = length_delimited::Framed::new(io);
|
||||
//! let frame = BytesMut::from("hello world");
|
||||
//!
|
||||
//! transport.send(frame).wait().unwrap();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The encoded frame will look like this:
|
||||
//!
|
||||
//! ```text
|
||||
//! +---- len: u32 ----+---- data ----+
|
||||
//! | \x00\x00\x00\x0b | hello world |
|
||||
//! +------------------+--------------+
|
||||
//! ```
|
||||
//!
|
||||
//! # Decoding
|
||||
//!
|
||||
//! [`FramedRead`] adapts an [`AsyncRead`] into a `Stream` of [`BytesMut`],
|
||||
//! such that each yielded [`BytesMut`] value contains the contents of an
|
||||
//! entire frame. There are many configuration parameters enabling
|
||||
//! [`FramedRead`] to handle a wide range of protocols. Here are some
|
||||
//! examples that will cover the various options at a high level.
|
||||
//!
|
||||
//! ## Example 1
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 0, including the
|
||||
//! frame head in the yielded `BytesMut`.
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_io::AsyncRead;
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//!
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(0) // default value
|
||||
//! .num_skip(0) // Do not strip frame header
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT DECODED
|
||||
//! +-- len ---+--- Payload ---+ +-- len ---+--- Payload ---+
|
||||
//! | \x00\x0B | Hello world | --> | \x00\x0B | Hello world |
|
||||
//! +----------+---------------+ +----------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! The value of the length field is 11 (`\x0B`) which represents the length
|
||||
//! of the payload, `hello world`. By default, [`FramedRead`] assumes that
|
||||
//! the length field represents the number of bytes that **follows** the
|
||||
//! length field. Thus, the entire frame has a length of 13: 2 bytes for the
|
||||
//! frame head + 11 bytes for the payload.
|
||||
//!
|
||||
//! ## Example 2
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 0, omitting the
|
||||
//! frame head in the yielded `BytesMut`.
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_io::AsyncRead;
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//!
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(0) // default value
|
||||
//! // `num_skip` is not needed, the default is to skip
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT DECODED
|
||||
//! +-- len ---+--- Payload ---+ +--- Payload ---+
|
||||
//! | \x00\x0B | Hello world | --> | Hello world |
|
||||
//! +----------+---------------+ +---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! This is similar to the first example, the only difference is that the
|
||||
//! frame head is **not** included in the yielded `BytesMut` value.
|
||||
//!
|
||||
//! ## Example 3
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 0, including the
|
||||
//! frame head in the yielded `BytesMut`. In this case, the length field
|
||||
//! **includes** the frame head length.
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_io::AsyncRead;
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//!
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(-2) // size of head
|
||||
//! .num_skip(0)
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT DECODED
|
||||
//! +-- len ---+--- Payload ---+ +-- len ---+--- Payload ---+
|
||||
//! | \x00\x0D | Hello world | --> | \x00\x0D | Hello world |
|
||||
//! +----------+---------------+ +----------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! In most cases, the length field represents the length of the payload
|
||||
//! only, as shown in the previous examples. However, in some protocols the
|
||||
//! length field represents the length of the whole frame, including the
|
||||
//! head. In such cases, we specify a negative `length_adjustment` to adjust
|
||||
//! the value provided in the frame head to represent the payload length.
|
||||
//!
|
||||
//! ## Example 4
|
||||
//!
|
||||
//! The following will parse a 3 byte length field at offset 0 in a 5 byte
|
||||
//! frame head, including the frame head in the yielded `BytesMut`.
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_io::AsyncRead;
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//!
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(0) // default value
|
||||
//! .length_field_length(3)
|
||||
//! .length_adjustment(2) // remaining head
|
||||
//! .num_skip(0)
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT
|
||||
//! +---- len -----+- head -+--- Payload ---+
|
||||
//! | \x00\x00\x0B | \xCAFE | Hello world |
|
||||
//! +--------------+--------+---------------+
|
||||
//!
|
||||
//! DECODED
|
||||
//! +---- len -----+- head -+--- Payload ---+
|
||||
//! | \x00\x00\x0B | \xCAFE | Hello world |
|
||||
//! +--------------+--------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! A more advanced example that shows a case where there is extra frame
|
||||
//! head data between the length field and the payload. In such cases, it is
|
||||
//! usually desirable to include the frame head as part of the yielded
|
||||
//! `BytesMut`. This lets consumers of the length delimited framer to
|
||||
//! process the frame head as needed.
|
||||
//!
|
||||
//! The positive `length_adjustment` value lets `FramedRead` factor in the
|
||||
//! additional head into the frame length calculation.
|
||||
//!
|
||||
//! ## Example 5
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 1 of a 4 byte
|
||||
//! frame head. The first byte and the length field will be omitted from the
|
||||
//! yielded `BytesMut`, but the trailing 2 bytes of the frame head will be
|
||||
//! included.
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_io::AsyncRead;
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//!
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(1) // length of hdr1
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(1) // length of hdr2
|
||||
//! .num_skip(3) // length of hdr1 + LEN
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT
|
||||
//! +- hdr1 -+-- len ---+- hdr2 -+--- Payload ---+
|
||||
//! | \xCA | \x00\x0B | \xFE | Hello world |
|
||||
//! +--------+----------+--------+---------------+
|
||||
//!
|
||||
//! DECODED
|
||||
//! +- hdr2 -+--- Payload ---+
|
||||
//! | \xFE | Hello world |
|
||||
//! +--------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! The length field is situated in the middle of the frame head. In this
|
||||
//! case, the first byte in the frame head could be a version or some other
|
||||
//! identifier that is not needed for processing. On the other hand, the
|
||||
//! second half of the head is needed.
|
||||
//!
|
||||
//! `length_field_offset` indicates how many bytes to skip before starting
|
||||
//! to read the length field. `length_adjustment` is the number of bytes to
|
||||
//! skip starting at the end of the length field. In this case, it is the
|
||||
//! second half of the head.
|
||||
//!
|
||||
//! ## Example 6
|
||||
//!
|
||||
//! The following will parse a `u16` length field at offset 1 of a 4 byte
|
||||
//! frame head. The first byte and the length field will be omitted from the
|
||||
//! yielded `BytesMut`, but the trailing 2 bytes of the frame head will be
|
||||
//! included. In this case, the length field **includes** the frame head
|
||||
//! length.
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_io::AsyncRead;
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//!
|
||||
//! # fn bind_read<T: AsyncRead>(io: T) {
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_offset(1) // length of hdr1
|
||||
//! .length_field_length(2)
|
||||
//! .length_adjustment(-3) // length of hdr1 + LEN, negative
|
||||
//! .num_skip(3)
|
||||
//! .new_read(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The following frame will be decoded as such:
|
||||
//!
|
||||
//! ```text
|
||||
//! INPUT
|
||||
//! +- hdr1 -+-- len ---+- hdr2 -+--- Payload ---+
|
||||
//! | \xCA | \x00\x0F | \xFE | Hello world |
|
||||
//! +--------+----------+--------+---------------+
|
||||
//!
|
||||
//! DECODED
|
||||
//! +- hdr2 -+--- Payload ---+
|
||||
//! | \xFE | Hello world |
|
||||
//! +--------+---------------+
|
||||
//! ```
|
||||
//!
|
||||
//! Similar to the example above, the difference is that the length field
|
||||
//! represents the length of the entire frame instead of just the payload.
|
||||
//! The length of `hdr1` and `len` must be counted in `length_adjustment`.
|
||||
//! Note that the length of `hdr2` does **not** need to be explicitly set
|
||||
//! anywhere because it already is factored into the total frame length that
|
||||
//! is read from the byte stream.
|
||||
//!
|
||||
//! # Encoding
|
||||
//!
|
||||
//! [`FramedWrite`] adapts an [`AsyncWrite`] into a `Sink` of [`BytesMut`],
|
||||
//! such that each submitted [`BytesMut`] is prefaced by a length field.
|
||||
//! There are fewer configuration options than [`FramedRead`]. Given
|
||||
//! protocols that have more complex frame heads, an encoder should probably
|
||||
//! be written by hand using [`Encoder`].
|
||||
//!
|
||||
//! Here is a simple example, given a `FramedWrite` with the following
|
||||
//! configuration:
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_io::AsyncWrite;
|
||||
//! use tokio_io::codec::length_delimited;
|
||||
//! use bytes::BytesMut;
|
||||
//!
|
||||
//! # fn write_frame<T: AsyncWrite>(io: T) {
|
||||
//! # let _: length_delimited::FramedWrite<T, BytesMut> =
|
||||
//! length_delimited::Builder::new()
|
||||
//! .length_field_length(2)
|
||||
//! .new_write(io);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! A payload of `hello world` will be encoded as:
|
||||
//!
|
||||
//! ```text
|
||||
//! +- len: u16 -+---- data ----+
|
||||
//! | \x00\x0b | hello world |
|
||||
//! +------------+--------------+
|
||||
//! ```
|
||||
//!
|
||||
//! [`FramedRead`]: struct.FramedRead.html
|
||||
//! [`FramedWrite`]: struct.FramedWrite.html
|
||||
//! [`AsyncRead`]: ../../trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: ../../trait.AsyncWrite.html
|
||||
//! [`Encoder`]: ../trait.Encoder.html
|
||||
//! [`BytesMut`]: https://docs.rs/bytes/0.4/bytes/struct.BytesMut.html
|
||||
|
||||
pub use crate::length_delimited::*;
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::codec::{Decoder, Encoder};
|
||||
use crate::framed_read::{framed_read2, framed_read2_with_buffer, FramedRead2};
|
||||
use crate::framed_write::{framed_write2, framed_write2_with_buffer, FramedWrite2};
|
||||
use crate::{AsyncRead, AsyncWrite};
|
||||
use bytes::BytesMut;
|
||||
use futures::{Poll, Sink, StartSend, Stream};
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
|
||||
/// the `Encoder` and `Decoder` traits to encode and decode frames.
|
||||
///
|
||||
/// You can create a `Framed` instance by using the `AsyncRead::framed` adapter.
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct Framed<T, U> {
|
||||
inner: FramedRead2<FramedWrite2<Fuse<T, U>>>,
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct Fuse<T, U>(pub T, pub U);
|
||||
|
||||
pub fn framed<T, U>(inner: T, codec: U) -> Framed<T, U>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite,
|
||||
U: Decoder + Encoder,
|
||||
{
|
||||
Framed {
|
||||
inner: framed_read2(framed_write2(Fuse(inner, codec))),
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Framed<T, U> {
|
||||
/// Provides a `Stream` and `Sink` interface for reading and writing to this
|
||||
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Codec`
|
||||
/// traits to handle encoding and decoding of messages frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
///
|
||||
/// This function returns a *single* object that is both `Stream` and
|
||||
/// `Sink`; grouping this into a single object is often useful for layering
|
||||
/// things like gzip or TLS, which require both read and write access to the
|
||||
/// underlying object.
|
||||
///
|
||||
/// This objects takes a stream and a readbuffer and a writebuffer. These field
|
||||
/// can be obtained from an existing `Framed` with the `into_parts` method.
|
||||
///
|
||||
/// If you want to work more directly with the streams and sink, consider
|
||||
/// calling `split` on the `Framed` returned by this method, which will
|
||||
/// break them into separate objects, allowing them to interact more easily.
|
||||
pub fn from_parts(parts: FramedParts<T>, codec: U) -> Framed<T, U> {
|
||||
Framed {
|
||||
inner: framed_read2_with_buffer(
|
||||
framed_write2_with_buffer(Fuse(parts.inner, codec), parts.writebuf),
|
||||
parts.readbuf,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `Frame`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.get_ref().get_ref().0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `Frame`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.get_mut().get_mut().0
|
||||
}
|
||||
|
||||
/// Consumes the `Frame`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.into_inner().into_inner().0
|
||||
}
|
||||
|
||||
/// Consumes the `Frame`, returning its underlying I/O stream and the buffer
|
||||
/// with unprocessed data.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_parts(self) -> FramedParts<T> {
|
||||
let (inner, readbuf) = self.inner.into_parts();
|
||||
let (inner, writebuf) = inner.into_parts();
|
||||
FramedParts {
|
||||
inner: inner.0,
|
||||
readbuf: readbuf,
|
||||
writebuf: writebuf,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the `Frame`, returning its underlying I/O stream and the buffer
|
||||
/// with unprocessed data, and also the current codec state.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
///
|
||||
/// Note that this function will be removed once the codec has been
|
||||
/// integrated into `FramedParts` in a new version (see
|
||||
/// [#53](https://github.com/tokio-rs/tokio-io/pull/53)).
|
||||
pub fn into_parts_and_codec(self) -> (FramedParts<T>, U) {
|
||||
let (inner, readbuf) = self.inner.into_parts();
|
||||
let (inner, writebuf) = inner.into_parts();
|
||||
(
|
||||
FramedParts {
|
||||
inner: inner.0,
|
||||
readbuf: readbuf,
|
||||
writebuf: writebuf,
|
||||
},
|
||||
inner.1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Stream for Framed<T, U>
|
||||
where
|
||||
T: AsyncRead,
|
||||
U: Decoder,
|
||||
{
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Sink for Framed<T, U>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
U: Encoder,
|
||||
U::Error: From<io::Error>,
|
||||
{
|
||||
type SinkItem = U::Item;
|
||||
type SinkError = U::Error;
|
||||
|
||||
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
|
||||
self.inner.get_mut().start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.get_mut().poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.get_mut().close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> fmt::Debug for Framed<T, U>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Framed")
|
||||
.field("io", &self.inner.get_ref().get_ref().0)
|
||||
.field("codec", &self.inner.get_ref().get_ref().1)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Fuse =====
|
||||
|
||||
impl<T: Read, U> Read for Fuse<T, U> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead, U> AsyncRead for Fuse<T, U> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.0.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write, U> Write for Fuse<T, U> {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(src)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, U> AsyncWrite for Fuse<T, U> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
self.0.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Decoder> Decoder for Fuse<T, U> {
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn decode(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.1.decode(buffer)
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.1.decode_eof(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Encoder> Encoder for Fuse<T, U> {
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
self.1.encode(item, dst)
|
||||
}
|
||||
}
|
||||
|
||||
/// `FramedParts` contains an export of the data of a Framed transport.
|
||||
/// It can be used to construct a new `Framed` with a different codec.
|
||||
/// It contains all current buffers and the inner transport.
|
||||
#[derive(Debug)]
|
||||
pub struct FramedParts<T> {
|
||||
/// The inner transport used to read bytes to and write bytes to
|
||||
pub inner: T,
|
||||
/// The buffer with read but unprocessed data.
|
||||
pub readbuf: BytesMut,
|
||||
/// A buffer with unprocessed data which are not written yet.
|
||||
pub writebuf: BytesMut,
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::codec::Decoder;
|
||||
use crate::framed::Fuse;
|
||||
use crate::AsyncRead;
|
||||
use bytes::BytesMut;
|
||||
use futures::{try_ready, Async, Poll, Sink, StartSend, Stream};
|
||||
use log::trace;
|
||||
use std::fmt;
|
||||
|
||||
/// A `Stream` of messages decoded from an `AsyncRead`.
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct FramedRead<T, D> {
|
||||
inner: FramedRead2<Fuse<T, D>>,
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct FramedRead2<T> {
|
||||
inner: T,
|
||||
eof: bool,
|
||||
is_readable: bool,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
|
||||
// ===== impl FramedRead =====
|
||||
|
||||
impl<T, D> FramedRead<T, D>
|
||||
where
|
||||
T: AsyncRead,
|
||||
D: Decoder,
|
||||
{
|
||||
/// Creates a new `FramedRead` with the given `decoder`.
|
||||
pub fn new(inner: T, decoder: D) -> FramedRead<T, D> {
|
||||
FramedRead {
|
||||
inner: framed_read2(Fuse(inner, decoder)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> FramedRead<T, D> {
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Consumes the `FramedRead`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying decoder.
|
||||
pub fn decoder(&self) -> &D {
|
||||
&self.inner.inner.1
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying decoder.
|
||||
pub fn decoder_mut(&mut self) -> &mut D {
|
||||
&mut self.inner.inner.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Stream for FramedRead<T, D>
|
||||
where
|
||||
T: AsyncRead,
|
||||
D: Decoder,
|
||||
{
|
||||
type Item = D::Item;
|
||||
type Error = D::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Sink for FramedRead<T, D>
|
||||
where
|
||||
T: Sink,
|
||||
{
|
||||
type SinkItem = T::SinkItem;
|
||||
type SinkError = T::SinkError;
|
||||
|
||||
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
|
||||
self.inner.inner.0.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.inner.0.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.inner.0.close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> fmt::Debug for FramedRead<T, D>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
D: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FramedRead")
|
||||
.field("inner", &self.inner.inner.0)
|
||||
.field("decoder", &self.inner.inner.1)
|
||||
.field("eof", &self.inner.eof)
|
||||
.field("is_readable", &self.inner.is_readable)
|
||||
.field("buffer", &self.inner.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedRead2 =====
|
||||
|
||||
pub fn framed_read2<T>(inner: T) -> FramedRead2<T> {
|
||||
FramedRead2 {
|
||||
inner: inner,
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn framed_read2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedRead2<T> {
|
||||
if buf.capacity() < INITIAL_CAPACITY {
|
||||
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
|
||||
buf.reserve(bytes_to_reserve);
|
||||
}
|
||||
FramedRead2 {
|
||||
inner: inner,
|
||||
eof: false,
|
||||
is_readable: buf.len() > 0,
|
||||
buffer: buf,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedRead2<T> {
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (T, BytesMut) {
|
||||
(self.inner, self.buffer)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for FramedRead2<T>
|
||||
where
|
||||
T: AsyncRead + Decoder,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
loop {
|
||||
// Repeatedly call `decode` or `decode_eof` as long as it is
|
||||
// "readable". Readable is defined as not having returned `None`. If
|
||||
// the upstream has returned EOF, and the decoder is no longer
|
||||
// readable, it can be assumed that the decoder will never become
|
||||
// readable again, at which point the stream is terminated.
|
||||
if self.is_readable {
|
||||
if self.eof {
|
||||
let frame = self.inner.decode_eof(&mut self.buffer)?;
|
||||
return Ok(Async::Ready(frame));
|
||||
}
|
||||
|
||||
trace!("attempting to decode a frame");
|
||||
|
||||
if let Some(frame) = self.inner.decode(&mut self.buffer)? {
|
||||
trace!("frame decoded from buffer");
|
||||
return Ok(Async::Ready(Some(frame)));
|
||||
}
|
||||
|
||||
self.is_readable = false;
|
||||
}
|
||||
|
||||
assert!(!self.eof);
|
||||
|
||||
// Otherwise, try to read more data and try again. Make sure we've
|
||||
// got room for at least one byte to read to ensure that we don't
|
||||
// get a spurious 0 that looks like EOF
|
||||
self.buffer.reserve(1);
|
||||
if 0 == try_ready!(self.inner.read_buf(&mut self.buffer)) {
|
||||
self.eof = true;
|
||||
}
|
||||
|
||||
self.is_readable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::codec::{Decoder, Encoder};
|
||||
use crate::framed::Fuse;
|
||||
use crate::{AsyncRead, AsyncWrite};
|
||||
use bytes::BytesMut;
|
||||
use futures::{try_ready, Async, AsyncSink, Poll, Sink, StartSend, Stream};
|
||||
use log::trace;
|
||||
use std::fmt;
|
||||
use std::io::{self, Read};
|
||||
|
||||
/// A `Sink` of frames encoded to an `AsyncWrite`.
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct FramedWrite<T, E> {
|
||||
inner: FramedWrite2<Fuse<T, E>>,
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.1.7", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct FramedWrite2<T> {
|
||||
inner: T,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
|
||||
|
||||
impl<T, E> FramedWrite<T, E>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
E: Encoder,
|
||||
{
|
||||
/// Creates a new `FramedWrite` with the given `encoder`.
|
||||
pub fn new(inner: T, encoder: E) -> FramedWrite<T, E> {
|
||||
FramedWrite {
|
||||
inner: framed_write2(Fuse(inner, encoder)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> FramedWrite<T, E> {
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Consumes the `FramedWrite`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.inner.0
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying decoder.
|
||||
pub fn encoder(&self) -> &E {
|
||||
&self.inner.inner.1
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying decoder.
|
||||
pub fn encoder_mut(&mut self) -> &mut E {
|
||||
&mut self.inner.inner.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Sink for FramedWrite<T, E>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
E: Encoder,
|
||||
{
|
||||
type SinkItem = E::Item;
|
||||
type SinkError = E::Error;
|
||||
|
||||
fn start_send(&mut self, item: E::Item) -> StartSend<E::Item, E::Error> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
Ok(self.inner.close()?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Stream for FramedWrite<T, D>
|
||||
where
|
||||
T: Stream,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
self.inner.inner.0.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> fmt::Debug for FramedWrite<T, U>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FramedWrite")
|
||||
.field("inner", &self.inner.get_ref().0)
|
||||
.field("encoder", &self.inner.get_ref().1)
|
||||
.field("buffer", &self.inner.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedWrite2 =====
|
||||
|
||||
pub fn framed_write2<T>(inner: T) -> FramedWrite2<T> {
|
||||
FramedWrite2 {
|
||||
inner: inner,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn framed_write2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedWrite2<T> {
|
||||
if buf.capacity() < INITIAL_CAPACITY {
|
||||
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
|
||||
buf.reserve(bytes_to_reserve);
|
||||
}
|
||||
FramedWrite2 {
|
||||
inner: inner,
|
||||
buffer: buf,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedWrite2<T> {
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (T, BytesMut) {
|
||||
(self.inner, self.buffer)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sink for FramedWrite2<T>
|
||||
where
|
||||
T: AsyncWrite + Encoder,
|
||||
{
|
||||
type SinkItem = T::Item;
|
||||
type SinkError = T::Error;
|
||||
|
||||
fn start_send(&mut self, item: T::Item) -> StartSend<T::Item, T::Error> {
|
||||
// If the buffer is already over 8KiB, then attempt to flush it. If after flushing it's
|
||||
// *still* over 8KiB, then apply backpressure (reject the send).
|
||||
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
self.poll_complete()?;
|
||||
|
||||
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
return Ok(AsyncSink::NotReady(item));
|
||||
}
|
||||
}
|
||||
|
||||
self.inner.encode(item, &mut self.buffer)?;
|
||||
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
trace!("flushing framed transport");
|
||||
|
||||
while !self.buffer.is_empty() {
|
||||
trace!("writing; remaining={}", self.buffer.len());
|
||||
|
||||
let n = try_ready!(self.inner.poll_write(&self.buffer));
|
||||
|
||||
if n == 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
"failed to
|
||||
write frame to transport",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
// TODO: Add a way to `bytes` to do this w/o returning the drained
|
||||
// data.
|
||||
let _ = self.buffer.split_to(n);
|
||||
}
|
||||
|
||||
// Try flushing the underlying IO
|
||||
try_ready!(self.inner.poll_flush());
|
||||
|
||||
trace!("framed transport flushed");
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
try_ready!(self.poll_complete());
|
||||
Ok(self.inner.shutdown()?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decoder> Decoder for FramedWrite2<T> {
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
|
||||
self.inner.decode(src)
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
|
||||
self.inner.decode_eof(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read> Read for FramedWrite2<T> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for FramedWrite2<T> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.inner.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
use crate::{AsyncRead, AsyncWrite};
|
||||
use futures::{try_ready, Future, Poll};
|
||||
use std::io;
|
||||
|
||||
/// A future which will copy all data from a reader into a writer.
|
||||
///
|
||||
/// Created by the [`copy`] function, this future will resolve to the number of
|
||||
/// bytes copied or an error if one happens.
|
||||
///
|
||||
/// [`copy`]: fn.copy.html
|
||||
#[derive(Debug)]
|
||||
pub struct Copy<R, W> {
|
||||
reader: Option<R>,
|
||||
read_done: bool,
|
||||
writer: Option<W>,
|
||||
pos: usize,
|
||||
cap: usize,
|
||||
amt: u64,
|
||||
buf: Box<[u8]>,
|
||||
}
|
||||
|
||||
/// Creates a future which represents copying all the bytes from one object to
|
||||
/// another.
|
||||
///
|
||||
/// The returned future will copy all the bytes read from `reader` into the
|
||||
/// `writer` specified. This future will only complete once the `reader` has hit
|
||||
/// EOF and all bytes have been written to and flushed from the `writer`
|
||||
/// provided.
|
||||
///
|
||||
/// On success the number of bytes is returned and the `reader` and `writer` are
|
||||
/// consumed. On error the error is returned and the I/O objects are consumed as
|
||||
/// well.
|
||||
pub fn copy<R, W>(reader: R, writer: W) -> Copy<R, W>
|
||||
where
|
||||
R: AsyncRead,
|
||||
W: AsyncWrite,
|
||||
{
|
||||
Copy {
|
||||
reader: Some(reader),
|
||||
read_done: false,
|
||||
writer: Some(writer),
|
||||
amt: 0,
|
||||
pos: 0,
|
||||
cap: 0,
|
||||
buf: Box::new([0; 2048]),
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, W> Future for Copy<R, W>
|
||||
where
|
||||
R: AsyncRead,
|
||||
W: AsyncWrite,
|
||||
{
|
||||
type Item = (u64, R, W);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(u64, R, W), io::Error> {
|
||||
loop {
|
||||
// If our buffer is empty, then we need to read some data to
|
||||
// continue.
|
||||
if self.pos == self.cap && !self.read_done {
|
||||
let reader = self.reader.as_mut().unwrap();
|
||||
let n = try_ready!(reader.poll_read(&mut self.buf));
|
||||
if n == 0 {
|
||||
self.read_done = true;
|
||||
} else {
|
||||
self.pos = 0;
|
||||
self.cap = n;
|
||||
}
|
||||
}
|
||||
|
||||
// If our buffer has some data, let's write it out!
|
||||
while self.pos < self.cap {
|
||||
let writer = self.writer.as_mut().unwrap();
|
||||
let i = try_ready!(writer.poll_write(&self.buf[self.pos..self.cap]));
|
||||
if i == 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
"write zero byte into writer",
|
||||
));
|
||||
} else {
|
||||
self.pos += i;
|
||||
self.amt += i as u64;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've written al the data and we've seen EOF, flush out the
|
||||
// data and finish the transfer.
|
||||
// done with the entire transfer.
|
||||
if self.pos == self.cap && self.read_done {
|
||||
try_ready!(self.writer.as_mut().unwrap().poll_flush());
|
||||
let reader = self.reader.take().unwrap();
|
||||
let writer = self.writer.take().unwrap();
|
||||
return Ok((self.amt, reader, writer).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
use crate::AsyncWrite;
|
||||
use futures::{try_ready, Async, Future, Poll};
|
||||
use std::io;
|
||||
|
||||
/// A future used to fully flush an I/O object.
|
||||
///
|
||||
/// Resolves to the underlying I/O object once the flush operation is complete.
|
||||
///
|
||||
/// Created by the [`flush`] function.
|
||||
///
|
||||
/// [`flush`]: fn.flush.html
|
||||
#[derive(Debug)]
|
||||
pub struct Flush<A> {
|
||||
a: Option<A>,
|
||||
}
|
||||
|
||||
/// Creates a future which will entirely flush an I/O object and then yield the
|
||||
/// object itself.
|
||||
///
|
||||
/// This function will consume the object provided if an error happens, and
|
||||
/// otherwise it will repeatedly call `flush` until it sees `Ok(())`, scheduling
|
||||
/// a retry if `WouldBlock` is seen along the way.
|
||||
pub fn flush<A>(a: A) -> Flush<A>
|
||||
where
|
||||
A: AsyncWrite,
|
||||
{
|
||||
Flush { a: Some(a) }
|
||||
}
|
||||
|
||||
impl<A> Future for Flush<A>
|
||||
where
|
||||
A: AsyncWrite,
|
||||
{
|
||||
type Item = A;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<A, io::Error> {
|
||||
try_ready!(self.a.as_mut().unwrap().poll_flush());
|
||||
Ok(Async::Ready(self.a.take().unwrap()))
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
//! I/O conveniences when working with primitives in `tokio-core`
|
||||
//!
|
||||
//! Contains various combinators to work with I/O objects and type definitions
|
||||
//! as well.
|
||||
//!
|
||||
//! A description of the high-level I/O combinators can be [found online] in
|
||||
//! addition to a description of the [low level details].
|
||||
//!
|
||||
//! [found online]: https://tokio.rs/docs/getting-started/core/
|
||||
//! [low level details]: https://tokio.rs/docs/going-deeper-tokio/core-low-level/
|
||||
|
||||
mod copy;
|
||||
mod flush;
|
||||
mod read;
|
||||
mod read_exact;
|
||||
mod read_to_end;
|
||||
mod read_until;
|
||||
mod shutdown;
|
||||
mod write_all;
|
||||
|
||||
pub use self::copy::{copy, Copy};
|
||||
pub use self::flush::{flush, Flush};
|
||||
pub use self::read::{read, Read};
|
||||
pub use self::read_exact::{read_exact, ReadExact};
|
||||
pub use self::read_to_end::{read_to_end, ReadToEnd};
|
||||
pub use self::read_until::{read_until, ReadUntil};
|
||||
pub use self::shutdown::{shutdown, Shutdown};
|
||||
pub use self::write_all::{write_all, WriteAll};
|
||||
pub use crate::allow_std::AllowStdIo;
|
||||
pub use crate::lines::{lines, Lines};
|
||||
pub use crate::split::{ReadHalf, WriteHalf};
|
||||
pub use crate::window::Window;
|
||||
@@ -1,58 +0,0 @@
|
||||
use crate::AsyncRead;
|
||||
use futures::{try_ready, Future, Poll};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<R, T> {
|
||||
Pending { rd: R, buf: T },
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Tries to read some bytes directly into the given `buf` in asynchronous
|
||||
/// manner, returning a future type.
|
||||
///
|
||||
/// The returned future will resolve to both the I/O stream and the buffer
|
||||
/// as well as the number of bytes read once the read operation is completed.
|
||||
pub fn read<R, T>(rd: R, buf: T) -> Read<R, T>
|
||||
where
|
||||
R: AsyncRead,
|
||||
T: AsMut<[u8]>,
|
||||
{
|
||||
Read {
|
||||
state: State::Pending { rd: rd, buf: buf },
|
||||
}
|
||||
}
|
||||
|
||||
/// A future which can be used to easily read available number of bytes to fill
|
||||
/// a buffer.
|
||||
///
|
||||
/// Created by the [`read`] function.
|
||||
#[derive(Debug)]
|
||||
pub struct Read<R, T> {
|
||||
state: State<R, T>,
|
||||
}
|
||||
|
||||
impl<R, T> Future for Read<R, T>
|
||||
where
|
||||
R: AsyncRead,
|
||||
T: AsMut<[u8]>,
|
||||
{
|
||||
type Item = (R, T, usize);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(R, T, usize), io::Error> {
|
||||
let nread = match self.state {
|
||||
State::Pending {
|
||||
ref mut rd,
|
||||
ref mut buf,
|
||||
} => try_ready!(rd.poll_read(&mut buf.as_mut()[..])),
|
||||
State::Empty => panic!("poll a Read after it's done"),
|
||||
};
|
||||
|
||||
match mem::replace(&mut self.state, State::Empty) {
|
||||
State::Pending { rd, buf } => Ok((rd, buf, nread).into()),
|
||||
State::Empty => panic!("invalid internal state"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
use crate::AsyncRead;
|
||||
use futures::{try_ready, Future, Poll};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
/// A future which can be used to easily read exactly enough bytes to fill
|
||||
/// a buffer.
|
||||
///
|
||||
/// Created by the [`read_exact`] function.
|
||||
///
|
||||
/// [`read_exact`]: fn.read_exact.html
|
||||
#[derive(Debug)]
|
||||
pub struct ReadExact<A, T> {
|
||||
state: State<A, T>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<A, T> {
|
||||
Reading { a: A, buf: T, pos: usize },
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Creates a future which will read exactly enough bytes to fill `buf`,
|
||||
/// returning an error if EOF is hit sooner.
|
||||
///
|
||||
/// The returned future will resolve to both the I/O stream as well as the
|
||||
/// buffer once the read operation is completed.
|
||||
///
|
||||
/// In the case of an error the buffer and the object will be discarded, with
|
||||
/// the error yielded. In the case of success the object will be destroyed and
|
||||
/// the buffer will be returned, with all data read from the stream appended to
|
||||
/// the buffer.
|
||||
pub fn read_exact<A, T>(a: A, buf: T) -> ReadExact<A, T>
|
||||
where
|
||||
A: AsyncRead,
|
||||
T: AsMut<[u8]>,
|
||||
{
|
||||
ReadExact {
|
||||
state: State::Reading {
|
||||
a: a,
|
||||
buf: buf,
|
||||
pos: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn eof() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::UnexpectedEof, "early eof")
|
||||
}
|
||||
|
||||
impl<A, T> Future for ReadExact<A, T>
|
||||
where
|
||||
A: AsyncRead,
|
||||
T: AsMut<[u8]>,
|
||||
{
|
||||
type Item = (A, T);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(A, T), io::Error> {
|
||||
match self.state {
|
||||
State::Reading {
|
||||
ref mut a,
|
||||
ref mut buf,
|
||||
ref mut pos,
|
||||
} => {
|
||||
let buf = buf.as_mut();
|
||||
while *pos < buf.len() {
|
||||
let n = try_ready!(a.poll_read(&mut buf[*pos..]));
|
||||
*pos += n;
|
||||
if n == 0 {
|
||||
return Err(eof());
|
||||
}
|
||||
}
|
||||
}
|
||||
State::Empty => panic!("poll a ReadExact after it's done"),
|
||||
}
|
||||
|
||||
match mem::replace(&mut self.state, State::Empty) {
|
||||
State::Reading { a, buf, .. } => Ok((a, buf).into()),
|
||||
State::Empty => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
use crate::AsyncRead;
|
||||
use futures::{Future, Poll};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
/// A future which can be used to easily read the entire contents of a stream
|
||||
/// into a vector.
|
||||
///
|
||||
/// Created by the [`read_to_end`] function.
|
||||
///
|
||||
/// [`read_to_end`]: fn.read_to_end.html
|
||||
#[derive(Debug)]
|
||||
pub struct ReadToEnd<A> {
|
||||
state: State<A>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<A> {
|
||||
Reading { a: A, buf: Vec<u8> },
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Creates a future which will read all the bytes associated with the I/O
|
||||
/// object `A` into the buffer provided.
|
||||
///
|
||||
/// In the case of an error the buffer and the object will be discarded, with
|
||||
/// the error yielded. In the case of success both the object and the buffer
|
||||
/// will be returned, with all data read from the stream appended to the buffer.
|
||||
pub fn read_to_end<A>(a: A, buf: Vec<u8>) -> ReadToEnd<A>
|
||||
where
|
||||
A: AsyncRead,
|
||||
{
|
||||
ReadToEnd {
|
||||
state: State::Reading { a: a, buf: buf },
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Future for ReadToEnd<A>
|
||||
where
|
||||
A: AsyncRead,
|
||||
{
|
||||
type Item = (A, Vec<u8>);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(A, Vec<u8>), io::Error> {
|
||||
match self.state {
|
||||
State::Reading {
|
||||
ref mut a,
|
||||
ref mut buf,
|
||||
} => {
|
||||
// If we get `Ok`, then we know the stream hit EOF and we're done. If we
|
||||
// hit "would block" then all the read data so far is in our buffer, and
|
||||
// otherwise we propagate errors
|
||||
try_nb!(a.read_to_end(buf));
|
||||
}
|
||||
State::Empty => panic!("poll ReadToEnd after it's done"),
|
||||
}
|
||||
|
||||
match mem::replace(&mut self.state, State::Empty) {
|
||||
State::Reading { a, buf } => Ok((a, buf).into()),
|
||||
State::Empty => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
use crate::AsyncRead;
|
||||
use futures::{Future, Poll};
|
||||
use std::io::{self, BufRead};
|
||||
use std::mem;
|
||||
|
||||
/// A future which can be used to easily read the contents of a stream into a
|
||||
/// vector until the delimiter is reached.
|
||||
///
|
||||
/// Created by the [`read_until`] function.
|
||||
///
|
||||
/// [`read_until`]: fn.read_until.html
|
||||
#[derive(Debug)]
|
||||
pub struct ReadUntil<A> {
|
||||
state: State<A>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<A> {
|
||||
Reading { a: A, byte: u8, buf: Vec<u8> },
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Creates a future which will read all the bytes associated with the I/O
|
||||
/// object `A` into the buffer provided until the delimiter `byte` is reached.
|
||||
/// This method is the async equivalent to [`BufRead::read_until`].
|
||||
///
|
||||
/// In case of an error the buffer and the object will be discarded, with
|
||||
/// the error yielded. In the case of success the object will be destroyed and
|
||||
/// the buffer will be returned, with all bytes up to, and including, the delimiter
|
||||
/// (if found).
|
||||
///
|
||||
/// [`BufRead::read_until`]: https://doc.rust-lang.org/std/io/trait.BufRead.html#method.read_until
|
||||
pub fn read_until<A>(a: A, byte: u8, buf: Vec<u8>) -> ReadUntil<A>
|
||||
where
|
||||
A: AsyncRead + BufRead,
|
||||
{
|
||||
ReadUntil {
|
||||
state: State::Reading {
|
||||
a: a,
|
||||
byte: byte,
|
||||
buf: buf,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Future for ReadUntil<A>
|
||||
where
|
||||
A: AsyncRead + BufRead,
|
||||
{
|
||||
type Item = (A, Vec<u8>);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(A, Vec<u8>), io::Error> {
|
||||
match self.state {
|
||||
State::Reading {
|
||||
ref mut a,
|
||||
byte,
|
||||
ref mut buf,
|
||||
} => {
|
||||
// If we get `Ok(n)`, then we know the stream hit EOF or the delimiter.
|
||||
// and just return it, as we are finished.
|
||||
// If we hit "would block" then all the read data so far
|
||||
// is in our buffer, and otherwise we propagate errors.
|
||||
try_nb!(a.read_until(byte, buf));
|
||||
}
|
||||
State::Empty => panic!("poll ReadUntil after it's done"),
|
||||
}
|
||||
|
||||
match mem::replace(&mut self.state, State::Empty) {
|
||||
State::Reading { a, byte: _, buf } => Ok((a, buf).into()),
|
||||
State::Empty => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
use crate::AsyncWrite;
|
||||
use futures::{try_ready, Async, Future, Poll};
|
||||
use std::io;
|
||||
|
||||
/// A future used to fully shutdown an I/O object.
|
||||
///
|
||||
/// Resolves to the underlying I/O object once the shutdown operation is
|
||||
/// complete.
|
||||
///
|
||||
/// Created by the [`shutdown`] function.
|
||||
///
|
||||
/// [`shutdown`]: fn.shutdown.html
|
||||
#[derive(Debug)]
|
||||
pub struct Shutdown<A> {
|
||||
a: Option<A>,
|
||||
}
|
||||
|
||||
/// Creates a future which will entirely shutdown an I/O object and then yield
|
||||
/// the object itself.
|
||||
///
|
||||
/// This function will consume the object provided if an error happens, and
|
||||
/// otherwise it will repeatedly call `shutdown` until it sees `Ok(())`,
|
||||
/// scheduling a retry if `WouldBlock` is seen along the way.
|
||||
pub fn shutdown<A>(a: A) -> Shutdown<A>
|
||||
where
|
||||
A: AsyncWrite,
|
||||
{
|
||||
Shutdown { a: Some(a) }
|
||||
}
|
||||
|
||||
impl<A> Future for Shutdown<A>
|
||||
where
|
||||
A: AsyncWrite,
|
||||
{
|
||||
type Item = A;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<A, io::Error> {
|
||||
try_ready!(self.a.as_mut().unwrap().shutdown());
|
||||
Ok(Async::Ready(self.a.take().unwrap()))
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
use crate::AsyncWrite;
|
||||
use futures::{try_ready, Future, Poll};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
/// A future used to write the entire contents of some data to a stream.
|
||||
///
|
||||
/// This is created by the [`write_all`] top-level method.
|
||||
///
|
||||
/// [`write_all`]: fn.write_all.html
|
||||
#[derive(Debug)]
|
||||
pub struct WriteAll<A, T> {
|
||||
state: State<A, T>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State<A, T> {
|
||||
Writing { a: A, buf: T, pos: usize },
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Creates a future that will write the entire contents of the buffer `buf` to
|
||||
/// the stream `a` provided.
|
||||
///
|
||||
/// The returned future will not return until all the data has been written, and
|
||||
/// the future will resolve to the stream as well as the buffer (for reuse if
|
||||
/// needed).
|
||||
///
|
||||
/// Any error which happens during writing will cause both the stream and the
|
||||
/// buffer to get destroyed.
|
||||
///
|
||||
/// The `buf` parameter here only requires the `AsRef<[u8]>` trait, which should
|
||||
/// be broadly applicable to accepting data which can be converted to a slice.
|
||||
/// The `Window` struct is also available in this crate to provide a different
|
||||
/// window into a slice if necessary.
|
||||
pub fn write_all<A, T>(a: A, buf: T) -> WriteAll<A, T>
|
||||
where
|
||||
A: AsyncWrite,
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
WriteAll {
|
||||
state: State::Writing {
|
||||
a: a,
|
||||
buf: buf,
|
||||
pos: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn zero_write() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::WriteZero, "zero-length write")
|
||||
}
|
||||
|
||||
impl<A, T> Future for WriteAll<A, T>
|
||||
where
|
||||
A: AsyncWrite,
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
type Item = (A, T);
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(A, T), io::Error> {
|
||||
match self.state {
|
||||
State::Writing {
|
||||
ref mut a,
|
||||
ref buf,
|
||||
ref mut pos,
|
||||
} => {
|
||||
let buf = buf.as_ref();
|
||||
while *pos < buf.len() {
|
||||
let n = try_ready!(a.poll_write(&buf[*pos..]));
|
||||
*pos += n;
|
||||
if n == 0 {
|
||||
return Err(zero_write());
|
||||
}
|
||||
}
|
||||
}
|
||||
State::Empty => panic!("poll a WriteAll after it's done"),
|
||||
}
|
||||
|
||||
match mem::replace(&mut self.state, State::Empty) {
|
||||
State::Writing { a, buf, .. } => Ok((a, buf).into()),
|
||||
State::Empty => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,936 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::{codec, AsyncRead, AsyncWrite};
|
||||
use bytes::buf::Chain;
|
||||
use bytes::{Buf, BufMut, BytesMut, IntoBuf};
|
||||
use futures::{try_ready, Async, AsyncSink, Poll, Sink, StartSend, Stream};
|
||||
use std::error::Error as StdError;
|
||||
use std::io::{self, Cursor};
|
||||
use std::{cmp, fmt};
|
||||
|
||||
/// Configure length delimited `FramedRead`, `FramedWrite`, and `Framed` values.
|
||||
///
|
||||
/// `Builder` enables constructing configured length delimited framers. Note
|
||||
/// that not all configuration settings apply to both encoding and decoding. See
|
||||
/// the documentation for specific methods for more detail.
|
||||
#[deprecated(since = "0.1.8", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Builder {
|
||||
// Maximum frame length
|
||||
max_frame_len: usize,
|
||||
|
||||
// Number of bytes representing the field length
|
||||
length_field_len: usize,
|
||||
|
||||
// Number of bytes in the header before the length field
|
||||
length_field_offset: usize,
|
||||
|
||||
// Adjust the length specified in the header field by this amount
|
||||
length_adjustment: isize,
|
||||
|
||||
// Total number of bytes to skip before reading the payload, if not set,
|
||||
// `length_field_len + length_field_offset`
|
||||
num_skip: Option<usize>,
|
||||
|
||||
// Length field byte order (little or big endian)
|
||||
length_field_is_big_endian: bool,
|
||||
}
|
||||
|
||||
/// Adapts a byte stream into a unified `Stream` and `Sink` that works over
|
||||
/// entire frame values.
|
||||
///
|
||||
/// See [module level] documentation for more detail.
|
||||
///
|
||||
/// [module level]: index.html
|
||||
#[deprecated(since = "0.1.8", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct Framed<T, B: IntoBuf = BytesMut> {
|
||||
inner: FramedRead<FramedWrite<T, B>>,
|
||||
}
|
||||
|
||||
/// Adapts a byte stream to a `Stream` yielding entire frame values.
|
||||
///
|
||||
/// See [module level] documentation for more detail.
|
||||
///
|
||||
/// [module level]: index.html
|
||||
#[deprecated(since = "0.1.8", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug)]
|
||||
pub struct FramedRead<T> {
|
||||
inner: codec::FramedRead<T, Decoder>,
|
||||
}
|
||||
|
||||
/// An error when the number of bytes read is more than max frame length.
|
||||
#[deprecated(since = "0.1.8", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct FrameTooBig {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Decoder {
|
||||
// Configuration values
|
||||
builder: Builder,
|
||||
|
||||
// Read state
|
||||
state: DecodeState,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum DecodeState {
|
||||
Head,
|
||||
Data(usize),
|
||||
}
|
||||
|
||||
/// Adapts a byte stream to a `Sink` accepting entire frame values.
|
||||
///
|
||||
/// See [module level] documentation for more detail.
|
||||
///
|
||||
/// [module level]: index.html
|
||||
#[deprecated(since = "0.1.8", note = "Moved to tokio-codec")]
|
||||
#[doc(hidden)]
|
||||
pub struct FramedWrite<T, B: IntoBuf = BytesMut> {
|
||||
// I/O type
|
||||
inner: T,
|
||||
|
||||
// Configuration values
|
||||
builder: Builder,
|
||||
|
||||
// Current frame being written
|
||||
frame: Option<Chain<Cursor<BytesMut>, B::Buf>>,
|
||||
}
|
||||
|
||||
// ===== impl Framed =====
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, B: IntoBuf> Framed<T, B> {
|
||||
/// Creates a new `Framed` with default configuration values.
|
||||
pub fn new(inner: T) -> Framed<T, B> {
|
||||
Builder::new().new_framed(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B: IntoBuf> Framed<T, B> {
|
||||
/// Returns a reference to the underlying I/O stream wrapped by `Framed`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
self.inner.get_ref().get_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `Framed`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.inner.get_mut().get_mut()
|
||||
}
|
||||
|
||||
/// Consumes the `Framed`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.into_inner().into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead, B: IntoBuf> Stream for Framed<T, B> {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<BytesMut>, io::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, B: IntoBuf> Sink for Framed<T, B> {
|
||||
type SinkItem = B;
|
||||
type SinkError = io::Error;
|
||||
|
||||
fn start_send(&mut self, item: B) -> StartSend<B, io::Error> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), io::Error> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), io::Error> {
|
||||
self.inner.close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B: IntoBuf> fmt::Debug for Framed<T, B>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
B::Buf: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Framed")
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedRead =====
|
||||
|
||||
impl<T: AsyncRead> FramedRead<T> {
|
||||
/// Creates a new `FramedRead` with default configuration values.
|
||||
pub fn new(inner: T) -> FramedRead<T> {
|
||||
Builder::new().new_read(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedRead<T> {
|
||||
/// Returns the current max frame setting
|
||||
///
|
||||
/// This is the largest size this codec will accept from the wire. Larger
|
||||
/// frames will be rejected.
|
||||
pub fn max_frame_length(&self) -> usize {
|
||||
self.inner.decoder().builder.max_frame_len
|
||||
}
|
||||
|
||||
/// Updates the max frame setting.
|
||||
///
|
||||
/// The change takes effect the next time a frame is decoded. In other
|
||||
/// words, if a frame is currently in process of being decoded with a frame
|
||||
/// size greater than `val` but less than the max frame length in effect
|
||||
/// before calling this function, then the frame will be allowed.
|
||||
pub fn set_max_frame_length(&mut self, val: usize) {
|
||||
self.inner.decoder_mut().builder.max_frame_length(val);
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying I/O stream wrapped by `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
self.inner.get_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedRead`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
|
||||
/// Consumes the `FramedRead`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> Stream for FramedRead<T> {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<BytesMut>, io::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sink> Sink for FramedRead<T> {
|
||||
type SinkItem = T::SinkItem;
|
||||
type SinkError = T::SinkError;
|
||||
|
||||
fn start_send(&mut self, item: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> {
|
||||
self.inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
||||
self.inner.poll_complete()
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), T::SinkError> {
|
||||
self.inner.close()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Write> io::Write for FramedRead<T> {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
self.inner.get_mut().write(src)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.get_mut().flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite> AsyncWrite for FramedRead<T> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
self.inner.get_mut().shutdown()
|
||||
}
|
||||
|
||||
fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
||||
self.inner.get_mut().write_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Decoder ======
|
||||
|
||||
impl Decoder {
|
||||
fn decode_head(&mut self, src: &mut BytesMut) -> io::Result<Option<usize>> {
|
||||
let head_len = self.builder.num_head_bytes();
|
||||
let field_len = self.builder.length_field_len;
|
||||
|
||||
if src.len() < head_len {
|
||||
// Not enough data
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let n = {
|
||||
let mut src = Cursor::new(&mut *src);
|
||||
|
||||
// Skip the required bytes
|
||||
src.advance(self.builder.length_field_offset);
|
||||
|
||||
// match endianess
|
||||
let n = if self.builder.length_field_is_big_endian {
|
||||
src.get_uint_be(field_len)
|
||||
} else {
|
||||
src.get_uint_le(field_len)
|
||||
};
|
||||
|
||||
if n > self.builder.max_frame_len as u64 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
FrameTooBig { _priv: () },
|
||||
));
|
||||
}
|
||||
|
||||
// The check above ensures there is no overflow
|
||||
let n = n as usize;
|
||||
|
||||
// Adjust `n` with bounds checking
|
||||
let n = if self.builder.length_adjustment < 0 {
|
||||
n.checked_sub(-self.builder.length_adjustment as usize)
|
||||
} else {
|
||||
n.checked_add(self.builder.length_adjustment as usize)
|
||||
};
|
||||
|
||||
// Error handling
|
||||
match n {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"provided length would overflow after adjustment",
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let num_skip = self.builder.get_num_skip();
|
||||
|
||||
if num_skip > 0 {
|
||||
let _ = src.split_to(num_skip);
|
||||
}
|
||||
|
||||
// Ensure that the buffer has enough space to read the incoming
|
||||
// payload
|
||||
src.reserve(n);
|
||||
|
||||
return Ok(Some(n));
|
||||
}
|
||||
|
||||
fn decode_data(&self, n: usize, src: &mut BytesMut) -> io::Result<Option<BytesMut>> {
|
||||
// At this point, the buffer has already had the required capacity
|
||||
// reserved. All there is to do is read.
|
||||
if src.len() < n {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(Some(src.split_to(n)))
|
||||
}
|
||||
}
|
||||
|
||||
impl codec::Decoder for Decoder {
|
||||
type Item = BytesMut;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> io::Result<Option<BytesMut>> {
|
||||
let n = match self.state {
|
||||
DecodeState::Head => match self.decode_head(src)? {
|
||||
Some(n) => {
|
||||
self.state = DecodeState::Data(n);
|
||||
n
|
||||
}
|
||||
None => return Ok(None),
|
||||
},
|
||||
DecodeState::Data(n) => n,
|
||||
};
|
||||
|
||||
match self.decode_data(n, src)? {
|
||||
Some(data) => {
|
||||
// Update the decode state
|
||||
self.state = DecodeState::Head;
|
||||
|
||||
// Make sure the buffer has enough space to read the next head
|
||||
src.reserve(self.builder.num_head_bytes());
|
||||
|
||||
Ok(Some(data))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedWrite =====
|
||||
|
||||
impl<T: AsyncWrite, B: IntoBuf> FramedWrite<T, B> {
|
||||
/// Creates a new `FramedWrite` with default configuration values.
|
||||
pub fn new(inner: T) -> FramedWrite<T, B> {
|
||||
Builder::new().new_write(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B: IntoBuf> FramedWrite<T, B> {
|
||||
/// Returns the current max frame setting
|
||||
///
|
||||
/// This is the largest size this codec will write to the wire. Larger
|
||||
/// frames will be rejected.
|
||||
pub fn max_frame_length(&self) -> usize {
|
||||
self.builder.max_frame_len
|
||||
}
|
||||
|
||||
/// Updates the max frame setting.
|
||||
///
|
||||
/// The change takes effect the next time a frame is encoded. In other
|
||||
/// words, if a frame is currently in process of being encoded with a frame
|
||||
/// size greater than `val` but less than the max frame length in effect
|
||||
/// before calling this function, then the frame will be allowed.
|
||||
pub fn set_max_frame_length(&mut self, val: usize) {
|
||||
self.builder.max_frame_length(val);
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
/// `FramedWrite`.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Consumes the `FramedWrite`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise being
|
||||
/// worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, B: IntoBuf> FramedWrite<T, B> {
|
||||
// If there is a buffered frame, try to write it to `T`
|
||||
fn do_write(&mut self) -> Poll<(), io::Error> {
|
||||
if self.frame.is_none() {
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
|
||||
loop {
|
||||
let frame = self.frame.as_mut().unwrap();
|
||||
if try_ready!(self.inner.write_buf(frame)) == 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
"failed to write frame to transport",
|
||||
));
|
||||
}
|
||||
|
||||
if !frame.has_remaining() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.frame = None;
|
||||
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
|
||||
fn set_frame(&mut self, buf: B::Buf) -> io::Result<()> {
|
||||
let mut head = BytesMut::with_capacity(8);
|
||||
let n = buf.remaining();
|
||||
|
||||
if n > self.builder.max_frame_len {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
FrameTooBig { _priv: () },
|
||||
));
|
||||
}
|
||||
|
||||
// Adjust `n` with bounds checking
|
||||
let n = if self.builder.length_adjustment < 0 {
|
||||
n.checked_add(-self.builder.length_adjustment as usize)
|
||||
} else {
|
||||
n.checked_sub(self.builder.length_adjustment as usize)
|
||||
};
|
||||
|
||||
// Error handling
|
||||
let n = match n {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"provided length would overflow after adjustment",
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if self.builder.length_field_is_big_endian {
|
||||
head.put_uint_be(n as u64, self.builder.length_field_len);
|
||||
} else {
|
||||
head.put_uint_le(n as u64, self.builder.length_field_len);
|
||||
}
|
||||
|
||||
debug_assert!(self.frame.is_none());
|
||||
|
||||
self.frame = Some(head.into_buf().chain(buf));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, B: IntoBuf> Sink for FramedWrite<T, B> {
|
||||
type SinkItem = B;
|
||||
type SinkError = io::Error;
|
||||
|
||||
fn start_send(&mut self, item: B) -> StartSend<B, io::Error> {
|
||||
if !self.do_write()?.is_ready() {
|
||||
return Ok(AsyncSink::NotReady(item));
|
||||
}
|
||||
|
||||
self.set_frame(item.into_buf())?;
|
||||
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), io::Error> {
|
||||
// Write any buffered frame to T
|
||||
try_ready!(self.do_write());
|
||||
|
||||
// Try flushing the underlying IO
|
||||
try_ready!(self.inner.poll_flush());
|
||||
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), io::Error> {
|
||||
try_ready!(self.poll_complete());
|
||||
self.inner.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stream, B: IntoBuf> Stream for FramedWrite<T, B> {
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Read, B: IntoBuf> io::Read for FramedWrite<T, B> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.get_mut().read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead, U: IntoBuf> AsyncRead for FramedWrite<T, U> {
|
||||
fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
||||
self.get_mut().read_buf(buf)
|
||||
}
|
||||
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.get_ref().prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B: IntoBuf> fmt::Debug for FramedWrite<T, B>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
B::Buf: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FramedWrite")
|
||||
.field("inner", &self.inner)
|
||||
.field("builder", &self.builder)
|
||||
.field("frame", &self.frame)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Builder =====
|
||||
|
||||
impl Builder {
|
||||
/// Creates a new length delimited framer builder with default configuration
|
||||
/// values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_offset(0)
|
||||
/// .length_field_length(2)
|
||||
/// .length_adjustment(0)
|
||||
/// .num_skip(0)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new() -> Builder {
|
||||
Builder {
|
||||
// Default max frame length of 8MB
|
||||
max_frame_len: 8 * 1_024 * 1_024,
|
||||
|
||||
// Default byte length of 4
|
||||
length_field_len: 4,
|
||||
|
||||
// Default to the header field being at the start of the header.
|
||||
length_field_offset: 0,
|
||||
|
||||
length_adjustment: 0,
|
||||
|
||||
// Total number of bytes to skip before reading the payload, if not set,
|
||||
// `length_field_len + length_field_offset`
|
||||
num_skip: None,
|
||||
|
||||
// Default to reading the length field in network (big) endian.
|
||||
length_field_is_big_endian: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the length field as a big endian integer
|
||||
///
|
||||
/// This is the default setting.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .big_endian()
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn big_endian(&mut self) -> &mut Self {
|
||||
self.length_field_is_big_endian = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Read the length field as a little endian integer
|
||||
///
|
||||
/// The default setting is big endian.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .little_endian()
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn little_endian(&mut self) -> &mut Self {
|
||||
self.length_field_is_big_endian = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Read the length field as a native endian integer
|
||||
///
|
||||
/// The default setting is big endian.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .native_endian()
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn native_endian(&mut self) -> &mut Self {
|
||||
if cfg!(target_endian = "big") {
|
||||
self.big_endian()
|
||||
} else {
|
||||
self.little_endian()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the max frame length
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding. The
|
||||
/// default value is 8MB.
|
||||
///
|
||||
/// When decoding, the length field read from the byte stream is checked
|
||||
/// against this setting **before** any adjustments are applied. When
|
||||
/// encoding, the length of the submitted payload is checked against this
|
||||
/// setting.
|
||||
///
|
||||
/// When frames exceed the max length, an `io::Error` with the custom value
|
||||
/// of the `FrameTooBig` type will be returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .max_frame_length(8 * 1024)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn max_frame_length(&mut self, val: usize) -> &mut Self {
|
||||
self.max_frame_len = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the number of bytes used to represent the length field
|
||||
///
|
||||
/// The default value is `4`. The max value is `8`.
|
||||
///
|
||||
/// This configuration option applies to both encoding and decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_length(4)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn length_field_length(&mut self, val: usize) -> &mut Self {
|
||||
assert!(val > 0 && val <= 8, "invalid length field length");
|
||||
self.length_field_len = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the number of bytes in the header before the length field
|
||||
///
|
||||
/// This configuration option only applies to decoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_offset(1)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn length_field_offset(&mut self, val: usize) -> &mut Self {
|
||||
self.length_field_offset = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Delta between the payload length specified in the header and the real
|
||||
/// payload length
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_adjustment(-2)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn length_adjustment(&mut self, val: isize) -> &mut Self {
|
||||
self.length_adjustment = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the number of bytes to skip before reading the payload
|
||||
///
|
||||
/// Default value is `length_field_len + length_field_offset`
|
||||
///
|
||||
/// This configuration option only applies to decoding
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .num_skip(4)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn num_skip(&mut self, val: usize) -> &mut Self {
|
||||
self.num_skip = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a configured length delimited `FramedRead`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio_io::AsyncRead;
|
||||
/// use tokio_io::codec::length_delimited::Builder;
|
||||
///
|
||||
/// # fn bind_read<T: AsyncRead>(io: T) {
|
||||
/// Builder::new()
|
||||
/// .length_field_offset(0)
|
||||
/// .length_field_length(2)
|
||||
/// .length_adjustment(0)
|
||||
/// .num_skip(0)
|
||||
/// .new_read(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new_read<T>(&self, upstream: T) -> FramedRead<T>
|
||||
where
|
||||
T: AsyncRead,
|
||||
{
|
||||
FramedRead {
|
||||
inner: codec::FramedRead::new(
|
||||
upstream,
|
||||
Decoder {
|
||||
builder: *self,
|
||||
state: DecodeState::Head,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a configured length delimited `FramedWrite`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio_io::AsyncWrite;
|
||||
/// use tokio_io::codec::length_delimited;
|
||||
/// use bytes::BytesMut;
|
||||
///
|
||||
/// # fn write_frame<T: AsyncWrite>(io: T) {
|
||||
/// # let _: length_delimited::FramedWrite<T, BytesMut> =
|
||||
/// length_delimited::Builder::new()
|
||||
/// .length_field_length(2)
|
||||
/// .new_write(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new_write<T, B>(&self, inner: T) -> FramedWrite<T, B>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
B: IntoBuf,
|
||||
{
|
||||
FramedWrite {
|
||||
inner: inner,
|
||||
builder: *self,
|
||||
frame: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a configured length delimited `Framed`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio_io::{AsyncRead, AsyncWrite};
|
||||
/// use tokio_io::codec::length_delimited;
|
||||
/// use bytes::BytesMut;
|
||||
///
|
||||
/// # fn write_frame<T: AsyncRead + AsyncWrite>(io: T) {
|
||||
/// # let _: length_delimited::Framed<T, BytesMut> =
|
||||
/// length_delimited::Builder::new()
|
||||
/// .length_field_length(2)
|
||||
/// .new_framed(io);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new_framed<T, B>(&self, inner: T) -> Framed<T, B>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite,
|
||||
B: IntoBuf,
|
||||
{
|
||||
let inner = self.new_read(self.new_write(inner));
|
||||
Framed { inner: inner }
|
||||
}
|
||||
|
||||
fn num_head_bytes(&self) -> usize {
|
||||
let num = self.length_field_offset + self.length_field_len;
|
||||
cmp::max(num, self.num_skip.unwrap_or(0))
|
||||
}
|
||||
|
||||
fn get_num_skip(&self) -> usize {
|
||||
self.num_skip
|
||||
.unwrap_or(self.length_field_offset + self.length_field_len)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FrameTooBig =====
|
||||
|
||||
impl fmt::Debug for FrameTooBig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FrameTooBig").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FrameTooBig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for FrameTooBig {
|
||||
fn description(&self) -> &str {
|
||||
"frame size too big"
|
||||
}
|
||||
}
|
||||
+4
-40
@@ -2,6 +2,7 @@
|
||||
#![deny(missing_debug_implementations, missing_docs, rust_2018_idioms)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]
|
||||
//#![feature(async_await)]
|
||||
|
||||
//! Core I/O traits and combinators when working with Tokio.
|
||||
//!
|
||||
@@ -11,54 +12,17 @@
|
||||
//! [found online]: https://tokio.rs/docs/getting-started/core/
|
||||
//! [low level details]: https://tokio.rs/docs/going-deeper-tokio/core-low-level/
|
||||
|
||||
use futures::{Future, Stream};
|
||||
use std::io as std_io;
|
||||
|
||||
/// A convenience typedef around a `Future` whose error component is `io::Error`
|
||||
pub type IoFuture<T> = Box<dyn Future<Item = T, Error = std_io::Error> + Send>;
|
||||
|
||||
/// A convenience typedef around a `Stream` whose error component is `io::Error`
|
||||
pub type IoStream<T> = Box<dyn Stream<Item = T, Error = std_io::Error> + Send>;
|
||||
|
||||
/// A convenience macro for working with `io::Result<T>` from the `Read` and
|
||||
/// `Write` traits.
|
||||
///
|
||||
/// This macro takes `io::Result<T>` as input, and returns `T` as the output. If
|
||||
/// the input type is of the `Err` variant, then `Poll::NotReady` is returned if
|
||||
/// it indicates `WouldBlock` or otherwise `Err` is returned.
|
||||
#[macro_export]
|
||||
macro_rules! try_nb {
|
||||
macro_rules! ready {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(t) => t,
|
||||
Err(ref e) if e.kind() == ::std::io::ErrorKind::WouldBlock => {
|
||||
return Ok(::futures::Async::NotReady);
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
::std::task::Poll::Ready(t) => t,
|
||||
::std::task::Poll::Pending => return ::std::task::Poll::Pending,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod codec;
|
||||
pub mod io;
|
||||
|
||||
pub mod _tokio_codec;
|
||||
mod allow_std;
|
||||
mod async_read;
|
||||
mod async_write;
|
||||
mod framed;
|
||||
mod framed_read;
|
||||
mod framed_write;
|
||||
mod length_delimited;
|
||||
mod lines;
|
||||
mod split;
|
||||
mod window;
|
||||
|
||||
pub use self::async_read::AsyncRead;
|
||||
pub use self::async_write::AsyncWrite;
|
||||
|
||||
fn _assert_objects() {
|
||||
fn _assert<T>() {}
|
||||
_assert::<Box<dyn AsyncRead>>();
|
||||
_assert::<Box<dyn AsyncWrite>>();
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
use crate::AsyncRead;
|
||||
use futures::{Poll, Stream};
|
||||
use std::io::{self, BufRead};
|
||||
use std::mem;
|
||||
|
||||
/// Combinator created by the top-level `lines` method which is a stream over
|
||||
/// the lines of text on an I/O object.
|
||||
#[derive(Debug)]
|
||||
pub struct Lines<A> {
|
||||
io: A,
|
||||
line: String,
|
||||
}
|
||||
|
||||
/// Creates a new stream from the I/O object given representing the lines of
|
||||
/// input that are found on `A`.
|
||||
///
|
||||
/// This method takes an asynchronous I/O object, `a`, and returns a `Stream` of
|
||||
/// lines that the object contains. The returned stream will reach its end once
|
||||
/// `a` reaches EOF.
|
||||
pub fn lines<A>(a: A) -> Lines<A>
|
||||
where
|
||||
A: AsyncRead + BufRead,
|
||||
{
|
||||
Lines {
|
||||
io: a,
|
||||
line: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Lines<A> {
|
||||
/// Returns the underlying I/O object.
|
||||
///
|
||||
/// Note that this may lose data already read into internal buffers. It's
|
||||
/// recommended to only call this once the stream has reached its end.
|
||||
pub fn into_inner(self) -> A {
|
||||
self.io
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Stream for Lines<A>
|
||||
where
|
||||
A: AsyncRead + BufRead,
|
||||
{
|
||||
type Item = String;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<String>, io::Error> {
|
||||
let n = try_nb!(self.io.read_line(&mut self.line));
|
||||
if n == 0 && self.line.len() == 0 {
|
||||
return Ok(None.into());
|
||||
}
|
||||
if self.line.ends_with("\n") {
|
||||
self.line.pop();
|
||||
if self.line.ends_with("\r") {
|
||||
self.line.pop();
|
||||
}
|
||||
}
|
||||
Ok(Some(mem::replace(&mut self.line, String::new())).into())
|
||||
}
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
use crate::{AsyncRead, AsyncWrite};
|
||||
use bytes::{Buf, BufMut};
|
||||
use futures::sync::BiLock;
|
||||
use futures::{try_ready, Async, Poll};
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
/// The readable half of an object returned from `AsyncRead::split`.
|
||||
#[derive(Debug)]
|
||||
pub struct ReadHalf<T> {
|
||||
handle: BiLock<T>,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite> ReadHalf<T> {
|
||||
/// Reunite with a previously split `WriteHalf`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If this `ReadHalf` and the given `WriteHalf` do not originate from
|
||||
/// the same `AsyncRead::split` operation this method will panic.
|
||||
pub fn unsplit(self, w: WriteHalf<T>) -> T {
|
||||
if let Ok(x) = self.handle.reunite(w.handle) {
|
||||
x
|
||||
} else {
|
||||
panic!("Unrelated `WriteHalf` passed to `ReadHalf::unsplit`.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The writable half of an object returned from `AsyncRead::split`.
|
||||
#[derive(Debug)]
|
||||
pub struct WriteHalf<T> {
|
||||
handle: BiLock<T>,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite> WriteHalf<T> {
|
||||
/// Reunite with a previously split `ReadHalf`.
|
||||
///
|
||||
/// # panics
|
||||
///
|
||||
/// If this `WriteHalf` and the given `ReadHalf` do not originate from
|
||||
/// the same `AsyncRead::split` operation this method will panic.
|
||||
pub fn unsplit(self, r: ReadHalf<T>) -> T {
|
||||
if let Ok(x) = self.handle.reunite(r.handle) {
|
||||
x
|
||||
} else {
|
||||
panic!("Unrelated `ReadHalf` passed to `WriteHalf::unsplit`.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split<T: AsyncRead + AsyncWrite>(t: T) -> (ReadHalf<T>, WriteHalf<T>) {
|
||||
let (a, b) = BiLock::new(t);
|
||||
(ReadHalf { handle: a }, WriteHalf { handle: b })
|
||||
}
|
||||
|
||||
fn would_block() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::WouldBlock, "would block")
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> Read for ReadHalf<T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self.handle.poll_lock() {
|
||||
Async::Ready(mut l) => l.read(buf),
|
||||
Async::NotReady => Err(would_block()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for ReadHalf<T> {
|
||||
fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
||||
let mut l = try_ready!(wrap_as_io(self.handle.poll_lock()));
|
||||
l.read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite> Write for WriteHalf<T> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self.handle.poll_lock() {
|
||||
Async::Ready(mut l) => l.write(buf),
|
||||
Async::NotReady => Err(would_block()),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self.handle.poll_lock() {
|
||||
Async::Ready(mut l) => l.flush(),
|
||||
Async::NotReady => Err(would_block()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite> AsyncWrite for WriteHalf<T> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
let mut l = try_ready!(wrap_as_io(self.handle.poll_lock()));
|
||||
l.shutdown()
|
||||
}
|
||||
|
||||
fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut l = try_ready!(wrap_as_io(self.handle.poll_lock()));
|
||||
l.write_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_as_io<T>(t: Async<T>) -> Result<Async<T>, io::Error> {
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AsyncRead, AsyncWrite, ReadHalf, WriteHalf};
|
||||
use bytes::{BytesMut, IntoBuf};
|
||||
use futures::sync::BiLock;
|
||||
use futures::{future::lazy, future::ok, Async, Poll};
|
||||
use std::io::{self, Read, Write};
|
||||
use tokio_current_thread;
|
||||
|
||||
struct RW;
|
||||
|
||||
impl Read for RW {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for RW {}
|
||||
|
||||
impl Write for RW {
|
||||
fn write(&mut self, _: &[u8]) -> io::Result<usize> {
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for RW {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_readhalf_translate_wouldblock_to_not_ready() {
|
||||
tokio_current_thread::block_on_all(lazy(move || {
|
||||
let rw = RW {};
|
||||
let (a, b) = BiLock::new(rw);
|
||||
let mut rx = ReadHalf { handle: a };
|
||||
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
|
||||
// First read is uncontended, should go through.
|
||||
assert!(rx.read_buf(&mut buf).unwrap().is_ready());
|
||||
|
||||
// Take lock from write side.
|
||||
let lock = b.poll_lock();
|
||||
|
||||
// Second read should be NotReady.
|
||||
assert!(!rx.read_buf(&mut buf).unwrap().is_ready());
|
||||
|
||||
drop(lock);
|
||||
|
||||
// Back to uncontended.
|
||||
assert!(rx.read_buf(&mut buf).unwrap().is_ready());
|
||||
|
||||
ok::<(), ()>(())
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_writehalf_translate_wouldblock_to_not_ready() {
|
||||
tokio_current_thread::block_on_all(lazy(move || {
|
||||
let rw = RW {};
|
||||
let (a, b) = BiLock::new(rw);
|
||||
let mut tx = WriteHalf { handle: a };
|
||||
|
||||
let bufmut = BytesMut::with_capacity(64);
|
||||
let mut buf = bufmut.into_buf();
|
||||
|
||||
// First write is uncontended, should go through.
|
||||
assert!(tx.write_buf(&mut buf).unwrap().is_ready());
|
||||
|
||||
// Take lock from read side.
|
||||
let lock = b.poll_lock();
|
||||
|
||||
// Second write should be NotReady.
|
||||
assert!(!tx.write_buf(&mut buf).unwrap().is_ready());
|
||||
|
||||
drop(lock);
|
||||
|
||||
// Back to uncontended.
|
||||
assert!(tx.write_buf(&mut buf).unwrap().is_ready());
|
||||
|
||||
ok::<(), ()>(())
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsplit_ok() {
|
||||
let (r, w) = RW.split();
|
||||
r.unsplit(w);
|
||||
|
||||
let (r, w) = RW.split();
|
||||
w.unsplit(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn unsplit_err1() {
|
||||
let (r, _) = RW.split();
|
||||
let (_, w) = RW.split();
|
||||
r.unsplit(w);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn unsplit_err2() {
|
||||
let (_, w) = RW.split();
|
||||
let (r, _) = RW.split();
|
||||
r.unsplit(w);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn unsplit_err3() {
|
||||
let (_, w) = RW.split();
|
||||
let (r, _) = RW.split();
|
||||
w.unsplit(r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn unsplit_err4() {
|
||||
let (r, _) = RW.split();
|
||||
let (_, w) = RW.split();
|
||||
w.unsplit(r);
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
use std::ops;
|
||||
|
||||
/// A owned window around an underlying buffer.
|
||||
///
|
||||
/// Normally slices work great for considering sub-portions of a buffer, but
|
||||
/// unfortunately a slice is a *borrowed* type in Rust which has an associated
|
||||
/// lifetime. When working with future and async I/O these lifetimes are not
|
||||
/// always appropriate, and are sometimes difficult to store in tasks. This
|
||||
/// type strives to fill this gap by providing an "owned slice" around an
|
||||
/// underlying buffer of bytes.
|
||||
///
|
||||
/// A `Window<T>` wraps an underlying buffer, `T`, and has configurable
|
||||
/// start/end indexes to alter the behavior of the `AsRef<[u8]>` implementation
|
||||
/// that this type carries.
|
||||
///
|
||||
/// This type can be particularly useful when working with the `write_all`
|
||||
/// combinator in this crate. Data can be sliced via `Window`, consumed by
|
||||
/// `write_all`, and then earned back once the write operation finishes through
|
||||
/// the `into_inner` method on this type.
|
||||
#[derive(Debug)]
|
||||
pub struct Window<T> {
|
||||
inner: T,
|
||||
range: ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> Window<T> {
|
||||
/// Creates a new window around the buffer `t` defaulting to the entire
|
||||
/// slice.
|
||||
///
|
||||
/// Further methods can be called on the returned `Window<T>` to alter the
|
||||
/// window into the data provided.
|
||||
pub fn new(t: T) -> Window<T> {
|
||||
Window {
|
||||
range: 0..t.as_ref().len(),
|
||||
inner: t,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a shared reference to the underlying buffer inside of this
|
||||
/// `Window`.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying buffer inside of this
|
||||
/// `Window`.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Consumes this `Window`, returning the underlying buffer.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Returns the starting index of this window into the underlying buffer
|
||||
/// `T`.
|
||||
pub fn start(&self) -> usize {
|
||||
self.range.start
|
||||
}
|
||||
|
||||
/// Returns the end index of this window into the underlying buffer
|
||||
/// `T`.
|
||||
pub fn end(&self) -> usize {
|
||||
self.range.end
|
||||
}
|
||||
|
||||
/// Changes the starting index of this window to the index specified.
|
||||
///
|
||||
/// Returns the windows back to chain multiple calls to this method.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if `start` is out of bounds for the underlying
|
||||
/// slice or if it comes after the `end` configured in this window.
|
||||
pub fn set_start(&mut self, start: usize) -> &mut Window<T> {
|
||||
assert!(start <= self.inner.as_ref().len());
|
||||
assert!(start <= self.range.end);
|
||||
self.range.start = start;
|
||||
self
|
||||
}
|
||||
|
||||
/// Changes the end index of this window to the index specified.
|
||||
///
|
||||
/// Returns the windows back to chain multiple calls to this method.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if `end` is out of bounds for the underlying
|
||||
/// slice or if it comes before the `start` configured in this window.
|
||||
pub fn set_end(&mut self, end: usize) -> &mut Window<T> {
|
||||
assert!(end <= self.inner.as_ref().len());
|
||||
assert!(self.range.start <= end);
|
||||
self.range.end = end;
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: how about a generic set() method along the lines of:
|
||||
//
|
||||
// buffer.set(..3)
|
||||
// .set(0..2)
|
||||
// .set(4..)
|
||||
//
|
||||
// etc.
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> AsRef<[u8]> for Window<T> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.inner.as_ref()[self.range.start..self.range.end]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsMut<[u8]>> AsMut<[u8]> for Window<T> {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.inner.as_mut()[self.range.start..self.range.end]
|
||||
}
|
||||
}
|
||||
@@ -1,144 +1,166 @@
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::Async;
|
||||
use std::io::{self, Read};
|
||||
use tokio_io::AsyncRead;
|
||||
use tokio_test::{assert_ready_ok, assert_ready_err};
|
||||
use tokio_test::task::MockTask;
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use pin_utils::pin_mut;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
#[test]
|
||||
fn assert_obj_safe() {
|
||||
fn _assert<T>() {}
|
||||
_assert::<Box<dyn AsyncRead>>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_success() {
|
||||
struct R;
|
||||
struct Rd;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
impl AsyncRead for Rd {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
buf: &mut [u8]) -> Poll<io::Result<usize>>
|
||||
{
|
||||
buf[0..11].copy_from_slice(b"hello world");
|
||||
Ok(11)
|
||||
Poll::Ready(Ok(11))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {}
|
||||
|
||||
let mut buf = BytesMut::with_capacity(65);
|
||||
let mut task = MockTask::new();
|
||||
|
||||
let n = match R.read_buf(&mut buf).unwrap() {
|
||||
Async::Ready(n) => n,
|
||||
_ => panic!(),
|
||||
};
|
||||
task.enter(|cx| {
|
||||
let rd = Rd;
|
||||
pin_mut!(rd);
|
||||
|
||||
assert_eq!(11, n);
|
||||
assert_eq!(buf[..], b"hello world"[..]);
|
||||
let n = assert_ready_ok!(rd.poll_read_buf(cx, &mut buf));
|
||||
|
||||
assert_eq!(11, n);
|
||||
assert_eq!(buf[..], b"hello world"[..]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_error() {
|
||||
struct R;
|
||||
struct Rd;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "other"))
|
||||
impl AsyncRead for Rd {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
_buf: &mut [u8]) -> Poll<io::Result<usize>>
|
||||
{
|
||||
let err = io::ErrorKind::Other.into();
|
||||
Poll::Ready(Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {}
|
||||
|
||||
let mut buf = BytesMut::with_capacity(65);
|
||||
let mut task = MockTask::new();
|
||||
|
||||
let err = R.read_buf(&mut buf).unwrap_err();
|
||||
assert_eq!(err.kind(), io::ErrorKind::Other);
|
||||
task.enter(|cx| {
|
||||
let rd = Rd;
|
||||
pin_mut!(rd);
|
||||
|
||||
let err = assert_ready_err!(rd.poll_read_buf(cx, &mut buf));
|
||||
assert_eq!(err.kind(), io::ErrorKind::Other);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_no_capacity() {
|
||||
struct R;
|
||||
struct Rd;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
impl AsyncRead for Rd {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
_buf: &mut [u8]) -> Poll<io::Result<usize>>
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {}
|
||||
|
||||
// Can't create BytesMut w/ zero capacity, so fill it up
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
let mut task = MockTask::new();
|
||||
|
||||
buf.put(&[0; 64][..]);
|
||||
|
||||
let n = match R.read_buf(&mut buf).unwrap() {
|
||||
Async::Ready(n) => n,
|
||||
_ => panic!(),
|
||||
};
|
||||
task.enter(|cx| {
|
||||
let rd = Rd;
|
||||
pin_mut!(rd);
|
||||
|
||||
assert_eq!(0, n);
|
||||
let n = assert_ready_ok!(rd.poll_read_buf(cx, &mut buf));
|
||||
assert_eq!(0, n);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_no_uninitialized() {
|
||||
struct R;
|
||||
struct Rd;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
impl AsyncRead for Rd {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
buf: &mut [u8]) -> Poll<io::Result<usize>>
|
||||
{
|
||||
for b in buf {
|
||||
assert_eq!(0, *b);
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
Poll::Ready(Ok(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {}
|
||||
|
||||
// Can't create BytesMut w/ zero capacity, so fill it up
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
let mut task = MockTask::new();
|
||||
|
||||
let n = match R.read_buf(&mut buf).unwrap() {
|
||||
Async::Ready(n) => n,
|
||||
_ => panic!(),
|
||||
};
|
||||
task.enter(|cx| {
|
||||
let rd = Rd;
|
||||
pin_mut!(rd);
|
||||
|
||||
assert_eq!(0, n);
|
||||
let n = assert_ready_ok!(rd.poll_read_buf(cx, &mut buf));
|
||||
assert_eq!(0, n);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_uninitialized_ok() {
|
||||
struct R;
|
||||
struct Rd;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
assert_eq!(buf[0..11], b"hello world"[..]);
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {
|
||||
impl AsyncRead for Rd {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
buf: &mut [u8]) -> Poll<io::Result<usize>>
|
||||
{
|
||||
assert_eq!(buf[0..11], b"hello world"[..]);
|
||||
Poll::Ready(Ok(0))
|
||||
}
|
||||
}
|
||||
|
||||
// Can't create BytesMut w/ zero capacity, so fill it up
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
let mut task = MockTask::new();
|
||||
|
||||
unsafe {
|
||||
buf.bytes_mut()[0..11].copy_from_slice(b"hello world");
|
||||
}
|
||||
|
||||
let n = match R.read_buf(&mut buf).unwrap() {
|
||||
Async::Ready(n) => n,
|
||||
_ => panic!(),
|
||||
};
|
||||
task.enter(|cx| {
|
||||
let rd = Rd;
|
||||
pin_mut!(rd);
|
||||
|
||||
assert_eq!(0, n);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_translate_wouldblock_to_not_ready() {
|
||||
struct R;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, ""))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for R {}
|
||||
|
||||
let mut buf = BytesMut::with_capacity(65);
|
||||
assert!(!R.read_buf(&mut buf).unwrap().is_ready());
|
||||
let n = assert_ready_ok!(rd.poll_read_buf(cx, &mut buf));
|
||||
assert_eq!(0, n);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,548 +0,0 @@
|
||||
// This file is testing deprecated code.
|
||||
#![allow(deprecated)]
|
||||
|
||||
use futures::Async::*;
|
||||
use futures::{Poll, Sink, Stream};
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
use tokio_io::codec::length_delimited::*;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
macro_rules! mock {
|
||||
($($x:expr,)*) => {{
|
||||
let mut v = VecDeque::new();
|
||||
v.extend(vec![$($x),*]);
|
||||
Mock { calls: v }
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_empty_io_yields_nothing() {
|
||||
let mut io = FramedRead::new(mock!());
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_one_packet() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_one_packet_little_endian() {
|
||||
let mut io = Builder::new().little_endian().new_read(mock! {
|
||||
Ok(b"\x09\x00\x00\x00abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_one_packet_native_endian() {
|
||||
let data = if cfg!(target_endian = "big") {
|
||||
b"\x00\x00\x00\x09abcdefghi"
|
||||
} else {
|
||||
b"\x09\x00\x00\x00abcdefghi"
|
||||
};
|
||||
let mut io = Builder::new().native_endian().new_read(mock! {
|
||||
Ok(data[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_multi_frame_one_packet() {
|
||||
let mut data: Vec<u8> = vec![];
|
||||
data.extend_from_slice(b"\x00\x00\x00\x09abcdefghi");
|
||||
data.extend_from_slice(b"\x00\x00\x00\x03123");
|
||||
data.extend_from_slice(b"\x00\x00\x00\x0bhello world");
|
||||
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(data.into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_multi_packet() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_multi_frame_multi_packet() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x0312"[..].into()),
|
||||
Ok(b"3\x00\x00\x00\x0bhello world"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_frame_multi_packet_wait() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
Err(would_block()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_multi_frame_multi_packet_wait() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x09abc"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"defghi"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x00\x00\x0312"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"3\x00\x00\x00\x0bhello world"[..].into()),
|
||||
Err(would_block()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_incomplete_head() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
});
|
||||
|
||||
assert!(io.poll().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_incomplete_head_multi() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Err(would_block()),
|
||||
Ok(b"\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert!(io.poll().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_incomplete_payload() {
|
||||
let mut io = FramedRead::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09ab"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"cd"[..].into()),
|
||||
Err(would_block()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
assert!(io.poll().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_max_frame_len() {
|
||||
let mut io = Builder::new().max_frame_length(5).new_read(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap_err().kind(), io::ErrorKind::InvalidData);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_update_max_frame_len_at_rest() {
|
||||
let mut io = Builder::new().new_read(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
io.set_max_frame_length(5);
|
||||
assert_eq!(io.poll().unwrap_err().kind(), io::ErrorKind::InvalidData);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_update_max_frame_len_in_flight() {
|
||||
let mut io = Builder::new().new_read(mock! {
|
||||
Ok(b"\x00\x00\x00\x09abcd"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"efghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), NotReady);
|
||||
io.set_max_frame_length(5);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap_err().kind(), io::ErrorKind::InvalidData);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_one_byte_length_field() {
|
||||
let mut io = Builder::new().length_field_length(1).new_read(mock! {
|
||||
Ok(b"\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_header_offset() {
|
||||
let mut io = Builder::new()
|
||||
.length_field_length(2)
|
||||
.length_field_offset(4)
|
||||
.new_read(mock! {
|
||||
Ok(b"zzzz\x00\x09abcdefghi"[..].into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_multi_frame_one_packet_skip_none_adjusted() {
|
||||
let mut data: Vec<u8> = vec![];
|
||||
data.extend_from_slice(b"xx\x00\x09abcdefghi");
|
||||
data.extend_from_slice(b"yy\x00\x03123");
|
||||
data.extend_from_slice(b"zz\x00\x0bhello world");
|
||||
|
||||
let mut io = Builder::new()
|
||||
.length_field_length(2)
|
||||
.length_field_offset(2)
|
||||
.num_skip(0)
|
||||
.length_adjustment(4)
|
||||
.new_read(mock! {
|
||||
Ok(data.into()),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
io.poll().unwrap(),
|
||||
Ready(Some(b"xx\x00\x09abcdefghi"[..].into()))
|
||||
);
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"yy\x00\x03123"[..].into())));
|
||||
assert_eq!(
|
||||
io.poll().unwrap(),
|
||||
Ready(Some(b"zz\x00\x0bhello world"[..].into()))
|
||||
);
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_single_multi_frame_one_packet_length_includes_head() {
|
||||
let mut data: Vec<u8> = vec![];
|
||||
data.extend_from_slice(b"\x00\x0babcdefghi");
|
||||
data.extend_from_slice(b"\x00\x05123");
|
||||
data.extend_from_slice(b"\x00\x0dhello world");
|
||||
|
||||
let mut io = Builder::new()
|
||||
.length_field_length(2)
|
||||
.length_adjustment(-2)
|
||||
.new_read(mock! {
|
||||
Ok(data.into()),
|
||||
});
|
||||
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"abcdefghi"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"123"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(Some(b"hello world"[..].into())));
|
||||
assert_eq!(io.poll().unwrap(), Ready(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_length_adjusted() {
|
||||
let mut io = Builder::new().length_adjustment(-2).new_write(mock! {
|
||||
Ok(b"\x00\x00\x00\x0b"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_nothing_yields_nothing() {
|
||||
let mut io: FramedWrite<_, &'static [u8]> = FramedWrite::new(mock!());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_one_packet() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_multi_frame_one_packet() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x03"[..].into()),
|
||||
Ok(b"123"[..].into()),
|
||||
Ok(b"\x00\x00\x00\x0b"[..].into()),
|
||||
Ok(b"hello world"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.start_send("123").unwrap().is_ready());
|
||||
assert!(io.start_send("hello world").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_multi_frame_multi_packet() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Ok(b"\x00\x00\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
Ok(b"\x00\x00\x00\x03"[..].into()),
|
||||
Ok(b"123"[..].into()),
|
||||
Ok(Flush),
|
||||
Ok(b"\x00\x00\x00\x0b"[..].into()),
|
||||
Ok(b"hello world"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.start_send("123").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.start_send("hello world").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_would_block() {
|
||||
let mut io = FramedWrite::new(mock! {
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x00"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"\x00\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(!io.poll_complete().unwrap().is_ready());
|
||||
assert!(!io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_little_endian() {
|
||||
let mut io = Builder::new().little_endian().new_write(mock! {
|
||||
Ok(b"\x09\x00\x00\x00"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_single_frame_with_short_length_field() {
|
||||
let mut io = Builder::new().length_field_length(1).new_write(mock! {
|
||||
Ok(b"\x09"[..].into()),
|
||||
Ok(b"abcdefghi"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdefghi").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_max_frame_len() {
|
||||
let mut io = Builder::new().max_frame_length(5).new_write(mock! {});
|
||||
|
||||
assert_eq!(
|
||||
io.start_send("abcdef").unwrap_err().kind(),
|
||||
io::ErrorKind::InvalidInput
|
||||
);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_zero() {
|
||||
let mut io = Builder::new().new_write(mock! {});
|
||||
|
||||
assert!(io.start_send("abcdef").unwrap().is_ready());
|
||||
assert_eq!(
|
||||
io.poll_complete().unwrap_err().kind(),
|
||||
io::ErrorKind::WriteZero
|
||||
);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_update_max_frame_len_at_rest() {
|
||||
let mut io = Builder::new().new_write(mock! {
|
||||
Ok(b"\x00\x00\x00\x06"[..].into()),
|
||||
Ok(b"abcdef"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdef").unwrap().is_ready());
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
io.set_max_frame_length(5);
|
||||
assert_eq!(
|
||||
io.start_send("abcdef").unwrap_err().kind(),
|
||||
io::ErrorKind::InvalidInput
|
||||
);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_update_max_frame_len_in_flight() {
|
||||
let mut io = Builder::new().new_write(mock! {
|
||||
Ok(b"\x00\x00\x00\x06"[..].into()),
|
||||
Ok(b"ab"[..].into()),
|
||||
Err(would_block()),
|
||||
Ok(b"cdef"[..].into()),
|
||||
Ok(Flush),
|
||||
});
|
||||
|
||||
assert!(io.start_send("abcdef").unwrap().is_ready());
|
||||
assert!(!io.poll_complete().unwrap().is_ready());
|
||||
io.set_max_frame_length(5);
|
||||
assert!(io.poll_complete().unwrap().is_ready());
|
||||
assert_eq!(
|
||||
io.start_send("abcdef").unwrap_err().kind(),
|
||||
io::ErrorKind::InvalidInput
|
||||
);
|
||||
assert!(io.get_ref().calls.is_empty());
|
||||
}
|
||||
|
||||
// ===== Test utils =====
|
||||
|
||||
fn would_block() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::WouldBlock, "would block")
|
||||
}
|
||||
|
||||
struct Mock {
|
||||
calls: VecDeque<io::Result<Op>>,
|
||||
}
|
||||
|
||||
enum Op {
|
||||
Data(Vec<u8>),
|
||||
Flush,
|
||||
}
|
||||
|
||||
use self::Op::*;
|
||||
|
||||
impl io::Read for Mock {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(Op::Data(data))) => {
|
||||
debug_assert!(dst.len() >= data.len());
|
||||
dst[..data.len()].copy_from_slice(&data[..]);
|
||||
Ok(data.len())
|
||||
}
|
||||
Some(Ok(_)) => panic!(),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Mock {}
|
||||
|
||||
impl io::Write for Mock {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(Op::Data(data))) => {
|
||||
let len = data.len();
|
||||
assert!(src.len() >= len, "expect={:?}; actual={:?}", data, src);
|
||||
assert_eq!(&data[..], &src[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
Some(Ok(_)) => panic!(),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self.calls.pop_front() {
|
||||
Some(Ok(Op::Flush)) => Ok(()),
|
||||
Some(Ok(_)) => panic!(),
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Mock {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for Op {
|
||||
fn from(src: &'a [u8]) -> Op {
|
||||
Op::Data(src.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Op {
|
||||
fn from(src: Vec<u8>) -> Op {
|
||||
Op::Data(src)
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,6 @@ publish = false
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
# This feature comes with no promise of stability. Things will
|
||||
# break with each patch release. Use at your own risk.
|
||||
async-await-preview = []
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "0.4.27"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![cfg(feature = "async-await-preview")]
|
||||
#![deny(missing_debug_implementations, unreachable_pub, rust_2018_idioms)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]
|
||||
|
||||
@@ -24,9 +24,8 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
crossbeam-utils = "0.6.0"
|
||||
futures = "0.1.19"
|
||||
lazy_static = "1.0.2"
|
||||
log = "0.4.1"
|
||||
log = "0.4.6"
|
||||
mio = "0.6.14"
|
||||
num_cpus = "1.8.0"
|
||||
parking_lot = "0.8"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings, rust_2018_idioms)]
|
||||
|
||||
/*
|
||||
extern crate test;
|
||||
|
||||
const NUM_YIELD: usize = 500;
|
||||
@@ -129,3 +130,4 @@ mod io_pool {
|
||||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
use crate::{AtomicTask, Handle, Reactor};
|
||||
use futures::{task, Async, Future, Poll};
|
||||
use log::debug;
|
||||
use std::io;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
/// Handle to the reactor running on a background thread.
|
||||
///
|
||||
/// Instances are created by calling [`Reactor::background`].
|
||||
///
|
||||
/// [`Reactor::background`]: struct.Reactor.html#method.background
|
||||
#[derive(Debug)]
|
||||
pub struct Background {
|
||||
/// When `None`, the reactor thread will run until the process terminates.
|
||||
inner: Option<Inner>,
|
||||
}
|
||||
|
||||
/// Future that resolves when the reactor thread has shutdown.
|
||||
#[derive(Debug)]
|
||||
pub struct Shutdown {
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
/// Actual Background handle.
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
/// Handle to the reactor
|
||||
handle: Handle,
|
||||
|
||||
/// Shared state between the background handle and the reactor thread.
|
||||
shared: Arc<Shared>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Shared {
|
||||
/// Signal the reactor thread to shutdown.
|
||||
shutdown: AtomicUsize,
|
||||
|
||||
/// Task to notify when the reactor thread enters a shutdown state.
|
||||
shutdown_task: AtomicTask,
|
||||
}
|
||||
|
||||
/// Notifies the reactor thread to shutdown once the reactor becomes idle.
|
||||
const SHUTDOWN_IDLE: usize = 1;
|
||||
|
||||
/// Notifies the reactor thread to shutdown immediately.
|
||||
const SHUTDOWN_NOW: usize = 2;
|
||||
|
||||
/// The reactor is currently shutdown.
|
||||
const SHUTDOWN: usize = 3;
|
||||
|
||||
// ===== impl Background =====
|
||||
|
||||
impl Background {
|
||||
/// Launch a reactor in the background and return a handle to the thread.
|
||||
pub(crate) fn new(reactor: Reactor) -> io::Result<Background> {
|
||||
// Grab a handle to the reactor
|
||||
let handle = reactor.handle().clone();
|
||||
|
||||
// Create the state shared between the background handle and the reactor
|
||||
// thread.
|
||||
let shared = Arc::new(Shared {
|
||||
shutdown: AtomicUsize::new(0),
|
||||
shutdown_task: AtomicTask::new(),
|
||||
});
|
||||
|
||||
// For the reactor thread
|
||||
let shared2 = shared.clone();
|
||||
|
||||
// Start the reactor thread
|
||||
thread::Builder::new().spawn(move || run(reactor, shared2))?;
|
||||
|
||||
Ok(Background {
|
||||
inner: Some(Inner { handle, shared }),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a reference to the reactor handle.
|
||||
pub fn handle(&self) -> &Handle {
|
||||
&self.inner.as_ref().unwrap().handle
|
||||
}
|
||||
|
||||
/// Shutdown the reactor on idle.
|
||||
///
|
||||
/// Returns a future that completes once the reactor thread has shutdown.
|
||||
pub fn shutdown_on_idle(mut self) -> Shutdown {
|
||||
let inner = self.inner.take().unwrap();
|
||||
inner.shutdown_on_idle();
|
||||
|
||||
Shutdown { inner }
|
||||
}
|
||||
|
||||
/// Shutdown the reactor immediately
|
||||
///
|
||||
/// Returns a future that completes once the reactor thread has shutdown.
|
||||
pub fn shutdown_now(mut self) -> Shutdown {
|
||||
let inner = self.inner.take().unwrap();
|
||||
inner.shutdown_now();
|
||||
|
||||
Shutdown { inner }
|
||||
}
|
||||
|
||||
/// Run the reactor on its thread until the process terminates.
|
||||
pub fn forget(mut self) {
|
||||
drop(self.inner.take());
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Background {
|
||||
fn drop(&mut self) {
|
||||
let inner = match self.inner.take() {
|
||||
Some(i) => i,
|
||||
None => return,
|
||||
};
|
||||
|
||||
inner.shutdown_now();
|
||||
|
||||
let shutdown = Shutdown { inner };
|
||||
let _ = shutdown.wait();
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Shutdown =====
|
||||
|
||||
impl Future for Shutdown {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
let task = task::current();
|
||||
self.inner.shared.shutdown_task.register_task(task);
|
||||
|
||||
if !self.inner.is_shutdown() {
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Inner =====
|
||||
|
||||
impl Inner {
|
||||
/// Returns true if the reactor thread is shutdown.
|
||||
fn is_shutdown(&self) -> bool {
|
||||
self.shared.shutdown.load(SeqCst) == SHUTDOWN
|
||||
}
|
||||
|
||||
/// Notify the reactor thread to shutdown once the reactor transitions to an
|
||||
/// idle state.
|
||||
fn shutdown_on_idle(&self) {
|
||||
self.shared
|
||||
.shutdown
|
||||
.compare_and_swap(0, SHUTDOWN_IDLE, SeqCst);
|
||||
self.handle.wakeup();
|
||||
}
|
||||
|
||||
/// Notify the reactor thread to shutdown immediately.
|
||||
fn shutdown_now(&self) {
|
||||
let mut curr = self.shared.shutdown.load(SeqCst);
|
||||
|
||||
loop {
|
||||
if curr >= SHUTDOWN_NOW {
|
||||
return;
|
||||
}
|
||||
|
||||
let act = self
|
||||
.shared
|
||||
.shutdown
|
||||
.compare_and_swap(curr, SHUTDOWN_NOW, SeqCst);
|
||||
|
||||
if act == curr {
|
||||
self.handle.wakeup();
|
||||
return;
|
||||
}
|
||||
|
||||
curr = act;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Reactor thread =====
|
||||
|
||||
fn run(mut reactor: Reactor, shared: Arc<Shared>) {
|
||||
debug!("starting background reactor");
|
||||
loop {
|
||||
let shutdown = shared.shutdown.load(SeqCst);
|
||||
|
||||
if shutdown == SHUTDOWN_NOW {
|
||||
debug!("shutting background reactor down NOW");
|
||||
break;
|
||||
}
|
||||
|
||||
if shutdown == SHUTDOWN_IDLE && reactor.is_idle() {
|
||||
debug!("shutting background reactor on idle");
|
||||
break;
|
||||
}
|
||||
|
||||
reactor.turn(None).unwrap();
|
||||
}
|
||||
|
||||
drop(reactor);
|
||||
|
||||
// Transition the state to shutdown
|
||||
shared.shutdown.store(SHUTDOWN, SeqCst);
|
||||
|
||||
// Notify any waiters
|
||||
shared.shutdown_task.notify();
|
||||
|
||||
debug!("background reactor has shutdown");
|
||||
}
|
||||
+28
-178
@@ -32,38 +32,43 @@
|
||||
//! [`PollEvented`]: struct.PollEvented.html
|
||||
//! [reactor module]: https://docs.rs/tokio/0.1/tokio/reactor/index.html
|
||||
|
||||
pub(crate) mod background;
|
||||
macro_rules! ready {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
::std::task::Poll::Ready(v) => v,
|
||||
::std::task::Poll::Pending => return ::std::task::Poll::Pending,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod poll_evented;
|
||||
mod registration;
|
||||
mod sharded_rwlock;
|
||||
|
||||
// ===== Public re-exports =====
|
||||
|
||||
pub use self::background::{Background, Shutdown};
|
||||
pub use self::poll_evented::PollEvented;
|
||||
pub use self::registration::Registration;
|
||||
|
||||
// ===== Private imports =====
|
||||
|
||||
use crate::sharded_rwlock::RwLock;
|
||||
use futures::task::Task;
|
||||
use log::{debug, log_enabled, trace, Level};
|
||||
use mio::event::Evented;
|
||||
use slab::Slab;
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::{Relaxed, SeqCst};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::task::Waker;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{fmt, usize};
|
||||
use tokio_executor::park::{Park, Unpark};
|
||||
use tokio_executor::Enter;
|
||||
use tokio_sync::task::AtomicTask;
|
||||
use tokio_sync::task::AtomicWaker;
|
||||
|
||||
/// The core reactor, or event loop.
|
||||
///
|
||||
@@ -109,14 +114,6 @@ pub struct Turn {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
/// Error returned from `Handle::set_fallback`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SetFallbackError(());
|
||||
|
||||
#[deprecated(since = "0.1.2", note = "use SetFallbackError instead")]
|
||||
#[doc(hidden)]
|
||||
pub type SetDefaultError = SetFallbackError;
|
||||
|
||||
#[test]
|
||||
fn test_handle_size() {
|
||||
use std::mem;
|
||||
@@ -140,8 +137,8 @@ struct Inner {
|
||||
struct ScheduledIo {
|
||||
aba_guard: usize,
|
||||
readiness: AtomicUsize,
|
||||
reader: AtomicTask,
|
||||
writer: AtomicTask,
|
||||
reader: AtomicWaker,
|
||||
writer: AtomicWaker,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
@@ -150,9 +147,6 @@ pub(crate) enum Direction {
|
||||
Write,
|
||||
}
|
||||
|
||||
/// The global fallback reactor.
|
||||
static HANDLE_FALLBACK: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
thread_local! {
|
||||
/// Tracks the reactor for the current execution context.
|
||||
static CURRENT_REACTOR: RefCell<Option<HandlePriv>> = RefCell::new(None)
|
||||
@@ -262,33 +256,6 @@ impl Reactor {
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the fallback handle to be returned from `Handle::default`.
|
||||
///
|
||||
/// The `Handle::default()` function will by default lazily spin up a global
|
||||
/// thread and run a reactor on this global thread. This behavior is not
|
||||
/// always desirable in all applications, however, and sometimes a different
|
||||
/// fallback reactor is desired.
|
||||
///
|
||||
/// This function will attempt to globally alter the return value of
|
||||
/// `Handle::default()` to return the `handle` specified rather than a
|
||||
/// lazily initialized global thread. If successful then all future calls to
|
||||
/// `Handle::default()` which would otherwise fall back to the global thread
|
||||
/// will instead return a clone of the handle specified.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may not always succeed in configuring the fallback handle.
|
||||
/// If this function was previously called (or perhaps concurrently called
|
||||
/// on many threads) only the *first* invocation of this function will
|
||||
/// succeed. All other invocations will return an error.
|
||||
///
|
||||
/// Additionally if the global reactor thread has already been initialized
|
||||
/// then this function will also return an error. (aka if `Handle::default`
|
||||
/// has been called previously in this program).
|
||||
pub fn set_fallback(&self) -> Result<(), SetFallbackError> {
|
||||
set_fallback(self.handle().into_priv().unwrap())
|
||||
}
|
||||
|
||||
/// Performs one iteration of the event loop, blocking on waiting for events
|
||||
/// for at most `max_wait` (forever if `None`).
|
||||
///
|
||||
@@ -328,16 +295,6 @@ impl Reactor {
|
||||
self.inner.io_dispatch.read().is_empty()
|
||||
}
|
||||
|
||||
/// Run this reactor on a background thread.
|
||||
///
|
||||
/// This function takes ownership, spawns a new thread, and moves the
|
||||
/// reactor to this new thread. It then runs the reactor, driving all
|
||||
/// associated I/O resources, until the `Background` handle is dropped or
|
||||
/// explicitly shutdown.
|
||||
pub fn background(self) -> io::Result<Background> {
|
||||
Background::new(self)
|
||||
}
|
||||
|
||||
fn poll(&mut self, max_wait: Option<Duration>) -> io::Result<()> {
|
||||
// Block waiting for an event to happen, peeling out how many events
|
||||
// happened.
|
||||
@@ -406,20 +363,20 @@ impl Reactor {
|
||||
io.readiness.fetch_or(ready.as_usize(), Relaxed);
|
||||
|
||||
if ready.is_writable() || platform::is_hup(&ready) {
|
||||
wr = io.writer.take_task();
|
||||
wr = io.writer.take_waker();
|
||||
}
|
||||
|
||||
if !(ready & (!mio::Ready::writable())).is_empty() {
|
||||
rd = io.reader.take_task();
|
||||
rd = io.reader.take_waker();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(task) = rd {
|
||||
task.notify();
|
||||
if let Some(w) = rd {
|
||||
w.wake();
|
||||
}
|
||||
|
||||
if let Some(task) = wr {
|
||||
task.notify();
|
||||
if let Some(w) = wr {
|
||||
w.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -475,16 +432,6 @@ impl Handle {
|
||||
fn as_priv(&self) -> Option<&HandlePriv> {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
|
||||
fn into_priv(self) -> Option<HandlePriv> {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn wakeup(&self) {
|
||||
if let Some(handle) = self.as_priv() {
|
||||
handle.wakeup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Unpark for Handle {
|
||||
@@ -508,19 +455,6 @@ impl fmt::Debug for Handle {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_fallback(handle: HandlePriv) -> Result<(), SetFallbackError> {
|
||||
unsafe {
|
||||
let val = handle.into_usize();
|
||||
match HANDLE_FALLBACK.compare_exchange(0, val, SeqCst, SeqCst) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => {
|
||||
drop(HandlePriv::from_usize(val));
|
||||
Err(SetFallbackError(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl HandlePriv =====
|
||||
|
||||
impl HandlePriv {
|
||||
@@ -530,71 +464,10 @@ impl HandlePriv {
|
||||
pub(crate) fn try_current() -> io::Result<HandlePriv> {
|
||||
CURRENT_REACTOR.with(|current| match *current.borrow() {
|
||||
Some(ref handle) => Ok(handle.clone()),
|
||||
None => HandlePriv::fallback(),
|
||||
None => Err(io::Error::new(io::ErrorKind::Other, "no current reactor")),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a handle to the fallback reactor.
|
||||
fn fallback() -> io::Result<HandlePriv> {
|
||||
let mut fallback = HANDLE_FALLBACK.load(SeqCst);
|
||||
|
||||
// If the fallback hasn't been previously initialized then let's spin
|
||||
// up a helper thread and try to initialize with that. If we can't
|
||||
// actually create a helper thread then we'll just return a "defunct"
|
||||
// handle which will return errors when I/O objects are attempted to be
|
||||
// associated.
|
||||
if fallback == 0 {
|
||||
let reactor = match Reactor::new() {
|
||||
Ok(reactor) => reactor,
|
||||
Err(_) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"failed to create reactor",
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// If we successfully set ourselves as the actual fallback then we
|
||||
// want to `forget` the helper thread to ensure that it persists
|
||||
// globally. If we fail to set ourselves as the fallback that means
|
||||
// that someone was racing with this call to `Handle::default`.
|
||||
// They ended up winning so we'll destroy our helper thread (which
|
||||
// shuts down the thread) and reload the fallback.
|
||||
if set_fallback(reactor.handle().into_priv().unwrap()).is_ok() {
|
||||
let ret = reactor.handle().into_priv().unwrap();
|
||||
|
||||
match reactor.background() {
|
||||
Ok(bg) => bg.forget(),
|
||||
// The global handle is fubar, but y'all probably got bigger
|
||||
// problems if a thread can't spawn.
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
fallback = HANDLE_FALLBACK.load(SeqCst);
|
||||
}
|
||||
|
||||
// At this point our fallback handle global was configured so we use
|
||||
// its value to reify a handle, clone it, and then forget our reified
|
||||
// handle as we don't actually have an owning reference to it.
|
||||
assert!(fallback != 0);
|
||||
|
||||
let ret = unsafe {
|
||||
let handle = HandlePriv::from_usize(fallback);
|
||||
let ret = handle.clone();
|
||||
|
||||
// This prevents `handle` from being dropped and having the ref
|
||||
// count decremented.
|
||||
drop(handle.into_usize());
|
||||
|
||||
ret
|
||||
};
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Forces a reactor blocked in a call to `turn` to wakeup, or otherwise
|
||||
/// makes the next call to `turn` return immediately.
|
||||
///
|
||||
@@ -610,15 +483,6 @@ impl HandlePriv {
|
||||
}
|
||||
}
|
||||
|
||||
fn into_usize(self) -> usize {
|
||||
unsafe { mem::transmute::<Weak<Inner>, usize>(self.inner) }
|
||||
}
|
||||
|
||||
unsafe fn from_usize(val: usize) -> HandlePriv {
|
||||
let inner = mem::transmute::<usize, Weak<Inner>>(val);;
|
||||
HandlePriv { inner }
|
||||
}
|
||||
|
||||
fn inner(&self) -> Option<Arc<Inner>> {
|
||||
self.inner.upgrade()
|
||||
}
|
||||
@@ -655,8 +519,8 @@ impl Inner {
|
||||
io_dispatch.insert(ScheduledIo {
|
||||
aba_guard,
|
||||
readiness: AtomicUsize::new(0),
|
||||
reader: AtomicTask::new(),
|
||||
writer: AtomicTask::new(),
|
||||
reader: AtomicWaker::new(),
|
||||
writer: AtomicWaker::new(),
|
||||
})
|
||||
};
|
||||
|
||||
@@ -684,20 +548,20 @@ impl Inner {
|
||||
}
|
||||
|
||||
/// Registers interest in the I/O resource associated with `token`.
|
||||
fn register(&self, token: usize, dir: Direction, t: Task) {
|
||||
fn register(&self, token: usize, dir: Direction, w: Waker) {
|
||||
debug!("scheduling {:?} for: {}", dir, token);
|
||||
let io_dispatch = self.io_dispatch.read();
|
||||
let sched = io_dispatch.get(token).unwrap();
|
||||
|
||||
let (task, ready) = match dir {
|
||||
let (waker, ready) = match dir {
|
||||
Direction::Read => (&sched.reader, !mio::Ready::writable()),
|
||||
Direction::Write => (&sched.writer, mio::Ready::writable()),
|
||||
};
|
||||
|
||||
task.register_task(t);
|
||||
waker.register(w);
|
||||
|
||||
if sched.readiness.load(SeqCst) & ready.as_usize() != 0 {
|
||||
task.notify();
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -709,8 +573,8 @@ impl Drop for Inner {
|
||||
// will start returning errors pretty quickly.
|
||||
let io = self.io_dispatch.read();
|
||||
for (_, io) in io.iter() {
|
||||
io.writer.notify();
|
||||
io.reader.notify();
|
||||
io.writer.wake();
|
||||
io.reader.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -753,17 +617,3 @@ mod platform {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl SetFallbackError =====
|
||||
|
||||
impl fmt::Display for SetFallbackError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SetFallbackError {
|
||||
fn description(&self) -> &str {
|
||||
"attempted to set fallback reactor while already configured"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use crate::{Handle, Registration};
|
||||
use futures::{task, try_ready, Async, Poll};
|
||||
use mio;
|
||||
use mio::event::Evented;
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::marker::Unpin;
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
/// Associates an I/O resource that implements the [`std::io::Read`] and/or
|
||||
@@ -116,7 +118,10 @@ macro_rules! poll_ready {
|
||||
// stream. This happens in a loop to ensure that the stream gets
|
||||
// drained.
|
||||
loop {
|
||||
let ready = try_ready!($poll);
|
||||
let ready = match $poll? {
|
||||
Poll::Ready(v) => v,
|
||||
Poll::Pending => return Poll::Pending,
|
||||
};
|
||||
cached |= ready.as_usize();
|
||||
|
||||
// Update the cache store
|
||||
@@ -125,7 +130,7 @@ macro_rules! poll_ready {
|
||||
ret |= ready & mask;
|
||||
|
||||
if !ret.is_empty() {
|
||||
return Ok(ret.into());
|
||||
return Poll::Ready(Ok(ret));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -136,7 +141,7 @@ macro_rules! poll_ready {
|
||||
$me.inner.$cache.store(cached, Relaxed);
|
||||
}
|
||||
|
||||
Ok(mio::Ready::from_usize(cached).into())
|
||||
Poll::Ready(Ok(mio::Ready::from_usize(cached)))
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -217,14 +222,18 @@ where
|
||||
///
|
||||
/// * `ready` includes writable.
|
||||
/// * called from outside of a task context.
|
||||
pub fn poll_read_ready(&self, mask: mio::Ready) -> Poll<mio::Ready, io::Error> {
|
||||
pub fn poll_read_ready(
|
||||
&self,
|
||||
cx: &mut Context<'_>,
|
||||
mask: mio::Ready,
|
||||
) -> Poll<io::Result<mio::Ready>> {
|
||||
assert!(!mask.is_writable(), "cannot poll for write readiness");
|
||||
poll_ready!(
|
||||
self,
|
||||
mask,
|
||||
read_readiness,
|
||||
take_read_ready,
|
||||
self.inner.registration.poll_read_ready()
|
||||
self.inner.registration.poll_read_ready(cx)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -243,7 +252,7 @@ where
|
||||
///
|
||||
/// * `ready` includes writable or HUP
|
||||
/// * called from outside of a task context.
|
||||
pub fn clear_read_ready(&self, ready: mio::Ready) -> io::Result<()> {
|
||||
pub fn clear_read_ready(&self, cx: &mut Context<'_>, ready: mio::Ready) -> io::Result<()> {
|
||||
// Cannot clear write readiness
|
||||
assert!(!ready.is_writable(), "cannot clear write readiness");
|
||||
assert!(
|
||||
@@ -255,9 +264,9 @@ where
|
||||
.read_readiness
|
||||
.fetch_and(!ready.as_usize(), Relaxed);
|
||||
|
||||
if self.poll_read_ready(ready)?.is_ready() {
|
||||
if self.poll_read_ready(cx, ready)?.is_ready() {
|
||||
// Notify the current task
|
||||
task::current().notify();
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -282,13 +291,13 @@ where
|
||||
///
|
||||
/// * `ready` contains bits besides `writable` and `hup`.
|
||||
/// * called from outside of a task context.
|
||||
pub fn poll_write_ready(&self) -> Poll<mio::Ready, io::Error> {
|
||||
pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<mio::Ready>> {
|
||||
poll_ready!(
|
||||
self,
|
||||
mio::Ready::writable(),
|
||||
write_readiness,
|
||||
take_write_ready,
|
||||
self.inner.registration.poll_write_ready()
|
||||
self.inner.registration.poll_write_ready(cx)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -304,16 +313,16 @@ where
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if called from outside of a task context.
|
||||
pub fn clear_write_ready(&self) -> io::Result<()> {
|
||||
pub fn clear_write_ready(&self, cx: &mut Context<'_>) -> io::Result<()> {
|
||||
let ready = mio::Ready::writable();
|
||||
|
||||
self.inner
|
||||
.write_readiness
|
||||
.fetch_and(!ready.as_usize(), Relaxed);
|
||||
|
||||
if self.poll_write_ready()?.is_ready() {
|
||||
if self.poll_write_ready(cx)?.is_ready() {
|
||||
// Notify the current task
|
||||
task::current().notify();
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -330,139 +339,64 @@ where
|
||||
|
||||
// ===== Read / Write impls =====
|
||||
|
||||
impl<E> Read for PollEvented<E>
|
||||
impl<E> AsyncRead for PollEvented<E>
|
||||
where
|
||||
E: Evented + Read,
|
||||
E: Evented + Read + Unpin,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
if let Async::NotReady = self.poll_read_ready(mio::Ready::readable())? {
|
||||
return Err(io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
ready!(self.poll_read_ready(cx, mio::Ready::readable()))?;
|
||||
|
||||
let r = self.get_mut().read(buf);
|
||||
let r = (*self).get_mut().read(buf);
|
||||
|
||||
if is_wouldblock(&r) {
|
||||
self.clear_read_ready(mio::Ready::readable())?;
|
||||
self.clear_read_ready(cx, mio::Ready::readable())?;
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
return r;
|
||||
Poll::Ready(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Write for PollEvented<E>
|
||||
where
|
||||
E: Evented + Write,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if let Async::NotReady = self.poll_write_ready()? {
|
||||
return Err(io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
|
||||
let r = self.get_mut().write(buf);
|
||||
|
||||
if is_wouldblock(&r) {
|
||||
self.clear_write_ready()?;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
if let Async::NotReady = self.poll_write_ready()? {
|
||||
return Err(io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
|
||||
let r = self.get_mut().flush();
|
||||
|
||||
if is_wouldblock(&r) {
|
||||
self.clear_write_ready()?;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> AsyncRead for PollEvented<E> where E: Evented + Read {}
|
||||
|
||||
impl<E> AsyncWrite for PollEvented<E>
|
||||
where
|
||||
E: Evented + Write,
|
||||
E: Evented + Write + Unpin,
|
||||
{
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
ready!(self.poll_write_ready(cx))?;
|
||||
|
||||
// ===== &'a Read / &'a Write impls =====
|
||||
|
||||
impl<'a, E> Read for &'a PollEvented<E>
|
||||
where
|
||||
E: Evented,
|
||||
&'a E: Read,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
if let Async::NotReady = self.poll_read_ready(mio::Ready::readable())? {
|
||||
return Err(io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
|
||||
let r = self.get_ref().read(buf);
|
||||
let r = (*self).get_mut().write(buf);
|
||||
|
||||
if is_wouldblock(&r) {
|
||||
self.clear_read_ready(mio::Ready::readable())?;
|
||||
self.clear_write_ready(cx)?;
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
return r;
|
||||
Poll::Ready(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E> Write for &'a PollEvented<E>
|
||||
where
|
||||
E: Evented,
|
||||
&'a E: Write,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if let Async::NotReady = self.poll_write_ready()? {
|
||||
return Err(io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
ready!(self.poll_write_ready(cx))?;
|
||||
|
||||
let r = self.get_ref().write(buf);
|
||||
let r = (*self).get_mut().flush();
|
||||
|
||||
if is_wouldblock(&r) {
|
||||
self.clear_write_ready()?;
|
||||
self.clear_write_ready(cx)?;
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
return r;
|
||||
Poll::Ready(r)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
if let Async::NotReady = self.poll_write_ready()? {
|
||||
return Err(io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
|
||||
let r = self.get_ref().flush();
|
||||
|
||||
if is_wouldblock(&r) {
|
||||
self.clear_write_ready()?;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E> AsyncRead for &'a PollEvented<E>
|
||||
where
|
||||
E: Evented,
|
||||
&'a E: Read,
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a, E> AsyncWrite for &'a PollEvented<E>
|
||||
where
|
||||
E: Evented,
|
||||
&'a E: Write,
|
||||
{
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(().into())
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::{Direction, Handle, HandlePriv, Task};
|
||||
use futures::{task, Async, Poll};
|
||||
use crate::{Direction, Handle, HandlePriv};
|
||||
use log::debug;
|
||||
use mio::{self, Evented};
|
||||
use std::cell::UnsafeCell;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
use std::task::{Context, Poll, Waker};
|
||||
use std::{io, ptr, usize};
|
||||
|
||||
/// Associates an I/O resource with the reactor instance that drives it.
|
||||
@@ -59,17 +59,11 @@ struct Inner {
|
||||
token: usize,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Notify {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
/// Tasks waiting on readiness notifications.
|
||||
#[derive(Debug)]
|
||||
struct Node {
|
||||
direction: Direction,
|
||||
task: Task,
|
||||
waker: Waker,
|
||||
next: *mut Node,
|
||||
}
|
||||
|
||||
@@ -228,7 +222,7 @@ impl Registration {
|
||||
let node = *node;
|
||||
let Node {
|
||||
direction,
|
||||
task,
|
||||
waker,
|
||||
next,
|
||||
} = node;
|
||||
|
||||
@@ -240,7 +234,7 @@ impl Registration {
|
||||
if !*flag {
|
||||
*flag = true;
|
||||
|
||||
inner.register(direction, task);
|
||||
inner.register(direction, waker);
|
||||
}
|
||||
|
||||
ptr = next;
|
||||
@@ -285,12 +279,12 @@ impl Registration {
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if called from outside of a task context.
|
||||
pub fn poll_read_ready(&self) -> Poll<mio::Ready, io::Error> {
|
||||
self.poll_ready(Direction::Read, Notify::Yes)
|
||||
.map(|v| match v {
|
||||
Some(v) => Async::Ready(v),
|
||||
_ => Async::NotReady,
|
||||
})
|
||||
pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<mio::Ready>> {
|
||||
let v = self.poll_ready(Direction::Read, Some(cx))?;
|
||||
match v {
|
||||
Some(v) => Poll::Ready(Ok(v)),
|
||||
None => Poll::Pending,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume any pending read readiness event.
|
||||
@@ -301,7 +295,7 @@ impl Registration {
|
||||
///
|
||||
/// [`poll_read_ready`]: #method.poll_read_ready
|
||||
pub fn take_read_ready(&self) -> io::Result<Option<mio::Ready>> {
|
||||
self.poll_ready(Direction::Read, Notify::No)
|
||||
self.poll_ready(Direction::Read, None)
|
||||
}
|
||||
|
||||
/// Poll for events on the I/O resource's write readiness stream.
|
||||
@@ -336,12 +330,12 @@ impl Registration {
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if called from outside of a task context.
|
||||
pub fn poll_write_ready(&self) -> Poll<mio::Ready, io::Error> {
|
||||
self.poll_ready(Direction::Write, Notify::Yes)
|
||||
.map(|v| match v {
|
||||
Some(v) => Async::Ready(v),
|
||||
_ => Async::NotReady,
|
||||
})
|
||||
pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<mio::Ready>> {
|
||||
let v = self.poll_ready(Direction::Write, Some(cx))?;
|
||||
match v {
|
||||
Some(v) => Poll::Ready(Ok(v)),
|
||||
None => Poll::Pending,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume any pending write readiness event.
|
||||
@@ -352,10 +346,14 @@ impl Registration {
|
||||
///
|
||||
/// [`poll_write_ready`]: #method.poll_write_ready
|
||||
pub fn take_write_ready(&self) -> io::Result<Option<mio::Ready>> {
|
||||
self.poll_ready(Direction::Write, Notify::No)
|
||||
self.poll_ready(Direction::Write, None)
|
||||
}
|
||||
|
||||
fn poll_ready(&self, direction: Direction, notify: Notify) -> io::Result<Option<mio::Ready>> {
|
||||
fn poll_ready(
|
||||
&self,
|
||||
direction: Direction,
|
||||
cx: Option<&mut Context<'_>>,
|
||||
) -> io::Result<Option<mio::Ready>> {
|
||||
let mut state = self.state.load(SeqCst);
|
||||
|
||||
// Cache the node pointer
|
||||
@@ -366,29 +364,28 @@ impl Registration {
|
||||
INIT => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"must call `register`
|
||||
before poll_read_ready",
|
||||
"must call register before poll_read_ready",
|
||||
));
|
||||
}
|
||||
READY => {
|
||||
let inner = unsafe { (*self.inner.get()).as_ref().unwrap() };
|
||||
return inner.poll_ready(direction, notify);
|
||||
return inner.poll_ready(direction, cx);
|
||||
}
|
||||
LOCKED => {
|
||||
if let Notify::No = notify {
|
||||
let cx = if let Some(ref cx) = cx {
|
||||
cx
|
||||
} else {
|
||||
// Skip the notification tracking junk.
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
let next_ptr = (state & !LIFECYCLE_MASK) as *mut Node;
|
||||
|
||||
let task = task::current();
|
||||
|
||||
// Get the node
|
||||
let mut n = node.take().unwrap_or_else(|| {
|
||||
Box::new(Node {
|
||||
direction,
|
||||
task: task,
|
||||
waker: cx.waker().clone(),
|
||||
next: ptr::null_mut(),
|
||||
})
|
||||
});
|
||||
@@ -450,21 +447,21 @@ impl Inner {
|
||||
(inner, res)
|
||||
}
|
||||
|
||||
fn register(&self, direction: Direction, task: Task) {
|
||||
fn register(&self, direction: Direction, waker: Waker) {
|
||||
if self.token == ERROR {
|
||||
task.notify();
|
||||
waker.wake();
|
||||
return;
|
||||
}
|
||||
|
||||
let inner = match self.handle.inner() {
|
||||
Some(inner) => inner,
|
||||
None => {
|
||||
task.notify();
|
||||
waker.wake();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
inner.register(self.token, direction, task);
|
||||
inner.register(self.token, direction, waker);
|
||||
}
|
||||
|
||||
fn deregister<E: Evented>(&self, io: &E) -> io::Result<()> {
|
||||
@@ -483,7 +480,11 @@ impl Inner {
|
||||
inner.deregister_source(io)
|
||||
}
|
||||
|
||||
fn poll_ready(&self, direction: Direction, notify: Notify) -> io::Result<Option<mio::Ready>> {
|
||||
fn poll_ready(
|
||||
&self,
|
||||
direction: Direction,
|
||||
cx: Option<&mut Context<'_>>,
|
||||
) -> io::Result<Option<mio::Ready>> {
|
||||
if self.token == ERROR {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
@@ -513,16 +514,19 @@ impl Inner {
|
||||
let mut ready =
|
||||
mask & mio::Ready::from_usize(sched.readiness.fetch_and(!mask_no_hup, SeqCst));
|
||||
|
||||
if ready.is_empty() && notify == Notify::Yes {
|
||||
debug!("scheduling {:?} for: {}", direction, self.token);
|
||||
// Update the task info
|
||||
match direction {
|
||||
Direction::Read => sched.reader.register(),
|
||||
Direction::Write => sched.writer.register(),
|
||||
}
|
||||
if ready.is_empty() {
|
||||
if let Some(cx) = cx {
|
||||
debug!("scheduling {:?} for: {}", direction, self.token);
|
||||
// Update the task info
|
||||
match direction {
|
||||
Direction::Read => sched.reader.register_by_ref(cx.waker()),
|
||||
Direction::Write => sched.writer.register_by_ref(cx.waker()),
|
||||
}
|
||||
|
||||
// Try again
|
||||
ready = mask & mio::Ready::from_usize(sched.readiness.fetch_and(!mask_no_hup, SeqCst));
|
||||
// Try again
|
||||
ready =
|
||||
mask & mio::Ready::from_usize(sched.readiness.fetch_and(!mask_no_hup, SeqCst));
|
||||
}
|
||||
}
|
||||
|
||||
if ready.is_empty() {
|
||||
|
||||
+10
-4
@@ -21,12 +21,18 @@ Synchronization utilities.
|
||||
categories = ["asynchronous"]
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
async-traits = ["async-sink", "futures-core-preview"]
|
||||
|
||||
[dependencies]
|
||||
fnv = "1.0.6"
|
||||
futures = "0.1.19"
|
||||
async-sink = { git = "https://github.com/tokio-rs/async", optional = true }
|
||||
futures-core-preview = { version = "0.3.0-alpha.16", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
async-util = { git = "https://github.com/tokio-rs/async" }
|
||||
env_logger = { version = "0.5", default-features = false }
|
||||
tokio = { version = "0.2.0", path = "../tokio" }
|
||||
tokio-mock-task = "0.1.1"
|
||||
loom = { version = "0.1.1", features = ["futures"] }
|
||||
pin-utils = "0.1.0-alpha.4"
|
||||
# tokio = { version = "0.2.0", path = "../tokio" }
|
||||
tokio-test = { version = "0.2.0", path = "../tokio-test" }
|
||||
loom = { git = "https://github.com/carllerche/loom", branch = "std-future2", features = ["futures"] }
|
||||
|
||||
@@ -20,6 +20,19 @@ macro_rules! debug {
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap a ready value or propagate `Poll::Pending`.
|
||||
#[macro_export]
|
||||
macro_rules! ready {
|
||||
($e:expr) => {{
|
||||
use std::task::Poll::{Pending, Ready};
|
||||
|
||||
match $e {
|
||||
Ready(v) => v,
|
||||
Pending => return Pending,
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! if_fuzz {
|
||||
($($t:tt)*) => {{
|
||||
if false { $($t)* }
|
||||
|
||||
@@ -41,11 +41,13 @@
|
||||
//! [`LockGuard`]: struct.LockGuard.html
|
||||
|
||||
use crate::semaphore;
|
||||
use futures::Async;
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll::Ready;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// An asynchronous mutual exclusion primitive useful for protecting shared data
|
||||
///
|
||||
@@ -103,14 +105,12 @@ impl<T> Lock<T> {
|
||||
/// Try to acquire the lock.
|
||||
///
|
||||
/// If the lock is already held, the current task is notified when it is released.
|
||||
pub fn poll_lock(&mut self) -> Async<LockGuard<T>> {
|
||||
if let Async::NotReady = self.permit.poll_acquire(&self.inner.s).unwrap_or_else(|_| {
|
||||
pub fn poll_lock(&mut self, cx: &mut Context<'_>) -> Poll<LockGuard<T>> {
|
||||
ready!(self.permit.poll_acquire(cx, &self.inner.s)).unwrap_or_else(|_| {
|
||||
// The semaphore was closed. but, we never explicitly close it, and we have a
|
||||
// handle to it through the Arc, which means that this can never happen.
|
||||
unreachable!()
|
||||
}) {
|
||||
return Async::NotReady;
|
||||
}
|
||||
});
|
||||
|
||||
// We want to move the acquired permit into the guard,
|
||||
// and leave an unacquired one in self.
|
||||
@@ -118,7 +118,7 @@ impl<T> Lock<T> {
|
||||
inner: self.inner.clone(),
|
||||
permit: ::std::mem::replace(&mut self.permit, semaphore::Permit::new()),
|
||||
};
|
||||
Async::Ready(LockGuard(acquired))
|
||||
Ready(LockGuard(acquired))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
pub(crate) mod futures {
|
||||
pub(crate) use crate::task::AtomicTask;
|
||||
pub(crate) use futures::task;
|
||||
pub(crate) use crate::task::AtomicWaker;
|
||||
}
|
||||
|
||||
pub(crate) mod sync {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use super::chan;
|
||||
use futures::{Poll, Sink, StartSend, Stream};
|
||||
|
||||
use std::fmt;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
#[cfg(feature = "async-traits")]
|
||||
use std::pin::Pin;
|
||||
|
||||
/// Send values to the associated `Receiver`.
|
||||
///
|
||||
@@ -127,6 +131,11 @@ impl<T> Receiver<T> {
|
||||
Receiver { chan }
|
||||
}
|
||||
|
||||
/// TODO: Dox
|
||||
pub fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<T>> {
|
||||
self.chan.recv(cx)
|
||||
}
|
||||
|
||||
/// Closes the receiving half of a channel, without dropping it.
|
||||
///
|
||||
/// This prevents any further messages from being sent on the channel while
|
||||
@@ -136,12 +145,12 @@ impl<T> Receiver<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for Receiver<T> {
|
||||
#[cfg(feature = "async-traits")]
|
||||
impl<T> futures_core::Stream for Receiver<T> {
|
||||
type Item = T;
|
||||
type Error = RecvError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<T>, Self::Error> {
|
||||
self.chan.recv().map_err(|_| RecvError(()))
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
|
||||
Receiver::poll_next(self.get_mut(), cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,13 +174,13 @@ impl<T> Sender<T> {
|
||||
///
|
||||
/// This method returns:
|
||||
///
|
||||
/// - `Ok(Async::Ready(_))` if capacity is reserved for a single message.
|
||||
/// - `Ok(Async::NotReady)` if the channel may not have capacity, in which
|
||||
/// - `Poll::Ready(Ok(_))` if capacity is reserved for a single message.
|
||||
/// - `Poll::Pending` if the channel may not have capacity, in which
|
||||
/// case the current task is queued to be notified once
|
||||
/// capacity is available;
|
||||
/// - `Err(SendError)` if the receiver has been dropped.
|
||||
pub fn poll_ready(&mut self) -> Poll<(), SendError> {
|
||||
self.chan.poll_ready().map_err(|_| SendError(()))
|
||||
/// - `Poll::Ready(Err(SendError))` if the receiver has been dropped.
|
||||
pub fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), SendError>> {
|
||||
self.chan.poll_ready(cx).map_err(|_| SendError(()))
|
||||
}
|
||||
|
||||
/// Attempts to send a message on this `Sender`, returning the message
|
||||
@@ -182,31 +191,29 @@ impl<T> Sender<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sink for Sender<T> {
|
||||
type SinkItem = T;
|
||||
type SinkError = SendError;
|
||||
#[cfg(feature = "async-traits")]
|
||||
impl<T> async_sink::Sink<T> for Sender<T> {
|
||||
type Error = SendError;
|
||||
|
||||
fn start_send(&mut self, msg: T) -> StartSend<T, Self::SinkError> {
|
||||
use futures::Async::*;
|
||||
use futures::AsyncSink;
|
||||
|
||||
match self.poll_ready()? {
|
||||
Ready(_) => {
|
||||
self.try_send(msg).map_err(|_| SendError(()))?;
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
NotReady => Ok(AsyncSink::NotReady(msg)),
|
||||
}
|
||||
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Sender::poll_ready(self.get_mut(), cx)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
use futures::Async::Ready;
|
||||
Ok(Ready(()))
|
||||
fn start_send(mut self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> {
|
||||
self.as_mut()
|
||||
.try_send(msg)
|
||||
.map_err(|err| {
|
||||
assert!(err.is_full(), "call `poll_ready` before sending");
|
||||
SendError(())
|
||||
})
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
use futures::Async::Ready;
|
||||
Ok(Ready(()))
|
||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-23
@@ -1,13 +1,14 @@
|
||||
use super::list;
|
||||
use crate::loom::{
|
||||
futures::AtomicTask,
|
||||
futures::AtomicWaker,
|
||||
sync::atomic::AtomicUsize,
|
||||
sync::{Arc, CausalCell},
|
||||
};
|
||||
use futures::Poll;
|
||||
use std::fmt;
|
||||
use std::process;
|
||||
use std::sync::atomic::Ordering::{AcqRel, Relaxed};
|
||||
use std::task::Poll::{Pending, Ready};
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Channel sender
|
||||
pub(crate) struct Tx<T, S: Semaphore> {
|
||||
@@ -61,7 +62,8 @@ pub(crate) trait Semaphore {
|
||||
|
||||
fn add_permit(&self);
|
||||
|
||||
fn poll_acquire(&self, permit: &mut Self::Permit) -> Poll<(), ()>;
|
||||
fn poll_acquire(&self, cx: &mut Context<'_>, permit: &mut Self::Permit)
|
||||
-> Poll<Result<(), ()>>;
|
||||
|
||||
fn try_acquire(&self, permit: &mut Self::Permit) -> Result<(), TrySendError>;
|
||||
|
||||
@@ -81,8 +83,8 @@ struct Chan<T, S> {
|
||||
/// Coordinates access to channel's capacity.
|
||||
semaphore: S,
|
||||
|
||||
/// Receiver task. Notified when a value is pushed into the channel.
|
||||
rx_task: AtomicTask,
|
||||
/// Receiver waker. Notified when a value is pushed into the channel.
|
||||
rx_waker: AtomicWaker,
|
||||
|
||||
/// Tracks the number of outstanding sender handles.
|
||||
///
|
||||
@@ -101,7 +103,7 @@ where
|
||||
fmt.debug_struct("Chan")
|
||||
.field("tx", &self.tx)
|
||||
.field("semaphore", &self.semaphore)
|
||||
.field("rx_task", &self.rx_task)
|
||||
.field("rx_waker", &self.rx_waker)
|
||||
.field("tx_count", &self.tx_count)
|
||||
.field("rx_fields", &"...")
|
||||
.finish()
|
||||
@@ -138,7 +140,7 @@ where
|
||||
let chan = Arc::new(Chan {
|
||||
tx,
|
||||
semaphore,
|
||||
rx_task: AtomicTask::new(),
|
||||
rx_waker: AtomicWaker::new(),
|
||||
tx_count: AtomicUsize::new(1),
|
||||
rx_fields: CausalCell::new(RxFields {
|
||||
list: rx,
|
||||
@@ -163,8 +165,8 @@ where
|
||||
}
|
||||
|
||||
/// TODO: Docs
|
||||
pub(crate) fn poll_ready(&mut self) -> Poll<(), ()> {
|
||||
self.inner.semaphore.poll_acquire(&mut self.permit)
|
||||
pub(crate) fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), ()>> {
|
||||
self.inner.semaphore.poll_acquire(cx, &mut self.permit)
|
||||
}
|
||||
|
||||
/// Send a message and notify the receiver.
|
||||
@@ -177,7 +179,7 @@ where
|
||||
self.inner.tx.push(value);
|
||||
|
||||
// Notify the rx task
|
||||
self.inner.rx_task.notify();
|
||||
self.inner.rx_waker.wake();
|
||||
|
||||
// Release the permit
|
||||
self.inner.semaphore.forget(&mut self.permit);
|
||||
@@ -217,7 +219,7 @@ where
|
||||
self.inner.tx.close();
|
||||
|
||||
// Notify the receiver
|
||||
self.inner.rx_task.notify();
|
||||
self.inner.rx_waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,9 +248,8 @@ where
|
||||
}
|
||||
|
||||
/// Receive the next value
|
||||
pub(crate) fn recv(&mut self) -> Poll<Option<T>, ()> {
|
||||
pub(crate) fn recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<T>> {
|
||||
use super::block::Read::*;
|
||||
use futures::Async::*;
|
||||
|
||||
self.inner.rx_fields.with_mut(|rx_fields_ptr| {
|
||||
let rx_fields = unsafe { &mut *rx_fields_ptr };
|
||||
@@ -258,7 +259,7 @@ where
|
||||
match rx_fields.list.pop(&self.inner.tx) {
|
||||
Some(Value(value)) => {
|
||||
self.inner.semaphore.add_permit();
|
||||
return Ok(Ready(Some(value)));
|
||||
return Ready(Some(value));
|
||||
}
|
||||
Some(Closed) => {
|
||||
// TODO: This check may not be required as it most
|
||||
@@ -268,7 +269,7 @@ where
|
||||
// which ensures that if dropping the tx handle is
|
||||
// visible, then all messages sent are also visible.
|
||||
assert!(self.inner.semaphore.is_idle());
|
||||
return Ok(Ready(None));
|
||||
return Ready(None);
|
||||
}
|
||||
None => {} // fall through
|
||||
}
|
||||
@@ -277,7 +278,7 @@ where
|
||||
|
||||
try_recv!();
|
||||
|
||||
self.inner.rx_task.register();
|
||||
self.inner.rx_waker.register_by_ref(cx.waker());
|
||||
|
||||
// It is possible that a value was pushed between attempting to read
|
||||
// and registering the task, so we have to check the channel a
|
||||
@@ -291,9 +292,9 @@ where
|
||||
);
|
||||
|
||||
if rx_fields.rx_closed && self.inner.semaphore.is_idle() {
|
||||
Ok(Ready(None))
|
||||
Ready(None)
|
||||
} else {
|
||||
Ok(NotReady)
|
||||
Pending
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -372,8 +373,8 @@ impl Semaphore for (crate::semaphore::Semaphore, usize) {
|
||||
self.0.available_permits() == self.1
|
||||
}
|
||||
|
||||
fn poll_acquire(&self, permit: &mut Permit) -> Poll<(), ()> {
|
||||
permit.poll_acquire(&self.0).map_err(|_| ())
|
||||
fn poll_acquire(&self, cx: &mut Context<'_>, permit: &mut Permit) -> Poll<Result<(), ()>> {
|
||||
permit.poll_acquire(cx, &self.0).map_err(|_| ())
|
||||
}
|
||||
|
||||
fn try_acquire(&self, permit: &mut Permit) -> Result<(), TrySendError> {
|
||||
@@ -415,9 +416,8 @@ impl Semaphore for AtomicUsize {
|
||||
self.load(Acquire) >> 1 == 0
|
||||
}
|
||||
|
||||
fn poll_acquire(&self, permit: &mut ()) -> Poll<(), ()> {
|
||||
use futures::Async::Ready;
|
||||
self.try_acquire(permit).map(Ready).map_err(|_| ())
|
||||
fn poll_acquire(&self, _cx: &mut Context<'_>, permit: &mut ()) -> Poll<Result<(), ()>> {
|
||||
Ready(self.try_acquire(permit).map_err(|_| ()))
|
||||
}
|
||||
|
||||
fn try_acquire(&self, _permit: &mut ()) -> Result<(), TrySendError> {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
use super::chan;
|
||||
use crate::loom::sync::atomic::AtomicUsize;
|
||||
use futures::{Poll, Sink, StartSend, Stream};
|
||||
|
||||
use std::fmt;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
#[cfg(feature = "async-traits")]
|
||||
use std::pin::Pin;
|
||||
|
||||
/// Send values to the associated `UnboundedReceiver`.
|
||||
///
|
||||
@@ -83,6 +87,11 @@ impl<T> UnboundedReceiver<T> {
|
||||
UnboundedReceiver { chan }
|
||||
}
|
||||
|
||||
/// TODO: dox
|
||||
pub fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<T>> {
|
||||
self.chan.recv(cx)
|
||||
}
|
||||
|
||||
/// Closes the receiving half of a channel, without dropping it.
|
||||
///
|
||||
/// This prevents any further messages from being sent on the channel while
|
||||
@@ -92,12 +101,12 @@ impl<T> UnboundedReceiver<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for UnboundedReceiver<T> {
|
||||
#[cfg(feature = "async-traits")]
|
||||
impl<T> futures_core::Stream for UnboundedReceiver<T> {
|
||||
type Item = T;
|
||||
type Error = UnboundedRecvError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<T>, Self::Error> {
|
||||
self.chan.recv().map_err(|_| UnboundedRecvError(()))
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
|
||||
self.chan.recv(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,25 +122,24 @@ impl<T> UnboundedSender<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sink for UnboundedSender<T> {
|
||||
type SinkItem = T;
|
||||
type SinkError = UnboundedSendError;
|
||||
#[cfg(feature = "async-traits")]
|
||||
impl<T> async_sink::Sink<T> for UnboundedSender<T> {
|
||||
type Error = UnboundedSendError;
|
||||
|
||||
fn start_send(&mut self, msg: T) -> StartSend<T, Self::SinkError> {
|
||||
use futures::AsyncSink;
|
||||
|
||||
self.try_send(msg).map_err(|_| UnboundedSendError(()))?;
|
||||
Ok(AsyncSink::Ready)
|
||||
fn poll_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
use futures::Async::Ready;
|
||||
Ok(Ready(()))
|
||||
fn start_send(mut self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> {
|
||||
self.try_send(msg).map_err(|_| UnboundedSendError(()))
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
use futures::Async::Ready;
|
||||
Ok(Ready(()))
|
||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+41
-49
@@ -1,15 +1,15 @@
|
||||
//! A channel for sending a single message between asynchronous tasks.
|
||||
|
||||
use crate::loom::{
|
||||
futures::task::{self, Task},
|
||||
sync::atomic::AtomicUsize,
|
||||
sync::CausalCell,
|
||||
};
|
||||
use futures::{Async, Future, Poll};
|
||||
use crate::loom::{sync::atomic::AtomicUsize, sync::CausalCell};
|
||||
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::Ordering::{self, AcqRel, Acquire};
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll::{Pending, Ready};
|
||||
use std::task::{Context, Poll, Waker};
|
||||
|
||||
/// Sends a value to the associated `Receiver`.
|
||||
///
|
||||
@@ -82,10 +82,10 @@ struct Inner<T> {
|
||||
value: CausalCell<Option<T>>,
|
||||
|
||||
/// The task to notify when the receiver drops without consuming the value.
|
||||
tx_task: CausalCell<ManuallyDrop<Task>>,
|
||||
tx_task: CausalCell<ManuallyDrop<Waker>>,
|
||||
|
||||
/// The task to notify when the value is sent.
|
||||
rx_task: CausalCell<ManuallyDrop<Task>>,
|
||||
rx_task: CausalCell<ManuallyDrop<Waker>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -167,33 +167,33 @@ impl<T> Sender<T> {
|
||||
///
|
||||
/// # Return values
|
||||
///
|
||||
/// If `Ok(Ready)` is returned then the associated `Receiver` has been
|
||||
/// If `Ready(Ok(_))` is returned then the associated `Receiver` has been
|
||||
/// dropped, which means any work required for sending should be canceled.
|
||||
///
|
||||
/// If `Ok(NotReady)` is returned then the associated `Receiver` is still
|
||||
/// If `Pending` is returned then the associated `Receiver` is still
|
||||
/// alive and may be able to receive a message if sent. The current task is
|
||||
/// registered to receive a notification if the `Receiver` handle goes away.
|
||||
///
|
||||
/// [`Receiver`]: struct.Receiver.html
|
||||
pub fn poll_close(&mut self) -> Poll<(), ()> {
|
||||
pub fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll<()> {
|
||||
let inner = self.inner.as_ref().unwrap();
|
||||
|
||||
let mut state = State::load(&inner.state, Acquire);
|
||||
|
||||
if state.is_closed() {
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
if state.is_tx_task_set() {
|
||||
let will_notify = inner
|
||||
.tx_task
|
||||
.with(|ptr| unsafe { (&*ptr).will_notify_current() });
|
||||
.with(|ptr| unsafe { (&*ptr).will_wake(cx.waker()) });
|
||||
|
||||
if !will_notify {
|
||||
state = State::unset_tx_task(&inner.state);
|
||||
|
||||
if state.is_closed() {
|
||||
return Ok(Async::Ready(()));
|
||||
return Ready(());
|
||||
} else {
|
||||
unsafe { inner.drop_tx_task() };
|
||||
}
|
||||
@@ -203,18 +203,18 @@ impl<T> Sender<T> {
|
||||
if !state.is_tx_task_set() {
|
||||
// Attempt to set the task
|
||||
unsafe {
|
||||
inner.set_tx_task();
|
||||
inner.set_tx_task(cx);
|
||||
}
|
||||
|
||||
// Update the state
|
||||
state = State::set_tx_task(&inner.state);
|
||||
|
||||
if state.is_closed() {
|
||||
return Ok(Async::Ready(()));
|
||||
return Ready(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Async::NotReady)
|
||||
Pending
|
||||
}
|
||||
|
||||
/// Check if the associated [`Receiver`] handle has been dropped.
|
||||
@@ -297,25 +297,18 @@ impl<T> Drop for Receiver<T> {
|
||||
}
|
||||
|
||||
impl<T> Future for Receiver<T> {
|
||||
type Item = T;
|
||||
type Error = RecvError;
|
||||
|
||||
fn poll(&mut self) -> Poll<T, RecvError> {
|
||||
use futures::Async::{NotReady, Ready};
|
||||
type Output = Result<T, RecvError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// If `inner` is `None`, then `poll()` has already completed.
|
||||
let ret = if let Some(inner) = self.inner.as_ref() {
|
||||
match inner.poll_recv() {
|
||||
Ok(Ready(v)) => Ok(Ready(v)),
|
||||
Ok(NotReady) => return Ok(NotReady),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
let ret = if let Some(inner) = self.as_ref().get_ref().inner.as_ref() {
|
||||
ready!(inner.poll_recv(cx))?
|
||||
} else {
|
||||
panic!("called after complete");
|
||||
};
|
||||
|
||||
self.inner = None;
|
||||
ret
|
||||
Ready(Ok(ret))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,30 +321,29 @@ impl<T> Inner<T> {
|
||||
}
|
||||
|
||||
if prev.is_rx_task_set() {
|
||||
self.rx_task.with(|ptr| unsafe { (&*ptr).notify() });
|
||||
// TODO: Consume waker?
|
||||
self.rx_task.with(|ptr| unsafe { (&*ptr).wake_by_ref() });
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn poll_recv(&self) -> Poll<T, RecvError> {
|
||||
use futures::Async::{NotReady, Ready};
|
||||
|
||||
fn poll_recv(&self, cx: &mut Context<'_>) -> Poll<Result<T, RecvError>> {
|
||||
// Load the state
|
||||
let mut state = State::load(&self.state, Acquire);
|
||||
|
||||
if state.is_complete() {
|
||||
match unsafe { self.consume_value() } {
|
||||
Some(value) => Ok(Ready(value)),
|
||||
None => Err(RecvError(())),
|
||||
Some(value) => Ready(Ok(value)),
|
||||
None => Ready(Err(RecvError(()))),
|
||||
}
|
||||
} else if state.is_closed() {
|
||||
Err(RecvError(()))
|
||||
Ready(Err(RecvError(())))
|
||||
} else {
|
||||
if state.is_rx_task_set() {
|
||||
let will_notify = self
|
||||
.rx_task
|
||||
.with(|ptr| unsafe { (&*ptr).will_notify_current() });
|
||||
.with(|ptr| unsafe { (&*ptr).will_wake(cx.waker()) });
|
||||
|
||||
// Check if the task is still the same
|
||||
if !will_notify {
|
||||
@@ -359,8 +351,8 @@ impl<T> Inner<T> {
|
||||
state = State::unset_rx_task(&self.state);
|
||||
if state.is_complete() {
|
||||
return match unsafe { self.consume_value() } {
|
||||
Some(value) => Ok(Ready(value)),
|
||||
None => Err(RecvError(())),
|
||||
Some(value) => Ready(Ok(value)),
|
||||
None => Ready(Err(RecvError(()))),
|
||||
};
|
||||
} else {
|
||||
unsafe { self.drop_rx_task() };
|
||||
@@ -371,7 +363,7 @@ impl<T> Inner<T> {
|
||||
if !state.is_rx_task_set() {
|
||||
// Attempt to set the task
|
||||
unsafe {
|
||||
self.set_rx_task();
|
||||
self.set_rx_task(cx);
|
||||
}
|
||||
|
||||
// Update the state
|
||||
@@ -379,14 +371,14 @@ impl<T> Inner<T> {
|
||||
|
||||
if state.is_complete() {
|
||||
match unsafe { self.consume_value() } {
|
||||
Some(value) => Ok(Ready(value)),
|
||||
None => Err(RecvError(())),
|
||||
Some(value) => Ready(Ok(value)),
|
||||
None => Ready(Err(RecvError(()))),
|
||||
}
|
||||
} else {
|
||||
return Ok(NotReady);
|
||||
return Pending;
|
||||
}
|
||||
} else {
|
||||
return Ok(NotReady);
|
||||
return Pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,7 +388,7 @@ impl<T> Inner<T> {
|
||||
let prev = State::set_closed(&self.state);
|
||||
|
||||
if prev.is_tx_task_set() && !prev.is_complete() {
|
||||
self.tx_task.with(|ptr| unsafe { (&*ptr).notify() });
|
||||
self.tx_task.with(|ptr| unsafe { (&*ptr).wake_by_ref() });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,14 +405,14 @@ impl<T> Inner<T> {
|
||||
self.tx_task.with_mut(|ptr| ManuallyDrop::drop(&mut *ptr))
|
||||
}
|
||||
|
||||
unsafe fn set_rx_task(&self) {
|
||||
unsafe fn set_rx_task(&self, cx: &mut Context<'_>) {
|
||||
self.rx_task
|
||||
.with_mut(|ptr| *ptr = ManuallyDrop::new(task::current()));
|
||||
.with_mut(|ptr| *ptr = ManuallyDrop::new(cx.waker().clone()));
|
||||
}
|
||||
|
||||
unsafe fn set_tx_task(&self) {
|
||||
unsafe fn set_tx_task(&self, cx: &mut Context<'_>) {
|
||||
self.tx_task
|
||||
.with_mut(|ptr| *ptr = ManuallyDrop::new(task::current()));
|
||||
.with_mut(|ptr| *ptr = ManuallyDrop::new(cx.waker().clone()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+38
-35
@@ -6,21 +6,23 @@
|
||||
//! Before accessing the shared resource, callers acquire a permit from the
|
||||
//! semaphore. Once the permit is acquired, the caller then enters the critical
|
||||
//! section. If no permits are available, then acquiring the semaphore returns
|
||||
//! `NotReady`. The task is notified once a permit becomes available.
|
||||
//! `Pending`. The task is woken once a permit becomes available.
|
||||
|
||||
use crate::loom::{
|
||||
futures::AtomicTask,
|
||||
futures::AtomicWaker,
|
||||
sync::{
|
||||
atomic::{AtomicPtr, AtomicUsize},
|
||||
CausalCell,
|
||||
},
|
||||
yield_now,
|
||||
};
|
||||
use futures::Poll;
|
||||
|
||||
use std::fmt;
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::sync::atomic::Ordering::{self, AcqRel, Acquire, Relaxed, Release};
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll::{Pending, Ready};
|
||||
use std::task::{Context, Poll};
|
||||
use std::usize;
|
||||
|
||||
/// Futures-aware semaphore.
|
||||
@@ -80,8 +82,8 @@ struct WaiterNode {
|
||||
/// See `NodeState` for more details.
|
||||
state: AtomicUsize,
|
||||
|
||||
/// Task to notify when a permit is made available.
|
||||
task: AtomicTask,
|
||||
/// Task to wake when a permit is made available.
|
||||
waker: AtomicWaker,
|
||||
|
||||
/// Next pointer in the queue of waiting senders.
|
||||
next: AtomicPtr<WaiterNode>,
|
||||
@@ -174,9 +176,10 @@ impl Semaphore {
|
||||
}
|
||||
|
||||
/// Poll for a permit
|
||||
fn poll_permit(&self, mut permit: Option<&mut Permit>) -> Poll<(), AcquireError> {
|
||||
use futures::Async::*;
|
||||
|
||||
fn poll_permit(
|
||||
&self,
|
||||
mut permit: Option<(&mut Context<'_>, &mut Permit)>,
|
||||
) -> Poll<Result<(), AcquireError>> {
|
||||
// Load the current state
|
||||
let mut curr = SemState::load(&self.state, Acquire);
|
||||
|
||||
@@ -205,7 +208,7 @@ impl Semaphore {
|
||||
|
||||
if curr.is_closed() {
|
||||
undo_strong!();
|
||||
return Err(AcquireError::closed());
|
||||
return Ready(Err(AcquireError::closed()));
|
||||
}
|
||||
|
||||
if !next.acquire_permit(&self.stub) {
|
||||
@@ -214,13 +217,13 @@ impl Semaphore {
|
||||
debug_assert!(curr.waiter().is_some());
|
||||
|
||||
if maybe_strong.is_none() {
|
||||
if let Some(ref mut permit) = permit {
|
||||
if let Some((ref mut cx, ref mut permit)) = permit {
|
||||
// Get the Sender's waiter node, or initialize one
|
||||
let waiter = permit
|
||||
.waiter
|
||||
.get_or_insert_with(|| Arc::new(WaiterNode::new()));
|
||||
|
||||
waiter.register();
|
||||
waiter.register(cx);
|
||||
|
||||
debug!(" + poll_permit -- to_queued_waiting");
|
||||
|
||||
@@ -228,14 +231,14 @@ impl Semaphore {
|
||||
debug!(" + poll_permit; waiter already queued");
|
||||
// The node is alrady queued, there is no further work
|
||||
// to do.
|
||||
return Ok(NotReady);
|
||||
return Pending;
|
||||
}
|
||||
|
||||
maybe_strong = Some(WaiterNode::into_non_null(waiter.clone()));
|
||||
} else {
|
||||
// If no `waiter`, then the task is not registered and there
|
||||
// is no further work to do.
|
||||
return Ok(NotReady);
|
||||
return Pending;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,14 +264,14 @@ impl Semaphore {
|
||||
|
||||
debug!(" + poll_permit -- waiter pushed");
|
||||
|
||||
return Ok(NotReady);
|
||||
return Pending;
|
||||
}
|
||||
None => {
|
||||
debug!(" + poll_permit -- permit acquired");
|
||||
|
||||
undo_strong!();
|
||||
|
||||
return Ok(Ready(()));
|
||||
return Ready(Ok(()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -571,42 +574,42 @@ impl Permit {
|
||||
|
||||
/// Try to acquire the permit. If no permits are available, the current task
|
||||
/// is notified once a new permit becomes available.
|
||||
pub fn poll_acquire(&mut self, semaphore: &Semaphore) -> Poll<(), AcquireError> {
|
||||
use futures::Async::*;
|
||||
|
||||
pub fn poll_acquire(
|
||||
&mut self,
|
||||
cx: &mut Context<'_>,
|
||||
semaphore: &Semaphore,
|
||||
) -> Poll<Result<(), AcquireError>> {
|
||||
match self.state {
|
||||
PermitState::Idle => {}
|
||||
PermitState::Waiting => {
|
||||
let waiter = self.waiter.as_ref().unwrap();
|
||||
|
||||
if waiter.acquire()? {
|
||||
if waiter.acquire(cx)? {
|
||||
self.state = PermitState::Acquired;
|
||||
return Ok(Ready(()));
|
||||
return Ready(Ok(()));
|
||||
} else {
|
||||
return Ok(NotReady);
|
||||
return Pending;
|
||||
}
|
||||
}
|
||||
PermitState::Acquired => {
|
||||
return Ok(Ready(()));
|
||||
return Ready(Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
match semaphore.poll_permit(Some(self))? {
|
||||
match semaphore.poll_permit(Some((cx, self)))? {
|
||||
Ready(v) => {
|
||||
self.state = PermitState::Acquired;
|
||||
Ok(Ready(v))
|
||||
Ready(Ok(v))
|
||||
}
|
||||
NotReady => {
|
||||
Pending => {
|
||||
self.state = PermitState::Waiting;
|
||||
Ok(NotReady)
|
||||
Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to acquire the permit.
|
||||
pub fn try_acquire(&mut self, semaphore: &Semaphore) -> Result<(), TryAcquireError> {
|
||||
use futures::Async::*;
|
||||
|
||||
match self.state {
|
||||
PermitState::Idle => {}
|
||||
PermitState::Waiting => {
|
||||
@@ -629,7 +632,7 @@ impl Permit {
|
||||
self.state = PermitState::Acquired;
|
||||
Ok(())
|
||||
}
|
||||
NotReady => Err(TryAcquireError::no_permits()),
|
||||
Pending => Err(TryAcquireError::no_permits()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,17 +751,17 @@ impl WaiterNode {
|
||||
fn new() -> WaiterNode {
|
||||
WaiterNode {
|
||||
state: AtomicUsize::new(NodeState::new().to_usize()),
|
||||
task: AtomicTask::new(),
|
||||
waker: AtomicWaker::new(),
|
||||
next: AtomicPtr::new(ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
fn acquire(&self) -> Result<bool, AcquireError> {
|
||||
fn acquire(&self, cx: &mut Context<'_>) -> Result<bool, AcquireError> {
|
||||
if self.acquire2()? {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
self.task.register();
|
||||
self.waker.register_by_ref(cx.waker());
|
||||
|
||||
self.acquire2()
|
||||
}
|
||||
@@ -773,8 +776,8 @@ impl WaiterNode {
|
||||
}
|
||||
}
|
||||
|
||||
fn register(&self) {
|
||||
self.task.register()
|
||||
fn register(&self, cx: &mut Context<'_>) {
|
||||
self.waker.register_by_ref(cx.waker())
|
||||
}
|
||||
|
||||
/// Returns `true` if the permit has been acquired
|
||||
@@ -860,7 +863,7 @@ impl WaiterNode {
|
||||
Ok(_) => match curr {
|
||||
QueuedWaiting => {
|
||||
debug!(" + notify -- task notified");
|
||||
self.task.notify();
|
||||
self.waker.wake();
|
||||
return true;
|
||||
}
|
||||
other => {
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
use crate::loom::{
|
||||
futures::task::{self, Task},
|
||||
sync::atomic::AtomicUsize,
|
||||
sync::CausalCell,
|
||||
};
|
||||
use std::fmt;
|
||||
use std::sync::atomic::Ordering::{AcqRel, Acquire, Release};
|
||||
|
||||
/// A synchronization primitive for task notification.
|
||||
///
|
||||
/// `AtomicTask` will coordinate concurrent notifications with the consumer
|
||||
/// potentially "updating" the underlying task to notify. This is useful in
|
||||
/// scenarios where a computation completes in another thread and wants to
|
||||
/// notify the consumer, but the consumer is in the process of being migrated to
|
||||
/// a new logical task.
|
||||
///
|
||||
/// Consumers should call `register` before checking the result of a computation
|
||||
/// and producers should call `notify` after producing the computation (this
|
||||
/// differs from the usual `thread::park` pattern). It is also permitted for
|
||||
/// `notify` to be called **before** `register`. This results in a no-op.
|
||||
///
|
||||
/// A single `AtomicTask` may be reused for any number of calls to `register` or
|
||||
/// `notify`.
|
||||
///
|
||||
/// `AtomicTask` does not provide any memory ordering guarantees, as such the
|
||||
/// user should use caution and use other synchronization primitives to guard
|
||||
/// the result of the underlying computation.
|
||||
pub struct AtomicTask {
|
||||
state: AtomicUsize,
|
||||
task: CausalCell<Option<Task>>,
|
||||
}
|
||||
|
||||
// `AtomicTask` is a multi-consumer, single-producer transfer cell. The cell
|
||||
// stores a `Task` value produced by calls to `register` and many threads can
|
||||
// race to take the task (to notify it) by calling `notify.
|
||||
//
|
||||
// If a new `Task` instance is produced by calling `register` before an existing
|
||||
// one is consumed, then the existing one is overwritten.
|
||||
//
|
||||
// While `AtomicTask` is single-producer, the implementation ensures memory
|
||||
// safety. In the event of concurrent calls to `register`, there will be a
|
||||
// single winner whose task will get stored in the cell. The losers will not
|
||||
// have their tasks notified. As such, callers should ensure to add
|
||||
// synchronization to calls to `register`.
|
||||
//
|
||||
// The implementation uses a single `AtomicUsize` value to coordinate access to
|
||||
// the `Task` cell. There are two bits that are operated on independently. These
|
||||
// are represented by `REGISTERING` and `NOTIFYING`.
|
||||
//
|
||||
// The `REGISTERING` bit is set when a producer enters the critical section. The
|
||||
// `NOTIFYING` bit is set when a consumer enters the critical section. Neither
|
||||
// bit being set is represented by `WAITING`.
|
||||
//
|
||||
// A thread obtains an exclusive lock on the task cell by transitioning the
|
||||
// state from `WAITING` to `REGISTERING` or `NOTIFYING`, depending on the
|
||||
// operation the thread wishes to perform. When this transition is made, it is
|
||||
// guaranteed that no other thread will access the task cell.
|
||||
//
|
||||
// # Registering
|
||||
//
|
||||
// On a call to `register`, an attempt to transition the state from WAITING to
|
||||
// REGISTERING is made. On success, the caller obtains a lock on the task cell.
|
||||
//
|
||||
// If the lock is obtained, then the thread sets the task cell to the task
|
||||
// provided as an argument. Then it attempts to transition the state back from
|
||||
// `REGISTERING` -> `WAITING`.
|
||||
//
|
||||
// If this transition is successful, then the registering process is complete
|
||||
// and the next call to `notify` will observe the task.
|
||||
//
|
||||
// If the transition fails, then there was a concurrent call to `notify` that
|
||||
// was unable to access the task cell (due to the registering thread holding the
|
||||
// lock). To handle this, the registering thread removes the task it just set
|
||||
// from the cell and calls `notify` on it. This call to notify represents the
|
||||
// attempt to notify by the other thread (that set the `NOTIFYING` bit). The
|
||||
// state is then transitioned from `REGISTERING | NOTIFYING` back to `WAITING`.
|
||||
// This transition must succeed because, at this point, the state cannot be
|
||||
// transitioned by another thread.
|
||||
//
|
||||
// # Notifying
|
||||
//
|
||||
// On a call to `notify`, an attempt to transition the state from `WAITING` to
|
||||
// `NOTIFYING` is made. On success, the caller obtains a lock on the task cell.
|
||||
//
|
||||
// If the lock is obtained, then the thread takes ownership of the current value
|
||||
// in teh task cell, and calls `notify` on it. The state is then transitioned
|
||||
// back to `WAITING`. This transition must succeed as, at this point, the state
|
||||
// cannot be transitioned by another thread.
|
||||
//
|
||||
// If the thread is unable to obtain the lock, the `NOTIFYING` bit is still.
|
||||
// This is because it has either been set by the current thread but the previous
|
||||
// value included the `REGISTERING` bit **or** a concurrent thread is in the
|
||||
// `NOTIFYING` critical section. Either way, no action must be taken.
|
||||
//
|
||||
// If the current thread is the only concurrent call to `notify` and another
|
||||
// thread is in the `register` critical section, when the other thread **exits**
|
||||
// the `register` critical section, it will observe the `NOTIFYING` bit and
|
||||
// handle the notify itself.
|
||||
//
|
||||
// If another thread is in the `notify` critical section, then it will handle
|
||||
// notifying the task.
|
||||
//
|
||||
// # A potential race (is safely handled).
|
||||
//
|
||||
// Imagine the following situation:
|
||||
//
|
||||
// * Thread A obtains the `notify` lock and notifies a task.
|
||||
//
|
||||
// * Before thread A releases the `notify` lock, the notified task is scheduled.
|
||||
//
|
||||
// * Thread B attempts to notify the task. In theory this should result in the
|
||||
// task being notified, but it cannot because thread A still holds the notify
|
||||
// lock.
|
||||
//
|
||||
// This case is handled by requiring users of `AtomicTask` to call `register`
|
||||
// **before** attempting to observe the application state change that resulted
|
||||
// in the task being notified. The notifiers also change the application state
|
||||
// before calling notify.
|
||||
//
|
||||
// Because of this, the task will do one of two things.
|
||||
//
|
||||
// 1) Observe the application state change that Thread B is notifying on. In
|
||||
// this case, it is OK for Thread B's notification to be lost.
|
||||
//
|
||||
// 2) Call register before attempting to observe the application state. Since
|
||||
// Thread A still holds the `notify` lock, the call to `register` will result
|
||||
// in the task notifying itself and get scheduled again.
|
||||
|
||||
/// Idle state
|
||||
const WAITING: usize = 0;
|
||||
|
||||
/// A new task value is being registered with the `AtomicTask` cell.
|
||||
const REGISTERING: usize = 0b01;
|
||||
|
||||
/// The task currently registered with the `AtomicTask` cell is being notified.
|
||||
const NOTIFYING: usize = 0b10;
|
||||
|
||||
impl AtomicTask {
|
||||
/// Create an `AtomicTask` initialized with the given `Task`
|
||||
pub fn new() -> AtomicTask {
|
||||
AtomicTask {
|
||||
state: AtomicUsize::new(WAITING),
|
||||
task: CausalCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers the current task to be notified on calls to `notify`.
|
||||
///
|
||||
/// This is the same as calling `register_task` with `task::current()`.
|
||||
pub fn register(&self) {
|
||||
self.do_register(CurrentTask);
|
||||
}
|
||||
|
||||
/// Registers the provided task to be notified on calls to `notify`.
|
||||
///
|
||||
/// The new task will take place of any previous tasks that were registered
|
||||
/// by previous calls to `register`. Any calls to `notify` that happen after
|
||||
/// a call to `register` (as defined by the memory ordering rules), will
|
||||
/// notify the `register` caller's task.
|
||||
///
|
||||
/// It is safe to call `register` with multiple other threads concurrently
|
||||
/// calling `notify`. This will result in the `register` caller's current
|
||||
/// task being notified once.
|
||||
///
|
||||
/// This function is safe to call concurrently, but this is generally a bad
|
||||
/// idea. Concurrent calls to `register` will attempt to register different
|
||||
/// tasks to be notified. One of the callers will win and have its task set,
|
||||
/// but there is no guarantee as to which caller will succeed.
|
||||
pub fn register_task(&self, task: Task) {
|
||||
self.do_register(ExactTask(task));
|
||||
}
|
||||
|
||||
fn do_register<R>(&self, reg: R)
|
||||
where
|
||||
R: Register,
|
||||
{
|
||||
debug!(" + register_task");
|
||||
match self.state.compare_and_swap(WAITING, REGISTERING, Acquire) {
|
||||
WAITING => {
|
||||
unsafe {
|
||||
// Locked acquired, update the waker cell
|
||||
self.task.with_mut(|t| reg.register(&mut *t));
|
||||
|
||||
// Release the lock. If the state transitioned to include
|
||||
// the `NOTIFYING` bit, this means that a notify has been
|
||||
// called concurrently, so we have to remove the task and
|
||||
// notify it.`
|
||||
//
|
||||
// Start by assuming that the state is `REGISTERING` as this
|
||||
// is what we jut set it to.
|
||||
let res = self
|
||||
.state
|
||||
.compare_exchange(REGISTERING, WAITING, AcqRel, Acquire);
|
||||
|
||||
match res {
|
||||
Ok(_) => {}
|
||||
Err(actual) => {
|
||||
// This branch can only be reached if a
|
||||
// concurrent thread called `notify`. In this
|
||||
// case, `actual` **must** be `REGISTERING |
|
||||
// `NOTIFYING`.
|
||||
debug_assert_eq!(actual, REGISTERING | NOTIFYING);
|
||||
|
||||
// Take the task to notify once the atomic operation has
|
||||
// completed.
|
||||
let notify = self.task.with_mut(|t| (*t).take()).unwrap();
|
||||
|
||||
// Just swap, because no one could change state
|
||||
// while state == `Registering | `Waking`
|
||||
self.state.swap(WAITING, AcqRel);
|
||||
|
||||
// The atomic swap was complete, now
|
||||
// notify the task and return.
|
||||
notify.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NOTIFYING => {
|
||||
// Currently in the process of notifying the task, i.e.,
|
||||
// `notify` is currently being called on the old task handle.
|
||||
// So, we call notify on the new task handle
|
||||
reg.notify();
|
||||
}
|
||||
state => {
|
||||
// In this case, a concurrent thread is holding the
|
||||
// "registering" lock. This probably indicates a bug in the
|
||||
// caller's code as racing to call `register` doesn't make much
|
||||
// sense.
|
||||
//
|
||||
// We just want to maintain memory safety. It is ok to drop the
|
||||
// call to `register`.
|
||||
debug_assert!(state == REGISTERING || state == REGISTERING | NOTIFYING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifies the task that last called `register`.
|
||||
///
|
||||
/// If `register` has not been called yet, then this does nothing.
|
||||
pub fn notify(&self) {
|
||||
debug!(" + notify");
|
||||
if let Some(task) = self.take_task() {
|
||||
task.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to take the `Task` value out of the `AtomicTask` with the
|
||||
/// intention that the caller will notify the task later.
|
||||
pub fn take_task(&self) -> Option<Task> {
|
||||
debug!(" + take_task");
|
||||
// AcqRel ordering is used in order to acquire the value of the `task`
|
||||
// cell as well as to establish a `release` ordering with whatever
|
||||
// memory the `AtomicTask` is associated with.
|
||||
match self.state.fetch_or(NOTIFYING, AcqRel) {
|
||||
WAITING => {
|
||||
debug!(" + WAITING");
|
||||
// The notifying lock has been acquired.
|
||||
let task = unsafe { self.task.with_mut(|t| (*t).take()) };
|
||||
|
||||
// Release the lock
|
||||
self.state.fetch_and(!NOTIFYING, Release);
|
||||
debug!(" + Done taking");
|
||||
|
||||
task
|
||||
}
|
||||
state => {
|
||||
debug!(" + state = {:?}", state);
|
||||
// There is a concurrent thread currently updating the
|
||||
// associated task.
|
||||
//
|
||||
// Nothing more to do as the `NOTIFYING` bit has been set. It
|
||||
// doesn't matter if there are concurrent registering threads or
|
||||
// not.
|
||||
//
|
||||
debug_assert!(
|
||||
state == REGISTERING || state == REGISTERING | NOTIFYING || state == NOTIFYING
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AtomicTask {
|
||||
fn default() -> Self {
|
||||
AtomicTask::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AtomicTask {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(fmt, "AtomicTask")
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for AtomicTask {}
|
||||
unsafe impl Sync for AtomicTask {}
|
||||
|
||||
trait Register {
|
||||
fn register(self, slot: &mut Option<Task>);
|
||||
fn notify(self);
|
||||
}
|
||||
|
||||
struct CurrentTask;
|
||||
|
||||
impl Register for CurrentTask {
|
||||
fn register(self, slot: &mut Option<Task>) {
|
||||
let should_update = (&*slot)
|
||||
.as_ref()
|
||||
.map(|prev| !prev.will_notify_current())
|
||||
.unwrap_or(true);
|
||||
if should_update {
|
||||
*slot = Some(task::current());
|
||||
}
|
||||
}
|
||||
|
||||
fn notify(self) {
|
||||
task::current().notify();
|
||||
}
|
||||
}
|
||||
|
||||
struct ExactTask(Task);
|
||||
|
||||
impl Register for ExactTask {
|
||||
fn register(self, slot: &mut Option<Task>) {
|
||||
// When calling register_task with an exact task, it doesn't matter
|
||||
// if the previous task would have notified current. We *always* want
|
||||
// to save that exact task.
|
||||
*slot = Some(self.0);
|
||||
}
|
||||
|
||||
fn notify(self) {
|
||||
self.0.notify();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
use crate::loom::{sync::atomic::AtomicUsize, sync::CausalCell};
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::atomic::Ordering::{AcqRel, Acquire, Release};
|
||||
use std::task::Waker;
|
||||
|
||||
/// A synchronization primitive for task waking.
|
||||
///
|
||||
/// `AtomicWaker` will coordinate concurrent wakes with the consumer
|
||||
/// potentially "waking" the underlying task. This is useful in scenarios
|
||||
/// where a computation completes in another thread and wants to wake the
|
||||
/// consumer, but the consumer is in the process of being migrated to a new
|
||||
/// logical task.
|
||||
///
|
||||
/// Consumers should call `register` before checking the result of a computation
|
||||
/// and producers should call `wake` after producing the computation (this
|
||||
/// differs from the usual `thread::park` pattern). It is also permitted for
|
||||
/// `wake` to be called **before** `register`. This results in a no-op.
|
||||
///
|
||||
/// A single `AtomicWaker` may be reused for any number of calls to `register` or
|
||||
/// `wake`.
|
||||
pub struct AtomicWaker {
|
||||
state: AtomicUsize,
|
||||
waker: CausalCell<Option<Waker>>,
|
||||
}
|
||||
|
||||
// `AtomicWaker` is a multi-consumer, single-producer transfer cell. The cell
|
||||
// stores a `Waker` value produced by calls to `register` and many threads can
|
||||
// race to take the waker by calling `wake.
|
||||
//
|
||||
// If a new `Waker` instance is produced by calling `register` before an existing
|
||||
// one is consumed, then the existing one is overwritten.
|
||||
//
|
||||
// While `AtomicWaker` is single-producer, the implementation ensures memory
|
||||
// safety. In the event of concurrent calls to `register`, there will be a
|
||||
// single winner whose waker will get stored in the cell. The losers will not
|
||||
// have their tasks woken. As such, callers should ensure to add synchronization
|
||||
// to calls to `register`.
|
||||
//
|
||||
// The implementation uses a single `AtomicUsize` value to coordinate access to
|
||||
// the `Waker` cell. There are two bits that are operated on independently. These
|
||||
// are represented by `REGISTERING` and `WAKING`.
|
||||
//
|
||||
// The `REGISTERING` bit is set when a producer enters the critical section. The
|
||||
// `WAKING` bit is set when a consumer enters the critical section. Neither
|
||||
// bit being set is represented by `WAITING`.
|
||||
//
|
||||
// A thread obtains an exclusive lock on the waker cell by transitioning the
|
||||
// state from `WAITING` to `REGISTERING` or `WAKING`, depending on the
|
||||
// operation the thread wishes to perform. When this transition is made, it is
|
||||
// guaranteed that no other thread will access the waker cell.
|
||||
//
|
||||
// # Registering
|
||||
//
|
||||
// On a call to `register`, an attempt to transition the state from WAITING to
|
||||
// REGISTERING is made. On success, the caller obtains a lock on the waker cell.
|
||||
//
|
||||
// If the lock is obtained, then the thread sets the waker cell to the waker
|
||||
// provided as an argument. Then it attempts to transition the state back from
|
||||
// `REGISTERING` -> `WAITING`.
|
||||
//
|
||||
// If this transition is successful, then the registering process is complete
|
||||
// and the next call to `wake` will observe the waker.
|
||||
//
|
||||
// If the transition fails, then there was a concurrent call to `wake` that
|
||||
// was unable to access the waker cell (due to the registering thread holding the
|
||||
// lock). To handle this, the registering thread removes the waker it just set
|
||||
// from the cell and calls `wake` on it. This call to wake represents the
|
||||
// attempt to wake by the other thread (that set the `WAKING` bit). The
|
||||
// state is then transitioned from `REGISTERING | WAKING` back to `WAITING`.
|
||||
// This transition must succeed because, at this point, the state cannot be
|
||||
// transitioned by another thread.
|
||||
//
|
||||
// # Waking
|
||||
//
|
||||
// On a call to `wake`, an attempt to transition the state from `WAITING` to
|
||||
// `WAKING` is made. On success, the caller obtains a lock on the waker cell.
|
||||
//
|
||||
// If the lock is obtained, then the thread takes ownership of the current value
|
||||
// in the waker cell, and calls `wake` on it. The state is then transitioned
|
||||
// back to `WAITING`. This transition must succeed as, at this point, the state
|
||||
// cannot be transitioned by another thread.
|
||||
//
|
||||
// If the thread is unable to obtain the lock, the `WAKING` bit is still.
|
||||
// This is because it has either been set by the current thread but the previous
|
||||
// value included the `REGISTERING` bit **or** a concurrent thread is in the
|
||||
// `WAKING` critical section. Either way, no action must be taken.
|
||||
//
|
||||
// If the current thread is the only concurrent call to `wake` and another
|
||||
// thread is in the `register` critical section, when the other thread **exits**
|
||||
// the `register` critical section, it will observe the `WAKING` bit and
|
||||
// handle the waker itself.
|
||||
//
|
||||
// If another thread is in the `waker` critical section, then it will handle
|
||||
// waking the caller task.
|
||||
//
|
||||
// # A potential race (is safely handled).
|
||||
//
|
||||
// Imagine the following situation:
|
||||
//
|
||||
// * Thread A obtains the `wake` lock and wakes a task.
|
||||
//
|
||||
// * Before thread A releases the `wake` lock, the woken task is scheduled.
|
||||
//
|
||||
// * Thread B attempts to wake the task. In theory this should result in the
|
||||
// task being woken, but it cannot because thread A still holds the wake
|
||||
// lock.
|
||||
//
|
||||
// This case is handled by requiring users of `AtomicWaker` to call `register`
|
||||
// **before** attempting to observe the application state change that resulted
|
||||
// in the task being woken. The wakers also change the application state
|
||||
// before calling wake.
|
||||
//
|
||||
// Because of this, the task will do one of two things.
|
||||
//
|
||||
// 1) Observe the application state change that Thread B is waking on. In
|
||||
// this case, it is OK for Thread B's wake to be lost.
|
||||
//
|
||||
// 2) Call register before attempting to observe the application state. Since
|
||||
// Thread A still holds the `wake` lock, the call to `register` will result
|
||||
// in the task waking itself and get scheduled again.
|
||||
|
||||
/// Idle state
|
||||
const WAITING: usize = 0;
|
||||
|
||||
/// A new waker value is being registered with the `AtomicWaker` cell.
|
||||
const REGISTERING: usize = 0b01;
|
||||
|
||||
/// The task currently registered with the `AtomicWaker` cell is being woken.
|
||||
const WAKING: usize = 0b10;
|
||||
|
||||
impl AtomicWaker {
|
||||
/// Create an `AtomicWaker`
|
||||
pub fn new() -> AtomicWaker {
|
||||
AtomicWaker {
|
||||
state: AtomicUsize::new(WAITING),
|
||||
waker: CausalCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers the current waker to be notified on calls to `wake`.
|
||||
///
|
||||
/// This is the same as calling `register_task` with `task::current()`.
|
||||
pub fn register(&self, waker: Waker) {
|
||||
self.do_register(waker);
|
||||
}
|
||||
|
||||
/// Registers the provided waker to be notified on calls to `wake`.
|
||||
///
|
||||
/// The new waker will take place of any previous wakers that were registered
|
||||
/// by previous calls to `register`. Any calls to `wake` that happen after
|
||||
/// a call to `register` (as defined by the memory ordering rules), will
|
||||
/// wake the `register` caller's task.
|
||||
///
|
||||
/// It is safe to call `register` with multiple other threads concurrently
|
||||
/// calling `wake`. This will result in the `register` caller's current
|
||||
/// task being woken once.
|
||||
///
|
||||
/// This function is safe to call concurrently, but this is generally a bad
|
||||
/// idea. Concurrent calls to `register` will attempt to register different
|
||||
/// tasks to be woken. One of the callers will win and have its task set,
|
||||
/// but there is no guarantee as to which caller will succeed.
|
||||
pub fn register_by_ref(&self, waker: &Waker) {
|
||||
self.do_register(waker);
|
||||
}
|
||||
|
||||
fn do_register<W>(&self, waker: W)
|
||||
where
|
||||
W: WakerRef,
|
||||
{
|
||||
debug!(" + register_task");
|
||||
match self.state.compare_and_swap(WAITING, REGISTERING, Acquire) {
|
||||
WAITING => {
|
||||
unsafe {
|
||||
// Locked acquired, update the waker cell
|
||||
self.waker.with_mut(|t| *t = Some(waker.into_waker()));
|
||||
|
||||
// Release the lock. If the state transitioned to include
|
||||
// the `WAKING` bit, this means that a wake has been
|
||||
// called concurrently, so we have to remove the waker and
|
||||
// wake it.`
|
||||
//
|
||||
// Start by assuming that the state is `REGISTERING` as this
|
||||
// is what we jut set it to.
|
||||
let res = self
|
||||
.state
|
||||
.compare_exchange(REGISTERING, WAITING, AcqRel, Acquire);
|
||||
|
||||
match res {
|
||||
Ok(_) => {}
|
||||
Err(actual) => {
|
||||
// This branch can only be reached if a
|
||||
// concurrent thread called `wake`. In this
|
||||
// case, `actual` **must** be `REGISTERING |
|
||||
// `WAKING`.
|
||||
debug_assert_eq!(actual, REGISTERING | WAKING);
|
||||
|
||||
// Take the waker to wake once the atomic operation has
|
||||
// completed.
|
||||
let waker = self.waker.with_mut(|t| (*t).take()).unwrap();
|
||||
|
||||
// Just swap, because no one could change state
|
||||
// while state == `Registering | `Waking`
|
||||
self.state.swap(WAITING, AcqRel);
|
||||
|
||||
// The atomic swap was complete, now
|
||||
// wake the waker and return.
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WAKING => {
|
||||
// Currently in the process of waking the task, i.e.,
|
||||
// `wake` is currently being called on the old waker.
|
||||
// So, we call wake on the new waker.
|
||||
waker.wake();
|
||||
}
|
||||
state => {
|
||||
// In this case, a concurrent thread is holding the
|
||||
// "registering" lock. This probably indicates a bug in the
|
||||
// caller's code as racing to call `register` doesn't make much
|
||||
// sense.
|
||||
//
|
||||
// We just want to maintain memory safety. It is ok to drop the
|
||||
// call to `register`.
|
||||
debug_assert!(state == REGISTERING || state == REGISTERING | WAKING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wakes the task that last called `register`.
|
||||
///
|
||||
/// If `register` has not been called yet, then this does nothing.
|
||||
pub fn wake(&self) {
|
||||
debug!(" + wake");
|
||||
if let Some(waker) = self.take_waker() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to take the `Waker` value out of the `AtomicWaker` with the
|
||||
/// intention that the caller will wake the task later.
|
||||
pub fn take_waker(&self) -> Option<Waker> {
|
||||
debug!(" + take_waker");
|
||||
// AcqRel ordering is used in order to acquire the value of the `waker`
|
||||
// cell as well as to establish a `release` ordering with whatever
|
||||
// memory the `AtomicWaker` is associated with.
|
||||
match self.state.fetch_or(WAKING, AcqRel) {
|
||||
WAITING => {
|
||||
debug!(" + WAITING");
|
||||
// The waking lock has been acquired.
|
||||
let waker = unsafe { self.waker.with_mut(|t| (*t).take()) };
|
||||
|
||||
// Release the lock
|
||||
self.state.fetch_and(!WAKING, Release);
|
||||
debug!(" + Done taking");
|
||||
|
||||
waker
|
||||
}
|
||||
state => {
|
||||
debug!(" + state = {:?}", state);
|
||||
// There is a concurrent thread currently updating the
|
||||
// associated waker.
|
||||
//
|
||||
// Nothing more to do as the `WAKING` bit has been set. It
|
||||
// doesn't matter if there are concurrent registering threads or
|
||||
// not.
|
||||
//
|
||||
debug_assert!(
|
||||
state == REGISTERING || state == REGISTERING | WAKING || state == WAKING
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AtomicWaker {
|
||||
fn default() -> Self {
|
||||
AtomicWaker::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AtomicWaker {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(fmt, "AtomicWaker")
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for AtomicWaker {}
|
||||
unsafe impl Sync for AtomicWaker {}
|
||||
|
||||
trait WakerRef {
|
||||
fn wake(self);
|
||||
fn into_waker(self) -> Waker;
|
||||
}
|
||||
|
||||
impl WakerRef for Waker {
|
||||
fn wake(self) {
|
||||
self.wake()
|
||||
}
|
||||
|
||||
fn into_waker(self) -> Waker {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WakerRef for &'a Waker {
|
||||
fn wake(self) {
|
||||
self.wake_by_ref()
|
||||
}
|
||||
|
||||
fn into_waker(self) -> Waker {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Thread-safe task notification primitives.
|
||||
|
||||
mod atomic_task;
|
||||
mod atomic_waker;
|
||||
|
||||
pub use self::atomic_task::AtomicTask;
|
||||
pub use self::atomic_waker::AtomicWaker;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user