diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index f0b11d71ff..000eeb4877 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -487,9 +487,16 @@ void spu_thread::cpu_stop() if (verify(HERE, group->running--) == 1) { { - group->stop_count++; std::lock_guard lock(group->mutex); + group->stop_count++; group->run_state = SPU_THREAD_GROUP_STATUS_INITIALIZED; + + if (const auto ppu = std::exchange(group->waiter, nullptr)) + { + // Send exit status directly to the joining thread + ppu->gpr[4] = group->join_state; + ppu->gpr[5] = group->exit_status; + } } // Notify on last thread stopped diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 85794fd601..1c84648034 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -633,9 +633,7 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr cause return CELL_ESRCH; } - u32 join_state = 0; - s32 exit_value = 0; - + do { std::unique_lock lock(group->mutex); @@ -644,12 +642,25 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr cause return CELL_ESTAT; } - if (group->join_state.fetch_or(SPU_TGJSF_IS_JOINING) & SPU_TGJSF_IS_JOINING) + if (group->waiter) { // another PPU thread is joining this thread group return CELL_EBUSY; } + if (group->run_state == SPU_THREAD_GROUP_STATUS_INITIALIZED) + { + // Already terminated + ppu.gpr[4] = group->join_state; + ppu.gpr[5] = group->exit_status; + break; + } + else + { + // Subscribe to receive status in r4-r5 + group->waiter = &ppu; + } + const u64 last_stop = group->stop_count - !group->running; lv2_obj::sleep(ppu); @@ -663,18 +674,15 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr cause group->cond.wait(lock); } - - join_state = group->join_state; - exit_value = group->exit_status; - group->join_state &= ~SPU_TGJSF_IS_JOINING; } + while (0); if (ppu.test_stopped()) { return 0; } - switch (join_state & ~SPU_TGJSF_IS_JOINING) + switch (ppu.gpr[4] & (SPU_TGJSF_GROUP_EXIT | SPU_TGJSF_TERMINATED)) { case 0: { @@ -699,7 +707,7 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr cause if (status) { - *status = group->exit_status; + *status = static_cast(ppu.gpr[5]); } return CELL_OK; diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.h b/rpcs3/Emu/Cell/lv2/sys_spu.h index 72b416fe2c..8a90656af0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.h +++ b/rpcs3/Emu/Cell/lv2/sys_spu.h @@ -210,7 +210,6 @@ enum : u32 // SPU Thread Group Join State Flag enum : u32 { - 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 }; @@ -237,6 +236,7 @@ struct lv2_spu_group atomic_t running; // Number of running threads cond_variable cond; // used to signal waiting PPU thread atomic_t stop_count; + class ppu_thread* waiter = nullptr; std::array>, 256> threads; // SPU Threads std::array>, 256> imgs; // SPU Images