diff options
| author | pavook <[email protected]> | 2025-09-26 13:21:26 +0300 |
|---|---|---|
| committer | pavook <[email protected]> | 2025-09-26 13:44:08 +0300 |
| commit | 9239d3fa83520c4cc193a272ed9c62bc5eec3e6b (patch) | |
| tree | a4a884dbfbbd2e91f35df4d953a43729b606313d /library/cpp | |
| parent | a9e79bf52fc76e605603b5c18079c2eca0ef11e9 (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')
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; }; |
