diff options
Diffstat (limited to 'library/cpp')
| -rw-r--r-- | library/cpp/tld/tlds-alpha-by-domain.txt | 2 | ||||
| -rw-r--r-- | library/cpp/yt/memory/exact_ref_counted_cast-inl.h | 46 | ||||
| -rw-r--r-- | library/cpp/yt/memory/exact_ref_counted_cast.h | 36 | ||||
| -rw-r--r-- | library/cpp/yt/memory/ref-inl.h | 5 | ||||
| -rw-r--r-- | library/cpp/yt/memory/ref.h | 3 | ||||
| -rw-r--r-- | library/cpp/yt/memory/unittests/exact_ref_counted_cast_ut.cpp | 102 | ||||
| -rw-r--r-- | library/cpp/yt/memory/unittests/ya.make | 1 | ||||
| -rw-r--r-- | library/cpp/yt/memory/ya.make | 6 | ||||
| -rw-r--r-- | library/cpp/yt/rseq/rseq.cpp | 8 | ||||
| -rw-r--r-- | library/cpp/yt/rseq/rseq.h | 20 | ||||
| -rw-r--r-- | library/cpp/yt/rseq/unittests/per_cpu_ut.cpp | 7 |
11 files changed, 226 insertions, 10 deletions
diff --git a/library/cpp/tld/tlds-alpha-by-domain.txt b/library/cpp/tld/tlds-alpha-by-domain.txt index 91c361781e3..d14bb7480c9 100644 --- a/library/cpp/tld/tlds-alpha-by-domain.txt +++ b/library/cpp/tld/tlds-alpha-by-domain.txt @@ -1,4 +1,4 @@ -# Version 2026061800, Last Updated Thu Jun 18 07:07:01 2026 UTC +# Version 2026062302, Last Updated Wed Jun 24 07:07:01 2026 UTC AAA AARP ABB diff --git a/library/cpp/yt/memory/exact_ref_counted_cast-inl.h b/library/cpp/yt/memory/exact_ref_counted_cast-inl.h new file mode 100644 index 00000000000..71163e26e0a --- /dev/null +++ b/library/cpp/yt/memory/exact_ref_counted_cast-inl.h @@ -0,0 +1,46 @@ +#ifndef EXACT_REF_COUNTED_CAST_INL_H_ +#error "Direct inclusion of this file is not allowed, include exact_ref_counted_cast.h" +// For the sake of sane code completion. +#include "exact_ref_counted_cast.h" +#endif + +#include "new.h" + +#include <typeinfo> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +template <class T, class U> +std::conditional_t<std::is_const_v<U>, const T, T>* ExactRefCountedCast(U* ptr) noexcept +{ + static_assert(std::is_polymorphic_v<U>, + "ExactRefCountedCast requires a polymorphic operand type"); + static_assert(std::is_base_of_v<TRefCountedBase, T>, + "ExactRefCountedCast target must derive from TRefCountedBase"); + static_assert(std::is_final_v<TRefCountedWrapper<T>>, + "TRefCountedWrapper must stay final for this cast to remain O(1)"); + + using TWrapper = std::conditional_t<std::is_const_v<U>, const TRefCountedWrapper<T>, TRefCountedWrapper<T>>; + // Wrapper is final, so dynamic_cast lowers to one type_info compare. + return dynamic_cast<TWrapper*>(ptr); +} + +template <class T, class U> +std::conditional_t<std::is_const_v<U>, const T, T>& ExactRefCountedCast(U& ref) +{ + static_assert(std::is_polymorphic_v<U>, + "ExactRefCountedCast requires a polymorphic operand type"); + static_assert(std::is_base_of_v<TRefCountedBase, T>, + "ExactRefCountedCast target must derive from TRefCountedBase"); + static_assert(std::is_final_v<TRefCountedWrapper<T>>, + "TRefCountedWrapper must stay final for this cast to remain O(1)"); + + using TWrapper = std::conditional_t<std::is_const_v<U>, const TRefCountedWrapper<T>, TRefCountedWrapper<T>>; + return dynamic_cast<TWrapper&>(ref); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/memory/exact_ref_counted_cast.h b/library/cpp/yt/memory/exact_ref_counted_cast.h new file mode 100644 index 00000000000..3b186103f3a --- /dev/null +++ b/library/cpp/yt/memory/exact_ref_counted_cast.h @@ -0,0 +1,36 @@ +#pragma once + +#include "ref_counted.h" + +#include <type_traits> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +//! A downcast that succeeds iff the object was allocated as *exactly* |New<T>()| +//! (not a subclass, not some other allocation path). +/*! + * |New<T>()| does not construct a |T| but a final |TRefCountedWrapper<T>| + * deriving from |T|, so |T| is never a leaf and |dynamic_cast<T*>| takes the + * slow is-a path. This casts to the wrapper instead and upcasts back to |T*|; + * since the wrapper is final, dynamic_cast lowers to one type_info compare -- + * O(1), as cheap on a miss as on a hit. + * + * Pointer form returns |nullptr| on a miss (or null operand); reference form + * throws |std::bad_cast|. Constness is preserved. |T| must derive from + * |TRefCountedBase| and |U| must be polymorphic. Requires RTTI. + */ +template <class T, class U> +[[nodiscard]] std::conditional_t<std::is_const_v<U>, const T, T>* ExactRefCountedCast(U* ptr) noexcept; + +template <class T, class U> +[[nodiscard]] std::conditional_t<std::is_const_v<U>, const T, T>& ExactRefCountedCast(U& ref); + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT + +#define EXACT_REF_COUNTED_CAST_INL_H_ +#include "exact_ref_counted_cast-inl.h" +#undef EXACT_REF_COUNTED_CAST_INL_H_ diff --git a/library/cpp/yt/memory/ref-inl.h b/library/cpp/yt/memory/ref-inl.h index 632a98f130d..5c6dc11e0a2 100644 --- a/library/cpp/yt/memory/ref-inl.h +++ b/library/cpp/yt/memory/ref-inl.h @@ -65,6 +65,11 @@ Y_FORCE_INLINE TRef TRef::Slice(size_t startOffset, size_t endOffset) const return TRef(Begin() + startOffset, endOffset - startOffset); } +Y_FORCE_INLINE bool TRef::Contains(TRef other) const +{ + return other.Begin() >= Begin() && other.End() <= End(); +} + //////////////////////////////////////////////////////////////////////////////// Y_FORCE_INLINE TMutableRef::TMutableRef(void* data, size_t size) diff --git a/library/cpp/yt/memory/ref.h b/library/cpp/yt/memory/ref.h index a6c6c4f2260..1fc451f50aa 100644 --- a/library/cpp/yt/memory/ref.h +++ b/library/cpp/yt/memory/ref.h @@ -54,6 +54,9 @@ public: //! Creates a TRef for a part of existing range. TRef Slice(size_t startOffset, size_t endOffset) const; + //! Returns |true| if #other's range lies entirely within this range. + bool Contains(TRef other) const; + //! Compares the content for bitwise equality. static bool AreBitwiseEqual(TRef lhs, TRef rhs); }; diff --git a/library/cpp/yt/memory/unittests/exact_ref_counted_cast_ut.cpp b/library/cpp/yt/memory/unittests/exact_ref_counted_cast_ut.cpp new file mode 100644 index 00000000000..75cce328dc0 --- /dev/null +++ b/library/cpp/yt/memory/unittests/exact_ref_counted_cast_ut.cpp @@ -0,0 +1,102 @@ +#include <library/cpp/testing/gtest/gtest.h> + +#include <library/cpp/yt/memory/exact_ref_counted_cast.h> +#include <library/cpp/yt/memory/new.h> +#include <library/cpp/yt/memory/ref_counted.h> + +#include <type_traits> +#include <typeinfo> + +namespace NYT { +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +struct TBase + : public TRefCounted +{ }; + +struct TDerived + : public TBase +{ + int Tag = 42; +}; + +struct TMoreDerived + : public TDerived +{ }; + +//////////////////////////////////////////////////////////////////////////////// +// Pointer form. + +TEST(TExactRefCountedCastTest, ExactMatch) +{ + auto object = New<TDerived>(); + TBase* base = object.Get(); + EXPECT_EQ(ExactRefCountedCast<TDerived>(base), object.Get()); + EXPECT_EQ(ExactRefCountedCast<TDerived>(base)->Tag, 42); +} + +TEST(TExactRefCountedCastTest, SubclassDoesNotMatch) +{ + auto object = New<TMoreDerived>(); + TBase* base = object.Get(); + // Object was New<TMoreDerived>(); an exact probe for TDerived must miss. + EXPECT_EQ(ExactRefCountedCast<TDerived>(base), nullptr); + // ...but the exact type matches. + EXPECT_EQ(ExactRefCountedCast<TMoreDerived>(base), object.Get()); +} + +TEST(TExactRefCountedCastTest, BaseDoesNotMatch) +{ + auto object = New<TDerived>(); + TBase* base = object.Get(); + // It is exactly a TDerived, not a TBase. + EXPECT_EQ(ExactRefCountedCast<TBase>(base), nullptr); +} + +TEST(TExactRefCountedCastTest, Null) +{ + TBase* base = nullptr; + EXPECT_EQ(ExactRefCountedCast<TDerived>(base), nullptr); +} + +TEST(TExactRefCountedCastTest, ConstnessPreserved) +{ + auto object = New<TDerived>(); + const TBase* base = object.Get(); + auto* result = ExactRefCountedCast<TDerived>(base); + static_assert(std::is_same_v<decltype(result), const TDerived*>); + EXPECT_EQ(result, object.Get()); +} + +//////////////////////////////////////////////////////////////////////////////// +// Reference form. + +TEST(TExactRefCountedCastTest, RefExactMatch) +{ + auto object = New<TDerived>(); + TBase& base = *object; + EXPECT_EQ(&ExactRefCountedCast<TDerived>(base), object.Get()); +} + +TEST(TExactRefCountedCastTest, RefSubclassThrows) +{ + auto object = New<TMoreDerived>(); + TBase& base = *object; + EXPECT_THROW((void)ExactRefCountedCast<TDerived>(base), std::bad_cast); + EXPECT_EQ(&ExactRefCountedCast<TMoreDerived>(base), object.Get()); +} + +TEST(TExactRefCountedCastTest, RefConstnessPreserved) +{ + auto object = New<TDerived>(); + const TBase& base = *object; + static_assert(std::is_same_v<decltype(ExactRefCountedCast<TDerived>(base)), const TDerived&>); + EXPECT_EQ(&ExactRefCountedCast<TDerived>(base), object.Get()); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace +} // namespace NYT diff --git a/library/cpp/yt/memory/unittests/ya.make b/library/cpp/yt/memory/unittests/ya.make index c3abcbefbe5..c545bc0d41e 100644 --- a/library/cpp/yt/memory/unittests/ya.make +++ b/library/cpp/yt/memory/unittests/ya.make @@ -9,6 +9,7 @@ SRCS( chunked_memory_pool_allocator_ut.cpp chunked_memory_pool_output_ut.cpp erased_storage_ut.cpp + exact_ref_counted_cast_ut.cpp free_list_ut.cpp function_view_ut.cpp intrusive_ptr_ut.cpp diff --git a/library/cpp/yt/memory/ya.make b/library/cpp/yt/memory/ya.make index 37b05048b76..de4cc766061 100644 --- a/library/cpp/yt/memory/ya.make +++ b/library/cpp/yt/memory/ya.make @@ -47,6 +47,12 @@ CHECK_DEPENDENT_DIRS( END() +IF (NOT OPENSOURCE) + RECURSE( + benchmark + ) +ENDIF() + RECURSE_FOR_TESTS( unittests ) diff --git a/library/cpp/yt/rseq/rseq.cpp b/library/cpp/yt/rseq/rseq.cpp index f8321cbb0bf..12b5a291d8d 100644 --- a/library/cpp/yt/rseq/rseq.cpp +++ b/library/cpp/yt/rseq/rseq.cpp @@ -133,7 +133,7 @@ YT_STATIC_INITIALIZER({ // the static TLS block, incl. tcmalloc) rather than a dlopen'd module's dynamically allocated // TLS, where the offset is valid only on the thread that computed it. Compares addresses // without dereferencing the suspect offset, so it is safe even when the offset is bogus. See -// IsPerCpuFastPathSafe. +// IsPerCpuFastPathSupported. YT_PREVENT_TLS_CACHING bool ValidateFastPathOnFreshThread() { if (OwnsRegistration) { @@ -177,7 +177,7 @@ YT_PREVENT_TLS_CACHING bool EnsureCurrentThreadRegistered() return ReadField<int>(CpuIdFieldOffset) >= 0; } -bool IsPerCpuFastPathSafe() +bool IsPerCpuFastPathSupported() { // Decided once, lazily, on a freshly spawned thread (the check is meaningful only off the // thread that computed the offset) and cached -- cost is one thread spawn at first use. @@ -205,14 +205,12 @@ bool IsPerCpuFastPathSafe() #else // YT_RSEQ_AVAILABLE -#include "per_cpu.h" - namespace NYT::NRseq { //////////////////////////////////////////////////////////////////////////////// // No rseq fast path on this platform; hot sensors use the atomic fallback. -bool IsPerCpuFastPathSafe() +bool IsPerCpuFastPathSupported() { return false; } diff --git a/library/cpp/yt/rseq/rseq.h b/library/cpp/yt/rseq/rseq.h index cfea5101c29..b821e34047a 100644 --- a/library/cpp/yt/rseq/rseq.h +++ b/library/cpp/yt/rseq/rseq.h @@ -6,6 +6,24 @@ #define YT_RSEQ_AVAILABLE #endif +namespace NYT::NRseq { + +//////////////////////////////////////////////////////////////////////////////// + +//! Returns whether the per-CPU rseq fast path is safe to use in this process. +/*! + * The fast path reads the rseq area at a thread-pointer offset cached at startup, which is + * sound only when __rseq_abi sits at a fixed offset from the thread pointer (a glibc-owned + * area or the static TLS block, incl. tcmalloc) -- not when it lands in a dlopen'd module's + * dynamically allocated TLS. Returns false there (and where there is no fast path) so callers + * fall back to atomics. Decided once on a spawned thread and cached: one spawn at first use. + */ +bool IsPerCpuFastPathSupported(); + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NRseq + #ifdef YT_RSEQ_AVAILABLE #include <cstddef> @@ -16,7 +34,7 @@ namespace NYT::NRseq { //! Byte offset from the thread pointer to the rseq area's cpu_id field (glibc's area when //! glibc registers rseq, otherwise our own). A fixed offset across threads only when the area -//! is glibc-owned or in the static TLS block; #IsPerCpuFastPathSafe probes this and gates the +//! is glibc-owned or in the static TLS block; #IsPerCpuFastPathSupported probes this and gates the //! fast path. NB: 0 until our startup initializer runs, but nothing reads rseq before then. extern std::ptrdiff_t CpuIdFieldOffset; diff --git a/library/cpp/yt/rseq/unittests/per_cpu_ut.cpp b/library/cpp/yt/rseq/unittests/per_cpu_ut.cpp index f742c8589de..27588aee75e 100644 --- a/library/cpp/yt/rseq/unittests/per_cpu_ut.cpp +++ b/library/cpp/yt/rseq/unittests/per_cpu_ut.cpp @@ -1,6 +1,7 @@ #include <library/cpp/testing/gtest/gtest.h> #include <library/cpp/yt/rseq/per_cpu.h> +#include <library/cpp/yt/rseq/rseq.h> #include <library/cpp/yt/memory/public.h> @@ -58,12 +59,12 @@ TEST(TPerCpuRseqTest, CpuCountIsSane) EXPECT_LE(GetCpuCount(), 1 << 20); } -TEST(TPerCpuRseqTest, FastPathSafetyIsStable) +TEST(TPerCpuRseqTest, FastPathSupportIsStable) { // The probe spawns a thread on first use and caches its verdict, so repeated calls must // agree. We avoid asserting a specific value: it depends on kernel rseq support. - bool safe = IsPerCpuFastPathSafe(); - EXPECT_EQ(safe, IsPerCpuFastPathSafe()); + bool supported = IsPerCpuFastPathSupported(); + EXPECT_EQ(supported, IsPerCpuFastPathSupported()); } TEST(TPerCpuRseqTest, ParsePossibleCpuCount) |
