Как использовать функцию std :: bind в качестве обработчика сигнала в C ++? - PullRequest
0 голосов
/ 25 августа 2018

Я использую следующий код для добавления обработки сигналов в мои классы C ++:

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);

Этот код работает, но он требует, чтобы функция обработчика сигнала была определена в той же области видимости, что и переменная флага сигнала. Я решил изменить код и передать signal_flag по ссылке на функцию и использовать std::bind, чтобы «специализировать» обработчик для моего класса.

 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);

Однако я получаю эту ошибку компиляции:

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)’}

Есть ли способ использовать связанную функцию вместе с функцией signal в C ++?

Ответы [ 2 ]

0 голосов
/ 16 сентября 2018

Если ваше программное обеспечение работает под управлением Linux, а ваш процесс - poll() или select(), возможно, будет гораздо удобнее использовать signalfd().

Я реализовал это в моем классе snap_communicator в это время в строке 2789. Есть копия реализации.

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
}

С этим «сокетом» вы можете сделать poll(), и он вызовет эквивалент события чтения при поступлении сигнала.

Вы получаете информацию о сигнале следующим образом:

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

с f_signal_info, объявленным как:

struct signalfd_siginfo     f_signal_info;

Самое замечательное то, что теперь все это происходит в моих классах, и у меня нет никаких «странных» обработчиков сигналов, которые могут заблокировать мой поток, неправильно обрабатывать исключения C ++ и другие потенциальные проблемы. С моей стороны, у меня есть классы C ++ с виртуальными функциями, которые вызываются всякий раз, когда происходит событие, включая сигналы Unix, такие как SIGINT, SIGPIPE, SIGCHLD ... Вы также можете реализовать обратные вызовы, используя сигналы повышения, которые дают вам сила использования std::bind().

Все это говорит, я все еще использую signal() для SIGSEGV, SIGBUS и т. Д. Те, которые я не собираюсь делать никакой дополнительной работы, когда они происходят. Я пытаюсь записать трассировку стека на тех и все. Поэтому я не использую signalfd() на них.

0 голосов
/ 25 августа 2018

Результатом std::bind является неопределенный функциональный объект, тип которого нельзя преобразовать в void (*)(int).Попробуйте инкапсулировать это:

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

Или, если доступен C ++ 11, лямбда может быть лучше:

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

Обратите внимание, что, поскольку signal_flag является глобальной переменной (пространство именпеременная -scope), захват не требуется.Не захватывающая лямбда может быть неявно преобразована в соответствующий тип указателя на функцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...