error_code sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64 timeout) { sysPrxForUser.trace("sys_lwmutex_lock(lwmutex=*0x%x, timeout=0x%llx)", lwmutex, timeout); if (g_cfg.core.hle_lwmutex) { return sys_mutex_lock(ppu, lwmutex->sleep_queue, timeout); } const be_t<u32> tid(ppu.id); // try to lock lightweight mutex const be_t<u32> old_owner = lwmutex->vars.owner.compare_and_swap(lwmutex_free, tid); if (old_owner == lwmutex_free) { // locking succeeded return CELL_OK; } if (old_owner == tid) { // recursive locking if ((lwmutex->attribute & SYS_SYNC_RECURSIVE) == 0) { // if not recursive return CELL_EDEADLK; } if (lwmutex->recursive_count == -1) { // if recursion limit reached return CELL_EKRESOURCE; } // recursive locking succeeded lwmutex->recursive_count++; _mm_mfence(); return CELL_OK; } if (old_owner == lwmutex_dead) { // invalid or deleted mutex return CELL_EINVAL; } for (u32 i = 0; i < 10; i++) { busy_wait(); if (lwmutex->vars.owner.load() == lwmutex_free) { if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid)) { // locking succeeded return CELL_OK; } } } // atomically increment waiter value using 64 bit op lwmutex->all_info++; if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid)) { // locking succeeded --lwmutex->all_info; return CELL_OK; } // lock using the syscall const error_code res = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout); lwmutex->all_info--; if (res == CELL_OK) { // locking succeeded auto old = lwmutex->vars.owner.exchange(tid); if (old != lwmutex_reserved) { fmt::throw_exception("Locking failed (lwmutex=*0x%x, owner=0x%x)" HERE, lwmutex, old); } return CELL_OK; } if (res == CELL_EBUSY && lwmutex->attribute & SYS_SYNC_RETRY) { while (true) { for (u32 i = 0; i < 10; i++) { busy_wait(); if (lwmutex->vars.owner.load() == lwmutex_free) { if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid)) { return CELL_OK; } } } lwmutex->all_info++; if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid)) { lwmutex->all_info--; return CELL_OK; } const u64 time0 = timeout ? get_system_time() : 0; const error_code res_ = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout); if (res_ == CELL_OK) { lwmutex->vars.owner.release(tid); } else if (timeout && res_ != CELL_ETIMEDOUT) { const u64 time_diff = get_system_time() - time0; if (timeout <= time_diff) { lwmutex->all_info--; return not_an_error(CELL_ETIMEDOUT); } timeout -= time_diff; } lwmutex->all_info--; if (res_ != CELL_EBUSY) { return res_; } } } return res; }
s32 sys_lwmutex_lock(PPUThread& CPU, vm::ptr<sys_lwmutex_t> lwmutex, u64 timeout) { sysPrxForUser.Log("sys_lwmutex_lock(lwmutex=*0x%x, timeout=0x%llx)", lwmutex, timeout); const be_t<u32> tid = be_t<u32>::make(CPU.GetId()); // try to lock lightweight mutex const be_t<u32> old_owner = lwmutex->owner.compare_and_swap(lwmutex::free, tid); if (old_owner.data() == se32(lwmutex_free)) { // locking succeeded return CELL_OK; } if (old_owner.data() == tid.data()) { // recursive locking if ((lwmutex->attribute.data() & se32(SYS_SYNC_RECURSIVE)) == 0) { // if not recursive return CELL_EDEADLK; } if (lwmutex->recursive_count.data() == -1) { // if recursion limit reached return CELL_EKRESOURCE; } // recursive locking succeeded lwmutex->recursive_count++; lwmutex->lock_var.read_sync(); return CELL_OK; } if (old_owner.data() == se32(lwmutex_dead)) { // invalid or deleted mutex return CELL_EINVAL; } for (u32 i = 0; i < 300; i++) { if (lwmutex->owner.read_relaxed().data() == se32(lwmutex_free)) { if (lwmutex->owner.compare_and_swap_test(lwmutex::free, tid)) { // locking succeeded return CELL_OK; } } } // atomically increment waiter value using 64 bit op lwmutex->all_info++; if (lwmutex->owner.compare_and_swap_test(lwmutex::free, tid)) { // locking succeeded lwmutex->all_info--; return CELL_OK; } // lock using the syscall const s32 res = _sys_lwmutex_lock(lwmutex->sleep_queue, timeout); lwmutex->all_info--; if (res == CELL_OK) { // locking succeeded auto old = lwmutex->owner.exchange(tid); if (old.data() != se32(lwmutex_reserved) && !Emu.IsStopped()) { sysPrxForUser.Fatal("sys_lwmutex_lock(lwmutex=*0x%x): locking failed (owner=0x%x)", lwmutex, old); } return CELL_OK; } if (res == CELL_EBUSY && lwmutex->attribute.data() & se32(SYS_SYNC_RETRY)) { // TODO (protocol is ignored in current implementation) throw __FUNCTION__; } return res; }