Switch GrandPa to std futures (replaces #3909) (#4612)

* Switch GrandPa to new futures

* Work on making tests work

* until_imported tests working again

* Work on switching tests to stable futures

* Modifications

* Re-add test as #[ignore]

* Don't ignore

* Add manual unpins

* Remove Header import

* Return concrete Sink type

* Switch to crates.io finality-grandpa version

* Remove use statement that slipped in

* Fix some nitpicks

* Remove unpin from i

* Fixed typo

* Move futures01 to dev-deps

* Fix nitpicks

* Update client/finality-grandpa/src/communication/mod.rs

Co-Authored-By: André Silva <andre.beat@gmail.com>

* nitpicking

Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>
Co-authored-by: André Silva <andre.beat@gmail.com>
This commit is contained in:
Ashley
2020-01-24 13:34:42 +01:00
committed by GitHub
parent 14e95f3398
commit c2c429877e
16 changed files with 468 additions and 513 deletions
@@ -14,10 +14,11 @@
// 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::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use futures::prelude::*;
use futures::{future, sync::mpsc};
use futures::{prelude::*, channel::mpsc};
use finality_grandpa::{
BlockNumberOps, Error as GrandpaError, voter, voter_set::VoterSet
@@ -64,14 +65,13 @@ fn grandpa_observer<B, E, Block: BlockT, RA, S, F>(
last_finalized_number: NumberFor<Block>,
commits: S,
note_round: F,
) -> impl Future<Item=(), Error=CommandOrError<Block::Hash, NumberFor<Block>>> where
) -> impl Future<Output=Result<(), CommandOrError<Block::Hash, NumberFor<Block>>>> where
NumberFor<Block>: BlockNumberOps,
B: Backend<Block>,
E: CallExecutor<Block> + Send + Sync + 'static,
RA: Send + Sync,
S: Stream<
Item = CommunicationIn<Block>,
Error = CommandOrError<Block::Hash, NumberFor<Block>>,
Item = Result<CommunicationIn<Block>, CommandOrError<Block::Hash, NumberFor<Block>>>,
>,
F: Fn(u64),
{
@@ -80,7 +80,7 @@ fn grandpa_observer<B, E, Block: BlockT, RA, S, F>(
let client = client.clone();
let voters = voters.clone();
let observer = commits.fold(last_finalized_number, move |last_finalized_number, global| {
let observer = commits.try_fold(last_finalized_number, move |last_finalized_number, global| {
let (round, commit, callback) = match global {
voter::CommunicationIn::Commit(round, commit, callback) => {
let commit = finality_grandpa::Commit::from(commit);
@@ -143,7 +143,7 @@ fn grandpa_observer<B, E, Block: BlockT, RA, S, F>(
}
});
observer.map(|_| ())
observer.map_ok(|_| ())
}
/// Run a GRANDPA observer as a task, the observer will finalize blocks only by
@@ -154,16 +154,16 @@ pub fn run_grandpa_observer<B, E, Block: BlockT, N, RA, SC, Sp>(
config: Config,
link: LinkHalf<B, E, Block, RA, SC>,
network: N,
on_exit: impl futures03::Future<Output=()> + Clone + Send + Unpin + 'static,
on_exit: impl futures::Future<Output=()> + Clone + Send + Unpin + 'static,
executor: Sp,
) -> sp_blockchain::Result<impl Future<Item=(), Error=()> + Send + 'static> where
) -> sp_blockchain::Result<impl Future<Output = ()> + Unpin + Send + 'static> where
B: Backend<Block> + 'static,
E: CallExecutor<Block> + Send + Sync + 'static,
N: NetworkT<Block> + Send + Clone + 'static,
SC: SelectChain<Block> + 'static,
NumberFor<Block>: BlockNumberOps,
RA: Send + Sync + 'static,
Sp: futures03::task::Spawn + 'static,
Sp: futures::task::Spawn + 'static,
Client<B, E, Block, RA>: AuxStore,
{
let LinkHalf {
@@ -189,20 +189,18 @@ pub fn run_grandpa_observer<B, E, Block: BlockT, N, RA, SC, Sp>(
);
let observer_work = observer_work
.map(|_| ())
.map_ok(|_| ())
.map_err(|e| {
warn!("GRANDPA Observer failed: {:?}", e);
});
use futures03::{FutureExt, TryFutureExt};
Ok(observer_work.select(on_exit.map(Ok).compat()).map(|_| ()).map_err(|_| ()))
Ok(future::select(observer_work, on_exit).map(drop))
}
/// Future that powers the observer.
#[must_use]
struct ObserverWork<B: BlockT, N: NetworkT<B>, E, Backend, RA> {
observer: Box<dyn Future<Item = (), Error = CommandOrError<B::Hash, NumberFor<B>>> + Send>,
observer: Pin<Box<dyn Future<Output = Result<(), CommandOrError<B::Hash, NumberFor<B>>>> + Send>>,
client: Arc<Client<Backend, E, B, RA>>,
network: NetworkBridge<B, N>,
persistent_data: PersistentData<B>,
@@ -231,7 +229,7 @@ where
let mut work = ObserverWork {
// `observer` is set to a temporary value and replaced below when
// calling `rebuild_observer`.
observer: Box::new(futures::empty()) as Box<_>,
observer: Box::pin(future::pending()) as Pin<Box<_>>,
client,
network,
persistent_data,
@@ -286,7 +284,7 @@ where
note_round,
);
self.observer = Box::new(observer);
self.observer = Box::pin(observer);
}
fn handle_voter_command(
@@ -336,44 +334,41 @@ where
Bk: Backend<B> + 'static,
Client<Bk, E, B, RA>: AuxStore,
{
type Item = ();
type Error = Error;
type Output = Result<(), Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.observer.poll() {
Ok(Async::NotReady) => {}
Ok(Async::Ready(())) => {
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = Pin::into_inner(self);
match Future::poll(Pin::new(&mut this.observer), cx) {
Poll::Pending => {}
Poll::Ready(Ok(())) => {
// observer commit stream doesn't conclude naturally; this could reasonably be an error.
return Ok(Async::Ready(()))
return Poll::Ready(Ok(()))
}
Err(CommandOrError::Error(e)) => {
Poll::Ready(Err(CommandOrError::Error(e))) => {
// return inner observer error
return Err(e)
return Poll::Ready(Err(e))
}
Err(CommandOrError::VoterCommand(command)) => {
Poll::Ready(Err(CommandOrError::VoterCommand(command))) => {
// some command issued internally
self.handle_voter_command(command)?;
futures::task::current().notify();
this.handle_voter_command(command)?;
cx.waker().wake_by_ref();
}
}
match self.voter_commands_rx.poll() {
Ok(Async::NotReady) => {}
Err(_) => {
// the `voter_commands_rx` stream should not fail.
return Ok(Async::Ready(()))
}
Ok(Async::Ready(None)) => {
match Stream::poll_next(Pin::new(&mut this.voter_commands_rx), cx) {
Poll::Pending => {}
Poll::Ready(None) => {
// the `voter_commands_rx` stream should never conclude since it's never closed.
return Ok(Async::Ready(()))
return Poll::Ready(Ok(()))
}
Ok(Async::Ready(Some(command))) => {
Poll::Ready(Some(command)) => {
// some command issued externally
self.handle_voter_command(command)?;
futures::task::current().notify();
this.handle_voter_command(command)?;
cx.waker().wake_by_ref();
}
}
Ok(Async::NotReady)
Poll::Pending
}
}