summaryrefslogtreecommitdiffstats
path: root/library/cpp
diff options
context:
space:
mode:
authorpavook <[email protected]>2025-09-26 13:21:26 +0300
committerpavook <[email protected]>2025-09-26 13:44:08 +0300
commit9239d3fa83520c4cc193a272ed9c62bc5eec3e6b (patch)
treea4a884dbfbbd2e91f35df4d953a43729b606313d /library/cpp
parenta9e79bf52fc76e605603b5c18079c2eca0ef11e9 (diff)
YT-26288: Revert fork lock to writer starving spinlock
`TForkAwareSpinLock` implementation takes `ForkLock_` reader, which can (and does) violate the non-reentrancy. commit_hash:6eb1092777ac21dd8303b938f855d0cd61276641
Diffstat (limited to 'library/cpp')
-rw-r--r--library/cpp/yt/threading/at_fork.cpp7
-rw-r--r--library/cpp/yt/threading/at_fork.h4
-rw-r--r--library/cpp/yt/threading/unittests/spin_lock_fork_ut.cpp3
-rw-r--r--library/cpp/yt/threading/writer_starving_rw_spin_lock-inl.h21
-rw-r--r--library/cpp/yt/threading/writer_starving_rw_spin_lock.cpp8
-rw-r--r--library/cpp/yt/threading/writer_starving_rw_spin_lock.h13
6 files changed, 51 insertions, 5 deletions
diff --git a/library/cpp/yt/threading/at_fork.cpp b/library/cpp/yt/threading/at_fork.cpp
index 2046918fcbf..bcf05f0c3b8 100644
--- a/library/cpp/yt/threading/at_fork.cpp
+++ b/library/cpp/yt/threading/at_fork.cpp
@@ -1,4 +1,5 @@
#include "at_fork.h"
+#include "writer_starving_rw_spin_lock.h"
#include <library/cpp/yt/memory/leaky_singleton.h>
@@ -37,7 +38,7 @@ public:
set.Initialized.store(true);
}
- TReaderWriterSpinLock* GetForkLock()
+ TWriterStarvingRWSpinLock* GetForkLock()
{
return &ForkLock_;
}
@@ -45,7 +46,7 @@ public:
private:
DECLARE_LEAKY_SINGLETON_FRIEND()
- TReaderWriterSpinLock ForkLock_;
+ TWriterStarvingRWSpinLock ForkLock_;
struct TAtForkHandlerSet
{
@@ -128,7 +129,7 @@ void RegisterAtForkHandlers(
std::move(child));
}
-TReaderWriterSpinLock* GetForkLock()
+TWriterStarvingRWSpinLock* GetForkLock()
{
return TAtForkManager::Get()->GetForkLock();
}
diff --git a/library/cpp/yt/threading/at_fork.h b/library/cpp/yt/threading/at_fork.h
index bb9a2f2ebd5..d95565f2b1c 100644
--- a/library/cpp/yt/threading/at_fork.h
+++ b/library/cpp/yt/threading/at_fork.h
@@ -1,6 +1,6 @@
#pragma once
-#include "rw_spin_lock.h"
+#include "writer_starving_rw_spin_lock.h"
#include <functional>
@@ -23,7 +23,7 @@ void RegisterAtForkHandlers(
TAtForkHandler child);
//! Returns the fork lock.
-TReaderWriterSpinLock* GetForkLock();
+TWriterStarvingRWSpinLock* GetForkLock();
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/threading/unittests/spin_lock_fork_ut.cpp b/library/cpp/yt/threading/unittests/spin_lock_fork_ut.cpp
index f7cc441379a..be85a4de565 100644
--- a/library/cpp/yt/threading/unittests/spin_lock_fork_ut.cpp
+++ b/library/cpp/yt/threading/unittests/spin_lock_fork_ut.cpp
@@ -101,6 +101,9 @@ TEST(TReaderWriterSpinLockTest, ForkFriendlyness)
TEST(TForkAwareSpinLockTest, ForkSafety)
{
+ // TODO(pavook): reenable.
+ GTEST_SKIP_("Writer processing guarantee not implemented yet - skipping the test");
+
std::atomic<bool> stopped = {false};
YT_DECLARE_SPIN_LOCK(TForkAwareSpinLock, lock);
diff --git a/library/cpp/yt/threading/writer_starving_rw_spin_lock-inl.h b/library/cpp/yt/threading/writer_starving_rw_spin_lock-inl.h
index 172c9ef9f55..b880135f244 100644
--- a/library/cpp/yt/threading/writer_starving_rw_spin_lock-inl.h
+++ b/library/cpp/yt/threading/writer_starving_rw_spin_lock-inl.h
@@ -20,6 +20,14 @@ inline void TWriterStarvingRWSpinLock::AcquireReader() noexcept
AcquireReaderSlow();
}
+inline void TWriterStarvingRWSpinLock::AcquireReaderForkFriendly() noexcept
+{
+ if (TryAcquireReaderForkFriendly()) {
+ return;
+ }
+ AcquireReaderForkFriendlySlow();
+}
+
inline void TWriterStarvingRWSpinLock::ReleaseReader() noexcept
{
auto prevValue = Value_.fetch_sub(ReaderDelta, std::memory_order::release);
@@ -77,6 +85,19 @@ inline bool TWriterStarvingRWSpinLock::TryAndTryAcquireReader() noexcept
return TryAcquireReader();
}
+inline bool TWriterStarvingRWSpinLock::TryAcquireReaderForkFriendly() noexcept
+{
+ auto oldValue = Value_.load(std::memory_order::relaxed);
+ if ((oldValue & WriterMask) != 0) {
+ return false;
+ }
+ auto newValue = oldValue + ReaderDelta;
+
+ bool acquired = Value_.compare_exchange_weak(oldValue, newValue, std::memory_order::acquire);
+ NDetail::MaybeRecordSpinLockAcquired(acquired);
+ return acquired;
+}
+
inline bool TWriterStarvingRWSpinLock::TryAcquireWriter() noexcept
{
auto expected = UnlockedValue;
diff --git a/library/cpp/yt/threading/writer_starving_rw_spin_lock.cpp b/library/cpp/yt/threading/writer_starving_rw_spin_lock.cpp
index 74c9f59db13..04eadb23607 100644
--- a/library/cpp/yt/threading/writer_starving_rw_spin_lock.cpp
+++ b/library/cpp/yt/threading/writer_starving_rw_spin_lock.cpp
@@ -12,6 +12,14 @@ void TWriterStarvingRWSpinLock::AcquireReaderSlow() noexcept
}
}
+void TWriterStarvingRWSpinLock::AcquireReaderForkFriendlySlow() noexcept
+{
+ TSpinWait spinWait(Location_, ESpinLockActivityKind::Read);
+ while (!TryAcquireReaderForkFriendly()) {
+ spinWait.Wait();
+ }
+}
+
void TWriterStarvingRWSpinLock::AcquireWriterSlow() noexcept
{
TSpinWait spinWait(Location_, ESpinLockActivityKind::Write);
diff --git a/library/cpp/yt/threading/writer_starving_rw_spin_lock.h b/library/cpp/yt/threading/writer_starving_rw_spin_lock.h
index 30aec51daf2..98e01fd7d55 100644
--- a/library/cpp/yt/threading/writer_starving_rw_spin_lock.h
+++ b/library/cpp/yt/threading/writer_starving_rw_spin_lock.h
@@ -45,9 +45,21 @@ public:
* leave lock forever stuck for the child process.
*/
void AcquireReader() noexcept;
+ //! Acquires the reader lock.
+ /*!
+ * A more expensive version of #AcquireReader (includes at least
+ * one atomic load and CAS; also may spin even if just readers are present).
+ * In contrast to #AcquireReader, this method can be used in the presence of forks.
+ * Note that fork-friendliness alone does not provide fork-safety: additional
+ * actions must be performed to release the lock after a fork.
+ */
+ void AcquireReaderForkFriendly() noexcept;
//! Tries acquiring the reader lock; see #AcquireReader.
//! Returns |true| on success.
bool TryAcquireReader() noexcept;
+ //! Tries acquiring the reader lock (and does this in a fork-friendly manner); see #AcquireReaderForkFriendly.
+ //! returns |true| on success.
+ bool TryAcquireReaderForkFriendly() noexcept;
//! Releases the reader lock.
/*!
* Cheap (just one atomic decrement).
@@ -102,6 +114,7 @@ private:
bool TryAndTryAcquireWriter() noexcept;
void AcquireReaderSlow() noexcept;
+ void AcquireReaderForkFriendlySlow() noexcept;
void AcquireWriterSlow() noexcept;
};