aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/yt/misc/function_view.h
diff options
context:
space:
mode:
authorarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-04-05 15:42:36 +0300
committerarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-04-05 15:51:06 +0300
commitbeb1725856691d2bd11a0e2ec8ce7b45cebb9804 (patch)
tree36347f8f8b8c3797fcab9747115ab53ed668b358 /library/cpp/yt/misc/function_view.h
parent431eb68d0e52caf69928b4581df3555d18e706b3 (diff)
downloadydb-beb1725856691d2bd11a0e2ec8ce7b45cebb9804.tar.gz
YT-21402: Fibers Refactoring pt.1: Introduce FunctionView to use it as AfterSwitch and improved registry algorithm
1) Added FunctionView -- non-owning type-erasure container. If we know that lambda lifetime is long enough, we can save up allocation by using this instead of TCallback. 2) Used FunctionView as AfterSwitch inside FiberSchedulerThread. We saved up a bunch of allocations (e.g. net worst-case allocations per suspend changed from 4 (x2 after switch + fiber allocation + enqueue to idle pool lf stack) to 2 (fiber allocation + enqueue to idle pool lf stack). 3) Fiber is not longer RefCounted. Its lifetime is managed via contract with TFiberRegistry. 4) TFiberRegistry is now lock-free for fiber insertion and deletion. For introspector it is still blocking. 5) "Introduced" SimpleIntrusiveList and IntrusiveMPSCStack to work be used in aforementioned TFiberRegistry. 6) elsedef branch of YT_REUSE_FIBERS was broken for about 3 years cause of double SetAfterSwitch. Now fixed. 7) (3), (4) and (5) caused some changes in yt_fiber_printers because some stuff was hardcoded there. Compat is in place. d6cf2ae5801c87813a21ca3e7243e1b2baa09f35
Diffstat (limited to 'library/cpp/yt/misc/function_view.h')
-rw-r--r--library/cpp/yt/misc/function_view.h139
1 files changed, 139 insertions, 0 deletions
diff --git a/library/cpp/yt/misc/function_view.h b/library/cpp/yt/misc/function_view.h
new file mode 100644
index 0000000000..259238521f
--- /dev/null
+++ b/library/cpp/yt/misc/function_view.h
@@ -0,0 +1,139 @@
+#pragma once
+
+#include <concepts>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace NDetail {
+
+template <class TSignature>
+struct TTypeErasureTraits;
+
+template <class TResult, bool NoExcept, class... TArgs>
+struct TTypeErasureTraits<TResult(TArgs...) noexcept(NoExcept)>
+{
+ using TSignature = TResult(TArgs...) noexcept(NoExcept);
+
+ // TODO(arkady-e1ppa): Support pointer-to-member-function?
+ template <class T>
+ static constexpr bool IsInvocable = NoExcept
+ ? requires (T obj, TArgs... args) {
+ { obj(std::forward<TArgs>(args)...) } noexcept -> std::same_as<TResult>;
+ }
+ : requires (T obj, TArgs... args) {
+ { obj(std::forward<TArgs>(args)...) } -> std::same_as<TResult>;
+ };
+};
+
+} // namespace NDetail
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Non-owning type-erasure container.
+/*
+ Example:
+
+ template <class T>
+ class TSerializedObject
+ {
+ public:
+ explicit TSerializedObject(T value)
+ : Object_(value)
+ { }
+
+ void Lock(TFunctionView<void(const T&)> callback)
+ {
+ auto guard = Guard(SpinLock_);
+ callback(Object_);
+ }
+
+ private:
+ TSpinLock SpinLock_;
+ T Object_;
+ };
+
+ int main()
+ {
+ TSerializedObject<int> object(42);
+
+ // object.Lock([] (const int& value) {
+ // fmt::println("Value is {}", value);
+ // });
+ // ^ CE -- cannot pass rvalue.
+
+ auto callback = [] (const int& value) {
+ fmt::println("Value is {}", value);
+ };
+
+ object.Lock(callback); // <- prints "Value is 42".
+ }
+*/
+template <class TSignature>
+class TFunctionView;
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T, class TSignature>
+concept CTypeErasable =
+ NDetail::TTypeErasureTraits<TSignature>::template IsInvocable<T> &&
+ (!std::same_as<T, TFunctionView<TSignature>>);
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class TResult, bool NoExcept, class... TArgs>
+class TFunctionView<TResult(TArgs...) noexcept(NoExcept)>
+{
+public:
+ using TSignature = TResult(TArgs...) noexcept(NoExcept);
+
+ TFunctionView() = default;
+
+ template <CTypeErasable<TSignature> TConcrete>
+ TFunctionView(TConcrete& concreteRef) noexcept;
+
+ template <CTypeErasable<TSignature> TConcrete>
+ TFunctionView(TConcrete* concretePtr) noexcept;
+
+ TResult operator()(TArgs... args) noexcept(NoExcept);
+
+ explicit operator bool() const noexcept;
+
+ TFunctionView Release() noexcept;
+
+ bool IsValid() const noexcept;
+ void Reset() noexcept;
+
+ // bool operator==(const TFunctionView& other) const & = default;
+
+private:
+ // NB: Technically, this is UB according to C standard, which
+ // was not changed for C++ standard.
+ // This is so because it is allowed to have
+ // function pointers to be modelled by entities
+ // different from object pointers.
+ // No reasonable system architecture (e.g. x86 or ARM
+ // or any other POSIX compliant one) does this.
+ // No reasonable compiler (clang/gcc) does anything with this.
+ // Accounting for such requirement would cause this class
+ // to have std::variant-like storage which would make this class
+ // weight more. Thus, we have decided to keep it this way,
+ // since we are safe on x86 or ARM + clang.
+ using TErasedPtr = void*;
+ using TErasedInvoke = TResult(*)(TArgs..., TErasedPtr);
+
+ TErasedPtr Ptr_ = nullptr;
+ TErasedInvoke Invoke_ = nullptr;
+
+ template <class TConcrete>
+ static TResult ConcreteInvoke(TArgs... args, TErasedPtr ptr) noexcept(NoExcept);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
+
+#define FUNCTION_VIEW_INL_H_
+#include "function_view-inl.h"
+#undef FUNCTION_VIEW_INL_H_