Commit 57d9e752 authored by Dominik Charousset's avatar Dominik Charousset

Abort watchdog properly via condition variable

parent 39aed93e
#include <mutex>
#include <atomic> #include <atomic>
#include <thread>
#include <condition_variable>
#include "test.hpp" #include "test.hpp"
#include "caf/all.hpp" #include "caf/all.hpp"
...@@ -8,9 +11,45 @@ using namespace std; ...@@ -8,9 +11,45 @@ using namespace std;
using namespace caf; using namespace caf;
namespace { namespace {
atomic<size_t> s_error_count{0}; atomic<size_t> s_error_count{0};
std::mutex s_stdout_mtx; std::mutex s_stdout_mtx;
}
class watchdog {
public:
watchdog() {
m_thread = thread([&] {
auto tp = chrono::high_resolution_clock::now() + chrono::seconds(10);
unique_lock<mutex> guard{m_mtx};
while (!m_canceled
&& m_cv.wait_until(guard, tp) != cv_status::timeout) {
// spin
}
if (!m_canceled) {
std::lock_guard<std::mutex> io_guard{s_stdout_mtx};
cerr << "WATCHDOG: unit test did finish within 10s, abort" << endl;
abort();
}
});
}
~watchdog() {
{ // lifetime scope of guard
std::lock_guard<std::mutex> guard{m_mtx};
m_canceled = true;
m_cv.notify_all();
}
m_thread.join();
}
volatile bool m_canceled = false;
std::mutex m_mtx;
std::condition_variable m_cv;
std::thread m_thread;
};
watchdog* s_watchdog;
} // namespace <anonymous>
std::mutex& caf_stdout_mtx() { std::mutex& caf_stdout_mtx() {
return s_stdout_mtx; return s_stdout_mtx;
...@@ -24,6 +63,14 @@ void caf_inc_error_count() { ...@@ -24,6 +63,14 @@ void caf_inc_error_count() {
++s_error_count; ++s_error_count;
} }
void caf_launch_watchdog() {
s_watchdog = new watchdog;
}
void caf_cancel_watchdog() {
delete s_watchdog;
}
string caf_fill4(size_t value) { string caf_fill4(size_t value) {
string result = to_string(value); string result = to_string(value);
while (result.size() < 4) result.insert(result.begin(), '0'); while (result.size() < 4) result.insert(result.begin(), '0');
......
...@@ -147,19 +147,16 @@ inline void caf_check_value(V1 v1, V2 v2, const char* fname, size_t line, ...@@ -147,19 +147,16 @@ inline void caf_check_value(V1 v1, V2 v2, const char* fname, size_t line,
#define CAF_VERBOSE_EVAL(LineOfCode) \ #define CAF_VERBOSE_EVAL(LineOfCode) \
CAF_PRINT(#LineOfCode << " = " << (LineOfCode)); CAF_PRINT(#LineOfCode << " = " << (LineOfCode));
void caf_launch_watchdog();
void caf_cancel_watchdog();
#define CAF_TEST(testname) \ #define CAF_TEST(testname) \
::std::thread([] { \ caf_launch_watchdog(); \
::std::this_thread::sleep_for(std::chrono::seconds(10)); \
::std::cerr << "WATCHDOG: unit test did finish within 10s, abort\n"; \
::abort(); \
}).detach(); \
auto caf_test_scope_guard = ::caf::detail::make_scope_guard([] { \
std::cout << caf_error_count() << " error(s) detected" << std::endl; \
}); \
set_default_test_settings(); \ set_default_test_settings(); \
CAF_LOGF_INFO("run unit test " << #testname) CAF_LOGF_INFO("run unit test " << #testname)
#define CAF_TEST_RESULT() ((caf_error_count() == 0) ? 0 : -1) #define CAF_TEST_RESULT() \
caf_cancel_watchdog(), ((caf_error_count() == 0) ? 0 : -1)
#define CAF_CHECK_VERBOSE(line_of_code, err_stream) \ #define CAF_CHECK_VERBOSE(line_of_code, err_stream) \
if (!(line_of_code)) { \ if (!(line_of_code)) { \
......
...@@ -79,7 +79,7 @@ behavior tester(event_based_actor* self, const actor& aut) { ...@@ -79,7 +79,7 @@ behavior tester(event_based_actor* self, const actor& aut) {
}; };
} }
#define BREAK_ON_ERROR() if (CAF_TEST_RESULT() > 0) return #define BREAK_ON_ERROR() if (caf_error_count() > 0) return
template <spawn_options O1, spawn_options O2> template <spawn_options O1, spawn_options O2>
void run() { void run() {
......
...@@ -100,7 +100,7 @@ void test_message_lifetime_in_scoped_actor() { ...@@ -100,7 +100,7 @@ void test_message_lifetime_in_scoped_actor() {
template <spawn_options Os> template <spawn_options Os>
void test_message_lifetime() { void test_message_lifetime() {
test_message_lifetime_in_scoped_actor(); test_message_lifetime_in_scoped_actor();
if (CAF_TEST_RESULT() != 0) { if (caf_error_count() != 0) {
return; return;
} }
// put some preassure on the scheduler (check for thread safety) // put some preassure on the scheduler (check for thread safety)
......
...@@ -8,6 +8,7 @@ using namespace std; ...@@ -8,6 +8,7 @@ using namespace std;
using namespace caf; using namespace caf;
int main() { int main() {
CAF_TEST(test_optional);
{ {
optional<int> i,j; optional<int> i,j;
CAF_CHECK(i == j); CAF_CHECK(i == j);
......
...@@ -73,6 +73,7 @@ uint16_t run_server() { ...@@ -73,6 +73,7 @@ uint16_t run_server() {
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
CAF_TEST(test_typed_remote_actor)
announce<ping>("ping", &ping::value); announce<ping>("ping", &ping::value);
announce<pong>("pong", &pong::value); announce<pong>("pong", &pong::value);
message_builder{argv + 1, argv + argc}.apply({ message_builder{argv + 1, argv + argc}.apply({
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment