Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use std::bind function as a signal handler in C++?

I am using the following code to add signal handling to my C++ classes:

namespace {
    std::atomic<bool> signal_flag(false);   
}
void terminate_or_interrupt_handler(int signal) {
    switch (signal) {
        case SIGTERM:
            WARN("SIGTERM received");
            signal_flag.store(true);
            break;
        case SIGINT:
            WARN("SIGINT received");
            signal_flag.store(true);
            break;
        default:
            throw (std::runtime_error("Unhandled signal received"));
    }
}
signal(SIGTERM, &terminate_or_interrupt_handler);

This code works, but it requires the signal handler function to be define in the same scope as the signal flag variable. I decided to modify the code and pass the signal_flag by reference to the function and use std::bind to "specialize" the handler to my class.

 void terminate_or_interrupt_handler(std::atomic<bool>& signal_flag, int signal) {
    switch (signal) {
        case SIGTERM:
            WARN("SIGTERM received");
            signal_flag.store(true);
            break;
        case SIGINT:
            WARN("SIGINT received");
            signal_flag.store(true);
            break;
        default:
            throw (std::runtime_error("Unhandled signal received"));
    }
}
auto my_handler = std::bind(terminate_or_interrupt_handler, std::ref(my_class_signal_flag), std::placeholders::_1);
signal(SIGTERM, &my_handler);

However, I get this compile error:

error: cannot convert ‘std::_Bind<void (*(std::reference_wrapper<std::atomic<bool> >, std::_Placeholder<1>))(std::atomic<bool>&, int)>*’ to ‘__sighandler_t’ {aka ‘void (*)(int)’}

Is there a way to use a bound function in conjunction with the signal function in C++?

like image 678
motam79 Avatar asked Aug 25 '18 00:08

motam79


People also ask

What does std :: bind do in C++?

std::bind. std::bind is a Standard Function Objects that acts as a Functional Adaptor i.e. it takes a function as input and returns a new function Object as an output with with one or more of the arguments of passed function bound or rearranged.

How does STD bind work?

std::bind allows you to create a std::function object that acts as a wrapper for the target function (or Callable object). std::bind also allows you to keep specific arguments at fixed values while leaving other arguments variable.

Does signal () call the signal handler?

The signal() system call just installs the handler: "signal() sets the disposition of the signal signum to handler, which is either SIG_IGN, SIG_DFL, or the address of a programmer-defined function (a "signal handler")." There you go.

What is the return type of std :: bind?

std::bind. Returns a function object based on fn , but with its arguments bound to args . Each argument may either be bound to a value or be a placeholder: - If bound to a value, calling the returned function object will always use that value as argument.


2 Answers

The result of std::bind is an unspecified function object whose type cannot be converted into void (*)(int). Try encapsulating it:

void handler_foo(int signal)
{
    return terminate_or_interrupt_handler(signal_flag, signal);
}

Or, if C++11 is available, a lambda might be better:

signal(SIGTERM, [](int signal) { return terminate_or_interrupt_handler(signal_flag, signal); });

Note that since signal_flag is a global variable (namespace-scope variable), no capture is required. A non-capturing lambda can be implicitly converted into the corresponding function pointer type.

like image 67
L. F. Avatar answered Sep 18 '22 04:09

L. F.


If your software runs under Linux and your process does a poll() or a select() it may be a lot cleaner to use signalfd().

I've implemented such in my eventdispatcher, the ed::signal class¹. Here is a copy of the constructor:

snap_communicator::snap_signal::snap_signal(int posix_signal)
    : f_signal(posix_signal)
    //, f_socket(-1) -- auto-init
    //, f_signal_info() -- auto-init
    //, f_unblock(false) -- auto-init
{
    int const r(sigismember(&g_signal_handlers, f_signal));
    if(r != 0)
    {
        if(r == 1)
        {
            // this could be fixed, but probably not worth the trouble...
            throw snap_communicator_initialization_error("the same signal cannot be created more than once in your entire process.");
        }
        // f_signal is not considered valid by this OS
        throw snap_communicator_initialization_error("posix_signal (f_signal) is not a valid/recognized signal number.");
    }

    // create a mask for that signal
    //
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, f_signal); // ignore error, we already know f_signal is valid

    // first we block the signal
    //
    if(sigprocmask(SIG_BLOCK, &set, nullptr) != 0)
    {
        throw snap_communicator_runtime_error("sigprocmask() failed to block signal.");
    }

    // second we create a "socket" for the signal (really it is a file
    // descriptor manager by the kernel)
    //
    f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC);
    if(f_socket == -1)
    {
        int const e(errno);
        SNAP_LOG_ERROR("signalfd() failed to create a signal listener for signal ")(f_signal)(" (errno: ")(e)(" -- ")(strerror(e))(")");
        throw snap_communicator_runtime_error("signalfd() failed to create a signal listener.");
    }

    // mark this signal as in use
    //
    sigaddset(&g_signal_handlers, f_signal); // ignore error, we already know f_signal is valid
}

With that socket you can do a poll() and it triggers an equivalent to a read event when a signal arrives.

You retrieve the signal information like this:

int const r(read(f_socket, &f_signal_info, sizeof(f_signal_info)));

with f_signal_info being declared as:

struct signalfd_siginfo     f_signal_info;

The great thing is that now all of that happens in my classes and I don't have any weird signal handlers which can get my thread locked up, does not handle C++ exceptions correctly, and other potential problems. On my end, I have C++ classes with virtual functions that get called whenever an event occurs, including Unix signals such as SIGINT, SIGPIPE, SIGCHLD... You could also implement callbacks using boost signals which give you the power of using std::bind().

All of that said, I still use signal() for SIGSEGV, SIGBUS, etc. those that I'm not going to do any extra work when they occur. I try to log the stack trace on those and that's it. So I don't use signalfd() on them (see my ed::signal_handler implementation).


¹ The old implementation was part of the snap_communicator environment and the signal class was around line 2789 at time of writing.

like image 28
Alexis Wilke Avatar answered Sep 19 '22 04:09

Alexis Wilke