feat(lib): Upgrade to Tokio 1.0 (#2369)

Closes #2370
This commit is contained in:
Sean McArthur
2020-12-23 10:36:12 -08:00
committed by GitHub
parent dad5c8792f
commit fad42acc79
14 changed files with 71 additions and 146 deletions

View File

@@ -22,20 +22,20 @@ include = [
] ]
[dependencies] [dependencies]
bytes = "0.6" bytes = "1"
futures-core = { version = "0.3", default-features = false } futures-core = { version = "0.3", default-features = false }
futures-channel = "0.3" futures-channel = "0.3"
futures-util = { version = "0.3", default-features = false } futures-util = { version = "0.3", default-features = false }
http = "0.2" http = "0.2"
http-body = { git = "https://github.com/hyperium/http-body" } http-body = "0.4"
httpdate = "0.3" httpdate = "0.3"
httparse = "1.0" httparse = "1.0"
h2 = { git = "https://github.com/hyperium/h2", optional = true } h2 = { version = "0.3", optional = true }
itoa = "0.4.1" itoa = "0.4.1"
tracing = { version = "0.1", default-features = false, features = ["std"] } tracing = { version = "0.1", default-features = false, features = ["std"] }
pin-project = "1.0" pin-project = "1.0"
tower-service = "0.3" tower-service = "0.3"
tokio = { version = "0.3.4", features = ["sync", "stream"] } tokio = { version = "1", features = ["sync"] }
want = "0.3" want = "0.3"
# Optional # Optional
@@ -51,7 +51,7 @@ spmc = "0.3"
serde = "1.0" serde = "1.0"
serde_derive = "1.0" serde_derive = "1.0"
serde_json = "1.0" serde_json = "1.0"
tokio = { version = "0.3", features = [ tokio = { version = "1", features = [
"fs", "fs",
"macros", "macros",
"io-std", "io-std",
@@ -62,8 +62,8 @@ tokio = { version = "0.3", features = [
"time", "time",
"test-util", "test-util",
] } ] }
tokio-test = "0.3" tokio-test = "0.4"
tokio-util = { version = "0.5", features = ["codec"] } tokio-util = { version = "0.6", features = ["codec"] }
tower-util = "0.3" tower-util = "0.3"
url = "1.0" url = "1.0"

View File

@@ -23,7 +23,7 @@ where
let second = if let Some(buf) = body.data().await { let second = if let Some(buf) = body.data().await {
buf? buf?
} else { } else {
return Ok(first.copy_to_bytes(first.bytes().len())); return Ok(first.copy_to_bytes(first.remaining()));
}; };
// With more than 1 buf, we gotta flatten into a Vec first. // With more than 1 buf, we gotta flatten into a Vec first.

View File

@@ -667,8 +667,11 @@ impl ConnectingTcp<'_> {
let fallback_fut = fallback.remote.connect(self.config); let fallback_fut = fallback.remote.connect(self.config);
futures_util::pin_mut!(fallback_fut); futures_util::pin_mut!(fallback_fut);
let fallback_delay = fallback.delay;
futures_util::pin_mut!(fallback_delay);
let (result, future) = let (result, future) =
match futures_util::future::select(preferred_fut, fallback.delay).await { match futures_util::future::select(preferred_fut, fallback_delay).await {
Either::Left((result, _fallback_delay)) => { Either::Left((result, _fallback_delay)) => {
(result, Either::Right(fallback_fut)) (result, Either::Right(fallback_fut))
} }

View File

@@ -1,7 +1,7 @@
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
use std::future::Future; use std::future::Future;
use tokio::stream::Stream; use futures_util::FutureExt;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
use crate::common::{task, Pin, Poll}; use crate::common::{task, Pin, Poll};
@@ -150,8 +150,8 @@ impl<T, U> Receiver<T, U> {
self: Pin<&mut Self>, self: Pin<&mut Self>,
cx: &mut task::Context<'_>, cx: &mut task::Context<'_>,
) -> Poll<Option<(T, Callback<T, U>)>> { ) -> Poll<Option<(T, Callback<T, U>)>> {
let this = self.project(); let mut this = self.project();
match this.inner.poll_next(cx) { match this.inner.poll_recv(cx) {
Poll::Ready(item) => { Poll::Ready(item) => {
Poll::Ready(item.map(|mut env| env.0.take().expect("envelope not dropped"))) Poll::Ready(item.map(|mut env| env.0.take().expect("envelope not dropped")))
} }
@@ -170,9 +170,9 @@ impl<T, U> Receiver<T, U> {
#[cfg(feature = "http1")] #[cfg(feature = "http1")]
pub(crate) fn try_recv(&mut self) -> Option<(T, Callback<T, U>)> { pub(crate) fn try_recv(&mut self) -> Option<(T, Callback<T, U>)> {
match self.inner.try_recv() { match self.inner.recv().now_or_never() {
Ok(mut env) => env.0.take(), Some(Some(mut env)) => env.0.take(),
Err(_) => None, _ => None,
} }
} }
} }

View File

@@ -731,7 +731,6 @@ impl<T: Poolable + 'static> Future for IdleTask<T> {
type Output = (); type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
use tokio::stream::Stream;
let mut this = self.project(); let mut this = self.project();
loop { loop {
match this.pool_drop_notifier.as_mut().poll(cx) { match this.pool_drop_notifier.as_mut().poll(cx) {
@@ -743,7 +742,7 @@ impl<T: Poolable + 'static> Future for IdleTask<T> {
} }
} }
ready!(this.interval.as_mut().poll_next(cx)); ready!(this.interval.as_mut().poll_tick(cx));
if let Some(inner) = this.pool.upgrade() { if let Some(inner) = this.pool.upgrade() {
if let Ok(mut inner) = inner.lock() { if let Ok(mut inner) = inner.lock() {

View File

@@ -34,8 +34,8 @@ impl<T: Buf> Buf for BufList<T> {
} }
#[inline] #[inline]
fn bytes(&self) -> &[u8] { fn chunk(&self) -> &[u8] {
self.bufs.front().map(Buf::bytes).unwrap_or_default() self.bufs.front().map(Buf::chunk).unwrap_or_default()
} }
#[inline] #[inline]
@@ -57,13 +57,13 @@ impl<T: Buf> Buf for BufList<T> {
} }
#[inline] #[inline]
fn bytes_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize {
if dst.is_empty() { if dst.is_empty() {
return 0; return 0;
} }
let mut vecs = 0; let mut vecs = 0;
for buf in &self.bufs { for buf in &self.bufs {
vecs += buf.bytes_vectored(&mut dst[vecs..]); vecs += buf.chunks_vectored(&mut dst[vecs..]);
if vecs == dst.len() { if vecs == dst.len() {
break; break;
} }

View File

@@ -1,4 +1,3 @@
mod rewind; mod rewind;
pub(crate) use self::rewind::Rewind; pub(crate) use self::rewind::Rewind;
pub(crate) const MAX_WRITEV_BUFS: usize = 64;

View File

@@ -229,12 +229,12 @@ where
} }
#[inline] #[inline]
fn bytes(&self) -> &[u8] { fn chunk(&self) -> &[u8] {
match self.kind { match self.kind {
BufKind::Exact(ref b) => b.bytes(), BufKind::Exact(ref b) => b.chunk(),
BufKind::Limited(ref b) => b.bytes(), BufKind::Limited(ref b) => b.chunk(),
BufKind::Chunked(ref b) => b.bytes(), BufKind::Chunked(ref b) => b.chunk(),
BufKind::ChunkedEnd(ref b) => b.bytes(), BufKind::ChunkedEnd(ref b) => b.chunk(),
} }
} }
@@ -249,12 +249,12 @@ where
} }
#[inline] #[inline]
fn bytes_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize {
match self.kind { match self.kind {
BufKind::Exact(ref b) => b.bytes_vectored(dst), BufKind::Exact(ref b) => b.chunks_vectored(dst),
BufKind::Limited(ref b) => b.bytes_vectored(dst), BufKind::Limited(ref b) => b.chunks_vectored(dst),
BufKind::Chunked(ref b) => b.bytes_vectored(dst), BufKind::Chunked(ref b) => b.chunks_vectored(dst),
BufKind::ChunkedEnd(ref b) => b.bytes_vectored(dst), BufKind::ChunkedEnd(ref b) => b.chunks_vectored(dst),
} }
} }
} }
@@ -295,7 +295,7 @@ impl Buf for ChunkSize {
} }
#[inline] #[inline]
fn bytes(&self) -> &[u8] { fn chunk(&self) -> &[u8] {
&self.bytes[self.pos.into()..self.len.into()] &self.bytes[self.pos.into()..self.len.into()]
} }

View File

@@ -186,7 +186,7 @@ where
self.read_buf.reserve(next); self.read_buf.reserve(next);
} }
let dst = self.read_buf.bytes_mut(); let dst = self.read_buf.chunk_mut();
let dst = unsafe { &mut *(dst as *mut _ as *mut [MaybeUninit<u8>]) }; let dst = unsafe { &mut *(dst as *mut _ as *mut [MaybeUninit<u8>]) };
let mut buf = ReadBuf::uninit(dst); let mut buf = ReadBuf::uninit(dst);
match Pin::new(&mut self.io).poll_read(cx, &mut buf) { match Pin::new(&mut self.io).poll_read(cx, &mut buf) {
@@ -231,10 +231,11 @@ where
return self.poll_flush_flattened(cx); return self.poll_flush_flattened(cx);
} }
const MAX_WRITEV_BUFS: usize = 64;
loop { loop {
let n = { let n = {
let mut iovs = [IoSlice::new(&[]); crate::common::io::MAX_WRITEV_BUFS]; let mut iovs = [IoSlice::new(&[]); MAX_WRITEV_BUFS];
let len = self.write_buf.bytes_vectored(&mut iovs); let len = self.write_buf.chunks_vectored(&mut iovs);
ready!(Pin::new(&mut self.io).poll_write_vectored(cx, &iovs[..len]))? ready!(Pin::new(&mut self.io).poll_write_vectored(cx, &iovs[..len]))?
}; };
// TODO(eliza): we have to do this manually because // TODO(eliza): we have to do this manually because
@@ -262,7 +263,7 @@ where
/// that skips some bookkeeping around using multiple buffers. /// that skips some bookkeeping around using multiple buffers.
fn poll_flush_flattened(&mut self, cx: &mut task::Context<'_>) -> Poll<io::Result<()>> { fn poll_flush_flattened(&mut self, cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {
loop { loop {
let n = ready!(Pin::new(&mut self.io).poll_write(cx, self.write_buf.headers.bytes()))?; let n = ready!(Pin::new(&mut self.io).poll_write(cx, self.write_buf.headers.chunk()))?;
debug!("flushed {} bytes", n); debug!("flushed {} bytes", n);
self.write_buf.headers.advance(n); self.write_buf.headers.advance(n);
if self.write_buf.headers.remaining() == 0 { if self.write_buf.headers.remaining() == 0 {
@@ -433,7 +434,7 @@ impl<T: AsRef<[u8]>> Buf for Cursor<T> {
} }
#[inline] #[inline]
fn bytes(&self) -> &[u8] { fn chunk(&self) -> &[u8] {
&self.bytes.as_ref()[self.pos..] &self.bytes.as_ref()[self.pos..]
} }
@@ -487,7 +488,7 @@ where
//but accomplishes the same result. //but accomplishes the same result.
loop { loop {
let adv = { let adv = {
let slice = buf.bytes(); let slice = buf.chunk();
if slice.is_empty() { if slice.is_empty() {
return; return;
} }
@@ -534,12 +535,12 @@ impl<B: Buf> Buf for WriteBuf<B> {
} }
#[inline] #[inline]
fn bytes(&self) -> &[u8] { fn chunk(&self) -> &[u8] {
let headers = self.headers.bytes(); let headers = self.headers.chunk();
if !headers.is_empty() { if !headers.is_empty() {
headers headers
} else { } else {
self.queue.bytes() self.queue.chunk()
} }
} }
@@ -559,9 +560,9 @@ impl<B: Buf> Buf for WriteBuf<B> {
} }
#[inline] #[inline]
fn bytes_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize {
let n = self.headers.bytes_vectored(dst); let n = self.headers.chunks_vectored(dst);
self.queue.bytes_vectored(&mut dst[n..]) + n self.queue.chunks_vectored(&mut dst[n..]) + n
} }
} }

View File

@@ -257,8 +257,8 @@ impl<B: Buf> Buf for SendBuf<B> {
} }
#[inline] #[inline]
fn bytes(&self) -> &[u8] { fn chunk(&self) -> &[u8] {
self.0.as_ref().map(|b| b.bytes()).unwrap_or(&[]) self.0.as_ref().map(|b| b.chunk()).unwrap_or(&[])
} }
#[inline] #[inline]
@@ -268,7 +268,7 @@ impl<B: Buf> Buf for SendBuf<B> {
} }
} }
fn bytes_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize { fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize {
self.0.as_ref().map(|b| b.bytes_vectored(dst)).unwrap_or(0) self.0.as_ref().map(|b| b.chunks_vectored(dst)).unwrap_or(0)
} }
} }

View File

@@ -60,7 +60,7 @@ pub(super) fn channel(ping_pong: PingPong, config: Config) -> (Recorder, Ponger)
interval, interval,
timeout: config.keep_alive_timeout, timeout: config.keep_alive_timeout,
while_idle: config.keep_alive_while_idle, while_idle: config.keep_alive_while_idle,
timer: tokio::time::sleep(interval), timer: Box::pin(tokio::time::sleep(interval)),
state: KeepAliveState::Init, state: KeepAliveState::Init,
}); });
@@ -156,7 +156,7 @@ struct KeepAlive {
while_idle: bool, while_idle: bool,
state: KeepAliveState, state: KeepAliveState,
timer: Sleep, timer: Pin<Box<Sleep>>,
} }
#[cfg(feature = "runtime")] #[cfg(feature = "runtime")]
@@ -441,7 +441,7 @@ impl KeepAlive {
self.state = KeepAliveState::Scheduled; self.state = KeepAliveState::Scheduled;
let interval = shared.last_read_at() + self.interval; let interval = shared.last_read_at() + self.interval;
self.timer.reset(interval); self.timer.as_mut().reset(interval);
} }
KeepAliveState::PingSent => { KeepAliveState::PingSent => {
if shared.is_ping_sent() { if shared.is_ping_sent() {
@@ -450,7 +450,7 @@ impl KeepAlive {
self.state = KeepAliveState::Scheduled; self.state = KeepAliveState::Scheduled;
let interval = shared.last_read_at() + self.interval; let interval = shared.last_read_at() + self.interval;
self.timer.reset(interval); self.timer.as_mut().reset(interval);
} }
KeepAliveState::Scheduled => (), KeepAliveState::Scheduled => (),
} }
@@ -472,7 +472,7 @@ impl KeepAlive {
shared.send_ping(); shared.send_ping();
self.state = KeepAliveState::PingSent; self.state = KeepAliveState::PingSent;
let timeout = Instant::now() + self.timeout; let timeout = Instant::now() + self.timeout;
self.timer.reset(timeout); self.timer.as_mut().reset(timeout);
} }
KeepAliveState::Init | KeepAliveState::PingSent => (), KeepAliveState::Init | KeepAliveState::PingSent => (),
} }

View File

@@ -19,7 +19,7 @@ pub struct AddrIncoming {
sleep_on_errors: bool, sleep_on_errors: bool,
tcp_keepalive_timeout: Option<Duration>, tcp_keepalive_timeout: Option<Duration>,
tcp_nodelay: bool, tcp_nodelay: bool,
timeout: Option<Sleep>, timeout: Option<Pin<Box<Sleep>>>,
} }
impl AddrIncoming { impl AddrIncoming {
@@ -160,9 +160,9 @@ impl AddrIncoming {
error!("accept error: {}", e); error!("accept error: {}", e);
// Sleep 1s. // Sleep 1s.
let mut timeout = tokio::time::sleep(Duration::from_secs(1)); let mut timeout = Box::pin(tokio::time::sleep(Duration::from_secs(1)));
match Pin::new(&mut timeout).poll(cx) { match timeout.as_mut().poll(cx) {
Poll::Ready(()) => { Poll::Ready(()) => {
// Wow, it's been a second already? Ok then... // Wow, it's been a second already? Ok then...
continue; continue;
@@ -263,7 +263,7 @@ mod addr_stream {
pub fn poll_peek( pub fn poll_peek(
&mut self, &mut self,
cx: &mut task::Context<'_>, cx: &mut task::Context<'_>,
buf: &mut [u8], buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.inner.poll_peek(cx, buf) self.inner.poll_peek(cx, buf)
} }

View File

@@ -11,7 +11,7 @@ use std::fmt;
use std::io; use std::io;
use std::marker::Unpin; use std::marker::Unpin;
use bytes::{Buf, Bytes}; use bytes::Bytes;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::sync::oneshot; use tokio::sync::oneshot;
@@ -82,7 +82,7 @@ impl Upgraded {
T: AsyncRead + AsyncWrite + Unpin + Send + 'static, T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{ {
Upgraded { Upgraded {
io: Rewind::new_buffered(Box::new(ForwardsWriteBuf(io)), read_buf), io: Rewind::new_buffered(Box::new(io), read_buf),
} }
} }
@@ -92,9 +92,9 @@ impl Upgraded {
/// `Upgraded` back. /// `Upgraded` back.
pub fn downcast<T: AsyncRead + AsyncWrite + Unpin + 'static>(self) -> Result<Parts<T>, Self> { pub fn downcast<T: AsyncRead + AsyncWrite + Unpin + 'static>(self) -> Result<Parts<T>, Self> {
let (io, buf) = self.io.into_inner(); let (io, buf) = self.io.into_inner();
match io.__hyper_downcast::<ForwardsWriteBuf<T>>() { match io.__hyper_downcast() {
Ok(t) => Ok(Parts { Ok(t) => Ok(Parts {
io: t.0, io: *t,
read_buf: buf, read_buf: buf,
_inner: (), _inner: (),
}), }),
@@ -221,20 +221,14 @@ impl StdError for UpgradeExpected {}
// ===== impl Io ===== // ===== impl Io =====
struct ForwardsWriteBuf<T>(T);
pub(crate) trait Io: AsyncRead + AsyncWrite + Unpin + 'static { pub(crate) trait Io: AsyncRead + AsyncWrite + Unpin + 'static {
fn poll_write_dyn_buf(
&mut self,
cx: &mut task::Context<'_>,
buf: &mut dyn Buf,
) -> Poll<io::Result<usize>>;
fn __hyper_type_id(&self) -> TypeId { fn __hyper_type_id(&self) -> TypeId {
TypeId::of::<Self>() TypeId::of::<Self>()
} }
} }
impl<T: AsyncRead + AsyncWrite + Unpin + 'static> Io for T {}
impl dyn Io + Send { impl dyn Io + Send {
fn __hyper_is<T: Io>(&self) -> bool { fn __hyper_is<T: Io>(&self) -> bool {
let t = TypeId::of::<T>(); let t = TypeId::of::<T>();
@@ -254,61 +248,6 @@ impl dyn Io + Send {
} }
} }
impl<T: AsyncRead + Unpin> AsyncRead for ForwardsWriteBuf<T> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_read(cx, buf)
}
}
impl<T: AsyncWrite + Unpin> AsyncWrite for ForwardsWriteBuf<T> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.0).poll_write(cx, buf)
}
fn poll_write_vectored(
mut self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.0).poll_write_vectored(cx, bufs)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_shutdown(cx)
}
fn is_write_vectored(&self) -> bool {
self.0.is_write_vectored()
}
}
impl<T: AsyncRead + AsyncWrite + Unpin + 'static> Io for ForwardsWriteBuf<T> {
fn poll_write_dyn_buf(
&mut self,
cx: &mut task::Context<'_>,
buf: &mut dyn Buf,
) -> Poll<io::Result<usize>> {
if self.0.is_write_vectored() {
let mut bufs = [io::IoSlice::new(&[]); crate::common::io::MAX_WRITEV_BUFS];
let cnt = buf.bytes_vectored(&mut bufs);
return Pin::new(&mut self.0).poll_write_vectored(cx, &bufs[..cnt]);
}
Pin::new(&mut self.0).poll_write(cx, buf.bytes())
}
}
mod sealed { mod sealed {
use super::OnUpgrade; use super::OnUpgrade;
@@ -352,7 +291,6 @@ mod sealed {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use tokio::io::AsyncWriteExt;
#[test] #[test]
fn upgraded_downcast() { fn upgraded_downcast() {
@@ -363,15 +301,6 @@ mod tests {
upgraded.downcast::<Mock>().unwrap(); upgraded.downcast::<Mock>().unwrap();
} }
#[tokio::test]
async fn upgraded_forwards_write_buf() {
// sanity check that the underlying IO implements write_buf
Mock.write_buf(&mut "hello".as_bytes()).await.unwrap();
let mut upgraded = Upgraded::new(Mock, Bytes::new());
upgraded.write_buf(&mut "hello".as_bytes()).await.unwrap();
}
// TODO: replace with tokio_test::io when it can test write_buf // TODO: replace with tokio_test::io when it can test write_buf
struct Mock; struct Mock;
@@ -395,17 +324,6 @@ mod tests {
Poll::Ready(Ok(buf.len())) Poll::Ready(Ok(buf.len()))
} }
// TODO(eliza): :(
// fn poll_write_buf<B: Buf>(
// self: Pin<&mut Self>,
// _cx: &mut task::Context<'_>,
// buf: &mut B,
// ) -> Poll<io::Result<usize>> {
// let n = buf.remaining();
// buf.advance(n);
// Poll::Ready(Ok(n))
// }
fn poll_flush(self: Pin<&mut Self>, _cx: &mut task::Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(self: Pin<&mut Self>, _cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {
unreachable!("Mock::poll_flush") unreachable!("Mock::poll_flush")
} }

View File

@@ -1209,6 +1209,7 @@ mod dispatch_impl {
// and wait a few ticks for the connections to close // and wait a few ticks for the connections to close
let t = tokio::time::sleep(Duration::from_millis(100)).map(|_| panic!("time out")); let t = tokio::time::sleep(Duration::from_millis(100)).map(|_| panic!("time out"));
futures_util::pin_mut!(t);
let close = closes.into_future().map(|(opt, _)| opt.expect("closes")); let close = closes.into_future().map(|(opt, _)| opt.expect("closes"));
future::select(t, close).await; future::select(t, close).await;
} }
@@ -1257,6 +1258,7 @@ mod dispatch_impl {
// res now dropped // res now dropped
let t = tokio::time::sleep(Duration::from_millis(100)).map(|_| panic!("time out")); let t = tokio::time::sleep(Duration::from_millis(100)).map(|_| panic!("time out"));
futures_util::pin_mut!(t);
let close = closes.into_future().map(|(opt, _)| opt.expect("closes")); let close = closes.into_future().map(|(opt, _)| opt.expect("closes"));
future::select(t, close).await; future::select(t, close).await;
} }
@@ -1312,6 +1314,7 @@ mod dispatch_impl {
// and wait a few ticks to see the connection drop // and wait a few ticks to see the connection drop
let t = tokio::time::sleep(Duration::from_millis(100)).map(|_| panic!("time out")); let t = tokio::time::sleep(Duration::from_millis(100)).map(|_| panic!("time out"));
futures_util::pin_mut!(t);
let close = closes.into_future().map(|(opt, _)| opt.expect("closes")); let close = closes.into_future().map(|(opt, _)| opt.expect("closes"));
future::select(t, close).await; future::select(t, close).await;
} }
@@ -1362,6 +1365,7 @@ mod dispatch_impl {
res.unwrap(); res.unwrap();
let t = tokio::time::sleep(Duration::from_millis(100)).map(|_| panic!("time out")); let t = tokio::time::sleep(Duration::from_millis(100)).map(|_| panic!("time out"));
futures_util::pin_mut!(t);
let close = closes.into_future().map(|(opt, _)| opt.expect("closes")); let close = closes.into_future().map(|(opt, _)| opt.expect("closes"));
future::select(t, close).await; future::select(t, close).await;
} }
@@ -1408,6 +1412,7 @@ mod dispatch_impl {
res.unwrap(); res.unwrap();
let t = tokio::time::sleep(Duration::from_millis(100)).map(|_| panic!("time out")); let t = tokio::time::sleep(Duration::from_millis(100)).map(|_| panic!("time out"));
futures_util::pin_mut!(t);
let close = closes.into_future().map(|(opt, _)| opt.expect("closes")); let close = closes.into_future().map(|(opt, _)| opt.expect("closes"));
future::select(t, close).await; future::select(t, close).await;
} }