2020-07-14 16:15:53 +08:00
Signal handler for multi threaded C++ applications on Linux
===========================================================
2019-09-15 04:41:56 +08:00
2019-10-13 21:00:20 +08:00
Signal handler that uses [pthread_sigmask ](http://man7.org/linux/man-pages/man3/pthread_sigmask.3.html ) and [sigwait ](http://man7.org/linux/man-pages/man3/sigwait.3.html ).
2019-09-15 04:41:56 +08:00
2019-09-17 00:14:15 +08:00
2019-10-13 21:00:20 +08:00
## Dependencies
* C++17
* Clang or GCC
* linux
* pthread
* cmake (recommended, but optional)
2019-10-13 21:11:06 +08:00
* Catch2 for testing
2019-09-17 00:14:15 +08:00
2019-10-13 21:00:20 +08:00
## Example usage
2020-07-14 03:16:03 +08:00
```C++
2019-09-15 04:41:56 +08:00
{
2019-10-13 21:00:20 +08:00
// Block signals
2019-11-28 01:03:34 +08:00
sgnl::SignalHandler signal_handler({SIGINT, SIGTERM});
2019-10-13 21:00:20 +08:00
// Wait for a signal
int signal_number = signal_handler.sigwait();
// Or, pass a handler
auto handler = [](int signum) {
if( signum == SIGINT )
// continue waiting for signals
return false;
if( signum == SIGTERM )
// stop waiting for signals
2019-09-15 04:41:56 +08:00
return true;
2019-10-13 21:00:20 +08:00
};
int last_signal = signal_handler.sigwait_handler(handler);
} // signals are unblocked again
```
2020-07-14 21:42:58 +08:00
Using a condition variable to shutdown all threads:
```C++
#include <sgnl/AtomicCondition.h>
#include <sgnl/SignalHandler.h>
#include <cstdlib>
#include <future>
#include <iostream>
#include <thread>
void Worker(const sgnl::AtomicCondition< bool > & exit_condition)
{
auto predicate = [& exit_condition]() {
return exit_condition.get();
};
while( true )
{
exit_condition.wait_for(std::chrono::minutes(1), predicate);
if( exit_condition.get() )
return;
/* ... do work ... */
}
}
int main()
{
sgnl::AtomicCondition< bool > exit_condition(false);
auto handler = [&exit_condition ](int signum ) {
std::cout << "received signal " < < signum << " \n";
if( signum == SIGTERM || signum == SIGINT )
{
exit_condition.set(true);
// wakeup all waiting threads
exit_condition.notify_all();
// stop polling for signals
return true;
}
// continue waiting for signals
return false;
};
// Block signals in this thread.
// Threads spawned later will inherit the signal mask.
sgnl::SignalHandler signal_handler({SIGINT, SIGTERM, SIGUSR1});
std::future< int > ft_sig_handler =
std::async(
std::launch::async,
& sgnl::SignalHandler::sigwait_handler,
& signal_handler,
std::ref(handler));
std::vector< std::future < void > > futures;
for(int i = 0; i < 10 ; + + i )
futures.push_back(
std::async(
std::launch::async,
Worker,
std::ref(exit_condition)));
// SIGUSR1
std::this_thread::sleep_for(std::chrono::milliseconds(100));
kill(0, SIGUSR1);
// SIGTERM
kill(0, SIGTERM);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
for(auto& future : futures)
future.wait();
int last_signal = ft_sig_handler.get();
std::cout << "exiting (received signal " < < last_signal << ") \n";
return EXIT_SUCCESS;
}
```
2019-10-13 21:00:20 +08:00
## Build & Install
2020-07-14 03:16:03 +08:00
```SH
2019-10-13 21:00:20 +08:00
mkdir -p build/ & & cd build/
cmake ..
# build and run tests
make sgnl-test & & ./test/sgnl-test
2019-10-13 21:06:50 +08:00
# build and run example
make example & & ./example
2019-10-13 21:00:20 +08:00
# install headers and CMake config
make install
2019-09-15 04:41:56 +08:00
```