diff --git a/src/xrpld/rpc/detail/ServerHandler.cpp b/src/xrpld/rpc/detail/ServerHandler.cpp index 73acdcd27e..0723f2933a 100644 --- a/src/xrpld/rpc/detail/ServerHandler.cpp +++ b/src/xrpld/rpc/detail/ServerHandler.cpp @@ -287,7 +287,14 @@ ServerHandler::onRequest(Session& session) std::shared_ptr detachedSession = session.detach(); auto const postResult = m_jobQueue.postCoroTask( jtCLIENT_RPC, "RPC-Client", [this, detachedSession](auto) -> CoroTask { - processSession(detachedSession); + try + { + processSession(detachedSession); + } + catch (std::exception const& e) + { + JLOG(m_journal.error()) << "RPC-Client coroutine exception: " << e.what(); + } co_return; }); if (postResult == nullptr) @@ -328,13 +335,21 @@ ServerHandler::onWSMessage( jtCLIENT_WEBSOCKET, "WS-Client", [this, session, jv = std::move(jv)](auto) -> CoroTask { - auto const jr = this->processSession(session, jv); - auto const s = to_string(jr); - auto const n = s.length(); - boost::beast::multi_buffer sb(n); - sb.commit(boost::asio::buffer_copy(sb.prepare(n), boost::asio::buffer(s.c_str(), n))); - session->send(std::make_shared>(std::move(sb))); - session->complete(); + try + { + auto const jr = this->processSession(session, jv); + auto const s = to_string(jr); + auto const n = s.length(); + boost::beast::multi_buffer sb(n); + sb.commit( + boost::asio::buffer_copy(sb.prepare(n), boost::asio::buffer(s.c_str(), n))); + session->send(std::make_shared>(std::move(sb))); + session->complete(); + } + catch (std::exception const& e) + { + JLOG(m_journal.error()) << "WS-Client coroutine exception: " << e.what(); + } co_return; }); if (postResult == nullptr) diff --git a/src/xrpld/rpc/handlers/RipplePathFind.cpp b/src/xrpld/rpc/handlers/RipplePathFind.cpp index 610f6a0e93..20c6309fdb 100644 --- a/src/xrpld/rpc/handlers/RipplePathFind.cpp +++ b/src/xrpld/rpc/handlers/RipplePathFind.cpp @@ -45,6 +45,8 @@ doRipplePathFind(RPC::JsonContext& context) // until the path-finding continuation signals completion. // If makeLegacyPathRequest cannot schedule the job (e.g. during // shutdown), it returns an empty request and we skip the wait. + // Replaces the old Coro yield/resume pattern with synchronous + // blocking, eliminating shutdown race conditions. std::mutex mtx; std::condition_variable cv; bool pathDone = false; @@ -63,8 +65,15 @@ doRipplePathFind(RPC::JsonContext& context) context.params); if (request) { + using namespace std::chrono_literals; std::unique_lock lk(mtx); - cv.wait(lk, [&] { return pathDone; }); + if (!cv.wait_for(lk, 30s, [&] { return pathDone; })) + { + // Path-finding continuation never fired (e.g. shutdown + // race or unexpected failure). Return an internal error + // rather than blocking the RPC thread indefinitely. + return rpcError(rpcINTERNAL); + } jvResult = request->doStatus(context.params); }