int sys_lwmutex_t::unlock(be_t<u32> tid) { if (mutex.unlock(tid, tid) != SMR_OK) { return CELL_EPERM; } else { if (!recursive_count || (recursive_count.ToBE() != se32(1) && (attribute.ToBE() & se32(SYS_SYNC_NOT_RECURSIVE)))) { sc_lwmutex.Error("sys_lwmutex_t::unlock(%d): wrong recursive value fixed (%d)", (u32)sleep_queue, (u32)recursive_count); recursive_count = 1; } recursive_count -= 1; if (!recursive_count.ToBE()) { be_t<u32> target = 0; switch (attribute.ToBE() & se32(SYS_SYNC_ATTR_PROTOCOL_MASK)) { case se32(SYS_SYNC_FIFO): case se32(SYS_SYNC_PRIORITY): SleepQueue* sq; if (!Emu.GetIdManager().GetIDData(sleep_queue, sq)) return CELL_ESRCH; target = attribute & SYS_SYNC_FIFO ? sq->pop() : sq->pop_prio(); case se32(SYS_SYNC_RETRY): break; } if (target) mutex.unlock(tid, target); else mutex.unlock(tid); } return CELL_OK; } }
int sys_lwcond_signal(mem_ptr_t<sys_lwcond_t> lwcond) { sys_lwcond.Log("sys_lwcond_signal(lwcond_addr=0x%x)", lwcond.GetAddr()); if (!lwcond.IsGood()) { return CELL_EFAULT; } SleepQueue* sq; if (!Emu.GetIdManager().GetIDData((u32)lwcond->lwcond_queue, sq)) { return CELL_ESRCH; } mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex); be_t<u32> tid = GetCurrentPPUThread().GetId(); if (be_t<u32> target = (mutex->attribute.ToBE() == se32(SYS_SYNC_PRIORITY) ? sq->pop_prio() : sq->pop())) { if (mutex->mutex.owner.trylock(target) != SMR_OK) { mutex->mutex.owner.lock(tid); mutex->recursive_count = 1; mutex->mutex.owner.unlock(tid, target); } } if (Emu.IsStopped()) { ConLog.Warning("sys_lwcond_signal(sq=%d) aborted", (u32)lwcond->lwcond_queue); } return CELL_OK; }
s32 sys_lwcond_wait(vm::ptr<sys_lwcond_t> lwcond, u64 timeout) { sys_lwcond.Log("sys_lwcond_wait(lwcond_addr=0x%x, timeout=%lld)", lwcond.addr(), timeout); Lwcond* lw; if (!Emu.GetIdManager().GetIDData((u32)lwcond->lwcond_queue, lw)) { return CELL_ESRCH; } auto mutex = vm::ptr<sys_lwmutex_t>::make(lwcond->lwmutex.addr()); u32 tid_le = GetCurrentPPUThread().GetId(); be_t<u32> tid = be_t<u32>::make(tid_le); SleepQueue* sq = nullptr; Emu.GetIdManager().GetIDData((u32)mutex->sleep_queue, sq); if (mutex->mutex.GetOwner() != tid) { sys_lwcond.Warning("sys_lwcond_wait(id=%d) failed (EPERM)", (u32)lwcond->lwcond_queue); return CELL_EPERM; // caller must own this lwmutex } lw->m_queue.push(tid_le); if (mutex->recursive_count.ToBE() != se32(1)) { sys_lwcond.Warning("sys_lwcond_wait(id=%d): associated mutex had wrong recursive value (%d)", (u32)lwcond->lwcond_queue, (u32)mutex->recursive_count); } mutex->recursive_count = 0; if (sq) { mutex->mutex.unlock(tid, be_t<u32>::make(mutex->attribute.ToBE() == se32(SYS_SYNC_PRIORITY) ? sq->pop_prio() : sq->pop())); } else if (mutex->attribute.ToBE() == se32(SYS_SYNC_RETRY)) { mutex->mutex.unlock(tid); // SYS_SYNC_RETRY } else { sys_lwcond.Warning("sys_lwcond_wait(id=%d): associated mutex had invalid sleep queue (%d)", (u32)lwcond->lwcond_queue, (u32)mutex->sleep_queue); } u64 counter = 0; const u64 max_counter = timeout ? (timeout / 1000) : ~0; while (true) { if (lw->signal.unlock(tid, tid) == SMR_OK) { switch (mutex->lock(tid, 0)) { case CELL_OK: break; case static_cast<int>(CELL_EDEADLK): sys_lwcond.Warning("sys_lwcond_wait(id=%d): associated mutex was locked", (u32)lwcond->lwcond_queue); return CELL_OK; case static_cast<int>(CELL_ESRCH): sys_lwcond.Warning("sys_lwcond_wait(id=%d): associated mutex not found (%d)", (u32)lwcond->lwcond_queue, (u32)mutex->sleep_queue); return CELL_ESRCH; case static_cast<int>(CELL_EINVAL): goto abort; } mutex->recursive_count = 1; lw->signal.unlock(tid); return CELL_OK; } std::this_thread::sleep_for(std::chrono::milliseconds(1)); if (counter++ > max_counter) { lw->m_queue.invalidate(tid_le); return CELL_ETIMEDOUT; } if (Emu.IsStopped()) { goto abort; } } abort: sys_lwcond.Warning("sys_lwcond_wait(id=%d) aborted", (u32)lwcond->lwcond_queue); return CELL_OK; }