diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index b24ad1036f..10970be896 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include "Emu/System.h" #include "Log.h" #include "Thread.h" @@ -207,3 +208,66 @@ bool thread::joinable() const { return m_thr.joinable(); } + +struct g_waiter_map_t +{ + // TODO: optimize (use custom lightweight readers-writer lock) + + std::mutex m_mutex; + + struct waiter + { + u64 signal_id; + NamedThreadBase* thread; + }; + + std::vector m_waiters; + +} g_waiter_map; + +bool waiter_is_stopped(const char* func_name, u64 signal_id) +{ + if (Emu.IsStopped()) + { + LOG_WARNING(Log::HLE, "%s() aborted (signal_id=0x%llx)", func_name, signal_id); + return true; + } + return false; +} + +void waiter_register(u64 signal_id, NamedThreadBase* thread) +{ + std::lock_guard lock(g_waiter_map.m_mutex); + + // add waiter + g_waiter_map.m_waiters.push_back({ signal_id, thread }); +} + +void waiter_unregister(u64 signal_id, NamedThreadBase* thread) +{ + std::lock_guard lock(g_waiter_map.m_mutex); + + // remove waiter + for (s32 i = g_waiter_map.m_waiters.size() - 1; i >= 0; i--) + { + if (g_waiter_map.m_waiters[i].signal_id == signal_id && g_waiter_map.m_waiters[i].thread == thread) + { + g_waiter_map.m_waiters.erase(g_waiter_map.m_waiters.begin() + i); + return; + } + } +} + +void waiter_signal(u64 signal_id) +{ + std::lock_guard lock(g_waiter_map.m_mutex); + + // find waiter and signal + for (auto& v : g_waiter_map.m_waiters) + { + if (v.signal_id == signal_id) + { + v.thread->Notify(); + } + } +} diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 2566cdf43b..acef1eb77b 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -69,4 +69,32 @@ public: void detach(); void join(); bool joinable() const; -}; \ No newline at end of file +}; + +// for internal use (checks if Emu is stopped) +bool waiter_is_stopped(const char* func_name, u64 signal_id); +// for internal use +void waiter_register(u64 signal_id, NamedThreadBase* thread); +// for internal use +void waiter_unregister(u64 signal_id, NamedThreadBase* thread); + +// wait until waiter_func() returns true, signal_id is arbitrary number +template static __forceinline void waiter_op(const char* func_name, u64 signal_id, const WT waiter_func) +{ + if (waiter_func()) return; + + NamedThreadBase* thread = GetCurrentNamedThread(); + waiter_register(signal_id, thread); + + while (true) + { + thread->WaitForAnySignal(1); + if (waiter_is_stopped(func_name, signal_id)) break; + if (waiter_func()) break; + } + + waiter_unregister(signal_id, thread); +} + +// signal all threads waiting on waiter_op() with the same signal_id (signaling only hints those threads that corresponding conditions are *probably* met) +void waiter_signal(u64 signal_id); diff --git a/rpcs3/Emu/SysCalls/Modules/cellSync.cpp b/rpcs3/Emu/SysCalls/Modules/cellSync.cpp index df4de7b5a8..5f55d6d0fa 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSync.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSync.cpp @@ -60,15 +60,10 @@ s32 cellSyncMutexLock(vm::ptr mutex) }); // prx: wait until this old value is equal to m_rel - while (order != mutex->data.read_relaxed().m_rel) + waiter_op(__FUNCTION__, mutex.addr(), [mutex, order]() { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack - if (Emu.IsStopped()) - { - cellSync->Warning("cellSyncMutexLock(mutex_addr=0x%x) aborted", mutex.addr()); - break; - } - } + return order == mutex->data.read_relaxed().m_rel; + }); // prx: sync mutex->data.read_sync(); @@ -116,6 +111,8 @@ s32 cellSyncMutexUnlock(vm::ptr mutex) { mutex.m_rel++; }); + + waiter_signal(mutex.addr()); return CELL_OK; }