Simultaneous futures compat (#172)

This patch adds opt-in support for futures 0.2.
This commit is contained in:
Aaron Turon
2018-03-13 13:57:35 -07:00
committed by Carl Lerche
parent 5846b3fc2a
commit d304791c0e
27 changed files with 1045 additions and 105 deletions
+17
View File
@@ -119,6 +119,9 @@ use std::marker::PhantomData;
use std::rc::Rc;
use std::time::{Duration, Instant};
#[cfg(feature = "unstable-futures")]
use futures2;
/// Executes tasks on the current thread
pub struct CurrentThread<P: Park = ParkThread> {
/// Execute futures and receive unpark notifications.
@@ -386,6 +389,13 @@ impl tokio_executor::Executor for CurrentThread {
self.borrow().spawn_local(future);
Ok(())
}
#[cfg(feature = "unstable-futures")]
fn spawn2(&mut self, _future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
-> Result<(), futures2::executor::SpawnError>
{
panic!("Futures 0.2 integration is not available for current_thread");
}
}
impl<P: Park> fmt::Debug for CurrentThread<P> {
@@ -591,6 +601,13 @@ impl tokio_executor::Executor for TaskExecutor {
self.spawn_local(future)
}
#[cfg(feature = "unstable-futures")]
fn spawn2(&mut self, _future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
-> Result<(), futures2::executor::SpawnError>
{
panic!("Futures 0.2 integration is not available for current_thread");
}
fn status(&self) -> Result<(), SpawnError> {
CURRENT.with(|current| {
if current.spawn.get().is_some() {
-1
View File
@@ -49,7 +49,6 @@
//! [`Executor`]: #
//! [`spawn`]: #
pub mod current_thread;
pub mod thread_pool {
+19
View File
@@ -79,6 +79,9 @@ extern crate tokio_threadpool;
#[macro_use]
extern crate log;
#[cfg(feature = "unstable-futures")]
extern crate futures2;
pub mod executor;
pub mod net;
pub mod reactor;
@@ -187,3 +190,19 @@ pub mod prelude {
task,
};
}
#[cfg(feature = "unstable-futures")]
fn lift_async<T>(old: futures::Async<T>) -> futures2::Async<T> {
match old {
futures::Async::Ready(x) => futures2::Async::Ready(x),
futures::Async::NotReady => futures2::Async::Pending,
}
}
#[cfg(feature = "unstable-futures")]
fn lower_async<T>(new: futures2::Async<T>) -> futures::Async<T> {
match new {
futures2::Async::Ready(x) => futures::Async::Ready(x),
futures2::Async::Pending => futures::Async::NotReady,
}
}
+15
View File
@@ -5,6 +5,9 @@ use std::io;
use futures::stream::Stream;
use futures::{Poll, Async};
#[cfg(feature = "unstable-futures")]
use futures2;
/// Stream returned by the `TcpListener::incoming` function representing the
/// stream of sockets received from a listener.
#[must_use = "streams do nothing unless polled"]
@@ -28,3 +31,15 @@ impl Stream for Incoming {
Ok(Async::Ready(Some(socket)))
}
}
#[cfg(feature = "unstable-futures")]
impl futures2::Stream for Incoming {
type Item = TcpStream;
type Error = io::Error;
fn poll_next(&mut self, cx: &mut futures2::task::Context)
-> futures2::Poll<Option<Self::Item>, io::Error>
{
Ok(self.inner.poll_accept2(cx)?.map(|(sock, _)| Some(sock)))
}
}
+38
View File
@@ -10,6 +10,9 @@ use mio;
use reactor::{Handle, PollEvented2};
#[cfg(feature = "unstable-futures")]
use futures2;
/// An I/O object representing a TCP socket listening for incoming connections.
///
/// This object can be converted into a stream of incoming connections for
@@ -64,6 +67,22 @@ impl TcpListener {
Ok((io, addr).into())
}
/// Like `poll_accept`, but for futures 0.2
#[cfg(feature = "unstable-futures")]
pub fn poll_accept2(&mut self, cx: &mut futures2::task::Context)
-> futures2::Poll<(TcpStream, SocketAddr), io::Error>
{
let (io, addr) = match self.poll_accept_std2(cx)? {
futures2::Async::Ready(x) => x,
futures2::Async::Pending => return Ok(futures2::Async::Pending),
};
let io = mio::net::TcpStream::from_stream(io)?;
let io = TcpStream::new(io);
Ok((io, addr).into())
}
#[deprecated(since = "0.1.2", note = "use poll_accept_std instead")]
#[doc(hidden)]
pub fn accept_std(&mut self) -> io::Result<(net::TcpStream, SocketAddr)> {
@@ -105,6 +124,25 @@ impl TcpListener {
}
}
/// Like `poll_accept_std`, but for futures 0.2.
#[cfg(feature = "unstable-futures")]
pub fn poll_accept_std2(&mut self, cx: &mut futures2::task::Context)
-> futures2::Poll<(net::TcpStream, SocketAddr), io::Error>
{
if let futures2::Async::Pending = self.io.poll_read_ready2(cx, mio::Ready::readable())? {
return Ok(futures2::Async::Pending);
}
match self.io.get_ref().accept_std() {
Ok(pair) => Ok(pair.into()),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io.clear_read_ready2(cx, mio::Ready::readable())?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
/// Create a new TCP listener from the standard library's TCP listener.
///
/// This method can be used when the `Handle::tcp_listen` method isn't
+107 -4
View File
@@ -12,6 +12,9 @@ use tokio_io::{AsyncRead, AsyncWrite};
use reactor::{Handle, PollEvented2};
#[cfg(feature = "unstable-futures")]
use futures2;
/// An I/O object representing a TCP stream connected to a remote endpoint.
///
/// A TCP stream can either be created by connecting to an endpoint, via the
@@ -208,6 +211,25 @@ impl TcpStream {
}
}
/// Like `poll_peek` but compatible with futures 0.2
#[cfg(feature = "unstable-futures")]
pub fn poll_peek2(&mut self, cx: &mut futures2::task::Context, buf: &mut [u8])
-> futures2::Poll<usize, io::Error>
{
if let futures2::Async::Pending = self.io.poll_read_ready2(cx, mio::Ready::readable())? {
return Ok(futures2::Async::Pending);
}
match self.io.get_ref().peek(buf) {
Ok(ret) => Ok(ret.into()),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io.clear_read_ready2(cx, mio::Ready::readable())?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
/// Shuts down the read, write, or both halves of this connection.
///
/// This function will cause all pending and future I/O on the specified
@@ -367,6 +389,15 @@ impl AsyncRead for TcpStream {
}
}
#[cfg(feature = "unstable-futures")]
impl futures2::io::AsyncRead for TcpStream {
fn poll_read(&mut self, cx: &mut futures2::task::Context, buf: &mut [u8])
-> futures2::Poll<usize, io::Error>
{
futures2::io::AsyncRead::poll_read(&mut self.io, cx, buf)
}
}
impl AsyncWrite for TcpStream {
fn shutdown(&mut self) -> Poll<(), io::Error> {
<&TcpStream>::shutdown(&mut &*self)
@@ -377,6 +408,23 @@ impl AsyncWrite for TcpStream {
}
}
#[cfg(feature = "unstable-futures")]
impl futures2::io::AsyncWrite for TcpStream {
fn poll_write(&mut self, cx: &mut futures2::task::Context, buf: &[u8])
-> futures2::Poll<usize, io::Error>
{
futures2::io::AsyncWrite::poll_write(&mut self.io, cx, buf)
}
fn poll_flush(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
futures2::io::AsyncWrite::poll_flush(&mut self.io, cx)
}
fn poll_close(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
futures2::io::AsyncWrite::poll_close(&mut self.io, cx)
}
}
// ===== impl Read / Write for &'a =====
impl<'a> Read for &'a TcpStream {
@@ -449,6 +497,15 @@ impl<'a> AsyncRead for &'a TcpStream {
}
}
#[cfg(feature = "unstable-futures")]
impl<'a> futures2::io::AsyncRead for &'a TcpStream {
fn poll_read(&mut self, cx: &mut futures2::task::Context, buf: &mut [u8])
-> futures2::Poll<usize, io::Error>
{
futures2::io::AsyncRead::poll_read(&mut &self.io, cx, buf)
}
}
impl<'a> AsyncWrite for &'a TcpStream {
fn shutdown(&mut self) -> Poll<(), io::Error> {
Ok(().into())
@@ -483,13 +540,29 @@ impl<'a> AsyncWrite for &'a TcpStream {
}
}
#[cfg(feature = "unstable-futures")]
impl<'a> futures2::io::AsyncWrite for &'a TcpStream {
fn poll_write(&mut self, cx: &mut futures2::task::Context, buf: &[u8])
-> futures2::Poll<usize, io::Error>
{
futures2::io::AsyncWrite::poll_write(&mut &self.io, cx, buf)
}
fn poll_flush(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
futures2::io::AsyncWrite::poll_flush(&mut &self.io, cx)
}
fn poll_close(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
futures2::io::AsyncWrite::poll_close(&mut &self.io, cx)
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.io.get_ref().fmt(f)
}
}
impl Future for ConnectFuture {
type Item = TcpStream;
type Error = io::Error;
@@ -499,11 +572,20 @@ impl Future for ConnectFuture {
}
}
impl Future for ConnectFutureState {
#[cfg(feature = "unstable-futures")]
impl futures2::Future for ConnectFuture {
type Item = TcpStream;
type Error = io::Error;
fn poll(&mut self) -> Poll<TcpStream, io::Error> {
fn poll(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<TcpStream, io::Error> {
futures2::Future::poll(&mut self.inner, cx)
}
}
impl ConnectFutureState {
fn poll_inner<F>(&mut self, f: F) -> Poll<TcpStream, io::Error>
where F: FnOnce(&mut PollEvented2<mio::net::TcpStream>) -> Poll<mio::Ready, io::Error>
{
{
let stream = match *self {
ConnectFutureState::Waiting(ref mut s) => s,
@@ -523,7 +605,7 @@ impl Future for ConnectFutureState {
// actually hit an error or not.
//
// If all that succeeded then we ship everything on up.
if let Async::NotReady = stream.io.poll_write_ready()? {
if let Async::NotReady = f(&mut stream.io)? {
return Ok(Async::NotReady)
}
@@ -531,6 +613,7 @@ impl Future for ConnectFutureState {
return Err(e)
}
}
match mem::replace(self, ConnectFutureState::Empty) {
ConnectFutureState::Waiting(stream) => Ok(Async::Ready(stream)),
_ => panic!(),
@@ -538,6 +621,26 @@ impl Future for ConnectFutureState {
}
}
impl Future for ConnectFutureState {
type Item = TcpStream;
type Error = io::Error;
fn poll(&mut self) -> Poll<TcpStream, io::Error> {
self.poll_inner(|io| io.poll_write_ready())
}
}
#[cfg(feature = "unstable-futures")]
impl futures2::Future for ConnectFutureState {
type Item = TcpStream;
type Error = io::Error;
fn poll(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<TcpStream, io::Error> {
self.poll_inner(|io| io.poll_write_ready2(cx).map(::lower_async))
.map(::lift_async)
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
mod sys {
use std::os::unix::prelude::*;
+50
View File
@@ -112,6 +112,9 @@ use futures::future::{self, Future};
use std::{fmt, io};
#[cfg(feature = "unstable-futures")]
use futures2;
/// Handle to the Tokio runtime.
///
/// The Tokio runtime includes a reactor as well as an executor for running
@@ -205,6 +208,18 @@ where F: Future<Item = (), Error = ()> + Send + 'static,
runtime.shutdown_on_idle().wait().unwrap();
}
/// Start the Tokio runtime using the supplied future to bootstrap execution.
///
/// Identical to `run` but works with futures 0.2-style futures.
#[cfg(feature = "unstable-futures")]
pub fn run2<F>(future: F)
where F: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
{
let mut runtime = Runtime::new().unwrap();
runtime.spawn2(future);
runtime.shutdown_on_idle().wait().unwrap();
}
impl Runtime {
/// Create a new runtime instance with default configuration values.
///
@@ -287,6 +302,19 @@ impl Runtime {
self
}
/// Spawn a futures 0.2-style future onto the Tokio runtime.
///
/// Otherwise identical to `spawn`
#[cfg(feature = "unstable-futures")]
pub fn spawn2<F>(&mut self, future: F) -> &mut Self
where F: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
{
futures2::executor::Executor::spawn(
self.inner_mut().pool.sender_mut(), Box::new(future)
).unwrap();
self
}
/// Signals the runtime to shutdown once it becomes idle.
///
/// Returns a future that completes once the shutdown operation has
@@ -420,8 +448,30 @@ impl ::executor::Executor for TaskExecutor {
{
self.inner.spawn(future)
}
#[cfg(feature = "unstable-futures")]
fn spawn2(&mut self, future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
-> Result<(), futures2::executor::SpawnError>
{
self.inner.spawn2(future)
}
}
#[cfg(feature = "unstable-futures")]
type Task2 = Box<futures2::Future<Item = (), Error = futures2::Never> + Send>;
#[cfg(feature = "unstable-futures")]
impl futures2::executor::Executor for TaskExecutor {
fn spawn(&mut self, f: Task2) -> Result<(), futures2::executor::SpawnError> {
futures2::executor::Executor::spawn(&mut self.inner, f)
}
fn status(&self) -> Result<(), futures2::executor::SpawnError> {
futures2::executor::Executor::status(&self.inner)
}
}
// ===== impl Shutdown =====
impl Shutdown {