Browse Source

Use AnyObject to remove type erasure logic from yt/yt/core
commit_hash:79b0a0b437e41879c3a7d4c079ab571bbaafd45f

arkady-e1ppa 2 months ago
parent
commit
174f410da8

+ 2 - 2
library/cpp/yt/memory/type_erasure.h

@@ -218,7 +218,7 @@ public:
             auto* mover = GetVTable().template GetFunctor<Mover<TStorage>>();
             auto* mover = GetVTable().template GetFunctor<Mover<TStorage>>();
             mover(std::move(other).GetStorage(), GetStorage());
             mover(std::move(other).GetStorage(), GetStorage());
 
 
-            other.Holder_.Reset();
+            other.Reset();
         }
         }
     }
     }
 
 
@@ -236,7 +236,7 @@ public:
             auto* mover = GetVTable().template GetFunctor<Mover<TStorage>>();
             auto* mover = GetVTable().template GetFunctor<Mover<TStorage>>();
             mover(std::move(other).GetStorage(), GetStorage());
             mover(std::move(other).GetStorage(), GetStorage());
 
 
-            other.Holder_.Reset();
+            other.Reset();
         }
         }
 
 
         return *this;
         return *this;

+ 11 - 3
library/cpp/yt/memory/type_erasure_detail.h

@@ -368,12 +368,20 @@ private:
 
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
 
+template <class T, class TStorage>
+struct TIsVTable
+    : public std::false_type
+{ };
+
+template <CStorage TStorage, class... TCpos>
+struct TIsVTable<TVTable<TStorage, TCpos...>, TStorage>
+    : public std::true_type
+{ };
+
 template <class T, class TStorage>
 template <class T, class TStorage>
 concept CVTableFor =
 concept CVTableFor =
     CStorage<TStorage> &&
     CStorage<TStorage> &&
-    requires (const T& t) {
-        [] <class... TCpos> (TVTable<TStorage, TCpos...>) { } (t);
-    };
+    TIsVTable<std::remove_cvref_t<T>, TStorage>::value;
 
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
 

+ 6 - 2
library/cpp/yt/memory/unittests/type_erasure_ut.cpp

@@ -292,7 +292,10 @@ TEST(TAnyObjectTest, CvRefCorrectness)
         EXPECT_EQ(TestCpo(movedOut).Val, 11);
         EXPECT_EQ(TestCpo(movedOut).Val, 11);
     }
     }
 
 
-    EXPECT_EQ(cust.DtorCount, 5);
+    // NB(arkady-e1ppa): Moved out any should be
+    // actually empty thus moving out both moves object out
+    // and destroys the moved out object.
+    EXPECT_EQ(cust.DtorCount, 6);
     EXPECT_THROW(any.AnyCast<TCustomized2>(), NDetail::TBadAnyCast);
     EXPECT_THROW(any.AnyCast<TCustomized2>(), NDetail::TBadAnyCast);
 }
 }
 
 
@@ -316,7 +319,8 @@ TEST(TAnyObjectTest, StaticVTableForAnyRef)
         EXPECT_EQ(TestCpo(movedOut).Val, 11);
         EXPECT_EQ(TestCpo(movedOut).Val, 11);
     }
     }
     EXPECT_EQ(cst.Value, 1111);
     EXPECT_EQ(cst.Value, 1111);
-    EXPECT_EQ(cst.DtorCount, 2);
+    // NB(arkady-e1ppa): See comment in previous test.
+    EXPECT_EQ(cst.DtorCount, 3);
     EXPECT_FALSE(any.IsValid());
     EXPECT_FALSE(any.IsValid());
 }
 }
 
 

+ 2 - 2
yt/yt/core/actions/cancelable_context.cpp

@@ -26,7 +26,7 @@ public:
     {
     {
         YT_ASSERT(callback);
         YT_ASSERT(callback);
 
 
-        auto guard = NDetail::MakeCancelableContextCurrentTokenGuard(Context_);
+        auto currentTokenGuard = NDetail::MakeCancelableContextCurrentTokenGuard(Context_);
 
 
         if (Context_->Canceled_) {
         if (Context_->Canceled_) {
             callback.Reset();
             callback.Reset();
@@ -53,7 +53,7 @@ public:
 
 
     void Invoke(TMutableRange<TClosure> callbacks) override
     void Invoke(TMutableRange<TClosure> callbacks) override
     {
     {
-        auto guard = NDetail::MakeCancelableContextCurrentTokenGuard(Context_);
+        auto currentTokenGuard = NDetail::MakeCancelableContextCurrentTokenGuard(Context_);
 
 
         std::vector<TClosure> capturedCallbacks;
         std::vector<TClosure> capturedCallbacks;
         capturedCallbacks.reserve(callbacks.size());
         capturedCallbacks.reserve(callbacks.size());

+ 0 - 142
yt/yt/core/actions/cancelation_token-inl.h

@@ -1,142 +0,0 @@
-#ifndef CANCELATION_TOKEN_INL_H_
-#error "Direct inclusion of this file is not allowed, include cancelation_token.h"
-// For the sake of sane code completion.
-#include "cancelation_token.h"
-#endif
-
-namespace NYT::NDetail {
-
-////////////////////////////////////////////////////////////////////////////////
-
-template <class TDecayedConcrete>
-constexpr TDecayedConcrete& TAnyCancelationToken::TStorage::AsConcrete() & noexcept
-{
-    using TPtr = TDecayedConcrete*;
-
-    if constexpr (SmallToken<TDecayedConcrete>) {
-        return *std::launder(reinterpret_cast<TPtr>(&Storage_));
-    } else {
-        return *static_cast<TPtr>(*std::launder(reinterpret_cast<void**>(&Storage_)));
-    }
-}
-
-template <class TDecayedConcrete>
-constexpr const TDecayedConcrete& TAnyCancelationToken::TStorage::AsConcrete() const & noexcept
-{
-    using TPtr = const TDecayedConcrete*;
-
-    if constexpr (SmallToken<TDecayedConcrete>) {
-        return *std::launder(reinterpret_cast<TPtr>(&Storage_));
-    } else {
-        return *static_cast<TPtr>(*std::launder(reinterpret_cast<void const* const*>(&Storage_)));
-    }
-}
-
-template <class TDecayedConcrete>
-constexpr TDecayedConcrete&& TAnyCancelationToken::TStorage::AsConcrete() && noexcept
-{
-    using TPtr = TDecayedConcrete*;
-
-    if constexpr (SmallToken<TDecayedConcrete>) {
-        return std::move(*std::launder(reinterpret_cast<TPtr>(&Storage_)));
-    } else {
-        return std::move(*static_cast<TPtr>(*std::launder(reinterpret_cast<void**>(&Storage_))));
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-template <CCancelationToken TDecayedConcrete>
-TAnyCancelationToken::TVTable TAnyCancelationToken::TVTable::Create() noexcept
-{
-    TVTable table = {};
-    table.Dtor_ = +[] (TStorage& what) {
-        TAlloc allocator = {};
-
-        auto* ptr = &what.template AsConcrete<TDecayedConcrete>();
-        std::destroy_at(ptr);
-
-        if constexpr (!SmallToken<TDecayedConcrete>) {
-            TTraits::deallocate(allocator, reinterpret_cast<std::byte*>(ptr), sizeof(TDecayedConcrete));
-        }
-    };
-
-    table.CopyCtor_ = +[] (TStorage& where, const TStorage& what) -> void {
-        TAlloc allocator = {};
-
-        if constexpr (SmallToken<TDecayedConcrete>) {
-            where.Set();
-        } else {
-            where.Set(TTraits::allocate(allocator, sizeof(TDecayedConcrete)));
-        }
-
-        TTraits::template construct<TDecayedConcrete>(
-            allocator,
-            &where.template AsConcrete<TDecayedConcrete>(),
-            what.template AsConcrete<TDecayedConcrete>());
-    };
-
-    table.MoveCtor_ = +[] (TStorage& where, TStorage&& what) -> void {
-        if constexpr (SmallToken<TDecayedConcrete>) {
-            TAlloc allocator = {};
-
-            where.Set();
-
-            TTraits::template construct<TDecayedConcrete>(
-                allocator,
-                &where.template AsConcrete<TDecayedConcrete>(),
-                std::move(what).template AsConcrete<TDecayedConcrete>());
-            TTraits::template destroy<TDecayedConcrete>(
-                allocator,
-                &what.template AsConcrete<TDecayedConcrete>());
-        } else {
-            where.Set(static_cast<void*>(&what));
-        }
-    };
-
-    table.IsCancelationRequested_ = +[] (const TStorage& what) -> bool {
-        return what.template AsConcrete<TDecayedConcrete>().IsCancelationRequested();
-    };
-
-    table.CancellationError_ = +[] (const TStorage& what) -> const TError& {
-        return what.template AsConcrete<TDecayedConcrete>().GetCancelationError();
-    };
-
-    return table;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-template <class TToken>
-    requires (!std::same_as<TAnyCancelationToken, std::remove_cvref_t<TToken>> &&
-                CCancelationToken<std::remove_cvref_t<TToken>>)
-TAnyCancelationToken::TAnyCancelationToken(TToken&& token)
-{
-    Set<TToken>(std::forward<TToken>(token));
-}
-
-template <class TToken>
-void TAnyCancelationToken::Set(TToken&& token)
-{
-    using TDecayed = std::remove_cvref_t<TToken>;
-    TAlloc allocator = {};
-
-    Reset();
-
-    VTable_ = &StaticTable<TDecayed>;
-
-    if constexpr (SmallToken<TDecayed>) {
-        Storage_.Set();
-    } else {
-        Storage_.Set(TTraits::allocate(allocator, sizeof(TDecayed)));
-    }
-
-    TTraits::template construct<TDecayed>(
-        allocator,
-        &Storage_.template AsConcrete<TDecayed>(),
-        std::forward<TToken>(token));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT::NDetail

+ 45 - 146
yt/yt/core/actions/cancelation_token.cpp

@@ -9,150 +9,20 @@ namespace NYT::NDetail {
 
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
 
-void TAnyCancelationToken::TStorage::Set(void* ptr) noexcept
+struct TNullToken
 {
 {
-    std::construct_at<void*>(reinterpret_cast<void**>(Storage_), ptr);
-}
-
-void TAnyCancelationToken::TStorage::Set() noexcept
-{
-    std::construct_at(Storage_);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-TAnyCancelationToken::TVTable::TDtor TAnyCancelationToken::TVTable::Dtor() const noexcept
-{
-    return Dtor_;
-}
-
-TAnyCancelationToken::TVTable::TCopyCtor TAnyCancelationToken::TVTable::CopyCtor() const noexcept
-{
-    return CopyCtor_;
-}
-
-TAnyCancelationToken::TVTable::TMoveCtor TAnyCancelationToken::TVTable::MoveCtor() const noexcept
-{
-    return MoveCtor_;
-}
-
-TAnyCancelationToken::TVTable::TIsCancelationRequested TAnyCancelationToken::TVTable::IsCancelationRequested() const noexcept
-{
-    return IsCancelationRequested_;
-}
-
-TAnyCancelationToken::TVTable::TCancellationError TAnyCancelationToken::TVTable::GetCancelationError() const noexcept
-{
-    return CancellationError_;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-TAnyCancelationToken::TAnyCancelationToken(TAnyCancelationToken&& other) noexcept
-    : VTable_(other.VTable_)
-{
-    if (VTable_) {
-        auto moveCtor = VTable_->MoveCtor();
-        moveCtor(Storage_, std::move(other.Storage_));
-
-        other.VTable_ = nullptr;
-    }
-}
-
-TAnyCancelationToken& TAnyCancelationToken::operator= (TAnyCancelationToken&& other) noexcept
-{
-    if (this == &other) {
-        return *this;
-    }
-
-    Reset();
-
-    VTable_ = other.VTable_;
-
-    if (VTable_) {
-        auto moveCtor = VTable_->MoveCtor();
-        moveCtor(Storage_, std::move(other.Storage_));
-
-        other.VTable_ = nullptr;
-    }
-
-    return *this;
-}
-
-TAnyCancelationToken::TAnyCancelationToken(const TAnyCancelationToken& other) noexcept
-    : VTable_(other.VTable_)
-{
-    if (VTable_) {
-        auto copyCtor = VTable_->CopyCtor();
-        copyCtor(Storage_, other.Storage_);
-    }
-}
-
-TAnyCancelationToken& TAnyCancelationToken::operator= (const TAnyCancelationToken& other) noexcept
-{
-    if (this == &other) {
-        return *this;
-    }
-
-    Reset();
-    VTable_ = other.VTable_;
-
-    if (VTable_) {
-        auto copyCtor = VTable_->CopyCtor();
-        copyCtor(Storage_, other.Storage_);
-    }
-
-    return *this;
-}
-
-TAnyCancelationToken::operator bool() const noexcept
-{
-    return VTable_ != nullptr;
-}
-
-TAnyCancelationToken::~TAnyCancelationToken()
-{
-    Reset();
-}
-
-bool TAnyCancelationToken::IsCancelationRequested() const noexcept
-{
-    if (!VTable_) {
+    friend bool TagInvoke(TTagInvokeTag<IsCancelationRequested>, const TNullToken&) noexcept
+    {
         return false;
         return false;
     }
     }
 
 
-    return VTable_->IsCancelationRequested()(Storage_);
-}
-
-const TError& TAnyCancelationToken::GetCancelationError() const noexcept
-{
-    YT_VERIFY(VTable_);
-    return VTable_->GetCancelationError()(Storage_);
-}
-
-void TAnyCancelationToken::Reset() noexcept
-{
-    if (VTable_) {
-        auto dtor = VTable_->Dtor();
-        dtor(Storage_);
-        VTable_ = nullptr;
+    friend const TError& TagInvoke(TTagInvokeTag<GetCancelationError>, const TNullToken&) noexcept
+    {
+        YT_ABORT();
     }
     }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-NConcurrency::TFlsSlot<TAnyCancelationToken> GlobalToken = {};
-
-////////////////////////////////////////////////////////////////////////////////
-
-TCurrentCancelationTokenGuard::TCurrentCancelationTokenGuard(TAnyCancelationToken nextToken)
-    : PrevToken_(std::exchange(*GlobalToken, std::move(nextToken)))
-{ }
+};
 
 
-TCurrentCancelationTokenGuard::~TCurrentCancelationTokenGuard()
-{
-    std::exchange(*GlobalToken, std::move(PrevToken_));
-}
+static_assert(CCancelationToken<TNullToken>);
 
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
 
@@ -160,14 +30,14 @@ struct TTokenForCancelableContext
 {
 {
     TCancelableContextPtr Context;
     TCancelableContextPtr Context;
 
 
-    bool IsCancelationRequested() const noexcept
+    friend bool TagInvoke(TTagInvokeTag<IsCancelationRequested>, const TTokenForCancelableContext& token) noexcept
     {
     {
-        return Context->IsCanceled();
+        return token.Context->IsCanceled();
     }
     }
 
 
-    const TError& GetCancelationError() const
+    friend const TError& TagInvoke(TTagInvokeTag<GetCancelationError>, const TTokenForCancelableContext& token)
     {
     {
-        return Context->GetCancelationError();
+        return token.Context->GetCancelationError();
     }
     }
 };
 };
 
 
@@ -183,14 +53,14 @@ struct TTokenForFuture
 
 
     TFutureState<void>* FutureState;
     TFutureState<void>* FutureState;
 
 
-    bool IsCancelationRequested() const noexcept
+    friend bool TagInvoke(TTagInvokeTag<IsCancelationRequested>, const TTokenForFuture& token) noexcept
     {
     {
-        return FutureState->IsCanceled();
+        return token.FutureState->IsCanceled();
     }
     }
 
 
-    const TError& GetCancelationError() const
+    friend const TError& TagInvoke(TTagInvokeTag<GetCancelationError>, const TTokenForFuture& token)
     {
     {
-        return FutureState->GetCancelationError();
+        return token.FutureState->GetCancelationError();
     }
     }
 };
 };
 
 
@@ -210,8 +80,37 @@ TCurrentCancelationTokenGuard MakeCancelableContextCurrentTokenGuard(const TCanc
 
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
 
+NConcurrency::TFlsSlot<TAnyCancelationToken> GlobalToken = {};
+
+namespace {
+
+void EnsureInitialized()
+{
+    [[unlikely]] if (!GlobalToken.IsInitialized()) {
+        *GlobalToken = TNullToken{};
+    }
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+TCurrentCancelationTokenGuard::TCurrentCancelationTokenGuard(TAnyCancelationToken nextToken)
+{
+    EnsureInitialized();
+    PrevToken_ = std::exchange(*GlobalToken, std::move(nextToken));
+}
+
+TCurrentCancelationTokenGuard::~TCurrentCancelationTokenGuard()
+{
+    std::exchange(*GlobalToken, std::move(PrevToken_));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 const TAnyCancelationToken& GetCurrentCancelationToken()
 const TAnyCancelationToken& GetCurrentCancelationToken()
 {
 {
+    EnsureInitialized();
     return *GlobalToken;
     return *GlobalToken;
 }
 }
 
 

+ 23 - 106
yt/yt/core/actions/cancelation_token.h

@@ -4,119 +4,40 @@
 
 
 #include <yt/yt/core/misc/error.h>
 #include <yt/yt/core/misc/error.h>
 
 
+#include <library/cpp/yt/memory/type_erasure.h>
+#include <library/cpp/yt/misc/tag_invoke_cpo.h>
+
 namespace NYT::NDetail {
 namespace NYT::NDetail {
 
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
 
+struct TIsCancelationRequestedFn
+    : public TTagInvokeCpoBase<TIsCancelationRequestedFn>
+{ };
+
+struct TGetCancelationErrorFn
+    : public TTagInvokeCpoBase<TGetCancelationErrorFn>
+{ };
+
+inline constexpr TIsCancelationRequestedFn IsCancelationRequested = {};
+inline constexpr TGetCancelationErrorFn GetCancelationError = {};
+
+////////////////////////////////////////////////////////////////////////////////
+
 // CancelToken is an entity which you can ask whether cancellation has been
 // CancelToken is an entity which you can ask whether cancellation has been
 // requested or not. If it has been requested, you can ask for cancelation error.
 // requested or not. If it has been requested, you can ask for cancelation error.
 template <class T>
 template <class T>
-concept CCancelationToken = requires (T& t) {
-    { t.IsCancelationRequested() } -> std::same_as<bool>;
-    { t.GetCancelationError() } -> std::same_as<const TError&>;
-} && std::copyable<T>;
+concept CCancelationToken =
+    std::copyable<T> &&
+    CTagInvocableS<TIsCancelationRequestedFn, bool(const T&) noexcept> &&
+    CTagInvocableS<TGetCancelationErrorFn, const TError&(const T&)>;
 
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
 
 // We need to read/write global variable which satisfies concept CCancelationToken.
 // We need to read/write global variable which satisfies concept CCancelationToken.
-class TAnyCancelationToken
-{
-private:
-    static constexpr size_t SmallTokenSize = sizeof(void*) * 2;
-    static constexpr size_t SmallTokenAlign = SmallTokenSize;
-
-    template <class T>
-    static constexpr bool SmallToken =
-        (sizeof(T) <= SmallTokenSize) &&
-        (alignof(T) <= SmallTokenAlign);
-
-    using TAlloc = std::allocator<std::byte>;
-    using TTraits = std::allocator_traits<TAlloc>;
-
-    class TStorage
-    {
-    public:
-        TStorage() = default;
-
-        /*constexpr in C++23*/ void Set(void* ptr) noexcept;
-        /*constexpr in C++23*/ void Set() noexcept;
-
-        template <class TDecayedConcrete>
-        constexpr TDecayedConcrete& AsConcrete() & noexcept;
-
-        template <class TDecayedConcrete>
-        constexpr const TDecayedConcrete& AsConcrete() const & noexcept;
-
-        template <class TDecayedConcrete>
-        constexpr TDecayedConcrete&& AsConcrete() && noexcept;
-
-    private:
-        alignas(SmallTokenAlign) std::byte Storage_[SmallTokenSize];
-    };
-
-    class TVTable
-    {
-    private:
-        using TDtor = void(*)(TStorage& what);
-        using TCopyCtor = void (*)(TStorage& where, const TStorage& what);
-        using TMoveCtor = void (*)(TStorage& where, TStorage&& what);
-        using TIsCancelationRequested = bool (*)(const TStorage& what);
-        using TCancellationError = const TError& (*)(const TStorage& what);
-
-    public:
-        template <CCancelationToken TDecayedConcrete>
-        static TVTable Create() noexcept;
-
-        TDtor Dtor() const noexcept;
-        TCopyCtor CopyCtor() const noexcept;
-        TMoveCtor MoveCtor() const noexcept;
-        TIsCancelationRequested IsCancelationRequested() const noexcept;
-        TCancellationError GetCancelationError() const noexcept;
-
-    private:
-        TDtor Dtor_ = nullptr;
-        TCopyCtor CopyCtor_ = nullptr;
-        TMoveCtor MoveCtor_ = nullptr;
-        TIsCancelationRequested IsCancelationRequested_ = nullptr;
-        TCancellationError CancellationError_ = nullptr;
-
-        TVTable() = default;
-    };
-
-    // Consider inline vtable storage.
-    template <CCancelationToken TToken>
-    static inline TVTable StaticTable = TVTable::template Create<TToken>();
-
-public:
-    TAnyCancelationToken() = default;
-
-    template <class TToken>
-        requires (!std::same_as<TAnyCancelationToken, std::remove_cvref_t<TToken>> &&
-                    CCancelationToken<std::remove_cvref_t<TToken>>)
-    TAnyCancelationToken(TToken&& token);
-
-    TAnyCancelationToken(TAnyCancelationToken&& other) noexcept;
-    TAnyCancelationToken& operator= (TAnyCancelationToken&& other) noexcept;
-
-    TAnyCancelationToken(const TAnyCancelationToken& other) noexcept;
-    TAnyCancelationToken& operator= (const TAnyCancelationToken& other) noexcept;
-
-    explicit operator bool() const noexcept;
-
-    ~TAnyCancelationToken();
-
-    bool IsCancelationRequested() const noexcept;
-    const TError& GetCancelationError() const noexcept;
-
-private:
-    TStorage Storage_ = {};
-    TVTable* VTable_ = nullptr;
-
-    void Reset() noexcept;
-
-    template <class TToken>
-    void Set(TToken&& token);
-};
+using TAnyCancelationToken = ::NYT::TAnyObject<
+    TOverload<IsCancelationRequested, bool(const TErasedThis&) noexcept>,
+    TOverload<GetCancelationError, const TError&(const TErasedThis&)>>;
 
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
 
@@ -145,7 +66,3 @@ const TAnyCancelationToken& GetCurrentCancelationToken();
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
 
 } // namespace NYT::NDetail
 } // namespace NYT::NDetail
-
-#define CANCELATION_TOKEN_INL_H_
-#include "cancelation_token-inl.h"
-#undef CANCELATION_TOKEN_INL_H_

+ 2 - 2
yt/yt/core/actions/future-inl.h

@@ -52,13 +52,13 @@ inline TError TryExtractCancelationError()
 {
 {
     const auto& currentToken = GetCurrentCancelationToken();
     const auto& currentToken = GetCurrentCancelationToken();
 
 
-    if (currentToken.IsCancelationRequested()) {
+    if (IsCancelationRequested(currentToken)) {
         // NB(arkady-e1ppa): This clown fiesta is present because some external
         // NB(arkady-e1ppa): This clown fiesta is present because some external
         // users managed to both hardcode "Promise abandoned" error message
         // users managed to both hardcode "Promise abandoned" error message
         // as the retriable one (or expected in tests) and
         // as the retriable one (or expected in tests) and
         // rely on their cancelation error to never be wrapped
         // rely on their cancelation error to never be wrapped
         // into anything with a different error code.
         // into anything with a different error code.
-        const auto& tokenError = currentToken.GetCancelationError();
+        const auto& tokenError = GetCancelationError(currentToken);
         return TError(tokenError.GetCode(), "Promise abandoned") << tokenError;
         return TError(tokenError.GetCode(), "Promise abandoned") << tokenError;
     }
     }
 
 

+ 13 - 12
yt/yt/core/actions/unittests/cancelation_token_ut.cpp

@@ -9,14 +9,14 @@ namespace {
 
 
 struct TSimpleToken
 struct TSimpleToken
 {
 {
-    bool IsCancelationRequested() const noexcept
+    friend bool TagInvoke(TTagInvokeTag<IsCancelationRequested>, const TSimpleToken& token) noexcept
     {
     {
-        return !Error.IsOK();
+        return !token.Error.IsOK();
     }
     }
 
 
-    const TError& GetCancelationError() const noexcept
+    friend const TError& TagInvoke(TTagInvokeTag<GetCancelationError>, const TSimpleToken& token)
     {
     {
-        return Error;
+        return token.Error;
     }
     }
 
 
     TError Error;
     TError Error;
@@ -72,7 +72,7 @@ TEST(TAnyTokenTest, JustWorks)
 {
 {
     ResetCounters();
     ResetCounters();
     TAnyCancelationToken any{TSimpleToken{}};
     TAnyCancelationToken any{TSimpleToken{}};
-    EXPECT_FALSE(any.IsCancelationRequested());
+    EXPECT_FALSE(IsCancelationRequested(any));
 }
 }
 
 
 TEST(TAnyTokenTest, Copy)
 TEST(TAnyTokenTest, Copy)
@@ -85,26 +85,27 @@ TEST(TAnyTokenTest, Copy)
     EXPECT_EQ(TSimpleToken::CopyCount, 1);
     EXPECT_EQ(TSimpleToken::CopyCount, 1);
     EXPECT_EQ(TSimpleToken::MoveCount, 0);
     EXPECT_EQ(TSimpleToken::MoveCount, 0);
 
 
-    EXPECT_TRUE(any.IsCancelationRequested());
+    EXPECT_TRUE(IsCancelationRequested(any));
 
 
     token.Error = TError{};
     token.Error = TError{};
 
 
     TAnyCancelationToken any1{};
     TAnyCancelationToken any1{};
 
 
-    // NB: Implicit move ctor.
+    // NB: Implicit copy ctor and then move assign.
     any1 = token;
     any1 = token;
+
     EXPECT_EQ(TSimpleToken::CtorCount, 0);
     EXPECT_EQ(TSimpleToken::CtorCount, 0);
     EXPECT_EQ(TSimpleToken::CopyCount, 2);
     EXPECT_EQ(TSimpleToken::CopyCount, 2);
     EXPECT_EQ(TSimpleToken::MoveCount, 1);
     EXPECT_EQ(TSimpleToken::MoveCount, 1);
     EXPECT_EQ(TSimpleToken::DtorCount, 1);
     EXPECT_EQ(TSimpleToken::DtorCount, 1);
-    EXPECT_FALSE(any1.IsCancelationRequested());
+    EXPECT_FALSE(IsCancelationRequested(any1));
 
 
     any1 = any;
     any1 = any;
     EXPECT_EQ(TSimpleToken::CtorCount, 0);
     EXPECT_EQ(TSimpleToken::CtorCount, 0);
     EXPECT_EQ(TSimpleToken::CopyCount, 3);
     EXPECT_EQ(TSimpleToken::CopyCount, 3);
     EXPECT_EQ(TSimpleToken::MoveCount, 1);
     EXPECT_EQ(TSimpleToken::MoveCount, 1);
     EXPECT_EQ(TSimpleToken::DtorCount, 2);
     EXPECT_EQ(TSimpleToken::DtorCount, 2);
-    EXPECT_TRUE(any1.IsCancelationRequested());
+    EXPECT_TRUE(IsCancelationRequested(any1));
 }
 }
 
 
 TEST(TAnyTokenTest, MoveSmallToken)
 TEST(TAnyTokenTest, MoveSmallToken)
@@ -117,7 +118,7 @@ TEST(TAnyTokenTest, MoveSmallToken)
     EXPECT_EQ(TSimpleToken::CopyCount, 0);
     EXPECT_EQ(TSimpleToken::CopyCount, 0);
     EXPECT_EQ(TSimpleToken::MoveCount, 1);
     EXPECT_EQ(TSimpleToken::MoveCount, 1);
 
 
-    EXPECT_TRUE(any.IsCancelationRequested());
+    EXPECT_TRUE(IsCancelationRequested(any));
 
 
     token.Error = TError{};
     token.Error = TError{};
 
 
@@ -132,14 +133,14 @@ TEST(TAnyTokenTest, MoveSmallToken)
     EXPECT_EQ(TSimpleToken::CopyCount, 0);
     EXPECT_EQ(TSimpleToken::CopyCount, 0);
     EXPECT_EQ(TSimpleToken::MoveCount, 3);
     EXPECT_EQ(TSimpleToken::MoveCount, 3);
     EXPECT_EQ(TSimpleToken::DtorCount, 1);
     EXPECT_EQ(TSimpleToken::DtorCount, 1);
-    EXPECT_FALSE(any1.IsCancelationRequested());
+    EXPECT_FALSE(IsCancelationRequested(any1));
 
 
     any1 = std::move(any);
     any1 = std::move(any);
     EXPECT_EQ(TSimpleToken::CtorCount, 0);
     EXPECT_EQ(TSimpleToken::CtorCount, 0);
     EXPECT_EQ(TSimpleToken::CopyCount, 0);
     EXPECT_EQ(TSimpleToken::CopyCount, 0);
     EXPECT_EQ(TSimpleToken::MoveCount, 4);
     EXPECT_EQ(TSimpleToken::MoveCount, 4);
     EXPECT_EQ(TSimpleToken::DtorCount, 3);
     EXPECT_EQ(TSimpleToken::DtorCount, 3);
-    EXPECT_TRUE(any1.IsCancelationRequested());
+    EXPECT_TRUE(IsCancelationRequested(any1));
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////