Skip to main content

fuel_core/
lib.rs

1#![deny(clippy::arithmetic_side_effects)]
2#![deny(clippy::cast_possible_truncation)]
3#![deny(unused_crate_dependencies)]
4#![deny(warnings)]
5
6#[cfg(test)]
7use fuel_core as _;
8use futures::{
9    StreamExt,
10    stream::FuturesUnordered,
11};
12#[cfg(test)]
13use tracing_subscriber as _;
14
15use crate::service::genesis::NotifyCancel;
16use tokio_util::sync::CancellationToken;
17
18pub const VERSION: &str = env!("CARGO_PKG_VERSION");
19
20#[doc(no_inline)]
21pub use fuel_core_chain_config as chain_config;
22#[cfg(feature = "p2p")]
23#[doc(no_inline)]
24pub use fuel_core_p2p as p2p;
25#[cfg(feature = "parallel-executor")]
26#[doc(no_inline)]
27pub use fuel_core_parallel_executor as parallel_executor;
28#[doc(no_inline)]
29pub use fuel_core_producer as producer;
30#[cfg(feature = "relayer")]
31#[doc(no_inline)]
32pub use fuel_core_relayer as relayer;
33#[cfg(feature = "p2p")]
34#[doc(no_inline)]
35pub use fuel_core_sync as sync;
36#[doc(no_inline)]
37pub use fuel_core_tx_status_manager as tx_status_manager;
38#[doc(no_inline)]
39pub use fuel_core_txpool as txpool;
40#[doc(no_inline)]
41pub use fuel_core_types as types;
42#[doc(no_inline)]
43pub use fuel_core_upgradable_executor as upgradable_executor;
44
45pub mod coins_query;
46pub mod combined_database;
47pub mod database;
48pub mod executor;
49pub mod model;
50#[cfg(all(feature = "p2p", feature = "test-helpers"))]
51pub mod p2p_test_helpers;
52pub mod query;
53pub mod schema;
54pub mod service;
55pub mod state;
56
57// In the future this module will be a separate crate for `fuel-core-graphql-api`.
58mod graphql_api;
59
60pub mod fuel_core_graphql_api {
61    pub use crate::graphql_api::*;
62}
63
64#[cfg(test)]
65fuel_core_trace::enable_tracing!();
66
67#[derive(Clone)]
68pub struct ShutdownListener {
69    pub token: CancellationToken,
70}
71
72pub struct SignalKind {
73    _inner: tokio::signal::unix::SignalKind,
74    variant: SignalVariant,
75}
76
77impl SignalKind {
78    pub fn new(variant: SignalVariant) -> Self {
79        let _inner = match variant {
80            SignalVariant::SIGTERM => tokio::signal::unix::SignalKind::terminate(),
81            SignalVariant::SIGINT => tokio::signal::unix::SignalKind::interrupt(),
82            SignalVariant::SIGABRT => {
83                tokio::signal::unix::SignalKind::from_raw(libc::SIGABRT)
84            }
85            SignalVariant::SIGHUP => tokio::signal::unix::SignalKind::hangup(),
86            SignalVariant::SIGQUIT => tokio::signal::unix::SignalKind::quit(),
87        };
88
89        Self { _inner, variant }
90    }
91
92    pub fn decompose(self) -> (tokio::signal::unix::SignalKind, SignalVariant) {
93        (self._inner, self.variant)
94    }
95}
96
97#[derive(Debug, Clone, Copy)]
98pub enum SignalVariant {
99    SIGTERM,
100    SIGINT,
101    SIGABRT,
102    SIGHUP,
103    SIGQUIT,
104}
105
106impl ShutdownListener {
107    pub fn spawn() -> Self {
108        let token = CancellationToken::new();
109        {
110            let token = token.clone();
111            tokio::spawn(async move {
112                #[cfg(unix)]
113                {
114                    let signal_kinds: Vec<_> = vec![
115                        SignalVariant::SIGINT,
116                        SignalVariant::SIGTERM,
117                        SignalVariant::SIGABRT,
118                        SignalVariant::SIGHUP,
119                        SignalVariant::SIGQUIT,
120                    ]
121                    .into_iter()
122                    .map(SignalKind::new)
123                    .collect();
124
125                    let signals_with_variants: Vec<_> = signal_kinds
126                        .into_iter()
127                        .filter_map(|signal_kind| {
128                            let (signal_kind, variant) = signal_kind.decompose();
129                            match tokio::signal::unix::signal(signal_kind) {
130                                Ok(signal) => {
131                                    tracing::info!("Registered signal handler for {variant:?}");
132                                    Some((signal, variant))
133                                }
134                                Err(e) => {
135                                    tracing::warn!("Failed to register signal handler for {variant:?}: {e}");
136                                    None
137                                }
138                            }
139                        })
140                        .collect();
141
142                    let mut signal_futs: FuturesUnordered<_> = signals_with_variants
143                        .into_iter()
144                        .map(|(mut signal, variant)| async move {
145                            signal.recv().await;
146                            variant
147                        })
148                        .collect();
149
150                    let variant = signal_futs.next().await;
151                    tracing::error!("Received shutdown signal: {variant:?}");
152                }
153                #[cfg(not(unix))]
154                {
155                    tokio::signal::ctrl_c().await?;
156                    tracing::info!("Received ctrl_c");
157                }
158                token.cancel();
159                tokio::io::Result::Ok(())
160            });
161        }
162        Self { token }
163    }
164}
165
166impl NotifyCancel for ShutdownListener {
167    async fn wait_until_cancelled(&self) -> anyhow::Result<()> {
168        self.token.cancelled().await;
169        Ok(())
170    }
171
172    fn is_cancelled(&self) -> bool {
173        self.token.is_cancelled()
174    }
175}
176
177impl combined_database::ShutdownListener for ShutdownListener {
178    fn is_cancelled(&self) -> bool {
179        self.token.is_cancelled()
180    }
181}