diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index 7cffdbc2bf..52c0442956 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -19,14 +19,29 @@ RawSPUThread::~RawSPUThread() void RawSPUThread::start() { - status.write_relaxed(SPU_STATUS_RUNNING); + bool do_start; - // calling Exec() directly in SIGSEGV handler may cause problems - // (probably because Exec() creates new thread, faults of this thread aren't handled by this handler anymore) - Emu.GetCallbackManager().Async([this](PPUThread& PPU) + status.atomic_op([&do_start](u32& status) { - FastRun(); + if (status & SPU_STATUS_RUNNING) + { + do_start = false; + } + else + { + status = SPU_STATUS_RUNNING; + do_start = true; + } }); + + if (do_start) + { + // starting thread directly in SIGSEGV handler may cause problems + Emu.GetCallbackManager().Async([this](PPUThread& PPU) + { + FastRun(); + }); + } } bool RawSPUThread::ReadReg(const u32 addr, u32& value) @@ -55,7 +70,7 @@ bool RawSPUThread::ReadReg(const u32 addr, u32& value) case SPU_MBox_Status_offs: { - value = (ch_out_mbox.get_count() & 0xff) | ((4 - ch_in_mbox.get_count()) << 8 & 0xff) | (ch_out_intr_mbox.get_count() << 16 & 0xff); + value = (ch_out_mbox.get_count() & 0xff) | ((4 - ch_in_mbox.get_count()) << 8 & 0xff00) | (ch_out_intr_mbox.get_count() << 16 & 0xff0000); return true; } diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 63c1b12dd8..cc2a7b0d09 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -307,7 +307,7 @@ void SPUThread::process_mfc_cmd(u32 cmd) { if (Ini.HLELogging.GetValue()) { - LOG_NOTICE(SPU, "DMA %s: cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", get_mfc_cmd_name(cmd), ch_mfc_args.lsa, ch_mfc_args.ea, ch_mfc_args.tag, ch_mfc_args.size, cmd); + LOG_NOTICE(SPU, "DMA %s: cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", get_mfc_cmd_name(cmd), cmd, ch_mfc_args.lsa, ch_mfc_args.ea, ch_mfc_args.tag, ch_mfc_args.size); } switch (cmd) @@ -398,11 +398,11 @@ void SPUThread::process_mfc_cmd(u32 cmd) // tag may be used here } - break; + return; } } - LOG_ERROR(SPU, "Unknown DMA %s: cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", get_mfc_cmd_name(cmd), ch_mfc_args.lsa, ch_mfc_args.ea, ch_mfc_args.tag, ch_mfc_args.size, cmd); + LOG_ERROR(SPU, "Unknown DMA %s: cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", get_mfc_cmd_name(cmd), cmd, ch_mfc_args.lsa, ch_mfc_args.ea, ch_mfc_args.tag, ch_mfc_args.size); throw __FUNCTION__; } @@ -614,9 +614,8 @@ void SPUThread::set_ch_value(u32 ch, u32 value) return; } - queue->events.emplace_back(SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data); + queue->push(SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data); ch_in_mbox.push_uncond(CELL_OK); - queue->cv.notify_one(); return; } else if (code < 128) @@ -654,8 +653,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value) return; } - queue->events.emplace_back(SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data); - queue->cv.notify_one(); + queue->push(SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data); return; } else if (code == 128) @@ -681,22 +679,24 @@ void SPUThread::set_ch_value(u32 ch, u32 value) LOG_WARNING(SPU, "sys_event_flag_set_bit(id=%d, value=0x%x (flag=%d))", data, value, flag); } - std::shared_ptr ef; + LV2_LOCK; + + std::shared_ptr ef; + if (!Emu.GetIdManager().GetIDData(data, ef)) { - LOG_ERROR(SPU, "sys_event_flag_set_bit(id=%d, value=0x%x (flag=%d)): EventFlag not found", data, value, flag); ch_in_mbox.push_uncond(CELL_ESRCH); return; } - std::lock_guard lock(ef->mutex); - - ef->flags |= (u64)1 << flag; - if (u32 target = ef->check()) + while (ef->waiters < 0) { - ef->signal.push(target); + ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); } + ef->flags |= 1ull << flag; + ef->cv.notify_all(); + ch_in_mbox.push_uncond(CELL_OK); return; } @@ -723,21 +723,22 @@ void SPUThread::set_ch_value(u32 ch, u32 value) LOG_WARNING(SPU, "sys_event_flag_set_bit_impatient(id=%d, value=0x%x (flag=%d))", data, value, flag); } - std::shared_ptr ef; + LV2_LOCK; + + std::shared_ptr ef; + if (!Emu.GetIdManager().GetIDData(data, ef)) { - LOG_WARNING(SPU, "sys_event_flag_set_bit_impatient(id=%d, value=0x%x (flag=%d)): EventFlag not found", data, value, flag); return; } - std::lock_guard lock(ef->mutex); - - ef->flags |= (u64)1 << flag; - if (u32 target = ef->check()) + while (ef->waiters < 0) { - ef->signal.push(target); + ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); } + ef->flags |= 1ull << flag; + ef->cv.notify_all(); return; } else @@ -968,24 +969,38 @@ void SPUThread::stop_and_signal(u32 code) LV2_LOCK; - auto found = this->spuq.find(spuq); - if (found == this->spuq.end()) + std::shared_ptr queue; + + for (auto& v : this->spuq) + { + if (spuq == v.first) + { + queue = v.second.lock(); + + if (queue) + { + break; + } + } + } + + if (!queue) { ch_in_mbox.push_uncond(CELL_EINVAL); // TODO: check error value return; } - - std::shared_ptr queue = found->second; // protocol is ignored in current implementation queue->waiters++; + assert(queue->waiters > 0); while (queue->events.empty()) { if (queue->waiters < 0) { - ch_in_mbox.push_uncond(CELL_ECANCELED); queue->waiters--; + assert(queue->waiters < 0); + ch_in_mbox.push_uncond(CELL_ECANCELED); return; } @@ -1006,6 +1021,13 @@ void SPUThread::stop_and_signal(u32 code) queue->events.pop_front(); queue->waiters--; + assert(queue->waiters >= 0); + + if (queue->events.size()) + { + queue->cv.notify_one(); + } + return; } @@ -1046,7 +1068,7 @@ void SPUThread::stop_and_signal(u32 code) group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED; group->exit_status = value; - group->join_state |= STGJSF_GROUP_EXIT; + group->join_state |= SPU_TGJSF_GROUP_EXIT; group->join_cv.notify_one(); return; } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index eec3c5af40..db395bb770 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -504,9 +504,9 @@ public: spu_interrupt_tag_t int0; // SPU Class 0 Interrupt Management spu_interrupt_tag_t int2; // SPU Class 2 Interrupt Management - std::weak_ptr tg; // SPU Thread Group Id + std::weak_ptr tg; // SPU Thread Group - std::unordered_map> spuq; // Event Queue Keys for SPU Thread + std::array>, 32> spuq; // Event Queue Keys for SPU Thread std::weak_ptr spup[64]; // SPU Ports void write_snr(bool number, u32 value) diff --git a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp index a5245772e1..2f45e35988 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp @@ -393,19 +393,18 @@ s32 cellAudioInit() keys.resize(g_audio.keys.size()); memcpy(keys.data(), g_audio.keys.data(), sizeof(u64) * keys.size()); } - for (u32 i = 0; i < keys.size(); i++) { - auto eq = Emu.GetEventManager().GetEventQueue(keys[i]); + LV2_LOCK; - if (eq) + for (u32 i = 0; i < keys.size(); i++) { - LV2_LOCK; - - // TODO: check event source - eq->events.emplace_back(0x10103000e010e07, 0, 0, 0); - eq->cv.notify_one(); + if (std::shared_ptr queue = Emu.GetEventManager().GetEventQueue(keys[i])) + { + queue->push(0, 0, 0, 0); // TODO: check arguments + } } } + //const u64 stamp3 = get_system_time(); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_event.cpp b/rpcs3/Emu/SysCalls/lv2/sys_event.cpp index 8d80c691bd..96cbdd5fe8 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_event.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_event.cpp @@ -65,6 +65,7 @@ s32 sys_event_queue_create(vm::ptr equeue_id, vm::ptr } *equeue_id = sys_event.GetNewId(queue, TYPE_EVENT_QUEUE); + return CELL_OK; } @@ -75,6 +76,7 @@ s32 sys_event_queue_destroy(u32 equeue_id, s32 mode) LV2_LOCK; std::shared_ptr queue; + if (!Emu.GetIdManager().GetIDData(equeue_id, queue)) { return CELL_ESRCH; @@ -85,6 +87,8 @@ s32 sys_event_queue_destroy(u32 equeue_id, s32 mode) return CELL_EINVAL; } + assert(queue->waiters >= 0); + if (!mode && queue->waiters) { return CELL_EBUSY; @@ -93,10 +97,12 @@ s32 sys_event_queue_destroy(u32 equeue_id, s32 mode) { // set special value for waiters queue->waiters.exchange(-1); + queue->cv.notify_all(); } Emu.GetEventManager().UnregisterKey(queue->key); Emu.GetIdManager().RemoveID(equeue_id); + return CELL_OK; } @@ -107,6 +113,7 @@ s32 sys_event_queue_tryreceive(u32 equeue_id, vm::ptr event_array, LV2_LOCK; std::shared_ptr queue; + if (!Emu.GetIdManager().GetIDData(equeue_id, queue)) { return CELL_ESRCH; @@ -133,6 +140,12 @@ s32 sys_event_queue_tryreceive(u32 equeue_id, vm::ptr event_array, } *number = count; + + if (queue->events.size()) + { + queue->cv.notify_one(); + } + return CELL_OK; } @@ -145,6 +158,7 @@ s32 sys_event_queue_receive(PPUThread& CPU, u32 equeue_id, vm::ptr LV2_LOCK; std::shared_ptr queue; + if (!Emu.GetIdManager().GetIDData(equeue_id, queue)) { return CELL_ESRCH; @@ -157,18 +171,21 @@ s32 sys_event_queue_receive(PPUThread& CPU, u32 equeue_id, vm::ptr // protocol is ignored in current implementation queue->waiters++; + assert(queue->waiters > 0); while (queue->events.empty()) { if (queue->waiters < 0) { queue->waiters--; + assert(queue->waiters < 0); return CELL_ECANCELED; } if (timeout && get_system_time() - start_time > timeout) { queue->waiters--; + assert(queue->waiters >= 0); return CELL_ETIMEDOUT; } @@ -190,6 +207,13 @@ s32 sys_event_queue_receive(PPUThread& CPU, u32 equeue_id, vm::ptr queue->events.pop_front(); queue->waiters--; + assert(queue->waiters >= 0); + + if (queue->events.size()) + { + queue->cv.notify_one(); + } + return CELL_OK; } @@ -200,12 +224,14 @@ s32 sys_event_queue_drain(u32 equeue_id) LV2_LOCK; std::shared_ptr queue; + if (!Emu.GetIdManager().GetIDData(equeue_id, queue)) { return CELL_ESRCH; } queue->events = {}; + return CELL_OK; } @@ -231,6 +257,7 @@ s32 sys_event_port_create(vm::ptr eport_id, s32 port_type, u64 name) std::shared_ptr eport(new event_port_t(port_type, name)); *eport_id = sys_event.GetNewId(eport, TYPE_EVENT_PORT); + return CELL_OK; } @@ -241,6 +268,7 @@ s32 sys_event_port_destroy(u32 eport_id) LV2_LOCK; std::shared_ptr port; + if (!Emu.GetIdManager().GetIDData(eport_id, port)) { return CELL_ESRCH; @@ -252,6 +280,7 @@ s32 sys_event_port_destroy(u32 eport_id) } Emu.GetIdManager().RemoveID(eport_id); + return CELL_OK; } @@ -263,6 +292,7 @@ s32 sys_event_port_connect_local(u32 eport_id, u32 equeue_id) std::shared_ptr port; std::shared_ptr queue; + if (!Emu.GetIdManager().GetIDData(eport_id, port) || !Emu.GetIdManager().GetIDData(equeue_id, queue)) { return CELL_ESRCH; @@ -279,6 +309,7 @@ s32 sys_event_port_connect_local(u32 eport_id, u32 equeue_id) } port->queue = queue; + return CELL_OK; } @@ -289,6 +320,7 @@ s32 sys_event_port_disconnect(u32 eport_id) LV2_LOCK; std::shared_ptr port; + if (!Emu.GetIdManager().GetIDData(eport_id, port)) { return CELL_ESRCH; @@ -314,6 +346,7 @@ s32 sys_event_port_disconnect(u32 eport_id) //} port->queue.reset(); + return CELL_OK; } @@ -324,6 +357,7 @@ s32 sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) LV2_LOCK; std::shared_ptr port; + if (!Emu.GetIdManager().GetIDData(eport_id, port)) { return CELL_ESRCH; @@ -343,7 +377,7 @@ s32 sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) const u64 source = port->name ? port->name : ((u64)process_getpid() << 32) | (u64)eport_id; - queue->events.emplace_back(source, data1, data2, data3); - queue->cv.notify_one(); + queue->push(source, data1, data2, data3); + return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/sys_event.h b/rpcs3/Emu/SysCalls/lv2/sys_event.h index 7542dd871f..781df1d5a5 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_event.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_event.h @@ -79,7 +79,7 @@ struct event_queue_t std::deque events; - // TODO: use sleep queue, remove condition variable (use thread's one instead) + // TODO: use sleep queue, possibly remove condition variable std::condition_variable cv; std::atomic waiters; @@ -92,6 +92,12 @@ struct event_queue_t , waiters(0) { } + + void push(u64 source, u64 data1, u64 data2, u64 data3) + { + events.emplace_back(source, data1, data2, data3); + cv.notify_one(); + } }; struct event_port_t diff --git a/rpcs3/Emu/SysCalls/lv2/sys_event_flag.cpp b/rpcs3/Emu/SysCalls/lv2/sys_event_flag.cpp index 3708e8956e..84ca4eba40 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_event_flag.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_event_flag.cpp @@ -6,74 +6,30 @@ #include "Emu/CPU/CPUThreadManager.h" #include "Emu/Cell/PPUThread.h" #include "sleep_queue.h" +#include "sys_time.h" #include "sys_event_flag.h" SysCallBase sys_event_flag("sys_event_flag"); -u32 EventFlag::check() +s32 sys_event_flag_create(vm::ptr id, vm::ptr attr, u64 init) { - u32 target = 0; - u64 highest_prio = ~0ull; - const u64 flag_set = flags.read_sync(); + sys_event_flag.Warning("sys_event_flag_create(id=*0x%x, attr=*0x%x, init=0x%llx)", id, attr, init); - for (u32 i = 0; i < waiters.size(); i++) + LV2_LOCK; + + if (!id || !attr) { - if (((waiters[i].mode & SYS_EVENT_FLAG_WAIT_AND) && (flag_set & waiters[i].bitptn) == waiters[i].bitptn) || - ((waiters[i].mode & SYS_EVENT_FLAG_WAIT_OR) && (flag_set & waiters[i].bitptn))) - { - if (protocol == SYS_SYNC_FIFO) - { - target = waiters[i].tid; - break; - } - else if (protocol == SYS_SYNC_PRIORITY) - { - if (std::shared_ptr t = Emu.GetCPU().GetThread(waiters[i].tid)) - { - const u64 prio = t->GetPrio(); - if (prio < highest_prio) - { - highest_prio = prio; - target = waiters[i].tid; - } - } - else - { - assert(!"EventFlag::check(): waiter not found"); - } - } - else - { - assert(!"EventFlag::check(): unknown protocol"); - } - } - } - - return target; -} - -s32 sys_event_flag_create(vm::ptr eflag_id, vm::ptr attr, u64 init) -{ - sys_event_flag.Warning("sys_event_flag_create(eflag_id_addr=0x%x, attr_addr=0x%x, init=0x%llx)", eflag_id.addr(), attr.addr(), init); - - if (!eflag_id) - { - sys_event_flag.Error("sys_event_flag_create(): invalid memory access (eflag_id_addr=0x%x)", eflag_id.addr()); return CELL_EFAULT; } - if (!attr) - { - sys_event_flag.Error("sys_event_flag_create(): invalid memory access (attr_addr=0x%x)", attr.addr()); - return CELL_EFAULT; - } + const u32 protocol = attr->protocol; - switch (attr->protocol.data()) + switch (protocol) { - case se32(SYS_SYNC_PRIORITY): break; - case se32(SYS_SYNC_RETRY): sys_event_flag.Todo("SYS_SYNC_RETRY"); break; - case se32(SYS_SYNC_PRIORITY_INHERIT): sys_event_flag.Todo("SYS_SYNC_PRIORITY_INHERIT"); break; - case se32(SYS_SYNC_FIFO): break; + case SYS_SYNC_PRIORITY: break; + case SYS_SYNC_RETRY: sys_event_flag.Todo("SYS_SYNC_RETRY"); break; + case SYS_SYNC_PRIORITY_INHERIT: sys_event_flag.Todo("SYS_SYNC_PRIORITY_INHERIT"); break; + case SYS_SYNC_FIFO: break; default: sys_event_flag.Error("Unknown protocol (0x%x)", attr->protocol); return CELL_EINVAL; } @@ -83,44 +39,62 @@ s32 sys_event_flag_create(vm::ptr eflag_id, vm::ptr at return CELL_EINVAL; } - switch (attr->type.data()) + const u32 type = attr->type; + + switch (type) { - case se32(SYS_SYNC_WAITER_SINGLE): break; - case se32(SYS_SYNC_WAITER_MULTIPLE): break; + case SYS_SYNC_WAITER_SINGLE: break; + case SYS_SYNC_WAITER_MULTIPLE: break; default: sys_event_flag.Error("Unknown event flag type (0x%x)", attr->type); return CELL_EINVAL; } - std::shared_ptr ef(new EventFlag(init, attr->protocol, attr->type, attr->name_u64)); - u32 id = sys_event_flag.GetNewId(ef, TYPE_EVENT_FLAG); - *eflag_id = id; - sys_event_flag.Warning("*** event_flag created [%s] (protocol=0x%x, type=0x%x): id = %d", std::string(attr->name, 8).c_str(), attr->protocol, attr->type, id); + std::shared_ptr ef(new event_flag_t(init, protocol, type, attr->name_u64)); + + *id = sys_event_flag.GetNewId(ef, TYPE_EVENT_FLAG); return CELL_OK; } -s32 sys_event_flag_destroy(u32 eflag_id) +s32 sys_event_flag_destroy(u32 id) { - sys_event_flag.Warning("sys_event_flag_destroy(eflag_id=%d)", eflag_id); + sys_event_flag.Warning("sys_event_flag_destroy(id=%d)", id); - std::shared_ptr ef; - if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; + LV2_LOCK; - if (ef->waiters.size()) // ??? + std::shared_ptr ef; + + if (!Emu.GetIdManager().GetIDData(id, ef)) + { + return CELL_ESRCH; + } + + while (ef->waiters < 0) + { + ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); + } + + if (ef->waiters) { return CELL_EBUSY; } - Emu.GetIdManager().RemoveID(eflag_id); + Emu.GetIdManager().RemoveID(id); return CELL_OK; } -s32 sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, vm::ptr result, u64 timeout) +s32 sys_event_flag_wait(u32 id, u64 bitptn, u32 mode, vm::ptr 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); + sys_event_flag.Log("sys_event_flag_wait(id=%d, bitptn=0x%llx, mode=0x%x, result=*0x%x, timeout=0x%llx)", id, bitptn, mode, result, timeout); - if (result) *result = 0; + const u64 start_time = get_system_time(); + + LV2_LOCK; + + if (result) + { + *result = 0; + } switch (mode & 0xf) { @@ -131,130 +105,104 @@ s32 sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, vm::ptr result, switch (mode & ~0xf) { - case 0: break; // ??? + case 0: break; case SYS_EVENT_FLAG_WAIT_CLEAR: break; case SYS_EVENT_FLAG_WAIT_CLEAR_ALL: break; default: return CELL_EINVAL; } - std::shared_ptr ef; - if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; - - const u32 tid = GetCurrentPPUThread().GetId(); + std::shared_ptr ef; + if (!Emu.GetIdManager().GetIDData(id, ef)) { - std::lock_guard lock(ef->mutex); - - if (ef->type == SYS_SYNC_WAITER_SINGLE && ef->waiters.size() > 0) - { - return CELL_EPERM; - } - - EventFlagWaiter rec; - rec.bitptn = bitptn; - rec.mode = mode; - rec.tid = tid; - ef->waiters.push_back(rec); - - if (ef->check() == tid) - { - const u64 flag_set = ef->flags.read_sync(); - - 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 = flag_set; - } - return CELL_OK; - } + return CELL_ESRCH; } - u64 counter = 0; - const u64 max_counter = timeout ? (timeout / 1000) : ~0; + if (ef->type == SYS_SYNC_WAITER_SINGLE && ef->waiters) + { + return CELL_EPERM; + } + + while (ef->waiters < 0) + { + // wait until other threads return CELL_ECANCELED (to prevent modifying bit pattern) + ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); + } + + // protocol is ignored in current implementation + ef->waiters++; while (true) { - u32 signaled; - if (ef->signal.try_peek(signaled) && signaled == tid) + if (result) { - std::lock_guard lock(ef->mutex); + *result = ef->flags; + } - const u64 flag_set = ef->flags.read_sync(); + if (mode & SYS_EVENT_FLAG_WAIT_AND && (ef->flags & bitptn) == bitptn) + { + break; + } - ef->signal.pop(signaled); - - 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()) - { - ef->signal.push(target); - } - - if (result) - { - *result = flag_set; - } - return CELL_OK; - } - } + if (mode & SYS_EVENT_FLAG_WAIT_OR && ef->flags & bitptn) + { + break; + } + if (ef->waiters <= 0) + { + ef->waiters++; + assert(ef->waiters <= 0); return CELL_ECANCELED; } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack - - if (counter++ > max_counter) + if (timeout && get_system_time() - start_time > timeout) { - std::lock_guard lock(ef->mutex); - - for (u32 i = 0; i < ef->waiters.size(); i++) - { - if (ef->waiters[i].tid == tid) - { - ef->waiters.erase(ef->waiters.begin() + i); - break; - } - } + ef->waiters--; + assert(ef->waiters >= 0); return CELL_ETIMEDOUT; } if (Emu.IsStopped()) { - sys_event_flag.Warning("sys_event_flag_wait(id=%d) aborted", eflag_id); + sys_event_flag.Warning("sys_event_flag_wait(id=%d) aborted", id); return CELL_OK; } + + ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); } + + if (mode & SYS_EVENT_FLAG_WAIT_CLEAR) + { + ef->flags &= ~bitptn; + } + + if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL) + { + ef->flags = 0; + } + + ef->waiters--; + assert(ef->waiters >= 0); + + if (ef->flags) + { + ef->cv.notify_one(); + } + + return CELL_OK; } -s32 sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, vm::ptr result) +s32 sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ptr result) { - sys_event_flag.Log("sys_event_flag_trywait(eflag_id=%d, bitptn=0x%llx, mode=0x%x, result_addr=0x%x)", - eflag_id, bitptn, mode, result.addr()); + sys_event_flag.Log("sys_event_flag_trywait(id=%d, bitptn=0x%llx, mode=0x%x, result=*0x%x)", id, bitptn, mode, result); - if (result) *result = 0; + LV2_LOCK; + + if (result) + { + *result = 0; + } switch (mode & 0xf) { @@ -265,122 +213,149 @@ s32 sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, vm::ptr resu switch (mode & ~0xf) { - case 0: break; // ??? + case 0: break; case SYS_EVENT_FLAG_WAIT_CLEAR: break; case SYS_EVENT_FLAG_WAIT_CLEAR_ALL: break; default: return CELL_EINVAL; } - std::shared_ptr ef; - if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; - - std::lock_guard lock(ef->mutex); + std::shared_ptr ef; - const u64 flag_set = ef->flags.read_sync(); - - if (((mode & SYS_EVENT_FLAG_WAIT_AND) && (flag_set & bitptn) == bitptn) || - ((mode & SYS_EVENT_FLAG_WAIT_OR) && (flag_set & bitptn))) + if (!Emu.GetIdManager().GetIDData(id, ef)) { - 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 = flag_set; - } - - return CELL_OK; + return CELL_ESRCH; } - return CELL_EBUSY; + if (!((mode & SYS_EVENT_FLAG_WAIT_AND) && (ef->flags & bitptn) == bitptn) && !((mode & SYS_EVENT_FLAG_WAIT_OR) && (ef->flags & bitptn))) + { + return CELL_EBUSY; + } + + while (ef->waiters < 0) + { + ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(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 = ef->flags; + } + + return CELL_OK; } -s32 sys_event_flag_set(u32 eflag_id, u64 bitptn) +s32 sys_event_flag_set(u32 id, u64 bitptn) { - sys_event_flag.Log("sys_event_flag_set(eflag_id=%d, bitptn=0x%llx)", eflag_id, bitptn); + sys_event_flag.Log("sys_event_flag_set(id=%d, bitptn=0x%llx)", id, bitptn); - std::shared_ptr ef; - if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; + LV2_LOCK; - std::lock_guard lock(ef->mutex); + std::shared_ptr ef; + + if (!Emu.GetIdManager().GetIDData(id, ef)) + { + return CELL_ESRCH; + } + + while (ef->waiters < 0) + { + ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); + } ef->flags |= bitptn; - if (u32 target = ef->check()) - { - ef->signal.push(target); - } + ef->cv.notify_all(); + return CELL_OK; } -s32 sys_event_flag_clear(u32 eflag_id, u64 bitptn) +s32 sys_event_flag_clear(u32 id, u64 bitptn) { - sys_event_flag.Log("sys_event_flag_clear(eflag_id=%d, bitptn=0x%llx)", eflag_id, bitptn); + sys_event_flag.Log("sys_event_flag_clear(id=%d, bitptn=0x%llx)", id, bitptn); - std::shared_ptr ef; - if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; + LV2_LOCK; + + std::shared_ptr ef; + + if (!Emu.GetIdManager().GetIDData(id, ef)) + { + return CELL_ESRCH; + } + + while (ef->waiters < 0) + { + ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); + } - std::lock_guard lock(ef->mutex); ef->flags &= bitptn; + return CELL_OK; } -s32 sys_event_flag_cancel(u32 eflag_id, vm::ptr num) +s32 sys_event_flag_cancel(u32 id, vm::ptr num) { - sys_event_flag.Log("sys_event_flag_cancel(eflag_id=%d, num_addr=0x%x)", eflag_id, num.addr()); + sys_event_flag.Log("sys_event_flag_cancel(id=%d, num=*0x%x)", id, num); - std::shared_ptr ef; - if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; + LV2_LOCK; - std::vector tids; + if (num) { - std::lock_guard lock(ef->mutex); - - tids.resize(ef->waiters.size()); - for (u32 i = 0; i < ef->waiters.size(); i++) - { - tids[i] = ef->waiters[i].tid; - } - ef->waiters.clear(); + *num = 0; } - for (auto& v : tids) + std::shared_ptr ef; + + if (!Emu.GetIdManager().GetIDData(id, ef)) { - ef->signal.push(v); + return CELL_ESRCH; } - if (Emu.IsStopped()) + while (ef->waiters < 0) { - sys_event_flag.Warning("sys_event_flag_cancel(id=%d) aborted", eflag_id); - return CELL_OK; + ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); } if (num) { - *num = (u32)tids.size(); + *num = ef->waiters; } + // negate value to signal waiting threads and prevent modifying bit pattern + ef->waiters = -ef->waiters; + ef->cv.notify_all(); + return CELL_OK; } -s32 sys_event_flag_get(u32 eflag_id, vm::ptr flags) +s32 sys_event_flag_get(u32 id, vm::ptr flags) { - sys_event_flag.Log("sys_event_flag_get(eflag_id=%d, flags_addr=0x%x)", eflag_id, flags.addr()); + sys_event_flag.Log("sys_event_flag_get(id=%d, flags=*0x%x)", id, flags); + + LV2_LOCK; if (!flags) { - sys_event_flag.Error("sys_event_flag_create(): invalid memory access (flags_addr=0x%x)", flags.addr()); return CELL_EFAULT; } - std::shared_ptr ef; - if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; + std::shared_ptr ef; + + if (!Emu.GetIdManager().GetIDData(id, ef)) + { + *flags = 0; + + return CELL_ESRCH; + } + + *flags = ef->flags; - *flags = ef->flags.read_sync(); return CELL_OK; -} \ No newline at end of file +} diff --git a/rpcs3/Emu/SysCalls/lv2/sys_event_flag.h b/rpcs3/Emu/SysCalls/lv2/sys_event_flag.h index 2272ca8ca1..ee63ce8369 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_event_flag.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_event_flag.h @@ -19,6 +19,7 @@ struct sys_event_flag_attr be_t ipc_key; be_t flags; be_t type; + union { char name[8]; @@ -26,40 +27,33 @@ struct sys_event_flag_attr }; }; -struct EventFlagWaiter +struct event_flag_t { - u32 tid; - u32 mode; - u64 bitptn; -}; - -struct EventFlag -{ - atomic_le_t flags; - squeue_t signal; - std::mutex mutex; // protects waiters - std::vector waiters; + std::atomic flags; const u32 protocol; const s32 type; const u64 name; - EventFlag(u64 pattern, u32 protocol, s32 type, u64 name) - : protocol(protocol) + // TODO: use sleep queue, possibly remove condition variable + std::condition_variable cv; + std::atomic waiters; + + event_flag_t(u64 pattern, u32 protocol, s32 type, u64 name) + : flags(pattern) + , protocol(protocol) , type(type) , name(name) + , waiters(0) { - flags.write_relaxed(pattern); } - - u32 check(); }; -s32 sys_event_flag_create(vm::ptr eflag_id, vm::ptr attr, u64 init); -s32 sys_event_flag_destroy(u32 eflag_id); -s32 sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, vm::ptr result, u64 timeout); -s32 sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, vm::ptr result); -s32 sys_event_flag_set(u32 eflag_id, u64 bitptn); -s32 sys_event_flag_clear(u32 eflag_id, u64 bitptn); -s32 sys_event_flag_cancel(u32 eflag_id, vm::ptr num); -s32 sys_event_flag_get(u32 eflag_id, vm::ptr flags); \ No newline at end of file +s32 sys_event_flag_create(vm::ptr id, vm::ptr attr, u64 init); +s32 sys_event_flag_destroy(u32 id); +s32 sys_event_flag_wait(u32 id, u64 bitptn, u32 mode, vm::ptr result, u64 timeout); +s32 sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ptr result); +s32 sys_event_flag_set(u32 id, u64 bitptn); +s32 sys_event_flag_clear(u32 id, u64 bitptn); +s32 sys_event_flag_cancel(u32 id, vm::ptr num); +s32 sys_event_flag_get(u32 id, vm::ptr flags); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp b/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp index 03ea5e701a..148a7fa4e6 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp @@ -320,6 +320,13 @@ s32 sys_spu_thread_group_start(u32 id) } } + // because SPU_THREAD_GROUP_STATUS_READY is not possible, run event is delivered immediately + + if (std::shared_ptr queue = group->ep_run.lock()) + { + queue->push(SYS_SPU_THREAD_GROUP_EVENT_RUN_KEY, id, 0, 0); // TODO: check data2 and data3 + } + for (auto& t : group->threads) { if (t) @@ -485,7 +492,7 @@ s32 sys_spu_thread_group_terminate(u32 id, s32 value) group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED; group->exit_status = value; - group->join_state |= STGJSF_TERMINATED; + group->join_state |= SPU_TGJSF_TERMINATED; group->join_cv.notify_one(); return CELL_OK; } @@ -507,13 +514,13 @@ s32 sys_spu_thread_group_join(u32 id, vm::ptr cause, vm::ptr status) return CELL_ESTAT; } - if (group->join_state.fetch_or(STGJSF_IS_JOINING) & STGJSF_IS_JOINING) + if (group->join_state.fetch_or(SPU_TGJSF_IS_JOINING) & SPU_TGJSF_IS_JOINING) { // another PPU thread is joining this thread group return CELL_EBUSY; } - while ((group->join_state & ~STGJSF_IS_JOINING) == 0) + while ((group->join_state & ~SPU_TGJSF_IS_JOINING) == 0) { bool stopped = true; @@ -545,19 +552,19 @@ s32 sys_spu_thread_group_join(u32 id, vm::ptr cause, vm::ptr status) group->join_cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); } - switch (group->join_state & ~STGJSF_IS_JOINING) + switch (group->join_state & ~SPU_TGJSF_IS_JOINING) { case 0: { if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT; break; } - case STGJSF_GROUP_EXIT: + case SPU_TGJSF_GROUP_EXIT: { if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT; break; } - case STGJSF_TERMINATED: + case SPU_TGJSF_TERMINATED: { if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_TERMINATED; break; @@ -570,7 +577,7 @@ s32 sys_spu_thread_group_join(u32 id, vm::ptr cause, vm::ptr status) *status = group->exit_status; } - group->join_state &= ~STGJSF_IS_JOINING; + group->join_state &= ~SPU_TGJSF_IS_JOINING; group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED; // hack return CELL_OK; } @@ -729,14 +736,111 @@ s32 sys_spu_thread_write_snr(u32 id, u32 number, u32 value) s32 sys_spu_thread_group_connect_event(u32 id, u32 eq, u32 et) { - sys_spu.Todo("sys_spu_thread_group_connect_event(id=%d, eq=%d, et=0x%x)", id, eq, et); + sys_spu.Warning("sys_spu_thread_group_connect_event(id=%d, eq=%d, et=%d)", id, eq, et); + + LV2_LOCK; + + std::shared_ptr group; + std::shared_ptr queue; + + if (!Emu.GetIdManager().GetIDData(id, group) || !Emu.GetIdManager().GetIDData(eq, queue)) + { + return CELL_ESRCH; + } + + switch (et) + { + case SYS_SPU_THREAD_GROUP_EVENT_RUN: + { + if (!group->ep_run.expired()) + { + return CELL_EBUSY; + } + + group->ep_run = queue; + break; + } + case SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION: + { + if (!group->ep_exception.expired()) + { + return CELL_EBUSY; + } + + group->ep_exception = queue; + break; + } + case SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE: + { + if (!group->ep_sysmodule.expired()) + { + return CELL_EBUSY; + } + + group->ep_sysmodule = queue; + break; + } + default: + { + sys_spu.Error("sys_spu_thread_group_connect_event(): unknown event type (%d)", et); + return CELL_EINVAL; + } + } return CELL_OK; } s32 sys_spu_thread_group_disconnect_event(u32 id, u32 et) { - sys_spu.Todo("sys_spu_thread_group_disconnect_event(id=%d, et=0x%x)", id, et); + sys_spu.Warning("sys_spu_thread_group_disconnect_event(id=%d, et=%d)", id, et); + + LV2_LOCK; + + std::shared_ptr group; + + if (!Emu.GetIdManager().GetIDData(id, group)) + { + return CELL_ESRCH; + } + + switch (et) + { + case SYS_SPU_THREAD_GROUP_EVENT_RUN: + { + if (group->ep_run.expired()) + { + return CELL_ENOTCONN; + } + + group->ep_run.reset(); + break; + } + case SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION: + { + if (group->ep_exception.expired()) + { + return CELL_ENOTCONN; + } + + group->ep_exception.reset(); + break; + } + case SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE: + { + if (group->ep_sysmodule.expired()) + { + return CELL_ENOTCONN; + } + + group->ep_sysmodule.reset(); + break; + } + default: + { + sys_spu.Error("sys_spu_thread_group_disconnect_event(): unknown event type (%d)", et); + return CELL_EINVAL; + } + } return CELL_OK; } @@ -839,20 +943,29 @@ s32 sys_spu_thread_bind_queue(u32 id, u32 spuq, u32 spuq_num) return CELL_EINVAL; } - if (spu.spuq.size() >= 32) + for (auto& v : spu.spuq) { - return CELL_EAGAIN; + if (auto q = v.second.lock()) + { + if (v.first == spuq_num || q == queue) + { + return CELL_EBUSY; + } + } } - auto found = spu.spuq.find(spuq_num); - if (found != spu.spuq.end()) + for (auto& v : spu.spuq) { - return CELL_EBUSY; + if (v.second.expired()) + { + v.first = spuq_num; + v.second = queue; + + return CELL_OK; + } } - spu.spuq[spuq_num] = queue; - - return CELL_OK; + return CELL_EAGAIN; } s32 sys_spu_thread_unbind_queue(u32 id, u32 spuq_num) @@ -870,15 +983,17 @@ s32 sys_spu_thread_unbind_queue(u32 id, u32 spuq_num) auto& spu = static_cast(*t); - auto found = spu.spuq.find(spuq_num); - if (found == spu.spuq.end()) + for (auto& v : spu.spuq) { - return CELL_ESRCH; + if (v.first == spuq_num && !v.second.expired()) + { + v.second.reset(); + + return CELL_OK; + } } - spu.spuq.erase(found); - - return CELL_OK; + return CELL_ESRCH; } s32 sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq, u64 req, vm::ptr spup) diff --git a/rpcs3/Emu/SysCalls/lv2/sys_spu.h b/rpcs3/Emu/SysCalls/lv2/sys_spu.h index 9879ecbd7b..cfa67f082c 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_spu.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_spu.h @@ -1,5 +1,7 @@ #pragma once +struct event_queue_t; + enum : s32 { SYS_SPU_THREAD_GROUP_TYPE_NORMAL = 0x00, @@ -132,9 +134,9 @@ struct spu_arg_t // SPU Thread Group Join State Flag enum : u32 { - STGJSF_IS_JOINING = (1 << 0), - STGJSF_TERMINATED = (1 << 1), // set if SPU Thread Group is terminated by sys_spu_thread_group_terminate - STGJSF_GROUP_EXIT = (1 << 2), // set if SPU Thread Group is terminated by sys_spu_thread_group_exit + SPU_TGJSF_IS_JOINING = (1 << 0), + SPU_TGJSF_TERMINATED = (1 << 1), // set if SPU Thread Group is terminated by sys_spu_thread_group_terminate + SPU_TGJSF_GROUP_EXIT = (1 << 2), // set if SPU Thread Group is terminated by sys_spu_thread_group_exit }; struct spu_group_t @@ -144,9 +146,9 @@ struct spu_group_t const s32 type; // SPU Thread Group Type const u32 ct; // Memory Container Id - std::array, 256> threads; + std::array, 256> threads; // SPU Threads + std::array, 256> images; // SPU Images std::array args; // SPU Thread Arguments - std::array, 256> images; // SPU Thread Images s32 prio; // SPU Thread Group Priority u32 state; // SPU Thread Group State @@ -155,6 +157,10 @@ struct spu_group_t std::atomic join_state; // flags used to detect exit cause std::condition_variable join_cv; // used to signal waiting PPU thread + std::weak_ptr ep_run; // port for SYS_SPU_THREAD_GROUP_EVENT_RUN events + std::weak_ptr ep_exception; // TODO: SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION + std::weak_ptr ep_sysmodule; // TODO: SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE + spu_group_t(std::string name, u32 num, s32 prio, s32 type, u32 ct) : name(name) , num(num)