aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
diff options
context:
space:
mode:
authorThog <me@thog.eu>2019-12-26 02:50:17 +0100
committerAc_K <Acoustik666@gmail.com>2019-12-26 02:50:17 +0100
commit55c956e2ec83b2b7f414688c4fe4ed9f1f316935 (patch)
treea543fffefd7b014d6f4a7c5688040e68edcbe018 /Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
parent87bfe681ef65ed692aa1e46e3f5f8229013cf46a (diff)
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
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs105
1 files changed, 102 insertions, 3 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
index 0232bc16..e1a49a56 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
@@ -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();