diff --git a/Utilities/cond.cpp b/Utilities/cond.cpp index 260a01474f..cc2de9b379 100644 --- a/Utilities/cond.cpp +++ b/Utilities/cond.cpp @@ -120,3 +120,97 @@ void cond_variable::imp_wake(u32 _count) noexcept } #endif } + +bool notifier::imp_try_lock(u32 count) +{ + return m_counter.atomic_op([&](u32& value) + { + if ((value % (max_readers + 1)) + count <= max_readers) + { + value += count; + return true; + } + + return false; + }); +} + +void notifier::imp_unlock(u32 count) +{ + const u32 counter = m_counter.sub_fetch(count); + + if (UNLIKELY(counter % (max_readers + 1))) + { + return; + } + + if (counter) + { + const u32 _old = m_counter.atomic_op([](u32& value) -> u32 + { + if (value % (max_readers + 1)) + { + return 0; + } + + return std::exchange(value, 0) / (max_readers + 1); + }); + + const u32 wc = m_cond.m_value; + + if (_old && wc) + { + m_cond.imp_wake(_old > wc ? wc : _old); + } + } +} + +u32 notifier::imp_notify(u32 count) +{ + return m_counter.atomic_op([&](u32& value) -> u32 + { + if (const u32 add = value % (max_readers + 1)) + { + // Mutex is locked + const u32 result = add > count ? count : add; + value += result * (max_readers + 1); + return result; + } + else + { + // Mutex is unlocked + value = 0; + return count; + } + }); +} + +explicit_bool_t notifier::wait(u64 usec_timeout) +{ + const u32 _old = m_cond.m_value.fetch_add(1); + + if (max_readers < m_counter.fetch_op([](u32& value) + { + if (value > max_readers) + { + value -= max_readers; + } + + value -= 1; + })) + { + // Return without waiting + m_cond.imp_wait(_old, 0); + return true; + } + + const bool res = m_cond.imp_wait(_old, usec_timeout); + + while (!try_lock_shared()) + { + // TODO + busy_wait(); + } + + return res; +} diff --git a/Utilities/cond.h b/Utilities/cond.h index 02c7908915..4831911822 100644 --- a/Utilities/cond.h +++ b/Utilities/cond.h @@ -59,87 +59,61 @@ class notifier atomic_t m_counter{0}; cond_variable m_cond; + bool imp_try_lock(u32 count); + + void imp_unlock(u32 count); + + u32 imp_notify(u32 count); + public: constexpr notifier() = default; - void lock_shared() + bool try_lock() { - m_counter++; + return imp_try_lock(max_readers); + } + + void unlock() + { + imp_unlock(max_readers); + } + + bool try_lock_shared() + { + return imp_try_lock(1); } void unlock_shared() { - const u32 counter = --m_counter; - - if (counter & 0x7f) - { - return; - } - - if (counter >= 0x80) - { - const u32 _old = m_counter.atomic_op([](u32& value) -> u32 - { - if (value & 0x7f) - { - return 0; - } - - return std::exchange(value, 0) >> 7; - }); - - if (_old && m_cond.m_value) - { - m_cond.imp_wake(_old); - } - } + imp_unlock(1); } - explicit_bool_t wait(u64 usec_timeout = -1) - { - const u32 _old = m_cond.m_value.fetch_add(1); - - if (0x80 <= m_counter.fetch_op([](u32& value) - { - value--; - - if (value >= 0x80) - { - value -= 0x80; - } - })) - { - // Return without waiting - m_cond.imp_wait(_old, 0); - m_counter++; - return true; - } - - const bool res = m_cond.imp_wait(_old, usec_timeout); - m_counter++; - return res; - } + explicit_bool_t wait(u64 usec_timeout = -1); void notify_all() { if (m_counter) { - m_counter.atomic_op([](u32& value) - { - if (const u32 add = value & 0x7f) - { - // Mutex is locked in shared mode - value += add << 7; - } - else - { - // Mutex is unlocked - value = 0; - } - }); + imp_notify(-1); } // Notify after imaginary "exclusive" lock+unlock m_cond.notify_all(); } + + void notify_one() + { + // TODO + if (m_counter) + { + if (imp_notify(1)) + { + return; + } + } + + m_cond.notify_one(); + } + + static constexpr u32 max_readers = 0x7f; }; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 92d6e70ec4..fe37cd8166 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1733,7 +1733,9 @@ s64 SPUThread::get_ch_value(u32 ch) fmt::throw_exception("Not supported: event mask 0x%x" HERE, mask1); } - std::shared_lock pseudo_lock(vm::reservation_notifier(raddr, 128)); + std::shared_lock pseudo_lock(vm::reservation_notifier(raddr, 128), std::try_to_lock); + + verify(HERE), pseudo_lock; while (res = get_events(), !res) {