diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
index ce2133fdc..403a1a684 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
@@ -7,6 +7,7 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging;
+using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using System.Collections.Concurrent;
@@ -23,16 +24,62 @@ namespace Ryujinx.HLE.Loaders.Processes
private ulong _latestPid;
- public ProcessResult ActiveApplication
+ private readonly object _pidLock = new();
+
+#nullable enable
+ public ProcessResult? ActiveApplication
{
get
{
- if (_latestPid != 0 && _processesByPid.TryGetValue(_latestPid, out ProcessResult process))
- return process;
+ lock (_pidLock)
+ {
+ // Check if _latestPid is still valid
+ if (_latestPid == 0)
+ {
+ return null;
+ }
- return null;
+ // 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;
+
+ 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;
+
+ 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
public ProcessLoader(Switch device)
{
@@ -235,7 +282,7 @@ namespace Ryujinx.HLE.Loaders.Processes
}
// Explicitly null TitleId to disable the shader cache.
- Graphics.Gpu.GraphicsConfig.TitleId = null;
+ GraphicsConfig.TitleId = null;
_device.Gpu.HostInitalized.Set();
ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
@@ -274,5 +321,37 @@ 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;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Clears all processes from the ProcessLoader's tracking.
+ /// This should be called during system shutdown.
+ ///
+ public void ClearAllProcesses()
+ {
+ lock (_pidLock)
+ {
+ _processesByPid.Clear();
+ _latestPid = 0;
+ }
+ }
}
}
diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs
index c7c01eb8c..96b0c0a48 100644
--- a/src/Ryujinx.HLE/Switch.cs
+++ b/src/Ryujinx.HLE/Switch.cs
@@ -208,6 +208,7 @@ namespace Ryujinx.HLE
{
if (disposing)
{
+ Processes.ClearAllProcesses();
System.Dispose();
AudioDeviceDriver.Dispose();
FileSystem.Dispose();