Improve kernel WaitSynchronization syscall implementation (#1362)

This commit is contained in:
gdkchan 2020-07-17 01:22:13 -03:00 committed by GitHub
parent 88619d71b8
commit 20774dab14
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 16 deletions

View file

@ -1,5 +1,6 @@
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using System;
namespace Ryujinx.HLE.HOS.Kernel.Common namespace Ryujinx.HLE.HOS.Kernel.Common
{ {
@ -22,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
return false; return false;
} }
public static bool UserToKernelInt32Array(KernelContext context, ulong address, int[] values) public static bool UserToKernelInt32Array(KernelContext context, ulong address, Span<int> values)
{ {
KProcess currentProcess = context.Scheduler.GetCurrentProcess(); KProcess currentProcess = context.Scheduler.GetCurrentProcess();

View file

@ -8,6 +8,7 @@ using Ryujinx.HLE.HOS.Kernel.Ipc;
using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
@ -2139,30 +2140,84 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ {
handleIndex = 0; handleIndex = 0;
if ((uint)handlesCount > 0x40) if ((uint)handlesCount > KThread.MaxWaitSyncObjects)
{ {
return KernelResult.MaximumExceeded; return KernelResult.MaximumExceeded;
} }
List<KSynchronizationObject> syncObjs = new List<KSynchronizationObject>(); KThread currentThread = _context.Scheduler.GetCurrentThread();
KProcess process = _context.Scheduler.GetCurrentProcess(); var syncObjs = new Span<KSynchronizationObject>(currentThread.WaitSyncObjects).Slice(0, handlesCount);
if (handlesCount != 0)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
if (currentProcess.MemoryManager.AddrSpaceStart > handlesPtr)
{
return KernelResult.UserCopyFailed;
}
long handlesSize = handlesCount * 4;
if (handlesPtr + (ulong)handlesSize <= handlesPtr)
{
return KernelResult.UserCopyFailed;
}
if (handlesPtr + (ulong)handlesSize - 1 > currentProcess.MemoryManager.AddrSpaceEnd - 1)
{
return KernelResult.UserCopyFailed;
}
Span<int> handles = new Span<int>(currentThread.WaitSyncHandles).Slice(0, handlesCount);
if (!KernelTransfer.UserToKernelInt32Array(_context, handlesPtr, handles))
{
return KernelResult.UserCopyFailed;
}
int processedHandles = 0;
for (; processedHandles < handlesCount; processedHandles++)
{
KSynchronizationObject syncObj = currentProcess.HandleTable.GetObject<KSynchronizationObject>(handles[processedHandles]);
if (syncObj == null)
{
break;
}
syncObjs[processedHandles] = syncObj;
syncObj.IncrementReferenceCount();
}
if (processedHandles != handlesCount)
{
// One or more handles are invalid.
for (int index = 0; index < processedHandles; index++)
{
currentThread.WaitSyncObjects[index].DecrementReferenceCount();
}
return KernelResult.InvalidHandle;
}
}
KernelResult result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex);
if (result == KernelResult.PortRemoteClosed)
{
result = KernelResult.Success;
}
for (int index = 0; index < handlesCount; index++) for (int index = 0; index < handlesCount; index++)
{ {
int handle = process.CpuMemory.Read<int>(handlesPtr + (ulong)index * 4); currentThread.WaitSyncObjects[index].DecrementReferenceCount();
KSynchronizationObject syncObj = process.HandleTable.GetObject<KSynchronizationObject>(handle);
if (syncObj == null)
{
break;
}
syncObjs.Add(syncObj);
} }
return _context.Synchronization.WaitFor(syncObjs.ToArray(), timeout, out handleIndex); return result;
} }
public KernelResult CancelSynchronization(int handle) public KernelResult CancelSynchronization(int handle)

View file

@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Threading namespace Ryujinx.HLE.HOS.Kernel.Threading
@ -12,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_context = context; _context = context;
} }
public KernelResult WaitFor(KSynchronizationObject[] syncObjs, long timeout, out int handleIndex) public KernelResult WaitFor(Span<KSynchronizationObject> syncObjs, long timeout, out int handleIndex)
{ {
handleIndex = 0; handleIndex = 0;

View file

@ -12,6 +12,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
class KThread : KSynchronizationObject, IKFutureSchedulerObject class KThread : KSynchronizationObject, IKFutureSchedulerObject
{ {
public const int MaxWaitSyncObjects = 64;
private int _hostThreadRunning; private int _hostThreadRunning;
public Thread HostThread { get; private set; } public Thread HostThread { get; private set; }
@ -39,6 +41,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public ulong TlsAddress => _tlsAddress; public ulong TlsAddress => _tlsAddress;
public ulong TlsDramAddress { get; private set; } public ulong TlsDramAddress { get; private set; }
public KSynchronizationObject[] WaitSyncObjects { get; }
public int[] WaitSyncHandles { get; }
public long LastScheduledTime { get; set; } public long LastScheduledTime { get; set; }
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; } public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
@ -96,6 +101,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_scheduler = KernelContext.Scheduler; _scheduler = KernelContext.Scheduler;
_schedulingData = KernelContext.Scheduler.SchedulingData; _schedulingData = KernelContext.Scheduler.SchedulingData;
WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects];
WaitSyncHandles = new int[MaxWaitSyncObjects];
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount]; SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
_mutexWaiters = new LinkedList<KThread>(); _mutexWaiters = new LinkedList<KThread>();