Switch consensus crates to new futures (#3146)

* Switch consensus-common to new futures

* Fix tests

* More tests fixing

* Fix Babe tests

* Fix Babe tests
This commit is contained in:
Pierre Krieger
2019-07-25 04:55:50 +02:00
committed by Gavin Wood
parent 3a6a309d84
commit b31dcdf342
21 changed files with 208 additions and 184 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ pub enum Error {
IoTerminated,
/// Unable to schedule wakeup.
#[display(fmt="Timer error: {}", _0)]
FaultyTimer(tokio_timer::Error),
FaultyTimer(std::io::Error),
/// Error while working with inherent data.
#[display(fmt="InherentData error: {}", _0)]
InherentData(String),
@@ -106,8 +106,8 @@ pub trait ImportQueue<B: BlockT>: Send {
///
/// This method should behave in a way similar to `Future::poll`. It can register the current
/// task and notify later when more actions are ready to be polled. To continue the comparison,
/// it is as if this method always returned `Ok(Async::NotReady)`.
fn poll_actions(&mut self, link: &mut dyn Link<B>);
/// it is as if this method always returned `Poll::Pending`.
fn poll_actions(&mut self, cx: &mut futures::task::Context, link: &mut dyn Link<B>);
}
/// Hooks that the verification queue can use to influence the synchronization
@@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use futures::{prelude::*, future::Executor, sync::mpsc};
use std::{pin::Pin, sync::Arc};
use futures::{prelude::*, channel::mpsc, task::SpawnExt as _, task::Context, task::Poll};
use runtime_primitives::{Justification, traits::{Block as BlockT, Header as HeaderT, NumberFor}};
use crate::error::Error as ConsensusError;
@@ -34,15 +34,12 @@ pub struct BasicQueue<B: BlockT> {
sender: mpsc::UnboundedSender<ToWorkerMsg<B>>,
/// Results coming from the worker task.
result_port: BufferedLinkReceiver<B>,
/// Since we have to be in a tokio context in order to spawn background tasks, we first store
/// the task to spawn here, then extract it as soon as we are in a tokio context.
/// If `Some`, contains the task to spawn in the background. If `None`, the future has already
/// been spawned.
future_to_spawn: Option<Box<dyn Future<Item = (), Error = ()> + Send>>,
/// If it isn't possible to spawn the future in `future_to_spawn` (which is notably the case in
/// "no std" environment), we instead put it in `manual_poll`. It is then polled manually from
/// `poll_actions`.
manual_poll: Option<Box<dyn Future<Item = (), Error = ()> + Send>>,
manual_poll: Option<Pin<Box<dyn Future<Output = ()> + Send>>>,
/// A thread pool where the background worker is being run.
pool: Option<futures::executor::ThreadPool>,
}
impl<B: BlockT> BasicQueue<B> {
@@ -65,11 +62,27 @@ impl<B: BlockT> BasicQueue<B> {
finality_proof_import,
);
let mut pool = futures::executor::ThreadPool::builder()
.name_prefix("import-queue-worker-")
.pool_size(1)
.create()
.ok();
let manual_poll;
if let Some(pool) = &mut pool {
// TODO: this expect() can be removed once
// https://github.com/rust-lang-nursery/futures-rs/pull/1750 is merged and deployed
pool.spawn(future).expect("ThreadPool can never fail to spawn tasks; QED");
manual_poll = None;
} else {
manual_poll = Some(Box::pin(future) as Pin<Box<_>>);
}
Self {
sender: worker_sender,
result_port,
future_to_spawn: Some(Box::new(future)),
manual_poll: None,
manual_poll,
pool,
}
}
}
@@ -99,25 +112,17 @@ impl<B: BlockT> ImportQueue<B> for BasicQueue<B> {
let _ = self.sender.unbounded_send(ToWorkerMsg::ImportFinalityProof(who, hash, number, finality_proof));
}
fn poll_actions(&mut self, link: &mut dyn Link<B>) {
// Try to spawn the future in `future_to_spawn`.
if let Some(future) = self.future_to_spawn.take() {
if let Err(err) = tokio_executor::DefaultExecutor::current().execute(future) {
debug_assert!(self.manual_poll.is_none());
self.manual_poll = Some(err.into_future());
}
}
fn poll_actions(&mut self, cx: &mut Context, link: &mut dyn Link<B>) {
// As a backup mechanism, if we failed to spawn the `future_to_spawn`, we instead poll
// manually here.
if let Some(manual_poll) = self.manual_poll.as_mut() {
match manual_poll.poll() {
Ok(Async::NotReady) => {}
match Future::poll(Pin::new(manual_poll), cx) {
Poll::Pending => {}
_ => self.manual_poll = None,
}
}
self.result_port.poll_actions(link);
self.result_port.poll_actions(cx, link);
}
}
@@ -144,7 +149,7 @@ impl<B: BlockT, V: 'static + Verifier<B>> BlockImportWorker<B, V> {
block_import: BoxBlockImport<B>,
justification_import: Option<BoxJustificationImport<B>>,
finality_proof_import: Option<BoxFinalityProofImport<B>>,
) -> (impl Future<Item = (), Error = ()> + Send, mpsc::UnboundedSender<ToWorkerMsg<B>>) {
) -> (impl Future<Output = ()> + Send, mpsc::UnboundedSender<ToWorkerMsg<B>>) {
let (sender, mut port) = mpsc::unbounded();
let mut worker = BlockImportWorker {
@@ -167,12 +172,12 @@ impl<B: BlockT, V: 'static + Verifier<B>> BlockImportWorker<B, V> {
}
}
let future = futures::future::poll_fn(move || {
let future = futures::future::poll_fn(move |cx| {
loop {
let msg = match port.poll() {
Ok(Async::Ready(Some(msg))) => msg,
Err(_) | Ok(Async::Ready(None)) => return Ok(Async::Ready(())),
Ok(Async::NotReady) => return Ok(Async::NotReady),
let msg = match Stream::poll_next(Pin::new(&mut port), cx) {
Poll::Ready(Some(msg)) => msg,
Poll::Ready(None) => return Poll::Ready(()),
Poll::Pending => return Poll::Pending,
};
match msg {
@@ -20,7 +20,7 @@
//!
//! # Example
//!
//! ```no_run
//! ```
//! use substrate_consensus_common::import_queue::Link;
//! # use substrate_consensus_common::import_queue::buffered_link::buffered_link;
//! # use test_client::runtime::Block;
@@ -28,12 +28,18 @@
//! # let mut my_link = DummyLink;
//! let (mut tx, mut rx) = buffered_link::<Block>();
//! tx.blocks_processed(0, 0, vec![]);
//! rx.poll_actions(&mut my_link); // Calls `my_link.blocks_processed(0, 0, vec![])`
//!
//! // Calls `my_link.blocks_processed(0, 0, vec![])` when polled.
//! let _fut = futures::future::poll_fn(move |cx| {
//! rx.poll_actions(cx, &mut my_link);
//! std::task::Poll::Pending::<()>
//! });
//! ```
//!
use futures::{prelude::*, sync::mpsc};
use futures::{prelude::*, channel::mpsc};
use runtime_primitives::traits::{Block as BlockT, NumberFor};
use std::{pin::Pin, task::Context, task::Poll};
use crate::import_queue::{Origin, Link, BlockImportResult, BlockImportError};
/// Wraps around an unbounded channel from the `futures` crate. The sender implements `Link` and
@@ -120,10 +126,10 @@ impl<B: BlockT> BufferedLinkReceiver<B> {
///
/// This method should behave in a way similar to `Future::poll`. It can register the current
/// task and notify later when more actions are ready to be polled. To continue the comparison,
/// it is as if this method always returned `Ok(Async::NotReady)`.
pub fn poll_actions(&mut self, link: &mut dyn Link<B>) {
/// it is as if this method always returned `Poll::Pending`.
pub fn poll_actions(&mut self, cx: &mut Context, link: &mut dyn Link<B>) {
loop {
let msg = if let Ok(Async::Ready(Some(msg))) = self.rx.poll() {
let msg = if let Poll::Ready(Some(msg)) = Stream::poll_next(Pin::new(&mut self.rx), cx) {
msg
} else {
break
+1 -1
View File
@@ -75,7 +75,7 @@ pub trait Proposer<B: BlockT> {
/// Error type which can occur when proposing or evaluating.
type Error: From<Error> + ::std::fmt::Debug + 'static;
/// Future that resolves to a committed proposal.
type Create: IntoFuture<Item=B, Error=Self::Error>;
type Create: Future<Output = Result<B, Self::Error>>;
/// Create a proposal.
fn propose(
&self,