diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
index 900703f6e..f217ecd0b 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
@@ -28,21 +28,61 @@ namespace Ryujinx.HLE.Loaders.Processes
private ulong _latestPid;
+ private readonly object _pidLock = new();
+
#nullable enable
public ProcessResult? ActiveApplication
{
get
{
- return _processesByPid.GetValueOrDefault(_latestPid);
-
- // Using this if statement locks up the UI and prevents a new game from loading.
- // Haven't quite deduced why yet.
-
- if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
- throw new RyujinxException(
- $"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
+ lock (_pidLock)
+ {
+ // Check if _latestPid is still valid
+ if (_latestPid == 0)
+ {
+ return null;
+ }
- return value;
+ // Verify process still exists in kernel (authoritative source)
+ if (!_device.System.KernelContext.Processes.TryGetValue(_latestPid, out HOS.Kernel.Process.KProcess? kernelProcess))
+ {
+ // Process no longer exists in kernel, clear stale state
+ Logger.Warning?.Print(LogClass.Loader,
+ $"ActiveApplication PID {_latestPid} no longer exists in kernel, clearing stale state");
+
+ _processesByPid.TryRemove(_latestPid, out _);
+ _latestPid = 0;
+ TitleIDs.CurrentApplication.Value = null;
+
+ return null;
+ }
+
+ // Verify process still exists in ProcessLoader's dictionary
+ if (_processesByPid.TryGetValue(_latestPid, out ProcessResult? processResult))
+ {
+ // Additional check: verify process state
+ if (kernelProcess.State == HOS.Kernel.Process.ProcessState.Exited ||
+ kernelProcess.State == HOS.Kernel.Process.ProcessState.Exiting)
+ {
+ Logger.Warning?.Print(LogClass.Loader,
+ $"ActiveApplication PID {_latestPid} is in state {kernelProcess.State}, clearing");
+
+ _processesByPid.TryRemove(_latestPid, out _);
+ _latestPid = 0;
+ TitleIDs.CurrentApplication.Value = null;
+
+ return null;
+ }
+
+ return processResult;
+ }
+
+ // Fallback: clear stale PID if not in our dictionary
+ Logger.Warning?.Print(LogClass.Loader,
+ $"ActiveApplication PID {_latestPid} not in ProcessLoader dictionary, clearing");
+ _latestPid = 0;
+ return null;
+ }
}
}
#nullable disable
@@ -285,5 +325,39 @@ namespace Ryujinx.HLE.Loaders.Processes
return false;
}
+
+ ///
+ /// Clears a specific process from the ProcessLoader's tracking.
+ /// This should be called when a process exits or is terminated.
+ ///
+ /// The process ID to clear
+ public void ClearProcess(ulong pid)
+ {
+ lock (_pidLock)
+ {
+ if (_processesByPid.TryRemove(pid, out _))
+ {
+ if (_latestPid == pid)
+ {
+ _latestPid = 0;
+ TitleIDs.CurrentApplication.Value = null;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Clears all processes from the ProcessLoader's tracking.
+ /// This should be called during system shutdown.
+ ///
+ public void ClearAllProcesses()
+ {
+ lock (_pidLock)
+ {
+ _processesByPid.Clear();
+ _latestPid = 0;
+ TitleIDs.CurrentApplication.Value = null;
+ }
+ }
}
}
diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs
index 850c8b5fa..90af47988 100644
--- a/src/Ryujinx.HLE/Switch.cs
+++ b/src/Ryujinx.HLE/Switch.cs
@@ -183,6 +183,7 @@ namespace Ryujinx.HLE
{
if (disposing)
{
+ Processes.ClearAllProcesses();
System.Dispose();
AudioDeviceDriver.Dispose();
FileSystem.Dispose();