// SPDX-FileCopyrightText: 2026 Icinga GmbH // SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include #include #include #include #define REQUIRE_JOINS_WITHIN(t, timeout) \ BOOST_REQUIRE_MESSAGE(t.TryJoinWithin(timeout), "Thread not joinable within timeout.") #define CHECK_JOINS_WITHIN(t, timeout) \ BOOST_REQUIRE_MESSAGE(t.TryJoinWithin(timeout), "Thread not joinable within timeout.") #define TEST_JOINS_WITHIN(t, timeout) \ BOOST_REQUIRE_MESSAGE(t.TryJoinWithin(timeout), "Thread not joinable within timeout.") #define REQUIRE_JOINABLE(t) BOOST_REQUIRE_MESSAGE(t.Joinable(), "Thread not joinable.") #define CHECK_JOINABLE(t) BOOST_REQUIRE_MESSAGE(t.Joinable(), "Thread not joinable.") #define TEST_JOINABLE(t) BOOST_REQUIRE_MESSAGE(t.Joinable(), "Thread not joinable.") namespace icinga { class TestThread { public: explicit TestThread(std::function fn) : TestThread(std::move(fn), std::promise{}) {} bool Joinable() { auto status = m_JoinFuture.wait_for(std::chrono::milliseconds{0}); return status == std::future_status::ready; } template bool TryJoinWithin(std::chrono::duration timeout) { auto status = m_JoinFuture.wait_for(timeout); if (status == std::future_status::ready) { m_Thread.join(); return true; } return false; } private: explicit TestThread(std::function fn, std::promise joinPromise) : m_JoinFuture(joinPromise.get_future()), m_Thread([fn = std::move(fn), jp = std::move(joinPromise)]() mutable { fn(); jp.set_value(); }) { } std::future m_JoinFuture; std::thread m_Thread; }; } // namespace icinga