Is destructor called if SIGINT or SIGSTP issued?
Asked Answered
L

3

53

I have a class with a user-defined destructor. If the class was instantiated initially, and then SIGINT is issued (using CTRL+C in unix) while the program is running, will the destructor be called? What is the behaviour for SIGSTP (CTRL + Z in unix)?

Lugansk answered 22/11, 2010 at 20:37 Comment(0)
B
57

No, by default, most signals cause an immediate, abnormal exit of your program.

However, you can easily change the default behavior for most signals.

This code shows how to make a signal exit your program normally, including calling all the usual destructors:

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <cstring>
#include <atomic>

std::atomic<bool> quit(false);    // signal flag

void got_signal(int)
{
    // Signal handler function.
    // Set the flag and return.
    // Never do real work inside this function.
    // See also: man 7 signal-safety
    quit.store(true);
}

class Foo
{
public:
    ~Foo() { std::cout << "destructor\n"; }
};

int main(void)
{
    struct sigaction sa;
    memset( &sa, 0, sizeof(sa) );
    sa.sa_handler = got_signal;
    sigfillset(&sa.sa_mask);
    sigaction(SIGINT,&sa,NULL);

    Foo foo;    // needs destruction before exit
    while (true)
    {
        // do real work here...
        sleep(1);
        if( quit.load() ) break;    // exit normally after SIGINT
    }
    return 0;
}

If you run this program and press control-C, you should see the word "destructor" printed.

Be aware that your signal handler function (got_signal) should rarely do any work, other than setting a flag and returning quietly, unless you really know what you are doing. See also: https://man7.org/linux/man-pages/man7/signal-safety.7.html

Most signals are catchable as shown above, but not SIGKILL, you have no control over it because SIGKILL is a last-ditch method for killing a runaway process, and not SIGSTOP which allows a user to freeze a process cold. Note that you can catch SIGTSTP (control-Z) if desired, but you don't need to if your only interest in signals is destructor behavior, because eventually after a control-Z the process will be woken up, will continue running, and will exit normally with all the destructors in effect.

Bombast answered 22/11, 2010 at 21:58 Comment(3)
IIRC, the correct type of quit should be volatile std::sig_atomic_t. It's UB to use bool for that purpose.Fougere
@MSalters: Right, I should have included a sigfillset() call before sigaction(), which would probably be even better than sig_atomic_t. Using a bool is more familiar and perfectly safe when additional signals are blocked from interrupting the signal handler. Edited my example code, thanks.Bombast
I actually get an error with this code: use of deleted function for the quit = false line. You have to do quit(false) instead of quit = false. Also worth noting that this code doesn't work on Windows; you have to use SetConsoleCtrlHandler().Sporulate
A
13

If you do not handle these signals yourself, then, no, the destructors are not called. However, the operating system will reclaim any resources your program used when it terminates.

If you wish to handle signals yourself, then consider checking out the sigaction standard library function.

Aquamarine answered 22/11, 2010 at 20:44 Comment(1)
Reclaiming resources owned by the OS. Within an application there are other other resources and they are usually wrapped in such a way that closing them correctly is required (otherwise you get corrupted resources (like a file that is not properly terminated)).Guenzi
M
10

Let's try it:

#include <stdio.h>
#include <unistd.h>

class Foo {
public:
  Foo() {};
  ~Foo() { printf("Yay!\n"); }
} bar;

int main(int argc, char **argv) {
  sleep(5);
}

And then:

$ g++ -o test ./test.cc 
$ ./test 
^C
$ ./test 
Yay!

So I'm afraid not, you'll have to catch it.

As for SIGSTOP, it cannot be caught, and pauses the process until a SIGCONT is sent.

Monosepalous answered 22/11, 2010 at 20:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.