Reputation: 93
I have an object which I want to guarantee will be destructed even if the program is terminated with Ctrl+C.
I have tried doing this using a signal handler which resets a unique pointer but I have been told that std::unique_ptr::reset is forbidden to be used in a signal handler.
std::unique_ptr<MotionControl> mc;
void signal_handler(int signal_num) {
// destruct MotionControl object and delete pointer
mc.reset();
// terminate program
exit(signal_num);
}
int main(int argc, char** argv)
{
signal(SIGINT, signal_handler);
try {
std::string deviceName("/dev/ttyACM0");
mc = std::unique_ptr<MotionControl>(new MotionControl(deviceName, 119, 65, 10));
...
}
...
}
Therefore, how can I go about doing this?
Edit 1: My OS is Ubuntu.
Edit 2: Failure to call the destructor will result in hardware damage the next time the program is run. I need to move some motion control stages to a safe location when the destructor is called.
Upvotes: 2
Views: 1708
Reputation: 73209
The way I do it is to set up a pipe or a socket-pair (using pipe()
or socketpair()
, respectively) at the top of main()
, and have the signal-handler write a byte on one socket of the socket-pair (or on the sending-fd of the pipe-fd-pair).
Your main thread's event-loop should be select()
-ing (or poll()
-ing or whatever) on the other socket (or on the receiving-fd of the pipe-fd-pair). When it receives a byte on that socket, the main thread should respond by gracefully shutting down the program.
Upvotes: 0
Reputation: 60107
The common "zero-cost" exception implementation should let you throw out of signal handlers. It's probably quite unsafe, though, as you might corrupt some async-unsafe state. (I guess it's only safe if the signal only arrives during purely async-safe code / pure computation.)
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void signal_handler(int signal_num)
{
throw signal_num;
}
int main()
{
try {
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = signal_handler;
sigaction(SIGINT,&sa,0);
pause();
}catch(int X){
printf("CAUGHT: %d\n", X);
return 1;
}
return 0;
}
Your best bet is probably to set a global (or thread local, but I guess that's not theoretically portable either) volatile sig_atomic_t
flag, have your regular context check it once in a while, and if it sees the flag set,
then your regular-context code may throw.
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
volatile sig_atomic_t flag;
void signal_handler(int signal_num)
{
flag = signal_num;
}
int main()
{
try {
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = signal_handler;
sigaction(SIGINT,&sa,0);
for(;;){
pause();
if (flag) throw (int)flag;
}
}catch(int X){
printf("CAUGHT: %d\n", X);
return 1;
}
return 0;
}
Upvotes: 2