1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
/* Copyright 2022 Philip Miller
*
* This file is part of WarpX.
*
* License: BSD-3-Clause-LBNL
*/
#include "SignalHandling.H"
#include "TextMsg.H"
#include <AMReX_ParallelDescriptor.H>
#include <AMReX_IParser.H>
#include <cctype>
#include <stdexcept>
// For sigaction() et al.
#if defined(__linux__) || defined(__APPLE__)
# include <signal.h>
#endif
namespace ablastr::utils {
bool SignalHandling::m_any_signal_action_active = false;
std::atomic<bool> SignalHandling::signal_received_flags[NUM_SIGNALS];
bool SignalHandling::signal_conf_requests[SIGNAL_REQUESTS_SIZE][NUM_SIGNALS];
bool SignalHandling::signal_actions_requested[SIGNAL_REQUESTS_SIZE];
#if defined(AMREX_USE_MPI)
MPI_Request SignalHandling::signal_mpi_ibcast_request;
#endif
int
SignalHandling::parseSignalNameToNumber (const std::string &str)
{
amrex::IParser signals_parser(str);
#if defined(__linux__) || defined(__APPLE__)
const struct {
const char* abbrev;
const int value;
} signals_to_parse[] = {
{"ABRT", SIGABRT},
{"ALRM", SIGALRM},
{"BUS", SIGBUS},
{"CHLD", SIGCHLD},
{"CLD", SIGCHLD}, // Synonymous to SIGCHLD on Linux
{"CONT", SIGCONT},
#if defined(SIGEMT)
{"EMT", SIGEMT}, // macOS and some Linux architectures
#endif
// Omitted because AMReX typically handles SIGFPE specially
// {"FPE", SIGFPE},
{"HUP", SIGHUP},
{"ILL", SIGILL},
#if defined(SIGINFO)
{"INFO", SIGINFO}, // macOS and some Linux architectures
#endif
{"INT", SIGINT},
{"IO", SIGIO},
{"IOT", SIGABRT}, // Synonymous to SIGABRT on Linux
// {"KILL", SIGKILL}, // Cannot be handled
{"PIPE", SIGPIPE},
{"POLL", SIGIO}, // Synonymous to SIGIO on Linux
{"PROF", SIGPROF},
#if defined(SIGPWR)
{"PWR", SIGPWR}, // Linux-only
#endif
{"QUIT", SIGQUIT},
{"SEGV", SIGSEGV},
#if defined(SIGSTKFLT)
{"STKFLT", SIGSTKFLT}, // Linux-only
#endif
// {"STOP", SIGSTOP}, // Cannot be handled
{"SYS", SIGSYS},
{"TERM", SIGTERM},
{"TRAP", SIGTRAP},
{"TSTP", SIGTSTP},
{"TTIN", SIGTTIN},
{"TTOU", SIGTTOU},
{"URG", SIGURG},
{"USR1", SIGUSR1},
{"USR2", SIGUSR2},
{"VTALRM", SIGVTALRM},
{"WINCH", SIGWINCH},
{"XCPU", SIGXCPU},
{"XFSZ", SIGXFSZ},
};
for (const auto& sp : signals_to_parse) {
std::string name_upper = sp.abbrev;
std::string name_lower = name_upper;
for (char &c : name_lower) {
c = std::tolower(c);
}
signals_parser.setConstant(name_upper, sp.value);
signals_parser.setConstant(name_lower, sp.value);
name_upper = "SIG" + name_upper;
name_lower = "sig" + name_lower;
signals_parser.setConstant(name_upper, sp.value);
signals_parser.setConstant(name_lower, sp.value);
}
#endif // #if defined(__linux__) || defined(__APPLE__)
auto spf = signals_parser.compileHost<0>();
const int sig = spf();
ABLASTR_ALWAYS_ASSERT_WITH_MESSAGE(sig < NUM_SIGNALS,
"Parsed signal value is outside the supported range of [1, 31]");
return sig;
}
void
SignalHandling::InitSignalHandling ()
{
#if defined(__linux__) || defined(__APPLE__)
struct sigaction sa;
sigemptyset(&sa.sa_mask);
for (int signal_number = 0; signal_number < NUM_SIGNALS; ++signal_number) {
signal_received_flags[signal_number] = false;
bool signal_active = false;
for (int signal_request = 0; signal_request < SIGNAL_REQUESTS_SIZE; ++signal_request) {
signal_active |= signal_conf_requests[signal_request][signal_number];
}
if (signal_active) {
// at least one signal action is configured
m_any_signal_action_active = true;
if (amrex::ParallelDescriptor::MyProc() == 0) {
sa.sa_handler = &SignalHandling::SignalSetFlag;
} else {
sa.sa_handler = SIG_IGN;
}
const int result = sigaction(signal_number, &sa, nullptr);
ABLASTR_ALWAYS_ASSERT_WITH_MESSAGE(result == 0,
"Failed to install signal handler for a configured signal");
}
}
#endif
}
void
SignalHandling::CheckSignals ()
{
// Is any signal handling action configured?
// If not, we can skip all handling and the MPI communication as well.
if (!m_any_signal_action_active)
return;
// We assume that signals will definitely be delivered to rank 0,
// and may be delivered to other ranks as well. For coordination,
// we process them according to when they're received by rank 0.
if (amrex::ParallelDescriptor::MyProc() == 0) {
for (int signal_number = 0; signal_number < NUM_SIGNALS; ++signal_number) {
// Read into a local temporary to ensure the same value is
// used throughout. Atomically exchange it with false to
// unset the flag without risking loss of a signal - if a
// signal arrives after this, it will be handled the next
// time this function is called.
const bool signal_received = signal_received_flags[signal_number].exchange(false);
if (signal_received) {
for (int signal_request = 0; signal_request < SIGNAL_REQUESTS_SIZE; ++signal_request) {
signal_actions_requested[signal_request] |= signal_conf_requests[signal_request][signal_number];
}
}
}
}
#if defined(AMREX_USE_MPI)
auto comm = amrex::ParallelDescriptor::Communicator();
// Due to a bug in Cray's MPICH 8.1.13 implementation (CUDA builds on Perlmutter@NERSC in 2022),
// we cannot use the MPI_CXX_BOOL C++ datatype here. See WarpX PR #3029 and NERSC INC0183281
static_assert(sizeof(bool) == 1, "We communicate bools as 1 byte-sized type in MPI");
BL_MPI_REQUIRE(MPI_Ibcast(signal_actions_requested, SIGNAL_REQUESTS_SIZE,
MPI_BYTE, 0, comm,&signal_mpi_ibcast_request));
#endif
}
void
SignalHandling::WaitSignals ()
{
// Is any signal handling action configured?
// If not, we can skip all handling and the MPI communication as well.
if (!m_any_signal_action_active)
return;
#if defined(AMREX_USE_MPI)
BL_MPI_REQUIRE(MPI_Wait(&signal_mpi_ibcast_request, MPI_STATUS_IGNORE));
#endif
}
bool
SignalHandling::TestAndResetActionRequestFlag (int action_to_test)
{
const bool retval = signal_actions_requested[action_to_test];
signal_actions_requested[action_to_test] = false;
return retval;
}
void
SignalHandling::SignalSetFlag (int signal_number)
{
signal_received_flags[signal_number] = true;
}
} // namespace ablastr::utils
|