RAR
RAR

Reputation: 93

How to call destructor of object on termination with Ctrl-c?

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

Answers (2)

Jeremy Friesner
Jeremy Friesner

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

Petr Skocik
Petr Skocik

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

Related Questions