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 a9ea030e9..d8b3bf041 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 @@ -175,6 +177,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/jsonrpcconnection.cpp b/lib/remote/jsonrpcconnection.cpp index 479fdea05..7fd8775a7 100644 --- a/lib/remote/jsonrpcconnection.cpp +++ b/lib/remote/jsonrpcconnection.cpp @@ -235,8 +235,20 @@ void JsonRpcConnection::Disconnect() m_Stream->lowest_layer().cancel(ec); + Timeout::Ptr shutdownTimeout (new Timeout( + m_IoStrand.context(), + m_IoStrand, + boost::posix_time::seconds(10), + [this, keepAlive](asio::yield_context yc) { + boost::system::error_code ec; + m_Stream->lowest_layer().cancel(ec); + } + )); + m_Stream->next_layer().async_shutdown(yc[ec]); + shutdownTimeout->Cancel(); + m_Stream->lowest_layer().shutdown(m_Stream->lowest_layer().shutdown_both, ec); } });