diff --git a/include/xrpl/telemetry/SpanGuard.h b/include/xrpl/telemetry/SpanGuard.h index ea8d0f9569..ea8a121a0e 100644 --- a/include/xrpl/telemetry/SpanGuard.h +++ b/include/xrpl/telemetry/SpanGuard.h @@ -164,6 +164,7 @@ #include #include #include +#include #include namespace xrpl::telemetry { @@ -426,6 +427,43 @@ public: operator bool() const; }; +// --- Detach-in-place helpers ----------------------------------------------- + +/** + * Detach an active `std::optional` member in place. + * + * Equivalent to `guard.emplace(std::move(*guard).detached())`, which is the + * required idiom for detaching a live guard stored in an `optional` (move + * assignment is deleted because the underlying Scope cannot be re-scoped in + * place). No-op if `guard` is empty or already null. + * + * @param guard The optional guard to detach. Must be called on the thread + * that constructed the active guard inside it (same rule as + * `SpanGuard::detached()`). + * @note Must run BEFORE any `captureContext()` snapshot is taken if the + * caller also needs the pre-detach context — capture first, then call + * this helper (same ordering rule `detached()` itself has). + */ +void +detachInPlace(std::optional& guard); + +/** + * Detach an active `std::shared_ptr`, returning the detached + * guard as a new `shared_ptr`. + * + * Equivalent to + * `guard = std::make_shared(std::move(*guard).detached())`. + * No-op (returns the input unchanged) if `guard` is null or points at a + * null guard. + * + * @param guard The guard to detach, taken by value (the caller's pointer + * is consumed; assign the return value back). + * @return A `shared_ptr` to the detached guard (new allocation), or the + * input pointer unchanged if it was null/inactive. + */ +[[nodiscard]] std::shared_ptr +detachInPlace(std::shared_ptr guard); + // --------------------------------------------------------------------------- // No-op stub (all inline, zero overhead, no OTel dependency) // --------------------------------------------------------------------------- @@ -539,6 +577,19 @@ public: } }; +// --- Detach-in-place helpers (no-op stubs) --------------------------------- + +inline void +detachInPlace(std::optional&) +{ +} + +[[nodiscard]] inline std::shared_ptr +detachInPlace(std::shared_ptr guard) +{ + return guard; +} + #endif // XRPL_ENABLE_TELEMETRY } // namespace xrpl::telemetry diff --git a/src/libxrpl/telemetry/SpanGuard.cpp b/src/libxrpl/telemetry/SpanGuard.cpp index 308e373374..b0437b8e17 100644 --- a/src/libxrpl/telemetry/SpanGuard.cpp +++ b/src/libxrpl/telemetry/SpanGuard.cpp @@ -321,6 +321,24 @@ SpanGuard::detached() && return SpanGuard(std::make_unique(std::move(s), Impl::Detached{})); } +// ===== Detach-in-place helpers ============================================= + +void +detachInPlace(std::optional& guard) +{ + if (!guard || !*guard) + return; + guard.emplace(std::move(*guard).detached()); +} + +std::shared_ptr +detachInPlace(std::shared_ptr guard) +{ + if (!guard || !*guard) + return guard; + return std::make_shared(std::move(*guard).detached()); +} + // ===== Context capture ===================================================== SpanContext