client/network-gossip: Integrate GossipEngine tasks into Future impl (#4767)

`GossipEngine` spawns two tasks, one for a periodic tick, one to forward
messages from the network to subscribers. These tasks hold an `Arc` to a
`GossipEngineInner`.

To reduce the amount of shared ownership (locking) this patch integrates
the two tasks into a `Future` implementation on the `GossipEngine`
struct. This `Future` implementation can now be called from a single
owner, e.g. the `finality-grandpa` `NetworkBridge`.

As a side effect this removes the requirement on the `network-gossip`
crate to spawn tasks and thereby removes the requirement on the
`finality-grandpa` crate to spawn any tasks.

This is part of a greater effort to reduce the number of owners of
components within `finality-grandpa`, `network` and `network-gossip` as
well as to reduce the amount of unbounded channels. For details see
d9837d7dd, 5f80929dc and 597c0a6c4.
This commit is contained in:
Max Inden
2020-02-12 13:15:26 +01:00
committed by GitHub
parent 13971fe2a7
commit 3f3910ccaf
9 changed files with 121 additions and 155 deletions
@@ -178,7 +178,6 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
service: N,
config: crate::Config,
set_state: crate::environment::SharedVoterSetState<B>,
executor: &impl futures::task::Spawn,
) -> Self {
let (validator, report_stream) = GossipValidator::new(
config,
@@ -186,7 +185,7 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
);
let validator = Arc::new(validator);
let gossip_engine = GossipEngine::new(service.clone(), executor, GRANDPA_ENGINE_ID, validator.clone());
let gossip_engine = GossipEngine::new(service.clone(), GRANDPA_ENGINE_ID, validator.clone());
{
// register all previous votes with the gossip service so that they're
@@ -374,10 +373,9 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
|to, neighbor| self.neighbor_sender.send(to, neighbor),
);
let service = self.gossip_engine.clone();
let topic = global_topic::<B>(set_id.0);
let incoming = incoming_global(
service,
self.gossip_engine.clone(),
topic,
voters,
self.validator.clone(),
@@ -419,7 +417,7 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
impl<B: BlockT, N: Network<B>> Future for NetworkBridge<B, N> {
type Output = Result<(), Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
loop {
match self.neighbor_packet_worker.lock().poll_next_unpin(cx) {
Poll::Ready(Some((to, packet))) => {
@@ -444,6 +442,12 @@ impl<B: BlockT, N: Network<B>> Future for NetworkBridge<B, N> {
}
}
match self.gossip_engine.poll_unpin(cx) {
// The gossip engine future finished. We should do the same.
Poll::Ready(()) => return Poll::Ready(Ok(())),
Poll::Pending => {},
}
Poll::Pending
}
}
@@ -165,7 +165,7 @@ fn voter_set_state() -> SharedVoterSetState<Block> {
}
// needs to run in a tokio runtime.
pub(crate) fn make_test_network(executor: &impl futures::task::Spawn) -> (
pub(crate) fn make_test_network() -> (
impl Future<Output = Tester>,
TestNetwork,
) {
@@ -187,7 +187,6 @@ pub(crate) fn make_test_network(executor: &impl futures::task::Spawn) -> (
net.clone(),
config(),
voter_set_state(),
executor,
);
(
@@ -261,8 +260,7 @@ fn good_commit_leads_to_relay() {
let id = sc_network::PeerId::random();
let global_topic = super::global_topic::<Block>(set_id);
let threads_pool = futures::executor::ThreadPool::new().unwrap();
let test = make_test_network(&threads_pool).0
let test = make_test_network().0
.then(move |tester| {
// register a peer.
tester.gossip_validator.new_peer(&mut NoopContext, &id, sc_network::config::Roles::FULL);
@@ -281,6 +279,7 @@ fn good_commit_leads_to_relay() {
}
let commit_to_send = encoded_commit.clone();
let network_bridge = tester.net_handle.clone();
// asking for global communication will cause the test network
// to send us an event asking us for a stream. use it to
@@ -325,7 +324,7 @@ fn good_commit_leads_to_relay() {
// once the message is sent and commit is "handled" we should have
// a repropagation event coming from the network.
future::join(send_message, handle_commit).then(move |(tester, ())| {
let fut = future::join(send_message, handle_commit).then(move |(tester, ())| {
tester.filter_network_events(move |event| match event {
Event::WriteNotification(_, data) => {
data == encoded_commit
@@ -333,7 +332,11 @@ fn good_commit_leads_to_relay() {
_ => false,
})
})
.map(|_| ())
.map(|_| ());
// Poll both the future sending and handling the commit, as well as the underlying
// NetworkBridge. Complete once the former completes.
future::select(fut, network_bridge)
});
futures::executor::block_on(test);
@@ -385,8 +388,7 @@ fn bad_commit_leads_to_report() {
let id = sc_network::PeerId::random();
let global_topic = super::global_topic::<Block>(set_id);
let threads_pool = futures::executor::ThreadPool::new().unwrap();
let test = make_test_network(&threads_pool).0
let test = make_test_network().0
.map(move |tester| {
// register a peer.
tester.gossip_validator.new_peer(&mut NoopContext, &id, sc_network::config::Roles::FULL);
@@ -405,6 +407,7 @@ fn bad_commit_leads_to_report() {
}
let commit_to_send = encoded_commit.clone();
let network_bridge = tester.net_handle.clone();
// asking for global communication will cause the test network
// to send us an event asking us for a stream. use it to
@@ -427,7 +430,7 @@ fn bad_commit_leads_to_report() {
_ => false,
});
// when the commit comes in, we'll tell the callback it was good.
// when the commit comes in, we'll tell the callback it was bad.
let handle_commit = commits_in.into_future()
.map(|(item, _)| {
match item.unwrap() {
@@ -440,7 +443,7 @@ fn bad_commit_leads_to_report() {
// once the message is sent and commit is "handled" we should have
// a report event coming from the network.
future::join(send_message, handle_commit).then(move |(tester, ())| {
let fut = future::join(send_message, handle_commit).then(move |(tester, ())| {
tester.filter_network_events(move |event| match event {
Event::Report(who, cost_benefit) => {
who == id && cost_benefit == super::cost::INVALID_COMMIT
@@ -448,7 +451,11 @@ fn bad_commit_leads_to_report() {
_ => false,
})
})
.map(|_| ())
.map(|_| ());
// Poll both the future sending and handling the commit, as well as the underlying
// NetworkBridge. Complete once the former completes.
future::select(fut, network_bridge)
});
futures::executor::block_on(test);
@@ -458,8 +465,7 @@ fn bad_commit_leads_to_report() {
fn peer_with_higher_view_leads_to_catch_up_request() {
let id = sc_network::PeerId::random();
let threads_pool = futures::executor::ThreadPool::new().unwrap();
let (tester, mut net) = make_test_network(&threads_pool);
let (tester, mut net) = make_test_network();
let test = tester
.map(move |tester| {
// register a peer with authority role.