From 3127543b6e0f1763e5336611c036c07b3a457aad Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Fri, 7 Mar 2014 16:03:42 +0400 Subject: [PATCH] sys_ppu_thread_join, sys_ppu_thread_exit fixed Some diagnostic messages in mutexes --- rpcs3/Emu/CPU/CPUThread.cpp | 2 +- rpcs3/Emu/CPU/CPUThread.h | 8 +-- rpcs3/Emu/Cell/PPUInterpreter.h | 7 +- rpcs3/Emu/Cell/PPUThread.cpp | 1 + rpcs3/Emu/Cell/PPUThread.h | 3 + rpcs3/Emu/SysCalls/Modules/cellDmux.cpp | 15 +++- rpcs3/Emu/SysCalls/Modules/libmixer.cpp | 88 ++++++++++++++++++++++-- rpcs3/Emu/SysCalls/Static.cpp | 4 +- rpcs3/Emu/SysCalls/SysCalls.h | 4 +- rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp | 32 ++++++++- rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h | 2 +- rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp | 71 ++++++++++++++----- rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp | 39 ++++++----- 13 files changed, 220 insertions(+), 56 deletions(-) diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index e2ce343447..1ec31434b2 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -361,7 +361,7 @@ void CPUThread::Task() if (Ini.HLELogging.GetValue()) ConLog.Write("%s leave", CPUThread::GetFName().wx_str()); } -int CPUThread::ExecAsCallback(u64 pc, bool wait, u64 a1, u64 a2, u64 a3, u64 a4) // not multithread-safe +s64 CPUThread::ExecAsCallback(u64 pc, bool wait, u64 a1, u64 a2, u64 a3, u64 a4) // not multithread-safe { while (m_alive) { diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index 4c0e94ed01..3c2bbafd3e 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -57,7 +57,7 @@ protected: u64 m_stack_size; u64 m_stack_point; - u32 m_exit_status; + u64 m_exit_status; CPUDecoder* m_dec; @@ -80,10 +80,10 @@ public: void SetName(const std::string& name); void SetPrio(const u64 prio) { m_prio = prio; } void SetOffset(const u64 offset) { m_offset = offset; } - void SetExitStatus(const u32 status) { m_exit_status = status; } + void SetExitStatus(const u64 status) { m_exit_status = status; } u64 GetOffset() const { return m_offset; } - u32 GetExitStatus() const { return m_exit_status; } + u64 GetExitStatus() const { return m_exit_status; } u64 GetPrio() const { return m_prio; } std::string GetName() const { return NamedThreadBase::GetThreadName(); } @@ -234,7 +234,7 @@ public: return pc + 4; } - int ExecAsCallback(u64 pc, bool wait, u64 a1 = 0, u64 a2 = 0, u64 a3 = 0, u64 a4 = 0); + s64 ExecAsCallback(u64 pc, bool wait, u64 a1 = 0, u64 a2 = 0, u64 a3 = 0, u64 a4 = 0); protected: virtual void DoReset()=0; diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index 32fbdd0a3b..708e4d3408 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -2096,8 +2096,11 @@ private: case 0x2: SysCall(); break; case 0x3: StaticExecute(CPU.GPR[11]); - ConLog.Write("'%s' done with code[0x%llx]! #pc: 0x%llx", - wxString(g_static_funcs_list[CPU.GPR[11]].name).wx_str(), CPU.GPR[3], CPU.PC); + if (Ini.HLELogging.GetValue()) + { + ConLog.Write("'%s' done with code[0x%llx]! #pc: 0x%llx", + wxString(g_static_funcs_list[CPU.GPR[11]].name).wx_str(), CPU.GPR[3], CPU.PC); + } break; case 0x22: UNK("HyperCall LV1"); break; default: UNK(wxString::Format("Unknown sc: %x", sc_code)); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 1c1c903b50..bd6c6570e5 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -17,6 +17,7 @@ PPUThread& GetCurrentPPUThread() PPUThread::PPUThread() : PPCThread(CPU_THREAD_PPU) { + owned_mutexes = 0; Reset(); } diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index b4a12efedf..fbb53bf1ae 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -525,6 +525,9 @@ static const s32 MAX_INT_VALUE = 0x7fffffff; class PPUThread : public PPCThread { +public: + std::atomic owned_mutexes; + public: PPCdouble FPR[32]; //Floating Point Register FPSCRhdr FPSCR; //Floating Point Status and Control Register diff --git a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp index a664683a29..96ad7083a6 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp @@ -80,8 +80,8 @@ u32 dmuxOpen(Demuxer* data) cb.SetAddr(dmux.cbFunc); cb.Handle(dmux.id, dmuxMsg.GetAddr(), dmux.cbArg); cb.Branch(task.type == dmuxResetStreamAndWaitDone);*/ - dmux.dmuxCb->ExecAsCallback(dmux.cbFunc, true, dmux.id, dmuxMsg.GetAddr(), dmux.cbArg); - updates_signaled++; + //dmux.dmuxCb->ExecAsCallback(dmux.cbFunc, true, dmux.id, dmuxMsg.GetAddr(), dmux.cbArg); + //updates_signaled++; } else switch (code.ToLE()) { @@ -584,6 +584,17 @@ int cellDmuxSetStream(u32 demuxerHandle, const u32 streamAddress, u32 streamSize return CELL_DMUX_ERROR_FATAL; } + if (dmux->is_running) // maximum hack + { + mem_ptr_t dmuxMsg(a128(dmux->memAddr) + 128); + dmuxMsg->msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE; + dmuxMsg->supplementalInfo = 0; // !!! + Callback cb; + cb.SetAddr(dmux->cbFunc); + cb.Handle(dmux->id, dmuxMsg.GetAddr(), dmux->cbArg); + cb.Branch(true); + } + while (dmux->is_running) // hack { if (Emu.IsStopped()) diff --git a/rpcs3/Emu/SysCalls/Modules/libmixer.cpp b/rpcs3/Emu/SysCalls/Modules/libmixer.cpp index 81ed26a5a8..cb2b3bb547 100644 --- a/rpcs3/Emu/SysCalls/Modules/libmixer.cpp +++ b/rpcs3/Emu/SysCalls/Modules/libmixer.cpp @@ -13,20 +13,21 @@ int cellAANAddData(u32 handle, u32 port, u32 offset, u32 addr, u32 samples) return CELL_OK; } -// libmixer Functions, NOT active in this moment -/*int cellAANConnect(CellAANHandle receive, u32 receivePortNo, CellAANHandle source, u32 sourcePortNo) +int cellAANConnect(u32 receive, u32 receivePortNo, u32 source, u32 sourcePortNo) { - UNIMPLEMENTED_FUNC(libmixer); + libmixer.Error("cellAANConnect(receive=0x%x, receivePortNo=0x%x, source=0x%x, sourcrPortNo=0x%x)", + receive, receivePortNo, source, sourcePortNo); return 0; } -int cellAANDisconnect(CellAANHandle receive, u32 receivePortNo, CellAANHandle source, u32 sourcePortNo) +int cellAANDisconnect(u32 receive, u32 receivePortNo, u32 source, u32 sourcePortNo) { - UNIMPLEMENTED_FUNC(libmixer); + libmixer.Error("cellAANDisconnect(receive=0x%x, receivePortNo=0x%x, source=0x%x, sourcrPortNo=0x%x)", + receive, receivePortNo, source, sourcePortNo); return 0; } -int cellSSPlayerCreate(CellAANHandle *handle, CellSSPlayerConfig *config) +/*int cellSSPlayerCreate(CellAANHandle *handle, CellSSPlayerConfig *config) { UNIMPLEMENTED_FUNC(libmixer); return 0; @@ -182,7 +183,6 @@ void libmixer_init() 0xffffffff81690000, 0xffffffff7c050378, 0xffffffff7cc43378, - // 78 63 00 20 clrldi r3,r3,32 # 20 // may be included 0xffffffff7d465378, 0xffffffff812b0030, 0xffffffff80090000, @@ -200,6 +200,80 @@ void libmixer_init() }; libmixer.AddFuncSub(cellAANAddData_table, "cellAANAddData", cellAANAddData); + u64 cellAANConnect_table[39] = { + 0xfffffffff821ff71, + 0xffffffff7c0802a6, + 0xffffffff2f830000, + 0xfffffffff80100a0, + 0xffffffff3c008031, + 0xffffffff7c691b78, + 0xffffffff7c8a2378, + 0xffffffff60000003, + 0xffffff00409e0018, // bne + 0xffffffff7c0307b4, + 0xffffffffe80100a0, + 0xffffffff38210090, + 0xffffffff7c0803a6, + 0xffffffff4e800020, + 0xffffffff2f850000, + 0xffffffff78630020, + 0xffffffff38810070, + 0xffffff00419effe0, // beq + 0xffffffff81690000, + 0xffffffff38000001, + 0xffffffff91210074, + 0xffffffff90a10070, + 0xffffffff90c10078, + 0xffffffff9141007c, + 0xffffffff812b0018, // [24] + 0xffffffff90010080, + 0xffffffff80090000, + 0xfffffffff8410028, + 0xffffffff7c0903a6, + 0xffffffff80490004, + 0xffffffff4e800421, + 0xffffffffe8410028, + 0xffffffff7c601b78, + 0xffffffff7c0307b4, + 0xffffffffe80100a0, + 0xffffffff38210090, + 0xffffffff7c0803a6, + 0xffffffff4e800020, + 0, // [38] + }; + libmixer.AddFuncSub(cellAANConnect_table, "cellAANConnect", cellAANConnect); + cellAANConnect_table[24] = 0xffffffff812b001c; + libmixer.AddFuncSub(cellAANConnect_table, "cellAANDisconnect", cellAANDisconnect); + + static const u64 cellAANAddData_table1[] = { + // TODO + 0xffffffff7c691b78, + 0xffffffff7c0802a6, + 0xfffffffff821ff91, + 0xfffffffff8010080, + 0xffffffff7c802378, + 0xffffffff7caa2b78, + 0xffffffff81690000, + 0xffffffff7c050378, + 0xffffffff7cc43378, + 0xffffffff78630020, // clrldi r3,r3,32 + 0xffffffff7d465378, + 0xffffffff812b0030, + 0xffffffff80090000, + 0xfffffffff8410028, + 0xffffffff7c0903a6, + 0xffffffff80490004, + 0xffffffff4e800421, + 0xffffffffe8410028, + 0xffffffffe8010080, + 0xffffffff7c6307b4, + 0xffffffff7c0803a6, + 0xffffffff38210070, + 0xffffffff4e800020, + 0 + }; + libmixer.AddFuncSub(cellAANAddData_table1, "cellAANAddData(1)", cellAANAddData); + static const u64 cellSurMixerCreate_table[] = { 0xffffffff2f830000, 0xffffffff7c0802a6, diff --git a/rpcs3/Emu/SysCalls/Static.cpp b/rpcs3/Emu/SysCalls/Static.cpp index 6d653a7b1c..2628a77272 100644 --- a/rpcs3/Emu/SysCalls/Static.cpp +++ b/rpcs3/Emu/SysCalls/Static.cpp @@ -9,6 +9,8 @@ void StaticAnalyse(void* ptr, u32 size) { u32* data = (u32*)ptr; size /= 4; + return; // disabled + // TODO: optimize search for (u32 i = 0; i < size; i++) { @@ -38,7 +40,7 @@ void StaticAnalyse(void* ptr, u32 size) data[i] = re(0x39600000 | j); // li r11, j data[i+1] = se32(0x44000003); // sc 3 data[i+2] = se32(0x4e800020); // blr - i += g_static_funcs_list[j].ops.GetCount(); // ??? + i += 2; // ??? } } } diff --git a/rpcs3/Emu/SysCalls/SysCalls.h b/rpcs3/Emu/SysCalls/SysCalls.h index 988c1391e3..c97518f7a4 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.h +++ b/rpcs3/Emu/SysCalls/SysCalls.h @@ -201,9 +201,9 @@ extern int sys_rwlock_trywlock(u32 rw_lock_id); extern int sys_rwlock_wunlock(u32 rw_lock_id); //ppu_thread -extern void sys_ppu_thread_exit(int errorcode); +extern void sys_ppu_thread_exit(u64 errorcode); extern int sys_ppu_thread_yield(); -extern int sys_ppu_thread_join(u32 thread_id, u32 vptr_addr); +extern int sys_ppu_thread_join(u32 thread_id, mem64_t vptr); extern int sys_ppu_thread_detach(u32 thread_id); extern void sys_ppu_thread_get_join_state(u32 isjoinable_addr); extern int sys_ppu_thread_set_priority(u32 thread_id, int prio); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp index 76a0cf4ebc..524c600033 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp @@ -207,17 +207,40 @@ int sys_lwmutex_t::trylock(be_t tid) { if (attribute.ToBE() == se32(0xDEADBEEF)) return CELL_EINVAL; + be_t owner_tid = mutex.GetOwner(); + + if (owner_tid != mutex.GetFreeValue()) + { + if (CPUThread* tt = Emu.GetCPU().GetThread(owner_tid)) + { + if (!tt->IsAlive()) + { + sc_lwmutex.Error("sys_lwmutex_t::(try)lock(%d): deadlock on invalid thread(%d)", (u32)sleep_queue, (u32)owner_tid); + mutex.unlock(owner_tid, tid); + recursive_count = 1; + return CELL_OK; + } + } + else + { + sc_lwmutex.Error("sys_lwmutex_t::(try)lock(%d): deadlock on invalid thread(%d)", (u32)sleep_queue, (u32)owner_tid); + mutex.unlock(owner_tid, tid); + recursive_count = 1; + return CELL_OK; + } + } + while ((attribute.ToBE() & se32(SYS_SYNC_ATTR_RECURSIVE_MASK)) == 0) { if (Emu.IsStopped()) { - ConLog.Warning("(hack) sys_lwmutex_t::trylock aborted (waiting for recursive attribute, attr=0x%x)", (u32)attribute); + ConLog.Warning("(hack) sys_lwmutex_t::(try)lock aborted (waiting for recursive attribute, attr=0x%x)", (u32)attribute); return CELL_ESRCH; } Sleep(1); } - if (tid == mutex.GetOwner()) + if (tid == owner_tid) { if (attribute.ToBE() & se32(SYS_SYNC_RECURSIVE)) { @@ -247,6 +270,11 @@ int sys_lwmutex_t::unlock(be_t tid) } else { + if (!recursive_count || (recursive_count.ToBE() != se32(1) && (attribute.ToBE() & se32(SYS_SYNC_NOT_RECURSIVE)))) + { + sc_lwmutex.Error("sys_lwmutex_t::unlock(%d): wrong recursive value (%d)", (u32)sleep_queue, (u32)recursive_count); + recursive_count = 1; + } recursive_count -= 1; if (!recursive_count.ToBE()) { diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h index 971363a92f..4a5481d0ce 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h +++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h @@ -65,7 +65,7 @@ struct SleepQueue struct sys_lwmutex_t { - /* volatile */ SMutexBase, ~0, 0> mutex; + /* volatile */ SMutexBase> mutex; /* volatile */ be_t waiter; // not used u64 &all_info(){return *(reinterpret_cast(this));} be_t attribute; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp index d3921cbcc0..90c64b963f 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp @@ -95,7 +95,8 @@ int sys_mutex_lock(u32 mutex_id, u64 timeout) return CELL_ESRCH; } - u32 tid = GetCurrentPPUThread().GetId(); + PPUThread& t = GetCurrentPPUThread(); + u32 tid = t.GetId(); u32 owner = mutex->m_mutex.GetOwner(); if (owner == tid) @@ -113,17 +114,32 @@ int sys_mutex_lock(u32 mutex_id, u64 timeout) return CELL_EDEADLK; } } - else if (owner && !Emu.GetIdManager().CheckID(owner)) + else if (owner) { - sys_mtx.Error("sys_mutex_lock(%d): deadlock on invalid thread(%d)", mutex_id, owner); - mutex->m_mutex.unlock(owner, tid); - mutex->recursive = 1; - return CELL_OK; + if (CPUThread* tt = Emu.GetCPU().GetThread(owner)) + { + if (!tt->IsAlive()) + { + sys_mtx.Error("sys_mutex_lock(%d): deadlock on invalid thread(%d)", mutex_id, owner); + mutex->m_mutex.unlock(owner, tid); + mutex->recursive = 1; + t.owned_mutexes++; + return CELL_OK; + } + } + else + { + sys_mtx.Error("sys_mutex_lock(%d): deadlock on invalid thread(%d)", mutex_id, owner); + mutex->m_mutex.unlock(owner, tid); + mutex->recursive = 1; + t.owned_mutexes++; + return CELL_OK; + } } switch (mutex->m_mutex.trylock(tid)) { - case SMR_OK: mutex->recursive = 1; return CELL_OK; + case SMR_OK: mutex->recursive = 1; t.owned_mutexes++; return CELL_OK; case SMR_FAILED: break; default: goto abort; } @@ -135,7 +151,7 @@ int sys_mutex_lock(u32 mutex_id, u64 timeout) case SMR_OK: mutex->m_queue.invalidate(tid); case SMR_SIGNAL: - mutex->recursive = 1; return CELL_OK; + mutex->recursive = 1; t.owned_mutexes++; return CELL_OK; case SMR_TIMEOUT: mutex->m_queue.invalidate(tid); return CELL_ETIMEDOUT; default: @@ -161,7 +177,8 @@ int sys_mutex_trylock(u32 mutex_id) return CELL_ESRCH; } - u32 tid = GetCurrentPPUThread().GetId(); + PPUThread& t = GetCurrentPPUThread(); + u32 tid = t.GetId(); u32 owner = mutex->m_mutex.GetOwner(); if (owner == tid) @@ -179,17 +196,32 @@ int sys_mutex_trylock(u32 mutex_id) return CELL_EDEADLK; } } - else if (owner && !Emu.GetIdManager().CheckID(owner)) + else if (owner) { - sys_mtx.Error("sys_mutex_trylock(%d): deadlock on invalid thread(%d)", mutex_id, owner); - mutex->m_mutex.unlock(owner, tid); - mutex->recursive = 1; - return CELL_OK; + if (CPUThread* tt = Emu.GetCPU().GetThread(owner)) + { + if (!tt->IsAlive()) + { + sys_mtx.Error("sys_mutex_trylock(%d): deadlock on invalid thread(%d)", mutex_id, owner); + mutex->m_mutex.unlock(owner, tid); + mutex->recursive = 1; + t.owned_mutexes++; + return CELL_OK; + } + } + else + { + sys_mtx.Error("sys_mutex_trylock(%d): deadlock on invalid thread(%d)", mutex_id, owner); + mutex->m_mutex.unlock(owner, tid); + mutex->recursive = 1; + t.owned_mutexes++; + return CELL_OK; + } } switch (mutex->m_mutex.trylock(tid)) { - case SMR_OK: mutex->recursive = 1; return CELL_OK; + case SMR_OK: mutex->recursive = 1; t.owned_mutexes++; return CELL_OK; } return CELL_EBUSY; @@ -205,18 +237,21 @@ int sys_mutex_unlock(u32 mutex_id) return CELL_ESRCH; } - u32 tid = GetCurrentPPUThread().GetId(); + PPUThread& t = GetCurrentPPUThread(); + u32 tid = t.GetId(); if (mutex->m_mutex.GetOwner() == tid) { - if (!mutex->recursive || (mutex->recursive > 1 && !mutex->is_recursive)) + if (!mutex->recursive || (mutex->recursive != 1 && !mutex->is_recursive)) { - sys_mtx.Warning("sys_mutex_unlock(%d): wrong recursive value (%d)", mutex_id, mutex->recursive); + sys_mtx.Error("sys_mutex_unlock(%d): wrong recursive value (%d)", mutex_id, mutex->recursive); + mutex->recursive = 1; } mutex->recursive--; if (!mutex->recursive) { mutex->m_mutex.unlock(tid, mutex->protocol == SYS_SYNC_PRIORITY ? mutex->m_queue.pop_prio() : mutex->m_queue.pop()); + t.owned_mutexes--; } return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp index fb39296f6e..30c31bea17 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp @@ -10,24 +10,21 @@ enum SYS_PPU_THREAD_DONE_INIT, }; -void sys_ppu_thread_exit(int errorcode) +void sys_ppu_thread_exit(u64 errorcode) { - if(errorcode == 0) - { - sysPrxForUser.Log("sys_ppu_thread_exit(errorcode=%d)", errorcode); - } - else - { - sysPrxForUser.Warning("sys_ppu_thread_exit(errorcode=%d)", errorcode); - } + sysPrxForUser.Log("sys_ppu_thread_exit(0x%llx)", errorcode); PPUThread& thr = GetCurrentPPUThread(); + u32 tid = thr.GetId(); + + if (thr.owned_mutexes) + { + ConLog.Error("Owned mutexes found (%d)", thr.owned_mutexes); + thr.owned_mutexes = 0; + } + thr.SetExitStatus(errorcode); thr.Stop(); - - //Emu.GetCPU().RemoveThread(thr.GetId()); - - //throw errorcode; } int sys_ppu_thread_yield() @@ -37,14 +34,24 @@ int sys_ppu_thread_yield() return CELL_OK; } -int sys_ppu_thread_join(u32 thread_id, u32 vptr_addr) +int sys_ppu_thread_join(u32 thread_id, mem64_t vptr) { - sysPrxForUser.Warning("sys_ppu_thread_join(thread_id=%d, vptr_addr=0x%x)", thread_id, vptr_addr); + sysPrxForUser.Warning("sys_ppu_thread_join(thread_id=%d, vptr_addr=0x%x)", thread_id, vptr.GetAddr()); CPUThread* thr = Emu.GetCPU().GetThread(thread_id); if(!thr) return CELL_ESRCH; - GetCurrentPPUThread().Wait(*thr); + while (thr->IsAlive()) + { + if (Emu.IsStopped()) + { + ConLog.Warning("sys_ppu_thread_join(%d) aborted", thread_id); + return CELL_OK; + } + Sleep(1); + } + + vptr = thr->GetExitStatus(); return CELL_OK; }