2019-09-15 04:41:56 +08:00
|
|
|
#define CATCH_CONFIG_MAIN
|
2019-09-16 03:54:01 +08:00
|
|
|
#define CATCH_CONFIG_FAST_COMPILE
|
2019-09-15 04:41:56 +08:00
|
|
|
|
|
|
|
#include <catch2/catch.hpp>
|
|
|
|
|
|
|
|
#include <sgnl/SignalHandler.h>
|
|
|
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
|
|
#include <chrono>
|
|
|
|
#include <future>
|
2019-09-17 00:11:13 +08:00
|
|
|
#include <map>
|
2019-09-15 04:41:56 +08:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
|
|
bool worker(sgnl::AtomicCondition<bool>& exit_condition)
|
|
|
|
{
|
|
|
|
exit_condition.wait_for(std::chrono::milliseconds(1000), true);
|
|
|
|
return exit_condition.get();
|
|
|
|
}
|
|
|
|
|
2019-09-16 03:54:01 +08:00
|
|
|
int looping_worker(sgnl::AtomicCondition<bool>& exit_condition)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
while( exit_condition.get() == false )
|
|
|
|
{
|
|
|
|
exit_condition.wait_for(std::chrono::milliseconds(2), true);
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2019-09-15 04:41:56 +08:00
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
2019-09-16 03:54:01 +08:00
|
|
|
TEST_CASE("condition-get-set")
|
|
|
|
{
|
|
|
|
sgnl::AtomicCondition<int> condition(23);
|
|
|
|
REQUIRE( condition.get() == 23 );
|
|
|
|
condition.set(42);
|
|
|
|
REQUIRE( condition.get() == 42 );
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE("constructor-thread-blocks-signals")
|
|
|
|
{
|
|
|
|
sgnl::AtomicCondition<bool> condition(false);
|
|
|
|
|
|
|
|
sgnl::SignalHandler signal_handler({{SIGTERM, true}, {SIGINT, true}}, condition);
|
|
|
|
|
|
|
|
std::promise<pthread_t> signal_handler_thread_id;
|
|
|
|
std::future<int> ft_sig_handler =
|
|
|
|
std::async(std::launch::async, [&]() {
|
|
|
|
signal_handler_thread_id.set_value(pthread_self());
|
|
|
|
return signal_handler();
|
|
|
|
});
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
std::this_thread::yield();
|
|
|
|
|
|
|
|
// send terminate signal to main thread ..
|
|
|
|
REQUIRE( pthread_kill(pthread_self(), SIGTERM) == 0 );
|
|
|
|
|
|
|
|
// .. but we're still running
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
std::this_thread::yield();
|
|
|
|
|
|
|
|
// send interrupt signal to signal handler thread
|
|
|
|
REQUIRE(
|
|
|
|
pthread_kill(
|
|
|
|
signal_handler_thread_id.get_future().get(),
|
|
|
|
SIGINT) == 0 );
|
|
|
|
|
|
|
|
CHECK( ft_sig_handler.get() == SIGINT );
|
|
|
|
CHECK( condition.get() == true );
|
|
|
|
|
|
|
|
CHECK( signal_handler() == SIGTERM );
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE("exit-condition")
|
2019-09-15 04:41:56 +08:00
|
|
|
{
|
|
|
|
std::vector<int> signals({SIGINT, SIGTERM, SIGUSR1, SIGUSR2});
|
|
|
|
for( auto test_signal : signals )
|
|
|
|
{
|
|
|
|
sgnl::AtomicCondition exit_condition(false);
|
|
|
|
|
|
|
|
sgnl::SignalHandler signal_handler({{test_signal, true}}, exit_condition);
|
|
|
|
std::promise<pthread_t> signal_handler_thread_id;
|
|
|
|
std::future<int> ft_sig_handler =
|
|
|
|
std::async(std::launch::async, [&]() {
|
|
|
|
signal_handler_thread_id.set_value(pthread_self());
|
|
|
|
return signal_handler();
|
|
|
|
});
|
|
|
|
|
|
|
|
std::vector<std::future<bool>> futures;
|
2019-09-16 03:54:01 +08:00
|
|
|
for(int i = 0; i < 50; ++i)
|
2019-09-15 04:41:56 +08:00
|
|
|
futures.push_back(
|
|
|
|
std::async(
|
|
|
|
std::launch::async,
|
|
|
|
&worker,
|
|
|
|
std::ref(exit_condition)));
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
2019-09-16 03:54:01 +08:00
|
|
|
std::this_thread::yield();
|
|
|
|
|
2019-09-15 04:41:56 +08:00
|
|
|
REQUIRE(
|
|
|
|
pthread_kill(
|
|
|
|
signal_handler_thread_id.get_future().get(),
|
|
|
|
test_signal) == 0 );
|
|
|
|
|
|
|
|
for(auto& future : futures)
|
|
|
|
REQUIRE(future.get() == true);
|
|
|
|
|
|
|
|
int signal = ft_sig_handler.get();
|
|
|
|
REQUIRE( signal == test_signal );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-16 03:54:01 +08:00
|
|
|
TEST_CASE("exit-condition-looping")
|
|
|
|
{
|
|
|
|
std::vector<int> signals({SIGINT, SIGTERM, SIGUSR1, SIGUSR2});
|
|
|
|
for( auto test_signal : signals )
|
|
|
|
{
|
|
|
|
sgnl::AtomicCondition exit_condition(false);
|
|
|
|
|
|
|
|
sgnl::SignalHandler signal_handler({{test_signal, true}}, exit_condition);
|
|
|
|
std::promise<pthread_t> signal_handler_thread_id;
|
|
|
|
std::future<int> ft_sig_handler =
|
|
|
|
std::async(std::launch::async, [&]() {
|
|
|
|
signal_handler_thread_id.set_value(pthread_self());
|
|
|
|
return signal_handler();
|
|
|
|
});
|
|
|
|
|
|
|
|
std::vector<std::future<int>> futures;
|
|
|
|
for(int i = 0; i < 10; ++i)
|
|
|
|
futures.push_back(
|
|
|
|
std::async(
|
|
|
|
std::launch::async,
|
|
|
|
&looping_worker,
|
|
|
|
std::ref(exit_condition)));
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
std::this_thread::yield();
|
|
|
|
|
|
|
|
REQUIRE(
|
|
|
|
pthread_kill(
|
|
|
|
signal_handler_thread_id.get_future().get(),
|
|
|
|
test_signal) == 0 );
|
|
|
|
|
|
|
|
for(auto& future : futures)
|
|
|
|
// After 100 milliseconds, each worker thread should
|
|
|
|
// have looped at least 10 times.
|
|
|
|
// This might break if system is under heavy load
|
|
|
|
// or really slow.
|
|
|
|
REQUIRE(future.get() > 10);
|
|
|
|
|
|
|
|
int signal = ft_sig_handler.get();
|
|
|
|
REQUIRE( signal == test_signal );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-17 00:11:13 +08:00
|
|
|
TEST_CASE("signal-handler-reuse")
|
|
|
|
{
|
|
|
|
std::map<int, int> signal_map({{SIGINT, 1 << 0},
|
|
|
|
{SIGTERM, 1 << 1},
|
|
|
|
{SIGUSR1, 1 << 2},
|
|
|
|
{SIGUSR2, 1 << 3}});
|
|
|
|
|
|
|
|
sgnl::AtomicCondition<int> condition(0);
|
|
|
|
sgnl::SignalHandler signal_handler(signal_map, condition);
|
|
|
|
|
|
|
|
for( auto p : signal_map )
|
|
|
|
{
|
|
|
|
std::promise<pthread_t> signal_handler_thread_id;
|
|
|
|
std::future<int> ft_sig_handler =
|
|
|
|
std::async(std::launch::async, [&]() {
|
|
|
|
signal_handler_thread_id.set_value(pthread_self());
|
|
|
|
return signal_handler();
|
|
|
|
});
|
|
|
|
REQUIRE(
|
|
|
|
pthread_kill(
|
|
|
|
signal_handler_thread_id.get_future().get(),
|
|
|
|
p.first) == 0 );
|
|
|
|
REQUIRE( ft_sig_handler.get() == p.first );
|
|
|
|
REQUIRE( condition.get() == p.second );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|