diff --git a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp index 6104cb99fb..c43658a119 100644 --- a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp @@ -139,7 +139,7 @@ s32 sys_lwmutex_destroy(PPUThread& CPU, vm::ptr lwmutex) } // call the syscall - if (s32 res = _sys_lwmutex_destroy(CPU, lwmutex->sleep_queue)) + if (s32 res = _sys_lwmutex_destroy(lwmutex->sleep_queue)) { // unlock the mutex if failed sys_lwmutex_unlock(CPU, lwmutex); @@ -209,20 +209,21 @@ s32 sys_lwmutex_lock(PPUThread& CPU, vm::ptr lwmutex, u64 timeout } } - lwmutex->waiter.atomic_op([](be_t& value){ value++; }); + // atomically increment waiter value using 64 bit op + lwmutex->all_info.atomic_op([](be_t& value){ value++; }); if (lwmutex->owner.compare_and_swap_test(lwmutex::free, tid)) { // locking succeeded - lwmutex->waiter.atomic_op([](be_t& value){ value--; }); + lwmutex->all_info.atomic_op([](be_t& value){ value--; }); return CELL_OK; } // lock using the syscall - const s32 res = _sys_lwmutex_lock(CPU, lwmutex->sleep_queue, timeout); + const s32 res = _sys_lwmutex_lock(lwmutex->sleep_queue, timeout); - lwmutex->waiter.atomic_op([](be_t& value){ value--; }); + lwmutex->all_info.atomic_op([](be_t& value){ value--; }); if (res == CELL_OK) { @@ -285,10 +286,10 @@ s32 sys_lwmutex_trylock(PPUThread& CPU, vm::ptr lwmutex) return CELL_EINVAL; } - if (old_owner.data() == se32(lwmutex_busy)) + if (old_owner.data() == se32(lwmutex_reserved)) { // should be locked by the syscall - const s32 res = _sys_lwmutex_trylock(CPU, lwmutex->sleep_queue); + const s32 res = _sys_lwmutex_trylock(lwmutex->sleep_queue); if (res == CELL_OK) { @@ -335,10 +336,11 @@ s32 sys_lwmutex_unlock(PPUThread& CPU, vm::ptr lwmutex) // TODO (protocol is ignored in current implementation) } - lwmutex->owner.exchange(lwmutex::busy); + // set special value + lwmutex->owner.exchange(lwmutex::reserved); // call the syscall - if (_sys_lwmutex_unlock(CPU, lwmutex->sleep_queue) == CELL_ESRCH) + if (_sys_lwmutex_unlock(lwmutex->sleep_queue) == CELL_ESRCH) { return CELL_ESRCH; } diff --git a/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.cpp b/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.cpp index 3336574681..43dc044e59 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.cpp @@ -14,35 +14,137 @@ SysCallBase sys_lwmutex("sys_lwmutex"); void lwmutex_create(sys_lwmutex_t& lwmutex, bool recursive, u32 protocol, u64 name) { - std::shared_ptr lw(new lwmutex_t(protocol, name)); + std::shared_ptr mutex(new lwmutex_t(protocol, name)); lwmutex.lock_var.write_relaxed({ lwmutex::free, lwmutex::zero }); lwmutex.attribute = protocol | (recursive ? SYS_SYNC_RECURSIVE : SYS_SYNC_NOT_RECURSIVE); lwmutex.recursive_count = 0; - lwmutex.sleep_queue = Emu.GetIdManager().GetNewID(lw, TYPE_LWMUTEX); + lwmutex.sleep_queue = Emu.GetIdManager().GetNewID(mutex, TYPE_LWMUTEX); } s32 _sys_lwmutex_create(vm::ptr lwmutex_id, u32 protocol, vm::ptr control, u32 arg4, u64 name, u32 arg6) { - throw __FUNCTION__; + sys_lwmutex.Warning("_sys_lwmutex_create(lwmutex_id=*0x%x, protocol=0x%x, control=*0x%x, arg4=0x%x, name=0x%llx, arg6=0x%x)", lwmutex_id, protocol, control, arg4, name, arg6); + + switch (protocol) + { + case SYS_SYNC_FIFO: break; + case SYS_SYNC_RETRY: break; + case SYS_SYNC_PRIORITY: break; + default: sys_lwmutex.Error("_sys_lwmutex_create(): invalid protocol (0x%x)", protocol); return CELL_EINVAL; + } + + if (arg4 != 0x80000001 || arg6) + { + sys_lwmutex.Error("_sys_lwmutex_create(): unknown parameters (arg4=0x%x, arg6=0x%x)", arg4, arg6); + } + + std::shared_ptr mutex(new lwmutex_t(protocol, name)); + + *lwmutex_id = Emu.GetIdManager().GetNewID(mutex, TYPE_LWMUTEX); + + return CELL_OK; } -s32 _sys_lwmutex_destroy(PPUThread& CPU, u32 lwmutex_id) +s32 _sys_lwmutex_destroy(u32 lwmutex_id) { - throw __FUNCTION__; + sys_lwmutex.Warning("_sys_lwmutex_destroy(lwmutex_id=%d)", lwmutex_id); + + LV2_LOCK; + + std::shared_ptr mutex; + if (!Emu.GetIdManager().GetIDData(lwmutex_id, mutex)) + { + return CELL_ESRCH; + } + + if (mutex->waiters) + { + return CELL_EBUSY; + } + + Emu.GetIdManager().RemoveID(lwmutex_id); + + return CELL_OK; } -s32 _sys_lwmutex_lock(PPUThread& CPU, u32 lwmutex_id, u64 timeout) +s32 _sys_lwmutex_lock(u32 lwmutex_id, u64 timeout) { - throw __FUNCTION__; + sys_lwmutex.Log("_sys_lwmutex_lock(lwmutex_id=%d, timeout=0x%llx)", lwmutex_id, timeout); + + const u64 start_time = get_system_time(); + + LV2_LOCK; + + std::shared_ptr mutex; + if (!Emu.GetIdManager().GetIDData(lwmutex_id, mutex)) + { + return CELL_ESRCH; + } + + // protocol is ignored in current implementation + mutex->waiters++; assert(mutex->waiters > 0); + + while (!mutex->signals) + { + if (timeout && get_system_time() - start_time > timeout) + { + mutex->waiters--; assert(mutex->waiters >= 0); + return CELL_ETIMEDOUT; + } + + if (Emu.IsStopped()) + { + sys_lwmutex.Warning("_sys_lwmutex_lock(lwmutex_id=%d) aborted", lwmutex_id); + return CELL_OK; + } + + mutex->cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); + } + + mutex->signals--; + + mutex->waiters--; assert(mutex->waiters >= 0); + + return CELL_OK; } -s32 _sys_lwmutex_trylock(PPUThread& CPU, u32 lwmutex_id) +s32 _sys_lwmutex_trylock(u32 lwmutex_id) { - throw __FUNCTION__; + sys_lwmutex.Log("_sys_lwmutex_trylock(lwmutex_id=%d)", lwmutex_id); + + LV2_LOCK; + + std::shared_ptr mutex; + if (!Emu.GetIdManager().GetIDData(lwmutex_id, mutex)) + { + return CELL_ESRCH; + } + + if (mutex->waiters || !mutex->signals) + { + return CELL_EBUSY; + } + + mutex->signals--; + + return CELL_OK; } -s32 _sys_lwmutex_unlock(PPUThread& CPU, u32 lwmutex_id) +s32 _sys_lwmutex_unlock(u32 lwmutex_id) { - throw __FUNCTION__; + sys_lwmutex.Log("_sys_lwmutex_unlock(lwmutex_id=%d)", lwmutex_id); + + LV2_LOCK; + + std::shared_ptr mutex; + if (!Emu.GetIdManager().GetIDData(lwmutex_id, mutex)) + { + return CELL_ESRCH; + } + + mutex->signals++; + mutex->cv.notify_one(); + + return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.h b/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.h index 7f76a4c8a2..4cf3a0ed3a 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.h @@ -14,10 +14,10 @@ struct sys_lwmutex_attribute_t enum : u32 { - lwmutex_zero = 0u, - lwmutex_free = 0u - 1u, - lwmutex_dead = 0u - 2u, - lwmutex_busy = 0u - 3u, + lwmutex_zero = 0u, + lwmutex_free = 0u - 1u, + lwmutex_dead = 0u - 2u, + lwmutex_reserved = 0u - 3u, }; namespace lwmutex @@ -36,7 +36,7 @@ namespace lwmutex static const_be_u32_t zero; static const_be_u32_t free; static const_be_u32_t dead; - static const_be_u32_t busy; + static const_be_u32_t reserved; } struct sys_lwmutex_t @@ -56,6 +56,8 @@ struct sys_lwmutex_t atomic_t owner; atomic_t waiter; }; + + atomic_t all_info; }; be_t attribute; @@ -69,6 +71,13 @@ struct lwmutex_t const u32 protocol; const u64 name; + // this object is not truly a mutex and its syscall names are wrong, it's probabably sleep queue or something + std::atomic signals; + + // TODO: use sleep queue, possibly remove condition variable + std::condition_variable cv; + std::atomic waiters; + lwmutex_t(u32 protocol, u64 name) : protocol(protocol) , name(name) @@ -83,7 +92,7 @@ class PPUThread; // SysCalls s32 _sys_lwmutex_create(vm::ptr lwmutex_id, u32 protocol, vm::ptr control, u32 arg4, u64 name, u32 arg6); -s32 _sys_lwmutex_destroy(PPUThread& CPU, u32 lwmutex_id); -s32 _sys_lwmutex_lock(PPUThread& CPU, u32 lwmutex_id, u64 timeout); -s32 _sys_lwmutex_trylock(PPUThread& CPU, u32 lwmutex_id); -s32 _sys_lwmutex_unlock(PPUThread& CPU, u32 lwmutex_id); +s32 _sys_lwmutex_destroy(u32 lwmutex_id); +s32 _sys_lwmutex_lock(u32 lwmutex_id, u64 timeout); +s32 _sys_lwmutex_trylock(u32 lwmutex_id); +s32 _sys_lwmutex_unlock(u32 lwmutex_id);