forked from Mirror/Ryujinx
Improve kernel WaitSynchronization syscall implementation (#1362)
This commit is contained in:
parent
88619d71b8
commit
20774dab14
4 changed files with 81 additions and 16 deletions
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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>();
|
||||||
|
|
Reference in a new issue