diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2018-06-10 21:46:42 -0300 |
|---|---|---|
| committer | gdkchan <gab.dark.100@gmail.com> | 2018-06-10 21:46:42 -0300 |
| commit | 76f3b1b3a4637ec72abfbb8cbc0679f2e0ca838f (patch) | |
| tree | 0411b709de31c1c0517763512df8eeb9f7491bc9 /Ryujinx.HLE/OsHle/Handles | |
| parent | 518fe799da6dd4f12c58c9e6e174767effb0b868 (diff) | |
Rename Ryujinx.Core to Ryujinx.HLE and add a separate project for a future LLE implementation
Diffstat (limited to 'Ryujinx.HLE/OsHle/Handles')
| -rw-r--r-- | Ryujinx.HLE/OsHle/Handles/HSharedMem.cs | 44 | ||||
| -rw-r--r-- | Ryujinx.HLE/OsHle/Handles/HTransferMem.cs | 21 | ||||
| -rw-r--r-- | Ryujinx.HLE/OsHle/Handles/KEvent.cs | 4 | ||||
| -rw-r--r-- | Ryujinx.HLE/OsHle/Handles/KProcessHandleTable.cs | 34 | ||||
| -rw-r--r-- | Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs | 351 | ||||
| -rw-r--r-- | Ryujinx.HLE/OsHle/Handles/KSession.cs | 31 | ||||
| -rw-r--r-- | Ryujinx.HLE/OsHle/Handles/KSynchronizationObject.cs | 28 | ||||
| -rw-r--r-- | Ryujinx.HLE/OsHle/Handles/KThread.cs | 86 | ||||
| -rw-r--r-- | Ryujinx.HLE/OsHle/Handles/SchedulerThread.cs | 48 | ||||
| -rw-r--r-- | Ryujinx.HLE/OsHle/Handles/ThreadQueue.cs | 158 |
10 files changed, 805 insertions, 0 deletions
diff --git a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs b/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs new file mode 100644 index 00000000..6426e585 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs @@ -0,0 +1,44 @@ +using ChocolArm64.Memory; +using System; +using System.Collections.Generic; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class HSharedMem + { + private List<(AMemory, long)> Positions; + + public EventHandler<EventArgs> MemoryMapped; + public EventHandler<EventArgs> MemoryUnmapped; + + public HSharedMem() + { + Positions = new List<(AMemory, long)>(); + } + + public void AddVirtualPosition(AMemory Memory, long Position) + { + lock (Positions) + { + Positions.Add((Memory, Position)); + + MemoryMapped?.Invoke(this, EventArgs.Empty); + } + } + + public void RemoveVirtualPosition(AMemory Memory, long Position) + { + lock (Positions) + { + Positions.Remove((Memory, Position)); + + MemoryUnmapped?.Invoke(this, EventArgs.Empty); + } + } + + public (AMemory, long)[] GetVirtualPositions() + { + return Positions.ToArray(); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs b/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs new file mode 100644 index 00000000..2969a2ba --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs @@ -0,0 +1,21 @@ +using ChocolArm64.Memory; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class HTransferMem + { + public AMemory Memory { get; private set; } + public AMemoryPerm Perm { get; private set; } + + public long Position { get; private set; } + public long Size { get; private set; } + + public HTransferMem(AMemory Memory, AMemoryPerm Perm, long Position, long Size) + { + this.Memory = Memory; + this.Perm = Perm; + this.Position = Position; + this.Size = Size; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KEvent.cs b/Ryujinx.HLE/OsHle/Handles/KEvent.cs new file mode 100644 index 00000000..df5108f9 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KEvent.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + class KEvent : KSynchronizationObject { } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KProcessHandleTable.cs b/Ryujinx.HLE/OsHle/Handles/KProcessHandleTable.cs new file mode 100644 index 00000000..d22b63c6 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KProcessHandleTable.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class KProcessHandleTable + { + private IdDictionary Handles; + + public KProcessHandleTable() + { + Handles = new IdDictionary(); + } + + public int OpenHandle(object Obj) + { + return Handles.Add(Obj); + } + + public T GetData<T>(int Handle) + { + return Handles.GetData<T>(Handle); + } + + public object CloseHandle(int Handle) + { + return Handles.Delete(Handle); + } + + public ICollection<object> Clear() + { + return Handles.Clear(); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs new file mode 100644 index 00000000..fa414f68 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs @@ -0,0 +1,351 @@ +using Ryujinx.HLE.Logging; +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class KProcessScheduler : IDisposable + { + private ConcurrentDictionary<KThread, SchedulerThread> AllThreads; + + private ThreadQueue WaitingToRun; + + private KThread[] CoreThreads; + + private bool[] CoreReschedule; + + private object SchedLock; + + private Logger Log; + + public KProcessScheduler(Logger Log) + { + this.Log = Log; + + AllThreads = new ConcurrentDictionary<KThread, SchedulerThread>(); + + WaitingToRun = new ThreadQueue(); + + CoreThreads = new KThread[4]; + + CoreReschedule = new bool[4]; + + SchedLock = new object(); + } + + public void StartThread(KThread Thread) + { + lock (SchedLock) + { + SchedulerThread SchedThread = new SchedulerThread(Thread); + + if (!AllThreads.TryAdd(Thread, SchedThread)) + { + return; + } + + if (TryAddToCore(Thread)) + { + Thread.Thread.Execute(); + + PrintDbgThreadInfo(Thread, "running."); + } + else + { + WaitingToRun.Push(SchedThread); + + PrintDbgThreadInfo(Thread, "waiting to run."); + } + } + } + + public void RemoveThread(KThread Thread) + { + PrintDbgThreadInfo(Thread, "exited."); + + lock (SchedLock) + { + if (AllThreads.TryRemove(Thread, out SchedulerThread SchedThread)) + { + WaitingToRun.Remove(SchedThread); + + SchedThread.Dispose(); + } + + int ActualCore = Thread.ActualCore; + + SchedulerThread NewThread = WaitingToRun.Pop(ActualCore); + + if (NewThread == null) + { + Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {ActualCore}!"); + + CoreThreads[ActualCore] = null; + + return; + } + + NewThread.Thread.ActualCore = ActualCore; + + RunThread(NewThread); + } + } + + public void SetThreadActivity(KThread Thread, bool Active) + { + if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) + { + throw new InvalidOperationException(); + } + + SchedThread.IsActive = Active; + + if (Active) + { + SchedThread.WaitActivity.Set(); + } + else + { + SchedThread.WaitActivity.Reset(); + } + } + + public void EnterWait(KThread Thread, int TimeoutMs = Timeout.Infinite) + { + SchedulerThread SchedThread = AllThreads[Thread]; + + Suspend(Thread); + + SchedThread.WaitSync.WaitOne(TimeoutMs); + + TryResumingExecution(SchedThread); + } + + public void WakeUp(KThread Thread) + { + AllThreads[Thread].WaitSync.Set(); + } + + public void TryToRun(KThread Thread) + { + lock (SchedLock) + { + if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) + { + if (WaitingToRun.HasThread(SchedThread) && TryAddToCore(Thread)) + { + RunThread(SchedThread); + } + else + { + SetReschedule(Thread.ProcessorId); + } + } + } + } + + public void Suspend(KThread Thread) + { + lock (SchedLock) + { + PrintDbgThreadInfo(Thread, "suspended."); + + int ActualCore = Thread.ActualCore; + + CoreReschedule[ActualCore] = false; + + SchedulerThread SchedThread = WaitingToRun.Pop(ActualCore); + + if (SchedThread != null) + { + SchedThread.Thread.ActualCore = ActualCore; + + CoreThreads[ActualCore] = SchedThread.Thread; + + RunThread(SchedThread); + } + else + { + Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ActualCore}!"); + + CoreThreads[ActualCore] = null; + } + } + } + + public void SetReschedule(int Core) + { + lock (SchedLock) + { + CoreReschedule[Core] = true; + } + } + + public void Reschedule(KThread Thread) + { + bool NeedsReschedule; + + lock (SchedLock) + { + int ActualCore = Thread.ActualCore; + + NeedsReschedule = CoreReschedule[ActualCore]; + + CoreReschedule[ActualCore] = false; + } + + if (NeedsReschedule) + { + Yield(Thread, Thread.ActualPriority - 1); + } + } + + public void Yield(KThread Thread) + { + Yield(Thread, Thread.ActualPriority); + } + + private void Yield(KThread Thread, int MinPriority) + { + PrintDbgThreadInfo(Thread, "yielded execution."); + + lock (SchedLock) + { + int ActualCore = Thread.ActualCore; + + SchedulerThread NewThread = WaitingToRun.Pop(ActualCore, MinPriority); + + if (NewThread == null) + { + PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run."); + + return; + } + + NewThread.Thread.ActualCore = ActualCore; + + CoreThreads[ActualCore] = NewThread.Thread; + + RunThread(NewThread); + } + + Resume(Thread); + } + + public void Resume(KThread Thread) + { + TryResumingExecution(AllThreads[Thread]); + } + + private void TryResumingExecution(SchedulerThread SchedThread) + { + KThread Thread = SchedThread.Thread; + + PrintDbgThreadInfo(Thread, "trying to resume..."); + + SchedThread.WaitActivity.WaitOne(); + + lock (SchedLock) + { + if (TryAddToCore(Thread)) + { + PrintDbgThreadInfo(Thread, "resuming execution..."); + + return; + } + + WaitingToRun.Push(SchedThread); + + SetReschedule(Thread.ProcessorId); + + PrintDbgThreadInfo(Thread, "entering wait state..."); + } + + SchedThread.WaitSched.WaitOne(); + + PrintDbgThreadInfo(Thread, "resuming execution..."); + } + + private void RunThread(SchedulerThread SchedThread) + { + if (!SchedThread.Thread.Thread.Execute()) + { + PrintDbgThreadInfo(SchedThread.Thread, "waked."); + + SchedThread.WaitSched.Set(); + } + else + { + PrintDbgThreadInfo(SchedThread.Thread, "running."); + } + } + + public void Resort(KThread Thread) + { + if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) + { + WaitingToRun.Resort(SchedThread); + } + } + + private bool TryAddToCore(KThread Thread) + { + //First, try running it on Ideal Core. + int IdealCore = Thread.IdealCore; + + if (IdealCore != -1 && CoreThreads[IdealCore] == null) + { + Thread.ActualCore = IdealCore; + + CoreThreads[IdealCore] = Thread; + + return true; + } + + //If that fails, then try running on any core allowed by Core Mask. + int CoreMask = Thread.CoreMask; + + for (int Core = 0; Core < CoreThreads.Length; Core++, CoreMask >>= 1) + { + if ((CoreMask & 1) != 0 && CoreThreads[Core] == null) + { + Thread.ActualCore = Core; + + CoreThreads[Core] = Thread; + + return true; + } + } + + return false; + } + + private void PrintDbgThreadInfo(KThread Thread, string Message) + { + Log.PrintDebug(LogClass.KernelScheduler, "(" + + "ThreadId = " + Thread.ThreadId + ", " + + "CoreMask = 0x" + Thread.CoreMask.ToString("x1") + ", " + + "ActualCore = " + Thread.ActualCore + ", " + + "IdealCore = " + Thread.IdealCore + ", " + + "ActualPriority = " + Thread.ActualPriority + ", " + + "WantedPriority = " + Thread.WantedPriority + ") " + Message); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + foreach (SchedulerThread SchedThread in AllThreads.Values) + { + SchedThread.Dispose(); + } + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KSession.cs b/Ryujinx.HLE/OsHle/Handles/KSession.cs new file mode 100644 index 00000000..e85de36c --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KSession.cs @@ -0,0 +1,31 @@ +using Ryujinx.HLE.OsHle.Services; +using System; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class KSession : IDisposable + { + public IpcService Service { get; private set; } + + public string ServiceName { get; private set; } + + public KSession(IpcService Service, string ServiceName) + { + this.Service = Service; + this.ServiceName = ServiceName; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing && Service is IDisposable DisposableService) + { + DisposableService.Dispose(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KSynchronizationObject.cs b/Ryujinx.HLE/OsHle/Handles/KSynchronizationObject.cs new file mode 100644 index 00000000..0e7d06f6 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KSynchronizationObject.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class KSynchronizationObject : IDisposable + { + public ManualResetEvent WaitEvent { get; private set; } + + public KSynchronizationObject() + { + WaitEvent = new ManualResetEvent(false); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + WaitEvent.Dispose(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KThread.cs b/Ryujinx.HLE/OsHle/Handles/KThread.cs new file mode 100644 index 00000000..d26a52f0 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KThread.cs @@ -0,0 +1,86 @@ +using ChocolArm64; +using System.Collections.Generic; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class KThread : KSynchronizationObject + { + public AThread Thread { get; private set; } + + public int CoreMask { get; set; } + + public long MutexAddress { get; set; } + public long CondVarAddress { get; set; } + + public bool CondVarSignaled { get; set; } + + private Process Process; + + public List<KThread> MutexWaiters { get; private set; } + + public KThread MutexOwner { get; set; } + + public int ActualPriority { get; private set; } + public int WantedPriority { get; private set; } + + public int ActualCore { get; set; } + public int ProcessorId { get; set; } + public int IdealCore { get; set; } + + public int WaitHandle { get; set; } + + public int ThreadId => Thread.ThreadId; + + public KThread( + AThread Thread, + Process Process, + int ProcessorId, + int Priority) + { + this.Thread = Thread; + this.Process = Process; + this.ProcessorId = ProcessorId; + this.IdealCore = ProcessorId; + + MutexWaiters = new List<KThread>(); + + CoreMask = 1 << ProcessorId; + + ActualPriority = WantedPriority = Priority; + } + + public void SetPriority(int Priority) + { + WantedPriority = Priority; + + UpdatePriority(); + } + + public void UpdatePriority() + { + int OldPriority = ActualPriority; + + int CurrPriority = WantedPriority; + + lock (Process.ThreadSyncLock) + { + foreach (KThread Thread in MutexWaiters) + { + if (CurrPriority > Thread.WantedPriority) + { + CurrPriority = Thread.WantedPriority; + } + } + } + + if (CurrPriority != OldPriority) + { + ActualPriority = CurrPriority; + + Process.Scheduler.Resort(this); + + MutexOwner?.UpdatePriority(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/SchedulerThread.cs b/Ryujinx.HLE/OsHle/Handles/SchedulerThread.cs new file mode 100644 index 00000000..5bdefe74 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/SchedulerThread.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class SchedulerThread : IDisposable + { + public KThread Thread { get; private set; } + + public SchedulerThread Next { get; set; } + + public bool IsActive { get; set; } + + public AutoResetEvent WaitSync { get; private set; } + public ManualResetEvent WaitActivity { get; private set; } + public AutoResetEvent WaitSched { get; private set; } + + public SchedulerThread(KThread Thread) + { + this.Thread = Thread; + + IsActive = true; + + WaitSync = new AutoResetEvent(false); + + WaitActivity = new ManualResetEvent(true); + + WaitSched = new AutoResetEvent(false); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + WaitSync.Dispose(); + + WaitActivity.Dispose(); + + WaitSched.Dispose(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/ThreadQueue.cs b/Ryujinx.HLE/OsHle/Handles/ThreadQueue.cs new file mode 100644 index 00000000..3cb82f0d --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/ThreadQueue.cs @@ -0,0 +1,158 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + class ThreadQueue + { + private const int LowestPriority = 0x3f; + + private SchedulerThread Head; + + private object ListLock; + + public ThreadQueue() + { + ListLock = new object(); + } + + public void Push(SchedulerThread Wait) + { + lock (ListLock) + { + //Ensure that we're not creating circular references + //by adding a thread that is already on the list. + if (HasThread(Wait)) + { + return; + } + + if (Head == null || Head.Thread.ActualPriority > Wait.Thread.ActualPriority) + { + Wait.Next = Head; + + Head = Wait; + + return; + } + + SchedulerThread Curr = Head; + + while (Curr.Next != null) + { + if (Curr.Next.Thread.ActualPriority > Wait.Thread.ActualPriority) + { + break; + } + + Curr = Curr.Next; + } + + Wait.Next = Curr.Next; + Curr.Next = Wait; + } + } + + public SchedulerThread Pop(int Core, int MinPriority = LowestPriority) + { + lock (ListLock) + { + int CoreMask = 1 << Core; + + SchedulerThread Prev = null; + SchedulerThread Curr = Head; + + while (Curr != null) + { + KThread Thread = Curr.Thread; + + if (Thread.ActualPriority <= MinPriority && (Thread.CoreMask & CoreMask) != 0) + { + if (Prev != null) + { + Prev.Next = Curr.Next; + } + else + { + Head = Head.Next; + } + + break; + } + + Prev = Curr; + Curr = Curr.Next; + } + + return Curr; + } + } + + public bool Remove(SchedulerThread Thread) + { + lock (ListLock) + { + if (Head == null) + { + return false; + } + else if (Head == Thread) + { + Head = Head.Next; + + return true; + } + + SchedulerThread Prev = Head; + SchedulerThread Curr = Head.Next; + + while (Curr != null) + { + if (Curr == Thread) + { + Prev.Next = Curr.Next; + + return true; + } + + Prev = Curr; + Curr = Curr.Next; + } + + return false; + } + } + + public bool Resort(SchedulerThread Thread) + { + lock (ListLock) + { + if (Remove(Thread)) + { + Push(Thread); + + return true; + } + + return false; + } + } + + public bool HasThread(SchedulerThread Thread) + { + lock (ListLock) + { + SchedulerThread Curr = Head; + + while (Curr != null) + { + if (Curr == Thread) + { + return true; + } + + Curr = Curr.Next; + } + + return false; + } + } + } +}
\ No newline at end of file |
