diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 7912ae598028..43522de68f26 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -3040,7 +3040,9 @@ bool spu_thread::stop_and_signal(u32 code) queue.reset(); // Check group status, wait if necessary - while (group->run_state >= SPU_THREAD_GROUP_STATUS_WAITING && group->run_state <= SPU_THREAD_GROUP_STATUS_SUSPENDED) + for (auto _state = +group->run_state; + _state >= SPU_THREAD_GROUP_STATUS_WAITING && _state <= SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED; + _state = group->run_state) { if (is_stopped()) { @@ -3054,6 +3056,11 @@ bool spu_thread::stop_and_signal(u32 code) std::lock_guard lock(group->mutex); + if (is_stopped()) + { + return false; + } + if (group->run_state >= SPU_THREAD_GROUP_STATUS_WAITING && group->run_state <= SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED) { // Try again @@ -3113,11 +3120,14 @@ bool spu_thread::stop_and_signal(u32 code) while (true) { - if (is_stopped()) + if (state & (cpu_flag::exit + cpu_flag::dbg_global_stop)) { return false; } + // The thread group cannot be stopped while waiting for an event + verify(HERE), !(state & cpu_flag::stop); + if (!state.test_and_reset(cpu_flag::signal)) { thread_ctrl::wait(); @@ -3238,19 +3248,42 @@ bool spu_thread::stop_and_signal(u32 code) spu_log.trace("sys_spu_thread_group_exit(status=0x%x)", value); - std::lock_guard lock(group->mutex); - - for (auto& thread : group->threads) + while (true) { - if (thread && thread.get() != this) + for (auto _state = +group->run_state; + _state >= SPU_THREAD_GROUP_STATUS_WAITING && _state <= SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED; + _state = group->run_state) + { + if (is_stopped()) + { + return false; + } + + thread_ctrl::wait(); + } + + std::lock_guard lock(group->mutex); + + if (auto _state = +group->run_state; + _state >= SPU_THREAD_GROUP_STATUS_WAITING && _state <= SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED) { - thread->state += cpu_flag::stop; - thread_ctrl::notify(*thread); + // We can't exit while we are waiting on an SPU event + continue; } - } - group->exit_status = value; - group->join_state = SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT; + for (auto& thread : group->threads) + { + if (thread && thread.get() != this) + { + thread->state += cpu_flag::stop; + thread_ctrl::notify(*thread); + } + } + + group->exit_status = value; + group->join_state = SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT; + break; + } state += cpu_flag::stop; check_state(); diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 66e5d44b78ae..c98cf7d4c38a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -668,6 +668,7 @@ error_code sys_spu_thread_group_start(ppu_thread& ppu, u32 id) group->join_state = 0; group->running = max_threads; + group->set_terminate = false; for (auto& thread : group->threads) { @@ -859,11 +860,14 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value) if (group->run_state <= SPU_THREAD_GROUP_STATUS_INITIALIZED || group->run_state == SPU_THREAD_GROUP_STATUS_WAITING || - group->run_state == SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED) + group->run_state == SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED || + group->set_terminate) { return CELL_ESTAT; } + group->set_terminate = true; + for (auto& thread : group->threads) { if (thread) diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.h b/rpcs3/Emu/Cell/lv2/sys_spu.h index e9cb1330211f..7e1fd0b6f329 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.h +++ b/rpcs3/Emu/Cell/lv2/sys_spu.h @@ -270,6 +270,7 @@ struct lv2_spu_group cond_variable cond; // used to signal waiting PPU thread atomic_t stop_count; class ppu_thread* waiter = nullptr; + bool set_terminate = false; std::array>, 8> threads; // SPU Threads std::array threads_map; // SPU Threads map based number