diff --git a/ChocolArm64/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs index a84e3242bf..b26571b025 100644 --- a/ChocolArm64/State/AThreadState.cs +++ b/ChocolArm64/State/AThreadState.cs @@ -100,6 +100,11 @@ namespace ChocolArm64.State TickCounter.Start(); } + internal bool Synchronize() + { + return Running; + } + internal void OnBreak(long Position, int Imm) { Break?.Invoke(this, new AInstExceptionEventArgs(Position, Imm)); diff --git a/ChocolArm64/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs index 3fa46e96d6..40e33ba8ea 100644 --- a/ChocolArm64/Translation/AILEmitterCtx.cs +++ b/ChocolArm64/Translation/AILEmitterCtx.cs @@ -110,6 +110,8 @@ namespace ChocolArm64.Translation if (OpcIndex == 0) { MarkLabel(GetLabel(CurrBlock.Position)); + + EmitSynchronization(); } CurrOp.Emitter(this); @@ -117,6 +119,25 @@ namespace ChocolArm64.Translation ILBlock.Add(new AILBarrier()); } + private void EmitSynchronization() + { + EmitLdarg(ATranslatedSub.StateArgIdx); + + EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.Synchronize)); + + EmitLdc_I4(0); + + AILLabel LblContinue = new AILLabel(); + + Emit(OpCodes.Bne_Un_S, LblContinue); + + EmitLdc_I8(0); + + Emit(OpCodes.Ret); + + MarkLabel(LblContinue); + } + public bool TryOptEmitSubroutineCall() { if (CurrBlock.Next == null) diff --git a/Ryujinx.Audio/IAalOutput.cs b/Ryujinx.Audio/IAalOutput.cs index e903c5c5cc..1dfac377f7 100644 --- a/Ryujinx.Audio/IAalOutput.cs +++ b/Ryujinx.Audio/IAalOutput.cs @@ -1,6 +1,8 @@ +using System; + namespace Ryujinx.Audio { - public interface IAalOutput + public interface IAalOutput : IDisposable { int OpenTrack(int SampleRate, int Channels, ReleaseCallback Callback); diff --git a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs index 85e2d803e9..80a070c9f3 100644 --- a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs +++ b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs @@ -8,7 +8,7 @@ using System.Threading; namespace Ryujinx.Audio.OpenAL { - public class OpenALAudioOut : IAalOutput + public class OpenALAudioOut : IAalOutput, IDisposable { private const int MaxTracks = 256; @@ -222,10 +222,17 @@ namespace Ryujinx.Audio.OpenAL Td.CallReleaseCallbackIfNeeded(); } - //If it's not slept it will waste cycles + //If it's not slept it will waste cycles. Thread.Sleep(10); } while (KeepPolling); + + foreach (Track Td in Tracks.Values) + { + Td.Dispose(); + } + + Tracks.Clear(); } public int OpenTrack(int SampleRate, int Channels, ReleaseCallback Callback) @@ -342,5 +349,18 @@ namespace Ryujinx.Audio.OpenAL return PlaybackState.Stopped; } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + KeepPolling = false; + } + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index aba004defb..48d559d09e 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -190,19 +190,22 @@ namespace Ryujinx.HLE.HOS { if (Processes.TryRemove(ProcessId, out Process Process)) { - Process.StopAllThreadsAsync(); Process.Dispose(); if (Processes.Count == 0) { - Device.OnFinish(EventArgs.Empty); + Unload(); + + Device.Unload(); } } } - internal bool TryGetProcess(int ProcessId, out Process Process) + private void Unload() { - return Processes.TryGetValue(ProcessId, out Process); + VsyncEvent.Dispose(); + + Scheduler.Dispose(); } public void Dispose() @@ -216,13 +219,8 @@ namespace Ryujinx.HLE.HOS { foreach (Process Process in Processes.Values) { - Process.StopAllThreadsAsync(); Process.Dispose(); } - - VsyncEvent.Dispose(); - - Scheduler.Dispose(); } } } diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessScheduler.cs b/Ryujinx.HLE/HOS/Kernel/KProcessScheduler.cs index 5c321e0113..2120f16c8e 100644 --- a/Ryujinx.HLE/HOS/Kernel/KProcessScheduler.cs +++ b/Ryujinx.HLE/HOS/Kernel/KProcessScheduler.cs @@ -124,6 +124,16 @@ namespace Ryujinx.HLE.HOS.Kernel AllThreads[Thread].WaitSync.Set(); } + public void ForceWakeUp(KThread Thread) + { + if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) + { + SchedThread.WaitSync.Set(); + SchedThread.WaitActivity.Set(); + SchedThread.WaitSched.Set(); + } + } + public void ChangeCore(KThread Thread, int IdealCore, int CoreMask) { lock (SchedLock) diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs index 5b88083376..fe5cca4d31 100644 --- a/Ryujinx.HLE/HOS/Process.cs +++ b/Ryujinx.HLE/HOS/Process.cs @@ -56,9 +56,7 @@ namespace Ryujinx.HLE.HOS private ConcurrentDictionary Threads; - private KThread MainThread; - - private List Executables; + private List Executables; private Dictionary SymbolTable; @@ -155,7 +153,7 @@ namespace Ryujinx.HLE.HOS return false; } - MainThread = HandleTable.GetData(Handle); + KThread MainThread = HandleTable.GetData(Handle); if (NeedsHbAbi) { @@ -182,24 +180,6 @@ namespace Ryujinx.HLE.HOS return true; } - public void StopAllThreadsAsync() - { - if (Disposed) - { - throw new ObjectDisposedException(nameof(Process)); - } - - if (MainThread != null) - { - MainThread.Thread.StopExecution(); - } - - foreach (KThread Thread in Threads.Values) - { - Thread.Thread.StopExecution(); - } - } - public int MakeThread( long EntryPoint, long StackTop, @@ -389,12 +369,9 @@ namespace Ryujinx.HLE.HOS if (Threads.Count == 0) { - if (ShouldDispose) - { - Dispose(); - } - Device.System.ExitProcess(ProcessId); + + Unload(); } } @@ -408,6 +385,35 @@ namespace Ryujinx.HLE.HOS return Thread; } + private void Unload() + { + if (Disposed || Threads.Count > 0) + { + return; + } + + Disposed = true; + + foreach (object Obj in HandleTable.Clear()) + { + if (Obj is KSession Session) + { + Session.Dispose(); + } + } + + INvDrvServices.UnloadProcess(this); + + AppletState.Dispose(); + + if (NeedsHbAbi && Executables.Count > 0 && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix)) + { + File.Delete(Executables[0].FilePath); + } + + Device.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting..."); + } + public void Dispose() { Dispose(true); @@ -415,41 +421,21 @@ namespace Ryujinx.HLE.HOS protected virtual void Dispose(bool Disposing) { - if (Disposing && !Disposed) + if (Disposing) { - //If there is still some thread running, disposing the objects is not - //safe as the thread may try to access those resources. Instead, we set - //the flag to have the Process disposed when all threads finishes. - //Note: This may not happen if the guest code gets stuck on a infinite loop. if (Threads.Count > 0) { - ShouldDispose = true; - - Device.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} waiting all threads terminate..."); - - return; - } - - Disposed = true; - - foreach (object Obj in HandleTable.Clear()) - { - if (Obj is KSession Session) + foreach (KThread Thread in Threads.Values) { - Session.Dispose(); + Thread.Thread.StopExecution(); + + Scheduler.ForceWakeUp(Thread); } } - - if (NeedsHbAbi && Executables.Count > 0 && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix)) + else { - File.Delete(Executables[0].FilePath); + Unload(); } - - INvDrvServices.UnloadProcess(this); - - AppletState.Dispose(); - - Device.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting..."); } } } diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs index 8d86f42cd7..8c83338d1d 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs @@ -309,6 +309,8 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer { if (Disposing) { + AudioOut.CloseTrack(Track); + UpdateEvent.Dispose(); } } diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 7eaca74831..272f1de3ae 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -27,8 +27,6 @@ namespace Ryujinx.HLE public Hid Hid { get; private set; } - public event EventHandler Finish; - public Switch(IGalRenderer Renderer, IAalOutput AudioOut) { if (Renderer == null) @@ -78,10 +76,9 @@ namespace Ryujinx.HLE Gpu.Fifo.DispatchCalls(); } - public virtual void OnFinish(EventArgs e) + internal void Unload() { - System.Dispose(); - Finish?.Invoke(this, e); + VFs.Dispose(); } public void Dispose() @@ -94,7 +91,6 @@ namespace Ryujinx.HLE if (Disposing) { System.Dispose(); - VFs.Dispose(); } } } diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs index 56d0ce8d25..7acf73a552 100644 --- a/Ryujinx/Ui/Program.cs +++ b/Ryujinx/Ui/Program.cs @@ -62,16 +62,12 @@ namespace Ryujinx using (GLScreen Screen = new GLScreen(Device, Renderer)) { - Device.Finish += (Sender, Args) => - { - Screen.Exit(); - }; - Screen.MainLoop(); - Device.OnFinish(EventArgs.Empty); + + Device.Dispose(); } - Environment.Exit(0); + AudioOut.Dispose(); } } }