error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id) { sys_ppu_thread.trace("sys_ppu_thread_start(thread_id=0x%x)", thread_id); const auto thread = idm::get<ppu_thread>(thread_id, [&](ppu_thread& thread) { lv2_obj::awake(thread, -2); }); if (!thread) { return CELL_ESRCH; } if (!thread->state.test_and_reset(cpu_flag::stop)) { // TODO: what happens there? return CELL_EPERM; } else { thread->notify(); // Dirty hack for sound: confirm the creation of _mxr000 event queue if (thread->m_name == "_cellsurMixerMain") { lv2_obj::sleep(ppu); while (!idm::select<lv2_obj, lv2_event_queue>([](u32, lv2_event_queue& eq) { //some games do not set event queue name, though key seems constant for them return (eq.name == "_mxr000\0"_u64) || (eq.key == 0x8000cafe02460300); })) { thread_ctrl::wait_for(50000); } ppu.test_state(); } } return CELL_OK; }
error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr<u64> vptr) { vm::temporary_unlock(ppu); sys_ppu_thread.trace("sys_ppu_thread_join(thread_id=0x%x, vptr=*0x%x)", thread_id, vptr); const auto thread = idm::get<ppu_thread>(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); } return result; }); if (!thread) { return CELL_ESRCH; } if (thread.ret && thread.ret != CELL_EBUSY) { return thread.ret; } // Wait for cleanup thread->join(); // Get the exit status from the register if (vptr) { ppu.test_state(); *vptr = thread->gpr[3]; } // Cleanup idm::remove<ppu_thread>(thread->id); return CELL_OK; }