diff --git a/substrate/frame/support/src/traits/hooks.rs b/substrate/frame/support/src/traits/hooks.rs index 45212c5650..965cce2342 100644 --- a/substrate/frame/support/src/traits/hooks.rs +++ b/substrate/frame/support/src/traits/hooks.rs @@ -19,7 +19,7 @@ use impl_trait_for_tuples::impl_for_tuples; use sp_arithmetic::traits::Saturating; -use sp_runtime::traits::MaybeSerializeDeserialize; +use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize}; /// The block initialization trait. /// @@ -80,13 +80,22 @@ pub trait OnIdle { } #[impl_for_tuples(30)] -impl OnIdle for Tuple { +impl OnIdle for Tuple { fn on_idle(n: BlockNumber, remaining_weight: crate::weights::Weight) -> crate::weights::Weight { + let on_idle_functions: &[fn( + BlockNumber, + crate::weights::Weight, + ) -> crate::weights::Weight] = &[for_tuples!( #( Tuple::on_idle ),* )]; let mut weight = 0; - for_tuples!( #( + let len = on_idle_functions.len(); + let start_index = n % (len as u32).into(); + let start_index = start_index.try_into().ok().expect( + "`start_index % len` always fits into `usize`, because `len` can be in maximum `usize::MAX`; qed" + ); + for on_idle in on_idle_functions.iter().cycle().skip(start_index).take(len) { let adjusted_remaining_weight = remaining_weight.saturating_sub(weight); - weight = weight.saturating_add(Tuple::on_idle(n.clone(), adjusted_remaining_weight)); - )* ); + weight = weight.saturating_add(on_idle(n, adjusted_remaining_weight)); + } weight } } @@ -341,4 +350,60 @@ mod tests { assert_eq!(<(Test, Test)>::on_initialize(0), 20); assert_eq!(<(Test, Test)>::on_runtime_upgrade(), 40); } + + #[test] + fn on_idle_round_robin_works() { + static mut ON_IDLE_INVOCATION_ORDER: sp_std::vec::Vec<&str> = sp_std::vec::Vec::new(); + + struct Test1; + struct Test2; + struct Test3; + type TestTuple = (Test1, Test2, Test3); + impl OnIdle for Test1 { + fn on_idle(_n: u32, _weight: crate::weights::Weight) -> crate::weights::Weight { + unsafe { + ON_IDLE_INVOCATION_ORDER.push("Test1"); + } + 0 + } + } + impl OnIdle for Test2 { + fn on_idle(_n: u32, _weight: crate::weights::Weight) -> crate::weights::Weight { + unsafe { + ON_IDLE_INVOCATION_ORDER.push("Test2"); + } + 0 + } + } + impl OnIdle for Test3 { + fn on_idle(_n: u32, _weight: crate::weights::Weight) -> crate::weights::Weight { + unsafe { + ON_IDLE_INVOCATION_ORDER.push("Test3"); + } + 0 + } + } + + unsafe { + TestTuple::on_idle(0, 0); + assert_eq!(ON_IDLE_INVOCATION_ORDER, ["Test1", "Test2", "Test3"].to_vec()); + ON_IDLE_INVOCATION_ORDER.clear(); + + TestTuple::on_idle(1, 0); + assert_eq!(ON_IDLE_INVOCATION_ORDER, ["Test2", "Test3", "Test1"].to_vec()); + ON_IDLE_INVOCATION_ORDER.clear(); + + TestTuple::on_idle(2, 0); + assert_eq!(ON_IDLE_INVOCATION_ORDER, ["Test3", "Test1", "Test2"].to_vec()); + ON_IDLE_INVOCATION_ORDER.clear(); + + TestTuple::on_idle(3, 0); + assert_eq!(ON_IDLE_INVOCATION_ORDER, ["Test1", "Test2", "Test3"].to_vec()); + ON_IDLE_INVOCATION_ORDER.clear(); + + TestTuple::on_idle(4, 0); + assert_eq!(ON_IDLE_INVOCATION_ORDER, ["Test2", "Test3", "Test1"].to_vec()); + ON_IDLE_INVOCATION_ORDER.clear(); + } + } } diff --git a/substrate/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr b/substrate/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr index 23651faa59..f3677113da 100644 --- a/substrate/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr @@ -5,9 +5,9 @@ error[E0107]: missing generics for trait `Hooks` | ^^^^^ expected 1 type argument | note: trait defined here, with 1 type parameter: `BlockNumber` - --> $DIR/hooks.rs:212:11 + --> $DIR/hooks.rs:221:11 | -212 | pub trait Hooks { +221 | pub trait Hooks { | ^^^^^ ----------- help: use angle brackets to add missing type argument |