How can I handle interrupt signal and call destruc

2019-03-28 17:03发布

问题:

Possible Duplicate:
Is destructor called if SIGINT or SIGSTP issued?

My code like this:

#include <iostream>
#include <signal.h>
#include <cstdlib>

void handler(int) {
    std::cout << "will exit..." << std::endl;
    exit(0);
}

class A {
public:
    A() {std::cout << "constructor" << std::endl;}
    ~A() {std::cout << "destructor" << std::endl;}
};

int main(void) {
    signal(SIGINT, &handler);

    A a;
    for (;;);

    return 0;
}

When I pressed Ctrl-C, it printed:

constructor
^Cwill exit...

There is no "destructor" printed. So, how can I exit cleanly?

回答1:

With difficulty. Already, the code you've written has undefined behavior; you're not allowed to output to a stream in a signal handler; for that matter, you're not allowed to call exit either. (I'm basing my assertions here on the Posix standard. In pure C++, all you're allowed to do is assign to a variable of sig_atomic_t type.)

In a simple case like your code, you could do something like:

sig_atomic_t stopFlag = 0;

void
handler( int )
{
    stopFlag = 1;
}

int
main()
{
    signal( SIGINT, &handler );
    A a;
    while ( stopFlag == 0 ) {
    }
    std::cout << "will exit..." << std::endl;
    return 0;
}

Depending on the application, you may be able to do something like this, checking the stopFlag at appropriate places. But generally, if you try this, there will be race conditions: you check stopFlag before starting an interuptable system call, then do the call; the signal arrives between the check and the call, you do the call, and it isn't interrupted. (I've used this technique, but in an application where the only interruptable system call was a socket read with a very short timeout.)

Typically, at least under Posix, you'll end up having to create a signal handling thread; this can then be used to cleanly shut down all of the other threads. Basically, you start by setting the signal mask to block all signals, then in the signal handling thread, once started, set it to accept the signals you're interested in and call sigwait(). This implies, however, that you do all of the usual actions necessary for a clean shutdown of the threads: the signal handling thread has to know about all other threads, call pthread_cancel on them, etc., and you're compiler has to generate the correct code to handle pthread_cancel, or you need to develop some other means of ensuring that all threads are correctly notified. (One would hope, today, that all compilers handle pthread_cancel correctly. But one never knows; doing so has significant runtime cost, and is not usually needed.)



回答2:

You need to exit from the main function's scope to have the destructor working:

#include <iostream>
#include <signal.h>
#include <cstdlib>

bool stop = false;
void handler(int) {
    std::cout << "will exit..." << std::endl;
    stop = true;
}

class A {
public:
    A() {std::cout << "constructor" << std::endl;}
    ~A() {std::cout << "destructor" << std::endl;}
};

int main(void) {
  A a;
  signal(SIGINT, &handler);

  for (;!stop;);

  return 0;
}


回答3:

It's because the context of the normal code and the signal handler is different. If you put the variable a in global scope (i.e. outside of any function) you will see that the destructor is called properly.

If you want to handle cleaning up yourself (instead of letting the run-time and OS handle it), you can have a conditional loop, something like this:

bool keep_running = true;

void handler(int) {
    std::cout << "will exit..." << std::endl;
    keep_running = false;
}

int main(void) {
    signal(SIGINT, &handler);

    A a;
    while (keep_running);

    return 0;
}


回答4:

Memory should be freed anyway. but if you've got code to be handled, I guess you'd have to track all your objects and then destroy them as needed (e.g. having the constructor adding them to a std::set, while the destructor removes them again). However this wouldn't ensure proper order of destruction (which might require some more complex solution).

You could as well use your signal handler to set some flag that will leave the infinite loop (or whatever you're doing in your main loop) instead of simply terminating using exit().



回答5:

exit terminates the process almost immediately; in particular, objects with automatic storage duration are not destroyed. Streams are also flushed and closed, but you're not allowed to touch streams from inside a signal handler. So...

Simply don't call exit from a signal handler; set some atomic flag to instruct the loop to end instead.

#include <iostream>
#include <signal.h>
#include <cstdlib>

sig_atomic_t exitRequested = 0;

void handler(int) {
    std::cout << "will exit..." << std::endl;
    exitRequested = 1;
}

struct A {
     A() { std::cout << "constructor" << std::endl; }
    ~A() { std::cout << "destructor" << std::endl; }
};

int main() {
    signal(SIGINT, &handler);

    A a;
    for (; !exitRequested; );
}