diff --git a/.circleci/config.yml b/.circleci/config.yml index db8afb59..22ffd304 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -37,6 +37,9 @@ jobs: - run: name: Run tests, inside a docker image, with all features command: docker run --rm -v "/cache/cargo/registry:/usr/local/cargo/registry" -v "/cache/target:/app/target" -it rust-libp2p cargo test --all --all-features + - run: + name: Try the async-await feature + command: docker run --rm -v "/cache/cargo/registry:/usr/local/cargo/registry" -v "/cache/target:/app/target" -it rust-libp2p cargo +nightly test --package libp2p-core --all-features - save_cache: key: test-cache paths: @@ -48,7 +51,7 @@ jobs: steps: - checkout - restore_cache: - keys: + keys: - test-wasm-cache-{{ epoch }} - test-wasm-cache - run: diff --git a/Cargo.toml b/Cargo.toml index cbef55bd..c7e39870 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,3 +78,7 @@ members = [ "transports/websocket", "transports/wasm-ext" ] + +# TODO: remove after https://github.com/matthunz/futures-codec/issues/22 +[patch.crates-io] +futures_codec = { git = "https://github.com/matthunz/futures-codec" } diff --git a/core/Cargo.toml b/core/Cargo.toml index d8f44e43..f0628340 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,12 +16,13 @@ bytes = "0.4" ed25519-dalek = "1.0.0-pre.1" failure = "0.1" fnv = "1.0" +futures-timer = "0.3" lazy_static = "1.2" log = "0.4" multiaddr = { package = "parity-multiaddr", version = "0.5.0", path = "../misc/multiaddr" } multihash = { package = "parity-multihash", version = "0.1.0", path = "../misc/multihash" } multistream-select = { version = "0.5.0", path = "../misc/multistream-select" } -futures = "0.1" +futures-preview = { version = "0.3.0-alpha.17", features = ["compat", "io-compat"] } parking_lot = "0.8" protobuf = "2.3" quick-error = "1.2" @@ -30,8 +31,6 @@ rw-stream-sink = { version = "0.1.1", path = "../misc/rw-stream-sink" } libsecp256k1 = { version = "0.2.2", optional = true } sha2 = "0.8.0" smallvec = "0.6" -tokio-executor = "0.1.4" -tokio-io = "0.1" wasm-timer = "0.1" unsigned-varint = "0.2" void = "1" @@ -42,6 +41,7 @@ ring = { version = "0.14", features = ["use_heap"], default-features = false } untrusted = { version = "0.6" } [dev-dependencies] +async-std = "0.99" libp2p-swarm = { version = "0.2.0", path = "../swarm" } libp2p-tcp = { version = "0.12.0", path = "../transports/tcp" } libp2p-mplex = { version = "0.12.0", path = "../muxers/mplex" } @@ -56,4 +56,4 @@ tokio-mock-task = "0.1" [features] default = ["secp256k1"] secp256k1 = ["libsecp256k1"] - +async-await = [] diff --git a/core/src/either.rs b/core/src/either.rs index d17f8bb7..b81691a3 100644 --- a/core/src/either.rs +++ b/core/src/either.rs @@ -19,9 +19,8 @@ // DEALINGS IN THE SOFTWARE. use crate::{muxing::StreamMuxer, ProtocolName, transport::ListenerEvent}; -use futures::prelude::*; -use std::{fmt, io::{Error as IoError, Read, Write}}; -use tokio_io::{AsyncRead, AsyncWrite}; +use futures::{prelude::*, io::Initializer}; +use std::{fmt, io::{Error as IoError, Read, Write}, pin::Pin, task::Context, task::Poll}; #[derive(Debug, Copy, Clone)] pub enum EitherError { @@ -65,24 +64,25 @@ pub enum EitherOutput { impl AsyncRead for EitherOutput where - A: AsyncRead, - B: AsyncRead, + A: AsyncRead + Unpin, + B: AsyncRead + Unpin, { - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { + unsafe fn initializer(&self) -> Initializer { match self { - EitherOutput::First(a) => a.prepare_uninitialized_buffer(buf), - EitherOutput::Second(b) => b.prepare_uninitialized_buffer(buf), + EitherOutput::First(a) => a.initializer(), + EitherOutput::Second(b) => b.initializer(), } } - fn read_buf(&mut self, buf: &mut Bu) -> Poll { - match self { - EitherOutput::First(a) => a.read_buf(buf), - EitherOutput::Second(b) => b.read_buf(buf), + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll> { + match &mut *self { + EitherOutput::First(a) => AsyncRead::poll_read(Pin::new(a), cx, buf), + EitherOutput::Second(b) => AsyncRead::poll_read(Pin::new(b), cx, buf), } } } +// TODO: remove? impl Read for EitherOutput where A: Read, @@ -98,17 +98,32 @@ where impl AsyncWrite for EitherOutput where - A: AsyncWrite, - B: AsyncWrite, + A: AsyncWrite + Unpin, + B: AsyncWrite + Unpin, { - fn shutdown(&mut self) -> Poll<(), IoError> { - match self { - EitherOutput::First(a) => a.shutdown(), - EitherOutput::Second(b) => b.shutdown(), + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + match &mut *self { + EitherOutput::First(a) => AsyncWrite::poll_write(Pin::new(a), cx, buf), + EitherOutput::Second(b) => AsyncWrite::poll_write(Pin::new(b), cx, buf), + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + match &mut *self { + EitherOutput::First(a) => AsyncWrite::poll_flush(Pin::new(a), cx), + EitherOutput::Second(b) => AsyncWrite::poll_flush(Pin::new(b), cx), + } + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + match &mut *self { + EitherOutput::First(a) => AsyncWrite::poll_close(Pin::new(a), cx), + EitherOutput::Second(b) => AsyncWrite::poll_close(Pin::new(b), cx), } } } +// TODO: remove? impl Write for EitherOutput where A: Write, @@ -131,46 +146,53 @@ where impl Stream for EitherOutput where - A: Stream, - B: Stream, + A: TryStream + Unpin, + B: TryStream + Unpin, { - type Item = I; - type Error = EitherError; + type Item = Result>; - fn poll(&mut self) -> Poll, Self::Error> { - match self { - EitherOutput::First(a) => a.poll().map_err(EitherError::A), - EitherOutput::Second(b) => b.poll().map_err(EitherError::B), + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + match &mut *self { + EitherOutput::First(a) => TryStream::try_poll_next(Pin::new(a), cx) + .map(|v| v.map(|r| r.map_err(EitherError::A))), + EitherOutput::Second(b) => TryStream::try_poll_next(Pin::new(b), cx) + .map(|v| v.map(|r| r.map_err(EitherError::B))), } } } -impl Sink for EitherOutput +impl Sink for EitherOutput where - A: Sink, - B: Sink, + A: Sink + Unpin, + B: Sink + Unpin, { - type SinkItem = I; - type SinkError = EitherError; + type Error = EitherError; - fn start_send(&mut self, item: Self::SinkItem) -> StartSend { - match self { - EitherOutput::First(a) => a.start_send(item).map_err(EitherError::A), - EitherOutput::Second(b) => b.start_send(item).map_err(EitherError::B), + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + match &mut *self { + EitherOutput::First(a) => Sink::poll_ready(Pin::new(a), cx).map_err(EitherError::A), + EitherOutput::Second(b) => Sink::poll_ready(Pin::new(b), cx).map_err(EitherError::B), } } - fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { - match self { - EitherOutput::First(a) => a.poll_complete().map_err(EitherError::A), - EitherOutput::Second(b) => b.poll_complete().map_err(EitherError::B), + fn start_send(mut self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> { + match &mut *self { + EitherOutput::First(a) => Sink::start_send(Pin::new(a), item).map_err(EitherError::A), + EitherOutput::Second(b) => Sink::start_send(Pin::new(b), item).map_err(EitherError::B), } } - fn close(&mut self) -> Poll<(), Self::SinkError> { - match self { - EitherOutput::First(a) => a.close().map_err(EitherError::A), - EitherOutput::Second(b) => b.close().map_err(EitherError::B), + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + match &mut *self { + EitherOutput::First(a) => Sink::poll_flush(Pin::new(a), cx).map_err(EitherError::A), + EitherOutput::Second(b) => Sink::poll_flush(Pin::new(b), cx).map_err(EitherError::B), + } + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + match &mut *self { + EitherOutput::First(a) => Sink::poll_close(Pin::new(a), cx).map_err(EitherError::A), + EitherOutput::Second(b) => Sink::poll_close(Pin::new(b), cx).map_err(EitherError::B), } } } @@ -184,10 +206,10 @@ where type OutboundSubstream = EitherOutbound; type Error = IoError; - fn poll_inbound(&self) -> Poll { + fn poll_inbound(&self, cx: &mut Context) -> Poll> { match self { - EitherOutput::First(inner) => inner.poll_inbound().map(|p| p.map(EitherOutput::First)).map_err(|e| e.into()), - EitherOutput::Second(inner) => inner.poll_inbound().map(|p| p.map(EitherOutput::Second)).map_err(|e| e.into()), + EitherOutput::First(inner) => inner.poll_inbound(cx).map(|p| p.map(EitherOutput::First)).map_err(|e| e.into()), + EitherOutput::Second(inner) => inner.poll_inbound(cx).map(|p| p.map(EitherOutput::Second)).map_err(|e| e.into()), } } @@ -198,13 +220,13 @@ where } } - fn poll_outbound(&self, substream: &mut Self::OutboundSubstream) -> Poll { + fn poll_outbound(&self, cx: &mut Context, substream: &mut Self::OutboundSubstream) -> Poll> { match (self, substream) { (EitherOutput::First(ref inner), EitherOutbound::A(ref mut substream)) => { - inner.poll_outbound(substream).map(|p| p.map(EitherOutput::First)).map_err(|e| e.into()) + inner.poll_outbound(cx, substream).map(|p| p.map(EitherOutput::First)).map_err(|e| e.into()) }, (EitherOutput::Second(ref inner), EitherOutbound::B(ref mut substream)) => { - inner.poll_outbound(substream).map(|p| p.map(EitherOutput::Second)).map_err(|e| e.into()) + inner.poll_outbound(cx, substream).map(|p| p.map(EitherOutput::Second)).map_err(|e| e.into()) }, _ => panic!("Wrong API usage") } @@ -227,56 +249,56 @@ where } } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { + unsafe fn initializer(&self) -> Initializer { match self { - EitherOutput::First(ref inner) => inner.prepare_uninitialized_buffer(buf), - EitherOutput::Second(ref inner) => inner.prepare_uninitialized_buffer(buf), + EitherOutput::First(ref inner) => inner.initializer(), + EitherOutput::Second(ref inner) => inner.initializer(), } } - fn read_substream(&self, sub: &mut Self::Substream, buf: &mut [u8]) -> Poll { + fn read_substream(&self, cx: &mut Context, sub: &mut Self::Substream, buf: &mut [u8]) -> Poll> { match (self, sub) { (EitherOutput::First(ref inner), EitherOutput::First(ref mut sub)) => { - inner.read_substream(sub, buf).map_err(|e| e.into()) + inner.read_substream(cx, sub, buf).map_err(|e| e.into()) }, (EitherOutput::Second(ref inner), EitherOutput::Second(ref mut sub)) => { - inner.read_substream(sub, buf).map_err(|e| e.into()) + inner.read_substream(cx, sub, buf).map_err(|e| e.into()) }, _ => panic!("Wrong API usage") } } - fn write_substream(&self, sub: &mut Self::Substream, buf: &[u8]) -> Poll { + fn write_substream(&self, cx: &mut Context, sub: &mut Self::Substream, buf: &[u8]) -> Poll> { match (self, sub) { (EitherOutput::First(ref inner), EitherOutput::First(ref mut sub)) => { - inner.write_substream(sub, buf).map_err(|e| e.into()) + inner.write_substream(cx, sub, buf).map_err(|e| e.into()) }, (EitherOutput::Second(ref inner), EitherOutput::Second(ref mut sub)) => { - inner.write_substream(sub, buf).map_err(|e| e.into()) + inner.write_substream(cx, sub, buf).map_err(|e| e.into()) }, _ => panic!("Wrong API usage") } } - fn flush_substream(&self, sub: &mut Self::Substream) -> Poll<(), Self::Error> { + fn flush_substream(&self, cx: &mut Context, sub: &mut Self::Substream) -> Poll> { match (self, sub) { (EitherOutput::First(ref inner), EitherOutput::First(ref mut sub)) => { - inner.flush_substream(sub).map_err(|e| e.into()) + inner.flush_substream(cx, sub).map_err(|e| e.into()) }, (EitherOutput::Second(ref inner), EitherOutput::Second(ref mut sub)) => { - inner.flush_substream(sub).map_err(|e| e.into()) + inner.flush_substream(cx, sub).map_err(|e| e.into()) }, _ => panic!("Wrong API usage") } } - fn shutdown_substream(&self, sub: &mut Self::Substream) -> Poll<(), Self::Error> { + fn shutdown_substream(&self, cx: &mut Context, sub: &mut Self::Substream) -> Poll> { match (self, sub) { (EitherOutput::First(ref inner), EitherOutput::First(ref mut sub)) => { - inner.shutdown_substream(sub).map_err(|e| e.into()) + inner.shutdown_substream(cx, sub).map_err(|e| e.into()) }, (EitherOutput::Second(ref inner), EitherOutput::Second(ref mut sub)) => { - inner.shutdown_substream(sub).map_err(|e| e.into()) + inner.shutdown_substream(cx, sub).map_err(|e| e.into()) }, _ => panic!("Wrong API usage") } @@ -306,17 +328,17 @@ where } } - fn close(&self) -> Poll<(), Self::Error> { + fn close(&self, cx: &mut Context) -> Poll> { match self { - EitherOutput::First(inner) => inner.close().map_err(|e| e.into()), - EitherOutput::Second(inner) => inner.close().map_err(|e| e.into()), + EitherOutput::First(inner) => inner.close(cx).map_err(|e| e.into()), + EitherOutput::Second(inner) => inner.close(cx).map_err(|e| e.into()), } } - fn flush_all(&self) -> Poll<(), Self::Error> { + fn flush_all(&self, cx: &mut Context) -> Poll> { match self { - EitherOutput::First(inner) => inner.flush_all().map_err(|e| e.into()), - EitherOutput::Second(inner) => inner.flush_all().map_err(|e| e.into()), + EitherOutput::First(inner) => inner.flush_all(cx).map_err(|e| e.into()), + EitherOutput::Second(inner) => inner.flush_all(cx).map_err(|e| e.into()), } } } @@ -338,20 +360,25 @@ pub enum EitherListenStream { impl Stream for EitherListenStream where - AStream: Stream>, - BStream: Stream>, + AStream: TryStream> + Unpin, + BStream: TryStream> + Unpin, { - type Item = ListenerEvent>; - type Error = EitherError; + type Item = Result>, EitherError>; - fn poll(&mut self) -> Poll, Self::Error> { - match self { - EitherListenStream::First(a) => a.poll() - .map(|i| (i.map(|v| (v.map(|e| e.map(EitherFuture::First)))))) - .map_err(EitherError::A), - EitherListenStream::Second(a) => a.poll() - .map(|i| (i.map(|v| (v.map(|e| e.map(EitherFuture::Second)))))) - .map_err(EitherError::B), + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + match &mut *self { + EitherListenStream::First(a) => match TryStream::try_poll_next(Pin::new(a), cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Ready(None), + Poll::Ready(Some(Ok(le))) => Poll::Ready(Some(Ok(le.map(EitherFuture::First)))), + Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(EitherError::A(err)))), + }, + EitherListenStream::Second(a) => match TryStream::try_poll_next(Pin::new(a), cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Ready(None), + Poll::Ready(Some(Ok(le))) => Poll::Ready(Some(Ok(le.map(EitherFuture::Second)))), + Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(EitherError::B(err)))), + }, } } } @@ -366,16 +393,17 @@ pub enum EitherFuture { impl Future for EitherFuture where - AFuture: Future, - BFuture: Future, + AFuture: TryFuture + Unpin, + BFuture: TryFuture + Unpin, { - type Item = EitherOutput; - type Error = EitherError; + type Output = Result, EitherError>; - fn poll(&mut self) -> Poll { - match self { - EitherFuture::First(a) => a.poll().map(|v| v.map(EitherOutput::First)).map_err(EitherError::A), - EitherFuture::Second(a) => a.poll().map(|v| v.map(EitherOutput::Second)).map_err(EitherError::B), + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match &mut *self { + EitherFuture::First(a) => TryFuture::try_poll(Pin::new(a), cx) + .map_ok(EitherOutput::First).map_err(EitherError::A), + EitherFuture::Second(a) => TryFuture::try_poll(Pin::new(a), cx) + .map_ok(EitherOutput::Second).map_err(EitherError::B), } } } @@ -386,21 +414,17 @@ pub enum EitherFuture2 { A(A), B(B) } impl Future for EitherFuture2 where - AFut: Future, - BFut: Future + AFut: TryFuture + Unpin, + BFut: TryFuture + Unpin, { - type Item = EitherOutput; - type Error = EitherError; + type Output = Result, EitherError>; - fn poll(&mut self) -> Poll { - match self { - EitherFuture2::A(a) => a.poll() - .map(|v| v.map(EitherOutput::First)) - .map_err(EitherError::A), - - EitherFuture2::B(b) => b.poll() - .map(|v| v.map(EitherOutput::Second)) - .map_err(EitherError::B) + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match &mut *self { + EitherFuture2::A(a) => TryFuture::try_poll(Pin::new(a), cx) + .map_ok(EitherOutput::First).map_err(EitherError::A), + EitherFuture2::B(a) => TryFuture::try_poll(Pin::new(a), cx) + .map_ok(EitherOutput::Second).map_err(EitherError::B), } } } diff --git a/core/src/lib.rs b/core/src/lib.rs index c3276415..471e928f 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -18,6 +18,8 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#![cfg_attr(feature = "async-await", feature(async_await))] + //! Transports, upgrades, multiplexing and node handling of *libp2p*. //! //! The main concepts of libp2p-core are: @@ -37,15 +39,12 @@ /// Multi-address re-export. pub use multiaddr; -pub use multistream_select::Negotiated; +pub type Negotiated = futures::compat::Compat01As03>>; mod keys_proto; mod peer_id; mod translation; -#[cfg(test)] -mod tests; - pub mod either; pub mod identity; pub mod muxing; diff --git a/core/src/muxing.rs b/core/src/muxing.rs index 28245666..0ed2068a 100644 --- a/core/src/muxing.rs +++ b/core/src/muxing.rs @@ -52,13 +52,9 @@ //! implementation of `StreamMuxer` to control everything that happens on the wire. use fnv::FnvHashMap; -use futures::{future, prelude::*, try_ready}; +use futures::{future, prelude::*, io::Initializer, task::Context, task::Poll}; use parking_lot::Mutex; -use std::io::{self, Read, Write}; -use std::ops::Deref; -use std::fmt; -use std::sync::atomic::{AtomicUsize, Ordering}; -use tokio_io::{AsyncRead, AsyncWrite}; +use std::{io, ops::Deref, fmt, pin::Pin, sync::atomic::{AtomicUsize, Ordering}}; pub use self::singleton::SingletonMuxer; @@ -90,12 +86,12 @@ pub trait StreamMuxer { /// /// This function behaves the same as a `Stream`. /// - /// If `NotReady` is returned, then the current task will be notified once the muxer + /// If `Pending` is returned, then the current task will be notified once the muxer /// is ready to be polled, similar to the API of `Stream::poll()`. /// Only the latest task that was used to call this method may be notified. /// /// An error can be generated if the connection has been closed. - fn poll_inbound(&self) -> Poll; + fn poll_inbound(&self, cx: &mut Context) -> Poll>; /// Opens a new outgoing substream, and produces the equivalent to a future that will be /// resolved when it becomes available. @@ -106,22 +102,23 @@ pub trait StreamMuxer { /// Polls the outbound substream. /// - /// If `NotReady` is returned, then the current task will be notified once the substream + /// If `Pending` is returned, then the current task will be notified once the substream /// is ready to be polled, similar to the API of `Future::poll()`. /// However, for each individual outbound substream, only the latest task that was used to /// call this method may be notified. /// /// May panic or produce an undefined result if an earlier polling of the same substream /// returned `Ready` or `Err`. - fn poll_outbound(&self, s: &mut Self::OutboundSubstream) -> Poll; + fn poll_outbound(&self, cx: &mut Context, s: &mut Self::OutboundSubstream) + -> Poll>; /// Destroys an outbound substream future. Use this after the outbound substream has finished, /// or if you want to interrupt it. fn destroy_outbound(&self, s: Self::OutboundSubstream); - /// Reads data from a substream. The behaviour is the same as `tokio_io::AsyncRead::poll_read`. + /// Reads data from a substream. The behaviour is the same as `futures::AsyncRead::poll_read`. /// - /// If `NotReady` is returned, then the current task will be notified once the substream + /// If `Pending` is returned, then the current task will be notified once the substream /// is ready to be read. However, for each individual substream, only the latest task that /// was used to call this method may be notified. /// @@ -130,25 +127,17 @@ pub trait StreamMuxer { /// /// An error can be generated if the connection has been closed, or if a protocol misbehaviour /// happened. - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll; + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) + -> Poll>; - /// Mimics the `prepare_uninitialized_buffer` method of the `AsyncRead` trait. - /// - /// This function isn't actually unsafe to call but unsafe to implement. The implementer must - /// ensure that either the whole buf has been zeroed or that `read_substream` overwrites the - /// buffer without reading it and returns correct value. - /// - /// If this function returns true, then the memory has been zeroed out. This allows - /// implementations of `AsyncRead` which are composed of multiple subimplementations to - /// efficiently implement `prepare_uninitialized_buffer`. - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - for b in buf.iter_mut() { *b = 0; } - true + /// Mimics the `initializer` method of the `AsyncRead` trait. + unsafe fn initializer(&self) -> Initializer { + Initializer::zeroing() } - /// Write data to a substream. The behaviour is the same as `tokio_io::AsyncWrite::poll_write`. + /// Write data to a substream. The behaviour is the same as `futures::AsyncWrite::poll_write`. /// - /// If `NotReady` is returned, then the current task will be notified once the substream + /// If `Pending` is returned, then the current task will be notified once the substream /// is ready to be read. For each individual substream, only the latest task that was used to /// call this method may be notified. /// @@ -157,24 +146,26 @@ pub trait StreamMuxer { /// /// It is incorrect to call this method on a substream if you called `shutdown_substream` on /// this substream earlier. - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll; + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) + -> Poll>; - /// Flushes a substream. The behaviour is the same as `tokio_io::AsyncWrite::poll_flush`. + /// Flushes a substream. The behaviour is the same as `futures::AsyncWrite::poll_flush`. /// /// After this method has been called, data written earlier on the substream is guaranteed to /// be received by the remote. /// - /// If `NotReady` is returned, then the current task will be notified once the substream + /// If `Pending` is returned, then the current task will be notified once the substream /// is ready to be read. For each individual substream, only the latest task that was used to /// call this method may be notified. /// /// > **Note**: This method may be implemented as a call to `flush_all`. - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error>; + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) + -> Poll>; /// Attempts to shut down the writing side of a substream. The behaviour is similar to - /// `tokio_io::AsyncWrite::shutdown`. + /// `AsyncWrite::poll_close`. /// - /// Contrary to `AsyncWrite::shutdown`, shutting down a substream does not imply + /// Contrary to `AsyncWrite::poll_close`, shutting down a substream does not imply /// `flush_substream`. If you want to make sure that the remote is immediately informed about /// the shutdown, use `flush_substream` or `flush_all`. /// @@ -182,7 +173,8 @@ pub trait StreamMuxer { /// /// An error can be generated if the connection has been closed, or if a protocol misbehaviour /// happened. - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error>; + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) + -> Poll>; /// Destroys a substream. fn destroy_substream(&self, s: Self::Substream); @@ -197,7 +189,7 @@ pub trait StreamMuxer { /// Closes this `StreamMuxer`. /// - /// After this has returned `Ok(Async::Ready(()))`, the muxer has become useless. All + /// After this has returned `Poll::Ready(Ok(()))`, the muxer has become useless. All /// subsequent reads must return either `EOF` or an error. All subsequent writes, shutdowns, /// or polls must generate an error or be ignored. /// @@ -207,14 +199,14 @@ pub trait StreamMuxer { /// > that the remote is properly informed of the shutdown. However, apart from /// > properly informing the remote, there is no difference between this and /// > immediately dropping the muxer. - fn close(&self) -> Poll<(), Self::Error>; + fn close(&self, cx: &mut Context) -> Poll>; /// Flush this `StreamMuxer`. /// /// This drains any write buffers of substreams and delivers any pending shutdown notifications /// due to `shutdown_substream` or `close`. One may thus shutdown groups of substreams /// followed by a final `flush_all` instead of having to do `flush_substream` for each. - fn flush_all(&self) -> Poll<(), Self::Error>; + fn flush_all(&self, cx: &mut Context) -> Poll>; } /// Polls for an inbound from the muxer but wraps the output in an object that @@ -222,14 +214,14 @@ pub trait StreamMuxer { #[inline] pub fn inbound_from_ref_and_wrap( muxer: P, -) -> impl Future, Error = ::Error> +) -> impl Future, ::Error>> where P: Deref + Clone, P::Target: StreamMuxer, { let muxer2 = muxer.clone(); - future::poll_fn(move || muxer.poll_inbound()) - .map(|substream| substream_from_ref(muxer2, substream)) + future::poll_fn(move |cx| muxer.poll_inbound(cx)) + .map_ok(|substream| substream_from_ref(muxer2, substream)) } /// Same as `outbound_from_ref`, but wraps the output in an object that @@ -258,17 +250,16 @@ where P: Deref + Clone, P::Target: StreamMuxer, { - type Item = SubstreamRef; - type Error = ::Error; + type Output = Result, ::Error>; - fn poll(&mut self) -> Poll { - match self.inner.poll() { - Ok(Async::Ready(substream)) => { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match Future::poll(Pin::new(&mut self.inner), cx) { + Poll::Ready(Ok(substream)) => { let out = substream_from_ref(self.inner.muxer.clone(), substream); - Ok(Async::Ready(out)) + Poll::Ready(Ok(out)) } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(err) => Err(err), + Poll::Pending => Poll::Pending, + Poll::Ready(Err(err)) => Poll::Ready(Err(err)), } } } @@ -297,18 +288,26 @@ where outbound: Option<::OutboundSubstream>, } +impl Unpin for OutboundSubstreamRefFuture +where + P: Deref, + P::Target: StreamMuxer, +{ +} + impl Future for OutboundSubstreamRefFuture where P: Deref, P::Target: StreamMuxer, { - type Item = ::Substream; - type Error = ::Error; + type Output = Result<::Substream, ::Error>; #[inline] - fn poll(&mut self) -> Poll { - self.muxer - .poll_outbound(self.outbound.as_mut().expect("outbound was empty")) + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + this.muxer.poll_outbound(cx, this.outbound.as_mut().expect("outbound was empty")) } } @@ -370,20 +369,11 @@ where } } - -impl Read for SubstreamRef +impl Unpin for SubstreamRef where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.read_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } } impl AsyncRead for SubstreamRef @@ -391,37 +381,17 @@ where P: Deref, P::Target: StreamMuxer, { - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.muxer.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.muxer.initializer() } - fn poll_read(&mut self, buf: &mut [u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.read_substream(s, buf).map_err(|e| e.into()) - } -} + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; -impl Write for SubstreamRef -where - P: Deref, - P::Target: StreamMuxer, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.write_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } - - #[inline] - fn flush(&mut self) -> Result<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.flush_substream(s).map_err(|e| e.into())? { - Async::Ready(()) => Ok(()), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.read_substream(cx, s, buf).map_err(|e| e.into()) } } @@ -430,36 +400,51 @@ where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn poll_write(&mut self, buf: &[u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.write_substream(s, buf).map_err(|e| e.into()) + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.write_substream(cx, s, buf).map_err(|e| e.into()) } - #[inline] - fn shutdown(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); loop { - match self.shutdown_state { + match this.shutdown_state { ShutdownState::Shutdown => { - try_ready!(self.muxer.shutdown_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Flush; + match this.muxer.shutdown_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Flush, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Flush => { - try_ready!(self.muxer.flush_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Done; + match this.muxer.flush_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Done, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Done => { - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } } } } - #[inline] - fn poll_flush(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.flush_substream(s).map_err(|e| e.into()) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.flush_substream(cx, s).map_err(|e| e.into()) } } @@ -507,8 +492,8 @@ impl StreamMuxer for StreamMuxerBox { type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - self.inner.poll_inbound() + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + self.inner.poll_inbound(cx) } #[inline] @@ -517,8 +502,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn poll_outbound(&self, s: &mut Self::OutboundSubstream) -> Poll { - self.inner.poll_outbound(s) + fn poll_outbound(&self, cx: &mut Context, s: &mut Self::OutboundSubstream) -> Poll> { + self.inner.poll_outbound(cx, s) } #[inline] @@ -526,28 +511,28 @@ impl StreamMuxer for StreamMuxerBox { self.inner.destroy_outbound(substream) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { - self.inner.read_substream(s, buf) + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + self.inner.read_substream(cx, s, buf) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.write_substream(s, buf) + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { + self.inner.write_substream(cx, s, buf) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.flush_substream(s) + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.flush_substream(cx, s) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.shutdown_substream(s) + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.shutdown_substream(cx, s) } #[inline] @@ -556,8 +541,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close() + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx) } #[inline] @@ -566,8 +551,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all() + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx) } } @@ -588,11 +573,16 @@ where type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - let substream = try_ready!(self.inner.poll_inbound().map_err(|e| e.into())); + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + let substream = match self.inner.poll_inbound(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; + let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -606,13 +596,18 @@ where #[inline] fn poll_outbound( &self, + cx: &mut Context, substream: &mut Self::OutboundSubstream, - ) -> Poll { + ) -> Poll> { let mut list = self.outbound.lock(); - let substream = try_ready!(self.inner.poll_outbound(list.get_mut(substream).unwrap()).map_err(|e| e.into())); + let substream = match self.inner.poll_outbound(cx, list.get_mut(substream).unwrap()) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -621,32 +616,32 @@ where self.inner.destroy_outbound(list.remove(&substream).unwrap()) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.read_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.read_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.write_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.write_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.flush_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.flush_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.shutdown_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.shutdown_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] @@ -656,8 +651,8 @@ where } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close().map_err(|e| e.into()) + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx).map_err(|e| e.into()) } #[inline] @@ -666,7 +661,7 @@ where } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all().map_err(|e| e.into()) + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx).map_err(|e| e.into()) } } diff --git a/core/src/muxing/singleton.rs b/core/src/muxing/singleton.rs index 7bec14ed..f85e22fd 100644 --- a/core/src/muxing/singleton.rs +++ b/core/src/muxing/singleton.rs @@ -19,10 +19,9 @@ // DEALINGS IN THE SOFTWARE. use crate::{Endpoint, muxing::StreamMuxer}; -use futures::prelude::*; +use futures::{prelude::*, io::Initializer}; use parking_lot::Mutex; -use std::{io, sync::atomic::{AtomicBool, Ordering}}; -use tokio_io::{AsyncRead, AsyncWrite}; +use std::{io, pin::Pin, sync::atomic::{AtomicBool, Ordering}, task::Context, task::Poll}; /// Implementation of `StreamMuxer` that allows only one substream on top of a connection, /// yielding the connection itself. @@ -62,22 +61,22 @@ pub struct OutboundSubstream {} impl StreamMuxer for SingletonMuxer where - TSocket: AsyncRead + AsyncWrite, + TSocket: AsyncRead + AsyncWrite + Unpin, { type Substream = Substream; type OutboundSubstream = OutboundSubstream; type Error = io::Error; - fn poll_inbound(&self) -> Poll { + fn poll_inbound(&self, _: &mut Context) -> Poll> { match self.endpoint { - Endpoint::Dialer => return Ok(Async::NotReady), + Endpoint::Dialer => return Poll::Pending, Endpoint::Listener => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -85,44 +84,44 @@ where OutboundSubstream {} } - fn poll_outbound(&self, _: &mut Self::OutboundSubstream) -> Poll { + fn poll_outbound(&self, _: &mut Context, _: &mut Self::OutboundSubstream) -> Poll> { match self.endpoint { - Endpoint::Listener => return Ok(Async::NotReady), + Endpoint::Listener => return Poll::Pending, Endpoint::Dialer => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } fn destroy_outbound(&self, _: Self::OutboundSubstream) { } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.lock().prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.lock().initializer() } - fn read_substream(&self, _: &mut Self::Substream, buf: &mut [u8]) -> Poll { - let res = self.inner.lock().poll_read(buf); - if let Ok(Async::Ready(_)) = res { + fn read_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + let res = AsyncRead::poll_read(Pin::new(&mut *self.inner.lock()), cx, buf); + if let Poll::Ready(Ok(_)) = res { self.remote_acknowledged.store(true, Ordering::Release); } res } - fn write_substream(&self, _: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.lock().poll_write(buf) + fn write_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &[u8]) -> Poll> { + AsyncWrite::poll_write(Pin::new(&mut *self.inner.lock()), cx, buf) } - fn flush_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } - fn shutdown_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().shutdown() + fn shutdown_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_close(Pin::new(&mut *self.inner.lock()), cx) } fn destroy_substream(&self, _: Self::Substream) { @@ -132,12 +131,12 @@ where self.remote_acknowledged.load(Ordering::Acquire) } - fn close(&self) -> Poll<(), io::Error> { + fn close(&self, cx: &mut Context) -> Poll> { // The `StreamMuxer` trait requires that `close()` implies `flush_all()`. - self.flush_all() + self.flush_all(cx) } - fn flush_all(&self) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_all(&self, cx: &mut Context) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } } diff --git a/core/src/nodes/collection.rs b/core/src/nodes/collection.rs index af8601d2..9e212810 100644 --- a/core/src/nodes/collection.rs +++ b/core/src/nodes/collection.rs @@ -29,11 +29,7 @@ use crate::{ }; use fnv::FnvHashMap; use futures::prelude::*; -use std::{error, fmt, hash::Hash, mem}; - -pub use crate::nodes::tasks::StartTakeOver; - -mod tests; +use std::{error, fmt, hash::Hash, mem, task::Context, task::Poll}; /// Implementation of `Stream` that handles a collection of nodes. pub struct CollectionStream { @@ -58,6 +54,9 @@ where } } +impl Unpin for + CollectionStream { } + /// State of a task. #[derive(Debug, Clone, PartialEq, Eq)] enum TaskState { @@ -323,7 +322,7 @@ where pub fn add_reach_attempt(&mut self, future: TFut, handler: THandler) -> ReachAttemptId where - TFut: Future + Send + 'static, + TFut: Future> + Unpin + Send + 'static, THandler: IntoNodeHandler + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, @@ -358,17 +357,19 @@ where } /// Sends an event to all nodes. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.inner.start_broadcast(event) } + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.inner.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_broadcast(cx) } /// Adds an existing connection to a node to the collection. @@ -447,13 +448,13 @@ where /// > **Note**: we use a regular `poll` method instead of implementing `Stream` in order to /// > remove the `Err` variant, but also because we want the `CollectionStream` to stay /// > borrowed if necessary. - pub fn poll(&mut self) -> Async> + pub fn poll(&mut self, cx: &mut Context) -> Poll> where TConnInfo: Clone, // TODO: Clone shouldn't be necessary { - let item = match self.inner.poll() { - Async::Ready(item) => item, - Async::NotReady => return Async::NotReady, + let item = match self.inner.poll(cx) { + Poll::Ready(item) => item, + Poll::Pending => return Poll::Pending, }; match item { @@ -463,7 +464,7 @@ where match (user_data, result, handler) { (TaskState::Pending, tasks::Error::Reach(err), Some(handler)) => { - Async::Ready(CollectionEvent::ReachError { + Poll::Ready(CollectionEvent::ReachError { id: ReachAttemptId(id), error: err, handler, @@ -482,7 +483,7 @@ where debug_assert!(_handler.is_none()); let _node_task_id = self.nodes.remove(conn_info.peer_id()); debug_assert_eq!(_node_task_id, Some(id)); - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error: err, user_data, @@ -497,8 +498,8 @@ where tasks::Event::NodeReached { task, conn_info } => { let id = task.id(); drop(task); - Async::Ready(CollectionEvent::NodeReached(CollectionReachEvent { - parent: self, + Poll::Ready(CollectionEvent::NodeReached(CollectionReachEvent { + parent: &mut *self, id, conn_info: Some(conn_info), })) @@ -512,7 +513,7 @@ where self.tasks is switched to the Connected state; QED"), }; drop(task); - Async::Ready(CollectionEvent::NodeEvent { + Poll::Ready(CollectionEvent::NodeEvent { // TODO: normally we'd build a `PeerMut` manually here, but the borrow checker // doesn't like it peer: self.peer_mut(&conn_info.peer_id()) @@ -616,14 +617,15 @@ where } } - /// Sends an event to the given node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Begin sending an event to the given node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.inner.start_send_event(event) } - /// Complete sending an event message initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { - self.inner.complete_send_event() + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_event(cx) } /// Closes the connections to this node. Returns the user data. @@ -648,23 +650,13 @@ where /// The reach attempt will only be effectively cancelled once the peer (the object you're /// manipulating) has received some network activity. However no event will be ever be /// generated from this reach attempt, and this takes effect immediately. - #[must_use] - pub fn start_take_over(&mut self, id: InterruptedReachAttempt) - -> StartTakeOver<(), InterruptedReachAttempt> - { - match self.inner.start_take_over(id.inner) { - StartTakeOver::Ready(_state) => { - debug_assert!(if let TaskState::Pending = _state { true } else { false }); - StartTakeOver::Ready(()) - } - StartTakeOver::NotReady(inner) => - StartTakeOver::NotReady(InterruptedReachAttempt { inner }), - StartTakeOver::Gone => StartTakeOver::Gone - } + pub fn start_take_over(&mut self, id: InterruptedReachAttempt) { + self.inner.start_take_over(id.inner) } - /// Complete a take over initiated by `start_take_over`. - pub fn complete_take_over(&mut self) -> Poll<(), ()> { - self.inner.complete_take_over() + /// Make sure we are ready to taking over with `start_take_over`. + #[must_use] + pub fn poll_ready_take_over(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_take_over(cx) } } diff --git a/core/src/nodes/collection/tests.rs b/core/src/nodes/collection/tests.rs deleted file mode 100644 index 69f82c05..00000000 --- a/core/src/nodes/collection/tests.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use futures::future; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, InEvent, OutEvent, HandlerState}; -use tokio::runtime::current_thread::Runtime; -use tokio::runtime::Builder; -use crate::nodes::NodeHandlerEvent; -use std::{io, sync::Arc}; -use parking_lot::Mutex; - -type TestCollectionStream = CollectionStream; - -#[test] -fn has_connection_is_false_before_a_connection_has_been_made() { - let cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(!cs.has_connection(&peer_id)); -} - -#[test] -fn connections_is_empty_before_connecting() { - let cs = TestCollectionStream::new(); - assert!(cs.connections().next().is_none()); -} - -#[test] -fn retrieving_a_peer_is_none_if_peer_is_missing_or_not_connected() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(cs.peer_mut(&peer_id).is_none()); - - let handler = Handler::default(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, handler); - assert!(cs.peer_mut(&peer_id).is_none()); // task is pending -} - -#[test] -fn collection_stream_reaches_the_nodes() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((peer_id, muxer)); - cs.add_reach_attempt(fut, Handler::default()); - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - let event = cs.poll(); - match poll_count { - 1 => assert_matches!(event, Async::NotReady), - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(_))); - return Ok(Async::Ready(())); // stop - } - _ => unreachable!() - } - Ok(Async::NotReady) - }); - rt.block_on(fut).unwrap(); -} - -#[test] -fn accepting_a_node_yields_new_entry() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, Handler::default()); - - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - { - let event = cs.poll(); - match poll_count { - 1 => { - assert_matches!(event, Async::NotReady); - return Ok(Async::NotReady) - } - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - let (accept_ev, accepted_peer_id) = reach_ev.accept(()); - assert_eq!(accepted_peer_id, peer_id); - assert_matches!(accept_ev, CollectionNodeAccept::NewEntry); - }); - } - _ => unreachable!() - } - } - assert!(cs.peer_mut(&peer_id).is_some(), "peer is not in the list"); - assert!(cs.has_connection(&peer_id), "peer is not connected"); - assert_eq!(cs.connections().collect::>(), vec![&peer_id]); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("running the future works"); -} - -#[test] -fn events_in_a_node_reaches_the_collection_stream() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_peer_id = PeerId::random(); - - let mut handler = Handler::default(); - handler.state = Some(HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("init")))); - let handler_states = vec![ - HandlerState::Err, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 3") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 2") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )), - ]; - handler.next_states = handler_states; - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((task_peer_id.clone(), muxer)); - cs.lock().add_reach_attempt(fut, handler); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("init")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 2")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn task_closed_with_error_while_task_is_pending_yields_reach_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_inner_fut = future::err(std::io::Error::new(std::io::ErrorKind::Other, "inner fut error")); - let reach_attempt_id = cs.lock().add_reach_attempt(task_inner_fut, Handler::default()); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::ReachError {id, error, ..} => { - assert_eq!(id, reach_attempt_id); - assert_eq!(error.to_string(), "inner fut error"); - }); - - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - -} - -#[test] -fn task_closed_with_error_when_task_is_connected_yields_node_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Err]; // triggered when sending a NextState event - - cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(cs.complete_broadcast()) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id)); - - // Assert the node errored - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::NodeClosed{..}); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn interrupting_a_pending_connection_attempt_is_ok() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - let interrupt = cs.interrupt(reach_id); - assert!(interrupt.is_ok()); -} - -#[test] -fn interrupting_a_connection_attempt_twice_is_err() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - assert!(cs.interrupt(reach_id).is_ok()); - assert_matches!(cs.interrupt(reach_id), Err(InterruptError::ReachAttemptNotFound)) -} - -#[test] -fn interrupting_an_established_connection_is_err() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let handler = Handler::default(); - - let reach_id = cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(Async::Ready(())) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id), "Connection was not established"); - - assert_matches!(cs.lock().interrupt(reach_id), Err(InterruptError::AlreadyReached)); -} diff --git a/core/src/nodes/handled_node.rs b/core/src/nodes/handled_node.rs index 150b5e45..f8b08d11 100644 --- a/core/src/nodes/handled_node.rs +++ b/core/src/nodes/handled_node.rs @@ -20,10 +20,7 @@ use crate::{PeerId, muxing::StreamMuxer}; use crate::nodes::node::{NodeEvent, NodeStream, Substream, Close}; -use futures::prelude::*; -use std::{error, fmt, io}; - -mod tests; +use std::{error, fmt, io, pin::Pin, task::Context, task::Poll}; /// Handler for the substreams of a node. // TODO: right now it is possible for a node handler to be built, then shut down right after if we @@ -59,7 +56,8 @@ pub trait NodeHandler { /// Should behave like `Stream::poll()`. /// /// Returning an error will close the connection to the remote. - fn poll(&mut self) -> Poll, Self::Error>; + fn poll(&mut self, cx: &mut Context) + -> Poll, Self::Error>>; } /// Prototype for a `NodeHandler`. @@ -172,6 +170,13 @@ where } } +impl Unpin for HandledNode +where + TMuxer: StreamMuxer, + THandler: NodeHandler>, +{ +} + impl HandledNode where TMuxer: StreamMuxer, @@ -214,37 +219,41 @@ where } /// API similar to `Future::poll` that polls the node for events. - pub fn poll(&mut self) -> Poll> { + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) + -> Poll>> + { loop { let mut node_not_ready = false; - match self.node.poll().map_err(HandledNodeError::Node)? { - Async::NotReady => node_not_ready = true, - Async::Ready(NodeEvent::InboundSubstream { substream }) => { + match self.node.poll(cx) { + Poll::Pending => node_not_ready = true, + Poll::Ready(Ok(NodeEvent::InboundSubstream { substream })) => { self.handler.inject_substream(substream, NodeHandlerEndpoint::Listener) } - Async::Ready(NodeEvent::OutboundSubstream { user_data, substream }) => { + Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream })) => { let endpoint = NodeHandlerEndpoint::Dialer(user_data); self.handler.inject_substream(substream, endpoint) } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Node(err))), } - match self.handler.poll().map_err(HandledNodeError::Handler)? { - Async::NotReady => { + match self.handler.poll(cx) { + Poll::Pending => { if node_not_ready { break } } - Async::Ready(NodeHandlerEvent::OutboundSubstreamRequest(user_data)) => { + Poll::Ready(Ok(NodeHandlerEvent::OutboundSubstreamRequest(user_data))) => { self.node.open_substream(user_data); } - Async::Ready(NodeHandlerEvent::Custom(event)) => { - return Ok(Async::Ready(event)); + Poll::Ready(Ok(NodeHandlerEvent::Custom(event))) => { + return Poll::Ready(Ok(event)); } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Handler(err))), } } - Ok(Async::NotReady) + Poll::Pending } } diff --git a/core/src/nodes/handled_node/tests.rs b/core/src/nodes/handled_node/tests.rs deleted file mode 100644 index ee138c2e..00000000 --- a/core/src/nodes/handled_node/tests.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent, TestHandledNode}; - -struct TestBuilder { - muxer: DummyMuxer, - handler: Handler, - want_open_substream: bool, - substream_user_data: usize, -} - -impl TestBuilder { - fn new() -> Self { - TestBuilder { - muxer: DummyMuxer::new(), - handler: Handler::default(), - want_open_substream: false, - substream_user_data: 0, - } - } - - fn with_muxer_inbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_inbound_connection_state(state); - self - } - - fn with_muxer_outbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_outbound_connection_state(state); - self - } - - fn with_handler_state(&mut self, state: HandlerState) -> &mut Self { - self.handler.state = Some(state); - self - } - - fn with_open_substream(&mut self, user_data: usize) -> &mut Self { - self.want_open_substream = true; - self.substream_user_data = user_data; - self - } - - fn handled_node(&mut self) -> TestHandledNode { - let mut h = HandledNode::new(self.muxer.clone(), self.handler.clone()); - if self.want_open_substream { - h.node.open_substream(self.substream_user_data); - } - h - } -} - -// Set the state of the `Handler` after `inject_outbound_closed` is called -fn set_next_handler_outbound_state( handled_node: &mut TestHandledNode, next_state: HandlerState) { - handled_node.handler.next_outbound_state = Some(next_state); -} - -#[test] -fn can_inject_event() { - let mut handled = TestBuilder::new() - .handled_node(); - - let event = InEvent::Custom("banana"); - handled.inject_event(event.clone()); - assert_eq!(handled.handler().events, vec![event]); -} - -#[test] -fn poll_with_unready_node_stream_and_handler_emits_custom_event() { - let expected_event = NodeHandlerEvent::Custom(OutEvent::Custom("pineapple")); - let mut handled = TestBuilder::new() - // make NodeStream return NotReady - .with_muxer_inbound_state(DummyConnectionState::Pending) - // make Handler return return Ready(Some(…)) - .with_handler_state(HandlerState::Ready(expected_event)) - .handled_node(); - - assert_matches!(handled.poll(), Ok(Async::Ready(event)) => { - assert_matches!(event, OutEvent::Custom("pineapple")) - }); -} - -#[test] -fn handler_emits_outbound_closed_when_opening_new_substream_on_closed_node() { - let open_event = NodeHandlerEvent::OutboundSubstreamRequest(456); - let mut handled = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Ready(open_event)) - .handled_node(); - - set_next_handler_outbound_state( - &mut handled, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("pear"))) - ); - handled.poll().expect("poll works"); -} - -#[test] -fn poll_yields_inbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_open_substream(32) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Opened) - .with_open_substream(1) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(Some(1))]); -} - -#[test] -fn poll_yields_inbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Opened) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(None)]); -} diff --git a/core/src/nodes/listeners.rs b/core/src/nodes/listeners.rs index effcea65..b9c8ebbf 100644 --- a/core/src/nodes/listeners.rs +++ b/core/src/nodes/listeners.rs @@ -21,11 +21,10 @@ //! Manage listening on multiple multiaddresses at once. use crate::{Multiaddr, Transport, transport::{TransportError, ListenerEvent}}; -use futures::prelude::*; +use futures::{prelude::*, task::Context, task::Poll}; use log::debug; use smallvec::SmallVec; -use std::{collections::VecDeque, fmt}; -use void::Void; +use std::{collections::VecDeque, fmt, pin::Pin}; /// Implementation of `futures::Stream` that allows listening on multiaddresses. /// @@ -158,7 +157,7 @@ where /// The ID of the listener that errored. listener_id: ListenerId, /// The error value. - error: ::Error + error: ::Error } } @@ -222,28 +221,31 @@ where self.listeners.iter().flat_map(|l| l.addresses.iter()) } - /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> { + /// Provides an API similar to `Stream`, except that it cannot end. + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> + where + TTrans::Listener: Unpin, + { // We remove each element from `listeners` one by one and add them back. let mut remaining = self.listeners.len(); while let Some(mut listener) = self.listeners.pop_back() { - match listener.listener.poll() { - Ok(Async::NotReady) => { + match TryStream::try_poll_next(Pin::new(&mut listener.listener), cx) { + Poll::Pending => { self.listeners.push_front(listener); remaining -= 1; if remaining == 0 { break } } - Ok(Async::Ready(Some(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { + Poll::Ready(Some(Ok(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::Incoming { + return Poll::Ready(ListenersEvent::Incoming { listener_id: id, upgrade, local_addr, send_back_addr: remote_addr }) } - Ok(Async::Ready(Some(ListenerEvent::NewAddress(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::NewAddress(a)))) => { if listener.addresses.contains(&a) { debug!("Transport has reported address {} multiple times", a) } @@ -252,28 +254,28 @@ where } let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::NewAddress { + return Poll::Ready(ListenersEvent::NewAddress { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(Some(ListenerEvent::AddressExpired(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::AddressExpired(a)))) => { listener.addresses.retain(|x| x != &a); let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::AddressExpired { + return Poll::Ready(ListenersEvent::AddressExpired { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(None)) => { - return Async::Ready(ListenersEvent::Closed { + Poll::Ready(None) => { + return Poll::Ready(ListenersEvent::Closed { listener_id: listener.id, listener: listener.listener }) } - Err(err) => { - return Async::Ready(ListenersEvent::Error { + Poll::Ready(Some(Err(err))) => { + return Poll::Ready(ListenersEvent::Error { listener_id: listener.id, error: err }) @@ -282,22 +284,28 @@ where } // We register the current task to be woken up if a new listener is added. - Async::NotReady + Poll::Pending } } impl Stream for ListenersStream where TTrans: Transport, + TTrans::Listener: Unpin, { type Item = ListenersEvent; - type Error = Void; // TODO: use ! once stable - fn poll(&mut self) -> Poll, Self::Error> { - Ok(self.poll().map(Option::Some)) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ListenersStream::poll(self, cx).map(Option::Some) } } +impl Unpin for ListenersStream +where + TTrans: Transport, +{ +} + impl fmt::Debug for ListenersStream where TTrans: Transport + fmt::Debug, @@ -313,7 +321,7 @@ where impl fmt::Debug for ListenersEvent where TTrans: Transport, - ::Error: fmt::Debug, + ::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { @@ -353,215 +361,37 @@ mod tests { use tokio::runtime::current_thread::Runtime; use std::{io, iter::FromIterator}; use futures::{future::{self}, stream}; - use crate::tests::dummy_transport::{DummyTransport, ListenerState}; - use crate::tests::dummy_muxer::DummyMuxer; use crate::PeerId; - fn set_listener_state(ls: &mut ListenersStream, idx: usize, state: ListenerState) { - ls.listeners[idx].listener = match state { - ListenerState::Error => - Box::new(stream::poll_fn(|| Err(io::Error::new(io::ErrorKind::Other, "oh noes")))), - ListenerState::Ok(state) => match state { - Async::NotReady => Box::new(stream::poll_fn(|| Ok(Async::NotReady))), - Async::Ready(Some(event)) => Box::new(stream::poll_fn(move || { - Ok(Async::Ready(Some(event.clone().map(future::ok)))) - })), - Async::Ready(None) => Box::new(stream::empty()) - } - ListenerState::Events(events) => - Box::new(stream::iter_ok(events.into_iter().map(|e| e.map(future::ok)))) - }; - } - #[test] fn incoming_event() { - let mem_transport = transport::MemoryTransport::default(); + futures::executor::block_on(async move { + let mem_transport = transport::MemoryTransport::default(); - let mut listeners = ListenersStream::new(mem_transport); - listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); + let mut listeners = ListenersStream::new(mem_transport); + listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); - let address = { - let event = listeners.by_ref().wait().next().expect("some event").expect("no error"); - if let ListenersEvent::NewAddress { listen_addr, .. } = event { - listen_addr - } else { - panic!("Was expecting the listen address to be reported") - } - }; - - let dial = mem_transport.dial(address.clone()).unwrap(); - - let future = listeners - .into_future() - .map_err(|(err, _)| err) - .and_then(|(event, _)| { - match event { - Some(ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. }) => { - assert_eq!(local_addr, address); - assert_eq!(send_back_addr, address); - upgrade.map(|_| ()).map_err(|_| panic!()) - }, - _ => panic!() + let address = { + let event = listeners.next().await.unwrap(); + if let ListenersEvent::NewAddress { listen_addr, .. } = event { + listen_addr + } else { + panic!("Was expecting the listen address to be reported") } - }) - .select(dial.map(|_| ()).map_err(|_| panic!())) - .map_err(|(err, _)| err); + }; - let mut runtime = Runtime::new().unwrap(); - runtime.block_on(future).unwrap(); - } + let address2 = address.clone(); + async_std::task::spawn(async move { + mem_transport.dial(address2).unwrap().await.unwrap(); + }); - #[test] - fn listener_stream_returns_transport() { - let t = DummyTransport::new(); - let t_clone = t.clone(); - let ls = ListenersStream::new(t); - assert_eq!(ls.transport(), &t_clone); - } - - #[test] - fn listener_stream_can_iterate_over_listeners() { - let mut t = DummyTransport::new(); - let addr1 = tcp4([127, 0, 0, 1], 1234); - let addr2 = tcp4([127, 0, 0, 1], 4321); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(addr1.clone()), - ListenerEvent::NewAddress(addr2.clone()) - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([0, 0, 0, 0], 0)).expect("listen_on"); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr1, listen_addr) - }); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr2, listen_addr) - }) - } - - #[test] - fn listener_stream_poll_without_listeners_is_not_ready() { - let t = DummyTransport::new(); - let mut ls = ListenersStream::new(t); - assert_matches!(ls.poll(), Async::NotReady); - } - - #[test] - fn listener_stream_poll_with_listeners_that_arent_ready_is_not_ready() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::NotReady)); - assert_matches!(ls.poll(), Async::NotReady); - assert_eq!(ls.listeners.len(), 1); // listener is still there - } - - #[test] - fn listener_stream_poll_with_ready_listeners_is_ready() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let expected_output = (peer_id.clone(), muxer.clone()); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(tcp4([127, 0, 0, 1], 9090)), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) + match listeners.next().await.unwrap() { + ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. } => { + assert_eq!(local_addr, address); + assert_eq!(send_back_addr, address); + }, + _ => panic!() } - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([127, 0, 0, 1], 1234)).expect("listen_on"); - ls.listen_on(tcp4([127, 0, 0, 1], 4321)).expect("listen_on"); - assert_eq!(ls.listeners.len(), 2); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - set_listener_state(&mut ls, 1, ListenerState::Ok(Async::NotReady)); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - } - - #[test] - fn listener_stream_poll_with_closed_listener_emits_closed_event() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::Ready(None))); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Closed{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - #[test] - fn listener_stream_poll_with_erroring_listener_emits_error_event() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let event = ListenerEvent::Upgrade { - upgrade: (peer_id, muxer), - local_addr: tcp4([127, 0, 0, 1], 1234), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }; - t.set_initial_listener_state(ListenerState::Ok(Async::Ready(Some(event)))); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Error); // simulate an error on the socket - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Error{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - fn tcp4(ip: [u8; 4], port: u16) -> Multiaddr { - let protos = std::iter::once(multiaddr::Protocol::Ip4(ip.into())) - .chain(std::iter::once(multiaddr::Protocol::Tcp(port))); - Multiaddr::from_iter(protos) } } diff --git a/core/src/nodes/network.rs b/core/src/nodes/network.rs index abe9e631..2f6634a1 100644 --- a/core/src/nodes/network.rs +++ b/core/src/nodes/network.rs @@ -49,10 +49,10 @@ use std::{ fmt, hash::Hash, num::NonZeroUsize, + pin::Pin, + task::{Context, Poll}, }; -pub use crate::nodes::collection::StartTakeOver; - mod tests; /// Implementation of `Stream` that handles the nodes. @@ -81,7 +81,7 @@ where /// If the pair's second element is `AsyncSink::Ready`, the take over /// message has been sent and needs to be flushed using /// `PeerMut::complete_take_over`. - take_over_to_complete: Option<(TPeerId, AsyncSink>)> + take_over_to_complete: Option<(TPeerId, InterruptedReachAttempt)> } impl fmt::Debug for @@ -102,6 +102,13 @@ where } } +impl Unpin for + Network +where + TTrans: Transport +{ +} + impl ConnectionInfo for (TConnInfo, ConnectedPoint) where TConnInfo: ConnectionInfo @@ -173,7 +180,7 @@ where /// The listener that errored. listener_id: ListenerId, /// The listener error. - error: ::Error + error: ::Error }, /// One of the listeners is now listening on an additional address. @@ -573,7 +580,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::ListenerUpgrade: Send + 'static, + TTrans::ListenerUpgrade: Unpin + Send + 'static, THandler: IntoNodeHandler<(TConnInfo, ConnectedPoint)> + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, // TODO: shouldn't be necessary @@ -609,9 +616,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -781,7 +788,7 @@ where where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TInEvent: Send + 'static, @@ -797,9 +804,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -840,19 +847,18 @@ where /// Start sending an event to all nodes. /// - /// Make sure to complete the broadcast with `complete_broadcast`. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.active_nodes.start_broadcast(event) } - /// Complete a broadcast initiated with `start_broadcast`. + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.active_nodes.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.active_nodes.poll_ready_broadcast(cx) } /// Returns a list of all the peers we are currently connected to. @@ -934,7 +940,7 @@ where fn start_dial_out(&mut self, peer_id: TPeerId, handler: THandler, first: Multiaddr, rest: Vec) where TTrans: Transport, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TTrans::Error: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -950,9 +956,9 @@ where .map_err(|err| InternalReachErr::Transport(TransportError::Other(err))) .and_then(move |(actual_conn_info, muxer)| { if *actual_conn_info.peer_id() == expected_peer_id { - Ok(((actual_conn_info, connected_point), muxer)) + future::ready(Ok(((actual_conn_info, connected_point), muxer))) } else { - Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info }) + future::ready(Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info })) } }); self.active_nodes.add_reach_attempt(fut, handler) @@ -976,11 +982,12 @@ where } /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> + pub fn poll<'a>(&'a mut self, cx: &mut Context) -> Poll> where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, + TTrans::Listener: Unpin, TTrans::ListenerUpgrade: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -998,9 +1005,9 @@ where Some(x) if self.incoming_negotiated().count() >= (x as usize) => (), _ => { - match self.listeners.poll() { - Async::NotReady => (), - Async::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { + match ListenersStream::poll(Pin::new(&mut self.listeners), cx) { + Poll::Pending => (), + Poll::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { let event = IncomingConnectionEvent { listener_id, upgrade, @@ -1010,19 +1017,19 @@ where active_nodes: &mut self.active_nodes, other_reach_attempts: &mut self.reach_attempts.other_reach_attempts, }; - return Async::Ready(NetworkEvent::IncomingConnection(event)); + return Poll::Ready(NetworkEvent::IncomingConnection(event)); } - Async::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::Closed { listener_id, listener }) => { - return Async::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) + Poll::Ready(ListenersEvent::Closed { listener_id, listener }) => { + return Poll::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) } - Async::Ready(ListenersEvent::Error { listener_id, error }) => { - return Async::Ready(NetworkEvent::ListenerError { listener_id, error }) + Poll::Ready(ListenersEvent::Error { listener_id, error }) => { + return Poll::Ready(NetworkEvent::ListenerError { listener_id, error }) } } } @@ -1031,36 +1038,30 @@ where // Attempt to deliver any pending take over messages. if let Some((id, interrupted)) = self.take_over_to_complete.take() { if let Some(mut peer) = self.active_nodes.peer_mut(&id) { - if let AsyncSink::NotReady(i) = interrupted { - if let StartTakeOver::NotReady(i) = peer.start_take_over(i) { - self.take_over_to_complete = Some((id, AsyncSink::NotReady(i))) - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) - } - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((id, interrupted)); + return Poll::Pending; } } } - if self.take_over_to_complete.is_some() { - return Async::NotReady - } // Poll the existing nodes. let (action, out_event); - match self.active_nodes.poll() { - Async::NotReady => return Async::NotReady, - Async::Ready(CollectionEvent::NodeReached(reach_event)) => { + match self.active_nodes.poll(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(CollectionEvent::NodeReached(reach_event)) => { let (a, e) = handle_node_reached(&mut self.reach_attempts, reach_event); action = a; out_event = e; } - Async::Ready(CollectionEvent::ReachError { id, error, handler }) => { + Poll::Ready(CollectionEvent::ReachError { id, error, handler }) => { let (a, e) = handle_reach_error(&mut self.reach_attempts, id, error, handler); action = a; out_event = e; } - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error, .. @@ -1078,7 +1079,7 @@ where error, }; } - Async::Ready(CollectionEvent::NodeEvent { peer, event }) => { + Poll::Ready(CollectionEvent::NodeEvent { peer, event }) => { action = Default::default(); out_event = NetworkEvent::NodeEvent { conn_info: peer.info().0.clone(), event }; } @@ -1099,17 +1100,15 @@ where out_reach_attempts should always be in sync with the actual \ attempts; QED"); let mut peer = self.active_nodes.peer_mut(&peer_id).unwrap(); - if let StartTakeOver::NotReady(i) = peer.start_take_over(interrupted) { - self.take_over_to_complete = Some((peer_id, AsyncSink::NotReady(i))); - return Async::NotReady - } - if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((peer_id, AsyncSink::Ready)); - return Async::NotReady + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((peer_id, interrupted)); + return Poll::Pending } } - Async::Ready(out_event) + Poll::Ready(out_event) } } @@ -1467,7 +1466,7 @@ impl<'a, TTrans, TMuxer, TInEvent, TOutEvent, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, @@ -1644,18 +1643,33 @@ where closed messages; QED") } - /// Start sending an event to the node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Sends an event to the handler of the node. + pub fn send_event<'s: 'a>(&'s mut self, event: TInEvent) -> impl Future + 's + 'a { + let mut event = Some(event); + futures::future::poll_fn(move |cx| { + match self.poll_ready_event(cx) { + Poll::Ready(()) => { + self.start_send_event(event.take().expect("Future called after finished")); + Poll::Ready(()) + }, + Poll::Pending => Poll::Pending, + } + }) + } + + /// Begin sending an event to the node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") .start_send_event(event) } - /// Complete sending an event message, initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") - .complete_send_event() + .poll_ready_event(cx) } } @@ -1749,7 +1763,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, diff --git a/core/src/nodes/network/tests.rs b/core/src/nodes/network/tests.rs index c64666aa..c4f307bb 100644 --- a/core/src/nodes/network/tests.rs +++ b/core/src/nodes/network/tests.rs @@ -21,363 +21,6 @@ #![cfg(test)] use super::*; -use crate::tests::dummy_transport::DummyTransport; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent}; -use crate::tests::dummy_transport::ListenerState; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::nodes::NodeHandlerEvent; -use crate::transport::ListenerEvent; -use assert_matches::assert_matches; -use parking_lot::Mutex; -use std::sync::Arc; -use tokio::runtime::{Builder, Runtime}; - -#[test] -fn query_transport() { - let transport = DummyTransport::new(); - let transport2 = transport.clone(); - let network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - assert_eq!(network.transport(), &transport2); -} - -#[test] -fn local_node_peer() { - let peer_id = PeerId::random(); - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), peer_id.clone()); - assert_matches!(network.peer(peer_id), Peer::LocalNode); -} - -#[test] -fn successful_dial_reaches_a_node() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let dial_res = network.dial(addr, Handler::default()); - assert!(dial_res.is_ok()); - - // Poll the network until we get a `NodeReached` then assert on the peer: - // it's there and it's connected. - let network = Arc::new(Mutex::new(network)); - - let mut rt = Runtime::new().unwrap(); - let mut peer_id : Option = None; - // Drive forward until we're Connected - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected(PeerConnected{..})); -} - -#[test] -fn num_incoming_negotiated() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - - let events = vec![ - ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap()), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ]; - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - // no incoming yet - assert_eq!(network.incoming_negotiated().count(), 0); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - // Now there's an incoming connection - assert_eq!(network.incoming_negotiated().count(), 1); -} - -#[test] -fn broadcasted_events_reach_active_nodes() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )),]; - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::NodeEvent { conn_info: _, event: inner_event } => { - // The event we sent reached the node and triggered sending the out event we told it to return - assert_matches!(inner_event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(false)) - }, - _ => Ok(Async::Ready(true)) - } - })).expect("tokio works"); - } -} - -#[test] -fn querying_for_pending_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); -} - -#[test] -fn querying_for_unknown_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected( PeerNotConnected { nodes: _, peer_id: node_peer_id }) => { - assert_eq!(node_peer_id, peer_id); - }); -} - -#[test] -fn querying_for_connected_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - - // Dial a node - let addr = "/ip4/127.0.0.1/tcp/1234".parse().expect("bad multiaddr"); - network.dial(addr, Handler::default()).expect("dialing works"); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we connect; extract the new PeerId. - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - // We're connected. - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected( PeerConnected { .. } )); -} - -#[test] -fn poll_with_closed_listener() { - let mut transport = DummyTransport::new(); - // Set up listener to be closed - transport.set_initial_listener_state(ListenerState::Ok(Async::Ready(None))); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::ListenerClosed { .. } )); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); -} - -#[test] -fn unknown_peer_that_is_unreachable_yields_unknown_peer_dial_error() { - let mut transport = DummyTransport::new(); - transport.make_dial_fail(); - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let handler = Handler::default(); - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::UnknownPeerDialError { .. } ); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn known_peer_that_is_unreachable_yields_dial_error() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - transport.make_dial_fail(); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); - } - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let peer_id = peer_id.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - let failed_peer_id = assert_matches!( - event, - NetworkEvent::DialError { new_state: _, peer_id: failed_peer_id, .. } => failed_peer_id - ); - assert_eq!(peer_id, failed_peer_id); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn yields_node_error_when_there_is_an_error_after_successful_connect() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - // Set up an outgoing connection with a PeerId we know - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - let addr = "/unix/reachable".parse().expect("bad multiaddr"); - let mut handler = Handler::default(); - // Force an error - handler.next_states = vec![ HandlerState::Err ]; - peer.into_not_connected().unwrap().connect(addr, handler); - } - - // Ensure we run on a single thread - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Drive it forward until we connect to the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - // Push the Handler into an error state on the next poll - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::Connected { .. }); - // We're connected, we can move on - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } - - // Poll again. It is going to be a NodeClosed because of how the - // handler's next state was set up. - let network_fut = network.clone(); - let expected_peer_id = peer_id.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::NodeClosed { conn_info, .. }) => { - assert_eq!(conn_info, expected_peer_id); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} #[test] fn local_prio_equivalence_relation() { @@ -387,59 +30,3 @@ fn local_prio_equivalence_relation() { assert_ne!(has_dial_prio(&a, &b), has_dial_prio(&b, &a)); } } - -#[test] -fn limit_incoming_connections() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let limit = 1; - - let mut events = vec![ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap())]; - events.extend(std::iter::repeat( - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ).take(10)); - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new_with_incoming_limit(transport, PeerId::random(), Some(limit)); - assert_eq!(network.incoming_limit(), Some(limit)); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - assert_eq!(network.incoming_negotiated().count(), 0); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - for i in 1..10 { - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - if i <= limit { - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), - Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - } else { - match network_fut.poll() { - Async::NotReady => (), - Async::Ready(x) => { - match x { - NetworkEvent::NewListenerAddress {..} => {} - NetworkEvent::ExpiredListenerAddress {..} => {} - NetworkEvent::IncomingConnection(_) => {} - NetworkEvent::Connected {..} => {} - e => panic!("Not expected event: {:?}", e) - } - }, - } - } - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - assert!(network.incoming_negotiated().count() <= (limit as usize)); - } -} diff --git a/core/src/nodes/node.rs b/core/src/nodes/node.rs index a1d0eac4..37da9954 100644 --- a/core/src/nodes/node.rs +++ b/core/src/nodes/node.rs @@ -21,9 +21,7 @@ use futures::prelude::*; use crate::muxing; use smallvec::SmallVec; -use std::fmt; -use std::io::Error as IoError; -use std::sync::Arc; +use std::{fmt, io::Error as IoError, pin::Pin, sync::Arc, task::Context, task::Poll}; // Implementation notes // ================= @@ -143,43 +141,44 @@ where } /// Provides an API similar to `Future`. - pub fn poll(&mut self) -> Poll, IoError> { + pub fn poll(&mut self, cx: &mut Context) -> Poll, IoError>> { // Polling inbound substream. - match self.muxer.poll_inbound().map_err(|e| e.into())? { - Async::Ready(substream) => { + match self.muxer.poll_inbound(cx) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); - return Ok(Async::Ready(NodeEvent::InboundSubstream { + return Poll::Ready(Ok(NodeEvent::InboundSubstream { substream, })); } - Async::NotReady => {} + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => {} } // Polling outbound substreams. // We remove each element from `outbound_substreams` one by one and add them back. for n in (0..self.outbound_substreams.len()).rev() { let (user_data, mut outbound) = self.outbound_substreams.swap_remove(n); - match self.muxer.poll_outbound(&mut outbound) { - Ok(Async::Ready(substream)) => { + match self.muxer.poll_outbound(cx, &mut outbound) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); self.muxer.destroy_outbound(outbound); - return Ok(Async::Ready(NodeEvent::OutboundSubstream { + return Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream, })); } - Ok(Async::NotReady) => { + Poll::Pending => { self.outbound_substreams.push((user_data, outbound)); } - Err(err) => { + Poll::Ready(Err(err)) => { self.muxer.destroy_outbound(outbound); - return Err(err.into()); + return Poll::Ready(Err(err.into())); } } } // Nothing happened. Register our task to be notified and return. - Ok(Async::NotReady) + Poll::Pending } } @@ -212,11 +211,14 @@ impl Future for Close where TMuxer: muxing::StreamMuxer, { - type Item = (); - type Error = IoError; + type Output = Result<(), IoError>; - fn poll(&mut self) -> Poll { - self.muxer.close().map_err(|e| e.into()) + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match self.muxer.close(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(err)) => Poll::Ready(Err(err.into())), + } } } @@ -252,70 +254,3 @@ where } } } - -#[cfg(test)] -mod node_stream { - use super::{NodeEvent, NodeStream}; - use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; - use assert_matches::assert_matches; - use futures::prelude::*; - use tokio_mock_task::MockTask; - - fn build_node_stream() -> NodeStream> { - let muxer = DummyMuxer::new(); - NodeStream::<_, Vec>::new(muxer) - } - - #[test] - fn closing_a_node_stream_destroys_substreams_and_returns_submitted_user_data() { - let mut ns = build_node_stream(); - ns.open_substream(vec![2]); - ns.open_substream(vec![3]); - ns.open_substream(vec![5]); - let user_data_submitted = ns.close(); - assert_eq!(user_data_submitted.1, vec![ - vec![2], vec![3], vec![5] - ]); - } - - #[test] - fn poll_returns_not_ready_when_there_is_nothing_to_do() { - let mut task = MockTask::new(); - task.enter(|| { - // ensure the address never resolves - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - - assert_matches!(ns.poll(), Ok(Async::NotReady)); - }); - } - - #[test] - fn poll_keeps_outbound_substreams_when_the_outgoing_connection_is_not_ready() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - ns.open_substream(vec![1]); - ns.poll().unwrap(); // poll past inbound - ns.poll().unwrap(); // poll outbound - assert!(format!("{:?}", ns).contains("outbound_substreams: 1")); - } - - #[test] - fn poll_returns_incoming_substream() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::Ready(subs) - muxer.set_inbound_connection_state(DummyConnectionState::Opened); - let mut ns = NodeStream::<_, Vec>::new(muxer); - assert_matches!(ns.poll(), Ok(Async::Ready(node_event)) => { - assert_matches!(node_event, NodeEvent::InboundSubstream{ substream: _ }); - }); - } -} diff --git a/core/src/nodes/tasks/manager.rs b/core/src/nodes/tasks/manager.rs index 33643aaa..aff72bd9 100644 --- a/core/src/nodes/tasks/manager.rs +++ b/core/src/nodes/tasks/manager.rs @@ -27,9 +27,8 @@ use crate::{ } }; use fnv::FnvHashMap; -use futures::{prelude::*, future::Executor, sync::mpsc}; -use smallvec::SmallVec; -use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt}; +use futures::{prelude::*, channel::mpsc, executor::ThreadPool, stream::FuturesUnordered, task::SpawnExt as _}; +use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt, pin::Pin, task::Context, task::Poll}; use super::{TaskId, task::{Task, FromTaskMessage, ToTaskMessage}, Error}; // Implementor notes @@ -64,12 +63,13 @@ pub struct Manager { /// Identifier for the next task to spawn. next_task_id: TaskId, - /// List of node tasks to spawn. - to_spawn: SmallVec<[Box + Send>; 8]>, + /// Threads pool where we spawn the nodes' tasks. If `None`, then we push tasks to the + /// `local_spawns` list instead. + threads_pool: Option, - /// If no tokio executor is available, we move tasks to this list, and futures are polled on - /// the current thread instead. - local_spawns: Vec + Send>>, + /// If no executor is available, we move tasks to this list, and futures are polled on the + /// current thread instead. + local_spawns: FuturesUnordered + Send>>>, /// Sender to emit events to the outside. Meant to be cloned and sent to tasks. events_tx: mpsc::Sender<(FromTaskMessage, TaskId)>, @@ -91,16 +91,13 @@ where /// Information about a running task. /// -/// Contains the sender to deliver event messages to the task, -/// the associated user data and a pending message if any, -/// meant to be delivered to the task via the sender. +/// Contains the sender to deliver event messages to the task, and +/// the associated user data. struct TaskInfo
( muxer: P, -) -> impl Future, Error = ::Error> +) -> impl Future, ::Error>> where P: Deref + Clone, P::Target: StreamMuxer, { let muxer2 = muxer.clone(); - future::poll_fn(move || muxer.poll_inbound()) - .map(|substream| substream_from_ref(muxer2, substream)) + future::poll_fn(move |cx| muxer.poll_inbound(cx)) + .map_ok(|substream| substream_from_ref(muxer2, substream)) } /// Same as `outbound_from_ref`, but wraps the output in an object that @@ -258,17 +250,16 @@ where P: Deref + Clone, P::Target: StreamMuxer, { - type Item = SubstreamRef; - type Error = ::Error; + type Output = Result, ::Error>; - fn poll(&mut self) -> Poll { - match self.inner.poll() { - Ok(Async::Ready(substream)) => { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match Future::poll(Pin::new(&mut self.inner), cx) { + Poll::Ready(Ok(substream)) => { let out = substream_from_ref(self.inner.muxer.clone(), substream); - Ok(Async::Ready(out)) + Poll::Ready(Ok(out)) } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(err) => Err(err), + Poll::Pending => Poll::Pending, + Poll::Ready(Err(err)) => Poll::Ready(Err(err)), } } } @@ -297,18 +288,26 @@ where outbound: Option<::OutboundSubstream>, } +impl Unpin for OutboundSubstreamRefFuture +where + P: Deref, + P::Target: StreamMuxer, +{ +} + impl Future for OutboundSubstreamRefFuture where P: Deref, P::Target: StreamMuxer, { - type Item = ::Substream; - type Error = ::Error; + type Output = Result<::Substream, ::Error>; #[inline] - fn poll(&mut self) -> Poll { - self.muxer - .poll_outbound(self.outbound.as_mut().expect("outbound was empty")) + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + this.muxer.poll_outbound(cx, this.outbound.as_mut().expect("outbound was empty")) } } @@ -370,20 +369,11 @@ where } } - -impl Read for SubstreamRef +impl Unpin for SubstreamRef where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.read_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } } impl AsyncRead for SubstreamRef @@ -391,37 +381,17 @@ where P: Deref, P::Target: StreamMuxer, { - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.muxer.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.muxer.initializer() } - fn poll_read(&mut self, buf: &mut [u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.read_substream(s, buf).map_err(|e| e.into()) - } -} + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; -impl Write for SubstreamRef -where - P: Deref, - P::Target: StreamMuxer, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.write_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } - - #[inline] - fn flush(&mut self) -> Result<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.flush_substream(s).map_err(|e| e.into())? { - Async::Ready(()) => Ok(()), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.read_substream(cx, s, buf).map_err(|e| e.into()) } } @@ -430,36 +400,51 @@ where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn poll_write(&mut self, buf: &[u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.write_substream(s, buf).map_err(|e| e.into()) + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.write_substream(cx, s, buf).map_err(|e| e.into()) } - #[inline] - fn shutdown(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); loop { - match self.shutdown_state { + match this.shutdown_state { ShutdownState::Shutdown => { - try_ready!(self.muxer.shutdown_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Flush; + match this.muxer.shutdown_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Flush, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Flush => { - try_ready!(self.muxer.flush_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Done; + match this.muxer.flush_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Done, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Done => { - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } } } } - #[inline] - fn poll_flush(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.flush_substream(s).map_err(|e| e.into()) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.flush_substream(cx, s).map_err(|e| e.into()) } } @@ -507,8 +492,8 @@ impl StreamMuxer for StreamMuxerBox { type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - self.inner.poll_inbound() + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + self.inner.poll_inbound(cx) } #[inline] @@ -517,8 +502,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn poll_outbound(&self, s: &mut Self::OutboundSubstream) -> Poll { - self.inner.poll_outbound(s) + fn poll_outbound(&self, cx: &mut Context, s: &mut Self::OutboundSubstream) -> Poll> { + self.inner.poll_outbound(cx, s) } #[inline] @@ -526,28 +511,28 @@ impl StreamMuxer for StreamMuxerBox { self.inner.destroy_outbound(substream) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { - self.inner.read_substream(s, buf) + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + self.inner.read_substream(cx, s, buf) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.write_substream(s, buf) + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { + self.inner.write_substream(cx, s, buf) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.flush_substream(s) + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.flush_substream(cx, s) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.shutdown_substream(s) + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.shutdown_substream(cx, s) } #[inline] @@ -556,8 +541,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close() + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx) } #[inline] @@ -566,8 +551,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all() + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx) } } @@ -588,11 +573,16 @@ where type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - let substream = try_ready!(self.inner.poll_inbound().map_err(|e| e.into())); + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + let substream = match self.inner.poll_inbound(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; + let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -606,13 +596,18 @@ where #[inline] fn poll_outbound( &self, + cx: &mut Context, substream: &mut Self::OutboundSubstream, - ) -> Poll { + ) -> Poll> { let mut list = self.outbound.lock(); - let substream = try_ready!(self.inner.poll_outbound(list.get_mut(substream).unwrap()).map_err(|e| e.into())); + let substream = match self.inner.poll_outbound(cx, list.get_mut(substream).unwrap()) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -621,32 +616,32 @@ where self.inner.destroy_outbound(list.remove(&substream).unwrap()) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.read_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.read_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.write_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.write_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.flush_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.flush_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.shutdown_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.shutdown_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] @@ -656,8 +651,8 @@ where } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close().map_err(|e| e.into()) + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx).map_err(|e| e.into()) } #[inline] @@ -666,7 +661,7 @@ where } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all().map_err(|e| e.into()) + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx).map_err(|e| e.into()) } } diff --git a/core/src/muxing/singleton.rs b/core/src/muxing/singleton.rs index 7bec14ed..f85e22fd 100644 --- a/core/src/muxing/singleton.rs +++ b/core/src/muxing/singleton.rs @@ -19,10 +19,9 @@ // DEALINGS IN THE SOFTWARE. use crate::{Endpoint, muxing::StreamMuxer}; -use futures::prelude::*; +use futures::{prelude::*, io::Initializer}; use parking_lot::Mutex; -use std::{io, sync::atomic::{AtomicBool, Ordering}}; -use tokio_io::{AsyncRead, AsyncWrite}; +use std::{io, pin::Pin, sync::atomic::{AtomicBool, Ordering}, task::Context, task::Poll}; /// Implementation of `StreamMuxer` that allows only one substream on top of a connection, /// yielding the connection itself. @@ -62,22 +61,22 @@ pub struct OutboundSubstream {} impl StreamMuxer for SingletonMuxer where - TSocket: AsyncRead + AsyncWrite, + TSocket: AsyncRead + AsyncWrite + Unpin, { type Substream = Substream; type OutboundSubstream = OutboundSubstream; type Error = io::Error; - fn poll_inbound(&self) -> Poll { + fn poll_inbound(&self, _: &mut Context) -> Poll> { match self.endpoint { - Endpoint::Dialer => return Ok(Async::NotReady), + Endpoint::Dialer => return Poll::Pending, Endpoint::Listener => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -85,44 +84,44 @@ where OutboundSubstream {} } - fn poll_outbound(&self, _: &mut Self::OutboundSubstream) -> Poll { + fn poll_outbound(&self, _: &mut Context, _: &mut Self::OutboundSubstream) -> Poll> { match self.endpoint { - Endpoint::Listener => return Ok(Async::NotReady), + Endpoint::Listener => return Poll::Pending, Endpoint::Dialer => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } fn destroy_outbound(&self, _: Self::OutboundSubstream) { } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.lock().prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.lock().initializer() } - fn read_substream(&self, _: &mut Self::Substream, buf: &mut [u8]) -> Poll { - let res = self.inner.lock().poll_read(buf); - if let Ok(Async::Ready(_)) = res { + fn read_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + let res = AsyncRead::poll_read(Pin::new(&mut *self.inner.lock()), cx, buf); + if let Poll::Ready(Ok(_)) = res { self.remote_acknowledged.store(true, Ordering::Release); } res } - fn write_substream(&self, _: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.lock().poll_write(buf) + fn write_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &[u8]) -> Poll> { + AsyncWrite::poll_write(Pin::new(&mut *self.inner.lock()), cx, buf) } - fn flush_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } - fn shutdown_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().shutdown() + fn shutdown_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_close(Pin::new(&mut *self.inner.lock()), cx) } fn destroy_substream(&self, _: Self::Substream) { @@ -132,12 +131,12 @@ where self.remote_acknowledged.load(Ordering::Acquire) } - fn close(&self) -> Poll<(), io::Error> { + fn close(&self, cx: &mut Context) -> Poll> { // The `StreamMuxer` trait requires that `close()` implies `flush_all()`. - self.flush_all() + self.flush_all(cx) } - fn flush_all(&self) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_all(&self, cx: &mut Context) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } } diff --git a/core/src/nodes/collection.rs b/core/src/nodes/collection.rs index af8601d2..9e212810 100644 --- a/core/src/nodes/collection.rs +++ b/core/src/nodes/collection.rs @@ -29,11 +29,7 @@ use crate::{ }; use fnv::FnvHashMap; use futures::prelude::*; -use std::{error, fmt, hash::Hash, mem}; - -pub use crate::nodes::tasks::StartTakeOver; - -mod tests; +use std::{error, fmt, hash::Hash, mem, task::Context, task::Poll}; /// Implementation of `Stream` that handles a collection of nodes. pub struct CollectionStream { @@ -58,6 +54,9 @@ where } } +impl Unpin for + CollectionStream { } + /// State of a task. #[derive(Debug, Clone, PartialEq, Eq)] enum TaskState { @@ -323,7 +322,7 @@ where pub fn add_reach_attempt(&mut self, future: TFut, handler: THandler) -> ReachAttemptId where - TFut: Future + Send + 'static, + TFut: Future> + Unpin + Send + 'static, THandler: IntoNodeHandler + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, @@ -358,17 +357,19 @@ where } /// Sends an event to all nodes. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.inner.start_broadcast(event) } + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.inner.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_broadcast(cx) } /// Adds an existing connection to a node to the collection. @@ -447,13 +448,13 @@ where /// > **Note**: we use a regular `poll` method instead of implementing `Stream` in order to /// > remove the `Err` variant, but also because we want the `CollectionStream` to stay /// > borrowed if necessary. - pub fn poll(&mut self) -> Async> + pub fn poll(&mut self, cx: &mut Context) -> Poll> where TConnInfo: Clone, // TODO: Clone shouldn't be necessary { - let item = match self.inner.poll() { - Async::Ready(item) => item, - Async::NotReady => return Async::NotReady, + let item = match self.inner.poll(cx) { + Poll::Ready(item) => item, + Poll::Pending => return Poll::Pending, }; match item { @@ -463,7 +464,7 @@ where match (user_data, result, handler) { (TaskState::Pending, tasks::Error::Reach(err), Some(handler)) => { - Async::Ready(CollectionEvent::ReachError { + Poll::Ready(CollectionEvent::ReachError { id: ReachAttemptId(id), error: err, handler, @@ -482,7 +483,7 @@ where debug_assert!(_handler.is_none()); let _node_task_id = self.nodes.remove(conn_info.peer_id()); debug_assert_eq!(_node_task_id, Some(id)); - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error: err, user_data, @@ -497,8 +498,8 @@ where tasks::Event::NodeReached { task, conn_info } => { let id = task.id(); drop(task); - Async::Ready(CollectionEvent::NodeReached(CollectionReachEvent { - parent: self, + Poll::Ready(CollectionEvent::NodeReached(CollectionReachEvent { + parent: &mut *self, id, conn_info: Some(conn_info), })) @@ -512,7 +513,7 @@ where self.tasks is switched to the Connected state; QED"), }; drop(task); - Async::Ready(CollectionEvent::NodeEvent { + Poll::Ready(CollectionEvent::NodeEvent { // TODO: normally we'd build a `PeerMut` manually here, but the borrow checker // doesn't like it peer: self.peer_mut(&conn_info.peer_id()) @@ -616,14 +617,15 @@ where } } - /// Sends an event to the given node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Begin sending an event to the given node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.inner.start_send_event(event) } - /// Complete sending an event message initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { - self.inner.complete_send_event() + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_event(cx) } /// Closes the connections to this node. Returns the user data. @@ -648,23 +650,13 @@ where /// The reach attempt will only be effectively cancelled once the peer (the object you're /// manipulating) has received some network activity. However no event will be ever be /// generated from this reach attempt, and this takes effect immediately. - #[must_use] - pub fn start_take_over(&mut self, id: InterruptedReachAttempt) - -> StartTakeOver<(), InterruptedReachAttempt> - { - match self.inner.start_take_over(id.inner) { - StartTakeOver::Ready(_state) => { - debug_assert!(if let TaskState::Pending = _state { true } else { false }); - StartTakeOver::Ready(()) - } - StartTakeOver::NotReady(inner) => - StartTakeOver::NotReady(InterruptedReachAttempt { inner }), - StartTakeOver::Gone => StartTakeOver::Gone - } + pub fn start_take_over(&mut self, id: InterruptedReachAttempt) { + self.inner.start_take_over(id.inner) } - /// Complete a take over initiated by `start_take_over`. - pub fn complete_take_over(&mut self) -> Poll<(), ()> { - self.inner.complete_take_over() + /// Make sure we are ready to taking over with `start_take_over`. + #[must_use] + pub fn poll_ready_take_over(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_take_over(cx) } } diff --git a/core/src/nodes/collection/tests.rs b/core/src/nodes/collection/tests.rs deleted file mode 100644 index 69f82c05..00000000 --- a/core/src/nodes/collection/tests.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use futures::future; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, InEvent, OutEvent, HandlerState}; -use tokio::runtime::current_thread::Runtime; -use tokio::runtime::Builder; -use crate::nodes::NodeHandlerEvent; -use std::{io, sync::Arc}; -use parking_lot::Mutex; - -type TestCollectionStream = CollectionStream; - -#[test] -fn has_connection_is_false_before_a_connection_has_been_made() { - let cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(!cs.has_connection(&peer_id)); -} - -#[test] -fn connections_is_empty_before_connecting() { - let cs = TestCollectionStream::new(); - assert!(cs.connections().next().is_none()); -} - -#[test] -fn retrieving_a_peer_is_none_if_peer_is_missing_or_not_connected() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(cs.peer_mut(&peer_id).is_none()); - - let handler = Handler::default(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, handler); - assert!(cs.peer_mut(&peer_id).is_none()); // task is pending -} - -#[test] -fn collection_stream_reaches_the_nodes() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((peer_id, muxer)); - cs.add_reach_attempt(fut, Handler::default()); - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - let event = cs.poll(); - match poll_count { - 1 => assert_matches!(event, Async::NotReady), - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(_))); - return Ok(Async::Ready(())); // stop - } - _ => unreachable!() - } - Ok(Async::NotReady) - }); - rt.block_on(fut).unwrap(); -} - -#[test] -fn accepting_a_node_yields_new_entry() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, Handler::default()); - - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - { - let event = cs.poll(); - match poll_count { - 1 => { - assert_matches!(event, Async::NotReady); - return Ok(Async::NotReady) - } - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - let (accept_ev, accepted_peer_id) = reach_ev.accept(()); - assert_eq!(accepted_peer_id, peer_id); - assert_matches!(accept_ev, CollectionNodeAccept::NewEntry); - }); - } - _ => unreachable!() - } - } - assert!(cs.peer_mut(&peer_id).is_some(), "peer is not in the list"); - assert!(cs.has_connection(&peer_id), "peer is not connected"); - assert_eq!(cs.connections().collect::>(), vec![&peer_id]); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("running the future works"); -} - -#[test] -fn events_in_a_node_reaches_the_collection_stream() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_peer_id = PeerId::random(); - - let mut handler = Handler::default(); - handler.state = Some(HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("init")))); - let handler_states = vec![ - HandlerState::Err, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 3") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 2") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )), - ]; - handler.next_states = handler_states; - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((task_peer_id.clone(), muxer)); - cs.lock().add_reach_attempt(fut, handler); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("init")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 2")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn task_closed_with_error_while_task_is_pending_yields_reach_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_inner_fut = future::err(std::io::Error::new(std::io::ErrorKind::Other, "inner fut error")); - let reach_attempt_id = cs.lock().add_reach_attempt(task_inner_fut, Handler::default()); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::ReachError {id, error, ..} => { - assert_eq!(id, reach_attempt_id); - assert_eq!(error.to_string(), "inner fut error"); - }); - - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - -} - -#[test] -fn task_closed_with_error_when_task_is_connected_yields_node_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Err]; // triggered when sending a NextState event - - cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(cs.complete_broadcast()) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id)); - - // Assert the node errored - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::NodeClosed{..}); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn interrupting_a_pending_connection_attempt_is_ok() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - let interrupt = cs.interrupt(reach_id); - assert!(interrupt.is_ok()); -} - -#[test] -fn interrupting_a_connection_attempt_twice_is_err() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - assert!(cs.interrupt(reach_id).is_ok()); - assert_matches!(cs.interrupt(reach_id), Err(InterruptError::ReachAttemptNotFound)) -} - -#[test] -fn interrupting_an_established_connection_is_err() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let handler = Handler::default(); - - let reach_id = cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(Async::Ready(())) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id), "Connection was not established"); - - assert_matches!(cs.lock().interrupt(reach_id), Err(InterruptError::AlreadyReached)); -} diff --git a/core/src/nodes/handled_node.rs b/core/src/nodes/handled_node.rs index 150b5e45..f8b08d11 100644 --- a/core/src/nodes/handled_node.rs +++ b/core/src/nodes/handled_node.rs @@ -20,10 +20,7 @@ use crate::{PeerId, muxing::StreamMuxer}; use crate::nodes::node::{NodeEvent, NodeStream, Substream, Close}; -use futures::prelude::*; -use std::{error, fmt, io}; - -mod tests; +use std::{error, fmt, io, pin::Pin, task::Context, task::Poll}; /// Handler for the substreams of a node. // TODO: right now it is possible for a node handler to be built, then shut down right after if we @@ -59,7 +56,8 @@ pub trait NodeHandler { /// Should behave like `Stream::poll()`. /// /// Returning an error will close the connection to the remote. - fn poll(&mut self) -> Poll, Self::Error>; + fn poll(&mut self, cx: &mut Context) + -> Poll, Self::Error>>; } /// Prototype for a `NodeHandler`. @@ -172,6 +170,13 @@ where } } +impl Unpin for HandledNode +where + TMuxer: StreamMuxer, + THandler: NodeHandler>, +{ +} + impl HandledNode where TMuxer: StreamMuxer, @@ -214,37 +219,41 @@ where } /// API similar to `Future::poll` that polls the node for events. - pub fn poll(&mut self) -> Poll> { + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) + -> Poll>> + { loop { let mut node_not_ready = false; - match self.node.poll().map_err(HandledNodeError::Node)? { - Async::NotReady => node_not_ready = true, - Async::Ready(NodeEvent::InboundSubstream { substream }) => { + match self.node.poll(cx) { + Poll::Pending => node_not_ready = true, + Poll::Ready(Ok(NodeEvent::InboundSubstream { substream })) => { self.handler.inject_substream(substream, NodeHandlerEndpoint::Listener) } - Async::Ready(NodeEvent::OutboundSubstream { user_data, substream }) => { + Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream })) => { let endpoint = NodeHandlerEndpoint::Dialer(user_data); self.handler.inject_substream(substream, endpoint) } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Node(err))), } - match self.handler.poll().map_err(HandledNodeError::Handler)? { - Async::NotReady => { + match self.handler.poll(cx) { + Poll::Pending => { if node_not_ready { break } } - Async::Ready(NodeHandlerEvent::OutboundSubstreamRequest(user_data)) => { + Poll::Ready(Ok(NodeHandlerEvent::OutboundSubstreamRequest(user_data))) => { self.node.open_substream(user_data); } - Async::Ready(NodeHandlerEvent::Custom(event)) => { - return Ok(Async::Ready(event)); + Poll::Ready(Ok(NodeHandlerEvent::Custom(event))) => { + return Poll::Ready(Ok(event)); } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Handler(err))), } } - Ok(Async::NotReady) + Poll::Pending } } diff --git a/core/src/nodes/handled_node/tests.rs b/core/src/nodes/handled_node/tests.rs deleted file mode 100644 index ee138c2e..00000000 --- a/core/src/nodes/handled_node/tests.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent, TestHandledNode}; - -struct TestBuilder { - muxer: DummyMuxer, - handler: Handler, - want_open_substream: bool, - substream_user_data: usize, -} - -impl TestBuilder { - fn new() -> Self { - TestBuilder { - muxer: DummyMuxer::new(), - handler: Handler::default(), - want_open_substream: false, - substream_user_data: 0, - } - } - - fn with_muxer_inbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_inbound_connection_state(state); - self - } - - fn with_muxer_outbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_outbound_connection_state(state); - self - } - - fn with_handler_state(&mut self, state: HandlerState) -> &mut Self { - self.handler.state = Some(state); - self - } - - fn with_open_substream(&mut self, user_data: usize) -> &mut Self { - self.want_open_substream = true; - self.substream_user_data = user_data; - self - } - - fn handled_node(&mut self) -> TestHandledNode { - let mut h = HandledNode::new(self.muxer.clone(), self.handler.clone()); - if self.want_open_substream { - h.node.open_substream(self.substream_user_data); - } - h - } -} - -// Set the state of the `Handler` after `inject_outbound_closed` is called -fn set_next_handler_outbound_state( handled_node: &mut TestHandledNode, next_state: HandlerState) { - handled_node.handler.next_outbound_state = Some(next_state); -} - -#[test] -fn can_inject_event() { - let mut handled = TestBuilder::new() - .handled_node(); - - let event = InEvent::Custom("banana"); - handled.inject_event(event.clone()); - assert_eq!(handled.handler().events, vec![event]); -} - -#[test] -fn poll_with_unready_node_stream_and_handler_emits_custom_event() { - let expected_event = NodeHandlerEvent::Custom(OutEvent::Custom("pineapple")); - let mut handled = TestBuilder::new() - // make NodeStream return NotReady - .with_muxer_inbound_state(DummyConnectionState::Pending) - // make Handler return return Ready(Some(…)) - .with_handler_state(HandlerState::Ready(expected_event)) - .handled_node(); - - assert_matches!(handled.poll(), Ok(Async::Ready(event)) => { - assert_matches!(event, OutEvent::Custom("pineapple")) - }); -} - -#[test] -fn handler_emits_outbound_closed_when_opening_new_substream_on_closed_node() { - let open_event = NodeHandlerEvent::OutboundSubstreamRequest(456); - let mut handled = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Ready(open_event)) - .handled_node(); - - set_next_handler_outbound_state( - &mut handled, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("pear"))) - ); - handled.poll().expect("poll works"); -} - -#[test] -fn poll_yields_inbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_open_substream(32) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Opened) - .with_open_substream(1) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(Some(1))]); -} - -#[test] -fn poll_yields_inbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Opened) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(None)]); -} diff --git a/core/src/nodes/listeners.rs b/core/src/nodes/listeners.rs index effcea65..b9c8ebbf 100644 --- a/core/src/nodes/listeners.rs +++ b/core/src/nodes/listeners.rs @@ -21,11 +21,10 @@ //! Manage listening on multiple multiaddresses at once. use crate::{Multiaddr, Transport, transport::{TransportError, ListenerEvent}}; -use futures::prelude::*; +use futures::{prelude::*, task::Context, task::Poll}; use log::debug; use smallvec::SmallVec; -use std::{collections::VecDeque, fmt}; -use void::Void; +use std::{collections::VecDeque, fmt, pin::Pin}; /// Implementation of `futures::Stream` that allows listening on multiaddresses. /// @@ -158,7 +157,7 @@ where /// The ID of the listener that errored. listener_id: ListenerId, /// The error value. - error: ::Error + error: ::Error } } @@ -222,28 +221,31 @@ where self.listeners.iter().flat_map(|l| l.addresses.iter()) } - /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> { + /// Provides an API similar to `Stream`, except that it cannot end. + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> + where + TTrans::Listener: Unpin, + { // We remove each element from `listeners` one by one and add them back. let mut remaining = self.listeners.len(); while let Some(mut listener) = self.listeners.pop_back() { - match listener.listener.poll() { - Ok(Async::NotReady) => { + match TryStream::try_poll_next(Pin::new(&mut listener.listener), cx) { + Poll::Pending => { self.listeners.push_front(listener); remaining -= 1; if remaining == 0 { break } } - Ok(Async::Ready(Some(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { + Poll::Ready(Some(Ok(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::Incoming { + return Poll::Ready(ListenersEvent::Incoming { listener_id: id, upgrade, local_addr, send_back_addr: remote_addr }) } - Ok(Async::Ready(Some(ListenerEvent::NewAddress(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::NewAddress(a)))) => { if listener.addresses.contains(&a) { debug!("Transport has reported address {} multiple times", a) } @@ -252,28 +254,28 @@ where } let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::NewAddress { + return Poll::Ready(ListenersEvent::NewAddress { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(Some(ListenerEvent::AddressExpired(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::AddressExpired(a)))) => { listener.addresses.retain(|x| x != &a); let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::AddressExpired { + return Poll::Ready(ListenersEvent::AddressExpired { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(None)) => { - return Async::Ready(ListenersEvent::Closed { + Poll::Ready(None) => { + return Poll::Ready(ListenersEvent::Closed { listener_id: listener.id, listener: listener.listener }) } - Err(err) => { - return Async::Ready(ListenersEvent::Error { + Poll::Ready(Some(Err(err))) => { + return Poll::Ready(ListenersEvent::Error { listener_id: listener.id, error: err }) @@ -282,22 +284,28 @@ where } // We register the current task to be woken up if a new listener is added. - Async::NotReady + Poll::Pending } } impl Stream for ListenersStream where TTrans: Transport, + TTrans::Listener: Unpin, { type Item = ListenersEvent; - type Error = Void; // TODO: use ! once stable - fn poll(&mut self) -> Poll, Self::Error> { - Ok(self.poll().map(Option::Some)) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ListenersStream::poll(self, cx).map(Option::Some) } } +impl Unpin for ListenersStream +where + TTrans: Transport, +{ +} + impl fmt::Debug for ListenersStream where TTrans: Transport + fmt::Debug, @@ -313,7 +321,7 @@ where impl fmt::Debug for ListenersEvent where TTrans: Transport, - ::Error: fmt::Debug, + ::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { @@ -353,215 +361,37 @@ mod tests { use tokio::runtime::current_thread::Runtime; use std::{io, iter::FromIterator}; use futures::{future::{self}, stream}; - use crate::tests::dummy_transport::{DummyTransport, ListenerState}; - use crate::tests::dummy_muxer::DummyMuxer; use crate::PeerId; - fn set_listener_state(ls: &mut ListenersStream, idx: usize, state: ListenerState) { - ls.listeners[idx].listener = match state { - ListenerState::Error => - Box::new(stream::poll_fn(|| Err(io::Error::new(io::ErrorKind::Other, "oh noes")))), - ListenerState::Ok(state) => match state { - Async::NotReady => Box::new(stream::poll_fn(|| Ok(Async::NotReady))), - Async::Ready(Some(event)) => Box::new(stream::poll_fn(move || { - Ok(Async::Ready(Some(event.clone().map(future::ok)))) - })), - Async::Ready(None) => Box::new(stream::empty()) - } - ListenerState::Events(events) => - Box::new(stream::iter_ok(events.into_iter().map(|e| e.map(future::ok)))) - }; - } - #[test] fn incoming_event() { - let mem_transport = transport::MemoryTransport::default(); + futures::executor::block_on(async move { + let mem_transport = transport::MemoryTransport::default(); - let mut listeners = ListenersStream::new(mem_transport); - listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); + let mut listeners = ListenersStream::new(mem_transport); + listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); - let address = { - let event = listeners.by_ref().wait().next().expect("some event").expect("no error"); - if let ListenersEvent::NewAddress { listen_addr, .. } = event { - listen_addr - } else { - panic!("Was expecting the listen address to be reported") - } - }; - - let dial = mem_transport.dial(address.clone()).unwrap(); - - let future = listeners - .into_future() - .map_err(|(err, _)| err) - .and_then(|(event, _)| { - match event { - Some(ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. }) => { - assert_eq!(local_addr, address); - assert_eq!(send_back_addr, address); - upgrade.map(|_| ()).map_err(|_| panic!()) - }, - _ => panic!() + let address = { + let event = listeners.next().await.unwrap(); + if let ListenersEvent::NewAddress { listen_addr, .. } = event { + listen_addr + } else { + panic!("Was expecting the listen address to be reported") } - }) - .select(dial.map(|_| ()).map_err(|_| panic!())) - .map_err(|(err, _)| err); + }; - let mut runtime = Runtime::new().unwrap(); - runtime.block_on(future).unwrap(); - } + let address2 = address.clone(); + async_std::task::spawn(async move { + mem_transport.dial(address2).unwrap().await.unwrap(); + }); - #[test] - fn listener_stream_returns_transport() { - let t = DummyTransport::new(); - let t_clone = t.clone(); - let ls = ListenersStream::new(t); - assert_eq!(ls.transport(), &t_clone); - } - - #[test] - fn listener_stream_can_iterate_over_listeners() { - let mut t = DummyTransport::new(); - let addr1 = tcp4([127, 0, 0, 1], 1234); - let addr2 = tcp4([127, 0, 0, 1], 4321); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(addr1.clone()), - ListenerEvent::NewAddress(addr2.clone()) - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([0, 0, 0, 0], 0)).expect("listen_on"); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr1, listen_addr) - }); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr2, listen_addr) - }) - } - - #[test] - fn listener_stream_poll_without_listeners_is_not_ready() { - let t = DummyTransport::new(); - let mut ls = ListenersStream::new(t); - assert_matches!(ls.poll(), Async::NotReady); - } - - #[test] - fn listener_stream_poll_with_listeners_that_arent_ready_is_not_ready() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::NotReady)); - assert_matches!(ls.poll(), Async::NotReady); - assert_eq!(ls.listeners.len(), 1); // listener is still there - } - - #[test] - fn listener_stream_poll_with_ready_listeners_is_ready() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let expected_output = (peer_id.clone(), muxer.clone()); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(tcp4([127, 0, 0, 1], 9090)), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) + match listeners.next().await.unwrap() { + ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. } => { + assert_eq!(local_addr, address); + assert_eq!(send_back_addr, address); + }, + _ => panic!() } - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([127, 0, 0, 1], 1234)).expect("listen_on"); - ls.listen_on(tcp4([127, 0, 0, 1], 4321)).expect("listen_on"); - assert_eq!(ls.listeners.len(), 2); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - set_listener_state(&mut ls, 1, ListenerState::Ok(Async::NotReady)); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - } - - #[test] - fn listener_stream_poll_with_closed_listener_emits_closed_event() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::Ready(None))); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Closed{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - #[test] - fn listener_stream_poll_with_erroring_listener_emits_error_event() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let event = ListenerEvent::Upgrade { - upgrade: (peer_id, muxer), - local_addr: tcp4([127, 0, 0, 1], 1234), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }; - t.set_initial_listener_state(ListenerState::Ok(Async::Ready(Some(event)))); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Error); // simulate an error on the socket - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Error{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - fn tcp4(ip: [u8; 4], port: u16) -> Multiaddr { - let protos = std::iter::once(multiaddr::Protocol::Ip4(ip.into())) - .chain(std::iter::once(multiaddr::Protocol::Tcp(port))); - Multiaddr::from_iter(protos) } } diff --git a/core/src/nodes/network.rs b/core/src/nodes/network.rs index abe9e631..2f6634a1 100644 --- a/core/src/nodes/network.rs +++ b/core/src/nodes/network.rs @@ -49,10 +49,10 @@ use std::{ fmt, hash::Hash, num::NonZeroUsize, + pin::Pin, + task::{Context, Poll}, }; -pub use crate::nodes::collection::StartTakeOver; - mod tests; /// Implementation of `Stream` that handles the nodes. @@ -81,7 +81,7 @@ where /// If the pair's second element is `AsyncSink::Ready`, the take over /// message has been sent and needs to be flushed using /// `PeerMut::complete_take_over`. - take_over_to_complete: Option<(TPeerId, AsyncSink>)> + take_over_to_complete: Option<(TPeerId, InterruptedReachAttempt)> } impl fmt::Debug for @@ -102,6 +102,13 @@ where } } +impl Unpin for + Network +where + TTrans: Transport +{ +} + impl ConnectionInfo for (TConnInfo, ConnectedPoint) where TConnInfo: ConnectionInfo @@ -173,7 +180,7 @@ where /// The listener that errored. listener_id: ListenerId, /// The listener error. - error: ::Error + error: ::Error }, /// One of the listeners is now listening on an additional address. @@ -573,7 +580,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::ListenerUpgrade: Send + 'static, + TTrans::ListenerUpgrade: Unpin + Send + 'static, THandler: IntoNodeHandler<(TConnInfo, ConnectedPoint)> + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, // TODO: shouldn't be necessary @@ -609,9 +616,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -781,7 +788,7 @@ where where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TInEvent: Send + 'static, @@ -797,9 +804,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -840,19 +847,18 @@ where /// Start sending an event to all nodes. /// - /// Make sure to complete the broadcast with `complete_broadcast`. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.active_nodes.start_broadcast(event) } - /// Complete a broadcast initiated with `start_broadcast`. + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.active_nodes.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.active_nodes.poll_ready_broadcast(cx) } /// Returns a list of all the peers we are currently connected to. @@ -934,7 +940,7 @@ where fn start_dial_out(&mut self, peer_id: TPeerId, handler: THandler, first: Multiaddr, rest: Vec) where TTrans: Transport, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TTrans::Error: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -950,9 +956,9 @@ where .map_err(|err| InternalReachErr::Transport(TransportError::Other(err))) .and_then(move |(actual_conn_info, muxer)| { if *actual_conn_info.peer_id() == expected_peer_id { - Ok(((actual_conn_info, connected_point), muxer)) + future::ready(Ok(((actual_conn_info, connected_point), muxer))) } else { - Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info }) + future::ready(Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info })) } }); self.active_nodes.add_reach_attempt(fut, handler) @@ -976,11 +982,12 @@ where } /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> + pub fn poll<'a>(&'a mut self, cx: &mut Context) -> Poll> where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, + TTrans::Listener: Unpin, TTrans::ListenerUpgrade: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -998,9 +1005,9 @@ where Some(x) if self.incoming_negotiated().count() >= (x as usize) => (), _ => { - match self.listeners.poll() { - Async::NotReady => (), - Async::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { + match ListenersStream::poll(Pin::new(&mut self.listeners), cx) { + Poll::Pending => (), + Poll::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { let event = IncomingConnectionEvent { listener_id, upgrade, @@ -1010,19 +1017,19 @@ where active_nodes: &mut self.active_nodes, other_reach_attempts: &mut self.reach_attempts.other_reach_attempts, }; - return Async::Ready(NetworkEvent::IncomingConnection(event)); + return Poll::Ready(NetworkEvent::IncomingConnection(event)); } - Async::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::Closed { listener_id, listener }) => { - return Async::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) + Poll::Ready(ListenersEvent::Closed { listener_id, listener }) => { + return Poll::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) } - Async::Ready(ListenersEvent::Error { listener_id, error }) => { - return Async::Ready(NetworkEvent::ListenerError { listener_id, error }) + Poll::Ready(ListenersEvent::Error { listener_id, error }) => { + return Poll::Ready(NetworkEvent::ListenerError { listener_id, error }) } } } @@ -1031,36 +1038,30 @@ where // Attempt to deliver any pending take over messages. if let Some((id, interrupted)) = self.take_over_to_complete.take() { if let Some(mut peer) = self.active_nodes.peer_mut(&id) { - if let AsyncSink::NotReady(i) = interrupted { - if let StartTakeOver::NotReady(i) = peer.start_take_over(i) { - self.take_over_to_complete = Some((id, AsyncSink::NotReady(i))) - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) - } - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((id, interrupted)); + return Poll::Pending; } } } - if self.take_over_to_complete.is_some() { - return Async::NotReady - } // Poll the existing nodes. let (action, out_event); - match self.active_nodes.poll() { - Async::NotReady => return Async::NotReady, - Async::Ready(CollectionEvent::NodeReached(reach_event)) => { + match self.active_nodes.poll(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(CollectionEvent::NodeReached(reach_event)) => { let (a, e) = handle_node_reached(&mut self.reach_attempts, reach_event); action = a; out_event = e; } - Async::Ready(CollectionEvent::ReachError { id, error, handler }) => { + Poll::Ready(CollectionEvent::ReachError { id, error, handler }) => { let (a, e) = handle_reach_error(&mut self.reach_attempts, id, error, handler); action = a; out_event = e; } - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error, .. @@ -1078,7 +1079,7 @@ where error, }; } - Async::Ready(CollectionEvent::NodeEvent { peer, event }) => { + Poll::Ready(CollectionEvent::NodeEvent { peer, event }) => { action = Default::default(); out_event = NetworkEvent::NodeEvent { conn_info: peer.info().0.clone(), event }; } @@ -1099,17 +1100,15 @@ where out_reach_attempts should always be in sync with the actual \ attempts; QED"); let mut peer = self.active_nodes.peer_mut(&peer_id).unwrap(); - if let StartTakeOver::NotReady(i) = peer.start_take_over(interrupted) { - self.take_over_to_complete = Some((peer_id, AsyncSink::NotReady(i))); - return Async::NotReady - } - if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((peer_id, AsyncSink::Ready)); - return Async::NotReady + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((peer_id, interrupted)); + return Poll::Pending } } - Async::Ready(out_event) + Poll::Ready(out_event) } } @@ -1467,7 +1466,7 @@ impl<'a, TTrans, TMuxer, TInEvent, TOutEvent, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, @@ -1644,18 +1643,33 @@ where closed messages; QED") } - /// Start sending an event to the node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Sends an event to the handler of the node. + pub fn send_event<'s: 'a>(&'s mut self, event: TInEvent) -> impl Future + 's + 'a { + let mut event = Some(event); + futures::future::poll_fn(move |cx| { + match self.poll_ready_event(cx) { + Poll::Ready(()) => { + self.start_send_event(event.take().expect("Future called after finished")); + Poll::Ready(()) + }, + Poll::Pending => Poll::Pending, + } + }) + } + + /// Begin sending an event to the node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") .start_send_event(event) } - /// Complete sending an event message, initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") - .complete_send_event() + .poll_ready_event(cx) } } @@ -1749,7 +1763,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, diff --git a/core/src/nodes/network/tests.rs b/core/src/nodes/network/tests.rs index c64666aa..c4f307bb 100644 --- a/core/src/nodes/network/tests.rs +++ b/core/src/nodes/network/tests.rs @@ -21,363 +21,6 @@ #![cfg(test)] use super::*; -use crate::tests::dummy_transport::DummyTransport; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent}; -use crate::tests::dummy_transport::ListenerState; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::nodes::NodeHandlerEvent; -use crate::transport::ListenerEvent; -use assert_matches::assert_matches; -use parking_lot::Mutex; -use std::sync::Arc; -use tokio::runtime::{Builder, Runtime}; - -#[test] -fn query_transport() { - let transport = DummyTransport::new(); - let transport2 = transport.clone(); - let network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - assert_eq!(network.transport(), &transport2); -} - -#[test] -fn local_node_peer() { - let peer_id = PeerId::random(); - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), peer_id.clone()); - assert_matches!(network.peer(peer_id), Peer::LocalNode); -} - -#[test] -fn successful_dial_reaches_a_node() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let dial_res = network.dial(addr, Handler::default()); - assert!(dial_res.is_ok()); - - // Poll the network until we get a `NodeReached` then assert on the peer: - // it's there and it's connected. - let network = Arc::new(Mutex::new(network)); - - let mut rt = Runtime::new().unwrap(); - let mut peer_id : Option = None; - // Drive forward until we're Connected - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected(PeerConnected{..})); -} - -#[test] -fn num_incoming_negotiated() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - - let events = vec![ - ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap()), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ]; - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - // no incoming yet - assert_eq!(network.incoming_negotiated().count(), 0); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - // Now there's an incoming connection - assert_eq!(network.incoming_negotiated().count(), 1); -} - -#[test] -fn broadcasted_events_reach_active_nodes() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )),]; - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::NodeEvent { conn_info: _, event: inner_event } => { - // The event we sent reached the node and triggered sending the out event we told it to return - assert_matches!(inner_event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(false)) - }, - _ => Ok(Async::Ready(true)) - } - })).expect("tokio works"); - } -} - -#[test] -fn querying_for_pending_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); -} - -#[test] -fn querying_for_unknown_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected( PeerNotConnected { nodes: _, peer_id: node_peer_id }) => { - assert_eq!(node_peer_id, peer_id); - }); -} - -#[test] -fn querying_for_connected_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - - // Dial a node - let addr = "/ip4/127.0.0.1/tcp/1234".parse().expect("bad multiaddr"); - network.dial(addr, Handler::default()).expect("dialing works"); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we connect; extract the new PeerId. - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - // We're connected. - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected( PeerConnected { .. } )); -} - -#[test] -fn poll_with_closed_listener() { - let mut transport = DummyTransport::new(); - // Set up listener to be closed - transport.set_initial_listener_state(ListenerState::Ok(Async::Ready(None))); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::ListenerClosed { .. } )); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); -} - -#[test] -fn unknown_peer_that_is_unreachable_yields_unknown_peer_dial_error() { - let mut transport = DummyTransport::new(); - transport.make_dial_fail(); - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let handler = Handler::default(); - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::UnknownPeerDialError { .. } ); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn known_peer_that_is_unreachable_yields_dial_error() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - transport.make_dial_fail(); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); - } - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let peer_id = peer_id.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - let failed_peer_id = assert_matches!( - event, - NetworkEvent::DialError { new_state: _, peer_id: failed_peer_id, .. } => failed_peer_id - ); - assert_eq!(peer_id, failed_peer_id); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn yields_node_error_when_there_is_an_error_after_successful_connect() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - // Set up an outgoing connection with a PeerId we know - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - let addr = "/unix/reachable".parse().expect("bad multiaddr"); - let mut handler = Handler::default(); - // Force an error - handler.next_states = vec![ HandlerState::Err ]; - peer.into_not_connected().unwrap().connect(addr, handler); - } - - // Ensure we run on a single thread - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Drive it forward until we connect to the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - // Push the Handler into an error state on the next poll - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::Connected { .. }); - // We're connected, we can move on - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } - - // Poll again. It is going to be a NodeClosed because of how the - // handler's next state was set up. - let network_fut = network.clone(); - let expected_peer_id = peer_id.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::NodeClosed { conn_info, .. }) => { - assert_eq!(conn_info, expected_peer_id); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} #[test] fn local_prio_equivalence_relation() { @@ -387,59 +30,3 @@ fn local_prio_equivalence_relation() { assert_ne!(has_dial_prio(&a, &b), has_dial_prio(&b, &a)); } } - -#[test] -fn limit_incoming_connections() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let limit = 1; - - let mut events = vec![ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap())]; - events.extend(std::iter::repeat( - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ).take(10)); - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new_with_incoming_limit(transport, PeerId::random(), Some(limit)); - assert_eq!(network.incoming_limit(), Some(limit)); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - assert_eq!(network.incoming_negotiated().count(), 0); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - for i in 1..10 { - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - if i <= limit { - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), - Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - } else { - match network_fut.poll() { - Async::NotReady => (), - Async::Ready(x) => { - match x { - NetworkEvent::NewListenerAddress {..} => {} - NetworkEvent::ExpiredListenerAddress {..} => {} - NetworkEvent::IncomingConnection(_) => {} - NetworkEvent::Connected {..} => {} - e => panic!("Not expected event: {:?}", e) - } - }, - } - } - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - assert!(network.incoming_negotiated().count() <= (limit as usize)); - } -} diff --git a/core/src/nodes/node.rs b/core/src/nodes/node.rs index a1d0eac4..37da9954 100644 --- a/core/src/nodes/node.rs +++ b/core/src/nodes/node.rs @@ -21,9 +21,7 @@ use futures::prelude::*; use crate::muxing; use smallvec::SmallVec; -use std::fmt; -use std::io::Error as IoError; -use std::sync::Arc; +use std::{fmt, io::Error as IoError, pin::Pin, sync::Arc, task::Context, task::Poll}; // Implementation notes // ================= @@ -143,43 +141,44 @@ where } /// Provides an API similar to `Future`. - pub fn poll(&mut self) -> Poll, IoError> { + pub fn poll(&mut self, cx: &mut Context) -> Poll, IoError>> { // Polling inbound substream. - match self.muxer.poll_inbound().map_err(|e| e.into())? { - Async::Ready(substream) => { + match self.muxer.poll_inbound(cx) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); - return Ok(Async::Ready(NodeEvent::InboundSubstream { + return Poll::Ready(Ok(NodeEvent::InboundSubstream { substream, })); } - Async::NotReady => {} + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => {} } // Polling outbound substreams. // We remove each element from `outbound_substreams` one by one and add them back. for n in (0..self.outbound_substreams.len()).rev() { let (user_data, mut outbound) = self.outbound_substreams.swap_remove(n); - match self.muxer.poll_outbound(&mut outbound) { - Ok(Async::Ready(substream)) => { + match self.muxer.poll_outbound(cx, &mut outbound) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); self.muxer.destroy_outbound(outbound); - return Ok(Async::Ready(NodeEvent::OutboundSubstream { + return Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream, })); } - Ok(Async::NotReady) => { + Poll::Pending => { self.outbound_substreams.push((user_data, outbound)); } - Err(err) => { + Poll::Ready(Err(err)) => { self.muxer.destroy_outbound(outbound); - return Err(err.into()); + return Poll::Ready(Err(err.into())); } } } // Nothing happened. Register our task to be notified and return. - Ok(Async::NotReady) + Poll::Pending } } @@ -212,11 +211,14 @@ impl Future for Close where TMuxer: muxing::StreamMuxer, { - type Item = (); - type Error = IoError; + type Output = Result<(), IoError>; - fn poll(&mut self) -> Poll { - self.muxer.close().map_err(|e| e.into()) + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match self.muxer.close(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(err)) => Poll::Ready(Err(err.into())), + } } } @@ -252,70 +254,3 @@ where } } } - -#[cfg(test)] -mod node_stream { - use super::{NodeEvent, NodeStream}; - use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; - use assert_matches::assert_matches; - use futures::prelude::*; - use tokio_mock_task::MockTask; - - fn build_node_stream() -> NodeStream> { - let muxer = DummyMuxer::new(); - NodeStream::<_, Vec>::new(muxer) - } - - #[test] - fn closing_a_node_stream_destroys_substreams_and_returns_submitted_user_data() { - let mut ns = build_node_stream(); - ns.open_substream(vec![2]); - ns.open_substream(vec![3]); - ns.open_substream(vec![5]); - let user_data_submitted = ns.close(); - assert_eq!(user_data_submitted.1, vec![ - vec![2], vec![3], vec![5] - ]); - } - - #[test] - fn poll_returns_not_ready_when_there_is_nothing_to_do() { - let mut task = MockTask::new(); - task.enter(|| { - // ensure the address never resolves - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - - assert_matches!(ns.poll(), Ok(Async::NotReady)); - }); - } - - #[test] - fn poll_keeps_outbound_substreams_when_the_outgoing_connection_is_not_ready() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - ns.open_substream(vec![1]); - ns.poll().unwrap(); // poll past inbound - ns.poll().unwrap(); // poll outbound - assert!(format!("{:?}", ns).contains("outbound_substreams: 1")); - } - - #[test] - fn poll_returns_incoming_substream() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::Ready(subs) - muxer.set_inbound_connection_state(DummyConnectionState::Opened); - let mut ns = NodeStream::<_, Vec>::new(muxer); - assert_matches!(ns.poll(), Ok(Async::Ready(node_event)) => { - assert_matches!(node_event, NodeEvent::InboundSubstream{ substream: _ }); - }); - } -} diff --git a/core/src/nodes/tasks/manager.rs b/core/src/nodes/tasks/manager.rs index 33643aaa..aff72bd9 100644 --- a/core/src/nodes/tasks/manager.rs +++ b/core/src/nodes/tasks/manager.rs @@ -27,9 +27,8 @@ use crate::{ } }; use fnv::FnvHashMap; -use futures::{prelude::*, future::Executor, sync::mpsc}; -use smallvec::SmallVec; -use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt}; +use futures::{prelude::*, channel::mpsc, executor::ThreadPool, stream::FuturesUnordered, task::SpawnExt as _}; +use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt, pin::Pin, task::Context, task::Poll}; use super::{TaskId, task::{Task, FromTaskMessage, ToTaskMessage}, Error}; // Implementor notes @@ -64,12 +63,13 @@ pub struct Manager { /// Identifier for the next task to spawn. next_task_id: TaskId, - /// List of node tasks to spawn. - to_spawn: SmallVec<[Box + Send>; 8]>, + /// Threads pool where we spawn the nodes' tasks. If `None`, then we push tasks to the + /// `local_spawns` list instead. + threads_pool: Option, - /// If no tokio executor is available, we move tasks to this list, and futures are polled on - /// the current thread instead. - local_spawns: Vec + Send>>, + /// If no executor is available, we move tasks to this list, and futures are polled on the + /// current thread instead. + local_spawns: FuturesUnordered + Send>>>, /// Sender to emit events to the outside. Meant to be cloned and sent to tasks. events_tx: mpsc::Sender<(FromTaskMessage, TaskId)>, @@ -91,16 +91,13 @@ where /// Information about a running task. /// -/// Contains the sender to deliver event messages to the task, -/// the associated user data and a pending message if any, -/// meant to be delivered to the task via the sender. +/// Contains the sender to deliver event messages to the task, and +/// the associated user data. struct TaskInfo
; - type Error = ::Error; + type Output = Result, ::Error>; - fn poll(&mut self) -> Poll { - match self.inner.poll() { - Ok(Async::Ready(substream)) => { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match Future::poll(Pin::new(&mut self.inner), cx) { + Poll::Ready(Ok(substream)) => { let out = substream_from_ref(self.inner.muxer.clone(), substream); - Ok(Async::Ready(out)) + Poll::Ready(Ok(out)) } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(err) => Err(err), + Poll::Pending => Poll::Pending, + Poll::Ready(Err(err)) => Poll::Ready(Err(err)), } } } @@ -297,18 +288,26 @@ where outbound: Option<::OutboundSubstream>, } +impl Unpin for OutboundSubstreamRefFuture +where + P: Deref, + P::Target: StreamMuxer, +{ +} + impl Future for OutboundSubstreamRefFuture where P: Deref, P::Target: StreamMuxer, { - type Item = ::Substream; - type Error = ::Error; + type Output = Result<::Substream, ::Error>; #[inline] - fn poll(&mut self) -> Poll { - self.muxer - .poll_outbound(self.outbound.as_mut().expect("outbound was empty")) + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + this.muxer.poll_outbound(cx, this.outbound.as_mut().expect("outbound was empty")) } } @@ -370,20 +369,11 @@ where } } - -impl Read for SubstreamRef +impl Unpin for SubstreamRef where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.read_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } } impl AsyncRead for SubstreamRef @@ -391,37 +381,17 @@ where P: Deref, P::Target: StreamMuxer, { - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.muxer.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.muxer.initializer() } - fn poll_read(&mut self, buf: &mut [u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.read_substream(s, buf).map_err(|e| e.into()) - } -} + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; -impl Write for SubstreamRef -where - P: Deref, - P::Target: StreamMuxer, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.write_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } - - #[inline] - fn flush(&mut self) -> Result<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.flush_substream(s).map_err(|e| e.into())? { - Async::Ready(()) => Ok(()), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.read_substream(cx, s, buf).map_err(|e| e.into()) } } @@ -430,36 +400,51 @@ where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn poll_write(&mut self, buf: &[u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.write_substream(s, buf).map_err(|e| e.into()) + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.write_substream(cx, s, buf).map_err(|e| e.into()) } - #[inline] - fn shutdown(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); loop { - match self.shutdown_state { + match this.shutdown_state { ShutdownState::Shutdown => { - try_ready!(self.muxer.shutdown_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Flush; + match this.muxer.shutdown_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Flush, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Flush => { - try_ready!(self.muxer.flush_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Done; + match this.muxer.flush_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Done, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Done => { - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } } } } - #[inline] - fn poll_flush(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.flush_substream(s).map_err(|e| e.into()) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.flush_substream(cx, s).map_err(|e| e.into()) } } @@ -507,8 +492,8 @@ impl StreamMuxer for StreamMuxerBox { type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - self.inner.poll_inbound() + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + self.inner.poll_inbound(cx) } #[inline] @@ -517,8 +502,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn poll_outbound(&self, s: &mut Self::OutboundSubstream) -> Poll { - self.inner.poll_outbound(s) + fn poll_outbound(&self, cx: &mut Context, s: &mut Self::OutboundSubstream) -> Poll> { + self.inner.poll_outbound(cx, s) } #[inline] @@ -526,28 +511,28 @@ impl StreamMuxer for StreamMuxerBox { self.inner.destroy_outbound(substream) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { - self.inner.read_substream(s, buf) + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + self.inner.read_substream(cx, s, buf) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.write_substream(s, buf) + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { + self.inner.write_substream(cx, s, buf) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.flush_substream(s) + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.flush_substream(cx, s) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.shutdown_substream(s) + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.shutdown_substream(cx, s) } #[inline] @@ -556,8 +541,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close() + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx) } #[inline] @@ -566,8 +551,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all() + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx) } } @@ -588,11 +573,16 @@ where type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - let substream = try_ready!(self.inner.poll_inbound().map_err(|e| e.into())); + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + let substream = match self.inner.poll_inbound(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; + let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -606,13 +596,18 @@ where #[inline] fn poll_outbound( &self, + cx: &mut Context, substream: &mut Self::OutboundSubstream, - ) -> Poll { + ) -> Poll> { let mut list = self.outbound.lock(); - let substream = try_ready!(self.inner.poll_outbound(list.get_mut(substream).unwrap()).map_err(|e| e.into())); + let substream = match self.inner.poll_outbound(cx, list.get_mut(substream).unwrap()) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -621,32 +616,32 @@ where self.inner.destroy_outbound(list.remove(&substream).unwrap()) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.read_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.read_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.write_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.write_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.flush_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.flush_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.shutdown_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.shutdown_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] @@ -656,8 +651,8 @@ where } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close().map_err(|e| e.into()) + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx).map_err(|e| e.into()) } #[inline] @@ -666,7 +661,7 @@ where } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all().map_err(|e| e.into()) + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx).map_err(|e| e.into()) } } diff --git a/core/src/muxing/singleton.rs b/core/src/muxing/singleton.rs index 7bec14ed..f85e22fd 100644 --- a/core/src/muxing/singleton.rs +++ b/core/src/muxing/singleton.rs @@ -19,10 +19,9 @@ // DEALINGS IN THE SOFTWARE. use crate::{Endpoint, muxing::StreamMuxer}; -use futures::prelude::*; +use futures::{prelude::*, io::Initializer}; use parking_lot::Mutex; -use std::{io, sync::atomic::{AtomicBool, Ordering}}; -use tokio_io::{AsyncRead, AsyncWrite}; +use std::{io, pin::Pin, sync::atomic::{AtomicBool, Ordering}, task::Context, task::Poll}; /// Implementation of `StreamMuxer` that allows only one substream on top of a connection, /// yielding the connection itself. @@ -62,22 +61,22 @@ pub struct OutboundSubstream {} impl StreamMuxer for SingletonMuxer where - TSocket: AsyncRead + AsyncWrite, + TSocket: AsyncRead + AsyncWrite + Unpin, { type Substream = Substream; type OutboundSubstream = OutboundSubstream; type Error = io::Error; - fn poll_inbound(&self) -> Poll { + fn poll_inbound(&self, _: &mut Context) -> Poll> { match self.endpoint { - Endpoint::Dialer => return Ok(Async::NotReady), + Endpoint::Dialer => return Poll::Pending, Endpoint::Listener => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -85,44 +84,44 @@ where OutboundSubstream {} } - fn poll_outbound(&self, _: &mut Self::OutboundSubstream) -> Poll { + fn poll_outbound(&self, _: &mut Context, _: &mut Self::OutboundSubstream) -> Poll> { match self.endpoint { - Endpoint::Listener => return Ok(Async::NotReady), + Endpoint::Listener => return Poll::Pending, Endpoint::Dialer => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } fn destroy_outbound(&self, _: Self::OutboundSubstream) { } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.lock().prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.lock().initializer() } - fn read_substream(&self, _: &mut Self::Substream, buf: &mut [u8]) -> Poll { - let res = self.inner.lock().poll_read(buf); - if let Ok(Async::Ready(_)) = res { + fn read_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + let res = AsyncRead::poll_read(Pin::new(&mut *self.inner.lock()), cx, buf); + if let Poll::Ready(Ok(_)) = res { self.remote_acknowledged.store(true, Ordering::Release); } res } - fn write_substream(&self, _: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.lock().poll_write(buf) + fn write_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &[u8]) -> Poll> { + AsyncWrite::poll_write(Pin::new(&mut *self.inner.lock()), cx, buf) } - fn flush_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } - fn shutdown_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().shutdown() + fn shutdown_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_close(Pin::new(&mut *self.inner.lock()), cx) } fn destroy_substream(&self, _: Self::Substream) { @@ -132,12 +131,12 @@ where self.remote_acknowledged.load(Ordering::Acquire) } - fn close(&self) -> Poll<(), io::Error> { + fn close(&self, cx: &mut Context) -> Poll> { // The `StreamMuxer` trait requires that `close()` implies `flush_all()`. - self.flush_all() + self.flush_all(cx) } - fn flush_all(&self) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_all(&self, cx: &mut Context) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } } diff --git a/core/src/nodes/collection.rs b/core/src/nodes/collection.rs index af8601d2..9e212810 100644 --- a/core/src/nodes/collection.rs +++ b/core/src/nodes/collection.rs @@ -29,11 +29,7 @@ use crate::{ }; use fnv::FnvHashMap; use futures::prelude::*; -use std::{error, fmt, hash::Hash, mem}; - -pub use crate::nodes::tasks::StartTakeOver; - -mod tests; +use std::{error, fmt, hash::Hash, mem, task::Context, task::Poll}; /// Implementation of `Stream` that handles a collection of nodes. pub struct CollectionStream { @@ -58,6 +54,9 @@ where } } +impl Unpin for + CollectionStream { } + /// State of a task. #[derive(Debug, Clone, PartialEq, Eq)] enum TaskState { @@ -323,7 +322,7 @@ where pub fn add_reach_attempt(&mut self, future: TFut, handler: THandler) -> ReachAttemptId where - TFut: Future + Send + 'static, + TFut: Future> + Unpin + Send + 'static, THandler: IntoNodeHandler + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, @@ -358,17 +357,19 @@ where } /// Sends an event to all nodes. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.inner.start_broadcast(event) } + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.inner.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_broadcast(cx) } /// Adds an existing connection to a node to the collection. @@ -447,13 +448,13 @@ where /// > **Note**: we use a regular `poll` method instead of implementing `Stream` in order to /// > remove the `Err` variant, but also because we want the `CollectionStream` to stay /// > borrowed if necessary. - pub fn poll(&mut self) -> Async> + pub fn poll(&mut self, cx: &mut Context) -> Poll> where TConnInfo: Clone, // TODO: Clone shouldn't be necessary { - let item = match self.inner.poll() { - Async::Ready(item) => item, - Async::NotReady => return Async::NotReady, + let item = match self.inner.poll(cx) { + Poll::Ready(item) => item, + Poll::Pending => return Poll::Pending, }; match item { @@ -463,7 +464,7 @@ where match (user_data, result, handler) { (TaskState::Pending, tasks::Error::Reach(err), Some(handler)) => { - Async::Ready(CollectionEvent::ReachError { + Poll::Ready(CollectionEvent::ReachError { id: ReachAttemptId(id), error: err, handler, @@ -482,7 +483,7 @@ where debug_assert!(_handler.is_none()); let _node_task_id = self.nodes.remove(conn_info.peer_id()); debug_assert_eq!(_node_task_id, Some(id)); - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error: err, user_data, @@ -497,8 +498,8 @@ where tasks::Event::NodeReached { task, conn_info } => { let id = task.id(); drop(task); - Async::Ready(CollectionEvent::NodeReached(CollectionReachEvent { - parent: self, + Poll::Ready(CollectionEvent::NodeReached(CollectionReachEvent { + parent: &mut *self, id, conn_info: Some(conn_info), })) @@ -512,7 +513,7 @@ where self.tasks is switched to the Connected state; QED"), }; drop(task); - Async::Ready(CollectionEvent::NodeEvent { + Poll::Ready(CollectionEvent::NodeEvent { // TODO: normally we'd build a `PeerMut` manually here, but the borrow checker // doesn't like it peer: self.peer_mut(&conn_info.peer_id()) @@ -616,14 +617,15 @@ where } } - /// Sends an event to the given node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Begin sending an event to the given node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.inner.start_send_event(event) } - /// Complete sending an event message initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { - self.inner.complete_send_event() + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_event(cx) } /// Closes the connections to this node. Returns the user data. @@ -648,23 +650,13 @@ where /// The reach attempt will only be effectively cancelled once the peer (the object you're /// manipulating) has received some network activity. However no event will be ever be /// generated from this reach attempt, and this takes effect immediately. - #[must_use] - pub fn start_take_over(&mut self, id: InterruptedReachAttempt) - -> StartTakeOver<(), InterruptedReachAttempt> - { - match self.inner.start_take_over(id.inner) { - StartTakeOver::Ready(_state) => { - debug_assert!(if let TaskState::Pending = _state { true } else { false }); - StartTakeOver::Ready(()) - } - StartTakeOver::NotReady(inner) => - StartTakeOver::NotReady(InterruptedReachAttempt { inner }), - StartTakeOver::Gone => StartTakeOver::Gone - } + pub fn start_take_over(&mut self, id: InterruptedReachAttempt) { + self.inner.start_take_over(id.inner) } - /// Complete a take over initiated by `start_take_over`. - pub fn complete_take_over(&mut self) -> Poll<(), ()> { - self.inner.complete_take_over() + /// Make sure we are ready to taking over with `start_take_over`. + #[must_use] + pub fn poll_ready_take_over(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_take_over(cx) } } diff --git a/core/src/nodes/collection/tests.rs b/core/src/nodes/collection/tests.rs deleted file mode 100644 index 69f82c05..00000000 --- a/core/src/nodes/collection/tests.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use futures::future; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, InEvent, OutEvent, HandlerState}; -use tokio::runtime::current_thread::Runtime; -use tokio::runtime::Builder; -use crate::nodes::NodeHandlerEvent; -use std::{io, sync::Arc}; -use parking_lot::Mutex; - -type TestCollectionStream = CollectionStream; - -#[test] -fn has_connection_is_false_before_a_connection_has_been_made() { - let cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(!cs.has_connection(&peer_id)); -} - -#[test] -fn connections_is_empty_before_connecting() { - let cs = TestCollectionStream::new(); - assert!(cs.connections().next().is_none()); -} - -#[test] -fn retrieving_a_peer_is_none_if_peer_is_missing_or_not_connected() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(cs.peer_mut(&peer_id).is_none()); - - let handler = Handler::default(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, handler); - assert!(cs.peer_mut(&peer_id).is_none()); // task is pending -} - -#[test] -fn collection_stream_reaches_the_nodes() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((peer_id, muxer)); - cs.add_reach_attempt(fut, Handler::default()); - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - let event = cs.poll(); - match poll_count { - 1 => assert_matches!(event, Async::NotReady), - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(_))); - return Ok(Async::Ready(())); // stop - } - _ => unreachable!() - } - Ok(Async::NotReady) - }); - rt.block_on(fut).unwrap(); -} - -#[test] -fn accepting_a_node_yields_new_entry() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, Handler::default()); - - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - { - let event = cs.poll(); - match poll_count { - 1 => { - assert_matches!(event, Async::NotReady); - return Ok(Async::NotReady) - } - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - let (accept_ev, accepted_peer_id) = reach_ev.accept(()); - assert_eq!(accepted_peer_id, peer_id); - assert_matches!(accept_ev, CollectionNodeAccept::NewEntry); - }); - } - _ => unreachable!() - } - } - assert!(cs.peer_mut(&peer_id).is_some(), "peer is not in the list"); - assert!(cs.has_connection(&peer_id), "peer is not connected"); - assert_eq!(cs.connections().collect::>(), vec![&peer_id]); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("running the future works"); -} - -#[test] -fn events_in_a_node_reaches_the_collection_stream() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_peer_id = PeerId::random(); - - let mut handler = Handler::default(); - handler.state = Some(HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("init")))); - let handler_states = vec![ - HandlerState::Err, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 3") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 2") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )), - ]; - handler.next_states = handler_states; - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((task_peer_id.clone(), muxer)); - cs.lock().add_reach_attempt(fut, handler); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("init")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 2")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn task_closed_with_error_while_task_is_pending_yields_reach_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_inner_fut = future::err(std::io::Error::new(std::io::ErrorKind::Other, "inner fut error")); - let reach_attempt_id = cs.lock().add_reach_attempt(task_inner_fut, Handler::default()); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::ReachError {id, error, ..} => { - assert_eq!(id, reach_attempt_id); - assert_eq!(error.to_string(), "inner fut error"); - }); - - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - -} - -#[test] -fn task_closed_with_error_when_task_is_connected_yields_node_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Err]; // triggered when sending a NextState event - - cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(cs.complete_broadcast()) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id)); - - // Assert the node errored - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::NodeClosed{..}); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn interrupting_a_pending_connection_attempt_is_ok() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - let interrupt = cs.interrupt(reach_id); - assert!(interrupt.is_ok()); -} - -#[test] -fn interrupting_a_connection_attempt_twice_is_err() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - assert!(cs.interrupt(reach_id).is_ok()); - assert_matches!(cs.interrupt(reach_id), Err(InterruptError::ReachAttemptNotFound)) -} - -#[test] -fn interrupting_an_established_connection_is_err() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let handler = Handler::default(); - - let reach_id = cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(Async::Ready(())) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id), "Connection was not established"); - - assert_matches!(cs.lock().interrupt(reach_id), Err(InterruptError::AlreadyReached)); -} diff --git a/core/src/nodes/handled_node.rs b/core/src/nodes/handled_node.rs index 150b5e45..f8b08d11 100644 --- a/core/src/nodes/handled_node.rs +++ b/core/src/nodes/handled_node.rs @@ -20,10 +20,7 @@ use crate::{PeerId, muxing::StreamMuxer}; use crate::nodes::node::{NodeEvent, NodeStream, Substream, Close}; -use futures::prelude::*; -use std::{error, fmt, io}; - -mod tests; +use std::{error, fmt, io, pin::Pin, task::Context, task::Poll}; /// Handler for the substreams of a node. // TODO: right now it is possible for a node handler to be built, then shut down right after if we @@ -59,7 +56,8 @@ pub trait NodeHandler { /// Should behave like `Stream::poll()`. /// /// Returning an error will close the connection to the remote. - fn poll(&mut self) -> Poll, Self::Error>; + fn poll(&mut self, cx: &mut Context) + -> Poll, Self::Error>>; } /// Prototype for a `NodeHandler`. @@ -172,6 +170,13 @@ where } } +impl Unpin for HandledNode +where + TMuxer: StreamMuxer, + THandler: NodeHandler>, +{ +} + impl HandledNode where TMuxer: StreamMuxer, @@ -214,37 +219,41 @@ where } /// API similar to `Future::poll` that polls the node for events. - pub fn poll(&mut self) -> Poll> { + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) + -> Poll>> + { loop { let mut node_not_ready = false; - match self.node.poll().map_err(HandledNodeError::Node)? { - Async::NotReady => node_not_ready = true, - Async::Ready(NodeEvent::InboundSubstream { substream }) => { + match self.node.poll(cx) { + Poll::Pending => node_not_ready = true, + Poll::Ready(Ok(NodeEvent::InboundSubstream { substream })) => { self.handler.inject_substream(substream, NodeHandlerEndpoint::Listener) } - Async::Ready(NodeEvent::OutboundSubstream { user_data, substream }) => { + Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream })) => { let endpoint = NodeHandlerEndpoint::Dialer(user_data); self.handler.inject_substream(substream, endpoint) } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Node(err))), } - match self.handler.poll().map_err(HandledNodeError::Handler)? { - Async::NotReady => { + match self.handler.poll(cx) { + Poll::Pending => { if node_not_ready { break } } - Async::Ready(NodeHandlerEvent::OutboundSubstreamRequest(user_data)) => { + Poll::Ready(Ok(NodeHandlerEvent::OutboundSubstreamRequest(user_data))) => { self.node.open_substream(user_data); } - Async::Ready(NodeHandlerEvent::Custom(event)) => { - return Ok(Async::Ready(event)); + Poll::Ready(Ok(NodeHandlerEvent::Custom(event))) => { + return Poll::Ready(Ok(event)); } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Handler(err))), } } - Ok(Async::NotReady) + Poll::Pending } } diff --git a/core/src/nodes/handled_node/tests.rs b/core/src/nodes/handled_node/tests.rs deleted file mode 100644 index ee138c2e..00000000 --- a/core/src/nodes/handled_node/tests.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent, TestHandledNode}; - -struct TestBuilder { - muxer: DummyMuxer, - handler: Handler, - want_open_substream: bool, - substream_user_data: usize, -} - -impl TestBuilder { - fn new() -> Self { - TestBuilder { - muxer: DummyMuxer::new(), - handler: Handler::default(), - want_open_substream: false, - substream_user_data: 0, - } - } - - fn with_muxer_inbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_inbound_connection_state(state); - self - } - - fn with_muxer_outbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_outbound_connection_state(state); - self - } - - fn with_handler_state(&mut self, state: HandlerState) -> &mut Self { - self.handler.state = Some(state); - self - } - - fn with_open_substream(&mut self, user_data: usize) -> &mut Self { - self.want_open_substream = true; - self.substream_user_data = user_data; - self - } - - fn handled_node(&mut self) -> TestHandledNode { - let mut h = HandledNode::new(self.muxer.clone(), self.handler.clone()); - if self.want_open_substream { - h.node.open_substream(self.substream_user_data); - } - h - } -} - -// Set the state of the `Handler` after `inject_outbound_closed` is called -fn set_next_handler_outbound_state( handled_node: &mut TestHandledNode, next_state: HandlerState) { - handled_node.handler.next_outbound_state = Some(next_state); -} - -#[test] -fn can_inject_event() { - let mut handled = TestBuilder::new() - .handled_node(); - - let event = InEvent::Custom("banana"); - handled.inject_event(event.clone()); - assert_eq!(handled.handler().events, vec![event]); -} - -#[test] -fn poll_with_unready_node_stream_and_handler_emits_custom_event() { - let expected_event = NodeHandlerEvent::Custom(OutEvent::Custom("pineapple")); - let mut handled = TestBuilder::new() - // make NodeStream return NotReady - .with_muxer_inbound_state(DummyConnectionState::Pending) - // make Handler return return Ready(Some(…)) - .with_handler_state(HandlerState::Ready(expected_event)) - .handled_node(); - - assert_matches!(handled.poll(), Ok(Async::Ready(event)) => { - assert_matches!(event, OutEvent::Custom("pineapple")) - }); -} - -#[test] -fn handler_emits_outbound_closed_when_opening_new_substream_on_closed_node() { - let open_event = NodeHandlerEvent::OutboundSubstreamRequest(456); - let mut handled = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Ready(open_event)) - .handled_node(); - - set_next_handler_outbound_state( - &mut handled, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("pear"))) - ); - handled.poll().expect("poll works"); -} - -#[test] -fn poll_yields_inbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_open_substream(32) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Opened) - .with_open_substream(1) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(Some(1))]); -} - -#[test] -fn poll_yields_inbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Opened) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(None)]); -} diff --git a/core/src/nodes/listeners.rs b/core/src/nodes/listeners.rs index effcea65..b9c8ebbf 100644 --- a/core/src/nodes/listeners.rs +++ b/core/src/nodes/listeners.rs @@ -21,11 +21,10 @@ //! Manage listening on multiple multiaddresses at once. use crate::{Multiaddr, Transport, transport::{TransportError, ListenerEvent}}; -use futures::prelude::*; +use futures::{prelude::*, task::Context, task::Poll}; use log::debug; use smallvec::SmallVec; -use std::{collections::VecDeque, fmt}; -use void::Void; +use std::{collections::VecDeque, fmt, pin::Pin}; /// Implementation of `futures::Stream` that allows listening on multiaddresses. /// @@ -158,7 +157,7 @@ where /// The ID of the listener that errored. listener_id: ListenerId, /// The error value. - error: ::Error + error: ::Error } } @@ -222,28 +221,31 @@ where self.listeners.iter().flat_map(|l| l.addresses.iter()) } - /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> { + /// Provides an API similar to `Stream`, except that it cannot end. + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> + where + TTrans::Listener: Unpin, + { // We remove each element from `listeners` one by one and add them back. let mut remaining = self.listeners.len(); while let Some(mut listener) = self.listeners.pop_back() { - match listener.listener.poll() { - Ok(Async::NotReady) => { + match TryStream::try_poll_next(Pin::new(&mut listener.listener), cx) { + Poll::Pending => { self.listeners.push_front(listener); remaining -= 1; if remaining == 0 { break } } - Ok(Async::Ready(Some(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { + Poll::Ready(Some(Ok(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::Incoming { + return Poll::Ready(ListenersEvent::Incoming { listener_id: id, upgrade, local_addr, send_back_addr: remote_addr }) } - Ok(Async::Ready(Some(ListenerEvent::NewAddress(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::NewAddress(a)))) => { if listener.addresses.contains(&a) { debug!("Transport has reported address {} multiple times", a) } @@ -252,28 +254,28 @@ where } let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::NewAddress { + return Poll::Ready(ListenersEvent::NewAddress { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(Some(ListenerEvent::AddressExpired(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::AddressExpired(a)))) => { listener.addresses.retain(|x| x != &a); let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::AddressExpired { + return Poll::Ready(ListenersEvent::AddressExpired { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(None)) => { - return Async::Ready(ListenersEvent::Closed { + Poll::Ready(None) => { + return Poll::Ready(ListenersEvent::Closed { listener_id: listener.id, listener: listener.listener }) } - Err(err) => { - return Async::Ready(ListenersEvent::Error { + Poll::Ready(Some(Err(err))) => { + return Poll::Ready(ListenersEvent::Error { listener_id: listener.id, error: err }) @@ -282,22 +284,28 @@ where } // We register the current task to be woken up if a new listener is added. - Async::NotReady + Poll::Pending } } impl Stream for ListenersStream where TTrans: Transport, + TTrans::Listener: Unpin, { type Item = ListenersEvent; - type Error = Void; // TODO: use ! once stable - fn poll(&mut self) -> Poll, Self::Error> { - Ok(self.poll().map(Option::Some)) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ListenersStream::poll(self, cx).map(Option::Some) } } +impl Unpin for ListenersStream +where + TTrans: Transport, +{ +} + impl fmt::Debug for ListenersStream where TTrans: Transport + fmt::Debug, @@ -313,7 +321,7 @@ where impl fmt::Debug for ListenersEvent where TTrans: Transport, - ::Error: fmt::Debug, + ::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { @@ -353,215 +361,37 @@ mod tests { use tokio::runtime::current_thread::Runtime; use std::{io, iter::FromIterator}; use futures::{future::{self}, stream}; - use crate::tests::dummy_transport::{DummyTransport, ListenerState}; - use crate::tests::dummy_muxer::DummyMuxer; use crate::PeerId; - fn set_listener_state(ls: &mut ListenersStream, idx: usize, state: ListenerState) { - ls.listeners[idx].listener = match state { - ListenerState::Error => - Box::new(stream::poll_fn(|| Err(io::Error::new(io::ErrorKind::Other, "oh noes")))), - ListenerState::Ok(state) => match state { - Async::NotReady => Box::new(stream::poll_fn(|| Ok(Async::NotReady))), - Async::Ready(Some(event)) => Box::new(stream::poll_fn(move || { - Ok(Async::Ready(Some(event.clone().map(future::ok)))) - })), - Async::Ready(None) => Box::new(stream::empty()) - } - ListenerState::Events(events) => - Box::new(stream::iter_ok(events.into_iter().map(|e| e.map(future::ok)))) - }; - } - #[test] fn incoming_event() { - let mem_transport = transport::MemoryTransport::default(); + futures::executor::block_on(async move { + let mem_transport = transport::MemoryTransport::default(); - let mut listeners = ListenersStream::new(mem_transport); - listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); + let mut listeners = ListenersStream::new(mem_transport); + listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); - let address = { - let event = listeners.by_ref().wait().next().expect("some event").expect("no error"); - if let ListenersEvent::NewAddress { listen_addr, .. } = event { - listen_addr - } else { - panic!("Was expecting the listen address to be reported") - } - }; - - let dial = mem_transport.dial(address.clone()).unwrap(); - - let future = listeners - .into_future() - .map_err(|(err, _)| err) - .and_then(|(event, _)| { - match event { - Some(ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. }) => { - assert_eq!(local_addr, address); - assert_eq!(send_back_addr, address); - upgrade.map(|_| ()).map_err(|_| panic!()) - }, - _ => panic!() + let address = { + let event = listeners.next().await.unwrap(); + if let ListenersEvent::NewAddress { listen_addr, .. } = event { + listen_addr + } else { + panic!("Was expecting the listen address to be reported") } - }) - .select(dial.map(|_| ()).map_err(|_| panic!())) - .map_err(|(err, _)| err); + }; - let mut runtime = Runtime::new().unwrap(); - runtime.block_on(future).unwrap(); - } + let address2 = address.clone(); + async_std::task::spawn(async move { + mem_transport.dial(address2).unwrap().await.unwrap(); + }); - #[test] - fn listener_stream_returns_transport() { - let t = DummyTransport::new(); - let t_clone = t.clone(); - let ls = ListenersStream::new(t); - assert_eq!(ls.transport(), &t_clone); - } - - #[test] - fn listener_stream_can_iterate_over_listeners() { - let mut t = DummyTransport::new(); - let addr1 = tcp4([127, 0, 0, 1], 1234); - let addr2 = tcp4([127, 0, 0, 1], 4321); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(addr1.clone()), - ListenerEvent::NewAddress(addr2.clone()) - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([0, 0, 0, 0], 0)).expect("listen_on"); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr1, listen_addr) - }); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr2, listen_addr) - }) - } - - #[test] - fn listener_stream_poll_without_listeners_is_not_ready() { - let t = DummyTransport::new(); - let mut ls = ListenersStream::new(t); - assert_matches!(ls.poll(), Async::NotReady); - } - - #[test] - fn listener_stream_poll_with_listeners_that_arent_ready_is_not_ready() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::NotReady)); - assert_matches!(ls.poll(), Async::NotReady); - assert_eq!(ls.listeners.len(), 1); // listener is still there - } - - #[test] - fn listener_stream_poll_with_ready_listeners_is_ready() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let expected_output = (peer_id.clone(), muxer.clone()); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(tcp4([127, 0, 0, 1], 9090)), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) + match listeners.next().await.unwrap() { + ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. } => { + assert_eq!(local_addr, address); + assert_eq!(send_back_addr, address); + }, + _ => panic!() } - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([127, 0, 0, 1], 1234)).expect("listen_on"); - ls.listen_on(tcp4([127, 0, 0, 1], 4321)).expect("listen_on"); - assert_eq!(ls.listeners.len(), 2); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - set_listener_state(&mut ls, 1, ListenerState::Ok(Async::NotReady)); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - } - - #[test] - fn listener_stream_poll_with_closed_listener_emits_closed_event() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::Ready(None))); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Closed{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - #[test] - fn listener_stream_poll_with_erroring_listener_emits_error_event() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let event = ListenerEvent::Upgrade { - upgrade: (peer_id, muxer), - local_addr: tcp4([127, 0, 0, 1], 1234), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }; - t.set_initial_listener_state(ListenerState::Ok(Async::Ready(Some(event)))); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Error); // simulate an error on the socket - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Error{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - fn tcp4(ip: [u8; 4], port: u16) -> Multiaddr { - let protos = std::iter::once(multiaddr::Protocol::Ip4(ip.into())) - .chain(std::iter::once(multiaddr::Protocol::Tcp(port))); - Multiaddr::from_iter(protos) } } diff --git a/core/src/nodes/network.rs b/core/src/nodes/network.rs index abe9e631..2f6634a1 100644 --- a/core/src/nodes/network.rs +++ b/core/src/nodes/network.rs @@ -49,10 +49,10 @@ use std::{ fmt, hash::Hash, num::NonZeroUsize, + pin::Pin, + task::{Context, Poll}, }; -pub use crate::nodes::collection::StartTakeOver; - mod tests; /// Implementation of `Stream` that handles the nodes. @@ -81,7 +81,7 @@ where /// If the pair's second element is `AsyncSink::Ready`, the take over /// message has been sent and needs to be flushed using /// `PeerMut::complete_take_over`. - take_over_to_complete: Option<(TPeerId, AsyncSink>)> + take_over_to_complete: Option<(TPeerId, InterruptedReachAttempt)> } impl fmt::Debug for @@ -102,6 +102,13 @@ where } } +impl Unpin for + Network +where + TTrans: Transport +{ +} + impl ConnectionInfo for (TConnInfo, ConnectedPoint) where TConnInfo: ConnectionInfo @@ -173,7 +180,7 @@ where /// The listener that errored. listener_id: ListenerId, /// The listener error. - error: ::Error + error: ::Error }, /// One of the listeners is now listening on an additional address. @@ -573,7 +580,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::ListenerUpgrade: Send + 'static, + TTrans::ListenerUpgrade: Unpin + Send + 'static, THandler: IntoNodeHandler<(TConnInfo, ConnectedPoint)> + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, // TODO: shouldn't be necessary @@ -609,9 +616,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -781,7 +788,7 @@ where where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TInEvent: Send + 'static, @@ -797,9 +804,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -840,19 +847,18 @@ where /// Start sending an event to all nodes. /// - /// Make sure to complete the broadcast with `complete_broadcast`. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.active_nodes.start_broadcast(event) } - /// Complete a broadcast initiated with `start_broadcast`. + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.active_nodes.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.active_nodes.poll_ready_broadcast(cx) } /// Returns a list of all the peers we are currently connected to. @@ -934,7 +940,7 @@ where fn start_dial_out(&mut self, peer_id: TPeerId, handler: THandler, first: Multiaddr, rest: Vec) where TTrans: Transport, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TTrans::Error: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -950,9 +956,9 @@ where .map_err(|err| InternalReachErr::Transport(TransportError::Other(err))) .and_then(move |(actual_conn_info, muxer)| { if *actual_conn_info.peer_id() == expected_peer_id { - Ok(((actual_conn_info, connected_point), muxer)) + future::ready(Ok(((actual_conn_info, connected_point), muxer))) } else { - Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info }) + future::ready(Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info })) } }); self.active_nodes.add_reach_attempt(fut, handler) @@ -976,11 +982,12 @@ where } /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> + pub fn poll<'a>(&'a mut self, cx: &mut Context) -> Poll> where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, + TTrans::Listener: Unpin, TTrans::ListenerUpgrade: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -998,9 +1005,9 @@ where Some(x) if self.incoming_negotiated().count() >= (x as usize) => (), _ => { - match self.listeners.poll() { - Async::NotReady => (), - Async::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { + match ListenersStream::poll(Pin::new(&mut self.listeners), cx) { + Poll::Pending => (), + Poll::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { let event = IncomingConnectionEvent { listener_id, upgrade, @@ -1010,19 +1017,19 @@ where active_nodes: &mut self.active_nodes, other_reach_attempts: &mut self.reach_attempts.other_reach_attempts, }; - return Async::Ready(NetworkEvent::IncomingConnection(event)); + return Poll::Ready(NetworkEvent::IncomingConnection(event)); } - Async::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::Closed { listener_id, listener }) => { - return Async::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) + Poll::Ready(ListenersEvent::Closed { listener_id, listener }) => { + return Poll::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) } - Async::Ready(ListenersEvent::Error { listener_id, error }) => { - return Async::Ready(NetworkEvent::ListenerError { listener_id, error }) + Poll::Ready(ListenersEvent::Error { listener_id, error }) => { + return Poll::Ready(NetworkEvent::ListenerError { listener_id, error }) } } } @@ -1031,36 +1038,30 @@ where // Attempt to deliver any pending take over messages. if let Some((id, interrupted)) = self.take_over_to_complete.take() { if let Some(mut peer) = self.active_nodes.peer_mut(&id) { - if let AsyncSink::NotReady(i) = interrupted { - if let StartTakeOver::NotReady(i) = peer.start_take_over(i) { - self.take_over_to_complete = Some((id, AsyncSink::NotReady(i))) - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) - } - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((id, interrupted)); + return Poll::Pending; } } } - if self.take_over_to_complete.is_some() { - return Async::NotReady - } // Poll the existing nodes. let (action, out_event); - match self.active_nodes.poll() { - Async::NotReady => return Async::NotReady, - Async::Ready(CollectionEvent::NodeReached(reach_event)) => { + match self.active_nodes.poll(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(CollectionEvent::NodeReached(reach_event)) => { let (a, e) = handle_node_reached(&mut self.reach_attempts, reach_event); action = a; out_event = e; } - Async::Ready(CollectionEvent::ReachError { id, error, handler }) => { + Poll::Ready(CollectionEvent::ReachError { id, error, handler }) => { let (a, e) = handle_reach_error(&mut self.reach_attempts, id, error, handler); action = a; out_event = e; } - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error, .. @@ -1078,7 +1079,7 @@ where error, }; } - Async::Ready(CollectionEvent::NodeEvent { peer, event }) => { + Poll::Ready(CollectionEvent::NodeEvent { peer, event }) => { action = Default::default(); out_event = NetworkEvent::NodeEvent { conn_info: peer.info().0.clone(), event }; } @@ -1099,17 +1100,15 @@ where out_reach_attempts should always be in sync with the actual \ attempts; QED"); let mut peer = self.active_nodes.peer_mut(&peer_id).unwrap(); - if let StartTakeOver::NotReady(i) = peer.start_take_over(interrupted) { - self.take_over_to_complete = Some((peer_id, AsyncSink::NotReady(i))); - return Async::NotReady - } - if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((peer_id, AsyncSink::Ready)); - return Async::NotReady + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((peer_id, interrupted)); + return Poll::Pending } } - Async::Ready(out_event) + Poll::Ready(out_event) } } @@ -1467,7 +1466,7 @@ impl<'a, TTrans, TMuxer, TInEvent, TOutEvent, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, @@ -1644,18 +1643,33 @@ where closed messages; QED") } - /// Start sending an event to the node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Sends an event to the handler of the node. + pub fn send_event<'s: 'a>(&'s mut self, event: TInEvent) -> impl Future + 's + 'a { + let mut event = Some(event); + futures::future::poll_fn(move |cx| { + match self.poll_ready_event(cx) { + Poll::Ready(()) => { + self.start_send_event(event.take().expect("Future called after finished")); + Poll::Ready(()) + }, + Poll::Pending => Poll::Pending, + } + }) + } + + /// Begin sending an event to the node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") .start_send_event(event) } - /// Complete sending an event message, initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") - .complete_send_event() + .poll_ready_event(cx) } } @@ -1749,7 +1763,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, diff --git a/core/src/nodes/network/tests.rs b/core/src/nodes/network/tests.rs index c64666aa..c4f307bb 100644 --- a/core/src/nodes/network/tests.rs +++ b/core/src/nodes/network/tests.rs @@ -21,363 +21,6 @@ #![cfg(test)] use super::*; -use crate::tests::dummy_transport::DummyTransport; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent}; -use crate::tests::dummy_transport::ListenerState; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::nodes::NodeHandlerEvent; -use crate::transport::ListenerEvent; -use assert_matches::assert_matches; -use parking_lot::Mutex; -use std::sync::Arc; -use tokio::runtime::{Builder, Runtime}; - -#[test] -fn query_transport() { - let transport = DummyTransport::new(); - let transport2 = transport.clone(); - let network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - assert_eq!(network.transport(), &transport2); -} - -#[test] -fn local_node_peer() { - let peer_id = PeerId::random(); - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), peer_id.clone()); - assert_matches!(network.peer(peer_id), Peer::LocalNode); -} - -#[test] -fn successful_dial_reaches_a_node() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let dial_res = network.dial(addr, Handler::default()); - assert!(dial_res.is_ok()); - - // Poll the network until we get a `NodeReached` then assert on the peer: - // it's there and it's connected. - let network = Arc::new(Mutex::new(network)); - - let mut rt = Runtime::new().unwrap(); - let mut peer_id : Option = None; - // Drive forward until we're Connected - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected(PeerConnected{..})); -} - -#[test] -fn num_incoming_negotiated() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - - let events = vec![ - ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap()), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ]; - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - // no incoming yet - assert_eq!(network.incoming_negotiated().count(), 0); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - // Now there's an incoming connection - assert_eq!(network.incoming_negotiated().count(), 1); -} - -#[test] -fn broadcasted_events_reach_active_nodes() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )),]; - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::NodeEvent { conn_info: _, event: inner_event } => { - // The event we sent reached the node and triggered sending the out event we told it to return - assert_matches!(inner_event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(false)) - }, - _ => Ok(Async::Ready(true)) - } - })).expect("tokio works"); - } -} - -#[test] -fn querying_for_pending_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); -} - -#[test] -fn querying_for_unknown_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected( PeerNotConnected { nodes: _, peer_id: node_peer_id }) => { - assert_eq!(node_peer_id, peer_id); - }); -} - -#[test] -fn querying_for_connected_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - - // Dial a node - let addr = "/ip4/127.0.0.1/tcp/1234".parse().expect("bad multiaddr"); - network.dial(addr, Handler::default()).expect("dialing works"); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we connect; extract the new PeerId. - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - // We're connected. - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected( PeerConnected { .. } )); -} - -#[test] -fn poll_with_closed_listener() { - let mut transport = DummyTransport::new(); - // Set up listener to be closed - transport.set_initial_listener_state(ListenerState::Ok(Async::Ready(None))); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::ListenerClosed { .. } )); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); -} - -#[test] -fn unknown_peer_that_is_unreachable_yields_unknown_peer_dial_error() { - let mut transport = DummyTransport::new(); - transport.make_dial_fail(); - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let handler = Handler::default(); - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::UnknownPeerDialError { .. } ); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn known_peer_that_is_unreachable_yields_dial_error() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - transport.make_dial_fail(); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); - } - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let peer_id = peer_id.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - let failed_peer_id = assert_matches!( - event, - NetworkEvent::DialError { new_state: _, peer_id: failed_peer_id, .. } => failed_peer_id - ); - assert_eq!(peer_id, failed_peer_id); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn yields_node_error_when_there_is_an_error_after_successful_connect() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - // Set up an outgoing connection with a PeerId we know - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - let addr = "/unix/reachable".parse().expect("bad multiaddr"); - let mut handler = Handler::default(); - // Force an error - handler.next_states = vec![ HandlerState::Err ]; - peer.into_not_connected().unwrap().connect(addr, handler); - } - - // Ensure we run on a single thread - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Drive it forward until we connect to the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - // Push the Handler into an error state on the next poll - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::Connected { .. }); - // We're connected, we can move on - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } - - // Poll again. It is going to be a NodeClosed because of how the - // handler's next state was set up. - let network_fut = network.clone(); - let expected_peer_id = peer_id.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::NodeClosed { conn_info, .. }) => { - assert_eq!(conn_info, expected_peer_id); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} #[test] fn local_prio_equivalence_relation() { @@ -387,59 +30,3 @@ fn local_prio_equivalence_relation() { assert_ne!(has_dial_prio(&a, &b), has_dial_prio(&b, &a)); } } - -#[test] -fn limit_incoming_connections() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let limit = 1; - - let mut events = vec![ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap())]; - events.extend(std::iter::repeat( - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ).take(10)); - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new_with_incoming_limit(transport, PeerId::random(), Some(limit)); - assert_eq!(network.incoming_limit(), Some(limit)); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - assert_eq!(network.incoming_negotiated().count(), 0); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - for i in 1..10 { - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - if i <= limit { - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), - Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - } else { - match network_fut.poll() { - Async::NotReady => (), - Async::Ready(x) => { - match x { - NetworkEvent::NewListenerAddress {..} => {} - NetworkEvent::ExpiredListenerAddress {..} => {} - NetworkEvent::IncomingConnection(_) => {} - NetworkEvent::Connected {..} => {} - e => panic!("Not expected event: {:?}", e) - } - }, - } - } - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - assert!(network.incoming_negotiated().count() <= (limit as usize)); - } -} diff --git a/core/src/nodes/node.rs b/core/src/nodes/node.rs index a1d0eac4..37da9954 100644 --- a/core/src/nodes/node.rs +++ b/core/src/nodes/node.rs @@ -21,9 +21,7 @@ use futures::prelude::*; use crate::muxing; use smallvec::SmallVec; -use std::fmt; -use std::io::Error as IoError; -use std::sync::Arc; +use std::{fmt, io::Error as IoError, pin::Pin, sync::Arc, task::Context, task::Poll}; // Implementation notes // ================= @@ -143,43 +141,44 @@ where } /// Provides an API similar to `Future`. - pub fn poll(&mut self) -> Poll, IoError> { + pub fn poll(&mut self, cx: &mut Context) -> Poll, IoError>> { // Polling inbound substream. - match self.muxer.poll_inbound().map_err(|e| e.into())? { - Async::Ready(substream) => { + match self.muxer.poll_inbound(cx) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); - return Ok(Async::Ready(NodeEvent::InboundSubstream { + return Poll::Ready(Ok(NodeEvent::InboundSubstream { substream, })); } - Async::NotReady => {} + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => {} } // Polling outbound substreams. // We remove each element from `outbound_substreams` one by one and add them back. for n in (0..self.outbound_substreams.len()).rev() { let (user_data, mut outbound) = self.outbound_substreams.swap_remove(n); - match self.muxer.poll_outbound(&mut outbound) { - Ok(Async::Ready(substream)) => { + match self.muxer.poll_outbound(cx, &mut outbound) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); self.muxer.destroy_outbound(outbound); - return Ok(Async::Ready(NodeEvent::OutboundSubstream { + return Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream, })); } - Ok(Async::NotReady) => { + Poll::Pending => { self.outbound_substreams.push((user_data, outbound)); } - Err(err) => { + Poll::Ready(Err(err)) => { self.muxer.destroy_outbound(outbound); - return Err(err.into()); + return Poll::Ready(Err(err.into())); } } } // Nothing happened. Register our task to be notified and return. - Ok(Async::NotReady) + Poll::Pending } } @@ -212,11 +211,14 @@ impl Future for Close where TMuxer: muxing::StreamMuxer, { - type Item = (); - type Error = IoError; + type Output = Result<(), IoError>; - fn poll(&mut self) -> Poll { - self.muxer.close().map_err(|e| e.into()) + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match self.muxer.close(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(err)) => Poll::Ready(Err(err.into())), + } } } @@ -252,70 +254,3 @@ where } } } - -#[cfg(test)] -mod node_stream { - use super::{NodeEvent, NodeStream}; - use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; - use assert_matches::assert_matches; - use futures::prelude::*; - use tokio_mock_task::MockTask; - - fn build_node_stream() -> NodeStream> { - let muxer = DummyMuxer::new(); - NodeStream::<_, Vec>::new(muxer) - } - - #[test] - fn closing_a_node_stream_destroys_substreams_and_returns_submitted_user_data() { - let mut ns = build_node_stream(); - ns.open_substream(vec![2]); - ns.open_substream(vec![3]); - ns.open_substream(vec![5]); - let user_data_submitted = ns.close(); - assert_eq!(user_data_submitted.1, vec![ - vec![2], vec![3], vec![5] - ]); - } - - #[test] - fn poll_returns_not_ready_when_there_is_nothing_to_do() { - let mut task = MockTask::new(); - task.enter(|| { - // ensure the address never resolves - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - - assert_matches!(ns.poll(), Ok(Async::NotReady)); - }); - } - - #[test] - fn poll_keeps_outbound_substreams_when_the_outgoing_connection_is_not_ready() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - ns.open_substream(vec![1]); - ns.poll().unwrap(); // poll past inbound - ns.poll().unwrap(); // poll outbound - assert!(format!("{:?}", ns).contains("outbound_substreams: 1")); - } - - #[test] - fn poll_returns_incoming_substream() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::Ready(subs) - muxer.set_inbound_connection_state(DummyConnectionState::Opened); - let mut ns = NodeStream::<_, Vec>::new(muxer); - assert_matches!(ns.poll(), Ok(Async::Ready(node_event)) => { - assert_matches!(node_event, NodeEvent::InboundSubstream{ substream: _ }); - }); - } -} diff --git a/core/src/nodes/tasks/manager.rs b/core/src/nodes/tasks/manager.rs index 33643aaa..aff72bd9 100644 --- a/core/src/nodes/tasks/manager.rs +++ b/core/src/nodes/tasks/manager.rs @@ -27,9 +27,8 @@ use crate::{ } }; use fnv::FnvHashMap; -use futures::{prelude::*, future::Executor, sync::mpsc}; -use smallvec::SmallVec; -use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt}; +use futures::{prelude::*, channel::mpsc, executor::ThreadPool, stream::FuturesUnordered, task::SpawnExt as _}; +use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt, pin::Pin, task::Context, task::Poll}; use super::{TaskId, task::{Task, FromTaskMessage, ToTaskMessage}, Error}; // Implementor notes @@ -64,12 +63,13 @@ pub struct Manager { /// Identifier for the next task to spawn. next_task_id: TaskId, - /// List of node tasks to spawn. - to_spawn: SmallVec<[Box + Send>; 8]>, + /// Threads pool where we spawn the nodes' tasks. If `None`, then we push tasks to the + /// `local_spawns` list instead. + threads_pool: Option, - /// If no tokio executor is available, we move tasks to this list, and futures are polled on - /// the current thread instead. - local_spawns: Vec + Send>>, + /// If no executor is available, we move tasks to this list, and futures are polled on the + /// current thread instead. + local_spawns: FuturesUnordered + Send>>>, /// Sender to emit events to the outside. Meant to be cloned and sent to tasks. events_tx: mpsc::Sender<(FromTaskMessage, TaskId)>, @@ -91,16 +91,13 @@ where /// Information about a running task. /// -/// Contains the sender to deliver event messages to the task, -/// the associated user data and a pending message if any, -/// meant to be delivered to the task via the sender. +/// Contains the sender to deliver event messages to the task, and +/// the associated user data. struct TaskInfo
Unpin for OutboundSubstreamRefFuture
+where + P: Deref, + P::Target: StreamMuxer, +{ +} + impl
Future for OutboundSubstreamRefFuture
where P: Deref, P::Target: StreamMuxer, { - type Item = ::Substream; - type Error = ::Error; + type Output = Result<::Substream, ::Error>; #[inline] - fn poll(&mut self) -> Poll { - self.muxer - .poll_outbound(self.outbound.as_mut().expect("outbound was empty")) + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + this.muxer.poll_outbound(cx, this.outbound.as_mut().expect("outbound was empty")) } } @@ -370,20 +369,11 @@ where } } - -impl Read for SubstreamRef +impl Unpin for SubstreamRef where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.read_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } } impl AsyncRead for SubstreamRef @@ -391,37 +381,17 @@ where P: Deref, P::Target: StreamMuxer, { - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.muxer.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.muxer.initializer() } - fn poll_read(&mut self, buf: &mut [u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.read_substream(s, buf).map_err(|e| e.into()) - } -} + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; -impl Write for SubstreamRef -where - P: Deref, - P::Target: StreamMuxer, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.write_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } - - #[inline] - fn flush(&mut self) -> Result<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.flush_substream(s).map_err(|e| e.into())? { - Async::Ready(()) => Ok(()), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.read_substream(cx, s, buf).map_err(|e| e.into()) } } @@ -430,36 +400,51 @@ where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn poll_write(&mut self, buf: &[u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.write_substream(s, buf).map_err(|e| e.into()) + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.write_substream(cx, s, buf).map_err(|e| e.into()) } - #[inline] - fn shutdown(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); loop { - match self.shutdown_state { + match this.shutdown_state { ShutdownState::Shutdown => { - try_ready!(self.muxer.shutdown_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Flush; + match this.muxer.shutdown_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Flush, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Flush => { - try_ready!(self.muxer.flush_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Done; + match this.muxer.flush_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Done, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Done => { - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } } } } - #[inline] - fn poll_flush(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.flush_substream(s).map_err(|e| e.into()) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.flush_substream(cx, s).map_err(|e| e.into()) } } @@ -507,8 +492,8 @@ impl StreamMuxer for StreamMuxerBox { type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - self.inner.poll_inbound() + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + self.inner.poll_inbound(cx) } #[inline] @@ -517,8 +502,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn poll_outbound(&self, s: &mut Self::OutboundSubstream) -> Poll { - self.inner.poll_outbound(s) + fn poll_outbound(&self, cx: &mut Context, s: &mut Self::OutboundSubstream) -> Poll> { + self.inner.poll_outbound(cx, s) } #[inline] @@ -526,28 +511,28 @@ impl StreamMuxer for StreamMuxerBox { self.inner.destroy_outbound(substream) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { - self.inner.read_substream(s, buf) + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + self.inner.read_substream(cx, s, buf) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.write_substream(s, buf) + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { + self.inner.write_substream(cx, s, buf) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.flush_substream(s) + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.flush_substream(cx, s) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.shutdown_substream(s) + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.shutdown_substream(cx, s) } #[inline] @@ -556,8 +541,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close() + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx) } #[inline] @@ -566,8 +551,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all() + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx) } } @@ -588,11 +573,16 @@ where type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - let substream = try_ready!(self.inner.poll_inbound().map_err(|e| e.into())); + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + let substream = match self.inner.poll_inbound(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; + let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -606,13 +596,18 @@ where #[inline] fn poll_outbound( &self, + cx: &mut Context, substream: &mut Self::OutboundSubstream, - ) -> Poll { + ) -> Poll> { let mut list = self.outbound.lock(); - let substream = try_ready!(self.inner.poll_outbound(list.get_mut(substream).unwrap()).map_err(|e| e.into())); + let substream = match self.inner.poll_outbound(cx, list.get_mut(substream).unwrap()) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -621,32 +616,32 @@ where self.inner.destroy_outbound(list.remove(&substream).unwrap()) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.read_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.read_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.write_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.write_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.flush_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.flush_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.shutdown_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.shutdown_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] @@ -656,8 +651,8 @@ where } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close().map_err(|e| e.into()) + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx).map_err(|e| e.into()) } #[inline] @@ -666,7 +661,7 @@ where } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all().map_err(|e| e.into()) + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx).map_err(|e| e.into()) } } diff --git a/core/src/muxing/singleton.rs b/core/src/muxing/singleton.rs index 7bec14ed..f85e22fd 100644 --- a/core/src/muxing/singleton.rs +++ b/core/src/muxing/singleton.rs @@ -19,10 +19,9 @@ // DEALINGS IN THE SOFTWARE. use crate::{Endpoint, muxing::StreamMuxer}; -use futures::prelude::*; +use futures::{prelude::*, io::Initializer}; use parking_lot::Mutex; -use std::{io, sync::atomic::{AtomicBool, Ordering}}; -use tokio_io::{AsyncRead, AsyncWrite}; +use std::{io, pin::Pin, sync::atomic::{AtomicBool, Ordering}, task::Context, task::Poll}; /// Implementation of `StreamMuxer` that allows only one substream on top of a connection, /// yielding the connection itself. @@ -62,22 +61,22 @@ pub struct OutboundSubstream {} impl StreamMuxer for SingletonMuxer where - TSocket: AsyncRead + AsyncWrite, + TSocket: AsyncRead + AsyncWrite + Unpin, { type Substream = Substream; type OutboundSubstream = OutboundSubstream; type Error = io::Error; - fn poll_inbound(&self) -> Poll { + fn poll_inbound(&self, _: &mut Context) -> Poll> { match self.endpoint { - Endpoint::Dialer => return Ok(Async::NotReady), + Endpoint::Dialer => return Poll::Pending, Endpoint::Listener => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -85,44 +84,44 @@ where OutboundSubstream {} } - fn poll_outbound(&self, _: &mut Self::OutboundSubstream) -> Poll { + fn poll_outbound(&self, _: &mut Context, _: &mut Self::OutboundSubstream) -> Poll> { match self.endpoint { - Endpoint::Listener => return Ok(Async::NotReady), + Endpoint::Listener => return Poll::Pending, Endpoint::Dialer => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } fn destroy_outbound(&self, _: Self::OutboundSubstream) { } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.lock().prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.lock().initializer() } - fn read_substream(&self, _: &mut Self::Substream, buf: &mut [u8]) -> Poll { - let res = self.inner.lock().poll_read(buf); - if let Ok(Async::Ready(_)) = res { + fn read_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + let res = AsyncRead::poll_read(Pin::new(&mut *self.inner.lock()), cx, buf); + if let Poll::Ready(Ok(_)) = res { self.remote_acknowledged.store(true, Ordering::Release); } res } - fn write_substream(&self, _: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.lock().poll_write(buf) + fn write_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &[u8]) -> Poll> { + AsyncWrite::poll_write(Pin::new(&mut *self.inner.lock()), cx, buf) } - fn flush_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } - fn shutdown_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().shutdown() + fn shutdown_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_close(Pin::new(&mut *self.inner.lock()), cx) } fn destroy_substream(&self, _: Self::Substream) { @@ -132,12 +131,12 @@ where self.remote_acknowledged.load(Ordering::Acquire) } - fn close(&self) -> Poll<(), io::Error> { + fn close(&self, cx: &mut Context) -> Poll> { // The `StreamMuxer` trait requires that `close()` implies `flush_all()`. - self.flush_all() + self.flush_all(cx) } - fn flush_all(&self) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_all(&self, cx: &mut Context) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } } diff --git a/core/src/nodes/collection.rs b/core/src/nodes/collection.rs index af8601d2..9e212810 100644 --- a/core/src/nodes/collection.rs +++ b/core/src/nodes/collection.rs @@ -29,11 +29,7 @@ use crate::{ }; use fnv::FnvHashMap; use futures::prelude::*; -use std::{error, fmt, hash::Hash, mem}; - -pub use crate::nodes::tasks::StartTakeOver; - -mod tests; +use std::{error, fmt, hash::Hash, mem, task::Context, task::Poll}; /// Implementation of `Stream` that handles a collection of nodes. pub struct CollectionStream { @@ -58,6 +54,9 @@ where } } +impl Unpin for + CollectionStream { } + /// State of a task. #[derive(Debug, Clone, PartialEq, Eq)] enum TaskState { @@ -323,7 +322,7 @@ where pub fn add_reach_attempt(&mut self, future: TFut, handler: THandler) -> ReachAttemptId where - TFut: Future + Send + 'static, + TFut: Future> + Unpin + Send + 'static, THandler: IntoNodeHandler + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, @@ -358,17 +357,19 @@ where } /// Sends an event to all nodes. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.inner.start_broadcast(event) } + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.inner.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_broadcast(cx) } /// Adds an existing connection to a node to the collection. @@ -447,13 +448,13 @@ where /// > **Note**: we use a regular `poll` method instead of implementing `Stream` in order to /// > remove the `Err` variant, but also because we want the `CollectionStream` to stay /// > borrowed if necessary. - pub fn poll(&mut self) -> Async> + pub fn poll(&mut self, cx: &mut Context) -> Poll> where TConnInfo: Clone, // TODO: Clone shouldn't be necessary { - let item = match self.inner.poll() { - Async::Ready(item) => item, - Async::NotReady => return Async::NotReady, + let item = match self.inner.poll(cx) { + Poll::Ready(item) => item, + Poll::Pending => return Poll::Pending, }; match item { @@ -463,7 +464,7 @@ where match (user_data, result, handler) { (TaskState::Pending, tasks::Error::Reach(err), Some(handler)) => { - Async::Ready(CollectionEvent::ReachError { + Poll::Ready(CollectionEvent::ReachError { id: ReachAttemptId(id), error: err, handler, @@ -482,7 +483,7 @@ where debug_assert!(_handler.is_none()); let _node_task_id = self.nodes.remove(conn_info.peer_id()); debug_assert_eq!(_node_task_id, Some(id)); - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error: err, user_data, @@ -497,8 +498,8 @@ where tasks::Event::NodeReached { task, conn_info } => { let id = task.id(); drop(task); - Async::Ready(CollectionEvent::NodeReached(CollectionReachEvent { - parent: self, + Poll::Ready(CollectionEvent::NodeReached(CollectionReachEvent { + parent: &mut *self, id, conn_info: Some(conn_info), })) @@ -512,7 +513,7 @@ where self.tasks is switched to the Connected state; QED"), }; drop(task); - Async::Ready(CollectionEvent::NodeEvent { + Poll::Ready(CollectionEvent::NodeEvent { // TODO: normally we'd build a `PeerMut` manually here, but the borrow checker // doesn't like it peer: self.peer_mut(&conn_info.peer_id()) @@ -616,14 +617,15 @@ where } } - /// Sends an event to the given node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Begin sending an event to the given node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.inner.start_send_event(event) } - /// Complete sending an event message initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { - self.inner.complete_send_event() + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_event(cx) } /// Closes the connections to this node. Returns the user data. @@ -648,23 +650,13 @@ where /// The reach attempt will only be effectively cancelled once the peer (the object you're /// manipulating) has received some network activity. However no event will be ever be /// generated from this reach attempt, and this takes effect immediately. - #[must_use] - pub fn start_take_over(&mut self, id: InterruptedReachAttempt) - -> StartTakeOver<(), InterruptedReachAttempt> - { - match self.inner.start_take_over(id.inner) { - StartTakeOver::Ready(_state) => { - debug_assert!(if let TaskState::Pending = _state { true } else { false }); - StartTakeOver::Ready(()) - } - StartTakeOver::NotReady(inner) => - StartTakeOver::NotReady(InterruptedReachAttempt { inner }), - StartTakeOver::Gone => StartTakeOver::Gone - } + pub fn start_take_over(&mut self, id: InterruptedReachAttempt) { + self.inner.start_take_over(id.inner) } - /// Complete a take over initiated by `start_take_over`. - pub fn complete_take_over(&mut self) -> Poll<(), ()> { - self.inner.complete_take_over() + /// Make sure we are ready to taking over with `start_take_over`. + #[must_use] + pub fn poll_ready_take_over(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_take_over(cx) } } diff --git a/core/src/nodes/collection/tests.rs b/core/src/nodes/collection/tests.rs deleted file mode 100644 index 69f82c05..00000000 --- a/core/src/nodes/collection/tests.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use futures::future; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, InEvent, OutEvent, HandlerState}; -use tokio::runtime::current_thread::Runtime; -use tokio::runtime::Builder; -use crate::nodes::NodeHandlerEvent; -use std::{io, sync::Arc}; -use parking_lot::Mutex; - -type TestCollectionStream = CollectionStream; - -#[test] -fn has_connection_is_false_before_a_connection_has_been_made() { - let cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(!cs.has_connection(&peer_id)); -} - -#[test] -fn connections_is_empty_before_connecting() { - let cs = TestCollectionStream::new(); - assert!(cs.connections().next().is_none()); -} - -#[test] -fn retrieving_a_peer_is_none_if_peer_is_missing_or_not_connected() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(cs.peer_mut(&peer_id).is_none()); - - let handler = Handler::default(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, handler); - assert!(cs.peer_mut(&peer_id).is_none()); // task is pending -} - -#[test] -fn collection_stream_reaches_the_nodes() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((peer_id, muxer)); - cs.add_reach_attempt(fut, Handler::default()); - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - let event = cs.poll(); - match poll_count { - 1 => assert_matches!(event, Async::NotReady), - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(_))); - return Ok(Async::Ready(())); // stop - } - _ => unreachable!() - } - Ok(Async::NotReady) - }); - rt.block_on(fut).unwrap(); -} - -#[test] -fn accepting_a_node_yields_new_entry() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, Handler::default()); - - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - { - let event = cs.poll(); - match poll_count { - 1 => { - assert_matches!(event, Async::NotReady); - return Ok(Async::NotReady) - } - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - let (accept_ev, accepted_peer_id) = reach_ev.accept(()); - assert_eq!(accepted_peer_id, peer_id); - assert_matches!(accept_ev, CollectionNodeAccept::NewEntry); - }); - } - _ => unreachable!() - } - } - assert!(cs.peer_mut(&peer_id).is_some(), "peer is not in the list"); - assert!(cs.has_connection(&peer_id), "peer is not connected"); - assert_eq!(cs.connections().collect::>(), vec![&peer_id]); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("running the future works"); -} - -#[test] -fn events_in_a_node_reaches_the_collection_stream() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_peer_id = PeerId::random(); - - let mut handler = Handler::default(); - handler.state = Some(HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("init")))); - let handler_states = vec![ - HandlerState::Err, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 3") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 2") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )), - ]; - handler.next_states = handler_states; - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((task_peer_id.clone(), muxer)); - cs.lock().add_reach_attempt(fut, handler); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("init")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 2")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn task_closed_with_error_while_task_is_pending_yields_reach_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_inner_fut = future::err(std::io::Error::new(std::io::ErrorKind::Other, "inner fut error")); - let reach_attempt_id = cs.lock().add_reach_attempt(task_inner_fut, Handler::default()); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::ReachError {id, error, ..} => { - assert_eq!(id, reach_attempt_id); - assert_eq!(error.to_string(), "inner fut error"); - }); - - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - -} - -#[test] -fn task_closed_with_error_when_task_is_connected_yields_node_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Err]; // triggered when sending a NextState event - - cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(cs.complete_broadcast()) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id)); - - // Assert the node errored - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::NodeClosed{..}); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn interrupting_a_pending_connection_attempt_is_ok() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - let interrupt = cs.interrupt(reach_id); - assert!(interrupt.is_ok()); -} - -#[test] -fn interrupting_a_connection_attempt_twice_is_err() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - assert!(cs.interrupt(reach_id).is_ok()); - assert_matches!(cs.interrupt(reach_id), Err(InterruptError::ReachAttemptNotFound)) -} - -#[test] -fn interrupting_an_established_connection_is_err() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let handler = Handler::default(); - - let reach_id = cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(Async::Ready(())) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id), "Connection was not established"); - - assert_matches!(cs.lock().interrupt(reach_id), Err(InterruptError::AlreadyReached)); -} diff --git a/core/src/nodes/handled_node.rs b/core/src/nodes/handled_node.rs index 150b5e45..f8b08d11 100644 --- a/core/src/nodes/handled_node.rs +++ b/core/src/nodes/handled_node.rs @@ -20,10 +20,7 @@ use crate::{PeerId, muxing::StreamMuxer}; use crate::nodes::node::{NodeEvent, NodeStream, Substream, Close}; -use futures::prelude::*; -use std::{error, fmt, io}; - -mod tests; +use std::{error, fmt, io, pin::Pin, task::Context, task::Poll}; /// Handler for the substreams of a node. // TODO: right now it is possible for a node handler to be built, then shut down right after if we @@ -59,7 +56,8 @@ pub trait NodeHandler { /// Should behave like `Stream::poll()`. /// /// Returning an error will close the connection to the remote. - fn poll(&mut self) -> Poll, Self::Error>; + fn poll(&mut self, cx: &mut Context) + -> Poll, Self::Error>>; } /// Prototype for a `NodeHandler`. @@ -172,6 +170,13 @@ where } } +impl Unpin for HandledNode +where + TMuxer: StreamMuxer, + THandler: NodeHandler>, +{ +} + impl HandledNode where TMuxer: StreamMuxer, @@ -214,37 +219,41 @@ where } /// API similar to `Future::poll` that polls the node for events. - pub fn poll(&mut self) -> Poll> { + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) + -> Poll>> + { loop { let mut node_not_ready = false; - match self.node.poll().map_err(HandledNodeError::Node)? { - Async::NotReady => node_not_ready = true, - Async::Ready(NodeEvent::InboundSubstream { substream }) => { + match self.node.poll(cx) { + Poll::Pending => node_not_ready = true, + Poll::Ready(Ok(NodeEvent::InboundSubstream { substream })) => { self.handler.inject_substream(substream, NodeHandlerEndpoint::Listener) } - Async::Ready(NodeEvent::OutboundSubstream { user_data, substream }) => { + Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream })) => { let endpoint = NodeHandlerEndpoint::Dialer(user_data); self.handler.inject_substream(substream, endpoint) } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Node(err))), } - match self.handler.poll().map_err(HandledNodeError::Handler)? { - Async::NotReady => { + match self.handler.poll(cx) { + Poll::Pending => { if node_not_ready { break } } - Async::Ready(NodeHandlerEvent::OutboundSubstreamRequest(user_data)) => { + Poll::Ready(Ok(NodeHandlerEvent::OutboundSubstreamRequest(user_data))) => { self.node.open_substream(user_data); } - Async::Ready(NodeHandlerEvent::Custom(event)) => { - return Ok(Async::Ready(event)); + Poll::Ready(Ok(NodeHandlerEvent::Custom(event))) => { + return Poll::Ready(Ok(event)); } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Handler(err))), } } - Ok(Async::NotReady) + Poll::Pending } } diff --git a/core/src/nodes/handled_node/tests.rs b/core/src/nodes/handled_node/tests.rs deleted file mode 100644 index ee138c2e..00000000 --- a/core/src/nodes/handled_node/tests.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent, TestHandledNode}; - -struct TestBuilder { - muxer: DummyMuxer, - handler: Handler, - want_open_substream: bool, - substream_user_data: usize, -} - -impl TestBuilder { - fn new() -> Self { - TestBuilder { - muxer: DummyMuxer::new(), - handler: Handler::default(), - want_open_substream: false, - substream_user_data: 0, - } - } - - fn with_muxer_inbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_inbound_connection_state(state); - self - } - - fn with_muxer_outbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_outbound_connection_state(state); - self - } - - fn with_handler_state(&mut self, state: HandlerState) -> &mut Self { - self.handler.state = Some(state); - self - } - - fn with_open_substream(&mut self, user_data: usize) -> &mut Self { - self.want_open_substream = true; - self.substream_user_data = user_data; - self - } - - fn handled_node(&mut self) -> TestHandledNode { - let mut h = HandledNode::new(self.muxer.clone(), self.handler.clone()); - if self.want_open_substream { - h.node.open_substream(self.substream_user_data); - } - h - } -} - -// Set the state of the `Handler` after `inject_outbound_closed` is called -fn set_next_handler_outbound_state( handled_node: &mut TestHandledNode, next_state: HandlerState) { - handled_node.handler.next_outbound_state = Some(next_state); -} - -#[test] -fn can_inject_event() { - let mut handled = TestBuilder::new() - .handled_node(); - - let event = InEvent::Custom("banana"); - handled.inject_event(event.clone()); - assert_eq!(handled.handler().events, vec![event]); -} - -#[test] -fn poll_with_unready_node_stream_and_handler_emits_custom_event() { - let expected_event = NodeHandlerEvent::Custom(OutEvent::Custom("pineapple")); - let mut handled = TestBuilder::new() - // make NodeStream return NotReady - .with_muxer_inbound_state(DummyConnectionState::Pending) - // make Handler return return Ready(Some(…)) - .with_handler_state(HandlerState::Ready(expected_event)) - .handled_node(); - - assert_matches!(handled.poll(), Ok(Async::Ready(event)) => { - assert_matches!(event, OutEvent::Custom("pineapple")) - }); -} - -#[test] -fn handler_emits_outbound_closed_when_opening_new_substream_on_closed_node() { - let open_event = NodeHandlerEvent::OutboundSubstreamRequest(456); - let mut handled = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Ready(open_event)) - .handled_node(); - - set_next_handler_outbound_state( - &mut handled, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("pear"))) - ); - handled.poll().expect("poll works"); -} - -#[test] -fn poll_yields_inbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_open_substream(32) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Opened) - .with_open_substream(1) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(Some(1))]); -} - -#[test] -fn poll_yields_inbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Opened) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(None)]); -} diff --git a/core/src/nodes/listeners.rs b/core/src/nodes/listeners.rs index effcea65..b9c8ebbf 100644 --- a/core/src/nodes/listeners.rs +++ b/core/src/nodes/listeners.rs @@ -21,11 +21,10 @@ //! Manage listening on multiple multiaddresses at once. use crate::{Multiaddr, Transport, transport::{TransportError, ListenerEvent}}; -use futures::prelude::*; +use futures::{prelude::*, task::Context, task::Poll}; use log::debug; use smallvec::SmallVec; -use std::{collections::VecDeque, fmt}; -use void::Void; +use std::{collections::VecDeque, fmt, pin::Pin}; /// Implementation of `futures::Stream` that allows listening on multiaddresses. /// @@ -158,7 +157,7 @@ where /// The ID of the listener that errored. listener_id: ListenerId, /// The error value. - error: ::Error + error: ::Error } } @@ -222,28 +221,31 @@ where self.listeners.iter().flat_map(|l| l.addresses.iter()) } - /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> { + /// Provides an API similar to `Stream`, except that it cannot end. + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> + where + TTrans::Listener: Unpin, + { // We remove each element from `listeners` one by one and add them back. let mut remaining = self.listeners.len(); while let Some(mut listener) = self.listeners.pop_back() { - match listener.listener.poll() { - Ok(Async::NotReady) => { + match TryStream::try_poll_next(Pin::new(&mut listener.listener), cx) { + Poll::Pending => { self.listeners.push_front(listener); remaining -= 1; if remaining == 0 { break } } - Ok(Async::Ready(Some(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { + Poll::Ready(Some(Ok(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::Incoming { + return Poll::Ready(ListenersEvent::Incoming { listener_id: id, upgrade, local_addr, send_back_addr: remote_addr }) } - Ok(Async::Ready(Some(ListenerEvent::NewAddress(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::NewAddress(a)))) => { if listener.addresses.contains(&a) { debug!("Transport has reported address {} multiple times", a) } @@ -252,28 +254,28 @@ where } let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::NewAddress { + return Poll::Ready(ListenersEvent::NewAddress { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(Some(ListenerEvent::AddressExpired(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::AddressExpired(a)))) => { listener.addresses.retain(|x| x != &a); let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::AddressExpired { + return Poll::Ready(ListenersEvent::AddressExpired { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(None)) => { - return Async::Ready(ListenersEvent::Closed { + Poll::Ready(None) => { + return Poll::Ready(ListenersEvent::Closed { listener_id: listener.id, listener: listener.listener }) } - Err(err) => { - return Async::Ready(ListenersEvent::Error { + Poll::Ready(Some(Err(err))) => { + return Poll::Ready(ListenersEvent::Error { listener_id: listener.id, error: err }) @@ -282,22 +284,28 @@ where } // We register the current task to be woken up if a new listener is added. - Async::NotReady + Poll::Pending } } impl Stream for ListenersStream where TTrans: Transport, + TTrans::Listener: Unpin, { type Item = ListenersEvent; - type Error = Void; // TODO: use ! once stable - fn poll(&mut self) -> Poll, Self::Error> { - Ok(self.poll().map(Option::Some)) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ListenersStream::poll(self, cx).map(Option::Some) } } +impl Unpin for ListenersStream +where + TTrans: Transport, +{ +} + impl fmt::Debug for ListenersStream where TTrans: Transport + fmt::Debug, @@ -313,7 +321,7 @@ where impl fmt::Debug for ListenersEvent where TTrans: Transport, - ::Error: fmt::Debug, + ::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { @@ -353,215 +361,37 @@ mod tests { use tokio::runtime::current_thread::Runtime; use std::{io, iter::FromIterator}; use futures::{future::{self}, stream}; - use crate::tests::dummy_transport::{DummyTransport, ListenerState}; - use crate::tests::dummy_muxer::DummyMuxer; use crate::PeerId; - fn set_listener_state(ls: &mut ListenersStream, idx: usize, state: ListenerState) { - ls.listeners[idx].listener = match state { - ListenerState::Error => - Box::new(stream::poll_fn(|| Err(io::Error::new(io::ErrorKind::Other, "oh noes")))), - ListenerState::Ok(state) => match state { - Async::NotReady => Box::new(stream::poll_fn(|| Ok(Async::NotReady))), - Async::Ready(Some(event)) => Box::new(stream::poll_fn(move || { - Ok(Async::Ready(Some(event.clone().map(future::ok)))) - })), - Async::Ready(None) => Box::new(stream::empty()) - } - ListenerState::Events(events) => - Box::new(stream::iter_ok(events.into_iter().map(|e| e.map(future::ok)))) - }; - } - #[test] fn incoming_event() { - let mem_transport = transport::MemoryTransport::default(); + futures::executor::block_on(async move { + let mem_transport = transport::MemoryTransport::default(); - let mut listeners = ListenersStream::new(mem_transport); - listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); + let mut listeners = ListenersStream::new(mem_transport); + listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); - let address = { - let event = listeners.by_ref().wait().next().expect("some event").expect("no error"); - if let ListenersEvent::NewAddress { listen_addr, .. } = event { - listen_addr - } else { - panic!("Was expecting the listen address to be reported") - } - }; - - let dial = mem_transport.dial(address.clone()).unwrap(); - - let future = listeners - .into_future() - .map_err(|(err, _)| err) - .and_then(|(event, _)| { - match event { - Some(ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. }) => { - assert_eq!(local_addr, address); - assert_eq!(send_back_addr, address); - upgrade.map(|_| ()).map_err(|_| panic!()) - }, - _ => panic!() + let address = { + let event = listeners.next().await.unwrap(); + if let ListenersEvent::NewAddress { listen_addr, .. } = event { + listen_addr + } else { + panic!("Was expecting the listen address to be reported") } - }) - .select(dial.map(|_| ()).map_err(|_| panic!())) - .map_err(|(err, _)| err); + }; - let mut runtime = Runtime::new().unwrap(); - runtime.block_on(future).unwrap(); - } + let address2 = address.clone(); + async_std::task::spawn(async move { + mem_transport.dial(address2).unwrap().await.unwrap(); + }); - #[test] - fn listener_stream_returns_transport() { - let t = DummyTransport::new(); - let t_clone = t.clone(); - let ls = ListenersStream::new(t); - assert_eq!(ls.transport(), &t_clone); - } - - #[test] - fn listener_stream_can_iterate_over_listeners() { - let mut t = DummyTransport::new(); - let addr1 = tcp4([127, 0, 0, 1], 1234); - let addr2 = tcp4([127, 0, 0, 1], 4321); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(addr1.clone()), - ListenerEvent::NewAddress(addr2.clone()) - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([0, 0, 0, 0], 0)).expect("listen_on"); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr1, listen_addr) - }); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr2, listen_addr) - }) - } - - #[test] - fn listener_stream_poll_without_listeners_is_not_ready() { - let t = DummyTransport::new(); - let mut ls = ListenersStream::new(t); - assert_matches!(ls.poll(), Async::NotReady); - } - - #[test] - fn listener_stream_poll_with_listeners_that_arent_ready_is_not_ready() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::NotReady)); - assert_matches!(ls.poll(), Async::NotReady); - assert_eq!(ls.listeners.len(), 1); // listener is still there - } - - #[test] - fn listener_stream_poll_with_ready_listeners_is_ready() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let expected_output = (peer_id.clone(), muxer.clone()); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(tcp4([127, 0, 0, 1], 9090)), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) + match listeners.next().await.unwrap() { + ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. } => { + assert_eq!(local_addr, address); + assert_eq!(send_back_addr, address); + }, + _ => panic!() } - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([127, 0, 0, 1], 1234)).expect("listen_on"); - ls.listen_on(tcp4([127, 0, 0, 1], 4321)).expect("listen_on"); - assert_eq!(ls.listeners.len(), 2); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - set_listener_state(&mut ls, 1, ListenerState::Ok(Async::NotReady)); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - } - - #[test] - fn listener_stream_poll_with_closed_listener_emits_closed_event() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::Ready(None))); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Closed{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - #[test] - fn listener_stream_poll_with_erroring_listener_emits_error_event() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let event = ListenerEvent::Upgrade { - upgrade: (peer_id, muxer), - local_addr: tcp4([127, 0, 0, 1], 1234), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }; - t.set_initial_listener_state(ListenerState::Ok(Async::Ready(Some(event)))); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Error); // simulate an error on the socket - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Error{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - fn tcp4(ip: [u8; 4], port: u16) -> Multiaddr { - let protos = std::iter::once(multiaddr::Protocol::Ip4(ip.into())) - .chain(std::iter::once(multiaddr::Protocol::Tcp(port))); - Multiaddr::from_iter(protos) } } diff --git a/core/src/nodes/network.rs b/core/src/nodes/network.rs index abe9e631..2f6634a1 100644 --- a/core/src/nodes/network.rs +++ b/core/src/nodes/network.rs @@ -49,10 +49,10 @@ use std::{ fmt, hash::Hash, num::NonZeroUsize, + pin::Pin, + task::{Context, Poll}, }; -pub use crate::nodes::collection::StartTakeOver; - mod tests; /// Implementation of `Stream` that handles the nodes. @@ -81,7 +81,7 @@ where /// If the pair's second element is `AsyncSink::Ready`, the take over /// message has been sent and needs to be flushed using /// `PeerMut::complete_take_over`. - take_over_to_complete: Option<(TPeerId, AsyncSink>)> + take_over_to_complete: Option<(TPeerId, InterruptedReachAttempt)> } impl fmt::Debug for @@ -102,6 +102,13 @@ where } } +impl Unpin for + Network +where + TTrans: Transport +{ +} + impl ConnectionInfo for (TConnInfo, ConnectedPoint) where TConnInfo: ConnectionInfo @@ -173,7 +180,7 @@ where /// The listener that errored. listener_id: ListenerId, /// The listener error. - error: ::Error + error: ::Error }, /// One of the listeners is now listening on an additional address. @@ -573,7 +580,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::ListenerUpgrade: Send + 'static, + TTrans::ListenerUpgrade: Unpin + Send + 'static, THandler: IntoNodeHandler<(TConnInfo, ConnectedPoint)> + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, // TODO: shouldn't be necessary @@ -609,9 +616,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -781,7 +788,7 @@ where where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TInEvent: Send + 'static, @@ -797,9 +804,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -840,19 +847,18 @@ where /// Start sending an event to all nodes. /// - /// Make sure to complete the broadcast with `complete_broadcast`. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.active_nodes.start_broadcast(event) } - /// Complete a broadcast initiated with `start_broadcast`. + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.active_nodes.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.active_nodes.poll_ready_broadcast(cx) } /// Returns a list of all the peers we are currently connected to. @@ -934,7 +940,7 @@ where fn start_dial_out(&mut self, peer_id: TPeerId, handler: THandler, first: Multiaddr, rest: Vec) where TTrans: Transport, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TTrans::Error: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -950,9 +956,9 @@ where .map_err(|err| InternalReachErr::Transport(TransportError::Other(err))) .and_then(move |(actual_conn_info, muxer)| { if *actual_conn_info.peer_id() == expected_peer_id { - Ok(((actual_conn_info, connected_point), muxer)) + future::ready(Ok(((actual_conn_info, connected_point), muxer))) } else { - Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info }) + future::ready(Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info })) } }); self.active_nodes.add_reach_attempt(fut, handler) @@ -976,11 +982,12 @@ where } /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> + pub fn poll<'a>(&'a mut self, cx: &mut Context) -> Poll> where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, + TTrans::Listener: Unpin, TTrans::ListenerUpgrade: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -998,9 +1005,9 @@ where Some(x) if self.incoming_negotiated().count() >= (x as usize) => (), _ => { - match self.listeners.poll() { - Async::NotReady => (), - Async::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { + match ListenersStream::poll(Pin::new(&mut self.listeners), cx) { + Poll::Pending => (), + Poll::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { let event = IncomingConnectionEvent { listener_id, upgrade, @@ -1010,19 +1017,19 @@ where active_nodes: &mut self.active_nodes, other_reach_attempts: &mut self.reach_attempts.other_reach_attempts, }; - return Async::Ready(NetworkEvent::IncomingConnection(event)); + return Poll::Ready(NetworkEvent::IncomingConnection(event)); } - Async::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::Closed { listener_id, listener }) => { - return Async::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) + Poll::Ready(ListenersEvent::Closed { listener_id, listener }) => { + return Poll::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) } - Async::Ready(ListenersEvent::Error { listener_id, error }) => { - return Async::Ready(NetworkEvent::ListenerError { listener_id, error }) + Poll::Ready(ListenersEvent::Error { listener_id, error }) => { + return Poll::Ready(NetworkEvent::ListenerError { listener_id, error }) } } } @@ -1031,36 +1038,30 @@ where // Attempt to deliver any pending take over messages. if let Some((id, interrupted)) = self.take_over_to_complete.take() { if let Some(mut peer) = self.active_nodes.peer_mut(&id) { - if let AsyncSink::NotReady(i) = interrupted { - if let StartTakeOver::NotReady(i) = peer.start_take_over(i) { - self.take_over_to_complete = Some((id, AsyncSink::NotReady(i))) - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) - } - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((id, interrupted)); + return Poll::Pending; } } } - if self.take_over_to_complete.is_some() { - return Async::NotReady - } // Poll the existing nodes. let (action, out_event); - match self.active_nodes.poll() { - Async::NotReady => return Async::NotReady, - Async::Ready(CollectionEvent::NodeReached(reach_event)) => { + match self.active_nodes.poll(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(CollectionEvent::NodeReached(reach_event)) => { let (a, e) = handle_node_reached(&mut self.reach_attempts, reach_event); action = a; out_event = e; } - Async::Ready(CollectionEvent::ReachError { id, error, handler }) => { + Poll::Ready(CollectionEvent::ReachError { id, error, handler }) => { let (a, e) = handle_reach_error(&mut self.reach_attempts, id, error, handler); action = a; out_event = e; } - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error, .. @@ -1078,7 +1079,7 @@ where error, }; } - Async::Ready(CollectionEvent::NodeEvent { peer, event }) => { + Poll::Ready(CollectionEvent::NodeEvent { peer, event }) => { action = Default::default(); out_event = NetworkEvent::NodeEvent { conn_info: peer.info().0.clone(), event }; } @@ -1099,17 +1100,15 @@ where out_reach_attempts should always be in sync with the actual \ attempts; QED"); let mut peer = self.active_nodes.peer_mut(&peer_id).unwrap(); - if let StartTakeOver::NotReady(i) = peer.start_take_over(interrupted) { - self.take_over_to_complete = Some((peer_id, AsyncSink::NotReady(i))); - return Async::NotReady - } - if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((peer_id, AsyncSink::Ready)); - return Async::NotReady + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((peer_id, interrupted)); + return Poll::Pending } } - Async::Ready(out_event) + Poll::Ready(out_event) } } @@ -1467,7 +1466,7 @@ impl<'a, TTrans, TMuxer, TInEvent, TOutEvent, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, @@ -1644,18 +1643,33 @@ where closed messages; QED") } - /// Start sending an event to the node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Sends an event to the handler of the node. + pub fn send_event<'s: 'a>(&'s mut self, event: TInEvent) -> impl Future + 's + 'a { + let mut event = Some(event); + futures::future::poll_fn(move |cx| { + match self.poll_ready_event(cx) { + Poll::Ready(()) => { + self.start_send_event(event.take().expect("Future called after finished")); + Poll::Ready(()) + }, + Poll::Pending => Poll::Pending, + } + }) + } + + /// Begin sending an event to the node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") .start_send_event(event) } - /// Complete sending an event message, initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") - .complete_send_event() + .poll_ready_event(cx) } } @@ -1749,7 +1763,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, diff --git a/core/src/nodes/network/tests.rs b/core/src/nodes/network/tests.rs index c64666aa..c4f307bb 100644 --- a/core/src/nodes/network/tests.rs +++ b/core/src/nodes/network/tests.rs @@ -21,363 +21,6 @@ #![cfg(test)] use super::*; -use crate::tests::dummy_transport::DummyTransport; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent}; -use crate::tests::dummy_transport::ListenerState; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::nodes::NodeHandlerEvent; -use crate::transport::ListenerEvent; -use assert_matches::assert_matches; -use parking_lot::Mutex; -use std::sync::Arc; -use tokio::runtime::{Builder, Runtime}; - -#[test] -fn query_transport() { - let transport = DummyTransport::new(); - let transport2 = transport.clone(); - let network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - assert_eq!(network.transport(), &transport2); -} - -#[test] -fn local_node_peer() { - let peer_id = PeerId::random(); - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), peer_id.clone()); - assert_matches!(network.peer(peer_id), Peer::LocalNode); -} - -#[test] -fn successful_dial_reaches_a_node() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let dial_res = network.dial(addr, Handler::default()); - assert!(dial_res.is_ok()); - - // Poll the network until we get a `NodeReached` then assert on the peer: - // it's there and it's connected. - let network = Arc::new(Mutex::new(network)); - - let mut rt = Runtime::new().unwrap(); - let mut peer_id : Option = None; - // Drive forward until we're Connected - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected(PeerConnected{..})); -} - -#[test] -fn num_incoming_negotiated() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - - let events = vec![ - ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap()), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ]; - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - // no incoming yet - assert_eq!(network.incoming_negotiated().count(), 0); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - // Now there's an incoming connection - assert_eq!(network.incoming_negotiated().count(), 1); -} - -#[test] -fn broadcasted_events_reach_active_nodes() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )),]; - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::NodeEvent { conn_info: _, event: inner_event } => { - // The event we sent reached the node and triggered sending the out event we told it to return - assert_matches!(inner_event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(false)) - }, - _ => Ok(Async::Ready(true)) - } - })).expect("tokio works"); - } -} - -#[test] -fn querying_for_pending_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); -} - -#[test] -fn querying_for_unknown_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected( PeerNotConnected { nodes: _, peer_id: node_peer_id }) => { - assert_eq!(node_peer_id, peer_id); - }); -} - -#[test] -fn querying_for_connected_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - - // Dial a node - let addr = "/ip4/127.0.0.1/tcp/1234".parse().expect("bad multiaddr"); - network.dial(addr, Handler::default()).expect("dialing works"); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we connect; extract the new PeerId. - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - // We're connected. - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected( PeerConnected { .. } )); -} - -#[test] -fn poll_with_closed_listener() { - let mut transport = DummyTransport::new(); - // Set up listener to be closed - transport.set_initial_listener_state(ListenerState::Ok(Async::Ready(None))); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::ListenerClosed { .. } )); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); -} - -#[test] -fn unknown_peer_that_is_unreachable_yields_unknown_peer_dial_error() { - let mut transport = DummyTransport::new(); - transport.make_dial_fail(); - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let handler = Handler::default(); - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::UnknownPeerDialError { .. } ); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn known_peer_that_is_unreachable_yields_dial_error() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - transport.make_dial_fail(); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); - } - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let peer_id = peer_id.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - let failed_peer_id = assert_matches!( - event, - NetworkEvent::DialError { new_state: _, peer_id: failed_peer_id, .. } => failed_peer_id - ); - assert_eq!(peer_id, failed_peer_id); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn yields_node_error_when_there_is_an_error_after_successful_connect() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - // Set up an outgoing connection with a PeerId we know - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - let addr = "/unix/reachable".parse().expect("bad multiaddr"); - let mut handler = Handler::default(); - // Force an error - handler.next_states = vec![ HandlerState::Err ]; - peer.into_not_connected().unwrap().connect(addr, handler); - } - - // Ensure we run on a single thread - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Drive it forward until we connect to the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - // Push the Handler into an error state on the next poll - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::Connected { .. }); - // We're connected, we can move on - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } - - // Poll again. It is going to be a NodeClosed because of how the - // handler's next state was set up. - let network_fut = network.clone(); - let expected_peer_id = peer_id.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::NodeClosed { conn_info, .. }) => { - assert_eq!(conn_info, expected_peer_id); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} #[test] fn local_prio_equivalence_relation() { @@ -387,59 +30,3 @@ fn local_prio_equivalence_relation() { assert_ne!(has_dial_prio(&a, &b), has_dial_prio(&b, &a)); } } - -#[test] -fn limit_incoming_connections() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let limit = 1; - - let mut events = vec![ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap())]; - events.extend(std::iter::repeat( - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ).take(10)); - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new_with_incoming_limit(transport, PeerId::random(), Some(limit)); - assert_eq!(network.incoming_limit(), Some(limit)); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - assert_eq!(network.incoming_negotiated().count(), 0); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - for i in 1..10 { - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - if i <= limit { - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), - Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - } else { - match network_fut.poll() { - Async::NotReady => (), - Async::Ready(x) => { - match x { - NetworkEvent::NewListenerAddress {..} => {} - NetworkEvent::ExpiredListenerAddress {..} => {} - NetworkEvent::IncomingConnection(_) => {} - NetworkEvent::Connected {..} => {} - e => panic!("Not expected event: {:?}", e) - } - }, - } - } - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - assert!(network.incoming_negotiated().count() <= (limit as usize)); - } -} diff --git a/core/src/nodes/node.rs b/core/src/nodes/node.rs index a1d0eac4..37da9954 100644 --- a/core/src/nodes/node.rs +++ b/core/src/nodes/node.rs @@ -21,9 +21,7 @@ use futures::prelude::*; use crate::muxing; use smallvec::SmallVec; -use std::fmt; -use std::io::Error as IoError; -use std::sync::Arc; +use std::{fmt, io::Error as IoError, pin::Pin, sync::Arc, task::Context, task::Poll}; // Implementation notes // ================= @@ -143,43 +141,44 @@ where } /// Provides an API similar to `Future`. - pub fn poll(&mut self) -> Poll, IoError> { + pub fn poll(&mut self, cx: &mut Context) -> Poll, IoError>> { // Polling inbound substream. - match self.muxer.poll_inbound().map_err(|e| e.into())? { - Async::Ready(substream) => { + match self.muxer.poll_inbound(cx) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); - return Ok(Async::Ready(NodeEvent::InboundSubstream { + return Poll::Ready(Ok(NodeEvent::InboundSubstream { substream, })); } - Async::NotReady => {} + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => {} } // Polling outbound substreams. // We remove each element from `outbound_substreams` one by one and add them back. for n in (0..self.outbound_substreams.len()).rev() { let (user_data, mut outbound) = self.outbound_substreams.swap_remove(n); - match self.muxer.poll_outbound(&mut outbound) { - Ok(Async::Ready(substream)) => { + match self.muxer.poll_outbound(cx, &mut outbound) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); self.muxer.destroy_outbound(outbound); - return Ok(Async::Ready(NodeEvent::OutboundSubstream { + return Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream, })); } - Ok(Async::NotReady) => { + Poll::Pending => { self.outbound_substreams.push((user_data, outbound)); } - Err(err) => { + Poll::Ready(Err(err)) => { self.muxer.destroy_outbound(outbound); - return Err(err.into()); + return Poll::Ready(Err(err.into())); } } } // Nothing happened. Register our task to be notified and return. - Ok(Async::NotReady) + Poll::Pending } } @@ -212,11 +211,14 @@ impl Future for Close where TMuxer: muxing::StreamMuxer, { - type Item = (); - type Error = IoError; + type Output = Result<(), IoError>; - fn poll(&mut self) -> Poll { - self.muxer.close().map_err(|e| e.into()) + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match self.muxer.close(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(err)) => Poll::Ready(Err(err.into())), + } } } @@ -252,70 +254,3 @@ where } } } - -#[cfg(test)] -mod node_stream { - use super::{NodeEvent, NodeStream}; - use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; - use assert_matches::assert_matches; - use futures::prelude::*; - use tokio_mock_task::MockTask; - - fn build_node_stream() -> NodeStream> { - let muxer = DummyMuxer::new(); - NodeStream::<_, Vec>::new(muxer) - } - - #[test] - fn closing_a_node_stream_destroys_substreams_and_returns_submitted_user_data() { - let mut ns = build_node_stream(); - ns.open_substream(vec![2]); - ns.open_substream(vec![3]); - ns.open_substream(vec![5]); - let user_data_submitted = ns.close(); - assert_eq!(user_data_submitted.1, vec![ - vec![2], vec![3], vec![5] - ]); - } - - #[test] - fn poll_returns_not_ready_when_there_is_nothing_to_do() { - let mut task = MockTask::new(); - task.enter(|| { - // ensure the address never resolves - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - - assert_matches!(ns.poll(), Ok(Async::NotReady)); - }); - } - - #[test] - fn poll_keeps_outbound_substreams_when_the_outgoing_connection_is_not_ready() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - ns.open_substream(vec![1]); - ns.poll().unwrap(); // poll past inbound - ns.poll().unwrap(); // poll outbound - assert!(format!("{:?}", ns).contains("outbound_substreams: 1")); - } - - #[test] - fn poll_returns_incoming_substream() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::Ready(subs) - muxer.set_inbound_connection_state(DummyConnectionState::Opened); - let mut ns = NodeStream::<_, Vec>::new(muxer); - assert_matches!(ns.poll(), Ok(Async::Ready(node_event)) => { - assert_matches!(node_event, NodeEvent::InboundSubstream{ substream: _ }); - }); - } -} diff --git a/core/src/nodes/tasks/manager.rs b/core/src/nodes/tasks/manager.rs index 33643aaa..aff72bd9 100644 --- a/core/src/nodes/tasks/manager.rs +++ b/core/src/nodes/tasks/manager.rs @@ -27,9 +27,8 @@ use crate::{ } }; use fnv::FnvHashMap; -use futures::{prelude::*, future::Executor, sync::mpsc}; -use smallvec::SmallVec; -use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt}; +use futures::{prelude::*, channel::mpsc, executor::ThreadPool, stream::FuturesUnordered, task::SpawnExt as _}; +use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt, pin::Pin, task::Context, task::Poll}; use super::{TaskId, task::{Task, FromTaskMessage, ToTaskMessage}, Error}; // Implementor notes @@ -64,12 +63,13 @@ pub struct Manager { /// Identifier for the next task to spawn. next_task_id: TaskId, - /// List of node tasks to spawn. - to_spawn: SmallVec<[Box + Send>; 8]>, + /// Threads pool where we spawn the nodes' tasks. If `None`, then we push tasks to the + /// `local_spawns` list instead. + threads_pool: Option, - /// If no tokio executor is available, we move tasks to this list, and futures are polled on - /// the current thread instead. - local_spawns: Vec + Send>>, + /// If no executor is available, we move tasks to this list, and futures are polled on the + /// current thread instead. + local_spawns: FuturesUnordered + Send>>>, /// Sender to emit events to the outside. Meant to be cloned and sent to tasks. events_tx: mpsc::Sender<(FromTaskMessage, TaskId)>, @@ -91,16 +91,13 @@ where /// Information about a running task. /// -/// Contains the sender to deliver event messages to the task, -/// the associated user data and a pending message if any, -/// meant to be delivered to the task via the sender. +/// Contains the sender to deliver event messages to the task, and +/// the associated user data. struct TaskInfo
Read for SubstreamRef
+impl
Unpin for SubstreamRef
where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.read_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } } impl AsyncRead for SubstreamRef @@ -391,37 +381,17 @@ where P: Deref, P::Target: StreamMuxer, { - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.muxer.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.muxer.initializer() } - fn poll_read(&mut self, buf: &mut [u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.read_substream(s, buf).map_err(|e| e.into()) - } -} + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; -impl Write for SubstreamRef -where - P: Deref, - P::Target: StreamMuxer, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.write_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } - - #[inline] - fn flush(&mut self) -> Result<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.flush_substream(s).map_err(|e| e.into())? { - Async::Ready(()) => Ok(()), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.read_substream(cx, s, buf).map_err(|e| e.into()) } } @@ -430,36 +400,51 @@ where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn poll_write(&mut self, buf: &[u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.write_substream(s, buf).map_err(|e| e.into()) + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.write_substream(cx, s, buf).map_err(|e| e.into()) } - #[inline] - fn shutdown(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); loop { - match self.shutdown_state { + match this.shutdown_state { ShutdownState::Shutdown => { - try_ready!(self.muxer.shutdown_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Flush; + match this.muxer.shutdown_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Flush, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Flush => { - try_ready!(self.muxer.flush_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Done; + match this.muxer.flush_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Done, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Done => { - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } } } } - #[inline] - fn poll_flush(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.flush_substream(s).map_err(|e| e.into()) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.flush_substream(cx, s).map_err(|e| e.into()) } } @@ -507,8 +492,8 @@ impl StreamMuxer for StreamMuxerBox { type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - self.inner.poll_inbound() + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + self.inner.poll_inbound(cx) } #[inline] @@ -517,8 +502,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn poll_outbound(&self, s: &mut Self::OutboundSubstream) -> Poll { - self.inner.poll_outbound(s) + fn poll_outbound(&self, cx: &mut Context, s: &mut Self::OutboundSubstream) -> Poll> { + self.inner.poll_outbound(cx, s) } #[inline] @@ -526,28 +511,28 @@ impl StreamMuxer for StreamMuxerBox { self.inner.destroy_outbound(substream) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { - self.inner.read_substream(s, buf) + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + self.inner.read_substream(cx, s, buf) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.write_substream(s, buf) + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { + self.inner.write_substream(cx, s, buf) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.flush_substream(s) + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.flush_substream(cx, s) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.shutdown_substream(s) + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.shutdown_substream(cx, s) } #[inline] @@ -556,8 +541,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close() + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx) } #[inline] @@ -566,8 +551,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all() + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx) } } @@ -588,11 +573,16 @@ where type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - let substream = try_ready!(self.inner.poll_inbound().map_err(|e| e.into())); + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + let substream = match self.inner.poll_inbound(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; + let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -606,13 +596,18 @@ where #[inline] fn poll_outbound( &self, + cx: &mut Context, substream: &mut Self::OutboundSubstream, - ) -> Poll { + ) -> Poll> { let mut list = self.outbound.lock(); - let substream = try_ready!(self.inner.poll_outbound(list.get_mut(substream).unwrap()).map_err(|e| e.into())); + let substream = match self.inner.poll_outbound(cx, list.get_mut(substream).unwrap()) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -621,32 +616,32 @@ where self.inner.destroy_outbound(list.remove(&substream).unwrap()) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.read_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.read_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.write_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.write_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.flush_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.flush_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.shutdown_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.shutdown_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] @@ -656,8 +651,8 @@ where } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close().map_err(|e| e.into()) + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx).map_err(|e| e.into()) } #[inline] @@ -666,7 +661,7 @@ where } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all().map_err(|e| e.into()) + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx).map_err(|e| e.into()) } } diff --git a/core/src/muxing/singleton.rs b/core/src/muxing/singleton.rs index 7bec14ed..f85e22fd 100644 --- a/core/src/muxing/singleton.rs +++ b/core/src/muxing/singleton.rs @@ -19,10 +19,9 @@ // DEALINGS IN THE SOFTWARE. use crate::{Endpoint, muxing::StreamMuxer}; -use futures::prelude::*; +use futures::{prelude::*, io::Initializer}; use parking_lot::Mutex; -use std::{io, sync::atomic::{AtomicBool, Ordering}}; -use tokio_io::{AsyncRead, AsyncWrite}; +use std::{io, pin::Pin, sync::atomic::{AtomicBool, Ordering}, task::Context, task::Poll}; /// Implementation of `StreamMuxer` that allows only one substream on top of a connection, /// yielding the connection itself. @@ -62,22 +61,22 @@ pub struct OutboundSubstream {} impl StreamMuxer for SingletonMuxer where - TSocket: AsyncRead + AsyncWrite, + TSocket: AsyncRead + AsyncWrite + Unpin, { type Substream = Substream; type OutboundSubstream = OutboundSubstream; type Error = io::Error; - fn poll_inbound(&self) -> Poll { + fn poll_inbound(&self, _: &mut Context) -> Poll> { match self.endpoint { - Endpoint::Dialer => return Ok(Async::NotReady), + Endpoint::Dialer => return Poll::Pending, Endpoint::Listener => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -85,44 +84,44 @@ where OutboundSubstream {} } - fn poll_outbound(&self, _: &mut Self::OutboundSubstream) -> Poll { + fn poll_outbound(&self, _: &mut Context, _: &mut Self::OutboundSubstream) -> Poll> { match self.endpoint { - Endpoint::Listener => return Ok(Async::NotReady), + Endpoint::Listener => return Poll::Pending, Endpoint::Dialer => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } fn destroy_outbound(&self, _: Self::OutboundSubstream) { } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.lock().prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.lock().initializer() } - fn read_substream(&self, _: &mut Self::Substream, buf: &mut [u8]) -> Poll { - let res = self.inner.lock().poll_read(buf); - if let Ok(Async::Ready(_)) = res { + fn read_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + let res = AsyncRead::poll_read(Pin::new(&mut *self.inner.lock()), cx, buf); + if let Poll::Ready(Ok(_)) = res { self.remote_acknowledged.store(true, Ordering::Release); } res } - fn write_substream(&self, _: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.lock().poll_write(buf) + fn write_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &[u8]) -> Poll> { + AsyncWrite::poll_write(Pin::new(&mut *self.inner.lock()), cx, buf) } - fn flush_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } - fn shutdown_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().shutdown() + fn shutdown_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_close(Pin::new(&mut *self.inner.lock()), cx) } fn destroy_substream(&self, _: Self::Substream) { @@ -132,12 +131,12 @@ where self.remote_acknowledged.load(Ordering::Acquire) } - fn close(&self) -> Poll<(), io::Error> { + fn close(&self, cx: &mut Context) -> Poll> { // The `StreamMuxer` trait requires that `close()` implies `flush_all()`. - self.flush_all() + self.flush_all(cx) } - fn flush_all(&self) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_all(&self, cx: &mut Context) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } } diff --git a/core/src/nodes/collection.rs b/core/src/nodes/collection.rs index af8601d2..9e212810 100644 --- a/core/src/nodes/collection.rs +++ b/core/src/nodes/collection.rs @@ -29,11 +29,7 @@ use crate::{ }; use fnv::FnvHashMap; use futures::prelude::*; -use std::{error, fmt, hash::Hash, mem}; - -pub use crate::nodes::tasks::StartTakeOver; - -mod tests; +use std::{error, fmt, hash::Hash, mem, task::Context, task::Poll}; /// Implementation of `Stream` that handles a collection of nodes. pub struct CollectionStream { @@ -58,6 +54,9 @@ where } } +impl Unpin for + CollectionStream { } + /// State of a task. #[derive(Debug, Clone, PartialEq, Eq)] enum TaskState { @@ -323,7 +322,7 @@ where pub fn add_reach_attempt(&mut self, future: TFut, handler: THandler) -> ReachAttemptId where - TFut: Future + Send + 'static, + TFut: Future> + Unpin + Send + 'static, THandler: IntoNodeHandler + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, @@ -358,17 +357,19 @@ where } /// Sends an event to all nodes. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.inner.start_broadcast(event) } + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.inner.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_broadcast(cx) } /// Adds an existing connection to a node to the collection. @@ -447,13 +448,13 @@ where /// > **Note**: we use a regular `poll` method instead of implementing `Stream` in order to /// > remove the `Err` variant, but also because we want the `CollectionStream` to stay /// > borrowed if necessary. - pub fn poll(&mut self) -> Async> + pub fn poll(&mut self, cx: &mut Context) -> Poll> where TConnInfo: Clone, // TODO: Clone shouldn't be necessary { - let item = match self.inner.poll() { - Async::Ready(item) => item, - Async::NotReady => return Async::NotReady, + let item = match self.inner.poll(cx) { + Poll::Ready(item) => item, + Poll::Pending => return Poll::Pending, }; match item { @@ -463,7 +464,7 @@ where match (user_data, result, handler) { (TaskState::Pending, tasks::Error::Reach(err), Some(handler)) => { - Async::Ready(CollectionEvent::ReachError { + Poll::Ready(CollectionEvent::ReachError { id: ReachAttemptId(id), error: err, handler, @@ -482,7 +483,7 @@ where debug_assert!(_handler.is_none()); let _node_task_id = self.nodes.remove(conn_info.peer_id()); debug_assert_eq!(_node_task_id, Some(id)); - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error: err, user_data, @@ -497,8 +498,8 @@ where tasks::Event::NodeReached { task, conn_info } => { let id = task.id(); drop(task); - Async::Ready(CollectionEvent::NodeReached(CollectionReachEvent { - parent: self, + Poll::Ready(CollectionEvent::NodeReached(CollectionReachEvent { + parent: &mut *self, id, conn_info: Some(conn_info), })) @@ -512,7 +513,7 @@ where self.tasks is switched to the Connected state; QED"), }; drop(task); - Async::Ready(CollectionEvent::NodeEvent { + Poll::Ready(CollectionEvent::NodeEvent { // TODO: normally we'd build a `PeerMut` manually here, but the borrow checker // doesn't like it peer: self.peer_mut(&conn_info.peer_id()) @@ -616,14 +617,15 @@ where } } - /// Sends an event to the given node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Begin sending an event to the given node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.inner.start_send_event(event) } - /// Complete sending an event message initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { - self.inner.complete_send_event() + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_event(cx) } /// Closes the connections to this node. Returns the user data. @@ -648,23 +650,13 @@ where /// The reach attempt will only be effectively cancelled once the peer (the object you're /// manipulating) has received some network activity. However no event will be ever be /// generated from this reach attempt, and this takes effect immediately. - #[must_use] - pub fn start_take_over(&mut self, id: InterruptedReachAttempt) - -> StartTakeOver<(), InterruptedReachAttempt> - { - match self.inner.start_take_over(id.inner) { - StartTakeOver::Ready(_state) => { - debug_assert!(if let TaskState::Pending = _state { true } else { false }); - StartTakeOver::Ready(()) - } - StartTakeOver::NotReady(inner) => - StartTakeOver::NotReady(InterruptedReachAttempt { inner }), - StartTakeOver::Gone => StartTakeOver::Gone - } + pub fn start_take_over(&mut self, id: InterruptedReachAttempt) { + self.inner.start_take_over(id.inner) } - /// Complete a take over initiated by `start_take_over`. - pub fn complete_take_over(&mut self) -> Poll<(), ()> { - self.inner.complete_take_over() + /// Make sure we are ready to taking over with `start_take_over`. + #[must_use] + pub fn poll_ready_take_over(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_take_over(cx) } } diff --git a/core/src/nodes/collection/tests.rs b/core/src/nodes/collection/tests.rs deleted file mode 100644 index 69f82c05..00000000 --- a/core/src/nodes/collection/tests.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use futures::future; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, InEvent, OutEvent, HandlerState}; -use tokio::runtime::current_thread::Runtime; -use tokio::runtime::Builder; -use crate::nodes::NodeHandlerEvent; -use std::{io, sync::Arc}; -use parking_lot::Mutex; - -type TestCollectionStream = CollectionStream; - -#[test] -fn has_connection_is_false_before_a_connection_has_been_made() { - let cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(!cs.has_connection(&peer_id)); -} - -#[test] -fn connections_is_empty_before_connecting() { - let cs = TestCollectionStream::new(); - assert!(cs.connections().next().is_none()); -} - -#[test] -fn retrieving_a_peer_is_none_if_peer_is_missing_or_not_connected() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(cs.peer_mut(&peer_id).is_none()); - - let handler = Handler::default(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, handler); - assert!(cs.peer_mut(&peer_id).is_none()); // task is pending -} - -#[test] -fn collection_stream_reaches_the_nodes() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((peer_id, muxer)); - cs.add_reach_attempt(fut, Handler::default()); - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - let event = cs.poll(); - match poll_count { - 1 => assert_matches!(event, Async::NotReady), - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(_))); - return Ok(Async::Ready(())); // stop - } - _ => unreachable!() - } - Ok(Async::NotReady) - }); - rt.block_on(fut).unwrap(); -} - -#[test] -fn accepting_a_node_yields_new_entry() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, Handler::default()); - - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - { - let event = cs.poll(); - match poll_count { - 1 => { - assert_matches!(event, Async::NotReady); - return Ok(Async::NotReady) - } - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - let (accept_ev, accepted_peer_id) = reach_ev.accept(()); - assert_eq!(accepted_peer_id, peer_id); - assert_matches!(accept_ev, CollectionNodeAccept::NewEntry); - }); - } - _ => unreachable!() - } - } - assert!(cs.peer_mut(&peer_id).is_some(), "peer is not in the list"); - assert!(cs.has_connection(&peer_id), "peer is not connected"); - assert_eq!(cs.connections().collect::>(), vec![&peer_id]); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("running the future works"); -} - -#[test] -fn events_in_a_node_reaches_the_collection_stream() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_peer_id = PeerId::random(); - - let mut handler = Handler::default(); - handler.state = Some(HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("init")))); - let handler_states = vec![ - HandlerState::Err, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 3") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 2") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )), - ]; - handler.next_states = handler_states; - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((task_peer_id.clone(), muxer)); - cs.lock().add_reach_attempt(fut, handler); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("init")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 2")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn task_closed_with_error_while_task_is_pending_yields_reach_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_inner_fut = future::err(std::io::Error::new(std::io::ErrorKind::Other, "inner fut error")); - let reach_attempt_id = cs.lock().add_reach_attempt(task_inner_fut, Handler::default()); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::ReachError {id, error, ..} => { - assert_eq!(id, reach_attempt_id); - assert_eq!(error.to_string(), "inner fut error"); - }); - - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - -} - -#[test] -fn task_closed_with_error_when_task_is_connected_yields_node_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Err]; // triggered when sending a NextState event - - cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(cs.complete_broadcast()) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id)); - - // Assert the node errored - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::NodeClosed{..}); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn interrupting_a_pending_connection_attempt_is_ok() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - let interrupt = cs.interrupt(reach_id); - assert!(interrupt.is_ok()); -} - -#[test] -fn interrupting_a_connection_attempt_twice_is_err() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - assert!(cs.interrupt(reach_id).is_ok()); - assert_matches!(cs.interrupt(reach_id), Err(InterruptError::ReachAttemptNotFound)) -} - -#[test] -fn interrupting_an_established_connection_is_err() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let handler = Handler::default(); - - let reach_id = cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(Async::Ready(())) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id), "Connection was not established"); - - assert_matches!(cs.lock().interrupt(reach_id), Err(InterruptError::AlreadyReached)); -} diff --git a/core/src/nodes/handled_node.rs b/core/src/nodes/handled_node.rs index 150b5e45..f8b08d11 100644 --- a/core/src/nodes/handled_node.rs +++ b/core/src/nodes/handled_node.rs @@ -20,10 +20,7 @@ use crate::{PeerId, muxing::StreamMuxer}; use crate::nodes::node::{NodeEvent, NodeStream, Substream, Close}; -use futures::prelude::*; -use std::{error, fmt, io}; - -mod tests; +use std::{error, fmt, io, pin::Pin, task::Context, task::Poll}; /// Handler for the substreams of a node. // TODO: right now it is possible for a node handler to be built, then shut down right after if we @@ -59,7 +56,8 @@ pub trait NodeHandler { /// Should behave like `Stream::poll()`. /// /// Returning an error will close the connection to the remote. - fn poll(&mut self) -> Poll, Self::Error>; + fn poll(&mut self, cx: &mut Context) + -> Poll, Self::Error>>; } /// Prototype for a `NodeHandler`. @@ -172,6 +170,13 @@ where } } +impl Unpin for HandledNode +where + TMuxer: StreamMuxer, + THandler: NodeHandler>, +{ +} + impl HandledNode where TMuxer: StreamMuxer, @@ -214,37 +219,41 @@ where } /// API similar to `Future::poll` that polls the node for events. - pub fn poll(&mut self) -> Poll> { + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) + -> Poll>> + { loop { let mut node_not_ready = false; - match self.node.poll().map_err(HandledNodeError::Node)? { - Async::NotReady => node_not_ready = true, - Async::Ready(NodeEvent::InboundSubstream { substream }) => { + match self.node.poll(cx) { + Poll::Pending => node_not_ready = true, + Poll::Ready(Ok(NodeEvent::InboundSubstream { substream })) => { self.handler.inject_substream(substream, NodeHandlerEndpoint::Listener) } - Async::Ready(NodeEvent::OutboundSubstream { user_data, substream }) => { + Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream })) => { let endpoint = NodeHandlerEndpoint::Dialer(user_data); self.handler.inject_substream(substream, endpoint) } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Node(err))), } - match self.handler.poll().map_err(HandledNodeError::Handler)? { - Async::NotReady => { + match self.handler.poll(cx) { + Poll::Pending => { if node_not_ready { break } } - Async::Ready(NodeHandlerEvent::OutboundSubstreamRequest(user_data)) => { + Poll::Ready(Ok(NodeHandlerEvent::OutboundSubstreamRequest(user_data))) => { self.node.open_substream(user_data); } - Async::Ready(NodeHandlerEvent::Custom(event)) => { - return Ok(Async::Ready(event)); + Poll::Ready(Ok(NodeHandlerEvent::Custom(event))) => { + return Poll::Ready(Ok(event)); } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Handler(err))), } } - Ok(Async::NotReady) + Poll::Pending } } diff --git a/core/src/nodes/handled_node/tests.rs b/core/src/nodes/handled_node/tests.rs deleted file mode 100644 index ee138c2e..00000000 --- a/core/src/nodes/handled_node/tests.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent, TestHandledNode}; - -struct TestBuilder { - muxer: DummyMuxer, - handler: Handler, - want_open_substream: bool, - substream_user_data: usize, -} - -impl TestBuilder { - fn new() -> Self { - TestBuilder { - muxer: DummyMuxer::new(), - handler: Handler::default(), - want_open_substream: false, - substream_user_data: 0, - } - } - - fn with_muxer_inbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_inbound_connection_state(state); - self - } - - fn with_muxer_outbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_outbound_connection_state(state); - self - } - - fn with_handler_state(&mut self, state: HandlerState) -> &mut Self { - self.handler.state = Some(state); - self - } - - fn with_open_substream(&mut self, user_data: usize) -> &mut Self { - self.want_open_substream = true; - self.substream_user_data = user_data; - self - } - - fn handled_node(&mut self) -> TestHandledNode { - let mut h = HandledNode::new(self.muxer.clone(), self.handler.clone()); - if self.want_open_substream { - h.node.open_substream(self.substream_user_data); - } - h - } -} - -// Set the state of the `Handler` after `inject_outbound_closed` is called -fn set_next_handler_outbound_state( handled_node: &mut TestHandledNode, next_state: HandlerState) { - handled_node.handler.next_outbound_state = Some(next_state); -} - -#[test] -fn can_inject_event() { - let mut handled = TestBuilder::new() - .handled_node(); - - let event = InEvent::Custom("banana"); - handled.inject_event(event.clone()); - assert_eq!(handled.handler().events, vec![event]); -} - -#[test] -fn poll_with_unready_node_stream_and_handler_emits_custom_event() { - let expected_event = NodeHandlerEvent::Custom(OutEvent::Custom("pineapple")); - let mut handled = TestBuilder::new() - // make NodeStream return NotReady - .with_muxer_inbound_state(DummyConnectionState::Pending) - // make Handler return return Ready(Some(…)) - .with_handler_state(HandlerState::Ready(expected_event)) - .handled_node(); - - assert_matches!(handled.poll(), Ok(Async::Ready(event)) => { - assert_matches!(event, OutEvent::Custom("pineapple")) - }); -} - -#[test] -fn handler_emits_outbound_closed_when_opening_new_substream_on_closed_node() { - let open_event = NodeHandlerEvent::OutboundSubstreamRequest(456); - let mut handled = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Ready(open_event)) - .handled_node(); - - set_next_handler_outbound_state( - &mut handled, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("pear"))) - ); - handled.poll().expect("poll works"); -} - -#[test] -fn poll_yields_inbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_open_substream(32) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Opened) - .with_open_substream(1) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(Some(1))]); -} - -#[test] -fn poll_yields_inbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Opened) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(None)]); -} diff --git a/core/src/nodes/listeners.rs b/core/src/nodes/listeners.rs index effcea65..b9c8ebbf 100644 --- a/core/src/nodes/listeners.rs +++ b/core/src/nodes/listeners.rs @@ -21,11 +21,10 @@ //! Manage listening on multiple multiaddresses at once. use crate::{Multiaddr, Transport, transport::{TransportError, ListenerEvent}}; -use futures::prelude::*; +use futures::{prelude::*, task::Context, task::Poll}; use log::debug; use smallvec::SmallVec; -use std::{collections::VecDeque, fmt}; -use void::Void; +use std::{collections::VecDeque, fmt, pin::Pin}; /// Implementation of `futures::Stream` that allows listening on multiaddresses. /// @@ -158,7 +157,7 @@ where /// The ID of the listener that errored. listener_id: ListenerId, /// The error value. - error: ::Error + error: ::Error } } @@ -222,28 +221,31 @@ where self.listeners.iter().flat_map(|l| l.addresses.iter()) } - /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> { + /// Provides an API similar to `Stream`, except that it cannot end. + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> + where + TTrans::Listener: Unpin, + { // We remove each element from `listeners` one by one and add them back. let mut remaining = self.listeners.len(); while let Some(mut listener) = self.listeners.pop_back() { - match listener.listener.poll() { - Ok(Async::NotReady) => { + match TryStream::try_poll_next(Pin::new(&mut listener.listener), cx) { + Poll::Pending => { self.listeners.push_front(listener); remaining -= 1; if remaining == 0 { break } } - Ok(Async::Ready(Some(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { + Poll::Ready(Some(Ok(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::Incoming { + return Poll::Ready(ListenersEvent::Incoming { listener_id: id, upgrade, local_addr, send_back_addr: remote_addr }) } - Ok(Async::Ready(Some(ListenerEvent::NewAddress(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::NewAddress(a)))) => { if listener.addresses.contains(&a) { debug!("Transport has reported address {} multiple times", a) } @@ -252,28 +254,28 @@ where } let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::NewAddress { + return Poll::Ready(ListenersEvent::NewAddress { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(Some(ListenerEvent::AddressExpired(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::AddressExpired(a)))) => { listener.addresses.retain(|x| x != &a); let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::AddressExpired { + return Poll::Ready(ListenersEvent::AddressExpired { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(None)) => { - return Async::Ready(ListenersEvent::Closed { + Poll::Ready(None) => { + return Poll::Ready(ListenersEvent::Closed { listener_id: listener.id, listener: listener.listener }) } - Err(err) => { - return Async::Ready(ListenersEvent::Error { + Poll::Ready(Some(Err(err))) => { + return Poll::Ready(ListenersEvent::Error { listener_id: listener.id, error: err }) @@ -282,22 +284,28 @@ where } // We register the current task to be woken up if a new listener is added. - Async::NotReady + Poll::Pending } } impl Stream for ListenersStream where TTrans: Transport, + TTrans::Listener: Unpin, { type Item = ListenersEvent; - type Error = Void; // TODO: use ! once stable - fn poll(&mut self) -> Poll, Self::Error> { - Ok(self.poll().map(Option::Some)) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ListenersStream::poll(self, cx).map(Option::Some) } } +impl Unpin for ListenersStream +where + TTrans: Transport, +{ +} + impl fmt::Debug for ListenersStream where TTrans: Transport + fmt::Debug, @@ -313,7 +321,7 @@ where impl fmt::Debug for ListenersEvent where TTrans: Transport, - ::Error: fmt::Debug, + ::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { @@ -353,215 +361,37 @@ mod tests { use tokio::runtime::current_thread::Runtime; use std::{io, iter::FromIterator}; use futures::{future::{self}, stream}; - use crate::tests::dummy_transport::{DummyTransport, ListenerState}; - use crate::tests::dummy_muxer::DummyMuxer; use crate::PeerId; - fn set_listener_state(ls: &mut ListenersStream, idx: usize, state: ListenerState) { - ls.listeners[idx].listener = match state { - ListenerState::Error => - Box::new(stream::poll_fn(|| Err(io::Error::new(io::ErrorKind::Other, "oh noes")))), - ListenerState::Ok(state) => match state { - Async::NotReady => Box::new(stream::poll_fn(|| Ok(Async::NotReady))), - Async::Ready(Some(event)) => Box::new(stream::poll_fn(move || { - Ok(Async::Ready(Some(event.clone().map(future::ok)))) - })), - Async::Ready(None) => Box::new(stream::empty()) - } - ListenerState::Events(events) => - Box::new(stream::iter_ok(events.into_iter().map(|e| e.map(future::ok)))) - }; - } - #[test] fn incoming_event() { - let mem_transport = transport::MemoryTransport::default(); + futures::executor::block_on(async move { + let mem_transport = transport::MemoryTransport::default(); - let mut listeners = ListenersStream::new(mem_transport); - listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); + let mut listeners = ListenersStream::new(mem_transport); + listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); - let address = { - let event = listeners.by_ref().wait().next().expect("some event").expect("no error"); - if let ListenersEvent::NewAddress { listen_addr, .. } = event { - listen_addr - } else { - panic!("Was expecting the listen address to be reported") - } - }; - - let dial = mem_transport.dial(address.clone()).unwrap(); - - let future = listeners - .into_future() - .map_err(|(err, _)| err) - .and_then(|(event, _)| { - match event { - Some(ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. }) => { - assert_eq!(local_addr, address); - assert_eq!(send_back_addr, address); - upgrade.map(|_| ()).map_err(|_| panic!()) - }, - _ => panic!() + let address = { + let event = listeners.next().await.unwrap(); + if let ListenersEvent::NewAddress { listen_addr, .. } = event { + listen_addr + } else { + panic!("Was expecting the listen address to be reported") } - }) - .select(dial.map(|_| ()).map_err(|_| panic!())) - .map_err(|(err, _)| err); + }; - let mut runtime = Runtime::new().unwrap(); - runtime.block_on(future).unwrap(); - } + let address2 = address.clone(); + async_std::task::spawn(async move { + mem_transport.dial(address2).unwrap().await.unwrap(); + }); - #[test] - fn listener_stream_returns_transport() { - let t = DummyTransport::new(); - let t_clone = t.clone(); - let ls = ListenersStream::new(t); - assert_eq!(ls.transport(), &t_clone); - } - - #[test] - fn listener_stream_can_iterate_over_listeners() { - let mut t = DummyTransport::new(); - let addr1 = tcp4([127, 0, 0, 1], 1234); - let addr2 = tcp4([127, 0, 0, 1], 4321); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(addr1.clone()), - ListenerEvent::NewAddress(addr2.clone()) - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([0, 0, 0, 0], 0)).expect("listen_on"); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr1, listen_addr) - }); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr2, listen_addr) - }) - } - - #[test] - fn listener_stream_poll_without_listeners_is_not_ready() { - let t = DummyTransport::new(); - let mut ls = ListenersStream::new(t); - assert_matches!(ls.poll(), Async::NotReady); - } - - #[test] - fn listener_stream_poll_with_listeners_that_arent_ready_is_not_ready() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::NotReady)); - assert_matches!(ls.poll(), Async::NotReady); - assert_eq!(ls.listeners.len(), 1); // listener is still there - } - - #[test] - fn listener_stream_poll_with_ready_listeners_is_ready() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let expected_output = (peer_id.clone(), muxer.clone()); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(tcp4([127, 0, 0, 1], 9090)), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) + match listeners.next().await.unwrap() { + ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. } => { + assert_eq!(local_addr, address); + assert_eq!(send_back_addr, address); + }, + _ => panic!() } - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([127, 0, 0, 1], 1234)).expect("listen_on"); - ls.listen_on(tcp4([127, 0, 0, 1], 4321)).expect("listen_on"); - assert_eq!(ls.listeners.len(), 2); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - set_listener_state(&mut ls, 1, ListenerState::Ok(Async::NotReady)); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - } - - #[test] - fn listener_stream_poll_with_closed_listener_emits_closed_event() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::Ready(None))); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Closed{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - #[test] - fn listener_stream_poll_with_erroring_listener_emits_error_event() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let event = ListenerEvent::Upgrade { - upgrade: (peer_id, muxer), - local_addr: tcp4([127, 0, 0, 1], 1234), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }; - t.set_initial_listener_state(ListenerState::Ok(Async::Ready(Some(event)))); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Error); // simulate an error on the socket - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Error{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - fn tcp4(ip: [u8; 4], port: u16) -> Multiaddr { - let protos = std::iter::once(multiaddr::Protocol::Ip4(ip.into())) - .chain(std::iter::once(multiaddr::Protocol::Tcp(port))); - Multiaddr::from_iter(protos) } } diff --git a/core/src/nodes/network.rs b/core/src/nodes/network.rs index abe9e631..2f6634a1 100644 --- a/core/src/nodes/network.rs +++ b/core/src/nodes/network.rs @@ -49,10 +49,10 @@ use std::{ fmt, hash::Hash, num::NonZeroUsize, + pin::Pin, + task::{Context, Poll}, }; -pub use crate::nodes::collection::StartTakeOver; - mod tests; /// Implementation of `Stream` that handles the nodes. @@ -81,7 +81,7 @@ where /// If the pair's second element is `AsyncSink::Ready`, the take over /// message has been sent and needs to be flushed using /// `PeerMut::complete_take_over`. - take_over_to_complete: Option<(TPeerId, AsyncSink>)> + take_over_to_complete: Option<(TPeerId, InterruptedReachAttempt)> } impl fmt::Debug for @@ -102,6 +102,13 @@ where } } +impl Unpin for + Network +where + TTrans: Transport +{ +} + impl ConnectionInfo for (TConnInfo, ConnectedPoint) where TConnInfo: ConnectionInfo @@ -173,7 +180,7 @@ where /// The listener that errored. listener_id: ListenerId, /// The listener error. - error: ::Error + error: ::Error }, /// One of the listeners is now listening on an additional address. @@ -573,7 +580,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::ListenerUpgrade: Send + 'static, + TTrans::ListenerUpgrade: Unpin + Send + 'static, THandler: IntoNodeHandler<(TConnInfo, ConnectedPoint)> + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, // TODO: shouldn't be necessary @@ -609,9 +616,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -781,7 +788,7 @@ where where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TInEvent: Send + 'static, @@ -797,9 +804,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -840,19 +847,18 @@ where /// Start sending an event to all nodes. /// - /// Make sure to complete the broadcast with `complete_broadcast`. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.active_nodes.start_broadcast(event) } - /// Complete a broadcast initiated with `start_broadcast`. + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.active_nodes.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.active_nodes.poll_ready_broadcast(cx) } /// Returns a list of all the peers we are currently connected to. @@ -934,7 +940,7 @@ where fn start_dial_out(&mut self, peer_id: TPeerId, handler: THandler, first: Multiaddr, rest: Vec) where TTrans: Transport, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TTrans::Error: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -950,9 +956,9 @@ where .map_err(|err| InternalReachErr::Transport(TransportError::Other(err))) .and_then(move |(actual_conn_info, muxer)| { if *actual_conn_info.peer_id() == expected_peer_id { - Ok(((actual_conn_info, connected_point), muxer)) + future::ready(Ok(((actual_conn_info, connected_point), muxer))) } else { - Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info }) + future::ready(Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info })) } }); self.active_nodes.add_reach_attempt(fut, handler) @@ -976,11 +982,12 @@ where } /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> + pub fn poll<'a>(&'a mut self, cx: &mut Context) -> Poll> where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, + TTrans::Listener: Unpin, TTrans::ListenerUpgrade: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -998,9 +1005,9 @@ where Some(x) if self.incoming_negotiated().count() >= (x as usize) => (), _ => { - match self.listeners.poll() { - Async::NotReady => (), - Async::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { + match ListenersStream::poll(Pin::new(&mut self.listeners), cx) { + Poll::Pending => (), + Poll::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { let event = IncomingConnectionEvent { listener_id, upgrade, @@ -1010,19 +1017,19 @@ where active_nodes: &mut self.active_nodes, other_reach_attempts: &mut self.reach_attempts.other_reach_attempts, }; - return Async::Ready(NetworkEvent::IncomingConnection(event)); + return Poll::Ready(NetworkEvent::IncomingConnection(event)); } - Async::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::Closed { listener_id, listener }) => { - return Async::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) + Poll::Ready(ListenersEvent::Closed { listener_id, listener }) => { + return Poll::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) } - Async::Ready(ListenersEvent::Error { listener_id, error }) => { - return Async::Ready(NetworkEvent::ListenerError { listener_id, error }) + Poll::Ready(ListenersEvent::Error { listener_id, error }) => { + return Poll::Ready(NetworkEvent::ListenerError { listener_id, error }) } } } @@ -1031,36 +1038,30 @@ where // Attempt to deliver any pending take over messages. if let Some((id, interrupted)) = self.take_over_to_complete.take() { if let Some(mut peer) = self.active_nodes.peer_mut(&id) { - if let AsyncSink::NotReady(i) = interrupted { - if let StartTakeOver::NotReady(i) = peer.start_take_over(i) { - self.take_over_to_complete = Some((id, AsyncSink::NotReady(i))) - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) - } - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((id, interrupted)); + return Poll::Pending; } } } - if self.take_over_to_complete.is_some() { - return Async::NotReady - } // Poll the existing nodes. let (action, out_event); - match self.active_nodes.poll() { - Async::NotReady => return Async::NotReady, - Async::Ready(CollectionEvent::NodeReached(reach_event)) => { + match self.active_nodes.poll(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(CollectionEvent::NodeReached(reach_event)) => { let (a, e) = handle_node_reached(&mut self.reach_attempts, reach_event); action = a; out_event = e; } - Async::Ready(CollectionEvent::ReachError { id, error, handler }) => { + Poll::Ready(CollectionEvent::ReachError { id, error, handler }) => { let (a, e) = handle_reach_error(&mut self.reach_attempts, id, error, handler); action = a; out_event = e; } - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error, .. @@ -1078,7 +1079,7 @@ where error, }; } - Async::Ready(CollectionEvent::NodeEvent { peer, event }) => { + Poll::Ready(CollectionEvent::NodeEvent { peer, event }) => { action = Default::default(); out_event = NetworkEvent::NodeEvent { conn_info: peer.info().0.clone(), event }; } @@ -1099,17 +1100,15 @@ where out_reach_attempts should always be in sync with the actual \ attempts; QED"); let mut peer = self.active_nodes.peer_mut(&peer_id).unwrap(); - if let StartTakeOver::NotReady(i) = peer.start_take_over(interrupted) { - self.take_over_to_complete = Some((peer_id, AsyncSink::NotReady(i))); - return Async::NotReady - } - if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((peer_id, AsyncSink::Ready)); - return Async::NotReady + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((peer_id, interrupted)); + return Poll::Pending } } - Async::Ready(out_event) + Poll::Ready(out_event) } } @@ -1467,7 +1466,7 @@ impl<'a, TTrans, TMuxer, TInEvent, TOutEvent, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, @@ -1644,18 +1643,33 @@ where closed messages; QED") } - /// Start sending an event to the node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Sends an event to the handler of the node. + pub fn send_event<'s: 'a>(&'s mut self, event: TInEvent) -> impl Future + 's + 'a { + let mut event = Some(event); + futures::future::poll_fn(move |cx| { + match self.poll_ready_event(cx) { + Poll::Ready(()) => { + self.start_send_event(event.take().expect("Future called after finished")); + Poll::Ready(()) + }, + Poll::Pending => Poll::Pending, + } + }) + } + + /// Begin sending an event to the node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") .start_send_event(event) } - /// Complete sending an event message, initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") - .complete_send_event() + .poll_ready_event(cx) } } @@ -1749,7 +1763,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, diff --git a/core/src/nodes/network/tests.rs b/core/src/nodes/network/tests.rs index c64666aa..c4f307bb 100644 --- a/core/src/nodes/network/tests.rs +++ b/core/src/nodes/network/tests.rs @@ -21,363 +21,6 @@ #![cfg(test)] use super::*; -use crate::tests::dummy_transport::DummyTransport; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent}; -use crate::tests::dummy_transport::ListenerState; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::nodes::NodeHandlerEvent; -use crate::transport::ListenerEvent; -use assert_matches::assert_matches; -use parking_lot::Mutex; -use std::sync::Arc; -use tokio::runtime::{Builder, Runtime}; - -#[test] -fn query_transport() { - let transport = DummyTransport::new(); - let transport2 = transport.clone(); - let network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - assert_eq!(network.transport(), &transport2); -} - -#[test] -fn local_node_peer() { - let peer_id = PeerId::random(); - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), peer_id.clone()); - assert_matches!(network.peer(peer_id), Peer::LocalNode); -} - -#[test] -fn successful_dial_reaches_a_node() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let dial_res = network.dial(addr, Handler::default()); - assert!(dial_res.is_ok()); - - // Poll the network until we get a `NodeReached` then assert on the peer: - // it's there and it's connected. - let network = Arc::new(Mutex::new(network)); - - let mut rt = Runtime::new().unwrap(); - let mut peer_id : Option = None; - // Drive forward until we're Connected - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected(PeerConnected{..})); -} - -#[test] -fn num_incoming_negotiated() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - - let events = vec![ - ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap()), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ]; - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - // no incoming yet - assert_eq!(network.incoming_negotiated().count(), 0); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - // Now there's an incoming connection - assert_eq!(network.incoming_negotiated().count(), 1); -} - -#[test] -fn broadcasted_events_reach_active_nodes() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )),]; - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::NodeEvent { conn_info: _, event: inner_event } => { - // The event we sent reached the node and triggered sending the out event we told it to return - assert_matches!(inner_event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(false)) - }, - _ => Ok(Async::Ready(true)) - } - })).expect("tokio works"); - } -} - -#[test] -fn querying_for_pending_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); -} - -#[test] -fn querying_for_unknown_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected( PeerNotConnected { nodes: _, peer_id: node_peer_id }) => { - assert_eq!(node_peer_id, peer_id); - }); -} - -#[test] -fn querying_for_connected_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - - // Dial a node - let addr = "/ip4/127.0.0.1/tcp/1234".parse().expect("bad multiaddr"); - network.dial(addr, Handler::default()).expect("dialing works"); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we connect; extract the new PeerId. - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - // We're connected. - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected( PeerConnected { .. } )); -} - -#[test] -fn poll_with_closed_listener() { - let mut transport = DummyTransport::new(); - // Set up listener to be closed - transport.set_initial_listener_state(ListenerState::Ok(Async::Ready(None))); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::ListenerClosed { .. } )); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); -} - -#[test] -fn unknown_peer_that_is_unreachable_yields_unknown_peer_dial_error() { - let mut transport = DummyTransport::new(); - transport.make_dial_fail(); - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let handler = Handler::default(); - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::UnknownPeerDialError { .. } ); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn known_peer_that_is_unreachable_yields_dial_error() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - transport.make_dial_fail(); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); - } - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let peer_id = peer_id.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - let failed_peer_id = assert_matches!( - event, - NetworkEvent::DialError { new_state: _, peer_id: failed_peer_id, .. } => failed_peer_id - ); - assert_eq!(peer_id, failed_peer_id); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn yields_node_error_when_there_is_an_error_after_successful_connect() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - // Set up an outgoing connection with a PeerId we know - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - let addr = "/unix/reachable".parse().expect("bad multiaddr"); - let mut handler = Handler::default(); - // Force an error - handler.next_states = vec![ HandlerState::Err ]; - peer.into_not_connected().unwrap().connect(addr, handler); - } - - // Ensure we run on a single thread - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Drive it forward until we connect to the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - // Push the Handler into an error state on the next poll - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::Connected { .. }); - // We're connected, we can move on - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } - - // Poll again. It is going to be a NodeClosed because of how the - // handler's next state was set up. - let network_fut = network.clone(); - let expected_peer_id = peer_id.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::NodeClosed { conn_info, .. }) => { - assert_eq!(conn_info, expected_peer_id); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} #[test] fn local_prio_equivalence_relation() { @@ -387,59 +30,3 @@ fn local_prio_equivalence_relation() { assert_ne!(has_dial_prio(&a, &b), has_dial_prio(&b, &a)); } } - -#[test] -fn limit_incoming_connections() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let limit = 1; - - let mut events = vec![ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap())]; - events.extend(std::iter::repeat( - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ).take(10)); - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new_with_incoming_limit(transport, PeerId::random(), Some(limit)); - assert_eq!(network.incoming_limit(), Some(limit)); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - assert_eq!(network.incoming_negotiated().count(), 0); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - for i in 1..10 { - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - if i <= limit { - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), - Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - } else { - match network_fut.poll() { - Async::NotReady => (), - Async::Ready(x) => { - match x { - NetworkEvent::NewListenerAddress {..} => {} - NetworkEvent::ExpiredListenerAddress {..} => {} - NetworkEvent::IncomingConnection(_) => {} - NetworkEvent::Connected {..} => {} - e => panic!("Not expected event: {:?}", e) - } - }, - } - } - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - assert!(network.incoming_negotiated().count() <= (limit as usize)); - } -} diff --git a/core/src/nodes/node.rs b/core/src/nodes/node.rs index a1d0eac4..37da9954 100644 --- a/core/src/nodes/node.rs +++ b/core/src/nodes/node.rs @@ -21,9 +21,7 @@ use futures::prelude::*; use crate::muxing; use smallvec::SmallVec; -use std::fmt; -use std::io::Error as IoError; -use std::sync::Arc; +use std::{fmt, io::Error as IoError, pin::Pin, sync::Arc, task::Context, task::Poll}; // Implementation notes // ================= @@ -143,43 +141,44 @@ where } /// Provides an API similar to `Future`. - pub fn poll(&mut self) -> Poll, IoError> { + pub fn poll(&mut self, cx: &mut Context) -> Poll, IoError>> { // Polling inbound substream. - match self.muxer.poll_inbound().map_err(|e| e.into())? { - Async::Ready(substream) => { + match self.muxer.poll_inbound(cx) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); - return Ok(Async::Ready(NodeEvent::InboundSubstream { + return Poll::Ready(Ok(NodeEvent::InboundSubstream { substream, })); } - Async::NotReady => {} + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => {} } // Polling outbound substreams. // We remove each element from `outbound_substreams` one by one and add them back. for n in (0..self.outbound_substreams.len()).rev() { let (user_data, mut outbound) = self.outbound_substreams.swap_remove(n); - match self.muxer.poll_outbound(&mut outbound) { - Ok(Async::Ready(substream)) => { + match self.muxer.poll_outbound(cx, &mut outbound) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); self.muxer.destroy_outbound(outbound); - return Ok(Async::Ready(NodeEvent::OutboundSubstream { + return Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream, })); } - Ok(Async::NotReady) => { + Poll::Pending => { self.outbound_substreams.push((user_data, outbound)); } - Err(err) => { + Poll::Ready(Err(err)) => { self.muxer.destroy_outbound(outbound); - return Err(err.into()); + return Poll::Ready(Err(err.into())); } } } // Nothing happened. Register our task to be notified and return. - Ok(Async::NotReady) + Poll::Pending } } @@ -212,11 +211,14 @@ impl Future for Close where TMuxer: muxing::StreamMuxer, { - type Item = (); - type Error = IoError; + type Output = Result<(), IoError>; - fn poll(&mut self) -> Poll { - self.muxer.close().map_err(|e| e.into()) + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match self.muxer.close(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(err)) => Poll::Ready(Err(err.into())), + } } } @@ -252,70 +254,3 @@ where } } } - -#[cfg(test)] -mod node_stream { - use super::{NodeEvent, NodeStream}; - use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; - use assert_matches::assert_matches; - use futures::prelude::*; - use tokio_mock_task::MockTask; - - fn build_node_stream() -> NodeStream> { - let muxer = DummyMuxer::new(); - NodeStream::<_, Vec>::new(muxer) - } - - #[test] - fn closing_a_node_stream_destroys_substreams_and_returns_submitted_user_data() { - let mut ns = build_node_stream(); - ns.open_substream(vec![2]); - ns.open_substream(vec![3]); - ns.open_substream(vec![5]); - let user_data_submitted = ns.close(); - assert_eq!(user_data_submitted.1, vec![ - vec![2], vec![3], vec![5] - ]); - } - - #[test] - fn poll_returns_not_ready_when_there_is_nothing_to_do() { - let mut task = MockTask::new(); - task.enter(|| { - // ensure the address never resolves - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - - assert_matches!(ns.poll(), Ok(Async::NotReady)); - }); - } - - #[test] - fn poll_keeps_outbound_substreams_when_the_outgoing_connection_is_not_ready() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - ns.open_substream(vec![1]); - ns.poll().unwrap(); // poll past inbound - ns.poll().unwrap(); // poll outbound - assert!(format!("{:?}", ns).contains("outbound_substreams: 1")); - } - - #[test] - fn poll_returns_incoming_substream() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::Ready(subs) - muxer.set_inbound_connection_state(DummyConnectionState::Opened); - let mut ns = NodeStream::<_, Vec>::new(muxer); - assert_matches!(ns.poll(), Ok(Async::Ready(node_event)) => { - assert_matches!(node_event, NodeEvent::InboundSubstream{ substream: _ }); - }); - } -} diff --git a/core/src/nodes/tasks/manager.rs b/core/src/nodes/tasks/manager.rs index 33643aaa..aff72bd9 100644 --- a/core/src/nodes/tasks/manager.rs +++ b/core/src/nodes/tasks/manager.rs @@ -27,9 +27,8 @@ use crate::{ } }; use fnv::FnvHashMap; -use futures::{prelude::*, future::Executor, sync::mpsc}; -use smallvec::SmallVec; -use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt}; +use futures::{prelude::*, channel::mpsc, executor::ThreadPool, stream::FuturesUnordered, task::SpawnExt as _}; +use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt, pin::Pin, task::Context, task::Poll}; use super::{TaskId, task::{Task, FromTaskMessage, ToTaskMessage}, Error}; // Implementor notes @@ -64,12 +63,13 @@ pub struct Manager { /// Identifier for the next task to spawn. next_task_id: TaskId, - /// List of node tasks to spawn. - to_spawn: SmallVec<[Box + Send>; 8]>, + /// Threads pool where we spawn the nodes' tasks. If `None`, then we push tasks to the + /// `local_spawns` list instead. + threads_pool: Option, - /// If no tokio executor is available, we move tasks to this list, and futures are polled on - /// the current thread instead. - local_spawns: Vec + Send>>, + /// If no executor is available, we move tasks to this list, and futures are polled on the + /// current thread instead. + local_spawns: FuturesUnordered + Send>>>, /// Sender to emit events to the outside. Meant to be cloned and sent to tasks. events_tx: mpsc::Sender<(FromTaskMessage, TaskId)>, @@ -91,16 +91,13 @@ where /// Information about a running task. /// -/// Contains the sender to deliver event messages to the task, -/// the associated user data and a pending message if any, -/// meant to be delivered to the task via the sender. +/// Contains the sender to deliver event messages to the task, and +/// the associated user data. struct TaskInfo
AsyncRead for SubstreamRef
@@ -391,37 +381,17 @@ where P: Deref, P::Target: StreamMuxer, { - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.muxer.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.muxer.initializer() } - fn poll_read(&mut self, buf: &mut [u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.read_substream(s, buf).map_err(|e| e.into()) - } -} + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; -impl Write for SubstreamRef -where - P: Deref, - P::Target: StreamMuxer, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.write_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } - - #[inline] - fn flush(&mut self) -> Result<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.flush_substream(s).map_err(|e| e.into())? { - Async::Ready(()) => Ok(()), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.read_substream(cx, s, buf).map_err(|e| e.into()) } } @@ -430,36 +400,51 @@ where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn poll_write(&mut self, buf: &[u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.write_substream(s, buf).map_err(|e| e.into()) + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.write_substream(cx, s, buf).map_err(|e| e.into()) } - #[inline] - fn shutdown(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); loop { - match self.shutdown_state { + match this.shutdown_state { ShutdownState::Shutdown => { - try_ready!(self.muxer.shutdown_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Flush; + match this.muxer.shutdown_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Flush, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Flush => { - try_ready!(self.muxer.flush_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Done; + match this.muxer.flush_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Done, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Done => { - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } } } } - #[inline] - fn poll_flush(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.flush_substream(s).map_err(|e| e.into()) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.flush_substream(cx, s).map_err(|e| e.into()) } } @@ -507,8 +492,8 @@ impl StreamMuxer for StreamMuxerBox { type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - self.inner.poll_inbound() + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + self.inner.poll_inbound(cx) } #[inline] @@ -517,8 +502,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn poll_outbound(&self, s: &mut Self::OutboundSubstream) -> Poll { - self.inner.poll_outbound(s) + fn poll_outbound(&self, cx: &mut Context, s: &mut Self::OutboundSubstream) -> Poll> { + self.inner.poll_outbound(cx, s) } #[inline] @@ -526,28 +511,28 @@ impl StreamMuxer for StreamMuxerBox { self.inner.destroy_outbound(substream) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { - self.inner.read_substream(s, buf) + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + self.inner.read_substream(cx, s, buf) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.write_substream(s, buf) + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { + self.inner.write_substream(cx, s, buf) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.flush_substream(s) + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.flush_substream(cx, s) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.shutdown_substream(s) + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.shutdown_substream(cx, s) } #[inline] @@ -556,8 +541,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close() + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx) } #[inline] @@ -566,8 +551,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all() + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx) } } @@ -588,11 +573,16 @@ where type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - let substream = try_ready!(self.inner.poll_inbound().map_err(|e| e.into())); + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + let substream = match self.inner.poll_inbound(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; + let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -606,13 +596,18 @@ where #[inline] fn poll_outbound( &self, + cx: &mut Context, substream: &mut Self::OutboundSubstream, - ) -> Poll { + ) -> Poll> { let mut list = self.outbound.lock(); - let substream = try_ready!(self.inner.poll_outbound(list.get_mut(substream).unwrap()).map_err(|e| e.into())); + let substream = match self.inner.poll_outbound(cx, list.get_mut(substream).unwrap()) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -621,32 +616,32 @@ where self.inner.destroy_outbound(list.remove(&substream).unwrap()) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.read_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.read_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.write_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.write_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.flush_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.flush_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.shutdown_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.shutdown_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] @@ -656,8 +651,8 @@ where } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close().map_err(|e| e.into()) + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx).map_err(|e| e.into()) } #[inline] @@ -666,7 +661,7 @@ where } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all().map_err(|e| e.into()) + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx).map_err(|e| e.into()) } } diff --git a/core/src/muxing/singleton.rs b/core/src/muxing/singleton.rs index 7bec14ed..f85e22fd 100644 --- a/core/src/muxing/singleton.rs +++ b/core/src/muxing/singleton.rs @@ -19,10 +19,9 @@ // DEALINGS IN THE SOFTWARE. use crate::{Endpoint, muxing::StreamMuxer}; -use futures::prelude::*; +use futures::{prelude::*, io::Initializer}; use parking_lot::Mutex; -use std::{io, sync::atomic::{AtomicBool, Ordering}}; -use tokio_io::{AsyncRead, AsyncWrite}; +use std::{io, pin::Pin, sync::atomic::{AtomicBool, Ordering}, task::Context, task::Poll}; /// Implementation of `StreamMuxer` that allows only one substream on top of a connection, /// yielding the connection itself. @@ -62,22 +61,22 @@ pub struct OutboundSubstream {} impl StreamMuxer for SingletonMuxer where - TSocket: AsyncRead + AsyncWrite, + TSocket: AsyncRead + AsyncWrite + Unpin, { type Substream = Substream; type OutboundSubstream = OutboundSubstream; type Error = io::Error; - fn poll_inbound(&self) -> Poll { + fn poll_inbound(&self, _: &mut Context) -> Poll> { match self.endpoint { - Endpoint::Dialer => return Ok(Async::NotReady), + Endpoint::Dialer => return Poll::Pending, Endpoint::Listener => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -85,44 +84,44 @@ where OutboundSubstream {} } - fn poll_outbound(&self, _: &mut Self::OutboundSubstream) -> Poll { + fn poll_outbound(&self, _: &mut Context, _: &mut Self::OutboundSubstream) -> Poll> { match self.endpoint { - Endpoint::Listener => return Ok(Async::NotReady), + Endpoint::Listener => return Poll::Pending, Endpoint::Dialer => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } fn destroy_outbound(&self, _: Self::OutboundSubstream) { } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.lock().prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.lock().initializer() } - fn read_substream(&self, _: &mut Self::Substream, buf: &mut [u8]) -> Poll { - let res = self.inner.lock().poll_read(buf); - if let Ok(Async::Ready(_)) = res { + fn read_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + let res = AsyncRead::poll_read(Pin::new(&mut *self.inner.lock()), cx, buf); + if let Poll::Ready(Ok(_)) = res { self.remote_acknowledged.store(true, Ordering::Release); } res } - fn write_substream(&self, _: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.lock().poll_write(buf) + fn write_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &[u8]) -> Poll> { + AsyncWrite::poll_write(Pin::new(&mut *self.inner.lock()), cx, buf) } - fn flush_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } - fn shutdown_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().shutdown() + fn shutdown_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_close(Pin::new(&mut *self.inner.lock()), cx) } fn destroy_substream(&self, _: Self::Substream) { @@ -132,12 +131,12 @@ where self.remote_acknowledged.load(Ordering::Acquire) } - fn close(&self) -> Poll<(), io::Error> { + fn close(&self, cx: &mut Context) -> Poll> { // The `StreamMuxer` trait requires that `close()` implies `flush_all()`. - self.flush_all() + self.flush_all(cx) } - fn flush_all(&self) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_all(&self, cx: &mut Context) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } } diff --git a/core/src/nodes/collection.rs b/core/src/nodes/collection.rs index af8601d2..9e212810 100644 --- a/core/src/nodes/collection.rs +++ b/core/src/nodes/collection.rs @@ -29,11 +29,7 @@ use crate::{ }; use fnv::FnvHashMap; use futures::prelude::*; -use std::{error, fmt, hash::Hash, mem}; - -pub use crate::nodes::tasks::StartTakeOver; - -mod tests; +use std::{error, fmt, hash::Hash, mem, task::Context, task::Poll}; /// Implementation of `Stream` that handles a collection of nodes. pub struct CollectionStream { @@ -58,6 +54,9 @@ where } } +impl Unpin for + CollectionStream { } + /// State of a task. #[derive(Debug, Clone, PartialEq, Eq)] enum TaskState { @@ -323,7 +322,7 @@ where pub fn add_reach_attempt(&mut self, future: TFut, handler: THandler) -> ReachAttemptId where - TFut: Future + Send + 'static, + TFut: Future> + Unpin + Send + 'static, THandler: IntoNodeHandler + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, @@ -358,17 +357,19 @@ where } /// Sends an event to all nodes. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.inner.start_broadcast(event) } + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.inner.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_broadcast(cx) } /// Adds an existing connection to a node to the collection. @@ -447,13 +448,13 @@ where /// > **Note**: we use a regular `poll` method instead of implementing `Stream` in order to /// > remove the `Err` variant, but also because we want the `CollectionStream` to stay /// > borrowed if necessary. - pub fn poll(&mut self) -> Async> + pub fn poll(&mut self, cx: &mut Context) -> Poll> where TConnInfo: Clone, // TODO: Clone shouldn't be necessary { - let item = match self.inner.poll() { - Async::Ready(item) => item, - Async::NotReady => return Async::NotReady, + let item = match self.inner.poll(cx) { + Poll::Ready(item) => item, + Poll::Pending => return Poll::Pending, }; match item { @@ -463,7 +464,7 @@ where match (user_data, result, handler) { (TaskState::Pending, tasks::Error::Reach(err), Some(handler)) => { - Async::Ready(CollectionEvent::ReachError { + Poll::Ready(CollectionEvent::ReachError { id: ReachAttemptId(id), error: err, handler, @@ -482,7 +483,7 @@ where debug_assert!(_handler.is_none()); let _node_task_id = self.nodes.remove(conn_info.peer_id()); debug_assert_eq!(_node_task_id, Some(id)); - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error: err, user_data, @@ -497,8 +498,8 @@ where tasks::Event::NodeReached { task, conn_info } => { let id = task.id(); drop(task); - Async::Ready(CollectionEvent::NodeReached(CollectionReachEvent { - parent: self, + Poll::Ready(CollectionEvent::NodeReached(CollectionReachEvent { + parent: &mut *self, id, conn_info: Some(conn_info), })) @@ -512,7 +513,7 @@ where self.tasks is switched to the Connected state; QED"), }; drop(task); - Async::Ready(CollectionEvent::NodeEvent { + Poll::Ready(CollectionEvent::NodeEvent { // TODO: normally we'd build a `PeerMut` manually here, but the borrow checker // doesn't like it peer: self.peer_mut(&conn_info.peer_id()) @@ -616,14 +617,15 @@ where } } - /// Sends an event to the given node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Begin sending an event to the given node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.inner.start_send_event(event) } - /// Complete sending an event message initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { - self.inner.complete_send_event() + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_event(cx) } /// Closes the connections to this node. Returns the user data. @@ -648,23 +650,13 @@ where /// The reach attempt will only be effectively cancelled once the peer (the object you're /// manipulating) has received some network activity. However no event will be ever be /// generated from this reach attempt, and this takes effect immediately. - #[must_use] - pub fn start_take_over(&mut self, id: InterruptedReachAttempt) - -> StartTakeOver<(), InterruptedReachAttempt> - { - match self.inner.start_take_over(id.inner) { - StartTakeOver::Ready(_state) => { - debug_assert!(if let TaskState::Pending = _state { true } else { false }); - StartTakeOver::Ready(()) - } - StartTakeOver::NotReady(inner) => - StartTakeOver::NotReady(InterruptedReachAttempt { inner }), - StartTakeOver::Gone => StartTakeOver::Gone - } + pub fn start_take_over(&mut self, id: InterruptedReachAttempt) { + self.inner.start_take_over(id.inner) } - /// Complete a take over initiated by `start_take_over`. - pub fn complete_take_over(&mut self) -> Poll<(), ()> { - self.inner.complete_take_over() + /// Make sure we are ready to taking over with `start_take_over`. + #[must_use] + pub fn poll_ready_take_over(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_take_over(cx) } } diff --git a/core/src/nodes/collection/tests.rs b/core/src/nodes/collection/tests.rs deleted file mode 100644 index 69f82c05..00000000 --- a/core/src/nodes/collection/tests.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use futures::future; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, InEvent, OutEvent, HandlerState}; -use tokio::runtime::current_thread::Runtime; -use tokio::runtime::Builder; -use crate::nodes::NodeHandlerEvent; -use std::{io, sync::Arc}; -use parking_lot::Mutex; - -type TestCollectionStream = CollectionStream; - -#[test] -fn has_connection_is_false_before_a_connection_has_been_made() { - let cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(!cs.has_connection(&peer_id)); -} - -#[test] -fn connections_is_empty_before_connecting() { - let cs = TestCollectionStream::new(); - assert!(cs.connections().next().is_none()); -} - -#[test] -fn retrieving_a_peer_is_none_if_peer_is_missing_or_not_connected() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(cs.peer_mut(&peer_id).is_none()); - - let handler = Handler::default(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, handler); - assert!(cs.peer_mut(&peer_id).is_none()); // task is pending -} - -#[test] -fn collection_stream_reaches_the_nodes() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((peer_id, muxer)); - cs.add_reach_attempt(fut, Handler::default()); - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - let event = cs.poll(); - match poll_count { - 1 => assert_matches!(event, Async::NotReady), - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(_))); - return Ok(Async::Ready(())); // stop - } - _ => unreachable!() - } - Ok(Async::NotReady) - }); - rt.block_on(fut).unwrap(); -} - -#[test] -fn accepting_a_node_yields_new_entry() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, Handler::default()); - - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - { - let event = cs.poll(); - match poll_count { - 1 => { - assert_matches!(event, Async::NotReady); - return Ok(Async::NotReady) - } - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - let (accept_ev, accepted_peer_id) = reach_ev.accept(()); - assert_eq!(accepted_peer_id, peer_id); - assert_matches!(accept_ev, CollectionNodeAccept::NewEntry); - }); - } - _ => unreachable!() - } - } - assert!(cs.peer_mut(&peer_id).is_some(), "peer is not in the list"); - assert!(cs.has_connection(&peer_id), "peer is not connected"); - assert_eq!(cs.connections().collect::>(), vec![&peer_id]); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("running the future works"); -} - -#[test] -fn events_in_a_node_reaches_the_collection_stream() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_peer_id = PeerId::random(); - - let mut handler = Handler::default(); - handler.state = Some(HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("init")))); - let handler_states = vec![ - HandlerState::Err, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 3") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 2") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )), - ]; - handler.next_states = handler_states; - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((task_peer_id.clone(), muxer)); - cs.lock().add_reach_attempt(fut, handler); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("init")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 2")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn task_closed_with_error_while_task_is_pending_yields_reach_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_inner_fut = future::err(std::io::Error::new(std::io::ErrorKind::Other, "inner fut error")); - let reach_attempt_id = cs.lock().add_reach_attempt(task_inner_fut, Handler::default()); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::ReachError {id, error, ..} => { - assert_eq!(id, reach_attempt_id); - assert_eq!(error.to_string(), "inner fut error"); - }); - - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - -} - -#[test] -fn task_closed_with_error_when_task_is_connected_yields_node_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Err]; // triggered when sending a NextState event - - cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(cs.complete_broadcast()) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id)); - - // Assert the node errored - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::NodeClosed{..}); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn interrupting_a_pending_connection_attempt_is_ok() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - let interrupt = cs.interrupt(reach_id); - assert!(interrupt.is_ok()); -} - -#[test] -fn interrupting_a_connection_attempt_twice_is_err() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - assert!(cs.interrupt(reach_id).is_ok()); - assert_matches!(cs.interrupt(reach_id), Err(InterruptError::ReachAttemptNotFound)) -} - -#[test] -fn interrupting_an_established_connection_is_err() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let handler = Handler::default(); - - let reach_id = cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(Async::Ready(())) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id), "Connection was not established"); - - assert_matches!(cs.lock().interrupt(reach_id), Err(InterruptError::AlreadyReached)); -} diff --git a/core/src/nodes/handled_node.rs b/core/src/nodes/handled_node.rs index 150b5e45..f8b08d11 100644 --- a/core/src/nodes/handled_node.rs +++ b/core/src/nodes/handled_node.rs @@ -20,10 +20,7 @@ use crate::{PeerId, muxing::StreamMuxer}; use crate::nodes::node::{NodeEvent, NodeStream, Substream, Close}; -use futures::prelude::*; -use std::{error, fmt, io}; - -mod tests; +use std::{error, fmt, io, pin::Pin, task::Context, task::Poll}; /// Handler for the substreams of a node. // TODO: right now it is possible for a node handler to be built, then shut down right after if we @@ -59,7 +56,8 @@ pub trait NodeHandler { /// Should behave like `Stream::poll()`. /// /// Returning an error will close the connection to the remote. - fn poll(&mut self) -> Poll, Self::Error>; + fn poll(&mut self, cx: &mut Context) + -> Poll, Self::Error>>; } /// Prototype for a `NodeHandler`. @@ -172,6 +170,13 @@ where } } +impl Unpin for HandledNode +where + TMuxer: StreamMuxer, + THandler: NodeHandler>, +{ +} + impl HandledNode where TMuxer: StreamMuxer, @@ -214,37 +219,41 @@ where } /// API similar to `Future::poll` that polls the node for events. - pub fn poll(&mut self) -> Poll> { + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) + -> Poll>> + { loop { let mut node_not_ready = false; - match self.node.poll().map_err(HandledNodeError::Node)? { - Async::NotReady => node_not_ready = true, - Async::Ready(NodeEvent::InboundSubstream { substream }) => { + match self.node.poll(cx) { + Poll::Pending => node_not_ready = true, + Poll::Ready(Ok(NodeEvent::InboundSubstream { substream })) => { self.handler.inject_substream(substream, NodeHandlerEndpoint::Listener) } - Async::Ready(NodeEvent::OutboundSubstream { user_data, substream }) => { + Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream })) => { let endpoint = NodeHandlerEndpoint::Dialer(user_data); self.handler.inject_substream(substream, endpoint) } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Node(err))), } - match self.handler.poll().map_err(HandledNodeError::Handler)? { - Async::NotReady => { + match self.handler.poll(cx) { + Poll::Pending => { if node_not_ready { break } } - Async::Ready(NodeHandlerEvent::OutboundSubstreamRequest(user_data)) => { + Poll::Ready(Ok(NodeHandlerEvent::OutboundSubstreamRequest(user_data))) => { self.node.open_substream(user_data); } - Async::Ready(NodeHandlerEvent::Custom(event)) => { - return Ok(Async::Ready(event)); + Poll::Ready(Ok(NodeHandlerEvent::Custom(event))) => { + return Poll::Ready(Ok(event)); } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Handler(err))), } } - Ok(Async::NotReady) + Poll::Pending } } diff --git a/core/src/nodes/handled_node/tests.rs b/core/src/nodes/handled_node/tests.rs deleted file mode 100644 index ee138c2e..00000000 --- a/core/src/nodes/handled_node/tests.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent, TestHandledNode}; - -struct TestBuilder { - muxer: DummyMuxer, - handler: Handler, - want_open_substream: bool, - substream_user_data: usize, -} - -impl TestBuilder { - fn new() -> Self { - TestBuilder { - muxer: DummyMuxer::new(), - handler: Handler::default(), - want_open_substream: false, - substream_user_data: 0, - } - } - - fn with_muxer_inbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_inbound_connection_state(state); - self - } - - fn with_muxer_outbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_outbound_connection_state(state); - self - } - - fn with_handler_state(&mut self, state: HandlerState) -> &mut Self { - self.handler.state = Some(state); - self - } - - fn with_open_substream(&mut self, user_data: usize) -> &mut Self { - self.want_open_substream = true; - self.substream_user_data = user_data; - self - } - - fn handled_node(&mut self) -> TestHandledNode { - let mut h = HandledNode::new(self.muxer.clone(), self.handler.clone()); - if self.want_open_substream { - h.node.open_substream(self.substream_user_data); - } - h - } -} - -// Set the state of the `Handler` after `inject_outbound_closed` is called -fn set_next_handler_outbound_state( handled_node: &mut TestHandledNode, next_state: HandlerState) { - handled_node.handler.next_outbound_state = Some(next_state); -} - -#[test] -fn can_inject_event() { - let mut handled = TestBuilder::new() - .handled_node(); - - let event = InEvent::Custom("banana"); - handled.inject_event(event.clone()); - assert_eq!(handled.handler().events, vec![event]); -} - -#[test] -fn poll_with_unready_node_stream_and_handler_emits_custom_event() { - let expected_event = NodeHandlerEvent::Custom(OutEvent::Custom("pineapple")); - let mut handled = TestBuilder::new() - // make NodeStream return NotReady - .with_muxer_inbound_state(DummyConnectionState::Pending) - // make Handler return return Ready(Some(…)) - .with_handler_state(HandlerState::Ready(expected_event)) - .handled_node(); - - assert_matches!(handled.poll(), Ok(Async::Ready(event)) => { - assert_matches!(event, OutEvent::Custom("pineapple")) - }); -} - -#[test] -fn handler_emits_outbound_closed_when_opening_new_substream_on_closed_node() { - let open_event = NodeHandlerEvent::OutboundSubstreamRequest(456); - let mut handled = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Ready(open_event)) - .handled_node(); - - set_next_handler_outbound_state( - &mut handled, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("pear"))) - ); - handled.poll().expect("poll works"); -} - -#[test] -fn poll_yields_inbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_open_substream(32) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Opened) - .with_open_substream(1) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(Some(1))]); -} - -#[test] -fn poll_yields_inbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Opened) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(None)]); -} diff --git a/core/src/nodes/listeners.rs b/core/src/nodes/listeners.rs index effcea65..b9c8ebbf 100644 --- a/core/src/nodes/listeners.rs +++ b/core/src/nodes/listeners.rs @@ -21,11 +21,10 @@ //! Manage listening on multiple multiaddresses at once. use crate::{Multiaddr, Transport, transport::{TransportError, ListenerEvent}}; -use futures::prelude::*; +use futures::{prelude::*, task::Context, task::Poll}; use log::debug; use smallvec::SmallVec; -use std::{collections::VecDeque, fmt}; -use void::Void; +use std::{collections::VecDeque, fmt, pin::Pin}; /// Implementation of `futures::Stream` that allows listening on multiaddresses. /// @@ -158,7 +157,7 @@ where /// The ID of the listener that errored. listener_id: ListenerId, /// The error value. - error: ::Error + error: ::Error } } @@ -222,28 +221,31 @@ where self.listeners.iter().flat_map(|l| l.addresses.iter()) } - /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> { + /// Provides an API similar to `Stream`, except that it cannot end. + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> + where + TTrans::Listener: Unpin, + { // We remove each element from `listeners` one by one and add them back. let mut remaining = self.listeners.len(); while let Some(mut listener) = self.listeners.pop_back() { - match listener.listener.poll() { - Ok(Async::NotReady) => { + match TryStream::try_poll_next(Pin::new(&mut listener.listener), cx) { + Poll::Pending => { self.listeners.push_front(listener); remaining -= 1; if remaining == 0 { break } } - Ok(Async::Ready(Some(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { + Poll::Ready(Some(Ok(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::Incoming { + return Poll::Ready(ListenersEvent::Incoming { listener_id: id, upgrade, local_addr, send_back_addr: remote_addr }) } - Ok(Async::Ready(Some(ListenerEvent::NewAddress(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::NewAddress(a)))) => { if listener.addresses.contains(&a) { debug!("Transport has reported address {} multiple times", a) } @@ -252,28 +254,28 @@ where } let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::NewAddress { + return Poll::Ready(ListenersEvent::NewAddress { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(Some(ListenerEvent::AddressExpired(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::AddressExpired(a)))) => { listener.addresses.retain(|x| x != &a); let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::AddressExpired { + return Poll::Ready(ListenersEvent::AddressExpired { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(None)) => { - return Async::Ready(ListenersEvent::Closed { + Poll::Ready(None) => { + return Poll::Ready(ListenersEvent::Closed { listener_id: listener.id, listener: listener.listener }) } - Err(err) => { - return Async::Ready(ListenersEvent::Error { + Poll::Ready(Some(Err(err))) => { + return Poll::Ready(ListenersEvent::Error { listener_id: listener.id, error: err }) @@ -282,22 +284,28 @@ where } // We register the current task to be woken up if a new listener is added. - Async::NotReady + Poll::Pending } } impl Stream for ListenersStream where TTrans: Transport, + TTrans::Listener: Unpin, { type Item = ListenersEvent; - type Error = Void; // TODO: use ! once stable - fn poll(&mut self) -> Poll, Self::Error> { - Ok(self.poll().map(Option::Some)) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ListenersStream::poll(self, cx).map(Option::Some) } } +impl Unpin for ListenersStream +where + TTrans: Transport, +{ +} + impl fmt::Debug for ListenersStream where TTrans: Transport + fmt::Debug, @@ -313,7 +321,7 @@ where impl fmt::Debug for ListenersEvent where TTrans: Transport, - ::Error: fmt::Debug, + ::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { @@ -353,215 +361,37 @@ mod tests { use tokio::runtime::current_thread::Runtime; use std::{io, iter::FromIterator}; use futures::{future::{self}, stream}; - use crate::tests::dummy_transport::{DummyTransport, ListenerState}; - use crate::tests::dummy_muxer::DummyMuxer; use crate::PeerId; - fn set_listener_state(ls: &mut ListenersStream, idx: usize, state: ListenerState) { - ls.listeners[idx].listener = match state { - ListenerState::Error => - Box::new(stream::poll_fn(|| Err(io::Error::new(io::ErrorKind::Other, "oh noes")))), - ListenerState::Ok(state) => match state { - Async::NotReady => Box::new(stream::poll_fn(|| Ok(Async::NotReady))), - Async::Ready(Some(event)) => Box::new(stream::poll_fn(move || { - Ok(Async::Ready(Some(event.clone().map(future::ok)))) - })), - Async::Ready(None) => Box::new(stream::empty()) - } - ListenerState::Events(events) => - Box::new(stream::iter_ok(events.into_iter().map(|e| e.map(future::ok)))) - }; - } - #[test] fn incoming_event() { - let mem_transport = transport::MemoryTransport::default(); + futures::executor::block_on(async move { + let mem_transport = transport::MemoryTransport::default(); - let mut listeners = ListenersStream::new(mem_transport); - listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); + let mut listeners = ListenersStream::new(mem_transport); + listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); - let address = { - let event = listeners.by_ref().wait().next().expect("some event").expect("no error"); - if let ListenersEvent::NewAddress { listen_addr, .. } = event { - listen_addr - } else { - panic!("Was expecting the listen address to be reported") - } - }; - - let dial = mem_transport.dial(address.clone()).unwrap(); - - let future = listeners - .into_future() - .map_err(|(err, _)| err) - .and_then(|(event, _)| { - match event { - Some(ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. }) => { - assert_eq!(local_addr, address); - assert_eq!(send_back_addr, address); - upgrade.map(|_| ()).map_err(|_| panic!()) - }, - _ => panic!() + let address = { + let event = listeners.next().await.unwrap(); + if let ListenersEvent::NewAddress { listen_addr, .. } = event { + listen_addr + } else { + panic!("Was expecting the listen address to be reported") } - }) - .select(dial.map(|_| ()).map_err(|_| panic!())) - .map_err(|(err, _)| err); + }; - let mut runtime = Runtime::new().unwrap(); - runtime.block_on(future).unwrap(); - } + let address2 = address.clone(); + async_std::task::spawn(async move { + mem_transport.dial(address2).unwrap().await.unwrap(); + }); - #[test] - fn listener_stream_returns_transport() { - let t = DummyTransport::new(); - let t_clone = t.clone(); - let ls = ListenersStream::new(t); - assert_eq!(ls.transport(), &t_clone); - } - - #[test] - fn listener_stream_can_iterate_over_listeners() { - let mut t = DummyTransport::new(); - let addr1 = tcp4([127, 0, 0, 1], 1234); - let addr2 = tcp4([127, 0, 0, 1], 4321); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(addr1.clone()), - ListenerEvent::NewAddress(addr2.clone()) - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([0, 0, 0, 0], 0)).expect("listen_on"); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr1, listen_addr) - }); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr2, listen_addr) - }) - } - - #[test] - fn listener_stream_poll_without_listeners_is_not_ready() { - let t = DummyTransport::new(); - let mut ls = ListenersStream::new(t); - assert_matches!(ls.poll(), Async::NotReady); - } - - #[test] - fn listener_stream_poll_with_listeners_that_arent_ready_is_not_ready() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::NotReady)); - assert_matches!(ls.poll(), Async::NotReady); - assert_eq!(ls.listeners.len(), 1); // listener is still there - } - - #[test] - fn listener_stream_poll_with_ready_listeners_is_ready() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let expected_output = (peer_id.clone(), muxer.clone()); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(tcp4([127, 0, 0, 1], 9090)), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) + match listeners.next().await.unwrap() { + ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. } => { + assert_eq!(local_addr, address); + assert_eq!(send_back_addr, address); + }, + _ => panic!() } - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([127, 0, 0, 1], 1234)).expect("listen_on"); - ls.listen_on(tcp4([127, 0, 0, 1], 4321)).expect("listen_on"); - assert_eq!(ls.listeners.len(), 2); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - set_listener_state(&mut ls, 1, ListenerState::Ok(Async::NotReady)); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - } - - #[test] - fn listener_stream_poll_with_closed_listener_emits_closed_event() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::Ready(None))); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Closed{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - #[test] - fn listener_stream_poll_with_erroring_listener_emits_error_event() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let event = ListenerEvent::Upgrade { - upgrade: (peer_id, muxer), - local_addr: tcp4([127, 0, 0, 1], 1234), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }; - t.set_initial_listener_state(ListenerState::Ok(Async::Ready(Some(event)))); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Error); // simulate an error on the socket - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Error{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - fn tcp4(ip: [u8; 4], port: u16) -> Multiaddr { - let protos = std::iter::once(multiaddr::Protocol::Ip4(ip.into())) - .chain(std::iter::once(multiaddr::Protocol::Tcp(port))); - Multiaddr::from_iter(protos) } } diff --git a/core/src/nodes/network.rs b/core/src/nodes/network.rs index abe9e631..2f6634a1 100644 --- a/core/src/nodes/network.rs +++ b/core/src/nodes/network.rs @@ -49,10 +49,10 @@ use std::{ fmt, hash::Hash, num::NonZeroUsize, + pin::Pin, + task::{Context, Poll}, }; -pub use crate::nodes::collection::StartTakeOver; - mod tests; /// Implementation of `Stream` that handles the nodes. @@ -81,7 +81,7 @@ where /// If the pair's second element is `AsyncSink::Ready`, the take over /// message has been sent and needs to be flushed using /// `PeerMut::complete_take_over`. - take_over_to_complete: Option<(TPeerId, AsyncSink>)> + take_over_to_complete: Option<(TPeerId, InterruptedReachAttempt)> } impl fmt::Debug for @@ -102,6 +102,13 @@ where } } +impl Unpin for + Network +where + TTrans: Transport +{ +} + impl ConnectionInfo for (TConnInfo, ConnectedPoint) where TConnInfo: ConnectionInfo @@ -173,7 +180,7 @@ where /// The listener that errored. listener_id: ListenerId, /// The listener error. - error: ::Error + error: ::Error }, /// One of the listeners is now listening on an additional address. @@ -573,7 +580,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::ListenerUpgrade: Send + 'static, + TTrans::ListenerUpgrade: Unpin + Send + 'static, THandler: IntoNodeHandler<(TConnInfo, ConnectedPoint)> + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, // TODO: shouldn't be necessary @@ -609,9 +616,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -781,7 +788,7 @@ where where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TInEvent: Send + 'static, @@ -797,9 +804,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -840,19 +847,18 @@ where /// Start sending an event to all nodes. /// - /// Make sure to complete the broadcast with `complete_broadcast`. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.active_nodes.start_broadcast(event) } - /// Complete a broadcast initiated with `start_broadcast`. + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.active_nodes.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.active_nodes.poll_ready_broadcast(cx) } /// Returns a list of all the peers we are currently connected to. @@ -934,7 +940,7 @@ where fn start_dial_out(&mut self, peer_id: TPeerId, handler: THandler, first: Multiaddr, rest: Vec) where TTrans: Transport, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TTrans::Error: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -950,9 +956,9 @@ where .map_err(|err| InternalReachErr::Transport(TransportError::Other(err))) .and_then(move |(actual_conn_info, muxer)| { if *actual_conn_info.peer_id() == expected_peer_id { - Ok(((actual_conn_info, connected_point), muxer)) + future::ready(Ok(((actual_conn_info, connected_point), muxer))) } else { - Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info }) + future::ready(Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info })) } }); self.active_nodes.add_reach_attempt(fut, handler) @@ -976,11 +982,12 @@ where } /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> + pub fn poll<'a>(&'a mut self, cx: &mut Context) -> Poll> where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, + TTrans::Listener: Unpin, TTrans::ListenerUpgrade: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -998,9 +1005,9 @@ where Some(x) if self.incoming_negotiated().count() >= (x as usize) => (), _ => { - match self.listeners.poll() { - Async::NotReady => (), - Async::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { + match ListenersStream::poll(Pin::new(&mut self.listeners), cx) { + Poll::Pending => (), + Poll::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { let event = IncomingConnectionEvent { listener_id, upgrade, @@ -1010,19 +1017,19 @@ where active_nodes: &mut self.active_nodes, other_reach_attempts: &mut self.reach_attempts.other_reach_attempts, }; - return Async::Ready(NetworkEvent::IncomingConnection(event)); + return Poll::Ready(NetworkEvent::IncomingConnection(event)); } - Async::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::Closed { listener_id, listener }) => { - return Async::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) + Poll::Ready(ListenersEvent::Closed { listener_id, listener }) => { + return Poll::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) } - Async::Ready(ListenersEvent::Error { listener_id, error }) => { - return Async::Ready(NetworkEvent::ListenerError { listener_id, error }) + Poll::Ready(ListenersEvent::Error { listener_id, error }) => { + return Poll::Ready(NetworkEvent::ListenerError { listener_id, error }) } } } @@ -1031,36 +1038,30 @@ where // Attempt to deliver any pending take over messages. if let Some((id, interrupted)) = self.take_over_to_complete.take() { if let Some(mut peer) = self.active_nodes.peer_mut(&id) { - if let AsyncSink::NotReady(i) = interrupted { - if let StartTakeOver::NotReady(i) = peer.start_take_over(i) { - self.take_over_to_complete = Some((id, AsyncSink::NotReady(i))) - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) - } - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((id, interrupted)); + return Poll::Pending; } } } - if self.take_over_to_complete.is_some() { - return Async::NotReady - } // Poll the existing nodes. let (action, out_event); - match self.active_nodes.poll() { - Async::NotReady => return Async::NotReady, - Async::Ready(CollectionEvent::NodeReached(reach_event)) => { + match self.active_nodes.poll(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(CollectionEvent::NodeReached(reach_event)) => { let (a, e) = handle_node_reached(&mut self.reach_attempts, reach_event); action = a; out_event = e; } - Async::Ready(CollectionEvent::ReachError { id, error, handler }) => { + Poll::Ready(CollectionEvent::ReachError { id, error, handler }) => { let (a, e) = handle_reach_error(&mut self.reach_attempts, id, error, handler); action = a; out_event = e; } - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error, .. @@ -1078,7 +1079,7 @@ where error, }; } - Async::Ready(CollectionEvent::NodeEvent { peer, event }) => { + Poll::Ready(CollectionEvent::NodeEvent { peer, event }) => { action = Default::default(); out_event = NetworkEvent::NodeEvent { conn_info: peer.info().0.clone(), event }; } @@ -1099,17 +1100,15 @@ where out_reach_attempts should always be in sync with the actual \ attempts; QED"); let mut peer = self.active_nodes.peer_mut(&peer_id).unwrap(); - if let StartTakeOver::NotReady(i) = peer.start_take_over(interrupted) { - self.take_over_to_complete = Some((peer_id, AsyncSink::NotReady(i))); - return Async::NotReady - } - if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((peer_id, AsyncSink::Ready)); - return Async::NotReady + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((peer_id, interrupted)); + return Poll::Pending } } - Async::Ready(out_event) + Poll::Ready(out_event) } } @@ -1467,7 +1466,7 @@ impl<'a, TTrans, TMuxer, TInEvent, TOutEvent, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, @@ -1644,18 +1643,33 @@ where closed messages; QED") } - /// Start sending an event to the node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Sends an event to the handler of the node. + pub fn send_event<'s: 'a>(&'s mut self, event: TInEvent) -> impl Future + 's + 'a { + let mut event = Some(event); + futures::future::poll_fn(move |cx| { + match self.poll_ready_event(cx) { + Poll::Ready(()) => { + self.start_send_event(event.take().expect("Future called after finished")); + Poll::Ready(()) + }, + Poll::Pending => Poll::Pending, + } + }) + } + + /// Begin sending an event to the node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") .start_send_event(event) } - /// Complete sending an event message, initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") - .complete_send_event() + .poll_ready_event(cx) } } @@ -1749,7 +1763,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, diff --git a/core/src/nodes/network/tests.rs b/core/src/nodes/network/tests.rs index c64666aa..c4f307bb 100644 --- a/core/src/nodes/network/tests.rs +++ b/core/src/nodes/network/tests.rs @@ -21,363 +21,6 @@ #![cfg(test)] use super::*; -use crate::tests::dummy_transport::DummyTransport; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent}; -use crate::tests::dummy_transport::ListenerState; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::nodes::NodeHandlerEvent; -use crate::transport::ListenerEvent; -use assert_matches::assert_matches; -use parking_lot::Mutex; -use std::sync::Arc; -use tokio::runtime::{Builder, Runtime}; - -#[test] -fn query_transport() { - let transport = DummyTransport::new(); - let transport2 = transport.clone(); - let network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - assert_eq!(network.transport(), &transport2); -} - -#[test] -fn local_node_peer() { - let peer_id = PeerId::random(); - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), peer_id.clone()); - assert_matches!(network.peer(peer_id), Peer::LocalNode); -} - -#[test] -fn successful_dial_reaches_a_node() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let dial_res = network.dial(addr, Handler::default()); - assert!(dial_res.is_ok()); - - // Poll the network until we get a `NodeReached` then assert on the peer: - // it's there and it's connected. - let network = Arc::new(Mutex::new(network)); - - let mut rt = Runtime::new().unwrap(); - let mut peer_id : Option = None; - // Drive forward until we're Connected - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected(PeerConnected{..})); -} - -#[test] -fn num_incoming_negotiated() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - - let events = vec![ - ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap()), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ]; - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - // no incoming yet - assert_eq!(network.incoming_negotiated().count(), 0); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - // Now there's an incoming connection - assert_eq!(network.incoming_negotiated().count(), 1); -} - -#[test] -fn broadcasted_events_reach_active_nodes() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )),]; - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::NodeEvent { conn_info: _, event: inner_event } => { - // The event we sent reached the node and triggered sending the out event we told it to return - assert_matches!(inner_event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(false)) - }, - _ => Ok(Async::Ready(true)) - } - })).expect("tokio works"); - } -} - -#[test] -fn querying_for_pending_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); -} - -#[test] -fn querying_for_unknown_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected( PeerNotConnected { nodes: _, peer_id: node_peer_id }) => { - assert_eq!(node_peer_id, peer_id); - }); -} - -#[test] -fn querying_for_connected_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - - // Dial a node - let addr = "/ip4/127.0.0.1/tcp/1234".parse().expect("bad multiaddr"); - network.dial(addr, Handler::default()).expect("dialing works"); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we connect; extract the new PeerId. - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - // We're connected. - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected( PeerConnected { .. } )); -} - -#[test] -fn poll_with_closed_listener() { - let mut transport = DummyTransport::new(); - // Set up listener to be closed - transport.set_initial_listener_state(ListenerState::Ok(Async::Ready(None))); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::ListenerClosed { .. } )); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); -} - -#[test] -fn unknown_peer_that_is_unreachable_yields_unknown_peer_dial_error() { - let mut transport = DummyTransport::new(); - transport.make_dial_fail(); - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let handler = Handler::default(); - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::UnknownPeerDialError { .. } ); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn known_peer_that_is_unreachable_yields_dial_error() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - transport.make_dial_fail(); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); - } - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let peer_id = peer_id.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - let failed_peer_id = assert_matches!( - event, - NetworkEvent::DialError { new_state: _, peer_id: failed_peer_id, .. } => failed_peer_id - ); - assert_eq!(peer_id, failed_peer_id); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn yields_node_error_when_there_is_an_error_after_successful_connect() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - // Set up an outgoing connection with a PeerId we know - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - let addr = "/unix/reachable".parse().expect("bad multiaddr"); - let mut handler = Handler::default(); - // Force an error - handler.next_states = vec![ HandlerState::Err ]; - peer.into_not_connected().unwrap().connect(addr, handler); - } - - // Ensure we run on a single thread - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Drive it forward until we connect to the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - // Push the Handler into an error state on the next poll - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::Connected { .. }); - // We're connected, we can move on - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } - - // Poll again. It is going to be a NodeClosed because of how the - // handler's next state was set up. - let network_fut = network.clone(); - let expected_peer_id = peer_id.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::NodeClosed { conn_info, .. }) => { - assert_eq!(conn_info, expected_peer_id); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} #[test] fn local_prio_equivalence_relation() { @@ -387,59 +30,3 @@ fn local_prio_equivalence_relation() { assert_ne!(has_dial_prio(&a, &b), has_dial_prio(&b, &a)); } } - -#[test] -fn limit_incoming_connections() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let limit = 1; - - let mut events = vec![ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap())]; - events.extend(std::iter::repeat( - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ).take(10)); - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new_with_incoming_limit(transport, PeerId::random(), Some(limit)); - assert_eq!(network.incoming_limit(), Some(limit)); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - assert_eq!(network.incoming_negotiated().count(), 0); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - for i in 1..10 { - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - if i <= limit { - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), - Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - } else { - match network_fut.poll() { - Async::NotReady => (), - Async::Ready(x) => { - match x { - NetworkEvent::NewListenerAddress {..} => {} - NetworkEvent::ExpiredListenerAddress {..} => {} - NetworkEvent::IncomingConnection(_) => {} - NetworkEvent::Connected {..} => {} - e => panic!("Not expected event: {:?}", e) - } - }, - } - } - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - assert!(network.incoming_negotiated().count() <= (limit as usize)); - } -} diff --git a/core/src/nodes/node.rs b/core/src/nodes/node.rs index a1d0eac4..37da9954 100644 --- a/core/src/nodes/node.rs +++ b/core/src/nodes/node.rs @@ -21,9 +21,7 @@ use futures::prelude::*; use crate::muxing; use smallvec::SmallVec; -use std::fmt; -use std::io::Error as IoError; -use std::sync::Arc; +use std::{fmt, io::Error as IoError, pin::Pin, sync::Arc, task::Context, task::Poll}; // Implementation notes // ================= @@ -143,43 +141,44 @@ where } /// Provides an API similar to `Future`. - pub fn poll(&mut self) -> Poll, IoError> { + pub fn poll(&mut self, cx: &mut Context) -> Poll, IoError>> { // Polling inbound substream. - match self.muxer.poll_inbound().map_err(|e| e.into())? { - Async::Ready(substream) => { + match self.muxer.poll_inbound(cx) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); - return Ok(Async::Ready(NodeEvent::InboundSubstream { + return Poll::Ready(Ok(NodeEvent::InboundSubstream { substream, })); } - Async::NotReady => {} + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => {} } // Polling outbound substreams. // We remove each element from `outbound_substreams` one by one and add them back. for n in (0..self.outbound_substreams.len()).rev() { let (user_data, mut outbound) = self.outbound_substreams.swap_remove(n); - match self.muxer.poll_outbound(&mut outbound) { - Ok(Async::Ready(substream)) => { + match self.muxer.poll_outbound(cx, &mut outbound) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); self.muxer.destroy_outbound(outbound); - return Ok(Async::Ready(NodeEvent::OutboundSubstream { + return Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream, })); } - Ok(Async::NotReady) => { + Poll::Pending => { self.outbound_substreams.push((user_data, outbound)); } - Err(err) => { + Poll::Ready(Err(err)) => { self.muxer.destroy_outbound(outbound); - return Err(err.into()); + return Poll::Ready(Err(err.into())); } } } // Nothing happened. Register our task to be notified and return. - Ok(Async::NotReady) + Poll::Pending } } @@ -212,11 +211,14 @@ impl Future for Close where TMuxer: muxing::StreamMuxer, { - type Item = (); - type Error = IoError; + type Output = Result<(), IoError>; - fn poll(&mut self) -> Poll { - self.muxer.close().map_err(|e| e.into()) + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match self.muxer.close(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(err)) => Poll::Ready(Err(err.into())), + } } } @@ -252,70 +254,3 @@ where } } } - -#[cfg(test)] -mod node_stream { - use super::{NodeEvent, NodeStream}; - use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; - use assert_matches::assert_matches; - use futures::prelude::*; - use tokio_mock_task::MockTask; - - fn build_node_stream() -> NodeStream> { - let muxer = DummyMuxer::new(); - NodeStream::<_, Vec>::new(muxer) - } - - #[test] - fn closing_a_node_stream_destroys_substreams_and_returns_submitted_user_data() { - let mut ns = build_node_stream(); - ns.open_substream(vec![2]); - ns.open_substream(vec![3]); - ns.open_substream(vec![5]); - let user_data_submitted = ns.close(); - assert_eq!(user_data_submitted.1, vec![ - vec![2], vec![3], vec![5] - ]); - } - - #[test] - fn poll_returns_not_ready_when_there_is_nothing_to_do() { - let mut task = MockTask::new(); - task.enter(|| { - // ensure the address never resolves - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - - assert_matches!(ns.poll(), Ok(Async::NotReady)); - }); - } - - #[test] - fn poll_keeps_outbound_substreams_when_the_outgoing_connection_is_not_ready() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - ns.open_substream(vec![1]); - ns.poll().unwrap(); // poll past inbound - ns.poll().unwrap(); // poll outbound - assert!(format!("{:?}", ns).contains("outbound_substreams: 1")); - } - - #[test] - fn poll_returns_incoming_substream() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::Ready(subs) - muxer.set_inbound_connection_state(DummyConnectionState::Opened); - let mut ns = NodeStream::<_, Vec>::new(muxer); - assert_matches!(ns.poll(), Ok(Async::Ready(node_event)) => { - assert_matches!(node_event, NodeEvent::InboundSubstream{ substream: _ }); - }); - } -} diff --git a/core/src/nodes/tasks/manager.rs b/core/src/nodes/tasks/manager.rs index 33643aaa..aff72bd9 100644 --- a/core/src/nodes/tasks/manager.rs +++ b/core/src/nodes/tasks/manager.rs @@ -27,9 +27,8 @@ use crate::{ } }; use fnv::FnvHashMap; -use futures::{prelude::*, future::Executor, sync::mpsc}; -use smallvec::SmallVec; -use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt}; +use futures::{prelude::*, channel::mpsc, executor::ThreadPool, stream::FuturesUnordered, task::SpawnExt as _}; +use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt, pin::Pin, task::Context, task::Poll}; use super::{TaskId, task::{Task, FromTaskMessage, ToTaskMessage}, Error}; // Implementor notes @@ -64,12 +63,13 @@ pub struct Manager { /// Identifier for the next task to spawn. next_task_id: TaskId, - /// List of node tasks to spawn. - to_spawn: SmallVec<[Box + Send>; 8]>, + /// Threads pool where we spawn the nodes' tasks. If `None`, then we push tasks to the + /// `local_spawns` list instead. + threads_pool: Option, - /// If no tokio executor is available, we move tasks to this list, and futures are polled on - /// the current thread instead. - local_spawns: Vec + Send>>, + /// If no executor is available, we move tasks to this list, and futures are polled on the + /// current thread instead. + local_spawns: FuturesUnordered + Send>>>, /// Sender to emit events to the outside. Meant to be cloned and sent to tasks. events_tx: mpsc::Sender<(FromTaskMessage, TaskId)>, @@ -91,16 +91,13 @@ where /// Information about a running task. /// -/// Contains the sender to deliver event messages to the task, -/// the associated user data and a pending message if any, -/// meant to be delivered to the task via the sender. +/// Contains the sender to deliver event messages to the task, and +/// the associated user data. struct TaskInfo
Write for SubstreamRef
-where - P: Deref, - P::Target: StreamMuxer, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.write_substream(s, buf).map_err(|e| e.into())? { - Async::Ready(n) => Ok(n), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } - } - - #[inline] - fn flush(&mut self) -> Result<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - match self.muxer.flush_substream(s).map_err(|e| e.into())? { - Async::Ready(()) => Ok(()), - Async::NotReady => Err(io::ErrorKind::WouldBlock.into()) - } + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.read_substream(cx, s, buf).map_err(|e| e.into()) } } @@ -430,36 +400,51 @@ where P: Deref, P::Target: StreamMuxer, { - #[inline] - fn poll_write(&mut self, buf: &[u8]) -> Poll { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.write_substream(s, buf).map_err(|e| e.into()) + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.write_substream(cx, s, buf).map_err(|e| e.into()) } - #[inline] - fn shutdown(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); loop { - match self.shutdown_state { + match this.shutdown_state { ShutdownState::Shutdown => { - try_ready!(self.muxer.shutdown_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Flush; + match this.muxer.shutdown_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Flush, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Flush => { - try_ready!(self.muxer.flush_substream(s).map_err(|e| e.into())); - self.shutdown_state = ShutdownState::Done; + match this.muxer.flush_substream(cx, s) { + Poll::Ready(Ok(())) => this.shutdown_state = ShutdownState::Done, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => return Poll::Pending, + } } ShutdownState::Done => { - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } } } } - #[inline] - fn poll_flush(&mut self) -> Poll<(), io::Error> { - let s = self.substream.as_mut().expect("substream was empty"); - self.muxer.flush_substream(s).map_err(|e| e.into()) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We use a `this` because the compiler isn't smart enough to allow mutably borrowing + // multiple different fields from the `Pin` at the same time. + let this = &mut *self; + + let s = this.substream.as_mut().expect("substream was empty"); + this.muxer.flush_substream(cx, s).map_err(|e| e.into()) } } @@ -507,8 +492,8 @@ impl StreamMuxer for StreamMuxerBox { type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - self.inner.poll_inbound() + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + self.inner.poll_inbound(cx) } #[inline] @@ -517,8 +502,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn poll_outbound(&self, s: &mut Self::OutboundSubstream) -> Poll { - self.inner.poll_outbound(s) + fn poll_outbound(&self, cx: &mut Context, s: &mut Self::OutboundSubstream) -> Poll> { + self.inner.poll_outbound(cx, s) } #[inline] @@ -526,28 +511,28 @@ impl StreamMuxer for StreamMuxerBox { self.inner.destroy_outbound(substream) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { - self.inner.read_substream(s, buf) + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + self.inner.read_substream(cx, s, buf) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.write_substream(s, buf) + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { + self.inner.write_substream(cx, s, buf) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.flush_substream(s) + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.flush_substream(cx, s) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { - self.inner.shutdown_substream(s) + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { + self.inner.shutdown_substream(cx, s) } #[inline] @@ -556,8 +541,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close() + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx) } #[inline] @@ -566,8 +551,8 @@ impl StreamMuxer for StreamMuxerBox { } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all() + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx) } } @@ -588,11 +573,16 @@ where type Error = io::Error; #[inline] - fn poll_inbound(&self) -> Poll { - let substream = try_ready!(self.inner.poll_inbound().map_err(|e| e.into())); + fn poll_inbound(&self, cx: &mut Context) -> Poll> { + let substream = match self.inner.poll_inbound(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; + let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -606,13 +596,18 @@ where #[inline] fn poll_outbound( &self, + cx: &mut Context, substream: &mut Self::OutboundSubstream, - ) -> Poll { + ) -> Poll> { let mut list = self.outbound.lock(); - let substream = try_ready!(self.inner.poll_outbound(list.get_mut(substream).unwrap()).map_err(|e| e.into())); + let substream = match self.inner.poll_outbound(cx, list.get_mut(substream).unwrap()) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(s)) => s, + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + }; let id = self.next_substream.fetch_add(1, Ordering::Relaxed); self.substreams.lock().insert(id, substream); - Ok(Async::Ready(id)) + Poll::Ready(Ok(id)) } #[inline] @@ -621,32 +616,32 @@ where self.inner.destroy_outbound(list.remove(&substream).unwrap()) } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() } #[inline] - fn read_substream(&self, s: &mut Self::Substream, buf: &mut [u8]) -> Poll { + fn read_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &mut [u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.read_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.read_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn write_substream(&self, s: &mut Self::Substream, buf: &[u8]) -> Poll { + fn write_substream(&self, cx: &mut Context, s: &mut Self::Substream, buf: &[u8]) -> Poll> { let mut list = self.substreams.lock(); - self.inner.write_substream(list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) + self.inner.write_substream(cx, list.get_mut(s).unwrap(), buf).map_err(|e| e.into()) } #[inline] - fn flush_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn flush_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.flush_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.flush_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] - fn shutdown_substream(&self, s: &mut Self::Substream) -> Poll<(), Self::Error> { + fn shutdown_substream(&self, cx: &mut Context, s: &mut Self::Substream) -> Poll> { let mut list = self.substreams.lock(); - self.inner.shutdown_substream(list.get_mut(s).unwrap()).map_err(|e| e.into()) + self.inner.shutdown_substream(cx, list.get_mut(s).unwrap()).map_err(|e| e.into()) } #[inline] @@ -656,8 +651,8 @@ where } #[inline] - fn close(&self) -> Poll<(), Self::Error> { - self.inner.close().map_err(|e| e.into()) + fn close(&self, cx: &mut Context) -> Poll> { + self.inner.close(cx).map_err(|e| e.into()) } #[inline] @@ -666,7 +661,7 @@ where } #[inline] - fn flush_all(&self) -> Poll<(), Self::Error> { - self.inner.flush_all().map_err(|e| e.into()) + fn flush_all(&self, cx: &mut Context) -> Poll> { + self.inner.flush_all(cx).map_err(|e| e.into()) } } diff --git a/core/src/muxing/singleton.rs b/core/src/muxing/singleton.rs index 7bec14ed..f85e22fd 100644 --- a/core/src/muxing/singleton.rs +++ b/core/src/muxing/singleton.rs @@ -19,10 +19,9 @@ // DEALINGS IN THE SOFTWARE. use crate::{Endpoint, muxing::StreamMuxer}; -use futures::prelude::*; +use futures::{prelude::*, io::Initializer}; use parking_lot::Mutex; -use std::{io, sync::atomic::{AtomicBool, Ordering}}; -use tokio_io::{AsyncRead, AsyncWrite}; +use std::{io, pin::Pin, sync::atomic::{AtomicBool, Ordering}, task::Context, task::Poll}; /// Implementation of `StreamMuxer` that allows only one substream on top of a connection, /// yielding the connection itself. @@ -62,22 +61,22 @@ pub struct OutboundSubstream {} impl StreamMuxer for SingletonMuxer where - TSocket: AsyncRead + AsyncWrite, + TSocket: AsyncRead + AsyncWrite + Unpin, { type Substream = Substream; type OutboundSubstream = OutboundSubstream; type Error = io::Error; - fn poll_inbound(&self) -> Poll { + fn poll_inbound(&self, _: &mut Context) -> Poll> { match self.endpoint { - Endpoint::Dialer => return Ok(Async::NotReady), + Endpoint::Dialer => return Poll::Pending, Endpoint::Listener => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -85,44 +84,44 @@ where OutboundSubstream {} } - fn poll_outbound(&self, _: &mut Self::OutboundSubstream) -> Poll { + fn poll_outbound(&self, _: &mut Context, _: &mut Self::OutboundSubstream) -> Poll> { match self.endpoint { - Endpoint::Listener => return Ok(Async::NotReady), + Endpoint::Listener => return Poll::Pending, Endpoint::Dialer => {} } if !self.substream_extracted.swap(true, Ordering::Relaxed) { - Ok(Async::Ready(Substream {})) + Poll::Ready(Ok(Substream {})) } else { - Ok(Async::NotReady) + Poll::Pending } } fn destroy_outbound(&self, _: Self::OutboundSubstream) { } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.lock().prepare_uninitialized_buffer(buf) + unsafe fn initializer(&self) -> Initializer { + self.inner.lock().initializer() } - fn read_substream(&self, _: &mut Self::Substream, buf: &mut [u8]) -> Poll { - let res = self.inner.lock().poll_read(buf); - if let Ok(Async::Ready(_)) = res { + fn read_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &mut [u8]) -> Poll> { + let res = AsyncRead::poll_read(Pin::new(&mut *self.inner.lock()), cx, buf); + if let Poll::Ready(Ok(_)) = res { self.remote_acknowledged.store(true, Ordering::Release); } res } - fn write_substream(&self, _: &mut Self::Substream, buf: &[u8]) -> Poll { - self.inner.lock().poll_write(buf) + fn write_substream(&self, cx: &mut Context, _: &mut Self::Substream, buf: &[u8]) -> Poll> { + AsyncWrite::poll_write(Pin::new(&mut *self.inner.lock()), cx, buf) } - fn flush_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } - fn shutdown_substream(&self, _: &mut Self::Substream) -> Poll<(), io::Error> { - self.inner.lock().shutdown() + fn shutdown_substream(&self, cx: &mut Context, _: &mut Self::Substream) -> Poll> { + AsyncWrite::poll_close(Pin::new(&mut *self.inner.lock()), cx) } fn destroy_substream(&self, _: Self::Substream) { @@ -132,12 +131,12 @@ where self.remote_acknowledged.load(Ordering::Acquire) } - fn close(&self) -> Poll<(), io::Error> { + fn close(&self, cx: &mut Context) -> Poll> { // The `StreamMuxer` trait requires that `close()` implies `flush_all()`. - self.flush_all() + self.flush_all(cx) } - fn flush_all(&self) -> Poll<(), io::Error> { - self.inner.lock().poll_flush() + fn flush_all(&self, cx: &mut Context) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut *self.inner.lock()), cx) } } diff --git a/core/src/nodes/collection.rs b/core/src/nodes/collection.rs index af8601d2..9e212810 100644 --- a/core/src/nodes/collection.rs +++ b/core/src/nodes/collection.rs @@ -29,11 +29,7 @@ use crate::{ }; use fnv::FnvHashMap; use futures::prelude::*; -use std::{error, fmt, hash::Hash, mem}; - -pub use crate::nodes::tasks::StartTakeOver; - -mod tests; +use std::{error, fmt, hash::Hash, mem, task::Context, task::Poll}; /// Implementation of `Stream` that handles a collection of nodes. pub struct CollectionStream { @@ -58,6 +54,9 @@ where } } +impl Unpin for + CollectionStream { } + /// State of a task. #[derive(Debug, Clone, PartialEq, Eq)] enum TaskState { @@ -323,7 +322,7 @@ where pub fn add_reach_attempt(&mut self, future: TFut, handler: THandler) -> ReachAttemptId where - TFut: Future + Send + 'static, + TFut: Future> + Unpin + Send + 'static, THandler: IntoNodeHandler + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, @@ -358,17 +357,19 @@ where } /// Sends an event to all nodes. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.inner.start_broadcast(event) } + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.inner.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_broadcast(cx) } /// Adds an existing connection to a node to the collection. @@ -447,13 +448,13 @@ where /// > **Note**: we use a regular `poll` method instead of implementing `Stream` in order to /// > remove the `Err` variant, but also because we want the `CollectionStream` to stay /// > borrowed if necessary. - pub fn poll(&mut self) -> Async> + pub fn poll(&mut self, cx: &mut Context) -> Poll> where TConnInfo: Clone, // TODO: Clone shouldn't be necessary { - let item = match self.inner.poll() { - Async::Ready(item) => item, - Async::NotReady => return Async::NotReady, + let item = match self.inner.poll(cx) { + Poll::Ready(item) => item, + Poll::Pending => return Poll::Pending, }; match item { @@ -463,7 +464,7 @@ where match (user_data, result, handler) { (TaskState::Pending, tasks::Error::Reach(err), Some(handler)) => { - Async::Ready(CollectionEvent::ReachError { + Poll::Ready(CollectionEvent::ReachError { id: ReachAttemptId(id), error: err, handler, @@ -482,7 +483,7 @@ where debug_assert!(_handler.is_none()); let _node_task_id = self.nodes.remove(conn_info.peer_id()); debug_assert_eq!(_node_task_id, Some(id)); - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error: err, user_data, @@ -497,8 +498,8 @@ where tasks::Event::NodeReached { task, conn_info } => { let id = task.id(); drop(task); - Async::Ready(CollectionEvent::NodeReached(CollectionReachEvent { - parent: self, + Poll::Ready(CollectionEvent::NodeReached(CollectionReachEvent { + parent: &mut *self, id, conn_info: Some(conn_info), })) @@ -512,7 +513,7 @@ where self.tasks is switched to the Connected state; QED"), }; drop(task); - Async::Ready(CollectionEvent::NodeEvent { + Poll::Ready(CollectionEvent::NodeEvent { // TODO: normally we'd build a `PeerMut` manually here, but the borrow checker // doesn't like it peer: self.peer_mut(&conn_info.peer_id()) @@ -616,14 +617,15 @@ where } } - /// Sends an event to the given node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Begin sending an event to the given node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.inner.start_send_event(event) } - /// Complete sending an event message initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { - self.inner.complete_send_event() + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_event(cx) } /// Closes the connections to this node. Returns the user data. @@ -648,23 +650,13 @@ where /// The reach attempt will only be effectively cancelled once the peer (the object you're /// manipulating) has received some network activity. However no event will be ever be /// generated from this reach attempt, and this takes effect immediately. - #[must_use] - pub fn start_take_over(&mut self, id: InterruptedReachAttempt) - -> StartTakeOver<(), InterruptedReachAttempt> - { - match self.inner.start_take_over(id.inner) { - StartTakeOver::Ready(_state) => { - debug_assert!(if let TaskState::Pending = _state { true } else { false }); - StartTakeOver::Ready(()) - } - StartTakeOver::NotReady(inner) => - StartTakeOver::NotReady(InterruptedReachAttempt { inner }), - StartTakeOver::Gone => StartTakeOver::Gone - } + pub fn start_take_over(&mut self, id: InterruptedReachAttempt) { + self.inner.start_take_over(id.inner) } - /// Complete a take over initiated by `start_take_over`. - pub fn complete_take_over(&mut self) -> Poll<(), ()> { - self.inner.complete_take_over() + /// Make sure we are ready to taking over with `start_take_over`. + #[must_use] + pub fn poll_ready_take_over(&mut self, cx: &mut Context) -> Poll<()> { + self.inner.poll_ready_take_over(cx) } } diff --git a/core/src/nodes/collection/tests.rs b/core/src/nodes/collection/tests.rs deleted file mode 100644 index 69f82c05..00000000 --- a/core/src/nodes/collection/tests.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use futures::future; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, InEvent, OutEvent, HandlerState}; -use tokio::runtime::current_thread::Runtime; -use tokio::runtime::Builder; -use crate::nodes::NodeHandlerEvent; -use std::{io, sync::Arc}; -use parking_lot::Mutex; - -type TestCollectionStream = CollectionStream; - -#[test] -fn has_connection_is_false_before_a_connection_has_been_made() { - let cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(!cs.has_connection(&peer_id)); -} - -#[test] -fn connections_is_empty_before_connecting() { - let cs = TestCollectionStream::new(); - assert!(cs.connections().next().is_none()); -} - -#[test] -fn retrieving_a_peer_is_none_if_peer_is_missing_or_not_connected() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - assert!(cs.peer_mut(&peer_id).is_none()); - - let handler = Handler::default(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, handler); - assert!(cs.peer_mut(&peer_id).is_none()); // task is pending -} - -#[test] -fn collection_stream_reaches_the_nodes() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((peer_id, muxer)); - cs.add_reach_attempt(fut, Handler::default()); - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - let event = cs.poll(); - match poll_count { - 1 => assert_matches!(event, Async::NotReady), - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(_))); - return Ok(Async::Ready(())); // stop - } - _ => unreachable!() - } - Ok(Async::NotReady) - }); - rt.block_on(fut).unwrap(); -} - -#[test] -fn accepting_a_node_yields_new_entry() { - let mut cs = TestCollectionStream::new(); - let peer_id = PeerId::random(); - let fut = future::ok((peer_id.clone(), DummyMuxer::new())); - cs.add_reach_attempt(fut, Handler::default()); - - let mut rt = Runtime::new().unwrap(); - let mut poll_count = 0; - let fut = future::poll_fn(move || -> Poll<(), ()> { - poll_count += 1; - { - let event = cs.poll(); - match poll_count { - 1 => { - assert_matches!(event, Async::NotReady); - return Ok(Async::NotReady) - } - 2 => { - assert_matches!(event, Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - let (accept_ev, accepted_peer_id) = reach_ev.accept(()); - assert_eq!(accepted_peer_id, peer_id); - assert_matches!(accept_ev, CollectionNodeAccept::NewEntry); - }); - } - _ => unreachable!() - } - } - assert!(cs.peer_mut(&peer_id).is_some(), "peer is not in the list"); - assert!(cs.has_connection(&peer_id), "peer is not connected"); - assert_eq!(cs.connections().collect::>(), vec![&peer_id]); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("running the future works"); -} - -#[test] -fn events_in_a_node_reaches_the_collection_stream() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_peer_id = PeerId::random(); - - let mut handler = Handler::default(); - handler.state = Some(HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("init")))); - let handler_states = vec![ - HandlerState::Err, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 3") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 2") )), - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )), - ]; - handler.next_states = handler_states; - - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - - let fut = future::ok((task_peer_id.clone(), muxer)); - cs.lock().add_reach_attempt(fut, handler); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("init")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - if cs.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeEvent{peer: _, event}) => { - assert_matches!(event, OutEvent::Custom("from handler 2")); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn task_closed_with_error_while_task_is_pending_yields_reach_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let task_inner_fut = future::err(std::io::Error::new(std::io::ErrorKind::Other, "inner fut error")); - let reach_attempt_id = cs.lock().add_reach_attempt(task_inner_fut, Handler::default()); - - let mut rt = Builder::new().core_threads(1).build().unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - Ok(Async::Ready(())) - })).expect("tokio works"); - - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::ReachError {id, error, ..} => { - assert_eq!(id, reach_attempt_id); - assert_eq!(error.to_string(), "inner fut error"); - }); - - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - -} - -#[test] -fn task_closed_with_error_when_task_is_connected_yields_node_error() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Err]; // triggered when sending a NextState event - - cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs2 = cs.clone(); - rt.block_on(future::poll_fn(move || { - if cs2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(cs.complete_broadcast()) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id)); - - // Assert the node errored - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::Ready(collection_ev) => { - assert_matches!(collection_ev, CollectionEvent::NodeClosed{..}); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} - -#[test] -fn interrupting_a_pending_connection_attempt_is_ok() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - let interrupt = cs.interrupt(reach_id); - assert!(interrupt.is_ok()); -} - -#[test] -fn interrupting_a_connection_attempt_twice_is_err() { - let mut cs = TestCollectionStream::new(); - let fut = future::empty(); - let reach_id = cs.add_reach_attempt(fut, Handler::default()); - assert!(cs.interrupt(reach_id).is_ok()); - assert_matches!(cs.interrupt(reach_id), Err(InterruptError::ReachAttemptNotFound)) -} - -#[test] -fn interrupting_an_established_connection_is_err() { - let cs = Arc::new(Mutex::new(TestCollectionStream::new())); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let task_inner_fut = future::ok((peer_id.clone(), muxer)); - let handler = Handler::default(); - - let reach_id = cs.lock().add_reach_attempt(task_inner_fut, handler); - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Kick it off - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - assert_matches!(cs.poll(), Async::NotReady); - // send an event so the Handler errors in two polls - Ok(Async::Ready(())) - })).expect("tokio works"); - - // Accept the new node - let cs_fut = cs.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut cs = cs_fut.lock(); - // NodeReached, accept the connection so the task transitions from Pending to Connected - assert_matches!(cs.poll(), Async::Ready(CollectionEvent::NodeReached(reach_ev)) => { - reach_ev.accept(()); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); - - assert!(cs.lock().has_connection(&peer_id), "Connection was not established"); - - assert_matches!(cs.lock().interrupt(reach_id), Err(InterruptError::AlreadyReached)); -} diff --git a/core/src/nodes/handled_node.rs b/core/src/nodes/handled_node.rs index 150b5e45..f8b08d11 100644 --- a/core/src/nodes/handled_node.rs +++ b/core/src/nodes/handled_node.rs @@ -20,10 +20,7 @@ use crate::{PeerId, muxing::StreamMuxer}; use crate::nodes::node::{NodeEvent, NodeStream, Substream, Close}; -use futures::prelude::*; -use std::{error, fmt, io}; - -mod tests; +use std::{error, fmt, io, pin::Pin, task::Context, task::Poll}; /// Handler for the substreams of a node. // TODO: right now it is possible for a node handler to be built, then shut down right after if we @@ -59,7 +56,8 @@ pub trait NodeHandler { /// Should behave like `Stream::poll()`. /// /// Returning an error will close the connection to the remote. - fn poll(&mut self) -> Poll, Self::Error>; + fn poll(&mut self, cx: &mut Context) + -> Poll, Self::Error>>; } /// Prototype for a `NodeHandler`. @@ -172,6 +170,13 @@ where } } +impl Unpin for HandledNode +where + TMuxer: StreamMuxer, + THandler: NodeHandler>, +{ +} + impl HandledNode where TMuxer: StreamMuxer, @@ -214,37 +219,41 @@ where } /// API similar to `Future::poll` that polls the node for events. - pub fn poll(&mut self) -> Poll> { + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) + -> Poll>> + { loop { let mut node_not_ready = false; - match self.node.poll().map_err(HandledNodeError::Node)? { - Async::NotReady => node_not_ready = true, - Async::Ready(NodeEvent::InboundSubstream { substream }) => { + match self.node.poll(cx) { + Poll::Pending => node_not_ready = true, + Poll::Ready(Ok(NodeEvent::InboundSubstream { substream })) => { self.handler.inject_substream(substream, NodeHandlerEndpoint::Listener) } - Async::Ready(NodeEvent::OutboundSubstream { user_data, substream }) => { + Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream })) => { let endpoint = NodeHandlerEndpoint::Dialer(user_data); self.handler.inject_substream(substream, endpoint) } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Node(err))), } - match self.handler.poll().map_err(HandledNodeError::Handler)? { - Async::NotReady => { + match self.handler.poll(cx) { + Poll::Pending => { if node_not_ready { break } } - Async::Ready(NodeHandlerEvent::OutboundSubstreamRequest(user_data)) => { + Poll::Ready(Ok(NodeHandlerEvent::OutboundSubstreamRequest(user_data))) => { self.node.open_substream(user_data); } - Async::Ready(NodeHandlerEvent::Custom(event)) => { - return Ok(Async::Ready(event)); + Poll::Ready(Ok(NodeHandlerEvent::Custom(event))) => { + return Poll::Ready(Ok(event)); } + Poll::Ready(Err(err)) => return Poll::Ready(Err(HandledNodeError::Handler(err))), } } - Ok(Async::NotReady) + Poll::Pending } } diff --git a/core/src/nodes/handled_node/tests.rs b/core/src/nodes/handled_node/tests.rs deleted file mode 100644 index ee138c2e..00000000 --- a/core/src/nodes/handled_node/tests.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#![cfg(test)] - -use super::*; -use assert_matches::assert_matches; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent, TestHandledNode}; - -struct TestBuilder { - muxer: DummyMuxer, - handler: Handler, - want_open_substream: bool, - substream_user_data: usize, -} - -impl TestBuilder { - fn new() -> Self { - TestBuilder { - muxer: DummyMuxer::new(), - handler: Handler::default(), - want_open_substream: false, - substream_user_data: 0, - } - } - - fn with_muxer_inbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_inbound_connection_state(state); - self - } - - fn with_muxer_outbound_state(&mut self, state: DummyConnectionState) -> &mut Self { - self.muxer.set_outbound_connection_state(state); - self - } - - fn with_handler_state(&mut self, state: HandlerState) -> &mut Self { - self.handler.state = Some(state); - self - } - - fn with_open_substream(&mut self, user_data: usize) -> &mut Self { - self.want_open_substream = true; - self.substream_user_data = user_data; - self - } - - fn handled_node(&mut self) -> TestHandledNode { - let mut h = HandledNode::new(self.muxer.clone(), self.handler.clone()); - if self.want_open_substream { - h.node.open_substream(self.substream_user_data); - } - h - } -} - -// Set the state of the `Handler` after `inject_outbound_closed` is called -fn set_next_handler_outbound_state( handled_node: &mut TestHandledNode, next_state: HandlerState) { - handled_node.handler.next_outbound_state = Some(next_state); -} - -#[test] -fn can_inject_event() { - let mut handled = TestBuilder::new() - .handled_node(); - - let event = InEvent::Custom("banana"); - handled.inject_event(event.clone()); - assert_eq!(handled.handler().events, vec![event]); -} - -#[test] -fn poll_with_unready_node_stream_and_handler_emits_custom_event() { - let expected_event = NodeHandlerEvent::Custom(OutEvent::Custom("pineapple")); - let mut handled = TestBuilder::new() - // make NodeStream return NotReady - .with_muxer_inbound_state(DummyConnectionState::Pending) - // make Handler return return Ready(Some(…)) - .with_handler_state(HandlerState::Ready(expected_event)) - .handled_node(); - - assert_matches!(handled.poll(), Ok(Async::Ready(event)) => { - assert_matches!(event, OutEvent::Custom("pineapple")) - }); -} - -#[test] -fn handler_emits_outbound_closed_when_opening_new_substream_on_closed_node() { - let open_event = NodeHandlerEvent::OutboundSubstreamRequest(456); - let mut handled = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Ready(open_event)) - .handled_node(); - - set_next_handler_outbound_state( - &mut handled, - HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("pear"))) - ); - handled.poll().expect("poll works"); -} - -#[test] -fn poll_yields_inbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_closed_event() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_open_substream(32) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); -} - -#[test] -fn poll_yields_outbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Pending) - .with_muxer_outbound_state(DummyConnectionState::Opened) - .with_open_substream(1) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(Some(1))]); -} - -#[test] -fn poll_yields_inbound_substream() { - let mut h = TestBuilder::new() - .with_muxer_inbound_state(DummyConnectionState::Opened) - .with_muxer_outbound_state(DummyConnectionState::Pending) - .with_handler_state(HandlerState::Err) // stop the loop - .handled_node(); - - assert_eq!(h.handler().events, vec![]); - let _ = h.poll(); - assert_eq!(h.handler().events, vec![InEvent::Substream(None)]); -} diff --git a/core/src/nodes/listeners.rs b/core/src/nodes/listeners.rs index effcea65..b9c8ebbf 100644 --- a/core/src/nodes/listeners.rs +++ b/core/src/nodes/listeners.rs @@ -21,11 +21,10 @@ //! Manage listening on multiple multiaddresses at once. use crate::{Multiaddr, Transport, transport::{TransportError, ListenerEvent}}; -use futures::prelude::*; +use futures::{prelude::*, task::Context, task::Poll}; use log::debug; use smallvec::SmallVec; -use std::{collections::VecDeque, fmt}; -use void::Void; +use std::{collections::VecDeque, fmt, pin::Pin}; /// Implementation of `futures::Stream` that allows listening on multiaddresses. /// @@ -158,7 +157,7 @@ where /// The ID of the listener that errored. listener_id: ListenerId, /// The error value. - error: ::Error + error: ::Error } } @@ -222,28 +221,31 @@ where self.listeners.iter().flat_map(|l| l.addresses.iter()) } - /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> { + /// Provides an API similar to `Stream`, except that it cannot end. + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> + where + TTrans::Listener: Unpin, + { // We remove each element from `listeners` one by one and add them back. let mut remaining = self.listeners.len(); while let Some(mut listener) = self.listeners.pop_back() { - match listener.listener.poll() { - Ok(Async::NotReady) => { + match TryStream::try_poll_next(Pin::new(&mut listener.listener), cx) { + Poll::Pending => { self.listeners.push_front(listener); remaining -= 1; if remaining == 0 { break } } - Ok(Async::Ready(Some(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { + Poll::Ready(Some(Ok(ListenerEvent::Upgrade { upgrade, local_addr, remote_addr }))) => { let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::Incoming { + return Poll::Ready(ListenersEvent::Incoming { listener_id: id, upgrade, local_addr, send_back_addr: remote_addr }) } - Ok(Async::Ready(Some(ListenerEvent::NewAddress(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::NewAddress(a)))) => { if listener.addresses.contains(&a) { debug!("Transport has reported address {} multiple times", a) } @@ -252,28 +254,28 @@ where } let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::NewAddress { + return Poll::Ready(ListenersEvent::NewAddress { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(Some(ListenerEvent::AddressExpired(a)))) => { + Poll::Ready(Some(Ok(ListenerEvent::AddressExpired(a)))) => { listener.addresses.retain(|x| x != &a); let id = listener.id; self.listeners.push_front(listener); - return Async::Ready(ListenersEvent::AddressExpired { + return Poll::Ready(ListenersEvent::AddressExpired { listener_id: id, listen_addr: a }) } - Ok(Async::Ready(None)) => { - return Async::Ready(ListenersEvent::Closed { + Poll::Ready(None) => { + return Poll::Ready(ListenersEvent::Closed { listener_id: listener.id, listener: listener.listener }) } - Err(err) => { - return Async::Ready(ListenersEvent::Error { + Poll::Ready(Some(Err(err))) => { + return Poll::Ready(ListenersEvent::Error { listener_id: listener.id, error: err }) @@ -282,22 +284,28 @@ where } // We register the current task to be woken up if a new listener is added. - Async::NotReady + Poll::Pending } } impl Stream for ListenersStream where TTrans: Transport, + TTrans::Listener: Unpin, { type Item = ListenersEvent; - type Error = Void; // TODO: use ! once stable - fn poll(&mut self) -> Poll, Self::Error> { - Ok(self.poll().map(Option::Some)) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + ListenersStream::poll(self, cx).map(Option::Some) } } +impl Unpin for ListenersStream +where + TTrans: Transport, +{ +} + impl fmt::Debug for ListenersStream where TTrans: Transport + fmt::Debug, @@ -313,7 +321,7 @@ where impl fmt::Debug for ListenersEvent where TTrans: Transport, - ::Error: fmt::Debug, + ::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { @@ -353,215 +361,37 @@ mod tests { use tokio::runtime::current_thread::Runtime; use std::{io, iter::FromIterator}; use futures::{future::{self}, stream}; - use crate::tests::dummy_transport::{DummyTransport, ListenerState}; - use crate::tests::dummy_muxer::DummyMuxer; use crate::PeerId; - fn set_listener_state(ls: &mut ListenersStream, idx: usize, state: ListenerState) { - ls.listeners[idx].listener = match state { - ListenerState::Error => - Box::new(stream::poll_fn(|| Err(io::Error::new(io::ErrorKind::Other, "oh noes")))), - ListenerState::Ok(state) => match state { - Async::NotReady => Box::new(stream::poll_fn(|| Ok(Async::NotReady))), - Async::Ready(Some(event)) => Box::new(stream::poll_fn(move || { - Ok(Async::Ready(Some(event.clone().map(future::ok)))) - })), - Async::Ready(None) => Box::new(stream::empty()) - } - ListenerState::Events(events) => - Box::new(stream::iter_ok(events.into_iter().map(|e| e.map(future::ok)))) - }; - } - #[test] fn incoming_event() { - let mem_transport = transport::MemoryTransport::default(); + futures::executor::block_on(async move { + let mem_transport = transport::MemoryTransport::default(); - let mut listeners = ListenersStream::new(mem_transport); - listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); + let mut listeners = ListenersStream::new(mem_transport); + listeners.listen_on("/memory/0".parse().unwrap()).unwrap(); - let address = { - let event = listeners.by_ref().wait().next().expect("some event").expect("no error"); - if let ListenersEvent::NewAddress { listen_addr, .. } = event { - listen_addr - } else { - panic!("Was expecting the listen address to be reported") - } - }; - - let dial = mem_transport.dial(address.clone()).unwrap(); - - let future = listeners - .into_future() - .map_err(|(err, _)| err) - .and_then(|(event, _)| { - match event { - Some(ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. }) => { - assert_eq!(local_addr, address); - assert_eq!(send_back_addr, address); - upgrade.map(|_| ()).map_err(|_| panic!()) - }, - _ => panic!() + let address = { + let event = listeners.next().await.unwrap(); + if let ListenersEvent::NewAddress { listen_addr, .. } = event { + listen_addr + } else { + panic!("Was expecting the listen address to be reported") } - }) - .select(dial.map(|_| ()).map_err(|_| panic!())) - .map_err(|(err, _)| err); + }; - let mut runtime = Runtime::new().unwrap(); - runtime.block_on(future).unwrap(); - } + let address2 = address.clone(); + async_std::task::spawn(async move { + mem_transport.dial(address2).unwrap().await.unwrap(); + }); - #[test] - fn listener_stream_returns_transport() { - let t = DummyTransport::new(); - let t_clone = t.clone(); - let ls = ListenersStream::new(t); - assert_eq!(ls.transport(), &t_clone); - } - - #[test] - fn listener_stream_can_iterate_over_listeners() { - let mut t = DummyTransport::new(); - let addr1 = tcp4([127, 0, 0, 1], 1234); - let addr2 = tcp4([127, 0, 0, 1], 4321); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(addr1.clone()), - ListenerEvent::NewAddress(addr2.clone()) - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([0, 0, 0, 0], 0)).expect("listen_on"); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr1, listen_addr) - }); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(ListenersEvent::NewAddress { listen_addr, .. })) => { - assert_eq!(addr2, listen_addr) - }) - } - - #[test] - fn listener_stream_poll_without_listeners_is_not_ready() { - let t = DummyTransport::new(); - let mut ls = ListenersStream::new(t); - assert_matches!(ls.poll(), Async::NotReady); - } - - #[test] - fn listener_stream_poll_with_listeners_that_arent_ready_is_not_ready() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::NotReady)); - assert_matches!(ls.poll(), Async::NotReady); - assert_eq!(ls.listeners.len(), 1); // listener is still there - } - - #[test] - fn listener_stream_poll_with_ready_listeners_is_ready() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let expected_output = (peer_id.clone(), muxer.clone()); - - t.set_initial_listener_state(ListenerState::Events(vec![ - ListenerEvent::NewAddress(tcp4([127, 0, 0, 1], 9090)), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }, - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: tcp4([127, 0, 0, 1], 9090), - remote_addr: tcp4([127, 0, 0, 1], 32000) + match listeners.next().await.unwrap() { + ListenersEvent::Incoming { local_addr, upgrade, send_back_addr, .. } => { + assert_eq!(local_addr, address); + assert_eq!(send_back_addr, address); + }, + _ => panic!() } - ])); - - let mut ls = ListenersStream::new(t); - ls.listen_on(tcp4([127, 0, 0, 1], 1234)).expect("listen_on"); - ls.listen_on(tcp4([127, 0, 0, 1], 4321)).expect("listen_on"); - assert_eq!(ls.listeners.len(), 2); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::NewAddress { .. }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - - set_listener_state(&mut ls, 1, ListenerState::Ok(Async::NotReady)); - - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Incoming { upgrade, .. } => { - assert_matches!(upgrade.wait(), Ok(output) => { - assert_eq!(output, expected_output) - }); - }) - }); - } - - #[test] - fn listener_stream_poll_with_closed_listener_emits_closed_event() { - let t = DummyTransport::new(); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Ok(Async::Ready(None))); - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Closed{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - #[test] - fn listener_stream_poll_with_erroring_listener_emits_error_event() { - let mut t = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let event = ListenerEvent::Upgrade { - upgrade: (peer_id, muxer), - local_addr: tcp4([127, 0, 0, 1], 1234), - remote_addr: tcp4([127, 0, 0, 1], 32000) - }; - t.set_initial_listener_state(ListenerState::Ok(Async::Ready(Some(event)))); - let addr = tcp4([127, 0, 0, 1], 1234); - let mut ls = ListenersStream::new(t); - ls.listen_on(addr).expect("listen_on failed"); - set_listener_state(&mut ls, 0, ListenerState::Error); // simulate an error on the socket - assert_matches!(ls.by_ref().wait().next(), Some(Ok(listeners_event)) => { - assert_matches!(listeners_event, ListenersEvent::Error{..}) - }); - assert_eq!(ls.listeners.len(), 0); // it's gone - } - - fn tcp4(ip: [u8; 4], port: u16) -> Multiaddr { - let protos = std::iter::once(multiaddr::Protocol::Ip4(ip.into())) - .chain(std::iter::once(multiaddr::Protocol::Tcp(port))); - Multiaddr::from_iter(protos) } } diff --git a/core/src/nodes/network.rs b/core/src/nodes/network.rs index abe9e631..2f6634a1 100644 --- a/core/src/nodes/network.rs +++ b/core/src/nodes/network.rs @@ -49,10 +49,10 @@ use std::{ fmt, hash::Hash, num::NonZeroUsize, + pin::Pin, + task::{Context, Poll}, }; -pub use crate::nodes::collection::StartTakeOver; - mod tests; /// Implementation of `Stream` that handles the nodes. @@ -81,7 +81,7 @@ where /// If the pair's second element is `AsyncSink::Ready`, the take over /// message has been sent and needs to be flushed using /// `PeerMut::complete_take_over`. - take_over_to_complete: Option<(TPeerId, AsyncSink>)> + take_over_to_complete: Option<(TPeerId, InterruptedReachAttempt)> } impl fmt::Debug for @@ -102,6 +102,13 @@ where } } +impl Unpin for + Network +where + TTrans: Transport +{ +} + impl ConnectionInfo for (TConnInfo, ConnectedPoint) where TConnInfo: ConnectionInfo @@ -173,7 +180,7 @@ where /// The listener that errored. listener_id: ListenerId, /// The listener error. - error: ::Error + error: ::Error }, /// One of the listeners is now listening on an additional address. @@ -573,7 +580,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::ListenerUpgrade: Send + 'static, + TTrans::ListenerUpgrade: Unpin + Send + 'static, THandler: IntoNodeHandler<(TConnInfo, ConnectedPoint)> + Send + 'static, THandler::Handler: NodeHandler, InEvent = TInEvent, OutEvent = TOutEvent, Error = THandlerErr> + Send + 'static, ::OutboundOpenInfo: Send + 'static, // TODO: shouldn't be necessary @@ -609,9 +616,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -781,7 +788,7 @@ where where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TInEvent: Send + 'static, @@ -797,9 +804,9 @@ where let connected_point = connected_point.clone(); move |(peer_id, muxer)| { if *peer_id.peer_id() == local_peer_id { - Err(InternalReachErr::FoundLocalPeerId) + future::ready(Err(InternalReachErr::FoundLocalPeerId)) } else { - Ok(((peer_id, connected_point), muxer)) + future::ready(Ok(((peer_id, connected_point), muxer))) } } }); @@ -840,19 +847,18 @@ where /// Start sending an event to all nodes. /// - /// Make sure to complete the broadcast with `complete_broadcast`. - #[must_use] - pub fn start_broadcast(&mut self, event: &TInEvent) -> AsyncSink<()> + /// Must be called only after a successful call to `poll_ready_broadcast`. + pub fn start_broadcast(&mut self, event: &TInEvent) where TInEvent: Clone { self.active_nodes.start_broadcast(event) } - /// Complete a broadcast initiated with `start_broadcast`. + /// Wait until we have enough room in senders to broadcast an event. #[must_use] - pub fn complete_broadcast(&mut self) -> Async<()> { - self.active_nodes.complete_broadcast() + pub fn poll_ready_broadcast(&mut self, cx: &mut Context) -> Poll<()> { + self.active_nodes.poll_ready_broadcast(cx) } /// Returns a list of all the peers we are currently connected to. @@ -934,7 +940,7 @@ where fn start_dial_out(&mut self, peer_id: TPeerId, handler: THandler, first: Multiaddr, rest: Vec) where TTrans: Transport, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TTrans::Error: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -950,9 +956,9 @@ where .map_err(|err| InternalReachErr::Transport(TransportError::Other(err))) .and_then(move |(actual_conn_info, muxer)| { if *actual_conn_info.peer_id() == expected_peer_id { - Ok(((actual_conn_info, connected_point), muxer)) + future::ready(Ok(((actual_conn_info, connected_point), muxer))) } else { - Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info }) + future::ready(Err(InternalReachErr::PeerIdMismatch { obtained: actual_conn_info })) } }); self.active_nodes.add_reach_attempt(fut, handler) @@ -976,11 +982,12 @@ where } /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll(&mut self) -> Async> + pub fn poll<'a>(&'a mut self, cx: &mut Context) -> Poll> where TTrans: Transport, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, + TTrans::Listener: Unpin, TTrans::ListenerUpgrade: Send + 'static, TMuxer: Send + Sync + 'static, TMuxer::OutboundSubstream: Send, @@ -998,9 +1005,9 @@ where Some(x) if self.incoming_negotiated().count() >= (x as usize) => (), _ => { - match self.listeners.poll() { - Async::NotReady => (), - Async::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { + match ListenersStream::poll(Pin::new(&mut self.listeners), cx) { + Poll::Pending => (), + Poll::Ready(ListenersEvent::Incoming { listener_id, upgrade, local_addr, send_back_addr }) => { let event = IncomingConnectionEvent { listener_id, upgrade, @@ -1010,19 +1017,19 @@ where active_nodes: &mut self.active_nodes, other_reach_attempts: &mut self.reach_attempts.other_reach_attempts, }; - return Async::Ready(NetworkEvent::IncomingConnection(event)); + return Poll::Ready(NetworkEvent::IncomingConnection(event)); } - Async::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { - return Async::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { + return Poll::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) } - Async::Ready(ListenersEvent::Closed { listener_id, listener }) => { - return Async::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) + Poll::Ready(ListenersEvent::Closed { listener_id, listener }) => { + return Poll::Ready(NetworkEvent::ListenerClosed { listener_id, listener }) } - Async::Ready(ListenersEvent::Error { listener_id, error }) => { - return Async::Ready(NetworkEvent::ListenerError { listener_id, error }) + Poll::Ready(ListenersEvent::Error { listener_id, error }) => { + return Poll::Ready(NetworkEvent::ListenerError { listener_id, error }) } } } @@ -1031,36 +1038,30 @@ where // Attempt to deliver any pending take over messages. if let Some((id, interrupted)) = self.take_over_to_complete.take() { if let Some(mut peer) = self.active_nodes.peer_mut(&id) { - if let AsyncSink::NotReady(i) = interrupted { - if let StartTakeOver::NotReady(i) = peer.start_take_over(i) { - self.take_over_to_complete = Some((id, AsyncSink::NotReady(i))) - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) - } - } else if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((id, AsyncSink::Ready)) + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((id, interrupted)); + return Poll::Pending; } } } - if self.take_over_to_complete.is_some() { - return Async::NotReady - } // Poll the existing nodes. let (action, out_event); - match self.active_nodes.poll() { - Async::NotReady => return Async::NotReady, - Async::Ready(CollectionEvent::NodeReached(reach_event)) => { + match self.active_nodes.poll(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(CollectionEvent::NodeReached(reach_event)) => { let (a, e) = handle_node_reached(&mut self.reach_attempts, reach_event); action = a; out_event = e; } - Async::Ready(CollectionEvent::ReachError { id, error, handler }) => { + Poll::Ready(CollectionEvent::ReachError { id, error, handler }) => { let (a, e) = handle_reach_error(&mut self.reach_attempts, id, error, handler); action = a; out_event = e; } - Async::Ready(CollectionEvent::NodeClosed { + Poll::Ready(CollectionEvent::NodeClosed { conn_info, error, .. @@ -1078,7 +1079,7 @@ where error, }; } - Async::Ready(CollectionEvent::NodeEvent { peer, event }) => { + Poll::Ready(CollectionEvent::NodeEvent { peer, event }) => { action = Default::default(); out_event = NetworkEvent::NodeEvent { conn_info: peer.info().0.clone(), event }; } @@ -1099,17 +1100,15 @@ where out_reach_attempts should always be in sync with the actual \ attempts; QED"); let mut peer = self.active_nodes.peer_mut(&peer_id).unwrap(); - if let StartTakeOver::NotReady(i) = peer.start_take_over(interrupted) { - self.take_over_to_complete = Some((peer_id, AsyncSink::NotReady(i))); - return Async::NotReady - } - if let Ok(Async::NotReady) = peer.complete_take_over() { - self.take_over_to_complete = Some((peer_id, AsyncSink::Ready)); - return Async::NotReady + if let Poll::Ready(()) = peer.poll_ready_take_over(cx) { + peer.start_take_over(interrupted); + } else { + self.take_over_to_complete = Some((peer_id, interrupted)); + return Poll::Pending } } - Async::Ready(out_event) + Poll::Ready(out_event) } } @@ -1467,7 +1466,7 @@ impl<'a, TTrans, TMuxer, TInEvent, TOutEvent, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, @@ -1644,18 +1643,33 @@ where closed messages; QED") } - /// Start sending an event to the node. - pub fn start_send_event(&mut self, event: TInEvent) -> StartSend { + /// Sends an event to the handler of the node. + pub fn send_event<'s: 'a>(&'s mut self, event: TInEvent) -> impl Future + 's + 'a { + let mut event = Some(event); + futures::future::poll_fn(move |cx| { + match self.poll_ready_event(cx) { + Poll::Ready(()) => { + self.start_send_event(event.take().expect("Future called after finished")); + Poll::Ready(()) + }, + Poll::Pending => Poll::Pending, + } + }) + } + + /// Begin sending an event to the node. Must be called only after a successful call to + /// `poll_ready_event`. + pub fn start_send_event(&mut self, event: TInEvent) { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") .start_send_event(event) } - /// Complete sending an event message, initiated by `start_send_event`. - pub fn complete_send_event(&mut self) -> Poll<(), ()> { + /// Make sure we are ready to accept an event to be sent with `start_send_event`. + pub fn poll_ready_event(&mut self, cx: &mut Context) -> Poll<()> { self.active_nodes.peer_mut(&self.peer_id) .expect("A PeerConnected is always created with a PeerId in active_nodes; QED") - .complete_send_event() + .poll_ready_event(cx) } } @@ -1749,7 +1763,7 @@ impl<'a, TTrans, TInEvent, TOutEvent, TMuxer, THandler, THandlerErr, TConnInfo, where TTrans: Transport + Clone, TTrans::Error: Send + 'static, - TTrans::Dial: Send + 'static, + TTrans::Dial: Unpin + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, TMuxer::Substream: Send, diff --git a/core/src/nodes/network/tests.rs b/core/src/nodes/network/tests.rs index c64666aa..c4f307bb 100644 --- a/core/src/nodes/network/tests.rs +++ b/core/src/nodes/network/tests.rs @@ -21,363 +21,6 @@ #![cfg(test)] use super::*; -use crate::tests::dummy_transport::DummyTransport; -use crate::tests::dummy_handler::{Handler, HandlerState, InEvent, OutEvent}; -use crate::tests::dummy_transport::ListenerState; -use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; -use crate::nodes::NodeHandlerEvent; -use crate::transport::ListenerEvent; -use assert_matches::assert_matches; -use parking_lot::Mutex; -use std::sync::Arc; -use tokio::runtime::{Builder, Runtime}; - -#[test] -fn query_transport() { - let transport = DummyTransport::new(); - let transport2 = transport.clone(); - let network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - assert_eq!(network.transport(), &transport2); -} - -#[test] -fn local_node_peer() { - let peer_id = PeerId::random(); - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), peer_id.clone()); - assert_matches!(network.peer(peer_id), Peer::LocalNode); -} - -#[test] -fn successful_dial_reaches_a_node() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let dial_res = network.dial(addr, Handler::default()); - assert!(dial_res.is_ok()); - - // Poll the network until we get a `NodeReached` then assert on the peer: - // it's there and it's connected. - let network = Arc::new(Mutex::new(network)); - - let mut rt = Runtime::new().unwrap(); - let mut peer_id : Option = None; - // Drive forward until we're Connected - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected(PeerConnected{..})); -} - -#[test] -fn num_incoming_negotiated() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - - let events = vec![ - ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap()), - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ]; - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - // no incoming yet - assert_eq!(network.incoming_negotiated().count(), 0); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - // Now there's an incoming connection - assert_eq!(network.incoming_negotiated().count(), 1); -} - -#[test] -fn broadcasted_events_reach_active_nodes() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let mut muxer = DummyMuxer::new(); - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - muxer.set_outbound_connection_state(DummyConnectionState::Opened); - let addr = "/ip4/127.0.0.1/tcp/1234".parse::().expect("bad multiaddr"); - let mut handler = Handler::default(); - handler.next_states = vec![HandlerState::Ready(NodeHandlerEvent::Custom(OutEvent::Custom("from handler 1") )),]; - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::NodeEvent { conn_info: _, event: inner_event } => { - // The event we sent reached the node and triggered sending the out event we told it to return - assert_matches!(inner_event, OutEvent::Custom("from handler 1")); - }); - Ok(Async::Ready(false)) - }, - _ => Ok(Async::Ready(true)) - } - })).expect("tokio works"); - } -} - -#[test] -fn querying_for_pending_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); -} - -#[test] -fn querying_for_unknown_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - let peer_id = PeerId::random(); - let peer = network.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected( PeerNotConnected { nodes: _, peer_id: node_peer_id }) => { - assert_eq!(node_peer_id, peer_id); - }); -} - -#[test] -fn querying_for_connected_peer() { - let mut network = Network::<_, _, _, Handler, _>::new(DummyTransport::new(), PeerId::random()); - - // Dial a node - let addr = "/ip4/127.0.0.1/tcp/1234".parse().expect("bad multiaddr"); - network.dial(addr, Handler::default()).expect("dialing works"); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we connect; extract the new PeerId. - let mut peer_id : Option = None; - while peer_id.is_none() { - let network_fut = network.clone(); - peer_id = rt.block_on(future::poll_fn(move || -> Poll, ()> { - let mut network = network_fut.lock(); - let poll_res = network.poll(); - match poll_res { - Async::Ready(NetworkEvent::Connected { conn_info, .. }) => Ok(Async::Ready(Some(conn_info))), - _ => Ok(Async::Ready(None)) - } - })).expect("tokio works"); - } - - // We're connected. - let mut network = network.lock(); - let peer = network.peer(peer_id.unwrap()); - assert_matches!(peer, Peer::Connected( PeerConnected { .. } )); -} - -#[test] -fn poll_with_closed_listener() { - let mut transport = DummyTransport::new(); - // Set up listener to be closed - transport.set_initial_listener_state(ListenerState::Ok(Async::Ready(None))); - - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - - let mut rt = Runtime::new().unwrap(); - let network = Arc::new(Mutex::new(network)); - - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::ListenerClosed { .. } )); - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); -} - -#[test] -fn unknown_peer_that_is_unreachable_yields_unknown_peer_dial_error() { - let mut transport = DummyTransport::new(); - transport.make_dial_fail(); - let mut network = Network::<_, _, _, Handler, _>::new(transport, PeerId::random()); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let handler = Handler::default(); - let dial_result = network.dial(addr, handler); - assert!(dial_result.is_ok()); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::UnknownPeerDialError { .. } ); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn known_peer_that_is_unreachable_yields_dial_error() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - transport.make_dial_fail(); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - assert_matches!(peer, Peer::NotConnected(PeerNotConnected{ .. })); - let addr = "/memory/0".parse::().expect("bad multiaddr"); - let pending_peer = peer.into_not_connected().unwrap().connect(addr, Handler::default()); - assert_matches!(pending_peer, PeerPendingConnect { .. }); - } - let mut rt = Runtime::new().unwrap(); - // Drive it forward until we hear back from the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let peer_id = peer_id.clone(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - let failed_peer_id = assert_matches!( - event, - NetworkEvent::DialError { new_state: _, peer_id: failed_peer_id, .. } => failed_peer_id - ); - assert_eq!(peer_id, failed_peer_id); - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } -} - -#[test] -fn yields_node_error_when_there_is_an_error_after_successful_connect() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - transport.set_next_peer_id(&peer_id); - let network = Arc::new(Mutex::new(Network::<_, _, _, Handler, _>::new(transport, PeerId::random()))); - - { - // Set up an outgoing connection with a PeerId we know - let network1 = network.clone(); - let mut network1 = network1.lock(); - let peer = network1.peer(peer_id.clone()); - let addr = "/unix/reachable".parse().expect("bad multiaddr"); - let mut handler = Handler::default(); - // Force an error - handler.next_states = vec![ HandlerState::Err ]; - peer.into_not_connected().unwrap().connect(addr, handler); - } - - // Ensure we run on a single thread - let mut rt = Builder::new().core_threads(1).build().unwrap(); - - // Drive it forward until we connect to the node. - let mut keep_polling = true; - while keep_polling { - let network_fut = network.clone(); - let network2 = network.clone(); - rt.block_on(future::poll_fn(move || { - if network2.lock().start_broadcast(&InEvent::NextState).is_not_ready() { - Ok::<_, ()>(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - })).unwrap(); - keep_polling = rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - // Push the Handler into an error state on the next poll - if network.complete_broadcast().is_not_ready() { - return Ok(Async::NotReady) - } - match network.poll() { - Async::NotReady => Ok(Async::Ready(true)), - Async::Ready(event) => { - assert_matches!(event, NetworkEvent::Connected { .. }); - // We're connected, we can move on - Ok(Async::Ready(false)) - }, - } - })).expect("tokio works"); - } - - // Poll again. It is going to be a NodeClosed because of how the - // handler's next state was set up. - let network_fut = network.clone(); - let expected_peer_id = peer_id.clone(); - rt.block_on(future::poll_fn(move || -> Poll<_, ()> { - let mut network = network_fut.lock(); - assert_matches!(network.poll(), Async::Ready(NetworkEvent::NodeClosed { conn_info, .. }) => { - assert_eq!(conn_info, expected_peer_id); - }); - Ok(Async::Ready(())) - })).expect("tokio works"); -} #[test] fn local_prio_equivalence_relation() { @@ -387,59 +30,3 @@ fn local_prio_equivalence_relation() { assert_ne!(has_dial_prio(&a, &b), has_dial_prio(&b, &a)); } } - -#[test] -fn limit_incoming_connections() { - let mut transport = DummyTransport::new(); - let peer_id = PeerId::random(); - let muxer = DummyMuxer::new(); - let limit = 1; - - let mut events = vec![ListenerEvent::NewAddress("/ip4/127.0.0.1/tcp/1234".parse().unwrap())]; - events.extend(std::iter::repeat( - ListenerEvent::Upgrade { - upgrade: (peer_id.clone(), muxer.clone()), - local_addr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), - remote_addr: "/ip4/127.0.0.1/tcp/32111".parse().unwrap() - } - ).take(10)); - transport.set_initial_listener_state(ListenerState::Events(events)); - - let mut network = Network::<_, _, _, Handler, _>::new_with_incoming_limit(transport, PeerId::random(), Some(limit)); - assert_eq!(network.incoming_limit(), Some(limit)); - network.listen_on("/memory/0".parse().unwrap()).unwrap(); - assert_eq!(network.incoming_negotiated().count(), 0); - - let network = Arc::new(Mutex::new(network)); - let mut rt = Runtime::new().unwrap(); - for i in 1..10 { - let network_fut = network.clone(); - let fut = future::poll_fn(move || -> Poll<_, ()> { - let mut network_fut = network_fut.lock(); - if i <= limit { - assert_matches!(network_fut.poll(), Async::Ready(NetworkEvent::NewListenerAddress {..})); - assert_matches!(network_fut.poll(), - Async::Ready(NetworkEvent::IncomingConnection(incoming)) => { - incoming.accept(Handler::default()); - }); - } else { - match network_fut.poll() { - Async::NotReady => (), - Async::Ready(x) => { - match x { - NetworkEvent::NewListenerAddress {..} => {} - NetworkEvent::ExpiredListenerAddress {..} => {} - NetworkEvent::IncomingConnection(_) => {} - NetworkEvent::Connected {..} => {} - e => panic!("Not expected event: {:?}", e) - } - }, - } - } - Ok(Async::Ready(())) - }); - rt.block_on(fut).expect("tokio works"); - let network = network.lock(); - assert!(network.incoming_negotiated().count() <= (limit as usize)); - } -} diff --git a/core/src/nodes/node.rs b/core/src/nodes/node.rs index a1d0eac4..37da9954 100644 --- a/core/src/nodes/node.rs +++ b/core/src/nodes/node.rs @@ -21,9 +21,7 @@ use futures::prelude::*; use crate::muxing; use smallvec::SmallVec; -use std::fmt; -use std::io::Error as IoError; -use std::sync::Arc; +use std::{fmt, io::Error as IoError, pin::Pin, sync::Arc, task::Context, task::Poll}; // Implementation notes // ================= @@ -143,43 +141,44 @@ where } /// Provides an API similar to `Future`. - pub fn poll(&mut self) -> Poll, IoError> { + pub fn poll(&mut self, cx: &mut Context) -> Poll, IoError>> { // Polling inbound substream. - match self.muxer.poll_inbound().map_err(|e| e.into())? { - Async::Ready(substream) => { + match self.muxer.poll_inbound(cx) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); - return Ok(Async::Ready(NodeEvent::InboundSubstream { + return Poll::Ready(Ok(NodeEvent::InboundSubstream { substream, })); } - Async::NotReady => {} + Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), + Poll::Pending => {} } // Polling outbound substreams. // We remove each element from `outbound_substreams` one by one and add them back. for n in (0..self.outbound_substreams.len()).rev() { let (user_data, mut outbound) = self.outbound_substreams.swap_remove(n); - match self.muxer.poll_outbound(&mut outbound) { - Ok(Async::Ready(substream)) => { + match self.muxer.poll_outbound(cx, &mut outbound) { + Poll::Ready(Ok(substream)) => { let substream = muxing::substream_from_ref(self.muxer.clone(), substream); self.muxer.destroy_outbound(outbound); - return Ok(Async::Ready(NodeEvent::OutboundSubstream { + return Poll::Ready(Ok(NodeEvent::OutboundSubstream { user_data, substream, })); } - Ok(Async::NotReady) => { + Poll::Pending => { self.outbound_substreams.push((user_data, outbound)); } - Err(err) => { + Poll::Ready(Err(err)) => { self.muxer.destroy_outbound(outbound); - return Err(err.into()); + return Poll::Ready(Err(err.into())); } } } // Nothing happened. Register our task to be notified and return. - Ok(Async::NotReady) + Poll::Pending } } @@ -212,11 +211,14 @@ impl Future for Close where TMuxer: muxing::StreamMuxer, { - type Item = (); - type Error = IoError; + type Output = Result<(), IoError>; - fn poll(&mut self) -> Poll { - self.muxer.close().map_err(|e| e.into()) + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match self.muxer.close(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(err)) => Poll::Ready(Err(err.into())), + } } } @@ -252,70 +254,3 @@ where } } } - -#[cfg(test)] -mod node_stream { - use super::{NodeEvent, NodeStream}; - use crate::tests::dummy_muxer::{DummyMuxer, DummyConnectionState}; - use assert_matches::assert_matches; - use futures::prelude::*; - use tokio_mock_task::MockTask; - - fn build_node_stream() -> NodeStream> { - let muxer = DummyMuxer::new(); - NodeStream::<_, Vec>::new(muxer) - } - - #[test] - fn closing_a_node_stream_destroys_substreams_and_returns_submitted_user_data() { - let mut ns = build_node_stream(); - ns.open_substream(vec![2]); - ns.open_substream(vec![3]); - ns.open_substream(vec![5]); - let user_data_submitted = ns.close(); - assert_eq!(user_data_submitted.1, vec![ - vec![2], vec![3], vec![5] - ]); - } - - #[test] - fn poll_returns_not_ready_when_there_is_nothing_to_do() { - let mut task = MockTask::new(); - task.enter(|| { - // ensure the address never resolves - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - - assert_matches!(ns.poll(), Ok(Async::NotReady)); - }); - } - - #[test] - fn poll_keeps_outbound_substreams_when_the_outgoing_connection_is_not_ready() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::NotReady - muxer.set_inbound_connection_state(DummyConnectionState::Pending); - // ensure muxer.poll_outbound() returns Async::NotReady - muxer.set_outbound_connection_state(DummyConnectionState::Pending); - let mut ns = NodeStream::<_, Vec>::new(muxer); - ns.open_substream(vec![1]); - ns.poll().unwrap(); // poll past inbound - ns.poll().unwrap(); // poll outbound - assert!(format!("{:?}", ns).contains("outbound_substreams: 1")); - } - - #[test] - fn poll_returns_incoming_substream() { - let mut muxer = DummyMuxer::new(); - // ensure muxer.poll_inbound() returns Async::Ready(subs) - muxer.set_inbound_connection_state(DummyConnectionState::Opened); - let mut ns = NodeStream::<_, Vec>::new(muxer); - assert_matches!(ns.poll(), Ok(Async::Ready(node_event)) => { - assert_matches!(node_event, NodeEvent::InboundSubstream{ substream: _ }); - }); - } -} diff --git a/core/src/nodes/tasks/manager.rs b/core/src/nodes/tasks/manager.rs index 33643aaa..aff72bd9 100644 --- a/core/src/nodes/tasks/manager.rs +++ b/core/src/nodes/tasks/manager.rs @@ -27,9 +27,8 @@ use crate::{ } }; use fnv::FnvHashMap; -use futures::{prelude::*, future::Executor, sync::mpsc}; -use smallvec::SmallVec; -use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt}; +use futures::{prelude::*, channel::mpsc, executor::ThreadPool, stream::FuturesUnordered, task::SpawnExt as _}; +use std::{collections::hash_map::{Entry, OccupiedEntry}, error, fmt, pin::Pin, task::Context, task::Poll}; use super::{TaskId, task::{Task, FromTaskMessage, ToTaskMessage}, Error}; // Implementor notes @@ -64,12 +63,13 @@ pub struct Manager { /// Identifier for the next task to spawn. next_task_id: TaskId, - /// List of node tasks to spawn. - to_spawn: SmallVec<[Box + Send>; 8]>, + /// Threads pool where we spawn the nodes' tasks. If `None`, then we push tasks to the + /// `local_spawns` list instead. + threads_pool: Option, - /// If no tokio executor is available, we move tasks to this list, and futures are polled on - /// the current thread instead. - local_spawns: Vec + Send>>, + /// If no executor is available, we move tasks to this list, and futures are polled on the + /// current thread instead. + local_spawns: FuturesUnordered + Send>>>, /// Sender to emit events to the outside. Meant to be cloned and sent to tasks. events_tx: mpsc::Sender<(FromTaskMessage, TaskId)>, @@ -91,16 +91,13 @@ where /// Information about a running task. /// -/// Contains the sender to deliver event messages to the task, -/// the associated user data and a pending message if any, -/// meant to be delivered to the task via the sender. +/// Contains the sender to deliver event messages to the task, and +/// the associated user data. struct TaskInfo