diff options
author | Thog <me@thog.eu> | 2019-12-26 02:50:17 +0100 |
---|---|---|
committer | Ac_K <Acoustik666@gmail.com> | 2019-12-26 02:50:17 +0100 |
commit | 55c956e2ec83b2b7f414688c4fe4ed9f1f316935 (patch) | |
tree | a543fffefd7b014d6f4a7c5688040e68edcbe018 /Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs | |
parent | 87bfe681ef65ed692aa1e46e3f5f8229013cf46a (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.cs | 105 |
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(); |