Consider the following code:
#include <stdlib.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
uint64_t counter = 0;
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
void sig_handler(int signo) {
printf( "%" PRIu64 "\n", counter);
}
int main() {
struct sigaction act;
act.sa_handler = &sig_handler;
sigaction(SIGINT, &act, NULL);
for( ;; ) {
counter++;
}
return 0;
}
If I compile the code with -O0, I can see that the counter gets incremented when I press CTR+C. With -O1, this is optimized away. Why is that and how could I avoid it?
It looks like the following section of the draft C++11 standard is relevant section 1.9 [intro.execution]:
When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects which are neither
- of type volatile std::sig_atomic_t nor
- lock-free atomic objects (29.4)
are unspecified during the execution of the signal handler, and the value of any object not in either of these two categories that is modified by the handler becomes undefined.
Since counter is nether volatile nor an atomic object the value is unspecified and therefore the compiler is allowed to optimize it away via the as-if rule.
The wording changed in C++14 draft and we have the following:
If a signal handler is executed as a result of a call to the raise function, then the execution of the handler is sequenced after the invocation of the raise function and before its return. [ Note: When a signal is received for another reason, the execution of the signal handler is usually unsequenced with respect to the rest of the program. —end note ]
which seems to leave it unspecified in a sense, since the it is only the note that says the sequence handler is unsequenced, but if we read N3910: N3910: What can signal handlers do? (CWG 1441) we can see that this seems to be considered a data race and thus undefined behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With