diff --git a/rpcs3/Emu/Cell/lv2/sys_event.cpp b/rpcs3/Emu/Cell/lv2/sys_event.cpp index 3047f87fa6..50615f31e1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event.cpp @@ -119,8 +119,13 @@ std::shared_ptr lv2_event_queue::find(u64 ipc_key) extern void resume_spu_thread_group_from_waiting(spu_thread& spu); -CellError lv2_event_queue::send(lv2_event event) +CellError lv2_event_queue::send(lv2_event event, bool* notified_thread, lv2_event_port* port) { + if (notified_thread) + { + *notified_thread = false; + } + std::lock_guard lock(mutex); if (!exists) @@ -140,6 +145,18 @@ CellError lv2_event_queue::send(lv2_event event) return CELL_EBUSY; } + if (port) + { + // Block event port disconnection for the time being of sending events + port->is_busy++; + ensure(notified_thread); + } + + if (notified_thread) + { + *notified_thread = true; + } + if (type == SYS_PPU_QUEUE) { // Store event in registers @@ -709,7 +726,10 @@ error_code sys_event_port_disconnect(ppu_thread& ppu, u32 eport_id) return CELL_ENOTCONN; } - // TODO: return CELL_EBUSY if necessary (can't detect the condition) + if (port->is_busy) + { + return CELL_EBUSY; + } port->queue.reset(); @@ -718,20 +738,32 @@ error_code sys_event_port_disconnect(ppu_thread& ppu, u32 eport_id) error_code sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) { - if (auto cpu = get_current_cpu_thread()) + const auto cpu = cpu_thread::get_current(); + const auto ppu = cpu ? cpu->try_get() : nullptr; + + if (cpu) { cpu->state += cpu_flag::wait; } sys_event.trace("sys_event_port_send(eport_id=0x%x, data1=0x%llx, data2=0x%llx, data3=0x%llx)", eport_id, data1, data2, data3); + bool notified_thread = false; + const auto port = idm::check(eport_id, [&, notify = lv2_obj::notify_all_t()](lv2_event_port& port) -> CellError { + if (ppu && ppu->loaded_from_savestate) + { + port.is_busy++; + notified_thread = true; + return {}; + } + if (lv2_obj::check(port.queue)) { const u64 source = port.name ? port.name : (u64{process_getpid() + 0u} << 32) | u64{eport_id}; - return port.queue->send(source, data1, data2, data3); + return port.queue->send(source, data1, data2, data3, ¬ified_thread, ppu ? &port : nullptr); } return CELL_ENOTCONN; @@ -742,6 +774,19 @@ error_code sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) return CELL_ESRCH; } + if (ppu && notified_thread) + { + // Wait to be requeued + if (ppu->test_stopped()) + { + // Wait again on savestate load + ppu->state += cpu_flag::again; + } + + port->is_busy--; + return CELL_OK; + } + if (port.ret) { if (port.ret == CELL_EAGAIN) diff --git a/rpcs3/Emu/Cell/lv2/sys_event.h b/rpcs3/Emu/Cell/lv2/sys_event.h index bf872cc4ba..506a45e7a0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event.h +++ b/rpcs3/Emu/Cell/lv2/sys_event.h @@ -79,6 +79,8 @@ struct sys_event_t // Source, data1, data2, data3 using lv2_event = std::tuple; +struct lv2_event_port; + struct lv2_event_queue final : public lv2_obj { static const u32 id_base = 0x8d000000; @@ -103,11 +105,11 @@ struct lv2_event_queue final : public lv2_obj static void save_ptr(utils::serial&, lv2_event_queue*); static std::shared_ptr load_ptr(utils::serial& ar, std::shared_ptr& queue, std::string_view msg = {}); - CellError send(lv2_event event); + CellError send(lv2_event event, bool* notified_thread = nullptr, lv2_event_port* port = nullptr); - CellError send(u64 source, u64 d1, u64 d2, u64 d3) + CellError send(u64 source, u64 d1, u64 d2, u64 d3, bool* notified_thread = nullptr, lv2_event_port* port = nullptr) { - return send(std::make_tuple(source, d1, d2, d3)); + return send(std::make_tuple(source, d1, d2, d3), notified_thread, port); } // Get event queue by its global key @@ -121,6 +123,7 @@ struct lv2_event_port final : lv2_obj const s32 type; // Port type, either IPC or local const u64 name; // Event source (generated from id and process id if not set) + atomic_t is_busy = 0; // Counts threads waiting on event sending std::shared_ptr queue; // Event queue this port is connected to lv2_event_port(s32 type, u64 name)