mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 11:41:02 +00:00
refactor overseer into proc-macro based pattern (#2962)
This commit is contained in:
committed by
GitHub
parent
2510bfc5d7
commit
3c9104daff
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "polkadot-overseer-gen"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
description = "Generate an overseer including builder pattern and message wrapper from a single struct."
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1"
|
||||
futures = "0.3"
|
||||
async-trait = "0.1"
|
||||
thiserror = "1"
|
||||
metered = { package = "metered-channel", path = "../../metered-channel" }
|
||||
polkadot-overseer-gen-proc-macro = { path = "./proc-macro" }
|
||||
polkadot-node-network-protocol = { path = "../../network/protocol"}
|
||||
# trait SpawnNamed
|
||||
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
futures-timer = "3.0.2"
|
||||
pin-project = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0.41"
|
||||
@@ -0,0 +1,134 @@
|
||||
//! A dummy to be used with cargo expand
|
||||
|
||||
use polkadot_overseer_gen::*;
|
||||
use polkadot_node_network_protocol::WrongVariant;
|
||||
|
||||
|
||||
/// Concrete subsystem implementation for `MsgStrukt` msg type.
|
||||
#[derive(Default)]
|
||||
pub struct AwesomeSubSys;
|
||||
|
||||
impl ::polkadot_overseer_gen::Subsystem<XxxSubsystemContext<MsgStrukt>, Yikes> for AwesomeSubSys {
|
||||
fn start(self, _ctx: XxxSubsystemContext<MsgStrukt>) -> SpawnedSubsystem < Yikes > {
|
||||
unimplemented!("starting yay!")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GoblinTower;
|
||||
|
||||
impl ::polkadot_overseer_gen::Subsystem<XxxSubsystemContext<Plinko>, Yikes> for GoblinTower {
|
||||
fn start(self, _ctx: XxxSubsystemContext<Plinko>) -> SpawnedSubsystem < Yikes > {
|
||||
unimplemented!("welcum")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A signal sent by the overseer.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SigSigSig;
|
||||
|
||||
|
||||
/// The external event.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EvX;
|
||||
|
||||
|
||||
impl EvX {
|
||||
pub fn focus<'a, T>(&'a self) -> Result<EvX, ()> {
|
||||
unimplemented!("dispatch")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Yikes;
|
||||
|
||||
impl std::fmt::Display for Yikes {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "yikes!")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Yikes {}
|
||||
|
||||
impl From<polkadot_overseer_gen::OverseerError> for Yikes {
|
||||
fn from(_: polkadot_overseer_gen::OverseerError) -> Yikes {
|
||||
Yikes
|
||||
}
|
||||
}
|
||||
|
||||
impl From<polkadot_overseer_gen::mpsc::SendError> for Yikes {
|
||||
fn from(_: polkadot_overseer_gen::mpsc::SendError) -> Yikes {
|
||||
Yikes
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MsgStrukt(u8);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Plinko;
|
||||
|
||||
impl From<NetworkMsg> for MsgStrukt {
|
||||
fn from(_event: NetworkMsg) -> Self {
|
||||
MsgStrukt(1u8)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum NetworkMsg {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
}
|
||||
|
||||
|
||||
impl NetworkMsg {
|
||||
fn focus(&self) -> Result<Self, WrongVariant> {
|
||||
Ok(match self {
|
||||
Self::B => return Err(WrongVariant),
|
||||
Self::A | Self::C => self.clone()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[overlord(signal=SigSigSig, event=EvX, error=Yikes, network=NetworkMsg, gen=AllMessages)]
|
||||
struct Xxx {
|
||||
#[subsystem(MsgStrukt)]
|
||||
sub0: AwesomeSubSys,
|
||||
|
||||
#[subsystem(no_dispatch, blocking, Plinko)]
|
||||
plinkos: GoblinTower,
|
||||
|
||||
i_like_pi: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DummySpawner;
|
||||
|
||||
impl SpawnNamed for DummySpawner{
|
||||
fn spawn_blocking(&self, name: &'static str, _future: futures::future::BoxFuture<'static, ()>) {
|
||||
unimplemented!("spawn blocking {}", name)
|
||||
}
|
||||
|
||||
fn spawn(&self, name: &'static str, _future: futures::future::BoxFuture<'static, ()>) {
|
||||
unimplemented!("spawn {}", name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DummyCtx;
|
||||
|
||||
fn main() {
|
||||
let (overseer, _handler): (Xxx<_>, _) = Xxx::builder()
|
||||
.sub0(AwesomeSubSys::default())
|
||||
.plinkos(GoblinTower::default())
|
||||
.i_like_pi(::std::f64::consts::PI)
|
||||
.spawner(DummySpawner)
|
||||
.build()
|
||||
.unwrap();
|
||||
assert_eq!(overseer.i_like_pi.floor() as i8, 3);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "polkadot-overseer-gen-proc-macro"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
description = "Generate an overseer including builder pattern and message wrapper from a single annotated struct definition."
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1.0.60", features = ["full", "extra-traits"] }
|
||||
quote = "1.0.9"
|
||||
proc-macro2 = "1.0.26"
|
||||
proc-macro-crate = "1.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
@@ -0,0 +1,373 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Implement a builder pattern for the `Overseer`-type,
|
||||
/// which acts as the gateway to constructing the overseer.
|
||||
///
|
||||
/// Elements tagged with `wip` are not covered here.
|
||||
pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
let overseer_name = info.overseer_name.clone();
|
||||
let builder = Ident::new(&(overseer_name.to_string() + "Builder"), overseer_name.span());
|
||||
let handle = Ident::new(&(overseer_name.to_string() + "Handle"), overseer_name.span());
|
||||
|
||||
let subsystem_name = &info.subsystem_names_without_wip();
|
||||
let builder_generic_ty = &info.builder_generic_types();
|
||||
|
||||
let channel_name = &info.channel_names_without_wip("");
|
||||
let channel_name_unbounded = &info.channel_names_without_wip("_unbounded");
|
||||
|
||||
let channel_name_tx = &info.channel_names_without_wip("_tx");
|
||||
let channel_name_unbounded_tx = &info.channel_names_without_wip("_unbounded_tx");
|
||||
|
||||
let channel_name_rx = &info.channel_names_without_wip("_rx");
|
||||
let channel_name_unbounded_rx = &info.channel_names_without_wip("_unbounded_rx");
|
||||
|
||||
let baggage_generic_ty = &info.baggage_generic_types();
|
||||
let baggage_name = &info.baggage_names();
|
||||
let baggage_ty = &info.baggage_types();
|
||||
|
||||
let error_ty = &info.extern_error_ty;
|
||||
|
||||
let support_crate = info.support_crate_name();
|
||||
|
||||
let blocking = &info
|
||||
.subsystems()
|
||||
.iter()
|
||||
.map(|x| {
|
||||
if x.blocking {
|
||||
quote! { Blocking }
|
||||
} else {
|
||||
quote! { Regular }
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let generics = quote! {
|
||||
< S, #( #baggage_generic_ty, )* >
|
||||
};
|
||||
let where_clause = quote! {
|
||||
where
|
||||
S: #support_crate ::SpawnNamed,
|
||||
};
|
||||
|
||||
let builder_generics = quote! {
|
||||
<S, #( #baggage_generic_ty, )* #( #builder_generic_ty, )* >
|
||||
};
|
||||
|
||||
// all subsystems must have the same context
|
||||
// even if the overseer does not impose such a limit.
|
||||
let builder_additional_generics = quote! {
|
||||
<#( #builder_generic_ty, )* >
|
||||
};
|
||||
|
||||
let consumes = &info.consumes();
|
||||
|
||||
let subsyste_ctx_name = Ident::new(
|
||||
&(overseer_name.to_string() + "SubsystemContext"),
|
||||
overseer_name.span()
|
||||
);
|
||||
|
||||
let builder_where_clause = quote! {
|
||||
where
|
||||
S: #support_crate ::SpawnNamed,
|
||||
#(
|
||||
#builder_generic_ty : Subsystem<#subsyste_ctx_name< #consumes >, #error_ty>,
|
||||
)*
|
||||
};
|
||||
|
||||
let event = &info.extern_event_ty;
|
||||
|
||||
let mut ts = quote! {
|
||||
impl #generics #overseer_name #generics #where_clause {
|
||||
/// Create a new overseer utilizing the builder.
|
||||
pub fn builder #builder_additional_generics () -> #builder #builder_generics
|
||||
#builder_where_clause
|
||||
{
|
||||
#builder :: default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle for an overseer.
|
||||
pub type #handle = #support_crate ::metered::MeteredSender< #event >;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub struct #builder #builder_generics {
|
||||
#(
|
||||
#subsystem_name : ::std::option::Option< #builder_generic_ty >,
|
||||
)*
|
||||
#(
|
||||
#baggage_name : ::std::option::Option< #baggage_ty >,
|
||||
)*
|
||||
spawner: ::std::option::Option< S >,
|
||||
}
|
||||
|
||||
impl #builder_generics Default for #builder #builder_generics {
|
||||
fn default() -> Self {
|
||||
// explicitly assure the required traits are implemented
|
||||
fn trait_from_must_be_implemented<E>()
|
||||
where
|
||||
E: std::error::Error + Send + Sync + 'static + From<#support_crate ::OverseerError>
|
||||
{}
|
||||
|
||||
trait_from_must_be_implemented::< #error_ty >();
|
||||
|
||||
Self {
|
||||
#(
|
||||
#subsystem_name: None,
|
||||
)*
|
||||
#(
|
||||
#baggage_name: None,
|
||||
)*
|
||||
spawner: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl #builder_generics #builder #builder_generics #builder_where_clause {
|
||||
/// The spawner to use for spawning tasks.
|
||||
pub fn spawner(mut self, spawner: S) -> Self
|
||||
where
|
||||
S: #support_crate ::SpawnNamed + Send
|
||||
{
|
||||
self.spawner = Some(spawner);
|
||||
self
|
||||
}
|
||||
|
||||
#(
|
||||
/// Specify the particular subsystem implementation.
|
||||
pub fn #subsystem_name (mut self, subsystem: #builder_generic_ty ) -> Self {
|
||||
self. #subsystem_name = Some( subsystem );
|
||||
self
|
||||
}
|
||||
)*
|
||||
|
||||
#(
|
||||
/// Attach the user defined addendum type.
|
||||
pub fn #baggage_name (mut self, baggage: #baggage_ty ) -> Self {
|
||||
self. #baggage_name = Some( baggage );
|
||||
self
|
||||
}
|
||||
)*
|
||||
|
||||
/// Complete the construction and create the overseer type.
|
||||
pub fn build(mut self) -> ::std::result::Result<(#overseer_name #generics, #handle), #error_ty>
|
||||
{
|
||||
let (events_tx, events_rx) = #support_crate ::metered::channel::<
|
||||
#event
|
||||
>(SIGNAL_CHANNEL_CAPACITY);
|
||||
|
||||
let handle: #handle = events_tx.clone();
|
||||
|
||||
let (to_overseer_tx, to_overseer_rx) = #support_crate ::metered::unbounded::<
|
||||
ToOverseer
|
||||
>();
|
||||
|
||||
#(
|
||||
let (#channel_name_tx, #channel_name_rx)
|
||||
=
|
||||
#support_crate ::metered::channel::<
|
||||
MessagePacket< #consumes >
|
||||
>(CHANNEL_CAPACITY);
|
||||
)*
|
||||
|
||||
#(
|
||||
let (#channel_name_unbounded_tx, #channel_name_unbounded_rx) =
|
||||
#support_crate ::metered::unbounded::<
|
||||
MessagePacket< #consumes >
|
||||
>();
|
||||
)*
|
||||
|
||||
let channels_out =
|
||||
ChannelsOut {
|
||||
#(
|
||||
#channel_name: #channel_name_tx .clone(),
|
||||
)*
|
||||
#(
|
||||
#channel_name_unbounded: #channel_name_unbounded_tx,
|
||||
)*
|
||||
};
|
||||
|
||||
let mut spawner = self.spawner.expect("Spawner is set. qed");
|
||||
|
||||
let mut running_subsystems = #support_crate ::FuturesUnordered::<
|
||||
BoxFuture<'static, ::std::result::Result<(), #error_ty > >
|
||||
>::new();
|
||||
|
||||
#(
|
||||
// TODO generate a builder pattern that ensures this
|
||||
// TODO https://github.com/paritytech/polkadot/issues/3427
|
||||
let #subsystem_name = self. #subsystem_name .expect("All subsystem must exist with the builder pattern.");
|
||||
|
||||
let unbounded_meter = #channel_name_unbounded_rx.meter().clone();
|
||||
|
||||
let message_rx: SubsystemIncomingMessages< #consumes > = #support_crate ::select(
|
||||
#channel_name_rx, #channel_name_unbounded_rx
|
||||
);
|
||||
let (signal_tx, signal_rx) = #support_crate ::metered::channel(SIGNAL_CHANNEL_CAPACITY);
|
||||
let ctx = #subsyste_ctx_name::< #consumes >::new(
|
||||
signal_rx,
|
||||
message_rx,
|
||||
channels_out.clone(),
|
||||
to_overseer_tx.clone(),
|
||||
);
|
||||
|
||||
let #subsystem_name: OverseenSubsystem< #consumes > =
|
||||
spawn::<_,_, #blocking, _, _, _>(
|
||||
&mut spawner,
|
||||
#channel_name_tx,
|
||||
signal_tx,
|
||||
unbounded_meter,
|
||||
channels_out.clone(),
|
||||
ctx,
|
||||
#subsystem_name,
|
||||
&mut running_subsystems,
|
||||
)?;
|
||||
)*
|
||||
|
||||
#(
|
||||
let #baggage_name = self. #baggage_name .expect(
|
||||
&format!("Baggage variable `{1}` of `{0}` ",
|
||||
stringify!(#overseer_name),
|
||||
stringify!( #baggage_name )
|
||||
)
|
||||
);
|
||||
)*
|
||||
|
||||
use #support_crate ::StreamExt;
|
||||
|
||||
let to_overseer_rx = to_overseer_rx.fuse();
|
||||
let overseer = #overseer_name {
|
||||
#(
|
||||
#subsystem_name,
|
||||
)*
|
||||
|
||||
#(
|
||||
#baggage_name,
|
||||
)*
|
||||
|
||||
spawner,
|
||||
running_subsystems,
|
||||
events_rx,
|
||||
to_overseer_rx,
|
||||
};
|
||||
|
||||
Ok((overseer, handle))
|
||||
}
|
||||
}
|
||||
};
|
||||
ts.extend(impl_task_kind(info));
|
||||
ts
|
||||
}
|
||||
|
||||
pub(crate) fn impl_task_kind(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
let signal = &info.extern_signal_ty;
|
||||
let error_ty = &info.extern_error_ty;
|
||||
let support_crate = info.support_crate_name();
|
||||
|
||||
let ts = quote! {
|
||||
|
||||
use #support_crate ::FutureExt as _;
|
||||
|
||||
/// Task kind to launch.
|
||||
pub trait TaskKind {
|
||||
/// Spawn a task, it depends on the implementer if this is blocking or not.
|
||||
fn launch_task<S: SpawnNamed>(spawner: &mut S, name: &'static str, future: BoxFuture<'static, ()>);
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
struct Regular;
|
||||
impl TaskKind for Regular {
|
||||
fn launch_task<S: SpawnNamed>(spawner: &mut S, name: &'static str, future: BoxFuture<'static, ()>) {
|
||||
spawner.spawn(name, future)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
struct Blocking;
|
||||
impl TaskKind for Blocking {
|
||||
fn launch_task<S: SpawnNamed>(spawner: &mut S, name: &'static str, future: BoxFuture<'static, ()>) {
|
||||
spawner.spawn_blocking(name, future)
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn task of kind `self` using spawner `S`.
|
||||
pub fn spawn<S, M, TK, Ctx, E, SubSys>(
|
||||
spawner: &mut S,
|
||||
message_tx: #support_crate ::metered::MeteredSender<MessagePacket<M>>,
|
||||
signal_tx: #support_crate ::metered::MeteredSender< #signal >,
|
||||
// meter for the unbounded channel
|
||||
unbounded_meter: #support_crate ::metered::Meter,
|
||||
// connection to the subsystems
|
||||
channels_out: ChannelsOut,
|
||||
ctx: Ctx,
|
||||
s: SubSys,
|
||||
futures: &mut #support_crate ::FuturesUnordered<BoxFuture<'static, ::std::result::Result<(), #error_ty> >>,
|
||||
) -> ::std::result::Result<OverseenSubsystem<M>, #error_ty >
|
||||
where
|
||||
S: #support_crate ::SpawnNamed,
|
||||
M: std::fmt::Debug + Send + 'static,
|
||||
TK: TaskKind,
|
||||
Ctx: #support_crate ::SubsystemContext<Message=M>,
|
||||
E: std::error::Error + Send + Sync + 'static + From<#support_crate ::OverseerError>,
|
||||
SubSys: #support_crate ::Subsystem<Ctx, E>,
|
||||
{
|
||||
let #support_crate ::SpawnedSubsystem::<E> { future, name } = s.start(ctx);
|
||||
|
||||
let (tx, rx) = #support_crate ::oneshot::channel();
|
||||
|
||||
let fut = Box::pin(async move {
|
||||
if let Err(e) = future.await {
|
||||
#support_crate ::tracing::error!(subsystem=name, err = ?e, "subsystem exited with error");
|
||||
} else {
|
||||
#support_crate ::tracing::debug!(subsystem=name, "subsystem exited without an error");
|
||||
}
|
||||
let _ = tx.send(());
|
||||
});
|
||||
|
||||
<TK as TaskKind>::launch_task(spawner, name, fut);
|
||||
|
||||
futures.push(Box::pin(
|
||||
rx.map(|e| {
|
||||
tracing::warn!(err = ?e, "dropping error");
|
||||
Ok(())
|
||||
})
|
||||
));
|
||||
|
||||
let instance = Some(SubsystemInstance {
|
||||
meters: #support_crate ::SubsystemMeters {
|
||||
unbounded: unbounded_meter,
|
||||
bounded: message_tx.meter().clone(),
|
||||
signals: signal_tx.meter().clone(),
|
||||
},
|
||||
tx_signal: signal_tx,
|
||||
tx_bounded: message_tx,
|
||||
signals_received: 0,
|
||||
name,
|
||||
});
|
||||
|
||||
Ok(OverseenSubsystem {
|
||||
instance,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
ts
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use quote::quote;
|
||||
use syn::Result;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Implement the helper type `ChannelsOut` and `MessagePacket<T>`.
|
||||
pub(crate) fn impl_channels_out_struct(info: &OverseerInfo) -> Result<proc_macro2::TokenStream> {
|
||||
let message_wrapper = info.message_wrapper.clone();
|
||||
|
||||
let channel_name = &info.channel_names_without_wip("");
|
||||
let channel_name_unbounded = &info.channel_names_without_wip("_unbounded");
|
||||
|
||||
let consumes = &info.consumes_without_wip();
|
||||
|
||||
let consumes_variant = &info.variant_names_without_wip();
|
||||
let unconsumes_variant = &info.variant_names_only_wip();
|
||||
|
||||
let support_crate = info.support_crate_name();
|
||||
|
||||
let ts = quote! {
|
||||
/// Collection of channels to the individual subsystems.
|
||||
///
|
||||
/// Naming is from the point of view of the overseer.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ChannelsOut {
|
||||
#(
|
||||
/// Bounded channel sender, connected to a subsystem.
|
||||
pub #channel_name:
|
||||
#support_crate ::metered::MeteredSender<
|
||||
MessagePacket< #consumes >
|
||||
>,
|
||||
)*
|
||||
|
||||
#(
|
||||
/// Unbounded channel sender, connected to a subsystem.
|
||||
pub #channel_name_unbounded:
|
||||
#support_crate ::metered::UnboundedMeteredSender<
|
||||
MessagePacket< #consumes >
|
||||
>,
|
||||
)*
|
||||
}
|
||||
|
||||
impl ChannelsOut {
|
||||
/// Send a message via a bounded channel.
|
||||
pub async fn send_and_log_error(
|
||||
&mut self,
|
||||
signals_received: usize,
|
||||
message: #message_wrapper,
|
||||
) {
|
||||
let res: ::std::result::Result<_, _> = match message {
|
||||
#(
|
||||
#message_wrapper :: #consumes_variant ( inner ) => {
|
||||
self. #channel_name .send(
|
||||
#support_crate ::make_packet(signals_received, inner)
|
||||
).await.map_err(|_| stringify!( #channel_name ))
|
||||
}
|
||||
)*
|
||||
// subsystems that are wip
|
||||
#(
|
||||
#message_wrapper :: #unconsumes_variant ( _ ) => Ok(()),
|
||||
)*
|
||||
// dummy message type
|
||||
#message_wrapper :: Empty => Ok(()),
|
||||
};
|
||||
|
||||
if let Err(subsystem_name) = res {
|
||||
#support_crate ::tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
"Failed to send (bounded) a message to {} subsystem",
|
||||
subsystem_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a message to another subsystem via an unbounded channel.
|
||||
pub fn send_unbounded_and_log_error(
|
||||
&self,
|
||||
signals_received: usize,
|
||||
message: #message_wrapper,
|
||||
) {
|
||||
use ::std::sync::mpsc::TrySendError;
|
||||
|
||||
let res: ::std::result::Result<_, _> = match message {
|
||||
#(
|
||||
#message_wrapper :: #consumes_variant (inner) => {
|
||||
self. #channel_name_unbounded .unbounded_send(
|
||||
#support_crate ::make_packet(signals_received, inner)
|
||||
)
|
||||
.map_err(|_| stringify!( #channel_name ))
|
||||
},
|
||||
)*
|
||||
// subsystems that are wip
|
||||
#(
|
||||
#message_wrapper :: #unconsumes_variant ( _ ) => Ok(()),
|
||||
)*
|
||||
// dummy message type
|
||||
#message_wrapper :: Empty => Ok(())
|
||||
};
|
||||
|
||||
if let Err(subsystem_name) = res {
|
||||
#support_crate ::tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
"Failed to send_unbounded a message to {} subsystem",
|
||||
subsystem_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
Ok(ts)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use proc_macro2::{TokenStream, Ident};
|
||||
use quote::quote;
|
||||
use syn::Path;
|
||||
|
||||
pub(crate) fn impl_dispatch(info: &OverseerInfo) -> TokenStream {
|
||||
let message_wrapper = &info.message_wrapper;
|
||||
|
||||
let dispatchable_variant = info
|
||||
.subsystems()
|
||||
.into_iter()
|
||||
.filter(|ssf| !ssf.no_dispatch)
|
||||
.filter(|ssf| !ssf.wip)
|
||||
.map(|ssf| ssf.generic.clone())
|
||||
.collect::<Vec<Ident>>();
|
||||
|
||||
let dispatchable_message = info
|
||||
.subsystems()
|
||||
.into_iter()
|
||||
.filter(|ssf| !ssf.no_dispatch)
|
||||
.filter(|ssf| !ssf.wip)
|
||||
.map(|ssf| ssf.consumes.clone())
|
||||
.collect::<Vec<Path>>();
|
||||
|
||||
let mut ts = TokenStream::new();
|
||||
if let Some(extern_network_ty) = &info.extern_network_ty.clone() {
|
||||
ts.extend(quote! {
|
||||
impl #message_wrapper {
|
||||
/// Generated dispatch iterator generator.
|
||||
pub fn dispatch_iter(extern_msg: #extern_network_ty) -> impl Iterator<Item=Self> + Send {
|
||||
::std::array::IntoIter::new([
|
||||
#(
|
||||
extern_msg
|
||||
// focuses on a `NetworkBridgeEvent< protocol_v1::* >`
|
||||
// TODO do not require this to be hardcoded, either externalize or ...
|
||||
// https://github.com/paritytech/polkadot/issues/3427
|
||||
.focus()
|
||||
.ok()
|
||||
.map(|event| {
|
||||
#message_wrapper :: #dispatchable_variant (
|
||||
// the inner type of the enum variant
|
||||
#dispatchable_message :: from( event )
|
||||
)
|
||||
}),
|
||||
)*
|
||||
])
|
||||
.into_iter()
|
||||
.filter_map(|x: Option<_>| x)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
ts
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use quote::quote;
|
||||
use syn::Result;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Generates the wrapper type enum.
|
||||
pub(crate) fn impl_message_wrapper_enum(info: &OverseerInfo) -> Result<proc_macro2::TokenStream> {
|
||||
let consumes = info.consumes();
|
||||
let consumes_variant = info.variant_names();
|
||||
|
||||
let outgoing = &info.outgoing_ty;
|
||||
|
||||
let message_wrapper = &info.message_wrapper;
|
||||
|
||||
let (outgoing_from_impl, outgoing_decl) = if let Some(outgoing) = outgoing {
|
||||
let outgoing_variant = outgoing
|
||||
.get_ident()
|
||||
.ok_or_else(||{
|
||||
syn::Error::new(outgoing.span(), "Missing identifier to use as enum variant for outgoing.")
|
||||
})?;
|
||||
(quote! {
|
||||
impl ::std::convert::From< #outgoing > for #message_wrapper {
|
||||
fn from(message: #outgoing) -> Self {
|
||||
#message_wrapper :: #outgoing_variant ( message )
|
||||
}
|
||||
}
|
||||
},
|
||||
quote! {
|
||||
#outgoing_variant ( #outgoing ) ,
|
||||
})
|
||||
} else {
|
||||
(TokenStream::new(), TokenStream::new())
|
||||
};
|
||||
|
||||
let ts = quote! {
|
||||
/// Generated message type wrapper
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug)]
|
||||
pub enum #message_wrapper {
|
||||
#(
|
||||
#consumes_variant ( #consumes ),
|
||||
)*
|
||||
#outgoing_decl
|
||||
// dummy message type
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl ::std::convert::From< () > for #message_wrapper {
|
||||
fn from(_: ()) -> Self {
|
||||
#message_wrapper :: Empty
|
||||
}
|
||||
}
|
||||
|
||||
#(
|
||||
impl ::std::convert::From< #consumes > for #message_wrapper {
|
||||
fn from(message: #consumes) -> Self {
|
||||
#message_wrapper :: #consumes_variant ( message )
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
#outgoing_from_impl
|
||||
};
|
||||
|
||||
Ok(ts)
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Implement a builder pattern for the `Overseer`-type,
|
||||
/// which acts as the gateway to constructing the overseer.
|
||||
pub(crate) fn impl_misc(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
let overseer_name = info.overseer_name.clone();
|
||||
let subsystem_sender_name = Ident::new(&(overseer_name.to_string() + "SubsystemSender"), overseer_name.span());
|
||||
let subsystem_ctx_name = Ident::new(&(overseer_name.to_string() + "SubsystemContext"), overseer_name.span());
|
||||
let consumes = &info.consumes();
|
||||
let signal = &info.extern_signal_ty;
|
||||
let wrapper_message = &info.message_wrapper;
|
||||
let error_ty = &info.extern_error_ty;
|
||||
let support_crate = info.support_crate_name();
|
||||
|
||||
let ts = quote! {
|
||||
/// Connector to send messages towards all subsystems,
|
||||
/// while tracking the which signals where already received.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct #subsystem_sender_name {
|
||||
/// Collection of channels to all subsystems.
|
||||
channels: ChannelsOut,
|
||||
/// Systemwide tick for which signals were received by all subsystems.
|
||||
signals_received: SignalsReceived,
|
||||
}
|
||||
|
||||
/// impl for wrapping message type...
|
||||
#[#support_crate ::async_trait]
|
||||
impl SubsystemSender< #wrapper_message > for #subsystem_sender_name {
|
||||
async fn send_message(&mut self, msg: #wrapper_message) {
|
||||
self.channels.send_and_log_error(self.signals_received.load(), msg).await;
|
||||
}
|
||||
|
||||
async fn send_messages<T>(&mut self, msgs: T)
|
||||
where
|
||||
T: IntoIterator<Item = #wrapper_message> + Send,
|
||||
T::IntoIter: Send,
|
||||
{
|
||||
// This can definitely be optimized if necessary.
|
||||
for msg in msgs {
|
||||
self.send_message(msg).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn send_unbounded_message(&mut self, msg: #wrapper_message) {
|
||||
self.channels.send_unbounded_and_log_error(self.signals_received.load(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
// ... but also implement for all individual messages to avoid
|
||||
// the necessity for manual wrapping, and do the conversion
|
||||
// based on the generated `From::from` impl for the individual variants.
|
||||
#(
|
||||
#[#support_crate ::async_trait]
|
||||
impl SubsystemSender< #consumes > for #subsystem_sender_name {
|
||||
async fn send_message(&mut self, msg: #consumes) {
|
||||
self.channels.send_and_log_error(self.signals_received.load(), #wrapper_message ::from ( msg )).await;
|
||||
}
|
||||
|
||||
async fn send_messages<T>(&mut self, msgs: T)
|
||||
where
|
||||
T: IntoIterator<Item = #consumes> + Send,
|
||||
T::IntoIter: Send,
|
||||
{
|
||||
// This can definitely be optimized if necessary.
|
||||
for msg in msgs {
|
||||
self.send_message(msg).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn send_unbounded_message(&mut self, msg: #consumes) {
|
||||
self.channels.send_unbounded_and_log_error(self.signals_received.load(), #wrapper_message ::from ( msg ));
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
/// A context type that is given to the [`Subsystem`] upon spawning.
|
||||
/// It can be used by [`Subsystem`] to communicate with other [`Subsystem`]s
|
||||
/// or to spawn it's [`SubsystemJob`]s.
|
||||
///
|
||||
/// [`Overseer`]: struct.Overseer.html
|
||||
/// [`Subsystem`]: trait.Subsystem.html
|
||||
/// [`SubsystemJob`]: trait.SubsystemJob.html
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct #subsystem_ctx_name<M>{
|
||||
signals: #support_crate ::metered::MeteredReceiver< #signal >,
|
||||
messages: SubsystemIncomingMessages<M>,
|
||||
to_subsystems: #subsystem_sender_name,
|
||||
to_overseer: #support_crate ::metered::UnboundedMeteredSender<
|
||||
#support_crate ::ToOverseer
|
||||
>,
|
||||
signals_received: SignalsReceived,
|
||||
pending_incoming: Option<(usize, M)>,
|
||||
}
|
||||
|
||||
impl<M> #subsystem_ctx_name<M> {
|
||||
/// Create a new context.
|
||||
fn new(
|
||||
signals: #support_crate ::metered::MeteredReceiver< #signal >,
|
||||
messages: SubsystemIncomingMessages<M>,
|
||||
to_subsystems: ChannelsOut,
|
||||
to_overseer: #support_crate ::metered::UnboundedMeteredSender<ToOverseer>,
|
||||
) -> Self {
|
||||
let signals_received = SignalsReceived::default();
|
||||
#subsystem_ctx_name {
|
||||
signals,
|
||||
messages,
|
||||
to_subsystems: #subsystem_sender_name {
|
||||
channels: to_subsystems,
|
||||
signals_received: signals_received.clone(),
|
||||
},
|
||||
to_overseer,
|
||||
signals_received,
|
||||
pending_incoming: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[#support_crate ::async_trait]
|
||||
impl<M: std::fmt::Debug + Send + 'static> SubsystemContext for #subsystem_ctx_name<M>
|
||||
where
|
||||
#subsystem_sender_name: #support_crate ::SubsystemSender< #wrapper_message >,
|
||||
#wrapper_message: From<M>,
|
||||
{
|
||||
type Message = M;
|
||||
type Signal = #signal;
|
||||
type Sender = #subsystem_sender_name;
|
||||
type AllMessages = #wrapper_message;
|
||||
type Error = #error_ty;
|
||||
|
||||
async fn try_recv(&mut self) -> ::std::result::Result<Option<FromOverseer<M, #signal>>, ()> {
|
||||
match #support_crate ::poll!(self.recv()) {
|
||||
#support_crate ::Poll::Ready(msg) => Ok(Some(msg.map_err(|_| ())?)),
|
||||
#support_crate ::Poll::Pending => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
async fn recv(&mut self) -> ::std::result::Result<FromOverseer<M, #signal>, #error_ty> {
|
||||
loop {
|
||||
// If we have a message pending an overseer signal, we only poll for signals
|
||||
// in the meantime.
|
||||
if let Some((needs_signals_received, msg)) = self.pending_incoming.take() {
|
||||
if needs_signals_received <= self.signals_received.load() {
|
||||
return Ok(#support_crate ::FromOverseer::Communication { msg });
|
||||
} else {
|
||||
self.pending_incoming = Some((needs_signals_received, msg));
|
||||
|
||||
// wait for next signal.
|
||||
let signal = self.signals.next().await
|
||||
.ok_or(#support_crate ::OverseerError::Context(
|
||||
"Signal channel is terminated and empty."
|
||||
.to_owned()
|
||||
))?;
|
||||
|
||||
self.signals_received.inc();
|
||||
return Ok(#support_crate ::FromOverseer::Signal(signal))
|
||||
}
|
||||
}
|
||||
|
||||
let mut await_message = self.messages.next().fuse();
|
||||
let mut await_signal = self.signals.next().fuse();
|
||||
let signals_received = self.signals_received.load();
|
||||
let pending_incoming = &mut self.pending_incoming;
|
||||
|
||||
// Otherwise, wait for the next signal or incoming message.
|
||||
let from_overseer = #support_crate ::futures::select_biased! {
|
||||
signal = await_signal => {
|
||||
let signal = signal
|
||||
.ok_or(#support_crate ::OverseerError::Context(
|
||||
"Signal channel is terminated and empty."
|
||||
.to_owned()
|
||||
))?;
|
||||
|
||||
#support_crate ::FromOverseer::Signal(signal)
|
||||
}
|
||||
msg = await_message => {
|
||||
let packet = msg
|
||||
.ok_or(#support_crate ::OverseerError::Context(
|
||||
"Message channel is terminated and empty."
|
||||
.to_owned()
|
||||
))?;
|
||||
|
||||
if packet.signals_received > signals_received {
|
||||
// wait until we've received enough signals to return this message.
|
||||
*pending_incoming = Some((packet.signals_received, packet.message));
|
||||
continue;
|
||||
} else {
|
||||
// we know enough to return this message.
|
||||
#support_crate ::FromOverseer::Communication { msg: packet.message}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let #support_crate ::FromOverseer::Signal(_) = from_overseer {
|
||||
self.signals_received.inc();
|
||||
}
|
||||
|
||||
return Ok(from_overseer);
|
||||
}
|
||||
}
|
||||
|
||||
fn sender(&mut self) -> &mut Self::Sender {
|
||||
&mut self.to_subsystems
|
||||
}
|
||||
|
||||
fn spawn(&mut self, name: &'static str, s: Pin<Box<dyn Future<Output = ()> + Send>>)
|
||||
-> ::std::result::Result<(), #error_ty>
|
||||
{
|
||||
self.to_overseer.unbounded_send(#support_crate ::ToOverseer::SpawnJob {
|
||||
name,
|
||||
s,
|
||||
}).map_err(|_| #support_crate ::OverseerError::TaskSpawn(name))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn_blocking(&mut self, name: &'static str, s: Pin<Box<dyn Future<Output = ()> + Send>>)
|
||||
-> ::std::result::Result<(), #error_ty>
|
||||
{
|
||||
self.to_overseer.unbounded_send(#support_crate ::ToOverseer::SpawnBlockingJob {
|
||||
name,
|
||||
s,
|
||||
}).map_err(|_| #support_crate ::OverseerError::TaskSpawn(name))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ts
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub(crate) fn impl_overseer_struct(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
let message_wrapper = &info.message_wrapper.clone();
|
||||
let overseer_name = info.overseer_name.clone();
|
||||
let subsystem_name = &info.subsystem_names_without_wip();
|
||||
let support_crate = info.support_crate_name();
|
||||
|
||||
let baggage_decl = &info.baggage_decl();
|
||||
|
||||
let baggage_generic_ty = &info.baggage_generic_types();
|
||||
|
||||
let generics = quote! {
|
||||
< S, #( #baggage_generic_ty, )* >
|
||||
};
|
||||
|
||||
let where_clause = quote! {
|
||||
where
|
||||
S: #support_crate ::SpawnNamed,
|
||||
};
|
||||
// TODO add `where ..` clauses for baggage types
|
||||
// TODO https://github.com/paritytech/polkadot/issues/3427
|
||||
|
||||
let consumes = &info.consumes_without_wip();
|
||||
let consumes_variant = &info.variant_names_without_wip();
|
||||
let unconsumes_variant = &info.variant_names_only_wip();
|
||||
|
||||
let signal_ty = &info.extern_signal_ty;
|
||||
|
||||
let error_ty = &info.extern_error_ty;
|
||||
|
||||
let event_ty = &info.extern_event_ty;
|
||||
|
||||
let message_channel_capacity = info.message_channel_capacity;
|
||||
let signal_channel_capacity = info.signal_channel_capacity;
|
||||
|
||||
let log_target = syn::LitStr::new(overseer_name.to_string().to_lowercase().as_str(), overseer_name.span());
|
||||
|
||||
let ts = quote! {
|
||||
const STOP_DELAY: ::std::time::Duration = ::std::time::Duration::from_secs(1);
|
||||
|
||||
/// Capacity of a bounded message channel between overseer and subsystem
|
||||
/// but also for bounded channels between two subsystems.
|
||||
const CHANNEL_CAPACITY: usize = #message_channel_capacity;
|
||||
|
||||
/// Capacity of a signal channel between a subsystem and the overseer.
|
||||
const SIGNAL_CHANNEL_CAPACITY: usize = #signal_channel_capacity;
|
||||
|
||||
/// The log target tag.
|
||||
const LOG_TARGET: &'static str = #log_target;
|
||||
|
||||
/// The overseer.
|
||||
pub struct #overseer_name #generics {
|
||||
|
||||
#(
|
||||
/// A subsystem instance.
|
||||
#subsystem_name: OverseenSubsystem< #consumes >,
|
||||
)*
|
||||
|
||||
#(
|
||||
/// A user specified addendum field.
|
||||
#baggage_decl ,
|
||||
)*
|
||||
|
||||
/// Responsible for driving the subsystem futures.
|
||||
spawner: S,
|
||||
|
||||
/// The set of running subsystems.
|
||||
running_subsystems: #support_crate ::FuturesUnordered<
|
||||
BoxFuture<'static, ::std::result::Result<(), #error_ty>>
|
||||
>,
|
||||
|
||||
/// Gather running subsystems' outbound streams into one.
|
||||
to_overseer_rx: #support_crate ::stream::Fuse<
|
||||
#support_crate ::metered::UnboundedMeteredReceiver< ToOverseer >
|
||||
>,
|
||||
|
||||
/// Events that are sent to the overseer from the outside world.
|
||||
events_rx: #support_crate ::metered::MeteredReceiver< #event_ty >,
|
||||
}
|
||||
|
||||
impl #generics #overseer_name #generics #where_clause {
|
||||
/// Send the given signal, a terminatin signal, to all subsystems
|
||||
/// and wait for all subsystems to go down.
|
||||
///
|
||||
/// The definition of a termination signal is up to the user and
|
||||
/// implementation specific.
|
||||
pub async fn wait_terminate(&mut self, signal: #signal_ty, timeout: ::std::time::Duration) -> ::std::result::Result<(), #error_ty > {
|
||||
#(
|
||||
::std::mem::drop(self. #subsystem_name .send_signal(signal.clone()).await);
|
||||
)*
|
||||
let _ = signal;
|
||||
|
||||
let mut timeout_fut = #support_crate ::Delay::new(
|
||||
timeout
|
||||
).fuse();
|
||||
|
||||
loop {
|
||||
select! {
|
||||
_ = self.running_subsystems.next() => {
|
||||
if self.running_subsystems.is_empty() {
|
||||
break;
|
||||
}
|
||||
},
|
||||
_ = timeout_fut => break,
|
||||
complete => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Broadcast a signal to all subsystems.
|
||||
pub async fn broadcast_signal(&mut self, signal: #signal_ty) -> ::std::result::Result<(), #error_ty > {
|
||||
#(
|
||||
let _ = self. #subsystem_name .send_signal(signal.clone()).await;
|
||||
)*
|
||||
let _ = signal;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Route a particular message to a subsystem that consumes the message.
|
||||
pub async fn route_message(&mut self, message: #message_wrapper, origin: &'static str) -> ::std::result::Result<(), #error_ty > {
|
||||
match message {
|
||||
#(
|
||||
#message_wrapper :: #consumes_variant ( inner ) =>
|
||||
OverseenSubsystem::< #consumes >::send_message2(&mut self. #subsystem_name, inner, origin ).await?,
|
||||
)*
|
||||
// subsystems that are still work in progress
|
||||
#(
|
||||
#message_wrapper :: #unconsumes_variant ( _ ) => {}
|
||||
)*
|
||||
#message_wrapper :: Empty => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Extract information from each subsystem.
|
||||
pub fn map_subsystems<'a, Mapper, Output>(&'a self, mapper: Mapper)
|
||||
-> Vec<Output>
|
||||
where
|
||||
#(
|
||||
Mapper: MapSubsystem<&'a OverseenSubsystem< #consumes >, Output=Output>,
|
||||
)*
|
||||
{
|
||||
vec![
|
||||
#(
|
||||
mapper.map_subsystem( & self. #subsystem_name ),
|
||||
)*
|
||||
]
|
||||
}
|
||||
|
||||
/// Get access to internal task spawner.
|
||||
pub fn spawner<'a> (&'a mut self) -> &'a mut S {
|
||||
&mut self.spawner
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ts
|
||||
}
|
||||
|
||||
pub(crate) fn impl_overseen_subsystem(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
let signal = &info.extern_signal_ty;
|
||||
let error_ty = &info.extern_error_ty;
|
||||
let support_crate = info.support_crate_name();
|
||||
|
||||
let ts = quote::quote! {
|
||||
|
||||
use #support_crate ::futures::SinkExt as _;
|
||||
|
||||
/// A subsystem that the overseer oversees.
|
||||
///
|
||||
/// Ties together the [`Subsystem`] itself and it's running instance
|
||||
/// (which may be missing if the [`Subsystem`] is not running at the moment
|
||||
/// for whatever reason).
|
||||
///
|
||||
/// [`Subsystem`]: trait.Subsystem.html
|
||||
pub struct OverseenSubsystem<M> {
|
||||
/// The instance.
|
||||
pub instance: std::option::Option<
|
||||
#support_crate ::SubsystemInstance<M, #signal>
|
||||
>,
|
||||
}
|
||||
|
||||
impl<M> OverseenSubsystem<M> {
|
||||
/// Send a message to the wrapped subsystem.
|
||||
///
|
||||
/// If the inner `instance` is `None`, nothing is happening.
|
||||
pub async fn send_message2(&mut self, message: M, origin: &'static str) -> ::std::result::Result<(), #error_ty > {
|
||||
const MESSAGE_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
|
||||
if let Some(ref mut instance) = self.instance {
|
||||
match instance.tx_bounded.send(MessagePacket {
|
||||
signals_received: instance.signals_received,
|
||||
message: message.into(),
|
||||
}).timeout(MESSAGE_TIMEOUT).await
|
||||
{
|
||||
None => {
|
||||
#support_crate ::tracing::error!(
|
||||
target: LOG_TARGET,
|
||||
%origin,
|
||||
"Subsystem {} appears unresponsive.",
|
||||
instance.name,
|
||||
);
|
||||
Err(#error_ty :: from(
|
||||
#support_crate ::OverseerError::SubsystemStalled(instance.name)
|
||||
))
|
||||
}
|
||||
Some(res) => res.map_err(Into::into),
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a signal to the wrapped subsystem.
|
||||
///
|
||||
/// If the inner `instance` is `None`, nothing is happening.
|
||||
pub async fn send_signal(&mut self, signal: #signal) -> ::std::result::Result<(), #error_ty > {
|
||||
const SIGNAL_TIMEOUT: ::std::time::Duration = ::std::time::Duration::from_secs(10);
|
||||
|
||||
if let Some(ref mut instance) = self.instance {
|
||||
match instance.tx_signal.send(signal).timeout(SIGNAL_TIMEOUT).await {
|
||||
None => {
|
||||
Err(#error_ty :: from(
|
||||
#support_crate ::OverseerError::SubsystemStalled(instance.name)
|
||||
))
|
||||
}
|
||||
Some(res) => {
|
||||
let res = res.map_err(Into::into);
|
||||
if res.is_ok() {
|
||||
instance.signals_received += 1;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
ts
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![deny(unused_crate_dependencies)]
|
||||
|
||||
use proc_macro2::{Span, Ident, TokenStream};
|
||||
use syn::{parse2, Result};
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
mod impl_builder;
|
||||
mod impl_misc;
|
||||
mod impl_overseer;
|
||||
mod parse_attr;
|
||||
mod parse_struct;
|
||||
mod impl_channels_out;
|
||||
mod impl_dispatch;
|
||||
mod impl_message_wrapper;
|
||||
|
||||
use impl_builder::*;
|
||||
use impl_channels_out::*;
|
||||
use impl_dispatch::*;
|
||||
use impl_message_wrapper::*;
|
||||
use impl_misc::*;
|
||||
use impl_overseer::*;
|
||||
use parse_attr::*;
|
||||
use parse_struct::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn overlord(attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let attr: TokenStream = attr.into();
|
||||
let item: TokenStream = item.into();
|
||||
impl_overseer_gen(attr, item).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||
}
|
||||
|
||||
pub(crate) fn impl_overseer_gen(attr: TokenStream, orig: TokenStream) -> Result<proc_macro2::TokenStream> {
|
||||
let args: AttrArgs = parse2(attr)?;
|
||||
let message_wrapper = args.message_wrapper;
|
||||
|
||||
let of: OverseerGuts = parse2(orig)?;
|
||||
|
||||
let support_crate_name = if cfg!(test) {
|
||||
quote!{crate}
|
||||
} else {
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
let crate_name = crate_name("polkadot-overseer-gen")
|
||||
.expect("Support crate polkadot-overseer-gen is present in `Cargo.toml`. qed");
|
||||
match crate_name {
|
||||
FoundCrate::Itself => quote!{crate},
|
||||
FoundCrate::Name(name) => Ident::new(&name, Span::call_site()).to_token_stream(),
|
||||
}
|
||||
};
|
||||
let info = OverseerInfo {
|
||||
support_crate_name,
|
||||
subsystems: of.subsystems,
|
||||
baggage: of.baggage,
|
||||
overseer_name: of.name,
|
||||
message_wrapper,
|
||||
message_channel_capacity: args.message_channel_capacity,
|
||||
signal_channel_capacity: args.signal_channel_capacity,
|
||||
extern_event_ty: args.extern_event_ty,
|
||||
extern_signal_ty: args.extern_signal_ty,
|
||||
extern_error_ty: args.extern_error_ty,
|
||||
extern_network_ty: args.extern_network_ty,
|
||||
outgoing_ty: args.outgoing_ty,
|
||||
};
|
||||
|
||||
let mut additive = impl_overseer_struct(&info);
|
||||
additive.extend(impl_builder(&info));
|
||||
|
||||
additive.extend(impl_overseen_subsystem(&info));
|
||||
additive.extend(impl_channels_out_struct(&info));
|
||||
additive.extend(impl_misc(&info));
|
||||
|
||||
additive.extend(impl_message_wrapper_enum(&info)?);
|
||||
additive.extend(impl_dispatch(&info));
|
||||
|
||||
Ok(additive)
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::Span;
|
||||
use std::collections::{hash_map::RandomState, HashMap};
|
||||
use syn::parse::{Parse, ParseBuffer};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Error, Ident, LitInt, Path, Result, Token};
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
mod kw {
|
||||
syn::custom_keyword!(event);
|
||||
syn::custom_keyword!(signal);
|
||||
syn::custom_keyword!(error);
|
||||
syn::custom_keyword!(network);
|
||||
syn::custom_keyword!(outgoing);
|
||||
syn::custom_keyword!(gen);
|
||||
syn::custom_keyword!(signal_capacity);
|
||||
syn::custom_keyword!(message_capacity);
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum OverseerAttrItem {
|
||||
ExternEventType {
|
||||
tag: kw::event,
|
||||
eq_token: Token![=],
|
||||
value: Path
|
||||
},
|
||||
ExternNetworkType {
|
||||
tag: kw::network,
|
||||
eq_token: Token![=],
|
||||
value: Path
|
||||
},
|
||||
ExternOverseerSignalType {
|
||||
tag: kw::signal,
|
||||
eq_token: Token![=],
|
||||
value: Path
|
||||
},
|
||||
ExternErrorType {
|
||||
tag: kw::error,
|
||||
eq_token: Token![=],
|
||||
value: Path
|
||||
},
|
||||
OutgoingType {
|
||||
tag: kw::outgoing,
|
||||
eq_token: Token![=],
|
||||
value: Path
|
||||
},
|
||||
MessageWrapperName {
|
||||
tag: kw::gen,
|
||||
eq_token: Token![=],
|
||||
value: Ident
|
||||
},
|
||||
SignalChannelCapacity {
|
||||
tag: kw::signal_capacity,
|
||||
eq_token: Token![=],
|
||||
value: usize
|
||||
},
|
||||
MessageChannelCapacity {
|
||||
tag: kw::message_capacity,
|
||||
eq_token: Token![=],
|
||||
value: usize
|
||||
},
|
||||
}
|
||||
|
||||
impl ToTokens for OverseerAttrItem {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
let ts = match self {
|
||||
Self::ExternEventType { tag, eq_token, value } => { quote!{ #tag #eq_token, #value } }
|
||||
Self::ExternNetworkType { tag, eq_token, value } => { quote!{ #tag #eq_token, #value } }
|
||||
Self::ExternOverseerSignalType { tag, eq_token, value } => { quote!{ #tag #eq_token, #value } }
|
||||
Self::ExternErrorType { tag, eq_token, value } => { quote!{ #tag #eq_token, #value } }
|
||||
Self::OutgoingType { tag, eq_token, value } => { quote!{ #tag #eq_token, #value } }
|
||||
Self::MessageWrapperName { tag, eq_token, value } => { quote!{ #tag #eq_token, #value } }
|
||||
Self::SignalChannelCapacity { tag, eq_token, value } => { quote!{ #tag #eq_token, #value } }
|
||||
Self::MessageChannelCapacity { tag, eq_token, value } => { quote!{ #tag #eq_token, #value } }
|
||||
};
|
||||
tokens.extend(ts.into_iter());
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for OverseerAttrItem {
|
||||
fn parse(input: &ParseBuffer) -> Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(kw::event) {
|
||||
Ok(OverseerAttrItem::ExternEventType {
|
||||
tag: input.parse::<kw::event>()?,
|
||||
eq_token: input.parse()?,
|
||||
value: input.parse()?,
|
||||
})
|
||||
} else if lookahead.peek(kw::signal) {
|
||||
Ok(OverseerAttrItem::ExternOverseerSignalType {
|
||||
tag: input.parse::<kw::signal>()?,
|
||||
eq_token: input.parse()?,
|
||||
value: input.parse()?,
|
||||
})
|
||||
} else if lookahead.peek(kw::error) {
|
||||
Ok(OverseerAttrItem::ExternErrorType {
|
||||
tag: input.parse::<kw::error>()?,
|
||||
eq_token: input.parse()?,
|
||||
value: input.parse()?,
|
||||
})
|
||||
} else if lookahead.peek(kw::network) {
|
||||
Ok(OverseerAttrItem::ExternNetworkType {
|
||||
tag: input.parse::<kw::network>()?,
|
||||
eq_token: input.parse()?,
|
||||
value: input.parse()?,
|
||||
})
|
||||
} else if lookahead.peek(kw::outgoing) {
|
||||
Ok(OverseerAttrItem::OutgoingType {
|
||||
tag: input.parse::<kw::outgoing>()?,
|
||||
eq_token: input.parse()?,
|
||||
value: input.parse()?,
|
||||
})
|
||||
} else if lookahead.peek(kw::gen) {
|
||||
Ok(OverseerAttrItem::MessageWrapperName {
|
||||
tag: input.parse::<kw::gen>()?,
|
||||
eq_token: input.parse()?,
|
||||
value: input.parse()?,
|
||||
})
|
||||
} else if lookahead.peek(kw::signal_capacity) {
|
||||
Ok(OverseerAttrItem::SignalChannelCapacity {
|
||||
tag: input.parse::<kw::signal_capacity>()?,
|
||||
eq_token: input.parse()?,
|
||||
value: input.parse::<LitInt>()?.base10_parse::<usize>()?
|
||||
})
|
||||
} else if lookahead.peek(kw::message_capacity) {
|
||||
Ok(OverseerAttrItem::MessageChannelCapacity {
|
||||
tag: input.parse::<kw::message_capacity>()?,
|
||||
eq_token: input.parse()?,
|
||||
value: input.parse::<LitInt>()?.base10_parse::<usize>()?,
|
||||
})
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attribute arguments
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct AttrArgs {
|
||||
pub(crate) message_wrapper: Ident,
|
||||
pub(crate) extern_event_ty: Path,
|
||||
pub(crate) extern_signal_ty: Path,
|
||||
pub(crate) extern_error_ty: Path,
|
||||
/// A external subsystem that both consumes and produces messages
|
||||
/// but is not part of the band of subsystems, it's a mere proxy
|
||||
/// to another entity that consumes/produces messages.
|
||||
pub(crate) extern_network_ty: Option<Path>,
|
||||
pub(crate) outgoing_ty: Option<Path>,
|
||||
pub(crate) signal_channel_capacity: usize,
|
||||
pub(crate) message_channel_capacity: usize,
|
||||
}
|
||||
|
||||
macro_rules! extract_variant {
|
||||
($unique:expr, $variant:ident ; default = $fallback:expr) => {
|
||||
extract_variant!($unique, $variant)
|
||||
.unwrap_or_else(|| { $fallback })
|
||||
};
|
||||
($unique:expr, $variant:ident ; err = $err:expr) => {
|
||||
extract_variant!($unique, $variant)
|
||||
.ok_or_else(|| {
|
||||
Error::new(Span::call_site(), $err)
|
||||
})
|
||||
};
|
||||
($unique:expr, $variant:ident) => {
|
||||
$unique.values()
|
||||
.find_map(|item| {
|
||||
if let OverseerAttrItem:: $variant { value, ..} = item {
|
||||
Some(value.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
impl Parse for AttrArgs {
|
||||
fn parse(input: &ParseBuffer) -> Result<Self> {
|
||||
let items: Punctuated<OverseerAttrItem, Token![,]> = input.parse_terminated(OverseerAttrItem::parse)?;
|
||||
|
||||
let mut unique = HashMap::<std::mem::Discriminant<OverseerAttrItem>, OverseerAttrItem, RandomState>::default();
|
||||
for item in items {
|
||||
if let Some(first) = unique.insert(std::mem::discriminant(&item), item.clone()) {
|
||||
let mut e = Error::new(item.span(), format!("Duplicate definition of overseer generation type found"));
|
||||
e.combine(Error::new(first.span(), "previously defined here."));
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
let signal_channel_capacity = extract_variant!(unique, SignalChannelCapacity; default = 64_usize);
|
||||
let message_channel_capacity = extract_variant!(unique, MessageChannelCapacity; default = 1024_usize);
|
||||
|
||||
let error = extract_variant!(unique, ExternErrorType; err = "Must declare the overseer error type via `error=..`.")?;
|
||||
let event = extract_variant!(unique, ExternEventType; err = "Must declare the overseer event type via `event=..`.")?;
|
||||
let signal = extract_variant!(unique, ExternOverseerSignalType; err = "Must declare the overseer signal type via `span=..`.")?;
|
||||
let message_wrapper = extract_variant!(unique, MessageWrapperName; err = "Must declare the overseer generated wrapping message type via `gen=..`.")?;
|
||||
let network = extract_variant!(unique, ExternNetworkType);
|
||||
let outgoing = extract_variant!(unique, OutgoingType);
|
||||
|
||||
Ok(AttrArgs {
|
||||
signal_channel_capacity,
|
||||
message_channel_capacity,
|
||||
extern_event_ty: event,
|
||||
extern_signal_ty: signal,
|
||||
extern_error_ty: error,
|
||||
extern_network_ty: network,
|
||||
outgoing_ty: outgoing,
|
||||
message_wrapper,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use std::collections::{hash_map::RandomState, HashSet, HashMap};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::{
|
||||
Attribute, Field, FieldsNamed, Ident, Token, Type, AttrStyle, Path,
|
||||
Error, GenericParam, ItemStruct, Result, Visibility
|
||||
};
|
||||
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
mod kw {
|
||||
syn::custom_keyword!(wip);
|
||||
syn::custom_keyword!(no_dispatch);
|
||||
syn::custom_keyword!(blocking);
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum SubSysAttrItem {
|
||||
/// The subsystem is still a work in progress
|
||||
/// and should not be communicated with.
|
||||
Wip(kw::wip),
|
||||
/// The subsystem is blocking and requires to be
|
||||
/// spawned on an exclusive thread.
|
||||
Blocking(kw::blocking),
|
||||
/// External messages should not be - after being converted -
|
||||
/// be dispatched to the annotated subsystem.
|
||||
NoDispatch(kw::no_dispatch),
|
||||
}
|
||||
|
||||
impl Parse for SubSysAttrItem {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
Ok(if lookahead.peek(kw::wip) {
|
||||
Self::Wip(input.parse::<kw::wip>()?)
|
||||
} else if lookahead.peek(kw::blocking) {
|
||||
Self::Blocking(input.parse::<kw::blocking>()?)
|
||||
} else if lookahead.peek(kw::no_dispatch) {
|
||||
Self::NoDispatch(input.parse::<kw::no_dispatch>()?)
|
||||
} else {
|
||||
return Err(lookahead.error())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for SubSysAttrItem {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
let ts = match self {
|
||||
Self::Wip(wip) => { quote!{ #wip } }
|
||||
Self::Blocking(blocking) => { quote!{ #blocking } }
|
||||
Self::NoDispatch(no_dispatch) => { quote!{ #no_dispatch } }
|
||||
};
|
||||
tokens.extend(ts.into_iter());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A field of the struct annotated with
|
||||
/// `#[subsystem(no_dispatch, , A | B | C)]`
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct SubSysField {
|
||||
/// Name of the field.
|
||||
pub(crate) name: Ident,
|
||||
/// Generate generic type name for the `AllSubsystems` type
|
||||
/// which is also used `#wrapper_message :: #variant` variant
|
||||
/// part.
|
||||
pub(crate) generic: Ident,
|
||||
/// Type to be consumed by the subsystem.
|
||||
pub(crate) consumes: Path,
|
||||
/// If `no_dispatch` is present, if the message is incoming via
|
||||
/// an extern `Event`, it will not be dispatched to all subsystems.
|
||||
pub(crate) no_dispatch: bool,
|
||||
/// If the subsystem implementation is blocking execution and hence
|
||||
/// has to be spawned on a separate thread or thread pool.
|
||||
pub(crate) blocking: bool,
|
||||
/// The subsystem is a work in progress.
|
||||
/// Avoids dispatching `Wrapper` type messages, but generates the variants.
|
||||
/// Does not require the subsystem to be instanciated with the builder pattern.
|
||||
pub(crate) wip: bool,
|
||||
}
|
||||
|
||||
fn try_type_to_path(ty: Type, span: Span) -> Result<Path> {
|
||||
match ty {
|
||||
Type::Path(path) => Ok(path.path),
|
||||
_ => Err(Error::new(span, "Type must be a path expression.")),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! extract_variant {
|
||||
($unique:expr, $variant:ident ; default = $fallback:expr) => {
|
||||
extract_variant!($unique, $variant)
|
||||
.unwrap_or_else(|| { $fallback })
|
||||
};
|
||||
($unique:expr, $variant:ident ; err = $err:expr) => {
|
||||
extract_variant!($unique, $variant)
|
||||
.ok_or_else(|| {
|
||||
Error::new(Span::call_site(), $err)
|
||||
})
|
||||
};
|
||||
($unique:expr, $variant:ident) => {
|
||||
$unique.values()
|
||||
.find_map(|item| {
|
||||
if let SubSysAttrItem:: $variant ( _ ) = item {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
pub(crate) struct SubSystemTags {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) no_dispatch: bool,
|
||||
/// The subsystem is WIP, only generate the `Wrapper` variant, but do not forward messages
|
||||
/// and also not include the subsystem in the list of subsystems.
|
||||
pub(crate) wip: bool,
|
||||
pub(crate) blocking: bool,
|
||||
pub(crate) consumes: Path,
|
||||
}
|
||||
|
||||
impl Parse for SubSystemTags {
|
||||
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
|
||||
let attrs = Attribute::parse_outer(input)?;
|
||||
|
||||
let input = input;
|
||||
let content;
|
||||
let _ = syn::parenthesized!(content in input);
|
||||
|
||||
let mut items = Punctuated::new();
|
||||
while let Ok(tag) = content.call(SubSysAttrItem::parse) {
|
||||
items.push_value(tag);
|
||||
items.push_punct(content.call(<Token![,]>::parse)?);
|
||||
}
|
||||
|
||||
assert!(items.empty_or_trailing(), "Always followed by the message type to consume. qed");
|
||||
|
||||
let consumes = content.parse::<Path>()?;
|
||||
|
||||
let mut unique = HashMap::<std::mem::Discriminant<SubSysAttrItem>, SubSysAttrItem, RandomState>::default();
|
||||
for item in items {
|
||||
if let Some(first) = unique.insert(std::mem::discriminant(&item), item.clone()) {
|
||||
let mut e = Error::new(item.span(), format!("Duplicate definition of subsystem attribute found"));
|
||||
e.combine(Error::new(first.span(), "previously defined here."));
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
let no_dispatch = extract_variant!(unique, NoDispatch; default = false);
|
||||
let blocking = extract_variant!(unique, Blocking; default = false);
|
||||
let wip = extract_variant!(unique, Wip; default = false);
|
||||
|
||||
Ok(Self { attrs, no_dispatch, blocking, consumes, wip })
|
||||
}
|
||||
}
|
||||
|
||||
/// Fields that are _not_ subsystems.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct BaggageField {
|
||||
pub(crate) field_name: Ident,
|
||||
pub(crate) field_ty: Path,
|
||||
pub(crate) generic: bool,
|
||||
pub(crate) vis: Visibility,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct OverseerInfo {
|
||||
/// Where the support crate `::polkadot_overseer_gen` lives.
|
||||
pub(crate) support_crate_name: TokenStream,
|
||||
|
||||
/// Fields annotated with `#[subsystem(..)]`.
|
||||
pub(crate) subsystems: Vec<SubSysField>,
|
||||
/// Fields that do not define a subsystem,
|
||||
/// but are mere baggage.
|
||||
pub(crate) baggage: Vec<BaggageField>,
|
||||
/// Name of the wrapping enum for all messages, defaults to `AllMessages`.
|
||||
pub(crate) message_wrapper: Ident,
|
||||
/// Name of the overseer struct, used as a prefix for
|
||||
/// almost all generated types.
|
||||
pub(crate) overseer_name: Ident,
|
||||
|
||||
/// Size of the bounded channel.
|
||||
pub(crate) message_channel_capacity: usize,
|
||||
/// Size of the bounded signal channel.
|
||||
pub(crate) signal_channel_capacity: usize,
|
||||
|
||||
/// Signals to be sent, sparse information that is used intermittently.
|
||||
pub(crate) extern_signal_ty: Path,
|
||||
|
||||
/// Incoming event type from the outer world, usually an external framework of some sort.
|
||||
pub(crate) extern_event_ty: Path,
|
||||
|
||||
/// Incoming event type from an external entity, commonly from the network.
|
||||
pub(crate) extern_network_ty: Option<Path>,
|
||||
|
||||
/// Type of messages that are sent to an external subsystem.
|
||||
/// Merely here to be included during generation of `message_wrapper` type.
|
||||
pub(crate) outgoing_ty: Option<Path>,
|
||||
|
||||
/// Incoming event type from the outer world, commonly from the network.
|
||||
pub(crate) extern_error_ty: Path,
|
||||
}
|
||||
|
||||
impl OverseerInfo {
|
||||
pub(crate) fn support_crate_name(&self) -> &TokenStream {
|
||||
&self.support_crate_name
|
||||
}
|
||||
|
||||
pub(crate) fn variant_names(&self) -> Vec<Ident> {
|
||||
self.subsystems
|
||||
.iter()
|
||||
.map(|ssf| ssf.generic.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn variant_names_without_wip(&self) -> Vec<Ident> {
|
||||
self.subsystems
|
||||
.iter()
|
||||
.filter(|ssf| !ssf.wip)
|
||||
.map(|ssf| ssf.generic.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn variant_names_only_wip(&self) -> Vec<Ident> {
|
||||
self.subsystems
|
||||
.iter()
|
||||
.filter(|ssf| ssf.wip)
|
||||
.map(|ssf| ssf.generic.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn subsystems(&self) -> &[SubSysField] {
|
||||
self.subsystems.as_slice()
|
||||
}
|
||||
|
||||
pub(crate) fn subsystem_names_without_wip(&self) -> Vec<Ident> {
|
||||
self.subsystems
|
||||
.iter()
|
||||
.filter(|ssf| !ssf.wip)
|
||||
.map(|ssf| ssf.name.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn baggage_names(&self) -> Vec<Ident> {
|
||||
self.baggage.iter().map(|bag| bag.field_name.clone()).collect::<Vec<_>>()
|
||||
}
|
||||
pub(crate) fn baggage_types(&self) -> Vec<Path> {
|
||||
self.baggage.iter().map(|bag| bag.field_ty.clone()).collect::<Vec<_>>()
|
||||
}
|
||||
pub(crate) fn baggage_decl(&self) -> Vec<TokenStream> {
|
||||
self.baggage
|
||||
.iter()
|
||||
.map(|bag| {
|
||||
let BaggageField {
|
||||
vis,
|
||||
field_ty,
|
||||
field_name,
|
||||
..
|
||||
} = bag;
|
||||
quote!{ #vis #field_name: #field_ty }
|
||||
})
|
||||
.collect::<Vec<TokenStream>>()
|
||||
}
|
||||
|
||||
/// Generic types per subsystem, as defined by the user.
|
||||
pub(crate) fn builder_generic_types(&self) -> Vec<Ident> {
|
||||
self.subsystems
|
||||
.iter()
|
||||
.filter(|ssf| !ssf.wip)
|
||||
.map(|sff| sff.generic.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn baggage_generic_types(&self) -> Vec<Ident> {
|
||||
self.baggage
|
||||
.iter()
|
||||
.filter(|bag| bag.generic)
|
||||
.filter_map(|bag| bag.field_ty.get_ident().cloned())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn consumes(&self) -> Vec<Path> {
|
||||
self.subsystems.iter().map(|ssf| ssf.consumes.clone()).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn channel_names_without_wip(&self, suffix: &'static str) -> Vec<Ident> {
|
||||
self.subsystems
|
||||
.iter()
|
||||
.filter(|ssf| !ssf.wip)
|
||||
.map(|ssf| Ident::new(&(ssf.name.to_string() + suffix), ssf.name.span()))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn consumes_without_wip(&self) -> Vec<Path> {
|
||||
self.subsystems
|
||||
.iter()
|
||||
.filter(|ssf| !ssf.wip)
|
||||
.map(|ssf| ssf.consumes.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
/// Internals of the overseer.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct OverseerGuts {
|
||||
pub(crate) name: Ident,
|
||||
pub(crate) subsystems: Vec<SubSysField>,
|
||||
pub(crate) baggage: Vec<BaggageField>,
|
||||
}
|
||||
|
||||
impl OverseerGuts {
|
||||
pub(crate) fn parse_fields(name: Ident, baggage_generics: HashSet<Ident>, fields: FieldsNamed) -> Result<Self> {
|
||||
let n = fields.named.len();
|
||||
let mut subsystems = Vec::with_capacity(n);
|
||||
let mut baggage = Vec::with_capacity(n);
|
||||
|
||||
// The types of `#[subsystem(..)]` annotated fields
|
||||
// have to be unique, since they are used as generics
|
||||
// for the builder pattern besides other places.
|
||||
let mut unique_subsystem_idents = HashSet::<Ident>::new();
|
||||
for Field { attrs, vis, ident, ty, .. } in fields.named.into_iter() {
|
||||
let mut consumes = attrs.iter().filter(|attr| attr.style == AttrStyle::Outer).filter_map(|attr| {
|
||||
let span = attr.path.span();
|
||||
attr.path.get_ident().filter(|ident| *ident == "subsystem").map(move |_ident| {
|
||||
let attr_tokens = attr.tokens.clone();
|
||||
(attr_tokens, span)
|
||||
})
|
||||
});
|
||||
let ident = ident.ok_or_else(|| Error::new(ty.span(), "Missing identifier for member. BUG"))?;
|
||||
|
||||
if let Some((attr_tokens, span)) = consumes.next() {
|
||||
if let Some((_attr_tokens2, span2)) = consumes.next() {
|
||||
return Err({
|
||||
let mut err = Error::new(span, "The first subsystem annotation is at");
|
||||
err.combine(Error::new(span2, "but another here for the same field."));
|
||||
err
|
||||
});
|
||||
}
|
||||
let mut consumes_paths = Vec::with_capacity(attrs.len());
|
||||
let attr_tokens = attr_tokens.clone();
|
||||
let variant: SubSystemTags = syn::parse2(attr_tokens.clone())?;
|
||||
consumes_paths.push(variant.consumes);
|
||||
|
||||
let field_ty = try_type_to_path(ty, span)?;
|
||||
let generic = field_ty.get_ident().ok_or_else(|| Error::new(field_ty.span(), "Must be an identifier, not a path."))?.clone();
|
||||
if let Some(previous) = unique_subsystem_idents.get(&generic) {
|
||||
let mut e = Error::new(generic.span(), format!("Duplicate subsystem names `{}`", generic));
|
||||
e.combine(Error::new(previous.span(), "previously defined here."));
|
||||
return Err(e)
|
||||
}
|
||||
unique_subsystem_idents.insert(generic.clone());
|
||||
|
||||
subsystems.push(SubSysField {
|
||||
name: ident,
|
||||
generic,
|
||||
consumes: consumes_paths[0].clone(),
|
||||
no_dispatch: variant.no_dispatch,
|
||||
wip: variant.wip,
|
||||
blocking: variant.blocking,
|
||||
});
|
||||
} else {
|
||||
let field_ty = try_type_to_path(ty, ident.span())?;
|
||||
let generic = field_ty.get_ident().map(|ident| baggage_generics.contains(ident)).unwrap_or_default();
|
||||
baggage.push(BaggageField { field_name: ident, generic, field_ty, vis });
|
||||
}
|
||||
}
|
||||
Ok(Self { name, subsystems, baggage })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for OverseerGuts {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let ds: ItemStruct = input.parse()?;
|
||||
match ds.fields {
|
||||
syn::Fields::Named(named) => {
|
||||
let name = ds.ident.clone();
|
||||
|
||||
// collect the indepedentent subsystem generics
|
||||
// which need to be carried along, there are the non-generated ones
|
||||
let mut orig_generics = ds.generics;
|
||||
|
||||
// remove defaults from types
|
||||
let mut baggage_generic_idents = HashSet::with_capacity(orig_generics.params.len());
|
||||
orig_generics.params = orig_generics
|
||||
.params
|
||||
.into_iter()
|
||||
.map(|mut generic| {
|
||||
match generic {
|
||||
GenericParam::Type(ref mut param) => {
|
||||
baggage_generic_idents.insert(param.ident.clone());
|
||||
param.eq_token = None;
|
||||
param.default = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
generic
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self::parse_fields(name, baggage_generic_idents, named)
|
||||
}
|
||||
syn::Fields::Unit => {
|
||||
Err(Error::new(ds.fields.span(), "Must be a struct with named fields. Not an unit struct."))
|
||||
}
|
||||
syn::Fields::Unnamed(unnamed) => {
|
||||
Err(Error::new(unnamed.span(), "Must be a struct with named fields. Not an unnamed fields struct."))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use assert_matches::assert_matches;
|
||||
use quote::quote;
|
||||
use syn::parse_quote;
|
||||
|
||||
#[test]
|
||||
fn print() {
|
||||
let attr = quote! {
|
||||
gen=AllMessage,
|
||||
event=::some::why::ExternEvent,
|
||||
signal=SigSigSig,
|
||||
signal_capacity=111,
|
||||
message_capacity=222,
|
||||
error=OverseerError,
|
||||
};
|
||||
|
||||
let item = quote! {
|
||||
pub struct Ooooh<X = Pffffffft> where X: Secrit {
|
||||
#[subsystem(no_dispatch, Foo)]
|
||||
sub0: FooSubsystem,
|
||||
|
||||
#[subsystem(blocking, Bar)]
|
||||
yyy: BaersBuyBilliardBalls,
|
||||
|
||||
#[subsystem(no_dispatch, blocking, Twain)]
|
||||
fff: Beeeeep,
|
||||
|
||||
#[subsystem(Rope)]
|
||||
mc: MountainCave,
|
||||
|
||||
metrics: Metrics,
|
||||
}
|
||||
};
|
||||
|
||||
let output = impl_overseer_gen(attr, item).expect("Simple example always works. qed");
|
||||
println!("//generated:");
|
||||
println!("{}", output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_parse_full() {
|
||||
let item: OverseerGuts = parse_quote! {
|
||||
pub struct Ooooh<X = Pffffffft> where X: Secrit {
|
||||
#[subsystem(no_dispatch, Foo)]
|
||||
sub0: FooSubsystem,
|
||||
|
||||
#[subsystem(blocking, Bar)]
|
||||
yyy: BaersBuyBilliardBalls,
|
||||
|
||||
#[subsystem(no_dispatch, blocking, Twain)]
|
||||
fff: Beeeeep,
|
||||
|
||||
#[subsystem(Rope)]
|
||||
mc: MountainCave,
|
||||
|
||||
metrics: Metrics,
|
||||
}
|
||||
};
|
||||
let _ = dbg!(item);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_parse_basic() {
|
||||
let item: OverseerGuts = parse_quote! {
|
||||
pub struct Ooooh {
|
||||
#[subsystem(Foo)]
|
||||
sub0: FooSubsystem,
|
||||
}
|
||||
};
|
||||
let _ = dbg!(item);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attr_full() {
|
||||
let attr: AttrArgs = parse_quote! {
|
||||
gen=AllMessage, event=::some::why::ExternEvent, signal=SigSigSig, signal_capacity=111, message_capacity=222,
|
||||
error=OverseerError,
|
||||
};
|
||||
assert_matches!(attr, AttrArgs {
|
||||
message_channel_capacity,
|
||||
signal_channel_capacity,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(message_channel_capacity, 222);
|
||||
assert_eq!(signal_channel_capacity, 111);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attr_partial() {
|
||||
let attr: AttrArgs = parse_quote! {
|
||||
gen=AllMessage, event=::some::why::ExternEvent, signal=::foo::SigSigSig,
|
||||
error=OverseerError,
|
||||
};
|
||||
assert_matches!(attr, AttrArgs {
|
||||
message_channel_capacity: _,
|
||||
signal_channel_capacity: _,
|
||||
..
|
||||
} => {
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,516 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! # Overseer
|
||||
//!
|
||||
//! `overseer` implements the Overseer architecture described in the
|
||||
//! [implementers-guide](https://w3f.github.io/parachain-implementers-guide/node/index.html).
|
||||
//! For the motivations behind implementing the overseer itself you should
|
||||
//! check out that guide, documentation in this crate will be mostly discussing
|
||||
//! technical stuff.
|
||||
//!
|
||||
//! An `Overseer` is something that allows spawning/stopping and overseeing
|
||||
//! asynchronous tasks as well as establishing a well-defined and easy to use
|
||||
//! protocol that the tasks can use to communicate with each other. It is desired
|
||||
//! that this protocol is the only way tasks communicate with each other, however
|
||||
//! at this moment there are no foolproof guards against other ways of communication.
|
||||
//!
|
||||
//! The `Overseer` is instantiated with a pre-defined set of `Subsystems` that
|
||||
//! share the same behavior from `Overseer`'s point of view.
|
||||
//!
|
||||
//! ```text
|
||||
//! +-----------------------------+
|
||||
//! | Overseer |
|
||||
//! +-----------------------------+
|
||||
//!
|
||||
//! ................| Overseer "holds" these and uses |..............
|
||||
//! . them to (re)start things .
|
||||
//! . .
|
||||
//! . +-------------------+ +---------------------+ .
|
||||
//! . | Subsystem1 | | Subsystem2 | .
|
||||
//! . +-------------------+ +---------------------+ .
|
||||
//! . | | .
|
||||
//! ..................................................................
|
||||
//! | |
|
||||
//! start() start()
|
||||
//! V V
|
||||
//! ..................| Overseer "runs" these |.......................
|
||||
//! . +--------------------+ +---------------------+ .
|
||||
//! . | SubsystemInstance1 | <-- bidir --> | SubsystemInstance2 | .
|
||||
//! . +--------------------+ +---------------------+ .
|
||||
//! ..................................................................
|
||||
//! ```
|
||||
|
||||
// #![deny(unused_results)]
|
||||
// unused dependencies can not work for test and examples at the same time
|
||||
// yielding false positives
|
||||
#![deny(missing_docs)]
|
||||
#![deny(unused_crate_dependencies)]
|
||||
|
||||
pub use polkadot_overseer_gen_proc_macro::overlord;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use tracing;
|
||||
#[doc(hidden)]
|
||||
pub use metered;
|
||||
#[doc(hidden)]
|
||||
pub use sp_core::traits::SpawnNamed;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use futures::{
|
||||
self,
|
||||
select,
|
||||
StreamExt,
|
||||
FutureExt,
|
||||
poll,
|
||||
future::{
|
||||
Fuse, Future, BoxFuture
|
||||
},
|
||||
stream::{
|
||||
self, select, FuturesUnordered,
|
||||
},
|
||||
task::{
|
||||
Poll, Context,
|
||||
},
|
||||
channel::{mpsc, oneshot},
|
||||
};
|
||||
#[doc(hidden)]
|
||||
pub use std::pin::Pin;
|
||||
#[doc(hidden)]
|
||||
pub use async_trait::async_trait;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use std::time::Duration;
|
||||
use std::sync::{Arc, atomic::{self, AtomicUsize}};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use futures_timer::Delay;
|
||||
|
||||
pub use polkadot_node_network_protocol::WrongVariant;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// A type of messages that are sent from [`Subsystem`] to [`Overseer`].
|
||||
///
|
||||
/// Used to launch jobs.
|
||||
pub enum ToOverseer {
|
||||
/// A message that wraps something the `Subsystem` is desiring to
|
||||
/// spawn on the overseer and a `oneshot::Sender` to signal the result
|
||||
/// of the spawn.
|
||||
SpawnJob {
|
||||
/// Name of the task to spawn which be shown in jaeger and tracing logs.
|
||||
name: &'static str,
|
||||
/// The future to execute.
|
||||
s: BoxFuture<'static, ()>,
|
||||
},
|
||||
|
||||
/// Same as `SpawnJob` but for blocking tasks to be executed on a
|
||||
/// dedicated thread pool.
|
||||
SpawnBlockingJob {
|
||||
/// Name of the task to spawn which be shown in jaeger and tracing logs.
|
||||
name: &'static str,
|
||||
/// The future to execute.
|
||||
s: BoxFuture<'static, ()>,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Debug for ToOverseer {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::SpawnJob{ name, .. } => writeln!(f, "SpawnJob{{ {}, ..}}", name),
|
||||
Self::SpawnBlockingJob{ name, .. } => writeln!(f, "SpawnBlockingJob{{ {}, ..}}", name),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// A helper trait to map a subsystem to smth. else.
|
||||
pub trait MapSubsystem<T> {
|
||||
/// The output type of the mapping.
|
||||
type Output;
|
||||
|
||||
/// Consumes a `T` per subsystem, and maps it to `Self::Output`.
|
||||
fn map_subsystem(&self, sub: T) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<F, T, U> MapSubsystem<T> for F where F: Fn(T) -> U {
|
||||
type Output = U;
|
||||
|
||||
fn map_subsystem(&self, sub: T) -> U {
|
||||
(self)(sub)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapping type for messages.
|
||||
///
|
||||
/// Includes a counter to synchronize signals with messages,
|
||||
/// such that no inconsistent message sequences are prevented.
|
||||
#[derive(Debug)]
|
||||
pub struct MessagePacket<T> {
|
||||
/// Signal level at the point of reception.
|
||||
///
|
||||
/// Required to assure signals were consumed _before_
|
||||
/// consuming messages that are based on the assumption
|
||||
/// that a certain signal was assumed.
|
||||
pub signals_received: usize,
|
||||
/// The message to be sent/consumed.
|
||||
pub message: T,
|
||||
}
|
||||
|
||||
/// Create a packet from its parts.
|
||||
pub fn make_packet<T>(signals_received: usize, message: T) -> MessagePacket<T> {
|
||||
MessagePacket {
|
||||
signals_received,
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
/// Incoming messages from both the bounded and unbounded channel.
|
||||
pub type SubsystemIncomingMessages<M> = self::stream::Select<
|
||||
self::metered::MeteredReceiver<MessagePacket<M>>,
|
||||
self::metered::UnboundedMeteredReceiver<MessagePacket<M>>,
|
||||
>;
|
||||
|
||||
|
||||
/// Watermark to track the received signals.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct SignalsReceived(Arc<AtomicUsize>);
|
||||
|
||||
impl SignalsReceived {
|
||||
/// Load the current value of received signals.
|
||||
pub fn load(&self) -> usize {
|
||||
// off by a few is ok
|
||||
self.0.load(atomic::Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Increase the number of signals by one.
|
||||
pub fn inc(&self) {
|
||||
self.0.fetch_add(1, atomic::Ordering::Acquire);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// A trait to support the origin annotation
|
||||
/// such that errors across subsystems can be easier tracked.
|
||||
pub trait AnnotateErrorOrigin: 'static + Send + Sync + std::error::Error {
|
||||
/// Annotate the error with a origin `str`.
|
||||
///
|
||||
/// Commonly this is used to create nested enum variants.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// E::WithOrigin("I am originally from Cowtown.", E::Variant)
|
||||
/// ```
|
||||
fn with_origin(self, origin: &'static str) -> Self;
|
||||
}
|
||||
|
||||
/// An asynchronous subsystem task..
|
||||
///
|
||||
/// In essence it's just a newtype wrapping a `BoxFuture`.
|
||||
pub struct SpawnedSubsystem<E>
|
||||
where
|
||||
E: std::error::Error
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ From<self::OverseerError>,
|
||||
{
|
||||
/// Name of the subsystem being spawned.
|
||||
pub name: &'static str,
|
||||
/// The task of the subsystem being spawned.
|
||||
pub future: BoxFuture<'static, Result<(), E>>,
|
||||
}
|
||||
|
||||
/// An error type that describes faults that may happen
|
||||
///
|
||||
/// These are:
|
||||
/// * Channels being closed
|
||||
/// * Subsystems dying when they are not expected to
|
||||
/// * Subsystems not dying when they are told to die
|
||||
/// * etc.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum OverseerError {
|
||||
#[error(transparent)]
|
||||
NotifyCancellation(#[from] oneshot::Canceled),
|
||||
|
||||
#[error(transparent)]
|
||||
QueueError(#[from] mpsc::SendError),
|
||||
|
||||
#[error("Failed to spawn task {0}")]
|
||||
TaskSpawn(&'static str),
|
||||
|
||||
#[error(transparent)]
|
||||
Infallible(#[from] std::convert::Infallible),
|
||||
|
||||
#[error("Failed to {0}")]
|
||||
Context(String),
|
||||
|
||||
#[error("Subsystem stalled: {0}")]
|
||||
SubsystemStalled(&'static str),
|
||||
|
||||
/// Per origin (or subsystem) annotations to wrap an error.
|
||||
#[error("Error originated in {origin}")]
|
||||
FromOrigin {
|
||||
/// An additional annotation tag for the origin of `source`.
|
||||
origin: &'static str,
|
||||
/// The wrapped error. Marked as source for tracking the error chain.
|
||||
#[source] source: Box<dyn 'static + std::error::Error + Send + Sync>
|
||||
},
|
||||
}
|
||||
|
||||
/// Alias for a result with error type `OverseerError`.
|
||||
pub type OverseerResult<T> = std::result::Result<T, self::OverseerError>;
|
||||
|
||||
/// Collection of meters related to a subsystem.
|
||||
#[derive(Clone)]
|
||||
pub struct SubsystemMeters {
|
||||
#[allow(missing_docs)]
|
||||
pub bounded: metered::Meter,
|
||||
#[allow(missing_docs)]
|
||||
pub unbounded: metered::Meter,
|
||||
#[allow(missing_docs)]
|
||||
pub signals: metered::Meter,
|
||||
}
|
||||
|
||||
impl SubsystemMeters {
|
||||
/// Read the values of all subsystem `Meter`s.
|
||||
pub fn read(&self) -> SubsystemMeterReadouts {
|
||||
SubsystemMeterReadouts {
|
||||
bounded: self.bounded.read(),
|
||||
unbounded: self.unbounded.read(),
|
||||
signals: self.signals.read(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Set of readouts of the `Meter`s of a subsystem.
|
||||
pub struct SubsystemMeterReadouts {
|
||||
#[allow(missing_docs)]
|
||||
pub bounded: metered::Readout,
|
||||
#[allow(missing_docs)]
|
||||
pub unbounded: metered::Readout,
|
||||
#[allow(missing_docs)]
|
||||
pub signals: metered::Readout,
|
||||
}
|
||||
|
||||
/// A running instance of some [`Subsystem`].
|
||||
///
|
||||
/// [`Subsystem`]: trait.Subsystem.html
|
||||
///
|
||||
/// `M` here is the inner message type, and _not_ the generated `enum AllMessages`.
|
||||
pub struct SubsystemInstance<Message, Signal> {
|
||||
/// Send sink for `Signal`s to be sent to a subsystem.
|
||||
pub tx_signal: crate::metered::MeteredSender<Signal>,
|
||||
/// Send sink for `Message`s to be sent to a subsystem.
|
||||
pub tx_bounded: crate::metered::MeteredSender<MessagePacket<Message>>,
|
||||
/// All meters of the particular subsystem instance.
|
||||
pub meters: SubsystemMeters,
|
||||
/// The number of signals already received.
|
||||
/// Required to assure messages and signals
|
||||
/// are processed correctly.
|
||||
pub signals_received: usize,
|
||||
/// Name of the subsystem instance.
|
||||
pub name: &'static str,
|
||||
}
|
||||
|
||||
/// A message type that a subsystem receives from an overseer.
|
||||
/// It wraps signals from an overseer and messages that are circulating
|
||||
/// between subsystems.
|
||||
///
|
||||
/// It is generic over over the message type `M` that a particular `Subsystem` may use.
|
||||
#[derive(Debug)]
|
||||
pub enum FromOverseer<Message, Signal> {
|
||||
/// Signal from the `Overseer`.
|
||||
Signal(Signal),
|
||||
|
||||
/// Some other `Subsystem`'s message.
|
||||
Communication {
|
||||
/// Contained message
|
||||
msg: Message,
|
||||
},
|
||||
}
|
||||
|
||||
impl<Signal, Message> From<Signal> for FromOverseer<Message, Signal> {
|
||||
fn from(signal: Signal) -> Self {
|
||||
Self::Signal(signal)
|
||||
}
|
||||
}
|
||||
|
||||
/// A context type that is given to the [`Subsystem`] upon spawning.
|
||||
/// It can be used by [`Subsystem`] to communicate with other [`Subsystem`]s
|
||||
/// or spawn jobs.
|
||||
///
|
||||
/// [`Overseer`]: struct.Overseer.html
|
||||
/// [`SubsystemJob`]: trait.SubsystemJob.html
|
||||
#[async_trait::async_trait]
|
||||
pub trait SubsystemContext: Send + 'static {
|
||||
/// The message type of this context. Subsystems launched with this context will expect
|
||||
/// to receive messages of this type. Commonly uses the wrapping enum commonly called
|
||||
/// `AllMessages`.
|
||||
type Message: std::fmt::Debug + Send + 'static;
|
||||
/// And the same for signals.
|
||||
type Signal: std::fmt::Debug + Send + 'static;
|
||||
/// The overarching all messages enum.
|
||||
/// In some cases can be identical to `Self::Message`.
|
||||
type AllMessages: From<Self::Message> + Send + 'static;
|
||||
/// The sender type as provided by `sender()` and underlying.
|
||||
type Sender: SubsystemSender<Self::AllMessages> + Send + 'static;
|
||||
/// The error type.
|
||||
type Error: ::std::error::Error + ::std::convert::From< OverseerError > + Sync + Send + 'static;
|
||||
|
||||
/// Try to asynchronously receive a message.
|
||||
///
|
||||
/// This has to be used with caution, if you loop over this without
|
||||
/// using `pending!()` macro you will end up with a busy loop!
|
||||
async fn try_recv(&mut self) -> Result<Option<FromOverseer<Self::Message, Self::Signal>>, ()>;
|
||||
|
||||
/// Receive a message.
|
||||
async fn recv(&mut self) -> Result<FromOverseer<Self::Message, Self::Signal>, Self::Error>;
|
||||
|
||||
/// Spawn a child task on the executor.
|
||||
fn spawn(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
s: ::std::pin::Pin<Box<dyn crate::Future<Output = ()> + Send>>
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
/// Spawn a blocking child task on the executor's dedicated thread pool.
|
||||
fn spawn_blocking(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
s: ::std::pin::Pin<Box<dyn crate::Future<Output = ()> + Send>>,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
/// Send a direct message to some other `Subsystem`, routed based on message type.
|
||||
async fn send_message<X>(&mut self, msg: X)
|
||||
where
|
||||
Self::AllMessages: From<X>,
|
||||
X: Send,
|
||||
{
|
||||
self.sender().send_message(<Self::AllMessages>::from(msg)).await
|
||||
}
|
||||
|
||||
/// Send multiple direct messages to other `Subsystem`s, routed based on message type.
|
||||
async fn send_messages<X, T>(&mut self, msgs: T)
|
||||
where
|
||||
T: IntoIterator<Item = X> + Send,
|
||||
T::IntoIter: Send,
|
||||
Self::AllMessages: From<X>,
|
||||
X: Send,
|
||||
{
|
||||
self.sender().send_messages(msgs.into_iter().map(|x| <Self::AllMessages>::from(x))).await
|
||||
}
|
||||
|
||||
/// Send a message using the unbounded connection.
|
||||
fn send_unbounded_message<X>(&mut self, msg: X)
|
||||
where
|
||||
Self::AllMessages: From<X>,
|
||||
X: Send,
|
||||
{
|
||||
self.sender().send_unbounded_message(Self::AllMessages::from(msg))
|
||||
}
|
||||
|
||||
/// Obtain the sender.
|
||||
fn sender(&mut self) -> &mut Self::Sender;
|
||||
}
|
||||
|
||||
/// A trait that describes the [`Subsystem`]s that can run on the [`Overseer`].
|
||||
///
|
||||
/// It is generic over the message type circulating in the system.
|
||||
/// The idea that we want some type containing persistent state that
|
||||
/// can spawn actually running subsystems when asked.
|
||||
///
|
||||
/// [`Overseer`]: struct.Overseer.html
|
||||
/// [`Subsystem`]: trait.Subsystem.html
|
||||
pub trait Subsystem<Ctx, E>
|
||||
where
|
||||
Ctx: SubsystemContext,
|
||||
E: std::error::Error + Send + Sync + 'static + From<self::OverseerError>,
|
||||
{
|
||||
/// Start this `Subsystem` and return `SpawnedSubsystem`.
|
||||
fn start(self, ctx: Ctx) -> SpawnedSubsystem < E >;
|
||||
}
|
||||
|
||||
|
||||
/// Sender end of a channel to interface with a subsystem.
|
||||
#[async_trait::async_trait]
|
||||
pub trait SubsystemSender<Message>: Send + Clone + 'static {
|
||||
/// Send a direct message to some other `Subsystem`, routed based on message type.
|
||||
async fn send_message(&mut self, msg: Message);
|
||||
|
||||
/// Send multiple direct messages to other `Subsystem`s, routed based on message type.
|
||||
async fn send_messages<T>(&mut self, msgs: T)
|
||||
where T: IntoIterator<Item = Message> + Send, T::IntoIter: Send;
|
||||
|
||||
/// Send a message onto the unbounded queue of some other `Subsystem`, routed based on message
|
||||
/// type.
|
||||
///
|
||||
/// This function should be used only when there is some other bounding factor on the messages
|
||||
/// sent with it. Otherwise, it risks a memory leak.
|
||||
fn send_unbounded_message(&mut self, msg: Message);
|
||||
}
|
||||
|
||||
/// A future that wraps another future with a `Delay` allowing for time-limited futures.
|
||||
#[pin_project::pin_project]
|
||||
pub struct Timeout<F: Future> {
|
||||
#[pin]
|
||||
future: F,
|
||||
#[pin]
|
||||
delay: Delay,
|
||||
}
|
||||
|
||||
/// Extends `Future` to allow time-limited futures.
|
||||
pub trait TimeoutExt: Future {
|
||||
/// Adds a timeout of `duration` to the given `Future`.
|
||||
/// Returns a new `Future`.
|
||||
fn timeout(self, duration: Duration) -> Timeout<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Timeout {
|
||||
future: self,
|
||||
delay: Delay::new(duration),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> TimeoutExt for F where F: Future{}
|
||||
|
||||
impl<F> Future for Timeout<F> where F: Future {
|
||||
type Output = Option<F::Output>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
|
||||
if this.delay.poll(ctx).is_ready() {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
if let Poll::Ready(output) = this.future.poll(ctx) {
|
||||
return Poll::Ready(Some(output));
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
// The generated code requires quite a bit of surrounding code to work.
|
||||
// Please refer to [the examples](examples/dummy.rs) and
|
||||
// [the minimal usage example](../examples/minimal-example.rs).
|
||||
|
||||
#[test]
|
||||
fn ui_compile_fail() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/err-*.rs");
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use polkadot_overseer_gen::*;
|
||||
|
||||
#[derive(Default)]
|
||||
struct AwesomeSubSys;
|
||||
|
||||
#[derive(Default)]
|
||||
struct AwesomeSubSys2;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SigSigSig;
|
||||
|
||||
struct Event;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MsgStrukt(u8);
|
||||
|
||||
#[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)]
|
||||
struct Overseer {
|
||||
#[subsystem(MsgStrukt)]
|
||||
sub0: AwesomeSubSys,
|
||||
|
||||
#[subsystem(MsgStrukt)]
|
||||
sub1: AwesomeSubSys2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DummySpawner;
|
||||
|
||||
struct DummyCtx;
|
||||
|
||||
fn main() {
|
||||
let overseer = Overseer::<_,_>::builder()
|
||||
.sub0(AwesomeSubSys::default())
|
||||
.spawner(DummySpawner)
|
||||
.build(|| -> DummyCtx { DummyCtx } );
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
error[E0119]: conflicting implementations of trait `std::convert::From<MsgStrukt>` for type `AllMessages`
|
||||
--> $DIR/err-01-duplicate-consumer.rs:19:1
|
||||
|
|
||||
19 | #[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| first implementation here
|
||||
| conflicting implementation for `AllMessages`
|
||||
|
|
||||
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0119]: conflicting implementations of trait `polkadot_overseer_gen::SubsystemSender<MsgStrukt>` for type `OverseerSubsystemSender`
|
||||
--> $DIR/err-01-duplicate-consumer.rs:19:1
|
||||
|
|
||||
19 | #[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| first implementation here
|
||||
| conflicting implementation for `OverseerSubsystemSender`
|
||||
|
|
||||
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,32 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use polkadot_overseer_gen::*;
|
||||
|
||||
#[derive(Default)]
|
||||
struct AwesomeSubSys;
|
||||
|
||||
struct SigSigSig;
|
||||
|
||||
struct Event;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct MsgStrukt(u8);
|
||||
|
||||
#[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)]
|
||||
enum Overseer {
|
||||
#[subsystem(MsgStrukt)]
|
||||
Sub0(AwesomeSubSys),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DummySpawner;
|
||||
|
||||
struct DummyCtx;
|
||||
|
||||
fn main() {
|
||||
let overseer = Overseer::<_,_>::builder()
|
||||
.sub0(AwesomeSubSys::default())
|
||||
.i_like_pie(std::f64::consts::PI)
|
||||
.spawner(DummySpawner)
|
||||
.build(|| -> DummyCtx { DummyCtx } );
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: expected `struct`
|
||||
--> $DIR/err-02-enum.rs:16:1
|
||||
|
|
||||
16 | enum Overseer {
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared type `Overseer`
|
||||
--> $DIR/err-02-enum.rs:27:17
|
||||
|
|
||||
27 | let overseer = Overseer::<_,_>::builder()
|
||||
| ^^^^^^^^ use of undeclared type `Overseer`
|
||||
@@ -0,0 +1,39 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use polkadot_overseer_gen::*;
|
||||
|
||||
#[derive(Default)]
|
||||
struct AwesomeSubSys;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SigSigSig;
|
||||
|
||||
struct Event;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct MsgStrukt(u8);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct MsgStrukt2(f64);
|
||||
|
||||
#[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)]
|
||||
struct Overseer {
|
||||
#[subsystem(MsgStrukt)]
|
||||
sub0: AwesomeSubSys,
|
||||
|
||||
#[subsystem(MsgStrukt2)]
|
||||
sub1: AwesomeSubSys,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DummySpawner;
|
||||
|
||||
struct DummyCtx;
|
||||
|
||||
fn main() {
|
||||
let overseer = Overseer::<_,_>::builder()
|
||||
.sub0(AwesomeSubSys::default())
|
||||
.i_like_pie(std::f64::consts::PI)
|
||||
.spawner(DummySpawner)
|
||||
.build(|| -> DummyCtx { DummyCtx } );
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
error: Duplicate subsystem names `AwesomeSubSys`
|
||||
--> $DIR/err-03-subsys-twice.rs:25:8
|
||||
|
|
||||
25 | sub1: AwesomeSubSys,
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: previously defined here.
|
||||
--> $DIR/err-03-subsys-twice.rs:22:8
|
||||
|
|
||||
22 | sub0: AwesomeSubSys,
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared type `Overseer`
|
||||
--> $DIR/err-03-subsys-twice.rs:34:17
|
||||
|
|
||||
34 | let overseer = Overseer::<_,_>::builder()
|
||||
| ^^^^^^^^ use of undeclared type `Overseer`
|
||||
@@ -0,0 +1,36 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use polkadot_overseer_gen::*;
|
||||
|
||||
#[derive(Default)]
|
||||
struct AwesomeSubSys;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SigSigSig;
|
||||
|
||||
struct Event;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MsgStrukt(u8);
|
||||
|
||||
#[overlord(signal=SigSigSig, event=Event, gen=AllMessages)]
|
||||
struct Overseer {
|
||||
#[subsystem(no_dispatch, MsgStrukt)]
|
||||
sub0: AwesomeSubSys,
|
||||
|
||||
i_like_pie: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DummySpawner;
|
||||
|
||||
struct DummyCtx;
|
||||
|
||||
fn main() {
|
||||
let _ = Overseer::builder()
|
||||
.sub0(AwesomeSubSys::default())
|
||||
.i_like_pie(std::f64::consts::PI)
|
||||
.spawner(DummySpawner)
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
error: Must declare the overseer error type via `error=..`.
|
||||
--> $DIR/err-04-missing-error.rs:16:1
|
||||
|
|
||||
16 | #[overlord(signal=SigSigSig, event=Event, gen=AllMessages)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared type `Overseer`
|
||||
--> $DIR/err-04-missing-error.rs:30:10
|
||||
|
|
||||
30 | let _ = Overseer::builder()
|
||||
| ^^^^^^^^ use of undeclared type `Overseer`
|
||||
Reference in New Issue
Block a user