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)?
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.
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.
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.
Related
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)?
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.
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.
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.
I am getting abort exception in simple VC++ program when main method completes.
Here is my sample test program.
#include "stdafx.h"
#include <thread>
#include <Windows.h>
class ThreadTest
{
public:
ThreadTest()
{
}
~ThreadTest()
{
}
void ThreadProc()
{
}
};
int _tmain(int argc, _TCHAR* argv[])
{
ThreadTest test;
std::thread t = std::thread(&ThreadTest::ThreadProc, std::ref(test));
Sleep(5000);
return 0;
}
I have experience in nativate pthread_create functions but it seems that something is missing. When I put Sleep(15000); in ThreadProc method same issue happens without any changes.
This is documented in std::thread's destructor: if a thread is destroyed while it is joinable, std::terminate is called.
Quote from the C++11 standard draft n3290 (§30.3.1.3 thread destructor):
If joinable() then terminate(), otherwise no effects. [ Note: Either implicitly detaching or joining a joinable() thread in its destructor could result in difficult to debug correctness (for detach) or performance (for join) bugs encountered only when an exception is raised. Thus the programmer must ensure that the destructor is never executed while the thread is still joinable. — end note ]
You must either join the thread, or detach it. Joining seems like the right option in your case.
I am getting abort exception in simple VC++ program when main method completes.
Here is my sample test program.
#include "stdafx.h"
#include <thread>
#include <Windows.h>
class ThreadTest
{
public:
ThreadTest()
{
}
~ThreadTest()
{
}
void ThreadProc()
{
}
};
int _tmain(int argc, _TCHAR* argv[])
{
ThreadTest test;
std::thread t = std::thread(&ThreadTest::ThreadProc, std::ref(test));
Sleep(5000);
return 0;
}
I have experience in nativate pthread_create functions but it seems that something is missing. When I put Sleep(15000); in ThreadProc method same issue happens without any changes.
This is documented in std::thread's destructor: if a thread is destroyed while it is joinable, std::terminate is called.
Quote from the C++11 standard draft n3290 (§30.3.1.3 thread destructor):
If joinable() then terminate(), otherwise no effects. [ Note: Either implicitly detaching or joining a joinable() thread in its destructor could result in difficult to debug correctness (for detach) or performance (for join) bugs encountered only when an exception is raised. Thus the programmer must ensure that the destructor is never executed while the thread is still joinable. — end note ]
You must either join the thread, or detach it. Joining seems like the right option in your case.
While reviewing bit of code, I came across a buggy std::terminate() handler
that wasn't terminating the program, but returning. Based on the documentation for std::set_terminate(), I think this falls within the realms of undefined - or at least implementation defined - behaviour.
Under Linux, and compiled using GCC, I found that cores were being dumped, which implies that some guardian angel was calling abort() or something similar on our behalf.
So I wrote the following test snippet, which confirmed my hunch. It looks like GCC or it's std library does wrap std::terminate() handlers so they do terminate the program.
#include <iostream>
#include <exception>
#include <dlfcn.h>
// Compile using
// g++ main.cpp -ldl
// Wrap abort() in my own implementation using
// dlsym(), so I can see if GCC generates code to
// call it if my std::terminate handler doesn't.
namespace std
{
void abort()
{
typedef void (*aborter)();
static aborter real_abort = 0x0;
if (0x0 == real_abort)
{
void * handle = 0x0;
handle = dlsym(RTLD_NEXT, "abort");
if (handle)
{
real_abort = (aborter )(handle);
}
}
std::cout << "2. Proof that GCC calls abort() if my buggy\n"
<< " terminate handler returns instead of terminating."
<< std::endl;
if (real_abort)
{
real_abort();
}
}
}
// Buggy terminate handler that returns instead of terminating
// execution via abort (or exit)
void buggyTerminateHandler()
{
std::cout << "1. In buggyTerminateHandler." << std::endl;
}
int main (int argc, char ** argv)
{
// Set terminate handler
std::set_terminate(buggyTerminateHandler);
// Raise unhandled exception
throw 1;
}
That a compiler (or library) would wrap std::terminate() handlers seems sensible to be, so at a guess I'd assume that most compilers do something along these lines.
Can anyone advise regarding the behaviour on Windows using Visual Studio or OS X using GCC?
I am developing an application that uses C++ and compiles using Linux GNU C Compiler.
I want to invoke a function as the user interrupts the script using Ctrl + C keys.
What should I do? Any answers would be much appreciated.
When you press Ctr + C, the operating system sends a signal to the process. There are many signals and one of them is SIGINT. The SIGINT ("program interrupt") is one of the Termination Signals.
There are a few more kinds of Termination Signals, but the interesting thing about SIGINT is that it can be handled (caught) by your program. The default action of SIGINT is program termination. That is, if your program doesn't specifically handle this signal, when you press Ctr + C your program terminates as the default action.
To change the default action of a signal you have to register the signal to be caught. To register a signal in a C program (at least under POSIX systems) there are two functions
signal(int signum, sighandler_t handler);
sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);.
These functions require the header signal.h to be included in your C code. I have provide a simple example of the signal function below with comments.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h> // our new library
volatile sig_atomic_t flag = 0;
void my_function(int sig){ // can be called asynchronously
flag = 1; // set flag
}
int main(){
// Register signals
signal(SIGINT, my_function);
// ^ ^
// Which-Signal |-- which user defined function registered
while(1)
if(flag){ // my action when signal set it 1
printf("\n Signal caught!\n");
printf("\n default action it not termination!\n");
flag = 0;
}
return 0;
}
Note: you should only call safe/authorized functions in signal handler. For example avoid calling printf in signal handler.
You can compile this code with gcc and execute it from the shell. There is an infinite loop in the code and it will run until you send a SIGINT signal by pressing Ctr + C.
Typing CtrlC normally causes the shell to send SIGINT to your program. Add a handler for that signal (via signal(2) or sigaction(2)), and you can do what you like when CtrlC is pressed.
Alternately, if you only care about doing cleanup before your program exits, setting up an exit handler via atexit(3) might be more appropriate.
You can use the signal macro.
Here is an example of how to deal with it:
#include <signal.h>
#include <stdio.h>
void sigint(int a)
{
printf("^C caught\n");
}
int main()
{
signal(SIGINT, sigint);
for (;;) {}
}
Sample output:
Ethans-MacBook-Pro:~ phyrrus9$ ./a.out
^C^C caught
^C^C caught
^C^C caught
^C^C caught
^C^C caught