Make HLE disposable safely (#850)
* Make HLE disposable safely This fix the oldest issue with the HLE code: the kernel side disposability. Changelog: - Implement KProcess::UnpauseAndTerminateAllThreadsExcept, KThread::Terminate, KThread::TerminateCurrentProcess, KThread::PrepareForTermiation and the svc post handler accurately. - Implement svcTerminateProcess and svcExitProcess. (both untested) - Fix KHandleTable::Destroy not decrementing refcount of all objects stored in the table. - Spawn a custom KProcess with the maximum priority to terminate every guest KProcess. (terminating kernel emulation safely) - General system stability improvements to enhance the user's experience. * Fix a typo in a comment in KProcess.cs * Address gdk's comments
This commit is contained in:
parent
87bfe681ef
commit
55c956e2ec
7 changed files with 293 additions and 31 deletions
|
@ -70,7 +70,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
public ThreadSchedState SchedFlags { get; private set; }
|
||||
|
||||
public bool ShallBeTerminated { get; private set; }
|
||||
private int _shallBeTerminated;
|
||||
|
||||
public bool ShallBeTerminated { get => _shallBeTerminated != 0; set => _shallBeTerminated = value ? 1 : 0; }
|
||||
|
||||
public bool SyncCancelled { get; set; }
|
||||
public bool WaitingSync { get; set; }
|
||||
|
@ -104,7 +106,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
int priority,
|
||||
int defaultCpuCore,
|
||||
KProcess owner,
|
||||
ThreadType type = ThreadType.User)
|
||||
ThreadType type = ThreadType.User,
|
||||
ThreadStart customHostThreadStart = null)
|
||||
{
|
||||
if ((uint)type > 3)
|
||||
{
|
||||
|
@ -156,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
is64Bits = true;
|
||||
}
|
||||
|
||||
HostThread = new Thread(() => ThreadStart(entrypoint));
|
||||
HostThread = new Thread(customHostThreadStart == null ? () => ThreadStart(entrypoint) : customHostThreadStart);
|
||||
|
||||
Context = new ARMeilleure.State.ExecutionContext();
|
||||
|
||||
|
@ -182,6 +185,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
ThreadUid = System.GetThreadUid();
|
||||
|
||||
HostThread.Name = $"Host Thread (thread id {ThreadUid})";
|
||||
|
||||
_hasBeenInitialized = true;
|
||||
|
||||
if (owner != null)
|
||||
|
@ -300,6 +305,100 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
DecrementReferenceCount();
|
||||
}
|
||||
|
||||
public ThreadSchedState PrepareForTermination()
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
ThreadSchedState result;
|
||||
|
||||
if (Interlocked.CompareExchange(ref _shallBeTerminated, 1, 0) == 0)
|
||||
{
|
||||
if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.None)
|
||||
{
|
||||
SchedFlags = ThreadSchedState.TerminationPending;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_forcePauseFlags != ThreadSchedState.None)
|
||||
{
|
||||
_forcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
|
||||
|
||||
ThreadSchedState oldSchedFlags = SchedFlags;
|
||||
|
||||
SchedFlags &= ThreadSchedState.LowMask;
|
||||
|
||||
AdjustScheduling(oldSchedFlags);
|
||||
}
|
||||
|
||||
if (BasePriority >= 0x10)
|
||||
{
|
||||
SetPriority(0xF);
|
||||
}
|
||||
|
||||
if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Running)
|
||||
{
|
||||
// TODO: GIC distributor stuffs (sgir changes ect)
|
||||
}
|
||||
|
||||
SignaledObj = null;
|
||||
ObjSyncResult = KernelResult.ThreadTerminating;
|
||||
|
||||
ReleaseAndResume();
|
||||
}
|
||||
}
|
||||
|
||||
result = SchedFlags;
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return result & ThreadSchedState.LowMask;
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
ThreadSchedState state = PrepareForTermination();
|
||||
|
||||
if (state != ThreadSchedState.TerminationPending)
|
||||
{
|
||||
System.Synchronization.WaitFor(new KSynchronizationObject[] { this }, -1, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandlePostSyscall()
|
||||
{
|
||||
ThreadSchedState state;
|
||||
|
||||
do
|
||||
{
|
||||
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
|
||||
{
|
||||
System.Scheduler.ExitThread(this);
|
||||
Exit();
|
||||
|
||||
// As the death of the thread is handled by the CPU emulator, we differ from the official kernel and return here.
|
||||
break;
|
||||
}
|
||||
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
|
||||
{
|
||||
state = ThreadSchedState.TerminationPending;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_forcePauseFlags != ThreadSchedState.None)
|
||||
{
|
||||
CombineForcePauseFlags();
|
||||
}
|
||||
|
||||
state = ThreadSchedState.Running;
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
} while (state == ThreadSchedState.TerminationPending);
|
||||
}
|
||||
|
||||
private void ExitImpl()
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue