[Emulator] Fix crashes with in-process relaunch

Gracefully shutdown dispatch thread and ensure we don't persist
alternate xex paths as separate discs
This commit is contained in:
Herman S.
2026-02-09 10:29:07 +09:00
parent 3eb5c17275
commit 65b481ed29
3 changed files with 22 additions and 12 deletions
+9 -3
View File
@@ -1461,8 +1461,11 @@ void Emulator::RelaunchTitle(const std::string& host_path,
// Tell WaitUntilExit not to fire on_exit when main thread dies.
relaunching_ = true;
// Force-terminate all threads. Cooperative shutdown isn't possible since
// workers may be stuck in processor_->Execute().
// Stop the dispatch thread gracefully before force-terminating threads,
// otherwise TerminateThread corrupts the CV it's blocked on.
kernel_state_->ShutdownDispatchThread();
// Force-terminate remaining threads.
{
auto threads =
kernel_state()->object_table()->GetObjectsByType<kernel::XThread>(
@@ -2012,7 +2015,10 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
game_info_database_ =
std::make_unique<kernel::util::GameInfoDatabase>(db.get());
kernel_state_->xam_state()->LoadSpaInfo(db.get(), path);
// Don't persist disc path during in-process relaunch; only for
// user-initiated file opens.
kernel_state_->xam_state()->LoadSpaInfo(
db.get(), relaunching_ ? std::filesystem::path{} : path);
// AddTitleToPlayedList is now called inside LoadSpaInfo/UpdateSpaInfo
+9 -9
View File
@@ -80,15 +80,7 @@ KernelState::KernelState(Emulator* emulator)
KernelState::~KernelState() {
SetExecutableModule(nullptr);
if (dispatch_thread_running_) {
dispatch_thread_running_ = false;
if (dispatch_thread_ && dispatch_thread_->is_running()) {
dispatch_cond_.notify_all();
dispatch_thread_->Wait(0, 0, 0, nullptr);
}
// Skip notify/Wait if already force-terminated — mutex may be abandoned.
dispatch_thread_.reset();
}
ShutdownDispatchThread();
executable_module_.reset();
user_modules_.clear();
@@ -103,6 +95,14 @@ KernelState::~KernelState() {
shared_kernel_state_ = nullptr;
}
void KernelState::ShutdownDispatchThread() {
if (dispatch_thread_running_) {
dispatch_thread_running_ = false;
dispatch_cond_.notify_all();
dispatch_thread_->Wait(0, 0, 0, nullptr);
}
}
KernelState* KernelState::shared() { return shared_kernel_state_; }
uint32_t KernelState::title_id() const {
+4
View File
@@ -262,6 +262,10 @@ class KernelState {
// This DOES NOT RETURN if called from a guest thread!
void TerminateTitle();
// Gracefully stops the dispatch thread. Call before force-terminating
// threads to avoid corrupting the CV it's blocked on.
void ShutdownDispatchThread();
void RegisterThread(XThread* thread);
void UnregisterThread(XThread* thread);
void OnThreadExecute(XThread* thread);