otsdaq-mu2e-stm  5.02.01
signal_handler.cc
1 // Signal handler header
2 #include "Mu2e-STMDAQ/utils/signal_handler.hh"
3 
4 // Define static members
5 // Initialize exit request flag
6 std::atomic<bool> SignalHandler::exit_requested(false);
7 // Initialize mutex
8 std::mutex SignalHandler::confirm_mutex;
9 // Initialize condition variable
10 std::condition_variable SignalHandler::confirm_cv;
11 // Initialize signal listening thread
12 std::thread SignalHandler::signal_thread;
13 // Static pointer to store instance
14 SignalHandler* SignalHandler::instance = nullptr;
15 // Define and set the cpu manager static variables
16 std::shared_ptr<cpu_utils> SignalHandler::cpu = nullptr;
17 
18 // Constructor
19 SignalHandler::SignalHandler(const std::shared_ptr<AsyncLogger>& logger_,
20  const std::shared_ptr<cpu_utils>& cpu_)
21  : logger(logger_)
22 {
23  // Set the static instance pointer
24  instance = this;
25  // Initialize exit request flag
26  exit_requested.store(false);
27  // Start listening for signals and handling confirmation
28  signal_thread = std::thread(listenForSignals);
29  // Set the cpu manager static variable
30  cpu = cpu_;
31 }
32 
33 // Signal handler function (executed immediately on receiving a signal)
34 void SignalHandler::handleSignal(int signal)
35 {
36  // Set the exit request flag to true
37  exit_requested.store(true);
38 
39  // Log the received signal if an instance exists and logging is enabled
40  if(instance && instance->logger)
41  {
42  instance->logger->log("SignalHandler: Signal " + std::to_string(signal) +
43  " received. Awaiting user confirmation.",
44  1);
45  }
46 }
47 
48 // Function to continuously listen for signals and process exit confirmation
49 void SignalHandler::listenForSignals()
50 {
51  // NEED TO FIX BACKWARDS COMPATABILITY ISSUE HERE
52  //ThreadManager::pin_thread_to_core(1,"SignalHandler");
53 
54  // Pin thread to core
55  if(cpu)
56  {
57  cpu->get_next_core("SignalHandler");
58  }
59 
60  // Struct to define signal behavior
61  struct sigaction action;
62  // Assign handler function to handle signals
63  action.sa_handler = handleSignal;
64  // Clear signal mask
65  sigemptyset(&action.sa_mask);
66  // Default flags
67  action.sa_flags = 0;
68  // Handle Ctrl+C (SIGINT)
69  sigaction(SIGINT, &action, nullptr);
70  // Handle termination signal (SIGTERM)
71  sigaction(SIGTERM, &action, nullptr);
72  // Handle user-defined signal (SIGUSR1)
73  sigaction(SIGUSR1, &action, nullptr);
74 
75  // Log that the signal listener has started (if logging is enabled)
76  if(instance && instance->logger)
77  {
78  instance->logger->log("SignalHandler: Signal handler initialised.", 1);
79  }
80 
81  // Continuous loop to handle incoming signals
82  while(!stop::should_stop())
83  {
84  // Wait until an exit request is triggered
85  while(!exit_requested.load() && !stop::should_stop())
86  {
87  std::this_thread::sleep_for(
88  std::chrono::milliseconds(100)); // Avoid busy-waiting
89  }
90 
91  // If stop was requested externally, break the loop
92  if(stop::should_stop())
93  break;
94 
95  // Ask for confirmation outside of the signal handler (signal-safe)
96  std::cout << "\nCaught interrupt. Are you sure you want to exit? (y/n): "
97  << std::endl;
98 
99  // Log the confirmation request (if logging is enabled)
100  if(instance && instance->logger)
101  {
102  instance->logger->log("SignalHandler: Awaiting user confirmation for exit.",
103  1);
104  }
105 
106  char choice;
107  auto start_time =
108  std::chrono::steady_clock::now(); // Start timer for timeout mechanism
109 
110  // Wait for user input or timeout after 5 seconds
111  while(!(std::cin >> choice) &&
112  (std::chrono::steady_clock::now() - start_time) < std::chrono::seconds(5))
113  {
114  std::this_thread::sleep_for(
115  std::chrono::milliseconds(100)); // Small sleep to prevent CPU overuse
116  }
117 
118  // If user confirms exit, stop the program
119  if(choice == 'y' || choice == 'Y')
120  {
121  std::cout << "Exiting safely...\n";
122  if(instance && instance->logger)
123  {
124  instance->logger->log(
125  "SignalHandler: User confirmed exit. Stopping program.", 1);
126  }
127 
128  // Request program stop
129  stop::trigger_user_stop();
130  }
131  else
132  {
133  // If user cancels, reset exit flag and continue execution
134  std::cout << "Resuming execution.\n";
135  if(instance && instance->logger)
136  {
137  instance->logger->log(
138  "SignalHandler: User denied exit. Resuming program.", 1);
139  }
140  exit_requested.store(
141  false); // Reset flag so future signals can trigger exit confirmation again
142  }
143  }
144 
145  // Log the shutdown of the signal listener
146  if(instance && instance->logger)
147  {
148  instance->logger->log("SignalHandler: Signal listener shutting down.", 1);
149  }
150 }