From 27170841487d6d8a7c9a50af930dcedb9503a9ee Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Tue, 12 May 2026 09:37:17 +0200 Subject: [PATCH 01/10] Fix potential nullptr-dereference in perfdata writer stats functions --- lib/perfdata/gelfwriter.cpp | 6 ++++-- lib/perfdata/gelfwriter.hpp | 1 + lib/perfdata/graphitewriter.cpp | 6 ++++-- lib/perfdata/graphitewriter.hpp | 1 + lib/perfdata/opentsdbwriter.cpp | 6 ++++-- lib/perfdata/opentsdbwriter.hpp | 1 + 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/perfdata/gelfwriter.cpp b/lib/perfdata/gelfwriter.cpp index 6f8567f7073..2828839eea4 100644 --- a/lib/perfdata/gelfwriter.cpp +++ b/lib/perfdata/gelfwriter.cpp @@ -49,11 +49,12 @@ void GelfWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perf for (const GelfWriter::Ptr& gelfwriter : ConfigType::GetObjectsByType()) { size_t workQueueItems = gelfwriter->m_WorkQueue.GetLength(); double workQueueItemRate = gelfwriter->m_WorkQueue.GetTaskCount(60) / 60.0; + auto connection = gelfwriter->m_LockedConnection.load(); nodes.emplace_back(gelfwriter->GetName(), new Dictionary({ { "work_queue_items", workQueueItems }, { "work_queue_item_rate", workQueueItemRate }, - { "connected", gelfwriter->m_Connection->IsConnected() }, + { "connected", connection && connection->IsConnected() }, { "source", gelfwriter->GetSource() } })); @@ -90,7 +91,8 @@ void GelfWriter::Resume() /* Register exception handler for WQ tasks. */ m_WorkQueue.SetExceptionCallback([this](boost::exception_ptr exp) { ExceptionHandler(std::move(exp)); }); - m_Connection = new PerfdataWriterConnection{this, GetHost(), GetPort(), m_SslContext, !GetInsecureNoverify()}; + m_LockedConnection.store(new PerfdataWriterConnection{this, GetHost(), GetPort(), m_SslContext, !GetInsecureNoverify()}); + m_Connection = m_LockedConnection.load(); /* Register event handlers. */ m_HandleCheckResults = Checkable::OnNewCheckResult.connect([this](const Checkable::Ptr& checkable, diff --git a/lib/perfdata/gelfwriter.hpp b/lib/perfdata/gelfwriter.hpp index f7d2a10c339..3663dfbe9de 100644 --- a/lib/perfdata/gelfwriter.hpp +++ b/lib/perfdata/gelfwriter.hpp @@ -34,6 +34,7 @@ class GelfWriter final : public ObjectImpl private: PerfdataWriterConnection::Ptr m_Connection; + Locked m_LockedConnection; WorkQueue m_WorkQueue{10000000, 1}; Shared::Ptr m_SslContext; diff --git a/lib/perfdata/graphitewriter.cpp b/lib/perfdata/graphitewriter.cpp index e00cd927589..71eaeb359b9 100644 --- a/lib/perfdata/graphitewriter.cpp +++ b/lib/perfdata/graphitewriter.cpp @@ -58,11 +58,12 @@ void GraphiteWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& for (const GraphiteWriter::Ptr& graphitewriter : ConfigType::GetObjectsByType()) { size_t workQueueItems = graphitewriter->m_WorkQueue.GetLength(); double workQueueItemRate = graphitewriter->m_WorkQueue.GetTaskCount(60) / 60.0; + auto connection = graphitewriter->m_LockedConnection.load(); nodes.emplace_back(graphitewriter->GetName(), new Dictionary({ { "work_queue_items", workQueueItems }, { "work_queue_item_rate", workQueueItemRate }, - { "connected", graphitewriter->m_Connection->IsConnected() } + { "connected", connection && connection->IsConnected() } })); perfdata->Add(new PerfdataValue("graphitewriter_" + graphitewriter->GetName() + "_work_queue_items", workQueueItems)); @@ -85,7 +86,8 @@ void GraphiteWriter::Resume() /* Register exception handler for WQ tasks. */ m_WorkQueue.SetExceptionCallback([this](boost::exception_ptr exp) { ExceptionHandler(std::move(exp)); }); - m_Connection = new PerfdataWriterConnection{this, GetHost(), GetPort()}; + m_LockedConnection.store(new PerfdataWriterConnection{this, GetHost(), GetPort()}); + m_Connection = m_LockedConnection.load(); /* Register event handlers. */ m_HandleCheckResults = Checkable::OnNewCheckResult.connect([this](const Checkable::Ptr& checkable, diff --git a/lib/perfdata/graphitewriter.hpp b/lib/perfdata/graphitewriter.hpp index 470fcc07dac..2785e5e29ca 100644 --- a/lib/perfdata/graphitewriter.hpp +++ b/lib/perfdata/graphitewriter.hpp @@ -36,6 +36,7 @@ class GraphiteWriter final : public ObjectImpl private: PerfdataWriterConnection::Ptr m_Connection; + Locked m_LockedConnection; WorkQueue m_WorkQueue{10000000, 1}; boost::signals2::connection m_HandleCheckResults; diff --git a/lib/perfdata/opentsdbwriter.cpp b/lib/perfdata/opentsdbwriter.cpp index 1b2f82a7d9c..c79e09d7ac1 100644 --- a/lib/perfdata/opentsdbwriter.cpp +++ b/lib/perfdata/opentsdbwriter.cpp @@ -55,11 +55,12 @@ void OpenTsdbWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& for (const OpenTsdbWriter::Ptr& opentsdbwriter : ConfigType::GetObjectsByType()) { size_t workQueueItems = opentsdbwriter->m_WorkQueue.GetLength(); double workQueueItemRate = opentsdbwriter->m_WorkQueue.GetTaskCount(60) / 60.0; + auto connection = opentsdbwriter->m_LockedConnection.load(); nodes.emplace_back( opentsdbwriter->GetName(), new Dictionary({ - { "connected", opentsdbwriter->m_Connection->IsConnected() }, + { "connected", connection && connection->IsConnected() }, {"work_queue_items", workQueueItems}, {"work_queue_item_rate", workQueueItemRate} } @@ -90,7 +91,8 @@ void OpenTsdbWriter::Resume() ReadConfigTemplate(); - m_Connection = new PerfdataWriterConnection{this, GetHost(), GetPort()}; + m_LockedConnection.store(new PerfdataWriterConnection{this, GetHost(), GetPort()}); + m_Connection = m_LockedConnection.load(); m_HandleCheckResults = Service::OnNewCheckResult.connect([this](const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MessageOrigin::Ptr&) { CheckResultHandler(checkable, cr); diff --git a/lib/perfdata/opentsdbwriter.hpp b/lib/perfdata/opentsdbwriter.hpp index 5db2985400a..0dc00f9d07b 100644 --- a/lib/perfdata/opentsdbwriter.hpp +++ b/lib/perfdata/opentsdbwriter.hpp @@ -37,6 +37,7 @@ class OpenTsdbWriter final : public ObjectImpl WorkQueue m_WorkQueue{10000000, 1}; std::string m_MsgBuf; PerfdataWriterConnection::Ptr m_Connection; + Locked m_LockedConnection; boost::signals2::connection m_HandleCheckResults; From 339434c8b4e3a20c64440d07d96a2931edc7cdc2 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Thu, 2 Apr 2026 13:32:43 +0200 Subject: [PATCH 02/10] Read until end of file in response reading test-cases --- test/perfdata-perfdatatargetfixture.hpp | 11 ++++++++++- test/perfdata-perfdatawriterconnection.cpp | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/test/perfdata-perfdatatargetfixture.hpp b/test/perfdata-perfdatatargetfixture.hpp index bac1c504de9..8c2a381bcf8 100644 --- a/test/perfdata-perfdatatargetfixture.hpp +++ b/test/perfdata-perfdatatargetfixture.hpp @@ -71,11 +71,20 @@ class PerfdataWriterTargetFixture BOOST_REQUIRE(stream->next_layer().IsVerifyOK()); } - void Shutdown() + void Shutdown(bool wait = false) { BOOST_REQUIRE(std::holds_alternative::Ptr>(m_Stream)); auto& stream = std::get::Ptr>(m_Stream); try { + if (wait) { + std::array buf{}; + boost::asio::mutable_buffer readBuf (buf.data(), buf.size()); + boost::system::error_code ec; + + do { + stream->read_some(readBuf, ec); + } while (!ec); + } stream->next_layer().shutdown(); } catch (const std::exception& ex) { if (const auto* se = dynamic_cast(&ex); diff --git a/test/perfdata-perfdatawriterconnection.cpp b/test/perfdata-perfdatawriterconnection.cpp index 16ed299a947..0f2435198d9 100644 --- a/test/perfdata-perfdatawriterconnection.cpp +++ b/test/perfdata-perfdatawriterconnection.cpp @@ -207,7 +207,7 @@ BOOST_AUTO_TEST_CASE(stuck_reading_response) requestReadPromise.set_value(); // Do not send a response but react to the shutdown to be polite. shutdownPromise.get_future().get(); - Shutdown(); + Shutdown(true); }}; TestThread timeoutThread{[&]() { @@ -315,7 +315,7 @@ BOOST_AUTO_TEST_CASE(http_send_retry) SendResponse(); - Shutdown(); + Shutdown(true); }}; boost::beast::http::request request{boost::beast::http::verb::post, "foo", 10}; From 55eb326a562b791082c2c0f42f4f3fee029668fd Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Wed, 22 Apr 2026 11:38:29 +0200 Subject: [PATCH 03/10] Fix a race-condition when perfdata writer is stuck in handshake The issue occurs when ::Connect in `EnsureConnected()` returns after `Disconnect()` has already set `m_Stopped` to true. By adding a check and throwing an exception before entering `async_handshake()` the behavior should now always be consistent. --- lib/perfdata/perfdatawriterconnection.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/perfdata/perfdatawriterconnection.cpp b/lib/perfdata/perfdatawriterconnection.cpp index 46000c28f47..e8a56e89980 100644 --- a/lib/perfdata/perfdatawriterconnection.cpp +++ b/lib/perfdata/perfdatawriterconnection.cpp @@ -62,7 +62,7 @@ bool PerfdataWriterConnection::IsStopped() const void PerfdataWriterConnection::Disconnect() { - if (m_Stopped.exchange(true, std::memory_order_relaxed)) { + if (m_Stopped.exchange(true)) { return; } @@ -133,6 +133,10 @@ void PerfdataWriterConnection::EnsureConnected(const boost::asio::yield_context& ::Connect(stream->lowest_layer(), m_Host, m_Port, yc); if constexpr (std::is_same_v, Shared::Ptr>) { + if (m_Stopped) { + BOOST_THROW_EXCEPTION(Stopped{}); + } + using type = boost::asio::ssl::stream_base::handshake_type; stream->next_layer().async_handshake(type::client, yc); From db7a056ceeb76c39c85ba57300d810200d43d56b Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Wed, 22 Apr 2026 11:42:31 +0200 Subject: [PATCH 04/10] Fix ineffective cancel() when stuck in perfdata writer handshake --- lib/perfdata/perfdatawriterconnection.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/perfdata/perfdatawriterconnection.cpp b/lib/perfdata/perfdatawriterconnection.cpp index e8a56e89980..c8cc80e9dbe 100644 --- a/lib/perfdata/perfdatawriterconnection.cpp +++ b/lib/perfdata/perfdatawriterconnection.cpp @@ -76,9 +76,13 @@ void PerfdataWriterConnection::Disconnect() * completion. */ std::visit( - [](const auto& stream) { + [&](const auto& stream) { if (stream->lowest_layer().is_open()) { - stream->lowest_layer().cancel(); + if (m_Connected) { + stream->lowest_layer().cancel(); + } else { + stream->lowest_layer().close(); + } } }, m_Stream @@ -160,7 +164,7 @@ void PerfdataWriterConnection::EnsureConnected(const boost::asio::yield_context& void PerfdataWriterConnection::Disconnect(boost::asio::yield_context yc) { - if (!m_Connected.exchange(false, std::memory_order_relaxed)) { + if (!m_Connected.exchange(false)) { return; } From 3cc5ee212213028f1c415687273e0e24fd0937a0 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Tue, 26 May 2026 12:04:26 +0200 Subject: [PATCH 05/10] Ignore errors for `close()` and TCP-`shutdown()` This makes the TCP-path also ignore all errors/exceptions, same as the TLS-path's `GracefulDisconnect()`. --- lib/perfdata/perfdatawriterconnection.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/perfdata/perfdatawriterconnection.cpp b/lib/perfdata/perfdatawriterconnection.cpp index c8cc80e9dbe..4b0838d0ae9 100644 --- a/lib/perfdata/perfdatawriterconnection.cpp +++ b/lib/perfdata/perfdatawriterconnection.cpp @@ -173,8 +173,9 @@ void PerfdataWriterConnection::Disconnect(boost::asio::yield_context yc) if constexpr (std::is_same_v, Shared::Ptr>) { stream->GracefulDisconnect(m_Strand, yc); } else { - stream->lowest_layer().shutdown(boost::asio::socket_base::shutdown_both); - stream->lowest_layer().close(); + boost::system::error_code ec; + stream->lowest_layer().shutdown(boost::asio::socket_base::shutdown_both, ec); + stream->lowest_layer().close(ec); } }, m_Stream From 792c70393ad5f7756bc98cb1ebec15bd5abf2c0d Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Wed, 29 Apr 2026 12:30:28 +0200 Subject: [PATCH 06/10] Fix `PerfdataWriterConnection` segfaults on non-X86 architectures The issue is that std::promise internally also used thread local storage, in a call to `std::call_once` in `std::promise::set_value()`. The theory is that since all paths in `Send()` run this `std::call_once` routine and from then on, then Coroutine function looks like a normal function, the compiler inlined `set_value()` and moved the common parts of it to a common location for all paths before the suspension point in WriteMessage(yc). When finally the coroutine is resumes, it is likely that that happens under a different thread, which still has `__once_callable` in `std::call_once` set as `nullptr`, leading to the segmentation fault. The fix is to not use std::promise across coroutine suspension points and instead reimplement the functionality we required from it in a small helper class `SyncResult` that does not require any thread local storage. --- lib/perfdata/perfdatawriterconnection.cpp | 8 +-- lib/perfdata/perfdatawriterconnection.hpp | 63 ++++++++++++++++++++--- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/lib/perfdata/perfdatawriterconnection.cpp b/lib/perfdata/perfdatawriterconnection.cpp index 4b0838d0ae9..8b50f061a9b 100644 --- a/lib/perfdata/perfdatawriterconnection.cpp +++ b/lib/perfdata/perfdatawriterconnection.cpp @@ -66,7 +66,7 @@ void PerfdataWriterConnection::Disconnect() return; } - std::promise promise; + SyncResult ret; IoEngine::SpawnCoroutine(m_Strand, [&](boost::asio::yield_context yc) { try { @@ -90,13 +90,13 @@ void PerfdataWriterConnection::Disconnect() m_ReconnectTimer.cancel(); Disconnect(std::move(yc)); - promise.set_value(); + ret.SetValue(); } catch (const std::exception& ex) { - promise.set_exception(std::current_exception()); + ret.SetException(std::current_exception()); } }); - promise.get_future().get(); + ret.Get(); } AsioTlsOrTcpStream PerfdataWriterConnection::MakeStream() const diff --git a/lib/perfdata/perfdatawriterconnection.hpp b/lib/perfdata/perfdatawriterconnection.hpp index 729878a2960..9f91f037ea6 100644 --- a/lib/perfdata/perfdatawriterconnection.hpp +++ b/lib/perfdata/perfdatawriterconnection.hpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace icinga { @@ -21,6 +22,56 @@ class PerfdataWriterConnection final : public Object static constexpr auto InitialRetryWait = 50ms; static constexpr auto FinalRetryWait = 32s; + template + class SyncResult + { + using ValueType = std::variant, bool, T>, std::exception_ptr>; + + public: + template>> + void SetValue(U&& v) + { + std::lock_guard lock(m_Mutex); + m_Value = std::forward(v); + m_Cv.notify_one(); + } + + template>> + void SetValue() + { + std::lock_guard lock(m_Mutex); + m_Value = true; + m_Cv.notify_one(); + } + + void SetException(std::exception_ptr ep) + { + std::lock_guard lock(m_Mutex); + m_Value = ValueType{ep}; + m_Cv.notify_one(); + } + + T Get() + { + std::unique_lock l(m_Mutex); + m_Cv.wait(l, [&] { return !std::holds_alternative(m_Value); }); + if (std::holds_alternative(m_Value)) { + std::rethrow_exception(std::get(m_Value)); + } + + if constexpr (std::is_void_v) { + return; + } else { + return std::move(std::get(m_Value)); + } + } + + private: + std::mutex m_Mutex; + std::condition_variable m_Cv; + ValueType m_Value; + }; + public: DECLARE_PTR_TYPEDEFS(PerfdataWriterConnection); @@ -66,7 +117,7 @@ class PerfdataWriterConnection final : public Object } using RetType = decltype(WriteMessage(std::declval(), std::declval())); - std::promise promise; + SyncResult ret; IoEngine::SpawnCoroutine(m_Strand, [&](boost::asio::yield_context yc) { while (true) { @@ -75,16 +126,16 @@ class PerfdataWriterConnection final : public Object if constexpr (std::is_void_v) { WriteMessage(std::forward(buf), yc); - promise.set_value(); + ret.SetValue(); } else { - promise.set_value(WriteMessage(std::forward(buf), yc)); + ret.SetValue(WriteMessage(std::forward(buf), yc)); } m_RetryTimeout = InitialRetryWait; return; } catch (const std::exception& ex) { if (m_Stopped) { - promise.set_exception(std::make_exception_ptr(Stopped{})); + ret.SetException(std::make_exception_ptr(Stopped{})); return; } @@ -98,14 +149,14 @@ class PerfdataWriterConnection final : public Object try { BackoffWait(yc); } catch (const std::exception&) { - promise.set_exception(std::make_exception_ptr(Stopped{})); + ret.SetException(std::make_exception_ptr(Stopped{})); return; } } } }); - return promise.get_future().get(); + return ret.Get(); } void Disconnect(); From 1ae216ae840eb4ddbffcbcb436d7a7776497373c Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Mon, 30 Mar 2026 09:25:06 +0200 Subject: [PATCH 07/10] Enable parallel testing in Linux CI and container image build --- .github/workflows/linux.bash | 3 ++- Containerfile | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux.bash b/.github/workflows/linux.bash index 3b41dc145e6..7888a7185e1 100755 --- a/.github/workflows/linux.bash +++ b/.github/workflows/linux.bash @@ -38,6 +38,7 @@ case "$DISTRO" in ) ln -vs /usr/bin/cmake3 /usr/local/bin/cmake + ln -vs /usr/bin/ctest3 /usr/local/bin/ctest ln -vs /usr/bin/ninja-build /usr/local/bin/ninja CMAKE_OPTS+=(-DBOOST_{INCLUDEDIR=/boost_1_69_0,LIBRARYDIR=/boost_1_69_0/stage/lib}) export LD_LIBRARY_PATH=/boost_1_69_0/stage/lib @@ -154,6 +155,6 @@ cd /icinga2/build ninja -v -ninja test +ctest -j$(nproc) --output-on-failure ninja install icinga2 daemon -C diff --git a/Containerfile b/Containerfile index ad3e2302b8a..1da77e4ba97 100644 --- a/Containerfile +++ b/Containerfile @@ -128,8 +128,11 @@ RUN --mount=type=bind,source=.,target=/icinga2,readonly \ -DICINGA2_RUNDIR=/run \ -DICINGA2_WITH_COMPAT=OFF \ -DICINGA2_WITH_LIVESTATUS=OFF && \ - make -j$([ "$MAKE_JOBS" = auto ] && nproc || echo "$MAKE_JOBS") && \ - if [ "${ICINGA2_BUILD_TESTING}" = ON ]; then CTEST_OUTPUT_ON_FAILURE=1 make test; fi && \ + JOBS=$([ "$MAKE_JOBS" = auto ] && nproc || echo "$MAKE_JOBS") && \ + make -j"$JOBS" && \ + if [ "${ICINGA2_BUILD_TESTING}" = ON ]; then \ + ctest -j"$JOBS" --output-on-failure; \ + fi && \ make install DESTDIR=/icinga2-install RUN rm -rf /icinga2-install/etc/icinga2/features-enabled/mainlog.conf \ From c703c1c2e40cb0bc62a0c2d44ec6951d87cb1cef Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Wed, 22 Apr 2026 11:31:01 +0200 Subject: [PATCH 08/10] Relax all timeouts for PerfdataWriterConnection test-cases This isn't strictly necessary, but since tests are now running in parallel, it doesn't hurt to give slower machines more time to complete these tests and this gives a little more headroom for potential changes that subtly affect the behavor of the components involved (like boost new versions). --- test/perfdata-elasticsearchwriter.cpp | 4 ++-- test/perfdata-gelfwriter.cpp | 4 ++-- test/perfdata-graphitewriter.cpp | 4 ++-- test/perfdata-influxdbwriter.cpp | 2 +- test/perfdata-opentsdbwriter.cpp | 4 ++-- test/perfdata-perfdatawriterconnection.cpp | 28 +++++++++++----------- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/test/perfdata-elasticsearchwriter.cpp b/test/perfdata-elasticsearchwriter.cpp index ac6abac8d76..4cb387356ad 100644 --- a/test/perfdata-elasticsearchwriter.cpp +++ b/test/perfdata-elasticsearchwriter.cpp @@ -45,13 +45,13 @@ BOOST_AUTO_TEST_CASE(pause_with_pending_work) ResumeWriter(); // Process check-results until the writer is stuck. - BOOST_REQUIRE_MESSAGE(GetWriterStuck(10s), "Failed to get Writer stuck."); + BOOST_REQUIRE_MESSAGE(GetWriterStuck(20s), "Failed to get Writer stuck."); // Now try to pause. PauseWriter(); REQUIRE_LOG_MESSAGE("Connection stopped\\.", 10s); - REQUIRE_LOG_MESSAGE("'ElasticsearchWriter' paused\\.", 10s); + REQUIRE_LOG_MESSAGE("'ElasticsearchWriter' paused\\.", 1s); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/perfdata-gelfwriter.cpp b/test/perfdata-gelfwriter.cpp index 8da07bc4a69..0d98423e5a8 100644 --- a/test/perfdata-gelfwriter.cpp +++ b/test/perfdata-gelfwriter.cpp @@ -36,12 +36,12 @@ BOOST_AUTO_TEST_CASE(pause_with_pending_work) ResumeWriter(); // Process check-results until the writer is stuck. - BOOST_REQUIRE_MESSAGE(GetWriterStuck(10s), "Failed to get Writer stuck."); + BOOST_REQUIRE_MESSAGE(GetWriterStuck(20s), "Failed to get Writer stuck."); // Now stop reading and try to pause OpenTsdbWriter. PauseWriter(); - REQUIRE_LOG_MESSAGE("Connection stopped\\.", 1s); + REQUIRE_LOG_MESSAGE("Connection stopped\\.", 10s); REQUIRE_LOG_MESSAGE("'GelfWriter' paused\\.", 1s); } diff --git a/test/perfdata-graphitewriter.cpp b/test/perfdata-graphitewriter.cpp index 9b5789fe219..a175ba1c5dc 100644 --- a/test/perfdata-graphitewriter.cpp +++ b/test/perfdata-graphitewriter.cpp @@ -35,13 +35,13 @@ BOOST_AUTO_TEST_CASE(pause_with_pending_work) ResumeWriter(); // Process check-results until the writer is stuck. - BOOST_REQUIRE_MESSAGE(GetWriterStuck(10s), "Failed to get Writer stuck."); + BOOST_REQUIRE_MESSAGE(GetWriterStuck(20s), "Failed to get Writer stuck."); // Now stop reading and try to pause OpenTsdbWriter. PauseWriter(); REQUIRE_LOG_MESSAGE("Connection stopped\\.", 10s); - REQUIRE_LOG_MESSAGE("'GraphiteWriter' paused\\.", 10s); + REQUIRE_LOG_MESSAGE("'GraphiteWriter' paused\\.", 1s); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/perfdata-influxdbwriter.cpp b/test/perfdata-influxdbwriter.cpp index 43837b50fcf..a10229a2c84 100644 --- a/test/perfdata-influxdbwriter.cpp +++ b/test/perfdata-influxdbwriter.cpp @@ -38,7 +38,7 @@ BOOST_AUTO_TEST_CASE(pause_with_pending_work) ResumeWriter(); // Process check-results until the writer is stuck. - BOOST_REQUIRE_MESSAGE(GetWriterStuck(10s), "Failed to get Writer stuck."); + BOOST_REQUIRE_MESSAGE(GetWriterStuck(20s), "Failed to get Writer stuck."); // Now try to pause. PauseWriter(); diff --git a/test/perfdata-opentsdbwriter.cpp b/test/perfdata-opentsdbwriter.cpp index c3ec47d6deb..5a3fb8d622b 100644 --- a/test/perfdata-opentsdbwriter.cpp +++ b/test/perfdata-opentsdbwriter.cpp @@ -41,13 +41,13 @@ BOOST_AUTO_TEST_CASE(pause_with_pending_work) ResumeWriter(); // Process check-results until the writer is stuck. - BOOST_REQUIRE_MESSAGE(GetWriterStuck(10s), "Failed to get Writer stuck."); + BOOST_REQUIRE_MESSAGE(GetWriterStuck(20s), "Failed to get Writer stuck."); // Now stop reading and try to pause OpenTsdbWriter. PauseWriter(); REQUIRE_LOG_MESSAGE("Connection stopped\\.", 10s); - REQUIRE_LOG_MESSAGE("'OpenTsdbWriter' paused\\.", 10s); + REQUIRE_LOG_MESSAGE("'OpenTsdbWriter' paused\\.", 1s); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/perfdata-perfdatawriterconnection.cpp b/test/perfdata-perfdatawriterconnection.cpp index 0f2435198d9..cd1d90b314e 100644 --- a/test/perfdata-perfdatawriterconnection.cpp +++ b/test/perfdata-perfdatawriterconnection.cpp @@ -56,14 +56,14 @@ BOOST_AUTO_TEST_CASE(connection_refused) std::promise p; TestThread timeoutThread{[&]() { auto f = p.get_future(); - GetConnection().CancelAfterTimeout(f, 50ms); + GetConnection().CancelAfterTimeout(f, 250ms); }}; BOOST_REQUIRE_THROW( GetConnection().Send(boost::asio::const_buffer{"foobar", 7}), PerfdataWriterConnection::Stopped ); - REQUIRE_JOINS_WITHIN(timeoutThread, 1s); + REQUIRE_JOINS_WITHIN(timeoutThread, 10s); } /* The PerfdataWriterConnection connects automatically when sending the first data. @@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(ensure_connected) BOOST_REQUIRE_NO_THROW(GetConnection().Disconnect()); disconnectedPromise.set_value(); - REQUIRE_JOINS_WITHIN(mockTargetThread, 1s); + REQUIRE_JOINS_WITHIN(mockTargetThread, 10s); } /* Verify that data can still be sent while CancelAfterTimeout is waiting and the timeout @@ -113,15 +113,15 @@ BOOST_AUTO_TEST_CASE(finish_during_timeout) TestThread timeoutThread{[&]() { auto f = p.get_future(); - GetConnection().CancelAfterTimeout(f, 50ms); + GetConnection().CancelAfterTimeout(f, 250ms); BOOST_REQUIRE(f.wait_for(0ms) == std::future_status::ready); BOOST_REQUIRE(!GetConnection().IsConnected()); }}; GetConnection().Send(boost::asio::const_buffer{"foobar", 7}); - REQUIRE_JOINS_WITHIN(timeoutThread, 1s); - REQUIRE_JOINS_WITHIN(mockTargetThread, 1s); + REQUIRE_JOINS_WITHIN(timeoutThread, 10s); + REQUIRE_JOINS_WITHIN(mockTargetThread, 10s); } /* For the client, even a hanging server will accept the connection immediately, since it's done @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(stuck_in_handshake) TestThread timeoutThread{[&]() { Accept(); auto f = p.get_future(); - GetConnection().CancelAfterTimeout(f, 50ms); + GetConnection().CancelAfterTimeout(f, 250ms); BOOST_REQUIRE(f.wait_for(0ms) == std::future_status::timeout); }}; @@ -142,7 +142,7 @@ BOOST_AUTO_TEST_CASE(stuck_in_handshake) GetConnection().Send(boost::asio::const_buffer{"foobar", 7}), PerfdataWriterConnection::Stopped ); - REQUIRE_JOINS_WITHIN(timeoutThread, 1s); + REQUIRE_JOINS_WITHIN(timeoutThread, 10s); } /* When the disconnect timeout runs out while sending something to a slow or blocking server, we @@ -185,8 +185,8 @@ BOOST_AUTO_TEST_CASE(stuck_sending) BOOST_REQUIRE_THROW(GetConnection().Send(buf), PerfdataWriterConnection::Stopped); shutdownPromise.set_value(); - REQUIRE_JOINS_WITHIN(timeoutThread, 1s); - REQUIRE_JOINS_WITHIN(mockTargetThread, 1s); + REQUIRE_JOINS_WITHIN(timeoutThread, 10s); + REQUIRE_JOINS_WITHIN(mockTargetThread, 10s); } /* This simulates a server that is stuck after receiving a HTTP request and before sending their @@ -226,8 +226,8 @@ BOOST_AUTO_TEST_CASE(stuck_reading_response) BOOST_REQUIRE_THROW(GetConnection().Send(request), PerfdataWriterConnection::Stopped); shutdownPromise.set_value(); - REQUIRE_JOINS_WITHIN(timeoutThread, 1s); - REQUIRE_JOINS_WITHIN(mockTargetThread, 1s); + REQUIRE_JOINS_WITHIN(timeoutThread, 10s); + REQUIRE_JOINS_WITHIN(mockTargetThread, 10s); } /* This test simulates a server that closes the connection and reappears at a later time. @@ -261,7 +261,7 @@ BOOST_AUTO_TEST_CASE(reconnect_failed) BOOST_REQUIRE_NO_THROW(GetConnection().Send(boost::asio::const_buffer{randomData.data(), randomData.size()})); BOOST_REQUIRE_NO_THROW(GetConnection().Disconnect()); - REQUIRE_JOINS_WITHIN(mockTargetThread, 1s); + REQUIRE_JOINS_WITHIN(mockTargetThread, 10s); } /* This tests if retrying an http send will reproducibly lead to the exact same message being @@ -329,7 +329,7 @@ BOOST_AUTO_TEST_CASE(http_send_retry) BOOST_REQUIRE_NO_THROW(GetConnection().Send(request)); BOOST_REQUIRE_NO_THROW(GetConnection().Disconnect()); - REQUIRE_JOINS_WITHIN(mockTargetThread, 1s); + REQUIRE_JOINS_WITHIN(mockTargetThread, 10s); } BOOST_AUTO_TEST_SUITE_END() From 69d5d6d121ce38cea8e04ebc66e0b1917c7b657e Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Mon, 27 Apr 2026 15:23:58 +0200 Subject: [PATCH 09/10] Double time-values for timer tests --- test/base-timer.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/base-timer.cpp b/test/base-timer.cpp index ff9496436e1..2d71db63311 100644 --- a/test/base-timer.cpp +++ b/test/base-timer.cpp @@ -10,7 +10,7 @@ * Windows needs a special handicap to keep up with the other OSs. */ #ifdef _WIN32 -static constexpr double timeMultiplier = 10; +static constexpr double timeMultiplier = 5; #else //_WIN32 static constexpr double timeMultiplier = 1; #endif //_WIN32 @@ -38,10 +38,10 @@ BOOST_AUTO_TEST_CASE(invoke) Timer::Ptr timer = Timer::Create(); timer->OnTimerExpired.connect([&counter](const Timer* const&) { counter++; }); - timer->SetInterval(.1 * timeMultiplier); + timer->SetInterval(.2 * timeMultiplier); timer->Start(); - Utility::Sleep(.55 * timeMultiplier); + Utility::Sleep(1.1 * timeMultiplier); timer->Stop(); // At this point, the timer should have fired exactly 5 times (0.5 / 0.1) and the sixth time @@ -55,12 +55,12 @@ BOOST_AUTO_TEST_CASE(scope) Timer::Ptr timer = Timer::Create(); timer->OnTimerExpired.connect([&counter](const Timer* const&) { counter++; }); - timer->SetInterval(.1 * timeMultiplier); + timer->SetInterval(.2 * timeMultiplier); timer->Start(); - Utility::Sleep(.55 * timeMultiplier); + Utility::Sleep(1.1 * timeMultiplier); timer.reset(); - Utility::Sleep(.1 * timeMultiplier); + Utility::Sleep(.2 * timeMultiplier); // At this point, the timer should have fired exactly 5 times (0.5 / 0.1) and the sixth time // should not have fired yet as we destroyed the timer after 0.55 seconds (0.6 would be needed), From caf2b0dd797c09e06d77f94ebc8284c1f5d13972 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Thu, 23 Apr 2026 12:04:58 +0200 Subject: [PATCH 10/10] Avoid too many 'connection refused' errors in log by listening early --- test/perfdata-perfdatawriterfixture.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/perfdata-perfdatawriterfixture.hpp b/test/perfdata-perfdatawriterfixture.hpp index e70b2123c84..a74cf1dd53a 100644 --- a/test/perfdata-perfdatawriterfixture.hpp +++ b/test/perfdata-perfdatawriterfixture.hpp @@ -121,6 +121,7 @@ object Host "h1" { void ResumeWriter() { + Listen(); static_cast(m_Writer)->OnConfigLoaded(); m_Writer->SetActive(true); m_Writer->Activate();