From 7a024f335576a1d0f9ec9ff742cd618236f7a105 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Mon, 1 Oct 2018 20:03:40 +0300 Subject: [PATCH] Implement shared_mutex::lock_unlock Minor fix for shared_mutex::try_lock - don't optimize for pessimistic case --- Utilities/mutex.cpp | 88 +++++++++++++++++++++++++++++++++++---------- Utilities/mutex.h | 13 ++++++- 2 files changed, 82 insertions(+), 19 deletions(-) diff --git a/Utilities/mutex.cpp b/Utilities/mutex.cpp index 3259a8a7c8..37ff76b1b7 100644 --- a/Utilities/mutex.cpp +++ b/Utilities/mutex.cpp @@ -38,13 +38,7 @@ void shared_mutex::imp_unlock_shared(u32 old) // Check reader count, notify the writer if necessary if ((old - 1) % c_one == 0) { -#ifdef _WIN32 - NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr); -#else - m_value += c_sig; - - futex(reinterpret_cast(&m_value.raw()), FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, c_sig); -#endif + imp_signal(); } } @@ -77,6 +71,17 @@ void shared_mutex::imp_wait() #endif } +void shared_mutex::imp_signal() +{ +#ifdef _WIN32 + NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr); +#else + m_value += c_sig; + futex(reinterpret_cast(&m_value.raw()), FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, c_sig); + //futex(reinterpret_cast(&m_value.raw()), FUTEX_WAKE_BITSET_PRIVATE, c_one, nullptr, nullptr, c_sig - 1); +#endif +} + void shared_mutex::imp_lock(u32 val) { verify("shared_mutex underflow" HERE), val < c_err; @@ -85,7 +90,7 @@ void shared_mutex::imp_lock(u32 val) { busy_wait(); - if (try_lock()) + if (!m_value && try_lock()) { return; } @@ -108,19 +113,10 @@ void shared_mutex::imp_unlock(u32 old) // 1) Notify the next writer if necessary // 2) Notify all readers otherwise if necessary (currently indistinguishable from writers) -#ifdef _WIN32 if (old - c_one) { - NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr); + imp_signal(); } -#else - if (old - c_one) - { - m_value += c_sig; - - futex(reinterpret_cast(&m_value.raw()), FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, c_sig); - } -#endif } void shared_mutex::imp_lock_upgrade() @@ -147,3 +143,59 @@ void shared_mutex::imp_lock_upgrade() imp_wait(); } + +void shared_mutex::imp_lock_unlock() +{ + u32 _max = 1; + + for (int i = 0; i < 30; i++) + { + const u32 val = m_value; + + if (val % c_one == 0 && (val / c_one < _max || val >= c_sig)) + { + // Return if have cought a state where: + // 1) Mutex is free + // 2) Total number of waiters decreased since last check + // 3) Signal bit is set (if used on the platform) + return; + } + + _max = val / c_one; + + busy_wait(1500); + } + +#ifndef _WIN32 + while (false) + { + const u32 val = m_value; + + if (val % c_one == 0 && (val / c_one < _max || val >= c_sig)) + { + return; + } + + if (val <= c_one) + { + // Can't expect a signal + break; + } + + _max = val / c_one; + + // Monitor all bits except c_sig + futex(reinterpret_cast(&m_value.raw()), FUTEX_WAIT_BITSET_PRIVATE, val, nullptr, nullptr, c_sig - 1); + } +#endif + + // Lock and unlock + if (!m_value.fetch_add(c_one)) + { + unlock(); + return; + } + + imp_wait(); + unlock(); +} diff --git a/Utilities/mutex.h b/Utilities/mutex.h index 1eb0ba3a88..0dc63a0e8d 100644 --- a/Utilities/mutex.h +++ b/Utilities/mutex.h @@ -19,9 +19,11 @@ class shared_mutex final void imp_lock_shared(u32 val); void imp_unlock_shared(u32 old); void imp_wait(); + void imp_signal(); void imp_lock(u32 val); void imp_unlock(u32 old); void imp_lock_upgrade(); + void imp_lock_unlock(); public: constexpr shared_mutex() = default; @@ -57,7 +59,7 @@ public: bool try_lock() { - return m_value == 0 && m_value.compare_and_swap_test(0, c_one); + return m_value.compare_and_swap_test(0, c_one); } void lock() @@ -103,6 +105,15 @@ public: m_value -= c_one - 1; } + // Optimized wait for lockability without locking, relaxed + void lock_unlock() + { + if (UNLIKELY(m_value != 0)) + { + imp_lock_unlock(); + } + } + // Check whether can immediately obtain an exclusive (writer) lock bool is_free() const {