55#include < fstream>
66#include < thread>
77
8+ #include < sys/eventfd.h>
89#include < execinfo.h>
10+ #include < unistd.h>
911
1012const int BT_ENTRY_MAX = 25 ;
1113struct CrashInfo {
@@ -16,13 +18,11 @@ struct CrashInfo {
1618
1719CrashInfo crash_info;
1820
19- /*
20- * As of c++17 the only safe stdc++ methods are plain lock-free atomic methods
21- * This sadly means that using std::semaphore *could* cause issues according to the standard.
22- */
23- std::atomic_bool crashed = false ;
24- std::atomic_bool crashlog_ready = false ;
25- std::atomic_bool crashlog_complete = false ;
21+ std::atomic<bool > crashed = false ;
22+ std::atomic<bool > crashlog_ready = false ;
23+
24+ // Use eventfd for async-signal safe waiting
25+ int crashlog_complete = -1 ;
2626
2727void flag_set (std::atomic_bool &atom) {
2828 atom.store (true );
@@ -32,20 +32,32 @@ void flag_wait(std::atomic_bool &atom) {
3232 atom.wait (false );
3333}
3434
35+ void signal_crashlog_complete () {
36+ if (crashlog_complete == -1 )
37+ return ;
38+ uint64_t v = 1 ;
39+ write (crashlog_complete, &v, sizeof (v));
40+ }
41+
3542std::thread crashlog_thread;
36- bool shutdown = false ;
43+ volatile bool shutdown = false ;
3744
3845extern " C" void dfhack_crashlog_handle_signal (int sig) {
39- if (crashed.exchange (true )) {
40- // Crashlog already produced, bail thread.
46+ if (shutdown || crashed.exchange (true ) || crashlog_ready.load ()) {
47+ // Ensure the signal handler doesn't try to write a crashlog
48+ // whilst the crashlog thread is unavailable.
4149 std::quick_exit (1 );
4250 }
4351 crash_info.signal = sig;
4452 crash_info.backtrace_entries = backtrace (crash_info.backtrace , BT_ENTRY_MAX);
4553
46- // Signal saving of crashlog and wait for completion
54+ // Signal saving of crashlog
4755 flag_set (crashlog_ready);
48- flag_wait (crashlog_complete);
56+ // Wait for completion via eventfd read, if fd isn't valid, bail
57+ if (crashlog_complete != -1 ) {
58+ [[maybe_unused]] uint64_t _;
59+ read (crashlog_complete, &_, sizeof (_));
60+ }
4961 std::quick_exit (1 );
5062}
5163
@@ -125,8 +137,7 @@ void dfhack_crashlog_thread() {
125137 return ;
126138
127139 dfhack_save_crashlog ();
128-
129- flag_set (crashlog_complete);
140+ signal_crashlog_complete ();
130141 std::quick_exit (1 );
131142}
132143
@@ -135,6 +146,11 @@ std::terminate_handler term_handler = nullptr;
135146const int desired_signals[3 ] = {SIGSEGV,SIGILL,SIGABRT};
136147namespace DFHack {
137148 void dfhack_crashlog_init () {
149+ // Initialize eventfd flag
150+ crashlog_complete = eventfd (0 , EFD_CLOEXEC);
151+
152+ crashlog_thread = std::thread (dfhack_crashlog_thread);
153+
138154 for (int signal : desired_signals) {
139155 std::signal (signal, dfhack_crashlog_handle_signal);
140156 }
@@ -144,19 +160,23 @@ namespace DFHack {
144160 // backtrace is AsyncSignal-Unsafe due to dynamic loading of libgcc_s
145161 // Using it here ensures it is loaded before use in the signal handler.
146162 [[maybe_unused]] int _ = backtrace (crash_info.backtrace , 1 );
147-
148- crashlog_thread = std::thread (dfhack_crashlog_thread);
149163 }
150164
151165 void dfhack_crashlog_shutdown () {
166+ shutdown = true ;
152167 for (int signal : desired_signals) {
153168 std::signal (signal, SIG_DFL);
154169 }
155170 std::set_terminate (term_handler);
156171
157- shutdown = true ;
172+ // Shutdown the crashlog thread.
158173 flag_set (crashlog_ready);
159174 crashlog_thread.join ();
175+
176+ // If the signal handler is somehow running whilst here, let it terminate
177+ signal_crashlog_complete ();
178+ if (crashlog_complete != -1 )
179+ close (crashlog_complete); // Close fd
160180 return ;
161181 }
162182}
0 commit comments