summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbabenko <[email protected]>2026-04-12 13:31:27 +0300
committerbabenko <[email protected]>2026-04-12 14:03:33 +0300
commit2f678ac94fa0e9aeffde6130e9015786e7ea4ed3 (patch)
tree7355bafd4df46c0670182161ef2c25980436e764
parent1dd39b59f3963c032eba42bffb547d849e33502e (diff)
YT-27910: Make some more fields in TServiceContext atomic
commit_hash:24a87198c8fc53da983f9678679b990070e329b8
-rw-r--r--library/cpp/yt/containers/sentinel_optional-inl.h118
-rw-r--r--library/cpp/yt/containers/sentinel_optional.h114
-rw-r--r--library/cpp/yt/containers/unittests/sentinel_optional_ut.cpp291
-rw-r--r--library/cpp/yt/containers/unittests/ya.make1
-rw-r--r--yt/yt/core/rpc/service_detail.cpp90
5 files changed, 581 insertions, 33 deletions
diff --git a/library/cpp/yt/containers/sentinel_optional-inl.h b/library/cpp/yt/containers/sentinel_optional-inl.h
new file mode 100644
index 00000000000..c16f843f8a6
--- /dev/null
+++ b/library/cpp/yt/containers/sentinel_optional-inl.h
@@ -0,0 +1,118 @@
+#ifndef SENTINEL_OPTIONAL_INL_H_
+#error "Direct inclusion of this file is not allowed, include sentinel_optional.h"
+// For the sake of sane code completion.
+#include "sentinel_optional.h"
+#endif
+
+#include <library/cpp/yt/assert/assert.h>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T, class TSentinel>
+constexpr TSentinelOptional<T, TSentinel>::TSentinelOptional(std::nullopt_t) noexcept
+ : Value_(TSentinel::Sentinel)
+{ }
+
+template <class T, class TSentinel>
+constexpr TSentinelOptional<T, TSentinel>::TSentinelOptional(T value) noexcept
+ : Value_(value)
+{ }
+
+template <class T, class TSentinel>
+constexpr TSentinelOptional<T, TSentinel>::TSentinelOptional(std::optional<T> opt) noexcept
+ : Value_(opt.has_value() ? *opt : TSentinel::Sentinel)
+{ }
+
+template <class T, class TSentinel>
+constexpr TSentinelOptional<T, TSentinel>& TSentinelOptional<T, TSentinel>::operator=(std::nullopt_t) noexcept
+{
+ Value_ = TSentinel::Sentinel;
+ return *this;
+}
+
+template <class T, class TSentinel>
+constexpr TSentinelOptional<T, TSentinel>& TSentinelOptional<T, TSentinel>::operator=(T value) noexcept
+{
+ Value_ = value;
+ return *this;
+}
+
+template <class T, class TSentinel>
+constexpr bool TSentinelOptional<T, TSentinel>::has_value() const noexcept
+{
+ return Value_ != TSentinel::Sentinel;
+}
+
+template <class T, class TSentinel>
+constexpr TSentinelOptional<T, TSentinel>::operator bool() const noexcept
+{
+ return has_value();
+}
+
+template <class T, class TSentinel>
+constexpr T& TSentinelOptional<T, TSentinel>::operator*() noexcept
+{
+ return Value_;
+}
+
+template <class T, class TSentinel>
+constexpr const T& TSentinelOptional<T, TSentinel>::operator*() const noexcept
+{
+ return Value_;
+}
+
+template <class T, class TSentinel>
+constexpr T* TSentinelOptional<T, TSentinel>::operator->() noexcept
+{
+ return &Value_;
+}
+
+template <class T, class TSentinel>
+constexpr const T* TSentinelOptional<T, TSentinel>::operator->() const noexcept
+{
+ return &Value_;
+}
+
+template <class T, class TSentinel>
+T& TSentinelOptional<T, TSentinel>::value() noexcept
+{
+ YT_ASSERT(has_value());
+ return Value_;
+}
+
+template <class T, class TSentinel>
+const T& TSentinelOptional<T, TSentinel>::value() const noexcept
+{
+ YT_ASSERT(has_value());
+ return Value_;
+}
+
+template <class T, class TSentinel>
+constexpr T TSentinelOptional<T, TSentinel>::value_or(T default_value) const noexcept
+{
+ return has_value() ? Value_ : default_value;
+}
+
+template <class T, class TSentinel>
+constexpr void TSentinelOptional<T, TSentinel>::reset() noexcept
+{
+ Value_ = TSentinel::Sentinel;
+}
+
+template <class T, class TSentinel>
+constexpr TSentinelOptional<T, TSentinel>::operator std::optional<T>() const noexcept
+{
+ return has_value() ? std::optional<T>(Value_) : std::nullopt;
+}
+
+template <class T, class TSentinel>
+constexpr bool TSentinelOptional<T, TSentinel>::operator==(std::nullopt_t) const noexcept
+{
+ return !has_value();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/containers/sentinel_optional.h b/library/cpp/yt/containers/sentinel_optional.h
new file mode 100644
index 00000000000..1d573550db9
--- /dev/null
+++ b/library/cpp/yt/containers/sentinel_optional.h
@@ -0,0 +1,114 @@
+#pragma once
+
+#include <optional>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+//! Convenience sentinel traits for structural types (integers, enums, pointers).
+/*!
+ * Usage: |TSentinelOptional<int, TValueSentinel<-1>>|
+ */
+template <auto V>
+struct TValueSentinel
+{
+ static constexpr auto Sentinel = V;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+//! A compact alternative to |std::optional<T>| that uses a sentinel value
+//! to represent the null state rather than an additional boolean field.
+/*!
+ * The null state is represented by |TSentinel::Sentinel| of type |T|.
+ * The stored representation is exactly one |T|, so:
+ * sizeof(TSentinelOptional<T, TSentinel>) == sizeof(T)
+ * alignof(TSentinelOptional<T, TSentinel>) == alignof(T)
+ *
+ * Whenever |T| is trivially copyable the class is also trivially copyable,
+ * which guarantees that |std::atomic<TSentinelOptional<T, TSentinel>>| is lock-free
+ * if and only if |std::atomic<T>| is lock-free.
+ *
+ * The interface is a drop-in replacement for |std::optional<T>|.
+ *
+ * |TSentinel| must provide a |static constexpr T Sentinel| member.
+ * For structural types, use |TValueSentinel<V>| as a convenience.
+ */
+template <class T, class TSentinel>
+class TSentinelOptional
+{
+public:
+ using value_type = T;
+
+ //! Constructs a null optional (stores the sentinel value).
+ constexpr TSentinelOptional() = default;
+
+ //! Constructs a null optional.
+ constexpr TSentinelOptional(std::nullopt_t) noexcept;
+
+ //! Constructs an optional holding |value|.
+ /*!
+ * \note The behavior is undefined if |value == TSentinel::Sentinel|.
+ */
+ constexpr TSentinelOptional(T value) noexcept;
+
+ //! Converts from |std::optional<T>|; nullopt maps to the null state.
+ constexpr TSentinelOptional(std::optional<T> opt) noexcept;
+
+ constexpr TSentinelOptional(const TSentinelOptional&) = default;
+ constexpr TSentinelOptional(TSentinelOptional&&) = default;
+
+ constexpr TSentinelOptional& operator=(const TSentinelOptional&) = default;
+ constexpr TSentinelOptional& operator=(TSentinelOptional&&) = default;
+
+ //! Resets to null.
+ constexpr TSentinelOptional& operator=(std::nullopt_t) noexcept;
+
+ //! Assigns |value|.
+ /*!
+ * \note The behavior is undefined if |value == TSentinel::Sentinel|.
+ */
+ constexpr TSentinelOptional& operator=(T value) noexcept;
+
+ //! Returns |true| iff the optional holds a value (i.e., the stored value differs from the sentinel).
+ [[nodiscard]] constexpr bool has_value() const noexcept;
+
+ //! Returns |true| iff the optional holds a value.
+ constexpr explicit operator bool() const noexcept;
+
+ //! Returns a reference to the held value; behavior is undefined if null.
+ constexpr T& operator*() noexcept;
+ constexpr const T& operator*() const noexcept;
+
+ //! Returns a pointer to the held value; behavior is undefined if null.
+ constexpr T* operator->() noexcept;
+ constexpr const T* operator->() const noexcept;
+
+ //! Returns a reference to the held value; aborts if null.
+ T& value() noexcept;
+ const T& value() const noexcept;
+
+ //! Returns the held value if present, otherwise |default_value|.
+ constexpr T value_or(T default_value) const noexcept;
+
+ //! Resets to null (equivalent to assigning |std::nullopt|).
+ constexpr void reset() noexcept;
+
+ //! Converts to |std::optional<T>|; the null state maps to nullopt.
+ constexpr operator std::optional<T>() const noexcept;
+
+ constexpr bool operator==(const TSentinelOptional& other) const = default;
+ [[nodiscard]] constexpr bool operator==(std::nullopt_t) const noexcept;
+
+private:
+ T Value_ = TSentinel::Sentinel;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
+
+#define SENTINEL_OPTIONAL_INL_H_
+#include "sentinel_optional-inl.h"
+#undef SENTINEL_OPTIONAL_INL_H_
diff --git a/library/cpp/yt/containers/unittests/sentinel_optional_ut.cpp b/library/cpp/yt/containers/unittests/sentinel_optional_ut.cpp
new file mode 100644
index 00000000000..64ec5a04367
--- /dev/null
+++ b/library/cpp/yt/containers/unittests/sentinel_optional_ut.cpp
@@ -0,0 +1,291 @@
+#include <library/cpp/testing/gtest/gtest.h>
+
+#include <library/cpp/yt/containers/sentinel_optional.h>
+
+#include <atomic>
+#include <type_traits>
+
+namespace NYT {
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+
+using TIntOpt = TSentinelOptional<int, TValueSentinel<-1>>;
+
+////////////////////////////////////////////////////////////////////////////////
+
+TEST(TSentinelOptionalTest, DefaultConstructNull)
+{
+ TIntOpt opt;
+ EXPECT_FALSE(opt.has_value());
+ EXPECT_FALSE(static_cast<bool>(opt));
+ EXPECT_EQ(opt, std::nullopt);
+}
+
+TEST(TSentinelOptionalTest, NulloptConstruct)
+{
+ TIntOpt opt(std::nullopt);
+ EXPECT_FALSE(opt.has_value());
+ EXPECT_EQ(opt, std::nullopt);
+}
+
+TEST(TSentinelOptionalTest, ValueConstruct)
+{
+ TIntOpt opt(42);
+ EXPECT_TRUE(opt.has_value());
+ EXPECT_TRUE(static_cast<bool>(opt));
+ EXPECT_EQ(*opt, 42);
+}
+
+TEST(TSentinelOptionalTest, ZeroIsNotNull)
+{
+ TIntOpt opt(0);
+ EXPECT_TRUE(opt.has_value());
+ EXPECT_EQ(*opt, 0);
+}
+
+TEST(TSentinelOptionalTest, AssignValue)
+{
+ TIntOpt opt;
+ opt = 7;
+ EXPECT_TRUE(opt.has_value());
+ EXPECT_EQ(*opt, 7);
+}
+
+TEST(TSentinelOptionalTest, AssignNullopt)
+{
+ TIntOpt opt(7);
+ opt = std::nullopt;
+ EXPECT_FALSE(opt.has_value());
+}
+
+TEST(TSentinelOptionalTest, Reset)
+{
+ TIntOpt opt(7);
+ opt.reset();
+ EXPECT_FALSE(opt.has_value());
+ EXPECT_EQ(opt, std::nullopt);
+}
+
+TEST(TSentinelOptionalTest, Dereference)
+{
+ TIntOpt opt(99);
+ EXPECT_EQ(*opt, 99);
+ *opt = 100;
+ EXPECT_EQ(*opt, 100);
+}
+
+TEST(TSentinelOptionalTest, ArrowOperator)
+{
+ TSentinelOptional<int, TValueSentinel<0>> opt(42);
+ EXPECT_EQ(*opt.operator->(), 42);
+}
+
+TEST(TSentinelOptionalTest, ValueChecked)
+{
+ TIntOpt opt(5);
+ EXPECT_EQ(opt.value(), 5);
+ opt.value() = 10;
+ EXPECT_EQ(opt.value(), 10);
+}
+
+TEST(TSentinelOptionalTest, ValueOr)
+{
+ TIntOpt opt;
+ EXPECT_EQ(opt.value_or(42), 42);
+
+ opt = 7;
+ EXPECT_EQ(opt.value_or(42), 7);
+}
+
+TEST(TSentinelOptionalTest, EqualityBetweenOptionals)
+{
+ TIntOpt a;
+ TIntOpt b;
+ EXPECT_EQ(a, b);
+
+ a = 5;
+ EXPECT_NE(a, b);
+
+ b = 5;
+ EXPECT_EQ(a, b);
+
+ b = 6;
+ EXPECT_NE(a, b);
+}
+
+TEST(TSentinelOptionalTest, EqualityWithNullopt)
+{
+ TIntOpt opt;
+ EXPECT_EQ(opt, std::nullopt);
+ EXPECT_EQ(std::nullopt, opt);
+
+ opt = 1;
+ EXPECT_NE(opt, std::nullopt);
+ EXPECT_NE(std::nullopt, opt);
+}
+
+TEST(TSentinelOptionalTest, CopyConstruct)
+{
+ TIntOpt a(3);
+ TIntOpt b = a;
+ EXPECT_EQ(*b, 3);
+ *a = 4;
+ EXPECT_EQ(*b, 3); // b is independent
+}
+
+TEST(TSentinelOptionalTest, CopyAssign)
+{
+ TIntOpt a(3);
+ TIntOpt b;
+ b = a;
+ EXPECT_EQ(*b, 3);
+}
+
+TEST(TSentinelOptionalTest, ConstAccess)
+{
+ const TIntOpt opt(77);
+ EXPECT_EQ(*opt, 77);
+ EXPECT_EQ(opt.value(), 77);
+ EXPECT_EQ(*opt.operator->(), 77);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Pointer sentinel.
+
+using TPtrOpt = TSentinelOptional<int*, TValueSentinel<nullptr>>;
+
+TEST(TSentinelOptionalTest, PointerSentinel)
+{
+ TPtrOpt opt;
+ EXPECT_FALSE(opt.has_value());
+
+ int x = 42;
+ opt = &x;
+ EXPECT_TRUE(opt.has_value());
+ EXPECT_EQ(*opt, &x);
+ EXPECT_EQ(**opt, 42);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Enum sentinel.
+
+enum class EColor
+{
+ Red,
+ Green,
+ Blue,
+ Unknown,
+};
+
+using TColorOpt = TSentinelOptional<EColor, TValueSentinel<EColor::Unknown>>;
+
+TEST(TSentinelOptionalTest, EnumSentinel)
+{
+ TColorOpt opt;
+ EXPECT_FALSE(opt.has_value());
+
+ opt = EColor::Red;
+ EXPECT_TRUE(opt.has_value());
+ EXPECT_EQ(*opt, EColor::Red);
+
+ opt.reset();
+ EXPECT_FALSE(opt.has_value());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Layout and atomic guarantees.
+
+TEST(TSentinelOptionalTest, SizeEqualsUnderlyingType)
+{
+ static_assert(sizeof(TIntOpt) == sizeof(int));
+ static_assert(sizeof(TPtrOpt) == sizeof(int*));
+ static_assert(sizeof(TColorOpt) == sizeof(EColor));
+}
+
+TEST(TSentinelOptionalTest, TriviallyCopyable)
+{
+ static_assert(std::is_trivially_copyable_v<TIntOpt>);
+ static_assert(std::is_trivially_copyable_v<TPtrOpt>);
+ static_assert(std::is_trivially_copyable_v<TColorOpt>);
+}
+
+TEST(TSentinelOptionalTest, AtomicLockFree)
+{
+ // std::atomic<TSentinelOptional<T, S>> must be lock-free whenever
+ // std::atomic<T> is lock-free.
+ if (std::atomic<int>{}.is_lock_free()) {
+ EXPECT_TRUE((std::atomic<TIntOpt>{}.is_lock_free()));
+ }
+ if (std::atomic<int*>{}.is_lock_free()) {
+ EXPECT_TRUE((std::atomic<TPtrOpt>{}.is_lock_free()));
+ }
+ if (std::atomic<EColor>{}.is_lock_free()) {
+ EXPECT_TRUE((std::atomic<TColorOpt>{}.is_lock_free()));
+ }
+}
+
+TEST(TSentinelOptionalTest, AtomicStoreLoad)
+{
+ std::atomic<TIntOpt> a;
+ a.store(TIntOpt{});
+ EXPECT_FALSE(a.load().has_value());
+
+ a.store(TIntOpt{42});
+ EXPECT_EQ(*a.load(), 42);
+
+ a.store(std::nullopt);
+ EXPECT_FALSE(a.load().has_value());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// std::optional conversions.
+
+TEST(TSentinelOptionalTest, FromStdOptionalNull)
+{
+ TIntOpt opt = std::optional<int>{};
+ EXPECT_FALSE(opt.has_value());
+ EXPECT_EQ(opt, std::nullopt);
+}
+
+TEST(TSentinelOptionalTest, FromStdOptionalValue)
+{
+ TIntOpt opt = std::optional<int>{42};
+ EXPECT_TRUE(opt.has_value());
+ EXPECT_EQ(*opt, 42);
+}
+
+TEST(TSentinelOptionalTest, ToStdOptionalNull)
+{
+ TIntOpt opt;
+ std::optional<int> stdOpt = opt;
+ EXPECT_FALSE(stdOpt.has_value());
+}
+
+TEST(TSentinelOptionalTest, ToStdOptionalValue)
+{
+ TIntOpt opt(7);
+ std::optional<int> stdOpt = opt;
+ EXPECT_TRUE(stdOpt.has_value());
+ EXPECT_EQ(*stdOpt, 7);
+}
+
+TEST(TSentinelOptionalTest, RoundTripThroughStdOptional)
+{
+ TIntOpt original(99);
+ TIntOpt roundTripped = std::optional<int>(original);
+ EXPECT_TRUE(roundTripped.has_value());
+ EXPECT_EQ(*roundTripped, 99);
+}
+
+TEST(TSentinelOptionalTest, RoundTripNullThroughStdOptional)
+{
+ TIntOpt original;
+ TIntOpt roundTripped = std::optional<int>(original);
+ EXPECT_FALSE(roundTripped.has_value());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace
+} // namespace NYT
diff --git a/library/cpp/yt/containers/unittests/ya.make b/library/cpp/yt/containers/unittests/ya.make
index 232fa2e0e5b..aa5aac875c8 100644
--- a/library/cpp/yt/containers/unittests/ya.make
+++ b/library/cpp/yt/containers/unittests/ya.make
@@ -9,6 +9,7 @@ SRCS(
expiring_set_ut.cpp
non_empty_ut.cpp
ordered_hash_map_ut.cpp
+ sentinel_optional_ut.cpp
sharded_set_ut.cpp
)
diff --git a/yt/yt/core/rpc/service_detail.cpp b/yt/yt/core/rpc/service_detail.cpp
index 9a502c57314..f51de7ae1f1 100644
--- a/yt/yt/core/rpc/service_detail.cpp
+++ b/yt/yt/core/rpc/service_detail.cpp
@@ -7,7 +7,6 @@
#include "helpers.h"
#include "message.h"
#include "request_queue_provider.h"
-#include "response_keeper.h"
#include "server_detail.h"
#include "stream.h"
@@ -34,6 +33,8 @@
#include <library/cpp/yt/misc/tls.h>
+#include <library/cpp/yt/containers/sentinel_optional.h>
+
namespace NYT::NRpc {
using namespace NBus;
@@ -542,7 +543,7 @@ public:
AbortStreamsUnlessClosed(TError(NYT::EErrorCode::Canceled, "Request canceled"));
}
- CancelInstant_ = NProfiling::GetInstant();
+ CancelInstant_.store(NProfiling::GetInstant(), std::memory_order::release);
MethodPerformanceCounters_->CanceledRequestCounter.Increment();
}
@@ -587,15 +588,15 @@ public:
std::optional<TInstant> GetRunInstant() const override
{
- return RunInstant_;
+ return RunInstant_.load(std::memory_order::acquire);
}
std::optional<TInstant> GetFinishInstant() const override
{
- if (ReplyInstant_) {
- return ReplyInstant_;
- } else if (CancelInstant_) {
- return CancelInstant_;
+ if (auto replyInstant = ReplyInstant_.load(std::memory_order::acquire)) {
+ return replyInstant;
+ } else if (auto cancelInstant = CancelInstant_.load(std::memory_order::acquire)) {
+ return cancelInstant;
} else {
return std::nullopt;
}
@@ -603,19 +604,20 @@ public:
std::optional<TDuration> GetWaitDuration() const override
{
- return LocalWaitTime_;
+ return LocalWaitTime_.load(std::memory_order::acquire);
}
std::optional<TDuration> GetExecutionDuration() const override
{
- return ExecutionTime_;
+ return ExecutionTime_.load(std::memory_order::acquire);
}
void RecordThrottling(TDuration throttleDuration) override
{
- ThrottlingTime_ = ThrottlingTime_ + throttleDuration;
- if (ExecutionTime_) {
- *ExecutionTime_ -= throttleDuration;
+ ThrottlingTime_.store(ThrottlingTime_.load(std::memory_order::acquire) +
+ throttleDuration, std::memory_order::release);
+ if (auto executionTime = ExecutionTime_.load(std::memory_order::acquire)) {
+ ExecutionTime_.store(*executionTime - throttleDuration, std::memory_order::release);
}
}
@@ -738,15 +740,27 @@ private:
bool Cancelable_ = false;
TSingleShotCallbackList<void(const TError&)> CanceledList_;
+ struct TInstantSentinel
+ {
+ static constexpr auto Sentinel = TInstant::Zero();
+ };
+ using TSentinelOptionalInstant = TSentinelOptional<TInstant, TInstantSentinel>;
+
+ struct TDurationSentinel
+ {
+ static constexpr auto Sentinel = TDuration::Max();
+ };
+ using TSentinelOptionalDuration = TSentinelOptional<TDuration, TDurationSentinel>;
+
const TInstant ArriveInstant_;
- std::optional<TInstant> RunInstant_;
- std::optional<TInstant> ReplyInstant_;
- std::optional<TInstant> CancelInstant_;
- TDuration ThrottlingTime_;
+ std::atomic<TSentinelOptionalInstant> RunInstant_;
+ std::atomic<TSentinelOptionalInstant> ReplyInstant_;
+ std::atomic<TSentinelOptionalInstant> CancelInstant_;
+ std::atomic<TDuration> ThrottlingTime_;
- std::optional<TDuration> ExecutionTime_;
- std::optional<TDuration> TotalTime_;
- std::optional<TDuration> LocalWaitTime_;
+ std::atomic<TSentinelOptionalDuration> ExecutionTime_;
+ std::atomic<TSentinelOptionalDuration> TotalTime_;
+ std::atomic<TSentinelOptionalDuration> LocalWaitTime_;
std::atomic<bool> CompletedLatch_ = false;
std::atomic<bool> TimedOutLatch_ = false;
@@ -888,9 +902,13 @@ private:
void DoRun(const TLiteHandler& handler)
{
- RunInstant_ = NProfiling::GetInstant();
- LocalWaitTime_ = *RunInstant_ - ArriveInstant_;
- MethodPerformanceCounters_->LocalWaitTimeCounter.Record(*LocalWaitTime_);
+ auto runInstant = NProfiling::GetInstant();
+ auto localWaitTime = runInstant - ArriveInstant_;
+
+ RunInstant_.store(runInstant, std::memory_order::release);
+ LocalWaitTime_.store(localWaitTime, std::memory_order::release);
+
+ MethodPerformanceCounters_->LocalWaitTimeCounter.Record(localWaitTime);
try {
TCurrentTraceContextGuard guard(TraceContext_);
@@ -915,7 +933,8 @@ private:
}
if (auto timeout = GetTimeout()) {
- auto remainingTimeout = *timeout - (*RunInstant_ - ArriveInstant_);
+ auto runInstant = RunInstant_.load(std::memory_order::acquire);
+ auto remainingTimeout = *timeout - (*runInstant - ArriveInstant_);
if (remainingTimeout == TDuration::Zero()) {
if (!TimedOutLatch_.exchange(true)) {
Reply(TError(NYT::EErrorCode::Timeout, "Request dropped due to timeout before being run"));
@@ -1026,15 +1045,20 @@ private:
MethodPerformanceCounters_->TraceContextTimeCounter.Add(*traceContextTime);
}
- ReplyInstant_ = NProfiling::GetInstant();
- ExecutionTime_ = RunInstant_ ? *ReplyInstant_ - *RunInstant_ : TDuration();
- if (RunInstant_) {
- *ExecutionTime_ -= ThrottlingTime_;
+ auto replyInstant = NProfiling::GetInstant();
+ ReplyInstant_.store(replyInstant, std::memory_order::release);
+
+ TDuration executionTime;
+ if (auto runInstant = RunInstant_.load(std::memory_order::acquire)) {
+ executionTime = (replyInstant - *runInstant) - ThrottlingTime_.load(std::memory_order::acquire);
}
- TotalTime_ = *ReplyInstant_ - ArriveInstant_;
+ ExecutionTime_.store(executionTime, std::memory_order::release);
+
+ auto totalTime = replyInstant - ArriveInstant_;
+ TotalTime_.store(replyInstant - ArriveInstant_, std::memory_order::release);
- MethodPerformanceCounters_->ExecutionTimeCounter.Record(*ExecutionTime_);
- MethodPerformanceCounters_->TotalTimeCounter.Record(*TotalTime_);
+ MethodPerformanceCounters_->ExecutionTimeCounter.Record(executionTime);
+ MethodPerformanceCounters_->TotalTimeCounter.Record(totalTime);
if (!Error_.IsOK()) {
if (Service_->EnableErrorCodeCounter_.load()) {
const auto* counter = MethodPerformanceCounters_->ErrorCodeCounters.GetCounter(Error_.GetNonTrivialCode());
@@ -1070,7 +1094,7 @@ private:
? FromProto<TDuration>(RequestHeader_->logging_suppression_timeout())
: RuntimeInfo_->LoggingSuppressionTimeout.load(std::memory_order::relaxed);
- if (*TotalTime_ >= timeout) {
+ if (*TotalTime_.load(std::memory_order::acquire) >= timeout) {
return;
}
@@ -1173,8 +1197,8 @@ private:
}
delimitedBuilder->AppendFormat("ExecutionTime: %v, TotalTime: %v",
- *ExecutionTime_,
- *TotalTime_);
+ *ExecutionTime_.load(std::memory_order::acquire),
+ *TotalTime_.load(std::memory_order::acquire));
if (auto traceContextTime = GetTraceContextTime()) {
delimitedBuilder->AppendFormat("CpuTime: %v", traceContextTime);