s32 sys_event_flag_set(u32 eflag_id, u64 bitptn) { sys_event_flag.Log("sys_event_flag_set(eflag_id=%d, bitptn=0x%llx)", eflag_id, bitptn); EventFlag* ef; if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; u32 tid = GetCurrentPPUThread().GetId(); ef->m_mutex.lock(tid); ef->flags |= bitptn; if (u32 target = ef->check()) { // if signal, leave both mutexes locked... ef->signal.lock(target); ef->m_mutex.unlock(tid, target); } else { ef->m_mutex.unlock(tid); } return CELL_OK; }
s32 sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, vm::ptr<u64> result, u64 timeout) { sys_event_flag.Log("sys_event_flag_wait(eflag_id=%d, bitptn=0x%llx, mode=0x%x, result_addr=0x%x, timeout=%lld)", eflag_id, bitptn, mode, result.addr(), timeout); if (result) *result = 0; switch (mode & 0xf) { case SYS_EVENT_FLAG_WAIT_AND: break; case SYS_EVENT_FLAG_WAIT_OR: break; default: return CELL_EINVAL; } switch (mode & ~0xf) { case 0: break; // ??? case SYS_EVENT_FLAG_WAIT_CLEAR: break; case SYS_EVENT_FLAG_WAIT_CLEAR_ALL: break; default: return CELL_EINVAL; } EventFlag* ef; if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; const u32 tid = GetCurrentPPUThread().GetId(); { ef->m_mutex.lock(tid); if (ef->m_type == SYS_SYNC_WAITER_SINGLE && ef->waiters.size() > 0) { ef->m_mutex.unlock(tid); return CELL_EPERM; } EventFlagWaiter rec; rec.bitptn = bitptn; rec.mode = mode; rec.tid = tid; ef->waiters.push_back(rec); if (ef->check() == tid) { u64 flags = ef->flags; ef->waiters.erase(ef->waiters.end() - 1); if (mode & SYS_EVENT_FLAG_WAIT_CLEAR) { ef->flags &= ~bitptn; } else if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL) { ef->flags = 0; } if (result) *result = flags; ef->m_mutex.unlock(tid); return CELL_OK; } ef->m_mutex.unlock(tid); } u64 counter = 0; const u64 max_counter = timeout ? (timeout / 1000) : ~0; while (true) { if (ef->signal.unlock(tid, tid) == SMR_OK) { ef->m_mutex.lock(tid); u64 flags = ef->flags; for (u32 i = 0; i < ef->waiters.size(); i++) { if (ef->waiters[i].tid == tid) { ef->waiters.erase(ef->waiters.begin() + i); if (mode & SYS_EVENT_FLAG_WAIT_CLEAR) { ef->flags &= ~bitptn; } else if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL) { ef->flags = 0; } if (u32 target = ef->check()) { // if signal, leave both mutexes locked... ef->signal.unlock(tid, target); ef->m_mutex.unlock(tid, target); } else { ef->signal.unlock(tid); } if (result) *result = flags; ef->m_mutex.unlock(tid); return CELL_OK; } } ef->signal.unlock(tid); ef->m_mutex.unlock(tid); return CELL_ECANCELED; } std::this_thread::sleep_for(std::chrono::milliseconds(1)); if (counter++ > max_counter) { ef->m_mutex.lock(tid); for (u32 i = 0; i < ef->waiters.size(); i++) { if (ef->waiters[i].tid == tid) { ef->waiters.erase(ef->waiters.begin() + i); break; } } ef->m_mutex.unlock(tid); return CELL_ETIMEDOUT; } if (Emu.IsStopped()) { sys_event_flag.Warning("sys_event_flag_wait(id=%d) aborted", eflag_id); return CELL_OK; } } }
void SPUThread::WriteChannel(u32 ch, const u128& r) { const u32 v = r._u32[3]; switch (ch) { case SPU_WrOutIntrMbox: { if (!group) // if RawSPU { if (Ini.HLELogging.GetValue()) LOG_NOTICE(Log::SPU, "SPU_WrOutIntrMbox: interrupt(v=0x%x)", v); while (!SPU.Out_IntrMBox.Push(v)) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); if (Emu.IsStopped()) { LOG_WARNING(Log::SPU, "%s(%s) aborted", __FUNCTION__, spu_ch_name[ch]); return; } } m_intrtag[2].stat |= 1; if (CPUThread* t = Emu.GetCPU().GetThread(m_intrtag[2].thread)) { if (t->GetType() == CPU_THREAD_PPU) { if (t->IsAlive()) { LOG_ERROR(Log::SPU, "%s(%s): interrupt thread was alive", __FUNCTION__, spu_ch_name[ch]); Emu.Pause(); return; } PPUThread& ppu = *(PPUThread*)t; ppu.GPR[3] = ppu.m_interrupt_arg; ppu.FastCall2(vm::read32(ppu.entry), vm::read32(ppu.entry + 4)); } } } else { const u8 code = v >> 24; if (code < 64) { /* ===== sys_spu_thread_send_event (used by spu_printf) ===== */ u8 spup = code & 63; u32 data; if (!SPU.Out_MBox.Pop(data)) { LOG_ERROR(Log::SPU, "sys_spu_thread_send_event(v=0x%x, spup=%d): Out_MBox is empty", v, spup); return; } if (Ini.HLELogging.GetValue()) { LOG_NOTICE(Log::SPU, "sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x)", spup, v & 0x00ffffff, data); } EventPort& port = SPUPs[spup]; std::lock_guard<std::mutex> lock(port.m_mutex); if (!port.eq) { LOG_WARNING(Log::SPU, "sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected", spup, (v & 0x00ffffff), data); SPU.In_MBox.PushUncond(CELL_ENOTCONN); // TODO: check error passing return; } if (!port.eq->events.push(SYS_SPU_THREAD_EVENT_USER_KEY, GetCurrentCPUThread()->GetId(), ((u64)spup << 32) | (v & 0x00ffffff), data)) { SPU.In_MBox.PushUncond(CELL_EBUSY); return; } SPU.In_MBox.PushUncond(CELL_OK); return; } else if (code < 128) { /* ===== sys_spu_thread_throw_event ===== */ const u8 spup = code & 63; u32 data; if (!SPU.Out_MBox.Pop(data)) { LOG_ERROR(Log::SPU, "sys_spu_thread_throw_event(v=0x%x, spup=%d): Out_MBox is empty", v, spup); return; } //if (Ini.HLELogging.GetValue()) { LOG_WARNING(Log::SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x)", spup, v & 0x00ffffff, data); } EventPort& port = SPUPs[spup]; std::lock_guard<std::mutex> lock(port.m_mutex); if (!port.eq) { LOG_WARNING(Log::SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected", spup, (v & 0x00ffffff), data); return; } // TODO: check passing spup value if (!port.eq->events.push(SYS_SPU_THREAD_EVENT_USER_KEY, GetCurrentCPUThread()->GetId(), ((u64)spup << 32) | (v & 0x00ffffff), data)) { LOG_WARNING(Log::SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x) failed (queue is full)", spup, (v & 0x00ffffff), data); return; } return; } else if (code == 128) { /* ===== sys_event_flag_set_bit ===== */ u32 flag = v & 0xffffff; u32 data; if (!SPU.Out_MBox.Pop(data)) { LOG_ERROR(Log::SPU, "sys_event_flag_set_bit(v=0x%x (flag=%d)): Out_MBox is empty", v, flag); return; } if (flag > 63) { LOG_ERROR(Log::SPU, "sys_event_flag_set_bit(id=%d, v=0x%x): flag > 63", data, v, flag); return; } //if (Ini.HLELogging.GetValue()) { LOG_WARNING(Log::SPU, "sys_event_flag_set_bit(id=%d, v=0x%x (flag=%d))", data, v, flag); } EventFlag* ef; if (!Emu.GetIdManager().GetIDData(data, ef)) { LOG_ERROR(Log::SPU, "sys_event_flag_set_bit(id=%d, v=0x%x (flag=%d)): EventFlag not found", data, v, flag); SPU.In_MBox.PushUncond(CELL_ESRCH); return; } u32 tid = GetCurrentCPUThread()->GetId(); ef->m_mutex.lock(tid); ef->flags |= (u64)1 << flag; if (u32 target = ef->check()) { // if signal, leave both mutexes locked... ef->signal.lock(target); ef->m_mutex.unlock(tid, target); } else { ef->m_mutex.unlock(tid); } SPU.In_MBox.PushUncond(CELL_OK); return; } else if (code == 192) { /* ===== sys_event_flag_set_bit_impatient ===== */ u32 flag = v & 0xffffff; u32 data; if (!SPU.Out_MBox.Pop(data)) { LOG_ERROR(Log::SPU, "sys_event_flag_set_bit_impatient(v=0x%x (flag=%d)): Out_MBox is empty", v, flag); return; } if (flag > 63) { LOG_ERROR(Log::SPU, "sys_event_flag_set_bit_impatient(id=%d, v=0x%x): flag > 63", data, v, flag); return; } //if (Ini.HLELogging.GetValue()) { LOG_WARNING(Log::SPU, "sys_event_flag_set_bit_impatient(id=%d, v=0x%x (flag=%d))", data, v, flag); } EventFlag* ef; if (!Emu.GetIdManager().GetIDData(data, ef)) { LOG_WARNING(Log::SPU, "sys_event_flag_set_bit_impatient(id=%d, v=0x%x (flag=%d)): EventFlag not found", data, v, flag); return; } u32 tid = GetCurrentCPUThread()->GetId(); ef->m_mutex.lock(tid); ef->flags |= (u64)1 << flag; if (u32 target = ef->check()) { // if signal, leave both mutexes locked... ef->signal.lock(target); ef->m_mutex.unlock(tid, target); } else { ef->m_mutex.unlock(tid); } return; } else { u32 data; if (SPU.Out_MBox.Pop(data)) { LOG_ERROR(Log::SPU, "SPU_WrOutIntrMbox: unknown data (v=0x%x); Out_MBox = 0x%x", v, data); } else { LOG_ERROR(Log::SPU, "SPU_WrOutIntrMbox: unknown data (v=0x%x)", v); } SPU.In_MBox.PushUncond(CELL_EINVAL); // ??? return; } } break; } case SPU_WrOutMbox: { while (!SPU.Out_MBox.Push(v) && !Emu.IsStopped()) std::this_thread::sleep_for(std::chrono::milliseconds(1)); break; } case MFC_WrTagMask: { MFC1.QueryMask.SetValue(v); break; } case MFC_WrTagUpdate: { MFC1.TagStatus.PushUncond(MFC1.QueryMask.GetValue()); break; } case MFC_LSA: { MFC1.LSA.SetValue(v); break; } case MFC_EAH: { MFC1.EAH.SetValue(v); break; } case MFC_EAL: { MFC1.EAL.SetValue(v); break; } case MFC_Size: { MFC1.Size_Tag.SetValue((MFC1.Size_Tag.GetValue() & 0xffff) | (v << 16)); break; } case MFC_TagID: { MFC1.Size_Tag.SetValue((MFC1.Size_Tag.GetValue() & ~0xffff) | (v & 0xffff)); break; } case MFC_Cmd: { MFC1.CMDStatus.SetValue(v); EnqMfcCmd(MFC1); break; } case MFC_WrListStallAck: { if (v >= 32) { LOG_ERROR(Log::SPU, "MFC_WrListStallAck error: invalid tag(%d)", v); return; } StalledList temp = StallList[v]; if (!temp.MFCArgs) { LOG_ERROR(Log::SPU, "MFC_WrListStallAck error: empty tag(%d)", v); return; } StallList[v].MFCArgs = nullptr; ListCmd(temp.lsa, temp.ea, temp.tag, temp.size, temp.cmd, *temp.MFCArgs); break; } case SPU_WrDec: { m_dec_start = get_time(); m_dec_value = v; break; } case SPU_WrEventMask: { m_event_mask = v; if (v & ~(SPU_EVENT_IMPLEMENTED)) LOG_ERROR(Log::SPU, "SPU_WrEventMask: unsupported event masked (0x%x)"); break; } case SPU_WrEventAck: { m_events &= ~v; break; } default: { LOG_ERROR(Log::SPU, "%s error (v=0x%x): unknown/illegal channel (%d [%s]).", __FUNCTION__, v, ch, spu_ch_name[ch]); break; } } if (Emu.IsStopped()) LOG_WARNING(Log::SPU, "%s(%s) aborted", __FUNCTION__, spu_ch_name[ch]); }
int sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result, u64 timeout) { sys_event_flag.Warning("sys_event_flag_wait(eflag_id=%d, bitptn=0x%llx, mode=0x%x, result_addr=0x%x, timeout=%lld)", eflag_id, bitptn, mode, result.GetAddr(), timeout); if (result.IsGood()) result = 0; switch (mode & 0xf) { case SYS_EVENT_FLAG_WAIT_AND: break; case SYS_EVENT_FLAG_WAIT_OR: break; default: return CELL_EINVAL; } switch (mode & ~0xf) { case 0: break; // ??? case SYS_EVENT_FLAG_WAIT_CLEAR: break; case SYS_EVENT_FLAG_WAIT_CLEAR_ALL: break; default: return CELL_EINVAL; } EventFlag* ef; if(!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; u32 tid = GetCurrentPPUThread().GetId(); { SMutexLocker lock(ef->m_mutex); if (ef->m_type == SYS_SYNC_WAITER_SINGLE && ef->waiters.GetCount() > 0) { return CELL_EPERM; } EventFlagWaiter rec; rec.bitptn = bitptn; rec.mode = mode; rec.tid = tid; ef->waiters.AddCpy(rec); if (ef->check() == tid) { u64 flags = ef->flags; ef->waiters.RemoveAt(ef->waiters.GetCount() - 1); if (mode & SYS_EVENT_FLAG_WAIT_CLEAR) { ef->flags &= ~bitptn; } else if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL) { ef->flags = 0; } if (result.IsGood()) { result = flags; return CELL_OK; } if (!result.GetAddr()) { return CELL_OK; } return CELL_EFAULT; } } u32 counter = 0; const u32 max_counter = timeout ? (timeout / 1000) : ~0; while (true) { if (ef->signal.GetOwner() == tid) { SMutexLocker lock(ef->m_mutex); ef->signal.unlock(tid); u64 flags = ef->flags; for (u32 i = 0; i < ef->waiters.GetCount(); i++) { if (ef->waiters[i].tid == tid) { ef->waiters.RemoveAt(i); if (mode & SYS_EVENT_FLAG_WAIT_CLEAR) { ef->flags &= ~bitptn; } else if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL) { ef->flags = 0; } if (result.IsGood()) { result = flags; return CELL_OK; } if (!result.GetAddr()) { return CELL_OK; } return CELL_EFAULT; } } return CELL_ECANCELED; } Sleep(1); if (counter++ > max_counter) { SMutexLocker lock(ef->m_mutex); for (u32 i = 0; i < ef->waiters.GetCount(); i++) { if (ef->waiters[i].tid == tid) { ef->waiters.RemoveAt(i); break; } } return CELL_ETIMEDOUT; } if (Emu.IsStopped()) { ConLog.Warning("sys_event_flag_wait(id=%d) aborted", eflag_id); return CELL_OK; } } }