diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs
index bc731dd2de..db60c94997 100644
--- a/substrate/client/service/src/builder.rs
+++ b/substrate/client/service/src/builder.rs
@@ -15,7 +15,7 @@
// along with Substrate. If not, see .
use crate::{Service, NetworkStatus, NetworkState, error::Error, DEFAULT_PROTOCOL_ID, MallocSizeOfWasm};
-use crate::{SpawnTaskHandle, start_rpc_servers, build_network_future, TransactionPoolAdapter};
+use crate::{TaskManagerBuilder, start_rpc_servers, build_network_future, TransactionPoolAdapter};
use crate::status_sinks;
use crate::config::{Configuration, DatabaseConfig, KeystoreConfig};
use sc_client_api::{
@@ -30,7 +30,7 @@ use sp_consensus::import_queue::ImportQueue;
use futures::{
Future, FutureExt, StreamExt,
channel::mpsc,
- future::{select, ready}
+ future::ready,
};
use sc_keystore::{Store as Keystore};
use log::{info, warn, error};
@@ -44,7 +44,6 @@ use sp_runtime::traits::{
use sp_api::ProvideRuntimeApi;
use sc_executor::{NativeExecutor, NativeExecutionDispatch};
use std::{
- borrow::Cow,
io::{Read, Write, Seek},
marker::PhantomData, sync::Arc, pin::Pin
};
@@ -117,6 +116,7 @@ pub struct ServiceBuilder,
pub (crate) client: Arc,
backend: Arc,
+ tasks_builder: TaskManagerBuilder,
keystore: Arc>,
fetcher: Option,
select_chain: Option,
@@ -181,6 +181,7 @@ type TFullParts = (
TFullClient,
Arc>,
Arc>,
+ TaskManagerBuilder,
);
/// Creates a new full client for the given config.
@@ -212,6 +213,8 @@ fn new_full_parts(
KeystoreConfig::None => return Err("No keystore config provided!".into()),
};
+ let tasks_builder = TaskManagerBuilder::new();
+
let executor = NativeExecutor::::new(
config.wasm_method,
config.default_heap_pages,
@@ -262,7 +265,7 @@ fn new_full_parts(
)?
};
- Ok((client, backend, keystore))
+ Ok((client, backend, keystore, tasks_builder))
}
impl ServiceBuilder<(), (), TGen, TCSExt, (), (), (), (), (), (), (), (), ()>
@@ -285,7 +288,7 @@ where TGen: RuntimeGenesis, TCSExt: Extension {
(),
TFullBackend,
>, Error> {
- let (client, backend, keystore) = new_full_parts(&config)?;
+ let (client, backend, keystore, tasks_builder) = new_full_parts(&config)?;
let client = Arc::new(client);
@@ -294,6 +297,7 @@ where TGen: RuntimeGenesis, TCSExt: Extension {
client,
backend,
keystore,
+ tasks_builder,
fetcher: None,
select_chain: None,
import_queue: (),
@@ -326,6 +330,8 @@ where TGen: RuntimeGenesis, TCSExt: Extension {
(),
TLightBackend,
>, Error> {
+ let tasks_builder = TaskManagerBuilder::new();
+
let keystore = match &config.keystore {
KeystoreConfig::Path { path, password } => Keystore::open(
path.clone(),
@@ -378,6 +384,7 @@ where TGen: RuntimeGenesis, TCSExt: Extension {
config,
client,
backend,
+ tasks_builder,
keystore,
fetcher: Some(fetcher.clone()),
select_chain: None,
@@ -451,6 +458,7 @@ impl + Send>>, Cow<'static, str>)>();
-
// A side-channel for essential tasks to communicate shutdown.
let (essential_failed_tx, essential_failed_rx) = mpsc::unbounded();
@@ -869,7 +878,7 @@ ServiceBuilder<
imports_external_transactions: !config.roles.is_light(),
pool: transaction_pool.clone(),
client: client.clone(),
- executor: SpawnTaskHandle { sender: to_spawn_tx.clone(), on_exit: exit.clone() },
+ executor: tasks_builder.spawn_handle(),
});
let protocol_id = {
@@ -899,11 +908,9 @@ ServiceBuilder<
let network_params = sc_network::config::Params {
roles: config.roles,
executor: {
- let to_spawn_tx = to_spawn_tx.clone();
+ let spawn_handle = tasks_builder.spawn_handle();
Some(Box::new(move |fut| {
- if let Err(e) = to_spawn_tx.unbounded_send((fut, From::from("libp2p-node"))) {
- error!("Failed to spawn libp2p background task: {:?}", e);
- }
+ spawn_handle.spawn("libp2p-node", fut);
}))
},
network_config: config.network.clone(),
@@ -935,20 +942,19 @@ ServiceBuilder<
_ => None,
};
+ let spawn_handle = tasks_builder.spawn_handle();
+
// Spawn background tasks which were stacked during the
// service building.
for (title, background_task) in background_tasks {
- let _ = to_spawn_tx.unbounded_send((
- background_task,
- title.into(),
- ));
+ spawn_handle.spawn(title, background_task);
}
{
// block notifications
let txpool = Arc::downgrade(&transaction_pool);
let offchain = offchain_workers.as_ref().map(Arc::downgrade);
- let to_spawn_tx_ = to_spawn_tx.clone();
+ let notifications_spawn_handle = tasks_builder.spawn_handle();
let network_state_info: Arc = network.clone();
let is_validator = config.roles.is_authority();
@@ -970,15 +976,14 @@ ServiceBuilder<
let offchain = offchain.as_ref().and_then(|o| o.upgrade());
match offchain {
Some(offchain) if is_new_best => {
- let future = offchain.on_block_imported(
- &header,
- network_state_info.clone(),
- is_validator,
+ notifications_spawn_handle.spawn(
+ "offchain-on-block",
+ offchain.on_block_imported(
+ &header,
+ network_state_info.clone(),
+ is_validator,
+ ),
);
- let _ = to_spawn_tx_.unbounded_send((
- Box::pin(future),
- From::from("offchain-on-block"),
- ));
},
Some(_) => log::debug!(
target: "sc_offchain",
@@ -991,20 +996,19 @@ ServiceBuilder<
let txpool = txpool.upgrade();
if let Some(txpool) = txpool.as_ref() {
- let future = txpool.maintain(event);
- let _ = to_spawn_tx_.unbounded_send((
- Box::pin(future),
- From::from("txpool-maintain")
- ));
+ notifications_spawn_handle.spawn(
+ "txpool-maintain",
+ txpool.maintain(event),
+ );
}
ready(())
});
- let _ = to_spawn_tx.unbounded_send((
- Box::pin(select(events, exit.clone()).map(drop)),
- From::from("txpool-and-offchain-notif"),
- ));
+ spawn_handle.spawn(
+ "txpool-and-offchain-notif",
+ events,
+ );
}
{
@@ -1024,28 +1028,20 @@ ServiceBuilder<
ready(())
});
- let _ = to_spawn_tx.unbounded_send((
- Box::pin(select(events, exit.clone()).map(drop)),
- From::from("telemetry-on-block"),
- ));
+ spawn_handle.spawn(
+ "telemetry-on-block",
+ events,
+ );
}
// Prometheus metrics
let metrics = if let Some((registry, port)) = prometheus_registry_and_port.clone() {
let metrics = ServiceMetrics::register(®istry)?;
-
metrics.node_roles.set(u64::from(config.roles.bits()));
-
- let future = select(
- prometheus_endpoint::init_prometheus(port, registry).boxed(),
- exit.clone()
- ).map(drop);
-
- let _ = to_spawn_tx.unbounded_send((
- Box::pin(future),
- From::from("prometheus-endpoint")
- ));
-
+ spawn_handle.spawn(
+ "prometheus-endpoint",
+ prometheus_endpoint::init_prometheus(port, registry).map(drop)
+ );
Some(metrics)
} else {
None
@@ -1123,10 +1119,10 @@ ServiceBuilder<
ready(())
});
- let _ = to_spawn_tx.unbounded_send((
- Box::pin(select(tel_task, exit.clone()).map(drop)),
- From::from("telemetry-periodic-send"),
- ));
+ spawn_handle.spawn(
+ "telemetry-periodic-send",
+ tel_task,
+ );
// Periodically send the network state to the telemetry.
let (netstat_tx, netstat_rx) = mpsc::unbounded::<(NetworkStatus<_>, NetworkState)>();
@@ -1139,10 +1135,10 @@ ServiceBuilder<
);
ready(())
});
- let _ = to_spawn_tx.unbounded_send((
- Box::pin(select(tel_task_2, exit.clone()).map(drop)),
- From::from("telemetry-periodic-network-state"),
- ));
+ spawn_handle.spawn(
+ "telemetry-periodic-network-state",
+ tel_task_2,
+ );
// RPC
let (system_rpc_tx, system_rpc_rx) = mpsc::unbounded();
@@ -1156,10 +1152,7 @@ ServiceBuilder<
properties: chain_spec.properties().clone(),
};
- let subscriptions = sc_rpc::Subscriptions::new(Arc::new(SpawnTaskHandle {
- sender: to_spawn_tx.clone(),
- on_exit: exit.clone()
- }));
+ let subscriptions = sc_rpc::Subscriptions::new(Arc::new(tasks_builder.spawn_handle()));
let (chain, state) = if let (Some(remote_backend), Some(on_demand)) =
(remote_backend.as_ref(), on_demand.as_ref()) {
@@ -1217,18 +1210,17 @@ ServiceBuilder<
let rpc_handlers = gen_handler();
let rpc = start_rpc_servers(&config, gen_handler)?;
-
- let _ = to_spawn_tx.unbounded_send((
- Box::pin(select(build_network_future(
+ spawn_handle.spawn(
+ "network-worker",
+ build_network_future(
config.roles,
network_mut,
client.clone(),
network_status_sinks.clone(),
system_rpc_rx,
has_bootnodes,
- ), exit.clone()).map(drop)),
- From::from("network-worker"),
- ));
+ ),
+ );
let telemetry_connection_sinks: Arc>>> = Default::default();
@@ -1269,9 +1261,12 @@ ServiceBuilder<
});
ready(())
});
- let _ = to_spawn_tx.unbounded_send((Box::pin(select(
- future, exit.clone()
- ).map(drop)), From::from("telemetry-worker")));
+
+ spawn_handle.spawn(
+ "telemetry-worker",
+ future,
+ );
+
telemetry
});
@@ -1288,21 +1283,13 @@ ServiceBuilder<
Ok(Service {
client,
+ task_manager: tasks_builder.into_task_manager(config.task_executor.ok_or(Error::TaskExecutorRequired)?),
network,
network_status_sinks,
select_chain,
transaction_pool,
- exit,
- signal: Some(signal),
essential_failed_tx,
essential_failed_rx,
- to_spawn_tx,
- to_spawn_rx,
- task_executor: if let Some(exec) = config.task_executor {
- exec
- } else {
- return Err(Error::TaskExecutorRequired);
- },
rpc_handlers,
_rpc: rpc,
_telemetry: telemetry,
diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs
index 5c59cdf91f..db56c141db 100644
--- a/substrate/client/service/src/lib.rs
+++ b/substrate/client/service/src/lib.rs
@@ -26,6 +26,7 @@ pub mod error;
mod builder;
mod status_sinks;
+mod task_manager;
use std::{borrow::Cow, io, pin::Pin};
use std::marker::PhantomData;
@@ -37,10 +38,9 @@ use std::task::{Poll, Context};
use parking_lot::Mutex;
use sc_client::Client;
-use exit_future::Signal;
use futures::{
Future, FutureExt, Stream, StreamExt,
- future::select, channel::mpsc,
+ channel::mpsc,
compat::*,
sink::SinkExt,
task::{Spawn, FutureObj, SpawnError},
@@ -69,6 +69,8 @@ pub use sc_executor::NativeExecutionDispatch;
pub use std::{ops::Deref, result::Result, sync::Arc};
#[doc(hidden)]
pub use sc_network::config::{FinalityProofProvider, OnDemand, BoxFinalityProofRequestBuilder};
+pub use task_manager::{TaskManagerBuilder, SpawnTaskHandle};
+use task_manager::TaskManager;
const DEFAULT_PROTOCOL_ID: &str = "sup";
@@ -85,28 +87,18 @@ impl MallocSizeOfWasm for T {}
/// Substrate service.
pub struct Service {
client: Arc,
+ task_manager: TaskManager,
select_chain: Option,
network: Arc,
/// Sinks to propagate network status updates.
/// For each element, every time the `Interval` fires we push an element on the sender.
network_status_sinks: Arc>>,
transaction_pool: Arc,
- /// A future that resolves when the service has exited, this is useful to
- /// make sure any internally spawned futures stop when the service does.
- exit: exit_future::Exit,
- /// A signal that makes the exit future above resolve, fired on service drop.
- signal: Option,
/// Send a signal when a spawned essential task has concluded. The next time
/// the service future is polled it should complete with an error.
essential_failed_tx: mpsc::UnboundedSender<()>,
/// A receiver for spawned essential-tasks concluding.
essential_failed_rx: mpsc::UnboundedReceiver<()>,
- /// Sender for futures that must be spawned as background tasks.
- to_spawn_tx: mpsc::UnboundedSender<(Pin + Send>>, Cow<'static, str>)>,
- /// Receiver for futures that must be spawned as background tasks.
- to_spawn_rx: mpsc::UnboundedReceiver<(Pin + Send>>, Cow<'static, str>)>,
- /// How to spawn background tasks.
- task_executor: Arc + Send>>) + Send + Sync>,
rpc_handlers: sc_rpc_server::RpcHandler,
_rpc: Box,
_telemetry: Option,
@@ -119,48 +111,6 @@ pub struct Service {
impl Unpin for Service {}
-/// Alias for a an implementation of `futures::future::Executor`.
-pub type TaskExecutor = Arc;
-
-/// An handle for spawning tasks in the service.
-#[derive(Clone)]
-pub struct SpawnTaskHandle {
- sender: mpsc::UnboundedSender<(Pin + Send>>, Cow<'static, str>)>,
- on_exit: exit_future::Exit,
-}
-
-impl SpawnTaskHandle {
- /// Spawns the given task with the given name.
- pub fn spawn(&self, name: impl Into>, task: impl Future