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
57mod 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}