aboutsummaryrefslogtreecommitdiff
path: root/Source/ablastr/utils/SignalHandling.cpp
blob: cdec9b653cc6d60254462688da7d8e1db0e84275 (plain) (blame)
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
/* Copyright 2022 Philip Miller
 *
 * This file is part of WarpX.
 *
 * License: BSD-3-Clause-LBNL
 */

#include "SignalHandling.H"
#include "TextMsg.H"

#include <AMReX.H>
#include <AMReX_ParallelDescriptor.H>
#include <AMReX_IParser.H>

#include <cctype>

// For sigaction() et al.
#if defined(__linux__) || defined(__APPLE__)
#   include <signal.h>
#endif

namespace ablastr::utils {

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__)
    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>();

    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) {
            if (amrex::ParallelDescriptor::MyProc() == 0) {
                sa.sa_handler = &SignalHandling::SignalSetFlag;
            } else {
                sa.sa_handler = SIG_IGN;
            }
            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()
{
    // 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.
            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();
    BL_MPI_REQUIRE(MPI_Ibcast(signal_actions_requested, SIGNAL_REQUESTS_SIZE,
                              MPI_CXX_BOOL, 0, comm,&signal_mpi_ibcast_request));
#endif
}

void
SignalHandling::WaitSignals()
{
#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)
{
    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