Consolix
Loading...
Searching...
No Matches
ConsoleApplication.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _CONSOLIX_CONSOLE_APPLICATION_HPP_INCLUDED
3#define _CONSOLIX_CONSOLE_APPLICATION_HPP_INCLUDED
4
8
9#include <csignal>
10#include <cstdlib>
11#include <iostream>
12
13#if !defined(_WIN32) && !defined(_WIN64)
14#include <signal.h>
15#endif
16
17namespace consolix {
18
26 public:
27
31 static ConsoleApplication* instance = new ConsoleApplication();
32 return *instance;
33 }
34
40 template <typename Component, typename... Args>
41 std::shared_ptr<Component> add(Args&&... args) {
42 return m_manager.add<Component>(std::forward<Args>(args)...);
43 }
44
47 void add(std::shared_ptr<IAppComponent> component) {
48 m_manager.add(std::move(component));
49 }
50
53 void init() {
54 try {
55 while (!stop_requested() && !m_manager.initialize()) {
56 std::this_thread::sleep_for(std::chrono::milliseconds(1));
57 }
59 } catch (const std::exception& e) {
61 }
62 }
63
67 template <typename InitAction>
68 void init(InitAction init_action) {
69 try {
70 while (!stop_requested() && !m_manager.initialize()) {
71 std::this_thread::sleep_for(std::chrono::milliseconds(1));
72 }
74 init_action();
76 } catch (const std::exception& e) {
78 }
79 }
80
82 void run() {
83 if (m_running) return;
84 m_running = true;
86 init();
88 }
89
93 template <typename IterationAction>
94 void run(IterationAction iteration_action) {
95 if (m_running) return;
96 m_running = true;
98 init();
99 lifecycle_loop(iteration_action);
100 }
101
103 void stop() {
104 if (m_stopping) return;
105 m_stopping = true;
106 }
107
109 void shutdown(int signal) {
110 m_stopping = true;
111 cleanup(signal);
112 }
113
114 private:
116 std::atomic<bool> m_init{false};
117 std::atomic<bool> m_running{false};
118 std::atomic<bool> m_stopping{false};
119 std::atomic<bool> m_cleanup{false};
120
123# if defined(_WIN32) || defined(_WIN64)
124 SetConsoleCtrlHandler(console_handler, TRUE);
125# else
127
128 struct sigaction action = {};
129 action.sa_handler = signal_handler;
130 sigemptyset(&action.sa_mask);
131 sigaddset(&action.sa_mask, SIGINT);
132 sigaddset(&action.sa_mask, SIGTERM);
133 action.sa_flags = 0;
134
135 sigaction(SIGINT, &action, nullptr);
136 sigaction(SIGTERM, &action, nullptr);
137# endif
138 std::atexit(on_exit_handler);
139 }
140
144 void cleanup(int exit_code, bool wait_for_press = false) {
145 bool expected = false;
146 if (!m_cleanup.compare_exchange_strong(expected, true)) return;
147
148 m_stopping = true;
149
150# if !defined(_WIN32) && !defined(_WIN64)
151 PosixTerminationSignalMaskGuard posix_signal_mask_guard;
152# endif
153
154# if CONSOLIX_USE_LOGIT == 1
155 LOGIT_PRINT_INFO("Cleaning up application for exit code: ", exit_code);
156 try {
157 m_manager.shutdown(exit_code);
158 } catch (const std::exception& e) {
159 LOGIT_FATAL(e);
160 }
161 try {
163 } catch (const std::exception& e) {
164 LOGIT_FATAL(e);
165 }
166 LOGIT_SHUTDOWN();
167# else
168 try {
169 m_manager.shutdown(exit_code);
170 } catch (...) {}
171 try {
173 } catch (...) {}
174# endif
175 if (wait_for_press) {
176 CONSOLIX_STREAM() << "Press Enter to exit..." << std::endl;
177 std::cin.get();
178 }
179 std::exit(exit_code);
180 }
181
184 void handle_fatal_exception(const std::exception& e) {
185# if CONSOLIX_USE_LOGIT == 1
186 LOGIT_PRINT_FATAL("Unhandled exception: ", e.what());
187# endif //
188 cleanup(-1, static_cast<bool>(CONSOLIX_WAIT_ON_ERROR));
189 }
190
194 template <typename IterationAction>
195 void lifecycle_loop(IterationAction iteration_action) {
196 try {
197 while (!stop_requested()) {
198 m_manager.process();
199 iteration_action();
200 }
202 } catch (const std::exception& e) {
204 }
205 }
206
209 try {
210 while (!stop_requested()) {
211 m_manager.process();
212 }
214 } catch (const std::exception& e) {
216 }
217 }
218
223
224# if defined(_WIN32) || defined(_WIN64)
225
229 static BOOL WINAPI console_handler(DWORD win_event) {
230 switch (win_event) {
231 case CTRL_C_EVENT:
232 log_event("CTRL_C_EVENT");
233 break;
234 case CTRL_CLOSE_EVENT:
235 log_event("CTRL_CLOSE_EVENT");
236 break;
237 case CTRL_LOGOFF_EVENT:
238 log_event("CTRL_LOGOFF_EVENT");
239 break;
240 case CTRL_SHUTDOWN_EVENT:
241 log_event("CTRL_SHUTDOWN_EVENT");
242 break;
243 default:
244 log_event("UNKNOWN_EVENT");
245 break;
246 }
247 ConsoleApplication::get_instance().shutdown(event_to_exit_code(win_event));
248 return FALSE;
249 }
250
253 static void log_event(const char* event_name) {
254# if CONSOLIX_USE_LOGIT == 1
255 LOGIT_PRINT_INFO("Console event received: ", event_name);
256# endif
257 }
258
262 static int event_to_exit_code(DWORD win_event) {
263 switch (win_event) {
264 case CTRL_C_EVENT:
265 return SIGINT;
266 default:
267 return SIGTERM;
268 };
269 }
270
271# else
272
274 public:
276 sigemptyset(&m_mask);
277 sigaddset(&m_mask, SIGINT);
278 sigaddset(&m_mask, SIGTERM);
279
280 if (sigprocmask(SIG_BLOCK, &m_mask, &m_old_mask) == 0) {
281 m_active = true;
282 }
283 }
284
286 if (m_active) {
287 sigprocmask(SIG_SETMASK, &m_old_mask, nullptr);
288 }
289 }
290
291 private:
292 sigset_t m_mask{};
293 sigset_t m_old_mask{};
294 bool m_active{false};
295 };
296
299 static void signal_handler(int exit_code) {
300 pending_signal_code() = static_cast<std::sig_atomic_t>(exit_code);
302 }
303
305 static void reset_signal_state() {
308 }
309
312 if (signal_stop_requested() != 0) {
313 m_stopping = true;
314 }
315 return m_stopping.load();
316 }
317
319 int resolve_stop_exit_code(int fallback_exit_code) const {
320 const int pending_exit_code = static_cast<int>(pending_signal_code());
321 if (pending_exit_code != 0) {
322 return pending_exit_code;
323 }
324 return fallback_exit_code;
325 }
326
329 if (stop_requested()) {
331 }
332 }
333
335 static volatile std::sig_atomic_t& pending_signal_code() {
336 static volatile std::sig_atomic_t value = 0;
337 return value;
338 }
339
341 static volatile std::sig_atomic_t& signal_stop_requested() {
342 static volatile std::sig_atomic_t value = 0;
343 return value;
344 }
345
346# endif
347
348# if defined(_WIN32) || defined(_WIN64)
349 bool stop_requested() {
350 return m_stopping.load();
351 }
352
353 int resolve_stop_exit_code(int fallback_exit_code) const {
354 return fallback_exit_code;
355 }
356
357 void cleanup_if_stopping() {
358 if (m_stopping) {
359 cleanup(0);
360 }
361 }
362# endif
363
366
367 // Deleting copy and move constructors and assignment operators to enforce singleton.
372 }; // ConsoleApplication
373
374}; // namespace consolix
375
376#endif // _CONSOLIX_CONSOLE_APPLICATION_HPP_INCLUDED
#define CONSOLIX_STREAM()
Fallback for general logging.
Manages a collection of application components with lifecycle support.
std::shared_ptr< Component > add(Args &&... args)
Adds a new component to the application.
void add(std::shared_ptr< IAppComponent > component)
Adds an existing component to the application.
ConsoleApplication(const ConsoleApplication &)=delete
void setup_signal_handlers()
Sets up signal handlers for graceful application termination.
ConsoleApplication & operator=(ConsoleApplication &&)=delete
std::atomic< bool > m_running
Flag indicating whether the loop is running.
static volatile std::sig_atomic_t & signal_stop_requested()
Storage for the POSIX stop-request flag.
bool stop_requested()
Checks whether a POSIX signal requested shutdown and synchronizes the runtime state.
void lifecycle_loop()
The main lifecycle loop.
void run()
Runs the application with the registered components.
void run(IterationAction iteration_action)
Runs the application with a custom loop action.
static ConsoleApplication & get_instance()
Retrieves the singleton instance of the application.
static void signal_handler(int exit_code)
Handles a POSIX signal by recording a deferred shutdown request.
void cleanup_if_stopping()
Executes cleanup immediately if a stop request is already pending.
void lifecycle_loop(IterationAction iteration_action)
The main lifecycle loop with a custom action.
void cleanup(int exit_code, bool wait_for_press=false)
Cleans up the application and shuts down all components.
ConsoleApplication & operator=(const ConsoleApplication &)=delete
void init(InitAction init_action)
Initializes the application with a custom action.
static void reset_signal_state()
Resets POSIX signal state before installing handlers.
ConsoleApplication(ConsoleApplication &&)=delete
static void on_exit_handler()
Called upon normal program termination.
void init()
Initializes the application and its components. This method initializes all components in the manager...
void stop()
Stops the application's main loop.
static volatile std::sig_atomic_t & pending_signal_code()
Storage for the last requested POSIX shutdown signal.
int resolve_stop_exit_code(int fallback_exit_code) const
Resolves the exit code for the active stop request.
void handle_fatal_exception(const std::exception &e)
Handles fatal exceptions by logging the error and terminating the application.
static ServiceLocator & get_instance()
Retrieves the singleton instance of the ServiceLocator.
void clear_all()
Clears all registered resources.
#define CONSOLIX_WAIT_ON_ERROR
Enables or disables waiting for user input before exiting on a fatal error.
< Utility modules and helpers.