diff --git a/lib/base/io-engine.cpp b/lib/base/io-engine.cpp index 2727236ad..246a448f2 100644 --- a/lib/base/io-engine.cpp +++ b/lib/base/io-engine.cpp @@ -148,6 +148,8 @@ void AsioConditionVariable::Wait(boost::asio::yield_context yc) void Timeout::Cancel() { + m_Cancelled->store(true); + boost::system::error_code ec; m_Timer.cancel(ec); } diff --git a/lib/base/io-engine.hpp b/lib/base/io-engine.hpp index a030d2044..919f773bc 100644 --- a/lib/base/io-engine.hpp +++ b/lib/base/io-engine.hpp @@ -3,10 +3,12 @@ #ifndef IO_ENGINE_H #define IO_ENGINE_H +#include "base/atomic.hpp" #include "base/debug.hpp" #include "base/exception.hpp" #include "base/lazy-init.hpp" #include "base/logger.hpp" +#include "base/shared.hpp" #include "base/shared-object.hpp" #include #include @@ -174,21 +176,29 @@ public: template Timeout(boost::asio::io_context::strand& strand, const Timer::duration_type& timeoutFromNow, OnTimeout onTimeout) - : m_Timer(strand.context(), timeoutFromNow) + : m_Timer(strand.context(), timeoutFromNow), m_Cancelled(Shared>::Make(false)) { VERIFY(strand.running_in_this_thread()); - m_Timer.async_wait(boost::asio::bind_executor(strand, [onTimeout = std::move(onTimeout)](boost::system::error_code ec) { - if (!ec) { - onTimeout(); + m_Timer.async_wait(boost::asio::bind_executor( + strand, [cancelled = m_Cancelled, onTimeout = std::move(onTimeout)](boost::system::error_code ec) { + if (!ec && !cancelled->load()) { + onTimeout(); + } } - })); + )); + } + + ~Timeout() override + { + Cancel(); } void Cancel(); private: Timer m_Timer; + Shared>::Ptr m_Cancelled; }; } diff --git a/test/base-io-engine.cpp b/test/base-io-engine.cpp index 74cb67319..d75ce706d 100644 --- a/test/base-io-engine.cpp +++ b/test/base-io-engine.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using namespace icinga; @@ -30,7 +31,10 @@ BOOST_AUTO_TEST_CASE(timeout_run) timer.async_wait(yc); }); + std::thread eventLoop ([&io] { io.run(); }); io.run(); + eventLoop.join(); + BOOST_CHECK_EQUAL(called, 1); } @@ -54,7 +58,10 @@ BOOST_AUTO_TEST_CASE(timeout_cancelled) timer.async_wait(yc); }); + std::thread eventLoop ([&io] { io.run(); }); io.run(); + eventLoop.join(); + BOOST_CHECK_EQUAL(called, 0); } @@ -80,7 +87,10 @@ BOOST_AUTO_TEST_CASE(timeout_scope) timer.async_wait(yc); }); + std::thread eventLoop ([&io] { io.run(); }); io.run(); + eventLoop.join(); + BOOST_CHECK_EQUAL(called, 0); } @@ -108,7 +118,10 @@ BOOST_AUTO_TEST_CASE(timeout_due_cancelled) timer.async_wait(yc); }); + std::thread eventLoop ([&io] { io.run(); }); io.run(); + eventLoop.join(); + BOOST_CHECK_EQUAL(called, 0); } @@ -136,7 +149,10 @@ BOOST_AUTO_TEST_CASE(timeout_due_scope) timer.async_wait(yc); }); + std::thread eventLoop ([&io] { io.run(); }); io.run(); + eventLoop.join(); + BOOST_CHECK_EQUAL(called, 0); }