From 00802ed9fadf626f95a0e1766b6919407db0ebfa Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Mon, 26 May 2025 10:01:05 +0200 Subject: [PATCH] Stop ApiListener::ListenerCoroutineProc() when Stop() is called --- lib/remote/apilistener.cpp | 49 ++++++++++++++++++++++++++++++++++++-- lib/remote/apilistener.hpp | 4 ++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index 5ba45b94f..d8f7b0888 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -368,6 +368,8 @@ void ApiListener::Stop(bool runtimeDeleted) m_Timer->Stop(true); m_RenewOwnCertTimer->Stop(true); + StopListener(); + m_WaitGroup->Join(); ObjectImpl::Stop(runtimeDeleted); @@ -486,13 +488,38 @@ bool ApiListener::AddListener(const String& node, const String& service) Log(LogInformation, "ApiListener") << "Started new listener on '[" << localEndpoint.address() << "]:" << localEndpoint.port() << "'"; - IoEngine::SpawnCoroutine(io, [this, acceptor](asio::yield_context yc) { ListenerCoroutineProc(yc, acceptor); }); + auto strand = Shared::Make(io); + + boost::signals2::scoped_connection closeSignal = m_OnListenerShutdown.connect([strand, acceptor]() { + boost::asio::post(*strand, [acceptor] { + try { + acceptor->close(); + } catch (const std::exception& ex) { + Log(LogCritical, "ApiListener") + << "Failed to close acceptor socket: " << ex.what(); + } + }); + }); + + IoEngine::SpawnCoroutine(*strand, [this, acceptor, closeSignal = std::move(closeSignal)](asio::yield_context yc) { + ListenerCoroutineProc(yc, acceptor); + }); UpdateStatusFile(localEndpoint); return true; } +/** + * Stops the listener(s). + */ +void ApiListener::StopListener() +{ + m_OnListenerShutdown(); + + m_ListenerWaitGroup->Join(); +} + void ApiListener::ListenerCoroutineProc(boost::asio::yield_context yc, const Shared::Ptr& server) { namespace asio = boost::asio; @@ -506,7 +533,14 @@ void ApiListener::ListenerCoroutineProc(boost::asio::yield_context yc, const Sha lastModified = Utility::GetFileCreationTime(crlPath); } - for (;;) { + std::shared_lock wgLock(*m_ListenerWaitGroup, std::try_to_lock); + if (!wgLock) { + Log(LogCritical, "ApiListener") + << "Could not lock the listener wait group."; + return; + } + + while (server->is_open()) { try { asio::ip::tcp::socket socket (io); @@ -546,6 +580,12 @@ void ApiListener::ListenerCoroutineProc(boost::asio::yield_context yc, const Sha NewClientHandler(yc, strand, sslConn, String(), RoleServer); }); } catch (const std::exception& ex) { + auto se (dynamic_cast(&ex)); + + if (se && se->code() == boost::asio::error::operation_aborted) { + return; + } + Log(LogCritical, "ApiListener") << "Cannot accept new connection: " << ex.what(); } @@ -828,6 +868,11 @@ void ApiListener::NewClientHandlerInternal( throw; } + std::shared_lock wgLock(*m_ListenerWaitGroup, std::try_to_lock); + if (!wgLock) { + return; + } + if (ctype == ClientJsonRpc) { Log(LogNotice, "ApiListener", "New JSON-RPC client"); diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index 4fef5ba7d..82137fa32 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -191,12 +191,16 @@ private: static ApiListener::Ptr m_Instance; static std::atomic m_UpdatedObjectAuthority; + boost::signals2::signal m_OnListenerShutdown; + StoppableWaitGroup::Ptr m_ListenerWaitGroup = new StoppableWaitGroup(); + void ApiTimerHandler(); void ApiReconnectTimerHandler(); void CleanupCertificateRequestsTimerHandler(); void CheckApiPackageIntegrity(); bool AddListener(const String& node, const String& service); + void StopListener(); void AddConnection(const Endpoint::Ptr& endpoint); void NewClientHandler(