This commit is contained in:
Johannes Schmidt 2026-07-07 09:07:48 +02:00 committed by GitHub
commit 8a0ae94552
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 88 additions and 19 deletions

View file

@ -62,11 +62,11 @@ bool PerfdataWriterConnection::IsStopped() const
void PerfdataWriterConnection::Disconnect()
{
if (m_Stopped.exchange(true, std::memory_order_relaxed)) {
if (m_Stopped.exchange(true)) {
return;
}
std::promise<void> promise;
SyncResult<void> ret;
IoEngine::SpawnCoroutine(m_Strand, [&](boost::asio::yield_context yc) {
try {
@ -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
@ -86,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
@ -133,6 +137,10 @@ void PerfdataWriterConnection::EnsureConnected(const boost::asio::yield_context&
::Connect(stream->lowest_layer(), m_Host, m_Port, yc);
if constexpr (std::is_same_v<std::decay_t<decltype(stream)>, Shared<AsioTlsStream>::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);
@ -156,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;
}
@ -165,8 +173,9 @@ void PerfdataWriterConnection::Disconnect(boost::asio::yield_context yc)
if constexpr (std::is_same_v<std::decay_t<decltype(stream)>, Shared<AsioTlsStream>::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

View file

@ -10,6 +10,7 @@
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/string_body.hpp>
#include <future>
#include <utility>
namespace icinga {
@ -21,6 +22,56 @@ class PerfdataWriterConnection final : public Object
static constexpr auto InitialRetryWait = 50ms;
static constexpr auto FinalRetryWait = 32s;
template<typename T>
class SyncResult
{
using ValueType = std::variant<std::monostate, std::conditional_t<std::is_void_v<T>, bool, T>, std::exception_ptr>;
public:
template<typename U, typename V = T, typename = std::enable_if_t<!std::is_void_v<V>>>
void SetValue(U&& v)
{
std::lock_guard lock(m_Mutex);
m_Value = std::forward<U>(v);
m_Cv.notify_one();
}
template<typename V = T, typename = std::enable_if_t<std::is_void_v<V>>>
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<std::monostate>(m_Value); });
if (std::holds_alternative<std::exception_ptr>(m_Value)) {
std::rethrow_exception(std::get<std::exception_ptr>(m_Value));
}
if constexpr (std::is_void_v<T>) {
return;
} else {
return std::move(std::get<T>(m_Value));
}
}
private:
std::mutex m_Mutex;
std::condition_variable m_Cv;
ValueType m_Value;
};
public:
DECLARE_PTR_TYPEDEFS(PerfdataWriterConnection);
@ -66,7 +117,7 @@ public:
}
using RetType = decltype(WriteMessage(std::declval<Buffer>(), std::declval<boost::asio::yield_context>()));
std::promise<RetType> promise;
SyncResult<RetType> ret;
IoEngine::SpawnCoroutine(m_Strand, [&](boost::asio::yield_context yc) {
while (true) {
@ -75,16 +126,16 @@ public:
if constexpr (std::is_void_v<RetType>) {
WriteMessage(std::forward<Buffer>(buf), yc);
promise.set_value();
ret.SetValue();
} else {
promise.set_value(WriteMessage(std::forward<Buffer>(buf), yc));
ret.SetValue(WriteMessage(std::forward<Buffer>(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 @@ public:
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();

View file

@ -71,11 +71,20 @@ public:
BOOST_REQUIRE(stream->next_layer().IsVerifyOK());
}
void Shutdown()
void Shutdown(bool wait = false)
{
BOOST_REQUIRE(std::holds_alternative<Shared<AsioTlsStream>::Ptr>(m_Stream));
auto& stream = std::get<Shared<AsioTlsStream>::Ptr>(m_Stream);
try {
if (wait) {
std::array<std::byte, 128> 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<const boost::system::system_error*>(&ex);

View file

@ -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<boost::beast::http::string_body> request{boost::beast::http::verb::post, "foo", 10};