1use futures::channel::oneshot;
2use futures::channel::oneshot::Canceled;
3use futures::future::{Either, FusedFuture};
4use std::fmt::Display;
5use std::future::Future;
6use std::ops::Deref;
7use std::pin::{Pin, pin};
8use std::process::exit;
9use std::task::{Context, Poll};
10use std::{io, panic, thread};
11use tokio::runtime::Handle;
12use tokio::{signal, task};
13use tracing::level_filters::LevelFilter;
14use tracing::{debug, info, warn};
15use tracing_subscriber::layer::SubscriberExt;
16use tracing_subscriber::util::SubscriberInitExt;
17use tracing_subscriber::{EnvFilter, Layer, fmt};
18
19#[cfg(test)]
20mod tests;
21
22pub fn init_logger() {
23 console::set_colors_enabled(false);
31 console::set_colors_enabled_stderr(false);
32 console_0_15::set_colors_enabled(false);
33 console_0_15::set_colors_enabled_stderr(false);
34
35 let enable_color = if cfg!(windows) {
38 false
39 } else {
40 supports_color::on(supports_color::Stream::Stderr).is_some()
41 };
42
43 let res = tracing_subscriber::registry()
44 .with(
45 fmt::layer().with_ansi(enable_color).with_filter(
46 EnvFilter::builder()
47 .with_default_directive(LevelFilter::INFO.into())
48 .from_env_lossy(),
49 ),
50 )
51 .try_init();
52
53 if let Err(e) = res {
54 eprintln!(
57 "Failed to initialize logger: {e}. \
58 This is expected when running nexttest test functions under `cargo test`."
59 );
60 }
61}
62
63#[derive(Debug)]
66pub struct AsyncJoinOnDrop<T> {
67 handle: Option<task::JoinHandle<T>>,
68 abort_on_drop: bool,
69}
70
71impl<T> Drop for AsyncJoinOnDrop<T> {
72 #[inline]
73 fn drop(&mut self) {
74 if let Some(handle) = self.handle.take() {
75 if self.abort_on_drop {
76 handle.abort();
77 }
78
79 if !handle.is_finished() {
80 task::block_in_place(move || {
81 let _ = Handle::current().block_on(handle);
82 });
83 }
84 }
85 }
86}
87
88impl<T> AsyncJoinOnDrop<T> {
89 #[inline]
91 pub fn new(handle: task::JoinHandle<T>, abort_on_drop: bool) -> Self {
92 Self {
93 handle: Some(handle),
94 abort_on_drop,
95 }
96 }
97}
98
99impl<T> FusedFuture for AsyncJoinOnDrop<T> {
100 fn is_terminated(&self) -> bool {
101 self.handle.is_none()
102 }
103}
104
105impl<T> Future for AsyncJoinOnDrop<T> {
106 type Output = Result<T, task::JoinError>;
107
108 #[inline]
109 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
110 if let Some(handle) = self.handle.as_mut() {
111 let result = Pin::new(handle).poll(cx);
112 if result.is_ready() {
113 self.handle.take();
115 }
116 result
117 } else {
118 Poll::Pending
119 }
120 }
121}
122
123pub(crate) struct JoinOnDrop(Option<thread::JoinHandle<()>>);
125
126impl Drop for JoinOnDrop {
127 #[inline]
128 fn drop(&mut self) {
129 self.0
130 .take()
131 .expect("Always called exactly once; qed")
132 .join()
133 .expect("Panic if background thread panicked");
134 }
135}
136
137impl JoinOnDrop {
138 #[inline]
140 pub(crate) fn new(handle: thread::JoinHandle<()>) -> Self {
141 Self(Some(handle))
142 }
143}
144
145impl Deref for JoinOnDrop {
146 type Target = thread::JoinHandle<()>;
147
148 #[inline]
149 fn deref(&self) -> &Self::Target {
150 self.0.as_ref().expect("Only dropped in Drop impl; qed")
151 }
152}
153
154pub fn run_future_in_dedicated_thread<CreateFut, Fut, T>(
159 create_future: CreateFut,
160 thread_name: String,
161) -> io::Result<impl Future<Output = Result<T, Canceled>> + Send>
162where
163 CreateFut: (FnOnce() -> Fut) + Send + 'static,
164 Fut: Future<Output = T> + 'static,
165 T: Send + 'static,
166{
167 let (drop_tx, drop_rx) = oneshot::channel::<()>();
168 let (result_tx, result_rx) = oneshot::channel();
169 let handle = Handle::current();
170 let join_handle = thread::Builder::new().name(thread_name).spawn(move || {
171 let _tokio_handle_guard = handle.enter();
172
173 let future = pin!(create_future());
174
175 let result = match handle.block_on(futures::future::select(future, drop_rx)) {
176 Either::Left((result, _)) => result,
177 Either::Right(_) => {
178 return;
180 }
181 };
182 if let Err(_error) = result_tx.send(result) {
183 debug!(
184 thread_name = ?thread::current().name(),
185 "Future finished, but receiver was already dropped",
186 );
187 }
188 })?;
189 let join_on_drop = JoinOnDrop::new(join_handle);
191
192 Ok(async move {
193 let result = result_rx.await;
194 drop(drop_tx);
195 drop(join_on_drop);
196 result
197 })
198}
199
200#[cfg(unix)]
202pub async fn shutdown_signal(process_kind: impl Display) {
203 use futures::FutureExt;
204 use std::pin::pin;
205
206 let mut sigint = signal::unix::signal(signal::unix::SignalKind::interrupt())
207 .expect("Setting signal handlers must never fail");
208 let mut sigterm = signal::unix::signal(signal::unix::SignalKind::terminate())
209 .expect("Setting signal handlers must never fail");
210
211 futures::future::select(
212 pin!(sigint.recv().map(|_| {
213 info!("Received SIGINT, shutting down {process_kind}...");
214 }),),
215 pin!(sigterm.recv().map(|_| {
216 info!("Received SIGTERM, shutting down {process_kind}...");
217 }),),
218 )
219 .await;
220}
221
222#[cfg(not(unix))]
224pub async fn shutdown_signal(process_kind: impl Display) {
225 signal::ctrl_c()
226 .await
227 .expect("Setting signal handlers must never fail");
228
229 info!("Received Ctrl+C, shutting down {process_kind}...");
230}
231
232pub fn raise_fd_limit() {
234 match fdlimit::raise_fd_limit() {
235 Ok(fdlimit::Outcome::LimitRaised { from, to }) => {
236 debug!(
237 "Increased file descriptor limit from previous (most likely soft) limit {} to \
238 new (most likely hard) limit {}",
239 from, to
240 );
241 }
242 Ok(fdlimit::Outcome::Unsupported) => {
243 }
245 Err(error) => {
246 warn!(
247 "Failed to increase file descriptor limit for the process due to an error: {}.",
248 error
249 );
250 }
251 }
252}
253
254pub fn set_exit_on_panic() {
257 let default_panic_hook = panic::take_hook();
258 panic::set_hook(Box::new(move |panic_info| {
259 default_panic_hook(panic_info);
260 exit(1);
261 }));
262}