diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 0eb6cbccd8..d4b7d43db8 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -176,7 +176,7 @@ const std::array s_ppu_syscall_table BIND_FUNC(sys_semaphore_get_value), //114 (0x072) BIND_FUNC(_sys_lwcond_signal), //115 (0x073) BIND_FUNC(_sys_lwcond_signal_all), //116 (0x074) - null_func,//BIND_FUNC(sys_semaphore_...) //117 (0x075) // internal, used by sys_lwmutex_unlock + BIND_FUNC(_sys_lwmutex_unlock2), //117 (0x075) BIND_FUNC(sys_event_flag_clear), //118 (0x076) null_func,//BIND_FUNC(sys_time_get_rtc) //119 (0x077) ROOT BIND_FUNC(sys_rwlock_create), //120 (0x078) diff --git a/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp b/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp index 8e1ee29c5c..42161acabd 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "Emu/Memory/vm.h" #include "Emu/System.h" #include "Emu/IdManager.h" @@ -73,17 +73,35 @@ error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout) { sys_lwmutex.trace("_sys_lwmutex_lock(lwmutex_id=0x%x, timeout=0x%llx)", lwmutex_id, timeout); + ppu.gpr[3] = CELL_OK; + const auto mutex = idm::get(lwmutex_id, [&](lv2_lwmutex& mutex) { - if (mutex.signaled.try_dec()) + if (mutex.signaled.try_dec(0)) { return true; } std::lock_guard lock(mutex.mutex); - if (mutex.signaled.try_dec()) + auto [old, _] = mutex.signaled.fetch_op([](s32& value) + { + if (value) + { + value = 0; + return true; + } + + return false; + }); + + if (old) { + if (old == (1 << 31)) + { + ppu.gpr[3] = CELL_EBUSY; + } + return true; } @@ -99,11 +117,9 @@ error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout) if (mutex.ret) { - return CELL_OK; + return not_an_error(ppu.gpr[3]); } - ppu.gpr[3] = CELL_OK; - while (!ppu.state.test_and_reset(cpu_flag::signal)) { if (ppu.is_stopped()) @@ -146,7 +162,18 @@ error_code _sys_lwmutex_trylock(u32 lwmutex_id) const auto mutex = idm::check(lwmutex_id, [&](lv2_lwmutex& mutex) { - return mutex.signaled.try_dec(); + auto [_, ok] = mutex.signaled.fetch_op([](s32& value) + { + if (value & 1) + { + value = 0; + return true; + } + + return false; + }); + + return ok; }); if (!mutex) @@ -175,7 +202,38 @@ error_code _sys_lwmutex_unlock(ppu_thread& ppu, u32 lwmutex_id) return cpu; } - mutex.signaled.release(1); + mutex.signaled |= 1; + return nullptr; + }); + + if (!mutex) + { + return CELL_ESRCH; + } + + if (mutex.ret) + { + mutex->awake(*mutex.ret); + } + + return CELL_OK; +} + +error_code _sys_lwmutex_unlock2(u32 lwmutex_id) +{ + sys_lwmutex.warning("_sys_lwmutex_unlock2(lwmutex_id=0x%x)", lwmutex_id); + + const auto mutex = idm::check(lwmutex_id, [&](lv2_lwmutex& mutex) -> cpu_thread* + { + std::lock_guard lock(mutex.mutex); + + if (const auto cpu = mutex.schedule(mutex.sq, mutex.protocol)) + { + static_cast(cpu)->gpr[3] = CELL_EBUSY; + return cpu; + } + + mutex.signaled |= 1 << 31; return nullptr; }); diff --git a/rpcs3/Emu/Cell/lv2/sys_lwmutex.h b/rpcs3/Emu/Cell/lv2/sys_lwmutex.h index d0c41f6416..b17ec0f1aa 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwmutex.h +++ b/rpcs3/Emu/Cell/lv2/sys_lwmutex.h @@ -58,7 +58,7 @@ struct lv2_lwmutex final : lv2_obj const u64 name; shared_mutex mutex; - atomic_t signaled{0}; + atomic_t signaled{0}; std::deque sq; lv2_lwmutex(u32 protocol, vm::ptr control, u64 name) @@ -79,3 +79,4 @@ error_code _sys_lwmutex_destroy(u32 lwmutex_id); error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout); error_code _sys_lwmutex_trylock(u32 lwmutex_id); error_code _sys_lwmutex_unlock(ppu_thread& ppu, u32 lwmutex_id); +error_code _sys_lwmutex_unlock2(u32 lwmutex_id);