aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp
diff options
context:
space:
mode:
authorarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-10-14 17:25:51 +0300
committerarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-10-14 17:40:51 +0300
commit3b92e669413cdc39137dbfd1d23417b23ef276ef (patch)
tree8e5709b2cbef24a7f0b42ad82574252896bb2dc7 /library/cpp
parentd74394815fdeebaee897571642e270f3d459557e (diff)
downloadydb-3b92e669413cdc39137dbfd1d23417b23ef276ef.tar.gz
Move atomic object to library/cpp/yt/threading
[nodiff:caesar] commit_hash:446e45e0378f6b2cb31d85bcc3e4516efbdfe5a7
Diffstat (limited to 'library/cpp')
-rw-r--r--library/cpp/yt/threading/atomic_object-inl.h97
-rw-r--r--library/cpp/yt/threading/atomic_object.h63
2 files changed, 160 insertions, 0 deletions
diff --git a/library/cpp/yt/threading/atomic_object-inl.h b/library/cpp/yt/threading/atomic_object-inl.h
new file mode 100644
index 00000000000..452f5a0bc77
--- /dev/null
+++ b/library/cpp/yt/threading/atomic_object-inl.h
@@ -0,0 +1,97 @@
+#ifndef ATOMIC_OBJECT_INL_H_
+#error "Direct inclusion of this file is not allowed, include atomic_object.h"
+// For the sake of sane code completion.
+#include "atomic_object.h"
+#endif
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+template <class U>
+TAtomicObject<T>::TAtomicObject(U&& u)
+ : Object_(std::forward<U>(u))
+{ }
+
+template <class T>
+template <class U>
+void TAtomicObject<T>::Store(U&& u)
+{
+ // NB: Using exchange to avoid destructing the old object while holding the lock.
+ std::ignore = Exchange(std::forward<U>(u));
+}
+
+template <class T>
+template <class U>
+T TAtomicObject<T>::Exchange(U&& u)
+{
+ T tmpObject = std::forward<U>(u);
+ {
+ auto guard = WriterGuard(Spinlock_);
+ std::swap(Object_, tmpObject);
+ }
+ return tmpObject;
+}
+
+template <class T>
+bool TAtomicObject<T>::CompareExchange(T& expected, const T& desired)
+{
+ auto guard = WriterGuard(Spinlock_);
+ if (Object_ == expected) {
+ auto oldObject = std::move(Object_);
+ Y_UNUSED(oldObject);
+ Object_ = desired;
+ guard.Release();
+ return true;
+ } else {
+ auto oldExpected = std::move(expected);
+ Y_UNUSED(oldExpected);
+ expected = Object_;
+ guard.Release();
+ return false;
+ }
+}
+
+template <class T>
+template <std::invocable<T&> F>
+std::invoke_result_t<F, T&> TAtomicObject<T>::Transform(const F& func)
+{
+ auto guard = WriterGuard(Spinlock_);
+ return func(Object_);
+}
+
+template <class T>
+template <std::invocable<const T&> F>
+std::invoke_result_t<F, const T&> TAtomicObject<T>::Read(const F& func) const
+{
+ auto guard = ReaderGuard(Spinlock_);
+ return func(Object_);
+}
+
+template <class T>
+T TAtomicObject<T>::Load() const
+{
+ auto guard = ReaderGuard(Spinlock_);
+ return Object_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class TOriginal, class TSerialized>
+void ToProto(TSerialized* serialized, const TAtomicObject<TOriginal>& original)
+{
+ ToProto(serialized, original.Load());
+}
+
+template <class TOriginal, class TSerialized>
+void FromProto(TAtomicObject<TOriginal>* original, const TSerialized& serialized)
+{
+ TOriginal data;
+ FromProto(&data, serialized);
+ original->Store(std::move(data));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/threading/atomic_object.h b/library/cpp/yt/threading/atomic_object.h
new file mode 100644
index 00000000000..e986f872a08
--- /dev/null
+++ b/library/cpp/yt/threading/atomic_object.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <library/cpp/yt/threading/rw_spin_lock.h>
+
+#include <concepts>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+//! A synchronization object to load and store nontrivial object.
+//! It looks like atomics but for objects.
+template <class T>
+class TAtomicObject
+{
+public:
+ TAtomicObject() = default;
+
+ template <class U>
+ TAtomicObject(U&& u);
+
+ template <class U>
+ void Store(U&& u);
+
+ //! Atomically replaces the old value with the new one and returns the old value.
+ template <class U>
+ T Exchange(U&& u);
+
+ //! Atomically checks if then current value equals #expected.
+ //! If so, replaces it with #desired and returns |true|.
+ //! Otherwise, copies it into #expected and returns |false|.
+ bool CompareExchange(T& expected, const T& desired);
+
+ //! Atomically transforms the value with function #func.
+ template <std::invocable<T&> F>
+ std::invoke_result_t<F, T&> Transform(const F& func);
+
+ //! Atomicaly reads the value with function #func.
+ template <std::invocable<const T&> F>
+ std::invoke_result_t<F, const T&> Read(const F& func) const;
+
+ T Load() const;
+
+private:
+ T Object_;
+ YT_DECLARE_SPIN_LOCK(NThreading::TReaderWriterSpinLock, Spinlock_);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class TOriginal, class TSerialized>
+void ToProto(TSerialized* serialized, const TAtomicObject<TOriginal>& original);
+
+template <class TOriginal, class TSerialized>
+void FromProto(TAtomicObject<TOriginal>* original, const TSerialized& serialized);
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
+
+#define ATOMIC_OBJECT_INL_H_
+#include "atomic_object-inl.h"
+#undef ATOMIC_OBJECT_INL_H_