referrerpolicy=no-referrer-when-downgrade

sc_cli/
signals.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use futures::{
20	future::{self, BoxFuture, FutureExt},
21	pin_mut, select, Future,
22};
23
24use sc_service::Error as ServiceError;
25
26/// Abstraction over OS signals to handle the shutdown of the node smoothly.
27///
28/// On `unix` this represents `SigInt` and `SigTerm`.
29pub struct Signals(BoxFuture<'static, ()>);
30
31impl Signals {
32	/// Return the signals future.
33	pub fn future(self) -> BoxFuture<'static, ()> {
34		self.0
35	}
36
37	/// Capture the relevant signals to handle shutdown of the node smoothly.
38	///
39	/// Needs to be called in a Tokio context to have access to the tokio reactor.
40	#[cfg(target_family = "unix")]
41	pub fn capture() -> std::result::Result<Self, ServiceError> {
42		use tokio::signal::unix::{signal, SignalKind};
43
44		let mut stream_int = signal(SignalKind::interrupt()).map_err(ServiceError::Io)?;
45		let mut stream_term = signal(SignalKind::terminate()).map_err(ServiceError::Io)?;
46
47		Ok(Signals(
48			async move {
49				future::select(stream_int.recv().boxed(), stream_term.recv().boxed()).await;
50			}
51			.boxed(),
52		))
53	}
54
55	/// Capture the relevant signals to handle shutdown of the node smoothly.
56	///
57	/// Needs to be called in a Tokio context to have access to the tokio reactor.
58	#[cfg(not(unix))]
59	pub fn capture() -> Result<Self, ServiceError> {
60		use tokio::signal::ctrl_c;
61
62		Ok(Signals(
63			async move {
64				let _ = ctrl_c().await;
65			}
66			.boxed(),
67		))
68	}
69
70	/// A dummy signal that never returns.
71	pub fn dummy() -> Self {
72		Self(future::pending().boxed())
73	}
74
75	/// Run a future task until receive a signal.
76	pub async fn run_until_signal<F, E>(self, func: F) -> Result<(), E>
77	where
78		F: Future<Output = Result<(), E>> + future::FusedFuture,
79		E: std::error::Error + Send + Sync + 'static,
80	{
81		let signals = self.future().fuse();
82
83		pin_mut!(func, signals);
84
85		select! {
86			_ = signals => {},
87			res = func => res?,
88		}
89
90		Ok(())
91	}
92
93	/// Execute the future task and returns it's value if it completes before the signal.
94	pub async fn try_until_signal<F, T>(self, func: F) -> Result<T, ()>
95	where
96		F: Future<Output = T> + future::FusedFuture,
97	{
98		let signals = self.future().fuse();
99
100		pin_mut!(func, signals);
101
102		select! {
103			s = signals => Err(s),
104			res = func => Ok(res),
105		}
106	}
107}