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/Process/KProcess.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/Process/KProcess.cs')
-rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 127 |
1 files changed, 108 insertions, 19 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 1b16d79a..c74f6fca 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public ulong PersonalMmHeapPagesCount { get; private set; } - private ProcessState _state; + public ProcessState State { get; private set; } private object _processLock; private object _threadingLock; @@ -383,7 +383,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process Name = creationInfo.Name; - _state = ProcessState.Created; + State = ProcessState.Created; _creationTimestamp = PerformanceCounter.ElapsedMilliseconds; @@ -579,7 +579,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { lock (_processLock) { - if (_state > ProcessState.CreatedAttached) + if (State > ProcessState.CreatedAttached) { return KernelResult.InvalidState; } @@ -733,8 +733,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process mainThread.SetEntryArguments(0, mainThreadHandle); - ProcessState oldState = _state; - ProcessState newState = _state != ProcessState.Created + ProcessState oldState = State; + ProcessState newState = State != ProcessState.Created ? ProcessState.Attached : ProcessState.Started; @@ -768,9 +768,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private void SetState(ProcessState newState) { - if (_state != newState) + if (State != newState) { - _state = newState; + State = newState; _signaled = true; Signal(); @@ -820,6 +820,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } + public void DecrementToZeroWhileTerminatingCurrent() + { + System.ThreadCounter.Signal(); + + while (Interlocked.Decrement(ref _threadCount) != 0) + { + Destroy(); + TerminateCurrentProcess(); + } + + // Nintendo panic here because if it reaches this point, the current thread should be already dead. + // As we handle the death of the thread in the post SVC handler and inside the CPU emulator, we don't panic here. + } + public ulong GetMemoryCapacity() { ulong totalCapacity = (ulong)ResourceLimit.GetRemainingValue(LimitableResource.Memory); @@ -909,12 +923,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process lock (_processLock) { - if (_state >= ProcessState.Started) + if (State >= ProcessState.Started) { - if (_state == ProcessState.Started || - _state == ProcessState.Crashed || - _state == ProcessState.Attached || - _state == ProcessState.DebugSuspended) + if (State == ProcessState.Started || + State == ProcessState.Crashed || + State == ProcessState.Attached || + State == ProcessState.DebugSuspended) { SetState(ProcessState.Exiting); @@ -933,23 +947,98 @@ namespace Ryujinx.HLE.HOS.Kernel.Process if (shallTerminate) { - // UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread()); + UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread()); HandleTable.Destroy(); - SignalExitForDebugEvent(); + SignalExitToDebugTerminated(); SignalExit(); } return result; } - private void UnpauseAndTerminateAllThreadsExcept(KThread thread) + public void TerminateCurrentProcess() { - // TODO. + bool shallTerminate = false; + + System.CriticalSection.Enter(); + + lock (_processLock) + { + if (State >= ProcessState.Started) + { + if (State == ProcessState.Started || + State == ProcessState.Attached || + State == ProcessState.DebugSuspended) + { + SetState(ProcessState.Exiting); + + shallTerminate = true; + } + } + } + + System.CriticalSection.Leave(); + + if (shallTerminate) + { + UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread()); + + HandleTable.Destroy(); + + // NOTE: this is supposed to be called in receiving of the mailbox. + SignalExitToDebugExited(); + SignalExit(); + } + } + + private void UnpauseAndTerminateAllThreadsExcept(KThread currentThread) + { + lock (_threadingLock) + { + System.CriticalSection.Enter(); + + foreach (KThread thread in _threads) + { + if ((thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending) + { + thread.PrepareForTermination(); + } + } + + System.CriticalSection.Leave(); + } + + KThread blockedThread = null; + + lock (_threadingLock) + { + foreach (KThread thread in _threads) + { + if (thread != currentThread && (thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending) + { + thread.IncrementReferenceCount(); + + blockedThread = thread; + break; + } + } + } + + if (blockedThread != null) + { + blockedThread.Terminate(); + blockedThread.DecrementReferenceCount(); + } + } + + private void SignalExitToDebugTerminated() + { + // TODO: Debug events. } - private void SignalExitForDebugEvent() + private void SignalExitToDebugExited() { // TODO: Debug events. } @@ -976,7 +1065,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process lock (_processLock) { - if (_state != ProcessState.Exited && _signaled) + if (State != ProcessState.Exited && _signaled) { _signaled = false; @@ -999,7 +1088,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { foreach (KThread thread in _threads) { - thread.Context.Running = false; + System.Scheduler.ExitThread(thread); System.Scheduler.CoreManager.Set(thread.HostThread); } |