Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

impl Debug for sc_service::Configuration #6400

Merged
merged 9 commits into from
Jun 23, 2020
6 changes: 6 additions & 0 deletions client/chain-spec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,9 @@ pub trait ChainSpec: BuildStorage + Send {
/// This will be used as storage at genesis.
fn set_storage(&mut self, storage: Storage);
}

impl std::fmt::Debug for dyn ChainSpec {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoa, didn't know you could impl for dyn things! What's the difference between doing this and impling for T where T: ChainSpec?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I have no idea. Maybe someone else know

Copy link
Contributor

@tomaka tomaka Jun 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiler will refuse to compile impl<T: ChainSpec> Debug for T {}

That being said, I don't think it's a good idea to implement Debug on trait objects. Not because it's wrong per se, but because it is a very obscure thing to do.

fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "ChainSpec(name = {:?}, id = {:?})", self.name(), self.id())
}
}
2 changes: 1 addition & 1 deletion client/cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ pub trait CliConfiguration: Sized {
Ok(Configuration {
impl_name: C::impl_name(),
impl_version: C::impl_version(),
task_executor,
task_executor: task_executor.into(),
transaction_pool: self.transaction_pool()?,
network: self.network_config(
&chain_spec,
Expand Down
2 changes: 1 addition & 1 deletion client/db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ pub struct DatabaseSettings {
}

/// Where to find the database..
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum DatabaseSettingsSrc {
/// Load a RocksDB database from a given path. Recommended for most uses.
RocksDb {
Expand Down
43 changes: 39 additions & 4 deletions client/service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use prometheus_endpoint::Registry;
use tempfile::TempDir;

/// Service configuration.
#[derive(Debug)]
pub struct Configuration {
/// Implementation name
pub impl_name: &'static str,
Expand All @@ -42,7 +43,7 @@ pub struct Configuration {
/// Node role.
pub role: Role,
/// How to spawn background tasks. Mandatory, otherwise creating a `Service` will error.
pub task_executor: Arc<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>, TaskType) + Send + Sync>,
pub task_executor: TaskExecutor,
/// Extrinsic pool configuration.
pub transaction_pool: TransactionPoolOptions,
/// Network configuration.
Expand Down Expand Up @@ -118,7 +119,7 @@ pub enum TaskType {
}

/// Configuration of the client keystore.
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum KeystoreConfig {
/// Keystore at a path on-disk. Recommended for native nodes.
Path {
Expand All @@ -141,7 +142,7 @@ impl KeystoreConfig {
}
}
/// Configuration of the database of the client.
#[derive(Clone, Default)]
#[derive(Debug, Clone, Default)]
pub struct OffchainWorkerConfig {
/// If this is allowed.
pub enabled: bool,
Expand All @@ -150,7 +151,7 @@ pub struct OffchainWorkerConfig {
}

/// Configuration of the Prometheus endpoint.
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct PrometheusConfig {
/// Port to use.
pub port: SocketAddr,
Expand Down Expand Up @@ -197,6 +198,7 @@ impl Default for RpcMethods {
}

/// The base path that is used for everything that needs to be write on disk to run a node.
#[derive(Debug)]
pub enum BasePath {
/// A temporary directory is used as base path and will be deleted when dropped.
#[cfg(not(target_os = "unknown"))]
Expand Down Expand Up @@ -251,3 +253,36 @@ impl std::convert::From<PathBuf> for BasePath {
BasePath::new(path)
}
}

/// Callable object that execute tasks.
#[derive(Clone)]
pub struct TaskExecutor(Arc<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>, TaskType) + Send + Sync>);

impl std::fmt::Debug for TaskExecutor {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "TaskExecutor")
}
}

impl std::convert::From<Arc<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>, TaskType) + Send + Sync>>
for TaskExecutor {
fn from(x: Arc<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>, TaskType) + Send + Sync>)
-> Self {
Self(x)
}
}

impl std::ops::Deref for TaskExecutor {
type Target = Arc<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>, TaskType) + Send + Sync>;

fn deref(&self) -> &Self::Target {
&self.0
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This basically makes the struct callable. I'm not sure if this is the best solution.

  1. We could say it's not callable at all and use a function instead
  2. We could implement Fn but I think this is only available in nightly
  3. It might be useful to be able to get the Arc<Fn(future)> from TaskExecutor

Any advice on what would be the cleanest implementation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As all the TaskExecutor has to do is to spawn futures and be cloned, I think the best idea would be to hide the internals and just add a spawn method.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While Arc<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>, TaskType) + Send + Sync> is not the most the most elegant type, anyone who knows the Rust programming language can understand this type. If you put it in a new TaskExecutor struct, now everyone has to go look at the definition of this TaskExecutor to know how to use it.

I know it's not much, but having so many typedefs and wrapping structs is in my opinion a major pain point in the Substrate code base as whole.

Copy link
Contributor Author

@cecton cecton Jun 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh well. My goal was not to wrap it, I just needed to implement Debug and there are only 3 possible ways:

  1. Wrapping that type and derive Debug on the whole Configuration
  2. Writing a complete Debug impl for Configuration and handle special cases separately (more maintenance)
  3. Use a crate that would handle that kind of use case (not sure which one) which will reduce the maintenance but add a dependency (this looks good: https://crates.io/crates/debug_stub_derive)

I think the trade-off is fine here especially with the From because you can simply pass a closure and do .into(). I think it would be better if I document it better so anyone who bumps into this will easily know what to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The other good point is that you don't need to import Arc and Pin, not even TaskExecutor, you just pass the closure and use .into())

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think adding some documentation would indeed be a good idea :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@expenses done (I just noticed your comment now)


impl TaskExecutor {
/// Create a `TaskExecutor` from a function
pub fn from_fn(f: impl Fn(Pin<Box<dyn Future<Output = ()> + Send>>, TaskType) + Send + Sync + 'static) -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this also be implemented as From<>?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!! Thanks it worked great 7b0590e

Self(Arc::new(f))
}
}
13 changes: 5 additions & 8 deletions client/service/src/task_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

//! Substrate service tasks management module.

use std::{panic, pin::Pin, result::Result, sync::Arc};
use std::{panic, result::Result};
use exit_future::Signal;
use log::debug;
use futures::{
Expand All @@ -28,18 +28,15 @@ use prometheus_endpoint::{
CounterVec, HistogramOpts, HistogramVec, Opts, Registry, U64
};
use sc_client_api::CloneableSpawn;
use crate::config::TaskType;
use crate::config::{TaskExecutor, TaskType};

mod prometheus_future;

/// Type alias for service task executor (usually runtime).
pub type ServiceTaskExecutor = Arc<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>, TaskType) + Send + Sync>;

/// An handle for spawning tasks in the service.
#[derive(Clone)]
pub struct SpawnTaskHandle {
on_exit: exit_future::Exit,
executor: ServiceTaskExecutor,
executor: TaskExecutor,
metrics: Option<Metrics>,
}

Expand Down Expand Up @@ -153,7 +150,7 @@ pub struct TaskManager {
/// A signal that makes the exit future above resolve, fired on service drop.
signal: Option<Signal>,
/// How to spawn background tasks.
executor: ServiceTaskExecutor,
executor: TaskExecutor,
/// Prometheus metric where to report the polling times.
metrics: Option<Metrics>,
}
Expand All @@ -162,7 +159,7 @@ impl TaskManager {
/// If a Prometheus registry is passed, it will be used to report statistics about the
/// service tasks.
pub(super) fn new(
executor: ServiceTaskExecutor,
executor: TaskExecutor,
prometheus_registry: Option<&Registry>
) -> Result<Self, PrometheusError> {
let (signal, on_exit) = exit_future::signal();
Expand Down
2 changes: 1 addition & 1 deletion client/service/test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ fn node_config<G: RuntimeGenesis + 'static, E: ChainSpecExtension + Clone + 'sta
impl_name: "network-test-impl",
impl_version: "0.1",
role,
task_executor,
task_executor: task_executor.into(),
transaction_pool: Default::default(),
network: network_config,
keystore: KeystoreConfig::Path {
Expand Down
6 changes: 6 additions & 0 deletions primitives/database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ pub trait Database<H: Clone>: Send + Sync {
}
}

impl<H> std::fmt::Debug for dyn Database<H> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Database")
}
}

/// Call `f` with the value previously stored against `key` and return the result, or `None` if
/// `key` is not currently in the database.
///
Expand Down
5 changes: 2 additions & 3 deletions utils/browser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@

use futures01::sync::mpsc as mpsc01;
use log::{debug, info};
use std::sync::Arc;
use sc_network::config::TransportConfig;
use sc_service::{
AbstractService, RpcSession, Role, Configuration,
config::{DatabaseConfig, KeystoreConfig, NetworkConfiguration},
config::{DatabaseConfig, KeystoreConfig, NetworkConfiguration, TaskExecutor},
GenericChainSpec, RuntimeGenesis
};
use wasm_bindgen::prelude::*;
Expand Down Expand Up @@ -64,7 +63,7 @@ where
network,
telemetry_endpoints: chain_spec.telemetry_endpoints().clone(),
chain_spec: Box::new(chain_spec),
task_executor: Arc::new(move |fut, _| wasm_bindgen_futures::spawn_local(fut)),
task_executor: TaskExecutor::from_fn(|fut, _| wasm_bindgen_futures::spawn_local(fut)),
telemetry_external_transport: Some(transport),
role: Role::Light,
database: {
Expand Down