diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index cff0dd6084..2b1ac341b6 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -89,6 +89,8 @@ cpu_thread::cpu_thread(u32 id) bool cpu_thread::check_state() { + bool cpu_sleep_called = false; + while (true) { if (test(state & cpu_flag::exit)) @@ -96,11 +98,23 @@ bool cpu_thread::check_state() return true; } + if (test(state & cpu_flag::signal) && state.test_and_reset(cpu_flag::signal)) + { + cpu_sleep_called = false; + } + if (!test(state & (cpu_state_pause + cpu_flag::dbg_global_stop))) { break; } + if (test(state & cpu_flag::suspend) && !cpu_sleep_called) + { + cpu_sleep(); + cpu_sleep_called = true; + continue; + } + thread_ctrl::wait(); } @@ -126,12 +140,6 @@ void cpu_thread::run() notify(); } -void cpu_thread::set_signal() -{ - verify("cpu_flag::signal" HERE), !state.test_and_set(cpu_flag::signal); - notify(); -} - std::string cpu_thread::dump() const { return fmt::format("Type: %s\n" "State: %s\n", typeid(*this).name(), state.load()); diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index e49cada8d6..12af13c623 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -8,7 +8,7 @@ enum class cpu_flag : u32 { stop, // Thread not running (HLE, initial state) exit, // Irreversible exit - suspend, // Thread paused + suspend, // Thread suspended ret, // Callback return requested signal, // Thread received a signal (HLE) @@ -38,18 +38,12 @@ public: // Public thread state atomic_t> state{+cpu_flag::stop}; - // Object associated with sleep state, possibly synchronization primitive (mutex, semaphore, etc.) - atomic_t owner{}; - // Process thread state, return true if the checker must return bool check_state(); // Run thread void run(); - // Set cpu_flag::signal - void set_signal(); - // Check thread type u32 id_type() { @@ -64,6 +58,9 @@ public: // Thread entry point function virtual void cpu_task() = 0; + + // Callback for cpu_flag::suspend + virtual void cpu_sleep() {} }; inline cpu_thread* get_current_cpu_thread() noexcept diff --git a/rpcs3/Emu/Cell/Modules/cellAdec.cpp b/rpcs3/Emu/Cell/Modules/cellAdec.cpp index b8026e4a9b..52920fb491 100644 --- a/rpcs3/Emu/Cell/Modules/cellAdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAdec.cpp @@ -2,6 +2,7 @@ #include "Emu/System.h" #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/lv2/sys_sync.h" extern "C" { @@ -199,6 +200,7 @@ public: // TODO: finalize cellAdec.warning("adecEndSeq:"); cbFunc(*this, id, CELL_ADEC_MSG_TYPE_SEQDONE, CELL_OK, cbArg); + lv2_obj::sleep(*this, -1); just_finished = true; break; @@ -375,11 +377,13 @@ public: { frame.data = nullptr; // to prevent destruction cbFunc(*this, id, CELL_ADEC_MSG_TYPE_PCMOUT, CELL_OK, cbArg); + lv2_obj::sleep(*this, -1); } } } cbFunc(*this, id, CELL_ADEC_MSG_TYPE_AUDONE, task.au.auInfo_addr, cbArg); + lv2_obj::sleep(*this, -1); break; } diff --git a/rpcs3/Emu/Cell/Modules/cellDmux.cpp b/rpcs3/Emu/Cell/Modules/cellDmux.cpp index 9bd8ffe4ce..c7dd813275 100644 --- a/rpcs3/Emu/Cell/Modules/cellDmux.cpp +++ b/rpcs3/Emu/Cell/Modules/cellDmux.cpp @@ -2,6 +2,7 @@ #include "Emu/System.h" #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/lv2/sys_sync.h" #include "cellPamf.h" #include "cellDmux.h" @@ -242,6 +243,7 @@ public: dmuxMsg->msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE; dmuxMsg->supplementalInfo = stream.userdata; cbFunc(*this, id, dmuxMsg, cbArg); + lv2_obj::sleep(*this, -1); is_working = false; @@ -395,6 +397,7 @@ public: esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND; esMsg->supplementalInfo = stream.userdata; es.cbFunc(*this, id, es.id, esMsg, es.cbArg); + lv2_obj::sleep(*this, -1); } } else @@ -460,6 +463,7 @@ public: esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND; esMsg->supplementalInfo = stream.userdata; es.cbFunc(*this, id, es.id, esMsg, es.cbArg); + lv2_obj::sleep(*this, -1); } if (pes.has_ts) @@ -536,6 +540,7 @@ public: dmuxMsg->msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE; dmuxMsg->supplementalInfo = stream.userdata; cbFunc(*this, id, dmuxMsg, cbArg); + lv2_obj::sleep(*this, -1); stream = {}; @@ -624,6 +629,7 @@ public: esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND; esMsg->supplementalInfo = stream.userdata; es.cbFunc(*this, id, es.id, esMsg, es.cbArg); + lv2_obj::sleep(*this, -1); } if (es.raw_data.size()) @@ -636,6 +642,7 @@ public: esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE; esMsg->supplementalInfo = stream.userdata; es.cbFunc(*this, id, es.id, esMsg, es.cbArg); + lv2_obj::sleep(*this, -1); break; } diff --git a/rpcs3/Emu/Cell/Modules/cellFs.cpp b/rpcs3/Emu/Cell/Modules/cellFs.cpp index d1039ccef5..8bdebe82f1 100644 --- a/rpcs3/Emu/Cell/Modules/cellFs.cpp +++ b/rpcs3/Emu/Cell/Modules/cellFs.cpp @@ -4,6 +4,7 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_fs.h" +#include "Emu/Cell/lv2/sys_sync.h" #include "cellFs.h" #include "Utilities/StrUtil.h" @@ -742,6 +743,7 @@ struct fs_aio_thread : ppu_thread } func(*this, aio, error, xid, result); + lv2_obj::sleep(*this, -1); } } }; diff --git a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp index d3a006c08f..50f3c422a7 100644 --- a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp @@ -84,7 +84,7 @@ namespace _spurs s32 add_default_syswkl(vm::ptr spurs, vm::cptr swlPriority, u32 swlMaxSpu, u32 swlIsPreem); // Destroy the SPURS SPU threads and thread group - s32 finalize_spu(vm::ptr spurs); + s32 finalize_spu(ppu_thread&, vm::ptr spurs); // Stop the event helper thread s32 stop_event_helper(ppu_thread& ppu, vm::ptr spurs); @@ -276,7 +276,7 @@ namespace _spurs //s32 cellSpursEventFlagWait(ppu_thread& ppu, vm::ptr eventFlag, vm::ptr mask, u32 mode); //s32 cellSpursEventFlagTryWait(ppu_thread& ppu, vm::ptr eventFlag, vm::ptr mask, u32 mode); //s32 cellSpursEventFlagAttachLv2EventQueue(ppu_thread& ppu, vm::ptr eventFlag); -//s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr eventFlag); +//s32 cellSpursEventFlagDetachLv2EventQueue(ppu_thread& ppu, vm::ptr eventFlag); //s32 cellSpursEventFlagGetDirection(vm::ptr eventFlag, vm::ptr direction); //s32 cellSpursEventFlagGetClearMode(vm::ptr eventFlag, vm::ptr clear_mode); //s32 cellSpursEventFlagGetTasksetAddress(vm::ptr eventFlag, vm::pptr taskset); @@ -380,7 +380,7 @@ s32 _spurs::create_lv2_eq(ppu_thread& ppu, vm::ptr spurs, vm::ptr spurs) CHECK_SUCCESS(sys_spu_thread_group_start(spurs->spuTG)); - if (s32 rc = sys_spu_thread_group_join(spurs->spuTG, vm::null, vm::null)) + if (s32 rc = sys_spu_thread_group_join(ppu, spurs->spuTG, vm::null, vm::null)) { if (rc == CELL_ESTAT) { @@ -674,7 +674,7 @@ s32 _spurs::wakeup_shutdown_completion_waiter(ppu_thread& ppu, vm::ptrhook || wklEvent->load() & 0x10) { verify(HERE), (wklF->x28 == 2); - rc = sys_semaphore_post((u32)wklF->sem, 1); + rc = sys_semaphore_post(ppu, (u32)wklF->sem, 1); } return rc; @@ -712,11 +712,11 @@ void _spurs::event_helper_entry(ppu_thread& ppu, vm::ptr spurs) for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++) { - sys_semaphore_post((u32)spurs->wklF1[i].sem, 1); + sys_semaphore_post(ppu, (u32)spurs->wklF1[i].sem, 1); if (spurs->flags1 & SF1_32_WORKLOADS) { - sys_semaphore_post((u32)spurs->wklF2[i].sem, 1); + sys_semaphore_post(ppu, (u32)spurs->wklF2[i].sem, 1); } } } @@ -747,7 +747,7 @@ void _spurs::event_helper_entry(ppu_thread& ppu, vm::ptr spurs) } else if (data0 == 2) { - CHECK_SUCCESS(sys_semaphore_post((u32)spurs->semPrv, 1)); + CHECK_SUCCESS(sys_semaphore_post(ppu, (u32)spurs->semPrv, 1)); } else if (data0 == 3) { @@ -775,7 +775,7 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr spurs, u32 p return CELL_SPURS_CORE_ERROR_AGAIN; } - sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE); + sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE); return CELL_SPURS_CORE_ERROR_AGAIN; } @@ -788,7 +788,7 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr spurs, u32 p return CELL_SPURS_CORE_ERROR_STAT; } - sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE); + sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE); return CELL_SPURS_CORE_ERROR_STAT; } @@ -814,7 +814,7 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr spurs, u32 p return CELL_SPURS_CORE_ERROR_STAT; } - sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE); + sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE); return CELL_SPURS_CORE_ERROR_STAT; } @@ -839,13 +839,13 @@ s32 _spurs::add_default_syswkl(vm::ptr spurs, vm::cptr swlPriorit return CELL_OK; } -s32 _spurs::finalize_spu(vm::ptr spurs) +s32 _spurs::finalize_spu(ppu_thread& ppu, vm::ptr spurs) { if (spurs->flags & SAF_UNKNOWN_FLAG_7 || spurs->flags & SAF_UNKNOWN_FLAG_8) { while (true) { - CHECK_SUCCESS(sys_spu_thread_group_join(spurs->spuTG, vm::null, vm::null)); + CHECK_SUCCESS(sys_spu_thread_group_join(ppu, spurs->spuTG, vm::null, vm::null)); if (s32 rc = sys_spu_thread_group_destroy(spurs->spuTG)) { @@ -880,7 +880,7 @@ s32 _spurs::stop_event_helper(ppu_thread& ppu, vm::ptr spurs) return CELL_SPURS_CORE_ERROR_STAT; } - if (sys_event_port_send(spurs->eventPort, 0, 1, 0) != CELL_OK) + if (sys_event_port_send(ppu, spurs->eventPort, 0, 1, 0) != CELL_OK) { return CELL_SPURS_CORE_ERROR_STAT; } @@ -895,7 +895,7 @@ s32 _spurs::stop_event_helper(ppu_thread& ppu, vm::ptr spurs) CHECK_SUCCESS(sys_event_port_disconnect(spurs->eventPort)); CHECK_SUCCESS(sys_event_port_destroy(spurs->eventPort)); CHECK_SUCCESS(_spurs::detach_lv2_eq(spurs, spurs->spuPort, true)); - CHECK_SUCCESS(sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE)); + CHECK_SUCCESS(sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE)); return CELL_OK; } @@ -1141,7 +1141,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr spurs, u32 revision, // Create a mutex to protect access to SPURS handler thread data if (s32 rc = sys_lwmutex_create(lwMutex, vm::make_var(sys_lwmutex_attribute_t{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, "_spuPrv" }))) { - _spurs::finalize_spu(spurs); + _spurs::finalize_spu(ppu, spurs); return rollback(), rc; } @@ -1149,7 +1149,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr spurs, u32 revision, if (s32 rc = sys_lwcond_create(lwCond, lwMutex, vm::make_var(sys_lwcond_attribute_t{ "_spuPrv" }))) { sys_lwmutex_destroy(ppu, lwMutex); - _spurs::finalize_spu(spurs); + _spurs::finalize_spu(ppu, spurs); return rollback(), rc; } @@ -1166,7 +1166,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr spurs, u32 revision, { sys_lwcond_destroy(lwCond); sys_lwmutex_destroy(ppu, lwMutex); - _spurs::finalize_spu(spurs); + _spurs::finalize_spu(ppu, spurs); return rollback(), rc; } @@ -1176,7 +1176,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr spurs, u32 revision, _spurs::stop_event_helper(ppu, spurs); sys_lwcond_destroy(lwCond); sys_lwmutex_destroy(ppu, lwMutex); - _spurs::finalize_spu(spurs); + _spurs::finalize_spu(ppu, spurs); return rollback(), rc; } @@ -1188,7 +1188,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr spurs, u32 revision, _spurs::stop_event_helper(ppu, spurs); sys_lwcond_destroy(lwCond); sys_lwmutex_destroy(ppu, lwMutex); - _spurs::finalize_spu(spurs); + _spurs::finalize_spu(ppu, spurs); return rollback(), rc; } @@ -2787,7 +2787,7 @@ s32 cellSpursEventFlagSet(ppu_thread& ppu, vm::ptr eventFlag // Signal the PPU thread to be woken up eventFlag->pendingRecvTaskEvents[ppuWaitSlot] = ppuEvents; - CHECK_SUCCESS(sys_event_port_send(eventFlag->eventPortId, 0, 0, 0)); + CHECK_SUCCESS(sys_event_port_send(ppu, eventFlag->eventPortId, 0, 0, 0)); } if (pendingRecv) @@ -3067,7 +3067,7 @@ s32 cellSpursEventFlagAttachLv2EventQueue(ppu_thread& ppu, vm::ptr eventFlag) +s32 cellSpursEventFlagDetachLv2EventQueue(ppu_thread& ppu, vm::ptr eventFlag) { cellSpurs.warning("cellSpursEventFlagDetachLv2EventQueue(eventFlag=*0x%x)", eventFlag); @@ -3131,7 +3131,7 @@ s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr eventFlag) if (rc == CELL_OK) { - rc = sys_event_queue_destroy(eventFlag->eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE); + rc = sys_event_queue_destroy(ppu, eventFlag->eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE); } return CELL_OK; diff --git a/rpcs3/Emu/Cell/Modules/cellVdec.cpp b/rpcs3/Emu/Cell/Modules/cellVdec.cpp index 3dbbd3fec8..79810768bc 100644 --- a/rpcs3/Emu/Cell/Modules/cellVdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVdec.cpp @@ -2,6 +2,7 @@ #include "Emu/System.h" #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/lv2/sys_sync.h" extern "C" { @@ -77,8 +78,8 @@ struct vdec_thread : ppu_thread std::mutex mutex; std::queue out; - vdec_thread(s32 type, u32 profile, u32 addr, u32 size, vm::ptr func, u32 arg) - : ppu_thread("HLE Video Decoder") + vdec_thread(s32 type, u32 profile, u32 addr, u32 size, vm::ptr func, u32 arg, u32 prio, u32 stack) + : ppu_thread("HLE Video Decoder", prio, stack) , type(type) , profile(profile) , mem_addr(addr) @@ -327,6 +328,7 @@ struct vdec_thread : ppu_thread std::lock_guard{mutex}, out.push(std::move(frame)); cb_func(*this, id, CELL_VDEC_MSG_TYPE_PICOUT, CELL_OK, cb_arg); + lv2_obj::sleep(*this, -1); } if (vcmd == vdec_cmd::decode) @@ -336,6 +338,7 @@ struct vdec_thread : ppu_thread } cb_func(*this, id, vcmd == vdec_cmd::decode ? CELL_VDEC_MSG_TYPE_AUDONE : CELL_VDEC_MSG_TYPE_SEQDONE, CELL_OK, cb_arg); + lv2_obj::sleep(*this, -1); while (std::lock_guard{mutex}, out.size() > 60) { @@ -405,7 +408,7 @@ s32 cellVdecOpen(vm::cptr type, vm::cptr res, vm cellVdec.warning("cellVdecOpen(type=*0x%x, res=*0x%x, cb=*0x%x, handle=*0x%x)", type, res, cb, handle); // Create decoder thread - auto&& vdec = idm::make_ptr(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg); + auto&& vdec = idm::make_ptr(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg, res->ppuThreadPriority, res->ppuThreadStackSize); // Hack: store thread id (normally it should be pointer) *handle = vdec->id; @@ -420,7 +423,7 @@ s32 cellVdecOpenEx(vm::cptr type, vm::cptr r cellVdec.warning("cellVdecOpenEx(type=*0x%x, res=*0x%x, cb=*0x%x, handle=*0x%x)", type, res, cb, handle); // Create decoder thread - auto&& vdec = idm::make_ptr(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg); + auto&& vdec = idm::make_ptr(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg, res->ppuThreadPriority, res->ppuThreadStackSize); // Hack: store thread id (normally it should be pointer) *handle = vdec->id; @@ -430,7 +433,7 @@ s32 cellVdecOpenEx(vm::cptr type, vm::cptr r return CELL_OK; } -s32 cellVdecClose(u32 handle) +s32 cellVdecClose(ppu_thread& ppu, u32 handle) { cellVdec.warning("cellVdecClose(handle=0x%x)", handle); @@ -441,6 +444,7 @@ s32 cellVdecClose(u32 handle) return CELL_VDEC_ERROR_ARG; } + lv2_obj::sleep(ppu, -1); vdec->cmd_push({vdec_cmd::close, 0}); vdec->notify(); vdec->join(); diff --git a/rpcs3/Emu/Cell/Modules/libmixer.cpp b/rpcs3/Emu/Cell/Modules/libmixer.cpp index 196b7d9cfd..609516ad34 100644 --- a/rpcs3/Emu/Cell/Modules/libmixer.cpp +++ b/rpcs3/Emu/Cell/Modules/libmixer.cpp @@ -2,6 +2,7 @@ #include "Emu/System.h" #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/lv2/sys_sync.h" #include "cellAudio.h" #include "libmixer.h" @@ -347,6 +348,7 @@ struct surmixer_thread : ppu_thread if (g_surmx.cb) { g_surmx.cb(*this, g_surmx.cb_arg, (u32)g_surmx.mixcount, 256); + lv2_obj::sleep(*this, -1); } //u64 stamp1 = get_system_time(); diff --git a/rpcs3/Emu/Cell/Modules/sys_lwcond_.cpp b/rpcs3/Emu/Cell/Modules/sys_lwcond_.cpp index 6efa2af1f2..5568e04150 100644 --- a/rpcs3/Emu/Cell/Modules/sys_lwcond_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_lwcond_.cpp @@ -56,7 +56,7 @@ s32 sys_lwcond_signal(ppu_thread& ppu, vm::ptr lwcond) lwmutex->all_info++; // call the syscall - if (s32 res = _sys_lwcond_signal(lwcond->lwcond_queue, lwmutex->sleep_queue, -1, 1)) + if (s32 res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, -1, 1)) { lwmutex->all_info--; @@ -76,14 +76,14 @@ s32 sys_lwcond_signal(ppu_thread& ppu, vm::ptr lwcond) } // call the syscall - return _sys_lwcond_signal(lwcond->lwcond_queue, 0, -1, 2); + return _sys_lwcond_signal(ppu, lwcond->lwcond_queue, 0, -1, 2); } // if locking succeeded lwmutex->all_info++; // call the syscall - if (s32 res = _sys_lwcond_signal(lwcond->lwcond_queue, lwmutex->sleep_queue, -1, 3)) + if (s32 res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, -1, 3)) { lwmutex->all_info--; @@ -111,7 +111,7 @@ s32 sys_lwcond_signal_all(ppu_thread& ppu, vm::ptr lwcond) if (lwmutex->vars.owner.load() == ppu.id) { // if owns the mutex, call the syscall - const s32 res = _sys_lwcond_signal_all(lwcond->lwcond_queue, lwmutex->sleep_queue, 1); + const s32 res = _sys_lwcond_signal_all(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, 1); if (res <= 0) { @@ -134,11 +134,11 @@ s32 sys_lwcond_signal_all(ppu_thread& ppu, vm::ptr lwcond) } // call the syscall - return _sys_lwcond_signal_all(lwcond->lwcond_queue, lwmutex->sleep_queue, 2); + return _sys_lwcond_signal_all(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, 2); } // if locking succeeded, call the syscall - s32 res = _sys_lwcond_signal_all(lwcond->lwcond_queue, lwmutex->sleep_queue, 1); + s32 res = _sys_lwcond_signal_all(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, 1); if (res > 0) { @@ -171,7 +171,7 @@ s32 sys_lwcond_signal_to(ppu_thread& ppu, vm::ptr lwcond, u32 ppu_ lwmutex->all_info++; // call the syscall - if (s32 res = _sys_lwcond_signal(lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 1)) + if (s32 res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 1)) { lwmutex->all_info--; @@ -191,14 +191,14 @@ s32 sys_lwcond_signal_to(ppu_thread& ppu, vm::ptr lwcond, u32 ppu_ } // call the syscall - return _sys_lwcond_signal(lwcond->lwcond_queue, 0, ppu_thread_id, 2); + return _sys_lwcond_signal(ppu, lwcond->lwcond_queue, 0, ppu_thread_id, 2); } // if locking succeeded lwmutex->all_info++; // call the syscall - if (s32 res = _sys_lwcond_signal(lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 3)) + if (s32 res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 3)) { lwmutex->all_info--; diff --git a/rpcs3/Emu/Cell/Modules/sys_lwmutex_.cpp b/rpcs3/Emu/Cell/Modules/sys_lwmutex_.cpp index bb5fc699d2..c955d0c9f6 100644 --- a/rpcs3/Emu/Cell/Modules/sys_lwmutex_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_lwmutex_.cpp @@ -273,7 +273,7 @@ s32 sys_lwmutex_unlock(ppu_thread& ppu, vm::ptr lwmutex) lwmutex->vars.owner.exchange(lwmutex_reserved); // call the syscall - if (_sys_lwmutex_unlock(lwmutex->sleep_queue) == CELL_ESRCH) + if (_sys_lwmutex_unlock(ppu, lwmutex->sleep_queue) == CELL_ESRCH) { return CELL_ESRCH; } diff --git a/rpcs3/Emu/Cell/Modules/sys_ppu_thread_.cpp b/rpcs3/Emu/Cell/Modules/sys_ppu_thread_.cpp index 74883c46fd..47861d2495 100644 --- a/rpcs3/Emu/Cell/Modules/sys_ppu_thread_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_ppu_thread_.cpp @@ -12,7 +12,7 @@ extern logs::channel sysPrxForUser; extern u32 ppu_alloc_tls(); extern void ppu_free_tls(u32 addr); -s32 sys_ppu_thread_create(vm::ptr thread_id, u32 entry, u64 arg, s32 prio, u32 stacksize, u64 flags, vm::cptr threadname) +s32 sys_ppu_thread_create(ppu_thread& ppu, vm::ptr thread_id, u32 entry, u64 arg, s32 prio, u32 stacksize, u64 flags, vm::cptr threadname) { sysPrxForUser.warning("sys_ppu_thread_create(thread_id=*0x%x, entry=0x%x, arg=0x%llx, prio=%d, stacksize=0x%x, flags=0x%llx, threadname=%s)", thread_id, entry, arg, prio, stacksize, flags, threadname); @@ -37,7 +37,7 @@ s32 sys_ppu_thread_create(vm::ptr thread_id, u32 entry, u64 arg, s32 prio, } // Run the thread - if (s32 res = sys_ppu_thread_start(static_cast(*thread_id))) + if (s32 res = sys_ppu_thread_start(ppu, static_cast(*thread_id))) { return res; } @@ -45,6 +45,8 @@ s32 sys_ppu_thread_create(vm::ptr thread_id, u32 entry, u64 arg, s32 prio, // Dirty hack for sound: confirm the creation of _mxr000 event queue if (threadname && std::memcmp(threadname.get_ptr(), "_cellsurMixerMain", 18) == 0) { + lv2_obj::sleep(ppu, -1); + while (!idm::select([](u32, lv2_event_queue& eq) { return eq.name == "_mxr000\0"_u64; diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 7f2e6e106d..ae03371aa9 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -8,6 +8,7 @@ #include "PPUInterpreter.h" #include "PPUAnalyser.h" #include "PPUModule.h" +#include "lv2/sys_sync.h" #ifdef LLVM_AVAILABLE #include "restore_new.h" @@ -195,7 +196,7 @@ std::string ppu_thread::get_name() const std::string ppu_thread::dump() const { std::string ret = cpu_thread::dump(); - ret += fmt::format("Priority: %d\n", prio); + ret += fmt::format("Priority: %d\n", +prio); ret += fmt::format("Last function: %s\n", last_function ? last_function : ""); ret += "\nRegisters:\n=========\n"; @@ -286,6 +287,11 @@ void ppu_thread::cpu_task() cmd_pop(), ppu_initialize(); break; } + case ppu_cmd::sleep: + { + cmd_pop(), lv2_obj::sleep(*this, -1); + break; + } default: { fmt::throw_exception("Unknown ppu_cmd(0x%x)" HERE, (u32)type); @@ -402,6 +408,9 @@ ppu_thread::ppu_thread(const std::string& name, u32 prio, u32 stack) } gpr[1] = ::align(stack_addr + stack_size, 0x200) - 0x200; + + // Trigger the scheduler + state += cpu_flag::suspend; } void ppu_thread::cmd_push(cmd64 cmd) @@ -449,7 +458,7 @@ cmd64 ppu_thread::cmd_wait() { if (UNLIKELY(test(state))) { - if (check_state()) + if (test(state, cpu_flag::stop + cpu_flag::exit)) { return cmd64{}; } diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 99b0dee40b..6322a94e67 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -15,6 +15,7 @@ enum class ppu_cmd : u32 lle_call, // Load addr and rtoc at *arg or *gpr[arg] and execute hle_call, // Execute function by index (arg) initialize, // ppu_initialize() + sleep, }; class ppu_thread : public cpu_thread @@ -27,6 +28,7 @@ public: virtual std::string get_name() const override; virtual std::string dump() const override; virtual void cpu_task() override; + virtual void cpu_sleep() override; virtual ~ppu_thread() override; ppu_thread(const std::string& name, u32 prio = 0, u32 stack = 0x10000); @@ -117,7 +119,7 @@ public: u64 ctr{}; // Counter Register u32 vrsave{0xffffffff}; // VR Save Register (almost unused) - u32 prio = 0; // Thread priority (0..3071) + atomic_t prio{0}; // Thread priority (0..3071) const u32 stack_size; // Stack size const u32 stack_addr; // Stack address diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index f5c826faa9..89094b0a09 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1015,3 +1015,179 @@ extern ppu_function_t ppu_get_syscall(u64 code) return nullptr; } + +extern u64 get_system_time(); + +DECLARE(lv2_obj::g_mutex); +DECLARE(lv2_obj::g_ppu); +DECLARE(lv2_obj::g_pending); +DECLARE(lv2_obj::g_waiting); + +// Amount of PPU threads running simultaneously (must be 2) +cfg::int_entry<1, 16> g_cfg_ppu_threads(cfg::root.core, "PPU Threads", 2); + +void lv2_obj::sleep(named_thread& thread, u64 wait_until) +{ + semaphore_lock lock(g_mutex); + + if (auto ppu = dynamic_cast(&thread)) + { + sys_ppu_thread.trace("sleep() - waiting (%zu)", g_pending.size()); + + auto state = ppu->state.fetch_op([&](auto& val) + { + if (!test(val, cpu_flag::signal)) + { + val += cpu_flag::suspend; + } + }); + + if (test(state, cpu_flag::signal)) + { + sys_ppu_thread.error("sleep() failed (signaled)"); + } + + // Find and remove the thread + unqueue(g_ppu, ppu); + unqueue(g_pending, ppu); + } + + if (wait_until < -2) + { + // Register timeout if necessary + for (auto it = g_waiting.begin(), end = g_waiting.end(); it != end; it++) + { + if (it->first > wait_until) + { + g_waiting.emplace(it, wait_until, &thread); + return; + } + } + + g_waiting.emplace_back(wait_until, &thread); + } + + schedule_all(); +} + +void lv2_obj::awake(cpu_thread& cpu, u32 prio) +{ + // Check thread type + if (cpu.id_type() != 1) return; + + semaphore_lock lock(g_mutex); + + if (prio == -4) + { + // Yield command + unqueue(g_ppu, &cpu); + unqueue(g_pending, &cpu); + } + + if (prio < INT32_MAX && !unqueue(g_ppu, &cpu)) + { + // Priority set + return; + } + + // Emplace current thread + for (std::size_t i = 0; i <= g_ppu.size(); i++) + { + if (i < g_ppu.size() && g_ppu[i] == &cpu) + { + sys_ppu_thread.trace("sleep() - suspended (p=%zu)", g_pending.size()); + break; + } + + // Use priority, also preserve FIFO order + if (i == g_ppu.size() || g_ppu[i]->prio > static_cast(cpu).prio) + { + sys_ppu_thread.trace("awake(): %s", cpu.id); + g_ppu.insert(g_ppu.cbegin() + i, &static_cast(cpu)); + + // Unregister timeout if necessary + for (auto it = g_waiting.cbegin(), end = g_waiting.cend(); it != end; it++) + { + if (it->second == &cpu) + { + g_waiting.erase(it); + break; + } + } + + break; + } + } + + // Remove pending if necessary + if (!g_pending.empty() && cpu.get() == thread_ctrl::get_current()) + { + unqueue(g_pending, &cpu); + } + + // Suspend threads if necessary + for (std::size_t i = g_cfg_ppu_threads; i < g_ppu.size(); i++) + { + const auto target = g_ppu[i]; + + if (!target->state.test_and_set(cpu_flag::suspend)) + { + sys_ppu_thread.trace("suspend(): %s", target->id); + g_pending.emplace_back(target); + } + } + + schedule_all(); +} + +void lv2_obj::cleanup() +{ + g_ppu.clear(); + g_pending.clear(); + g_waiting.clear(); +} + +void lv2_obj::schedule_all() +{ + if (g_pending.empty()) + { + // Wake up threads + for (std::size_t i = 0, x = std::min(g_cfg_ppu_threads, g_ppu.size()); i < x; i++) + { + const auto target = g_ppu[i]; + + if (test(target->state, cpu_flag::suspend)) + { + sys_ppu_thread.trace("schedule(): %s", target->id); + target->state ^= (cpu_flag::signal + cpu_flag::suspend); + + if (target->get() != thread_ctrl::get_current()) + { + target->notify(); + } + } + } + } + + // Check registered timeouts + while (!g_waiting.empty()) + { + auto& pair = g_waiting.front(); + + if (pair.first <= get_system_time()) + { + pair.second->notify(); + g_waiting.pop_front(); + } + else + { + // The list is sorted so assume no more timeouts + break; + } + } +} + +void ppu_thread::cpu_sleep() +{ + lv2_obj::awake(*this); +} diff --git a/rpcs3/Emu/Cell/lv2/sys_cond.cpp b/rpcs3/Emu/Cell/lv2/sys_cond.cpp index 631f7e1d8a..5a6a811c69 100644 --- a/rpcs3/Emu/Cell/lv2/sys_cond.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_cond.cpp @@ -68,7 +68,7 @@ error_code sys_cond_destroy(u32 cond_id) return CELL_OK; } -error_code sys_cond_signal(u32 cond_id) +error_code sys_cond_signal(ppu_thread& ppu, u32 cond_id) { sys_cond.trace("sys_cond_signal(cond_id=0x%x)", cond_id); @@ -99,13 +99,14 @@ error_code sys_cond_signal(u32 cond_id) if (cond.ret) { - cond.ret->set_signal(); + cond->awake(*cond.ret); + ppu.check_state(); } return CELL_OK; } -error_code sys_cond_signal_all(u32 cond_id) +error_code sys_cond_signal_all(ppu_thread& ppu, u32 cond_id) { sys_cond.trace("sys_cond_signal_all(cond_id=0x%x)", cond_id); @@ -138,13 +139,14 @@ error_code sys_cond_signal_all(u32 cond_id) if (cond.ret) { - cond.ret->set_signal(); + cond->awake(*cond.ret); + ppu.check_state(); } return CELL_OK; } -error_code sys_cond_signal_to(u32 cond_id, u32 thread_id) +error_code sys_cond_signal_to(ppu_thread& ppu, u32 cond_id, u32 thread_id) { sys_cond.trace("sys_cond_signal_to(cond_id=0x%x, thread_id=0x%x)", cond_id, thread_id); @@ -180,7 +182,8 @@ error_code sys_cond_signal_to(u32 cond_id, u32 thread_id) if (cond.ret && cond.ret != (cpu_thread*)(1)) { - cond.ret->set_signal(); + cond->awake(*cond.ret); + ppu.check_state(); } else if (!cond.ret) { @@ -223,6 +226,7 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout) // Register waiter cond->sq.emplace_back(&ppu); + cond->sleep(ppu, start_time, timeout); // Unlock the mutex cond->mutex->lock_count = 0; @@ -232,8 +236,6 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout) ppu.gpr[3] = CELL_OK; } - // SLEEP - while (!ppu.state.test_and_reset(cpu_flag::signal)) { if (timeout) @@ -276,6 +278,8 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout) // Restore the recursive value cond->mutex->lock_count = cond.ret; + ppu.check_state(); + if (ppu.gpr[3] == CELL_ETIMEDOUT) { return not_an_error(CELL_ETIMEDOUT); diff --git a/rpcs3/Emu/Cell/lv2/sys_cond.h b/rpcs3/Emu/Cell/lv2/sys_cond.h index 4e5ed94b54..0bb9e8b8c9 100644 --- a/rpcs3/Emu/Cell/lv2/sys_cond.h +++ b/rpcs3/Emu/Cell/lv2/sys_cond.h @@ -48,6 +48,6 @@ class ppu_thread; error_code sys_cond_create(vm::ps3::ptr cond_id, u32 mutex_id, vm::ps3::ptr attr); error_code sys_cond_destroy(u32 cond_id); error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout); -error_code sys_cond_signal(u32 cond_id); -error_code sys_cond_signal_all(u32 cond_id); -error_code sys_cond_signal_to(u32 cond_id, u32 thread_id); +error_code sys_cond_signal(ppu_thread& ppu, u32 cond_id); +error_code sys_cond_signal_all(ppu_thread& ppu, u32 cond_id); +error_code sys_cond_signal_to(ppu_thread& ppu, u32 cond_id, u32 thread_id); diff --git a/rpcs3/Emu/Cell/lv2/sys_event.cpp b/rpcs3/Emu/Cell/lv2/sys_event.cpp index c932588632..b3b22ba26b 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event.cpp @@ -52,7 +52,7 @@ bool lv2_event_queue::send(lv2_event event) std::tie(ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7]) = event; - ppu.set_signal(); + awake(ppu); } else { @@ -67,7 +67,8 @@ bool lv2_event_queue::send(lv2_event event) const u32 data3 = static_cast(std::get<3>(event)); spu.ch_in_mbox.set_values(4, CELL_OK, data1, data2, data3); - spu.set_signal(); + spu.state += cpu_flag::signal; + spu.notify(); } return true; @@ -131,7 +132,7 @@ error_code sys_event_queue_create(vm::ptr equeue_id, vm::ptrtype == SYS_PPU_QUEUE) { static_cast(*cpu).gpr[3] = CELL_ECANCELED; + queue->awake(*cpu); } else { static_cast(*cpu).ch_in_mbox.set_values(1, CELL_ECANCELED); + cpu->state += cpu_flag::signal; + cpu->notify(); } - - cpu->set_signal(); } } + ppu.check_state(); + return CELL_OK; } @@ -236,6 +240,7 @@ error_code sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptr equeue_id, vm::ps3::ptr attr, u64 event_queue_key, s32 size); -error_code sys_event_queue_destroy(u32 equeue_id, s32 mode); +error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode); error_code sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ps3::ptr dummy_event, u64 timeout); error_code sys_event_queue_tryreceive(u32 equeue_id, vm::ps3::ptr event_array, s32 size, vm::ps3::ptr number); error_code sys_event_queue_drain(u32 event_queue_id); @@ -137,4 +137,4 @@ error_code sys_event_port_create(vm::ps3::ptr eport_id, s32 port_type, u64 error_code sys_event_port_destroy(u32 eport_id); error_code sys_event_port_connect_local(u32 event_port_id, u32 event_queue_id); error_code sys_event_port_disconnect(u32 eport_id); -error_code sys_event_port_send(u32 event_port_id, u64 data1, u64 data2, u64 data3); +error_code sys_event_port_send(ppu_thread& ppu, u32 event_port_id, u64 data1, u64 data2, u64 data3); diff --git a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp index cf351f9391..3e9947a0e6 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp @@ -26,6 +26,11 @@ error_code sys_event_flag_create(vm::ptr id, vm::ptrprotocol; + if (protocol == SYS_SYNC_RETRY) + sys_event_flag.todo("sys_event_flag_create(): SYS_SYNC_RETRY"); + if (protocol == SYS_SYNC_PRIORITY_INHERIT) + sys_event_flag.todo("sys_event_flag_create(): SYS_SYNC_PRIORITY_INHERIT"); + if (protocol != SYS_SYNC_FIFO && protocol != SYS_SYNC_RETRY && protocol != SYS_SYNC_PRIORITY && protocol != SYS_SYNC_PRIORITY_INHERIT) { sys_event_flag.error("sys_event_flag_create(): unknown protocol (0x%x)", protocol); @@ -89,6 +94,7 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm const u64 start_time = ppu.gpr[10] = get_system_time(); // Fix function arguments for external access + ppu.gpr[3] = -1; ppu.gpr[4] = bitptn; ppu.gpr[5] = mode; ppu.gpr[6] = 0; @@ -124,6 +130,7 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm flag.waiters++; flag.sq.emplace_back(&ppu); + flag.sleep(ppu, start_time, timeout); return CELL_EBUSY; }); @@ -145,8 +152,6 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm return CELL_OK; } - // SLEEP - while (!ppu.state.test_and_reset(cpu_flag::signal)) { if (timeout) @@ -164,8 +169,9 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm } flag->waiters--; - if (result) *result = flag->pattern; - return not_an_error(CELL_ETIMEDOUT); + ppu.gpr[3] = CELL_ETIMEDOUT; + ppu.gpr[6] = flag->pattern; + break; } thread_ctrl::wait_for(timeout - passed); @@ -176,8 +182,9 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm } } + ppu.check_state(); if (result) *result = ppu.gpr[6]; - return not_an_error(ppu.gpr[5] ? CELL_OK : CELL_ECANCELED); + return not_an_error(ppu.gpr[3]); } error_code sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ptr result) @@ -231,48 +238,63 @@ error_code sys_event_flag_set(u32 id, u64 bitptn) return CELL_OK; } - semaphore_lock lock(flag->mutex); - - // Sort sleep queue in required order - if (flag->protocol != SYS_SYNC_FIFO) + if (true) { - std::stable_sort(flag->sq.begin(), flag->sq.end(), [](cpu_thread* a, cpu_thread* b) + semaphore_lock lock(flag->mutex); + + // Sort sleep queue in required order + if (flag->protocol != SYS_SYNC_FIFO) { - return static_cast(a)->prio < static_cast(b)->prio; + std::stable_sort(flag->sq.begin(), flag->sq.end(), [](cpu_thread* a, cpu_thread* b) + { + return static_cast(a)->prio < static_cast(b)->prio; + }); + } + + // Process all waiters in single atomic op + flag->pattern.atomic_op([&](u64& value) + { + value |= bitptn; + + for (auto cpu : flag->sq) + { + auto& ppu = static_cast(*cpu); + + const u64 pattern = ppu.gpr[4]; + const u64 mode = ppu.gpr[5]; + + if (lv2_event_flag::check_pattern(value, pattern, mode, &ppu.gpr[6])) + { + ppu.gpr[3] = CELL_OK; + } + } }); - } - // Process all waiters in single atomic op - flag->pattern.atomic_op([&](u64& value) - { - value |= bitptn; - - for (auto cpu : flag->sq) + // Remove waiters + const auto tail = std::remove_if(flag->sq.begin(), flag->sq.end(), [&](cpu_thread* cpu) { auto& ppu = static_cast(*cpu); - const u64 pattern = ppu.gpr[4]; - const u64 mode = ppu.gpr[5]; - ppu.gpr[3] = lv2_event_flag::check_pattern(value, pattern, mode, &ppu.gpr[6]); - } - }); + if (ppu.gpr[3] == CELL_OK) + { + flag->waiters--; + flag->awake(ppu); + return true; + } - // Remove waiters - const auto tail = std::remove_if(flag->sq.begin(), flag->sq.end(), [&](cpu_thread* cpu) + return false; + }); + + flag->sq.erase(tail, flag->sq.end()); + } + + if (auto cpu = get_current_cpu_thread()) { - auto& ppu = static_cast(*cpu); - - if (ppu.gpr[3]) + if (cpu->id_type() == 1) { - flag->waiters--; - ppu.set_signal(); - return true; + static_cast(*cpu).check_state(); } - - return false; - }); - - flag->sq.erase(tail, flag->sq.end()); + } return CELL_OK; } @@ -294,7 +316,7 @@ error_code sys_event_flag_clear(u32 id, u64 bitptn) return CELL_OK; } -error_code sys_event_flag_cancel(u32 id, vm::ptr num) +error_code sys_event_flag_cancel(ppu_thread& ppu, u32 id, vm::ptr num) { sys_event_flag.trace("sys_event_flag_cancel(id=0x%x, num=*0x%x)", id, num); @@ -307,26 +329,33 @@ error_code sys_event_flag_cancel(u32 id, vm::ptr num) return CELL_ESRCH; } - semaphore_lock lock(flag->mutex); - - // Get current pattern - const u64 pattern = flag->pattern; - - // Set count - *num = ::size32(flag->sq); - - // Signal all threads to return CELL_ECANCELED - while (auto thread = flag->schedule(flag->sq, flag->protocol)) + u32 value = 0; { - auto& ppu = static_cast(*thread); + semaphore_lock lock(flag->mutex); - ppu.gpr[4] = pattern; - ppu.gpr[5] = 0; + // Get current pattern + const u64 pattern = flag->pattern; - thread->set_signal(); - flag->waiters--; + // Set count + value = ::size32(flag->sq); + + // Signal all threads to return CELL_ECANCELED + while (auto thread = flag->schedule(flag->sq, flag->protocol)) + { + auto& ppu = static_cast(*thread); + + ppu.gpr[3] = CELL_ECANCELED; + ppu.gpr[6] = pattern; + + flag->waiters--; + flag->awake(ppu); + } } + if (num) *num = value; + + ppu.check_state(); + return CELL_OK; } diff --git a/rpcs3/Emu/Cell/lv2/sys_event_flag.h b/rpcs3/Emu/Cell/lv2/sys_event_flag.h index 5b822ee2a8..6002630fe4 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event_flag.h +++ b/rpcs3/Emu/Cell/lv2/sys_event_flag.h @@ -118,5 +118,5 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm error_code sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ps3::ptr result); error_code sys_event_flag_set(u32 id, u64 bitptn); error_code sys_event_flag_clear(u32 id, u64 bitptn); -error_code sys_event_flag_cancel(u32 id, vm::ps3::ptr num); +error_code sys_event_flag_cancel(ppu_thread& ppu, u32 id, vm::ps3::ptr num); error_code sys_event_flag_get(u32 id, vm::ps3::ptr flags); diff --git a/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp b/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp index a49130a31d..c8278e93bf 100644 --- a/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp @@ -18,6 +18,7 @@ void lv2_int_serv::exec() ({ { ppu_cmd::set_args, 2 }, arg1, arg2, { ppu_cmd::lle_call, 2 }, + { ppu_cmd::sleep, 0 } }); thread->notify(); diff --git a/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp b/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp index 47d9d4e810..e71a13870c 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp @@ -60,7 +60,7 @@ error_code _sys_lwcond_destroy(u32 lwcond_id) return CELL_OK; } -error_code _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mode) +error_code _sys_lwcond_signal(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mode) { sys_lwcond.trace("_sys_lwcond_signal(lwcond_id=0x%x, lwmutex_id=0x%x, ppu_thread_id=0x%x, mode=%d)", lwcond_id, lwmutex_id, ppu_thread_id, mode); @@ -106,7 +106,10 @@ error_code _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, { cond.waiters--; - static_cast(result)->gpr[3] = mode == 2; + if (mode == 2) + { + static_cast(result)->gpr[3] = CELL_EBUSY; + } if (mode != 2 && !mutex->signaled.fetch_op([](u32& v) { if (v) v--; })) { @@ -129,7 +132,8 @@ error_code _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, if (cond.ret) { - cond.ret->set_signal(); + cond->awake(*cond.ret); + ppu.check_state(); } else if (mode == 2) { @@ -147,7 +151,7 @@ error_code _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, return CELL_OK; } -error_code _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode) +error_code _sys_lwcond_signal_all(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u32 mode) { sys_lwcond.trace("_sys_lwcond_signal_all(lwcond_id=0x%x, lwmutex_id=0x%x, mode=%d)", lwcond_id, lwmutex_id, mode); @@ -177,7 +181,10 @@ error_code _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode) { cond.waiters--; - static_cast(cpu)->gpr[3] = mode == 2; + if (mode == 2) + { + static_cast(cpu)->gpr[3] = CELL_EBUSY; + } if (mode != 2 && !mutex->signaled.fetch_op([](u32& v) { if (v) v--; })) { @@ -202,10 +209,14 @@ error_code _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode) return CELL_ESRCH; } - // TODO: signal only one thread for (auto cpu : threads) { - cpu->set_signal(); + cond->awake(*cpu); + } + + if (threads.size()) + { + ppu.check_state(); } if (mode == 1) @@ -239,6 +250,7 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id // Add a waiter cond.waiters++; cond.sq.emplace_back(&ppu); + cond.sleep(ppu, start_time, timeout); // Process lwmutex sleep queue if (const auto cpu = mutex->schedule(mutex->sq, mutex->protocol)) @@ -257,10 +269,10 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id if (cond.ret) { - cond.ret->set_signal(); + cond->awake(*cond.ret); } - // SLEEP + ppu.gpr[3] = CELL_OK; while (!ppu.state.test_and_reset(cpu_flag::signal)) { @@ -282,10 +294,12 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id if (mutex->signaled.fetch_op([](u32& v) { if (v) v--; })) { - return not_an_error(CELL_EDEADLK); + ppu.gpr[3] = CELL_EDEADLK; + break; } - return not_an_error(CELL_ETIMEDOUT); + ppu.gpr[3] = CELL_ETIMEDOUT; + break; } thread_ctrl::wait_for(timeout - passed); @@ -297,5 +311,6 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id } // Return cause - return not_an_error(ppu.gpr[3] ? CELL_EBUSY : CELL_OK); + ppu.check_state(); + return not_an_error(ppu.gpr[3]); } diff --git a/rpcs3/Emu/Cell/lv2/sys_lwcond.h b/rpcs3/Emu/Cell/lv2/sys_lwcond.h index 7379adf3b6..83ceedb73e 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwcond.h +++ b/rpcs3/Emu/Cell/lv2/sys_lwcond.h @@ -43,6 +43,6 @@ class ppu_thread; error_code _sys_lwcond_create(vm::ps3::ptr lwcond_id, u32 lwmutex_id, vm::ps3::ptr control, u64 name, u32 arg5); error_code _sys_lwcond_destroy(u32 lwcond_id); -error_code _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mode); -error_code _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode); +error_code _sys_lwcond_signal(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mode); +error_code _sys_lwcond_signal_all(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u32 mode); error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u64 timeout); diff --git a/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp b/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp index c2a244a440..319c659924 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp @@ -17,6 +17,9 @@ error_code _sys_lwmutex_create(vm::ptr lwmutex_id, u32 protocol, vm::ptrset_signal(); + mutex->awake(*mutex.ret); + ppu.check_state(); } return CELL_OK; diff --git a/rpcs3/Emu/Cell/lv2/sys_lwmutex.h b/rpcs3/Emu/Cell/lv2/sys_lwmutex.h index f4a00092e9..601f2769a1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwmutex.h +++ b/rpcs3/Emu/Cell/lv2/sys_lwmutex.h @@ -78,4 +78,4 @@ error_code _sys_lwmutex_create(vm::ps3::ptr lwmutex_id, u32 protocol, vm::p error_code _sys_lwmutex_destroy(u32 lwmutex_id); error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout); error_code _sys_lwmutex_trylock(u32 lwmutex_id); -error_code _sys_lwmutex_unlock(u32 lwmutex_id); +error_code _sys_lwmutex_unlock(ppu_thread& ppu, u32 lwmutex_id); diff --git a/rpcs3/Emu/Cell/lv2/sys_mutex.cpp b/rpcs3/Emu/Cell/lv2/sys_mutex.cpp index e023f26d03..af08f30cb8 100644 --- a/rpcs3/Emu/Cell/lv2/sys_mutex.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_mutex.cpp @@ -28,8 +28,9 @@ error_code sys_mutex_create(vm::ptr mutex_id, vm::ptr(mutex_id, [&](lv2_mutex& mutex) { - return mutex.lock(ppu, ppu.id); + CellError result = mutex.try_lock(ppu.id); + + if (result == CELL_EBUSY) + { + semaphore_lock lock(mutex.mutex); + + if (mutex.try_own(ppu, ppu.id)) + { + result = {}; + } + else + { + mutex.sleep(ppu, start_time, timeout); + } + } + + return result; }); if (!mutex) @@ -125,7 +142,7 @@ error_code sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout) return CELL_OK; } - // SLEEP + ppu.gpr[3] = CELL_OK; while (!ppu.state.test_and_reset(cpu_flag::signal)) { @@ -143,7 +160,8 @@ error_code sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout) continue; } - return not_an_error(CELL_ETIMEDOUT); + ppu.gpr[3] = CELL_ETIMEDOUT; + break; } thread_ctrl::wait_for(timeout - passed); @@ -154,7 +172,8 @@ error_code sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout) } } - return CELL_OK; + ppu.check_state(); + return not_an_error(ppu.gpr[3]); } error_code sys_mutex_trylock(ppu_thread& ppu, u32 mutex_id) diff --git a/rpcs3/Emu/Cell/lv2/sys_mutex.h b/rpcs3/Emu/Cell/lv2/sys_mutex.h index 90cc6c35c6..74dfb769b0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_mutex.h +++ b/rpcs3/Emu/Cell/lv2/sys_mutex.h @@ -101,23 +101,6 @@ struct lv2_mutex final : lv2_obj return true; } - CellError lock(cpu_thread& cpu, u32 id) - { - CellError result = try_lock(id); - - if (result == CELL_EBUSY) - { - semaphore_lock lock(mutex); - - if (try_own(cpu, id)) - { - return {}; - } - } - - return result; - } - CellError try_unlock(u32 id) { const u32 value = owner; @@ -151,7 +134,7 @@ struct lv2_mutex final : lv2_obj { owner = cpu->id << 1 | !sq.empty(); - cpu->set_signal(); + awake(*cpu); } else { diff --git a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp index 989d3011e3..623bb91f4f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp @@ -7,23 +7,51 @@ #include "Emu/Cell/PPUThread.h" #include "sys_ppu_thread.h" -#include - namespace vm { using namespace ps3; } logs::channel sys_ppu_thread("sys_ppu_thread", logs::level::notice); void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode) { - sys_ppu_thread.warning("_sys_ppu_thread_exit(errorcode=0x%llx)", errorcode); + sys_ppu_thread.trace("_sys_ppu_thread_exit(errorcode=0x%llx)", errorcode); ppu.state += cpu_flag::exit; - // Delete detached thread - if (ppu.joiner == -1) + // Get joiner ID + const u32 jid = ppu.joiner.fetch_op([](u32& value) { + if (value == 0) + { + // Joinable, not joined + value = -3; + } + else if (value != -1) + { + // Joinable, joined + value = -2; + } + + // Detached otherwise + }); + + if (jid == -1) + { + // Delete detached thread and unqueue idm::remove(ppu.id); } + else if (jid != 0) + { + writer_lock lock(id_manager::g_mutex); + + // Schedule joiner and unqueue + lv2_obj::awake(*idm::check_unlocked(jid), -2); + } + + // Unqueue + lv2_obj::sleep(ppu, -1); + + // Remove suspend state (TODO) + ppu.state -= cpu_flag::suspend; // Throw if this syscall was not called directly by the SC instruction (hack) if (ppu.lr == 0 || ppu.gpr[11] != 41) @@ -32,41 +60,67 @@ void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode) } } -void sys_ppu_thread_yield() +void sys_ppu_thread_yield(ppu_thread& ppu) { sys_ppu_thread.trace("sys_ppu_thread_yield()"); - std::this_thread::yield(); + lv2_obj::awake(ppu, -4); + ppu.check_state(); } error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr vptr) { - sys_ppu_thread.warning("sys_ppu_thread_join(thread_id=0x%x, vptr=*0x%x)", thread_id, vptr); + sys_ppu_thread.trace("sys_ppu_thread_join(thread_id=0x%x, vptr=*0x%x)", thread_id, vptr); - const auto thread = idm::get(thread_id); + const auto thread = idm::get(thread_id, [&](ppu_thread& thread) -> CellError + { + CellError result = thread.joiner.atomic_op([&](u32& value) -> CellError + { + if (value == -3) + { + value = -2; + return CELL_EBUSY; + } + + if (value == -2) + { + return CELL_ESRCH; + } + + if (value) + { + return CELL_EINVAL; + } + + // TODO: check precedence? + if (&ppu == &thread) + { + return CELL_EDEADLK; + } + + value = ppu.id; + return {}; + }); + + if (!result) + { + lv2_obj::sleep(ppu, -1); + } + + return result; + }); if (!thread) { return CELL_ESRCH; } - - // TODO: this is race condition if EDEADLK check doesn't actually take the precedence - if (thread->joiner) + + if (thread.ret && thread.ret != CELL_EBUSY) { - return CELL_EINVAL; + return thread.ret; } - if (&ppu == thread.get()) - { - return CELL_EDEADLK; - } - - if (!thread->joiner.compare_and_swap_test(0, ppu.id)) - { - return CELL_EINVAL; - } - - // Actually join + // Wait for cleanup thread->join(); // Get the exit status from the register @@ -83,33 +137,53 @@ error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr vptr error_code sys_ppu_thread_detach(u32 thread_id) { - sys_ppu_thread.warning("sys_ppu_thread_detach(thread_id=0x%x)", thread_id); + sys_ppu_thread.trace("sys_ppu_thread_detach(thread_id=0x%x)", thread_id); - const auto thread = idm::get(thread_id); + const auto thread = idm::check(thread_id, [&](ppu_thread& thread) -> CellError + { + return thread.joiner.atomic_op([&](u32& value) -> CellError + { + if (value == -3) + { + value = -2; + return CELL_EAGAIN; + } + + if (value == -2) + { + return CELL_ESRCH; + } + + if (value == -1) + { + return CELL_EINVAL; + } + + if (value) + { + return CELL_EBUSY; + } + + value = -1; + return {}; + }); + }); if (!thread) { return CELL_ESRCH; } - u32 joiner = thread->joiner; - - if (!joiner) + if (thread.ret && thread.ret != CELL_EAGAIN) { - // Detach thread by assigning -1 - joiner = thread->joiner.compare_and_swap(0, -1); + return thread.ret; } - if (joiner == -1) + if (thread.ret == CELL_EAGAIN) { - return CELL_EINVAL; + idm::remove(thread_id); } - - if (joiner) - { - return CELL_EBUSY; - } - + return CELL_OK; } @@ -120,23 +194,32 @@ void sys_ppu_thread_get_join_state(ppu_thread& ppu, vm::ptr isjoinable) *isjoinable = ppu.joiner != -1; } -error_code sys_ppu_thread_set_priority(u32 thread_id, s32 prio) +error_code sys_ppu_thread_set_priority(ppu_thread& ppu, u32 thread_id, s32 prio) { sys_ppu_thread.trace("sys_ppu_thread_set_priority(thread_id=0x%x, prio=%d)", thread_id, prio); - const auto thread = idm::get(thread_id); - - if (!thread) - { - return CELL_ESRCH; - } - if (prio < 0 || prio > 3071) { return CELL_EINVAL; } - thread->prio = prio; + const auto thread = idm::check(thread_id, [&](ppu_thread& thread) + { + if (thread.prio != prio && thread.prio.exchange(prio) != prio) + { + lv2_obj::awake(thread, prio); + } + }); + + if (!thread) + { + return CELL_ESRCH; + } + + if (&ppu == thread) + { + ppu.check_state(); + } return CELL_OK; } @@ -145,15 +228,16 @@ error_code sys_ppu_thread_get_priority(u32 thread_id, vm::ptr priop) { sys_ppu_thread.trace("sys_ppu_thread_get_priority(thread_id=0x%x, priop=*0x%x)", thread_id, priop); - const auto thread = idm::get(thread_id); + const auto thread = idm::check(thread_id, [&](ppu_thread& thread) + { + *priop = thread.prio; + }); if (!thread) { return CELL_ESRCH; } - *priop = thread->prio; - return CELL_OK; } @@ -248,20 +332,31 @@ error_code _sys_ppu_thread_create(vm::ptr thread_id, vm::ptr(thread_id); + const auto thread = idm::get(thread_id, [&](ppu_thread& thread) + { + lv2_obj::awake(thread, -2); + }); if (!thread) { return CELL_ESRCH; } - // TODO - thread->run(); + if (!thread->state.test_and_reset(cpu_flag::stop)) + { + // TODO: what happens there? + return CELL_EPERM; + } + else + { + thread->notify(); + } + ppu.check_state(); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.h b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.h index 7191c4a136..1cc1c3f7ac 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.h +++ b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.h @@ -44,15 +44,15 @@ enum : u32 // Syscalls void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode); -void sys_ppu_thread_yield(); +void sys_ppu_thread_yield(ppu_thread& ppu); error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ps3::ptr vptr); error_code sys_ppu_thread_detach(u32 thread_id); void sys_ppu_thread_get_join_state(ppu_thread& ppu, vm::ps3::ptr isjoinable); -error_code sys_ppu_thread_set_priority(u32 thread_id, s32 prio); +error_code sys_ppu_thread_set_priority(ppu_thread& ppu, u32 thread_id, s32 prio); error_code sys_ppu_thread_get_priority(u32 thread_id, vm::ps3::ptr priop); error_code sys_ppu_thread_get_stack_information(ppu_thread& ppu, vm::ps3::ptr sp); error_code sys_ppu_thread_stop(u32 thread_id); error_code sys_ppu_thread_restart(u32 thread_id); error_code _sys_ppu_thread_create(vm::ps3::ptr thread_id, vm::ps3::ptr param, u64 arg, u64 arg4, s32 prio, u32 stacksize, u64 flags, vm::ps3::cptr threadname); -error_code sys_ppu_thread_start(u32 thread_id); +error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id); error_code sys_ppu_thread_rename(u32 thread_id, vm::ps3::cptr name); diff --git a/rpcs3/Emu/Cell/lv2/sys_rwlock.cpp b/rpcs3/Emu/Cell/lv2/sys_rwlock.cpp index 775b7fbac9..42aa9e8eb4 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rwlock.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rwlock.cpp @@ -24,6 +24,9 @@ error_code sys_rwlock_create(vm::ptr rw_lock_id, vm::ptrprotocol; + if (protocol == SYS_SYNC_PRIORITY_INHERIT) + sys_rwlock.todo("sys_rwlock_create(): SYS_SYNC_PRIORITY_INHERIT"); + if (protocol != SYS_SYNC_FIFO && protocol != SYS_SYNC_PRIORITY && protocol != SYS_SYNC_PRIORITY_INHERIT) { sys_rwlock.error("sys_rwlock_create(): unknown protocol (0x%x)", protocol); @@ -107,6 +110,7 @@ error_code sys_rwlock_rlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout) if (_old > 0 || _old & 1) { rwlock.rq.emplace_back(&ppu); + rwlock.sleep(ppu, start_time, timeout); return false; } @@ -123,7 +127,7 @@ error_code sys_rwlock_rlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout) return CELL_OK; } - // SLEEP + ppu.gpr[3] = CELL_OK; while (!ppu.state.test_and_reset(cpu_flag::signal)) { @@ -141,7 +145,8 @@ error_code sys_rwlock_rlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout) continue; } - return not_an_error(CELL_ETIMEDOUT); + ppu.gpr[3] = CELL_ETIMEDOUT; + break; } thread_ctrl::wait_for(timeout - passed); @@ -152,7 +157,8 @@ error_code sys_rwlock_rlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout) } } - return CELL_OK; + ppu.check_state(); + return not_an_error(ppu.gpr[3]); } error_code sys_rwlock_tryrlock(u32 rw_lock_id) @@ -187,7 +193,7 @@ error_code sys_rwlock_tryrlock(u32 rw_lock_id) return CELL_OK; } -error_code sys_rwlock_runlock(u32 rw_lock_id) +error_code sys_rwlock_runlock(ppu_thread& ppu, u32 rw_lock_id) { sys_rwlock.trace("sys_rwlock_runlock(rw_lock_id=0x%x)", rw_lock_id); @@ -239,7 +245,7 @@ error_code sys_rwlock_runlock(u32 rw_lock_id) { rwlock->owner = cpu->id << 1 | !rwlock->wq.empty(); - cpu->set_signal(); + rwlock->awake(*cpu); } else { @@ -250,6 +256,7 @@ error_code sys_rwlock_runlock(u32 rw_lock_id) } } + ppu.check_state(); return CELL_OK; } @@ -292,6 +299,7 @@ error_code sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout) if (_old != 0) { rwlock.wq.emplace_back(&ppu); + rwlock.sleep(ppu, start_time, timeout); return false; } @@ -313,7 +321,7 @@ error_code sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout) return CELL_EDEADLK; } - // SLEEP + ppu.gpr[3] = CELL_OK; while (!ppu.state.test_and_reset(cpu_flag::signal)) { @@ -338,13 +346,14 @@ error_code sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout) while (auto cpu = rwlock->schedule(rwlock->rq, SYS_SYNC_PRIORITY)) { - cpu->set_signal(); + rwlock->awake(*cpu); } rwlock->owner &= ~1; } - return not_an_error(CELL_ETIMEDOUT); + ppu.gpr[3] = CELL_ETIMEDOUT; + break; } thread_ctrl::wait_for(timeout - passed); @@ -355,7 +364,8 @@ error_code sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout) } } - return CELL_OK; + ppu.check_state(); + return not_an_error(ppu.gpr[3]); } error_code sys_rwlock_trywlock(ppu_thread& ppu, u32 rw_lock_id) @@ -418,7 +428,7 @@ error_code sys_rwlock_wunlock(ppu_thread& ppu, u32 rw_lock_id) { rwlock->owner = cpu->id << 1 | !rwlock->wq.empty(); - cpu->set_signal(); + rwlock->awake(*cpu); } else if (auto readers = rwlock->rq.size()) { @@ -426,7 +436,7 @@ error_code sys_rwlock_wunlock(ppu_thread& ppu, u32 rw_lock_id) while (auto cpu = rwlock->schedule(rwlock->rq, SYS_SYNC_PRIORITY)) { - cpu->set_signal(); + rwlock->awake(*cpu); } rwlock->owner &= ~1; @@ -437,5 +447,10 @@ error_code sys_rwlock_wunlock(ppu_thread& ppu, u32 rw_lock_id) } } + if (rwlock.ret & 1) + { + ppu.check_state(); + } + return CELL_OK; } diff --git a/rpcs3/Emu/Cell/lv2/sys_rwlock.h b/rpcs3/Emu/Cell/lv2/sys_rwlock.h index 63612417d2..d99ba60e86 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rwlock.h +++ b/rpcs3/Emu/Cell/lv2/sys_rwlock.h @@ -51,7 +51,7 @@ error_code sys_rwlock_create(vm::ps3::ptr rw_lock_id, vm::ps3::ptr sem_id, vm::ptrprotocol; + if (protocol == SYS_SYNC_PRIORITY_INHERIT) + sys_semaphore.todo("sys_semaphore_create(): SYS_SYNC_PRIORITY_INHERIT"); + if (protocol != SYS_SYNC_FIFO && protocol != SYS_SYNC_PRIORITY && protocol != SYS_SYNC_PRIORITY_INHERIT) { sys_semaphore.error("sys_semaphore_create(): unknown protocol (0x%x)", protocol); @@ -101,6 +104,7 @@ error_code sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout) if (sema.val-- <= 0) { sema.sq.emplace_back(&ppu); + sema.sleep(ppu, start_time, timeout); return false; } @@ -117,7 +121,7 @@ error_code sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout) return CELL_OK; } - // SLEEP + ppu.gpr[3] = CELL_OK; while (!ppu.state.test_and_reset(cpu_flag::signal)) { @@ -144,7 +148,8 @@ error_code sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout) } verify(HERE), sem->unqueue(sem->sq, &ppu); - return not_an_error(CELL_ETIMEDOUT); + ppu.gpr[3] = CELL_ETIMEDOUT; + break; } thread_ctrl::wait_for(timeout - passed); @@ -155,7 +160,8 @@ error_code sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout) } } - return CELL_OK; + ppu.check_state(); + return not_an_error(ppu.gpr[3]); } error_code sys_semaphore_trywait(u32 sem_id) @@ -190,7 +196,7 @@ error_code sys_semaphore_trywait(u32 sem_id) return CELL_OK; } -error_code sys_semaphore_post(u32 sem_id, s32 count) +error_code sys_semaphore_post(ppu_thread& ppu, u32 sem_id, s32 count) { sys_semaphore.trace("sys_semaphore_post(sem_id=0x%x, count=%d)", sem_id, count); @@ -245,10 +251,11 @@ error_code sys_semaphore_post(u32 sem_id, s32 count) { const auto cpu = verify(HERE, sem->schedule(sem->sq, sem->protocol)); - cpu->set_signal(); + sem->awake(*cpu); } } + ppu.check_state(); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/lv2/sys_semaphore.h b/rpcs3/Emu/Cell/lv2/sys_semaphore.h index 5159c0db04..844a8abbc7 100644 --- a/rpcs3/Emu/Cell/lv2/sys_semaphore.h +++ b/rpcs3/Emu/Cell/lv2/sys_semaphore.h @@ -53,5 +53,5 @@ error_code sys_semaphore_create(vm::ps3::ptr sem_id, vm::ps3::ptr count); diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index e770a19b5a..d6aa999442 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -6,6 +6,7 @@ #include "Loader/ELF.h" #include "Emu/Cell/ErrorCodes.h" +#include "Emu/Cell/PPUThread.h" #include "Emu/Cell/RawSPUThread.h" #include "sys_interrupt.h" #include "sys_event.h" @@ -470,7 +471,7 @@ error_code sys_spu_thread_group_terminate(u32 id, s32 value) return CELL_OK; } -error_code sys_spu_thread_group_join(u32 id, vm::ptr cause, vm::ptr status) +error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr cause, vm::ptr status) { sys_spu.warning("sys_spu_thread_group_join(id=0x%x, cause=*0x%x, status=*0x%x)", id, cause, status); @@ -494,6 +495,8 @@ error_code sys_spu_thread_group_join(u32 id, vm::ptr cause, vm::ptr st return CELL_EBUSY; } + lv2_obj::sleep(ppu, -1); + while ((group->join_state & ~SPU_TGJSF_IS_JOINING) == 0) { bool stopped = true; diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.h b/rpcs3/Emu/Cell/lv2/sys_spu.h index bb223ce4bc..6b793688a2 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.h +++ b/rpcs3/Emu/Cell/lv2/sys_spu.h @@ -221,7 +221,7 @@ error_code sys_spu_thread_group_suspend(u32 id); error_code sys_spu_thread_group_resume(u32 id); error_code sys_spu_thread_group_yield(u32 id); error_code sys_spu_thread_group_terminate(u32 id, s32 value); -error_code sys_spu_thread_group_join(u32 id, vm::ps3::ptr cause, vm::ps3::ptr status); +error_code sys_spu_thread_group_join(ppu_thread&, u32 id, vm::ps3::ptr cause, vm::ps3::ptr status); error_code sys_spu_thread_group_connect_event(u32 id, u32 eq, u32 et); error_code sys_spu_thread_group_disconnect_event(u32 id, u32 et); error_code sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq_id, u64 req, vm::ps3::ptr spup); diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index 80915194e0..ddf9e6c7a1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -105,4 +105,39 @@ struct lv2_obj queue.erase(it); return res; } + + // Remove the current thread from the scheduling queue, register timeout + static void sleep(named_thread&, u64 wait_until); + + template + static void sleep(T& thread, u64 start_time, u64 timeout) + { + sleep(thread, timeout ? start_time + timeout : -1); + } + + // Schedule the thread + static void awake(class cpu_thread&, u32 prio); + + template + static void awake(T& cpu) + { + awake(cpu, -1); + } + + static void cleanup(); + +private: + // Scheduler mutex + static semaphore<> g_mutex; + + // Scheduler queue for active PPU threads + static std::deque g_ppu; + + // Waiting for the response from + static std::deque g_pending; + + // Scheduler queue for timeouts (wait until -> thread) + static std::deque> g_waiting; + + static void schedule_all(); }; diff --git a/rpcs3/Emu/Cell/lv2/sys_timer.cpp b/rpcs3/Emu/Cell/lv2/sys_timer.cpp index 60e88d2f3b..38f7fc14a5 100644 --- a/rpcs3/Emu/Cell/lv2/sys_timer.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_timer.cpp @@ -19,7 +19,7 @@ extern u64 get_system_time(); void lv2_timer::on_task() { - while (true) + while (!Emu.IsStopped()) { const u32 _state = state; @@ -50,11 +50,12 @@ void lv2_timer::on_task() } // TODO: use single global dedicated thread for busy waiting, no timer threads + lv2_obj::sleep(*this, next); thread_ctrl::wait_for(next - _now); } else if (_state == SYS_TIMER_STATE_STOP) { - thread_ctrl::wait(); + thread_ctrl::wait_for(10000); } else { @@ -143,7 +144,7 @@ error_code _sys_timer_start(u32 timer_id, u64 base_time, u64 period) if (!period && start_time >= base_time) { // Invalid oneshot (TODO: what will happen if both args are 0?) - return CELL_ETIMEDOUT; + return not_an_error(CELL_ETIMEDOUT); } if (period && period < 100) @@ -292,11 +293,10 @@ error_code sys_timer_usleep(ppu_thread& ppu, u64 sleep_time) u64 start = ppu.gpr[10] = get_system_time(); u64 passed = 0; - // SLEEP + lv2_obj::sleep(ppu, start, std::max(1, sleep_time)); while (sleep_time >= passed) { - // TODO: use single global dedicated thread for busy waiting thread_ctrl::wait_for(std::max(1, sleep_time - passed)); passed = get_system_time() - start; } diff --git a/rpcs3/Emu/PSP2/ARMv7Thread.h b/rpcs3/Emu/PSP2/ARMv7Thread.h index 2ebb223a0b..8687d8b2fe 100644 --- a/rpcs3/Emu/PSP2/ARMv7Thread.h +++ b/rpcs3/Emu/PSP2/ARMv7Thread.h @@ -139,6 +139,8 @@ public: const std::string m_name; + atomic_t owner{}; + const char* last_function = nullptr; void write_pc(u32 value, u32 size) diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index dc79a378a7..f481059d13 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -375,6 +375,7 @@ namespace rsx ({ { ppu_cmd::set_args, 1 }, u64{1}, { ppu_cmd::lle_call, vblank_handler }, + { ppu_cmd::sleep, 0 } }); intr_thread->notify(); diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp index 41a7a25fea..1faf1598d1 100644 --- a/rpcs3/Emu/RSX/rsx_methods.cpp +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -779,6 +779,7 @@ namespace rsx ({ { ppu_cmd::set_args, 1 }, u64{1}, { ppu_cmd::lle_call, rsx->flip_handler }, + { ppu_cmd::sleep, 0 } }); rsx->intr_thread->notify(); @@ -793,6 +794,7 @@ namespace rsx ({ { ppu_cmd::set_args, 1 }, u64{arg}, { ppu_cmd::lle_call, rsx->user_handler }, + { ppu_cmd::sleep, 0 } }); rsx->intr_thread->notify(); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index d4081e00ba..e9fe0b370d 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -466,6 +466,7 @@ void Emulator::Stop() LOG_NOTICE(GENERAL, "All threads stopped..."); + lv2_obj::cleanup(); idm::clear(); fxm::clear();