Reputation: 3138
I'm playing with signal handling in UNIX and C++ and came across with this issue. I'm trying to write a program that counts to 10, one number per second, and when the user tries to interrupt it with a SIGINT (like CTRL+C) it prints a message telling it it will continue to count no matter what.
So far, I got this:
#include <iostream>
#include <signal.h>
#include <zconf.h>
using namespace std;
sig_atomic_t they_want_to_interrupt = 0;
void sigint_handler(int signum) {
assert(signum == SIGINT);
they_want_to_interrupt = 1;
}
void register_handler() {
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGINT);
sa.sa_handler = sigint_handler;
sigaction(SIGINT, &sa, 0);
}
int main() {
register_handler();
cout << "Hi! We'll count to a hundred no matter what" << endl;
for (int i = 1; i <= 100; i++) {
if (they_want_to_interrupt == 1) {
cout << endl << "DON'T INTERRUPT ME WHILE I'M COUNTING! I'll count ALL THE WAY THROUGH!!!" << endl;
they_want_to_interrupt = 0;
}
cout << i << " " << flush;
sleep(1);
}
cout << "Done!" << endl;
return 0;
}
Now, the first time around I send the interrupt signal it works properly:
Hi! We'll count to a hundred no matter what
1 2 ^C
DON'T INTERRUPT ME WHILE I'M COUNTING! I'll count ALL THE WAY THROUGH!!!
3 4
But if I send a second interrupt signal, the process is stopped.
Why does it happen? I tried reading the manual on ´sigaction´ to try to see if there's something that will make the handler I created not be popped when the signal is caught and roll back to SIG_DFL, but couldn't work it out.
Thanks
Upvotes: 2
Views: 1198
Reputation: 141554
In this code:
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGINT);
sa.sa_handler = sigint_handler;
sigaction(SIGINT, &sa, 0);
the sa.sa_flags
field (and others) are uninitialized which may cause unexpected results. It would be better to zero-initialize the struct at the start, e.g.:
struct sigaction sa = { 0 };
Also, the sig_atomic_t
flag should be declared as volatile
to prevent the optimizer introducing unexpected behaviour.
Upvotes: 1
Reputation: 2336
You can just reset the signal handler each time a signal is sent. I've seen this for handling SIGUSR when a signal might be expected repeatedly.
#include <iostream>
#include <cassert>
#include <signal.h>
#include <zconf.h>
using namespace std;
void register_handler();
sig_atomic_t they_want_to_interrupt = 0;
void sigint_handler(int signum) {
assert(signum == SIGINT);
they_want_to_interrupt = 1;
register_handler();
}
void register_handler() {
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGINT);
sa.sa_handler = sigint_handler;
sigaction(SIGINT, &sa, 0);
}
int main() {
register_handler();
cout << "Hi! We'll count to a hundred no matter what" << endl;
for (int i = 1; i <= 100; i++) {
if (they_want_to_interrupt == 1) {
cout << endl << "DON'T INTERRUPT ME WHILE I'M COUNTING! I'll count ALL THE WAY THROUGH!!!" << endl;
they_want_to_interrupt = 0;
}
cout << i << " " << flush;
sleep(1);
}
cout << "Done!" << endl;
return 0;
}
Upvotes: 2