Thread scheduler rewrite (#393)

* Started to rewrite the thread scheduler

* Add a single core-like scheduling mode, enabled by default

* Clear exclusive monitor on context switch

* Add SetThreadActivity, misc fixes

* Implement WaitForAddress and SignalToAddress svcs, misc fixes

* Misc fixes (on SetActivity and Arbiter), other tweaks

* Rebased

* Add missing null check

* Rename multicore key on config, fix UpdatePriorityInheritance

* Make scheduling data MLQs private

* nit: Ordering
This commit is contained in:
gdkchan 2018-09-18 20:36:43 -03:00 committed by GitHub
parent 33e2810ef3
commit b8133c1997
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 3262 additions and 1540 deletions

View file

@ -68,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Event != null)
{
Event.WaitEvent.Reset();
Event.Reset();
ThreadState.X0 = 0;
}
@ -80,115 +80,6 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
private void SvcWaitSynchronization(AThreadState ThreadState)
{
long HandlesPtr = (long)ThreadState.X1;
int HandlesCount = (int)ThreadState.X2;
ulong Timeout = ThreadState.X3;
Device.Log.PrintDebug(LogClass.KernelSvc,
"HandlesPtr = 0x" + HandlesPtr .ToString("x16") + ", " +
"HandlesCount = 0x" + HandlesCount.ToString("x8") + ", " +
"Timeout = 0x" + Timeout .ToString("x16"));
if ((uint)HandlesCount > 0x40)
{
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.CountOutOfRange);
return;
}
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
WaitHandle[] Handles = new WaitHandle[HandlesCount + 1];
for (int Index = 0; Index < HandlesCount; Index++)
{
int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
KSynchronizationObject SyncObj = Process.HandleTable.GetData<KSynchronizationObject>(Handle);
if (SyncObj == null)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
}
Handles[Index] = SyncObj.WaitEvent;
}
using (AutoResetEvent WaitEvent = new AutoResetEvent(false))
{
if (!SyncWaits.TryAdd(CurrThread, WaitEvent))
{
throw new InvalidOperationException();
}
Handles[HandlesCount] = WaitEvent;
Process.Scheduler.Suspend(CurrThread);
int HandleIndex;
ulong Result = 0;
if (Timeout != ulong.MaxValue)
{
HandleIndex = WaitHandle.WaitAny(Handles, NsTimeConverter.GetTimeMs(Timeout));
}
else
{
HandleIndex = WaitHandle.WaitAny(Handles);
}
if (HandleIndex == WaitHandle.WaitTimeout)
{
Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
}
else if (HandleIndex == HandlesCount)
{
Result = MakeError(ErrorModule.Kernel, KernelErr.Canceled);
}
SyncWaits.TryRemove(CurrThread, out _);
Process.Scheduler.Resume(CurrThread);
ThreadState.X0 = Result;
if (Result == 0)
{
ThreadState.X1 = (ulong)HandleIndex;
}
}
}
private void SvcCancelSynchronization(AThreadState ThreadState)
{
int ThreadHandle = (int)ThreadState.X0;
KThread Thread = GetThread(ThreadState.Tpidr, ThreadHandle);
if (Thread == null)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
}
if (SyncWaits.TryRemove(Thread, out AutoResetEvent WaitEvent))
{
WaitEvent.Set();
}
ThreadState.X0 = 0;
}
private void SvcGetSystemTick(AThreadState ThreadState)
{
ThreadState.X0 = ThreadState.CntpctEl0;
@ -203,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Kernel
//TODO: Validate that app has perms to access the service, and that the service
//actually exists, return error codes otherwise.
KSession Session = new KSession(ServiceFactory.MakeService(Name), Name);
KSession Session = new KSession(ServiceFactory.MakeService(System, Name), Name);
ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
@ -225,27 +116,38 @@ namespace Ryujinx.HLE.HOS.Kernel
(int)ThreadState.X2);
}
private void SendSyncRequest(AThreadState ThreadState, long CmdPtr, long Size, int Handle)
private void SendSyncRequest(AThreadState ThreadState, long MessagePtr, long Size, int Handle)
{
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
byte[] CmdData = Memory.ReadBytes(CmdPtr, Size);
byte[] MessageData = Memory.ReadBytes(MessagePtr, Size);
KSession Session = Process.HandleTable.GetData<KSession>(Handle);
if (Session != null)
{
Process.Scheduler.Suspend(CurrThread);
//Process.Scheduler.Suspend(CurrThread);
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr);
System.CriticalSectionLock.Lock();
long Result = IpcHandler.IpcCall(Device, Process, Memory, Session, Cmd, CmdPtr);
KThread CurrentThread = System.Scheduler.GetCurrentThread();
Thread.Yield();
CurrentThread.SignaledObj = null;
CurrentThread.ObjSyncResult = 0;
Process.Scheduler.Resume(CurrThread);
CurrentThread.Reschedule(ThreadSchedState.Paused);
ThreadState.X0 = (ulong)Result;
IpcMessage Message = new IpcMessage(MessageData, MessagePtr);
ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
CurrentThread,
Session,
Message,
MessagePtr));
System.CriticalSectionLock.Unlock();
ThreadState.X0 = (ulong)CurrentThread.ObjSyncResult;
}
else
{
@ -255,6 +157,21 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
private void ProcessIpcRequest(object State)
{
HleIpcMessage IpcMessage = (HleIpcMessage)State;
IpcMessage.Thread.ObjSyncResult = (int)IpcHandler.IpcCall(
Device,
Process,
Memory,
IpcMessage.Session,
IpcMessage.Message,
IpcMessage.MessagePtr);
IpcMessage.Thread.Reschedule(ThreadSchedState.Running);
}
private void SvcBreak(AThreadState ThreadState)
{
long Reason = (long)ThreadState.X0;