diff --git a/lib/base/io-engine.cpp b/lib/base/io-engine.cpp index 5dd3ee59c..d3197790e 100644 --- a/lib/base/io-engine.cpp +++ b/lib/base/io-engine.cpp @@ -144,3 +144,11 @@ void AsioConditionVariable::Wait(boost::asio::yield_context yc) boost::system::error_code ec; m_Timer.async_wait(yc[ec]); } + +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 ba4ebcfc5..dabd6730b 100644 --- a/lib/base/io-engine.hpp +++ b/lib/base/io-engine.hpp @@ -6,10 +6,12 @@ #include "base/exception.hpp" #include "base/lazy-init.hpp" #include "base/logger.hpp" +#include "base/shared-object.hpp" #include #include #include #include +#include #include #include #include @@ -153,6 +155,56 @@ private: boost::asio::deadline_timer m_Timer; }; +/** + * I/O timeout emulator + * + * @ingroup base + */ +class Timeout : public SharedObject +{ +public: + DECLARE_PTR_TYPEDEFS(Timeout); + + template + Timeout(boost::asio::io_context& io, Executor& executor, TimeoutFromNow timeoutFromNow, OnTimeout onTimeout) + : m_Timer(io) + { + Ptr keepAlive (this); + + m_Cancelled.store(false); + m_Timer.expires_from_now(std::move(timeoutFromNow)); + + IoEngine::SpawnCoroutine(executor, [this, keepAlive, onTimeout](boost::asio::yield_context yc) { + if (m_Cancelled.load()) { + return; + } + + { + boost::system::error_code ec; + + m_Timer.async_wait(yc[ec]); + + if (ec) { + return; + } + } + + if (m_Cancelled.load()) { + return; + } + + auto f (onTimeout); + f(std::move(yc)); + }); + } + + void Cancel(); + +private: + boost::asio::deadline_timer m_Timer; + std::atomic m_Cancelled; +}; + } #endif /* IO_ENGINE_H */ diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index cb024e1fd..0eb9c248b 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -542,32 +542,19 @@ void ApiListener::NewClientHandlerInternal( boost::system::error_code ec; { - struct DoneHandshake - { - bool Done = false; - }; - - auto doneHandshake (Shared::Make()); - - IoEngine::SpawnCoroutine(*strand, [strand, client, doneHandshake](asio::yield_context yc) { - namespace sys = boost::system; - - { - boost::asio::deadline_timer timer (strand->context()); - timer.expires_from_now(boost::posix_time::microseconds(intmax_t(Configuration::TlsHandshakeTimeout * 1000000))); - - sys::error_code ec; - timer.async_wait(yc[ec]); - } - - if (!doneHandshake->Done) { - sys::error_code ec; + Timeout::Ptr handshakeTimeout (new Timeout( + strand->context(), + *strand, + boost::posix_time::microseconds(intmax_t(Configuration::TlsHandshakeTimeout * 1000000)), + [strand, client](asio::yield_context yc) { + boost::system::error_code ec; client->lowest_layer().cancel(ec); } - }); + )); sslConn.async_handshake(role == RoleClient ? sslConn.client : sslConn.server, yc[ec]); - doneHandshake->Done = true; + + handshakeTimeout->Cancel(); } if (ec) {