Skip to content

Commit 25ac663

Browse files
committed
Use eventfd to wait instead of cvar for as-safety
1 parent 0d4bc8a commit 25ac663

File tree

1 file changed

+37
-17
lines changed

1 file changed

+37
-17
lines changed

library/Crashlog.cpp

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
#include <fstream>
66
#include <thread>
77

8+
#include <sys/eventfd.h>
89
#include <execinfo.h>
10+
#include <unistd.h>
911

1012
const int BT_ENTRY_MAX = 25;
1113
struct CrashInfo {
@@ -16,13 +18,11 @@ struct CrashInfo {
1618

1719
CrashInfo 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

2727
void 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+
3542
std::thread crashlog_thread;
36-
bool shutdown = false;
43+
volatile bool shutdown = false;
3744

3845
extern "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;
135146
const int desired_signals[3] = {SIGSEGV,SIGILL,SIGABRT};
136147
namespace 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

Comments
 (0)