summaryrefslogtreecommitdiffstats
path: root/library/cpp
diff options
context:
space:
mode:
Diffstat (limited to 'library/cpp')
-rw-r--r--library/cpp/tld/tlds-alpha-by-domain.txt2
-rw-r--r--library/cpp/yt/memory/exact_ref_counted_cast-inl.h46
-rw-r--r--library/cpp/yt/memory/exact_ref_counted_cast.h36
-rw-r--r--library/cpp/yt/memory/ref-inl.h5
-rw-r--r--library/cpp/yt/memory/ref.h3
-rw-r--r--library/cpp/yt/memory/unittests/exact_ref_counted_cast_ut.cpp102
-rw-r--r--library/cpp/yt/memory/unittests/ya.make1
-rw-r--r--library/cpp/yt/memory/ya.make6
-rw-r--r--library/cpp/yt/rseq/rseq.cpp8
-rw-r--r--library/cpp/yt/rseq/rseq.h20
-rw-r--r--library/cpp/yt/rseq/unittests/per_cpu_ut.cpp7
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)