Exit gracefully when the application is closed
This commit is contained in:
parent
4be888c213
commit
4a6ea59302
10 changed files with 115 additions and 79 deletions
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Audio
|
||||
{
|
||||
public interface IAalOutput
|
||||
public interface IAalOutput : IDisposable
|
||||
{
|
||||
int OpenTrack(int SampleRate, int Channels, ReleaseCallback Callback);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -56,9 +56,7 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
private ConcurrentDictionary<long, KThread> Threads;
|
||||
|
||||
private KThread MainThread;
|
||||
|
||||
private List<Executable> Executables;
|
||||
private List<Executable> Executables;
|
||||
|
||||
private Dictionary<long, string> SymbolTable;
|
||||
|
||||
|
@ -155,7 +153,7 @@ namespace Ryujinx.HLE.HOS
|
|||
return false;
|
||||
}
|
||||
|
||||
MainThread = HandleTable.GetData<KThread>(Handle);
|
||||
KThread MainThread = HandleTable.GetData<KThread>(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...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -309,6 +309,8 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
|
|||
{
|
||||
if (Disposing)
|
||||
{
|
||||
AudioOut.CloseTrack(Track);
|
||||
|
||||
UpdateEvent.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue