s32 sys_semaphore_wait(u32 sem_id, u64 timeout) { sys_semaphore.Log("sys_semaphore_wait(sem_id=%d, timeout=%lld)", sem_id, timeout); Semaphore* sem; if (!Emu.GetIdManager().GetIDData(sem_id, sem)) { return CELL_ESRCH; } const u32 tid = GetCurrentPPUThread().GetId(); const u64 start_time = get_system_time(); { std::lock_guard<std::mutex> lock(sem->m_mutex); if (sem->m_value > 0) { sem->m_value--; return CELL_OK; } sem->m_queue.push(tid); } while (true) { if (Emu.IsStopped()) { sys_semaphore.Warning("sys_semaphore_wait(%d) aborted", sem_id); return CELL_OK; } if (timeout && get_system_time() - start_time > timeout) { sem->m_queue.invalidate(tid); return CELL_ETIMEDOUT; } if (tid == sem->signal) { std::lock_guard<std::mutex> lock(sem->m_mutex); if (tid != sem->signal) { continue; } sem->signal = 0; // TODO: notify signaler return CELL_OK; } SM_Sleep(); } }
s32 sys_semaphore_post(u32 sem_id, s32 count) { sys_semaphore.Log("sys_semaphore_post(sem_id=%d, count=%d)", sem_id, count); Semaphore* sem; if (!Emu.GetIdManager().GetIDData(sem_id, sem)) { return CELL_ESRCH; } if (count < 0) { return CELL_EINVAL; } if (count + sem->m_value - (s32)sem->m_queue.count() > sem->max) { return CELL_EBUSY; } while (count > 0) { if (Emu.IsStopped()) { sys_semaphore.Warning("sys_semaphore_post(%d) aborted", sem_id); return CELL_OK; } std::lock_guard<std::mutex> lock(sem->m_mutex); if (sem->signal && sem->m_queue.count()) { SM_Sleep(); continue; } if (u32 target = (sem->protocol == SYS_SYNC_FIFO) ? sem->m_queue.pop() : sem->m_queue.pop_prio()) { count--; sem->signal = target; Emu.GetCPU().NotifyThread(target); } else { sem->m_value += count; count = 0; } } return CELL_OK; }
void sys_ppu_thread_once(mem_ptr_t<std::atomic<be_t<u32>>> once_ctrl, u32 entry) { sysPrxForUser->Warning("sys_ppu_thread_once(once_ctrl_addr=0x%x, entry=0x%x)", once_ctrl.GetAddr(), entry); be_t<u32> old = SYS_PPU_THREAD_ONCE_INIT; if (once_ctrl->compare_exchange_weak(old, SYS_PPU_THREAD_DONE_INIT)) { CPUThread& new_thread = Emu.GetCPU().AddThread(CPU_THREAD_PPU); new_thread.SetEntry(entry); new_thread.Run(); new_thread.Exec(); while (new_thread.IsAlive()) SM_Sleep(); } }
s32 sys_cond_wait(u32 cond_id, u64 timeout) { sys_cond.Log("sys_cond_wait(cond_id=%d, timeout=%lld)", cond_id, timeout); Cond* cond; if (!Emu.GetIdManager().GetIDData(cond_id, cond)) { return CELL_ESRCH; } Mutex* mutex = cond->mutex; u32 tid = GetCurrentPPUThread().GetId(); if (mutex->m_mutex.GetOwner() != tid) { sys_cond.Warning("sys_cond_wait(cond_id=%d) failed (EPERM)", cond_id); return CELL_EPERM; } cond->m_queue.push(tid); if (mutex->recursive != 1) { sys_cond.Warning("sys_cond_wait(cond_id=%d): associated mutex had wrong recursive value (%d)", cond_id, mutex->recursive); } mutex->recursive = 0; mutex->m_mutex.unlock(tid, mutex->protocol == SYS_SYNC_PRIORITY ? mutex->m_queue.pop_prio() : mutex->m_queue.pop()); u64 counter = 0; const u64 max_counter = timeout ? (timeout / 1000) : ~0ull; while (true) { if (cond->signal.unlock(tid, tid) == SMR_OK) { //const u64 stamp2 = get_system_time(); if (SMutexResult res = mutex->m_mutex.trylock(tid)) { if (res != SMR_FAILED) { goto abort; } mutex->m_queue.push(tid); switch (mutex->m_mutex.lock(tid)) { case SMR_OK: mutex->m_queue.invalidate(tid); case SMR_SIGNAL: break; default: goto abort; } } mutex->recursive = 1; const volatile u64 stamp = cond->signal_stamp; cond->signal.unlock(tid); Emu.GetCPU().NotifyThread(cond->signaler); //ConLog.Write("sys_cond_wait(): signal latency %lld (minimum %lld)", get_system_time() - stamp, stamp2 - stamp); return CELL_OK; } SM_Sleep(); if (counter++ > max_counter) { cond->m_queue.invalidate(tid); GetCurrentPPUThread().owned_mutexes--; // ??? return CELL_ETIMEDOUT; } if (Emu.IsStopped()) { goto abort; } } abort: sys_cond.Warning("sys_cond_wait(id=%d) aborted", cond_id); return CELL_OK; }