client/common/
shutdown_handler.rs

1use std::{
2    sync::{Arc, Weak},
3    thread,
4    time::{Duration, Instant},
5};
6
7use ctrlc::CtrlC;
8use log::{debug, warn};
9use parking_lot::{Condvar, Mutex};
10
11use super::ClientTrait;
12
13pub fn run(
14    this: Box<dyn ClientTrait>, exit_cond_var: Arc<(Mutex<bool>, Condvar)>,
15) -> bool {
16    CtrlC::set_handler({
17        let e = exit_cond_var.clone();
18        move || {
19            *e.0.lock() = true;
20            e.1.notify_all();
21        }
22    });
23
24    let mut lock = exit_cond_var.0.lock();
25    if !*lock {
26        exit_cond_var.1.wait(&mut lock);
27    }
28
29    shutdown(this)
30}
31
32/// Returns whether the shutdown is considered clean.
33pub fn shutdown(this: Box<dyn ClientTrait>) -> bool {
34    // Signal metrics reporter threads to stop before dropping components.
35    metrics::stop();
36    diem_metrics::stop();
37
38    let (ledger_db, maybe_pos_handler, maybe_blockgen) =
39        this.take_out_components_for_shutdown();
40    drop(this);
41    if let Some(blockgen) = maybe_blockgen {
42        blockgen.stop();
43        drop(blockgen);
44    }
45    let maybe_pos_db = if let Some(pos_handler) = maybe_pos_handler {
46        let maybe_pos_db = pos_handler.stop();
47        drop(pos_handler);
48        maybe_pos_db
49    } else {
50        None
51    };
52
53    // Make sure ledger_db is properly dropped, so rocksdb can be closed
54    // cleanly
55    let mut graceful = true;
56    graceful &= check_graceful_shutdown(ledger_db);
57    debug!("ledger_db drop: graceful = {}", graceful);
58    if let Some((pos_ledger_db, consensus_db)) = maybe_pos_db {
59        graceful &= check_graceful_shutdown(pos_ledger_db);
60        debug!("pos_ledger_db drop: graceful = {}", graceful);
61        graceful &= check_graceful_shutdown(consensus_db);
62        debug!("consensus_db drop: graceful = {}", graceful);
63    }
64    graceful
65}
66
67/// Most Conflux components references block data manager.
68/// When block data manager is freed, all background threads must have
69/// already stopped.
70fn check_graceful_shutdown<T>(blockdata_manager_weak_ptr: Weak<T>) -> bool {
71    let sleep_duration = Duration::from_secs(1);
72    let warn_timeout = Duration::from_secs(5);
73    let max_timeout = Duration::from_secs(1200);
74    let instant = Instant::now();
75    let mut warned = false;
76    while instant.elapsed() < max_timeout {
77        if blockdata_manager_weak_ptr.upgrade().is_none() {
78            return true;
79        }
80        if !warned && instant.elapsed() > warn_timeout {
81            warned = true;
82            warn!("Shutdown is taking longer than expected.");
83        }
84        thread::sleep(sleep_duration);
85    }
86    eprintln!("Shutdown timeout reached, exiting uncleanly.");
87    false
88}