s32 sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout) { sys_semaphore.trace("sys_semaphore_wait(sem_id=0x%x, timeout=0x%llx)", sem_id, timeout); const u64 start_time = get_system_time(); LV2_LOCK; const auto sem = idm::get<lv2_sema_t>(sem_id); if (!sem) { return CELL_ESRCH; } if (sem->value > 0) { sem->value--; return CELL_OK; } // add waiter; protocol is ignored in current implementation sleep_entry<cpu_thread> waiter(sem->sq, ppu); while (!ppu.state.test_and_reset(cpu_state::signal)) { CHECK_EMU_STATUS; if (timeout) { const u64 passed = get_system_time() - start_time; if (passed >= timeout) { return CELL_ETIMEDOUT; } get_current_thread_cv().wait_for(lv2_lock, std::chrono::microseconds(timeout - passed)); } else { get_current_thread_cv().wait(lv2_lock); } } return CELL_OK; }
bool cpu_thread::check_status() { std::unique_lock<std::mutex> lock(get_current_thread_mutex(), std::defer_lock); while (true) { CHECK_EMU_STATUS; // check at least once if (state & cpu_state::exit) { return true; } if (!state.test(cpu_state_pause) && !state.test(cpu_state::interrupt)) { break; } if (!lock) { lock.lock(); continue; } if (!state.test(cpu_state_pause) && state & cpu_state::interrupt && handle_interrupt()) { continue; } get_current_thread_cv().wait(lock); } const auto state_ = state.load(); if (state_ & make_bitset(cpu_state::ret, cpu_state::stop)) { return true; } if (state_ & cpu_state::dbg_step) { state += cpu_state::dbg_pause; state -= cpu_state::dbg_step; } return false; }
void cpu_thread::on_task() { state -= cpu_state::exit; g_tls_current_cpu_thread = this; Emu.SendDbgCommand(DID_CREATE_THREAD, this); std::unique_lock<std::mutex> lock(get_current_thread_mutex()); // Check thread status while (!(state & cpu_state::exit)) { CHECK_EMU_STATUS; // check stop status if (!(state & cpu_state::stop)) { if (lock) lock.unlock(); try { cpu_task(); } catch (cpu_state _s) { state += _s; } catch (const std::exception&) { LOG_NOTICE(GENERAL, "\n%s", dump()); throw; } state -= cpu_state::ret; continue; } if (!lock) { lock.lock(); continue; } get_current_thread_cv().wait(lock); } }
void lv2_int_serv_t::join(ppu_thread& ppu, lv2_lock_t lv2_lock) { // Enqueue _sys_ppu_thread_exit call thread->cmd_list ({ { ppu_cmd::set_args, 1 }, u64{0}, { ppu_cmd::set_gpr, 11 }, u64{41}, { ppu_cmd::opcode, ppu_instructions::SC(0) }, }); thread->lock_notify(); // Join thread (TODO) while (!test(thread->state & cpu_flag::exit)) { CHECK_EMU_STATUS; get_current_thread_cv().wait_for(lv2_lock, 1ms); } // Cleanup idm::remove<lv2_int_serv_t>(id); }