Rename Ryujinx.Core to Ryujinx.HLE and add a separate project for a future LLE implementation

This commit is contained in:
gdkchan 2018-06-10 21:46:42 -03:00
parent 518fe799da
commit 76f3b1b3a4
248 changed files with 2266 additions and 2244 deletions

View file

@ -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();
}
}
}

View file

@ -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;
}
}
}

View file

@ -0,0 +1,4 @@
namespace Ryujinx.HLE.OsHle.Handles
{
class KEvent : KSynchronizationObject { }
}

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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;
}
}
}
}