substrate_api_client/api/rpc_api/
runtime_update.rs

1/*
2	Copyright 2023 Supercomputing Systems AG
3	Licensed under the Apache License, Version 2.0 (the "License");
4	you may not use this file except in compliance with the License.
5	You may obtain a copy of the License at
6		http://www.apache.org/licenses/LICENSE-2.0
7	Unless required by applicable law or agreed to in writing, software
8	distributed under the License is distributed on an "AS IS" BASIS,
9	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10	See the License for the specific language governing permissions and
11	limitations under the License.
12*/
13use crate::{api::Error, rpc::Subscribe, rpc_api::EventSubscriptionFor, Result};
14use alloc::sync::Arc;
15use codec::{Decode, Encode};
16use core::sync::atomic::{AtomicBool, Ordering};
17use serde::de::DeserializeOwned;
18
19/// Struct to support waiting for runtime updates.
20pub struct RuntimeUpdateDetector<Hash, Client>
21where
22	Hash: DeserializeOwned + Copy + Decode,
23	Client: Subscribe,
24{
25	subscription: EventSubscriptionFor<Client, Hash>,
26	external_cancellation: Option<Arc<AtomicBool>>,
27}
28
29impl<Hash, Client> RuntimeUpdateDetector<Hash, Client>
30where
31	Hash: DeserializeOwned + Copy + Encode + Decode,
32	Client: Subscribe,
33{
34	pub fn new(subscription: EventSubscriptionFor<Client, Hash>) -> Self {
35		Self { subscription, external_cancellation: None }
36	}
37
38	/// Provide the `RuntimeUpdateDetector` with the additional option to cancel the waiting
39	/// from the outside.
40	pub fn new_with_cancellation(
41		subscription: EventSubscriptionFor<Client, Hash>,
42		cancellation: Arc<AtomicBool>,
43	) -> Self {
44		Self { subscription, external_cancellation: Some(cancellation) }
45	}
46
47	/// Returns true if a runtime update was detected, false if the wait was cancelled
48	/// If not cancelled, this method only returns/resolves once a runtime update is detected.
49	#[maybe_async::maybe_async(?Send)]
50	pub async fn detect_runtime_update(&mut self) -> Result<bool> {
51		'outer: loop {
52			if let Some(canceled) = &self.external_cancellation {
53				if canceled.load(Ordering::SeqCst) {
54					return Ok(false)
55				}
56			}
57			let event_records = self
58				.subscription
59				.next_events_from_metadata()
60				.await
61				.ok_or(Error::Other("Error receiving events".into()))??;
62			let event_iter = event_records.iter();
63			for event in event_iter {
64				if event?.is_code_update() {
65					break 'outer
66				}
67			}
68		}
69		Ok(true)
70	}
71}