diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index cf81a8625e..084fd7130b 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -914,15 +914,6 @@ void ppu_thread::exec_task() ppu_thread::~ppu_thread() { - // Deallocate Stack Area - ensure(vm::dealloc(stack_addr, vm::stack)); - - if (const auto dct = g_fxo->get()) - { - dct->used -= stack_size; - } - - perf_log.notice("Perf stats for STCX reload: successs %u, failure %u", last_succ, last_fail); } ppu_thread::ppu_thread(const ppu_thread_params& param, std::string_view name, u32 prio, int detached) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 32f145253e..ce34720404 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1675,26 +1675,12 @@ void spu_thread::cpu_task() } } -spu_thread::~spu_thread() +void spu_thread::cleanup() { - { - vm::writer_lock(0); + const u32 addr = group ? SPU_FAKE_BASE_ADDR + SPU_LS_SIZE * (id & 0xffffff) : RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index; - for (s32 i = -1; i < 2; i++) - { - // Unmap LS mirrors - shm->unmap_critical(ls + (i * SPU_LS_SIZE)); - } - } - - if (!group) - { - // Deallocate local storage (thread groups are handled in sys_spu.cpp) - ensure(vm::dealloc(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index, vm::spu)); - } - - // Release LS mirrors area - utils::memory_release(ls - (SPU_LS_SIZE * 2), SPU_LS_SIZE * 5); + // Deallocate local storage + ensure(vm::dealloc(addr, vm::spu, &shm)); // Deallocate RawSPU ID if (get_type() >= spu_type::raw) @@ -1710,14 +1696,28 @@ spu_thread::~spu_thread() perf_log.notice("Perf stats for PUTLLC reload: successs %u, failure %u", last_succ, last_fail); } +spu_thread::~spu_thread() +{ + { + vm::writer_lock lock(0); + + for (s32 i = -1; i < 2; i++) + { + // Unmap LS mirrors + shm->unmap_critical(ls + (i * SPU_LS_SIZE)); + } + } + + // Release LS mirrors area + utils::memory_release(ls - (SPU_LS_SIZE * 2), SPU_LS_SIZE * 5); +} + spu_thread::spu_thread(lv2_spu_group* group, u32 index, std::string_view name, u32 lv2_id, bool is_isolated, u32 option) : cpu_thread(idm::last_id()) , index(index) , shm(std::make_shared(SPU_LS_SIZE)) , ls([&]() { - const auto addr = static_cast(utils::memory_reserve(SPU_LS_SIZE * 5)); - if (!group) { ensure(vm::get(vm::spu)->falloc(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index, SPU_LS_SIZE, &shm)); @@ -1728,7 +1728,9 @@ spu_thread::spu_thread(lv2_spu_group* group, u32 index, std::string_view name, u ensure(vm::get(vm::spu)->falloc(SPU_FAKE_BASE_ADDR + SPU_LS_SIZE * (cpu_thread::id & 0xffffff), SPU_LS_SIZE, &shm, 0x1000)); } - vm::writer_lock(0); + vm::writer_lock lock(0); + + const auto addr = static_cast(utils::memory_reserve(SPU_LS_SIZE * 5)); for (u32 i = 1; i < 4; i++) { diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 7f4d6143fd..f84c379682 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -634,6 +634,7 @@ public: virtual void cpu_task() override final; virtual void cpu_return() override; virtual ~spu_thread() override; + void cleanup(); void cpu_init(); static const u32 id_base = 0x02000000; // TODO (used to determine thread type) diff --git a/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp b/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp index c5131debe8..b23dc49a10 100644 --- a/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp @@ -22,18 +22,14 @@ void lv2_int_serv::exec() thread_ctrl::notify(*thread); } -bool interrupt_thread_exit(ppu_thread& ppu) -{ - ppu.state += cpu_flag::exit; - return false; -} +bool ppu_thread_exit(ppu_thread& ppu); void lv2_int_serv::join() { thread->cmd_list ({ { ppu_cmd::ptr_call, 0 }, - std::bit_cast(&interrupt_thread_exit) + std::bit_cast(&ppu_thread_exit) }); thread_ctrl::notify(*thread); diff --git a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp index 613f835d4d..c72b335b71 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp @@ -2,6 +2,7 @@ #include "sys_ppu_thread.h" #include "Emu/IdManager.h" +#include "Emu/perf_meter.hpp" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" @@ -36,6 +37,22 @@ struct ppu_thread_cleaner } }; +bool ppu_thread_exit(ppu_thread& ppu) +{ + ppu.state += cpu_flag::exit + cpu_flag::wait; + + // Deallocate Stack Area + ensure(vm::dealloc(ppu.stack_addr, vm::stack) == ppu.stack_size); + + if (const auto dct = g_fxo->get()) + { + dct->used -= ppu.stack_size; + } + + perf_log.notice("Perf stats for STCX reload: successs %u, failure %u", ppu.last_succ, ppu.last_fail); + return false; +} + void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode) { ppu.state += cpu_flag::wait; @@ -77,10 +94,15 @@ void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode) ppu.state -= cpu_flag::suspend; } - if (old_status == ppu_join_status::detached) + g_fxo->get()->clean(old_status == ppu_join_status::detached ? ppu.id : 0); + + if (old_status == ppu_join_status::joinable) { - g_fxo->get()->clean(ppu.id); + // Wait for termination + ppu.joiner.wait(ppu_join_status::zombie); } + + ppu_thread_exit(ppu); } s32 sys_ppu_thread_yield(ppu_thread& ppu) @@ -114,7 +136,7 @@ error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr vptr if (value == ppu_join_status::zombie) { value = ppu_join_status::exited; - return CELL_EBUSY; + return CELL_EAGAIN; } if (value == ppu_join_status::exited) @@ -135,6 +157,10 @@ error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr vptr { lv2_obj::sleep(ppu); } + else if (result == CELL_EAGAIN) + { + thread.joiner.notify_one(); + } return result; }); @@ -144,7 +170,7 @@ error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr vptr return CELL_ESRCH; } - if (thread.ret && thread.ret != CELL_EBUSY) + if (thread.ret && thread.ret != CELL_EAGAIN) { return thread.ret; } @@ -183,7 +209,7 @@ error_code sys_ppu_thread_detach(ppu_thread& ppu, u32 thread_id) const auto thread = idm::check>(thread_id, [&](ppu_thread& thread) -> CellError { - return thread.joiner.atomic_op([](ppu_join_status& value) -> CellError + CellError result = thread.joiner.atomic_op([](ppu_join_status& value) -> CellError { if (value == ppu_join_status::zombie) { @@ -209,6 +235,13 @@ error_code sys_ppu_thread_detach(ppu_thread& ppu, u32 thread_id) value = ppu_join_status::detached; return {}; }); + + if (result == CELL_EAGAIN) + { + thread.joiner.notify_one(); + } + + return result; }); if (!thread) @@ -223,7 +256,8 @@ error_code sys_ppu_thread_detach(ppu_thread& ppu, u32 thread_id) if (thread.ret == CELL_EAGAIN) { - ensure(idm::remove>(thread_id)); + g_fxo->get()->clean(thread_id); + g_fxo->get()->clean(0); } return CELL_OK; diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 8d8964e643..e3b9a647a0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -687,7 +687,7 @@ error_code sys_spu_thread_group_destroy(ppu_thread& ppu, u32 id) if (auto thread = t.get()) { // Deallocate LS - ensure(vm::get(vm::spu)->dealloc(SPU_FAKE_BASE_ADDR + SPU_LS_SIZE * (thread->id & 0xffffff), &thread->shm)); + thread->cleanup(); // Remove ID from IDM (destruction will occur in group destructor) idm::remove>(thread->id); @@ -1989,7 +1989,16 @@ error_code raw_spu_destroy(ppu_thread& ppu, u32 id) (*thread)(); - if (!idm::remove_verify>(idm_id, std::move(thread.ptr))) + if (idm::withdraw>(idm_id, [&](spu_thread& spu) -> CellError + { + if (std::addressof(spu) != std::addressof(*thread)) + { + return CELL_ESRCH; + } + + spu.cleanup(); + return {}; + }).ret) { // Other thread destroyed beforehead return CELL_ESRCH;