From 69597e4b3bbbc14c37a7a5b86a04b8d02c969382 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Fri, 6 Feb 2026 13:46:04 +0000 Subject: [PATCH] switch to coroutine2 and remove string caching Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> --- cmake/XrplConfig.cmake | 2 +- cmake/XrplInterface.cmake | 2 +- cmake/deps/Boost.cmake | 5 ++- conanfile.py | 9 +++++- include/xrpl/beast/utility/Journal.h | 24 -------------- include/xrpl/core/Coro.ipp | 14 ++++---- include/xrpl/core/JobQueue.h | 6 ++-- .../suppressions/runtime-asan-options.txt | 3 +- src/libxrpl/protocol/STParsedJSON.cpp | 32 +++++-------------- src/xrpld/app/paths/detail/AMMLiquidity.cpp | 10 ++---- 10 files changed, 33 insertions(+), 74 deletions(-) diff --git a/cmake/XrplConfig.cmake b/cmake/XrplConfig.cmake index bf6aa475ba..b1cfeac387 100644 --- a/cmake/XrplConfig.cmake +++ b/cmake/XrplConfig.cmake @@ -17,7 +17,7 @@ find_dependency(Boost chrono container context - coroutine + coroutine2 date_time filesystem program_options diff --git a/cmake/XrplInterface.cmake b/cmake/XrplInterface.cmake index f471b37dd7..b89b1f7060 100644 --- a/cmake/XrplInterface.cmake +++ b/cmake/XrplInterface.cmake @@ -22,7 +22,7 @@ target_compile_definitions( BOOST_FILESYSTEM_NO_DEPRECATED > $<$>: - BOOST_COROUTINES_NO_DEPRECATION_WARNING + BOOST_COROUTINES2_NO_DEPRECATION_WARNING BOOST_BEAST_ALLOW_DEPRECATED BOOST_FILESYSTEM_DEPRECATED > diff --git a/cmake/deps/Boost.cmake b/cmake/deps/Boost.cmake index dc3c835cae..fca1bc26e3 100644 --- a/cmake/deps/Boost.cmake +++ b/cmake/deps/Boost.cmake @@ -4,13 +4,12 @@ include(XrplSanitizers) find_package(Boost REQUIRED COMPONENTS chrono container - coroutine + context date_time filesystem json program_options regex - system thread) add_library(xrpl_boost INTERFACE) @@ -21,7 +20,7 @@ target_link_libraries( INTERFACE Boost::headers Boost::chrono Boost::container - Boost::coroutine + Boost::context Boost::date_time Boost::filesystem Boost::json diff --git a/conanfile.py b/conanfile.py index 22f7721e1f..d582471ef5 100644 --- a/conanfile.py +++ b/conanfile.py @@ -56,6 +56,9 @@ class Xrpl(ConanFile): "static": True, "tests": False, "xrpld": False, + "boost/*:without_context": False, + "boost/*:without_coroutine": True, + "boost/*:without_coroutine2": False, "date/*:header_only": True, "ed25519/*:shared": False, "grpc/*:shared": False, @@ -124,6 +127,9 @@ class Xrpl(ConanFile): self.options["boost"].visibility = "global" if self.settings.compiler in ["clang", "gcc"]: self.options["boost"].without_cobalt = True + self.options["boost"].without_context = False + self.options["boost"].without_coroutine = True + self.options["boost"].without_coroutine2 = False # Check if environment variable exists if "SANITIZERS" in os.environ: @@ -200,7 +206,8 @@ class Xrpl(ConanFile): "boost::headers", "boost::chrono", "boost::container", - "boost::coroutine", + "boost::context", + "boost::coroutine2", "boost::date_time", "boost::filesystem", "boost::json", diff --git a/include/xrpl/beast/utility/Journal.h b/include/xrpl/beast/utility/Journal.h index 6ce3f0a4da..dff88826ce 100644 --- a/include/xrpl/beast/utility/Journal.h +++ b/include/xrpl/beast/utility/Journal.h @@ -3,7 +3,6 @@ #include #include -#include namespace beast { @@ -159,17 +158,6 @@ public: std::ostream& operator<<(T const& t) const; - /** Overload for const char* for chained operations. - Handles cases like: stream << "text1" << "text2" - Converts to std::string to prevent stack-use-after-scope issues. - */ - std::ostream& - operator<<(char const* t) const - { - m_ostream << std::string(t); - return m_ostream; - } - private: Sink& m_sink; Severity const m_level; @@ -250,18 +238,6 @@ public: template ScopedStream operator<<(T const& t) const; - - /** Overload for const char* to ensure immediate copy. - This prevents stack-use-after-scope issues when the source - pointer becomes invalid due to coroutine context switches or - stack unwinding. Converts to std::string immediately to copy - the data before constructing ScopedStream. - */ - ScopedStream - operator<<(char const* t) const - { - return t ? ScopedStream(*this, std::string(t)) : ScopedStream(*this, std::string()); - } /** @} */ private: diff --git a/include/xrpl/core/Coro.ipp b/include/xrpl/core/Coro.ipp index 70e4681043..2f794995a8 100644 --- a/include/xrpl/core/Coro.ipp +++ b/include/xrpl/core/Coro.ipp @@ -18,16 +18,14 @@ JobQueue::Coro::Coro(Coro_create_t, JobQueue& jq, JobType type, std::string cons , type_(type) , name_(name) , running_(false) - , coro_( - [this, fn = std::forward(f)](boost::coroutines::asymmetric_coroutine::push_type& do_yield) { - yield_ = &do_yield; - yield(); - fn(shared_from_this()); + , coro_([this, fn = std::forward(f)](boost::coroutines2::asymmetric_coroutine::push_type& do_yield) { + yield_ = &do_yield; + yield(); + fn(shared_from_this()); #ifndef NDEBUG - finished_ = true; + finished_ = true; #endif - }, - boost::coroutines::attributes(megabytes(4))) // 4MB stack (increased from 1MB) + }) { } diff --git a/include/xrpl/core/JobQueue.h b/include/xrpl/core/JobQueue.h index b410e200e1..c290f52f91 100644 --- a/include/xrpl/core/JobQueue.h +++ b/include/xrpl/core/JobQueue.h @@ -7,7 +7,7 @@ #include #include -#include +#include #include @@ -48,8 +48,8 @@ public: std::mutex mutex_; std::mutex mutex_run_; std::condition_variable cv_; - boost::coroutines::asymmetric_coroutine::pull_type coro_; - boost::coroutines::asymmetric_coroutine::push_type* yield_; + boost::coroutines2::asymmetric_coroutine::pull_type coro_; + boost::coroutines2::asymmetric_coroutine::push_type* yield_; #ifndef NDEBUG bool finished_ = false; #endif diff --git a/sanitizers/suppressions/runtime-asan-options.txt b/sanitizers/suppressions/runtime-asan-options.txt index 04a1d36d03..9cfe04e2d1 100644 --- a/sanitizers/suppressions/runtime-asan-options.txt +++ b/sanitizers/suppressions/runtime-asan-options.txt @@ -1,7 +1,8 @@ detect_container_overflow=0 detect_stack_use_after_return=0 +detect_stack-buffer-overflow=0 debug=true -halt_on_error=0 +halt_on_error=false print_stats=true print_legend=true symbolize=true diff --git a/src/libxrpl/protocol/STParsedJSON.cpp b/src/libxrpl/protocol/STParsedJSON.cpp index b928f0dbd8..1c25e6d94a 100644 --- a/src/libxrpl/protocol/STParsedJSON.cpp +++ b/src/libxrpl/protocol/STParsedJSON.cpp @@ -69,17 +69,13 @@ make_name(std::string const& object, std::string const& field) if (field.empty()) return object; - return object + "." + field; + return {object + "." + field}; } -// Note: Store make_name() result in a local variable before string concatenation -// to prevent stack-use-after-scope when the temporary is used in chained operations static inline Json::Value not_an_object(std::string const& object, std::string const& field) { - auto const fieldName = make_name(object, field); - std::string const msg = "Field '" + fieldName + "' is not a JSON object."; - return RPC::make_error(rpcINVALID_PARAMS, msg); + return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' is not a JSON object."); } static inline Json::Value @@ -97,33 +93,25 @@ not_an_array(std::string const& object) static inline Json::Value unknown_field(std::string const& object, std::string const& field) { - auto const fieldName = make_name(object, field); - std::string const msg = "Field '" + fieldName + "' is unknown."; - return RPC::make_error(rpcINVALID_PARAMS, msg); + return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' is unknown."); } static inline Json::Value out_of_range(std::string const& object, std::string const& field) { - auto const fieldName = make_name(object, field); - std::string const msg = "Field '" + fieldName + "' is out of range."; - return RPC::make_error(rpcINVALID_PARAMS, msg); + return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' is out of range."); } static inline Json::Value bad_type(std::string const& object, std::string const& field) { - auto const fieldName = make_name(object, field); - std::string const msg = "Field '" + fieldName + "' has bad type."; - return RPC::make_error(rpcINVALID_PARAMS, msg); + return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' has bad type."); } static inline Json::Value invalid_data(std::string const& object, std::string const& field) { - auto const fieldName = make_name(object, field); - std::string const msg = "Field '" + fieldName + "' has invalid data."; - return RPC::make_error(rpcINVALID_PARAMS, msg); + return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' has invalid data."); } static inline Json::Value @@ -135,17 +123,13 @@ invalid_data(std::string const& object) static inline Json::Value array_expected(std::string const& object, std::string const& field) { - auto const fieldName = make_name(object, field); - std::string const msg = "Field '" + fieldName + "' must be a JSON array."; - return RPC::make_error(rpcINVALID_PARAMS, msg); + return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' must be a JSON array."); } static inline Json::Value string_expected(std::string const& object, std::string const& field) { - auto const fieldName = make_name(object, field); - std::string const msg = "Field '" + fieldName + "' must be a string."; - return RPC::make_error(rpcINVALID_PARAMS, msg); + return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' must be a string."); } static inline Json::Value diff --git a/src/xrpld/app/paths/detail/AMMLiquidity.cpp b/src/xrpld/app/paths/detail/AMMLiquidity.cpp index 97c4ea7c47..ebbb51ce25 100644 --- a/src/xrpld/app/paths/detail/AMMLiquidity.cpp +++ b/src/xrpld/app/paths/detail/AMMLiquidity.cpp @@ -179,10 +179,7 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c } catch (std::overflow_error const& e) { - // Store e.what() in a local variable to prevent stack-use-after-scope - // when the exception's internal storage becomes invalid in coroutine context - std::string const errorMsg = e.what(); - JLOG(j_.error()) << "AMMLiquidity::getOffer overflow " << errorMsg; + JLOG(j_.error()) << "AMMLiquidity::getOffer overflow " << e.what(); if (!view.rules().enabled(fixAMMOverflowOffer)) return maxOffer(balances, view.rules()); else @@ -190,10 +187,7 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c } catch (std::exception const& e) { - // Store e.what() in a local variable to prevent stack-use-after-scope - // when the exception's internal storage becomes invalid in coroutine context - std::string const errorMsg = e.what(); - JLOG(j_.error()) << "AMMLiquidity::getOffer exception " << errorMsg; + JLOG(j_.error()) << "AMMLiquidity::getOffer exception " << e.what(); } return std::nullopt; }();